11package com.cloudogu.ces.cesbuildlib
22
33import com.cloudbees.groovy.cps.NonCPS
4+ import groovy.json.JsonSlurper
45
56class K3d {
67 /**
@@ -12,9 +13,17 @@ class K3d {
1213 */
1314 private static String K3D_VERSION = " 5.6.0"
1415 private static String K3D_LOG_FILENAME = " k8sLogs"
15- private static String K3D_SETUP_JSON_FILE = " k3d_setup.json"
1616 private static String K3D_VALUES_YAML_FILE = " k3d_values.yaml"
17+ private static String K3D_BLUEPRINT_FILE = " k3d_blueprint.yaml"
1718 private static String YQ_VERSION = " 4.40.5"
19+ // need to be installed before apply values.yaml
20+ private static String VERSION_ECOSYSTEM_CORE ; // e.g. "1.2.0"
21+ private static String VERSION_K8S_COMPONENT_OPERATOR_CRD ; // e.g. "1.10.1"
22+ // configured by values.yaml
23+ private static String VERSION_K8S_DOGU_OPERATOR ; // e.g. "3.15.0"
24+ private static String VERSION_K8S_DOGU_OPERATOR_CRD ; // e.g. "2.10.0"
25+ private static String VERSION_K8S_BLUEPRINT_OPERATOR ; // e.g. "3.0.2"
26+ private static String VERSION_K8S_BLUEPRINT_OPERATOR_CRD ; // e.g. "3.1.0"
1827
1928 private String clusterName
2029 private script
@@ -36,14 +45,14 @@ class K3d {
3645 adminGroup : " CesAdministrators" ,
3746 dependencies : [" official/ldap" ,
3847 " official/cas" ,
39- " k8s/nginx-ingress" ,
40- " k8s/nginx-static" ,
4148 " official/postfix" ,
4249 " official/usermgt" ],
4350 defaultDogu : " " ,
4451 additionalDependencies : [],
4552 registryConfig : " " ,
46- registryConfigEncrypted : " "
53+ registryConfigEncrypted : " " ,
54+ " enableBackup" : false ,
55+ " enableMonitoring" : false
4756 ]
4857
4958 String getRegistryName () {
@@ -254,40 +263,60 @@ class K3d {
254263 }
255264 }
256265
257- void configureSetupJson (config = [:]) {
258- String setupJsonConfigKey = " .setup_json"
259266
260- script. echo " configuring setup..."
267+ static void setVersionEcosystemCore (String v ) {
268+ VERSION_ECOSYSTEM_CORE = v;
269+ }
270+ static void setVersionComponentOperatorCrd (String v ) {
271+ VERSION_K8S_COMPONENT_OPERATOR_CRD = v;
272+ }
273+ static void setVersionDoguOperator (String v ) {
274+ VERSION_K8S_DOGU_OPERATOR = v;
275+ }
276+ static void setVersionDoguOperatorCrd (String v ) {
277+ VERSION_K8S_DOGU_OPERATOR_CRD = v;
278+ }
279+ static void setVersionBlueprintOperator (String v ) {
280+ VERSION_K8S_BLUEPRINT_OPERATOR = v;
281+ }
282+ static void setVersionBlueprintOperatorCrd (String v ) {
283+ VERSION_K8S_BLUEPRINT_OPERATOR_CRD = v;
284+ }
285+
286+ void configureEcosystemCoreValues (config = [:]) {
261287 // Merge default config with the one passed as parameter
262288 config = defaultSetupConfig << config
263- writeSetupJson(config)
264289
265- appendFileToYamlFile(K3D_VALUES_YAML_FILE , setupJsonConfigKey, K3D_SETUP_JSON_FILE )
266- }
290+ yqEvalYamlFile(K3D_VALUES_YAML_FILE , " .defaultConfig.env.waitTimeoutMinutes = 5" )
267291
268- void configureSetupImage (String image ) {
269- String hostKey = " .setup.image.registry"
270- String repositoryKey = " .setup.image.repository"
271- String tagKey = " .setup.image.tag"
272- def repositorySeparatorIndex = image. indexOf(" /" )
273- def tagSeparatorIndex = image. lastIndexOf(" :" )
292+ if (VERSION_K8S_DOGU_OPERATOR_CRD != null ) {
293+ appendToYamlFile(K3D_VALUES_YAML_FILE , " .components.k8s-dogu-operator-crd.version" , VERSION_K8S_DOGU_OPERATOR_CRD )
294+ }
295+ if (VERSION_K8S_DOGU_OPERATOR != null ) {
296+ appendToYamlFile(K3D_VALUES_YAML_FILE , " .components.k8s-dogu-operator.version" , VERSION_K8S_DOGU_OPERATOR )
297+ }
298+ if (VERSION_K8S_BLUEPRINT_OPERATOR_CRD != null ) {
299+ appendToYamlFile(K3D_VALUES_YAML_FILE , " .components.k8s-blueprint-operator-crd.version" , VERSION_K8S_BLUEPRINT_OPERATOR_CRD )
300+ }
301+ if (VERSION_K8S_BLUEPRINT_OPERATOR != null ) {
302+ appendToYamlFile(K3D_VALUES_YAML_FILE , " .components.k8s-blueprint-operator.version" , VERSION_K8S_BLUEPRINT_OPERATOR )
303+ }
274304
275- appendToYamlFile(K3D_VALUES_YAML_FILE , hostKey, image. substring(0 , repositorySeparatorIndex))
276- appendToYamlFile(K3D_VALUES_YAML_FILE , repositoryKey, image. substring(repositorySeparatorIndex + 1 , tagSeparatorIndex))
277- appendToYamlFile(K3D_VALUES_YAML_FILE , tagKey, image. substring(tagSeparatorIndex + 1 , image. length()))
278- }
305+ yqEvalYamlFile(K3D_VALUES_YAML_FILE , " .components.k8s-ces-control.disabled = true" )
279306
280- void configureComponentOperatorVersion (String operatorVersion , String crdVersion = operatorVersion, String namespace = " k8s" ) {
281- String componentOpKey = " .component_operator_chart"
282- String componentCRDKey = " .component_operator_crd_chart"
307+ appendToYamlFile(K3D_VALUES_YAML_FILE , " .components.k8s-service-discovery.valuesObject.loadBalancerService.internalTrafficPolicy" , " Cluster" )
308+ appendToYamlFile(K3D_VALUES_YAML_FILE , " .components.k8s-service-discovery.valuesObject.loadBalancerService.externalTrafficPolicy" , " Cluster" )
283309
310+ yqEvalYamlFile(K3D_VALUES_YAML_FILE , " .backup.enabled = ${ config.enableBackup} " )
311+ yqEvalYamlFile(K3D_VALUES_YAML_FILE , " .monitoring.enabled = ${ config.enableMonitoring} " )
312+
313+ script. echo " configuring ecosystem core..."
314+ writeBlueprintYaml(config)
315+ }
284316
285- def builder = new StringBuilder (namespace)
286- String operatorValue = builder. append(" /k8s-component-operator:" ). append(operatorVersion). toString()
287- appendToYamlFile(K3D_VALUES_YAML_FILE , componentOpKey, operatorValue)
288- builder. delete(0 , builder. length());
289- String crdValue = builder. append(namespace). append(" /k8s-component-operator-crd:" ). append(crdVersion). toString()
290- appendToYamlFile(K3D_VALUES_YAML_FILE , componentCRDKey, crdValue)
317+ @Deprecated
318+ void configureSetupJson (config = [:]) {
319+ configureEcosystemCoreValues(config)
291320 }
292321
293322 void configureComponents (components = [:]) {
@@ -308,63 +337,69 @@ class K3d {
308337 }
309338 }
310339
311- void configureLogLevel (String loglevel ) {
312- appendToYamlFile(K3D_VALUES_YAML_FILE , " .logLevel" , loglevel)
313- }
314-
315- void installAndTriggerSetup (String tag , Integer timeout = 300 , Integer interval = 5 ) {
340+ void installAndTriggerSetup (Integer timeout = 300 , Integer interval = 5 ) {
316341 script. echo " Installing setup..."
317342 String registryUrl = " registry.cloudogu.com"
318343 String registryNamespace = " k8s"
319344 script. withCredentials([[$class : ' UsernamePasswordMultiBinding' , credentialsId : ' harborhelmchartpush' , usernameVariable : ' HARBOR_USERNAME' , passwordVariable : ' HARBOR_PASSWORD' ]]) {
320345 helm(" registry login ${ registryUrl} --username '${ script.env.HARBOR_USERNAME} ' --password '${ script.env.HARBOR_PASSWORD} '" )
321346 }
322347
323- helm(" install -f ${ K3D_VALUES_YAML_FILE} k8s-ces-setup oci://${ registryUrl} /${ registryNamespace} /k8s-ces-setup --version ${ tag} --namespace default" )
324- helm(" registry logout ${ registryUrl} " )
348+ // install crd first
349+ String comp_crd_version = VERSION_K8S_COMPONENT_OPERATOR_CRD == null ? " " : " --version ${ VERSION_K8S_COMPONENT_OPERATOR_CRD} "
350+ helm(" install k8s-component-operator-crd oci://${ registryUrl} /${ registryNamespace} /k8s-component-operator-crd ${ comp_crd_version} --namespace default" )
351+
352+ kubectl(" --namespace default create configmap global-config --from-literal=config.yaml='fqdn: ${ externalIP} '" )
325353
326- script . echo " Wait for dogu-operator to be ready... "
327- waitForDeploymentRollout( " k8s-dogu-operator-controller-manager " , timeout, interval )
354+ String eco_core_version = VERSION_ECOSYSTEM_CORE == null ? " " : " --version ${ VERSION_ECOSYSTEM_CORE } "
355+ helm( " install -f ${ K3D_VALUES_YAML_FILE } ecosystem-core oci:// ${ registryUrl } / ${ registryNamespace } /ecosystem-core ${ eco_core_version } --namespace default -- timeout 15m " )
328356
329- script. echo " Wait for setup-finisher to be executed..."
330- waitForSetupToFinish(timeout, interval)
357+ script. echo " Wait for blueprint-operator to be ready..."
358+ waitForDeploymentRollout(" k8s-blueprint-operator-controller-manager" , timeout, interval)
359+
360+ kubectl(" apply -f ${ K3D_BLUEPRINT_FILE} --namespace default" )
361+
362+ script. echo " Wait for blueprint to be ready..."
363+ waitForBlueprintToBeReady(timeout, interval)
331364
332365 script. echo " Wait for dogus to be ready..."
333366 waitForDogusToBeRolledOut(timeout, interval)
367+
368+ helm(" registry logout ${ registryUrl} " )
334369 }
335370
336371 void waitForDogusToBeRolledOut (Integer timeout , Integer interval ) {
337372 String dogus = kubectl(" get dogus --template '{{range .items}}{{.metadata.name}}{{\"\\ n\" }}{{end}}'" , true )
338- String [] doguList = dogus. split(" \n " )
373+ String [] doguList = dogus. trim() . split(" \n " )
339374 for (String dogu : doguList) {
340375 script. echo " Wait for $dogu to be rolled out..."
341376 waitForDeploymentRollout(dogu, timeout, interval)
342377 }
343378 }
344379
345- void waitForSetupToFinish (Integer timeout , Integer interval ) {
380+ void waitForBlueprintToBeReady (Integer timeout , Integer interval ) {
346381 for (int i = 0 ; i < timeout / interval; i++ ) {
347382 script. sh(" sleep ${ interval} s" )
348- String deploys = kubectl(" get deployments --template '{{range .items}}{{.metadata.name}}{{\"\\ n\" }}{{end}}'" , true )
349- if (! deploys. contains(" k8s-ces-setup" )) {
383+ String blueprintReady = kubectl(" get blueprint -n=default blueprint-ces-module -o jsonpath='{.status.conditions[?(@.type==\" EcosystemHealthy\" )].status}{\" \" }{.status.conditions[?(@.type==\" Completed\" )].status}'" , true )
384+ script. echo blueprintReady
385+ if (blueprintReady == " True True" ) {
350386 return
351387 }
352388 }
353389
354- this . script. error " failed to wait for setup to finish: timeout"
390+ this . script. error " failed to wait for ecosystem-core setup to finish: timeout"
355391 }
356392
357393/**
358- * Installs the setup to the cluster. Creates an example setup.json with usermgt as dogu and executes the setup.
359- * After that the method will wait until the dogu-operator is ready.
360- * @param tag Tag of the setup e. g. "v0.6.0"
361- * @param timout Timeout in seconds for the setup process e. g. 300
394+ * Installs the ecosystem-core-setup to the cluster. Creates an example values.yaml and a blueprint-file with usermgt as dogu and executes the ecosystem-core-setup.
395+ * After that the method will wait until the blueprint is ready.
396+ * @param timout Timeout in seconds for the installation process e. g. 300
362397 * @param interval Interval in seconds for querying the actual state of the setup e. g. 2
363398 */
364- void setup (String tag , config = [:], Integer timout = 300 , Integer interval = 5 ) {
399+ void setup (config = [:], Integer timout = 300 , Integer interval = 5 ) {
365400 assignExternalIP()
366- configureSetupJson (config)
367- installAndTriggerSetup(tag, timout, interval)
401+ configureEcosystemCoreValues (config)
402+ installAndTriggerSetup(timout, interval)
368403 }
369404
370405
@@ -606,66 +641,94 @@ data:
606641 return [registryIp, registryPort]
607642 }
608643
609- static String formatDependencies (List<String > deps ) {
644+ String formatDependencies (List<String > deps ) {
610645 String formatted = " "
611-
612646 for (int i = 0 ; i < deps. size(); i++ ) {
613- formatted + = " \" ${ deps[i]} \" "
614-
647+ String [] parts = deps[i]. split(" :" )
648+ String version;
649+ // "latest" needs to be replaced with actual last version
650+ if (parts. length != 2 || parts[1 ] == " latest" ) {
651+ version = this . getLatestVersion(parts[0 ])
652+ } else {
653+ version = parts[1 ]
654+ }
655+ formatted + = " - name: ${ parts[0]} \n " +
656+ " version: ${ version} "
615657 if ((i + 1 ) < deps. size()) {
616- formatted + = ' , '
658+ formatted + = ' \n '
617659 }
618660 }
619661
620662 return formatted
621663 }
622664
623- private void writeSetupJson (config ) {
624- List<String > deps = config. dependencies + config. additionalDependencies
625- String formattedDeps = formatDependencies(deps)
665+ private String getLatestVersion (String doguName ) {
666+ String tags = " {}" ;
667+ script. withCredentials([[$class : ' UsernamePasswordMultiBinding' , credentialsId : this . backendCredentialsID, usernameVariable : ' TOKEN_ID' , passwordVariable : ' TOKEN_SECRET' ]]) {
668+ tags = this . sh. returnStdOut(" curl https://registry.cloudogu.com/v2/${ doguName} /tags/list -u ${ script.env.TOKEN_ID} :${ script.env.TOKEN_SECRET} " ). trim()
669+ }
670+ def obj = new JsonSlurper (). parseText(tags)
671+ return obj. tags. max { t -> parseTag(" ${ t} " ) }
672+ }
673+
674+ private String parseTag (String tag ) {
675+ def m = (tag =~ / ^(\d +)(?:\. (\d +))?(?:\. (\d +))?(?:-(\d +))?$/ )
676+ if (! m. matches()) {
677+ // Fallback: set all to 0 to ingnore invalid tags
678+ return " 00000.00000.00000.00000"
679+ }
680+ def major = (m[0 ][1 ] ?: " 0" ) as int
681+ def minor = (m[0 ][2 ] ?: " 0" ) as int
682+ def patch = (m[0 ][3 ] ?: " 0" ) as int
683+ def build = (m[0 ][4 ] ?: " 0" ) as int
626684
627- script. writeFile file : K3D_SETUP_JSON_FILE , text : """
628- {
629- "naming":{
630- "fqdn":"${ externalIP} ",
631- "hostname":"ces",
632- "domain":"ces.local",
633- "certificateType":"selfsigned",
634- "relayHost":"mail.ces.local",
635- "completed":true
636- },
637- "dogus":{
638- "defaultDogu":"${ config.defaultDogu} ",
639- "install":[
640- ${ formattedDeps}
641- ],
642- "completed":true
643- },
644- "admin":{
645- "username":"${ config.adminUsername} ",
646- "mail":"ces-admin@cloudogu.com",
647- "password":"${ config.adminPassword} ",
648- "adminGroup":"${ config.adminGroup} ",
649- "adminMember":true,
650- "completed":true
651- },
652- "userBackend":{
653- "port":"389",
654- "useUserConnectionToFetchAttributes":true,
655- "dsType":"embedded",
656- "attributeID":"uid",
657- "attributeFullname":"cn",
658- "attributeMail":"mail",
659- "attributeGroup":"memberOf",
660- "searchFilter":"(objectClass=person)",
661- "host":"ldap",
662- "completed":true
663- },
664- "registryConfig": {${ config.registryConfig} },
665- "registryConfigEncrypted": {${ config.registryConfigEncrypted} }
666- }"""
685+ // Zero-padding → lexicographically sortable
686+ return sprintf (" %05d.%05d.%05d.%05d" , major, minor, patch, build)
667687 }
668688
689+ private void writeBlueprintYaml (config ) {
690+ List<String > deps = config. dependencies + config. additionalDependencies
691+ String formattedDeps = formatDependencies(deps)
692+ script. writeFile file : K3D_BLUEPRINT_FILE , text : """
693+ apiVersion: k8s.cloudogu.com/v3
694+ kind: Blueprint
695+ metadata:
696+ labels:
697+ app: ces
698+ app.kubernetes.io/name: k8s-blueprint-lib
699+ name: blueprint-ces-module
700+ namespace: default
701+ spec:
702+ displayName: "Blueprint K3D CES-Module"
703+ blueprint:
704+ dogus:
705+ ${ formattedDeps}
706+ config:
707+ dogus:
708+ ldap:
709+ - key: admin_username
710+ value: "${ config.adminUsername} "
711+ - key: admin_mail
712+ value: "ces-admin@cloudogu.com"
713+ - key: admin_member
714+ value: "true"
715+ - key: admin_password
716+ value: "${ config.adminPassword} "
717+ global:
718+ - key: fqdn
719+ value: "${ externalIP} "
720+ - key: domain
721+ value: "ces.local"
722+ - key: certificate/type
723+ value: "selfsigned"
724+ - key: k8s/use_internal_ip
725+ value: "false"
726+ - key: internalIp
727+ value: ""
728+ - key: admin_group
729+ value: "${ config.adminGroup} "
730+ """
731+ }
669732
670733/**
671734 * Collects all necessary resources and log information used to identify problems with our kubernetes cluster.
@@ -677,7 +740,9 @@ data:
677740 script. deleteDir()
678741 }
679742 script. sh(" rm -rf ${ K3D_LOG_FILENAME} .zip" . toString())
680- script. sh(" rm -rf ${ K3D_SETUP_JSON_FILE} " . toString())
743+ script. archiveArtifacts(artifacts : K3D_BLUEPRINT_FILE )
744+ script. sh(" rm -rf ${ K3D_BLUEPRINT_FILE} " . toString())
745+ script. archiveArtifacts(artifacts : K3D_VALUES_YAML_FILE )
681746 script. sh(" rm -rf ${ K3D_VALUES_YAML_FILE} " . toString())
682747
683748 collectResourcesSummaries()
0 commit comments