Skip to content

Commit f8536bf

Browse files
authored
Merge pull request #152 from cloudogu/feature/151-use-ecosystem-core
Feature/151 use ecosystem core
2 parents 5bcdd24 + 61e3da3 commit f8536bf

File tree

3 files changed

+254
-193
lines changed

3 files changed

+254
-193
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88

99
## [Unreleased]
10+
- use ecosystem-core instead of k8e-ces-setup to install cluster
1011

1112
## [4.4.0](https://github.com/cloudogu/ces-build-lib/releases/tag/4.4.0) - 2025-08-21
1213
### Changed

src/com/cloudogu/ces/cesbuildlib/K3d.groovy

Lines changed: 166 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.cloudogu.ces.cesbuildlib
22

33
import com.cloudbees.groovy.cps.NonCPS
4+
import groovy.json.JsonSlurper
45

56
class 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

Comments
 (0)