From 45e1458ed758af56ef394c0eab551355e19f88c6 Mon Sep 17 00:00:00 2001 From: Thomas Michael Date: Mon, 1 Dec 2025 11:59:25 +0100 Subject: [PATCH] add profiles and first test classes --- README.md | 71 ++++++++++++------- .../gitops/cli/GitopsPlaygroundCli.groovy | 41 +++++++++-- .../com/cloudogu/gitops/config/Config.groovy | 3 + .../gitops/config/ConfigConstants.groovy | 1 + .../application-content-examples.yaml | 16 +++++ .../resources/application-full-prefix.yaml | 24 +++++++ src/main/resources/application-full.yaml | 23 ++++++ .../resources/application-operator-full.yaml | 25 +++++++ .../application-operator-mandants.yaml | 4 ++ .../application-operator-petclinic.yaml | 16 +++++ .../resources/application-operator-small.yaml | 9 +++ src/main/resources/application-small.yaml | 8 +++ .../profiles/ArgoCDProfileTest.groovy | 69 ++++++++++++++++++ .../profiles/CertManagerProfileTest.groovy | 71 +++++++++++++++++++ .../profiles/PetclincProfileTest.groovy | 47 ++++++++++++ 15 files changed, 395 insertions(+), 33 deletions(-) create mode 100644 src/main/resources/application-content-examples.yaml create mode 100644 src/main/resources/application-full-prefix.yaml create mode 100644 src/main/resources/application-full.yaml create mode 100644 src/main/resources/application-operator-full.yaml create mode 100644 src/main/resources/application-operator-mandants.yaml create mode 100644 src/main/resources/application-operator-petclinic.yaml create mode 100644 src/main/resources/application-operator-small.yaml create mode 100644 src/main/resources/application-small.yaml create mode 100644 src/test/groovy/com/cloudogu/gitops/integration/profiles/ArgoCDProfileTest.groovy create mode 100644 src/test/groovy/com/cloudogu/gitops/integration/profiles/CertManagerProfileTest.groovy create mode 100644 src/test/groovy/com/cloudogu/gitops/integration/profiles/PetclincProfileTest.groovy diff --git a/README.md b/README.md index 7a4d4510d..b956d7d5b 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ We recommend running this command as an unprivileged user, that is inside the [d - [External Mailserver](#external-mailserver) - [Secrets Management](#secrets-management) - [Certificate Management](#certificate-management) + - [Profiles](#profiles) - [Remove playground](#remove-playground) - [Running on Windows or Mac](#running-on-windows-or-mac) - [Mac and Windows WSL](#mac-and-windows-wsl) @@ -289,32 +290,33 @@ That is, if you pass a param via CLI, for example, it will overwrite the corresp ###### Application -| CLI | Config | Default | Type | Description | -|-----|--------|---------|------|-------------| -| `--config-file` | - | `''` | String | Config file path | -| `--config-map` | - | `''` | String | Config map name | -| `-d, --debug` | `application.debug` | - | Boolean | Enable debug mode | -| `-x, --trace` | `application.trace` | - | Boolean | Enable trace mode | -| `--output-config-file` | `application.outputConfigFile` | `false` | Boolean | Output configuration file | -| `-v, --version` | `application.versionInfoRequested` | `false` | Boolean | Display version and license info | -| `-h, --help` | `application.usageHelpRequested` | `false` | Boolean | Display help message | -| `--remote` | `application.remote` | `false` | Boolean | Expose services as LoadBalancers | -| `--insecure` | `application.insecure` | `false` | Boolean | Sets insecure-mode in cURL which skips cert validation | -| `--openshift` | `application.openshift` | `false` | Boolean | When set, openshift specific resources and configurations are applied | -| `--username` | `application.username` | `'admin'` | String | Set initial admin username | -| `--password` | `application.password` | `'admin'` | String | Set initial admin passwords | -| `-y, --yes` | `application.yes` | `false` | Boolean | Skip confirmation | -| `--name-prefix` | `application.namePrefix` | `''` | String | Set name-prefix for repos, jobs, namespaces | -| `--destroy` | `application.destroy` | `false` | Boolean | Unroll playground | -| `--pod-resources` | `application.podResources` | `false` | Boolean | Write kubernetes resource requests and limits on each pod | -| `--git-name` | `application.gitName` | `'Cloudogu'` | String | Sets git author and committer name used for initial commits | -| `--git-email` | `application.gitEmail` | `'hello@cloudogu.com'` | String | Sets git author and committer email used for initial commits | -| `--base-url` | `application.baseUrl` | `''` | String | The external base url (TLD) for all tools | -| `--url-separator-hyphen` | `application.urlSeparatorHyphen` | `false` | Boolean | Use hyphens instead of dots to separate application name from base-url | -| `--mirror-repos` | `application.mirrorRepos` | `false` | Boolean | Changes the sources of deployed tools so they work in air-gapped environments | -| `--skip-crds` | `application.skipCrds` | `false` | Boolean | Skip installation of CRDs | -| `--namespace-isolation` | `application.namespaceIsolation` | `false` | Boolean | Configure tools to work with given namespaces only | -| `--netpols` | `application.netpols` | `false` | Boolean | Sets Network Policies | +| CLI | Config | Default | Type | Description | +|--------------------------|------------------------------------|---------|----------|-------------------------------------------------------------------------------| +| `--config-file` | - | `''` | String | Config file path | +| `--config-map` | - | `''` | String | Config map name | +| `-d, --debug` | `application.debug` | - | Boolean | Enable debug mode | +| `-x, --trace` | `application.trace` | - | Boolean | Enable trace mode | +| `--output-config-file` | `application.outputConfigFile` | `false` | Boolean | Output configuration file | +| `-v, --version` | `application.versionInfoRequested` | `false` | Boolean | Display version and license info | +| `-h, --help` | `application.usageHelpRequested` | `false` | Boolean | Display help message | +| `--remote` | `application.remote` | `false` | Boolean | Expose services as LoadBalancers | +| `--insecure` | `application.insecure` | `false` | Boolean | Sets insecure-mode in cURL which skips cert validation | +| `--openshift` | `application.openshift` | `false` | Boolean | When set, openshift specific resources and configurations are applied | +| `--username` | `application.username` | `'admin'` | String | Set initial admin username | +| `--password` | `application.password` | `'admin'` | String | Set initial admin passwords | +| `-y, --yes` | `application.yes` | `false` | Boolean | Skip confirmation | +| `--name-prefix` | `application.namePrefix` | `''` | String | Set name-prefix for repos, jobs, namespaces | +| `--destroy` | `application.destroy` | `false` | Boolean | Unroll playground | +| `--pod-resources` | `application.podResources` | `false` | Boolean | Write kubernetes resource requests and limits on each pod | +| `--git-name` | `application.gitName` | `'Cloudogu'` | String | Sets git author and committer name used for initial commits | +| `--git-email` | `application.gitEmail` | `'hello@cloudogu.com'` | String | Sets git author and committer email used for initial commits | +| `--base-url` | `application.baseUrl` | `''` | String | The external base url (TLD) for all tools | +| `--url-separator-hyphen` | `application.urlSeparatorHyphen` | `false` | Boolean | Use hyphens instead of dots to separate application name from base-url | +| `--mirror-repos` | `application.mirrorRepos` | `false` | Boolean | Changes the sources of deployed tools so they work in air-gapped environments | +| `--skip-crds` | `application.skipCrds` | `false` | Boolean | Skip installation of CRDs | +| `--namespace-isolation` | `application.namespaceIsolation` | `false` | Boolean | Configure tools to work with given namespaces only | +| `--netpols` | `application.netpols` | `false` | Boolean | Sets Network Policies | +| `-p, --profiles` | `application.profile` | `''` | String | Sets a profile for pre-defined parameter | ###### Registry @@ -882,6 +884,23 @@ i.e. ``` --cert-manager-image someRegistry/cert-manager-controller:latest ``` +#### Profiles +GOP includes some pre-defined profiles for easy usage. +e.g. set `--profile=full` to start GOP with all features enabled. + + +Current existing profiles for argocd in non-operator mode: +- `full` - all features enabled +- `small` - starts only with ArgoCD and SCM-Manger +- `content-examples` - starts with ArgoCD, Jenkins, SCM-Manager and Petclinic + +Follow profils for ArgoCD in Operator mode which has to be installed first: +- `operator-full` - all features enabled +- `operator-small` - starts only with ArgoCD and SCM-Manger +- `operator-petclinic` - starts with ArgoCD, Jenkins, SCM-Manager and Petclinic +- `operator-mandant` - starts mandant/tenant example + + ### Remove playground For k3d, you can just `k3d cluster delete gitops-playground`. This will delete the whole cluster. diff --git a/src/main/groovy/com/cloudogu/gitops/cli/GitopsPlaygroundCli.groovy b/src/main/groovy/com/cloudogu/gitops/cli/GitopsPlaygroundCli.groovy index 2d32c8a19..7addb8f2d 100644 --- a/src/main/groovy/com/cloudogu/gitops/cli/GitopsPlaygroundCli.groovy +++ b/src/main/groovy/com/cloudogu/gitops/cli/GitopsPlaygroundCli.groovy @@ -55,7 +55,7 @@ class GitopsPlaygroundCli { new CommandLine(cliParams).execute(args) return ReturnCode.SUCCESS } - + def version = createVersionOutput() if (cliParams.application.versionInfoRequested) { println version @@ -72,7 +72,7 @@ class GitopsPlaygroundCli { println(config.toYaml(false)) return ReturnCode.SUCCESS } - + // Set internal values in config after help/version/output because these should work without connecting to k8s // eg a simple docker run .. --help should not fail with connection refused config = applicationConfigurator.initConfig(config) @@ -186,10 +186,15 @@ class GitopsPlaygroundCli { def cliParams = new Config() new CommandLine(cliParams).parseArgs(args) + // first evaluate profile for setting predefined values e.g. examples, if applicable + Config profileConfig = extractProfile(cliParams) + + + String configFilePath = cliParams.application.configFile String configMapName = cliParams.application.configMap - Boolean contentExamples = cliParams.content.examples - Boolean multiTenancyExamples = cliParams.content.multitenancyExamples + Boolean contentExamples = cliParams.content.examples || profileConfig.content.examples + Boolean multiTenancyExamples = cliParams.content.multitenancyExamples || profileConfig.content.multitenancyExamples Map configFile = [:] Map configMap = [:] @@ -219,20 +224,21 @@ class GitopsPlaygroundCli { multiTenancyContentExamplesFile = validateConfig(new File(multiTenancyContentExamplesConfigPath).text) } + // Last one takes precedence - def configPrecedence = [configMap, configFile, contentExamplesFile, multiTenancyContentExamplesFile] + def configPrecedence = [profileConfig.toMap(), configMap, configFile, contentExamplesFile, multiTenancyContentExamplesFile] Map mergedConfigs = [:] configPrecedence.each { deepMerge(it, mergedConfigs) } // DeepMerge with default Config values to keep the default values defined in Config.groovy - mergedConfigs = deepMerge(mergedConfigs,new Config().toMap()) + mergedConfigs = deepMerge(mergedConfigs, new Config().toMap()) log.debug("Writing CLI params into config") Config mergedConfig = Config.fromMap(mergedConfigs) new CommandLine(mergedConfig).parseArgs(args) - + return mergedConfig } @@ -274,4 +280,25 @@ class GitopsPlaygroundCli { } } } + + private static Config extractProfile(Config newConfig) { + + String profile = newConfig.application.profile + + Config profileConfig = new Config() + if (profile) { + String profileName = "src/main/resources/application-${profile}.yaml" + log.debug("Loading profile '${profileName}'") + def file + try { + file = new File(profileName) + + } catch (Exception e) { + throw new RuntimeException("Profile '${profileName}' does not exist.") + } + Map profileFile = validateConfig(file.text) + profileConfig = Config.fromMap(profileFile) + } + return profileConfig + } } \ No newline at end of file diff --git a/src/main/groovy/com/cloudogu/gitops/config/Config.groovy b/src/main/groovy/com/cloudogu/gitops/config/Config.groovy index d1060ec4f..4b11940be 100644 --- a/src/main/groovy/com/cloudogu/gitops/config/Config.groovy +++ b/src/main/groovy/com/cloudogu/gitops/config/Config.groovy @@ -407,6 +407,9 @@ class Config { @JsonPropertyDescription(CLUSTER_ADMIN_DESCRIPTION) Boolean clusterAdmin = false + @Option(names = ["-p", "--profile"], description = APPLICATION_PROFIL) + String profile + static class NamespaceSchema { LinkedHashSet dedicatedNamespaces = new LinkedHashSet<>() LinkedHashSet tenantNamespaces = new LinkedHashSet<>() diff --git a/src/main/groovy/com/cloudogu/gitops/config/ConfigConstants.groovy b/src/main/groovy/com/cloudogu/gitops/config/ConfigConstants.groovy index d319da958..431831baa 100644 --- a/src/main/groovy/com/cloudogu/gitops/config/ConfigConstants.groovy +++ b/src/main/groovy/com/cloudogu/gitops/config/ConfigConstants.groovy @@ -92,6 +92,7 @@ interface ConfigConstants { String NETPOLS_DESCRIPTION = 'Sets Network Policies' String CLUSTER_ADMIN_DESCRIPTION = 'Binds ArgoCD controllers to cluster-admin ClusterRole' String OPENSHIFT_DESCRIPTION = 'When set, openshift specific resources and configurations are applied' + String APPLICATION_PROFIL = 'Use predefined profile (full, only-argocd, operator-mandants aso.)' // group metrics String MONITORING_DESCRIPTION = 'Config parameters for the Monitoring system (prometheus)' diff --git a/src/main/resources/application-content-examples.yaml b/src/main/resources/application-content-examples.yaml new file mode 100644 index 000000000..2300d1795 --- /dev/null +++ b/src/main/resources/application-content-examples.yaml @@ -0,0 +1,16 @@ +# $schema: https://raw.githubusercontent.com/cloudogu/gitops-playground/main/docs/configuration.schema.json +application: + "yes": true + baseUrl: http://localhost +features: + argocd: + active: true + operator: false + ingressNginx: + active: true +content: + examples: true +jenkins: + active: true +registry: + active: true diff --git a/src/main/resources/application-full-prefix.yaml b/src/main/resources/application-full-prefix.yaml new file mode 100644 index 000000000..f1403a47a --- /dev/null +++ b/src/main/resources/application-full-prefix.yaml @@ -0,0 +1,24 @@ +# $schema: https://raw.githubusercontent.com/cloudogu/gitops-playground/main/docs/configuration.schema.json +application: + "yes": true + baseUrl: http://my-prefix.localhost + namePrefix: my-prefix +features: + certManager: + active: true + argocd: + active: true + operator: false + ingressNginx: + active: true + monitoring: + active: true + secrets: + vault: + mode: "dev" +content: + examples: true +jenkins: + active: true +registry: + active: true diff --git a/src/main/resources/application-full.yaml b/src/main/resources/application-full.yaml new file mode 100644 index 000000000..89d5ad1ab --- /dev/null +++ b/src/main/resources/application-full.yaml @@ -0,0 +1,23 @@ +# $schema: https://raw.githubusercontent.com/cloudogu/gitops-playground/main/docs/configuration.schema.json +application: + "yes": true + baseUrl: http://localhost +features: + certManager: + active: true + argocd: + active: true + operator: false + ingressNginx: + active: true + monitoring: + active: true + secrets: + vault: + mode: "dev" +content: + examples: true +jenkins: + active: true +registry: + active: true diff --git a/src/main/resources/application-operator-full.yaml b/src/main/resources/application-operator-full.yaml new file mode 100644 index 000000000..72079f19d --- /dev/null +++ b/src/main/resources/application-operator-full.yaml @@ -0,0 +1,25 @@ +# $schema: https://raw.githubusercontent.com/cloudogu/gitops-playground/main/docs/configuration.schema.json +application: + "yes": true + baseUrl: http://localhost + insecure: true +features: + certManager: + active: true + argocd: + active: true + operator: true + resourceInclusionsCluster: "https://10.43.0.1:443" + ingressNginx: + active: true + monitoring: + active: true + secrets: + vault: + mode: "dev" +content: + examples: true +jenkins: + active: true +registry: + active: true diff --git a/src/main/resources/application-operator-mandants.yaml b/src/main/resources/application-operator-mandants.yaml new file mode 100644 index 000000000..aa3e238cb --- /dev/null +++ b/src/main/resources/application-operator-mandants.yaml @@ -0,0 +1,4 @@ +# $schema: https://raw.githubusercontent.com/cloudogu/gitops-playground/main/docs/configuration.schema.json +content: + multitenancyExamples: true + diff --git a/src/main/resources/application-operator-petclinic.yaml b/src/main/resources/application-operator-petclinic.yaml new file mode 100644 index 000000000..f285e530b --- /dev/null +++ b/src/main/resources/application-operator-petclinic.yaml @@ -0,0 +1,16 @@ +# $schema: https://raw.githubusercontent.com/cloudogu/gitops-playground/main/docs/configuration.schema.json +application: + "yes": true + baseUrl: http://localhost + insecure: true +features: + argocd: + active: true + operator: true + resourceInclusionsCluster: "https://10.43.0.1:443" +content: + examples: true +jenkins: + active: true +registry: + active: true \ No newline at end of file diff --git a/src/main/resources/application-operator-small.yaml b/src/main/resources/application-operator-small.yaml new file mode 100644 index 000000000..ae387d952 --- /dev/null +++ b/src/main/resources/application-operator-small.yaml @@ -0,0 +1,9 @@ +# $schema: https://raw.githubusercontent.com/cloudogu/gitops-playground/main/docs/configuration.schema.json +application: + "yes": true + baseUrl: http://localhost +features: + argocd: + active: true + operator: + resourceInclusionsCluster: "https://10.43.0.1:443" diff --git a/src/main/resources/application-small.yaml b/src/main/resources/application-small.yaml new file mode 100644 index 000000000..52c48b9d6 --- /dev/null +++ b/src/main/resources/application-small.yaml @@ -0,0 +1,8 @@ +# $schema: https://raw.githubusercontent.com/cloudogu/gitops-playground/main/docs/configuration.schema.json +application: + "yes": true + baseUrl: http://localhost +features: + argocd: + active: true + operator: false diff --git a/src/test/groovy/com/cloudogu/gitops/integration/profiles/ArgoCDProfileTest.groovy b/src/test/groovy/com/cloudogu/gitops/integration/profiles/ArgoCDProfileTest.groovy new file mode 100644 index 000000000..279b96092 --- /dev/null +++ b/src/test/groovy/com/cloudogu/gitops/integration/profiles/ArgoCDProfileTest.groovy @@ -0,0 +1,69 @@ +package com.cloudogu.gitops.integration.profiles + +import com.cloudogu.gitops.integration.features.KubenetesApiTestSetup +import io.kubernetes.client.openapi.models.V1Pod +import io.kubernetes.client.openapi.models.V1PodList +import io.micronaut.context.annotation.Requires +import io.micronaut.test.extensions.junit5.annotation.MicronautTest +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledIfSystemProperty + +import static org.assertj.core.api.Assertions.assertThat + +/** + * This tests can only be successfull, if one of theses profiles used. + */ + +@EnabledIfSystemProperty(named = "micronaut.environments", matches = "full|small|operator-full|petclinic|operator-small") +class ArgoCDProfileTest extends KubenetesApiTestSetup { + + String namespace = 'argocd' + + @BeforeAll + static void labelTest() { + println "###### Integration ArgoCD test ######" + } + + @Test + void ensureNamespaceExists() { + def namespaces = api.listNamespace().execute() + assertThat(namespaces).isNotNull() + assertThat(namespaces.getItems().isEmpty()).isFalse() + def namespace = namespaces.getItems().find { namespace.equals(it.getMetadata().name) } + assertThat(namespace).isNotNull() + } + + /** + * ArgoCD uses 7 pods. All have to run*/ + @Test + void ensureArgoCDIsOnlineAndRunning() { + def expectedSumOfArgoPods = 6 + + V1PodList list = api.listNamespacedPod(namespace ) + .execute() + List argoPods = list.getItems().findAll { it.getMetadata().getName().startsWith("argo") } + assertThat(argoPods.size()).isGreaterThanOrEqualTo(expectedSumOfArgoPods) // 6 or 7 depends on operator + + for (V1Pod pod : argoPods) { + assertThat(pod.status.phase).isEqualTo("Running") + } + + } + + @Override + boolean isReadyToStartTests() { + V1PodList list = api.listPodForAllNamespaces() + .execute() + if (list && !list.items.isEmpty()) { + + List argoPods = list.getItems().findAll { it.getMetadata().getName().startsWith("argo") } + + if (argoPods.size() >= 6) + { + return "Running".equals(argoPods.get(0).status.phase) + } + } + return false + } +} \ No newline at end of file diff --git a/src/test/groovy/com/cloudogu/gitops/integration/profiles/CertManagerProfileTest.groovy b/src/test/groovy/com/cloudogu/gitops/integration/profiles/CertManagerProfileTest.groovy new file mode 100644 index 000000000..e556e492e --- /dev/null +++ b/src/test/groovy/com/cloudogu/gitops/integration/profiles/CertManagerProfileTest.groovy @@ -0,0 +1,71 @@ +package com.cloudogu.gitops.integration.profiles + +import com.cloudogu.gitops.integration.features.KubenetesApiTestSetup +import groovy.util.logging.Slf4j +import io.kubernetes.client.openapi.models.V1Pod +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledIfSystemProperty + +import static org.assertj.core.api.Assertions.assertThat + +/** + * This class checks if cert-manager is started well. + * Cert-Manager contains own namespace ('cert-manager') which owns and 3 Pods: + */ +@Slf4j +@EnabledIfSystemProperty(named = "micronaut.environments", matches = "full") +class CertManagerProfileTest extends KubenetesApiTestSetup { + + String namespace = 'cert-manager' + int sumOfPods = 3 + + @Override + boolean isReadyToStartTests() { + // cert-manager should has 3 running pods + def pods = api.listNamespacedPod(namespace).execute() + if (pods.items.size() != 3) { + return false + } + for (V1Pod pod : pods.getItems()) { + println("Pod ${pod.getMetadata().name} with status ${pod.status.phase}") + if (!"Running".equals(pod.status.phase)) { + return false + } + } + return true + } + + @BeforeAll + static void labelTest() { + println "###### Profile CERT-MANAGER ######" + } + + @Test + void ensureNamespaceExists() { + def namespaces = api.listNamespace().execute() + assertThat(namespaces).isNotNull() + assertThat(namespaces.getItems().isEmpty()).isFalse() + def namespace = namespaces.getItems().find { namespace.equals(it.getMetadata().name) } + assertThat(namespace).isNotNull() + + } + + @Test + void ensureAllCertManagerPodsAreExist() { + + def pods = api.listNamespacedPod(namespace).execute() + assertThat(pods).isNotNull() + assertThat(pods.getItems().isEmpty()).isFalse() + + } + + @Test + void ensureNumberOfPodsAreEqualToSumOfPods() { + + def pods = api.listNamespacedPod(namespace).execute() + assertThat(pods.getItems().size()).isEqualTo(sumOfPods) + + } + +} diff --git a/src/test/groovy/com/cloudogu/gitops/integration/profiles/PetclincProfileTest.groovy b/src/test/groovy/com/cloudogu/gitops/integration/profiles/PetclincProfileTest.groovy new file mode 100644 index 000000000..bbc03f855 --- /dev/null +++ b/src/test/groovy/com/cloudogu/gitops/integration/profiles/PetclincProfileTest.groovy @@ -0,0 +1,47 @@ +package com.cloudogu.gitops.integration.profiles + +import com.cloudogu.gitops.integration.features.KubenetesApiTestSetup +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledIfSystemProperty + +import static org.assertj.core.api.Assertions.assertThat + +/** + * This tests can only be successfull, if one of theses profiles used. + */ +@EnabledIfSystemProperty(named = "micronaut.environments", matches = "full|petclinic") +class PetclincProfileTest extends KubenetesApiTestSetup { + + String namespace1 = 'example-apps-staging' + String namespace2 = 'example-apps-production' + + @BeforeAll + static void labelTest() { + println "###### Testing Petclinic ######" + } + + @Test + void ensureNamespaceStagingExists() { + def namespaces = api.listNamespace().execute() + assertThat(namespaces).isNotNull() + assertThat(namespaces.getItems().isEmpty()).isFalse() + def namespace = namespaces.getItems().find { namespace1.equals(it.getMetadata().name) } + assertThat(namespace).isNotNull() + } + @Test + void ensureNamespaceProductionExists() { + def namespaces = api.listNamespace().execute() + assertThat(namespaces).isNotNull() + assertThat(namespaces.getItems().isEmpty()).isFalse() + def namespace = namespaces.getItems().find { namespace2.equals(it.getMetadata().name) } + assertThat(namespace).isNotNull() + } + + + @Override + boolean isReadyToStartTests() { + + return true + } +} \ No newline at end of file