diff --git a/README.md b/README.md index d82f2dec..d4a0046b 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ Set the following environment variables: - CABPR_WK_REPLICAS - KUBERNETES_VERSION -for example : +for example: ```bash export CABPR_NAMESPACE=example @@ -197,6 +197,62 @@ To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. :tada: CONGRATULATIONS ! :tada: You created your first RKE2 cluster with CAPD as an infrastructure provider. +### Using ClusterClass for cluster creation + +This provider supports using [ClusterClass](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20210526-cluster-class-and-managed-topologies.md), a Cluster API feature that implements an extra level of abstraction on top of the existing Cluster API functionality. The `ClusterClass` object is used to define a collection of template resources (control plane and machine deployment) which are used to generate one or more clusters of the same flavor. + +If you are interested in leveraging this functionality, you can refer to the examples [here](./samples/docker/clusterclass/): +- [clusterclass-quick-start.yaml](./samples/docker/clusterclass/clusterclass-quick-start.yaml): creates a sample `ClusterClass` and necessary resources. +- [rke2-sample.yaml](./samples/docker/clusterclass/rke2-sample.yaml): creates a workload cluster using the `ClusterClass`. + +As with other sample templates, you will need to set a number environment variables: +- CLUSTER_NAME +- CABPR_CP_REPLICAS +- CABPR_WK_REPLICAS +- KUBERNETES_VERSION +- KIND_IP + +for example: + +```bash +export CLUSTER_NAME=capd-rke2-clusterclass +export CABPR_CP_REPLICAS=3 +export CABPR_WK_REPLICAS=2 +export KUBERNETES_VERSION=v1.25.11 +export KIND_IP=192.168.20.20 +``` + +**Remember that, since we are using Kind, the value of `KIND_IP` must be an IP address in the range of the `kind` network.** +You can check the range Docker assigns to this network by inspecting it: + +```bash +docker network inspect kind +``` + +The next step is to substitue the values in the YAML using the following commands: + +```bash +cat clusterclass-quick-start.yaml | clusterctl generate yaml > clusterclass-example.yaml +``` + +At this moment, you can take some time to study the resulting YAML, then you can apply it to the management cluster: + +```bash +kubectl apply -f clusterclass-example.yaml +``` + +This will create a new `ClusterClass` template that can be used to provision one or multiple workload clusters of the same flavor. +To do so, you can follow the same procedure and substitute the values in the YAML for the cluster definition: + +```bash +cat rke2-sample.yaml | clusterctl generate yaml > rke2-clusterclass-example.yaml +``` + +And then apply the resulting YAML file to create a cluster from the existing `ClusterClass`. +```bash +kubectl apply -f rke2-clusterclass-example.yaml +``` + ## Testing the DEV main branch These instructions are for development purposes initially and will be changed in the future for user facing instructions. diff --git a/bootstrap/api/v1beta1/rke2config_types.go b/bootstrap/api/v1beta1/rke2config_types.go index c3b503c4..223b9784 100644 --- a/bootstrap/api/v1beta1/rke2config_types.go +++ b/bootstrap/api/v1beta1/rke2config_types.go @@ -147,6 +147,7 @@ type RKE2AgentConfig struct { LoadBalancerPort int `json:"loadBalancerPort,omitempty"` // Version specifies the rke2 version. + // This field will be deprecated in newer versions of the API and RKE2ControlPlaneSpec.Version will be used instead. //+optional Version string `json:"version,omitempty"` diff --git a/bootstrap/config/crd/bases/bootstrap.cluster.x-k8s.io_rke2configs.yaml b/bootstrap/config/crd/bases/bootstrap.cluster.x-k8s.io_rke2configs.yaml index e58b93ed..a087d87d 100644 --- a/bootstrap/config/crd/bases/bootstrap.cluster.x-k8s.io_rke2configs.yaml +++ b/bootstrap/config/crd/bases/bootstrap.cluster.x-k8s.io_rke2configs.yaml @@ -850,7 +850,9 @@ spec: for all system images. type: string version: - description: Version specifies the rke2 version. + description: Version specifies the rke2 version. This field will + be deprecated in newer versions of the API and RKE2ControlPlaneSpec.Version + will be used instead. type: string type: object files: diff --git a/bootstrap/config/crd/bases/bootstrap.cluster.x-k8s.io_rke2configtemplates.yaml b/bootstrap/config/crd/bases/bootstrap.cluster.x-k8s.io_rke2configtemplates.yaml index cdb25155..fb10e29c 100644 --- a/bootstrap/config/crd/bases/bootstrap.cluster.x-k8s.io_rke2configtemplates.yaml +++ b/bootstrap/config/crd/bases/bootstrap.cluster.x-k8s.io_rke2configtemplates.yaml @@ -869,7 +869,9 @@ spec: be used for all system images. type: string version: - description: Version specifies the rke2 version. + description: Version specifies the rke2 version. This + field will be deprecated in newer versions of the API + and RKE2ControlPlaneSpec.Version will be used instead. type: string type: object files: diff --git a/bootstrap/config/crd/kustomization.yaml b/bootstrap/config/crd/kustomization.yaml index d67468b4..d493ba23 100644 --- a/bootstrap/config/crd/kustomization.yaml +++ b/bootstrap/config/crd/kustomization.yaml @@ -1,5 +1,5 @@ commonLabels: - cluster.x-k8s.io/v1beta1: v1alpha1 + cluster.x-k8s.io/v1beta1: v1alpha1_v1beta1 # This kustomization.yaml is not intended to be run by itself, # since it depends on service name and namespace that are out of this kustomize package. diff --git a/bootstrap/config/crd/patches/webhook_in_rke2configs.yaml b/bootstrap/config/crd/patches/webhook_in_rke2configs.yaml index 675c4ddf..85f6bdd8 100644 --- a/bootstrap/config/crd/patches/webhook_in_rke2configs.yaml +++ b/bootstrap/config/crd/patches/webhook_in_rke2configs.yaml @@ -17,3 +17,4 @@ spec: path: /convert conversionReviewVersions: - v1 + - v1beta1 diff --git a/controlplane/api/v1alpha1/conversion.go b/controlplane/api/v1alpha1/conversion.go index 0c9404fb..6ba3b9d8 100644 --- a/controlplane/api/v1alpha1/conversion.go +++ b/controlplane/api/v1alpha1/conversion.go @@ -19,6 +19,9 @@ package v1alpha1 import ( "fmt" + apiconversion "k8s.io/apimachinery/pkg/conversion" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + controlplanev1 "github.com/rancher-sandbox/cluster-api-provider-rke2/controlplane/api/v1beta1" "sigs.k8s.io/controller-runtime/pkg/conversion" ) @@ -28,11 +31,20 @@ func (src *RKE2ControlPlane) ConvertTo(dstRaw conversion.Hub) error { if !ok { return fmt.Errorf("not a RKE2ControlPlane: %v", dst) } - if err := Convert_v1alpha1_RKE2ControlPlane_To_v1beta1_RKE2ControlPlane(src, dst, nil); err != nil { return err } + // Manually restore data. + restored := &controlplanev1.RKE2ControlPlane{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + dst.Spec.MachineTemplate = restored.Spec.MachineTemplate + dst.Spec.Version = restored.Spec.Version + dst.Status = restored.Status + return nil } @@ -46,6 +58,11 @@ func (dst *RKE2ControlPlane) ConvertFrom(srcRaw conversion.Hub) error { return err } + // Preserve Hub data on down-conversion + if err := utilconversion.MarshalData(src, dst); err != nil { + return err + } + return nil } @@ -85,6 +102,15 @@ func (src *RKE2ControlPlaneTemplate) ConvertTo(dstRaw conversion.Hub) error { return err } + // Manually restore data. + restored := &controlplanev1.RKE2ControlPlaneTemplate{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + dst.Spec.Template = restored.Spec.Template + dst.Status = restored.Status + return nil } @@ -98,6 +124,11 @@ func (dst *RKE2ControlPlaneTemplate) ConvertFrom(srcRaw conversion.Hub) error { return err } + // Preserve Hub data on down-conversion + if err := utilconversion.MarshalData(src, dst); err != nil { + return err + } + return nil } @@ -126,3 +157,33 @@ func (dst *RKE2ControlPlaneTemplateList) ConvertFrom(srcRaw conversion.Hub) erro return nil } + +func Convert_v1beta1_RKE2ControlPlaneSpec_To_v1alpha1_RKE2ControlPlaneSpec(in *controlplanev1.RKE2ControlPlaneSpec, out *RKE2ControlPlaneSpec, s apiconversion.Scope) error { + // Version was added in v1beta1. + // MachineTemplate was added in v1beta1. + return autoConvert_v1beta1_RKE2ControlPlaneSpec_To_v1alpha1_RKE2ControlPlaneSpec(in, out, s) +} + +func Convert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStatus(in *controlplanev1.RKE2ControlPlaneStatus, out *RKE2ControlPlaneStatus, s apiconversion.Scope) error { + return autoConvert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStatus(in, out, s) +} + +func Convert_v1alpha1_RKE2ControlPlaneStatus_To_v1beta1_RKE2ControlPlaneStatus(in *RKE2ControlPlaneStatus, out *controlplanev1.RKE2ControlPlaneStatus, s apiconversion.Scope) error { + return autoConvert_v1alpha1_RKE2ControlPlaneStatus_To_v1beta1_RKE2ControlPlaneStatus(in, out, s) +} + +func Convert_v1beta1_RKE2ControlPlaneTemplateSpec_To_v1alpha1_RKE2ControlPlaneTemplateSpec(in *controlplanev1.RKE2ControlPlaneTemplateSpec, out *RKE2ControlPlaneTemplateSpec, s apiconversion.Scope) error { + return autoConvert_v1beta1_RKE2ControlPlaneTemplateSpec_To_v1alpha1_RKE2ControlPlaneTemplateSpec(in, out, s) +} + +func Convert_v1alpha1_RKE2ControlPlaneTemplateSpec_To_v1beta1_RKE2ControlPlaneTemplateSpec(in *RKE2ControlPlaneTemplateSpec, out *controlplanev1.RKE2ControlPlaneTemplateSpec, s apiconversion.Scope) error { + return autoConvert_v1alpha1_RKE2ControlPlaneTemplateSpec_To_v1beta1_RKE2ControlPlaneTemplateSpec(in, out, s) +} + +func Convert_v1alpha1_RKE2ControlPlaneTemplateStatus_To_v1beta1_RKE2ControlPlaneStatus(in *RKE2ControlPlaneTemplateStatus, out *controlplanev1.RKE2ControlPlaneStatus, s apiconversion.Scope) error { + return nil +} + +func Convert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneTemplateStatus(in *controlplanev1.RKE2ControlPlaneStatus, out *RKE2ControlPlaneTemplateStatus, s apiconversion.Scope) error { + return nil +} diff --git a/controlplane/api/v1alpha1/rke2controlplanetemplate_types.go b/controlplane/api/v1alpha1/rke2controlplanetemplate_types.go index 968a9714..c445e66c 100644 --- a/controlplane/api/v1alpha1/rke2controlplanetemplate_types.go +++ b/controlplane/api/v1alpha1/rke2controlplanetemplate_types.go @@ -27,9 +27,6 @@ import ( type RKE2ControlPlaneTemplateSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - - // Foo is an example field of RKE2ControlPlaneTemplate. Edit rke2controlplanetemplate_types.go to remove/update - Foo string `json:"foo,omitempty"` } // RKE2ControlPlaneTemplateStatus defines the observed state of RKE2ControlPlaneTemplate. diff --git a/controlplane/api/v1alpha1/zz_generated.conversion.go b/controlplane/api/v1alpha1/zz_generated.conversion.go index 55a80772..a2902407 100644 --- a/controlplane/api/v1alpha1/zz_generated.conversion.go +++ b/controlplane/api/v1alpha1/zz_generated.conversion.go @@ -106,88 +106,88 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ControlPlaneSpec)(nil), (*RKE2ControlPlaneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_RKE2ControlPlaneSpec_To_v1alpha1_RKE2ControlPlaneSpec(a.(*v1beta1.RKE2ControlPlaneSpec), b.(*RKE2ControlPlaneSpec), scope) + if err := s.AddGeneratedConversionFunc((*RKE2ControlPlaneTemplate)(nil), (*v1beta1.RKE2ControlPlaneTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_RKE2ControlPlaneTemplate_To_v1beta1_RKE2ControlPlaneTemplate(a.(*RKE2ControlPlaneTemplate), b.(*v1beta1.RKE2ControlPlaneTemplate), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*RKE2ControlPlaneStatus)(nil), (*v1beta1.RKE2ControlPlaneStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_RKE2ControlPlaneStatus_To_v1beta1_RKE2ControlPlaneStatus(a.(*RKE2ControlPlaneStatus), b.(*v1beta1.RKE2ControlPlaneStatus), scope) + if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ControlPlaneTemplate)(nil), (*RKE2ControlPlaneTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RKE2ControlPlaneTemplate_To_v1alpha1_RKE2ControlPlaneTemplate(a.(*v1beta1.RKE2ControlPlaneTemplate), b.(*RKE2ControlPlaneTemplate), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ControlPlaneStatus)(nil), (*RKE2ControlPlaneStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStatus(a.(*v1beta1.RKE2ControlPlaneStatus), b.(*RKE2ControlPlaneStatus), scope) + if err := s.AddGeneratedConversionFunc((*RKE2ControlPlaneTemplateList)(nil), (*v1beta1.RKE2ControlPlaneTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_RKE2ControlPlaneTemplateList_To_v1beta1_RKE2ControlPlaneTemplateList(a.(*RKE2ControlPlaneTemplateList), b.(*v1beta1.RKE2ControlPlaneTemplateList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*RKE2ControlPlaneTemplate)(nil), (*v1beta1.RKE2ControlPlaneTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_RKE2ControlPlaneTemplate_To_v1beta1_RKE2ControlPlaneTemplate(a.(*RKE2ControlPlaneTemplate), b.(*v1beta1.RKE2ControlPlaneTemplate), scope) + if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ControlPlaneTemplateList)(nil), (*RKE2ControlPlaneTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RKE2ControlPlaneTemplateList_To_v1alpha1_RKE2ControlPlaneTemplateList(a.(*v1beta1.RKE2ControlPlaneTemplateList), b.(*RKE2ControlPlaneTemplateList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ControlPlaneTemplate)(nil), (*RKE2ControlPlaneTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_RKE2ControlPlaneTemplate_To_v1alpha1_RKE2ControlPlaneTemplate(a.(*v1beta1.RKE2ControlPlaneTemplate), b.(*RKE2ControlPlaneTemplate), scope) + if err := s.AddGeneratedConversionFunc((*RKE2ServerConfig)(nil), (*v1beta1.RKE2ServerConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_RKE2ServerConfig_To_v1beta1_RKE2ServerConfig(a.(*RKE2ServerConfig), b.(*v1beta1.RKE2ServerConfig), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*RKE2ControlPlaneTemplateList)(nil), (*v1beta1.RKE2ControlPlaneTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_RKE2ControlPlaneTemplateList_To_v1beta1_RKE2ControlPlaneTemplateList(a.(*RKE2ControlPlaneTemplateList), b.(*v1beta1.RKE2ControlPlaneTemplateList), scope) + if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ServerConfig)(nil), (*RKE2ServerConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RKE2ServerConfig_To_v1alpha1_RKE2ServerConfig(a.(*v1beta1.RKE2ServerConfig), b.(*RKE2ServerConfig), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ControlPlaneTemplateList)(nil), (*RKE2ControlPlaneTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_RKE2ControlPlaneTemplateList_To_v1alpha1_RKE2ControlPlaneTemplateList(a.(*v1beta1.RKE2ControlPlaneTemplateList), b.(*RKE2ControlPlaneTemplateList), scope) + if err := s.AddGeneratedConversionFunc((*RollingUpdate)(nil), (*v1beta1.RollingUpdate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_RollingUpdate_To_v1beta1_RollingUpdate(a.(*RollingUpdate), b.(*v1beta1.RollingUpdate), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*RKE2ControlPlaneTemplateSpec)(nil), (*v1beta1.RKE2ControlPlaneTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_RKE2ControlPlaneTemplateSpec_To_v1beta1_RKE2ControlPlaneTemplateSpec(a.(*RKE2ControlPlaneTemplateSpec), b.(*v1beta1.RKE2ControlPlaneTemplateSpec), scope) + if err := s.AddGeneratedConversionFunc((*v1beta1.RollingUpdate)(nil), (*RollingUpdate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RollingUpdate_To_v1alpha1_RollingUpdate(a.(*v1beta1.RollingUpdate), b.(*RollingUpdate), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ControlPlaneTemplateSpec)(nil), (*RKE2ControlPlaneTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_RKE2ControlPlaneTemplateSpec_To_v1alpha1_RKE2ControlPlaneTemplateSpec(a.(*v1beta1.RKE2ControlPlaneTemplateSpec), b.(*RKE2ControlPlaneTemplateSpec), scope) + if err := s.AddGeneratedConversionFunc((*RolloutStrategy)(nil), (*v1beta1.RolloutStrategy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_RolloutStrategy_To_v1beta1_RolloutStrategy(a.(*RolloutStrategy), b.(*v1beta1.RolloutStrategy), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*RKE2ControlPlaneTemplateStatus)(nil), (*v1beta1.RKE2ControlPlaneTemplateStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_RKE2ControlPlaneTemplateStatus_To_v1beta1_RKE2ControlPlaneTemplateStatus(a.(*RKE2ControlPlaneTemplateStatus), b.(*v1beta1.RKE2ControlPlaneTemplateStatus), scope) + if err := s.AddGeneratedConversionFunc((*v1beta1.RolloutStrategy)(nil), (*RolloutStrategy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RolloutStrategy_To_v1alpha1_RolloutStrategy(a.(*v1beta1.RolloutStrategy), b.(*RolloutStrategy), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ControlPlaneTemplateStatus)(nil), (*RKE2ControlPlaneTemplateStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_RKE2ControlPlaneTemplateStatus_To_v1alpha1_RKE2ControlPlaneTemplateStatus(a.(*v1beta1.RKE2ControlPlaneTemplateStatus), b.(*RKE2ControlPlaneTemplateStatus), scope) + if err := s.AddConversionFunc((*RKE2ControlPlaneStatus)(nil), (*v1beta1.RKE2ControlPlaneStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_RKE2ControlPlaneStatus_To_v1beta1_RKE2ControlPlaneStatus(a.(*RKE2ControlPlaneStatus), b.(*v1beta1.RKE2ControlPlaneStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*RKE2ServerConfig)(nil), (*v1beta1.RKE2ServerConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_RKE2ServerConfig_To_v1beta1_RKE2ServerConfig(a.(*RKE2ServerConfig), b.(*v1beta1.RKE2ServerConfig), scope) + if err := s.AddConversionFunc((*RKE2ControlPlaneTemplateSpec)(nil), (*v1beta1.RKE2ControlPlaneTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_RKE2ControlPlaneTemplateSpec_To_v1beta1_RKE2ControlPlaneTemplateSpec(a.(*RKE2ControlPlaneTemplateSpec), b.(*v1beta1.RKE2ControlPlaneTemplateSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.RKE2ServerConfig)(nil), (*RKE2ServerConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_RKE2ServerConfig_To_v1alpha1_RKE2ServerConfig(a.(*v1beta1.RKE2ServerConfig), b.(*RKE2ServerConfig), scope) + if err := s.AddConversionFunc((*RKE2ControlPlaneTemplateStatus)(nil), (*v1beta1.RKE2ControlPlaneStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_RKE2ControlPlaneTemplateStatus_To_v1beta1_RKE2ControlPlaneStatus(a.(*RKE2ControlPlaneTemplateStatus), b.(*v1beta1.RKE2ControlPlaneStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*RollingUpdate)(nil), (*v1beta1.RollingUpdate)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_RollingUpdate_To_v1beta1_RollingUpdate(a.(*RollingUpdate), b.(*v1beta1.RollingUpdate), scope) + if err := s.AddConversionFunc((*v1beta1.RKE2ControlPlaneSpec)(nil), (*RKE2ControlPlaneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RKE2ControlPlaneSpec_To_v1alpha1_RKE2ControlPlaneSpec(a.(*v1beta1.RKE2ControlPlaneSpec), b.(*RKE2ControlPlaneSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.RollingUpdate)(nil), (*RollingUpdate)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_RollingUpdate_To_v1alpha1_RollingUpdate(a.(*v1beta1.RollingUpdate), b.(*RollingUpdate), scope) + if err := s.AddConversionFunc((*v1beta1.RKE2ControlPlaneStatus)(nil), (*RKE2ControlPlaneStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStatus(a.(*v1beta1.RKE2ControlPlaneStatus), b.(*RKE2ControlPlaneStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*RolloutStrategy)(nil), (*v1beta1.RolloutStrategy)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_RolloutStrategy_To_v1beta1_RolloutStrategy(a.(*RolloutStrategy), b.(*v1beta1.RolloutStrategy), scope) + if err := s.AddConversionFunc((*v1beta1.RKE2ControlPlaneStatus)(nil), (*RKE2ControlPlaneTemplateStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneTemplateStatus(a.(*v1beta1.RKE2ControlPlaneStatus), b.(*RKE2ControlPlaneTemplateStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.RolloutStrategy)(nil), (*RolloutStrategy)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_RolloutStrategy_To_v1alpha1_RolloutStrategy(a.(*v1beta1.RolloutStrategy), b.(*RolloutStrategy), scope) + if err := s.AddConversionFunc((*v1beta1.RKE2ControlPlaneTemplateSpec)(nil), (*RKE2ControlPlaneTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_RKE2ControlPlaneTemplateSpec_To_v1alpha1_RKE2ControlPlaneTemplateSpec(a.(*v1beta1.RKE2ControlPlaneTemplateSpec), b.(*RKE2ControlPlaneTemplateSpec), scope) }); err != nil { return err } @@ -423,6 +423,8 @@ func autoConvert_v1beta1_RKE2ControlPlaneSpec_To_v1alpha1_RKE2ControlPlaneSpec(i return err } out.Replicas = (*int32)(unsafe.Pointer(in.Replicas)) + // WARNING: in.Version requires manual conversion: does not exist in peer-type + // WARNING: in.MachineTemplate requires manual conversion: does not exist in peer-type if err := Convert_v1beta1_RKE2ServerConfig_To_v1alpha1_RKE2ServerConfig(&in.ServerConfig, &out.ServerConfig, s); err != nil { return err } @@ -435,11 +437,6 @@ func autoConvert_v1beta1_RKE2ControlPlaneSpec_To_v1alpha1_RKE2ControlPlaneSpec(i return nil } -// Convert_v1beta1_RKE2ControlPlaneSpec_To_v1alpha1_RKE2ControlPlaneSpec is an autogenerated conversion function. -func Convert_v1beta1_RKE2ControlPlaneSpec_To_v1alpha1_RKE2ControlPlaneSpec(in *v1beta1.RKE2ControlPlaneSpec, out *RKE2ControlPlaneSpec, s conversion.Scope) error { - return autoConvert_v1beta1_RKE2ControlPlaneSpec_To_v1alpha1_RKE2ControlPlaneSpec(in, out, s) -} - func autoConvert_v1alpha1_RKE2ControlPlaneStatus_To_v1beta1_RKE2ControlPlaneStatus(in *RKE2ControlPlaneStatus, out *v1beta1.RKE2ControlPlaneStatus, s conversion.Scope) error { out.Ready = in.Ready out.Initialized = in.Initialized @@ -456,11 +453,6 @@ func autoConvert_v1alpha1_RKE2ControlPlaneStatus_To_v1beta1_RKE2ControlPlaneStat return nil } -// Convert_v1alpha1_RKE2ControlPlaneStatus_To_v1beta1_RKE2ControlPlaneStatus is an autogenerated conversion function. -func Convert_v1alpha1_RKE2ControlPlaneStatus_To_v1beta1_RKE2ControlPlaneStatus(in *RKE2ControlPlaneStatus, out *v1beta1.RKE2ControlPlaneStatus, s conversion.Scope) error { - return autoConvert_v1alpha1_RKE2ControlPlaneStatus_To_v1beta1_RKE2ControlPlaneStatus(in, out, s) -} - func autoConvert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStatus(in *v1beta1.RKE2ControlPlaneStatus, out *RKE2ControlPlaneStatus, s conversion.Scope) error { out.Ready = in.Ready out.Initialized = in.Initialized @@ -470,6 +462,7 @@ func autoConvert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStat out.ObservedGeneration = in.ObservedGeneration out.Conditions = *(*clusterapiapiv1beta1.Conditions)(unsafe.Pointer(&in.Conditions)) out.Replicas = in.Replicas + // WARNING: in.Version requires manual conversion: does not exist in peer-type out.ReadyReplicas = in.ReadyReplicas out.UpdatedReplicas = in.UpdatedReplicas out.UnavailableReplicas = in.UnavailableReplicas @@ -477,17 +470,12 @@ func autoConvert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStat return nil } -// Convert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStatus is an autogenerated conversion function. -func Convert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStatus(in *v1beta1.RKE2ControlPlaneStatus, out *RKE2ControlPlaneStatus, s conversion.Scope) error { - return autoConvert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneStatus(in, out, s) -} - func autoConvert_v1alpha1_RKE2ControlPlaneTemplate_To_v1beta1_RKE2ControlPlaneTemplate(in *RKE2ControlPlaneTemplate, out *v1beta1.RKE2ControlPlaneTemplate, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1alpha1_RKE2ControlPlaneTemplateSpec_To_v1beta1_RKE2ControlPlaneTemplateSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1alpha1_RKE2ControlPlaneTemplateStatus_To_v1beta1_RKE2ControlPlaneTemplateStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1alpha1_RKE2ControlPlaneTemplateStatus_To_v1beta1_RKE2ControlPlaneStatus(&in.Status, &out.Status, s); err != nil { return err } return nil @@ -503,7 +491,7 @@ func autoConvert_v1beta1_RKE2ControlPlaneTemplate_To_v1alpha1_RKE2ControlPlaneTe if err := Convert_v1beta1_RKE2ControlPlaneTemplateSpec_To_v1alpha1_RKE2ControlPlaneTemplateSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1beta1_RKE2ControlPlaneTemplateStatus_To_v1alpha1_RKE2ControlPlaneTemplateStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1beta1_RKE2ControlPlaneStatus_To_v1alpha1_RKE2ControlPlaneTemplateStatus(&in.Status, &out.Status, s); err != nil { return err } return nil @@ -516,7 +504,17 @@ func Convert_v1beta1_RKE2ControlPlaneTemplate_To_v1alpha1_RKE2ControlPlaneTempla func autoConvert_v1alpha1_RKE2ControlPlaneTemplateList_To_v1beta1_RKE2ControlPlaneTemplateList(in *RKE2ControlPlaneTemplateList, out *v1beta1.RKE2ControlPlaneTemplateList, s conversion.Scope) error { out.ListMeta = in.ListMeta - out.Items = *(*[]v1beta1.RKE2ControlPlaneTemplate)(unsafe.Pointer(&in.Items)) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta1.RKE2ControlPlaneTemplate, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_RKE2ControlPlaneTemplate_To_v1beta1_RKE2ControlPlaneTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } return nil } @@ -527,7 +525,17 @@ func Convert_v1alpha1_RKE2ControlPlaneTemplateList_To_v1beta1_RKE2ControlPlaneTe func autoConvert_v1beta1_RKE2ControlPlaneTemplateList_To_v1alpha1_RKE2ControlPlaneTemplateList(in *v1beta1.RKE2ControlPlaneTemplateList, out *RKE2ControlPlaneTemplateList, s conversion.Scope) error { out.ListMeta = in.ListMeta - out.Items = *(*[]RKE2ControlPlaneTemplate)(unsafe.Pointer(&in.Items)) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RKE2ControlPlaneTemplate, len(*in)) + for i := range *in { + if err := Convert_v1beta1_RKE2ControlPlaneTemplate_To_v1alpha1_RKE2ControlPlaneTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } return nil } @@ -537,43 +545,14 @@ func Convert_v1beta1_RKE2ControlPlaneTemplateList_To_v1alpha1_RKE2ControlPlaneTe } func autoConvert_v1alpha1_RKE2ControlPlaneTemplateSpec_To_v1beta1_RKE2ControlPlaneTemplateSpec(in *RKE2ControlPlaneTemplateSpec, out *v1beta1.RKE2ControlPlaneTemplateSpec, s conversion.Scope) error { - out.Foo = in.Foo return nil } -// Convert_v1alpha1_RKE2ControlPlaneTemplateSpec_To_v1beta1_RKE2ControlPlaneTemplateSpec is an autogenerated conversion function. -func Convert_v1alpha1_RKE2ControlPlaneTemplateSpec_To_v1beta1_RKE2ControlPlaneTemplateSpec(in *RKE2ControlPlaneTemplateSpec, out *v1beta1.RKE2ControlPlaneTemplateSpec, s conversion.Scope) error { - return autoConvert_v1alpha1_RKE2ControlPlaneTemplateSpec_To_v1beta1_RKE2ControlPlaneTemplateSpec(in, out, s) -} - func autoConvert_v1beta1_RKE2ControlPlaneTemplateSpec_To_v1alpha1_RKE2ControlPlaneTemplateSpec(in *v1beta1.RKE2ControlPlaneTemplateSpec, out *RKE2ControlPlaneTemplateSpec, s conversion.Scope) error { - out.Foo = in.Foo - return nil -} - -// Convert_v1beta1_RKE2ControlPlaneTemplateSpec_To_v1alpha1_RKE2ControlPlaneTemplateSpec is an autogenerated conversion function. -func Convert_v1beta1_RKE2ControlPlaneTemplateSpec_To_v1alpha1_RKE2ControlPlaneTemplateSpec(in *v1beta1.RKE2ControlPlaneTemplateSpec, out *RKE2ControlPlaneTemplateSpec, s conversion.Scope) error { - return autoConvert_v1beta1_RKE2ControlPlaneTemplateSpec_To_v1alpha1_RKE2ControlPlaneTemplateSpec(in, out, s) -} - -func autoConvert_v1alpha1_RKE2ControlPlaneTemplateStatus_To_v1beta1_RKE2ControlPlaneTemplateStatus(in *RKE2ControlPlaneTemplateStatus, out *v1beta1.RKE2ControlPlaneTemplateStatus, s conversion.Scope) error { + // WARNING: in.Template requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha1_RKE2ControlPlaneTemplateStatus_To_v1beta1_RKE2ControlPlaneTemplateStatus is an autogenerated conversion function. -func Convert_v1alpha1_RKE2ControlPlaneTemplateStatus_To_v1beta1_RKE2ControlPlaneTemplateStatus(in *RKE2ControlPlaneTemplateStatus, out *v1beta1.RKE2ControlPlaneTemplateStatus, s conversion.Scope) error { - return autoConvert_v1alpha1_RKE2ControlPlaneTemplateStatus_To_v1beta1_RKE2ControlPlaneTemplateStatus(in, out, s) -} - -func autoConvert_v1beta1_RKE2ControlPlaneTemplateStatus_To_v1alpha1_RKE2ControlPlaneTemplateStatus(in *v1beta1.RKE2ControlPlaneTemplateStatus, out *RKE2ControlPlaneTemplateStatus, s conversion.Scope) error { - return nil -} - -// Convert_v1beta1_RKE2ControlPlaneTemplateStatus_To_v1alpha1_RKE2ControlPlaneTemplateStatus is an autogenerated conversion function. -func Convert_v1beta1_RKE2ControlPlaneTemplateStatus_To_v1alpha1_RKE2ControlPlaneTemplateStatus(in *v1beta1.RKE2ControlPlaneTemplateStatus, out *RKE2ControlPlaneTemplateStatus, s conversion.Scope) error { - return autoConvert_v1beta1_RKE2ControlPlaneTemplateStatus_To_v1alpha1_RKE2ControlPlaneTemplateStatus(in, out, s) -} - func autoConvert_v1alpha1_RKE2ServerConfig_To_v1beta1_RKE2ServerConfig(in *RKE2ServerConfig, out *v1beta1.RKE2ServerConfig, s conversion.Scope) error { out.AuditPolicySecret = (*v1.ObjectReference)(unsafe.Pointer(in.AuditPolicySecret)) out.BindAddress = in.BindAddress diff --git a/controlplane/api/v1beta1/conversion.go b/controlplane/api/v1beta1/conversion.go index bc54697e..338108cb 100644 --- a/controlplane/api/v1beta1/conversion.go +++ b/controlplane/api/v1beta1/conversion.go @@ -26,4 +26,4 @@ func (*RKE2ControlPlaneList) Hub() {} func (*RKE2ControlPlaneTemplate) Hub() {} // Hub is a conversion hub for the RKE2ControlPlaneTemplateList resource. -func (*RKE2ControlPlaneTemplateList) Hub() {} \ No newline at end of file +func (*RKE2ControlPlaneTemplateList) Hub() {} diff --git a/controlplane/api/v1beta1/rke2controlplane_types.go b/controlplane/api/v1beta1/rke2controlplane_types.go index 66ed607f..d2bfc164 100644 --- a/controlplane/api/v1beta1/rke2controlplane_types.go +++ b/controlplane/api/v1beta1/rke2controlplane_types.go @@ -27,8 +27,11 @@ import ( ) const ( + // RKE2ControlPlaneLegacyFinalizer allows the controller to clean up resources on delete. + // this is the old finalizer name. It is kept to ensure backward compatibility. + RKE2ControlPlaneLegacyFinalizer = "rke2.controleplane.cluster.x-k8s.io" // RKE2ControlPlaneFinalizer allows the controller to clean up resources on delete. - RKE2ControlPlaneFinalizer = "rke2.controleplane.cluster.x-k8s.io" + RKE2ControlPlaneFinalizer = "rke2.controlplane.cluster.x-k8s.io" // RKE2ServerConfigurationAnnotation is a machine annotation that stores the json-marshalled string of RKE2Config // This annotation is used to detect any changes in RKE2Config and trigger machine rollout. @@ -43,6 +46,18 @@ type RKE2ControlPlaneSpec struct { // Replicas is the number of replicas for the Control Plane. Replicas *int32 `json:"replicas,omitempty"` + // Version defines the desired Kubernetes version. + // This is only a placeholder for now, and the RKE2ConfigSpec.AgentConfig.Version field should be used instead. + // In future iterations, this field overrides the RKE2 Version specificied in RKE2ConfigSpec.AgentConfig.Version + // which will be deprecated in newer versions of the API. + // +optional + Version string `json:"version"` + + // MachineTemplate contains information about how machines + // should be shaped when creating or updating a control plane. + // +optional + MachineTemplate RKE2ControlPlaneMachineTemplate `json:"machineTemplate,omitempty"` + // ServerConfig specifies configuration for the agent nodes. //+optional ServerConfig RKE2ServerConfig `json:"serverConfig,omitempty"` @@ -79,6 +94,25 @@ type RKE2ControlPlaneSpec struct { RolloutStrategy *RolloutStrategy `json:"rolloutStrategy,omitempty"` } +// RKE2ControlPlaneMachineTemplate defines the template for Machines +// in a RKE2ControlPlane object. +type RKE2ControlPlaneMachineTemplate struct { + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + ObjectMeta clusterv1.ObjectMeta `json:"metadata,omitempty"` + + // InfrastructureRef is a required reference to a custom resource + // offered by an infrastructure provider. + InfrastructureRef corev1.ObjectReference `json:"infrastructureRef"` + + // NodeDrainTimeout is the total amount of time that the controller will spend on draining a controlplane node + // The default value is 0, meaning that the node can be drained without any time limitations. + // NOTE: NodeDrainTimeout is different from `kubectl drain --timeout` + // +optional + NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"` +} + // RKE2ServerConfig specifies configuration for the agent nodes. type RKE2ServerConfig struct { // AuditPolicySecret path to the file that defines the audit policy configuration. @@ -188,6 +222,11 @@ type RKE2ControlPlaneStatus struct { // Replicas is the number of replicas current attached to this ControlPlane Resource. Replicas int32 `json:"replicas,omitempty"` + // Version represents the minimum Kubernetes version for the control plane machines + // in the cluster. + // +optional + Version *string `json:"version,omitempty"` + // ReadyReplicas is the number of replicas current attached to this ControlPlane Resource and that have Ready Status. ReadyReplicas int32 `json:"readyReplicas,omitempty"` diff --git a/controlplane/api/v1beta1/rke2controlplanetemplate_types.go b/controlplane/api/v1beta1/rke2controlplanetemplate_types.go index 1a961477..e42eec5b 100644 --- a/controlplane/api/v1beta1/rke2controlplanetemplate_types.go +++ b/controlplane/api/v1beta1/rke2controlplanetemplate_types.go @@ -20,26 +20,19 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - // RKE2ControlPlaneTemplateSpec defines the desired state of RKE2ControlPlaneTemplate. type RKE2ControlPlaneTemplateSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Foo is an example field of RKE2ControlPlaneTemplate. Edit rke2controlplanetemplate_types.go to remove/update - Foo string `json:"foo,omitempty"` + Template RKE2ControlPlaneTemplateResource `json:"template"` } -// RKE2ControlPlaneTemplateStatus defines the observed state of RKE2ControlPlaneTemplate. -type RKE2ControlPlaneTemplateStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file +// RKE2ControlPlaneTemplateResource contains spec for RKE2ControlPlaneTemplate. +type RKE2ControlPlaneTemplateResource struct { + // Spec is the specification of the desired behavior of the control plane. + Spec RKE2ControlPlaneSpec `json:"spec"` } // +kubebuilder:object:root=true -// +kubebuilder:subresource:status +// +kubebuilder:resource:path=rke2controlplanetemplates,scope=Namespaced,categories=cluster-api,shortName=rke2ct // +kubebuilder:storageversion // RKE2ControlPlaneTemplate is the Schema for the rke2controlplanetemplates API. @@ -47,8 +40,10 @@ type RKE2ControlPlaneTemplate struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec RKE2ControlPlaneTemplateSpec `json:"spec,omitempty"` - Status RKE2ControlPlaneTemplateStatus `json:"status,omitempty"` + // Spec is the control plane specification for the template resource. + Spec RKE2ControlPlaneTemplateSpec `json:"spec,omitempty"` + // Status is the current state of the control plane. + Status RKE2ControlPlaneStatus `json:"status,omitempty"` } //+kubebuilder:object:root=true diff --git a/controlplane/api/v1beta1/rke2controlplanetemplate_webhook.go b/controlplane/api/v1beta1/rke2controlplanetemplate_webhook.go index 6adc22b5..2ed42cce 100644 --- a/controlplane/api/v1beta1/rke2controlplanetemplate_webhook.go +++ b/controlplane/api/v1beta1/rke2controlplanetemplate_webhook.go @@ -17,15 +17,18 @@ limitations under the License. package v1beta1 import ( + "errors" + + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) -// log is for logging in this package. -var rke2controlplanetemplatelog = logf.Log.WithName("rke2controlplanetemplate-resource") + bootstrapv1 "github.com/rancher-sandbox/cluster-api-provider-rke2/bootstrap/api/v1beta1" +) // SetupWebhookWithManager sets up the Controller Manager for the Webhook for the RKE2ControlPlaneTemplate resource. func (r *RKE2ControlPlaneTemplate) SetupWebhookWithManager(mgr ctrl.Manager) error { @@ -40,7 +43,7 @@ var _ webhook.Defaulter = &RKE2ControlPlaneTemplate{} // Default implements webhook.Defaulter so a webhook will be registered for the type. func (r *RKE2ControlPlaneTemplate) Default() { - rke2controlplanetemplatelog.Info("default", "name", r.Name) + bootstrapv1.DefaultRKE2ConfigSpec(&r.Spec.Template.Spec.RKE2ConfigSpec) } //+kubebuilder:webhook:path=/validate-controlplane-cluster-x-k8s-io-v1beta1-rke2controlplanetemplate,mutating=false,failurePolicy=fail,sideEffects=None,groups=controlplane.cluster.x-k8s.io,resources=rke2controlplanetemplates,verbs=create;update,versions=v1beta1,name=vrke2controlplanetemplate.kb.io,admissionReviewVersions=v1 @@ -49,21 +52,81 @@ var _ webhook.Validator = &RKE2ControlPlaneTemplate{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type. func (r *RKE2ControlPlaneTemplate) ValidateCreate() (admission.Warnings, error) { - rke2controlplanetemplatelog.Info("validate create", "name", r.Name) + rke2controlplanelog.Info("RKE2ControlPlane validate create", "control-plane", klog.KObj(r)) - return nil, nil + var allErrs field.ErrorList + + allErrs = append(allErrs, bootstrapv1.ValidateRKE2ConfigSpec(r.Name, &r.Spec.Template.Spec.RKE2ConfigSpec)...) + allErrs = append(allErrs, r.validateCNI()...) + allErrs = append(allErrs, r.validateRegistrationMethod()...) + + if len(allErrs) == 0 { + return nil, nil + } + + return nil, apierrors.NewInvalid(GroupVersion.WithKind("RKE2ControlPlane").GroupKind(), r.Name, allErrs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *RKE2ControlPlaneTemplate) ValidateUpdate(_ runtime.Object) (admission.Warnings, error) { - rke2controlplanetemplatelog.Info("validate update", "name", r.Name) - - return nil, nil +func (r *RKE2ControlPlaneTemplate) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + oldControlplane, ok := old.(*RKE2ControlPlaneTemplate) + if !ok { + return nil, apierrors.NewInvalid(GroupVersion.WithKind("RKE2ControlPlane").GroupKind(), r.Name, field.ErrorList{ + field.InternalError(nil, errors.New("failed to convert old RKE2ControlPlane to object")), + }) + } + + var allErrs field.ErrorList + + allErrs = append(allErrs, bootstrapv1.ValidateRKE2ConfigSpec(r.Name, &r.Spec.Template.Spec.RKE2ConfigSpec)...) + allErrs = append(allErrs, r.validateCNI()...) + + if r.Spec.Template.Spec.RegistrationMethod != oldControlplane.Spec.Template.Spec.RegistrationMethod { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec", "registrationMethod"), r.Spec.Template.Spec.RegistrationMethod, "field is immutable"), + ) + } + + if len(allErrs) == 0 { + return nil, nil + } + + return nil, apierrors.NewInvalid(GroupVersion.WithKind("RKE2ControlPlane").GroupKind(), r.Name, allErrs) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type. func (r *RKE2ControlPlaneTemplate) ValidateDelete() (admission.Warnings, error) { - rke2controlplanetemplatelog.Info("validate delete", "name", r.Name) + rke2controlplanelog.Info("validate delete", "name", r.Name) return nil, nil } + +func (r *RKE2ControlPlaneTemplate) validateCNI() field.ErrorList { + var allErrs field.ErrorList + + spec := r.Spec.Template.Spec + + if spec.ServerConfig.CNIMultusEnable && r.Spec.Template.Spec.ServerConfig.CNI == "" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec", "serverConfig", "cni"), + r.Spec.Template.Spec.ServerConfig.CNI, "must be specified when cniMultusEnable is true")) + } + + return allErrs +} + +func (r *RKE2ControlPlaneTemplate) validateRegistrationMethod() field.ErrorList { + var allErrs field.ErrorList + + spec := r.Spec.Template.Spec + + if spec.RegistrationMethod == RegistrationMethodAddress { + if spec.RegistrationAddress == "" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec.registrationAddress"), + spec.RegistrationAddress, "registrationAddress must be supplied when using registration method 'address'")) + } + } + + return allErrs +} diff --git a/controlplane/api/v1beta1/rke2controlplanetemplate_webhook_test.go b/controlplane/api/v1beta1/rke2controlplanetemplate_webhook_test.go new file mode 100644 index 00000000..8e5ac45f --- /dev/null +++ b/controlplane/api/v1beta1/rke2controlplanetemplate_webhook_test.go @@ -0,0 +1,108 @@ +/* +Copyright 2024 SUSE. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "testing" + + . "github.com/onsi/gomega" +) + +func TestRKE2ControlPlaneTemplateValidateCreate(t *testing.T) { + g := NewWithT(t) + + tests := []struct { + name string + inputTemplate *RKE2ControlPlaneTemplate + wantErr bool + }{ + { + name: "don't allow RKE2ControlPlaneTemplate with invalid CNI", + inputTemplate: &RKE2ControlPlaneTemplate{ + Spec: RKE2ControlPlaneTemplateSpec{ + Template: RKE2ControlPlaneTemplateResource{ + Spec: RKE2ControlPlaneSpec{ + ServerConfig: RKE2ServerConfig{ + CNIMultusEnable: true, + CNI: "", + }, + }, + }, + }, + }, + wantErr: true, + }, + } + for _, test := range tests { + tt := test + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + warn, err := tt.inputTemplate.ValidateCreate() + if tt.wantErr { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).NotTo(HaveOccurred()) + } + g.Expect(warn).To(BeNil()) + }) + } +} + +func TestRKE2ControlPlaneTemplateValidateUpdate(t *testing.T) { + g := NewWithT(t) + + replicas := int32(3) + tests := []struct { + name string + newTemplate *RKE2ControlPlaneTemplate + oldTemplate *RKE2ControlPlaneTemplate + wantErr bool + }{ + { + name: "RKE2ControlPlaneTemplate with immutable spec", + newTemplate: &RKE2ControlPlaneTemplate{ + Spec: RKE2ControlPlaneTemplateSpec{ + Template: RKE2ControlPlaneTemplateResource{ + Spec: RKE2ControlPlaneSpec{ + Replicas: &replicas, + }, + }, + }, + }, + oldTemplate: &RKE2ControlPlaneTemplate{ + Spec: RKE2ControlPlaneTemplateSpec{ + Template: RKE2ControlPlaneTemplateResource{ + Spec: RKE2ControlPlaneSpec{ + Replicas: &replicas, + }, + }, + }, + }, + wantErr: false, + }, + } + for _, test := range tests { + tt := test + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + warn, err := tt.newTemplate.ValidateUpdate(tt.oldTemplate) + if tt.wantErr { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).NotTo(HaveOccurred()) + } + g.Expect(warn).To(BeNil()) + }) + } +} diff --git a/controlplane/api/v1beta1/zz_generated.deepcopy.go b/controlplane/api/v1beta1/zz_generated.deepcopy.go index ff608f69..ad2569b4 100644 --- a/controlplane/api/v1beta1/zz_generated.deepcopy.go +++ b/controlplane/api/v1beta1/zz_generated.deepcopy.go @@ -180,6 +180,28 @@ func (in *RKE2ControlPlaneList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RKE2ControlPlaneMachineTemplate) DeepCopyInto(out *RKE2ControlPlaneMachineTemplate) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.InfrastructureRef = in.InfrastructureRef + if in.NodeDrainTimeout != nil { + in, out := &in.NodeDrainTimeout, &out.NodeDrainTimeout + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RKE2ControlPlaneMachineTemplate. +func (in *RKE2ControlPlaneMachineTemplate) DeepCopy() *RKE2ControlPlaneMachineTemplate { + if in == nil { + return nil + } + out := new(RKE2ControlPlaneMachineTemplate) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RKE2ControlPlaneSpec) DeepCopyInto(out *RKE2ControlPlaneSpec) { *out = *in @@ -189,6 +211,7 @@ func (in *RKE2ControlPlaneSpec) DeepCopyInto(out *RKE2ControlPlaneSpec) { *out = new(int32) **out = **in } + in.MachineTemplate.DeepCopyInto(&out.MachineTemplate) in.ServerConfig.DeepCopyInto(&out.ServerConfig) out.ManifestsConfigMapReference = in.ManifestsConfigMapReference out.InfrastructureRef = in.InfrastructureRef @@ -229,6 +252,11 @@ func (in *RKE2ControlPlaneStatus) DeepCopyInto(out *RKE2ControlPlaneStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Version != nil { + in, out := &in.Version, &out.Version + *out = new(string) + **out = **in + } if in.AvailableServerIPs != nil { in, out := &in.AvailableServerIPs, &out.AvailableServerIPs *out = make([]string, len(*in)) @@ -251,8 +279,8 @@ func (in *RKE2ControlPlaneTemplate) DeepCopyInto(out *RKE2ControlPlaneTemplate) *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RKE2ControlPlaneTemplate. @@ -306,31 +334,33 @@ func (in *RKE2ControlPlaneTemplateList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RKE2ControlPlaneTemplateSpec) DeepCopyInto(out *RKE2ControlPlaneTemplateSpec) { +func (in *RKE2ControlPlaneTemplateResource) DeepCopyInto(out *RKE2ControlPlaneTemplateResource) { *out = *in + in.Spec.DeepCopyInto(&out.Spec) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RKE2ControlPlaneTemplateSpec. -func (in *RKE2ControlPlaneTemplateSpec) DeepCopy() *RKE2ControlPlaneTemplateSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RKE2ControlPlaneTemplateResource. +func (in *RKE2ControlPlaneTemplateResource) DeepCopy() *RKE2ControlPlaneTemplateResource { if in == nil { return nil } - out := new(RKE2ControlPlaneTemplateSpec) + out := new(RKE2ControlPlaneTemplateResource) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RKE2ControlPlaneTemplateStatus) DeepCopyInto(out *RKE2ControlPlaneTemplateStatus) { +func (in *RKE2ControlPlaneTemplateSpec) DeepCopyInto(out *RKE2ControlPlaneTemplateSpec) { *out = *in + in.Template.DeepCopyInto(&out.Template) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RKE2ControlPlaneTemplateStatus. -func (in *RKE2ControlPlaneTemplateStatus) DeepCopy() *RKE2ControlPlaneTemplateStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RKE2ControlPlaneTemplateSpec. +func (in *RKE2ControlPlaneTemplateSpec) DeepCopy() *RKE2ControlPlaneTemplateSpec { if in == nil { return nil } - out := new(RKE2ControlPlaneTemplateStatus) + out := new(RKE2ControlPlaneTemplateSpec) in.DeepCopyInto(out) return out } diff --git a/controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanes.yaml b/controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanes.yaml index 140636a3..9d9aa0e2 100644 --- a/controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanes.yaml +++ b/controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanes.yaml @@ -1472,7 +1472,9 @@ spec: for all system images. type: string version: - description: Version specifies the rke2 version. + description: Version specifies the rke2 version. This field will + be deprecated in newer versions of the API and RKE2ControlPlaneSpec.Version + will be used instead. type: string type: object files: @@ -1569,6 +1571,79 @@ spec: type: string type: object x-kubernetes-map-type: atomic + machineTemplate: + description: MachineTemplate contains information about how machines + should be shaped when creating or updating a control plane. + properties: + infrastructureRef: + description: InfrastructureRef is a required reference to a custom + resource offered by an infrastructure provider. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead + of an entire object, this string should contain a valid + JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen + only to have some well-defined way of referencing a part + of an object. TODO: this design is not final and this field + is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value map + stored with a resource that may be set by external tools + to store and retrieve arbitrary metadata. They are not queryable + and should be preserved when modifying objects. More info: + http://kubernetes.io/docs/user-guide/annotations' + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can be used + to organize and categorize (scope and select) objects. May + match selectors of replication controllers and services. + More info: http://kubernetes.io/docs/user-guide/labels' + type: object + type: object + nodeDrainTimeout: + description: 'NodeDrainTimeout is the total amount of time that + the controller will spend on draining a controlplane node The + default value is 0, meaning that the node can be drained without + any time limitations. NOTE: NodeDrainTimeout is different from + `kubectl drain --timeout`' + type: string + required: + - infrastructureRef + type: object manifestsConfigMapReference: description: ManifestsConfigMapReference references a ConfigMap which contains Kubernetes manifests to be deployed automatically on the @@ -2267,6 +2342,13 @@ spec: type: string type: array type: object + version: + description: Version defines the desired Kubernetes version. This + is only a placeholder for now, and the RKE2ConfigSpec.AgentConfig.Version + field should be used instead. In future iterations, this field overrides + the RKE2 Version specificied in RKE2ConfigSpec.AgentConfig.Version + which will be deprecated in newer versions of the API. + type: string required: - infrastructureRef type: object @@ -2369,6 +2451,10 @@ spec: Plane config. format: int32 type: integer + version: + description: Version represents the minimum Kubernetes version for + the control plane machines in the cluster. + type: string type: object type: object served: true diff --git a/controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanetemplates.yaml b/controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanetemplates.yaml index dadc640e..40c09837 100644 --- a/controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanetemplates.yaml +++ b/controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanetemplates.yaml @@ -8,9 +8,13 @@ metadata: spec: group: controlplane.cluster.x-k8s.io names: + categories: + - cluster-api kind: RKE2ControlPlaneTemplate listKind: RKE2ControlPlaneTemplateList plural: rke2controlplanetemplates + shortNames: + - rke2ct singular: rke2controlplanetemplate scope: Namespaced versions: @@ -35,11 +39,6 @@ spec: spec: description: RKE2ControlPlaneTemplateSpec defines the desired state of RKE2ControlPlaneTemplate. - properties: - foo: - description: Foo is an example field of RKE2ControlPlaneTemplate. - Edit rke2controlplanetemplate_types.go to remove/update - type: string type: object status: description: RKE2ControlPlaneTemplateStatus defines the observed state @@ -69,20 +68,1340 @@ spec: metadata: type: object spec: - description: RKE2ControlPlaneTemplateSpec defines the desired state of - RKE2ControlPlaneTemplate. + description: Spec is the control plane specification for the template + resource. properties: - foo: - description: Foo is an example field of RKE2ControlPlaneTemplate. - Edit rke2controlplanetemplate_types.go to remove/update - type: string + template: + description: RKE2ControlPlaneTemplateResource contains spec for RKE2ControlPlaneTemplate. + properties: + spec: + description: Spec is the specification of the desired behavior + of the control plane. + properties: + agentConfig: + description: AgentConfig specifies configuration for the agent + nodes. + properties: + additionalUserData: + description: AdditionalUserData is a field that allows + users to specify additional cloud-init or ignition configuration + to be included in the generated cloud-init/ignition + script. + properties: + config: + description: 'In case of using ignition, the data + format is documented here: https://kinvolk.io/docs/flatcar-container-linux/latest/provisioning/cl-config/ + NOTE: All fields of the UserData that are managed + by the RKE2Config controller will be ignored, this + include "write_files", "runcmd", "ntp".' + type: string + data: + additionalProperties: + type: string + description: Data allows to pass arbitrary set of + key/value pairs consistent with https://cloudinit.readthedocs.io/en/latest/reference/modules.html + to extend existing cloud-init configuration + type: object + strict: + description: Strict controls if Config should be strictly + parsed. If so, warnings are treated as errors. + type: boolean + type: object + x-kubernetes-validations: + - message: Only config or data could be populated at once + rule: '!has(self.data) || !has(self.config)' + airGapped: + description: AirGapped is a boolean value to define if + the bootstrapping should be air-gapped, basically supposing + that online container registries and RKE2 install scripts + are not reachable. + type: boolean + cisProfile: + description: CISProfile activates CIS compliance of RKE2 + for a certain profile + enum: + - cis-1.23 + - cis-1.5 + - cis-1.6 + type: string + containerRuntimeEndpoint: + description: ContainerRuntimeEndpoint Disable embedded + containerd and use alternative CRI implementation. + type: string + dataDir: + description: DataDir Folder to hold state. + type: string + enableContainerdSElinux: + description: EnableContainerdSElinux defines the policy + for enabling SELinux for Containerd if value is true, + Containerd will run with selinux-enabled=true flag if + value is false, Containerd will run without the above + flag + type: boolean + format: + description: Format specifies the output format of the + bootstrap data. Defaults to cloud-config. + enum: + - cloud-config + - ignition + type: string + imageCredentialProviderConfigMap: + description: ImageCredentialProviderConfigMap is a reference + to the ConfigMap that contains credential provider plugin + config The config map should contain a key "credential-config.yaml" + with YAML file content and a key "credential-provider-binaries" + with the a path to the binaries for the credential provider. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object + instead of an entire object, this string should + contain a valid JSON/Go field access statement, + such as desiredState.manifest.containers[2]. For + example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container + that triggered the event) or if no container name + is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part + of an object. TODO: this design is not final and + this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this + reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + kubeProxy: + description: KubeProxyArgs Customized flag for kube-proxy + process. + properties: + extraArgs: + description: 'ExtraArgs is a list of command line + arguments (format: flag=value) to pass to a Kubernetes + Component command.' + items: + type: string + type: array + extraEnv: + additionalProperties: + type: string + description: ExtraEnv is a map of environment variables + to pass on to a Kubernetes Component command. + type: object + extraMounts: + additionalProperties: + type: string + description: ExtraMounts is a map of volume mounts + to be added for the Kubernetes component StaticPod + type: object + overrideImage: + description: OverrideImage is a string that references + a container image to override the default one for + the Kubernetes Component + type: string + type: object + kubelet: + description: KubeletArgs Customized flag for kubelet process. + properties: + extraArgs: + description: 'ExtraArgs is a list of command line + arguments (format: flag=value) to pass to a Kubernetes + Component command.' + items: + type: string + type: array + extraEnv: + additionalProperties: + type: string + description: ExtraEnv is a map of environment variables + to pass on to a Kubernetes Component command. + type: object + extraMounts: + additionalProperties: + type: string + description: ExtraMounts is a map of volume mounts + to be added for the Kubernetes component StaticPod + type: object + overrideImage: + description: OverrideImage is a string that references + a container image to override the default one for + the Kubernetes Component + type: string + type: object + kubeletPath: + description: KubeletPath Override kubelet binary path. + type: string + loadBalancerPort: + description: 'LoadBalancerPort local port for supervisor + client load-balancer. If the supervisor and apiserver + are not colocated an additional port 1 less than this + port will also be used for the apiserver client load-balancer + (default: 6444).' + type: integer + nodeAnnotations: + additionalProperties: + type: string + description: 'NodeAnnotations are annotations that are + created on nodes post bootstrap phase. Unfortunately + it is not possible to apply annotations via kubelet + using current bootstrap configurations. Issue: https://github.com/kubernetes/kubernetes/issues/108046' + type: object + nodeLabels: + description: NodeLabels Registering and starting kubelet + with set of labels. + items: + type: string + type: array + nodeName: + description: NodeNamePrefix Prefix to the Node Name that + CAPI will generate. + type: string + nodeTaints: + description: NodeTaints Registering kubelet with set of + taints. + items: + type: string + type: array + ntp: + description: NTP specifies NTP configuration + properties: + enabled: + description: Enabled specifies whether NTP should + be enabled + type: boolean + servers: + description: Servers specifies which NTP servers to + use + items: + type: string + type: array + type: object + protectKernelDefaults: + description: ProtectKernelDefaults defines Kernel tuning + behavior. If true, error if kernel tunables are different + than kubelet defaults. if false, kernel tunable can + be different from kubelet defaults + type: boolean + resolvConf: + description: ResolvConf is a reference to a ConfigMap + containing resolv.conf content for the node. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object + instead of an entire object, this string should + contain a valid JSON/Go field access statement, + such as desiredState.manifest.containers[2]. For + example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container + that triggered the event) or if no container name + is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part + of an object. TODO: this design is not final and + this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this + reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + runtimeImage: + description: RuntimeImage override image to use for runtime + binaries (containerd, kubectl, crictl, etc). + type: string + snapshotter: + description: 'Snapshotter override default containerd + snapshotter (default: "overlayfs").' + type: string + systemDefaultRegistry: + description: SystemDefaultRegistry Private registry to + be used for all system images. + type: string + version: + description: Version specifies the rke2 version. This + field will be deprecated in newer versions of the API + and RKE2ControlPlaneSpec.Version will be used instead. + type: string + type: object + files: + description: Files specifies extra files to be passed to user_data + upon creation. + items: + description: File defines the input for generating write_files + in cloud-init. + properties: + content: + description: Content is the actual content of the file. + type: string + contentFrom: + description: ContentFrom is a referenced source of content + to populate the file. + properties: + secret: + description: SecretFileSource represents a secret + that should populate this file. + properties: + key: + description: Key is the key in the secret's + data map for this value. + type: string + name: + description: Name of the secret in the RKE2BootstrapConfig's + namespace to use. + type: string + required: + - key + - name + type: object + required: + - secret + type: object + encoding: + description: Encoding specifies the encoding of the + file contents. + enum: + - base64 + - gzip + - gzip+base64 + type: string + owner: + description: Owner specifies the ownership of the file, + e.g. "root:root". + type: string + path: + description: Path specifies the full path on disk where + to store the file. + type: string + permissions: + description: Permissions specifies the permissions to + assign to the file, e.g. "0640". + type: string + required: + - path + type: object + type: array + infrastructureRef: + description: InfrastructureRef is a required reference to + a custom resource offered by an infrastructure provider. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead + of an entire object, this string should contain a valid + JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that + triggered the event) or if no container name is specified + "spec.containers[2]" (container with index 2 in this + pod). This syntax is chosen only to have some well-defined + way of referencing a part of an object. TODO: this design + is not final and this field is subject to change in + the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + machineTemplate: + description: MachineTemplate contains information about how + machines should be shaped when creating or updating a control + plane. + properties: + infrastructureRef: + description: InfrastructureRef is a required reference + to a custom resource offered by an infrastructure provider. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object + instead of an entire object, this string should + contain a valid JSON/Go field access statement, + such as desiredState.manifest.containers[2]. For + example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container + that triggered the event) or if no container name + is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part + of an object. TODO: this design is not final and + this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this + reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + metadata: + description: 'Standard object''s metadata. More info: + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value + map stored with a resource that may be set by external + tools to store and retrieve arbitrary metadata. + They are not queryable and should be preserved when + modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations' + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can + be used to organize and categorize (scope and select) + objects. May match selectors of replication controllers + and services. More info: http://kubernetes.io/docs/user-guide/labels' + type: object + type: object + nodeDrainTimeout: + description: 'NodeDrainTimeout is the total amount of + time that the controller will spend on draining a controlplane + node The default value is 0, meaning that the node can + be drained without any time limitations. NOTE: NodeDrainTimeout + is different from `kubectl drain --timeout`' + type: string + required: + - infrastructureRef + type: object + manifestsConfigMapReference: + description: ManifestsConfigMapReference references a ConfigMap + which contains Kubernetes manifests to be deployed automatically + on the cluster Each data entry in the ConfigMap will be + will be copied to a folder on the control plane nodes that + RKE2 scans and uses to deploy manifests. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead + of an entire object, this string should contain a valid + JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that + triggered the event) or if no container name is specified + "spec.containers[2]" (container with index 2 in this + pod). This syntax is chosen only to have some well-defined + way of referencing a part of an object. TODO: this design + is not final and this field is subject to change in + the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + nodeDrainTimeout: + description: 'NodeDrainTimeout is the total amount of time + that the controller will spend on draining a controlplane + node The default value is 0, meaning that the node can be + drained without any time limitations. NOTE: NodeDrainTimeout + is different from `kubectl drain --timeout`' + type: string + postRKE2Commands: + description: PostRKE2Commands specifies extra commands to + run after rke2 setup runs. + items: + type: string + type: array + preRKE2Commands: + description: PreRKE2Commands specifies extra commands to run + before rke2 setup runs. + items: + type: string + type: array + privateRegistriesConfig: + description: PrivateRegistriesConfig defines the containerd + configuration for private registries and local registry + mirrors. + properties: + configs: + additionalProperties: + description: RegistryConfig contains configuration used + to communicate with the registry. + properties: + authSecret: + description: Auth is a reference to a Secret containing + information to authenticate to the registry. The + Secret must provite a username and a password + data entry. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an + object instead of an entire object, this string + should contain a valid JSON/Go field access + statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to + a container within a pod, this would take + on a value like: "spec.containers{name}" (where + "name" refers to the name of the container + that triggered the event) or if no container + name is specified "spec.containers[2]" (container + with index 2 in this pod). This syntax is + chosen only to have some well-defined way + of referencing a part of an object. TODO: + this design is not final and this field is + subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which + this reference is made, if any. More info: + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + tls: + description: TLS is a pair of CA/Cert/Key which + then are used when creating the transport that + communicates with the registry. + properties: + insecureSkipVerify: + description: InsecureSkipVerify may be set to + false to skip verifying the registry's certificate, + default is true. + type: boolean + tlsConfigSecret: + description: 'TLSConfigSecret is a reference + to a secret of type `kubernetes.io/tls` thich + has up to 3 entries: tls.crt, tls.key and + ca.crt which describe the TLS configuration + necessary to connect to the registry.' + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of + an object instead of an entire object, + this string should contain a valid JSON/Go + field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is + to a container within a pod, this would + take on a value like: "spec.containers{name}" + (where "name" refers to the name of the + container that triggered the event) or + if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). + This syntax is chosen only to have some + well-defined way of referencing a part + of an object. TODO: this design is not + final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to + which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: object + description: Configs are configs for each registry. The + key is the FDQN or IP of the registry. + type: object + mirrors: + additionalProperties: + description: Mirror contains the config related to the + registry mirror. + properties: + endpoint: + description: Endpoints are endpoints for a namespace. + CRI plugin will try the endpoints one by one until + a working one is found. The endpoint must be a + valid url with host specified. The scheme, host + and path from the endpoint URL will be used. + items: + type: string + type: array + rewrite: + additionalProperties: + type: string + description: Rewrites are repository rewrite rules + for a namespace. When fetching image resources + from an endpoint and a key matches the repository + via regular expression matching it will be replaced + with the corresponding value from the map in the + resource request. + type: object + type: object + description: Mirrors are namespace to mirror mapping for + all namespaces. + type: object + type: object + registrationAddress: + description: RegistrationAddress is an explicit address to + use when registering a node. This is required if the registration + type is "address". Its for scenarios where a load-balancer + or VIP is used. + type: string + registrationMethod: + default: internal-first + description: RegistrationMethod is the method to use for registering + nodes into the RKE2 cluster. + enum: + - internal-first + - internal-only-ips + - external-only-ips + - address + type: string + replicas: + description: Replicas is the number of replicas for the Control + Plane. + format: int32 + type: integer + rolloutStrategy: + default: + rollingUpdate: + maxSurge: 1 + type: RollingUpdate + description: The RolloutStrategy to use to replace control + plane machines with new ones. + properties: + rollingUpdate: + description: Rolling update config params. Present only + if RolloutStrategyType = RollingUpdate. + properties: + maxSurge: + anyOf: + - type: integer + - type: string + description: 'The maximum number of control planes + that can be scheduled above or under the desired + number of control planes. Value can be an absolute + number 1 or 0. Defaults to 1. Example: when this + is set to 1, the control plane can be scaled up + immediately when the rolling update starts.' + x-kubernetes-int-or-string: true + type: object + type: + description: Type of rollout. Currently the only supported + strategy is "RollingUpdate". Default is RollingUpdate. + type: string + type: object + serverConfig: + description: ServerConfig specifies configuration for the + agent nodes. + properties: + advertiseAddress: + description: 'AdvertiseAddress IP address that apiserver + uses to advertise to members of the cluster (default: + node-external-ip/node-ip).' + type: string + auditPolicySecret: + description: AuditPolicySecret path to the file that defines + the audit policy configuration. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object + instead of an entire object, this string should + contain a valid JSON/Go field access statement, + such as desiredState.manifest.containers[2]. For + example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container + that triggered the event) or if no container name + is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part + of an object. TODO: this design is not final and + this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this + reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + bindAddress: + description: 'BindAddress describes the rke2 bind address + (default: 0.0.0.0).' + type: string + cloudControllerManager: + description: CloudControllerManager defines optional custom + configuration of the Cloud Controller Manager. + properties: + extraArgs: + description: 'ExtraArgs is a list of command line + arguments (format: flag=value) to pass to a Kubernetes + Component command.' + items: + type: string + type: array + extraEnv: + additionalProperties: + type: string + description: ExtraEnv is a map of environment variables + to pass on to a Kubernetes Component command. + type: object + extraMounts: + additionalProperties: + type: string + description: ExtraMounts is a map of volume mounts + to be added for the Kubernetes component StaticPod + type: object + overrideImage: + description: OverrideImage is a string that references + a container image to override the default one for + the Kubernetes Component + type: string + type: object + cloudProviderConfigMap: + description: CloudProviderConfigMap is a reference to + a ConfigMap containing Cloud provider configuration. + The config map must contain a key named cloud-config. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object + instead of an entire object, this string should + contain a valid JSON/Go field access statement, + such as desiredState.manifest.containers[2]. For + example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container + that triggered the event) or if no container name + is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part + of an object. TODO: this design is not final and + this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this + reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + cloudProviderName: + description: CloudProviderName cloud provider name. + type: string + clusterDNS: + description: 'ClusterDNS is the cluster IP for CoreDNS + service. Should be in your service-cidr range (default: + 10.43.0.10).' + type: string + clusterDomain: + description: 'ClusterDomain is the cluster domain name + (default: "cluster.local").' + type: string + cni: + description: 'CNI describes the CNI Plugins to deploy, + one of none, calico, canal, cilium; optionally with + multus as the first value to enable the multus meta-plugin + (default: canal).' + enum: + - none + - calico + - canal + - cilium + type: string + cniMultusEnable: + description: 'CNIMultusEnable enables multus as the first + CNI plugin (default: false). This option will automatically + make Multus a primary CNI, and the value, if specified + in the CNI field, as a secondary CNI plugin.' + type: boolean + disableComponents: + description: DisableComponents lists Kubernetes components + and RKE2 plugin components that will be disabled. + properties: + kubernetesComponents: + description: KubernetesComponents is a list of Kubernetes + components to disable. + items: + description: 'DisabledKubernetesComponent is an + enum field that can take one of the following + values: scheduler, kubeProxy or cloudController.' + enum: + - scheduler + - kubeProxy + - cloudController + type: string + type: array + pluginComponents: + description: PluginComponents is a list of PluginComponents + to disable. + items: + description: DisabledPluginComponent selects a plugin + Components to be disabled. + enum: + - rke2-coredns + - rke2-ingress-nginx + - rke2-metrics-server + type: string + type: array + type: object + etcd: + description: Etcd defines optional custom configuration + of ETCD. + properties: + backupConfig: + description: 'BackupConfig defines how RKE2 will snapshot + ETCD: target storage, schedule, etc.' + properties: + directory: + description: Directory to save db snapshots. + type: string + disableAutomaticSnapshots: + description: DisableAutomaticSnapshots defines + the policy for ETCD snapshots. true means automatic + snapshots will be scheduled, false means automatic + snapshots will not be scheduled. + type: boolean + retention: + description: 'Retention Number of snapshots to + retain Default: 5 (default: 5).' + type: string + s3: + description: S3 Enable backup to an S3-compatible + Object Store. + properties: + bucket: + description: Bucket S3 bucket name. + type: string + endpoint: + description: 'Endpoint S3 endpoint url (default: + "s3.amazonaws.com").' + type: string + endpointCAsecret: + description: EndpointCA references the Secret + that contains a custom CA that should be + trusted to connect to S3 endpoint. The secret + must contain a key named "ca.pem" that contains + the CA certificate. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece + of an object instead of an entire object, + this string should contain a valid JSON/Go + field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference + is to a container within a pod, this + would take on a value like: "spec.containers{name}" + (where "name" refers to the name of + the container that triggered the event) + or if no container name is specified + "spec.containers[2]" (container with + index 2 in this pod). This syntax is + chosen only to have some well-defined + way of referencing a part of an object. + TODO: this design is not final and this + field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion + to which this reference is made, if + any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + enforceSslVerify: + description: EnforceSSLVerify may be set to + false to skip verifying the registry's certificate, + default is true. + type: boolean + folder: + description: Folder S3 folder. + type: string + region: + description: 'Region S3 region / bucket location + (optional) (default: "us-east-1").' + type: string + s3CredentialSecret: + description: 'S3CredentialSecret is a reference + to a Secret containing the Access Key and + Secret Key necessary to access the target + S3 Bucket. The Secret must contain the following + keys: "aws_access_key_id" and "aws_secret_access_key".' + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece + of an object instead of an entire object, + this string should contain a valid JSON/Go + field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference + is to a container within a pod, this + would take on a value like: "spec.containers{name}" + (where "name" refers to the name of + the container that triggered the event) + or if no container name is specified + "spec.containers[2]" (container with + index 2 in this pod). This syntax is + chosen only to have some well-defined + way of referencing a part of an object. + TODO: this design is not final and this + field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion + to which this reference is made, if + any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - endpoint + - s3CredentialSecret + type: object + scheduleCron: + description: 'ScheduleCron Snapshot interval time + in cron spec. eg. every 5 hours ''* */5 * * + *'' (default: "0 */12 * * *").' + type: string + snapshotName: + description: 'SnapshotName Set the base name of + etcd snapshots. Default: etcd-snapshot- + (default: "etcd-snapshot").' + type: string + type: object + customConfig: + description: CustomConfig defines the custom settings + for ETCD. + properties: + extraArgs: + description: 'ExtraArgs is a list of command line + arguments (format: flag=value) to pass to a + Kubernetes Component command.' + items: + type: string + type: array + extraEnv: + additionalProperties: + type: string + description: ExtraEnv is a map of environment + variables to pass on to a Kubernetes Component + command. + type: object + extraMounts: + additionalProperties: + type: string + description: ExtraMounts is a map of volume mounts + to be added for the Kubernetes component StaticPod + type: object + overrideImage: + description: OverrideImage is a string that references + a container image to override the default one + for the Kubernetes Component + type: string + type: object + exposeMetrics: + description: ExposeEtcdMetrics defines the policy + for ETCD Metrics exposure. if value is true, ETCD + metrics will be exposed if value is false, ETCD + metrics will NOT be exposed + type: boolean + type: object + kubeAPIServer: + description: KubeAPIServer defines optional custom configuration + of the Kube API Server. + properties: + extraArgs: + description: 'ExtraArgs is a list of command line + arguments (format: flag=value) to pass to a Kubernetes + Component command.' + items: + type: string + type: array + extraEnv: + additionalProperties: + type: string + description: ExtraEnv is a map of environment variables + to pass on to a Kubernetes Component command. + type: object + extraMounts: + additionalProperties: + type: string + description: ExtraMounts is a map of volume mounts + to be added for the Kubernetes component StaticPod + type: object + overrideImage: + description: OverrideImage is a string that references + a container image to override the default one for + the Kubernetes Component + type: string + type: object + kubeControllerManager: + description: KubeControllerManager defines optional custom + configuration of the Kube Controller Manager. + properties: + extraArgs: + description: 'ExtraArgs is a list of command line + arguments (format: flag=value) to pass to a Kubernetes + Component command.' + items: + type: string + type: array + extraEnv: + additionalProperties: + type: string + description: ExtraEnv is a map of environment variables + to pass on to a Kubernetes Component command. + type: object + extraMounts: + additionalProperties: + type: string + description: ExtraMounts is a map of volume mounts + to be added for the Kubernetes component StaticPod + type: object + overrideImage: + description: OverrideImage is a string that references + a container image to override the default one for + the Kubernetes Component + type: string + type: object + kubeScheduler: + description: KubeScheduler defines optional custom configuration + of the Kube Scheduler. + properties: + extraArgs: + description: 'ExtraArgs is a list of command line + arguments (format: flag=value) to pass to a Kubernetes + Component command.' + items: + type: string + type: array + extraEnv: + additionalProperties: + type: string + description: ExtraEnv is a map of environment variables + to pass on to a Kubernetes Component command. + type: object + extraMounts: + additionalProperties: + type: string + description: ExtraMounts is a map of volume mounts + to be added for the Kubernetes component StaticPod + type: object + overrideImage: + description: OverrideImage is a string that references + a container image to override the default one for + the Kubernetes Component + type: string + type: object + pauseImage: + description: PauseImage Override image to use for pause. + type: string + serviceNodePortRange: + description: 'ServiceNodePortRange is the port range to + reserve for services with NodePort visibility (default: + "30000-32767").' + type: string + tlsSan: + description: TLSSan Add additional hostname or IP as a + Subject Alternative Name in the TLS cert. + items: + type: string + type: array + type: object + version: + description: Version defines the desired Kubernetes version. + This is only a placeholder for now, and the RKE2ConfigSpec.AgentConfig.Version + field should be used instead. In future iterations, this + field overrides the RKE2 Version specificied in RKE2ConfigSpec.AgentConfig.Version + which will be deprecated in newer versions of the API. + type: string + required: + - infrastructureRef + type: object + required: + - spec + type: object + required: + - template type: object status: - description: RKE2ControlPlaneTemplateStatus defines the observed state - of RKE2ControlPlaneTemplate. + description: Status is the current state of the control plane. + properties: + availableServerIPs: + description: AvailableServerIPs is a list of the Control Plane IP + adds that can be used to register further nodes. + items: + type: string + type: array + conditions: + description: Conditions defines current service state of the RKE2Config. + items: + description: Condition defines an observation of a Cluster API resource + operational state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. This field may be empty. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. The specific API may choose whether or not this + field is considered a guaranteed API. This field may not be + empty. + type: string + severity: + description: Severity provides an explicit classification of + Reason code, so the users or machines can immediately understand + the current situation and act accordingly. The Severity field + MUST be set only when Status=False. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + dataSecretName: + description: DataSecretName is the name of the secret that stores + the bootstrap data script. + type: string + failureMessage: + description: FailureMessage will be set on non-retryable errors. + type: string + failureReason: + description: FailureReason will be set on non-retryable errors. + type: string + initialized: + description: Initialized indicates the target cluster has completed + initialization. + type: boolean + observedGeneration: + description: ObservedGeneration is the latest generation observed + by the controller. + format: int64 + type: integer + ready: + description: Ready indicates the BootstrapData field is ready to be + consumed. + type: boolean + readyReplicas: + description: ReadyReplicas is the number of replicas current attached + to this ControlPlane Resource and that have Ready Status. + format: int32 + type: integer + replicas: + description: Replicas is the number of replicas current attached to + this ControlPlane Resource. + format: int32 + type: integer + unavailableReplicas: + description: UnavailableReplicas is the number of replicas current + attached to this ControlPlane Resource and that are up-to-date with + Control Plane config. + format: int32 + type: integer + updatedReplicas: + description: UpdatedReplicas is the number of replicas current attached + to this ControlPlane Resource and that are up-to-date with Control + Plane config. + format: int32 + type: integer + version: + description: Version represents the minimum Kubernetes version for + the control plane machines in the cluster. + type: string type: object type: object served: true storage: true - subresources: - status: {} diff --git a/controlplane/config/crd/kustomization.yaml b/controlplane/config/crd/kustomization.yaml index 240b1240..d6d796bc 100644 --- a/controlplane/config/crd/kustomization.yaml +++ b/controlplane/config/crd/kustomization.yaml @@ -1,5 +1,5 @@ commonLabels: - cluster.x-k8s.io/v1beta1: v1alpha1 + cluster.x-k8s.io/v1beta1: v1alpha1_v1beta1 # This kustomization.yaml is not intended to be run by itself, # since it depends on service name and namespace that are out of this kustomize package. diff --git a/controlplane/config/crd/patches/webhook_in_rke2controlplanes.yaml b/controlplane/config/crd/patches/webhook_in_rke2controlplanes.yaml index 9b76dcf2..9e56007c 100644 --- a/controlplane/config/crd/patches/webhook_in_rke2controlplanes.yaml +++ b/controlplane/config/crd/patches/webhook_in_rke2controlplanes.yaml @@ -17,3 +17,4 @@ spec: path: /convert conversionReviewVersions: - v1 + - v1beta1 diff --git a/controlplane/config/crd/patches/webhook_in_rke2controlplanetemplates.yaml b/controlplane/config/crd/patches/webhook_in_rke2controlplanetemplates.yaml index 109e7f19..1c5a90eb 100644 --- a/controlplane/config/crd/patches/webhook_in_rke2controlplanetemplates.yaml +++ b/controlplane/config/crd/patches/webhook_in_rke2controlplanetemplates.yaml @@ -17,3 +17,4 @@ spec: path: /convert conversionReviewVersions: - v1 + - v1beta1 diff --git a/controlplane/internal/controllers/rke2controlplane_controller.go b/controlplane/internal/controllers/rke2controlplane_controller.go index 88da3ec3..c00626e6 100644 --- a/controlplane/internal/controllers/rke2controlplane_controller.go +++ b/controlplane/internal/controllers/rke2controlplane_controller.go @@ -537,6 +537,11 @@ func (r *RKE2ControlPlaneReconciler) reconcileDelete(ctx context.Context, // If no control plane machines remain, remove the finalizer if len(ownedMachines) == 0 { + // If the legacy finalizer is present, remove it. + if controllerutil.ContainsFinalizer(rcp, controlplanev1.RKE2ControlPlaneLegacyFinalizer) { + controllerutil.RemoveFinalizer(rcp, controlplanev1.RKE2ControlPlaneLegacyFinalizer) + } + controllerutil.RemoveFinalizer(rcp, controlplanev1.RKE2ControlPlaneFinalizer) return ctrl.Result{}, nil diff --git a/samples/docker/clusterclass/clusterclass-quick-start.yaml b/samples/docker/clusterclass/clusterclass-quick-start.yaml new file mode 100644 index 00000000..004a4cbe --- /dev/null +++ b/samples/docker/clusterclass/clusterclass-quick-start.yaml @@ -0,0 +1,233 @@ +apiVersion: cluster.x-k8s.io/v1beta1 +kind: ClusterClass +metadata: + name: rke2-class +spec: + controlPlane: + ref: + apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + kind: RKE2ControlPlaneTemplate + name: rke2-class-control-plane + machineInfrastructure: + ref: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: DockerMachineTemplate + name: rke2-class-control-plane + infrastructure: + ref: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: DockerClusterTemplate + name: rke2-class-cluster + workers: + machineDeployments: + - class: default-worker + template: + bootstrap: + ref: + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: RKE2ConfigTemplate + name: rke2-class-default-worker-bootstraptemplate + infrastructure: + ref: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: DockerMachineTemplate + name: rke2-class-default-worker-machinetemplate + machinePools: + - class: default-worker + template: + bootstrap: + ref: + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: RKE2ConfigTemplate + name: rke2-class-default-worker-bootstraptemplate + infrastructure: + ref: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: DockerMachinePoolTemplate + name: rke2-class-default-worker-machinepooltemplate +--- +apiVersion: v1 +data: + value: |- + # generated by kind + global + log /dev/log local0 + log /dev/log local1 notice + daemon + # limit memory usage to approximately 18 MB + # (see https://github.com/kubernetes-sigs/kind/pull/3115) + maxconn 100000 + + resolvers docker + nameserver dns 127.0.0.11:53 + + defaults + log global + mode tcp + option dontlognull + # TODO: tune these + timeout connect 5000 + timeout client 50000 + timeout server 50000 + # allow to boot despite dns don't resolve backends + default-server init-addr none + + frontend stats + bind *:8404 + stats enable + stats uri / + stats refresh 10s + + frontend control-plane + bind *:{{ .FrontendControlPlanePort }} + {{ if .IPv6 -}} + bind :::{{ .FrontendControlPlanePort }}; + {{- end }} + default_backend kube-apiservers + + backend kube-apiservers + option httpchk GET /healthz + http-check expect status 401 + # TODO: we should be verifying (!) + {{range $server, $address := .BackendServers}} + server {{ $server }} {{ JoinHostPort $address $.BackendControlPlanePort }} check check-ssl verify none resolvers docker resolve-prefer {{ if $.IPv6 -}} ipv6 {{- else -}} ipv4 {{- end }} + {{- end}} + + frontend rke2-join + bind *:9345 + {{ if .IPv6 -}} + bind :::9345; + {{- end }} + default_backend rke2-servers + + backend rke2-servers + option httpchk GET /v1-rke2/readyz + http-check expect status 403 + {{range $server, $address := .BackendServers}} + server {{ $server }} {{ $address }}:9345 check check-ssl verify none + {{- end}} +kind: ConfigMap +metadata: + name: rke2-class-lb-config + namespace: default +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: DockerClusterTemplate +metadata: + name: rke2-class-cluster +spec: + template: + spec: + loadBalancer: + customHAProxyConfigTemplateRef: + name: rke2-class-lb-config +--- +kind: RKE2ControlPlaneTemplate +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +metadata: + name: rke2-class-control-plane +spec: + template: + spec: + agentConfig: + version: ${KUBERNETES_VERSION}+rke2r1 + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: DockerMachineTemplate + name: rke2-class-control-plane + serverConfig: + cni: calico + disableComponents: + kubernetesComponents: [ "cloudController"] + nodeDrainTimeout: 2m + rolloutStrategy: + type: "RollingUpdate" + rollingUpdate: + maxSurge: 1 + registrationMethod: "address" + registrationAddress: "${KIND_IP}" + preRKE2Commands: + - mkdir -p /var/lib/rancher/rke2/server/manifests/ && ctr images pull ghcr.io/kube-vip/kube-vip:v0.6.0 + && ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:v0.6.0 vip /kube-vip + manifest daemonset --arp --interface $(ip -4 -j route list default | jq -r + .[0].dev) --address "${KIND_IP}" --controlplane --leaderElection --taint + --services --inCluster | tee /var/lib/rancher/rke2/server/manifests/kube-vip.yaml + files: + - path: /var/lib/rancher/rke2/server/manifests/kube-vip-rbac.yaml + content: | + apiVersion: v1 + kind: ServiceAccount + metadata: + name: kube-vip + namespace: kube-system + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + annotations: + rbac.authorization.kubernetes.io/autoupdate: "true" + name: system:kube-vip-role + rules: + - apiGroups: [""] + resources: ["services", "services/status", "nodes", "endpoints"] + verbs: ["list","get","watch", "update"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["list", "get", "watch", "update", "create"] + --- + kind: ClusterRoleBinding + apiVersion: rbac.authorization.k8s.io/v1 + metadata: + name: system:kube-vip-binding + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:kube-vip-role + subjects: + - kind: ServiceAccount + name: kube-vip + namespace: kube-system + owner: root:root +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: DockerMachineTemplate +metadata: + name: rke2-class-control-plane +spec: + template: + spec: + extraMounts: + - containerPath: "/var/run/docker.sock" + hostPath: "/var/run/docker.sock" + bootstrapTimeout: 10m +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: DockerMachineTemplate +metadata: + name: rke2-class-default-worker-machinetemplate +spec: + template: + spec: + extraMounts: + - containerPath: "/var/run/docker.sock" + hostPath: "/var/run/docker.sock" + bootstrapTimeout: 10m +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: DockerMachinePoolTemplate +metadata: + name: rke2-class-default-worker-machinepooltemplate +spec: + template: + spec: + template: {} +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +kind: RKE2ConfigTemplate +metadata: + name: rke2-class-default-worker-bootstraptemplate +spec: + template: + spec: + agentConfig: + version: ${KUBERNETES_VERSION}+rke2r1 diff --git a/samples/docker/clusterclass/rke2-sample.yaml b/samples/docker/clusterclass/rke2-sample.yaml new file mode 100644 index 00000000..d4c1930d --- /dev/null +++ b/samples/docker/clusterclass/rke2-sample.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: "${CLUSTER_NAME}" + namespace: default +spec: + clusterNetwork: + pods: + cidrBlocks: + - 10.45.0.0/16 + serviceDomain: cluster.local + services: + cidrBlocks: + - 10.46.0.0/16 + topology: + class: rke2-class + controlPlane: + metadata: {} + replicas: ${CABPR_CP_REPLICAS} + version: ${KUBERNETES_VERSION}+rke2r1 + workers: + machineDeployments: + - class: default-worker + name: md-0 + replicas: ${CABPR_WK_REPLICAS} +