From 927580cbe4c0dfb0ad1e7ff91cbc012547009151 Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 14 Jun 2022 14:58:34 +0200 Subject: [PATCH 01/10] #79 Make registryName public for development purpose --- src/com/cloudogu/ces/cesbuildlib/K3d.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy index 7d10c6e..fcaff8b 100644 --- a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy @@ -20,7 +20,7 @@ class K3d { private String backendCredentialsID private Sh sh private K3dRegistry registry - private String registryName + public String registryName /** * Create an object to set up, modify and tear down a local k3d cluster From 07a545c3d26615dd53054af0c33b7b7e6ed85e01 Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 14 Jun 2022 16:48:37 +0200 Subject: [PATCH 02/10] #79 Use http on localhost --- src/com/cloudogu/ces/cesbuildlib/K3d.groovy | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy index fcaff8b..4725c55 100644 --- a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy @@ -90,6 +90,12 @@ class K3d { * Initializes the cluster by creating a respective cluster in k3d. */ private void initializeCluster() { + script.writeFile file:'registry_config.yaml', text: """ +mirrors: + "localhost:5000": + endpoint: + - http://localhost:5000 +""" script.sh "k3d cluster create ${clusterName} " + // Allow services to bind to ports < 30000 " --k3s-server-arg=--kube-apiserver-arg=service-node-port-range=8010-32767 " + @@ -107,8 +113,11 @@ class K3d { " --image=${K8S_IMAGE} " + // Use our k3d registry " --registry-use ${registry.getImageRegistryInternalWithPort()} " + + // TODO Test + "--registry-config registry_config.yaml" + " >/dev/null" + script.echo "Adding k3d cluster to ~/.kube/config" script.sh "k3d kubeconfig merge ${clusterName} --kubeconfig-switch-context > /dev/null" } From 961f8ea7b1183430bef1d68152d11d577e60df6a Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 15 Jun 2022 10:04:50 +0200 Subject: [PATCH 03/10] #79 Test insecure registry configuration --- src/com/cloudogu/ces/cesbuildlib/K3d.groovy | 22 +++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy index 4725c55..6f026b4 100644 --- a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy @@ -90,11 +90,29 @@ class K3d { * Initializes the cluster by creating a respective cluster in k3d. */ private void initializeCluster() { + def dockerInspect = sh(script: "docker inspect k3d-${this.registryName}", returnStdout: true) + def registryIp + + Docker docker = new Docker(this) + + docker.image('mikefarah/yq:4.22.1') + .mountJenkinsUser() + .inside("--volume ${WORKSPACE}:/workdir -w /workdir") { + registryIp = sh(script: "echo '${dockerInspect}' | yq '.[].NetworkSettings.Networks.k3d-${this.registryName}.IPAddress'", returnStdout: true).trim() + } + + sh "echo testttttttttttttttttt lib" + sh "echo ${registryIp}" + script.writeFile file:'registry_config.yaml', text: """ mirrors: - "localhost:5000": + ${registryIp}:5000: endpoint: - - http://localhost:5000 + - http://${registryIp}:5000 +configs: + ${registryIp}: + tls: + insecure_skip_verify: true """ script.sh "k3d cluster create ${clusterName} " + // Allow services to bind to ports < 30000 From f1b6fc4255c198b3a4c588ebca6c85d54ad2af97 Mon Sep 17 00:00:00 2001 From: Niklas Date: Thu, 16 Jun 2022 14:43:18 +0200 Subject: [PATCH 04/10] #79 Implement setup of the ecosystem in k3d with a minimal setup.json and the ability to install a dogu to the cluster --- src/com/cloudogu/ces/cesbuildlib/K3d.groovy | 260 +++++++++++++++++--- 1 file changed, 232 insertions(+), 28 deletions(-) diff --git a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy index 6f026b4..6bc60a3 100644 --- a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy @@ -90,30 +90,6 @@ class K3d { * Initializes the cluster by creating a respective cluster in k3d. */ private void initializeCluster() { - def dockerInspect = sh(script: "docker inspect k3d-${this.registryName}", returnStdout: true) - def registryIp - - Docker docker = new Docker(this) - - docker.image('mikefarah/yq:4.22.1') - .mountJenkinsUser() - .inside("--volume ${WORKSPACE}:/workdir -w /workdir") { - registryIp = sh(script: "echo '${dockerInspect}' | yq '.[].NetworkSettings.Networks.k3d-${this.registryName}.IPAddress'", returnStdout: true).trim() - } - - sh "echo testttttttttttttttttt lib" - sh "echo ${registryIp}" - - script.writeFile file:'registry_config.yaml', text: """ -mirrors: - ${registryIp}:5000: - endpoint: - - http://${registryIp}:5000 -configs: - ${registryIp}: - tls: - insecure_skip_verify: true -""" script.sh "k3d cluster create ${clusterName} " + // Allow services to bind to ports < 30000 " --k3s-server-arg=--kube-apiserver-arg=service-node-port-range=8010-32767 " + @@ -131,11 +107,8 @@ configs: " --image=${K8S_IMAGE} " + // Use our k3d registry " --registry-use ${registry.getImageRegistryInternalWithPort()} " + - // TODO Test - "--registry-config registry_config.yaml" + " >/dev/null" - script.echo "Adding k3d cluster to ~/.kube/config" script.sh "k3d kubeconfig merge ${clusterName} --kubeconfig-switch-context > /dev/null" } @@ -169,7 +142,81 @@ configs: * @param command The kubectl command you want to execute, without the leading "kubectl" */ void kubectl(command) { - script.sh "sudo KUBECONFIG=${k3dDir}/.kube/config kubectl ${command}" + kubectl(command, false) + } + + String kubectl(command, returnStdout) { + return script.sh(script: "sudo KUBECONFIG=${k3dDir}/.kube/config kubectl ${command}", returnStdout: returnStdout) + } + + /** + * Installs the setup to the cluster. Creates an example setup.json with plantuml as dogu and executes the setup. + * After that the method will wait until the dogu-operator is ready. + * @param tag Tag of the setup e. g. "v0.6.0" + */ + void setup(String tag) { + // Config + script.echo "Installing setup..." + kubectl("apply -f https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup-config.yaml") + writeSetupJson() + kubectl('create configmap k8s-ces-setup-json --from-file=setup.json') + + String setup = script.sh(script: "curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml", returnStdout: true) + setup = setup.replace("{{ .Namespace }}", "default") + script.writeFile file: 'setup.yaml', text: setup + kubectl('apply -f setup.yaml') + + script.sh "Wait for dogu-operator to be ready..." + waitForDeploymentRollout("k8s-dogu-operator-controller-manager", 300, 5) + } + + /** + * Installs a given dogu. Before applying the dogu.yaml to the cluster the method creates a custom dogu + * descriptor with an .local ip. This is required for the crane library in the dogu operator. The .local forces it + * to use http. Afterwards the .local will be patched out from the dogu resources so that kubelet has no problem to + * pull the image- + * + * @param dogu Name of the dogu e. g. "nginx-ingress" + * @param image Name of the image e. g. "k3d-citest-d9753c5632bc:1234/k8s/nginx-ingress" + * @param doguYaml Name of the custom resources + */ + void installDogu(String dogu, String image, String doguYaml) { + Docker docker = new Docker(this) + String[] IpPort = getRegistryIpAndPort(docker) + String imageUrl = image.split(":")[0] + patchCoreDNS(IpPort[0], imageUrl) + + applyDevDoguDescriptor(docker, dogu, imageUrl, IpPort[1]) + kubectl("apply -f ${doguYaml}") + + // Remove .local from Images. + patchDoguExecPod(dogu, image) + patchDoguDeployment(dogu, image) + } + + private void applyDevDoguDescriptor(Docker docker, String dogu, String imageUrl, String port) { + String imageDev + String doguJsonDevFile = "${WORKSPACE}/target/dogu.json" + docker.image('mikefarah/yq:4.22.1') + .mountJenkinsUser() + .inside("--volume ${WORKSPACE}:/workdir -w /workdir") { + imageDev = script.sh(script: "yq -e '.Image' dogu.json | sed 's|registry\\.cloudogu\\.com\\(.\\+\\)|${imageUrl}.local:${port}\\1|g'", returnStdout: true) + script.sh "yq '.Image=\"${imageDev}\"' dogu.json > ${doguJsonDevFile}" + } + kubectl("create configmap ${dogu}-descriptor --from-file=${doguJsonDevFile}") + } + + private void patchDoguExecPod(String dogu, String image) { + String execPodName = getExecPodName(dogu, 30, 2) + if (execPodName == "") { + return + } + kubectl("patch pod '${execPodName}' -p '{\"spec\":{\"containers\":[{\"name\":\"${execPodName}\",\"image\":\"${image}\"}]}}'") + } + + private void patchDoguDeployment(String dogu, String image) { + waitForDeployment(dogu, 300, 5) + kubectl("patch deployment '${dogu}' -p '{\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\":\"${dogu}\",\"image\":\"${image}\"}]}}}}'") } /** @@ -220,4 +267,161 @@ configs: script.echo "Installing kubectl..." script.sh script: "sudo snap install kubectl --classic" } + + private String getExecPodName(String dogu, Integer timeout, Integer interval) { + String execPodName + for (int i = 0; i < timeout/interval; i++) { + sleep(time: interval, unit: "SECONDS") + try { + execPodName = script.sh(script: "kubectl get pod --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}' | grep '${dogu}-execpod'", returnStdout: true).trim() + if (execPodName.contains("${dogu}-execpod")) { + return execPodName + } + } catch (exception) { + // Ignore Error. + } + } + + // Don't throw an error here because in the future the won't be exec pods for every dogu + return "" + } + + void waitForDeployment(String deployment, Integer timeout, Integer interval) { + for (int i = 0; i < timeout/interval; i++) { + sleep(time: interval, unit: "SECONDS") + try { + String statusMsg = script.sh(script: "kubectl get deployment --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}' | grep '${deployment}'", returnStdout: true) + if (statusMsg == deployment) { + return + } + } catch (ignored) { + // Ignore Error. + } + } + + throw new Exception("failed to wait for deployment/${deployment}: timeout") + } + + void waitForDeploymentRollout(String deployment, Integer timeout, Integer interval) { + for (int i = 0; i < timeout/interval; i++) { + sleep(time: interval, unit: "SECONDS") + try { + String statusMsg = kubectl("rollout status deployment/${deployment}", true) + if (statusMsg.contains("successfully rolled out")) { + return + } + } catch (ignored) { + // If the deployment does not even exists an error will be thrown. Ignore this case. + } + } + + throw new Exception("failed to wait for deployment/${deployment} rollout: timeout") + } + + private void patchCoreDNS(String ip, String imageUrl) { + String fileName = "coreDNSPatch.yaml" + script.writeFile file: fileName, text: """ +data: + Corefile: | + ${imageUrl}.local:53 { + hosts { + ${ip} ${imageUrl}.local + } + } + .:53 { + errors + health + ready + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + hosts /etc/coredns/NodeHosts { + reload 1s + fallthrough + } + prometheus :9153 + forward . /etc/resolv.conf + cache 30 + loop + reload + loadbalance + } +""" + + kubectl("-n kube-system patch cm coredns --patch-file ${fileName}") + kubectl("rollout restart -n kube-system deployment/coredns") + } + + private String[] getRegistryIpAndPort(Docker docker) { + String registryIp + String registryPortProtocol + String prefixedRegistryName = "k3d-${this.registryName}" + String dockerInspect = script.sh(script: "docker inspect ${prefixedRegistryName}", returnStdout: true) + docker.image('mikefarah/yq:4.22.1') + .mountJenkinsUser().inside("--volume ${WORKSPACE}:/workdir -w /workdir") { + registryIp = script.sh(script: "echo '${dockerInspect}' | yq '.[].NetworkSettings.Networks.${prefixedRegistryName}.IPAddress'", returnStdout: true).trim() + registryPortProtocol = script.sh(script: "echo '${dockerInspect}' | yq '.[].Config.Labels.\"k3s.registry.port.internal\"'", returnStdout: true).trim() + } + String registryPort = registryPortProtocol.split("/")[0] + + return [registryIp, registryPort] + } + + private void writeSetupJson() { + script.writeFile file: 'setup.json', text: """ +{ + "naming": { + "fqdn": "192.168.56.2", + "domain": "k3ces.local", + "certificateType": "selfsigned", + "relayHost": "asdf", + "completed": true, + "useInternalIp": false, + "internalIp": "" + }, + "dogus": { + "defaultDogu": "plantuml", + "install": [ + "official/plantuml" + ], + "completed": true + }, + "admin": { + "username": "admin", + "mail": "admin@admin.admin", + "password": "adminpw", + "adminGroup": "cesAdmin", + "completed": true, + "adminMember": true, + "sendWelcomeMail": false + }, + "userBackend": { + "dsType": "embedded", + "server": "", + "attributeID": "uid", + "attributeGivenName": "", + "attributeSurname": "", + "attributeFullname": "cn", + "attributeMail": "mail", + "attributeGroup": "memberOf", + "baseDN": "", + "searchFilter": "(objectClass=person)", + "connectionDN": "", + "password": "", + "host": "ldap", + "port": "389", + "loginID": "", + "loginPassword": "", + "encryption": "", + "completed": true, + "groupBaseDN": "", + "groupSearchFilter": "", + "groupAttributeName": "", + "groupAttributeDescription": "", + "groupAttributeMember": "" + } +} +""" + } } From 638292d0fd93ad6532b72195f9aa3733098e12a3 Mon Sep 17 00:00:00 2001 From: Niklas Date: Thu, 16 Jun 2022 17:00:48 +0200 Subject: [PATCH 05/10] #79 Bug fixes --- src/com/cloudogu/ces/cesbuildlib/K3d.groovy | 48 +++++++++++-------- .../cloudogu/ces/cesbuildlib/K3dTest.groovy | 41 ++++++++++++++-- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy index 6bc60a3..115a6fc 100644 --- a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy @@ -20,21 +20,24 @@ class K3d { private String backendCredentialsID private Sh sh private K3dRegistry registry - public String registryName + private String registryName + private String workspace /** * Create an object to set up, modify and tear down a local k3d cluster * * @param script The Jenkins script you are coming from (aka "this") * @param envWorkspace The designated directory for the K3d installation + * @param workspace The designated directory for working dir * @param envPath The PATH environment variable; in Jenkins use "env.PATH" for example * @param backendCredentialsID Identifier of credentials used to log into the backend. Default: cesmarvin-setup */ - K3d(script, String envWorkspace, String envPath, String backendCredentialsID="cesmarvin-setup") { + K3d(script, String workspace, String envWorkspace, String envPath, String backendCredentialsID="cesmarvin-setup") { this.clusterName = createClusterName() this.registryName = clusterName this.script = script this.path = envPath + this.workspace = workspace this.k3dDir = "${envWorkspace}/.k3d" this.k3dBinaryDir = "${k3dDir}/bin" this.backendCredentialsID = backendCredentialsID @@ -153,21 +156,23 @@ class K3d { * Installs the setup to the cluster. Creates an example setup.json with plantuml as dogu and executes the setup. * After that the method will wait until the dogu-operator is ready. * @param tag Tag of the setup e. g. "v0.6.0" + * @param timout Timeout in seconds for the setup process e. g. 300 + * @param interval Interval in seconds for querying the actual state of the setup e. g. 2 */ - void setup(String tag) { + void setup(String tag, Integer timout, Integer interval) { // Config script.echo "Installing setup..." kubectl("apply -f https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup-config.yaml") writeSetupJson() kubectl('create configmap k8s-ces-setup-json --from-file=setup.json') - String setup = script.sh(script: "curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml", returnStdout: true) + String setup = this.sh.returnStdOut("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml") setup = setup.replace("{{ .Namespace }}", "default") script.writeFile file: 'setup.yaml', text: setup kubectl('apply -f setup.yaml') - script.sh "Wait for dogu-operator to be ready..." - waitForDeploymentRollout("k8s-dogu-operator-controller-manager", 300, 5) + script.echo "Wait for dogu-operator to be ready..." + waitForDeploymentRollout("k8s-dogu-operator-controller-manager", timout, interval) } /** @@ -181,7 +186,7 @@ class K3d { * @param doguYaml Name of the custom resources */ void installDogu(String dogu, String image, String doguYaml) { - Docker docker = new Docker(this) + Docker docker = new Docker(script) String[] IpPort = getRegistryIpAndPort(docker) String imageUrl = image.split(":")[0] patchCoreDNS(IpPort[0], imageUrl) @@ -196,11 +201,11 @@ class K3d { private void applyDevDoguDescriptor(Docker docker, String dogu, String imageUrl, String port) { String imageDev - String doguJsonDevFile = "${WORKSPACE}/target/dogu.json" + String doguJsonDevFile = "${this.workspace}/target/dogu.json" docker.image('mikefarah/yq:4.22.1') .mountJenkinsUser() - .inside("--volume ${WORKSPACE}:/workdir -w /workdir") { - imageDev = script.sh(script: "yq -e '.Image' dogu.json | sed 's|registry\\.cloudogu\\.com\\(.\\+\\)|${imageUrl}.local:${port}\\1|g'", returnStdout: true) + .inside("--volume ${this.workspace}:/workdir -w /workdir") { + imageDev = this.sh.returnStdOut("yq -e '.Image' dogu.json | sed 's|registry\\.cloudogu\\.com\\(.\\+\\)|${imageUrl}.local:${port}\\1|g'") script.sh "yq '.Image=\"${imageDev}\"' dogu.json > ${doguJsonDevFile}" } kubectl("create configmap ${dogu}-descriptor --from-file=${doguJsonDevFile}") @@ -269,15 +274,15 @@ class K3d { } private String getExecPodName(String dogu, Integer timeout, Integer interval) { - String execPodName for (int i = 0; i < timeout/interval; i++) { - sleep(time: interval, unit: "SECONDS") + script.sh("sleep ${interval}s") try { - execPodName = script.sh(script: "kubectl get pod --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}' | grep '${dogu}-execpod'", returnStdout: true).trim() + String podList = kubectl("get pod --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'", true) + String execPodName = this.sh.returnStdOut("echo '${podList}' | grep '${dogu}-execpod'") if (execPodName.contains("${dogu}-execpod")) { return execPodName } - } catch (exception) { + } catch (ignored) { // Ignore Error. } } @@ -288,9 +293,10 @@ class K3d { void waitForDeployment(String deployment, Integer timeout, Integer interval) { for (int i = 0; i < timeout/interval; i++) { - sleep(time: interval, unit: "SECONDS") + script.sh("sleep ${interval}s") try { - String statusMsg = script.sh(script: "kubectl get deployment --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}' | grep '${deployment}'", returnStdout: true) + String deploymentList = kubectl("get deployment --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'", true) + String statusMsg = this.sh.returnStdOut("echo '${deploymentList}' | grep '${deployment}'") if (statusMsg == deployment) { return } @@ -304,7 +310,7 @@ class K3d { void waitForDeploymentRollout(String deployment, Integer timeout, Integer interval) { for (int i = 0; i < timeout/interval; i++) { - sleep(time: interval, unit: "SECONDS") + script.sh("sleep ${interval}s") try { String statusMsg = kubectl("rollout status deployment/${deployment}", true) if (statusMsg.contains("successfully rolled out")) { @@ -359,10 +365,10 @@ data: String prefixedRegistryName = "k3d-${this.registryName}" String dockerInspect = script.sh(script: "docker inspect ${prefixedRegistryName}", returnStdout: true) docker.image('mikefarah/yq:4.22.1') - .mountJenkinsUser().inside("--volume ${WORKSPACE}:/workdir -w /workdir") { - registryIp = script.sh(script: "echo '${dockerInspect}' | yq '.[].NetworkSettings.Networks.${prefixedRegistryName}.IPAddress'", returnStdout: true).trim() - registryPortProtocol = script.sh(script: "echo '${dockerInspect}' | yq '.[].Config.Labels.\"k3s.registry.port.internal\"'", returnStdout: true).trim() - } + .mountJenkinsUser().inside("--volume ${this.workspace}:/workdir -w /workdir") { + registryIp = script.sh(script: "echo '${dockerInspect}' | yq '.[].NetworkSettings.Networks.${prefixedRegistryName}.IPAddress'", returnStdout: true).trim() + registryPortProtocol = script.sh(script: "echo '${dockerInspect}' | yq '.[].Config.Labels.\"k3s.registry.port.internal\"'", returnStdout: true).trim() + } String registryPort = registryPortProtocol.split("/")[0] return [registryIp, registryPort] diff --git a/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy b/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy index 6151267..f1ea909 100644 --- a/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy +++ b/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy @@ -11,7 +11,7 @@ import static org.mockito.Mockito.when class K3dTest extends GroovyTestCase { void testCreateClusterName() { - K3d sut = new K3d("script", "workspace", "path") + K3d sut = new K3d("script", "leWorkSpace", "leK3dWorkSpace", "path") String testClusterName = sut.createClusterName() assertTrue(testClusterName.contains("citest-")) assertTrue(testClusterName != "citest-") @@ -26,7 +26,7 @@ class K3dTest extends GroovyTestCase { scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsocconfig kubectl get nodeskname()[1]); s.close()\');'.toString(), "54321") scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") - K3d sut = new K3d(scriptMock, "workspace", "path") + K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") // we need to create a registry so that the deletion of the registry is triggered sut.startK3d() @@ -41,12 +41,15 @@ class K3dTest extends GroovyTestCase { } void testKubectl() { + // given String workspaceDir = "leWorkspace" def scriptMock = new ScriptMock() - K3d sut = new K3d(scriptMock, workspaceDir, "path") + K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") + // when sut.kubectl("get nodes") + // then assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl get nodes".trim()) assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(1) } @@ -58,7 +61,7 @@ class K3dTest extends GroovyTestCase { def scriptMock = new ScriptMock() scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") - K3d sut = new K3d(scriptMock, "${workspaceDir}", "path") + K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") sut.startK3d() @@ -126,7 +129,7 @@ class K3dTest extends GroovyTestCase { }) scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") - K3d sut = new K3d(scriptMock, "leWorkspace", "path") + K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") sut.startK3d() // when @@ -136,4 +139,32 @@ class K3dTest extends GroovyTestCase { assertThat(scriptMock.actualShStringArgs[11].trim()).isEqualTo("image pushed".toString()) assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(12) } + + void testSetup() { + // given + def workspaceEnvDir = "leWorkspace/k3d" + String tag = "v0.6.0" + def scriptMock = new ScriptMock() + scriptMock.expectedShRetValueForScript.put("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".toString(), "fake setup yaml with {{ .Namespace }}") + scriptMock.expectedShRetValueForScript.put("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".toString(), "successfully rolled out") + + K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") + + // when + sut.setup(tag, 1, 1) + + // then + assertThat(scriptMock.actualEcho.get(0)).isEqualTo("Installing setup...") + assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup-config.yaml".trim()) + assertThat(scriptMock.writeFileParams.get(0)["setup.json"]).isNotEmpty() + assertThat(scriptMock.actualShStringArgs[1].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl create configmap k8s-ces-setup-json --from-file=setup.json".trim()) + assertThat(scriptMock.actualShStringArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".trim()) + String setupYaml = scriptMock.writeFileParams.get(1)["setup.yaml"] + assertThat(setupYaml).isNotEmpty() + assertThat(setupYaml.contains("{{ .Namespace }}")).isFalse() + assertThat(scriptMock.actualShStringArgs[3].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f setup.yaml".trim()) + assertThat(scriptMock.actualEcho.get(0)).isEqualTo("Wait for dogu-operator to be ready...") + assertThat(scriptMock.actualShStringArgs[4].trim()).isEqualTo("sleep 1s") + assertThat(scriptMock.actualShStringArgs[5].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".trim()) + } } From 5aadae62c09732362f7cb17ea0917bac1174a6a2 Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Mon, 20 Jun 2022 10:01:16 +0200 Subject: [PATCH 06/10] #79 Provide configuration when performing the ecosystem setup --- src/com/cloudogu/ces/cesbuildlib/K3d.groovy | 139 ++++++++++++-------- 1 file changed, 83 insertions(+), 56 deletions(-) diff --git a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy index 115a6fc..14932e5 100644 --- a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy @@ -18,11 +18,27 @@ class K3d { private String k3dDir private String k3dBinaryDir private String backendCredentialsID + private String externalIP private Sh sh private K3dRegistry registry private String registryName private String workspace + def defaultSetupConfig = [ + adminUsername : "ces-admin", + adminPassword : "ecosystem2016", + adminGroup : "CesAdministrators", + dependencies : ["official/ldap", + "official/cas", + "k8s/nginx-ingress", + "official/postfix", + "official/usermgt"], + defaultDogu : "cockpit", + additionalDependencies : [], + registryConfig : "", + registryConfigEncrypted: "" + ] + /** * Create an object to set up, modify and tear down a local k3d cluster * @@ -32,7 +48,7 @@ class K3d { * @param envPath The PATH environment variable; in Jenkins use "env.PATH" for example * @param backendCredentialsID Identifier of credentials used to log into the backend. Default: cesmarvin-setup */ - K3d(script, String workspace, String envWorkspace, String envPath, String backendCredentialsID="cesmarvin-setup") { + K3d(script, String workspace, String envWorkspace, String envPath, String backendCredentialsID = "cesmarvin-setup") { this.clusterName = createClusterName() this.registryName = clusterName this.script = script @@ -159,11 +175,18 @@ class K3d { * @param timout Timeout in seconds for the setup process e. g. 300 * @param interval Interval in seconds for querying the actual state of the setup e. g. 2 */ - void setup(String tag, Integer timout, Integer interval) { + void setup(String tag, config = [:], Integer timout = 300, Integer interval = 5) { + this.externalIP = this.sh.returnStdOut("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip") + script.echo "Get external ip..." + externalIP + // Config script.echo "Installing setup..." kubectl("apply -f https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup-config.yaml") - writeSetupJson() + + // Merge default config with the one passed as parameter + config = defaultSetupConfig << config + writeSetupJson(config) + kubectl('create configmap k8s-ces-setup-json --from-file=setup.json') String setup = this.sh.returnStdOut("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml") @@ -205,7 +228,7 @@ class K3d { docker.image('mikefarah/yq:4.22.1') .mountJenkinsUser() .inside("--volume ${this.workspace}:/workdir -w /workdir") { - imageDev = this.sh.returnStdOut("yq -e '.Image' dogu.json | sed 's|registry\\.cloudogu\\.com\\(.\\+\\)|${imageUrl}.local:${port}\\1|g'") + imageDev = this.sh.returnStdOut("yq -e '.Image' dogu.json | sed 's|registry\\.cloudogu\\.com\\(.\\+\\)|${imageUrl}.local:${port}\\1|g'") script.sh "yq '.Image=\"${imageDev}\"' dogu.json > ${doguJsonDevFile}" } kubectl("create configmap ${dogu}-descriptor --from-file=${doguJsonDevFile}") @@ -274,7 +297,7 @@ class K3d { } private String getExecPodName(String dogu, Integer timeout, Integer interval) { - for (int i = 0; i < timeout/interval; i++) { + for (int i = 0; i < timeout / interval; i++) { script.sh("sleep ${interval}s") try { String podList = kubectl("get pod --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'", true) @@ -292,7 +315,7 @@ class K3d { } void waitForDeployment(String deployment, Integer timeout, Integer interval) { - for (int i = 0; i < timeout/interval; i++) { + for (int i = 0; i < timeout / interval; i++) { script.sh("sleep ${interval}s") try { String deploymentList = kubectl("get deployment --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'", true) @@ -309,7 +332,7 @@ class K3d { } void waitForDeploymentRollout(String deployment, Integer timeout, Integer interval) { - for (int i = 0; i < timeout/interval; i++) { + for (int i = 0; i < timeout / interval; i++) { script.sh("sleep ${interval}s") try { String statusMsg = kubectl("rollout status deployment/${deployment}", true) @@ -374,60 +397,64 @@ data: return [registryIp, registryPort] } - private void writeSetupJson() { + static String formatDependencies(List deps) { + String formatted = "" + + for (int i = 0; i < deps.size(); i++) { + formatted += "\"${deps[i]}\"" + + if ((i + 1) < deps.size()) { + formatted += ', ' + } + } + + return formatted + } + + private void writeSetupJson(config) { + List deps = config.dependencies + config.additionalDependencies + String formattedDeps = formatDependencies(deps) + script.writeFile file: 'setup.json', text: """ { - "naming": { - "fqdn": "192.168.56.2", - "domain": "k3ces.local", - "certificateType": "selfsigned", - "relayHost": "asdf", - "completed": true, - "useInternalIp": false, - "internalIp": "" + "naming":{ + "fqdn":"${externalIP}", + "hostname":"ces", + "domain":"ces.local", + "certificateType":"selfsigned", + "relayHost":"mail.ces.local", + "completed":true }, - "dogus": { - "defaultDogu": "plantuml", - "install": [ - "official/plantuml" + "dogus":{ + "defaultDogu":"${config.defaultDogu}", + "install":[ + ${formattedDeps} ], - "completed": true + "completed":true }, - "admin": { - "username": "admin", - "mail": "admin@admin.admin", - "password": "adminpw", - "adminGroup": "cesAdmin", - "completed": true, - "adminMember": true, - "sendWelcomeMail": false + "admin":{ + "username":"${config.adminUsername}", + "mail":"ces-admin@cloudogu.com", + "password":"${config.adminPassword}", + "adminGroup":"${config.adminGroup}", + "adminMember":true, + "completed":true }, - "userBackend": { - "dsType": "embedded", - "server": "", - "attributeID": "uid", - "attributeGivenName": "", - "attributeSurname": "", - "attributeFullname": "cn", - "attributeMail": "mail", - "attributeGroup": "memberOf", - "baseDN": "", - "searchFilter": "(objectClass=person)", - "connectionDN": "", - "password": "", - "host": "ldap", - "port": "389", - "loginID": "", - "loginPassword": "", - "encryption": "", - "completed": true, - "groupBaseDN": "", - "groupSearchFilter": "", - "groupAttributeName": "", - "groupAttributeDescription": "", - "groupAttributeMember": "" - } -} -""" + "userBackend":{ + "port":"389", + "useUserConnectionToFetchAttributes":true, + "dsType":"embedded", + "attributeID":"uid", + "attributeFullname":"cn", + "attributeMail":"mail", + "attributeGroup":"memberOf", + "searchFilter":"(objectClass=person)", + "host":"ldap", + "completed":true + }, + "registryConfig": {${config.registryConfig}}, + "registryConfigEncrypted": {${config.registryConfigEncrypted}} +}""" } } + From 6cf352e37248432d21d9e1203965f52ccb76196d Mon Sep 17 00:00:00 2001 From: Niklas Date: Mon, 20 Jun 2022 11:23:35 +0200 Subject: [PATCH 07/10] #79 Use script error. Add test case for setup. --- src/com/cloudogu/ces/cesbuildlib/K3d.groovy | 5 +- .../cloudogu/ces/cesbuildlib/K3dTest.groovy | 328 ++++++++++-------- 2 files changed, 186 insertions(+), 147 deletions(-) diff --git a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy index 14932e5..f115f94 100644 --- a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy @@ -177,7 +177,6 @@ class K3d { */ void setup(String tag, config = [:], Integer timout = 300, Integer interval = 5) { this.externalIP = this.sh.returnStdOut("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip") - script.echo "Get external ip..." + externalIP // Config script.echo "Installing setup..." @@ -328,7 +327,7 @@ class K3d { } } - throw new Exception("failed to wait for deployment/${deployment}: timeout") + this.script.error "failed to wait for deployment/${deployment}: timeout" } void waitForDeploymentRollout(String deployment, Integer timeout, Integer interval) { @@ -344,7 +343,7 @@ class K3d { } } - throw new Exception("failed to wait for deployment/${deployment} rollout: timeout") + this.script.error "failed to wait for deployment/${deployment} rollout: timeout" } private void patchCoreDNS(String ip, String imageUrl) { diff --git a/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy b/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy index f1ea909..9e94b63 100644 --- a/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy +++ b/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy @@ -1,5 +1,9 @@ package com.cloudogu.ces.cesbuildlib +import org.junit.Rule +import org.junit.Test +import org.junit.internal.runners.statements.ExpectException +import org.junit.rules.ExpectedException import org.mockito.Mockito import org.mockito.invocation.InvocationOnMock import org.mockito.stubbing.Answer @@ -10,161 +14,197 @@ import static org.mockito.Mockito.mock import static org.mockito.Mockito.when class K3dTest extends GroovyTestCase { - void testCreateClusterName() { - K3d sut = new K3d("script", "leWorkSpace", "leK3dWorkSpace", "path") - String testClusterName = sut.createClusterName() - assertTrue(testClusterName.contains("citest-")) - assertTrue(testClusterName != "citest-") - assertTrue(testClusterName.length() <= 32) - String testClusterName2 = sut.createClusterName() - assertTrue(testClusterName != testClusterName2) - } - - void testDeleteK3d() { - // given - def scriptMock = new ScriptMock() - scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsocconfig kubectl get nodeskname()[1]); s.close()\');'.toString(), "54321") - scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") - - K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") - - // we need to create a registry so that the deletion of the registry is triggered - sut.startK3d() - - // when - sut.deleteK3d() - - // then - assertThat(scriptMock.actualShStringArgs[11].trim()).contains("k3d registry delete citest-") - assertThat(scriptMock.actualShStringArgs[12].trim()).contains("k3d cluster delete citest-") - assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(13) - } - - void testKubectl() { +// void testCreateClusterName() { +// K3d sut = new K3d("script", "leWorkSpace", "leK3dWorkSpace", "path") +// String testClusterName = sut.createClusterName() +// assertTrue(testClusterName.contains("citest-")) +// assertTrue(testClusterName != "citest-") +// assertTrue(testClusterName.length() <= 32) +// String testClusterName2 = sut.createClusterName() +// assertTrue(testClusterName != testClusterName2) +// } +// +// void testDeleteK3d() { +// // given +// def scriptMock = new ScriptMock() +// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsocconfig kubectl get nodeskname()[1]); s.close()\');'.toString(), "54321") +// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") +// +// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") +// +// // we need to create a registry so that the deletion of the registry is triggered +// sut.startK3d() +// +// // when +// sut.deleteK3d() +// +// // then +// assertThat(scriptMock.actualShStringArgs[11].trim()).contains("k3d registry delete citest-") +// assertThat(scriptMock.actualShStringArgs[12].trim()).contains("k3d cluster delete citest-") +// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(13) +// } +// +// void testKubectl() { +// // given +// String workspaceDir = "leWorkspace" +// def scriptMock = new ScriptMock() +// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") +// +// // when +// sut.kubectl("get nodes") +// +// // then +// assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl get nodes".trim()) +// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(1) +// } +// +// void testStartK3d() { +// def workspaceDir = "leWorkspace" +// def k3dVer = "4.4.7" +// +// def scriptMock = new ScriptMock() +// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") +// +// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") +// +// sut.startK3d() +// +// assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("rm -rf ${workspaceDir}/.k3d".toString()) +// assertThat(scriptMock.actualShStringArgs[1].trim()).isEqualTo("mkdir -p ${workspaceDir}/.k3d/bin".toString()) +// assertThat(scriptMock.actualShStringArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v${k3dVer} K3D_INSTALL_DIR=${workspaceDir}/.k3d/bin bash -s -- --no-sudo".toString()) +// assertThat(scriptMock.actualShStringArgs[3].trim()).matches("k3d registry create citest-[0-9a-f]+ --port 54321") +// assertThat(scriptMock.actualShStringArgs[4].trim()).startsWith("k3d cluster create citest-") +// assertThat(scriptMock.actualShStringArgs[5].trim()).startsWith("k3d kubeconfig merge citest-") +// assertThat(scriptMock.actualShStringArgs[6].trim()).startsWith("echo \"Using credentials: cesmarvin-setup\"") +// assertThat(scriptMock.actualShStringArgs[7].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-dogu-registry || true") +// assertThat(scriptMock.actualShStringArgs[8].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-docker-registry || true") +// assertThat(scriptMock.actualShStringArgs[9].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret generic k8s-dogu-operator-dogu-registry --from-literal=endpoint=\"https://dogu.cloudogu.com/api/v2/dogus\" --from-literal=username=\"null\" --from-literal=password=\"null\"") +// assertThat(scriptMock.actualShStringArgs[10].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret docker-registry k8s-dogu-operator-docker-registry --docker-server=\"registry.cloudogu.com\" --docker-username=\"null\" --docker-email=\"a@b.c\" --docker-password=\"null\"") +// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(11) +// } +// +// void testStartK3dWithCustomCredentials() { +// def workspaceDir = "leWorkspace" +// def k3dVer = "4.4.7" +// +// def scriptMock = new ScriptMock() +// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") +// +// K3d sut = new K3d(scriptMock, "${workspaceDir}", "path", "myBackendCredentialsID") +// +// sut.startK3d() +// +// assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("rm -rf ${workspaceDir}/.k3d".toString()) +// assertThat(scriptMock.actualShStringArgs[1].trim()).isEqualTo("mkdir -p ${workspaceDir}/.k3d/bin".toString()) +// assertThat(scriptMock.actualShStringArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v${k3dVer} K3D_INSTALL_DIR=${workspaceDir}/.k3d/bin bash -s -- --no-sudo".toString()) +// assertThat(scriptMock.actualShStringArgs[3].trim()).matches("k3d registry create citest-[0-9a-f]+ --port 54321") +// assertThat(scriptMock.actualShStringArgs[4].trim()).startsWith("k3d cluster create citest-") +// assertThat(scriptMock.actualShStringArgs[5].trim()).startsWith("k3d kubeconfig merge citest-") +// assertThat(scriptMock.actualShStringArgs[6].trim()).startsWith("echo \"Using credentials: myBackendCredentialsID\"") +// assertThat(scriptMock.actualShStringArgs[7].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-dogu-registry || true") +// assertThat(scriptMock.actualShStringArgs[8].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-docker-registry || true") +// assertThat(scriptMock.actualShStringArgs[9].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret generic k8s-dogu-operator-dogu-registry --from-literal=endpoint=\"https://dogu.cloudogu.com/api/v2/dogus\" --from-literal=username=\"null\" --from-literal=password=\"null\"") +// assertThat(scriptMock.actualShStringArgs[10].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret docker-registry k8s-dogu-operator-docker-registry --docker-server=\"registry.cloudogu.com\" --docker-username=\"null\" --docker-email=\"a@b.c\" --docker-password=\"null\"") +// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(11) +// } +// +// void testBuildAndPush() { +// // given +// String imageName = "superimage" +// String imageTag = "1.2.1" +// +// Docker dockerMock = mock(Docker.class) +// Docker.Image imageMock = mock(Docker.Image.class) +// def scriptMock = new ScriptMock(dockerMock) +// +// when(dockerMock.build(anyString())).thenReturn(imageMock) +// when(dockerMock.withRegistry(anyString(), Mockito.any(Closure.class))).then(new Answer() { +// @Override +// Object answer(InvocationOnMock invocation) throws Throwable { +// Closure doThings = (Closure)invocation.getArgument(1) +// doThings.call() +// } +// }) +// when(imageMock.push(imageTag)).then(new Answer() { +// @Override +// Object answer(InvocationOnMock invocation) throws Throwable { +// scriptMock.sh("image pushed") +// } +// }) +// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") +// +// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") +// sut.startK3d() +// +// // when +// sut.buildAndPushToLocalRegistry(imageName, imageTag) +// +// // then +// assertThat(scriptMock.actualShStringArgs[11].trim()).isEqualTo("image pushed".toString()) +// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(12) +// } + +// void testSetup() { +// // given +// def workspaceEnvDir = "leK3dWorkSpace" +// String tag = "v0.6.0" +// def scriptMock = new ScriptMock() +// scriptMock.expectedShRetValueForScript.put("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".toString(), "fake setup yaml with {{ .Namespace }}") +// scriptMock.expectedShRetValueForScript.put("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".toString(), "successfully rolled out") +// scriptMock.expectedShRetValueForScript.put("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip", "192.168.56.2") +// +// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") +// +// // when +// sut.setup(tag, [:], 1, 1) +// +// // then +// assertThat(scriptMock.actualEcho.get(0)).isEqualTo("Installing setup...") +// assertThat(scriptMock.actualShMapArgs[0].trim()).isEqualTo("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip") +// assertThat(scriptMock.actualShMapArgs[1].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup-config.yaml".trim()) +// assertThat(scriptMock.writeFileParams.get(0)).isNotNull() +// assertThat(scriptMock.actualShMapArgs[2].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl create configmap k8s-ces-setup-json --from-file=setup.json".trim()) +// assertThat(scriptMock.actualShMapArgs[3].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".trim()) +// String setupYaml = scriptMock.writeFileParams.get(1) +// assertThat(setupYaml).isNotNull() +// assertThat(setupYaml.contains("{{ .Namespace }}")).isFalse() +// assertThat(scriptMock.actualShMapArgs[4].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f setup.yaml".trim()) +// assertThat(scriptMock.actualEcho.get(1)).isEqualTo("Wait for dogu-operator to be ready...") +// assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sleep 1s") +// assertThat(scriptMock.actualShMapArgs[5].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".trim()) +// } + + + void testSetupShouldThrowExceptionOnDoguOperatorRollout() { // given - String workspaceDir = "leWorkspace" - def scriptMock = new ScriptMock() - K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") - - // when - sut.kubectl("get nodes") - - // then - assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl get nodes".trim()) - assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(1) - } - - void testStartK3d() { - def workspaceDir = "leWorkspace" - def k3dVer = "4.4.7" - - def scriptMock = new ScriptMock() - scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") - - K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") - - sut.startK3d() - - assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("rm -rf ${workspaceDir}/.k3d".toString()) - assertThat(scriptMock.actualShStringArgs[1].trim()).isEqualTo("mkdir -p ${workspaceDir}/.k3d/bin".toString()) - assertThat(scriptMock.actualShStringArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v${k3dVer} K3D_INSTALL_DIR=${workspaceDir}/.k3d/bin bash -s -- --no-sudo".toString()) - assertThat(scriptMock.actualShStringArgs[3].trim()).matches("k3d registry create citest-[0-9a-f]+ --port 54321") - assertThat(scriptMock.actualShStringArgs[4].trim()).startsWith("k3d cluster create citest-") - assertThat(scriptMock.actualShStringArgs[5].trim()).startsWith("k3d kubeconfig merge citest-") - assertThat(scriptMock.actualShStringArgs[6].trim()).startsWith("echo \"Using credentials: cesmarvin-setup\"") - assertThat(scriptMock.actualShStringArgs[7].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-dogu-registry || true") - assertThat(scriptMock.actualShStringArgs[8].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-docker-registry || true") - assertThat(scriptMock.actualShStringArgs[9].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret generic k8s-dogu-operator-dogu-registry --from-literal=endpoint=\"https://dogu.cloudogu.com/api/v2/dogus\" --from-literal=username=\"null\" --from-literal=password=\"null\"") - assertThat(scriptMock.actualShStringArgs[10].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret docker-registry k8s-dogu-operator-docker-registry --docker-server=\"registry.cloudogu.com\" --docker-username=\"null\" --docker-email=\"a@b.c\" --docker-password=\"null\"") - assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(11) - } - - void testStartK3dWithCustomCredentials() { - def workspaceDir = "leWorkspace" - def k3dVer = "4.4.7" - - def scriptMock = new ScriptMock() - scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") - - K3d sut = new K3d(scriptMock, "${workspaceDir}", "path", "myBackendCredentialsID") - - sut.startK3d() - - assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("rm -rf ${workspaceDir}/.k3d".toString()) - assertThat(scriptMock.actualShStringArgs[1].trim()).isEqualTo("mkdir -p ${workspaceDir}/.k3d/bin".toString()) - assertThat(scriptMock.actualShStringArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v${k3dVer} K3D_INSTALL_DIR=${workspaceDir}/.k3d/bin bash -s -- --no-sudo".toString()) - assertThat(scriptMock.actualShStringArgs[3].trim()).matches("k3d registry create citest-[0-9a-f]+ --port 54321") - assertThat(scriptMock.actualShStringArgs[4].trim()).startsWith("k3d cluster create citest-") - assertThat(scriptMock.actualShStringArgs[5].trim()).startsWith("k3d kubeconfig merge citest-") - assertThat(scriptMock.actualShStringArgs[6].trim()).startsWith("echo \"Using credentials: myBackendCredentialsID\"") - assertThat(scriptMock.actualShStringArgs[7].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-dogu-registry || true") - assertThat(scriptMock.actualShStringArgs[8].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-docker-registry || true") - assertThat(scriptMock.actualShStringArgs[9].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret generic k8s-dogu-operator-dogu-registry --from-literal=endpoint=\"https://dogu.cloudogu.com/api/v2/dogus\" --from-literal=username=\"null\" --from-literal=password=\"null\"") - assertThat(scriptMock.actualShStringArgs[10].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret docker-registry k8s-dogu-operator-docker-registry --docker-server=\"registry.cloudogu.com\" --docker-username=\"null\" --docker-email=\"a@b.c\" --docker-password=\"null\"") - assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(11) - } - - void testBuildAndPush() { - // given - String imageName = "superimage" - String imageTag = "1.2.1" - - Docker dockerMock = mock(Docker.class) - Docker.Image imageMock = mock(Docker.Image.class) - def scriptMock = new ScriptMock(dockerMock) - - when(dockerMock.build(anyString())).thenReturn(imageMock) - when(dockerMock.withRegistry(anyString(), Mockito.any(Closure.class))).then(new Answer() { - @Override - Object answer(InvocationOnMock invocation) throws Throwable { - Closure doThings = (Closure)invocation.getArgument(1) - doThings.call() - } - }) - when(imageMock.push(imageTag)).then(new Answer() { - @Override - Object answer(InvocationOnMock invocation) throws Throwable { - scriptMock.sh("image pushed") - } - }) - scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") - - K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") - sut.startK3d() - - // when - sut.buildAndPushToLocalRegistry(imageName, imageTag) - - // then - assertThat(scriptMock.actualShStringArgs[11].trim()).isEqualTo("image pushed".toString()) - assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(12) - } - - void testSetup() { - // given - def workspaceEnvDir = "leWorkspace/k3d" + def workspaceEnvDir = "leK3dWorkSpace" String tag = "v0.6.0" def scriptMock = new ScriptMock() scriptMock.expectedShRetValueForScript.put("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".toString(), "fake setup yaml with {{ .Namespace }}") - scriptMock.expectedShRetValueForScript.put("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".toString(), "successfully rolled out") + scriptMock.expectedShRetValueForScript.put("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".toString(), "error rollout") + scriptMock.expectedShRetValueForScript.put("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip", "192.168.56.2") + K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") // when - sut.setup(tag, 1, 1) + shouldFail(RuntimeException) { + sut.setup(tag, [:], 1, 1) + } // then assertThat(scriptMock.actualEcho.get(0)).isEqualTo("Installing setup...") - assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup-config.yaml".trim()) - assertThat(scriptMock.writeFileParams.get(0)["setup.json"]).isNotEmpty() - assertThat(scriptMock.actualShStringArgs[1].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl create configmap k8s-ces-setup-json --from-file=setup.json".trim()) - assertThat(scriptMock.actualShStringArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".trim()) - String setupYaml = scriptMock.writeFileParams.get(1)["setup.yaml"] - assertThat(setupYaml).isNotEmpty() + assertThat(scriptMock.actualShMapArgs[0].trim()).isEqualTo("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip") + assertThat(scriptMock.actualShMapArgs[1].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup-config.yaml".trim()) + assertThat(scriptMock.writeFileParams.get(0)).isNotNull() + assertThat(scriptMock.actualShMapArgs[2].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl create configmap k8s-ces-setup-json --from-file=setup.json".trim()) + assertThat(scriptMock.actualShMapArgs[3].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".trim()) + String setupYaml = scriptMock.writeFileParams.get(1) + assertThat(setupYaml).isNotNull() assertThat(setupYaml.contains("{{ .Namespace }}")).isFalse() - assertThat(scriptMock.actualShStringArgs[3].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f setup.yaml".trim()) - assertThat(scriptMock.actualEcho.get(0)).isEqualTo("Wait for dogu-operator to be ready...") - assertThat(scriptMock.actualShStringArgs[4].trim()).isEqualTo("sleep 1s") - assertThat(scriptMock.actualShStringArgs[5].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".trim()) + assertThat(scriptMock.actualShMapArgs[4].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f setup.yaml".trim()) + assertThat(scriptMock.actualEcho.get(1)).isEqualTo("Wait for dogu-operator to be ready...") + assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sleep 1s") + assertThat(scriptMock.actualShMapArgs[5].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".trim()) } } From c1cb91cd9cf94a04441e865fc960f3c49d0ce92a Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Mon, 20 Jun 2022 14:06:16 +0200 Subject: [PATCH 08/10] #79 Add unit tests for new k3d functionality --- CHANGELOG.md | 2 + README.md | 9 +- src/com/cloudogu/ces/cesbuildlib/K3d.groovy | 4 + .../cloudogu/ces/cesbuildlib/K3dTest.groovy | 445 +++++++++++------- 4 files changed, 295 insertions(+), 165 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e158d36..1312838 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- A ces setup is can automatically be performed for a K3d CES instance #79 ## [1.53.0](https://github.com/cloudogu/ces-build-lib/releases/tag/1.53.0) - 2022-04-20 ## Changed diff --git a/README.md b/README.md index 5002c04..1967ed1 100644 --- a/README.md +++ b/README.md @@ -1063,13 +1063,20 @@ try { stage('Do something with your cluster') { k3d.kubectl("get nodes") } - + stage('build and push development artefact') { String myCurrentArtefactVersion = "yourTag-1.2.3-dev" imageName = k3d.buildAndPushToLocalRegistry("your/image", myCurrentArtefactVersion) // your image name may look like this: k3d-citest-123456/your/image:yourTag-1.2.3-dev // the image name can be applied to your cluster as usual, f. i. with k3d.kubectl() with a customized K8s resource } + + stage('install resources and wait for them') { + imageName = "registry.cloudogu.com/official/my-dogu-name:1.0.0" + k3d.installDogu("my-dogu-name", imageName, myDoguResourceYamlFile) + + k3d.waitForDeploymentRollout("my-dogu-name", 300, 5) + } } finally { stage('Remove k3d cluster') { diff --git a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy index f115f94..d6da72b 100644 --- a/src/com/cloudogu/ces/cesbuildlib/K3d.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/K3d.groovy @@ -39,6 +39,10 @@ class K3d { registryConfigEncrypted: "" ] + String getRegistryName() { + return registryName + } + /** * Create an object to set up, modify and tear down a local k3d cluster * diff --git a/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy b/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy index 9e94b63..55090a1 100644 --- a/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy +++ b/test/com/cloudogu/ces/cesbuildlib/K3dTest.groovy @@ -1,179 +1,184 @@ package com.cloudogu.ces.cesbuildlib -import org.junit.Rule -import org.junit.Test -import org.junit.internal.runners.statements.ExpectException -import org.junit.rules.ExpectedException import org.mockito.Mockito import org.mockito.invocation.InvocationOnMock import org.mockito.stubbing.Answer -import static org.assertj.core.api.Assertions.* +import static org.assertj.core.api.Assertions.assertThat import static org.mockito.ArgumentMatchers.anyString import static org.mockito.Mockito.mock import static org.mockito.Mockito.when class K3dTest extends GroovyTestCase { -// void testCreateClusterName() { -// K3d sut = new K3d("script", "leWorkSpace", "leK3dWorkSpace", "path") -// String testClusterName = sut.createClusterName() -// assertTrue(testClusterName.contains("citest-")) -// assertTrue(testClusterName != "citest-") -// assertTrue(testClusterName.length() <= 32) -// String testClusterName2 = sut.createClusterName() -// assertTrue(testClusterName != testClusterName2) -// } -// -// void testDeleteK3d() { -// // given -// def scriptMock = new ScriptMock() -// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsocconfig kubectl get nodeskname()[1]); s.close()\');'.toString(), "54321") -// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") -// -// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") -// -// // we need to create a registry so that the deletion of the registry is triggered -// sut.startK3d() -// -// // when -// sut.deleteK3d() -// -// // then -// assertThat(scriptMock.actualShStringArgs[11].trim()).contains("k3d registry delete citest-") -// assertThat(scriptMock.actualShStringArgs[12].trim()).contains("k3d cluster delete citest-") -// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(13) -// } -// -// void testKubectl() { -// // given -// String workspaceDir = "leWorkspace" -// def scriptMock = new ScriptMock() -// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") -// -// // when -// sut.kubectl("get nodes") -// -// // then -// assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl get nodes".trim()) -// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(1) -// } -// -// void testStartK3d() { -// def workspaceDir = "leWorkspace" -// def k3dVer = "4.4.7" -// -// def scriptMock = new ScriptMock() -// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") -// -// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") -// -// sut.startK3d() -// -// assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("rm -rf ${workspaceDir}/.k3d".toString()) -// assertThat(scriptMock.actualShStringArgs[1].trim()).isEqualTo("mkdir -p ${workspaceDir}/.k3d/bin".toString()) -// assertThat(scriptMock.actualShStringArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v${k3dVer} K3D_INSTALL_DIR=${workspaceDir}/.k3d/bin bash -s -- --no-sudo".toString()) -// assertThat(scriptMock.actualShStringArgs[3].trim()).matches("k3d registry create citest-[0-9a-f]+ --port 54321") -// assertThat(scriptMock.actualShStringArgs[4].trim()).startsWith("k3d cluster create citest-") -// assertThat(scriptMock.actualShStringArgs[5].trim()).startsWith("k3d kubeconfig merge citest-") -// assertThat(scriptMock.actualShStringArgs[6].trim()).startsWith("echo \"Using credentials: cesmarvin-setup\"") -// assertThat(scriptMock.actualShStringArgs[7].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-dogu-registry || true") -// assertThat(scriptMock.actualShStringArgs[8].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-docker-registry || true") -// assertThat(scriptMock.actualShStringArgs[9].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret generic k8s-dogu-operator-dogu-registry --from-literal=endpoint=\"https://dogu.cloudogu.com/api/v2/dogus\" --from-literal=username=\"null\" --from-literal=password=\"null\"") -// assertThat(scriptMock.actualShStringArgs[10].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret docker-registry k8s-dogu-operator-docker-registry --docker-server=\"registry.cloudogu.com\" --docker-username=\"null\" --docker-email=\"a@b.c\" --docker-password=\"null\"") -// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(11) -// } -// -// void testStartK3dWithCustomCredentials() { -// def workspaceDir = "leWorkspace" -// def k3dVer = "4.4.7" -// -// def scriptMock = new ScriptMock() -// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") -// -// K3d sut = new K3d(scriptMock, "${workspaceDir}", "path", "myBackendCredentialsID") -// -// sut.startK3d() -// -// assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("rm -rf ${workspaceDir}/.k3d".toString()) -// assertThat(scriptMock.actualShStringArgs[1].trim()).isEqualTo("mkdir -p ${workspaceDir}/.k3d/bin".toString()) -// assertThat(scriptMock.actualShStringArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v${k3dVer} K3D_INSTALL_DIR=${workspaceDir}/.k3d/bin bash -s -- --no-sudo".toString()) -// assertThat(scriptMock.actualShStringArgs[3].trim()).matches("k3d registry create citest-[0-9a-f]+ --port 54321") -// assertThat(scriptMock.actualShStringArgs[4].trim()).startsWith("k3d cluster create citest-") -// assertThat(scriptMock.actualShStringArgs[5].trim()).startsWith("k3d kubeconfig merge citest-") -// assertThat(scriptMock.actualShStringArgs[6].trim()).startsWith("echo \"Using credentials: myBackendCredentialsID\"") -// assertThat(scriptMock.actualShStringArgs[7].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-dogu-registry || true") -// assertThat(scriptMock.actualShStringArgs[8].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-docker-registry || true") -// assertThat(scriptMock.actualShStringArgs[9].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret generic k8s-dogu-operator-dogu-registry --from-literal=endpoint=\"https://dogu.cloudogu.com/api/v2/dogus\" --from-literal=username=\"null\" --from-literal=password=\"null\"") -// assertThat(scriptMock.actualShStringArgs[10].trim()).startsWith("sudo KUBECONFIG=${workspaceDir}/.k3d/.kube/config kubectl create secret docker-registry k8s-dogu-operator-docker-registry --docker-server=\"registry.cloudogu.com\" --docker-username=\"null\" --docker-email=\"a@b.c\" --docker-password=\"null\"") -// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(11) -// } -// -// void testBuildAndPush() { -// // given -// String imageName = "superimage" -// String imageTag = "1.2.1" -// -// Docker dockerMock = mock(Docker.class) -// Docker.Image imageMock = mock(Docker.Image.class) -// def scriptMock = new ScriptMock(dockerMock) -// -// when(dockerMock.build(anyString())).thenReturn(imageMock) -// when(dockerMock.withRegistry(anyString(), Mockito.any(Closure.class))).then(new Answer() { -// @Override -// Object answer(InvocationOnMock invocation) throws Throwable { -// Closure doThings = (Closure)invocation.getArgument(1) -// doThings.call() -// } -// }) -// when(imageMock.push(imageTag)).then(new Answer() { -// @Override -// Object answer(InvocationOnMock invocation) throws Throwable { -// scriptMock.sh("image pushed") -// } -// }) -// scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") -// -// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") -// sut.startK3d() -// -// // when -// sut.buildAndPushToLocalRegistry(imageName, imageTag) -// -// // then -// assertThat(scriptMock.actualShStringArgs[11].trim()).isEqualTo("image pushed".toString()) -// assertThat(scriptMock.actualShStringArgs.size()).isEqualTo(12) -// } - -// void testSetup() { -// // given -// def workspaceEnvDir = "leK3dWorkSpace" -// String tag = "v0.6.0" -// def scriptMock = new ScriptMock() -// scriptMock.expectedShRetValueForScript.put("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".toString(), "fake setup yaml with {{ .Namespace }}") -// scriptMock.expectedShRetValueForScript.put("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".toString(), "successfully rolled out") -// scriptMock.expectedShRetValueForScript.put("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip", "192.168.56.2") -// -// K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") -// -// // when -// sut.setup(tag, [:], 1, 1) -// -// // then -// assertThat(scriptMock.actualEcho.get(0)).isEqualTo("Installing setup...") -// assertThat(scriptMock.actualShMapArgs[0].trim()).isEqualTo("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip") -// assertThat(scriptMock.actualShMapArgs[1].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup-config.yaml".trim()) -// assertThat(scriptMock.writeFileParams.get(0)).isNotNull() -// assertThat(scriptMock.actualShMapArgs[2].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl create configmap k8s-ces-setup-json --from-file=setup.json".trim()) -// assertThat(scriptMock.actualShMapArgs[3].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".trim()) -// String setupYaml = scriptMock.writeFileParams.get(1) -// assertThat(setupYaml).isNotNull() -// assertThat(setupYaml.contains("{{ .Namespace }}")).isFalse() -// assertThat(scriptMock.actualShMapArgs[4].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f setup.yaml".trim()) -// assertThat(scriptMock.actualEcho.get(1)).isEqualTo("Wait for dogu-operator to be ready...") -// assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sleep 1s") -// assertThat(scriptMock.actualShMapArgs[5].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".trim()) -// } + void testCreateClusterName() { + K3d sut = new K3d("script", "leWorkSpace", "leK3dWorkSpace", "path") + String testClusterName = sut.createClusterName() + assertTrue(testClusterName.contains("citest-")) + assertTrue(testClusterName != "citest-") + assertTrue(testClusterName.length() <= 32) + String testClusterName2 = sut.createClusterName() + assertTrue(testClusterName != testClusterName2) + } + + void testDeleteK3d() { + // given + def scriptMock = new ScriptMock() + scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsocconfig kubectl get nodeskname()[1]); s.close()\');'.toString(), "54321") + scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") + + K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") + + // we need to create a registry so that the deletion of the registry is triggered + sut.startK3d() + + // when + sut.deleteK3d() + + // then + assertThat(scriptMock.allActualArgs[14].trim()).contains("k3d registry delete citest-") + assertThat(scriptMock.allActualArgs[15].trim()).contains("k3d cluster delete citest-") + assertThat(scriptMock.allActualArgs.size()).isEqualTo(16) + } + + void testKubectl() { + // given + String workspaceDir = "leWorkspace" + def scriptMock = new ScriptMock() + K3d sut = new K3d(scriptMock, workspaceDir, "leK3dWorkSpace", "path") + + // when + sut.kubectl("get nodes") + + // then + assertThat(scriptMock.allActualArgs[0].trim()).isEqualTo("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl get nodes".trim()) + assertThat(scriptMock.allActualArgs.size()).isEqualTo(1) + } + void testStartK3d() { + def workspaceDir = "leWorkspace" + def k3dWorkspaceDir = "leK3dWorkSpace" + def k3dVer = "4.4.7" + + def scriptMock = new ScriptMock() + scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") + + K3d sut = new K3d(scriptMock, workspaceDir, k3dWorkspaceDir, "path") + + sut.startK3d() + + assertThat(scriptMock.allActualArgs[0].trim()).isEqualTo("rm -rf ${k3dWorkspaceDir}/.k3d".toString()) + assertThat(scriptMock.allActualArgs[1].trim()).isEqualTo("mkdir -p ${k3dWorkspaceDir}/.k3d/bin".toString()) + assertThat(scriptMock.allActualArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v${k3dVer} K3D_INSTALL_DIR=${k3dWorkspaceDir}/.k3d/bin bash -s -- --no-sudo".toString()) + assertThat(scriptMock.allActualArgs[3].trim()).isEqualTo("echo -n \$(python3 -c 'import socket; s=socket.socket(); s.bind((\"\", 0)); print(s.getsockname()[1]); s.close()');") + assertThat(scriptMock.allActualArgs[4].trim()).matches("k3d registry create citest-[0-9a-f]+ --port 54321") + assertThat(scriptMock.allActualArgs[5].trim()).startsWith("k3d cluster create citest-") + assertThat(scriptMock.allActualArgs[6].trim()).startsWith("k3d kubeconfig merge citest-") + assertThat(scriptMock.allActualArgs[7].trim()).startsWith("snap list kubectl") + assertThat(scriptMock.allActualArgs[8].trim()).startsWith("sudo snap install kubectl") + assertThat(scriptMock.allActualArgs[9].trim()).startsWith("echo \"Using credentials: cesmarvin-setup\"") + assertThat(scriptMock.allActualArgs[10].trim()).startsWith("sudo KUBECONFIG=${k3dWorkspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-dogu-registry || true") + assertThat(scriptMock.allActualArgs[11].trim()).startsWith("sudo KUBECONFIG=${k3dWorkspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-docker-registry || true") + assertThat(scriptMock.allActualArgs[12].trim()).startsWith("sudo KUBECONFIG=${k3dWorkspaceDir}/.k3d/.kube/config kubectl create secret generic k8s-dogu-operator-dogu-registry --from-literal=endpoint=\"https://dogu.cloudogu.com/api/v2/dogus\" --from-literal=username=\"null\" --from-literal=password=\"null\"") + assertThat(scriptMock.allActualArgs[13].trim()).startsWith("sudo KUBECONFIG=${k3dWorkspaceDir}/.k3d/.kube/config kubectl create secret docker-registry k8s-dogu-operator-docker-registry --docker-server=\"registry.cloudogu.com\" --docker-username=\"null\" --docker-email=\"a@b.c\" --docker-password=\"null\"") + assertThat(scriptMock.allActualArgs.size()).isEqualTo(14) + } + + void testStartK3dWithCustomCredentials() { + def workspaceDir = "leWorkspace" + def k3dWorkspaceDir = "path" + def k3dVer = "4.4.7" + + def scriptMock = new ScriptMock() + scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") + + K3d sut = new K3d(scriptMock, workspaceDir, k3dWorkspaceDir, "", "myBackendCredentialsID") + + sut.startK3d() + + assertThat(scriptMock.allActualArgs[0].trim()).isEqualTo("rm -rf ${k3dWorkspaceDir}/.k3d".toString()) + assertThat(scriptMock.allActualArgs[1].trim()).isEqualTo("mkdir -p ${k3dWorkspaceDir}/.k3d/bin".toString()) + assertThat(scriptMock.allActualArgs[2].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v${k3dVer} K3D_INSTALL_DIR=${k3dWorkspaceDir}/.k3d/bin bash -s -- --no-sudo".toString()) + assertThat(scriptMock.allActualArgs[3].trim()).isEqualTo("echo -n \$(python3 -c 'import socket; s=socket.socket(); s.bind((\"\", 0)); print(s.getsockname()[1]); s.close()');") + assertThat(scriptMock.allActualArgs[4].trim()).matches("k3d registry create citest-[0-9a-f]+ --port 54321") + assertThat(scriptMock.allActualArgs[5].trim()).startsWith("k3d cluster create citest-") + assertThat(scriptMock.allActualArgs[6].trim()).startsWith("k3d kubeconfig merge citest-") + assertThat(scriptMock.allActualArgs[7].trim()).startsWith("snap list kubectl") + assertThat(scriptMock.allActualArgs[8].trim()).startsWith("sudo snap install kubectl") + assertThat(scriptMock.allActualArgs[9].trim()).startsWith("echo \"Using credentials: myBackendCredentialsID\"") + assertThat(scriptMock.allActualArgs[10].trim()).startsWith("sudo KUBECONFIG=${k3dWorkspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-dogu-registry || true") + assertThat(scriptMock.allActualArgs[11].trim()).startsWith("sudo KUBECONFIG=${k3dWorkspaceDir}/.k3d/.kube/config kubectl delete secret k8s-dogu-operator-docker-registry || true") + assertThat(scriptMock.allActualArgs[12].trim()).startsWith("sudo KUBECONFIG=${k3dWorkspaceDir}/.k3d/.kube/config kubectl create secret generic k8s-dogu-operator-dogu-registry --from-literal=endpoint=\"https://dogu.cloudogu.com/api/v2/dogus\" --from-literal=username=\"null\" --from-literal=password=\"null\"") + assertThat(scriptMock.allActualArgs[13].trim()).startsWith("sudo KUBECONFIG=${k3dWorkspaceDir}/.k3d/.kube/config kubectl create secret docker-registry k8s-dogu-operator-docker-registry --docker-server=\"registry.cloudogu.com\" --docker-username=\"null\" --docker-email=\"a@b.c\" --docker-password=\"null\"") + assertThat(scriptMock.allActualArgs.size()).isEqualTo(14) + } + + void testBuildAndPush() { + // given + String imageName = "superimage" + String imageTag = "1.2.1" + + Docker dockerMock = mock(Docker.class) + Docker.Image imageMock = mock(Docker.Image.class) + def scriptMock = new ScriptMock(dockerMock) + + when(dockerMock.build(anyString())).thenReturn(imageMock) + when(dockerMock.withRegistry(anyString(), Mockito.any(Closure.class))).then(new Answer() { + @Override + Object answer(InvocationOnMock invocation) throws Throwable { + Closure doThings = (Closure) invocation.getArgument(1) + doThings.call() + } + }) + when(imageMock.push(imageTag)).then(new Answer() { + @Override + Object answer(InvocationOnMock invocation) throws Throwable { + scriptMock.sh("image pushed") + } + }) + scriptMock.expectedShRetValueForScript.put('echo -n $(python3 -c \'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()\');'.toString(), "54321") + + K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") + sut.startK3d() + + // when + sut.buildAndPushToLocalRegistry(imageName, imageTag) + + // then + assertThat(scriptMock.allActualArgs[14].trim()).isEqualTo("image pushed".toString()) + assertThat(scriptMock.allActualArgs.size()).isEqualTo(15) + } + + void testSetup() { + // given + def workspaceEnvDir = "leK3dWorkSpace" + String tag = "v0.6.0" + def scriptMock = new ScriptMock() + scriptMock.expectedShRetValueForScript.put("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".toString(), "fake setup yaml with {{ .Namespace }}") + scriptMock.expectedShRetValueForScript.put("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".toString(), "successfully rolled out") + scriptMock.expectedShRetValueForScript.put("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip", "192.168.56.2") + + K3d sut = new K3d(scriptMock, "leWorkSpace", "leK3dWorkSpace", "path") + + // when + sut.setup(tag, [:], 1, 1) + + // then + assertThat(scriptMock.actualEcho.get(0)).isEqualTo("Installing setup...") + assertThat(scriptMock.actualEcho.get(1)).isEqualTo("Wait for dogu-operator to be ready...") + + assertThat(scriptMock.allActualArgs[0].trim()).isEqualTo("curl -H \"Metadata-Flavor: Google\" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip") + assertThat(scriptMock.allActualArgs[1].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup-config.yaml".trim()) + assertThat(scriptMock.allActualArgs[2].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl create configmap k8s-ces-setup-json --from-file=setup.json".trim()) + assertThat(scriptMock.allActualArgs[3].trim()).isEqualTo("curl -s https://raw.githubusercontent.com/cloudogu/k8s-ces-setup/${tag}/k8s/k8s-ces-setup.yaml".trim()) + assertThat(scriptMock.allActualArgs[4].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl apply -f setup.yaml".trim()) + assertThat(scriptMock.allActualArgs[5].trim()).isEqualTo("sleep 1s") + assertThat(scriptMock.allActualArgs[6].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".trim()) + + assertThat(scriptMock.writeFileParams.get(0)).isNotNull() + String setupYaml = scriptMock.writeFileParams.get(1) + assertThat(setupYaml).isNotNull() + assertThat(setupYaml.contains("{{ .Namespace }}")).isFalse() + } void testSetupShouldThrowExceptionOnDoguOperatorRollout() { // given @@ -207,4 +212,116 @@ class K3dTest extends GroovyTestCase { assertThat(scriptMock.actualShStringArgs[0].trim()).isEqualTo("sleep 1s") assertThat(scriptMock.actualShMapArgs[5].trim()).isEqualTo("sudo KUBECONFIG=${workspaceEnvDir}/.k3d/.kube/config kubectl rollout status deployment/k8s-dogu-operator-controller-manager".trim()) } + + void testK3d_installDogu() { + def workspaceDir = "leWorkspace" + def k3dWorkspaceDir = "leK3dWorkSpace" + + String exampleDockerInspect = """ +[ + { + "HostConfig": { + "PortBindings": { + "5000/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "35419" + } + ] + }, + "Config": { + "Hostname": "k3d-citest-c6cc60667cf3", + "Labels": { + "k3s.registry.port.internal": "5000" + } + }, + "NetworkSettings": { + "Networks": { + "k3d-citest-c6cc60667cf3": { + "IPAMConfig": null, + "Links": null, + "Aliases": [ + "89309458869a", + "k3d-citest-c6cc60667cf3" + ], + "NetworkID": "34aa9caac510f0ba78e3f18aa8e5c5d7e1d915ee4404524f93a95b2ee3281254", + "EndpointID": "961364ea3a160c66472d5618c5842e2e4f80f27dbd795eea0f32b3442b696b66", + "Gateway": "192.168.32.1", + "IPAddress": "192.168.32.2", + "IPPrefixLen": 20, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "02:42:c0:a8:20:02", + "DriverOpts": null + } + } + } + } +] +""" + + def scriptMock = new ScriptMock() + K3d sut = new K3d(scriptMock, workspaceDir, k3dWorkspaceDir, "path") + String prefixedRegistryName = "k3d-${sut.getRegistryName()}" + String port = "5000" + String imageUrl = "myIP:1234/test/myimage:0.1.2" + + scriptMock.expectedShRetValueForScript.put('whoami'.toString(), "itsme") + scriptMock.expectedShRetValueForScript.put('cat /etc/passwd | grep itsme'.toString(), "test:x:900:1001::/home/test:/bin/sh") + scriptMock.expectedShRetValueForScript.put("docker inspect " + prefixedRegistryName, exampleDockerInspect) + scriptMock.expectedShRetValueForScript.put("echo '" + exampleDockerInspect + "' | yq '.[].NetworkSettings.Networks." + prefixedRegistryName + ".IPAddress'", "192.168.32.2") + scriptMock.expectedShRetValueForScript.put("echo '" + exampleDockerInspect + "' | yq '.[].Config.Labels.\"k3s.registry.port.internal\"'", port) + scriptMock.expectedShRetValueForScript.put("yq -e '.Image' dogu.json | sed 's|registry\\.cloudogu\\.com\\(.\\+\\)|" + "myIP" + ".local:" + port + "\\1|g'", "myIP.local:5000/test/myimage:0.1.2") + scriptMock.expectedShRetValueForScript.put("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl get pod --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'", "test-execpod") + scriptMock.expectedShRetValueForScript.put("echo 'test-execpod' | grep 'test-execpod'", "test-execpod") + scriptMock.expectedShRetValueForScript.put("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl get deployment --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'", "test") + scriptMock.expectedShRetValueForScript.put("echo 'test' | grep 'test'", "test") + + String doguYaml = """ +apiVersion: k8s.cloudogu.com/v1 +kind: Dogu +metadata: + name: nginx-ingress + labels: + dogu: nginx-ingress +spec: + name: k8s/nginx-ingress + version: 1.1.2-1-0 +""" + + sut.installDogu("test", "myIP:1234/test/myimage:0.1.2", doguYaml) + + assertThat(scriptMock.allActualArgs[0].trim()).contains("docker inspect k3d-citest-") + assertThat(scriptMock.allActualArgs[1].trim()).isEqualTo("whoami") + assertThat(scriptMock.allActualArgs[2].trim()).isEqualTo("cat /etc/passwd | grep itsme") + assertThat(scriptMock.allActualArgs[3].trim()).isEqualTo("echo '" + exampleDockerInspect + "' | yq '.[].NetworkSettings.Networks." + prefixedRegistryName + ".IPAddress'") + assertThat(scriptMock.allActualArgs[4].trim()).isEqualTo("echo '" + exampleDockerInspect + "' | yq '.[].Config.Labels.\"k3s.registry.port.internal\"'") + assertThat(scriptMock.allActualArgs[5].trim()).isEqualTo("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl -n kube-system patch cm coredns --patch-file coreDNSPatch.yaml") + assertThat(scriptMock.allActualArgs[6].trim()).isEqualTo("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl rollout restart -n kube-system deployment/coredns") + assertThat(scriptMock.allActualArgs[7].trim()).isEqualTo("whoami") + assertThat(scriptMock.allActualArgs[8].trim()).isEqualTo("cat /etc/passwd | grep itsme") + assertThat(scriptMock.allActualArgs[9].trim()).isEqualTo("yq -e '.Image' dogu.json | sed 's|registry\\.cloudogu\\.com\\(.\\+\\)|myIP.local:5000\\1|g'") + assertThat(scriptMock.allActualArgs[10].trim()).isEqualTo("yq '.Image=\"myIP.local:5000/test/myimage:0.1.2\"' dogu.json > leWorkspace/target/dogu.json") + assertThat(scriptMock.allActualArgs[11].trim()).isEqualTo("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl create configmap test-descriptor --from-file=leWorkspace/target/dogu.json") + assertThat(scriptMock.allActualArgs[12].trim()).isEqualTo("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl apply -f \n" + + "apiVersion: k8s.cloudogu.com/v1\n" + + "kind: Dogu\n" + + "metadata:\n" + + " name: nginx-ingress\n" + + " labels:\n" + + " dogu: nginx-ingress\n" + + "spec:\n" + + " name: k8s/nginx-ingress\n" + + " version: 1.1.2-1-0") + assertThat(scriptMock.allActualArgs[13].trim()).isEqualTo("sleep 2s") + assertThat(scriptMock.allActualArgs[14].trim()).isEqualTo("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl get pod --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'") + assertThat(scriptMock.allActualArgs[15].trim()).isEqualTo("echo 'test-execpod' | grep 'test-execpod'") + assertThat(scriptMock.allActualArgs[16].trim()).isEqualTo("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl patch pod 'test-execpod' -p '{\"spec\":{\"containers\":[{\"name\":\"test-execpod\",\"image\":\"myIP:1234/test/myimage:0.1.2\"}]}}'") + assertThat(scriptMock.allActualArgs[17].trim()).isEqualTo("sleep 5s") + assertThat(scriptMock.allActualArgs[18].trim()).isEqualTo("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl get deployment --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'") + assertThat(scriptMock.allActualArgs[19].trim()).isEqualTo("echo 'test' | grep 'test'") + assertThat(scriptMock.allActualArgs[20].trim()).startsWith("sudo KUBECONFIG=leK3dWorkSpace/.k3d/.kube/config kubectl patch deployment 'test' -p '{\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\":\"test\",\"image\":\"myIP:1234/test/myimage:0.1.2\"}]}}}}'") + assertThat(scriptMock.allActualArgs.size()).isEqualTo(21) + } } From 02f5bb5c70695a517d078ec34135543f9c95506a Mon Sep 17 00:00:00 2001 From: Niklas Date: Mon, 20 Jun 2022 17:08:30 +0200 Subject: [PATCH 09/10] #79 Escape single quotes in data of http requests --- src/com/cloudogu/ces/cesbuildlib/HttpClient.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/cloudogu/ces/cesbuildlib/HttpClient.groovy b/src/com/cloudogu/ces/cesbuildlib/HttpClient.groovy index 06ae576..c4638e9 100644 --- a/src/com/cloudogu/ces/cesbuildlib/HttpClient.groovy +++ b/src/com/cloudogu/ces/cesbuildlib/HttpClient.groovy @@ -52,12 +52,12 @@ class HttpClient implements Serializable { def body executeWithCredentials { - + String dataStr = data.toString().replaceAll("'", "'\"'\"'") String curlCommand = "curl -i -X ${httpMethod} " + (credentials ? "${getCurlAuthParam()} " : '') + (contentType ? "-H 'Content-Type: ${contentType}' " : '') + - (data ? "-d '${data.toString()}' " : '') + + (data ? "-d '" + dataStr + "' " : '') + "${url}" // Command must be run inside this closure, otherwise the credentials will not be masked (using '*') in the console From 926d000c2aec1d4582bfad68be8d1ea6622cfaf6 Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Tue, 21 Jun 2022 07:22:28 +0200 Subject: [PATCH 10/10] #79 bump version & update CHANGELOG.md --- CHANGELOG.md | 2 ++ pom.xml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1312838..d803040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [1.54.0](https://github.com/cloudogu/ces-build-lib/releases/tag/1.54.0) - 2022-06-21 ### Added - A ces setup is can automatically be performed for a K3d CES instance #79 diff --git a/pom.xml b/pom.xml index a5b3751..3a10c6e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.cloudogu.ces ces-build-lib ces-build-lib - 1.53.0 + 1.54.0 UTF-8