Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
f3584d7
[151] first test with custom setup
Oct 30, 2025
a8bb857
[151] first test with custom setup
Oct 30, 2025
47b4a13
[151] first test with custom setup
Oct 30, 2025
6ef7004
[151] first test with custom setup
Oct 30, 2025
0655e98
[151] first test with custom setup
Oct 30, 2025
11dcafe
[151] first test with custom setup
Oct 30, 2025
b204a5d
[151] first test with custom setup
Oct 30, 2025
50c2bd4
[151] first test with custom setup
Oct 30, 2025
d141954
[151] first test with custom setup
Oct 30, 2025
e857b6b
[151] first test with custom setup
Nov 3, 2025
8dc3d66
[151] first test with custom setup
Nov 3, 2025
09e6e95
[151] first test with custom setup
Nov 3, 2025
366507e
[151] first test with custom setup
Nov 3, 2025
f9d7c46
[151] first test with custom setup
Nov 3, 2025
43493de
[151] first test with custom setup
Nov 3, 2025
f5d3c9e
[151] first test with custom setup
Nov 3, 2025
20c5bef
[151] first test with custom setup
Nov 3, 2025
ead0373
[151] first test with custom setup
Nov 3, 2025
c8c834e
[151] first test with custom setup
Nov 3, 2025
5e8057b
[151] first test with custom setup
Nov 3, 2025
94b3169
[151] first test with custom setup
Nov 3, 2025
90ff380
[151] first test with custom setup
Nov 3, 2025
ae8c148
[151] first test with custom setup
Nov 3, 2025
851dc59
[151] first test with custom setup
Nov 3, 2025
85466bc
[151] first test with custom setup
Nov 3, 2025
a5f81f1
[151] first test with custom setup
Nov 3, 2025
a014a2b
[151] first test with custom setup
Nov 3, 2025
df4331b
[151] first test with custom setup
Nov 3, 2025
6418d0e
[151] first test with custom setup
Nov 3, 2025
2165081
[151] first test with custom setup
Nov 3, 2025
8650c7c
[151] first test with custom setup
Nov 3, 2025
d888cef
[151] first test with custom setup
Nov 3, 2025
c7c3e52
[151] first test with custom setup
Nov 3, 2025
f78d8d1
[151] use yq instead of unsecure jq image
Nov 3, 2025
08f4a28
[151] use yq instead of unsecure jq image
Nov 3, 2025
8a0cd1c
[151] use yq instead of unsecure jq image
Nov 3, 2025
2ced7bc
[151] use yq instead of unsecure jq image
Nov 3, 2025
9da79fb
[151] use yq instead of unsecure jq image
Nov 3, 2025
5ab7d19
[151] use yq instead of unsecure jq image
Nov 3, 2025
18ca15b
[151] use yq instead of unsecure jq image
Nov 3, 2025
e2ee1fe
[151] use yq instead of unsecure jq image
Nov 3, 2025
fdc9e19
[151] use yq instead of unsecure jq image
Nov 3, 2025
de05272
[151] use yq instead of unsecure jq image
Nov 3, 2025
be19961
[151] use yq instead of unsecure jq image
Nov 3, 2025
d2d9810
[151] use yq instead of unsecure jq image
Nov 3, 2025
14ad169
[151] use yq instead of unsecure jq image
Nov 3, 2025
4cec2df
[151] use yq instead of unsecure jq image
Nov 3, 2025
85484cd
[151] use yq instead of unsecure jq image
Nov 3, 2025
b840fca
[151] use yq instead of unsecure jq image
Nov 4, 2025
c2e250f
[151] use yq instead of unsecure jq image
Nov 4, 2025
e03c089
[151] use yq instead of unsecure jq image
Nov 4, 2025
8c5c6a4
[151] use yq instead of unsecure jq image
Nov 4, 2025
7f728b3
[151] use yq instead of unsecure jq image
Nov 4, 2025
c5359a3
[151] use yq instead of unsecure jq image
Nov 4, 2025
ac9133c
[151] use yq instead of unsecure jq image
Nov 4, 2025
9c7abf8
[151] use yq instead of unsecure jq image
Nov 4, 2025
159cfa7
[151] use yq instead of unsecure jq image
Nov 4, 2025
4498d55
[151] cleanup
Nov 4, 2025
fbaad5a
[151] cleanup
Nov 4, 2025
a6bda43
[151] cleanup
Nov 4, 2025
9299dc9
[151] cleanup
Nov 4, 2025
54e447f
[151] adapt unit-tests to fit new ecosystem-core workflow
Nov 6, 2025
095c595
[151] add unittest for parse tags
Nov 7, 2025
88f3c8c
[151] add unittest for parse tags
Nov 7, 2025
1a63c11
[151] add unittest for parse tags
Nov 10, 2025
44862aa
[151] add unittest for parse tags
Nov 10, 2025
e055463
[151] bump version of ecosystem core
Nov 17, 2025
6c129fe
[151] add changelog
Nov 19, 2025
704e13e
[151] use 1.2.0 of ecosystem-core
Nov 20, 2025
adeaa4e
[151] make monitoring and backup optional
Nov 20, 2025
88b8d86
[151] blueprint v3
Nov 20, 2025
f6477b3
[151] cleanup k3d, remove obsolete config methodes due to ecosystem-core
Dec 1, 2025
5177860
[151] test without patching coredns
Dec 1, 2025
db3112b
[151] revert unpatch
Dec 1, 2025
1cdbae4
[151] remove obsolte tests
Dec 2, 2025
d6cc8f7
[151] use defaults
Dec 4, 2025
3cefcaa
[151] use special functions for every version
Dec 4, 2025
a1d2680
[151] replace component version tests
Dec 4, 2025
0dcf671
[151] fixed unit tests
Dec 4, 2025
468540a
#151 small fix for blueprint-operator-version
kahoona77 Dec 5, 2025
0dede76
[151] remove obsolet parameter "tag"
Dec 8, 2025
d97846a
Merge remote-tracking branch 'origin/develop' into feature/151-use-ec…
Dec 8, 2025
8e44baf
[151] fixed tests
Dec 8, 2025
d5d3274
[151] rename wait for blueprint
Dec 8, 2025
61e3da3
[151] fixed tests
Dec 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0


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

## [4.4.0](https://github.com/cloudogu/ces-build-lib/releases/tag/4.4.0) - 2025-08-21
### Changed
Expand Down
267 changes: 166 additions & 101 deletions src/com/cloudogu/ces/cesbuildlib/K3d.groovy
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.cloudogu.ces.cesbuildlib

import com.cloudbees.groovy.cps.NonCPS
import groovy.json.JsonSlurper

class K3d {
/**
Expand All @@ -12,9 +13,17 @@ class K3d {
*/
private static String K3D_VERSION = "5.6.0"
private static String K3D_LOG_FILENAME = "k8sLogs"
private static String K3D_SETUP_JSON_FILE = "k3d_setup.json"
private static String K3D_VALUES_YAML_FILE = "k3d_values.yaml"
private static String K3D_BLUEPRINT_FILE = "k3d_blueprint.yaml"
private static String YQ_VERSION = "4.40.5"
// need to be installed before apply values.yaml
private static String VERSION_ECOSYSTEM_CORE; // e.g. "1.2.0"
private static String VERSION_K8S_COMPONENT_OPERATOR_CRD; // e.g. "1.10.1"
// configured by values.yaml
private static String VERSION_K8S_DOGU_OPERATOR; // e.g. "3.15.0"
private static String VERSION_K8S_DOGU_OPERATOR_CRD; // e.g. "2.10.0"
private static String VERSION_K8S_BLUEPRINT_OPERATOR; // e.g. "3.0.2"
private static String VERSION_K8S_BLUEPRINT_OPERATOR_CRD ; // e.g. "3.1.0"

private String clusterName
private script
Expand All @@ -36,14 +45,14 @@ class K3d {
adminGroup : "CesAdministrators",
dependencies : ["official/ldap",
"official/cas",
"k8s/nginx-ingress",
"k8s/nginx-static",
"official/postfix",
"official/usermgt"],
defaultDogu : "",
additionalDependencies : [],
registryConfig : "",
registryConfigEncrypted: ""
registryConfigEncrypted: "",
"enableBackup" : false,
"enableMonitoring" : false
]

String getRegistryName() {
Expand Down Expand Up @@ -254,40 +263,60 @@ class K3d {
}
}

void configureSetupJson(config = [:]) {
String setupJsonConfigKey = ".setup_json"

script.echo "configuring setup..."
static void setVersionEcosystemCore(String v) {
VERSION_ECOSYSTEM_CORE = v;
}
static void setVersionComponentOperatorCrd(String v) {
VERSION_K8S_COMPONENT_OPERATOR_CRD = v;
}
static void setVersionDoguOperator(String v) {
VERSION_K8S_DOGU_OPERATOR = v;
}
static void setVersionDoguOperatorCrd(String v) {
VERSION_K8S_DOGU_OPERATOR_CRD = v;
}
static void setVersionBlueprintOperator(String v) {
VERSION_K8S_BLUEPRINT_OPERATOR = v;
}
static void setVersionBlueprintOperatorCrd(String v) {
VERSION_K8S_BLUEPRINT_OPERATOR_CRD = v;
}

void configureEcosystemCoreValues(config = [:]) {
// Merge default config with the one passed as parameter
config = defaultSetupConfig << config
writeSetupJson(config)

appendFileToYamlFile(K3D_VALUES_YAML_FILE, setupJsonConfigKey, K3D_SETUP_JSON_FILE)
}
yqEvalYamlFile(K3D_VALUES_YAML_FILE, ".defaultConfig.env.waitTimeoutMinutes = 5")

void configureSetupImage(String image) {
String hostKey = ".setup.image.registry"
String repositoryKey = ".setup.image.repository"
String tagKey = ".setup.image.tag"
def repositorySeparatorIndex = image.indexOf("/")
def tagSeparatorIndex = image.lastIndexOf(":")
if (VERSION_K8S_DOGU_OPERATOR_CRD != null) {
appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-dogu-operator-crd.version", VERSION_K8S_DOGU_OPERATOR_CRD)
}
if (VERSION_K8S_DOGU_OPERATOR != null) {
appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-dogu-operator.version", VERSION_K8S_DOGU_OPERATOR)
}
if (VERSION_K8S_BLUEPRINT_OPERATOR_CRD != null) {
appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-blueprint-operator-crd.version", VERSION_K8S_BLUEPRINT_OPERATOR_CRD)
}
if (VERSION_K8S_BLUEPRINT_OPERATOR != null) {
appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-blueprint-operator.version", VERSION_K8S_BLUEPRINT_OPERATOR)
}

appendToYamlFile(K3D_VALUES_YAML_FILE, hostKey, image.substring(0, repositorySeparatorIndex))
appendToYamlFile(K3D_VALUES_YAML_FILE, repositoryKey, image.substring(repositorySeparatorIndex + 1, tagSeparatorIndex))
appendToYamlFile(K3D_VALUES_YAML_FILE, tagKey, image.substring(tagSeparatorIndex + 1, image.length()))
}
yqEvalYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-ces-control.disabled = true")

void configureComponentOperatorVersion(String operatorVersion, String crdVersion = operatorVersion, String namespace = "k8s") {
String componentOpKey = ".component_operator_chart"
String componentCRDKey = ".component_operator_crd_chart"
appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-service-discovery.valuesObject.loadBalancerService.internalTrafficPolicy", "Cluster")
appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-service-discovery.valuesObject.loadBalancerService.externalTrafficPolicy", "Cluster")

yqEvalYamlFile(K3D_VALUES_YAML_FILE, ".backup.enabled = ${config.enableBackup}")
yqEvalYamlFile(K3D_VALUES_YAML_FILE, ".monitoring.enabled = ${config.enableMonitoring}")

script.echo "configuring ecosystem core..."
writeBlueprintYaml(config)
}

def builder = new StringBuilder(namespace)
String operatorValue = builder.append("/k8s-component-operator:").append(operatorVersion).toString()
appendToYamlFile(K3D_VALUES_YAML_FILE, componentOpKey, operatorValue)
builder.delete(0, builder.length());
String crdValue = builder.append(namespace).append("/k8s-component-operator-crd:").append(crdVersion).toString()
appendToYamlFile(K3D_VALUES_YAML_FILE, componentCRDKey, crdValue)
@Deprecated
void configureSetupJson(config = [:]) {
configureEcosystemCoreValues(config)
}

void configureComponents(components = [:]) {
Expand All @@ -308,63 +337,69 @@ class K3d {
}
}

void configureLogLevel(String loglevel) {
appendToYamlFile(K3D_VALUES_YAML_FILE, ".logLevel", loglevel)
}

void installAndTriggerSetup(String tag, Integer timeout = 300, Integer interval = 5) {
void installAndTriggerSetup(Integer timeout = 300, Integer interval = 5) {
script.echo "Installing setup..."
String registryUrl = "registry.cloudogu.com"
String registryNamespace = "k8s"
script.withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'harborhelmchartpush', usernameVariable: 'HARBOR_USERNAME', passwordVariable: 'HARBOR_PASSWORD']]) {
helm("registry login ${registryUrl} --username '${script.env.HARBOR_USERNAME}' --password '${script.env.HARBOR_PASSWORD}'")
}

helm("install -f ${K3D_VALUES_YAML_FILE} k8s-ces-setup oci://${registryUrl}/${registryNamespace}/k8s-ces-setup --version ${tag} --namespace default")
helm("registry logout ${registryUrl}")
// install crd first
String comp_crd_version = VERSION_K8S_COMPONENT_OPERATOR_CRD == null ? "" : " --version ${VERSION_K8S_COMPONENT_OPERATOR_CRD}"
helm("install k8s-component-operator-crd oci://${registryUrl}/${registryNamespace}/k8s-component-operator-crd ${comp_crd_version} --namespace default")

kubectl("--namespace default create configmap global-config --from-literal=config.yaml='fqdn: ${externalIP}'")

script.echo "Wait for dogu-operator to be ready..."
waitForDeploymentRollout("k8s-dogu-operator-controller-manager", timeout, interval)
String eco_core_version = VERSION_ECOSYSTEM_CORE == null ? "" : " --version ${VERSION_ECOSYSTEM_CORE}"
helm("install -f ${K3D_VALUES_YAML_FILE} ecosystem-core oci://${registryUrl}/${registryNamespace}/ecosystem-core ${eco_core_version} --namespace default --timeout 15m")

script.echo "Wait for setup-finisher to be executed..."
waitForSetupToFinish(timeout, interval)
script.echo "Wait for blueprint-operator to be ready..."
waitForDeploymentRollout("k8s-blueprint-operator-controller-manager", timeout, interval)

kubectl("apply -f ${K3D_BLUEPRINT_FILE} --namespace default")

script.echo "Wait for blueprint to be ready..."
waitForBlueprintToBeReady(timeout, interval)

script.echo "Wait for dogus to be ready..."
waitForDogusToBeRolledOut(timeout, interval)

helm("registry logout ${registryUrl}")
}

void waitForDogusToBeRolledOut(Integer timeout, Integer interval) {
String dogus = kubectl("get dogus --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'", true)
String[] doguList = dogus.split("\n")
String[] doguList = dogus.trim().split("\n")
for (String dogu : doguList) {
script.echo "Wait for $dogu to be rolled out..."
waitForDeploymentRollout(dogu, timeout, interval)
}
}

void waitForSetupToFinish(Integer timeout, Integer interval) {
void waitForBlueprintToBeReady(Integer timeout, Integer interval) {
for (int i = 0; i < timeout / interval; i++) {
script.sh("sleep ${interval}s")
String deploys = kubectl("get deployments --template '{{range .items}}{{.metadata.name}}{{\"\\n\"}}{{end}}'", true)
if (!deploys.contains("k8s-ces-setup")) {
String blueprintReady = kubectl("get blueprint -n=default blueprint-ces-module -o jsonpath='{.status.conditions[?(@.type==\"EcosystemHealthy\")].status}{\" \"}{.status.conditions[?(@.type==\"Completed\")].status}'", true)
script.echo blueprintReady
if (blueprintReady == "True True") {
return
}
}

this.script.error "failed to wait for setup to finish: timeout"
this.script.error "failed to wait for ecosystem-core setup to finish: timeout"
}

/**
* Installs the setup to the cluster. Creates an example setup.json with usermgt 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
* 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.
* After that the method will wait until the blueprint is ready.
* @param timout Timeout in seconds for the installation process e. g. 300
* @param interval Interval in seconds for querying the actual state of the setup e. g. 2
*/
void setup(String tag, config = [:], Integer timout = 300, Integer interval = 5) {
void setup(config = [:], Integer timout = 300, Integer interval = 5) {
assignExternalIP()
configureSetupJson(config)
installAndTriggerSetup(tag, timout, interval)
configureEcosystemCoreValues(config)
installAndTriggerSetup(timout, interval)
}


Expand Down Expand Up @@ -606,66 +641,94 @@ data:
return [registryIp, registryPort]
}

static String formatDependencies(List<String> deps) {
String formatDependencies(List<String> deps) {
String formatted = ""

for (int i = 0; i < deps.size(); i++) {
formatted += "\"${deps[i]}\""

String[] parts = deps[i].split(":")
String version;
// "latest" needs to be replaced with actual last version
if (parts.length != 2 || parts[1] == "latest") {
version = this.getLatestVersion(parts[0])
} else {
version = parts[1]
}
formatted += " - name: ${parts[0]}\n" +
" version: ${version}"
if ((i + 1) < deps.size()) {
formatted += ', '
formatted += '\n'
}
}

return formatted
}

private void writeSetupJson(config) {
List<String> deps = config.dependencies + config.additionalDependencies
String formattedDeps = formatDependencies(deps)
private String getLatestVersion(String doguName) {
String tags = "{}";
script.withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: this.backendCredentialsID, usernameVariable: 'TOKEN_ID', passwordVariable: 'TOKEN_SECRET']]) {
tags = this.sh.returnStdOut("curl https://registry.cloudogu.com/v2/${doguName}/tags/list -u ${script.env.TOKEN_ID}:${script.env.TOKEN_SECRET}").trim()
}
def obj = new JsonSlurper().parseText(tags)
return obj.tags.max { t -> parseTag("${t}") }
}

private String parseTag(String tag) {
def m = (tag =~ /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-(\d+))?$/)
if (!m.matches()) {
// Fallback: set all to 0 to ingnore invalid tags
return "00000.00000.00000.00000"
}
def major = (m[0][1] ?: "0") as int
def minor = (m[0][2] ?: "0") as int
def patch = (m[0][3] ?: "0") as int
def build = (m[0][4] ?: "0") as int

script.writeFile file: K3D_SETUP_JSON_FILE, text: """
{
"naming":{
"fqdn":"${externalIP}",
"hostname":"ces",
"domain":"ces.local",
"certificateType":"selfsigned",
"relayHost":"mail.ces.local",
"completed":true
},
"dogus":{
"defaultDogu":"${config.defaultDogu}",
"install":[
${formattedDeps}
],
"completed":true
},
"admin":{
"username":"${config.adminUsername}",
"mail":"ces-admin@cloudogu.com",
"password":"${config.adminPassword}",
"adminGroup":"${config.adminGroup}",
"adminMember":true,
"completed":true
},
"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}}
}"""
// Zero-padding → lexicographically sortable
return sprintf("%05d.%05d.%05d.%05d", major, minor, patch, build)
}

private void writeBlueprintYaml(config) {
List<String> deps = config.dependencies + config.additionalDependencies
String formattedDeps = formatDependencies(deps)
script.writeFile file: K3D_BLUEPRINT_FILE, text: """
apiVersion: k8s.cloudogu.com/v3
kind: Blueprint
metadata:
labels:
app: ces
app.kubernetes.io/name: k8s-blueprint-lib
name: blueprint-ces-module
namespace: default
spec:
displayName: "Blueprint K3D CES-Module"
blueprint:
dogus:
${formattedDeps}
config:
dogus:
ldap:
- key: admin_username
value: "${config.adminUsername}"
- key: admin_mail
value: "ces-admin@cloudogu.com"
- key: admin_member
value: "true"
- key: admin_password
value: "${config.adminPassword}"
global:
- key: fqdn
value: "${externalIP}"
- key: domain
value: "ces.local"
- key: certificate/type
value: "selfsigned"
- key: k8s/use_internal_ip
value: "false"
- key: internalIp
value: ""
- key: admin_group
value: "${config.adminGroup}"
"""
}

/**
* Collects all necessary resources and log information used to identify problems with our kubernetes cluster.
Expand All @@ -677,7 +740,9 @@ data:
script.deleteDir()
}
script.sh("rm -rf ${K3D_LOG_FILENAME}.zip".toString())
script.sh("rm -rf ${K3D_SETUP_JSON_FILE}".toString())
script.archiveArtifacts(artifacts: K3D_BLUEPRINT_FILE)
script.sh("rm -rf ${K3D_BLUEPRINT_FILE}".toString())
script.archiveArtifacts(artifacts: K3D_VALUES_YAML_FILE)
script.sh("rm -rf ${K3D_VALUES_YAML_FILE}".toString())

collectResourcesSummaries()
Expand Down
Loading