[kubernetes] User-selectable cluster version (#1191)

## What this PR does

This patch adds a new version field to the kubernetes chart, letting
end-users specify the version of kubernetes they want to deploy.

### Release note

```release-note
[kubernetes] Let users specify desired version of tenant k8s cluster.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a configurable Kubernetes version parameter, allowing selection
of specific minor versions for cluster deployments.
* Introduced a version mapping system to ensure clusters use precise
Kubernetes patch versions.
* **Bug Fixes**
* Ensured only supported Kubernetes versions can be selected, reducing
configuration errors.
* **Documentation**
* Updated documentation to describe the new version parameter and its
usage.
* **Tests**
* Enhanced end-to-end tests to cover deployments with both the latest
and previous Kubernetes versions.
* **Chores**
* Consolidated version references for multiple packages to streamline
version management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Timofei Larkin
2025-07-16 22:12:00 +04:00
committed by GitHub
10 changed files with 88 additions and 18 deletions

View File

@@ -1,11 +1,16 @@
#!/usr/bin/env bats
@test "Create a tenant Kubernetes control plane" {
run_kubernetes_test() {
local version_expr="$1"
local test_name="$2"
local port="$3"
local k8s_version=$(yq "$version_expr" packages/apps/kubernetes/files/versions.yaml)
kubectl apply -f - <<EOF
apiVersion: apps.cozystack.io/v1alpha1
kind: Kubernetes
metadata:
name: test
name: "${test_name}"
namespace: tenant-test
spec:
addons:
@@ -60,13 +65,49 @@ spec:
roles:
- ingress-nginx
storageClass: replicated
version: "${k8s_version}"
EOF
# Wait for the tenant-test namespace to be active
kubectl wait namespace tenant-test --timeout=20s --for=jsonpath='{.status.phase}'=Active
timeout 10 sh -ec 'until kubectl get kamajicontrolplane -n tenant-test kubernetes-test; do sleep 1; done'
kubectl wait --for=condition=TenantControlPlaneCreated kamajicontrolplane -n tenant-test kubernetes-test --timeout=4m
kubectl wait tcp -n tenant-test kubernetes-test --timeout=2m --for=jsonpath='{.status.kubernetesResources.version.status}'=Ready
kubectl wait deploy --timeout=4m --for=condition=available -n tenant-test kubernetes-test kubernetes-test-cluster-autoscaler kubernetes-test-kccm kubernetes-test-kcsi-controller
kubectl wait machinedeployment kubernetes-test-md0 -n tenant-test --timeout=1m --for=jsonpath='{.status.replicas}'=2
kubectl wait machinedeployment kubernetes-test-md0 -n tenant-test --timeout=10m --for=jsonpath='{.status.v1beta2.readyReplicas}'=2
kubectl -n tenant-test delete kuberneteses.apps.cozystack.io test
# Wait for the Kamaji control plane to be created (retry for up to 10 seconds)
timeout 10 sh -ec 'until kubectl get kamajicontrolplane -n tenant-test kubernetes-'"${test_name}"'; do sleep 1; done'
# Wait for the tenant control plane to be fully created (timeout after 4 minutes)
kubectl wait --for=condition=TenantControlPlaneCreated kamajicontrolplane -n tenant-test kubernetes-${test_name} --timeout=4m
# Wait for Kubernetes resources to be ready (timeout after 2 minutes)
kubectl wait tcp -n tenant-test kubernetes-${test_name} --timeout=2m --for=jsonpath='{.status.kubernetesResources.version.status}'=Ready
# Wait for all required deployments to be available (timeout after 4 minutes)
kubectl wait deploy --timeout=4m --for=condition=available -n tenant-test kubernetes-${test_name} kubernetes-${test_name}-cluster-autoscaler kubernetes-${test_name}-kccm kubernetes-${test_name}-kcsi-controller
# Wait for the machine deployment to scale to 2 replicas (timeout after 1 minute)
kubectl wait machinedeployment kubernetes-${test_name}-md0 -n tenant-test --timeout=1m --for=jsonpath='{.status.replicas}'=2
# Get the admin kubeconfig and save it to a file
kubectl get secret kubernetes-${test_name}-admin-kubeconfig -ojsonpath='{.data.super-admin\.conf}' -n tenant-test | base64 -d > tenantkubeconfig
# Update the kubeconfig to use localhost for the API server
yq -i ".clusters[0].cluster.server = \"https://localhost:${port}\"" tenantkubeconfig
# Set up port forwarding to the Kubernetes API server for a 40 second timeout
bash -c 'timeout 40s kubectl port-forward service/kubernetes-'"${test_name}"' -n tenant-test '"${port}"':6443 > /dev/null 2>&1 &'
# Verify the Kubernetes version matches what we expect (retry for up to 20 seconds)
timeout 20 sh -ec 'until kubectl --kubeconfig tenantkubeconfig version 2>/dev/null | grep -Fq "Server Version: ${k8s_version}"; do sleep 5; done'
# Wait for all machine deployment replicas to be ready (timeout after 10 minutes)
kubectl wait machinedeployment kubernetes-${test_name}-md0 -n tenant-test --timeout=10m --for=jsonpath='{.status.v1beta2.readyReplicas}'=2
# Clean up by deleting the Kubernetes resource
kubectl -n tenant-test delete kuberneteses.apps.cozystack.io $test_name
}
@test "Create a tenant Kubernetes control plane with latest version" {
run_kubernetes_test 'keys | sort_by(.) | .[-1]' 'test-latest-version' '59991'
}
@test "Create a tenant Kubernetes control plane with previous version" {
run_kubernetes_test 'keys | sort_by(.) | .[-2]' 'test-previous-version' '59992'
}

View File

@@ -16,10 +16,10 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.25.3
version: 0.26.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: 1.32.4
appVersion: 1.32.6

View File

@@ -7,6 +7,7 @@ include ../../../scripts/package.mk
generate:
readme-generator -v values.yaml -s values.schema.json -r README.md
yq -o=json -i '.properties.version.enum = (load("files/versions.yaml") | keys)' values.schema.json
yq -o json -i '.properties.addons.properties.ingressNginx.properties.exposeMethod.enum = ["Proxied","LoadBalancer"]' values.schema.json
yq -o json -i '.properties.controlPlane.properties.apiServer.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
yq -o json -i '.properties.controlPlane.properties.controllerManager.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json

View File

@@ -86,6 +86,7 @@ See the reference for components utilized in this service:
| `host` | Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty. | `""` |
| `controlPlane.replicas` | Number of replicas for Kubernetes control-plane components. | `2` |
| `storageClass` | StorageClass used to store user data. | `replicated` |
| `version` | Kubernetes version given as vMAJOR.MINOR | `v1.32` |
| `nodeGroups` | nodeGroups configuration | `{}` |
### Cluster Addons

View File

@@ -0,0 +1,6 @@
"v1.28": "v1.28.15"
"v1.29": "v1.29.15"
"v1.30": "v1.30.14"
"v1.31": "v1.31.10"
"v1.32": "v1.32.6"
"v1.33": "v1.33.0"

View File

@@ -0,0 +1,7 @@
{{- define "kubernetes.versionMap" }}
{{- $versionMap := .Files.Get "files/versions.yaml" | fromYaml }}
{{- if not (hasKey $versionMap .Values.version) }}
{{- printf `Kubernetes version %s is not supported, allowed versions are %s` $.Values.version (keys $versionMap) | fail }}
{{- end }}
{{- index $versionMap .Values.version }}
{{- end }}

View File

@@ -151,7 +151,7 @@ spec:
labels:
policy.cozystack.io/allow-to-etcd: "true"
replicas: 2
version: {{ $.Chart.AppVersion }}
version: {{ include "kubernetes.versionMap" $ }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
@@ -290,7 +290,7 @@ spec:
kind: KubevirtMachineTemplate
name: {{ $.Release.Name }}-{{ $groupName }}-{{ $kubevirtmachinetemplateHash }}
namespace: {{ $.Release.Namespace }}
version: v{{ $.Chart.AppVersion }}
version: {{ include "kubernetes.versionMap" $}}
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineHealthCheck

View File

@@ -123,6 +123,19 @@
"description": "StorageClass used to store user data.",
"default": "replicated"
},
"version": {
"type": "string",
"description": "Kubernetes version given as vMAJOR.MINOR",
"default": "v1.32",
"enum": [
"v1.28",
"v1.29",
"v1.30",
"v1.31",
"v1.32",
"v1.33"
]
},
"addons": {
"type": "object",
"properties": {

View File

@@ -3,10 +3,11 @@
## @param host Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty.
## @param controlPlane.replicas Number of replicas for Kubernetes control-plane components.
## @param storageClass StorageClass used to store user data.
## @param version Kubernetes version given as vMAJOR.MINOR
##
host: ""
storageClass: replicated
version: "v1.32"
## @param nodeGroups [object] nodeGroups configuration
##
nodeGroups:

View File

@@ -61,7 +61,7 @@ kubernetes 0.24.0 62cb694d
kubernetes 0.25.0 70f82667
kubernetes 0.25.1 acd4663a
kubernetes 0.25.2 08cb7c0f
kubernetes 0.25.3 HEAD
kubernetes 0.26.0 HEAD
mysql 0.1.0 263e47be
mysql 0.2.0 c24a103f
mysql 0.3.0 53f2365e
@@ -150,7 +150,7 @@ tcp-balancer 0.4.2 4369b031
tcp-balancer 0.5.0 08cb7c0f
tcp-balancer 0.5.1 HEAD
tenant 1.10.0 4369b031
tenant 1.11.0 70f82667
tenant 1.11.0 08cb7c0f
tenant 1.11.1 HEAD
virtual-machine 0.1.4 f2015d65
virtual-machine 0.1.5 263e47be
@@ -169,7 +169,7 @@ virtual-machine 0.9.1 93bdf411
virtual-machine 0.10.0 6130f43d
virtual-machine 0.10.2 632224a3
virtual-machine 0.11.0 4369b031
virtual-machine 0.12.0 70f82667
virtual-machine 0.12.0 acd4663a
virtual-machine 0.12.1 HEAD
vm-disk 0.1.0 d971f2ff
vm-disk 0.1.1 6130f43d
@@ -187,7 +187,7 @@ vm-instance 0.6.0 721c12a7
vm-instance 0.7.0 6130f43d
vm-instance 0.7.2 632224a3
vm-instance 0.8.0 4369b031
vm-instance 0.9.0 70f82667
vm-instance 0.9.0 acd4663a
vm-instance 0.10.0 HEAD
vpn 0.1.0 263e47be
vpn 0.2.0 53f2365e