├── docs ├── book │ ├── .gitignore │ ├── src │ │ ├── 04_developer │ │ │ ├── 02_releasing.md │ │ │ └── 00.md │ │ ├── 05_reference │ │ │ └── 00.md │ │ ├── 01_user │ │ │ └── 00.md │ │ ├── 02_topics │ │ │ ├── 00.md │ │ │ ├── 09_secrets_encryption.md │ │ │ ├── 05_user_data_compression.md │ │ │ ├── 07_load_balancer_exclusion.md │ │ │ ├── 06_configure-manager-options.md │ │ │ ├── 04_embedded-registry.md │ │ │ ├── 02_node-registration-methods.md │ │ │ └── 03_cis-psa.md │ │ ├── 03_examples │ │ │ ├── 00.md │ │ │ ├── 03_docker.md │ │ │ └── 02_vsphere.md │ │ ├── SUMMARY.md │ │ └── 00_introduction.md │ ├── book.toml │ ├── Makefile │ ├── README.md │ └── util-embed.sh └── adr │ ├── 0000-template.md │ ├── 0002-deprecate-kubebuilder-defaults.md │ └── 0001-separate-CP-and-worker-versions.md ├── controlplane ├── internal │ ├── webhooks │ │ └── .gitkeep │ ├── contract │ │ └── doc.go │ ├── controllers │ │ ├── const.go │ │ └── suite_test.go │ └── util │ │ └── hash │ │ └── hash.go ├── config │ ├── manager │ │ ├── kustomization.yaml │ │ └── manager.yaml │ ├── certmanager │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ └── certificate.yaml │ ├── rbac │ │ ├── service_account.yaml │ │ ├── aggregated_role.yaml │ │ ├── role_binding.yaml │ │ ├── leader_election_role_binding.yaml │ │ ├── leader_election_role.yaml │ │ ├── kustomization.yaml │ │ └── role.yaml │ ├── webhook │ │ ├── kustomization.yaml │ │ ├── service.yaml │ │ ├── kustomizeconfig.yaml │ │ └── manifests.yaml │ ├── default │ │ ├── namespace.yaml │ │ ├── manager_pull_policy.yaml │ │ ├── manager_image_patch.yaml │ │ ├── manager_role_aggregation_patch.yaml │ │ ├── manager_webhook_patch.yaml │ │ ├── webhookcainjection_patch.yaml │ │ └── kustomization.yaml │ └── crd │ │ ├── patches │ │ ├── cainjection_in_rke2controlplanes.yaml │ │ ├── cainjection_in_rke2controlplanetemplates.yaml │ │ ├── webhook_in_rke2controlplanes.yaml │ │ └── webhook_in_rke2controlplanetemplates.yaml │ │ ├── kustomizeconfig.yaml │ │ └── kustomization.yaml ├── api │ ├── v1beta1 │ │ ├── doc.go │ │ ├── conversion.go │ │ ├── groupversion_info.go │ │ ├── types.go │ │ ├── rke2controlplanetemplate_types.go │ │ ├── rke2controlplane_webhook_test.go │ │ └── rke2controlplanetemplate_webhook_test.go │ └── v1alpha1 │ │ ├── doc.go │ │ ├── groupversion_info.go │ │ ├── conversion_test.go │ │ ├── types.go │ │ └── rke2controlplanetemplate_types.go └── PROJECT ├── bootstrap ├── config │ ├── manager │ │ ├── kustomization.yaml │ │ └── manager.yaml │ ├── certmanager │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ └── certificate.yaml │ ├── rbac │ │ ├── service_account.yaml │ │ ├── role_binding.yaml │ │ ├── leader_election_role_binding.yaml │ │ ├── leader_election_role.yaml │ │ ├── kustomization.yaml │ │ ├── rke2configtemplate_viewer_role.yaml │ │ ├── rke2configtemplate_editor_role.yaml │ │ └── role.yaml │ ├── webhook │ │ ├── kustomization.yaml │ │ ├── service.yaml │ │ ├── kustomizeconfig.yaml │ │ └── manifests.yaml │ ├── default │ │ ├── namespace.yaml │ │ ├── manager_pull_policy.yaml │ │ ├── manager_image_patch.yaml │ │ ├── manager_webhook_patch.yaml │ │ └── webhookcainjection_patch.yaml │ ├── crd │ │ ├── patches │ │ │ ├── cainjection_in_rke2configs.yaml │ │ │ ├── cainjection_in_rke2configtemplates.yaml │ │ │ ├── webhook_in_rke2configtemplates.yaml │ │ │ └── webhook_in_rke2configs.yaml │ │ ├── kustomizeconfig.yaml │ │ └── kustomization.yaml │ └── samples │ │ └── bootstrap_v1alpha2_rke2configtemplate.yaml ├── api │ ├── v1beta1 │ │ ├── doc.go │ │ ├── conversion.go │ │ ├── fuzzer.go │ │ ├── groupversion_info.go │ │ ├── rke2configtemplate_types.go │ │ └── condition_consts.go │ └── v1alpha1 │ │ ├── doc.go │ │ ├── groupversion_info.go │ │ ├── rke2configtemplate_types.go │ │ ├── condition_consts.go │ │ └── conversion_test.go ├── internal │ ├── cloudinit │ │ ├── cloudinit_suite_test.go │ │ ├── controlplane_join.go │ │ ├── worker_join.go │ │ └── controlplane_init.go │ └── controllers │ │ └── suite_test.go └── PROJECT ├── examples ├── templates │ └── docker │ │ ├── air-gapped │ │ └── image-building │ │ │ ├── artifact-list.txt │ │ │ ├── download-rke2-artifacts.sh │ │ │ ├── Dockerfile │ │ │ └── README.md │ │ └── enable-multus │ │ └── rke2controlplane-test.yaml └── clusterclass │ └── docker │ └── cluster-template-topology.yaml ├── CODEOWNERS ├── hack ├── tools │ ├── Makefile │ ├── go.mod │ ├── go.sum │ └── tools.go ├── boilerplate.go.txt ├── utils.sh └── ensure-kubectl.sh ├── .markdown-link-check.json ├── .github ├── release.yml ├── ISSUE_TEMPLATE │ ├── feature.md │ └── bug.md ├── workflows │ ├── lint.yml │ ├── ci.yml │ ├── pr-md-link-check.yml │ ├── golangci-lint.yml │ ├── docs.yml │ ├── e2e.yml │ ├── stale.yml │ └── release.yml ├── PULL_REQUEST_TEMPLATE.md └── dependabot.yml ├── test └── e2e │ ├── data │ ├── shared │ │ └── v1beta1 │ │ │ └── metadata.yaml │ └── infrastructure │ │ ├── cluster-from-clusterclass-template-docker.yaml │ │ └── postgres.yaml │ ├── const.go │ └── rcp_remediation_test.go ├── image-builder ├── aws │ └── opensuse-leap-156.json ├── README.md ├── scripts │ └── opensuse │ │ └── bootstrap.sh ├── Makefile └── files │ └── features.py ├── .dockerignore ├── pkg ├── util │ ├── suite_test.go │ └── util_test.go ├── proxy │ ├── proxy.go │ ├── addr.go │ └── conn.go ├── rke2 │ └── suite_test.go ├── consts │ └── global_consts.go └── etcd │ ├── util │ └── util.go │ ├── fake │ └── client.go │ └── etcd_test.go ├── README.md ├── .gitignore ├── tilt-provider.json ├── scripts ├── ci-lint-dockerfiles.sh ├── install-mdbook.sh └── go-install.sh ├── Dockerfile ├── metadata.yaml └── .devcontainer └── devcontainer.json /docs/book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /controlplane/internal/webhooks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bootstrap/config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | -------------------------------------------------------------------------------- /controlplane/config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | -------------------------------------------------------------------------------- /docs/book/src/04_developer/02_releasing.md: -------------------------------------------------------------------------------- 1 | {{#include ../../../../docs/release.md}} -------------------------------------------------------------------------------- /docs/book/src/05_reference/00.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | This section contains reference documentation for CAPRKE2 API types. -------------------------------------------------------------------------------- /docs/book/src/01_user/00.md: -------------------------------------------------------------------------------- 1 | # User guide 2 | 3 | This section contains a getting started guide to help new users utilise CAPRKE2. -------------------------------------------------------------------------------- /bootstrap/config/certmanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - certificate.yaml 3 | 4 | configurations: 5 | - kustomizeconfig.yaml 6 | -------------------------------------------------------------------------------- /controlplane/config/certmanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - certificate.yaml 3 | 4 | configurations: 5 | - kustomizeconfig.yaml 6 | -------------------------------------------------------------------------------- /bootstrap/config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /controlplane/config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /bootstrap/config/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manifests.yaml 3 | - service.yaml 4 | 5 | configurations: 6 | - kustomizeconfig.yaml 7 | -------------------------------------------------------------------------------- /controlplane/config/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manifests.yaml 3 | - service.yaml 4 | 5 | configurations: 6 | - kustomizeconfig.yaml 7 | -------------------------------------------------------------------------------- /docs/book/src/02_topics/00.md: -------------------------------------------------------------------------------- 1 | # Topics 2 | 3 | This section contains more detailed information about the features that CAPRKE2 offers and how to use them. -------------------------------------------------------------------------------- /examples/templates/docker/air-gapped/image-building/artifact-list.txt: -------------------------------------------------------------------------------- 1 | rke2-images.linux-amd64.tar.zst 2 | rke2.linux-amd64.tar.gz 3 | sha256sum-amd64.txt 4 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence. 3 | 4 | * @rancher/highlander -------------------------------------------------------------------------------- /bootstrap/config/default/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system -------------------------------------------------------------------------------- /controlplane/config/default/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system -------------------------------------------------------------------------------- /docs/book/src/04_developer/00.md: -------------------------------------------------------------------------------- 1 | # Developer Guide 2 | 3 | This section describes the workflow for regular developer tasks, such as: 4 | - Development guide 5 | - Releasing a new version of CAPRKE2 6 | -------------------------------------------------------------------------------- /bootstrap/config/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: webhook-service 6 | namespace: system 7 | spec: 8 | ports: 9 | - port: 443 10 | targetPort: webhook-server 11 | -------------------------------------------------------------------------------- /controlplane/config/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: webhook-service 6 | namespace: system 7 | spec: 8 | ports: 9 | - port: 443 10 | targetPort: webhook-server 11 | -------------------------------------------------------------------------------- /hack/tools/Makefile: -------------------------------------------------------------------------------- 1 | # Directories. 2 | BIN_DIR := bin 3 | 4 | MDBOOK_EMBED := $(BIN_DIR)/mdbook-embed 5 | $(MDBOOK_EMBED): $(BIN_DIR) go.mod go.sum 6 | go build -tags=tools -o $(BIN_DIR)/mdbook-embed sigs.k8s.io/cluster-api/hack/tools/mdbook/embed 7 | 8 | -------------------------------------------------------------------------------- /.markdown-link-check.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": "20s", 3 | "retryOn429": true, 4 | "retryCount": 10, 5 | "fallbackRetryDelay": "10s", 6 | "ignorePatterns": [ 7 | { 8 | "pattern": "^http://localhost:3000/$" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /docs/book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["The Cluster API Provider RKE2 Maintainers"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Kubernetes Cluster API Provider RKE2" 7 | 8 | [preprocessor.embed] 9 | command = "./util-embed.sh" -------------------------------------------------------------------------------- /bootstrap/config/default/manager_pull_policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | imagePullPolicy: Always -------------------------------------------------------------------------------- /controlplane/config/default/manager_pull_policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | imagePullPolicy: Always -------------------------------------------------------------------------------- /hack/tools/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rancher/cluster-api-provider-rke2/hack/tools 2 | 3 | go 1.23.0 4 | 5 | require sigs.k8s.io/cluster-api/hack/tools v0.0.0-20240820112706-3abe3058a6a8 6 | 7 | require sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d // indirect 8 | -------------------------------------------------------------------------------- /bootstrap/config/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name -------------------------------------------------------------------------------- /controlplane/config/rbac/aggregated_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: aggregated-manager-role 5 | aggregationRule: 6 | clusterRoleSelectors: 7 | - matchLabels: 8 | rke2.controlplane.cluster.x-k8s.io/aggregate-to-manager: "true" 9 | rules: [] -------------------------------------------------------------------------------- /controlplane/config/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | -------------------------------------------------------------------------------- /bootstrap/config/default/manager_image_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - image: ghcr.io/rancher/cluster-api-provider-rke2-bootstrap:dev 11 | name: manager -------------------------------------------------------------------------------- /controlplane/config/default/manager_image_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - image: ghcr.io/rancher/cluster-api-provider-rke2-controlplane:dev 11 | name: manager -------------------------------------------------------------------------------- /bootstrap/config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /examples/templates/docker/air-gapped/image-building/download-rke2-artifacts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | : "${RKE2_VERSION:=v1.26.0+rke2r1}" 3 | echo "Downloading RKE2 artifacts for version $RKE2_VERSION ..." 4 | while read p; do 5 | curl -sfL -o files/$p https://github.com/rancher/rke2/releases/download/$RKE2_VERSION/$p 6 | done 10 | 11 | **What this PR does / why we need it**: 12 | 13 | 14 | 15 | **Which issue(s) this PR fixes** *(optional, in `fixes #(, fixes #, ...)` format, will close the issue(s) when PR gets merged)*: 16 | Fixes # 17 | 18 | **Special notes for your reviewer**: 19 | 20 | **Checklist**: 21 | 22 | 23 | - [ ] squashed commits into logical changes 24 | - [ ] includes documentation 25 | - [ ] adds unit tests 26 | - [ ] adds or updates e2e tests 27 | -------------------------------------------------------------------------------- /bootstrap/api/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains a v1alpha1 api for bootstrap resources. 18 | // 19 | // +k8s:conversion-gen=github.com/rancher/cluster-api-provider-rke2/bootstrap/api/v1beta1 20 | // +k8s:deepcopy-gen=package 21 | package v1alpha1 22 | -------------------------------------------------------------------------------- /bootstrap/config/webhook/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # the following config is for teaching kustomize where to look at when substituting vars. 2 | # It requires kustomize v2.1.0 or newer to work properly. 3 | nameReference: 4 | - kind: Service 5 | version: v1 6 | fieldSpecs: 7 | - kind: MutatingWebhookConfiguration 8 | group: admissionregistration.k8s.io 9 | path: webhooks/clientConfig/service/name 10 | - kind: ValidatingWebhookConfiguration 11 | group: admissionregistration.k8s.io 12 | path: webhooks/clientConfig/service/name 13 | 14 | namespace: 15 | - kind: MutatingWebhookConfiguration 16 | group: admissionregistration.k8s.io 17 | path: webhooks/clientConfig/service/namespace 18 | create: true 19 | - kind: ValidatingWebhookConfiguration 20 | group: admissionregistration.k8s.io 21 | path: webhooks/clientConfig/service/namespace 22 | create: true 23 | -------------------------------------------------------------------------------- /controlplane/api/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains a v1alpha1 api for controlplane resources. 18 | // 19 | // +k8s:conversion-gen=github.com/rancher/cluster-api-provider-rke2/controlplane/api/v1beta1 20 | // +k8s:deepcopy-gen=package 21 | package v1alpha1 22 | -------------------------------------------------------------------------------- /controlplane/config/webhook/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # the following config is for teaching kustomize where to look at when substituting vars. 2 | # It requires kustomize v2.1.0 or newer to work properly. 3 | nameReference: 4 | - kind: Service 5 | version: v1 6 | fieldSpecs: 7 | - kind: MutatingWebhookConfiguration 8 | group: admissionregistration.k8s.io 9 | path: webhooks/clientConfig/service/name 10 | - kind: ValidatingWebhookConfiguration 11 | group: admissionregistration.k8s.io 12 | path: webhooks/clientConfig/service/name 13 | 14 | namespace: 15 | - kind: MutatingWebhookConfiguration 16 | group: admissionregistration.k8s.io 17 | path: webhooks/clientConfig/service/namespace 18 | create: true 19 | - kind: ValidatingWebhookConfiguration 20 | group: admissionregistration.k8s.io 21 | path: webhooks/clientConfig/service/namespace 22 | create: true 23 | -------------------------------------------------------------------------------- /pkg/util/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package util 18 | 19 | import ( 20 | "testing" 21 | 22 | . "github.com/onsi/ginkgo/v2" 23 | . "github.com/onsi/gomega" 24 | ) 25 | 26 | func TestUtil(t *testing.T) { 27 | RegisterFailHandler(Fail) 28 | 29 | RunSpecs(t, "Util Suite") 30 | } 31 | -------------------------------------------------------------------------------- /hack/tools/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | /* 5 | Copyright 2022 The Kubernetes Authors. 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // This package imports things required by build scripts, to force `go mod` to see them as dependencies 18 | package tools 19 | 20 | import ( 21 | _ "sigs.k8s.io/cluster-api/hack/tools/mdbook/embed" 22 | ) 23 | -------------------------------------------------------------------------------- /bootstrap/config/rbac/rke2configtemplate_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit rke2configtemplates. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: rke2configtemplate-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: bootstrap 10 | app.kubernetes.io/part-of: bootstrap 11 | app.kubernetes.io/managed-by: kustomize 12 | name: rke2configtemplate-editor-role 13 | rules: 14 | - apiGroups: 15 | - bootstrap.cluster.x-k8s.io 16 | resources: 17 | - rke2configtemplates 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - bootstrap.cluster.x-k8s.io 28 | resources: 29 | - rke2configtemplates/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy docs 2 | on: 3 | push: 4 | branches: 5 | - main 6 | workflow_dispatch: 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write # To push a branch 13 | pages: write # To push to a GitHub Pages site 14 | id-token: write # To update the deployment status 15 | steps: 16 | - uses: actions/checkout@v6 17 | with: 18 | fetch-depth: 0 19 | - name: Install mdbook and build book 20 | run: | 21 | cd ./docs/book 22 | make build 23 | - name: Setup Pages 24 | uses: actions/configure-pages@v5 25 | - name: Upload artifact 26 | uses: actions/upload-pages-artifact@v4 27 | with: 28 | path: './docs/book/book' 29 | - name: Deploy to GitHub Pages 30 | id: deployment 31 | uses: actions/deploy-pages@v4 -------------------------------------------------------------------------------- /bootstrap/internal/cloudinit/cloudinit_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cloudinit 18 | 19 | import ( 20 | "testing" 21 | 22 | . "github.com/onsi/ginkgo/v2" 23 | . "github.com/onsi/gomega" 24 | ) 25 | 26 | func TestCloudInit(t *testing.T) { 27 | RegisterFailHandler(Fail) 28 | 29 | RunSpecs(t, "CloudInit Suite") 30 | } 31 | -------------------------------------------------------------------------------- /docs/book/util-embed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | REPO_ROOT=$(git rev-parse --show-toplevel) 22 | EMBED=${REPO_ROOT}/hack/tools/bin/mdbook-embed 23 | make "${EMBED}" GOPROXY="${GOPROXY:-"https://proxy.golang.org"}" &>/dev/null 24 | ${EMBED} "$@" -------------------------------------------------------------------------------- /controlplane/PROJECT: -------------------------------------------------------------------------------- 1 | domain: cluster.x-k8s.io 2 | layout: 3 | - go.kubebuilder.io/v3 4 | projectName: controlplane 5 | repo: github.com/rancher/cluster-api-provider-rke2/controlplane 6 | resources: 7 | - api: 8 | crdVersion: v1 9 | namespaced: true 10 | controller: true 11 | domain: cluster.x-k8s.io 12 | group: controlplane 13 | kind: Rke2ControlPlane 14 | path: github.com/rancher/cluster-api-provider-rke2/controlplane/api/v1alpha1 15 | version: v1alpha1 16 | webhooks: 17 | defaulting: true 18 | validation: true 19 | webhookVersion: v1 20 | - api: 21 | crdVersion: v1 22 | namespaced: true 23 | domain: cluster.x-k8s.io 24 | group: controlplane 25 | kind: Rke2ControlPlaneTemplate 26 | path: github.com/rancher/cluster-api-provider-rke2/controlplane/api/v1alpha1 27 | version: v1alpha1 28 | webhooks: 29 | defaulting: true 30 | validation: true 31 | webhookVersion: v1 32 | version: "3" 33 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yml: -------------------------------------------------------------------------------- 1 | name: E2E tests 2 | on: 3 | pull_request: 4 | merge_group: 5 | schedule: 6 | - cron: '0 1 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | e2e-tests: 11 | runs-on: runs-on,runner=4cpu-linux-x64,run-id=${{ github.run_id }} 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v6 15 | with: 16 | fetch-depth: 0 17 | - name: Install Go 18 | uses: actions/setup-go@v6 19 | with: 20 | go-version-file: go.mod 21 | - name: Setup kind 22 | uses: helm/kind-action@v1.13.0 23 | with: 24 | install_only: true 25 | - name: Run E2E tests 26 | run: make test-e2e 27 | - name: Archive artifacts 28 | if: always() 29 | uses: actions/upload-artifact@v6.0.0 30 | with: 31 | name: e2e-artifacts 32 | path: _artifacts 33 | if-no-files-found: ignore 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cluster API Provider RKE2 2 | 3 | ![GitHub](https://img.shields.io/github/license/rancher/cluster-api-provider-rke2) 4 | 5 | ------ 6 | 7 | ## What is Cluster API Provider RKE2 8 | 9 | The [Cluster API](https://cluster-api.sigs.k8s.io/) brings declarative, Kubernetes-style APIs to cluster creation, configuration and management. 10 | 11 | Cluster API Provider RKE2 is a combination of 2 provider types, a __Cluster API Control Plane Provider__ for provisioning Kubernetes control plane nodes and a __Cluster API Bootstrap Provider__ for bootstrapping Kubernetes on a machine where [RKE2](https://docs.rke2.io/) is used as the Kubernetes distro. 12 | 13 | ------ 14 | 15 | Please see our [book](https://rancher.github.io/cluster-api-provider-rke2/) for in-depth documentation. 16 | 17 | ------ 18 | 19 | ## How to contribute? 20 | 21 | See our [contributor guide](https://rancher.github.io/cluster-api-provider-rke2/04_developer/00.html) for more details on how to get involved. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | bin 9 | hack/tools/bin 10 | 11 | # Test binary, build with `go test -c` 12 | *.test 13 | 14 | # Output of Makefile targets using sed on MacOS systems 15 | *.yaml-e 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | 20 | # IntelliJ 21 | .idea/ 22 | *.iml 23 | 24 | # VSCode 25 | .vscode/ 26 | *.code-workspace 27 | 28 | # Kubernetes Generated files - skip generated files, except for vendored files 29 | 30 | !vendor/**/zz_generated.* 31 | 32 | # private key files and certificates 33 | *.key 34 | *.pem 35 | *.crt 36 | 37 | # environment configuration files 38 | *.env 39 | 40 | # editor and IDE paraphernalia 41 | .idea 42 | *.swp 43 | *.swo 44 | *~ 45 | 46 | # Common editor / temporary files 47 | *~ 48 | *.tmp 49 | .DS_Store 50 | 51 | # test results 52 | _artifacts 53 | 54 | # release artifacts 55 | out 56 | _releasenotes 57 | 58 | .tiltbuild 59 | -------------------------------------------------------------------------------- /tilt-provider.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "rke2-control-plane", 4 | "config": { 5 | "context": "controlplane", 6 | "image": "ghcr.io/rancher/cluster-api-provider-rke2-controlplane:dev", 7 | "live_reload_deps": [ 8 | "main.go", 9 | "go.mod", 10 | "go.sum", 11 | "api", 12 | "internal", 13 | "pkg", 14 | "../pkg" 15 | ], 16 | "label": "CAPRKE2", 17 | "kustomize_config": true 18 | } 19 | }, 20 | { 21 | "name": "rke2-bootstrap", 22 | "config": { 23 | "context": "bootstrap", 24 | "image": "ghcr.io/rancher/cluster-api-provider-rke2-bootstrap:dev", 25 | "live_reload_deps": [ 26 | "main.go", 27 | "go.mod", 28 | "go.sum", 29 | "api", 30 | "internal", 31 | "pkg", 32 | "../pkg" 33 | ], 34 | "label": "CAPBPR", 35 | "kustomize_config": true 36 | } 37 | } 38 | ] -------------------------------------------------------------------------------- /bootstrap/api/v1beta1/conversion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE LLC. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1beta1 18 | 19 | // Hub is a conversion hub for RKE2ConfigTemplate. 20 | func (*RKE2ConfigTemplate) Hub() {} 21 | 22 | // Hub is a conversion hub for RKE2ConfigTemplateList. 23 | func (*RKE2ConfigTemplateList) Hub() {} 24 | 25 | // Hub is a conversion hub for RKE2Config. 26 | func (*RKE2Config) Hub() {} 27 | 28 | // Hub is a conversion hub for RKE2ConfigList. 29 | func (*RKE2ConfigList) Hub() {} 30 | -------------------------------------------------------------------------------- /docs/book/src/02_topics/09_secrets_encryption.md: -------------------------------------------------------------------------------- 1 | # Configuring Secrets encryption 2 | 3 | ## Overview 4 | 5 | By default, RKE2 enables Secret encryotion at rest with `aescbc` provider and generate private key automatically. [Refer](https://docs.rke2.io/security/secrets_encryption) 6 | 7 | ## Customizing Encryption provider 8 | 9 | To configure different provider (`aescbc` or `secretbox`) or specify encryption key explicitly configure `spec.serverConfig.secretsEncryption` block 10 | 11 | Expample: 12 | 13 | ```yaml 14 | apiVersion: controlplane.cluster.x-k8s.io/v1beta1 15 | kind: RKE2ControlPlane 16 | metadata: 17 | name: my-cluster-control-plane 18 | spec: 19 | serverConfig: 20 | secretsEncryption: 21 | provider: "secretbox" 22 | encryptionKeySecret: 23 | name: encryption-key 24 | namespace: exmaple 25 | ``` 26 | 27 | ## Encryption secret format 28 | 29 | When configuring the `encryptionKeySecret`, ensure the secret contains the following keys: 30 | 31 | - **encryptionKey** - base64 decoded value of the encryption key 32 | -------------------------------------------------------------------------------- /controlplane/internal/controllers/const.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controllers 18 | 19 | import "time" 20 | 21 | const ( 22 | // deleteRequeueAfter is how long to wait before checking again to see if 23 | // all control plane machines have been deleted. 24 | deleteRequeueAfter = 30 * time.Second 25 | 26 | // preflightFailedRequeueAfter is how long to wait before trying to scale 27 | // up/down if some preflight check for those operation has failed. 28 | preflightFailedRequeueAfter = 15 * time.Second 29 | ) 30 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: '15 7 * * *' 6 | 7 | jobs: 8 | stale: 9 | 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | pull-requests: write 14 | 15 | steps: 16 | - uses: actions/stale@v10 17 | with: 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | days-before-issue-stale: 90 20 | days-before-pr-stale: 90 21 | days-before-issue-close: 365 22 | days-before-pr-close: -1 23 | stale-issue-message: 'This issue is stale because it has been open 90 days with no activity.' 24 | stale-issue-label: 'lifecycle/stale' 25 | stale-pr-message: 'This PR is stale because it has been open 90 days with no activity.' 26 | stale-pr-label: 'lifecycle/stale' 27 | close-issue-message: 'This issue was closed because it has been stalled for 365 days with no activity.' 28 | close-issue-label: 'lifecycle/rotten' 29 | exempt-issue-labels: 'lifecycle/frozen' 30 | exempt-pr-labels: 'lifecycle/frozen' 31 | 32 | -------------------------------------------------------------------------------- /bootstrap/config/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | # The following manifests contain a self-signed issuer CR and a certificate CR. 2 | # More document can be found at https://docs.cert-manager.io 3 | # WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. 4 | apiVersion: cert-manager.io/v1 5 | kind: Issuer 6 | metadata: 7 | name: selfsigned-issuer 8 | namespace: system 9 | spec: 10 | selfSigned: {} 11 | --- 12 | apiVersion: cert-manager.io/v1 13 | kind: Certificate 14 | metadata: 15 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 16 | namespace: system 17 | spec: 18 | # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize 19 | dnsNames: 20 | - SERVICE_NAME.SERVICE_NAMESPACE.svc 21 | - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local 22 | issuerRef: 23 | kind: Issuer 24 | name: selfsigned-issuer 25 | secretName: rke2-bootstrap-webhook-service-cert # this secret will not be prefixed, since it's not managed by kustomize 26 | subject: 27 | organizations: 28 | - Rancher by SUSE -------------------------------------------------------------------------------- /controlplane/config/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | # The following manifests contain a self-signed issuer CR and a certificate CR. 2 | # More document can be found at https://docs.cert-manager.io 3 | # WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. 4 | apiVersion: cert-manager.io/v1 5 | kind: Issuer 6 | metadata: 7 | name: selfsigned-issuer 8 | namespace: system 9 | spec: 10 | selfSigned: {} 11 | --- 12 | apiVersion: cert-manager.io/v1 13 | kind: Certificate 14 | metadata: 15 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 16 | namespace: system 17 | spec: 18 | # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize 19 | dnsNames: 20 | - SERVICE_NAME.SERVICE_NAMESPACE.svc 21 | - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local 22 | issuerRef: 23 | kind: Issuer 24 | name: selfsigned-issuer 25 | secretName: rke2-controlplane-webhook-service-cert # this secret will not be prefixed, since it's not managed by kustomize 26 | subject: 27 | organizations: 28 | - Rancher by SUSE 29 | -------------------------------------------------------------------------------- /image-builder/README.md: -------------------------------------------------------------------------------- 1 | # RKE2 CAPI image builder 2 | 3 | ## Description 4 | 5 | This directory contains the scripts and configuration files to build the images used by the RKE2 CAPI provider. It relies on the [packer](https://www.packer.io/) tool to build the images. 6 | 7 | We are using bash scripts to provision images with required dependencies, scripts for each platform are located in the `scripts` directory. 8 | 9 | ## AWS 10 | 11 | ### Requirements 12 | 13 | - Your AWS account must have the following permissions: https://developer.hashicorp.com/packer/plugins/builders/amazon#iam-task-or-instance-role 14 | 15 | - You must a default VPC in your AWS account. If you don't have one, you can create one using the following command: 16 | 17 | ```bash 18 | aws ec2 create-default-vpc 19 | ``` 20 | 21 | ### Steps 22 | 23 | For building the AWS AMIs, you can run the following command, this command will build a private openSUSE AMI: 24 | 25 | ```bash 26 | make build-aws-opensuse-leap-155 RKE2_VERSION=${RKE2_VERSION} 27 | ``` 28 | or 29 | 30 | ```bash 31 | make help 32 | ``` 33 | and it will show you the available options. 34 | -------------------------------------------------------------------------------- /controlplane/api/v1beta1/conversion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE LLC. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1beta1 18 | 19 | // Hub is a conversion hub for the RKE2ControlPlane resource. 20 | func (*RKE2ControlPlane) Hub() {} 21 | 22 | // Hub is a conversion hub for the RKE2ControlPlaneList resource. 23 | func (*RKE2ControlPlaneList) Hub() {} 24 | 25 | // Hub is a conversion hub for the RKE2ControlPlaneTemplate resource. 26 | func (*RKE2ControlPlaneTemplate) Hub() {} 27 | 28 | // Hub is a conversion hub for the RKE2ControlPlaneTemplateList resource. 29 | func (*RKE2ControlPlaneTemplateList) Hub() {} 30 | -------------------------------------------------------------------------------- /docs/book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](./00_introduction.md) 4 | - [User Guide](./01_user/00.md) 5 | - [Getting Started](./01_user/01_getting-started.md) 6 | - [Topics](./02_topics/00.md) 7 | - [Air-gapped installation](./02_topics/01_air-gapped-installation.md) 8 | - [Node registration methods](./02_topics/02_node-registration-methods.md) 9 | - [CIS and PSA](./02_topics/03_cis-psa.md) 10 | - [Embedded registry](./02_topics/04_embedded-registry.md) 11 | - [User data compression](./02_topics/05_user_data_compression.md) 12 | - [Configuring manager options](./02_topics/06_configure-manager-options.md) 13 | - [External load balancer exclusion](./02_topics/07_load_balancer_exclusion.md) 14 | - [External datastore](./02_topics/08_external_datastore.md) 15 | - [Examples](./03_examples/00.md) 16 | - [AWS](./03_examples/01_aws.md) 17 | - [vSphere](./03_examples/02_vsphere.md) 18 | - [Docker](./03_examples/03_docker.md) 19 | - [Developer Guide](./04_developer/00.md) 20 | - [Development](./04_developer/01_development.md) 21 | - [Releasing](./04_developer/02_releasing.md) 22 | - [Reference](./05_reference/00.md) 23 | -------------------------------------------------------------------------------- /test/e2e/const.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | /* 5 | Copyright 2024 SUSE. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | package e2e 21 | 22 | import ( 23 | _ "embed" 24 | ) 25 | 26 | var ( 27 | //go:embed data/infrastructure/clusterclass-template-docker.yaml 28 | ClusterClassDocker []byte 29 | //go:embed data/infrastructure/cluster-from-clusterclass-template-docker.yaml 30 | ClusterFromClusterClassDocker []byte 31 | //go:embed data/infrastructure/cluster-template-docker-external-datastore.yaml 32 | ClusterTemplateDockerExternalDatastore []byte 33 | //go:embed data/infrastructure/postgres.yaml 34 | Postgres []byte 35 | ) 36 | -------------------------------------------------------------------------------- /scripts/ci-lint-dockerfiles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2022 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | HADOLINT_VER=${1:-latest} 22 | HADOLINT_FAILURE_THRESHOLD=${2:-warning} 23 | 24 | FILES=$(find -- * -name Dockerfile) 25 | while read -r file; do 26 | echo "Linting: ${file}" 27 | # Configure the linter to fail for warnings and errors. Can be set to: error | warning | info | style | ignore | none 28 | docker run --rm -i ghcr.io/hadolint/hadolint:"${HADOLINT_VER}" hadolint --failure-threshold "${HADOLINT_FAILURE_THRESHOLD}" - < "${file}" 29 | done <<< "${FILES}" 30 | -------------------------------------------------------------------------------- /docs/book/src/00_introduction.md: -------------------------------------------------------------------------------- 1 | # Cluster API Provider RKE2 2 | 3 | ![GitHub](https://img.shields.io/github/license/rancher/cluster-api-provider-rke2) 4 | 5 | ------ 6 | 7 | ## What is Cluster API Provider RKE2 8 | 9 | The [Cluster API](https://cluster-api.sigs.k8s.io/) brings declarative, Kubernetes-style APIs to cluster creation, configuration and management. 10 | 11 | Cluster API Provider RKE2 (CAPRKE2) is a combination of 2 provider types, a __Cluster API Control Plane Provider__ for provisioning Kubernetes control plane nodes and a __Cluster API Bootstrap Provider__ for bootstrapping Kubernetes on a machine where [RKE2](https://docs.rke2.io/) is used as the Kubernetes distro. 12 | 13 | ------ 14 | 15 | ## Getting Started 16 | Follow our [getting started guide](./01_user/01_getting-started.md) to start creating RKE2 clusters with CAPI. 17 | 18 | ## Developer Guide 19 | Check our [developer guide](./04_developer/01_development.md) for instructions on how to setup your dev environment in order to contribute to this project. 20 | 21 | ## Get in contact 22 | You can get in contact with us via the [#capbr](https://rancher-users.slack.com/archives/C046X0CDKCH) channel on the [Rancher Users Slack](https://slack.rancher.io/). 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | labels: 8 | - "kind/cleanup" 9 | - "area/dependency" 10 | ignore: 11 | # Ignore Cluster-API as its upgraded manually. 12 | - dependency-name: "sigs.k8s.io/cluster-api" 13 | # Ignore controller-runtime as its upgraded manually. 14 | - dependency-name: "sigs.k8s.io/controller-runtime" 15 | # Ignore k8s and its transitives modules as they are upgraded manually 16 | # together with controller-runtime. 17 | - dependency-name: "k8s.io/*" 18 | - dependency-name: "go.etcd.io/*" 19 | - dependency-name: "google.golang.org/grpc" 20 | - package-ecosystem: "docker" 21 | directory: "/" 22 | schedule: 23 | interval: "weekly" 24 | labels: 25 | - "kind/cleanup" 26 | - "area/build-and-release" 27 | - package-ecosystem: "github-actions" 28 | directory: "/" 29 | schedule: 30 | interval: "weekly" 31 | labels: 32 | - "kind/cleanup" 33 | - "area/build-and-release" 34 | - package-ecosystem: "devcontainers" 35 | directory: "/" 36 | schedule: 37 | interval: weekly 38 | -------------------------------------------------------------------------------- /docs/book/src/03_examples/03_docker.md: -------------------------------------------------------------------------------- 1 | # Cluster API Docker Infrastructure Provider 2 | 3 | This page focuses on using the RKE2 provider with the Docker Infrastructure provider. 4 | 5 | ## Setting up the Management Cluster 6 | 7 | Make sure you set up a Management Cluster to use with Cluster API, you can follow instructions from the Cluster API [book](https://cluster-api.sigs.k8s.io/user/quick-start.html). 8 | 9 | ## Create a workload cluster 10 | 11 | Before creating a workload clusters, it is required to set the following environment variables: 12 | 13 | ```bash 14 | export CONTROL_PLANE_MACHINE_COUNT=3 15 | export WORKER_MACHINE_COUNT=1 16 | export RKE2_VERSION=v1.30.2+rke2r1 17 | export KIND_IMAGE_VERSION=v1.30.0 18 | ``` 19 | 20 | Now, we can generate the YAML files from the templates using `clusterctl generate yaml` command: 21 | 22 | ```bash 23 | clusterctl generate cluster --from https://github.com/rancher/cluster-api-provider-rke2/blob/main/examples/templates/docker/cluster-template.yaml -n example-docker rke2-docker > docker-rke2-clusterctl.yaml 24 | ``` 25 | 26 | After examining the result YAML file, you can apply to the management cluster using: 27 | 28 | ```bash 29 | kubectl apply -f docker-rke2-clusterctl.yaml 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/book/src/02_topics/05_user_data_compression.md: -------------------------------------------------------------------------------- 1 | # Configuring User Data Compression 2 | 3 | ## Overview 4 | 5 | Cloud-init user-data can grow significantly in size, especially when many configurations and embedded scripts are used. Some infrastructure providers (e.g., AWS, OpenStack) have strict size limits for user-data, which can lead to provisioning failures. To address this, the gzipUserData field in RKE2ConfigSpec allows optional gzip compression of the user-data payload before it is passed to the infrastructure provider. 6 | 7 | This feature helps reduce the size of the cloud-init payload but should only be used when the infrastructure provider supports gzipped user-data. For example, OpenStack typically handles it well, while others like Metal3 may not. 8 | 9 | ## Using Gzip Compression for User Data 10 | 11 | The `gzipUserData` field in RKE2ConfigSpec allows you to enable gzip compression for the rendered cloud-init user data. 12 | By default, `gzipUserData` is set to `false`. When set to `true`, the user-data is gzipped before being passed to the infrastructure provider. 13 | 14 | Example: 15 | 16 | ```yaml 17 | apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 18 | kind: RKE2Config 19 | metadata: 20 | name: my-cluster-bootstrap 21 | spec: 22 | gzipUserData: true 23 | ``` 24 | -------------------------------------------------------------------------------- /pkg/util/util_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("Testing RKE2 to Kubernetes Version conversion", func() { 9 | machineVersion := "1.24.6" 10 | rke2Version := "v1.24.6+rke2r1" 11 | It("Should match RKE2 and Kubernetes version", func() { 12 | cpKubeVersion, err := Rke2ToKubeVersion(rke2Version) 13 | Expect(err).ToNot(HaveOccurred()) 14 | Expect(cpKubeVersion).To(Equal(machineVersion)) 15 | }) 16 | }) 17 | 18 | var _ = Describe(("Testing GetMapKeysAsString"), func() { 19 | It("Should return a slice of strings", func() { 20 | testMap := map[string][]byte{ 21 | "hello": []byte("world"), 22 | "foo": []byte("bar"), 23 | } 24 | keys := GetMapKeysAsString(testMap) 25 | keysValid := keys == "hello,foo" || keys == "foo,hello" 26 | Expect(keysValid).To(BeTrue()) 27 | }) 28 | }) 29 | 30 | var _ = Describe("Testing IsRKE2Version", func() { 31 | k8sVersion := "1.24.6" 32 | rke2Version := "v1.24.6+rke2r1" 33 | 34 | It("Should return true if string is RKE2 version", func() { 35 | Expect(IsRKE2Version(rke2Version)).To(BeTrue()) 36 | }) 37 | 38 | It("Should return false if string is not RKE2 version", func() { 39 | Expect(IsRKE2Version(k8sVersion)).To(BeFalse()) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /docs/adr/0000-template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | - [Title](#title) 5 | - [Context](#context) 6 | - [Decision](#decision) 7 | - [Consequences](#consequences) 8 | 9 | 10 | 11 | # Title 12 | 13 | 14 | 15 | 16 | - Status: [proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)] 17 | - Date: 2020-10-29 [YYY-MM-DD - date of the decision] 18 | - Authors: [list of GitHub handles for the authors] 19 | - Deciders: [list of GitHub handles for those that made the decision] 20 | 21 | ## Context 22 | 23 | 24 | ## Decision 25 | 26 | 27 | ## Consequences 28 | -------------------------------------------------------------------------------- /bootstrap/config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | - events 12 | - secrets 13 | verbs: 14 | - create 15 | - delete 16 | - get 17 | - list 18 | - patch 19 | - update 20 | - watch 21 | - apiGroups: 22 | - authentication.k8s.io 23 | resources: 24 | - tokenreviews 25 | verbs: 26 | - create 27 | - apiGroups: 28 | - authorization.k8s.io 29 | resources: 30 | - subjectaccessreviews 31 | verbs: 32 | - create 33 | - apiGroups: 34 | - bootstrap.cluster.x-k8s.io 35 | resources: 36 | - rke2configs 37 | - rke2configs/finalizers 38 | - rke2configs/status 39 | verbs: 40 | - create 41 | - delete 42 | - get 43 | - list 44 | - patch 45 | - update 46 | - watch 47 | - apiGroups: 48 | - cluster.x-k8s.io 49 | resources: 50 | - clusters 51 | - clusters/status 52 | - machinepools 53 | - machinepools/status 54 | - machines 55 | - machines/status 56 | - machinesets 57 | verbs: 58 | - get 59 | - list 60 | - watch 61 | - apiGroups: 62 | - controlplane.cluster.x-k8s.io 63 | resources: 64 | - rke2controlplanes 65 | - rke2controlplanes/status 66 | verbs: 67 | - get 68 | - list 69 | - watch 70 | -------------------------------------------------------------------------------- /bootstrap/config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | labels: 2 | - includeSelectors: true 3 | pairs: 4 | cluster.x-k8s.io/v1beta1: v1alpha1_v1beta1 5 | 6 | # This kustomization.yaml is not intended to be run by itself, 7 | # since it depends on service name and namespace that are out of this kustomize package. 8 | # It should be run by config/default 9 | resources: 10 | - bases/bootstrap.cluster.x-k8s.io_rke2configs.yaml 11 | - bases/bootstrap.cluster.x-k8s.io_rke2configtemplates.yaml 12 | #+kubebuilder:scaffold:crdkustomizeresource 13 | 14 | patches: 15 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 16 | # patches here are for enabling the conversion webhook for each CRD 17 | - path: patches/webhook_in_rke2configs.yaml 18 | - path: patches/webhook_in_rke2configtemplates.yaml 19 | #+kubebuilder:scaffold:crdkustomizewebhookpatch 20 | 21 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 22 | # patches here are for enabling the CA injection for each CRD 23 | - path: patches/cainjection_in_rke2configs.yaml 24 | - path: patches/cainjection_in_rke2configtemplates.yaml 25 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch 26 | 27 | # the following config is for teaching kustomize how to do kustomization for CRDs. 28 | configurations: 29 | - kustomizeconfig.yaml 30 | -------------------------------------------------------------------------------- /controlplane/config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | labels: 2 | - includeSelectors: true 3 | pairs: 4 | cluster.x-k8s.io/v1beta1: v1alpha1_v1beta1 5 | 6 | # This kustomization.yaml is not intended to be run by itself, 7 | # since it depends on service name and namespace that are out of this kustomize package. 8 | # It should be run by config/default 9 | resources: 10 | - bases/controlplane.cluster.x-k8s.io_rke2controlplanes.yaml 11 | - bases/controlplane.cluster.x-k8s.io_rke2controlplanetemplates.yaml 12 | #+kubebuilder:scaffold:crdkustomizeresource 13 | 14 | patches: 15 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 16 | # patches here are for enabling the conversion webhook for each CRD 17 | - path: patches/webhook_in_rke2controlplanes.yaml 18 | - path: patches/webhook_in_rke2controlplanetemplates.yaml 19 | #+kubebuilder:scaffold:crdkustomizewebhookpatch 20 | 21 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 22 | # patches here are for enabling the CA injection for each CRD 23 | - path: patches/cainjection_in_rke2controlplanes.yaml 24 | - path: patches/cainjection_in_rke2controlplanetemplates.yaml 25 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch 26 | 27 | # the following config is for teaching kustomize how to do kustomization for CRDs. 28 | configurations: 29 | - kustomizeconfig.yaml 30 | -------------------------------------------------------------------------------- /scripts/install-mdbook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2022 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | VERSION=${1} 22 | OUTPUT_PATH=${2} 23 | 24 | # Ensure the output folder exists 25 | mkdir -p "${OUTPUT_PATH}" 26 | 27 | # Get what release to download 28 | RELEASE_NAME="" 29 | case "$OSTYPE" in 30 | darwin*) RELEASE_NAME="x86_64-apple-darwin.tar.gz" ;; 31 | linux*) RELEASE_NAME="x86_64-unknown-linux-gnu.tar.gz" ;; 32 | # msys*) echo "WINDOWS" ;; 33 | *) echo "No mdBook release available for: $OSTYPE" && exit 1;; 34 | esac 35 | 36 | # Download and extract the mdBook release 37 | curl -L "https://github.com/rust-lang/mdBook/releases/download/${VERSION}/mdbook-${VERSION}-${RELEASE_NAME}" | tar -xvz -C "${OUTPUT_PATH}" 38 | -------------------------------------------------------------------------------- /examples/templates/docker/air-gapped/image-building/README.md: -------------------------------------------------------------------------------- 1 | # Air-Gapped CAPD image building for CAPRKE2 2 | 3 | ## Needed Artifacts for RKE2 Air Gapped Installation 4 | 5 | In the folder [files](files/), you need to add the files `rke2.linux-amd64.tar.gz` and `rke2-images.linux-amd64.tar.zst` from the RKE2 target release. You can use the script [download-rke2-artifacts.sh](download-rke2-artifacts.sh) to download a specific version of RKE2 by setting the environment variable `RKE2_VERSION` to the desired version. 6 | 7 | ```bash 8 | RKE2_VERSION=v1.23.16+rke2r1 ./download-rke2-artifacts.sh 9 | ``` 10 | which should, after successful download show: 11 | 12 | ``` 13 | Downloading RKE2 artifacts for version v1.23.16+rke2r1 ... 14 | Done. 15 | ``` 16 | 17 | ## Image Building 18 | 19 | There is a [Dockerfile](Dockerfile) provided for reference in order to build a container image that works in Air-Gapped mode. After making sure the [files](files/) folder contains the RKE2 artifact files and the `install.sh` file, you can build a container image using the command: 20 | 21 | ``` 22 | export KUBERNETES_VERSION=v1.23.16 23 | docker build -t rke2-ubuntu:$KUBERNETES_VERSION . 24 | ``` 25 | 26 | This image can then be uploaded to a container image registry and used in [manifest template for CAPD](../cluster-template.yaml) under the `spec.template.spec.customImage` field for the `DockerMachineTemplate` object. 27 | -------------------------------------------------------------------------------- /pkg/proxy/proxy.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package proxy 18 | 19 | import ( 20 | "time" 21 | 22 | "k8s.io/client-go/rest" 23 | ) 24 | 25 | // Proxy defines the API server port-forwarded proxy. 26 | type Proxy struct { 27 | // Kind is the kind of Kubernetes resource 28 | Kind string 29 | 30 | // Namespace is the namespace in which the Kubernetes resource exists 31 | Namespace string 32 | 33 | // ResourceName is the name of the Kubernetes resource 34 | ResourceName string 35 | 36 | // KubeConfig is the config to connect to the API server 37 | KubeConfig *rest.Config 38 | 39 | // KeepAlive specifies how often a keep alive message is sent to hold 40 | // the connection open 41 | KeepAlive *time.Duration 42 | 43 | // Port is the port to be forwarded from the relevant resource 44 | Port int 45 | } 46 | -------------------------------------------------------------------------------- /test/e2e/data/infrastructure/postgres.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: postgres 5 | namespace: default 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: postgres 11 | template: 12 | metadata: 13 | labels: 14 | app: postgres 15 | spec: 16 | containers: 17 | - name: postgres 18 | image: 'postgres:17' 19 | imagePullPolicy: IfNotPresent 20 | ports: 21 | - containerPort: 5432 22 | envFrom: 23 | - configMapRef: 24 | name: postgres-secret 25 | volumeMounts: 26 | - mountPath: /var/lib/postgresql/data 27 | name: postgresdata 28 | volumes: 29 | - name: postgresdata 30 | emptyDir: 31 | sizeLimit: 500Mi 32 | medium: Memory 33 | --- 34 | apiVersion: v1 35 | kind: Service 36 | metadata: 37 | name: postgres 38 | namespace: default 39 | labels: 40 | app: postgres 41 | spec: 42 | ports: 43 | - port: 5432 44 | protocol: TCP 45 | nodePort: 30000 46 | selector: 47 | app: postgres 48 | type: NodePort 49 | --- 50 | apiVersion: v1 51 | kind: ConfigMap 52 | metadata: 53 | name: postgres-secret 54 | namespace: default 55 | labels: 56 | app: postgres 57 | data: 58 | POSTGRES_DB: postgres 59 | POSTGRES_USER: postgres 60 | POSTGRES_PASSWORD: postgres -------------------------------------------------------------------------------- /docs/book/src/02_topics/07_load_balancer_exclusion.md: -------------------------------------------------------------------------------- 1 | # Excluding control plane Nodes from external load balancers 2 | 3 | ## Overview 4 | 5 | The `rke2.controlplane.cluster.x-k8s.io/load-balancer-exclusion: "true"` annotation can be set on `RKE2ControlPlanes` in order to apply the [nodes.kubernetes.io/exclude-from-external-load-balancers](https://kubernetes.io/docs/reference/labels-annotations-taints/#node-kubernetes-io-exclude-from-external-load-balancers) label on Nodes, during the pre-drain CAPI Machine phase, just before the Machine is deleted. 6 | 7 | This allows external load balancers that honor this label, to have enough time to stop advertising the Node, before etcd membership is lost, or the Machine is shut down. 8 | 9 | ## Using Load Balancer Exclusion 10 | 11 | The annotation can be set on `RKE2ControlPlanes`. 12 | Upon setting the annotation, all control plane Machines will be marked with `pre-drain.delete.hook.machine.cluster.x-k8s.io/rke2-lb-exclusion` annotation. 13 | 14 | Note that it is possible to remove the annotation from the `RKE2ControlPlane`, triggering a cleanup of the related `pre-drain` hook annotation on all Machines. 15 | 16 | Example: 17 | 18 | ```yaml 19 | apiVersion: controlplane.cluster.x-k8s.io/v1beta1 20 | kind: RKE2ControlPlane 21 | metadata: 22 | name: my-control-plane 23 | annotations: 24 | rke2.controlplane.cluster.x-k8s.io/load-balancer-exclusion: "true" 25 | ``` 26 | -------------------------------------------------------------------------------- /scripts/go-install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2021 The Kubernetes Authors. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -o errexit 17 | set -o nounset 18 | set -o pipefail 19 | 20 | if [ -z "${1}" ]; then 21 | echo "must provide module as first parameter" 22 | exit 1 23 | fi 24 | 25 | if [ -z "${2}" ]; then 26 | echo "must provide binary name as second parameter" 27 | exit 1 28 | fi 29 | 30 | if [ -z "${3}" ]; then 31 | echo "must provide version as third parameter" 32 | exit 1 33 | fi 34 | 35 | if [ -z "${GOBIN}" ]; then 36 | echo "GOBIN is not set. Must set GOBIN to install the bin in a specified directory." 37 | exit 1 38 | fi 39 | 40 | rm -f "${GOBIN}/${2}"* || true 41 | 42 | # install the golang module specified as the first argument 43 | go install "${1}@${3}" 44 | mv "${GOBIN}/${2}" "${GOBIN}/${2}-${3}" 45 | ln -sf "${GOBIN}/${2}-${3}" "${GOBIN}/${2}" 46 | -------------------------------------------------------------------------------- /bootstrap/PROJECT: -------------------------------------------------------------------------------- 1 | # Code generated by tool. DO NOT EDIT. 2 | # This file is used to track the info used to scaffold your project 3 | # and allow the plugins properly work. 4 | # More info: https://book.kubebuilder.io/reference/project-config.html 5 | domain: cluster.x-k8s.io 6 | layout: 7 | - go.kubebuilder.io/v3 8 | projectName: bootstrap 9 | repo: github.com/rancher/cluster-api-provider-rke2/bootstrap 10 | resources: 11 | - api: 12 | crdVersion: v1 13 | namespaced: true 14 | controller: true 15 | domain: cluster.x-k8s.io 16 | group: bootstrap 17 | kind: Rke2Config 18 | path: github.com/rancher/cluster-api-provider-rke2/bootstrap/api/v1alpha1 19 | version: v1alpha1 20 | webhooks: 21 | defaulting: true 22 | validation: true 23 | webhookVersion: v1 24 | - api: 25 | crdVersion: v1 26 | namespaced: true 27 | domain: cluster.x-k8s.io 28 | group: bootstrap 29 | kind: Rke2ConfigTemplate 30 | path: github.com/rancher/cluster-api-provider-rke2/bootstrap/api/v1alpha1 31 | version: v1alpha1 32 | webhooks: 33 | defaulting: true 34 | validation: true 35 | webhookVersion: v1 36 | - api: 37 | crdVersion: v1 38 | namespaced: true 39 | domain: cluster.x-k8s.io 40 | group: bootstrap 41 | kind: RKE2ConfigTemplate 42 | path: github.com/rancher/cluster-api-provider-rke2/bootstrap/api/v1beta1 43 | version: v1beta1 44 | webhooks: 45 | conversion: true 46 | webhookVersion: v1 47 | version: "3" 48 | -------------------------------------------------------------------------------- /hack/utils.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright © 2023 - 2024 SUSE LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # get_root_path returns the root path of the project source tree 18 | get_root_path() { 19 | git rev-parse --show-toplevel 20 | } 21 | 22 | # cd_root_path cds to the root path of the project source tree 23 | cd_root_path() { 24 | cd "$(get_root_path)" || exit 25 | } 26 | 27 | # ensure GOPATH/bin is in PATH as we may install binaries to that directory in 28 | # other ensure-* scripts, and expect them to be found in PATH later on 29 | verify_gopath_bin() { 30 | local gopath_bin 31 | 32 | gopath_bin="$(go env GOPATH)/bin" 33 | if ! printenv PATH | grep -q "${gopath_bin}"; then 34 | cat < **Note:** Enabling/Disabling feature gates is supported only for `rke2-bootstrap-controller-manager`. 24 | ```shell 25 | $ kubectl -n system edit deployment/rke2-bootstrap-controller-manager 26 | 27 | // Enable/disable available features by modifying args below. 28 | args: 29 | ... 30 | - "--feature-gates=MachinePool=true,ClusterResourceSet=true" 31 | ``` 32 | 33 | - Configure the concurrency options for manager: 34 | > **Note:** Configuring manager options is supported for `rke2-bootstrap-controller-manager` as well as `rke2-control-plane-controller-manager`. 35 | 36 | - Update `concurrency` arg: 37 | ```shell 38 | $ kubectl -n system edit deployment/controller-manager 39 | 40 | // Modify the value of --concurrency in args 41 | args: 42 | ... 43 | - "--concurrency=" 44 | ``` 45 | 46 | The patch shall rollout the deployment, and should spin up new pods with updated values. -------------------------------------------------------------------------------- /image-builder/scripts/opensuse/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | set -o xtrace 6 | 7 | setup_infrastructure () { 8 | if [[ "$1" == "aws" ]]; then 9 | zypper --gpg-auto-import-keys --non-interactive install awscli amazon-ssm-agent 10 | systemctl enable amazon-ssm-agent 11 | fi 12 | } 13 | 14 | configure_systemd () { 15 | echo "Enabling required systemd services" 16 | 17 | systemctl enable sshd 18 | systemctl enable cloud-final 19 | systemctl enable cloud-config 20 | systemctl enable cloud-init 21 | systemctl enable cloud-init-local 22 | 23 | systemctl stop cloud-final 24 | systemctl stop cloud-config 25 | systemctl stop cloud-init 26 | systemctl stop cloud-init-local 27 | } 28 | 29 | configure_cloudinit () { 30 | echo "Configuring cloud-init" 31 | 32 | cloud-init clean -s -l 33 | rm -f /var/log/cloud-init* 34 | rm -rf /var/lib/cloud/* 35 | 36 | mkdir -p /usr/lib/python3/dist-packages/cloudinit 37 | mv /tmp/features.py /usr/lib/python3.6/site-packages/cloudinit/features.py 38 | } 39 | 40 | cleanup_ssh_keys () { 41 | echo "Cleaning up SSH keys" 42 | 43 | rm -rf /etc/ssh/ssh_host_* 44 | rm -rf /root/.ssh/authorized_keys 45 | rm -rf /home/ec2-user/.ssh/authorized_keys 46 | } 47 | 48 | echo "Provisioning instance for $2" 49 | 50 | echo "Install required packages" 51 | 52 | zypper --gpg-auto-import-keys ref && \ 53 | zypper --gpg-auto-import-keys --non-interactive install \ 54 | curl \ 55 | openssh-server \ 56 | cloud-init \ 57 | systemd \ 58 | openssh \ 59 | 60 | echo "Install RKE2 components" 61 | 62 | mkdir -p /opt/rke2-artifacts 63 | curl -sfL -o /opt/rke2-artifacts/rke2-images.linux-amd64.tar.zst https://github.com/rancher/rke2/releases/download/v${1}/rke2-images.linux-amd64.tar.zst 64 | curl -sfL -o /opt/rke2-artifacts/rke2.linux-amd64.tar.gz https://github.com/rancher/rke2/releases/download/v${1}/rke2.linux-amd64.tar.gz 65 | curl -sfL -o /opt/rke2-artifacts/sha256sum-amd64.txt https://github.com/rancher/rke2/releases/download/v${1}/sha256sum-amd64.txt 66 | curl -sfL -o /opt/install.sh https://get.rke2.io 67 | 68 | configure_systemd 69 | configure_cloudinit 70 | cleanup_ssh_keys 71 | setup_infrastructure $2 72 | 73 | echo "Done" -------------------------------------------------------------------------------- /bootstrap/api/v1alpha1/rke2configtemplate_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // RKE2ConfigTemplateSpec defines the specification of RKE2ConfigTemplate. 24 | type RKE2ConfigTemplateSpec struct { 25 | // Template references a RKE2ConfigTemplate, which is used to include an RKE2ConfigSpec struct. 26 | // This is used to include a desired RKE2ConfigSpec configuration when an RKE2Config resource is generated by a MachineDeployment resource. 27 | Template RKE2ConfigTemplateResource `json:"template"` 28 | } 29 | 30 | // +kubebuilder:object:root=true 31 | // +kubebuilder:subresource:status 32 | 33 | // RKE2ConfigTemplate is the Schema for the RKE2configtemplates API. 34 | type RKE2ConfigTemplate struct { 35 | metav1.TypeMeta `json:",inline"` 36 | metav1.ObjectMeta `json:"metadata,omitempty"` 37 | 38 | // Spec details the RKE2ConfigTemplate specification. 39 | Spec RKE2ConfigTemplateSpec `json:"spec"` 40 | } 41 | 42 | //+kubebuilder:object:root=true 43 | 44 | // RKE2ConfigTemplateList contains a list of RKE2ConfigTemplate. 45 | type RKE2ConfigTemplateList struct { 46 | metav1.TypeMeta `json:",inline"` 47 | metav1.ListMeta `json:"metadata,omitempty"` 48 | Items []RKE2ConfigTemplate `json:"items"` 49 | } 50 | 51 | // RKE2ConfigTemplateResource is a struct that wraps the desired spec for the RKE2ConfigSpec inside the template field. 52 | type RKE2ConfigTemplateResource struct { 53 | // Spec is the RKE2ConfigSpec that should be used for the template. 54 | Spec RKE2ConfigSpec `json:"spec"` 55 | } 56 | 57 | func init() { 58 | SchemeBuilder.Register(&RKE2ConfigTemplate{}, &RKE2ConfigTemplateList{}) 59 | } 60 | -------------------------------------------------------------------------------- /bootstrap/api/v1beta1/rke2configtemplate_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE LLC. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1beta1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // RKE2ConfigTemplateSpec defines the specification of RKE2ConfigTemplate. 24 | type RKE2ConfigTemplateSpec struct { 25 | // Template references a RKE2ConfigTemplate, which is used to include an RKE2ConfigSpec struct. 26 | // This is used to include a desired RKE2ConfigSpec configuration when an RKE2Config resource is generated by a MachineDeployment resource. 27 | Template RKE2ConfigTemplateResource `json:"template"` 28 | } 29 | 30 | // +kubebuilder:object:root=true 31 | // +kubebuilder:subresource:status 32 | // +kubebuilder:storageversion 33 | 34 | // RKE2ConfigTemplate is the Schema for the RKE2configtemplates API. 35 | type RKE2ConfigTemplate struct { 36 | metav1.TypeMeta `json:",inline"` 37 | metav1.ObjectMeta `json:"metadata,omitempty"` 38 | 39 | // Spec details the RKE2ConfigTemplate specification. 40 | Spec RKE2ConfigTemplateSpec `json:"spec"` 41 | } 42 | 43 | //+kubebuilder:object:root=true 44 | 45 | // RKE2ConfigTemplateList contains a list of RKE2ConfigTemplate. 46 | type RKE2ConfigTemplateList struct { 47 | metav1.TypeMeta `json:",inline"` 48 | metav1.ListMeta `json:"metadata,omitempty"` 49 | Items []RKE2ConfigTemplate `json:"items"` 50 | } 51 | 52 | // RKE2ConfigTemplateResource is a struct that wraps the desired spec for the RKE2ConfigSpec inside the template field. 53 | type RKE2ConfigTemplateResource struct { 54 | // Spec is the RKE2ConfigSpec that should be used for the template. 55 | Spec RKE2ConfigSpec `json:"spec"` 56 | } 57 | 58 | func init() { 59 | objectTypes = append(objectTypes, &RKE2ConfigTemplate{}, &RKE2ConfigTemplateList{}) 60 | } 61 | -------------------------------------------------------------------------------- /bootstrap/config/webhook/manifests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: MutatingWebhookConfiguration 4 | metadata: 5 | name: mutating-webhook-configuration 6 | webhooks: 7 | - admissionReviewVersions: 8 | - v1 9 | clientConfig: 10 | service: 11 | name: webhook-service 12 | namespace: system 13 | path: /mutate-bootstrap-cluster-x-k8s-io-v1beta1-rke2config 14 | failurePolicy: Fail 15 | name: mrke2config.kb.io 16 | rules: 17 | - apiGroups: 18 | - bootstrap.cluster.x-k8s.io 19 | apiVersions: 20 | - v1beta1 21 | operations: 22 | - CREATE 23 | - UPDATE 24 | resources: 25 | - rke2configs 26 | sideEffects: None 27 | - admissionReviewVersions: 28 | - v1 29 | clientConfig: 30 | service: 31 | name: webhook-service 32 | namespace: system 33 | path: /mutate-bootstrap-cluster-x-k8s-io-v1beta1-rke2configtemplate 34 | failurePolicy: Fail 35 | name: mrke2configtemplate.kb.io 36 | rules: 37 | - apiGroups: 38 | - bootstrap.cluster.x-k8s.io 39 | apiVersions: 40 | - v1beta1 41 | operations: 42 | - CREATE 43 | - UPDATE 44 | resources: 45 | - rke2configtemplates 46 | sideEffects: None 47 | --- 48 | apiVersion: admissionregistration.k8s.io/v1 49 | kind: ValidatingWebhookConfiguration 50 | metadata: 51 | name: validating-webhook-configuration 52 | webhooks: 53 | - admissionReviewVersions: 54 | - v1 55 | clientConfig: 56 | service: 57 | name: webhook-service 58 | namespace: system 59 | path: /validate-bootstrap-cluster-x-k8s-io-v1beta1-rke2config 60 | failurePolicy: Fail 61 | name: vrke2config.kb.io 62 | rules: 63 | - apiGroups: 64 | - bootstrap.cluster.x-k8s.io 65 | apiVersions: 66 | - v1beta1 67 | operations: 68 | - CREATE 69 | - UPDATE 70 | resources: 71 | - rke2configs 72 | sideEffects: None 73 | - admissionReviewVersions: 74 | - v1 75 | clientConfig: 76 | service: 77 | name: webhook-service 78 | namespace: system 79 | path: /validate-bootstrap-cluster-x-k8s-io-v1beta1-rke2configtemplate 80 | failurePolicy: Fail 81 | name: vrke2configtemplate.kb.io 82 | rules: 83 | - apiGroups: 84 | - bootstrap.cluster.x-k8s.io 85 | apiVersions: 86 | - v1beta1 87 | operations: 88 | - CREATE 89 | - UPDATE 90 | resources: 91 | - rke2configtemplates 92 | sideEffects: None 93 | -------------------------------------------------------------------------------- /controlplane/api/v1beta1/rke2controlplanetemplate_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE LLC. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1beta1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // RKE2ControlPlaneTemplateSpec defines the desired state of RKE2ControlPlaneTemplate. 24 | type RKE2ControlPlaneTemplateSpec struct { 25 | Template RKE2ControlPlaneTemplateResource `json:"template"` 26 | } 27 | 28 | // RKE2ControlPlaneTemplateResource contains spec for RKE2ControlPlaneTemplate. 29 | type RKE2ControlPlaneTemplateResource struct { 30 | // Spec is the specification of the desired behavior of the control plane. 31 | Spec RKE2ControlPlaneSpec `json:"spec"` 32 | } 33 | 34 | // +kubebuilder:object:root=true 35 | // +kubebuilder:resource:path=rke2controlplanetemplates,scope=Namespaced,categories=cluster-api,shortName=rke2ct 36 | // +kubebuilder:storageversion 37 | 38 | // RKE2ControlPlaneTemplate is the Schema for the rke2controlplanetemplates API. 39 | type RKE2ControlPlaneTemplate struct { 40 | metav1.TypeMeta `json:",inline"` 41 | metav1.ObjectMeta `json:"metadata,omitempty"` 42 | 43 | // Spec is the control plane specification for the template resource. 44 | Spec RKE2ControlPlaneTemplateSpec `json:"spec,omitempty"` 45 | // Status is the current state of the control plane. 46 | Status RKE2ControlPlaneStatus `json:"status,omitempty"` 47 | } 48 | 49 | //+kubebuilder:object:root=true 50 | 51 | // RKE2ControlPlaneTemplateList contains a list of RKE2ControlPlaneTemplate. 52 | type RKE2ControlPlaneTemplateList struct { 53 | metav1.TypeMeta `json:",inline"` 54 | metav1.ListMeta `json:"metadata,omitempty"` 55 | Items []RKE2ControlPlaneTemplate `json:"items"` 56 | } 57 | 58 | func init() { //nolint:gochecknoinits 59 | objectTypes = append(objectTypes, &RKE2ControlPlaneTemplate{}, &RKE2ControlPlaneTemplateList{}) 60 | } 61 | -------------------------------------------------------------------------------- /controlplane/internal/controllers/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controllers 18 | 19 | import ( 20 | "fmt" 21 | "path" 22 | "testing" 23 | 24 | . "github.com/onsi/ginkgo/v2" 25 | . "github.com/onsi/gomega" 26 | 27 | // +kubebuilder:scaffold:imports 28 | bootstrapv1 "github.com/rancher/cluster-api-provider-rke2/bootstrap/api/v1beta1" 29 | controlplanev1 "github.com/rancher/cluster-api-provider-rke2/controlplane/api/v1beta1" 30 | "github.com/rancher/cluster-api-provider-rke2/test/helpers" 31 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 32 | "k8s.io/client-go/kubernetes/scheme" 33 | clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 34 | ctrl "sigs.k8s.io/controller-runtime" 35 | ) 36 | 37 | var ( 38 | testEnv *helpers.TestEnvironment 39 | ctx = ctrl.SetupSignalHandler() 40 | ) 41 | 42 | func TestAPIs(t *testing.T) { 43 | RegisterFailHandler(Fail) 44 | setup() 45 | defer teardown() 46 | RunSpecs(t, "Controller Suite") 47 | } 48 | 49 | func setup() { 50 | utilruntime.Must(clusterv1.AddToScheme(scheme.Scheme)) 51 | utilruntime.Must(bootstrapv1.AddToScheme(scheme.Scheme)) 52 | utilruntime.Must(controlplanev1.AddToScheme(scheme.Scheme)) 53 | 54 | testEnvConfig := helpers.NewTestEnvironmentConfiguration([]string{ 55 | path.Join("bootstrap", "config", "crd", "bases"), 56 | path.Join("controlplane", "config", "crd", "bases"), 57 | }, 58 | ) 59 | var err error 60 | testEnv, err = testEnvConfig.Build() 61 | if err != nil { 62 | panic(err) 63 | } 64 | go func() { 65 | fmt.Println("Starting the manager") 66 | if err := testEnv.StartManager(ctx); err != nil { 67 | panic(fmt.Sprintf("Failed to start the envtest manager: %v", err)) 68 | } 69 | }() 70 | } 71 | 72 | func teardown() { 73 | if err := testEnv.Stop(); err != nil { 74 | panic(fmt.Sprintf("Failed to stop envtest: %v", err)) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /docs/book/src/02_topics/04_embedded-registry.md: -------------------------------------------------------------------------------- 1 | # Configuring Embedded Registry in RKE2 2 | 3 | ## Overview 4 | 5 | RKE2 allows users to enable an **embedded registry** on control plane nodes. When the `embeddedRegistry` option is set to `true` in the `serverConfig`, users can configure the registry using the `PrivateRegistriesConfig` field. 6 | The process follows [RKE2 docs](https://docs.rke2.io/install/registry_mirror). 7 | 8 | ## Enabling Embedded Registry 9 | 10 | To enable the embedded registry, set the `embeddedRegistry` field to `true` in the `serverConfig` section of the `RKE2ControlPlane` configuration: 11 | 12 | ```yaml 13 | apiVersion: controlplane.cluster.x-k8s.io/v1beta1 14 | kind: RKE2ControlPlane 15 | metadata: 16 | name: my-cluster-control-plane 17 | spec: 18 | serverConfig: 19 | embeddedRegistry: true 20 | ``` 21 | 22 | ## Configuring Private Registries 23 | 24 | Once the embedded registry is enabled, you can configure private registries using the `PrivateRegistriesConfig` field in `RKE2ConfigSpec`. This field allows you to define registry mirrors, authentication, and TLS settings. 25 | 26 | Example: 27 | 28 | ```yaml 29 | apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 30 | kind: RKE2Config 31 | metadata: 32 | name: my-cluster-bootstrap 33 | spec: 34 | privateRegistriesConfig: 35 | mirrors: 36 | "myregistry.example.com": 37 | endpoint: 38 | - "https://mirror1.example.com" 39 | - "https://mirror2.example.com" 40 | configs: 41 | "myregistry.example.com": 42 | authSecret: 43 | name: my-registry-secret 44 | namespace: my-secrets-namespace 45 | tls: 46 | tlsConfigSecret: 47 | name: my-registry-tls-secret 48 | namespace: my-secrets-namespace 49 | insecureSkipVerify: false 50 | ``` 51 | 52 | ## TLS Secret Format 53 | 54 | When configuring the `tlsConfigSecret`, ensure the secret contains the following keys: 55 | 56 | - **`ca.crt`** – CA certificate 57 | - **`tls.key`** – TLS private key 58 | - **`tls.crt`** – TLS certificate 59 | 60 | ## Auth Secret Format 61 | 62 | When configuring the `authSecret`, ensure the secret contains the following keys: 63 | 64 | - **`username` and `password`** - When using Basic Auth credentials 65 | - **`identity-token`** - When using a personal access token 66 | - **`auth`** - Is a base64 encoded string from the concatenation of the username, a colon, and the password 67 | -------------------------------------------------------------------------------- /controlplane/api/v1alpha1/rke2controlplanetemplate_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 24 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 25 | 26 | // RKE2ControlPlaneTemplateSpec defines the desired state of RKE2ControlPlaneTemplate. 27 | type RKE2ControlPlaneTemplateSpec struct { 28 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 29 | // Important: Run "make" to regenerate code after modifying this file 30 | } 31 | 32 | // RKE2ControlPlaneTemplateStatus defines the observed state of RKE2ControlPlaneTemplate. 33 | type RKE2ControlPlaneTemplateStatus struct { 34 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 35 | // Important: Run "make" to regenerate code after modifying this file 36 | } 37 | 38 | //+kubebuilder:object:root=true 39 | //+kubebuilder:subresource:status 40 | 41 | // RKE2ControlPlaneTemplate is the Schema for the rke2controlplanetemplates API. 42 | type RKE2ControlPlaneTemplate struct { 43 | metav1.TypeMeta `json:",inline"` 44 | metav1.ObjectMeta `json:"metadata,omitempty"` 45 | 46 | Spec RKE2ControlPlaneTemplateSpec `json:"spec,omitempty"` 47 | Status RKE2ControlPlaneTemplateStatus `json:"status,omitempty"` 48 | } 49 | 50 | //+kubebuilder:object:root=true 51 | 52 | // RKE2ControlPlaneTemplateList contains a list of RKE2ControlPlaneTemplate. 53 | type RKE2ControlPlaneTemplateList struct { 54 | metav1.TypeMeta `json:",inline"` 55 | metav1.ListMeta `json:"metadata,omitempty"` 56 | Items []RKE2ControlPlaneTemplate `json:"items"` 57 | } 58 | 59 | func init() { //nolint:gochecknoinits 60 | SchemeBuilder.Register(&RKE2ControlPlaneTemplate{}, &RKE2ControlPlaneTemplateList{}) 61 | } 62 | -------------------------------------------------------------------------------- /docs/adr/0002-deprecate-kubebuilder-defaults.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | - [Deprecate kubebuilder defaults](#deprecate-kubebuilder-defaults) 5 | - [Context](#context) 6 | - [Decision](#decision) 7 | - [Consequences](#consequences) 8 | 9 | 10 | 11 | # Deprecate kubebuilder defaults 12 | 13 | 14 | 15 | 16 | - Status: accepted 17 | - Date: 2024-05-21 18 | - Authors: @alexander-demicev 19 | - Deciders: @Danil-Grigorev @furkatgofurov7 @salasberryfin @mjura @yiannistri 20 | 21 | ## Context 22 | 23 | 24 | Enforcing default values for fields in CRD definitions can cause problem with the GitOps approach. It can lead to a situation where defined resources in Git repository differs from the actual state of applied resources in the cluster. This can lead to unexpected behavior like state drift, unless is manually handled. 25 | 26 | ## Decision 27 | 28 | 29 | Kubebuilder defaulting annotations were [deprecated in CAPRKE2 API](https://github.com/rancher/cluster-api-provider-rke2/commit/86025754c0993e6e0d549110cc7f38687ac420e3). As a result of this deprecation 30 | CRD definitions don't include default values for some fields. 31 | 32 | ## Consequences 33 | 34 | 35 | Users have to specify the following fields manually when creating RKE2ControlPlane resources: 36 | 37 | - `spec.rolloutStrategy`: 38 | 39 | ```yaml 40 | rolloutStrategy: 41 | type: "RollingUpdate" 42 | rollingUpdate: 43 | maxSurge: 1 # can be 0 44 | ``` 45 | 46 | - `spec.registrationMethod`: 47 | 48 | ```yaml 49 | registrationMethod: 50 | type: "control-plane-endpoint" # other supported values are "internal-only-ips", "external-only-ips", "address", "internal-first" 51 | ``` 52 | `"control-plane-endpoint"` is the recommended value for `registrationMethod` field as other can cause issue with scaling the cluster and should be used with caution. -------------------------------------------------------------------------------- /bootstrap/api/v1alpha1/condition_consts.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 21 | ) 22 | 23 | const ( 24 | // DataSecretAvailableCondition documents the status of the bootstrap secret generation process. 25 | // 26 | // NOTE: When the DataSecret generation starts the process completes immediately and within the 27 | // same reconciliation, so the user will always see a transition from Wait to Generated without having 28 | // to wait for the next reconciliation. 29 | DataSecretAvailableCondition clusterv1.ConditionType = "Available" 30 | ) 31 | 32 | const ( 33 | // DataSecretGenerationFailedReason (Severity=Warning) documents a RKE2Config controller detecting 34 | // an error while generating a data secret; those kind of errors are usually due to misconfigurations 35 | // and user intervention is required to get them fixed. 36 | DataSecretGenerationFailedReason string = "DataSecretGenerationFailed" 37 | 38 | // WaitingForClusterInfrastructureReason (Severity=Info) document a bootstrap secret generation process 39 | // waiting for the cluster infrastructure to be ready. 40 | // 41 | // NOTE: Having the cluster infrastructure ready is a pre-condition for starting to create machines. 42 | WaitingForClusterInfrastructureReason string = "WaitingForClusterInfrastructure" 43 | ) 44 | 45 | const ( 46 | // CertificatesAvailableCondition documents the status of the certificates generation process. 47 | CertificatesAvailableCondition clusterv1.ConditionType = "CertificatesAvailable" 48 | 49 | // CertificatesGenerationFailedReason documents a RKE2Config controller detecting 50 | // an error while generating certificates; those kind of errors are usually due to misconfigurations 51 | // and user intervention is required to get them fixed. 52 | CertificatesGenerationFailedReason string = "CertificateGenerationFailed" 53 | ) 54 | -------------------------------------------------------------------------------- /bootstrap/api/v1beta1/condition_consts.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE LLC. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1beta1 18 | 19 | import ( 20 | clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 21 | ) 22 | 23 | const ( 24 | // DataSecretAvailableCondition documents the status of the bootstrap secret generation process. 25 | // 26 | // NOTE: When the DataSecret generation starts the process completes immediately and within the 27 | // same reconciliation, so the user will always see a transition from Wait to Generated without having 28 | // to wait for the next reconciliation. 29 | DataSecretAvailableCondition clusterv1.ConditionType = "Available" 30 | ) 31 | 32 | const ( 33 | // DataSecretGenerationFailedReason (Severity=Warning) documents a RKE2Config controller detecting 34 | // an error while generating a data secret; those kind of errors are usually due to misconfigurations 35 | // and user intervention is required to get them fixed. 36 | DataSecretGenerationFailedReason string = "DataSecretGenerationFailed" 37 | 38 | // WaitingForClusterInfrastructureReason (Severity=Info) document a bootstrap secret generation process 39 | // waiting for the cluster infrastructure to be ready. 40 | // 41 | // NOTE: Having the cluster infrastructure ready is a pre-condition for starting to create machines. 42 | WaitingForClusterInfrastructureReason string = "WaitingForClusterInfrastructure" 43 | ) 44 | 45 | const ( 46 | // CertificatesAvailableCondition documents the status of the certificates generation process. 47 | CertificatesAvailableCondition clusterv1.ConditionType = "CertificatesAvailable" 48 | 49 | // CertificatesGenerationFailedReason documents a RKE2Config controller detecting 50 | // an error while generating certificates; those kind of errors are usually due to misconfigurations 51 | // and user intervention is required to get them fixed. 52 | CertificatesGenerationFailedReason string = "CertificateGenerationFailed" 53 | ) 54 | -------------------------------------------------------------------------------- /bootstrap/api/v1alpha1/conversion_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE LLC. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "testing" 21 | 22 | fuzz "github.com/google/gofuzz" 23 | . "github.com/onsi/gomega" 24 | 25 | "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" 26 | "k8s.io/apimachinery/pkg/runtime" 27 | runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" 28 | 29 | bootstrapv1 "github.com/rancher/cluster-api-provider-rke2/bootstrap/api/v1beta1" 30 | utilconversion "sigs.k8s.io/cluster-api/util/conversion" 31 | ) 32 | 33 | func TestFuzzyConversion(t *testing.T) { 34 | g := NewWithT(t) 35 | scheme := runtime.NewScheme() 36 | g.Expect(AddToScheme(scheme)).To(Succeed()) 37 | g.Expect(bootstrapv1.AddToScheme(scheme)).To(Succeed()) 38 | 39 | t.Run("for RKE2Config", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ 40 | Scheme: scheme, 41 | Hub: &bootstrapv1.RKE2Config{}, 42 | Spoke: &RKE2Config{}, 43 | FuzzerFuncs: []fuzzer.FuzzerFuncs{ 44 | fuzzFuncs, // v1alpha1 fuzzer 45 | bootstrapv1.FuzzFuncsv1beta1, // v1beta1 fuzzer 46 | }, 47 | })) 48 | 49 | t.Run("for RKE2ConfigTemplate", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ 50 | Scheme: scheme, 51 | Hub: &bootstrapv1.RKE2ConfigTemplate{}, 52 | Spoke: &RKE2ConfigTemplate{}, 53 | FuzzerFuncs: []fuzzer.FuzzerFuncs{ 54 | fuzzFuncs, 55 | bootstrapv1.FuzzFuncsv1beta1, 56 | }, 57 | })) 58 | } 59 | 60 | func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { 61 | return []interface{}{ 62 | rke2ConfigFuzzer, 63 | rke2ConfigTemplateFuzzer, 64 | } 65 | } 66 | 67 | func rke2ConfigFuzzer(obj *RKE2Config, c fuzz.Continue) { 68 | c.FuzzNoCustom(obj) 69 | 70 | obj.Spec.AgentConfig.Version = "" 71 | } 72 | 73 | func rke2ConfigTemplateFuzzer(obj *RKE2ConfigTemplate, c fuzz.Continue) { 74 | c.FuzzNoCustom(obj) 75 | 76 | obj.Spec.Template.Spec.AgentConfig.Version = "" 77 | } 78 | -------------------------------------------------------------------------------- /controlplane/config/webhook/manifests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: MutatingWebhookConfiguration 4 | metadata: 5 | name: mutating-webhook-configuration 6 | webhooks: 7 | - admissionReviewVersions: 8 | - v1 9 | clientConfig: 10 | service: 11 | name: webhook-service 12 | namespace: system 13 | path: /mutate-controlplane-cluster-x-k8s-io-v1beta1-rke2controlplane 14 | failurePolicy: Fail 15 | name: mrke2controlplane.kb.io 16 | rules: 17 | - apiGroups: 18 | - controlplane.cluster.x-k8s.io 19 | apiVersions: 20 | - v1beta1 21 | operations: 22 | - CREATE 23 | - UPDATE 24 | resources: 25 | - rke2controlplanes 26 | sideEffects: None 27 | - admissionReviewVersions: 28 | - v1 29 | clientConfig: 30 | service: 31 | name: webhook-service 32 | namespace: system 33 | path: /mutate-controlplane-cluster-x-k8s-io-v1beta1-rke2controlplanetemplate 34 | failurePolicy: Fail 35 | name: mrke2controlplanetemplate.kb.io 36 | rules: 37 | - apiGroups: 38 | - controlplane.cluster.x-k8s.io 39 | apiVersions: 40 | - v1beta1 41 | operations: 42 | - CREATE 43 | - UPDATE 44 | resources: 45 | - rke2controlplanetemplates 46 | sideEffects: None 47 | --- 48 | apiVersion: admissionregistration.k8s.io/v1 49 | kind: ValidatingWebhookConfiguration 50 | metadata: 51 | name: validating-webhook-configuration 52 | webhooks: 53 | - admissionReviewVersions: 54 | - v1 55 | clientConfig: 56 | service: 57 | name: webhook-service 58 | namespace: system 59 | path: /validate-controlplane-cluster-x-k8s-io-v1beta1-rke2controlplane 60 | failurePolicy: Fail 61 | name: vrke2controlplane.kb.io 62 | rules: 63 | - apiGroups: 64 | - controlplane.cluster.x-k8s.io 65 | apiVersions: 66 | - v1beta1 67 | operations: 68 | - CREATE 69 | - UPDATE 70 | resources: 71 | - rke2controlplanes 72 | sideEffects: None 73 | - admissionReviewVersions: 74 | - v1 75 | clientConfig: 76 | service: 77 | name: webhook-service 78 | namespace: system 79 | path: /validate-controlplane-cluster-x-k8s-io-v1beta1-rke2controlplanetemplate 80 | failurePolicy: Fail 81 | name: vrke2controlplanetemplate.kb.io 82 | rules: 83 | - apiGroups: 84 | - controlplane.cluster.x-k8s.io 85 | apiVersions: 86 | - v1beta1 87 | operations: 88 | - CREATE 89 | - UPDATE 90 | resources: 91 | - rke2controlplanetemplates 92 | sideEffects: None 93 | -------------------------------------------------------------------------------- /bootstrap/internal/controllers/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controllers 18 | 19 | import ( 20 | "path/filepath" 21 | "testing" 22 | 23 | . "github.com/onsi/ginkgo/v2" 24 | . "github.com/onsi/gomega" 25 | 26 | "k8s.io/client-go/kubernetes/scheme" 27 | "k8s.io/client-go/rest" 28 | "sigs.k8s.io/controller-runtime/pkg/client" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest" 30 | logf "sigs.k8s.io/controller-runtime/pkg/log" 31 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 32 | 33 | bootstrapv1 "github.com/rancher/cluster-api-provider-rke2/bootstrap/api/v1beta1" 34 | ) 35 | 36 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 37 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 38 | 39 | var ( 40 | cfg *rest.Config 41 | k8sClient client.Client 42 | testEnv *envtest.Environment 43 | ) 44 | 45 | func TestAPIs(t *testing.T) { 46 | RegisterFailHandler(Fail) 47 | 48 | RunSpecs(t, "Controller Suite") 49 | } 50 | 51 | var _ = BeforeSuite(func() { 52 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 53 | 54 | By("bootstrapping test environment") 55 | testEnv = &envtest.Environment{ 56 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, 57 | ErrorIfCRDPathMissing: true, 58 | } 59 | 60 | var err error 61 | // cfg is defined in this file globally. 62 | cfg, err = testEnv.Start() 63 | Expect(err).NotTo(HaveOccurred()) 64 | Expect(cfg).NotTo(BeNil()) 65 | 66 | err = bootstrapv1.AddToScheme(scheme.Scheme) 67 | Expect(err).NotTo(HaveOccurred()) 68 | 69 | //+kubebuilder:scaffold:scheme 70 | 71 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 72 | Expect(err).NotTo(HaveOccurred()) 73 | Expect(k8sClient).NotTo(BeNil()) 74 | }) 75 | 76 | var _ = AfterSuite(func() { 77 | By("tearing down the test environment") 78 | err := testEnv.Stop() 79 | Expect(err).NotTo(HaveOccurred()) 80 | }) 81 | -------------------------------------------------------------------------------- /bootstrap/internal/cloudinit/worker_join.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cloudinit 18 | 19 | import ( 20 | "fmt" 21 | ) 22 | 23 | //nolint:lll 24 | const ( 25 | workerCloudInit = `{{.Header}} 26 | {{template "files" .WriteFiles}} 27 | {{template "ntp" .NTPServers}} 28 | {{template "arbitrary" .AdditionalArbitraryData}} 29 | runcmd: 30 | {{- template "commands" .PreRKE2Commands }} 31 | {{- if .AirGappedChecksum }} 32 | - [[ $(sha256sum /opt/rke2-artifacts/sha256sum*.txt | awk '{print $1}') == {{ .AirGappedChecksum }} ]] || exit 1{{ end }} 33 | - '{{ if .AirGapped }}INSTALL_RKE2_ARTIFACT_PATH=/opt/rke2-artifacts INSTALL_RKE2_TYPE="agent" sh /opt/install.sh{{ else }}curl -sfL https://get.rke2.io | INSTALL_RKE2_VERSION=%[1]s INSTALL_RKE2_TYPE="agent" sh -s -{{end}}' 34 | {{- if .CISEnabled }} 35 | - '/opt/rke2-cis-script.sh'{{ end }} 36 | - 'systemctl enable rke2-agent.service' 37 | - 'systemctl start rke2-agent.service' 38 | - 'mkdir -p /run/cluster-api' 39 | - '{{ .SentinelFileCommand }}' 40 | {{- template "commands" .PostRKE2Commands }} 41 | {{ .AdditionalCloudInit -}} 42 | ` 43 | ) 44 | 45 | // NewJoinWorker returns the user data string to be used on a controlplane instance. 46 | // 47 | // nolint:gofumpt 48 | func NewJoinWorker(input *BaseUserData) ([]byte, error) { 49 | input.Header = cloudConfigHeader 50 | input.WriteFiles = append(input.WriteFiles, input.ConfigFile) 51 | input.SentinelFileCommand = sentinelFileCommand 52 | 53 | var err error 54 | 55 | input.AdditionalCloudInit, err = cleanupAdditionalCloudInit(input.AdditionalCloudInit) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | if err := cleanupArbitraryData(input.AdditionalArbitraryData); err != nil { 61 | return nil, err 62 | } 63 | 64 | workerCloudJoinWithVersion := fmt.Sprintf(workerCloudInit, input.RKE2Version) 65 | userData, err := generate("JoinWorker", workerCloudJoinWithVersion, input) 66 | 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | return userData, nil 72 | } 73 | -------------------------------------------------------------------------------- /test/e2e/rcp_remediation_test.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | /* 5 | Copyright 2023 SUSE. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | package e2e 21 | 22 | import ( 23 | "os" 24 | "time" 25 | 26 | . "github.com/onsi/ginkgo/v2" 27 | . "github.com/onsi/gomega" 28 | "k8s.io/utils/ptr" 29 | capi_e2e "sigs.k8s.io/cluster-api/test/e2e" 30 | ) 31 | 32 | var _ = Describe("When testing RCP remediation", func() { 33 | var ( 34 | specName = "kcp-remediation" 35 | ) 36 | 37 | BeforeEach(func() { 38 | Expect(e2eConfig).ToNot(BeNil(), "Invalid argument. e2eConfig can't be nil when calling %s spec", specName) 39 | Expect(clusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. clusterctlConfigPath must be an existing file when calling %s spec", specName) 40 | Expect(bootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. bootstrapClusterProxy can't be nil when calling %s spec", specName) 41 | Expect(os.MkdirAll(artifactFolder, 0755)).To(Succeed(), "Invalid argument. artifactFolder can't be created for %s spec", specName) 42 | 43 | Expect(e2eConfig.Variables).To(HaveKey(KubernetesVersion)) 44 | 45 | By("Initializing the bootstrap cluster") 46 | initBootstrapCluster(bootstrapClusterProxy, e2eConfig, clusterctlConfigPath, artifactFolder) 47 | 48 | // Since the upstream KCPRemediatonSpec test has a 10 seconds hardcoded timeout on webhooks 49 | // we are waiting here a bit to mitigate flakyness. For reference caprke2 tests normally timeout after 3 minutes. 50 | time.Sleep(1 * time.Minute) 51 | }) 52 | 53 | capi_e2e.KCPRemediationSpec(ctx, func() capi_e2e.KCPRemediationSpecInput { 54 | return capi_e2e.KCPRemediationSpecInput{ 55 | E2EConfig: e2eConfig, 56 | ClusterctlConfigPath: clusterctlConfigPath, 57 | BootstrapClusterProxy: bootstrapClusterProxy, 58 | ArtifactFolder: artifactFolder, 59 | SkipCleanup: skipCleanup, 60 | InfrastructureProvider: ptr.To("docker:v1.10.5"), 61 | Flavor: ptr.To("kcp-remediation"), 62 | } 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /pkg/proxy/conn.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package proxy 18 | 19 | import ( 20 | "net" 21 | "time" 22 | 23 | kerrors "k8s.io/apimachinery/pkg/util/errors" 24 | "k8s.io/apimachinery/pkg/util/httpstream" 25 | ) 26 | 27 | // Conn is a Kubernetes API server proxied type of net/conn. 28 | type Conn struct { 29 | connection httpstream.Connection 30 | stream httpstream.Stream 31 | readDeadline time.Time 32 | writeDeadline time.Time 33 | } 34 | 35 | // NewConn creates a new net/conn interface based on an underlying Kubernetes 36 | // API server proxy connection. 37 | func NewConn(connection httpstream.Connection, stream httpstream.Stream) *Conn { 38 | return &Conn{ 39 | connection: connection, 40 | stream: stream, 41 | } 42 | } 43 | 44 | // Read from the connection. 45 | func (c *Conn) Read(b []byte) (n int, err error) { 46 | return c.stream.Read(b) 47 | } 48 | 49 | // Close the underlying proxied connection. 50 | func (c *Conn) Close() error { 51 | return kerrors.NewAggregate([]error{c.stream.Close(), c.connection.Close()}) 52 | } 53 | 54 | // Write to the connection. 55 | func (c *Conn) Write(b []byte) (n int, err error) { 56 | return c.stream.Write(b) 57 | } 58 | 59 | // LocalAddr returns a fake address representing the proxied connection. 60 | func (c *Conn) LocalAddr() net.Addr { 61 | return NewAddrFromConn(c) 62 | } 63 | 64 | // RemoteAddr returns a fake address representing the proxied connection. 65 | func (c *Conn) RemoteAddr() net.Addr { 66 | return NewAddrFromConn(c) 67 | } 68 | 69 | // SetDeadline sets the read and write deadlines to the specified interval. 70 | func (c *Conn) SetDeadline(t time.Time) error { 71 | c.readDeadline = t 72 | c.writeDeadline = t 73 | 74 | return nil 75 | } 76 | 77 | // SetWriteDeadline sets the read and write deadlines to the specified interval. 78 | func (c *Conn) SetWriteDeadline(t time.Time) error { 79 | c.writeDeadline = t 80 | 81 | return nil 82 | } 83 | 84 | // SetReadDeadline sets the read and write deadlines to the specified interval. 85 | func (c *Conn) SetReadDeadline(t time.Time) error { 86 | c.readDeadline = t 87 | 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /docs/adr/0001-separate-CP-and-worker-versions.md: -------------------------------------------------------------------------------- 1 | - [1. Separate Control Plane and Worker Versions](#1-separate-control-plane-and-worker-versions) 2 | - [Context](#context) 3 | - [Decision](#decision) 4 | - [Consequences](#consequences) 5 | 6 | 7 | # 1. Separate Control Plane and Worker Versions 8 | 9 | - Status: accepted 10 | - Date: 2024-05-20 11 | - Authors: @Danil-Grigorev 12 | - Deciders: @alexander-demicev @furkatgofurov7 @salasberryfin @mjura @yiannistri 13 | 14 | ## Context 15 | 16 | In the context of Cluster API, having separate worker and control plane versions is a valid and supported scenario, particularly useful during upgrades. 17 | 18 | Three specific scenarios highlight the use-cases for separate version management: 19 | 20 | 1. **Separate CP and workers upgrade**: When a control plane node is upgraded to a new Kubernetes version while the worker nodes remain on an 21 | older version, or vice versa. In this situation, it's essential to manage the state of the cluster, including the different versions of the workers and control plane nodes. 22 | 2. **Failed upgrade**: A situation where some worker or control plane machine wasn't upgraded successfully and is 23 | stuck in the previous version. The cluster remains functional, but the desired agent version doesn't declare the state of all 24 | machines. This requires a manual downgrade of the affected machine templates version, but should not force downgrade  25 | separate group of machines (CP or workers). 26 | 3. **ClusterClass usage**: When a ClusterClass is used as a template to declare a cluster, the version field 27 | inside the `MachineDeployment` template doesn't hold true, but instead the `AgentConfig` `spec.version` is used. 28 | In this case, the template becomes useless for declaring the version of the control plane or worker nodes. 29 | 30 | ## Decision 31 | 32 | To follow the upstream approach we remove the `AgentConfig` `spec.version` field in favor of `MachineDeployment` version 33 | and the `RKE2ControlPlane` version fields. Existing `AgentConfig` version will be transferred by conversion webhooks to `v1beta1.RKE2ControlPlane` resource. 34 | 35 | ## Consequences 36 | 37 | `AgentConfig` version is removed, so the `RKE2ControlPlane` and `MachineDeployment` should declare valid versions, following `RKE2` naming [pattern](https://github.com/rancher/rke2/releases). 38 | 39 | For users affected by the [#315](https://github.com/rancher/cluster-api-provider-rke2/issues/315) this will require 2 step process: 40 | 1. Check that version defined in `MachineDeployment` matches rke2 releases: [https://github.com/rancher/rke2/releases](https://github.com/rancher/rke2/releases) 41 | 2. Force re-rollout of all worker nodes to the version currently set in `MachineDeployment` or upgrade workers to the new version. 42 | -------------------------------------------------------------------------------- /controlplane/api/v1beta1/rke2controlplane_webhook_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 SUSE LLC. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1beta1 18 | 19 | import ( 20 | "context" 21 | 22 | . "github.com/onsi/ginkgo/v2" 23 | . "github.com/onsi/gomega" 24 | v1 "k8s.io/api/core/v1" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | "k8s.io/utils/ptr" 27 | ) 28 | 29 | var _ = Describe("RKE2ControlPlane webhook", func() { 30 | var ( 31 | oldRcp *RKE2ControlPlane 32 | rcp *RKE2ControlPlane 33 | defaulter = &RKE2ControlPlaneCustomDefaulter{} 34 | validator = &RKE2ControlPlaneCustomValidator{} 35 | ) 36 | BeforeEach(func() { 37 | rcp = &RKE2ControlPlane{ 38 | ObjectMeta: metav1.ObjectMeta{ 39 | Name: "test-control-plane", 40 | Namespace: "test", 41 | }, 42 | Spec: RKE2ControlPlaneSpec{ 43 | MachineTemplate: RKE2ControlPlaneMachineTemplate{ 44 | InfrastructureRef: v1.ObjectReference{ 45 | Name: "foo", 46 | Namespace: "bar", 47 | }, 48 | }, 49 | }, 50 | } 51 | oldRcp = rcp.DeepCopy() 52 | Expect(defaulter.Default(context.TODO(), rcp)).Should(Succeed()) 53 | Expect(defaulter.Default(context.TODO(), oldRcp)).Should(Succeed()) 54 | }) 55 | It("Should not create RKE2ControlPlane with 0 replicas", func() { 56 | rcp.Spec.Replicas = nil 57 | _, err := validator.ValidateCreate(context.TODO(), rcp) 58 | Expect(err).Should(HaveOccurred()) 59 | rcp.Spec.Replicas = ptr.To(int32(0)) 60 | _, err = validator.ValidateCreate(context.TODO(), rcp) 61 | Expect(err).Should(HaveOccurred()) 62 | rcp.Spec.Replicas = ptr.To(int32(1)) 63 | _, err = validator.ValidateCreate(context.TODO(), rcp) 64 | Expect(err).ShouldNot(HaveOccurred()) 65 | }) 66 | It("Should not update RKE2ControlPlane with 0 replicas", func() { 67 | rcp.Spec.Replicas = nil 68 | _, err := validator.ValidateUpdate(context.TODO(), oldRcp, rcp) 69 | Expect(err).Should(HaveOccurred()) 70 | rcp.Spec.Replicas = ptr.To(int32(0)) 71 | _, err = validator.ValidateUpdate(context.TODO(), oldRcp, rcp) 72 | Expect(err).Should(HaveOccurred()) 73 | rcp.Spec.Replicas = ptr.To(int32(1)) 74 | _, err = validator.ValidateUpdate(context.TODO(), oldRcp, rcp) 75 | Expect(err).ShouldNot(HaveOccurred()) 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | env: 9 | TAG: ${{ github.ref_name }} 10 | GHCR_REGISTRY: ghcr.io 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | packages: write 18 | actions: read 19 | id-token: write 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v6 23 | with: 24 | fetch-depth: 0 25 | - name: setupGo 26 | uses: actions/setup-go@v6 27 | with: 28 | go-version-file: go.mod 29 | - name: Docker login ghcr.io 30 | uses: docker/login-action@v3 31 | with: 32 | registry: ${{ env.GHCR_REGISTRY }} 33 | username: ${{ github.actor }} 34 | password: ${{ secrets.GITHUB_TOKEN }} 35 | - name: Build and push docker image to ghcr.io 36 | run: make docker-build-and-push TAG=${{ env.TAG }} REGISTRY=${{ env.GHCR_REGISTRY }} 37 | - name: Read prime registry secrets 38 | uses: rancher-eio/read-vault-secrets@main 39 | with: 40 | secrets: | 41 | secret/data/github/repo/${{ github.repository }}/rancher-prime-registry/credentials username | PRIME-REGISTRY-USERNAME; 42 | secret/data/github/repo/${{ github.repository }}/rancher-prime-registry/credentials password | PRIME-REGISTRY-PASSWORD; 43 | secret/data/github/repo/${{ github.repository }}/rancher-prime-registry/credentials registry | PRIME-REGISTRY-REGISTRY; 44 | - name: Docker login to prime registry 45 | uses: docker/login-action@v3 46 | with: 47 | registry: ${{ env.PRIME-REGISTRY-REGISTRY }} 48 | username: ${{ env.PRIME-REGISTRY-USERNAME }} 49 | password: ${{ env.PRIME-REGISTRY-PASSWORD }} 50 | - name: Build and push docker image to prime registry 51 | run: make docker-build-and-push TAG=${{ env.TAG }} REGISTRY=${{ env.PRIME-REGISTRY-REGISTRY }} 52 | release: 53 | runs-on: ubuntu-latest 54 | permissions: 55 | contents: write 56 | needs: [build] 57 | steps: 58 | - name: Checkout 59 | uses: actions/checkout@v6 60 | with: 61 | fetch-depth: 0 62 | - name: setupGo 63 | uses: actions/setup-go@v6 64 | with: 65 | go-version-file: go.mod 66 | - name: Update manifests 67 | run: | 68 | make release RELEASE_TAG=${{ env.TAG }} REGISTRY=${{ env.GHCR_REGISTRY }} 69 | - name: Release 70 | env: 71 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 72 | run: | 73 | gh release create ${{ env.TAG }} --draft --generate-notes 74 | gh release upload ${{ env.TAG }} out/metadata.yaml 75 | gh release upload ${{ env.TAG }} out/bootstrap-components.yaml 76 | gh release upload ${{ env.TAG }} out/control-plane-components.yaml 77 | -------------------------------------------------------------------------------- /hack/ensure-kubectl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright © 2023 - 2024 SUSE LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | if [[ "${TRACE-0}" == "1" ]]; then 22 | set -o xtrace 23 | fi 24 | 25 | # shellcheck source=./hack/utils.sh 26 | source "$(dirname "${BASH_SOURCE[0]}")/utils.sh" 27 | 28 | GOPATH_BIN="$(go env GOPATH)/bin/" 29 | MINIMUM_KUBECTL_VERSION=v1.27.0 30 | goarch="$(go env GOARCH)" 31 | goos="$(go env GOOS)" 32 | 33 | # Ensure the kubectl tool exists and is a viable version, or installs it 34 | verify_kubectl_version() { 35 | 36 | # If kubectl is not available on the path, get it 37 | if ! [ -x "$(command -v kubectl)" ]; then 38 | if [ "$goos" == "linux" ] || [ "$goos" == "darwin" ]; then 39 | if ! [ -d "${GOPATH_BIN}" ]; then 40 | mkdir -p "${GOPATH_BIN}" 41 | fi 42 | echo 'kubectl not found, installing' 43 | curl -sLo "${GOPATH_BIN}/kubectl" "https://dl.k8s.io/release/${MINIMUM_KUBECTL_VERSION}/bin/${goos}/${goarch}/kubectl" 44 | chmod +x "${GOPATH_BIN}/kubectl" 45 | verify_gopath_bin 46 | else 47 | echo "Missing required binary in path: kubectl" 48 | return 2 49 | fi 50 | fi 51 | 52 | local kubectl_version 53 | IFS=" " read -ra kubectl_version <<< "$(kubectl version --client)" 54 | if [[ "${MINIMUM_KUBECTL_VERSION}" != $(echo -e "${MINIMUM_KUBECTL_VERSION}\n${kubectl_version[2]}" | sort -s -t. -k 1,1 -k 2,2n -k 3,3n | head -n1) ]]; then 55 | cat <\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-35s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 28 | 29 | ## -------------------------------------- 30 | ## Packer flags 31 | ## -------------------------------------- 32 | 33 | # Set Packer color to true if not already set in env variables 34 | # Only valid for builds 35 | ifneq (,$(findstring build-, $(MAKECMDGOALS))) 36 | # A build target 37 | PACKER_COLOR ?= true 38 | PACKER_FLAGS += -color=$(PACKER_COLOR) 39 | endif 40 | 41 | # If ON_ERROR_ASK=1 then Packer will set -on-error to ask, causing the Packer 42 | # build to pause when any error happens, instead of simply exiting. This is 43 | # useful when debugging unknown issues logging into the remote machine via ssh. 44 | ifeq (1,$(strip $(ON_ERROR_ASK))) 45 | PACKER_FLAGS += -on-error=ask 46 | endif 47 | 48 | # If DEBUG=1 then Packer will set -debug, enabling debug mode for builds, providing 49 | # more verbose logging 50 | ifeq (1,$(strip $(DEBUG))) 51 | PACKER_FLAGS += -debug 52 | endif 53 | 54 | ## -------------------------------------- 55 | ## Platform and version combinations 56 | ## -------------------------------------- 57 | 58 | AWS_BUILD_NAMES ?= aws-opensuse-leap-156 59 | 60 | ## -------------------------------------- 61 | ## Dynamic build targets 62 | ## -------------------------------------- 63 | 64 | AWS_BUILD_TARGETS := $(addprefix build-,$(AWS_BUILD_NAMES)) 65 | 66 | .PHONY: $(AWS_BUILD_TARGETS) 67 | $(AWS_BUILD_TARGETS): 68 | packer build $(PACKER_FLAGS) -var-file="$(abspath aws/$(subst build-aws-,,$@).json)" -var "rke2_semver=$(RKE2_VERSION)" aws/packer.json 69 | 70 | ## -------------------------------------- 71 | ## Document dynamic build targets 72 | ## -------------------------------------- 73 | ##@ Builds 74 | build-aws-opensuse-leap-156: ## Builds an AMI for OpenSUSE Leap 15.6 75 | build-aws-all: $(AWS_BUILD_TARGETS) ## Builds all AMIs 76 | -------------------------------------------------------------------------------- /bootstrap/internal/cloudinit/controlplane_init.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cloudinit 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/rancher/cluster-api-provider-rke2/pkg/secret" 23 | ) 24 | 25 | //nolint:lll 26 | const ( 27 | controlPlaneCloudInit = `{{.Header}} 28 | {{template "files" .WriteFiles}} 29 | {{template "ntp" .NTPServers}} 30 | {{template "arbitrary" .AdditionalArbitraryData}} 31 | 32 | runcmd: 33 | {{- template "commands" .PreRKE2Commands }} 34 | {{- if .AirGappedChecksum }} 35 | - [[ $(sha256sum /opt/rke2-artifacts/sha256sum*.txt | awk '{print $1}') == {{ .AirGappedChecksum }} ]] || exit 1{{ end }} 36 | - {{ if .AirGapped }}INSTALL_RKE2_ARTIFACT_PATH=/opt/rke2-artifacts sh /opt/install.sh{{ else }}'curl -sfL https://get.rke2.io | INSTALL_RKE2_VERSION=%[1]s sh -s - server'{{ end }} 37 | {{- if .CISEnabled }} 38 | - '/opt/rke2-cis-script.sh'{{ end }} 39 | - 'systemctl enable rke2-server.service' 40 | - 'systemctl start rke2-server.service' 41 | - 'mkdir -p /run/cluster-api' 42 | - '{{ .SentinelFileCommand }}' 43 | {{- template "commands" .PostRKE2Commands }} 44 | {{ .AdditionalCloudInit -}} 45 | ` 46 | ) 47 | 48 | // ControlPlaneInput defines the context to generate a controlplane instance user data. 49 | type ControlPlaneInput struct { 50 | BaseUserData 51 | secret.Certificates 52 | } 53 | 54 | // NewInitControlPlane returns the user data string to be used on a controlplane instance. 55 | // 56 | //nolint:gofumpt 57 | func NewInitControlPlane(input *ControlPlaneInput) ([]byte, error) { 58 | input.Header = cloudConfigHeader 59 | input.WriteFiles = append(input.WriteFiles, input.AsFiles()...) 60 | input.WriteFiles = append(input.WriteFiles, input.ConfigFile) 61 | input.SentinelFileCommand = sentinelFileCommand 62 | 63 | var err error 64 | 65 | input.AdditionalCloudInit, err = cleanupAdditionalCloudInit(input.AdditionalCloudInit) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | if err := cleanupArbitraryData(input.AdditionalArbitraryData); err != nil { 71 | return nil, err 72 | } 73 | 74 | controlPlaneCloudJoinWithVersion := fmt.Sprintf(controlPlaneCloudInit, input.RKE2Version) 75 | userData, err := generate("InitControlplane", controlPlaneCloudJoinWithVersion, input) 76 | 77 | if err != nil { 78 | return nil, err 79 | } 80 | 81 | return userData, nil 82 | } 83 | -------------------------------------------------------------------------------- /pkg/etcd/fake/client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package fake implements testing fakes. 18 | package fake 19 | 20 | import ( 21 | "context" 22 | 23 | clientv3 "go.etcd.io/etcd/client/v3" 24 | ) 25 | 26 | // FakeEtcdClient represents a testing fake client for etcd interactions. 27 | type FakeEtcdClient struct { //nolint:revive 28 | AlarmResponse *clientv3.AlarmResponse 29 | EtcdEndpoints []string 30 | MemberListResponse *clientv3.MemberListResponse 31 | MemberRemoveResponse *clientv3.MemberRemoveResponse 32 | MemberUpdateResponse *clientv3.MemberUpdateResponse 33 | MoveLeaderResponse *clientv3.MoveLeaderResponse 34 | StatusResponse *clientv3.StatusResponse 35 | ErrorResponse error 36 | MovedLeader uint64 37 | RemovedMember uint64 38 | } 39 | 40 | // Endpoints returns available etcd endpoint. 41 | func (c *FakeEtcdClient) Endpoints() []string { 42 | return c.EtcdEndpoints 43 | } 44 | 45 | // MoveLeader is moving etcd leader. 46 | func (c *FakeEtcdClient) MoveLeader(_ context.Context, i uint64) (*clientv3.MoveLeaderResponse, error) { 47 | c.MovedLeader = i 48 | 49 | return c.MoveLeaderResponse, c.ErrorResponse 50 | } 51 | 52 | // Close is closing fake client (no-op). 53 | func (c *FakeEtcdClient) Close() error { 54 | return nil 55 | } 56 | 57 | // AlarmList returns a list or alarms on etcd cluster. 58 | func (c *FakeEtcdClient) AlarmList(_ context.Context) (*clientv3.AlarmResponse, error) { 59 | return c.AlarmResponse, c.ErrorResponse 60 | } 61 | 62 | // MemberList returnl a list of etcd members for the cluster. 63 | func (c *FakeEtcdClient) MemberList(_ context.Context) (*clientv3.MemberListResponse, error) { 64 | return c.MemberListResponse, c.ErrorResponse 65 | } 66 | 67 | // MemberRemove removes the member by id. 68 | func (c *FakeEtcdClient) MemberRemove(_ context.Context, i uint64) (*clientv3.MemberRemoveResponse, error) { 69 | c.RemovedMember = i 70 | 71 | return c.MemberRemoveResponse, c.ErrorResponse 72 | } 73 | 74 | // MemberUpdate updates the member by id. 75 | func (c *FakeEtcdClient) MemberUpdate(_ context.Context, _ uint64, _ []string) (*clientv3.MemberUpdateResponse, error) { 76 | return c.MemberUpdateResponse, c.ErrorResponse 77 | } 78 | 79 | // Status return a status response for the etcd member. 80 | func (c *FakeEtcdClient) Status(_ context.Context, _ string) (*clientv3.StatusResponse, error) { 81 | return c.StatusResponse, nil 82 | } 83 | -------------------------------------------------------------------------------- /docs/book/src/02_topics/02_node-registration-methods.md: -------------------------------------------------------------------------------- 1 | # Node Registration Methods 2 | 3 | The provider supports multiple methods for registering a new node into the cluster. 4 | 5 | ## Usage 6 | 7 | The method to use is specified on the **RKEControlPlane** within the **spec**. If no method is supplied then the default method of **control-plane-endpoint** will be used. 8 | 9 | > You cannot change the registration method after creation. 10 | 11 | An example of using a different method: 12 | 13 | ```yaml 14 | apiVersion: controlplane.cluster.x-k8s.io/v1beta1 15 | kind: RKE2ControlPlane 16 | metadata: 17 | name: test1-control-plane 18 | namespace: default 19 | spec: 20 | agentConfig: 21 | version: v1.26.4+rke2r1 22 | machineTemplate: 23 | infrastructureRef: 24 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 25 | kind: DockerMachineTemplate 26 | name: controlplane 27 | nodeDrainTimeout: 2m 28 | nodeDeletionTimeout: 30s 29 | nodeVolumeDetachTimeout: 5m 30 | replicas: 3 31 | serverConfig: 32 | cni: calico 33 | registrationMethod: "address" 34 | registrationAddress: "172.19.0.3" 35 | ``` 36 | 37 | ## Registration Methods 38 | 39 | ### control-plane-endpoint 40 | 41 | The CAPI (Infra)Cluster's `controlPlaneEndpoint` value will be used for node registration. If this endpoint is served by a load balancer, make sure that it allows traffic to the control plane nodes on the `9345` registration port. 42 | 43 | ### internal-first 44 | 45 | For each CAPI `Machine` that is used for the control plane, we take the **internal** ip address from `Machine.status.addresses` if it exists. If there is no **internal** ip for a machine then we will use an **external** address instead. For the ip address found for a machine then we add it to `RKEControlPlane.status.availableServerIPs`. 46 | 47 | The first IP address listed in `RKEControlPlane.status.availableServerIPs` is then used for the join. 48 | 49 | ### internal-only-ips 50 | 51 | For each CAPI `Machine` that is used for the control plane, we take the **internal** ip address from `Machine.status.addresses` if it exists and then we add it to `RKEControlPlane.status.availableServerIPs`. 52 | 53 | The first IP address listed in `RKEControlPlane.status.availableServerIPs` is then used for the join. 54 | 55 | ### external-only-ips 56 | 57 | For each CAPI `Machine` that is used for the control plane, we take the **external** ip address from `Machine.status.addresses` if it exists and then we add it to `RKEControlPlane.status.availableServerIPs`. 58 | 59 | The first IP address listed in `RKEControlPlane.status.availableServerIPs` is then used for the join. 60 | 61 | ### address 62 | 63 | For this method you must supply an address in the control plane spec (i.e. `RKE2ControlPlane.spec.registrationAddress`). This address is then used for the join. 64 | 65 | With this method its expected that you have a load balancer / VIP solution sitting in front of all the control plane machines and all the join requests will be routed via this. 66 | -------------------------------------------------------------------------------- /docs/book/src/02_topics/03_cis-psa.md: -------------------------------------------------------------------------------- 1 | # CIS and Pod Security Admission 2 | 3 | In order to set a custom Pod Security Admission policy when CIS profile is selected it's required to create a secret with the policy content and set an appropriate field on the `RKE2ControlPlane` object: 4 | 5 | ```yaml 6 | apiVersion: v1 7 | kind: Secret 8 | metadata: 9 | name: pod-security-admission-config 10 | data: 11 | pod-security-admission-config.yaml: | 12 | apiVersion: apiserver.config.k8s.io/v1 13 | kind: AdmissionConfiguration 14 | plugins: 15 | - name: PodSecurity 16 | configuration: 17 | apiVersion: pod-security.admission.config.k8s.io/v1beta1 18 | kind: PodSecurityConfiguration 19 | defaults: 20 | enforce: "restricted" 21 | enforce-version: "latest" 22 | audit: "restricted" 23 | audit-version: "latest" 24 | warn: "restricted" 25 | warn-version: "latest" 26 | exemptions: 27 | usernames: [] 28 | runtimeClasses: [] 29 | namespaces: [kube-system, cis-operator-system, tigera-operator] 30 | ``` 31 | 32 | ```yaml 33 | apiVersion: controlplane.cluster.x-k8s.io/v1beta1 34 | kind: RKE2ControlPlane 35 | metadata: 36 | ... 37 | spec: 38 | ... 39 | files: 40 | - path: /path/to/pod-security-admission-config.yaml 41 | contentFrom: 42 | secret: 43 | name: pod-security-admission-config 44 | key: pod-security-admission-config.yaml 45 | agentConfig: 46 | profile: cis 47 | podSecurityAdmissionConfigFile: /path/to/pod-security-admission-config.yaml 48 | ... 49 | ``` 50 | 51 | > **_NOTE:_**: You can also use a ConfigMap instead of a Secret for the above configuration by using `.contentFrom.configMap` instead of `.contentFrom.secret`. 52 | 53 | ## Example of PSA to allow Rancher components to run in the cluster: 54 | 55 | ```yaml 56 | apiVersion: apiserver.config.k8s.io/v1 57 | kind: AdmissionConfiguration 58 | plugins: 59 | - name: PodSecurity 60 | configuration: 61 | apiVersion: pod-security.admission.config.k8s.io/v1 62 | kind: PodSecurityConfiguration 63 | defaults: 64 | enforce: "restricted" 65 | enforce-version: "latest" 66 | audit: "restricted" 67 | audit-version: "latest" 68 | warn: "restricted" 69 | warn-version: "latest" 70 | exemptions: 71 | usernames: [] 72 | runtimeClasses: [] 73 | namespaces: [cattle-alerting, 74 | cattle-fleet-local-system, 75 | cattle-fleet-system, 76 | cattle-global-data, 77 | cattle-impersonation-system, 78 | cattle-monitoring-system, 79 | cattle-prometheus, 80 | cattle-resources-system, 81 | cattle-system, 82 | cattle-ui-plugin-system, 83 | cert-manager, 84 | cis-operator-system, 85 | fleet-default, 86 | ingress-nginx, 87 | kube-node-lease, 88 | kube-public, 89 | kube-system, 90 | rancher-alerting-drivers] 91 | ``` -------------------------------------------------------------------------------- /controlplane/api/v1beta1/rke2controlplanetemplate_webhook_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | package v1beta1 15 | 16 | import ( 17 | "context" 18 | "testing" 19 | 20 | . "github.com/onsi/gomega" 21 | ) 22 | 23 | func TestRKE2ControlPlaneTemplateValidateCreate(t *testing.T) { 24 | g := NewWithT(t) 25 | 26 | tests := []struct { 27 | name string 28 | inputTemplate *RKE2ControlPlaneTemplate 29 | wantErr bool 30 | }{ 31 | { 32 | name: "don't allow RKE2ControlPlaneTemplate with invalid CNI", 33 | inputTemplate: &RKE2ControlPlaneTemplate{ 34 | Spec: RKE2ControlPlaneTemplateSpec{ 35 | Template: RKE2ControlPlaneTemplateResource{ 36 | Spec: RKE2ControlPlaneSpec{ 37 | ServerConfig: RKE2ServerConfig{ 38 | CNIMultusEnable: true, 39 | CNI: "", 40 | }, 41 | }, 42 | }, 43 | }, 44 | }, 45 | wantErr: true, 46 | }, 47 | } 48 | validator := RKE2ControlPlaneTemplateCustomValidator{} 49 | for _, test := range tests { 50 | tt := test 51 | t.Run(tt.name, func(t *testing.T) { 52 | t.Parallel() 53 | warn, err := validator.ValidateCreate(context.Background(), tt.inputTemplate) 54 | if tt.wantErr { 55 | g.Expect(err).To(HaveOccurred()) 56 | } else { 57 | g.Expect(err).NotTo(HaveOccurred()) 58 | } 59 | g.Expect(warn).To(BeNil()) 60 | }) 61 | } 62 | } 63 | 64 | func TestRKE2ControlPlaneTemplateValidateUpdate(t *testing.T) { 65 | g := NewWithT(t) 66 | 67 | replicas := int32(3) 68 | tests := []struct { 69 | name string 70 | newTemplate *RKE2ControlPlaneTemplate 71 | oldTemplate *RKE2ControlPlaneTemplate 72 | wantErr bool 73 | }{ 74 | { 75 | name: "RKE2ControlPlaneTemplate with immutable spec", 76 | newTemplate: &RKE2ControlPlaneTemplate{ 77 | Spec: RKE2ControlPlaneTemplateSpec{ 78 | Template: RKE2ControlPlaneTemplateResource{ 79 | Spec: RKE2ControlPlaneSpec{ 80 | Replicas: &replicas, 81 | }, 82 | }, 83 | }, 84 | }, 85 | oldTemplate: &RKE2ControlPlaneTemplate{ 86 | Spec: RKE2ControlPlaneTemplateSpec{ 87 | Template: RKE2ControlPlaneTemplateResource{ 88 | Spec: RKE2ControlPlaneSpec{ 89 | Replicas: &replicas, 90 | }, 91 | }, 92 | }, 93 | }, 94 | wantErr: false, 95 | }, 96 | } 97 | validator := RKE2ControlPlaneTemplateCustomValidator{} 98 | for _, test := range tests { 99 | tt := test 100 | t.Run(tt.name, func(t *testing.T) { 101 | t.Parallel() 102 | warn, err := validator.ValidateUpdate(context.Background(), tt.oldTemplate, tt.newTemplate) 103 | if tt.wantErr { 104 | g.Expect(err).To(HaveOccurred()) 105 | } else { 106 | g.Expect(err).NotTo(HaveOccurred()) 107 | } 108 | g.Expect(warn).To(BeNil()) 109 | }) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /image-builder/files/features.py: -------------------------------------------------------------------------------- 1 | # This file is part of cloud-init. See LICENSE file for license information. 2 | """ 3 | Feature flags are used as a way to easily toggle configuration 4 | **at build time**. They are provided to accommodate feature deprecation and 5 | downstream configuration changes. 6 | 7 | Currently used upstream values for feature flags are set in 8 | ``cloudinit/features.py``. Overrides to these values should be 9 | patched directly (e.g., via quilt patch) by downstreams. 10 | 11 | Each flag should include a short comment regarding the reason for 12 | the flag and intended lifetime. 13 | 14 | Tests are required for new feature flags, and tests must verify 15 | all valid states of a flag, not just the default state. 16 | """ 17 | import re 18 | import sys 19 | from typing import Dict 20 | 21 | ERROR_ON_USER_DATA_FAILURE = False 22 | """ 23 | If there is a failure in obtaining user data (i.e., #include or 24 | decompress fails) and ``ERROR_ON_USER_DATA_FAILURE`` is ``False``, 25 | cloud-init will log a warning and proceed. If it is ``True``, 26 | cloud-init will instead raise an exception. 27 | 28 | As of 20.3, ``ERROR_ON_USER_DATA_FAILURE`` is ``True``. 29 | 30 | (This flag can be removed after Focal is no longer supported.) 31 | """ 32 | 33 | 34 | ALLOW_EC2_MIRRORS_ON_NON_AWS_INSTANCE_TYPES = False 35 | """ 36 | When configuring apt mirrors, if 37 | ``ALLOW_EC2_MIRRORS_ON_NON_AWS_INSTANCE_TYPES`` is ``True`` cloud-init 38 | will detect that a datasource's ``availability_zone`` property looks 39 | like an EC2 availability zone and set the ``ec2_region`` variable when 40 | generating mirror URLs; this can lead to incorrect mirrors being 41 | configured in clouds whose AZs follow EC2's naming pattern. 42 | 43 | As of 20.3, ``ALLOW_EC2_MIRRORS_ON_NON_AWS_INSTANCE_TYPES`` is ``False`` 44 | so we no longer include ``ec2_region`` in mirror determination on 45 | non-AWS cloud platforms. 46 | 47 | If the old behavior is desired, users can provide the appropriate 48 | mirrors via :py:mod:`apt: ` 49 | directives in cloud-config. 50 | """ 51 | 52 | 53 | EXPIRE_APPLIES_TO_HASHED_USERS = True 54 | """ 55 | If ``EXPIRE_APPLIES_TO_HASHED_USERS`` is True, then when expire is set true 56 | in cc_set_passwords, hashed passwords will be expired. Previous to 22.3, 57 | only non-hashed passwords were expired. 58 | 59 | (This flag can be removed after Jammy is no longer supported.) 60 | """ 61 | 62 | NETPLAN_CONFIG_ROOT_READ_ONLY = True 63 | """ 64 | If ``NETPLAN_CONFIG_ROOT_READ_ONLY`` is True, then netplan configuration will 65 | be written as a single root readon-only file /etc/netplan/50-cloud-init.yaml. 66 | This prevents wifi passwords in network v2 configuration from being 67 | world-readable. Prior to 23.1, netplan configuration is world-readable. 68 | 69 | (This flag can be removed after Jammy is no longer supported.) 70 | """ 71 | 72 | 73 | NOCLOUD_SEED_URL_APPEND_FORWARD_SLASH = True 74 | """ 75 | Append a forward slash '/' if NoCloud seedurl does not end with either 76 | a querystring or forward slash. Prior to 23.1, nocloud seedurl would be used 77 | unaltered, appending meta-data, user-data and vendor-data to without URL path 78 | separators. 79 | 80 | (This flag can be removed when Jammy is no longer supported.) 81 | """ 82 | 83 | 84 | def get_features() -> Dict[str, bool]: 85 | """Return a dict of applicable features/overrides and their values.""" 86 | return { 87 | k: getattr(sys.modules["cloudinit.features"], k) 88 | for k in sys.modules["cloudinit.features"].__dict__.keys() 89 | if re.match(r"^[_A-Z0-9]+$", k) 90 | } -------------------------------------------------------------------------------- /controlplane/config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: rke2-control-plane-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: rke2-control-plane- 10 | 11 | # Labels to add to all resources and selectors. 12 | labels: 13 | - includeSelectors: true 14 | pairs: 15 | cluster.x-k8s.io/provider: control-plane-rke2 16 | 17 | resources: 18 | - namespace.yaml 19 | - ../crd 20 | - ../rbac 21 | - ../manager 22 | - ../webhook 23 | - ../certmanager 24 | 25 | patches: 26 | # Provide customizable hook for make targets. 27 | - path: manager_image_patch.yaml 28 | - path: manager_pull_policy.yaml 29 | # Enable webhook. 30 | - path: manager_webhook_patch.yaml 31 | # Inject certificate in the webhook definition. 32 | - path: webhookcainjection_patch.yaml 33 | # Enable aggregated ClusterRole aggregation 34 | - path: manager_role_aggregation_patch.yaml 35 | 36 | replacements: 37 | - source: 38 | fieldPath: .metadata.namespace 39 | group: cert-manager.io 40 | kind: Certificate 41 | name: serving-cert 42 | version: v1 43 | targets: 44 | - fieldPaths: 45 | - .metadata.annotations.[cert-manager.io/inject-ca-from] 46 | options: 47 | create: true 48 | delimiter: / 49 | select: 50 | kind: ValidatingWebhookConfiguration 51 | - fieldPaths: 52 | - .metadata.annotations.[cert-manager.io/inject-ca-from] 53 | options: 54 | create: true 55 | delimiter: / 56 | select: 57 | kind: MutatingWebhookConfiguration 58 | - fieldPaths: 59 | - .metadata.annotations.[cert-manager.io/inject-ca-from] 60 | options: 61 | create: true 62 | delimiter: / 63 | select: 64 | kind: CustomResourceDefinition 65 | - source: 66 | fieldPath: .metadata.name 67 | group: cert-manager.io 68 | kind: Certificate 69 | name: serving-cert 70 | version: v1 71 | targets: 72 | - fieldPaths: 73 | - .metadata.annotations.[cert-manager.io/inject-ca-from] 74 | options: 75 | create: true 76 | delimiter: / 77 | index: 1 78 | select: 79 | kind: ValidatingWebhookConfiguration 80 | - fieldPaths: 81 | - .metadata.annotations.[cert-manager.io/inject-ca-from] 82 | options: 83 | create: true 84 | delimiter: / 85 | index: 1 86 | select: 87 | kind: MutatingWebhookConfiguration 88 | - fieldPaths: 89 | - .metadata.annotations.[cert-manager.io/inject-ca-from] 90 | options: 91 | create: true 92 | delimiter: / 93 | index: 1 94 | select: 95 | kind: CustomResourceDefinition 96 | - source: 97 | fieldPath: .metadata.name 98 | kind: Service 99 | name: webhook-service 100 | version: v1 101 | targets: 102 | - fieldPaths: 103 | - .spec.dnsNames.0 104 | - .spec.dnsNames.1 105 | options: 106 | create: true 107 | delimiter: . 108 | select: 109 | group: cert-manager.io 110 | kind: Certificate 111 | version: v1 112 | - source: 113 | fieldPath: .metadata.namespace 114 | kind: Service 115 | name: webhook-service 116 | version: v1 117 | targets: 118 | - fieldPaths: 119 | - .spec.dnsNames.0 120 | - .spec.dnsNames.1 121 | options: 122 | create: true 123 | delimiter: . 124 | index: 1 125 | select: 126 | group: cert-manager.io 127 | kind: Certificate 128 | version: v1 129 | -------------------------------------------------------------------------------- /docs/book/src/03_examples/02_vsphere.md: -------------------------------------------------------------------------------- 1 | # Cluster API vSphere Infrastructure Provider 2 | 3 | ## Installing the vSphere provider and creating a workload cluster 4 | 5 | This config includes a kubevip loadbalancer on the controlplane nodes. The VIP of the loadbalancer for the Kubernetes API is set by the CONTROL_PLANE_ENDPOINT_IP. 6 | 7 | Prerequisites: 8 | 9 | - VM template to be used for the cluster machine should be present in the vSphere environment. 10 | - If airgapped environment is required then the VM template should already include RKE2 binaries as described in the [docs](https://docs.rke2.io/install/airgap#tarball-method). CAPRKE2 is using the tarball method to install RKE2 on the machines. 11 | Any additional images like vSphere CPI image should be present in the local environment too. 12 | 13 | To initialize Cluster API Provider vSphere, clusterctl requires the following variables, which should be set in ~/.cluster-api/clusterctl.yaml as the following: 14 | 15 | ```bash 16 | ## -- Controller settings -- ## 17 | VSPHERE_USERNAME: "" # The username used to access the remote vSphere endpoint 18 | VSPHERE_PASSWORD: "" # The password used to access the remote vSphere endpoint 19 | 20 | ## -- Required workload cluster default settings -- ## 21 | VSPHERE_SERVER: "10.0.0.1" # The vCenter server IP or FQDN 22 | VSPHERE_DATACENTER: "SDDC-Datacenter" # The vSphere datacenter to deploy the management cluster on 23 | VSPHERE_DATASTORE: "DefaultDatastore" # The vSphere datastore to deploy the management cluster on 24 | VSPHERE_NETWORK: "VM Network" # The VM network to deploy the management cluster on 25 | VSPHERE_RESOURCE_POOL: "*/Resources" # The vSphere resource pool for your VMs 26 | VSPHERE_FOLDER: "vm" # The VM folder for your VMs. Set to "" to use the root vSphere folder 27 | VSPHERE_TEMPLATE: "ubuntu-1804-kube-v1.17.3" # The VM template to use for your management cluster. 28 | CONTROL_PLANE_ENDPOINT_IP: "192.168.9.230" # the IP that kube-vip is going to use as a control plane endpoint 29 | VSPHERE_TLS_THUMBPRINT: "..." # sha256 thumbprint of the vcenter certificate: openssl x509 -sha256 -fingerprint -in ca.crt -noout 30 | EXP_CLUSTER_RESOURCE_SET: "true" # This enables the ClusterResourceSet feature that we are using to deploy CSI 31 | VSPHERE_SSH_AUTHORIZED_KEY: "ssh-rsa AAAAB3N..." # The public ssh authorized key on all machines in this cluster. 32 | # Set to "" if you don't want to enable SSH, or are using another solution. 33 | "CPI_IMAGE_K8S_VERSION": "v1.30.0" # The version of the vSphere CPI image to be used by the CPI workloads 34 | # Keep this close to the minimum Kubernetes version of the cluster being created. 35 | ``` 36 | 37 |
38 | **Warning:** This example uses KubeVIP, and there may be upstream issues with it. We do not provide support for resolving any such issues. Use at your own risk. 39 |
40 | 41 | Then run the following command to generate the RKE2 cluster manifests: 42 | 43 | ```bash 44 | clusterctl generate cluster --from https://github.com/rancher/cluster-api-provider-rke2/blob/main/examples/templates/vmware/cluster-template.yaml -n example-vsphere rke2-vsphere > vsphere-rke2-clusterctl.yaml 45 | ``` 46 | 47 | ```bash 48 | kubectl apply -f vsphere-rke2-clusterctl.yaml 49 | ``` 50 | 51 | -------------------------------------------------------------------------------- /pkg/etcd/etcd_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 SUSE. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package etcd 18 | 19 | import ( 20 | "testing" 21 | 22 | . "github.com/onsi/gomega" 23 | "github.com/pkg/errors" 24 | etcdfake "github.com/rancher/cluster-api-provider-rke2/pkg/etcd/fake" 25 | "go.etcd.io/etcd/api/v3/etcdserverpb" 26 | clientv3 "go.etcd.io/etcd/client/v3" 27 | ctrl "sigs.k8s.io/controller-runtime" 28 | ) 29 | 30 | var ctx = ctrl.SetupSignalHandler() 31 | 32 | func TestEtcdMembers_WithErrors(t *testing.T) { 33 | g := NewWithT(t) 34 | 35 | fakeEtcdClient := &etcdfake.FakeEtcdClient{ 36 | EtcdEndpoints: []string{"https://etcd-instance:2379"}, 37 | MemberListResponse: &clientv3.MemberListResponse{ 38 | Header: &etcdserverpb.ResponseHeader{}, 39 | Members: []*etcdserverpb.Member{ 40 | {ID: 1234, Name: "foo", PeerURLs: []string{"https://1.2.3.4:2000"}}, 41 | }, 42 | }, 43 | MoveLeaderResponse: &clientv3.MoveLeaderResponse{}, 44 | MemberRemoveResponse: &clientv3.MemberRemoveResponse{}, 45 | StatusResponse: &clientv3.StatusResponse{}, 46 | ErrorResponse: errors.New("something went wrong"), 47 | } 48 | 49 | client, err := newEtcdClient(ctx, fakeEtcdClient, DefaultCallTimeout) 50 | g.Expect(err).ToNot(HaveOccurred()) 51 | 52 | members, err := client.Members(ctx) 53 | g.Expect(err).To(HaveOccurred()) 54 | g.Expect(members).To(BeEmpty()) 55 | 56 | err = client.MoveLeader(ctx, 1) 57 | g.Expect(err).To(HaveOccurred()) 58 | 59 | err = client.RemoveMember(ctx, 1234) 60 | g.Expect(err).To(HaveOccurred()) 61 | } 62 | 63 | func TestEtcdMembers_WithSuccess(t *testing.T) { 64 | g := NewWithT(t) 65 | 66 | fakeEtcdClient := &etcdfake.FakeEtcdClient{ 67 | EtcdEndpoints: []string{"https://etcd-instance:2379"}, 68 | MemberListResponse: &clientv3.MemberListResponse{ 69 | Header: &etcdserverpb.ResponseHeader{}, 70 | Members: []*etcdserverpb.Member{ 71 | {ID: 1234, Name: "foo", PeerURLs: []string{"https://1.2.3.4:2000"}}, 72 | }, 73 | }, 74 | MoveLeaderResponse: &clientv3.MoveLeaderResponse{}, 75 | MemberUpdateResponse: &clientv3.MemberUpdateResponse{ 76 | Header: &etcdserverpb.ResponseHeader{}, 77 | Members: []*etcdserverpb.Member{ 78 | {ID: 1234, Name: "foo", PeerURLs: []string{"https://1.2.3.4:2000", "https://4.5.6.7:2000"}}, 79 | }, 80 | }, 81 | MemberRemoveResponse: &clientv3.MemberRemoveResponse{}, 82 | AlarmResponse: &clientv3.AlarmResponse{}, 83 | StatusResponse: &clientv3.StatusResponse{}, 84 | } 85 | 86 | client, err := newEtcdClient(ctx, fakeEtcdClient, DefaultCallTimeout) 87 | g.Expect(err).ToNot(HaveOccurred()) 88 | 89 | members, err := client.Members(ctx) 90 | g.Expect(err).ToNot(HaveOccurred()) 91 | g.Expect(members).To(HaveLen(1)) 92 | 93 | err = client.MoveLeader(ctx, 1) 94 | g.Expect(err).ToNot(HaveOccurred()) 95 | 96 | err = client.RemoveMember(ctx, 1234) 97 | g.Expect(err).ToNot(HaveOccurred()) 98 | 99 | updatedMembers, err := client.UpdateMemberPeerURLs(ctx, 1234, []string{"https://4.5.6.7:2000"}) 100 | g.Expect(err).ToNot(HaveOccurred()) 101 | g.Expect(updatedMembers[0].PeerURLs).To(HaveLen(2)) 102 | g.Expect(updatedMembers[0].PeerURLs).To(Equal([]string{"https://1.2.3.4:2000", "https://4.5.6.7:2000"})) 103 | } 104 | --------------------------------------------------------------------------------