From e046611bfb40df0570cd7bd8a64919e9b3c61ab0 Mon Sep 17 00:00:00 2001 From: Nils Arnold Date: Wed, 14 Jan 2026 16:52:24 +0100 Subject: [PATCH 1/2] add hosted-control-plane Signed-off-by: Nils Arnold --- .../openstack/hosted-control-plane/README.md | 485 +++++++++++++ .../cluster-addon/ccm/Chart.yaml | 10 + .../cluster-addon/ccm/overwrite.yaml | 4 + .../cluster-addon/ccm/values.yaml | 21 + .../cluster-addon/cni/Chart.yaml | 10 + .../cluster-addon/cni/values.yaml | 14 + .../cluster-addon/csi/Chart.yaml | 10 + .../cluster-addon/csi/overwrite.yaml | 3 + .../cluster-addon/csi/values.yaml | 41 ++ .../cluster-addon/metrics-server/Chart.yaml | 10 + .../metrics-server/overwrite.yaml | 4 + .../cluster-addon/metrics-server/values.yaml | 4 + .../cluster-class/.helmignore | 23 + .../cluster-class/Chart.yaml | 9 + .../cluster-class/templates/_helpers.tpl | 62 ++ .../templates/cluster-class.yaml | 664 ++++++++++++++++++ .../hosted-control-plane-template.yaml | 10 + ...ubeadm-config-template-default-worker.yaml | 13 + .../templates/openstack-cluster-template.yaml | 43 ++ ...stack-machine-template-default-worker.yaml | 12 + .../cluster-class/values.yaml | 24 + .../hosted-control-plane/clusteraddon.yaml | 21 + .../openstack/hosted-control-plane/csctl.yaml | 7 + .../hosted-control-plane/versions.yaml | 9 + 24 files changed, 1513 insertions(+) create mode 100644 providers/openstack/hosted-control-plane/README.md create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/ccm/Chart.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/ccm/overwrite.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/ccm/values.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/cni/Chart.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/cni/values.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/csi/Chart.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/csi/overwrite.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/csi/values.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/metrics-server/Chart.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/metrics-server/overwrite.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-addon/metrics-server/values.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-class/.helmignore create mode 100644 providers/openstack/hosted-control-plane/cluster-class/Chart.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-class/templates/_helpers.tpl create mode 100644 providers/openstack/hosted-control-plane/cluster-class/templates/cluster-class.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-class/templates/hosted-control-plane-template.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-class/templates/kubeadm-config-template-default-worker.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-class/templates/openstack-cluster-template.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-class/templates/openstack-machine-template-default-worker.yaml create mode 100644 providers/openstack/hosted-control-plane/cluster-class/values.yaml create mode 100644 providers/openstack/hosted-control-plane/clusteraddon.yaml create mode 100644 providers/openstack/hosted-control-plane/csctl.yaml create mode 100644 providers/openstack/hosted-control-plane/versions.yaml diff --git a/providers/openstack/hosted-control-plane/README.md b/providers/openstack/hosted-control-plane/README.md new file mode 100644 index 00000000..919d6963 --- /dev/null +++ b/providers/openstack/hosted-control-plane/README.md @@ -0,0 +1,485 @@ +# OpenStack Hosted Control Plane Cluster Stack + +This cluster stack provides Kubernetes clusters on OpenStack infrastructure using the teutonet hosted control plane provider. Unlike traditional cluster stacks where control plane components run on dedicated OpenStack VMs, this implementation runs control plane components (API server, controller manager, scheduler, etcd) in the management cluster. + +## Architecture + +- **Control Plane**: Hosted in management cluster (no OpenStack VMs) +- **Worker Nodes**: OpenStack VMs managed by CAPO +- **Control Plane Provider**: teutonet cluster-api-provider-hosted-control-plane +- **Infrastructure Provider**: cluster-api-provider-openstack (CAPO) + +## Key Advantages + +- **Cost Efficiency**: No dedicated OpenStack resources for control plane +- **Faster Provisioning**: No need to provision/bootstrap control plane VMs +- **Simplified Management**: Control plane managed as pods in management cluster +- **Resource Efficiency**: Control plane scales independently of worker nodes +- **High Availability**: Built-in HA through pod replicas in management cluster + +## Prerequisites + +### Management Cluster Requirements + +1. **Kubernetes** v1.28+ on management cluster +2. **Cluster API** v1.10+ components: + - cluster-api-controller-manager + - cluster-api-provider-openstack (CAPO) +3. **cert-manager** v1.18+ +4. **teutonet hosted-control-plane provider** + +### Installing the Hosted Control Plane Provider + +```bash +# Install the latest version +kubectl apply -f https://github.com/teutonet/cluster-api-provider-hosted-control-plane/releases/latest/download/control-plane-components.yaml + +# Or install a specific version (recommended for production) +kubectl apply -f https://github.com/teutonet/cluster-api-provider-hosted-control-plane/releases/download/v0.1.0/control-plane-components.yaml + +# Verify installation +kubectl get pods -n capi-system | grep hosted-control-plane +kubectl get crd hostedcontrolplanes.controlplane.cluster.x-k8s.io +kubectl get crd hostedcontrolplanetemplates.controlplane.cluster.x-k8s.io +``` + +### Optional Components + +- Gateway API CRDs (recommended for advanced ingress configuration) +- OpenStack Resource Controller (ORC) for image management + +### OpenStack Requirements + +- Valid OpenStack credentials (clouds.yaml) +- External network with connectivity +- Sufficient quota for worker nodes +- Ubuntu/Flatcar images compatible with Cluster API + +## Getting Started + +### Initialize Management Cluster + +```bash +# Create bootstrap cluster (if needed) +kind create cluster --name capi-management + +# Set required environment variables +export CLUSTER_TOPOLOGY=true +export EXP_CLUSTER_RESOURCE_SET=true +export EXP_RUNTIME_SDK=true + +# Initialize Cluster API with OpenStack provider +clusterctl init --infrastructure openstack + +# Install hosted control plane provider +kubectl apply -f https://github.com/teutonet/cluster-api-provider-hosted-control-plane/releases/latest/download/control-plane-components.yaml + +# Wait for deployments to be ready +kubectl -n capi-system rollout status deployment --timeout=5m +kubectl -n capo-system rollout status deployment --timeout=5m +``` + +### Install Cluster Stack Operator (CSO) + +```bash +# Install CSO +helm upgrade -i cso \ + -n cso-system \ + --create-namespace \ + oci://registry.scs.community/cluster-stacks/cso + +# Wait for CSO to be ready +kubectl -n cso-system rollout status deployment cso-controller-manager +``` + +## Deploying a Cluster + +### 1. Prepare Environment Variables + +```bash +export CLUSTER_NAMESPACE=cluster +export CLUSTER_NAME=my-hosted-cluster +export CLUSTERSTACK_NAMESPACE=cluster +export CLUSTERSTACK_VERSION=v1 +export OS_CLIENT_CONFIG_FILE=${PWD}/clouds.yaml +``` + +### 2. Create Namespace and Secrets + +```bash +# Create namespace +kubectl create namespace $CLUSTER_NAMESPACE + +# Create OpenStack credentials secret +kubectl create secret -n $CLUSTER_NAMESPACE generic openstack \ + --from-file=clouds.yaml=$OS_CLIENT_CONFIG_FILE + +# Create clouds.yaml secret for workload cluster +kubectl create secret -n kube-system generic clouds-yaml \ + --from-file=clouds.yaml=$OS_CLIENT_CONFIG_FILE \ + --dry-run=client -o yaml > clouds-yaml-secret + +kubectl create -n $CLUSTER_NAMESPACE secret generic clouds-yaml \ + --from-file=clouds-yaml-secret \ + --type=addons.cluster.x-k8s.io/resource-set +``` + +### 3. Create ClusterResourceSet + +```bash +cat < $CLUSTER_NAME-kubeconfig + +# Verify worker nodes are ready +kubectl --kubeconfig $CLUSTER_NAME-kubeconfig get nodes +``` + +## Configuration Variables + +### Control Plane Variables + +| Variable | Type | Default | Description | +|----------|------|---------|-------------| +| `controlPlaneReplicas` | integer | 3 | Number of control plane replicas for HA | +| `gatewayName` | string | "" | Name of Gateway API resource for control plane ingress (optional) | +| `gatewayNamespace` | string | "default" | Namespace of Gateway API resource | +| `certSANs` | array | [] | Extra Subject Alternative Names for API Server cert | + +### Network Variables + +| Variable | Type | Default | Description | +|----------|------|---------|-------------| +| `networkExternalID` | string | "" | External OpenStack network UUID (required) | +| `networkMTU` | integer | - | Network MTU value | +| `dnsNameservers` | array | ["9.9.9.9", "149.112.112.112"] | DNS servers for cluster network | +| `nodeCIDR` | string | "10.8.0.0/20" | CIDR for cluster node network | + +### Worker Node Variables + +| Variable | Type | Default | Description | +|----------|------|---------|-------------| +| `workerFlavor` | string | "SCS-4V-8" | OpenStack flavor for worker nodes | +| `workerRootDisk` | integer | 50 | Root disk size in GiB for workers (0 = use ephemeral) | +| `workerServerGroupID` | string | "" | Server group ID for worker anti-affinity | +| `workerAdditionalBlockDevices` | array | [] | Additional block devices for workers | +| `workerSecurityGroups` | array | [] | Extra security groups for workers (by name) | +| `workerSecurityGroupIDs` | array | [] | Extra security groups for workers (by UUID) | + +### Image Variables + +| Variable | Type | Default | Description | +|----------|------|---------|-------------| +| `imageName` | string | "ubuntu-capi-image" | Base image name for nodes | +| `imageIsOrc` | boolean | false | Use ORC image resource | +| `imageAddVersion` | boolean | true | Append K8s version to image name | + +### Access Variables + +| Variable | Type | Default | Description | +|----------|------|---------|-------------| +| `sshKeyName` | string | "" | SSH key name for node access (debugging) | +| `identityRef` | object | {name: "openstack", cloudName: "openstack"} | OpenStack credentials reference | +| `securityGroups` | array | [] | Extra security groups for all nodes (by name) | +| `securityGroupIDs` | array | [] | Extra security groups for all nodes (by UUID) | + +### API Server Variables + +| Variable | Type | Default | Description | +|----------|------|---------|-------------| +| `apiServerLoadBalancer` | string | "none" | Load balancer type (none, octavia-amphora, octavia-ovn) | +| `apiServerLoadBalancerOctaviaAmphoraAllowedCIDRs` | array | [] | Allowed CIDRs for Octavia Amphora LB | +| `disableAPIServerFloatingIP` | boolean | true | Disable floating IP for API server (recommended for hosted CP) | + +## Component Versions + +### Kubernetes 1.33.6 + +| Component | Version | Repository | +|-----------|---------|------------| +| OpenStack Cloud Controller Manager | 2.33.1 | kubernetes.github.io/cloud-provider-openstack | +| OpenStack Cinder CSI | 2.33.1 | kubernetes.github.io/cloud-provider-openstack | +| Cilium CNI | 1.18.1 | helm.cilium.io | +| Metrics Server | 3.13.0 | kubernetes-sigs.github.io/metrics-server | + +### Kubernetes 1.32.8 + +| Component | Version | Repository | +|-----------|---------|------------| +| OpenStack Cloud Controller Manager | 2.32.0 | kubernetes.github.io/cloud-provider-openstack | +| OpenStack Cinder CSI | 2.32.2 | kubernetes.github.io/cloud-provider-openstack | +| Cilium CNI | 1.18.1 | helm.cilium.io | +| Metrics Server | 3.13.0 | kubernetes-sigs.github.io/metrics-server | + +### Kubernetes 1.31.12 + +| Component | Version | Repository | +|-----------|---------|------------| +| OpenStack Cloud Controller Manager | 2.31.3 | kubernetes.github.io/cloud-provider-openstack | +| OpenStack Cinder CSI | 2.31.7 | kubernetes.github.io/cloud-provider-openstack | +| Cilium CNI | 1.18.1 | helm.cilium.io | +| Metrics Server | 3.13.0 | kubernetes-sigs.github.io/metrics-server | + +## Key Differences from Traditional Cluster Stacks + +### Architectural Differences + +| Aspect | Traditional (scs/scs2) | Hosted Control Plane | +|--------|----------------------|---------------------| +| Control Plane Location | OpenStack VMs | Management cluster pods | +| Control Plane Type | KubeadmControlPlane | HostedControlPlane | +| Control Plane VMs | 3+ VMs required | 0 VMs (pods only) | +| API Server Access | LoadBalancer or Floating IP | Via management cluster | +| etcd Storage | On control plane VMs | In management cluster (PVs) | +| Provisioning Time | ~10-15 minutes | ~5-7 minutes | +| Cost | Control plane + workers | Workers only | + +### Operational Differences + +- **Scaling**: Control plane scales as pods (faster, more flexible) +- **Upgrades**: Control plane upgraded via pod rolling update +- **Backup**: etcd backed up via standard Kubernetes backup tools +- **Monitoring**: Control plane metrics available in management cluster +- **Networking**: Control plane not on OpenStack network + +## Troubleshooting + +### Check Control Plane Status + +```bash +# View control plane resource +kubectl get hostedcontrolplane -n $CLUSTER_NAMESPACE + +# View control plane pods +kubectl get pods -n $CLUSTER_NAMESPACE -l cluster.x-k8s.io/cluster-name=$CLUSTER_NAME + +# Check control plane logs +kubectl logs -n $CLUSTER_NAMESPACE -l cluster.x-k8s.io/cluster-name=$CLUSTER_NAME -c kube-apiserver +kubectl logs -n $CLUSTER_NAMESPACE -l cluster.x-k8s.io/cluster-name=$CLUSTER_NAME -c kube-controller-manager +kubectl logs -n $CLUSTER_NAMESPACE -l cluster.x-k8s.io/cluster-name=$CLUSTER_NAME -c kube-scheduler +``` + +### Check Worker Nodes + +```bash +# Get kubeconfig +clusterctl get kubeconfig -n $CLUSTER_NAMESPACE $CLUSTER_NAME > /tmp/kubeconfig + +# View worker nodes +kubectl --kubeconfig /tmp/kubeconfig get nodes + +# View worker pods +kubectl --kubeconfig /tmp/kubeconfig get pods -A +``` + +### Check Addons + +```bash +# Verify CNI (Cilium) +kubectl --kubeconfig /tmp/kubeconfig get pods -n kube-system -l k8s-app=cilium + +# Verify CCM +kubectl --kubeconfig /tmp/kubeconfig get pods -n kube-system -l app=openstack-cloud-controller-manager + +# Verify CSI +kubectl --kubeconfig /tmp/kubeconfig get pods -n kube-system -l app=cinder-csi + +# Verify metrics-server +kubectl --kubeconfig /tmp/kubeconfig top nodes +``` + +### Common Issues + +#### Control plane pods not starting + +**Symptom**: HostedControlPlane created but no pods appear + +**Solution**: +1. Check teutonet provider is running: `kubectl get pods -n capi-system | grep hosted-control-plane` +2. Check HostedControlPlane events: `kubectl describe hostedcontrolplane -n $CLUSTER_NAMESPACE` +3. Verify management cluster has sufficient resources + +#### Worker nodes not joining + +**Symptom**: Machines created but nodes don't appear in `kubectl get nodes` + +**Solution**: +1. Check cloud-init logs on worker VMs +2. Verify OpenStack network connectivity +3. Check control plane is accessible from workers +4. Verify clouds.yaml secret exists in workload cluster: `kubectl --kubeconfig /tmp/kubeconfig get secret -n kube-system clouds-yaml` + +#### Cluster addon failures + +**Symptom**: Addons fail to install or pods crash + +**Solution**: +1. Check ClusterResourceSet applied: `kubectl get clusterresourceset -n $CLUSTER_NAMESPACE` +2. Verify addon configurations match component versions +3. Check addon logs in workload cluster + +## Upgrading + +### Upgrade Kubernetes Version + +```bash +# Update cluster version +kubectl patch cluster -n $CLUSTER_NAMESPACE $CLUSTER_NAME --type merge -p '{"spec":{"topology":{"version":"v1.32.8"}}}' + +# Monitor upgrade +kubectl get cluster -n $CLUSTER_NAMESPACE -w +``` + +### Scale Control Plane + +```bash +# Update control plane replicas +kubectl patch cluster -n $CLUSTER_NAMESPACE $CLUSTER_NAME --type merge -p '{"spec":{"topology":{"variables":[{"name":"controlPlaneReplicas","value":5}]}}}' + +# Monitor scaling +kubectl get hostedcontrolplane -n $CLUSTER_NAMESPACE -w +``` + +### Scale Workers + +```bash +# Update worker replicas +kubectl patch cluster -n $CLUSTER_NAMESPACE $CLUSTER_NAME --type merge -p '{"spec":{"topology":{"workers":{"machineDeployments":[{"class":"default-worker","name":"md-0","replicas":5}]}}}}' + +# Monitor scaling +kubectl get machines -n $CLUSTER_NAMESPACE -w +``` + +## Deleting a Cluster + +```bash +# Delete cluster resource (will delete all associated resources) +kubectl delete cluster -n $CLUSTER_NAMESPACE $CLUSTER_NAME + +# Monitor deletion +kubectl get cluster -n $CLUSTER_NAMESPACE -w + +# Verify OpenStack resources are cleaned up +openstack server list +openstack network list +``` + +## References + +- [teutonet hosted-control-plane provider](https://github.com/teutonet/cluster-api-provider-hosted-control-plane) +- [Cluster API documentation](https://cluster-api.sigs.k8s.io/) +- [OpenStack provider documentation](https://github.com/kubernetes-sigs/cluster-api-provider-openstack) +- [Cluster Stacks documentation](https://docs.scs.community/docs/category/cluster-stacks) + +## Support + +For issues and questions: +- Hosted control plane provider: https://github.com/teutonet/cluster-api-provider-hosted-control-plane/issues +- Cluster stacks: https://github.com/SovereignCloudStack/cluster-stacks/issues diff --git a/providers/openstack/hosted-control-plane/cluster-addon/ccm/Chart.yaml b/providers/openstack/hosted-control-plane/cluster-addon/ccm/Chart.yaml new file mode 100644 index 00000000..541cecd0 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/ccm/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +type: application +description: CCM +name: CCM +version: v1 +dependencies: + - alias: openstack-cloud-controller-manager + name: openstack-cloud-controller-manager + repository: https://kubernetes.github.io/cloud-provider-openstack + version: 2.33.1 diff --git a/providers/openstack/hosted-control-plane/cluster-addon/ccm/overwrite.yaml b/providers/openstack/hosted-control-plane/cluster-addon/ccm/overwrite.yaml new file mode 100644 index 00000000..39076ecd --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/ccm/overwrite.yaml @@ -0,0 +1,4 @@ +values: | + openstack-cloud-controller-manager: + cluster: + name: {{ .Cluster.metadata.name }} diff --git a/providers/openstack/hosted-control-plane/cluster-addon/ccm/values.yaml b/providers/openstack/hosted-control-plane/cluster-addon/ccm/values.yaml new file mode 100644 index 00000000..3f290366 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/ccm/values.yaml @@ -0,0 +1,21 @@ +openstack-cloud-controller-manager: + secret: + enabled: true + name: ccm-cloud-config + create: true + nodeSelector: + tolerations: + - key: node.cloudprovider.kubernetes.io/uninitialized + value: "true" + effect: NoSchedule + extraVolumes: + - name: clouds-yaml + secret: + secretName: clouds-yaml + extraVolumeMounts: + - name: clouds-yaml + readOnly: true + mountPath: /etc/openstack + cloudConfig: + global: + use-clouds: true diff --git a/providers/openstack/hosted-control-plane/cluster-addon/cni/Chart.yaml b/providers/openstack/hosted-control-plane/cluster-addon/cni/Chart.yaml new file mode 100644 index 00000000..c8a962f4 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/cni/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +type: application +description: CNI +name: CNI +version: v1 +dependencies: + - alias: cilium + name: cilium + repository: https://helm.cilium.io/ + version: 1.18.1 diff --git a/providers/openstack/hosted-control-plane/cluster-addon/cni/values.yaml b/providers/openstack/hosted-control-plane/cluster-addon/cni/values.yaml new file mode 100644 index 00000000..8a312f0c --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/cni/values.yaml @@ -0,0 +1,14 @@ +cilium: + namespaceOverride: kube-system + tls: + secretsNamespace: + name: "kube-system" + sessionAffinity: true + sctp: + enabled: true + ipam: + mode: "kubernetes" + gatewayAPI: + enabled: true + secretsNamespace: + name: "kube-system" diff --git a/providers/openstack/hosted-control-plane/cluster-addon/csi/Chart.yaml b/providers/openstack/hosted-control-plane/cluster-addon/csi/Chart.yaml new file mode 100644 index 00000000..e275b2ff --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/csi/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +type: application +description: CSI +name: CSI +version: v1 +dependencies: + - alias: openstack-cinder-csi + name: openstack-cinder-csi + repository: https://kubernetes.github.io/cloud-provider-openstack + version: 2.33.1 diff --git a/providers/openstack/hosted-control-plane/cluster-addon/csi/overwrite.yaml b/providers/openstack/hosted-control-plane/cluster-addon/csi/overwrite.yaml new file mode 100644 index 00000000..d191a115 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/csi/overwrite.yaml @@ -0,0 +1,3 @@ +values: | + openstack-cinder-csi: + clusterID: "{{ .Cluster.metadata.name }}" diff --git a/providers/openstack/hosted-control-plane/cluster-addon/csi/values.yaml b/providers/openstack/hosted-control-plane/cluster-addon/csi/values.yaml new file mode 100644 index 00000000..4e648a4f --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/csi/values.yaml @@ -0,0 +1,41 @@ +openstack-cinder-csi: + secret: + enabled: true + name: csi-cloud-config + create: true + filename: cloud.conf + data: + cloud.conf: |- + [Global] + use-clouds = "true" + clouds-file = /etc/openstack/clouds.yaml + storageClass: + delete: + isDefault: true + csi: + plugin: + volumes: + - name: clouds-yaml + secret: + secretName: clouds-yaml + - name: cloud-conf + secret: + secretName: csi-cloud-config + volumeMounts: + - name: clouds-yaml + readOnly: true + mountPath: /etc/openstack + - name: cloud-conf + readOnly: true + mountPath: /etc/kubernetes + - name: cloud-conf + readOnly: true + mountPath: /etc/config + nodeSelector: + node-role.kubernetes.io/control-plane: "" + tolerations: + - key: node.cloudprovider.kubernetes.io/uninitialized + value: "true" + effect: NoSchedule + - key: node-role.kubernetes.io/control-plane + effect: NoSchedule diff --git a/providers/openstack/hosted-control-plane/cluster-addon/metrics-server/Chart.yaml b/providers/openstack/hosted-control-plane/cluster-addon/metrics-server/Chart.yaml new file mode 100644 index 00000000..2ac06b1a --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/metrics-server/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +type: application +description: Metrics Server +name: metrics-server +version: v1 +dependencies: + - name: "metrics-server" + version: "3.13.0" + repository: "https://kubernetes-sigs.github.io/metrics-server/" + alias: "metrics-server" diff --git a/providers/openstack/hosted-control-plane/cluster-addon/metrics-server/overwrite.yaml b/providers/openstack/hosted-control-plane/cluster-addon/metrics-server/overwrite.yaml new file mode 100644 index 00000000..7b1dcd5b --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/metrics-server/overwrite.yaml @@ -0,0 +1,4 @@ +values: | + metrics-server: + commonLabels: + domain: "{{ .Cluster.spec.controlPlaneEndpoint.host }}" diff --git a/providers/openstack/hosted-control-plane/cluster-addon/metrics-server/values.yaml b/providers/openstack/hosted-control-plane/cluster-addon/metrics-server/values.yaml new file mode 100644 index 00000000..a89bf027 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-addon/metrics-server/values.yaml @@ -0,0 +1,4 @@ +metrics-server: + fullnameOverride: metrics-server + args: + - --kubelet-insecure-tls diff --git a/providers/openstack/hosted-control-plane/cluster-class/.helmignore b/providers/openstack/hosted-control-plane/cluster-class/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-class/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/providers/openstack/hosted-control-plane/cluster-class/Chart.yaml b/providers/openstack/hosted-control-plane/cluster-class/Chart.yaml new file mode 100644 index 00000000..9a6a1fcc --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-class/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v2 +description: "This chart installs and configures: + + * OpenStack hosted-control-plane Cluster Class + + " +name: openstack-hosted-control-plane-1-33-cluster-class +type: application +version: v1 diff --git a/providers/openstack/hosted-control-plane/cluster-class/templates/_helpers.tpl b/providers/openstack/hosted-control-plane/cluster-class/templates/_helpers.tpl new file mode 100644 index 00000000..2339c125 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-class/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "cluster-class.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "cluster-class.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "cluster-class.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "cluster-class.labels" -}} +helm.sh/chart: {{ include "cluster-class.chart" . }} +{{ include "cluster-class.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "cluster-class.selectorLabels" -}} +app.kubernetes.io/name: {{ include "cluster-class.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "cluster-class.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "cluster-class.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/providers/openstack/hosted-control-plane/cluster-class/templates/cluster-class.yaml b/providers/openstack/hosted-control-plane/cluster-class/templates/cluster-class.yaml new file mode 100644 index 00000000..50505cd6 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-class/templates/cluster-class.yaml @@ -0,0 +1,664 @@ +apiVersion: cluster.x-k8s.io/v1beta1 +kind: ClusterClass +metadata: + name: {{ .Release.Name }}-{{ .Chart.Version }} +spec: + controlPlane: + ref: + apiVersion: controlplane.cluster.x-k8s.io/v1alpha1 + kind: HostedControlPlaneTemplate + name: {{ .Release.Name }}-{{ .Chart.Version }}-control-plane + # No machineInfrastructure section - control plane is hosted in management cluster + infrastructure: + ref: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackClusterTemplate + name: {{ .Release.Name }}-{{ .Chart.Version }}-cluster + workers: + machineDeployments: + - class: default-worker + template: + bootstrap: + ref: + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: KubeadmConfigTemplate + name: {{ .Release.Name }}-{{ .Chart.Version }}-default-worker + infrastructure: + ref: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + name: {{ .Release.Name }}-{{ .Chart.Version }}-default-worker + variables: + # Control plane variables (hosted in management cluster) + - name: controlPlaneReplicas + required: false + schema: + openAPIV3Schema: + type: integer + minimum: 1 + default: 3 + description: "Number of hosted control plane replicas for high availability." + - name: gatewayName + required: false + schema: + openAPIV3Schema: + type: string + default: "" + description: "Name of the Gateway API resource for control plane ingress. Optional - control plane accessible via standard kubeconfig if not set." + - name: gatewayNamespace + required: false + schema: + openAPIV3Schema: + type: string + default: "default" + description: "Namespace of the Gateway API resource." + # Image variables + - name: imageName + required: false + schema: + openAPIV3Schema: + type: string + description: | + The base name of the OpenStack image used for provisioning servers. + If `imageIsOrc` is enabled, this name refers to an ORC image resource. + If `imageIsOrc` is disabled, the name is used to filter images available in the OpenStack project. In this case, the specified image must already exist within the project. + If `imageAddVersion` is enabled, the Kubernetes version will be appended to form the complete image name (e.g., imageName-v1.33.6) + default: "ubuntu-capi-image" + - name: imageIsOrc + required: false + schema: + openAPIV3Schema: + type: boolean + description: | + Indicates whether the image name refers to an ORC image resource. + If set to true (default), the `imageName` is interpreted as a reference to an ORC image. + If set to false, the `imageName` is used to filter images in the OpenStack project instead. + default: false + - name: imageAddVersion + required: false + schema: + openAPIV3Schema: + type: boolean + description: | + Add a suffix with the Kubernetes version to the imageName. E.g. imageName-v1.33.6. + default: true + - name: disableAPIServerFloatingIP + required: false + schema: + openAPIV3Schema: + type: boolean + default: true + example: true + description: "DisableAPIServerFloatingIP controls whether a floating IP should be attached to the API server. Set to true for hosted control plane since API server runs in management cluster." + # Network variables + - name: networkExternalID + required: false + schema: + openAPIV3Schema: + type: string + example: "ebfe5546-f09f-4f42-ab54-094e457d42ec" + format: "uuid4" + description: "networkExternalID is the ID of an external OpenStack Network. This is necessary to get public internet to the VMs in case there are several external networks." + - name: networkMTU + required: false + schema: + openAPIV3Schema: + type: integer + example: 1500 + description: "networkMTU sets the maximum transmission unit (MTU) value to address fragmentation for the private network ID." + - name: dnsNameservers + required: false + schema: + openAPIV3Schema: + type: array + description: "dnsNameservers is the list of nameservers for the OpenStack Subnet being created. Set this value when you need to create a new network/subnet which requires access to DNS." + default: ["9.9.9.9", "149.112.112.112"] + example: ["9.9.9.9", "149.112.112.112"] + items: + type: string + - name: nodeCIDR + required: false + schema: + openAPIV3Schema: + type: string + format: "cidr" + default: "10.8.0.0/20" + example: "10.8.0.0/20" + description: |- + nodeCIDR is the OpenStack Subnet to be created. + Cluster actuator will create a network, a subnet with nodeCIDR, + and a router connected to this subnet. + If you leave this empty, no network will be created. + # Workers + - name: workerFlavor + required: false + schema: + openAPIV3Schema: + type: string + default: "SCS-4V-8" + example: "SCS-4V-8" + description: "OpenStack instance flavor for worker nodes (default: SCS-4V-8, which requires workerRootDisk)." + - name: workerRootDisk + required: false + schema: + openAPIV3Schema: + type: integer + minimum: 0 + example: 25 + default: 50 + description: |- + Root disk size in GiB for worker nodes. + OpenStack volume will be created and used instead of an ephemeral disk defined in flavor. + Should be used for the diskless flavors (>= 20), otherwise set to 0. + - name: workerServerGroupID + required: false + schema: + openAPIV3Schema: + type: string + default: "" + example: "869fe071-1e56-46a9-9166-47c9f228e297" + description: "The server group to assign the worker nodes to." + - name: workerAdditionalBlockDevices + required: false + schema: + openAPIV3Schema: + type: array + default: [] + items: + type: object + properties: + name: + type: string + sizeGiB: + type: integer + default: 20 + type: + type: string + default: "__DEFAULT__" + required: ["name"] + # Access management + - name: sshKeyName + required: false + schema: + openAPIV3Schema: + type: string + default: "" + example: "capi-keypair" + description: "The ssh key name to inject in the nodes (for debugging)." + - name: securityGroups + required: false + schema: + openAPIV3Schema: + type: array + default: [] + example: ["security-group-1"] + description: |- + The names of extra security groups to assign to worker nodes. + Will be ignored if `securityGroupIDs` is used. + items: + type: string + - name: securityGroupIDs + required: false + schema: + openAPIV3Schema: + format: "uuid4" + type: array + default: [] + example: ["9ae2f488-30a3-4629-bd51-07acb8eb4278"] + description: "The UUIDs of extra security groups to assign to worker nodes" + items: + type: string + - name: workerSecurityGroups + required: false + schema: + openAPIV3Schema: + type: array + default: [] + example: ["security-group-1"] + description: |- + The names of extra security groups to assign to the worker nodes. + Will be ignored if `workerSecurityGroupIDs` is used. + items: + type: string + - name: workerSecurityGroupIDs + required: false + schema: + openAPIV3Schema: + format: "uuid4" + type: array + default: [] + example: ["9ae2f488-30a3-4629-bd51-07acb8eb4278"] + description: "The UUIDs of extra security groups to assign to the worker nodes" + items: + type: string + - name: identityRef + required: false + schema: + openAPIV3Schema: + type: object + default: {} + properties: + name: + type: string + example: "openstack" + default: "openstack" + description: "The name of the secret that carries the OpenStack clouds.yaml" + cloudName: + type: string + example: "openstack" + default: "openstack" + description: "The name of the cloud to use from the clouds.yaml" + # Kubernetes API server + - name: certSANs + required: false + schema: + openAPIV3Schema: + type: array + default: [] + example: ["mydomain.example"] + description: "certSANs sets extra Subject Alternative Names for the API Server signing cert." + items: + type: string + - name: apiServerLoadBalancer + required: false + schema: + openAPIV3Schema: + type: string + default: "none" + example: "none, octavia-amphora, octavia-ovn" + description: | + For hosted control plane, typically set to "none" since the API server runs in the management cluster. + You can choose from 3 options: + + none: + (default) No LoadBalancer solution will be deployed + + octavia-amphora: + Uses OpenStack's LoadBalancer service Octavia (provider:amphora) + + octavia-ovn: + Uses OpenStack's LoadBalancer service Octavia (provider:ovn) + - name: apiServerLoadBalancerOctaviaAmphoraAllowedCIDRs + required: false + schema: + openAPIV3Schema: + type: array + example: ["192.168.10.0/24"] + description: |- + apiServerLoadBalancerOctaviaAmphoraAllowedCIDRs restrict access to the Kubernetes API server on a network level. + Ensure that at least the outgoing IP of your Management Cluster is added to the list of allowed CIDRs. + Otherwise CAPO can't reconcile the target Cluster correctly. + This requires amphora as load balancer provider in version >= v2.12. + items: + type: string + # + # Patches + # + patches: + # + # Patches for control plane variables (apply to HostedControlPlaneTemplate) + # + - name: controlPlaneReplicas + description: "Sets the number of hosted control plane replicas." + definitions: + - selector: + apiVersion: controlplane.cluster.x-k8s.io/v1alpha1 + kind: HostedControlPlaneTemplate + matchResources: + controlPlane: true + jsonPatches: + - op: replace + path: "/spec/template/spec/replicas" + valueFrom: + variable: controlPlaneReplicas + - name: gatewayName + description: "Sets the Gateway API resource for control plane ingress." + enabledIf: {{ `'{{ ne .gatewayName "" }}'` }} + definitions: + - selector: + apiVersion: controlplane.cluster.x-k8s.io/v1alpha1 + kind: HostedControlPlaneTemplate + matchResources: + controlPlane: true + jsonPatches: + - op: add + path: "/spec/template/spec/gateway" + valueFrom: + template: | + name: {{ `{{ .gatewayName }}` }} + namespace: {{ `{{ .gatewayNamespace }}` }} + - name: certSANs + description: "certSANs sets extra Subject Alternative Names for the API Server signing cert." + enabledIf: {{ `'{{ if .certSANs }}true{{end}}'` }} + definitions: + - selector: + apiVersion: controlplane.cluster.x-k8s.io/v1alpha1 + kind: HostedControlPlaneTemplate + matchResources: + controlPlane: true + jsonPatches: + - op: add + path: "/spec/template/spec/apiServer/certSANs" + valueFrom: + variable: certSANs + # + # Patches for OpenStackClusterTemplate resource. + # + - name: apiServerLoadBalancerOctaviaAmphora + description: "Takes care of the patches that should be applied when variable apiServerLoadBalancer is set to octavia-amphora." + enabledIf: {{ `'{{ eq .apiServerLoadBalancer "octavia-amphora" }}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackClusterTemplate + matchResources: + infrastructureCluster: true + jsonPatches: + - op: replace + path: "/spec/template/spec/apiServerLoadBalancer/enabled" + value: true + - op: add + path: "/spec/template/spec/apiServerLoadBalancer/provider" + value: "amphora" + - name: apiServerLoadBalancerOctaviaOVN + description: "Takes care of the patches that should be applied when variable apiServerLoadBalancer is set to octavia-ovn." + enabledIf: {{ `'{{ eq .apiServerLoadBalancer "octavia-ovn" }}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackClusterTemplate + matchResources: + infrastructureCluster: true + jsonPatches: + - op: replace + path: "/spec/template/spec/apiServerLoadBalancer/enabled" + value: true + - op: add + path: "/spec/template/spec/apiServerLoadBalancer/provider" + value: "ovn" + - name: apiServerLoadBalancerOctaviaAmphoraAllowedCIDRs + description: "Takes care of the patches that should be applied when variable allowedCIDRs is set." + enabledIf: {{ `'{{ and .apiServerLoadBalancerOctaviaAmphoraAllowedCIDRs (eq .apiServerLoadBalancer "octavia-amphora") }}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackClusterTemplate + matchResources: + infrastructureCluster: true + jsonPatches: + - op: add + path: "/spec/template/spec/apiServerLoadBalancer/allowedCIDRs" + valueFrom: + variable: apiServerLoadBalancerOctaviaAmphoraAllowedCIDRs + - name: networkExternalID + description: "Sets the ID of an external OpenStack Network. This is necessary to get public internet to the VMs." + enabledIf: {{ `'{{ if .networkExternalID }}true{{end}}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackClusterTemplate + matchResources: + infrastructureCluster: true + jsonPatches: + - op: add + path: "/spec/template/spec/externalNetwork" + value: {} + - op: add + path: "/spec/template/spec/externalNetwork/id" + valueFrom: + variable: networkExternalID + - name: networkMTU + description: "Sets the network MTU when variable networkMTU exist in cluster resource." + enabledIf: {{ `'{{ if .networkMTU }}true{{end}}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackClusterTemplate + matchResources: + infrastructureCluster: true + jsonPatches: + - op: add + path: "/spec/template/spec/networkMTU" + valueFrom: + variable: networkMTU + - name: identityRef + description: "Sets the OpenStack identity reference." + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackClusterTemplate + matchResources: + infrastructureCluster: true + jsonPatches: + - op: add + path: /spec/template/spec/identityRef + valueFrom: + variable: identityRef + - name: nodeCIDRSubnet + description: |- + Sets the NodeCIDR for the OpenStack Subnet to be created. + Cluster actuator will create a network, a subnet with NodeCIDR, + and a router connected to this subnet. + enabledIf: {{ `'{{ if .nodeCIDR }}true{{end}}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackClusterTemplate + matchResources: + infrastructureCluster: true + jsonPatches: + - op: add + path: "/spec/template/spec/managedSubnets" + valueFrom: + template: | + - cidr: '{{ `{{ .nodeCIDR }}` }}' + dnsNameservers: + {{ `{{- range .dnsNameservers }}` }} + - {{ `{{ . }}` }} + {{ `{{- end }}` }} + - name: disableAPIServerFloatingIP + description: "DisableAPIServerFloatingIP controls whether a floating IP should be attached to the API server." + enabledIf: {{ `"{{ if .disableAPIServerFloatingIP }}true{{end}}"` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackClusterTemplate + matchResources: + infrastructureCluster: true + jsonPatches: + - op: add + path: "/spec/template/spec/disableAPIServerFloatingIP" + valueFrom: + variable: disableAPIServerFloatingIP + # + # Patches for worker's OpenStackMachineTemplate resources. + # Note: No control plane patches since control plane runs in management cluster + # + - name: workerImage + description: "Sets the OpenStack image name that is used for creating the worker servers." + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: add + path: /spec/template/spec/image + valueFrom: + template: | + {{ `{{ if .imageIsOrc }}imageRef{{ else }}filter{{ end }}` }}: + name: {{ `{{ .imageName }}{{ if .imageAddVersion }}-{{ .builtin.machineDeployment.version }}{{ end }}` }} + - name: workerFlavor + description: "Sets the openstack instance flavor for the worker nodes." + enabledIf: {{ `'{{ ne .workerFlavor "" }}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: replace + path: "/spec/template/spec/flavor" + valueFrom: + variable: workerFlavor + - name: workerRootDisk + description: "Sets the root disk size in GiB for worker nodes." + enabledIf: {{ `'{{ if .workerRootDisk }}true{{end}}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: add + path: "/spec/template/spec/rootVolume" + valueFrom: + template: | + sizeGiB: {{ `{{ .workerRootDisk }}` }} + - name: workerServerGroupID + description: "Sets the server group to assign the worker nodes to." + enabledIf: {{ `'{{ ne .workerServerGroupID "" }}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: add + path: "/spec/template/spec/serverGroup" + valueFrom: + template: | + id: {{ `{{ .workerServerGroupID }}` }} + - name: workerAdditionalBlockDevices + enabledIf: {{ `'{{ if .workerAdditionalBlockDevices }}true{{end}}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: add + path: /spec/template/spec/additionalBlockDevices + valueFrom: + template: | + {{ `{{- range .workerAdditionalBlockDevices }}` }} + - name: {{ `{{ .name }}` }} + sizeGiB: {{ `{{ .sizeGiB }}` }} + storage: + type: Volume + volume: + type: {{ `{{ .type }}` }} + {{ `{{- end }}` }} + # Note: The securityGroups patch must be placed before securityGroupIDs, workerSecurityGroups, and workerSecurityGroupIDs. + # The patch order ensures the last applied patch overwrites previous ones. + - name: securityGroups + description: "Sets the list of the openstack security groups for the worker instances." + enabledIf: {{ `'{{ if .securityGroups }}true{{end}}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: add + path: "/spec/template/spec/securityGroups" + valueFrom: + template: {{ `'[ {{ range .securityGroups }} { filter: { name: {{ . }}}}, {{ end }} ]'` }} + # Note: The securityGroupIDs patch must be placed before workerSecurityGroups, workerSecurityGroupIDs and after securityGroupIDs. + # The patch order ensures the last applied patch overwrites previous ones. + - name: securityGroupIDs + description: "Sets the list of the openstack security groups for the worker instances by UUID." + enabledIf: {{ `'{{ if .securityGroupIDs }}true{{end}}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: add + path: "/spec/template/spec/securityGroups" + valueFrom: + template: {{ `'[ {{ range .securityGroupIDs }} { id: {{ . }} }, {{ end }} ]'` }} + - name: sshKeyName + description: "Sets the ssh key to inject in the nodes." + enabledIf: {{ `'{{ ne .sshKeyName "" }}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: add + path: "/spec/template/spec/sshKeyName" + valueFrom: + variable: sshKeyName + # Note: The workerSecurityGroups patch must be placed before workerSecurityGroupIDs and after securityGroups and securityGroupIDs. + # The patch order ensures the last applied patch overwrites previous ones. + - name: workerSecurityGroups + description: "Sets the list of the openstack security groups for the worker instances." + enabledIf: {{ `'{{ if .workerSecurityGroups }}true{{end}}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: add + path: "/spec/template/spec/securityGroups" + valueFrom: + template: {{ `'[ {{ range .workerSecurityGroups }} { filter: { name: {{ . }}}}, {{ end }} ]'` }} + # Note: The workerSecurityGroupIDs patch must be placed after securityGroups, securityGroupIDs and workerSecurityGroupIDs. + # The patch order ensures the last applied patch overwrites previous ones. + - name: workerSecurityGroupIDs + description: "Sets the list of the openstack security groups for the worker instances by UUID." + enabledIf: {{ `'{{ if .workerSecurityGroupIDs }}true{{end}}'` }} + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: false + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: add + path: "/spec/template/spec/securityGroups" + valueFrom: + template: {{ `'[ {{ range .workerSecurityGroupIDs }} { id: {{ . }} }, {{ end }} ]'` }} diff --git a/providers/openstack/hosted-control-plane/cluster-class/templates/hosted-control-plane-template.yaml b/providers/openstack/hosted-control-plane/cluster-class/templates/hosted-control-plane-template.yaml new file mode 100644 index 00000000..c1cff1dd --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-class/templates/hosted-control-plane-template.yaml @@ -0,0 +1,10 @@ +apiVersion: controlplane.cluster.x-k8s.io/v1alpha1 +kind: HostedControlPlaneTemplate +metadata: + name: {{ .Release.Name }}-{{ .Chart.Version }}-control-plane +spec: + template: + spec: + gateway: + namespace: + name: diff --git a/providers/openstack/hosted-control-plane/cluster-class/templates/kubeadm-config-template-default-worker.yaml b/providers/openstack/hosted-control-plane/cluster-class/templates/kubeadm-config-template-default-worker.yaml new file mode 100644 index 00000000..4c1494ed --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-class/templates/kubeadm-config-template-default-worker.yaml @@ -0,0 +1,13 @@ +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +kind: KubeadmConfigTemplate +metadata: + name: {{ .Release.Name }}-{{ .Chart.Version }}-default-worker +spec: + template: + spec: + joinConfiguration: + nodeRegistration: + kubeletExtraArgs: + cloud-provider: external + provider-id: 'openstack:///{{ `{{ instance_id }}` }}' + name: '{{ `{{ local_hostname }}` }}' diff --git a/providers/openstack/hosted-control-plane/cluster-class/templates/openstack-cluster-template.yaml b/providers/openstack/hosted-control-plane/cluster-class/templates/openstack-cluster-template.yaml new file mode 100644 index 00000000..3b8ae609 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-class/templates/openstack-cluster-template.yaml @@ -0,0 +1,43 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: OpenStackClusterTemplate +metadata: + name: {{ .Release.Name }}-{{ .Chart.Version }}-cluster +spec: + template: + spec: + identityRef: + cloudName: overridden-by-patch + name: overridden-by-patch + apiServerLoadBalancer: + enabled: false + disableAPIServerFloatingIP: true + managedSecurityGroups: + allNodesSecurityGroupRules: + - remoteManagedGroups: + - worker + direction: ingress + etherType: IPv4 + name: VXLAN (Cilium) + portRangeMin: 8472 + portRangeMax: 8472 + protocol: udp + description: "Allow VXLAN traffic for Cilium" + - remoteManagedGroups: + - worker + direction: ingress + etherType: IPv4 + name: HealthCheck (Cilium) + portRangeMin: 4240 + portRangeMax: 4240 + protocol: tcp + description: "Allow HealthCheck traffic for Cilium" + - remoteManagedGroups: + - worker + direction: ingress + etherType: IPv4 + name: Hubble (Cilium) + portRangeMin: 4244 + portRangeMax: 4244 + protocol: tcp + description: "Allow Hubble traffic for Cilium" diff --git a/providers/openstack/hosted-control-plane/cluster-class/templates/openstack-machine-template-default-worker.yaml b/providers/openstack/hosted-control-plane/cluster-class/templates/openstack-machine-template-default-worker.yaml new file mode 100644 index 00000000..920dbb0a --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-class/templates/openstack-machine-template-default-worker.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: OpenStackMachineTemplate +metadata: + name: {{ .Release.Name }}-{{ .Chart.Version }}-default-worker +spec: + template: + spec: + flavor: overridden-by-patch + image: + imageRef: + name: overridden-by-patch diff --git a/providers/openstack/hosted-control-plane/cluster-class/values.yaml b/providers/openstack/hosted-control-plane/cluster-class/values.yaml new file mode 100644 index 00000000..5ebb9d61 --- /dev/null +++ b/providers/openstack/hosted-control-plane/cluster-class/values.yaml @@ -0,0 +1,24 @@ +# Default values for hosted-control-plane cluster class + +# OpenStack credentials +identityRef: + name: "openstack" + cloudName: "openstack" + +# Network configuration +networkExternalID: "" +dnsNameservers: + - "9.9.9.9" + - "149.112.112.112" +nodeCIDR: "10.8.0.0/20" + +# Worker node configuration +workerFlavor: "SCS-2V-4" +workerRootDisk: 25 + +# Control plane configuration (hosted in management cluster) +controlPlaneReplicas: 3 + +# Gateway configuration for hosted control plane (optional) +gatewayName: "" +gatewayNamespace: "" diff --git a/providers/openstack/hosted-control-plane/clusteraddon.yaml b/providers/openstack/hosted-control-plane/clusteraddon.yaml new file mode 100644 index 00000000..d346ba22 --- /dev/null +++ b/providers/openstack/hosted-control-plane/clusteraddon.yaml @@ -0,0 +1,21 @@ +apiVersion: clusteraddonconfig.x-k8s.io/v1alpha1 +clusterAddonVersion: clusteraddons.clusterstack.x-k8s.io/v1alpha1 +addonStages: + AfterControlPlaneInitialized: + - name: cni + action: apply + - name: metrics-server + action: apply + - name: csi + action: apply + - name: ccm + action: apply + BeforeClusterUpgrade: + - name: cni + action: apply + - name: metrics-server + action: apply + - name: csi + action: apply + - name: ccm + action: apply diff --git a/providers/openstack/hosted-control-plane/csctl.yaml b/providers/openstack/hosted-control-plane/csctl.yaml new file mode 100644 index 00000000..1b25972e --- /dev/null +++ b/providers/openstack/hosted-control-plane/csctl.yaml @@ -0,0 +1,7 @@ +apiVersion: csctl.clusterstack.x-k8s.io/v1alpha1 +config: + clusterStackName: hosted-control-plane + kubernetesVersion: v1.33.6 + provider: + apiVersion: openstack.csctl.clusterstack.x-k8s.io/v1alpha1 + type: openstack diff --git a/providers/openstack/hosted-control-plane/versions.yaml b/providers/openstack/hosted-control-plane/versions.yaml new file mode 100644 index 00000000..87a97050 --- /dev/null +++ b/providers/openstack/hosted-control-plane/versions.yaml @@ -0,0 +1,9 @@ +- kubernetes: 1.31.12 + cinder_csi: 2.31.7 + occm: 2.31.3 +- kubernetes: 1.32.8 + cinder_csi: 2.32.2 + occm: 2.32.0 +- kubernetes: 1.33.6 + cinder_csi: 2.33.1 + occm: 2.33.1 From 84729589596a6d518b876d3868b494d208475ab4 Mon Sep 17 00:00:00 2001 From: Jan Schoone Date: Wed, 28 Jan 2026 16:09:04 +0100 Subject: [PATCH 2/2] make cluster class working Signed-off-by: Jan Schoone --- .../openstack/hosted-control-plane/cluster-class/Chart.yaml | 2 +- .../cluster-class/templates/cluster-class.yaml | 2 +- .../templates/hosted-control-plane-template.yaml | 4 ++-- providers/openstack/hosted-control-plane/csctl.yaml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/providers/openstack/hosted-control-plane/cluster-class/Chart.yaml b/providers/openstack/hosted-control-plane/cluster-class/Chart.yaml index 9a6a1fcc..3321367f 100644 --- a/providers/openstack/hosted-control-plane/cluster-class/Chart.yaml +++ b/providers/openstack/hosted-control-plane/cluster-class/Chart.yaml @@ -4,6 +4,6 @@ description: "This chart installs and configures: * OpenStack hosted-control-plane Cluster Class " -name: openstack-hosted-control-plane-1-33-cluster-class +name: openstack-hosted-control-plane-1-34-cluster-class type: application version: v1 diff --git a/providers/openstack/hosted-control-plane/cluster-class/templates/cluster-class.yaml b/providers/openstack/hosted-control-plane/cluster-class/templates/cluster-class.yaml index 50505cd6..0cd5c804 100644 --- a/providers/openstack/hosted-control-plane/cluster-class/templates/cluster-class.yaml +++ b/providers/openstack/hosted-control-plane/cluster-class/templates/cluster-class.yaml @@ -307,7 +307,7 @@ spec: matchResources: controlPlane: true jsonPatches: - - op: replace + - op: add path: "/spec/template/spec/replicas" valueFrom: variable: controlPlaneReplicas diff --git a/providers/openstack/hosted-control-plane/cluster-class/templates/hosted-control-plane-template.yaml b/providers/openstack/hosted-control-plane/cluster-class/templates/hosted-control-plane-template.yaml index c1cff1dd..b1caeea8 100644 --- a/providers/openstack/hosted-control-plane/cluster-class/templates/hosted-control-plane-template.yaml +++ b/providers/openstack/hosted-control-plane/cluster-class/templates/hosted-control-plane-template.yaml @@ -6,5 +6,5 @@ spec: template: spec: gateway: - namespace: - name: + namespace: overridden-by-patch + name: overridden-by-patch diff --git a/providers/openstack/hosted-control-plane/csctl.yaml b/providers/openstack/hosted-control-plane/csctl.yaml index 1b25972e..587a3cbf 100644 --- a/providers/openstack/hosted-control-plane/csctl.yaml +++ b/providers/openstack/hosted-control-plane/csctl.yaml @@ -1,7 +1,7 @@ apiVersion: csctl.clusterstack.x-k8s.io/v1alpha1 config: clusterStackName: hosted-control-plane - kubernetesVersion: v1.33.6 + kubernetesVersion: v1.34.3 provider: apiVersion: openstack.csctl.clusterstack.x-k8s.io/v1alpha1 type: openstack