Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 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
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
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ 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.3.0](https://github.com/cloudogu/ces-build-lib/releases/tag/4.3.0) - 2025-08-21
### Changed
- Updates the BATS shell test image to 1.12 which supports the `--report-formatter` switch
Expand Down Expand Up @@ -256,6 +259,4 @@ the login data used for the dogu-registry; #75
- Add gpg class to perform gpp based task such as signing; #64
- Add option to upload artifacts to a GitHub release; #64

## v0.0.1 - v1.47.1 / previous versions

Up till version v1.47.1 there was no change log
## v0.0.1 - v1.47.1 /
215 changes: 148 additions & 67 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,10 +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"

private static String VERSION_ECOSYSTEM_CORE = "1.2.0"
private static String VERSION_K8s_COMPONENT_OPERATOR_CRD = "1.10.1"
private static String VERSION_K8S_DOGU_OPERATOR = "3.15.0"
private static String VERSION_K8S_DOGU_OPERATOR_CRD = "2.10.0"
private static String VERSION_K8S_BLUEPRINT_OPERATOR = "3.0.2"
private static String VERSION_K8S_BLUEPRINT_OPERATOR_CRD = "3.1.0"

private String clusterName
private script
private String path
Expand All @@ -36,14 +44,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,15 +262,53 @@ class K3d {
}
}

void configureSetupJson(config = [:]) {
String setupJsonConfigKey = ".setup_json"
/**
* override component versions
*/
static void setComponentVersions(String dogu_op = VERSION_K8S_DOGU_OPERATOR, String dogu_op_crd = VERSION_K8S_DOGU_OPERATOR_CRD, String blue_op = VERSION_K8S_BLUEPRINT_OPERATOR, String blue_op_crd = VERSION_K8S_BLUEPRINT_OPERATOR_CRD) {
if (dogu_op != null) {
VERSION_K8S_DOGU_OPERATOR = dogu_op
}
if (dogu_op_crd != null) {
VERSION_K8S_DOGU_OPERATOR_CRD = dogu_op_crd
}
if (blue_op != null) {
VERSION_K8S_BLUEPRINT_OPERATOR = blue_op
}
if (blue_op_crd != null) {
VERSION_K8S_BLUEPRINT_OPERATOR_CRD = blue_op_crd
}
}

script.echo "configuring setup..."
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")
appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-dogu-operator-crd.version", VERSION_K8S_DOGU_OPERATOR_CRD)
appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-dogu-operator.version", VERSION_K8S_DOGU_OPERATOR)

appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-blueprint-operator-crd.version", VERSION_K8S_BLUEPRINT_OPERATOR_CRD)
appendToYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-blueprint-operator.version", VERSION_K8S_BLUEPRINT_OPERATOR)

yqEvalYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-ces-control.disabled = true")

yqEvalYamlFile(K3D_VALUES_YAML_FILE, ".components.k8s-blueprint-operator.valuesObject.healthConfig.components.required = [{\\\"name\\\": \\\"k8s-dogu-operator\\\"}, {\\\"name\\\": \\\"k8s-service-discovery\\\"}]")


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)
}

@Deprecated
void configureSetupJson(config = [:]) {
configureEcosystemCoreValues(config)
}

void configureSetupImage(String image) {
Expand Down Expand Up @@ -320,22 +366,30 @@ class K3d {
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
helm("install k8s-component-operator-crd oci://${registryUrl}/${registryNamespace}/k8s-component-operator-crd --version ${VERSION_K8s_COMPONENT_OPERATOR_CRD} --namespace default")

script.echo "Wait for dogu-operator to be ready..."
waitForDeploymentRollout("k8s-dogu-operator-controller-manager", timeout, interval)
kubectl("--namespace default create configmap global-config --from-literal=config.yaml='fqdn: ${externalIP}'")

helm("install -f ${K3D_VALUES_YAML_FILE} ecosystem-core oci://${registryUrl}/${registryNamespace}/ecosystem-core --version ${VERSION_ECOSYSTEM_CORE} --namespace default --timeout 15m")

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 setup-finisher to be executed..."
waitForSetupToFinish(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)
Expand All @@ -345,13 +399,14 @@ class K3d {
void waitForSetupToFinish(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"
}

/**
Expand All @@ -363,7 +418,7 @@ class K3d {
*/
void setup(String tag, config = [:], Integer timout = 300, Integer interval = 5) {
assignExternalIP()
configureSetupJson(config)
configureEcosystemCoreValues(config)
installAndTriggerSetup(tag, timout, interval)
}

Expand Down Expand Up @@ -606,66 +661,90 @@ 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") {
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/${parts[0]}/tags/list -u ${script.env.TOKEN_ID}:${script.env.TOKEN_SECRET}").trim()
}
def obj = new JsonSlurper().parseText(tags)
version = obj.tags.max { t -> parseTag("${t}") }
} 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 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 +756,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