From db4d83222b034b97f3ee080235a608522f965b6f Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Thu, 18 Jun 2026 16:28:26 +0200 Subject: [PATCH 01/24] Draft initial implementation --- class/defaults.yml | 51 +++- component/main.jsonnet | 272 +++++++++++++++++- .../capi_cluster.yaml | 90 ++++++ .../worker_group_worker.yaml | 52 ++++ 4 files changed, 461 insertions(+), 4 deletions(-) create mode 100644 tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml create mode 100644 tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml diff --git a/class/defaults.yml b/class/defaults.yml index 844aa49..7df9abf 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -1,5 +1,54 @@ parameters: talos_capi_cluster_cloudscale: =_metadata: + multi_instance: true multi_tenant: true - namespace: syn-talos-capi-cluster-cloudscale + namespace: syn-cluster-api + + clusterName: ${cluster:name} + + # TODO(sg): how will updates work with CAPI? + talosVersion: '1.13.4' + # NOTE(sg): this is the well-known default schematic UUID + talosSchematicUUID: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba + kubernetesVersion: '1.36.1' + + cloudscale: + # TODO(sg): document this + customImageSlug: talos-v${talos_capi_cluster_cloudscale:talosVersion}-37656798 + # TODO(sg): decide on default sizing + privateNetwork: + # TODO(sg): does this need to match the privnet name on cloudscale? + name: privnet-${talos_capi_cluster_cloudscale:clusterName} + uuid: TO_BE_REPLACED + region: ${facts:region} + + controlPlane: + count: 1 + flavor: plus-16-4 + rootVolumeSize: 50 + + workerGroups: + worker: + count: 1 + flavor: plus-16-4 + rootVolumeSize: 50 + + cluster: + spec: + clusterNetwork: + pods: + cidrBlocks: + - 10.128.0.0/14 + services: + cidrBlocks: + - 172.30.0.0/16 + serviceDomain: cluster.local + + cloudscaleCluster: + spec: + region: ${talos_capi_cluster_cloudscale:cloudscale:region} + credentialsRef: + name: capcs-cloudscale + + talosControlPlane: {} diff --git a/component/main.jsonnet b/component/main.jsonnet index 00e30bb..bbc6c93 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -1,10 +1,276 @@ -// main template for talos-capi-cluster-cloudscale +local com = import 'lib/commodore.libjsonnet'; local kap = import 'lib/kapitan.libjsonnet'; local kube = import 'lib/kube.libjsonnet'; + local inv = kap.inventory(); -// The hiera parameters for the component local params = inv.parameters.talos_capi_cluster_cloudscale; -// Define outputs below +local cloudscaleImageSlug = 'custom:%s' % params.cloudscale.customImageSlug; + +local resourceSetLabelKey = 'talos-capi-cluster-cloudscale.syn.tools/bootstrap'; + +local capiCluster = params.cluster { + apiVersion: 'cluster.x-k8s.io/v1beta2', + kind: 'Cluster', + metadata+: { + name: params.clusterName, + namespace: params.namespace, + labels+: { + [resourceSetLabelKey]: 'cloudscale', + }, + }, + spec+: { + infrastructureRef: { + apiGroup: 'infrastructure.cluster.x-k8s.io', + kind: 'CloudscaleCluster', + name: params.clusterName, + }, + controlPlaneRef: { + apiGroup: 'controlplane.cluster.x-k8s.io', + kind: 'TalosControlPlane', + name: params.clusterName, + }, + }, +}; + +local capiCloudscaleCluster = params.cloudscaleCluster { + apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', + kind: 'CloudscaleCluster', + metadata+: { + name: params.clusterName, + namespace: params.namespace, + }, + spec+: { + networks: [ + { + name: params.cloudscale.privateNetwork.name, + uuid: params.cloudscale.privateNetwork.uuid, + }, + ], + }, +}; + +local capiCloudscaleMachineTemplateControlPlane = { + apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', + kind: 'CloudscaleMachineTemplate', + metadata+: { + name: '%s-control-plane' % params.clusterName, + namespace: params.namespace, + }, + spec: { + template: { + spec: { + flavor: params.controlPlane.flavor, + image: cloudscaleImageSlug, + rootVolumeSize: params.controlPlane.rootVolumeSize, + serverGroup: { + name: $.metadata.name, + }, + interfaces: [ + { + network: params.cloudscale.privateNetwork.name, + }, + ], + }, + }, + }, +}; + +local talosStrategicPatch = { + machine: { + network: { + interfaces: [ + { + deviceSelector: { + physical: true, + }, + dhcp: true, + }, + ], + }, + install: { + disk: '/dev/sda', + }, + }, + cluster: { + // TODO(sg): document how to inject CCM manifests during bootstrap + externalCloudProvider: { + enabled: true, + }, + }, +}; + +local capiTalosControlPlane = params.talosControlPlane { + apiVersion: 'controlplane.cluster.x-k8s.io/v1alpha3', + kind: 'TalosControlPlane', + metadata+: { + name: params.clusterName, + namespace: params.namespace, + }, + spec+: { + replicas: params.controlPlane.count, + version: params.kubernetesVersion, + infrastructureTemplate: { + apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', + kind: 'CloudscaleMachineTemplate', + name: capiCloudscaleMachineTemplateControlPlane.metadata.name, + }, + controlPlaneConfig: { + controlplane: { + generateType: 'controlplane', + talosVersion: params.talosVersion, + hostname: { + // we want to use the VM name defined by the cloudscale CAPI + // provider. + source: 'InfrastructureName', + }, + strategicPatches: [ + std.manifestJsonMinified(talosStrategicPatch { + machine+: { + install+: { + // TODO(sg): do installers for custom schematic ids even exist? + image: 'factory.talos.dev/openstack-installer/%(schematic_uuid)s:v%(version)s' % { + schematic_uuid: params.talosSchematicUUID, + version: params.talosVersion, + }, + }, + }, + }), + ], + }, + }, + }, +}; + +// NOTE(sg): figure out if this is even needed after initial bootstrap +local capiClusterResourceSetBootstrap = { + apiVersion: 'addons.cluster.x-k8s.io/v1beta2', + kind: 'ClusterResourceSet', + metadata: { + name: 'cloudscale-bootstrap-%s' % params.clusterName, + namespace: params.namespace, + }, + spec: { + strategy: 'ApplyOnce', + clusterSelector: { + matchLabels: { + [resourceSetLabelKey]: 'cloudscale', + }, + }, + resources: [ + // NOTE(sg): the configmaps are externally generated for bootstrap + { + name: '%s-ccm' % params.clusterName, + kind: 'ConfigMap', + }, + { + name: '%s-cilium' % params.clusterName, + kind: 'ConfigMap', + }, + ], + }, +}; + +local capiWorkerGroup(name) = + local machineDeployment = { + apiVersion: 'cluster.x-k8s.io/v1beta2', + kind: 'MachineDeployment', + metadata: { + name: name, + namespace: params.namespace, + }, + spec: { + clusterName: params.clusterName, + replicas: params.workerGroups[name].count, + template: { + spec: { + clusterName: params.clusterName, + version: params.kubernetesVersion, + bootstrap: { + configRef: { + name: name, + apiGroup: 'bootstrap.cluster.x-k8s.io', + kind: 'TalosConfigTemplate', + }, + }, + infrastructureRef: { + name: name, + apiGroup: 'infrastructure.cluster.x-k8s.io', + kind: 'CloudscaleMachineTemplate', + }, + }, + }, + }, + }; + local cloudscaleMachineTemplate = { + apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', + kind: 'CloudscaleMachineTemplate', + metadata: { + name: name, + namespace: params.namespace, + }, + spec: { + template: { + spec: { + flavor: params.workerGroups[name].flavor, + image: cloudscaleImageSlug, + rootVolumeSize: params.workerGroups[name].rootVolumeSize, + serverGroup: { + name: name, + }, + interfaces: [ + { + network: params.cloudscale.privateNetwork.name, + }, + ], + }, + }, + }, + }; + local talosConfigTemplate = { + apiVersion: 'bootstrap.cluster.x-k8s.io/v1alpha3', + kind: 'TalosConfigTemplate', + metadata: { + name: name, + namespace: params.namespace, + }, + spec: { + template: { + spec: { + generateType: 'join', + talosVersion: params.talosVersion, + hostname: { + source: 'InfrastructureName', + }, + strategicPatches: [ + std.manifestJsonMinified(talosStrategicPatch), + ], + }, + }, + }, + }; + + // NOTE(sg): we're slightly abusing com.generateResources() below. The + // function doesn't really support rendering multiple objects instead of + // rendering a single object and merging the parameter dict values into it. + { + name: name, + resources: [ + machineDeployment, + cloudscaleMachineTemplate, + talosConfigTemplate, + ], + }; + { + capi_cluster: [ + capiCluster, + capiCloudscaleCluster, + capiCloudscaleMachineTemplateControlPlane, + capiTalosControlPlane, + capiClusterResourceSetBootstrap, + ], +} + { + ['worker_group_%s' % wg.name]: wg.resources + for wg in com.generateResources(params.workerGroups, capiWorkerGroup) } diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml new file mode 100644 index 0000000..c50263f --- /dev/null +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -0,0 +1,90 @@ +apiVersion: cluster.x-k8s.io/v1beta2 +kind: Cluster +metadata: + labels: + talos-capi-cluster-cloudscale.syn.tools/bootstrap: cloudscale + name: c-green-test-1234 + namespace: syn-cluster-api +spec: + clusterNetwork: + pods: + cidrBlocks: + - 10.128.0.0/14 + serviceDomain: cluster.local + services: + cidrBlocks: + - 172.30.0.0/16 + controlPlaneRef: + apiGroup: controlplane.cluster.x-k8s.io + kind: TalosControlPlane + name: c-green-test-1234 + infrastructureRef: + apiGroup: infrastructure.cluster.x-k8s.io + kind: CloudscaleCluster + name: c-green-test-1234 +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +kind: CloudscaleCluster +metadata: + name: c-green-test-1234 + namespace: syn-cluster-api +spec: + credentialsRef: + name: capcs-cloudscale + networks: + - name: privnet-c-green-test-1234 + uuid: TO_BE_REPLACED + region: rma1 +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +kind: CloudscaleMachineTemplate +metadata: + name: c-green-test-1234-control-plane + namespace: syn-cluster-api +spec: + template: + spec: + flavor: plus-16-4 + image: custom:talos-v1.13.4-37656798 + interfaces: + - network: privnet-c-green-test-1234 + rootVolumeSize: 50 + serverGroup: + name: c-green-test-1234-control-plane +--- +apiVersion: controlplane.cluster.x-k8s.io/v1alpha3 +kind: TalosControlPlane +metadata: + name: c-green-test-1234 + namespace: syn-cluster-api +spec: + controlPlaneConfig: + controlplane: + generateType: controlplane + hostname: + source: InfrastructureName + strategicPatches: + - '{"cluster":{"externalCloudProvider":{"enabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.4"},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + talosVersion: 1.13.4 + infrastructureTemplate: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + kind: CloudscaleMachineTemplate + name: c-green-test-1234-control-plane + replicas: 1 + version: 1.36.1 +--- +apiVersion: addons.cluster.x-k8s.io/v1beta2 +kind: ClusterResourceSet +metadata: + name: cloudscale-bootstrap-c-green-test-1234 + namespace: syn-cluster-api +spec: + clusterSelector: + matchLabels: + talos-capi-cluster-cloudscale.syn.tools/bootstrap: cloudscale + resources: + - kind: ConfigMap + name: c-green-test-1234-ccm + - kind: ConfigMap + name: c-green-test-1234-cilium + strategy: ApplyOnce diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml new file mode 100644 index 0000000..ff738c1 --- /dev/null +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -0,0 +1,52 @@ +apiVersion: cluster.x-k8s.io/v1beta2 +kind: MachineDeployment +metadata: + name: worker + namespace: syn-cluster-api +spec: + clusterName: c-green-test-1234 + replicas: 1 + template: + spec: + bootstrap: + configRef: + apiGroup: bootstrap.cluster.x-k8s.io + kind: TalosConfigTemplate + name: worker + clusterName: c-green-test-1234 + infrastructureRef: + apiGroup: infrastructure.cluster.x-k8s.io + kind: CloudscaleMachineTemplate + name: worker + version: 1.36.1 +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +kind: CloudscaleMachineTemplate +metadata: + name: worker + namespace: syn-cluster-api +spec: + template: + spec: + flavor: plus-16-4 + image: custom:talos-v1.13.4-37656798 + interfaces: + - network: privnet-c-green-test-1234 + rootVolumeSize: 50 + serverGroup: + name: worker +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 +kind: TalosConfigTemplate +metadata: + name: worker + namespace: syn-cluster-api +spec: + template: + spec: + generateType: join + hostname: + source: InfrastructureName + strategicPatches: + - '{"cluster":{"externalCloudProvider":{"enabled":true}},"machine":{"install":{"disk":"/dev/sda"},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + talosVersion: 1.13.4 From 8e4ccc8d7922ed326a43f6ef92ddeac70521c28b Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Tue, 23 Jun 2026 20:27:20 +0200 Subject: [PATCH 02/24] Refactor component to generate unique names for manifests with immutable fields in `spec` --- class/defaults.yml | 4 +- component/main.jsonnet | 135 ++++++++++-------- .../capi_cluster.yaml | 4 +- .../worker_group_worker.yaml | 11 +- 4 files changed, 86 insertions(+), 68 deletions(-) diff --git a/class/defaults.yml b/class/defaults.yml index 7df9abf..c2eca9f 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -51,4 +51,6 @@ parameters: credentialsRef: name: capcs-cloudscale - talosControlPlane: {} + talosControlPlane: + metadata: {} + spec: {} diff --git a/component/main.jsonnet b/component/main.jsonnet index bbc6c93..dec695e 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -9,38 +9,21 @@ local cloudscaleImageSlug = 'custom:%s' % params.cloudscale.customImageSlug; local resourceSetLabelKey = 'talos-capi-cluster-cloudscale.syn.tools/bootstrap'; -local capiCluster = params.cluster { - apiVersion: 'cluster.x-k8s.io/v1beta2', - kind: 'Cluster', - metadata+: { - name: params.clusterName, - namespace: params.namespace, - labels+: { - [resourceSetLabelKey]: 'cloudscale', - }, - }, - spec+: { - infrastructureRef: { - apiGroup: 'infrastructure.cluster.x-k8s.io', - kind: 'CloudscaleCluster', - name: params.clusterName, - }, - controlPlaneRef: { - apiGroup: 'controlplane.cluster.x-k8s.io', - kind: 'TalosControlPlane', - name: params.clusterName, - }, - }, -}; +// TODO(sg): figure out which resources need to have `nameWithHash()` +local nameWithHash(name, spec, length=16) = + '%s-%s' % [ + name, + std.sha256(std.manifestJsonMinified(spec))[:length], + ]; -local capiCloudscaleCluster = params.cloudscaleCluster { +local capiCloudscaleCluster = { apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', kind: 'CloudscaleCluster', - metadata+: { + metadata+: std.get(params.cloudscaleCluster, 'metadata', {}) { name: params.clusterName, namespace: params.namespace, }, - spec+: { + spec+: params.cloudscaleCluster.spec { networks: [ { name: params.cloudscale.privateNetwork.name, @@ -54,7 +37,7 @@ local capiCloudscaleMachineTemplateControlPlane = { apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', kind: 'CloudscaleMachineTemplate', metadata+: { - name: '%s-control-plane' % params.clusterName, + name: nameWithHash('%s-control-plane' % params.clusterName, $.spec), namespace: params.namespace, }, spec: { @@ -64,7 +47,7 @@ local capiCloudscaleMachineTemplateControlPlane = { image: cloudscaleImageSlug, rootVolumeSize: params.controlPlane.rootVolumeSize, serverGroup: { - name: $.metadata.name, + name: '%s-control-plane' % params.clusterName, }, interfaces: [ { @@ -103,11 +86,11 @@ local talosStrategicPatch = { local capiTalosControlPlane = params.talosControlPlane { apiVersion: 'controlplane.cluster.x-k8s.io/v1alpha3', kind: 'TalosControlPlane', - metadata+: { + metadata+: std.get(params.talosControlPlane, 'metadata', {}) { name: params.clusterName, namespace: params.namespace, }, - spec+: { + spec+: params.talosControlPlane.spec { replicas: params.controlPlane.count, version: params.kubernetesVersion, infrastructureTemplate: { @@ -129,6 +112,7 @@ local capiTalosControlPlane = params.talosControlPlane { machine+: { install+: { // TODO(sg): do installers for custom schematic ids even exist? + // TODO(sg): figure out the new way to do this image: 'factory.talos.dev/openstack-installer/%(schematic_uuid)s:v%(version)s' % { schematic_uuid: params.talosSchematicUUID, version: params.talosVersion, @@ -172,32 +156,24 @@ local capiClusterResourceSetBootstrap = { }; local capiWorkerGroup(name) = - local machineDeployment = { - apiVersion: 'cluster.x-k8s.io/v1beta2', - kind: 'MachineDeployment', + local talosConfigTemplate = { + apiVersion: 'bootstrap.cluster.x-k8s.io/v1alpha3', + kind: 'TalosConfigTemplate', metadata: { - name: name, + name: nameWithHash(name, $.spec), namespace: params.namespace, }, spec: { - clusterName: params.clusterName, - replicas: params.workerGroups[name].count, template: { spec: { - clusterName: params.clusterName, - version: params.kubernetesVersion, - bootstrap: { - configRef: { - name: name, - apiGroup: 'bootstrap.cluster.x-k8s.io', - kind: 'TalosConfigTemplate', - }, - }, - infrastructureRef: { - name: name, - apiGroup: 'infrastructure.cluster.x-k8s.io', - kind: 'CloudscaleMachineTemplate', + generateType: 'join', + talosVersion: params.talosVersion, + hostname: { + source: 'InfrastructureName', }, + strategicPatches: [ + std.manifestJsonMinified(talosStrategicPatch), + ], }, }, }, @@ -206,7 +182,7 @@ local capiWorkerGroup(name) = apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', kind: 'CloudscaleMachineTemplate', metadata: { - name: name, + name: nameWithHash(name, $.spec), namespace: params.namespace, }, spec: { @@ -227,24 +203,37 @@ local capiWorkerGroup(name) = }, }, }; - local talosConfigTemplate = { - apiVersion: 'bootstrap.cluster.x-k8s.io/v1alpha3', - kind: 'TalosConfigTemplate', + local machineDeployment = { + apiVersion: 'cluster.x-k8s.io/v1beta2', + kind: 'MachineDeployment', metadata: { name: name, namespace: params.namespace, }, spec: { - template: { + clusterName: params.clusterName, + replicas: params.workerGroups[name].count, + template: std.get(params.workerGroups[name], 'template', {}) { + metadata: { + labels+: { + 'node-role.kubernetes.io/worker': '', + }, + }, spec: { - generateType: 'join', - talosVersion: params.talosVersion, - hostname: { - source: 'InfrastructureName', + clusterName: params.clusterName, + version: params.kubernetesVersion, + bootstrap: { + configRef: { + name: talosConfigTemplate.metadata.name, + apiGroup: 'bootstrap.cluster.x-k8s.io', + kind: 'TalosConfigTemplate', + }, + }, + infrastructureRef: { + name: cloudscaleMachineTemplate.metadata.name, + apiGroup: 'infrastructure.cluster.x-k8s.io', + kind: 'CloudscaleMachineTemplate', }, - strategicPatches: [ - std.manifestJsonMinified(talosStrategicPatch), - ], }, }, }, @@ -262,6 +251,30 @@ local capiWorkerGroup(name) = ], }; +local capiCluster = params.cluster { + apiVersion: 'cluster.x-k8s.io/v1beta2', + kind: 'Cluster', + metadata+: { + name: params.clusterName, + namespace: params.namespace, + labels+: { + [resourceSetLabelKey]: 'cloudscale', + }, + }, + spec+: { + infrastructureRef: { + apiGroup: 'infrastructure.cluster.x-k8s.io', + kind: 'CloudscaleCluster', + name: capiCloudscaleCluster.metadata.name, + }, + controlPlaneRef: { + apiGroup: 'controlplane.cluster.x-k8s.io', + kind: 'TalosControlPlane', + name: capiTalosControlPlane.metadata.name, + }, + }, +}; + { capi_cluster: [ capiCluster, diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index c50263f..4cc9ebf 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -39,7 +39,7 @@ spec: apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleMachineTemplate metadata: - name: c-green-test-1234-control-plane + name: c-green-test-1234-control-plane-e84c4ba30597bd6a namespace: syn-cluster-api spec: template: @@ -69,7 +69,7 @@ spec: infrastructureTemplate: apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleMachineTemplate - name: c-green-test-1234-control-plane + name: c-green-test-1234-control-plane-e84c4ba30597bd6a replicas: 1 version: 1.36.1 --- diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index ff738c1..c5be5ce 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -7,23 +7,26 @@ spec: clusterName: c-green-test-1234 replicas: 1 template: + metadata: + labels: + node-role.kubernetes.io/worker: '' spec: bootstrap: configRef: apiGroup: bootstrap.cluster.x-k8s.io kind: TalosConfigTemplate - name: worker + name: worker-0dd528d045332e9c clusterName: c-green-test-1234 infrastructureRef: apiGroup: infrastructure.cluster.x-k8s.io kind: CloudscaleMachineTemplate - name: worker + name: worker-cb7ea8521a7ab803 version: 1.36.1 --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleMachineTemplate metadata: - name: worker + name: worker-cb7ea8521a7ab803 namespace: syn-cluster-api spec: template: @@ -39,7 +42,7 @@ spec: apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate metadata: - name: worker + name: worker-0dd528d045332e9c namespace: syn-cluster-api spec: template: From 902bec5e6242b5fddd8332b70aa0c1099ff1184f Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Tue, 23 Jun 2026 20:28:25 +0200 Subject: [PATCH 03/24] Adjust Talos machineconfig to deploy Cilium via `ClusterResourceSet` during bootstrap --- class/defaults.yml | 2 + component/main.jsonnet | 43 +++++++++++++------ tests/defaults.yml | 8 +++- .../capi_cluster.yaml | 2 +- .../worker_group_worker.yaml | 6 +-- 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/class/defaults.yml b/class/defaults.yml index c2eca9f..d531903 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -13,6 +13,8 @@ parameters: talosSchematicUUID: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba kubernetesVersion: '1.36.1' + cni: cilium + cloudscale: # TODO(sg): document this customImageSlug: talos-v${talos_capi_cluster_cloudscale:talosVersion}-37656798 diff --git a/component/main.jsonnet b/component/main.jsonnet index dec695e..f5b59bb 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -80,6 +80,22 @@ local talosStrategicPatch = { externalCloudProvider: { enabled: true, }, + network: { + cni: { + // valid values: `flannel`, `custom`, `none`. + // `custom` uses custom manifests provided via `cni.urls` + // `none` indicates externally provisioned & managed CNI + // we currently assume that we'll always deploy custom CNIs via CAPI + // resourcesets. + [if params.cni != 'flannel' then 'name']: 'none', + }, + }, + proxy: { + disabled: if params.cni == 'cilium' then + inv.parameters.cilium.cilium_helm_values.kubeProxyReplacement == 'true' + else + std.trace('Not disabling kube-proxy for CNI %s' % params.cni, false), + }, }, }; @@ -275,15 +291,18 @@ local capiCluster = params.cluster { }, }; -{ - capi_cluster: [ - capiCluster, - capiCloudscaleCluster, - capiCloudscaleMachineTemplateControlPlane, - capiTalosControlPlane, - capiClusterResourceSetBootstrap, - ], -} + { - ['worker_group_%s' % wg.name]: wg.resources - for wg in com.generateResources(params.workerGroups, capiWorkerGroup) -} +if params.cni == 'cilium' && !std.member(inv.applications, 'cilium') then + error 'Component talos-capi-cluster-cloudscale expects that component-cilium is present when parameter cni=cilium' +else + { + capi_cluster: [ + capiCluster, + capiCloudscaleCluster, + capiCloudscaleMachineTemplateControlPlane, + capiTalosControlPlane, + capiClusterResourceSetBootstrap, + ], + } + { + ['worker_group_%s' % wg.name]: wg.resources + for wg in com.generateResources(params.workerGroups, capiWorkerGroup) + } diff --git a/tests/defaults.yml b/tests/defaults.yml index a4da5b7..f5d828d 100644 --- a/tests/defaults.yml +++ b/tests/defaults.yml @@ -1,3 +1,7 @@ -# Overwrite parameters here +applications: + - cilium -# parameters: {...} +parameters: + cilium: + cilium_helm_values: + kubeProxyReplacement: "true" diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index 4cc9ebf..0306406 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -64,7 +64,7 @@ spec: hostname: source: InfrastructureName strategicPatches: - - '{"cluster":{"externalCloudProvider":{"enabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.4"},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.4"},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' talosVersion: 1.13.4 infrastructureTemplate: apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index c5be5ce..a066568 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -15,7 +15,7 @@ spec: configRef: apiGroup: bootstrap.cluster.x-k8s.io kind: TalosConfigTemplate - name: worker-0dd528d045332e9c + name: worker-95f76d53508bb41f clusterName: c-green-test-1234 infrastructureRef: apiGroup: infrastructure.cluster.x-k8s.io @@ -42,7 +42,7 @@ spec: apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate metadata: - name: worker-0dd528d045332e9c + name: worker-95f76d53508bb41f namespace: syn-cluster-api spec: template: @@ -51,5 +51,5 @@ spec: hostname: source: InfrastructureName strategicPatches: - - '{"cluster":{"externalCloudProvider":{"enabled":true}},"machine":{"install":{"disk":"/dev/sda"},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda"},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' talosVersion: 1.13.4 From c38b80e18b244c3c496646851b868d3b497a4845 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Tue, 23 Jun 2026 20:29:22 +0200 Subject: [PATCH 04/24] Update TalosControlPlane api version to v1beta1 Also adjust machinetemplate reference for v1beta1 Needs https://github.com/siderolabs/cluster-api-control-plane-provider-talos/pull/251 or equivalent. --- component/main.jsonnet | 16 ++++++++++------ .../capi_cluster.yaml | 12 +++++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/component/main.jsonnet b/component/main.jsonnet index f5b59bb..9cd972e 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -99,8 +99,8 @@ local talosStrategicPatch = { }, }; -local capiTalosControlPlane = params.talosControlPlane { - apiVersion: 'controlplane.cluster.x-k8s.io/v1alpha3', +local capiTalosControlPlane = { + apiVersion: 'controlplane.cluster.x-k8s.io/v1beta1', kind: 'TalosControlPlane', metadata+: std.get(params.talosControlPlane, 'metadata', {}) { name: params.clusterName, @@ -109,10 +109,14 @@ local capiTalosControlPlane = params.talosControlPlane { spec+: params.talosControlPlane.spec { replicas: params.controlPlane.count, version: params.kubernetesVersion, - infrastructureTemplate: { - apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', - kind: 'CloudscaleMachineTemplate', - name: capiCloudscaleMachineTemplateControlPlane.metadata.name, + machineTemplate: { + spec: { + infrastructureRef: { + apiGroup: 'infrastructure.cluster.x-k8s.io', + kind: 'CloudscaleMachineTemplate', + name: capiCloudscaleMachineTemplateControlPlane.metadata.name, + }, + }, }, controlPlaneConfig: { controlplane: { diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index 0306406..b36345c 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -52,7 +52,7 @@ spec: serverGroup: name: c-green-test-1234-control-plane --- -apiVersion: controlplane.cluster.x-k8s.io/v1alpha3 +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 kind: TalosControlPlane metadata: name: c-green-test-1234 @@ -66,10 +66,12 @@ spec: strategicPatches: - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.4"},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' talosVersion: 1.13.4 - infrastructureTemplate: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 - kind: CloudscaleMachineTemplate - name: c-green-test-1234-control-plane-e84c4ba30597bd6a + machineTemplate: + spec: + infrastructureRef: + apiGroup: infrastructure.cluster.x-k8s.io + kind: CloudscaleMachineTemplate + name: c-green-test-1234-control-plane-e84c4ba30597bd6a replicas: 1 version: 1.36.1 --- From 06ee1980d6575a7cab266248327b0226fa8c7386 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Fri, 26 Jun 2026 08:59:39 +0200 Subject: [PATCH 05/24] Add parameter to set API URL as extra Talos API server cert SAN --- class/defaults.yml | 1 + component/main.jsonnet | 3 +++ 2 files changed, 4 insertions(+) diff --git a/class/defaults.yml b/class/defaults.yml index d531903..94a2932 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -12,6 +12,7 @@ parameters: # NOTE(sg): this is the well-known default schematic UUID talosSchematicUUID: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba kubernetesVersion: '1.36.1' + apiURL: "" cni: cilium diff --git a/component/main.jsonnet b/component/main.jsonnet index 9cd972e..297c8be 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -76,6 +76,9 @@ local talosStrategicPatch = { }, }, cluster: { + [if params.apiURL != '' then 'apiServer']: { + certSANs: [ params.apiURL ], + }, // TODO(sg): document how to inject CCM manifests during bootstrap externalCloudProvider: { enabled: true, From 16f8b579570ab1d9a7f15fa2bf973b507d2fe618 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Fri, 26 Jun 2026 09:00:05 +0200 Subject: [PATCH 06/24] Add talosconfig patch todo --- component/main.jsonnet | 1 + 1 file changed, 1 insertion(+) diff --git a/component/main.jsonnet b/component/main.jsonnet index 297c8be..560003b 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -75,6 +75,7 @@ local talosStrategicPatch = { disk: '/dev/sda', }, }, + // TODO(sg): figure out if this section is really needed for worker groups. cluster: { [if params.apiURL != '' then 'apiServer']: { certSANs: [ params.apiURL ], From e145cb8db48e56f3f84810efa759bb66d210b848 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Thu, 2 Jul 2026 13:10:22 +0200 Subject: [PATCH 07/24] CAPI provider canonical Kubernetes version format is prefixed with `v` Update component defaults and rewrite non-prefixed versions. --- class/defaults.yml | 2 +- component/main.jsonnet | 16 ++++++++++++++-- .../capi_cluster.yaml | 2 +- .../worker_group_worker.yaml | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/class/defaults.yml b/class/defaults.yml index 94a2932..6af83e4 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -11,7 +11,7 @@ parameters: talosVersion: '1.13.4' # NOTE(sg): this is the well-known default schematic UUID talosSchematicUUID: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba - kubernetesVersion: '1.36.1' + kubernetesVersion: 'v1.36.1' apiURL: "" cni: cilium diff --git a/component/main.jsonnet b/component/main.jsonnet index 560003b..fa500f1 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -9,6 +9,18 @@ local cloudscaleImageSlug = 'custom:%s' % params.cloudscale.customImageSlug; local resourceSetLabelKey = 'talos-capi-cluster-cloudscale.syn.tools/bootstrap'; +local kubernetesVersion = + local formatter = + if std.startsWith(params.kubernetesVersion, 'v') then + '%s' + else + std.trace( + "CAPI expects kubernetesVersion to be prefixed with 'v', adjusting %s" + % params.kubernetesVersion, + 'v%s' + ); + formatter % params.kubernetesVersion; + // TODO(sg): figure out which resources need to have `nameWithHash()` local nameWithHash(name, spec, length=16) = '%s-%s' % [ @@ -112,7 +124,7 @@ local capiTalosControlPlane = { }, spec+: params.talosControlPlane.spec { replicas: params.controlPlane.count, - version: params.kubernetesVersion, + version: kubernetesVersion, machineTemplate: { spec: { infrastructureRef: { @@ -245,7 +257,7 @@ local capiWorkerGroup(name) = }, spec: { clusterName: params.clusterName, - version: params.kubernetesVersion, + version: kubernetesVersion, bootstrap: { configRef: { name: talosConfigTemplate.metadata.name, diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index b36345c..f74e521 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -73,7 +73,7 @@ spec: kind: CloudscaleMachineTemplate name: c-green-test-1234-control-plane-e84c4ba30597bd6a replicas: 1 - version: 1.36.1 + version: v1.36.1 --- apiVersion: addons.cluster.x-k8s.io/v1beta2 kind: ClusterResourceSet diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index a066568..c899bd5 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -21,7 +21,7 @@ spec: apiGroup: infrastructure.cluster.x-k8s.io kind: CloudscaleMachineTemplate name: worker-cb7ea8521a7ab803 - version: 1.36.1 + version: v1.36.1 --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleMachineTemplate From 414ef435184599e030276aff0462e15925e1c875 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Thu, 2 Jul 2026 16:56:38 +0200 Subject: [PATCH 08/24] Set Talos `MachineConfig` `install.wipe=true` by default TODO: figture out if/how we want to allow users to adjust the patches. --- component/main.jsonnet | 1 + .../talos-capi-cluster-cloudscale/capi_cluster.yaml | 2 +- .../talos-capi-cluster-cloudscale/worker_group_worker.yaml | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/component/main.jsonnet b/component/main.jsonnet index fa500f1..e148e23 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -85,6 +85,7 @@ local talosStrategicPatch = { }, install: { disk: '/dev/sda', + wipe: true, }, }, // TODO(sg): figure out if this section is really needed for worker groups. diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index f74e521..e51ca90 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -64,7 +64,7 @@ spec: hostname: source: InfrastructureName strategicPatches: - - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.4"},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.4","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' talosVersion: 1.13.4 machineTemplate: spec: diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index c899bd5..a06943b 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -15,7 +15,7 @@ spec: configRef: apiGroup: bootstrap.cluster.x-k8s.io kind: TalosConfigTemplate - name: worker-95f76d53508bb41f + name: worker-d7863e308f443d77 clusterName: c-green-test-1234 infrastructureRef: apiGroup: infrastructure.cluster.x-k8s.io @@ -42,7 +42,7 @@ spec: apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate metadata: - name: worker-95f76d53508bb41f + name: worker-d7863e308f443d77 namespace: syn-cluster-api spec: template: @@ -51,5 +51,5 @@ spec: hostname: source: InfrastructureName strategicPatches: - - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda"},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' talosVersion: 1.13.4 From 6488ddcc69ff26b2516d2447335a6d14a555e530 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Fri, 3 Jul 2026 08:31:43 +0200 Subject: [PATCH 09/24] Give users more control over TalosControlPlane config --- class/defaults.yml | 1 + component/main.jsonnet | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/class/defaults.yml b/class/defaults.yml index 6af83e4..affc0f9 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -57,3 +57,4 @@ parameters: talosControlPlane: metadata: {} spec: {} + strategicPatches: {} diff --git a/component/main.jsonnet b/component/main.jsonnet index e148e23..f86b6b0 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -119,7 +119,7 @@ local talosStrategicPatch = { local capiTalosControlPlane = { apiVersion: 'controlplane.cluster.x-k8s.io/v1beta1', kind: 'TalosControlPlane', - metadata+: std.get(params.talosControlPlane, 'metadata', {}) { + metadata: std.get(params.talosControlPlane, 'metadata', {}) { name: params.clusterName, namespace: params.namespace, }, @@ -135,8 +135,8 @@ local capiTalosControlPlane = { }, }, }, - controlPlaneConfig: { - controlplane: { + controlPlaneConfig+: { + controlplane+: { generateType: 'controlplane', talosVersion: params.talosVersion, hostname: { @@ -150,6 +150,7 @@ local capiTalosControlPlane = { install+: { // TODO(sg): do installers for custom schematic ids even exist? // TODO(sg): figure out the new way to do this + // TODO(sg): do we even need this at all? image: 'factory.talos.dev/openstack-installer/%(schematic_uuid)s:v%(version)s' % { schematic_uuid: params.talosSchematicUUID, version: params.talosVersion, @@ -157,6 +158,9 @@ local capiTalosControlPlane = { }, }, }), + ] + [ + std.manifestJsonMinified(patch) + for patch in std.objectValues(params.talosControlPlane.strategicPatches) ], }, }, From 31bc41285980a9314be59ac524f0697b0aa79896 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Fri, 3 Jul 2026 08:45:11 +0200 Subject: [PATCH 10/24] Add parameter `strategicPatches` to apply custom patches to all nodes --- class/defaults.yml | 2 ++ component/main.jsonnet | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/class/defaults.yml b/class/defaults.yml index affc0f9..f71657e 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -58,3 +58,5 @@ parameters: metadata: {} spec: {} strategicPatches: {} + + talosStrategicPatches: {} diff --git a/component/main.jsonnet b/component/main.jsonnet index f86b6b0..cc85bb5 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -116,6 +116,11 @@ local talosStrategicPatch = { }, }; +local strategicPatches = [ + std.manifestJsonMinified(patch) + for patch in std.objectValues(params.talosStrategicPatches) +]; + local capiTalosControlPlane = { apiVersion: 'controlplane.cluster.x-k8s.io/v1beta1', kind: 'TalosControlPlane', @@ -144,7 +149,7 @@ local capiTalosControlPlane = { // provider. source: 'InfrastructureName', }, - strategicPatches: [ + strategicPatches: strategicPatches + [ std.manifestJsonMinified(talosStrategicPatch { machine+: { install+: { @@ -212,7 +217,7 @@ local capiWorkerGroup(name) = hostname: { source: 'InfrastructureName', }, - strategicPatches: [ + strategicPatches: strategicPatches + [ std.manifestJsonMinified(talosStrategicPatch), ], }, From 9a955f4f05306d33b9606155c65e13deae7fbc62 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Fri, 3 Jul 2026 09:24:01 +0200 Subject: [PATCH 11/24] Set `machine.install.image` on all nodes This field is required for Tuppr to be able to perform upgrades. --- component/main.jsonnet | 26 +++++++------------ .../worker_group_worker.yaml | 6 ++--- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/component/main.jsonnet b/component/main.jsonnet index cc85bb5..a4126c1 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -86,6 +86,11 @@ local talosStrategicPatch = { install: { disk: '/dev/sda', wipe: true, + // NOTE(sg): image is required by Tuppr in order to compute the update + image: 'factory.talos.dev/openstack-installer/%(schematic_uuid)s:v%(version)s' % { + schematic_uuid: params.talosSchematicUUID, + version: params.talosVersion, + }, }, }, // TODO(sg): figure out if this section is really needed for worker groups. @@ -116,9 +121,12 @@ local talosStrategicPatch = { }, }; +// TODO(sg): does order matter here? local strategicPatches = [ std.manifestJsonMinified(patch) for patch in std.objectValues(params.talosStrategicPatches) +] + [ + std.manifestJsonMinified(talosStrategicPatch), ]; local capiTalosControlPlane = { @@ -150,20 +158,6 @@ local capiTalosControlPlane = { source: 'InfrastructureName', }, strategicPatches: strategicPatches + [ - std.manifestJsonMinified(talosStrategicPatch { - machine+: { - install+: { - // TODO(sg): do installers for custom schematic ids even exist? - // TODO(sg): figure out the new way to do this - // TODO(sg): do we even need this at all? - image: 'factory.talos.dev/openstack-installer/%(schematic_uuid)s:v%(version)s' % { - schematic_uuid: params.talosSchematicUUID, - version: params.talosVersion, - }, - }, - }, - }), - ] + [ std.manifestJsonMinified(patch) for patch in std.objectValues(params.talosControlPlane.strategicPatches) ], @@ -217,9 +211,7 @@ local capiWorkerGroup(name) = hostname: { source: 'InfrastructureName', }, - strategicPatches: strategicPatches + [ - std.manifestJsonMinified(talosStrategicPatch), - ], + strategicPatches: strategicPatches, }, }, }, diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index a06943b..bc002af 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -15,7 +15,7 @@ spec: configRef: apiGroup: bootstrap.cluster.x-k8s.io kind: TalosConfigTemplate - name: worker-d7863e308f443d77 + name: worker-e7cba96fd5f9ccbc clusterName: c-green-test-1234 infrastructureRef: apiGroup: infrastructure.cluster.x-k8s.io @@ -42,7 +42,7 @@ spec: apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate metadata: - name: worker-d7863e308f443d77 + name: worker-e7cba96fd5f9ccbc namespace: syn-cluster-api spec: template: @@ -51,5 +51,5 @@ spec: hostname: source: InfrastructureName strategicPatches: - - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.4","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' talosVersion: 1.13.4 From b7d76ec0d7a0eba99bab8cd3eb1f8c7ad483c886 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Fri, 3 Jul 2026 09:40:13 +0200 Subject: [PATCH 12/24] Configure ArgoCD to ignore changes to `spec.replicas` of MachineDeployment --- component/app.jsonnet | 17 ++++++++++++++++- .../apps/talos-capi-cluster-cloudscale.yaml | 9 +++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/component/app.jsonnet b/component/app.jsonnet index 934fccb..18049b0 100644 --- a/component/app.jsonnet +++ b/component/app.jsonnet @@ -3,7 +3,22 @@ local inv = kap.inventory(); local params = inv.parameters.talos_capi_cluster_cloudscale; local argocd = import 'lib/argocd.libjsonnet'; -local app = argocd.App('talos-capi-cluster-cloudscale', params.namespace); +local app = argocd.App('talos-capi-cluster-cloudscale', params.namespace) { + spec+: { + ignoreDifferences+: [ + { + group: 'cluster.x-k8s.io', + kind: 'MachineDeployment', + jsonPointers: [ '/spec/replicas' ], + }, + ], + syncPolicy+: { + syncOptions+: [ + 'RespectIgnoreDifferences=true', + ], + }, + }, +}; local appPath = local project = std.get(std.get(app, 'spec', {}), 'project', 'syn'); diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/apps/talos-capi-cluster-cloudscale.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/apps/talos-capi-cluster-cloudscale.yaml index e69de29..5861bd2 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/apps/talos-capi-cluster-cloudscale.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/apps/talos-capi-cluster-cloudscale.yaml @@ -0,0 +1,9 @@ +spec: + ignoreDifferences: + - group: cluster.x-k8s.io + jsonPointers: + - /spec/replicas + kind: MachineDeployment + syncPolicy: + syncOptions: + - RespectIgnoreDifferences=true From 0742b1a068f26feb3d9549a513e68faf7d595c75 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Fri, 3 Jul 2026 10:38:14 +0200 Subject: [PATCH 13/24] Make MachineDeployment deletion order configurable And set default to `Oldest` --- component/main.jsonnet | 12 ++++++++++++ .../worker_group_worker.yaml | 2 ++ 2 files changed, 14 insertions(+) diff --git a/component/main.jsonnet b/component/main.jsonnet index a4126c1..1b66ccc 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -241,6 +241,14 @@ local capiWorkerGroup(name) = }, }, }; + local mdDeletionOrder = + local valOrDefault = std.get(params.workerGroups[name], 'deletionOrder', 'Oldest'); + local validDeletionOrders = [ 'Newest', 'Oldest', 'Random' ]; + assert + std.member(validDeletionOrders, valOrDefault) + : "Invalid value '%s' for deletion order for machinedeployment '%s': " % [ valOrDefault, name ] + + 'valid options are %s' % validDeletionOrders; + valOrDefault; local machineDeployment = { apiVersion: 'cluster.x-k8s.io/v1beta2', kind: 'MachineDeployment', @@ -251,6 +259,10 @@ local capiWorkerGroup(name) = spec: { clusterName: params.clusterName, replicas: params.workerGroups[name].count, + deletion: { + // TODO(sg): decide how we want to expose useful config options. + order: std.get(params.workerGroups[name], 'deletionOrder', 'Oldest'), + }, template: std.get(params.workerGroups[name], 'template', {}) { metadata: { labels+: { diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index bc002af..9fdac39 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -5,6 +5,8 @@ metadata: namespace: syn-cluster-api spec: clusterName: c-green-test-1234 + deletion: + order: Oldest replicas: 1 template: metadata: From fb31f453ad9138c7d0e08ec16a0927f58b7c1534 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Mon, 6 Jul 2026 09:33:32 +0200 Subject: [PATCH 14/24] Update component to make parameter `talosVersion` less misleading We default to patch version `0` for `install.image` in the MachineConfig. This should be unproblematic since the field only needs to be present in order for Tuppr to be able to determine the base image for upgrades and the whole `install` section has no effect on machines created from the OpenStack raw base image. --- class/defaults.yml | 6 +++++- component/main.jsonnet | 20 ++++++++++++++++--- .../capi_cluster.yaml | 10 +++++----- .../worker_group_worker.yaml | 14 ++++++------- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/class/defaults.yml b/class/defaults.yml index f71657e..31adca5 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -8,7 +8,11 @@ parameters: clusterName: ${cluster:name} # TODO(sg): how will updates work with CAPI? - talosVersion: '1.13.4' + # IMPORTANT: this is only used for ensuring CAPI renders a suitable + # MachineConfig and isn't used to define the cluster's actual Talos + # version (at least when using the OpenStack raw image from the image + # factory). + talosVersion: '1.13' # NOTE(sg): this is the well-known default schematic UUID talosSchematicUUID: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba kubernetesVersion: 'v1.36.1' diff --git a/component/main.jsonnet b/component/main.jsonnet index 1b66ccc..43eb90a 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -5,6 +5,19 @@ local kube = import 'lib/kube.libjsonnet'; local inv = kap.inventory(); local params = inv.parameters.talos_capi_cluster_cloudscale; +local validateTalosVersion(tver) = + local parts = std.split(tver, '.'); + assert std.length(parts) == 2 : 'Expected Talos version to contain exacty 1 dot'; + local major = std.parseJson(parts[0]); + local minor = std.parseJson(parts[1]); + if !std.isInteger(major) || !std.isInteger(minor) then + error "Expected Talos version to be '.', got '%s'" % tver + else + { + major: major, + minor: minor, + }; + local cloudscaleImageSlug = 'custom:%s' % params.cloudscale.customImageSlug; local resourceSetLabelKey = 'talos-capi-cluster-cloudscale.syn.tools/bootstrap'; @@ -89,7 +102,8 @@ local talosStrategicPatch = { // NOTE(sg): image is required by Tuppr in order to compute the update image: 'factory.talos.dev/openstack-installer/%(schematic_uuid)s:v%(version)s' % { schematic_uuid: params.talosSchematicUUID, - version: params.talosVersion, + version: + '%(major)s.%(minor)s.0' % validateTalosVersion(params.talosVersion), }, }, }, @@ -151,7 +165,7 @@ local capiTalosControlPlane = { controlPlaneConfig+: { controlplane+: { generateType: 'controlplane', - talosVersion: params.talosVersion, + talosVersion: '%(major)s.%(minor)s' % validateTalosVersion(params.talosVersion), hostname: { // we want to use the VM name defined by the cloudscale CAPI // provider. @@ -207,7 +221,7 @@ local capiWorkerGroup(name) = template: { spec: { generateType: 'join', - talosVersion: params.talosVersion, + talosVersion: '%(major)s.%(minor)s' % validateTalosVersion(params.talosVersion), hostname: { source: 'InfrastructureName', }, diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index e51ca90..32c2acc 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -39,13 +39,13 @@ spec: apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleMachineTemplate metadata: - name: c-green-test-1234-control-plane-e84c4ba30597bd6a + name: c-green-test-1234-control-plane-1a05392e618ad8f5 namespace: syn-cluster-api spec: template: spec: flavor: plus-16-4 - image: custom:talos-v1.13.4-37656798 + image: custom:talos-v1.13-37656798 interfaces: - network: privnet-c-green-test-1234 rootVolumeSize: 50 @@ -64,14 +64,14 @@ spec: hostname: source: InfrastructureName strategicPatches: - - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.4","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' - talosVersion: 1.13.4 + - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.0","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + talosVersion: '1.13' machineTemplate: spec: infrastructureRef: apiGroup: infrastructure.cluster.x-k8s.io kind: CloudscaleMachineTemplate - name: c-green-test-1234-control-plane-e84c4ba30597bd6a + name: c-green-test-1234-control-plane-1a05392e618ad8f5 replicas: 1 version: v1.36.1 --- diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index 9fdac39..24ccf93 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -17,24 +17,24 @@ spec: configRef: apiGroup: bootstrap.cluster.x-k8s.io kind: TalosConfigTemplate - name: worker-e7cba96fd5f9ccbc + name: worker-b49f8b43afa7f968 clusterName: c-green-test-1234 infrastructureRef: apiGroup: infrastructure.cluster.x-k8s.io kind: CloudscaleMachineTemplate - name: worker-cb7ea8521a7ab803 + name: worker-140736b57cf90932 version: v1.36.1 --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleMachineTemplate metadata: - name: worker-cb7ea8521a7ab803 + name: worker-140736b57cf90932 namespace: syn-cluster-api spec: template: spec: flavor: plus-16-4 - image: custom:talos-v1.13.4-37656798 + image: custom:talos-v1.13-37656798 interfaces: - network: privnet-c-green-test-1234 rootVolumeSize: 50 @@ -44,7 +44,7 @@ spec: apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate metadata: - name: worker-e7cba96fd5f9ccbc + name: worker-b49f8b43afa7f968 namespace: syn-cluster-api spec: template: @@ -53,5 +53,5 @@ spec: hostname: source: InfrastructureName strategicPatches: - - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.4","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' - talosVersion: 1.13.4 + - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.0","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + talosVersion: '1.13' From a964e3df98997ee863ca3764f603c44a5d4a23b1 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Mon, 3 Aug 2026 15:12:51 +0200 Subject: [PATCH 15/24] Add logic to render and configure a K8s API server `AuthenticationConfiguration` Tested that this config works on Talos 1.13. Note that on Talos 1.14 we should get a real MachineConfiguration field for `AuthenticationConfiguration`. We can't write to one of the already mounted directories of the K8s API server pods, so we write our config file to `/var/config/kubernetes/kube-apiserver` and mount that directory in the API server pods. --- class/defaults.yml | 3 +++ component/main.jsonnet | 43 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/class/defaults.yml b/class/defaults.yml index 31adca5..3b2db42 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -64,3 +64,6 @@ parameters: strategicPatches: {} talosStrategicPatches: {} + + kubernetesApiServer: + authenticationConfigurationJWT: {} diff --git a/component/main.jsonnet b/component/main.jsonnet index 43eb90a..23ae04c 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -143,6 +143,17 @@ local strategicPatches = [ std.manifestJsonMinified(talosStrategicPatch), ]; +local authenticationConfiguration = { + apiVersion: 'apiserver.config.k8s.io/v1', + kind: 'AuthenticationConfiguration', + jwt: std.filter( + function(it) it != null, + std.objectValues(params.kubernetesApiServer.authenticationConfigurationJWT) + ), + //TODO(sg): do we want to allow configuring other top-level fields? are + //there even any other top-level fields? +}; + local capiTalosControlPlane = { apiVersion: 'controlplane.cluster.x-k8s.io/v1beta1', kind: 'TalosControlPlane', @@ -174,7 +185,37 @@ local capiTalosControlPlane = { strategicPatches: strategicPatches + [ std.manifestJsonMinified(patch) for patch in std.objectValues(params.talosControlPlane.strategicPatches) - ], + ] + if std.length(authenticationConfiguration.jwt) > 0 then + local filedir = '/var/config/kubernetes/kube-apiserver'; + local filepath = '%s/syn-authentication-configuration.yaml' % filedir; + [ + std.manifestJsonMinified({ + machine: { + files: [ + { + content: std.manifestYamlDoc(authenticationConfiguration), + permissions: std.parseOctal('0644'), + path: filepath, + op: 'create', + }, + ], + }, + cluster: { + apiServer: { + extraArgs: { + 'authentication-config': filepath, + }, + extraVolumes: [ + { + hostPath: filedir, + mountPath: filedir, + readonly: true, + }, + ], + }, + }, + }), + ] else [], }, }, }, From 526248e097b234d95a3b3a7ad64e37c0514cc770 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Wed, 12 Aug 2026 11:04:11 +0200 Subject: [PATCH 16/24] Enable server-side apply in ArgoCD app --- component/app.jsonnet | 1 + .../apps/talos-capi-cluster-cloudscale.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/component/app.jsonnet b/component/app.jsonnet index 18049b0..dacead8 100644 --- a/component/app.jsonnet +++ b/component/app.jsonnet @@ -15,6 +15,7 @@ local app = argocd.App('talos-capi-cluster-cloudscale', params.namespace) { syncPolicy+: { syncOptions+: [ 'RespectIgnoreDifferences=true', + 'ServerSideApply=true', ], }, }, diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/apps/talos-capi-cluster-cloudscale.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/apps/talos-capi-cluster-cloudscale.yaml index 5861bd2..3dc4bb6 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/apps/talos-capi-cluster-cloudscale.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/apps/talos-capi-cluster-cloudscale.yaml @@ -7,3 +7,4 @@ spec: syncPolicy: syncOptions: - RespectIgnoreDifferences=true + - ServerSideApply=true From 152f37bd47c7944a3db2d5ee349a9bacb38e8742 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Thu, 13 Aug 2026 09:08:33 +0200 Subject: [PATCH 17/24] Enable kubelet server certificate rotation This is required for metrics-server, but requires a mechanism to approve Kubelet CSRs. We currently use https://github.com/alex1989hu/kubelet-serving-cert-approver --- component/main.jsonnet | 8 ++++++++ .../talos-capi-cluster-cloudscale/capi_cluster.yaml | 2 +- .../worker_group_worker.yaml | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/component/main.jsonnet b/component/main.jsonnet index 23ae04c..cf48880 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -106,6 +106,14 @@ local talosStrategicPatch = { '%(major)s.%(minor)s.0' % validateTalosVersion(params.talosVersion), }, }, + kubelet: { + extraArgs: { + // NOTE(sg): required for metrics-server, but requires a mechanism to + // approve Kubelet CSRs. We currently use + // https://github.com/alex1989hu/kubelet-serving-cert-approver + 'rotate-server-certificates': true, + }, + }, }, // TODO(sg): figure out if this section is really needed for worker groups. cluster: { diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index 32c2acc..07bda45 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -64,7 +64,7 @@ spec: hostname: source: InfrastructureName strategicPatches: - - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.0","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.0","wipe":true},"kubelet":{"extraArgs":{"rotate-server-certificates":true}},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' talosVersion: '1.13' machineTemplate: spec: diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index 24ccf93..8796926 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -17,7 +17,7 @@ spec: configRef: apiGroup: bootstrap.cluster.x-k8s.io kind: TalosConfigTemplate - name: worker-b49f8b43afa7f968 + name: worker-596c13fcc77c1a6b clusterName: c-green-test-1234 infrastructureRef: apiGroup: infrastructure.cluster.x-k8s.io @@ -44,7 +44,7 @@ spec: apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate metadata: - name: worker-b49f8b43afa7f968 + name: worker-596c13fcc77c1a6b namespace: syn-cluster-api spec: template: @@ -53,5 +53,5 @@ spec: hostname: source: InfrastructureName strategicPatches: - - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.0","wipe":true},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' + - '{"cluster":{"externalCloudProvider":{"enabled":true},"network":{"cni":{"name":"none"}},"proxy":{"disabled":true}},"machine":{"install":{"disk":"/dev/sda","image":"factory.talos.dev/openstack-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.13.0","wipe":true},"kubelet":{"extraArgs":{"rotate-server-certificates":true}},"network":{"interfaces":[{"deviceSelector":{"physical":true},"dhcp":true}]}}}' talosVersion: '1.13' From 5cb0b865ae425025b3259ce0d881cde77ae898d1 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Thu, 10 Sep 2026 17:48:39 +0200 Subject: [PATCH 18/24] Use CAPI component libraries to render manifests Note that this commit removes the explicit namespace from a bunch of manifests. This isn't an issue when applying the config via ArgoCD (which is configured with the correct target namespace), and we account for the missing namespace in the manifests by explicitly using `kubectl apply -n syn-cluster-api` in the install workflow. --- component/main.jsonnet | 95 +++++++------------ tests/defaults.yml | 17 ++++ .../capi_cluster.yaml | 5 - .../worker_group_worker.yaml | 3 - 4 files changed, 53 insertions(+), 67 deletions(-) diff --git a/component/main.jsonnet b/component/main.jsonnet index cf48880..6ca49e2 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -2,6 +2,10 @@ local com = import 'lib/commodore.libjsonnet'; local kap = import 'lib/kapitan.libjsonnet'; local kube = import 'lib/kube.libjsonnet'; +local capi = import 'lib/capi-core.libsonnet'; +local capcs = import 'lib/capi-provider-cloudscale.libsonnet'; +local capi_talos = import 'lib/capi-provider-talos.libsonnet'; + local inv = kap.inventory(); local params = inv.parameters.talos_capi_cluster_cloudscale; @@ -41,13 +45,14 @@ local nameWithHash(name, spec, length=16) = std.sha256(std.manifestJsonMinified(spec))[:length], ]; -local capiCloudscaleCluster = { - apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', - kind: 'CloudscaleCluster', - metadata+: std.get(params.cloudscaleCluster, 'metadata', {}) { - name: params.clusterName, - namespace: params.namespace, - }, +local filteredMetadata(meta) = { + [k]: meta[k] + for k in std.objectFields(meta) + if !std.member([ 'name', 'namespace' ], k) +}; + +local capiCloudscaleCluster = capcs.CloudscaleCluster(params.clusterName) { + metadata+: filteredMetadata(std.get(params.cloudscaleCluster, 'metadata', {})), spec+: params.cloudscaleCluster.spec { networks: [ { @@ -58,12 +63,9 @@ local capiCloudscaleCluster = { }, }; -local capiCloudscaleMachineTemplateControlPlane = { - apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', - kind: 'CloudscaleMachineTemplate', +local capiCloudscaleMachineTemplateControlPlane = capcs.CloudscaleMachineTemplate(params.clusterName) { metadata+: { name: nameWithHash('%s-control-plane' % params.clusterName, $.spec), - namespace: params.namespace, }, spec: { template: { @@ -162,21 +164,16 @@ local authenticationConfiguration = { //there even any other top-level fields? }; -local capiTalosControlPlane = { - apiVersion: 'controlplane.cluster.x-k8s.io/v1beta1', - kind: 'TalosControlPlane', - metadata: std.get(params.talosControlPlane, 'metadata', {}) { - name: params.clusterName, - namespace: params.namespace, - }, +local capiTalosControlPlane = capi_talos.TalosControlPlane(params.clusterName) { + metadata+: filteredMetadata(std.get(params.talosControlPlane, 'metadata', {})), spec+: params.talosControlPlane.spec { replicas: params.controlPlane.count, version: kubernetesVersion, machineTemplate: { spec: { infrastructureRef: { - apiGroup: 'infrastructure.cluster.x-k8s.io', - kind: 'CloudscaleMachineTemplate', + apiGroup: capcs.apiGroup, + kind: capiCloudscaleMachineTemplateControlPlane.kind, name: capiCloudscaleMachineTemplateControlPlane.metadata.name, }, }, @@ -230,13 +227,9 @@ local capiTalosControlPlane = { }; // NOTE(sg): figure out if this is even needed after initial bootstrap -local capiClusterResourceSetBootstrap = { - apiVersion: 'addons.cluster.x-k8s.io/v1beta2', - kind: 'ClusterResourceSet', - metadata: { - name: 'cloudscale-bootstrap-%s' % params.clusterName, - namespace: params.namespace, - }, +local capiClusterResourceSetBootstrap = capi.ClusterResourceSet( + 'cloudscale-bootstrap-%s' % params.clusterName +) { spec: { strategy: 'ApplyOnce', clusterSelector: { @@ -259,12 +252,9 @@ local capiClusterResourceSetBootstrap = { }; local capiWorkerGroup(name) = - local talosConfigTemplate = { - apiVersion: 'bootstrap.cluster.x-k8s.io/v1alpha3', - kind: 'TalosConfigTemplate', - metadata: { + local talosConfigTemplate = capi_talos.TalosConfigTemplate(name) { + metadata+: { name: nameWithHash(name, $.spec), - namespace: params.namespace, }, spec: { template: { @@ -279,12 +269,9 @@ local capiWorkerGroup(name) = }, }, }; - local cloudscaleMachineTemplate = { - apiVersion: 'infrastructure.cluster.x-k8s.io/v1beta2', - kind: 'CloudscaleMachineTemplate', - metadata: { + local cloudscaleMachineTemplate = capcs.CloudscaleMachineTemplate(name) { + metadata+: { name: nameWithHash(name, $.spec), - namespace: params.namespace, }, spec: { template: { @@ -312,13 +299,7 @@ local capiWorkerGroup(name) = : "Invalid value '%s' for deletion order for machinedeployment '%s': " % [ valOrDefault, name ] + 'valid options are %s' % validDeletionOrders; valOrDefault; - local machineDeployment = { - apiVersion: 'cluster.x-k8s.io/v1beta2', - kind: 'MachineDeployment', - metadata: { - name: name, - namespace: params.namespace, - }, + local machineDeployment = capi.MachineDeployment(name) { spec: { clusterName: params.clusterName, replicas: params.workerGroups[name].count, @@ -337,15 +318,15 @@ local capiWorkerGroup(name) = version: kubernetesVersion, bootstrap: { configRef: { + apiGroup: capi_talos.bootstrapApiGroup, + kind: talosConfigTemplate.kind, name: talosConfigTemplate.metadata.name, - apiGroup: 'bootstrap.cluster.x-k8s.io', - kind: 'TalosConfigTemplate', }, }, infrastructureRef: { + apiGroup: capcs.apiGroup, + kind: cloudscaleMachineTemplate.kind, name: cloudscaleMachineTemplate.metadata.name, - apiGroup: 'infrastructure.cluster.x-k8s.io', - kind: 'CloudscaleMachineTemplate', }, }, }, @@ -364,25 +345,21 @@ local capiWorkerGroup(name) = ], }; -local capiCluster = params.cluster { - apiVersion: 'cluster.x-k8s.io/v1beta2', - kind: 'Cluster', - metadata+: { - name: params.clusterName, - namespace: params.namespace, +local capiCluster = capi.Cluster(params.clusterName) { + metadata+: filteredMetadata(std.get(params.cluster, 'metadata', {})) { labels+: { [resourceSetLabelKey]: 'cloudscale', }, }, - spec+: { + spec+: std.get(params.cluster, 'spec', {}) { infrastructureRef: { - apiGroup: 'infrastructure.cluster.x-k8s.io', - kind: 'CloudscaleCluster', + apiGroup: capcs.apiGroup, + kind: capiCloudscaleCluster.kind, name: capiCloudscaleCluster.metadata.name, }, controlPlaneRef: { - apiGroup: 'controlplane.cluster.x-k8s.io', - kind: 'TalosControlPlane', + apiGroup: capi_talos.controlPlaneApiGroup, + kind: capiTalosControlPlane.kind, name: capiTalosControlPlane.metadata.name, }, }, diff --git a/tests/defaults.yml b/tests/defaults.yml index f5d828d..84bf5d6 100644 --- a/tests/defaults.yml +++ b/tests/defaults.yml @@ -2,6 +2,23 @@ applications: - cilium parameters: + kapitan: + dependencies: + - type: https + source: https://raw.githubusercontent.com/projectsyn/component-capi-core/master/lib/capi-core.libsonnet + output_path: vendor/lib/capi-core.libsonnet + - type: https + source: https://raw.githubusercontent.com/projectsyn/component-capi-provider-cloudscale/master/lib/capi-provider-cloudscale.libsonnet + output_path: vendor/lib/capi-provider-cloudscale.libsonnet + - type: https + source: https://raw.githubusercontent.com/projectsyn/component-capi-provider-talos/master/lib/capi-provider-talos.libsonnet + output_path: vendor/lib/capi-provider-talos.libsonnet + cilium: cilium_helm_values: kubeProxyReplacement: "true" + + capi_provider_talos: + images: + capi-controlplane-provider-talos: + tag: v0.6.0-alpha.2 diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index 07bda45..5c26d1f 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -4,7 +4,6 @@ metadata: labels: talos-capi-cluster-cloudscale.syn.tools/bootstrap: cloudscale name: c-green-test-1234 - namespace: syn-cluster-api spec: clusterNetwork: pods: @@ -27,7 +26,6 @@ apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleCluster metadata: name: c-green-test-1234 - namespace: syn-cluster-api spec: credentialsRef: name: capcs-cloudscale @@ -40,7 +38,6 @@ apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleMachineTemplate metadata: name: c-green-test-1234-control-plane-1a05392e618ad8f5 - namespace: syn-cluster-api spec: template: spec: @@ -56,7 +53,6 @@ apiVersion: controlplane.cluster.x-k8s.io/v1beta1 kind: TalosControlPlane metadata: name: c-green-test-1234 - namespace: syn-cluster-api spec: controlPlaneConfig: controlplane: @@ -79,7 +75,6 @@ apiVersion: addons.cluster.x-k8s.io/v1beta2 kind: ClusterResourceSet metadata: name: cloudscale-bootstrap-c-green-test-1234 - namespace: syn-cluster-api spec: clusterSelector: matchLabels: diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index 8796926..24368f0 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -2,7 +2,6 @@ apiVersion: cluster.x-k8s.io/v1beta2 kind: MachineDeployment metadata: name: worker - namespace: syn-cluster-api spec: clusterName: c-green-test-1234 deletion: @@ -29,7 +28,6 @@ apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleMachineTemplate metadata: name: worker-140736b57cf90932 - namespace: syn-cluster-api spec: template: spec: @@ -45,7 +43,6 @@ apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 kind: TalosConfigTemplate metadata: name: worker-596c13fcc77c1a6b - namespace: syn-cluster-api spec: template: spec: From 06258cd69dcad4ea14e38a08456ec1d7ba8f2501 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Fri, 11 Sep 2026 16:01:12 +0200 Subject: [PATCH 19/24] Refactor control plane machineconfiguration patch rendering --- component/main.jsonnet | 73 ++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/component/main.jsonnet b/component/main.jsonnet index 6ca49e2..ac3c535 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -164,6 +164,44 @@ local authenticationConfiguration = { //there even any other top-level fields? }; +local authenticationPatch = + if std.length(authenticationConfiguration.jwt) > 0 then + local filedir = '/var/config/kubernetes/kube-apiserver'; + local filepath = '%s/syn-authentication-configuration.yaml' % filedir; + [ + std.manifestJsonMinified({ + machine: { + files: [ + { + content: std.manifestYamlDoc(authenticationConfiguration), + permissions: std.parseOctal('0644'), + path: filepath, + op: 'create', + }, + ], + }, + cluster: { + apiServer: { + extraArgs: { + 'authentication-config': filepath, + }, + extraVolumes: [ + { + hostPath: filedir, + mountPath: filedir, + readonly: true, + }, + ], + }, + }, + }), + ] else []; + +local controlPlaneStrategicPatches = [ + std.manifestJsonMinified(patch) + for patch in std.objectValues(params.talosControlPlane.strategicPatches) +] + authenticationPatch; + local capiTalosControlPlane = capi_talos.TalosControlPlane(params.clusterName) { metadata+: filteredMetadata(std.get(params.talosControlPlane, 'metadata', {})), spec+: params.talosControlPlane.spec { @@ -187,40 +225,7 @@ local capiTalosControlPlane = capi_talos.TalosControlPlane(params.clusterName) { // provider. source: 'InfrastructureName', }, - strategicPatches: strategicPatches + [ - std.manifestJsonMinified(patch) - for patch in std.objectValues(params.talosControlPlane.strategicPatches) - ] + if std.length(authenticationConfiguration.jwt) > 0 then - local filedir = '/var/config/kubernetes/kube-apiserver'; - local filepath = '%s/syn-authentication-configuration.yaml' % filedir; - [ - std.manifestJsonMinified({ - machine: { - files: [ - { - content: std.manifestYamlDoc(authenticationConfiguration), - permissions: std.parseOctal('0644'), - path: filepath, - op: 'create', - }, - ], - }, - cluster: { - apiServer: { - extraArgs: { - 'authentication-config': filepath, - }, - extraVolumes: [ - { - hostPath: filedir, - mountPath: filedir, - readonly: true, - }, - ], - }, - }, - }), - ] else [], + strategicPatches: strategicPatches + controlPlaneStrategicPatches, }, }, }, From 1d25541811bd09239588f86f1f58074d4deb04b8 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Mon, 14 Sep 2026 14:32:52 +0200 Subject: [PATCH 20/24] Remove bootstrap ClusterResourceSet from component --- component/main.jsonnet | 26 ------------------- .../capi_cluster.yaml | 15 ----------- 2 files changed, 41 deletions(-) diff --git a/component/main.jsonnet b/component/main.jsonnet index ac3c535..81a45a9 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -231,31 +231,6 @@ local capiTalosControlPlane = capi_talos.TalosControlPlane(params.clusterName) { }, }; -// NOTE(sg): figure out if this is even needed after initial bootstrap -local capiClusterResourceSetBootstrap = capi.ClusterResourceSet( - 'cloudscale-bootstrap-%s' % params.clusterName -) { - spec: { - strategy: 'ApplyOnce', - clusterSelector: { - matchLabels: { - [resourceSetLabelKey]: 'cloudscale', - }, - }, - resources: [ - // NOTE(sg): the configmaps are externally generated for bootstrap - { - name: '%s-ccm' % params.clusterName, - kind: 'ConfigMap', - }, - { - name: '%s-cilium' % params.clusterName, - kind: 'ConfigMap', - }, - ], - }, -}; - local capiWorkerGroup(name) = local talosConfigTemplate = capi_talos.TalosConfigTemplate(name) { metadata+: { @@ -379,7 +354,6 @@ else capiCloudscaleCluster, capiCloudscaleMachineTemplateControlPlane, capiTalosControlPlane, - capiClusterResourceSetBootstrap, ], } + { ['worker_group_%s' % wg.name]: wg.resources diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index 5c26d1f..8bd3c80 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -70,18 +70,3 @@ spec: name: c-green-test-1234-control-plane-1a05392e618ad8f5 replicas: 1 version: v1.36.1 ---- -apiVersion: addons.cluster.x-k8s.io/v1beta2 -kind: ClusterResourceSet -metadata: - name: cloudscale-bootstrap-c-green-test-1234 -spec: - clusterSelector: - matchLabels: - talos-capi-cluster-cloudscale.syn.tools/bootstrap: cloudscale - resources: - - kind: ConfigMap - name: c-green-test-1234-ccm - - kind: ConfigMap - name: c-green-test-1234-cilium - strategy: ApplyOnce From fc5146b14e2a2fda8a0ae971297897c87c11cf13 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Wed, 16 Sep 2026 11:32:50 +0200 Subject: [PATCH 21/24] Update component defaults to K8s 1.36.4 Also remove note regarding upgrades. --- class/defaults.yml | 7 +------ .../talos-capi-cluster-cloudscale/capi_cluster.yaml | 2 +- .../talos-capi-cluster-cloudscale/worker_group_worker.yaml | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/class/defaults.yml b/class/defaults.yml index 3b2db42..d2d5fdb 100644 --- a/class/defaults.yml +++ b/class/defaults.yml @@ -7,15 +7,10 @@ parameters: clusterName: ${cluster:name} - # TODO(sg): how will updates work with CAPI? - # IMPORTANT: this is only used for ensuring CAPI renders a suitable - # MachineConfig and isn't used to define the cluster's actual Talos - # version (at least when using the OpenStack raw image from the image - # factory). talosVersion: '1.13' # NOTE(sg): this is the well-known default schematic UUID talosSchematicUUID: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba - kubernetesVersion: 'v1.36.1' + kubernetesVersion: 'v1.36.4' apiURL: "" cni: cilium diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml index 8bd3c80..4d5dd69 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/capi_cluster.yaml @@ -69,4 +69,4 @@ spec: kind: CloudscaleMachineTemplate name: c-green-test-1234-control-plane-1a05392e618ad8f5 replicas: 1 - version: v1.36.1 + version: v1.36.4 diff --git a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml index 24368f0..e1cb40f 100644 --- a/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml +++ b/tests/golden/defaults/talos-capi-cluster-cloudscale/talos-capi-cluster-cloudscale/worker_group_worker.yaml @@ -22,7 +22,7 @@ spec: apiGroup: infrastructure.cluster.x-k8s.io kind: CloudscaleMachineTemplate name: worker-140736b57cf90932 - version: v1.36.1 + version: v1.36.4 --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 kind: CloudscaleMachineTemplate From 282ff6f426ab4a6d7e4380011e35e70175812091 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Wed, 16 Sep 2026 11:33:13 +0200 Subject: [PATCH 22/24] Update documentation --- docs/modules/ROOT/pages/index.adoc | 8 +- .../ROOT/pages/references/parameters.adoc | 130 +++++++++++++++++- 2 files changed, 131 insertions(+), 7 deletions(-) diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 69d0e22..c8a0b29 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -1,5 +1,11 @@ = talos-capi-cluster-cloudscale -talos-capi-cluster-cloudscale is a Commodore component to manage talos-capi-cluster-cloudscale. +talos-capi-cluster-cloudscale is a Commodore component to manage https://www.siderolabs.com/talos-linux[Talos] clusters using https://cluster-api.sigs.k8s.io/[Cluster API (CAPI)] on https://cloudscale.ch[cloudscale]. + +The component expects that the following Cluster API components are available on the target cluster: + +* https://hub.syn.tools/capi-core[component-capi-core] +* https://hub.syn.tools/capi-provider-cloudscale[component-capi-provider-cloudscale] +* https://hub.syn.tools/capi-provider-talos[component-capi-provider-talos] See the xref:references/parameters.adoc[parameters] reference for further details. diff --git a/docs/modules/ROOT/pages/references/parameters.adoc b/docs/modules/ROOT/pages/references/parameters.adoc index 3ead03a..e21f61a 100644 --- a/docs/modules/ROOT/pages/references/parameters.adoc +++ b/docs/modules/ROOT/pages/references/parameters.adoc @@ -6,14 +6,132 @@ The parent key for all of the following parameters is `talos_capi_cluster_clouds [horizontal] type:: string -default:: `syn-talos-capi-cluster-cloudscale` +default:: `syn-cluster-api` The namespace in which to deploy this component. +TIP: We recommend using `syn-cluster-api` if you're also using the Commodore CAPI components. -== Example +== `clusterName` -[source,yaml] ----- -namespace: example-namespace ----- +[horizontal] +type:: string +default:: `${cluster:name}` + +The name to use for the Cluster API cluster. + +== `talosVersion` + +[horizontal] +type:: string +default:: https://github.com/projectsyn/component-talos-capi-cluster-cloudscale/blob/master/class/defaults.yml[See `class/defaults.yml`] + +The Talos version to use when provisioning the cluster. + +NOTE: The Talos CAPI provider uses the value of this parameter only to decide how to render the Talos `MachineConfiguration`. + +== `talosSchematicUUID` + +[horizontal] +type:: string +default:: `376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba` + +The https://docs.siderolabs.com/talos/v1.13/learn-more/image-factory#schematics[Talos schematic UUID] to use when provisioning the cluster. +This should match the schematic UUID that's used to generate the base image specified in parameter `cloudscale.customImageSlug`. + +== `kubernetesVersion` + +[horizontal] +type:: string +default:: https://github.com/projectsyn/component-talos-capi-cluster-cloudscale/blob/master/class/defaults.yml[See `class/defaults.yml`] + +The Kubernetes version to use when provisioning the cluster. +Changing the value of this parameter will trigger node replacements. + +== `apiURL` + +[horizontal] +type:: string +default:: `""` + +When set to a non-empty string, the component configures Talos to add the value of this parameter as an additional SAN to the Kubernetes API certificate. +This enables users to use the value of this parameter to connect to the Kubernetes API. + +== `cni` + +[horizontal] +type:: string +default:: `cilium` + +The CNI to use when provisioning the cluster. +If this is set to a value other than `flannel`, the component configures Talos to expect an externally provisioned CNI by setting `network.cni.name=none` in the `MachineConfiguration`. + +When the parameter is set to `cilium` and Cilium's kube-proxy replacement is enabled, the component configures Talos with `proxy.disabled=true` to disable kube-proxy. + +== `cloudscale` + +This section contains configurations that refer to cloudscale resources. + +=== `cloudscale.customImageSlug` +=== `cloudscale.privateNetwork` +=== `region` + +== `controlPlane` + +== `workerGroups` + +== `cluster` + +[horizontal] +type:: object +default:: https://github.com/projectsyn/component-talos-capi-cluster-cloudscale/blob/master/class/defaults.yml[See `class/defaults.yml`] + +This parameter allows customizing the CAPI `Cluster` resource rendered by the component. +The component looks for fields `metadata` and `spec` and merges those with the hard-coded configuration. + +== `cloudscaleCluster` + +[horizontal] +type:: object +default:: https://github.com/projectsyn/component-talos-capi-cluster-cloudscale/blob/master/class/defaults.yml[See `class/defaults.yml`] + +This parameter allows customizing the CAPI `CloudscaleCluster` resource rendered by the component. +The component looks for fields `metadata` and `spec` and merges those with the hard-coded configuration. + +== `talosControlPlane` + +[horizontal] +type:: object +default:: https://github.com/projectsyn/component-talos-capi-cluster-cloudscale/blob/master/class/defaults.yml[See `class/defaults.yml`] + +This parameter allows customizing the CAPI `TalosControlPlane` resource rendered by the component. +The component looks for fields `metadata` and `spec` and merges those with the hard-coded configuration. + +Additionally, the component applies all entries of field `strategicPatches` to the Talos control plane nodes only. +See <<`talosStrategicPatches`>> for details on the semantics of the parameter. + +== `talosStrategicPatches` + +[horizontal] +type:: object +default:: https://github.com/projectsyn/component-talos-capi-cluster-cloudscale/blob/master/class/defaults.yml[See `class/defaults.yml`] + +Each entry of this parameter is applies to all Talos nodes. +The component ignores the object field names, and expects that the values are valid strategic patches when converted to JSON. + +== `kubernetesApiServer` + +[horizontal] +type:: object +default:: https://github.com/projectsyn/component-talos-capi-cluster-cloudscale/blob/master/class/defaults.yml[See `class/defaults.yml`] + +=== `kubernetesApiServer.authenticationConfigurationJWT` + +[horizontal] +type:: object +default:: `{}` + +This parameter allows configuring custom entries for field `jwt` of the https://kubernetes.io/docs/reference/access-authn-authz/authentication/#using-authentication-configuration[Kubernetes API server `AuthenticationConfiguration`]. +This parameter is intended to enable users to configure OIDC authentication for the Talos Kubernetes API server. + +NOTE: This parameter may change once we upgrade to Talos 1.14. From 4de862e33bc09a28b04f570abab3f0d401b037f2 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Wed, 16 Sep 2026 11:40:42 +0200 Subject: [PATCH 23/24] Assert that CAPI components are present --- component/main.jsonnet | 8 ++++++++ tests/defaults.yml | 3 +++ 2 files changed, 11 insertions(+) diff --git a/component/main.jsonnet b/component/main.jsonnet index 81a45a9..925e8f4 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -9,6 +9,14 @@ local capi_talos = import 'lib/capi-provider-talos.libsonnet'; local inv = kap.inventory(); local params = inv.parameters.talos_capi_cluster_cloudscale; +assert + std.member(inv.applications, 'capi-core') + && std.member(inv.applications, 'capi-provider-cloudscale') + && std.member(inv.applications, 'capi-provider-talos') + : '\n\nComponent talos-capi-cluster-cloudscale requires components ' + + 'capi-core, capi-provider-cloudscale, and capi-provider-talos'; + + local validateTalosVersion(tver) = local parts = std.split(tver, '.'); assert std.length(parts) == 2 : 'Expected Talos version to contain exacty 1 dot'; diff --git a/tests/defaults.yml b/tests/defaults.yml index 84bf5d6..95dede2 100644 --- a/tests/defaults.yml +++ b/tests/defaults.yml @@ -1,5 +1,8 @@ applications: - cilium + - capi-core + - capi-provider-cloudscale + - capi-provider-talos parameters: kapitan: From 0de5260275a142debb11404c0a912529637b2046 Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Thu, 17 Sep 2026 13:31:17 +0200 Subject: [PATCH 24/24] Ensure stable order for Talos `MachineConfiguration` strategic patches --- component/main.jsonnet | 19 ++++++++++++++----- .../ROOT/pages/references/parameters.adoc | 6 +++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/component/main.jsonnet b/component/main.jsonnet index 925e8f4..7069532 100644 --- a/component/main.jsonnet +++ b/component/main.jsonnet @@ -153,10 +153,13 @@ local talosStrategicPatch = { }, }; -// TODO(sg): does order matter here? +// NOTE(sg): We order user-provided patches by their names in asciibetical +// order. local strategicPatches = [ - std.manifestJsonMinified(patch) - for patch in std.objectValues(params.talosStrategicPatches) + std.manifestJsonMinified(params.talosStrategicPatches[p]) + for p in std.sort(std.objectFields( + params.talosStrategicPatches + )) ] + [ std.manifestJsonMinified(talosStrategicPatch), ]; @@ -205,9 +208,15 @@ local authenticationPatch = }), ] else []; +// NOTE(sg): We sort user-provided control plane patches by their names in +// asciibetical order. local controlPlaneStrategicPatches = [ - std.manifestJsonMinified(patch) - for patch in std.objectValues(params.talosControlPlane.strategicPatches) + std.manifestJsonMinified( + params.talosControlPlane.strategicPatches[p] + ) + for p in std.sort(std.objectFields( + params.talosControlPlane.strategicPatches + )) ] + authenticationPatch; local capiTalosControlPlane = capi_talos.TalosControlPlane(params.clusterName) { diff --git a/docs/modules/ROOT/pages/references/parameters.adoc b/docs/modules/ROOT/pages/references/parameters.adoc index e21f61a..004106f 100644 --- a/docs/modules/ROOT/pages/references/parameters.adoc +++ b/docs/modules/ROOT/pages/references/parameters.adoc @@ -117,7 +117,11 @@ type:: object default:: https://github.com/projectsyn/component-talos-capi-cluster-cloudscale/blob/master/class/defaults.yml[See `class/defaults.yml`] Each entry of this parameter is applies to all Talos nodes. -The component ignores the object field names, and expects that the values are valid strategic patches when converted to JSON. +The component sorts the patches by object field name to ensure stable patch order. +This ensures that patches are applied in a predictable order. +The component expects that the values are valid strategic patches when converted to JSON. + +NOTE: If multiple patches modify the same field, the values provided by patches that are ordered later take precedence. == `kubernetesApiServer`