Repository: rancher-sandbox/cluster-api-provider-rke2
Pull request #51 (2022-11-30 12:37:48)

Generate config for agent and server
<!--
Label the PR with the kind of change this for:

kind/feature
kind/bug
kind/documentation
kind/regression
kind/*
-->

**What this PR does / why we need it**:
Generate config for agent and server.

<!-- Enter a description of the change and why this change is needed -->

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #

**Special notes for your reviewer**:

**Checklist**:
<!-- Put an "X" character inside the brackets of each completed task. Some may be optional depending on the PR in which case these can be deleted -->

- [ ] squashed commits into logical changes
- [ ] includes documentation
- [ ] adds unit tests
- [ ] adds or updates e2e tests



Patch:
diff --git a/bootstrap/api/v1alpha1/condition_consts.go b/bootstrap/api/v1alpha1/condition_consts.go
index 08de291..ed02c9f 100644
--- a/bootstrap/api/v1alpha1/condition_consts.go
+++ b/bootstrap/api/v1alpha1/condition_consts.go
@@ -1,19 +1,3 @@
-/*
-Copyright 2022 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 v1alpha1
 
 import (
diff --git a/bootstrap/api/v1alpha1/rke2config_types.go b/bootstrap/api/v1alpha1/rke2config_types.go
index b81be5d..eed9737 100644
--- a/bootstrap/api/v1alpha1/rke2config_types.go
+++ b/bootstrap/api/v1alpha1/rke2config_types.go
@@ -68,9 +68,10 @@ type RKE2AgentConfig struct {
 	NTP *NTP `json:"ntp,omitempty"`
 
 	// ImageCredentialProviderConfigMap is a reference to the ConfigMap that contains credential provider plugin config
-	// The configMap should contain a YAML file content + a Path to the Binaries for Credential Provider.
+	// 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.
 	//+optional
-	ImageCredentialProviderConfigMap corev1.ObjectReference `json:"imageCredentialProviderConfigMap,omitempty"`
+	ImageCredentialProviderConfigMap *corev1.ObjectReference `json:"imageCredentialProviderConfigMap,omitempty"`
 
 	// TODO: Remove ContainerRuntimeEndpoint since this feature will probably not be offered by CAPI Bootstrap provider?
 
@@ -78,6 +79,10 @@ type RKE2AgentConfig struct {
 	//+optional
 	ContainerRuntimeEndpoint string `json:"containerRuntimeEndpoint,omitempty"`
 
+	// Snapshotter override default containerd snapshotter (default: "overlayfs").
+	//+optional
+	Snapshotter string `json:"snapshotter,omitempty"`
+
 	// TODO: Find a way to handle IP addresses that should be advertised but that RKE2 cannot find on the host (Example: Elastic IPs on Cloud Providers).
 
 	// NodeIp IPv4/IPv6 addresses to advertise for node.
@@ -95,7 +100,7 @@ type RKE2AgentConfig struct {
 
 	// ResolvConf is a reference to a ConfigMap containing resolv.conf content for the node.
 	//+optional
-	ResolvConf corev1.ObjectReference `json:"resolvConf,omitempty"`
+	ResolvConf *corev1.ObjectReference `json:"resolvConf,omitempty"`
 
 	// 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
@@ -118,11 +123,19 @@ type RKE2AgentConfig struct {
 
 	// KubeletArgs Customized flag for kubelet process.
 	//+optional
-	Kubelet ComponentConfig `json:"kubelet,omitempty"`
+	Kubelet *ComponentConfig `json:"kubelet,omitempty"`
 
 	// KubeProxyArgs Customized flag for kube-proxy process.
 	//+optional
-	KubeProxy ComponentConfig `json:"kubeProxy,omitempty"`
+	KubeProxy *ComponentConfig `json:"kubeProxy,omitempty"`
+
+	// RuntimeImage override image to use for runtime binaries (containerd, kubectl, crictl, etc).
+	//+optional
+	RuntimeImage string `json:"runtimeImage,omitempty"`
+
+	// 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).
+	//+optional
+	LoadBalancerPort int `json:"loadBalancerPort,omitempty"`
 
 	// Version specifies the rke2 version.
 	//+optional
@@ -321,7 +334,7 @@ type ComponentConfig struct {
 
 	// ExtraArgs is a map of command line arguments to pass to a Kubernetes Component command.
 	//+optional
-	ExtraArgs map[string]string `json:"extraArgs,omitempty"`
+	ExtraArgs []string `json:"extraArgs,omitempty"`
 
 	// ExtraMounts is a map of volume mounts to be added for the Kubernetes component StaticPod
 	//+optional
diff --git a/bootstrap/api/v1alpha1/zz_generated.deepcopy.go b/bootstrap/api/v1alpha1/zz_generated.deepcopy.go
index 9086735..27ead47 100644
--- a/bootstrap/api/v1alpha1/zz_generated.deepcopy.go
+++ b/bootstrap/api/v1alpha1/zz_generated.deepcopy.go
@@ -22,6 +22,7 @@ limitations under the License.
 package v1alpha1
 
 import (
+	"k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"sigs.k8s.io/cluster-api/api/v1beta1"
 )
@@ -38,10 +39,8 @@ func (in *ComponentConfig) DeepCopyInto(out *ComponentConfig) {
 	}
 	if in.ExtraArgs != nil {
 		in, out := &in.ExtraArgs, &out.ExtraArgs
-		*out = make(map[string]string, len(*in))
-		for key, val := range *in {
-			(*out)[key] = val
-		}
+		*out = make([]string, len(*in))
+		copy(*out, *in)
 	}
 	if in.ExtraMounts != nil {
 		in, out := &in.ExtraMounts, &out.ExtraMounts
@@ -168,10 +167,26 @@ func (in *RKE2AgentConfig) DeepCopyInto(out *RKE2AgentConfig) {
 		*out = new(NTP)
 		(*in).DeepCopyInto(*out)
 	}
-	out.ImageCredentialProviderConfigMap = in.ImageCredentialProviderConfigMap
-	out.ResolvConf = in.ResolvConf
-	in.Kubelet.DeepCopyInto(&out.Kubelet)
-	in.KubeProxy.DeepCopyInto(&out.KubeProxy)
+	if in.ImageCredentialProviderConfigMap != nil {
+		in, out := &in.ImageCredentialProviderConfigMap, &out.ImageCredentialProviderConfigMap
+		*out = new(v1.ObjectReference)
+		**out = **in
+	}
+	if in.ResolvConf != nil {
+		in, out := &in.ResolvConf, &out.ResolvConf
+		*out = new(v1.ObjectReference)
+		**out = **in
+	}
+	if in.Kubelet != nil {
+		in, out := &in.Kubelet, &out.Kubelet
+		*out = new(ComponentConfig)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.KubeProxy != nil {
+		in, out := &in.KubeProxy, &out.KubeProxy
+		*out = new(ComponentConfig)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RKE2AgentConfig.
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 69a85b1..2953604 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
@@ -60,8 +60,9 @@ spec:
                   imageCredentialProviderConfigMap:
                     description: ImageCredentialProviderConfigMap is a reference to
                       the ConfigMap that contains credential provider plugin config
-                      The configMap should contain a YAML file content + a Path to
-                      the Binaries for Credential Provider.
+                      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.
@@ -101,11 +102,11 @@ spec:
                     description: KubeProxyArgs Customized flag for kube-proxy process.
                     properties:
                       extraArgs:
-                        additionalProperties:
-                          type: string
                         description: ExtraArgs is a map of command line arguments
                           to pass to a Kubernetes Component command.
-                        type: object
+                        items:
+                          type: string
+                        type: array
                       extraEnv:
                         additionalProperties:
                           type: string
@@ -127,11 +128,11 @@ spec:
                     description: KubeletArgs Customized flag for kubelet process.
                     properties:
                       extraArgs:
-                        additionalProperties:
-                          type: string
                         description: ExtraArgs is a map of command line arguments
                           to pass to a Kubernetes Component command.
-                        type: object
+                        items:
+                          type: string
+                        type: array
                       extraEnv:
                         additionalProperties:
                           type: string
@@ -152,6 +153,12 @@ spec:
                   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
                   nodeLabels:
                     description: NodeLabels  Registering and starting kubelet with
                       set of labels.
@@ -223,6 +230,14 @@ spec:
                         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.
diff --git a/controlplane/api/v1alpha1/condition_consts.go b/controlplane/api/v1alpha1/condition_consts.go
index 14fda48..3f7ea94 100644
--- a/controlplane/api/v1alpha1/condition_consts.go
+++ b/controlplane/api/v1alpha1/condition_consts.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 SUSE.
+Copyright 2020 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/controlplane/api/v1alpha1/rke2controlplane_types.go b/controlplane/api/v1alpha1/rke2controlplane_types.go
index e9f5fc1..ca0172f 100644
--- a/controlplane/api/v1alpha1/rke2controlplane_types.go
+++ b/controlplane/api/v1alpha1/rke2controlplane_types.go
@@ -36,7 +36,7 @@ type RKE2ControlPlaneSpec struct {
 	// Replicas is the number of replicas for the Control Plane
 	Replicas *int32 `json:"replicas,omitempty"`
 
-	// bootstrapv1.RKE2AgentConfig references fields from the Agent Configuration in the Bootstrap Provider because an RKE2 Server node also has an agent
+	// RKE2AgentConfig references fields from the Agent Configuration in the Bootstrap Provider because an RKE2 Server node also has an agent
 	bootstrapv1.RKE2AgentConfig `json:",inline"`
 
 	// ServerConfig specifies configuration for the agent nodes.
@@ -60,6 +60,10 @@ type RKE2ControlPlaneSpec struct {
 }
 
 type RKE2ServerConfig struct {
+	// AuditPolicySecret path to the file that defines the audit policy configuration.
+	//+optional
+	AuditPolicySecret *corev1.ObjectReference `json:"auditPolicySecret,omitempty"`
+
 	// BindAddress describes the rke2 bind address (default: 0.0.0.0).
 	//+optional
 	BindAddress string `json:"bindAddress,omitempty"`
@@ -88,10 +92,6 @@ type RKE2ServerConfig struct {
 	//+optional
 	DisableComponents DisableComponents `json:"disableComponents,omitempty"`
 
-	// 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).
-	//+optional
-	LoadBalancerPort int `json:"loadBalancerPort,omitempty"`
-
 	// 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).
 	// +kubebuilder:validation:Enum=none;calico;canal;cilium
 	//+optional
@@ -101,41 +101,33 @@ type RKE2ServerConfig struct {
 	//+optional
 	PauseImage string `json:"pauseImage,omitempty"`
 
-	// RuntimeImage Override image to use for runtime binaries (containerd, kubectl, crictl, etc).
-	//+optional
-	RuntimeImage string `json:"runtimeImage,omitempty"`
-
-	// CloudProviderName  Cloud provider name.
-	//+optional
-	CloudProviderName string `json:"cloudProviderName,omitempty"`
-
-	// CloudProviderConfigMap  is a reference to a ConfigMap containing Cloud provider configuration.
-	//+optional
-	CloudProviderConfigMap corev1.ObjectReference `json:"cloudProviderConfigMap,omitempty"`
-
-	// AuditPolicySecret Path to the file that defines the audit policy configuration.
-	//+optional
-	AuditPolicySecret corev1.ObjectReference `json:"auditPolicySecret,omitempty"`
-
 	// Etcd defines optional custom configuration of ETCD.
 	//+optional
 	Etcd EtcdConfig `json:"etcd,omitempty"`
 
 	// KubeAPIServer defines optional custom configuration of the Kube API Server.
 	//+optional
-	KubeAPIServer bootstrapv1.ComponentConfig `json:"kubeAPIServer,omitempty"`
+	KubeAPIServer *bootstrapv1.ComponentConfig `json:"kubeAPIServer,omitempty"`
 
 	// KubeControllerManager defines optional custom configuration of the Kube Controller Manager.
 	//+optional
-	KubeControllerManager bootstrapv1.ComponentConfig `json:"kubeControllerManager,omitempty"`
+	KubeControllerManager *bootstrapv1.ComponentConfig `json:"kubeControllerManager,omitempty"`
 
 	// KubeScheduler defines optional custom configuration of the Kube Scheduler.
 	//+optional
-	KubeScheduler bootstrapv1.ComponentConfig `json:"kubeScheduler,omitempty"`
+	KubeScheduler *bootstrapv1.ComponentConfig `json:"kubeScheduler,omitempty"`
 
 	// CloudControllerManager defines optional custom configuration of the Cloud Controller Manager.
 	//+optional
-	CloudControllerManager bootstrapv1.ComponentConfig `json:"cloudControllerManager,omitempty"`
+	CloudControllerManager *bootstrapv1.ComponentConfig `json:"cloudControllerManager,omitempty"`
+
+	// CloudProviderName cloud provider name.
+	//+optional
+	CloudProviderName string `json:"cloudProviderName,omitempty"`
+	// CloudProviderConfigMap is a reference to a ConfigMap containing Cloud provider configuration.
+	// The config map must contain a key named cloud-config.
+	//+optional
+	CloudProviderConfigMap *corev1.ObjectReference `json:"cloudProviderConfigMap,omitempty"`
 }
 
 // RKE2ControlPlaneStatus defines the observed state of RKE2ControlPlane
@@ -194,14 +186,14 @@ type EtcdConfig struct {
 	// if value is true, ETCD metrics will be exposed
 	// if value is false, ETCD metrics will NOT be exposed
 	// +optional
-	ExposeEtcdMetrics bool `json:"exposeEtcdMetrics,omitempty"`
+	ExposeMetrics bool `json:"exposeMetrics,omitempty"`
 
 	// BackupConfig defines how RKE2 will snapshot ETCD: target storage, schedule, etc.
 	//+optional
 	BackupConfig EtcdBackupConfig `json:"backupConfig,omitempty"`
 
 	// CustomConfig defines the custom settings for ETCD.
-	CustomConfig bootstrapv1.ComponentConfig `json:"customConfig,omitempty"`
+	CustomConfig *bootstrapv1.ComponentConfig `json:"customConfig,omitempty"`
 }
 
 // EtcdBackupConfig describes the backup configuration for ETCD.
@@ -228,7 +220,7 @@ type EtcdBackupConfig struct {
 
 	// S3 Enable backup to an S3-compatible Object Store.
 	//+optional
-	S3 EtcdS3 `json:"s3,omitempty"`
+	S3 *EtcdS3 `json:"s3,omitempty"`
 }
 
 type EtcdS3 struct {
@@ -236,15 +228,17 @@ type EtcdS3 struct {
 	Endpoint string `json:"endpoint"`
 
 	// 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.
 	//+optional
-	EndpointCA corev1.ObjectReference `json:"endpointCA,omitempty"`
+	EndpointCASecret *corev1.ObjectReference `json:"endpointCAsecret,omitempty"`
 
 	// EnforceSSLVerify may be set to false to skip verifying the registry's certificate, default is true.
 	//+optional
 	EnforceSSLVerify bool `json:"enforceSslVerify,omitempty"`
 
 	// S3CredentialSecret is a reference to a Secret containing the Access Key and Secret Key necessary to access the target S3 Bucket.
-	S3CredentialSecret corev1.ObjectReference `json:"S3CredentialSecret"`
+	// The Secret must contain the following keys: "aws_access_key_id" and "aws_secret_access_key".
+	S3CredentialSecret corev1.ObjectReference `json:"s3CredentialSecret"`
 
 	// Bucket S3 bucket name.
 	//+optional
diff --git a/controlplane/api/v1alpha1/zz_generated.deepcopy.go b/controlplane/api/v1alpha1/zz_generated.deepcopy.go
index b51bbf6..caa1894 100644
--- a/controlplane/api/v1alpha1/zz_generated.deepcopy.go
+++ b/controlplane/api/v1alpha1/zz_generated.deepcopy.go
@@ -22,6 +22,8 @@ limitations under the License.
 package v1alpha1
 
 import (
+	apiv1alpha1 "github.com/rancher-sandbox/cluster-api-provider-rke2/bootstrap/api/v1alpha1"
+	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"sigs.k8s.io/cluster-api/api/v1beta1"
@@ -55,7 +57,11 @@ func (in *DisableComponents) DeepCopy() *DisableComponents {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *EtcdBackupConfig) DeepCopyInto(out *EtcdBackupConfig) {
 	*out = *in
-	out.S3 = in.S3
+	if in.S3 != nil {
+		in, out := &in.S3, &out.S3
+		*out = new(EtcdS3)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EtcdBackupConfig.
@@ -71,8 +77,12 @@ func (in *EtcdBackupConfig) DeepCopy() *EtcdBackupConfig {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *EtcdConfig) DeepCopyInto(out *EtcdConfig) {
 	*out = *in
-	out.BackupConfig = in.BackupConfig
-	in.CustomConfig.DeepCopyInto(&out.CustomConfig)
+	in.BackupConfig.DeepCopyInto(&out.BackupConfig)
+	if in.CustomConfig != nil {
+		in, out := &in.CustomConfig, &out.CustomConfig
+		*out = new(apiv1alpha1.ComponentConfig)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EtcdConfig.
@@ -88,7 +98,11 @@ func (in *EtcdConfig) DeepCopy() *EtcdConfig {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *EtcdS3) DeepCopyInto(out *EtcdS3) {
 	*out = *in
-	out.EndpointCA = in.EndpointCA
+	if in.EndpointCASecret != nil {
+		in, out := &in.EndpointCASecret, &out.EndpointCASecret
+		*out = new(corev1.ObjectReference)
+		**out = **in
+	}
 	out.S3CredentialSecret = in.S3CredentialSecret
 }
 
@@ -309,19 +323,43 @@ func (in *RKE2ControlPlaneTemplateStatus) DeepCopy() *RKE2ControlPlaneTemplateSt
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *RKE2ServerConfig) DeepCopyInto(out *RKE2ServerConfig) {
 	*out = *in
+	if in.AuditPolicySecret != nil {
+		in, out := &in.AuditPolicySecret, &out.AuditPolicySecret
+		*out = new(corev1.ObjectReference)
+		**out = **in
+	}
 	if in.TLSSan != nil {
 		in, out := &in.TLSSan, &out.TLSSan
 		*out = make([]string, len(*in))
 		copy(*out, *in)
 	}
 	in.DisableComponents.DeepCopyInto(&out.DisableComponents)
-	out.CloudProviderConfigMap = in.CloudProviderConfigMap
-	out.AuditPolicySecret = in.AuditPolicySecret
 	in.Etcd.DeepCopyInto(&out.Etcd)
-	in.KubeAPIServer.DeepCopyInto(&out.KubeAPIServer)
-	in.KubeControllerManager.DeepCopyInto(&out.KubeControllerManager)
-	in.KubeScheduler.DeepCopyInto(&out.KubeScheduler)
-	in.CloudControllerManager.DeepCopyInto(&out.CloudControllerManager)
+	if in.KubeAPIServer != nil {
+		in, out := &in.KubeAPIServer, &out.KubeAPIServer
+		*out = new(apiv1alpha1.ComponentConfig)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.KubeControllerManager != nil {
+		in, out := &in.KubeControllerManager, &out.KubeControllerManager
+		*out = new(apiv1alpha1.ComponentConfig)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.KubeScheduler != nil {
+		in, out := &in.KubeScheduler, &out.KubeScheduler
+		*out = new(apiv1alpha1.ComponentConfig)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.CloudControllerManager != nil {
+		in, out := &in.CloudControllerManager, &out.CloudControllerManager
+		*out = new(apiv1alpha1.ComponentConfig)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.CloudProviderConfigMap != nil {
+		in, out := &in.CloudProviderConfigMap, &out.CloudProviderConfigMap
+		*out = new(corev1.ObjectReference)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RKE2ServerConfig.
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 13f01e7..bc7263a 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
@@ -56,9 +56,10 @@ spec:
                 type: boolean
               imageCredentialProviderConfigMap:
                 description: ImageCredentialProviderConfigMap is a reference to the
-                  ConfigMap that contains credential provider plugin config The configMap
-                  should contain a YAML file content + a Path to the Binaries for
-                  Credential Provider.
+                  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.
@@ -136,11 +137,11 @@ spec:
                 description: KubeProxyArgs Customized flag for kube-proxy process.
                 properties:
                   extraArgs:
-                    additionalProperties:
-                      type: string
                     description: ExtraArgs is a map of command line arguments to pass
                       to a Kubernetes Component command.
-                    type: object
+                    items:
+                      type: string
+                    type: array
                   extraEnv:
                     additionalProperties:
                       type: string
@@ -162,11 +163,11 @@ spec:
                 description: KubeletArgs Customized flag for kubelet process.
                 properties:
                   extraArgs:
-                    additionalProperties:
-                      type: string
                     description: ExtraArgs is a map of command line arguments to pass
                       to a Kubernetes Component command.
-                    type: object
+                    items:
+                      type: string
+                    type: array
                   extraEnv:
                     additionalProperties:
                       type: string
@@ -187,6 +188,12 @@ spec:
               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
               manifestsConfigMapReference:
                 description: ManifestsConfigMapReference references a ConfigMap which
                   contains Kubernetes manifests to be deployed automatically on the
@@ -309,6 +316,10 @@ spec:
                     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
               serverConfig:
                 description: ServerConfig specifies configuration for the agent nodes.
                 properties:
@@ -317,7 +328,7 @@ spec:
                       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
+                    description: AuditPolicySecret path to the file that defines the
                       audit policy configuration.
                     properties:
                       apiVersion:
@@ -363,11 +374,11 @@ spec:
                       of the Cloud Controller Manager.
                     properties:
                       extraArgs:
-                        additionalProperties:
-                          type: string
                         description: ExtraArgs is a map of command line arguments
                           to pass to a Kubernetes Component command.
-                        type: object
+                        items:
+                          type: string
+                        type: array
                       extraEnv:
                         additionalProperties:
                           type: string
@@ -386,8 +397,9 @@ spec:
                         type: string
                     type: object
                   cloudProviderConfigMap:
-                    description: CloudProviderConfigMap  is a reference to a ConfigMap
-                      containing Cloud provider configuration.
+                    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.
@@ -424,7 +436,7 @@ spec:
                     type: object
                     x-kubernetes-map-type: atomic
                   cloudProviderName:
-                    description: CloudProviderName  Cloud provider name.
+                    description: CloudProviderName cloud provider name.
                     type: string
                   clusterDNS:
                     description: 'ClusterDNS is the cluster IP for CoreDNS service.
@@ -498,10 +510,17 @@ spec:
                             description: S3 Enable backup to an S3-compatible Object
                               Store.
                             properties:
-                              S3CredentialSecret:
-                                description: S3CredentialSecret is a reference to
-                                  a Secret containing the Access Key and Secret Key
-                                  necessary to access the target S3 Bucket.
+                              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.
@@ -545,16 +564,24 @@ spec:
                                     type: string
                                 type: object
                                 x-kubernetes-map-type: atomic
-                              bucket:
-                                description: Bucket S3 bucket name.
+                              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
-                              endpoint:
-                                description: 'Endpoint S3 endpoint url (default: "s3.amazonaws.com").'
+                              region:
+                                description: 'Region S3 region / bucket location (optional)
+                                  (default: "us-east-1").'
                                 type: string
-                              endpointCA:
-                                description: EndpointCA references the Secret that
-                                  contains a custom CA that should be trusted to connect
-                                  to S3 endpoint.
+                              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.
@@ -598,21 +625,9 @@ spec:
                                     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
                             required:
-                            - S3CredentialSecret
                             - endpoint
+                            - s3CredentialSecret
                             type: object
                           scheduleCron:
                             description: 'ScheduleCron Snapshot interval time in cron
@@ -629,11 +644,11 @@ spec:
                           ETCD.
                         properties:
                           extraArgs:
-                            additionalProperties:
-                              type: string
                             description: ExtraArgs is a map of command line arguments
                               to pass to a Kubernetes Component command.
-                            type: object
+                            items:
+                              type: string
+                            type: array
                           extraEnv:
                             additionalProperties:
                               type: string
@@ -652,7 +667,7 @@ spec:
                               Kubernetes Component
                             type: string
                         type: object
-                      exposeEtcdMetrics:
+                      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
@@ -663,11 +678,11 @@ spec:
                       of the Kube API Server.
                     properties:
                       extraArgs:
-                        additionalProperties:
-                          type: string
                         description: ExtraArgs is a map of command line arguments
                           to pass to a Kubernetes Component command.
-                        type: object
+                        items:
+                          type: string
+                        type: array
                       extraEnv:
                         additionalProperties:
                           type: string
@@ -690,11 +705,11 @@ spec:
                       of the Kube Controller Manager.
                     properties:
                       extraArgs:
-                        additionalProperties:
-                          type: string
                         description: ExtraArgs is a map of command line arguments
                           to pass to a Kubernetes Component command.
-                        type: object
+                        items:
+                          type: string
+                        type: array
                       extraEnv:
                         additionalProperties:
                           type: string
@@ -717,11 +732,11 @@ spec:
                       of the Kube Scheduler.
                     properties:
                       extraArgs:
-                        additionalProperties:
-                          type: string
                         description: ExtraArgs is a map of command line arguments
                           to pass to a Kubernetes Component command.
-                        type: object
+                        items:
+                          type: string
+                        type: array
                       extraEnv:
                         additionalProperties:
                           type: string
@@ -739,19 +754,9 @@ spec:
                           image to override the default one for the Kubernetes Component
                         type: string
                     type: object
-                  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
                   pauseImage:
                     description: PauseImage Override image to use for pause.
                     type: string
-                  runtimeImage:
-                    description: RuntimeImage Override image to use for runtime binaries
-                      (containerd, kubectl, crictl, etc).
-                    type: string
                   serviceNodePortRange:
                     description: 'ServiceNodePortRange is the port range to reserve
                       for services with NodePort visibility (default: "30000-32767").'
@@ -763,6 +768,10 @@ spec:
                       type: string
                     type: array
                 type: object
+              snapshotter:
+                description: 'Snapshotter override default containerd snapshotter
+                  (default: "overlayfs").'
+                type: string
               systemDefaultRegistry:
                 description: SystemDefaultRegistry Private registry to be used for
                   all system images.
diff --git a/controlplane/internal/controllers/const.go b/controlplane/internal/controllers/const.go
index c29307b..32c722e 100644
--- a/controlplane/internal/controllers/const.go
+++ b/controlplane/internal/controllers/const.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 SUSE.
+Copyright 2020 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/controlplane/internal/controllers/scale.go b/controlplane/internal/controllers/scale.go
index bda8649..5fa006f 100644
--- a/controlplane/internal/controllers/scale.go
+++ b/controlplane/internal/controllers/scale.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 SUSE.
+Copyright 2020 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/go.mod b/go.mod
index 236630a..4b2db0c 100644
--- a/go.mod
+++ b/go.mod
@@ -10,11 +10,11 @@ require (
 	github.com/spf13/pflag v1.0.5
 	k8s.io/api v0.25.4
 	k8s.io/apimachinery v0.25.4
-	k8s.io/apiserver v0.25.4
+	k8s.io/apiserver v0.25.0
 	k8s.io/client-go v0.25.4
 	k8s.io/klog/v2 v2.80.1
 	k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
-	sigs.k8s.io/cluster-api v1.2.6
+	sigs.k8s.io/cluster-api v1.2.5
 	sigs.k8s.io/controller-runtime v0.13.1
 )
 
@@ -33,6 +33,7 @@ require (
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/emicklei/go-restful/v3 v3.8.0 // indirect
+	github.com/evanphx/json-patch v4.12.0+incompatible // indirect
 	github.com/evanphx/json-patch/v5 v5.6.0 // indirect
 	github.com/fsnotify/fsnotify v1.5.4 // indirect
 	github.com/go-logr/zapr v1.2.3 // indirect
@@ -80,7 +81,7 @@ require (
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	k8s.io/apiextensions-apiserver v0.25.0 // indirect
 	k8s.io/cluster-bootstrap v0.24.0 // indirect
-	k8s.io/component-base v0.25.4 // indirect
+	k8s.io/component-base v0.25.0 // indirect
 	k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
 	sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
diff --git a/go.sum b/go.sum
index 33259bf..9321c4c 100644
--- a/go.sum
+++ b/go.sum
@@ -398,7 +398,7 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
 go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
+go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
 go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
 go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
@@ -851,14 +851,14 @@ k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjC
 k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
 k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc=
 k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo=
-k8s.io/apiserver v0.25.4 h1:/3TwZcgLqX7wUxq7TtXOUqXeBTwXIblVMQdhR5XZ7yo=
-k8s.io/apiserver v0.25.4/go.mod h1:rPcm567XxjOnnd7jedDUnGJGmDGAo+cT6H7QHAN+xV0=
+k8s.io/apiserver v0.25.0 h1:8kl2ifbNffD440MyvHtPaIz1mw4mGKVgWqM0nL+oyu4=
+k8s.io/apiserver v0.25.0/go.mod h1:BKwsE+PTC+aZK+6OJQDPr0v6uS91/HWxX7evElAH6xo=
 k8s.io/client-go v0.25.4 h1:3RNRDffAkNU56M/a7gUfXaEzdhZlYhoW8dgViGy5fn8=
 k8s.io/client-go v0.25.4/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw=
 k8s.io/cluster-bootstrap v0.24.0 h1:MTs2x3Vfcl/PWvB5bfX7gzTFRyi4ZSbNSQgGJTCb6Sw=
 k8s.io/cluster-bootstrap v0.24.0/go.mod h1:xw+IfoaUweMCAoi+VYhmqkcjii2G7gNg59dmGn7hi0g=
-k8s.io/component-base v0.25.4 h1:n1bjg9Yt+G1C0WnIDJmg2fo6wbEU1UGMRiQSjmj7hNQ=
-k8s.io/component-base v0.25.4/go.mod h1:nnZJU8OP13PJEm6/p5V2ztgX2oyteIaAGKGMYb2L2cY=
+k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y=
+k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
 k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
 k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
@@ -875,8 +875,8 @@ k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/cluster-api v1.2.6 h1:wueslUK8LdTH8rOI697WIj1jutbaGY3a24t4XjZvLI8=
-sigs.k8s.io/cluster-api v1.2.6/go.mod h1:Ye5gn15u+q6VcE+Se7nBMSo6INm55F+zBcCiWzrjxFc=
+sigs.k8s.io/cluster-api v1.2.5 h1:Am4W5ITO0v5FUNkfCuD6gwE29phDt1gyQY9EGWnTYIU=
+sigs.k8s.io/cluster-api v1.2.5/go.mod h1:Ye5gn15u+q6VcE+Se7nBMSo6INm55F+zBcCiWzrjxFc=
 sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg=
 sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI=
 sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
diff --git a/pkg/machinefilters/machine_filters.go b/pkg/machinefilters/machine_filters.go
index e513794..103adcd 100644
--- a/pkg/machinefilters/machine_filters.go
+++ b/pkg/machinefilters/machine_filters.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 SUSE.
+Copyright 2020 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/rke2/config.go b/pkg/rke2/config.go
index 3fc9907..d7e85c8 100644
--- a/pkg/rke2/config.go
+++ b/pkg/rke2/config.go
@@ -1,179 +1,439 @@
-/*
-Copyright 2022 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 rke2
 
 import (
-	bootstrapv1 "github.com/rancher-sandbox/cluster-api-provider-rke2/bootstrap/api/v1alpha1"
+	"context"
+	"fmt"
 
+	bootstrapv1 "github.com/rancher-sandbox/cluster-api-provider-rke2/bootstrap/api/v1alpha1"
 	controlplanev1 "github.com/rancher-sandbox/cluster-api-provider-rke2/controlplane/api/v1alpha1"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 const DefaultRKE2ConfigLocation = "/etc/rancher/rke2/config.yaml"
 const DefaultRKE2JoinPort = 9345
 
-type RKE2ServerConfig struct {
-	// TODO: check what this is supposed to do and modify the struct accordingly
-	TLSSan                            []string `json:"tls-san,omitempty"`
-	BindAddress                       string   `json:"bind-address,omitempty"`
-	AdvertiseAddress                  string   `json:"advertise-address,omitempty"`
-	ClusterCidr                       string   `json:"cluster-cidr,omitempty"`
-	ServiceCidr                       string   `json:"service-cidr,omitempty"`
-	ClusterDNS                        string   `json:"cluster-dns,omitempty"`
-	ClusterDomain                     string   `json:"cluster-domain,omitempty"`
-	DisableComponents                 []string `json:"disable,omitempty"`
-	CNI                               string   `json:"cni"`
-	EnableServiceLoadBalancer         bool     `json:"enable-servicelb,omitempty"`
-	DisableScheduler                  bool     `json:"disable-scheduler,omitempty"`
-	DisableCloudController            bool     `json:"disable-cloud-controller,omitempty"`
-	DisableKubeProxy                  bool     `json:"disable-kube-proxy,omitempty"`
-	DisableAPIserver                  bool     `json:"disable-apiserver,omitempty"`
-	DisableControllerManager          bool     `json:"disable-controller-manager,omitempty"`
-	DisableEtcd                       bool     `json:"disable-etcd,omitempty"`
-	EtcdDisableSnapshots              bool     `json:"etcd-disable-snapshots,omitempty"`
-	EtcdSnapshotScheduleCron          string   `json:"etcd-snapshot-schedule-cron,omitempty"`
-	EtcdSnapshotRetention             string   `json:"etcd-snapshot-retention,omitempty"`
-	EtcdSnapshotDir                   string   `json:"etcd-snapshot-dir,omitempty"`
-	EtcdSnapshotName                  string   `json:"etcd-snapshot-name,omitempty"`
-	EtcdSnapshotCompress              string   `json:"etcd-snapshot-compress,omitempty"`
-	EgressSelectorMode                string   `json:"egress-selector-mode,omitempty"`
-	AgentToken                        string   `json:"agent-token,omitempty"`
-	AgentTokenFile                    string   `json:"agent-token-file,omitempty"`
-	Server                            string   `json:"server,omitempty"`
-	Snapshotter                       string   `json:"snapshotter,omitempty"`
-	ServiceNodePortRange              string   `json:"service-node-port-range,omitempty"`
-	EtcdExposeMetrics                 bool     `json:"etcd-expose-metrics,omitempty"`
-	AirgapExtraRegistry               string   `json:"airgap-extra-registry,omitempty"`
-	EtcdS3                            bool     `json:"etcd-s3,omitempty"`
-	EtcdS3Endpoint                    string   `json:"etcd-s3-endpoint,omitempty"`
-	EtcdS3EndpointCa                  string   `json:"etcd-s3-endpoint-ca,omitempty"`
-	EtcdS3SkipSslVerify               bool     `json:"etcd-s3-skip-ssl-verify,omitempty"`
-	EtcdS3AccessKey                   string   `json:"etcd-s3-access-key,omitempty"`
-	EtcdS3SecretKey                   string   `json:"etcd-s3-secret-key,omitempty"`
-	EtcdS3Bucket                      string   `json:"etcd-s3-bucket,omitempty"`
-	EtcdS3Region                      string   `json:"etcd-s3-region,omitempty"`
-	EtcdS3Folder                      string   `json:"etcd-s3-folder,omitempty"`
-	EtcdS3Insecure                    bool     `json:"etcd-s3-insecure,omitempty"`
-	EtcdS3Timeout                     string   `json:"etcd-s3-timeout,omitempty"`
-	EnablePprof                       bool     `json:"enable-pprof,omitempty"`
-	ServicelbNamespace                string   `json:"servicelb-namespace,omitempty"`
-	KubeAPIServerArgs                 []string `json:"kube-apiserver-arg,omitempty"`
-	KubeAPIserverImage                string   `json:"kube-apiserver-image,omitempty"`
-	KubeAPIserverExtraMounts          []string `json:"kube-apiserver-extra-mount,omitempty"`
-	KubeAPIserverExtraEnvs            []string `json:"kube-apiserver-extra-env,omitempty"`
-	EtcdArgs                          []string `json:"etcd-arg,omitempty"`
-	KubeSchedulerArgs                 []string `json:"kube-scheduler-arg,omitempty"`
-	KubeSchedulerImage                string   `json:"kube-scheduler-image,omitempty"`
-	KubeSchedulerExtraMounts          []string `json:"kube-scheduler-extra-mount,omitempty"`
-	KubeSchedulerExtraEnvs            []string `json:"kube-scheduler-extra-env,omitempty"`
-	KubeControllerManagerArgs         []string `json:"kube-controller-manager-arg,omitempty"`
-	KubeControllerManagerImage        string   `json:"kube-controller-manager-image,omitempty"`
-	KubeControllerManagerExtraMounts  []string `json:"kube-controller-manager-extra-mount,omitempty"`
-	KubeControllerManagerExtraEnvs    []string `json:"kube-controller-manager-extra-env,omitempty"`
-	EtcdExtraMounts                   []string `json:"etcd-extra-mount,omitempty"`
-	EtcdExtraEnvs                     []string `json:"etcd-extra-env,omitempty"`
-	CloudControllerManagerExtraMounts []string `json:"cloud-controller-manager-extra-mount,omitempty"`
-	CloudControllerManagerExtraEnvs   []string `json:"cloud-controller-manager-extra-env,omitempty"`
-	RKE2AgentConfig                   `json:",inline"`
+type rke2ServerConfig struct {
+	AdvertiseAddress                  string            `json:"advertise-address,omitempty"`
+	AuditPolicyFile                   string            `json:"audit-policy-file,omitempty"`
+	BindAddress                       string            `json:"bind-address,omitempty"`
+	CNI                               string            `json:"cni"`
+	CloudControllerManagerExtraEnv    map[string]string `json:"cloud-controller-manager-extra-env,omitempty"`
+	CloudControllerManagerExtraMounts map[string]string `json:"cloud-controller-manager-extra-mount,omitempty"`
+	CloudProviderConfig               string            `json:"cloud-provider-config,omitempty"`
+	CloudProviderName                 string            `json:"cloud-provider-name,omitempty"`
+	ClusterDNS                        string            `json:"cluster-dns,omitempty"`
+	ClusterDomain                     string            `json:"cluster-domain,omitempty"`
+	DisableCloudController            bool              `json:"disable-cloud-controller,omitempty"`
+	DisableComponents                 []string          `json:"disable,omitempty"`
+	DisableKubeProxy                  bool              `json:"disable-kube-proxy,omitempty"`
+	DisableScheduler                  bool              `json:"disable-scheduler,omitempty"`
+	EtcdDisableSnapshots              bool              `json:"etcd-disable-snapshots,omitempty"`
+	EtcdExposeMetrics                 bool              `json:"etcd-expose-metrics,omitempty"`
+	EtcdS3                            bool              `json:"etcd-s3,omitempty"`
+	EtcdS3AccessKey                   string            `json:"etcd-s3-access-key,omitempty"`
+	EtcdS3Bucket                      string            `json:"etcd-s3-bucket,omitempty"`
+	EtcdS3Endpoint                    string            `json:"etcd-s3-endpoint,omitempty"`
+	EtcdS3EndpointCA                  string            `json:"etcd-s3-endpoint-ca,omitempty"`
+	EtcdS3Folder                      string            `json:"etcd-s3-folder,omitempty"`
+	EtcdS3Region                      string            `json:"etcd-s3-region,omitempty"`
+	EtcdS3SecretKey                   string            `json:"etcd-s3-secret-key,omitempty"`
+	EtcdS3SkipSslVerify               bool              `json:"etcd-s3-skip-ssl-verify,omitempty"`
+	EtcdSnapshotDir                   string            `json:"etcd-snapshot-dir,omitempty"`
+	EtcdSnapshotName                  string            `json:"etcd-snapshot-name,omitempty"`
+	EtcdSnapshotRetention             string            `json:"etcd-snapshot-retention,omitempty"`
+	EtcdSnapshotScheduleCron          string            `json:"etcd-snapshot-schedule-cron,omitempty"`
+	KubeAPIServerArgs                 []string          `json:"kube-apiserver-arg,omitempty"`
+	KubeAPIserverExtraEnv             map[string]string `json:"kube-apiserver-extra-env,omitempty"`
+	KubeAPIserverExtraMounts          map[string]string `json:"kube-apiserver-extra-mount,omitempty"`
+	KubeAPIserverImage                string            `json:"kube-apiserver-image,omitempty"`
+	KubeControllerManagerArgs         []string          `json:"kube-controller-manager-arg,omitempty"`
+	KubeControllerManagerExtraEnv     map[string]string `json:"kube-controller-manager-extra-env,omitempty"`
+	KubeControllerManagerExtraMounts  map[string]string `json:"kube-controller-manager-extra-mount,omitempty"`
+	KubeControllerManagerImage        string            `json:"kube-controller-manager-image,omitempty"`
+	KubeSchedulerArgs                 []string          `json:"kube-scheduler-arg,omitempty"`
+	KubeSchedulerExtraEnv             map[string]string `json:"kube-scheduler-extra-env,omitempty"`
+	KubeSchedulerExtraMounts          map[string]string `json:"kube-scheduler-extra-mount,omitempty"`
+	KubeSchedulerImage                string            `json:"kube-scheduler-image,omitempty"`
+	ServiceNodePortRange              string            `json:"service-node-port-range,omitempty"`
+	TLSSan                            []string          `json:"tls-san,omitempty"`
+
+	// We don't expose these fields in the API
+	ClusterCIDR string `json:"cluster-cidr,omitempty"`
+	ServiceCIDR string `json:"service-cidr,omitempty"`
+
+	// Fields below are missing from our API and RKE2 docs
+	AirgapExtraRegistry       string `json:"airgap-extra-registry,omitempty"`
+	DisableAPIserver          bool   `json:"disable-apiserver,omitempty"`
+	DisableControllerManager  bool   `json:"disable-controller-manager,omitempty"`
+	EgressSelectorMode        string `json:"egress-selector-mode,omitempty"`
+	EnablePprof               bool   `json:"enable-pprof,omitempty"`
+	EnableServiceLoadBalancer bool   `json:"enable-servicelb,omitempty"`
+	EtcdS3Insecure            bool   `json:"etcd-s3-insecure,omitempty"`
+	EtcdS3Timeout             string `json:"etcd-s3-timeout,omitempty"`
+	EtcdSnapshotCompress      string `json:"etcd-snapshot-compress,omitempty"`
+	ServicelbNamespace        string `json:"servicelb-namespace,omitempty"`
+
+	rke2AgentConfig `json:",inline"`
+}
+
+type RKE2ServerConfigOpts struct {
+	ControlPlaneEndpoint string
+	Token                string
+	ServerURL            string
+	ServerConfig         controlplanev1.RKE2ServerConfig
+	AgentConfig          bootstrapv1.RKE2AgentConfig
+	Ctx                  context.Context
+	Client               client.Client
+}
+
+func newRKE2ServerConfig(opts RKE2ServerConfigOpts) (*rke2ServerConfig, []bootstrapv1.File, error) {
+	rke2ServerConfig := &rke2ServerConfig{}
+	files := []bootstrapv1.File{}
+	rke2ServerConfig.AdvertiseAddress = opts.ServerConfig.AdvertiseAddress
+	if opts.ServerConfig.AuditPolicySecret != nil {
+		auditPolicySecret := &corev1.Secret{}
+		if err := opts.Client.Get(opts.Ctx, types.NamespacedName{
+			Name:      opts.ServerConfig.AuditPolicySecret.Name,
+			Namespace: opts.ServerConfig.AuditPolicySecret.Namespace,
+		}, auditPolicySecret); err != nil {
+			return nil, nil, fmt.Errorf("failed to get audit policy secret: %w", err)
+		}
+		auditPolicy, ok := auditPolicySecret.Data["audit-policy.yaml"]
+		if !ok {
+			return nil, nil, fmt.Errorf("audit policy secret is missing audit-policy.yaml key")
+		}
+		rke2ServerConfig.AuditPolicyFile = "/etc/rancher/rke2/audit-policy.yaml"
+		files = append(files, bootstrapv1.File{
+			Path:        rke2ServerConfig.AuditPolicyFile,
+			Content:     string(auditPolicy),
+			Owner:       "root:root",
+			Permissions: "0644",
+		})
+	}
+	rke2ServerConfig.BindAddress = opts.ServerConfig.BindAddress
+	rke2ServerConfig.CNI = string(opts.ServerConfig.CNI)
+	rke2ServerConfig.ClusterDNS = opts.ServerConfig.ClusterDNS
+	rke2ServerConfig.ClusterDomain = opts.ServerConfig.ClusterDomain
+	if opts.ServerConfig.CloudProviderConfigMap != nil {
+		cloudProviderConfigMap := &corev1.ConfigMap{}
+		if err := opts.Client.Get(opts.Ctx, types.NamespacedName{
+			Name:      opts.ServerConfig.CloudProviderConfigMap.Name,
+			Namespace: opts.ServerConfig.CloudProviderConfigMap.Namespace,
+		}, cloudProviderConfigMap); err != nil {
+			return nil, nil, fmt.Errorf("failed to get cloud provider config map: %w", err)
+		}
+		cloudProviderConfig, ok := cloudProviderConfigMap.Data["cloud-config"]
+		if !ok {
+			return nil, nil, fmt.Errorf("cloud provider config map is missing cloud-config key")
+		}
+		rke2ServerConfig.CloudProviderConfig = "/etc/rancher/rke2/cloud-provider-config"
+		files = append(files, bootstrapv1.File{
+			Path:        rke2ServerConfig.CloudProviderConfig,
+			Content:     cloudProviderConfig,
+			Owner:       "root:root",
+			Permissions: "0644",
+		})
+	}
+	rke2ServerConfig.CloudProviderName = opts.ServerConfig.CloudProviderName
+	rke2ServerConfig.DisableComponents = func() []string {
+		disabled := []string{}
+		for _, plugin := range opts.ServerConfig.DisableComponents.PluginComponents {
+			disabled = append(disabled, string(plugin))
+		}
+		return disabled
+	}()
+	for _, component := range opts.ServerConfig.DisableComponents.KubernetesComponents {
+		switch component {
+		case controlplanev1.KubeProxy:
+			rke2ServerConfig.DisableKubeProxy = true
+		case controlplanev1.CloudController:
+			rke2ServerConfig.DisableCloudController = true
+		case controlplanev1.Scheduler:
+			rke2ServerConfig.DisableScheduler = true
+		}
+	}
+	rke2ServerConfig.EtcdDisableSnapshots = !opts.ServerConfig.Etcd.BackupConfig.EnableAutomaticSnapshots // TODO: change API to disable so don't have to negate?
+	rke2ServerConfig.EtcdExposeMetrics = opts.ServerConfig.Etcd.ExposeMetrics
+	if opts.ServerConfig.Etcd.BackupConfig.S3 != nil {
+		rke2ServerConfig.EtcdS3 = true
+		awsCredentialsSecret := &corev1.Secret{}
+		if err := opts.Client.Get(opts.Ctx, types.NamespacedName{
+			Name:      opts.ServerConfig.Etcd.BackupConfig.S3.S3CredentialSecret.Name,
+			Namespace: opts.ServerConfig.Etcd.BackupConfig.S3.S3CredentialSecret.Namespace,
+		}, awsCredentialsSecret); err != nil {
+			return nil, nil, fmt.Errorf("failed to get aws credentials secret: %w", err)
+		}
+		accessKeyID, ok := awsCredentialsSecret.Data["aws_access_key_id"]
+		if !ok {
+			return nil, nil, fmt.Errorf("aws credentials secret is missing aws_access_key_id")
+		}
+		secretAccessKey, ok := awsCredentialsSecret.Data["aws_secret_access_key"]
+		if !ok {
+			return nil, nil, fmt.Errorf("aws credentials secret is missing aws_secret_access_key")
+		}
+		rke2ServerConfig.EtcdS3AccessKey = string(accessKeyID)
+		rke2ServerConfig.EtcdS3SecretKey = string(secretAccessKey)
+		rke2ServerConfig.EtcdS3Bucket = opts.ServerConfig.Etcd.BackupConfig.S3.Bucket
+		rke2ServerConfig.EtcdS3Region = opts.ServerConfig.Etcd.BackupConfig.S3.Region
+		rke2ServerConfig.EtcdS3Folder = opts.ServerConfig.Etcd.BackupConfig.S3.Folder
+		rke2ServerConfig.EtcdS3Endpoint = opts.ServerConfig.Etcd.BackupConfig.S3.Endpoint
+		if opts.ServerConfig.Etcd.BackupConfig.S3.EndpointCASecret != nil {
+			endpointCAsecret := &corev1.Secret{}
+			if err := opts.Client.Get(opts.Ctx, types.NamespacedName{
+				Name:      opts.ServerConfig.Etcd.BackupConfig.S3.EndpointCASecret.Name,
+				Namespace: opts.ServerConfig.Etcd.BackupConfig.S3.EndpointCASecret.Namespace,
+			}, endpointCAsecret); err != nil {
+				return nil, nil, fmt.Errorf("failed to get aws credentials secret: %w", err)
+			}
+			caCert, ok := endpointCAsecret.Data["ca.pem"]
+			if !ok {
+				return nil, nil, fmt.Errorf("endpoint CA secret is missing ca.pem")
+			}
+			rke2ServerConfig.EtcdS3EndpointCA = "/etc/rancher/rke2/etcd-s3-ca.crt"
+			files = append(files, bootstrapv1.File{
+				Path:        rke2ServerConfig.EtcdS3EndpointCA,
+				Content:     string(caCert),
+				Owner:       "root:root",
+				Permissions: "0640",
+			})
+		}
+		rke2ServerConfig.EtcdSnapshotDir = opts.ServerConfig.Etcd.BackupConfig.Directory
+		rke2ServerConfig.EtcdSnapshotName = opts.ServerConfig.Etcd.BackupConfig.SnapshotName
+		rke2ServerConfig.EtcdSnapshotRetention = opts.ServerConfig.Etcd.BackupConfig.Retention
+		rke2ServerConfig.EtcdSnapshotScheduleCron = opts.ServerConfig.Etcd.BackupConfig.ScheduleCron
+		rke2ServerConfig.EtcdS3SkipSslVerify = !opts.ServerConfig.Etcd.BackupConfig.S3.EnforceSSLVerify // TODO: change API to skip so don't have to negate?
+	}
+
+	if opts.ServerConfig.Etcd.CustomConfig != nil {
+		rke2ServerConfig.EtcdArgs = opts.ServerConfig.Etcd.CustomConfig.ExtraArgs
+		rke2ServerConfig.EtcdImage = opts.ServerConfig.Etcd.CustomConfig.OverrideImage
+		rke2ServerConfig.EtcdExtraMounts = opts.ServerConfig.Etcd.CustomConfig.ExtraMounts
+		rke2ServerConfig.EtcdExtraEnv = opts.ServerConfig.Etcd.CustomConfig.ExtraEnv
+	}
+
+	rke2ServerConfig.ServiceNodePortRange = opts.ServerConfig.ServiceNodePortRange
+	rke2ServerConfig.TLSSan = append(opts.ServerConfig.TLSSan, opts.ControlPlaneEndpoint)
+
+	if opts.ServerConfig.KubeAPIServer != nil {
+		rke2ServerConfig.KubeAPIServerArgs = opts.ServerConfig.KubeAPIServer.ExtraArgs
+		rke2ServerConfig.KubeAPIserverImage = opts.ServerConfig.KubeAPIServer.OverrideImage
+		rke2ServerConfig.KubeAPIserverExtraMounts = opts.ServerConfig.KubeAPIServer.ExtraMounts
+		rke2ServerConfig.KubeAPIserverExtraEnv = opts.ServerConfig.KubeAPIServer.ExtraEnv
+	}
+
+	if opts.ServerConfig.KubeScheduler != nil {
+		rke2ServerConfig.KubeSchedulerArgs = opts.ServerConfig.KubeScheduler.ExtraArgs
+		rke2ServerConfig.KubeSchedulerImage = opts.ServerConfig.KubeScheduler.OverrideImage
+		rke2ServerConfig.KubeSchedulerExtraMounts = opts.ServerConfig.KubeScheduler.ExtraMounts
+		rke2ServerConfig.KubeSchedulerExtraEnv = opts.ServerConfig.KubeScheduler.ExtraEnv
+	}
+
+	if opts.ServerConfig.KubeControllerManager != nil {
+		rke2ServerConfig.KubeControllerManagerArgs = opts.ServerConfig.KubeControllerManager.ExtraArgs
+		rke2ServerConfig.KubeControllerManagerImage = opts.ServerConfig.KubeControllerManager.OverrideImage
+		rke2ServerConfig.KubeControllerManagerExtraMounts = opts.ServerConfig.KubeControllerManager.ExtraMounts
+		rke2ServerConfig.KubeControllerManagerExtraEnv = opts.ServerConfig.KubeControllerManager.ExtraEnv
+	}
+
+	if opts.ServerConfig.CloudControllerManager != nil {
+		rke2ServerConfig.CloudControllerManagerExtraMounts = opts.ServerConfig.CloudControllerManager.ExtraMounts
+		rke2ServerConfig.CloudControllerManagerExtraEnv = opts.ServerConfig.CloudControllerManager.ExtraEnv
+	}
+
+	return rke2ServerConfig, files, nil
+}
+
+type rke2AgentConfig struct {
+	ContainerRuntimeEndpoint      string            `json:"container-runtime-endpoint,omitempty"`
+	DataDir                       string            `json:"data-dir"`
+	EtcdArgs                      []string          `json:"etcd-arg,omitempty"`
+	EtcdExtraEnv                  map[string]string `json:"etcd-extra-env,omitempty"`
+	EtcdExtraMounts               map[string]string `json:"etcd-extra-mount,omitempty"`
+	EtcdImage                     string            `json:"etcd-image,omitempty"`
+	ImageCredentialProviderConfig string            `json:"image-credential-provider-config,omitempty"`
+	ImageCredentialProviderBinDir string            `json:"image-credential-provider-bin-dir,omitempty"`
+	KubeProxyArgs                 []string          `json:"kube-proxy-arg,omitempty"`
+	KubeProxyExtraEnv             map[string]string `json:"kube-proxy-extra-env,omitempty"`
+	KubeProxyExtraMounts          map[string]string `json:"kube-proxy-extra-mount,omitempty"`
+	KubeProxyImage                string            `json:"kube-proxy-image,omitempty"`
+	KubeletArgs                   []string          `json:"kubelet-arg,omitempty"`
+	KubeletPath                   string            `json:"kubelet-path,omitempty"`
+	LbServerPort                  int               `json:"lb-server-port,omitempty"`
+	NodeLabels                    []string          `json:"node-label,omitempty"`
+	NodeTaints                    []string          `json:"node-taint,omitempty"`
+	Profile                       string            `json:"profile,omitempty"`
+	ProtectKernelDefaults         bool              `json:"protect-kernel-defaults,omitempty"`
+	ResolvConf                    string            `json:"resolv-conf,omitempty"`
+	RuntimeImage                  string            `json:"runtime-image,omitempty"`
+	Selinux                       bool              `json:"selinux,omitempty"`
+	Server                        string            `json:"server,omitempty"`
+	Snapshotter                   string            `json:"snapshotter,omitempty"`
+	Token                         string            `json:"token,omitempty"` // TODO: generate the token?
+
+	// We don't expose these in the API
+	PauseImage                     string `json:"pause-image,omitempty"`
+	PodSecurityAdmissionConfigFile string `json:"pod-security-admission-config-file,omitempty"` // new flag, not present in the RKE2 docs yet
+	PrivateRegistry                string `json:"private-registry,omitempty"`
+
+	NodeExternalIp string `json:"node-external-ip,omitempty"` // TODO: infra provider can provider external ip, we should use it here and for TLS SAN.
+	NodeIp         string `json:"node-ip,omitempty"`          // TODO: obtain the node ip from the infra provider
+	NodeName       string `json:"node-name,omitempty"`        // TODO: figure our how to handle this using node name prefix from API
+}
+
+type RKE2AgentConfigOpts struct {
+	ServerURL   string
+	Token       string
+	AgentConfig bootstrapv1.RKE2AgentConfig
+	Ctx         context.Context
+	Client      client.Client
 }
 
-type RKE2AgentConfig struct {
-	// TODO: check what this is supposed to do and modify the struct accordingly
-	Token                          string   `json:"token,omitempty"`
-	Server                         string   `json:"server,omitempty"`
-	DataDir                        string   `json:"data-dir"`
-	NodeName                       string   `json:"node-name,omitempty"`
-	NodeLabels                     []string `json:"node-label,omitempty"`
-	NodeTaints                     []string `json:"node-taint,omitempty"`
-	ImageCredentialProviderBinDir  string   `json:"image-credential-provider-bin-dir,omitempty"`
-	ImageCredentialProviderConfig  string   `json:"image-credential-provider-config,omitempty"`
-	ContainerRuntimeEndpoint       string   `json:"container-runtime-endpoint,omitempty"`
-	PrivateRegistry                string   `json:"private-registry,omitempty"`
-	NodeIp                         string   `json:"node-ip,omitempty"`
-	NodeExternalIp                 string   `json:"node-external-ip,omitempty"`
-	ResolvConf                     string   `json:"resolv-conf,omitempty"`
-	KubeletArgs                    []string `json:"kubelet-arg,omitempty"`
-	KubeProxyArgs                  []string `json:"kube-proxy-arg,omitempty"`
-	KubeProxyExtraMounts           []string `json:"kube-proxy-extra-mount,omitempty"`
-	KubeProxyExtraEnvs             []string `json:"kube-proxy-extra-env,omitempty"`
-	ProtectKernelDefaults          bool     `json:"protect-kernel-defaults,omitempty"`
-	Selinux                        bool     `json:"selinux,omitempty"`
-	LbServerPort                   string   `json:"lb-server-port,omitempty"`
-	KubeProxyImage                 string   `json:"kube-proxy-image,omitempty"`
-	PauseImage                     string   `json:"pause-image,omitempty"`
-	RuntimeImage                   string   `json:"runtime-image,omitempty"`
-	EtcdImage                      string   `json:"etcd-image,omitempty"`
-	KubeletPath                    string   `json:"kubelet-path,omitempty"`
-	CloudProviderName              string   `json:"cloud-provider-name,omitempty"`
-	CloudProviderConfig            string   `json:"cloud-provider-config,omitempty"`
-	Profile                        string   `json:"profile,omitempty"`
-	AuditPolicyFile                string   `json:"audit-policy-file,omitempty"`
-	PodSecurityAdmissionConfigFile string   `json:"pod-security-admission-config-file,omitempty"`
-	ControlPlaneResourceRequests   []string `json:"control-plane-resource-requests,omitempty"`
-	ControlPlaneResourceLimits     []string `json:"control-plane-resource-limits,omitempty"`
+func newRKE2AgentConfig(opts RKE2AgentConfigOpts) (*rke2AgentConfig, []bootstrapv1.File, error) {
+	rke2AgentConfig := &rke2AgentConfig{}
+	files := []bootstrapv1.File{}
+	rke2AgentConfig.ContainerRuntimeEndpoint = opts.AgentConfig.ContainerRuntimeEndpoint
+	rke2AgentConfig.DataDir = opts.AgentConfig.DataDir
+	if opts.AgentConfig.ImageCredentialProviderConfigMap != nil {
+		imageCredentialProviderCM := &corev1.ConfigMap{}
+		if err := opts.Client.Get(opts.Ctx, types.NamespacedName{
+			Name:      opts.AgentConfig.ImageCredentialProviderConfigMap.Name,
+			Namespace: opts.AgentConfig.ImageCredentialProviderConfigMap.Namespace,
+		}, imageCredentialProviderCM); err != nil {
+			return nil, nil, fmt.Errorf("failed to get image credential provider config map: %w", err)
+		}
+		credentialConfig, ok := imageCredentialProviderCM.Data["credential-config.yaml"]
+		if !ok {
+			return nil, nil, fmt.Errorf("image credential provider config map is missing config.yaml")
+		}
+		credentialProviderBinaries, ok := imageCredentialProviderCM.Data["credential-provider-binaries"]
+		if !ok {
+			return nil, nil, fmt.Errorf("image credential provider config map is missing credential-provider-binaries")
+		}
+		rke2AgentConfig.ImageCredentialProviderBinDir = credentialProviderBinaries
+		rke2AgentConfig.ImageCredentialProviderConfig = "/etc/rancher/rke2/credential-config.yaml"
+		files = append(files, bootstrapv1.File{
+			Path:        rke2AgentConfig.ImageCredentialProviderConfig,
+			Content:     credentialConfig,
+			Owner:       "root:root",
+			Permissions: "0644",
+		})
+	}
+	rke2AgentConfig.KubeletPath = opts.AgentConfig.KubeletPath
+	if opts.AgentConfig.Kubelet != nil {
+		rke2AgentConfig.KubeletArgs = opts.AgentConfig.Kubelet.ExtraArgs // TODO: Add a webhook validation to ensure that only args are used in this struct.
+	}
+	rke2AgentConfig.LbServerPort = opts.AgentConfig.LoadBalancerPort
+	rke2AgentConfig.NodeLabels = opts.AgentConfig.NodeLabels
+	rke2AgentConfig.NodeTaints = opts.AgentConfig.NodeTaints
+	rke2AgentConfig.Profile = string(opts.AgentConfig.CISProfile)
+	rke2AgentConfig.ProtectKernelDefaults = opts.AgentConfig.ProtectKernelDefaults
+	if opts.AgentConfig.ResolvConf != nil {
+		resolvConfCM := &corev1.ConfigMap{}
+		if err := opts.Client.Get(opts.Ctx, types.NamespacedName{
+			Name:      opts.AgentConfig.ResolvConf.Name,
+			Namespace: opts.AgentConfig.ResolvConf.Namespace,
+		}, resolvConfCM); err != nil {
+			return nil, nil, fmt.Errorf("failed to get resolv.conf config map: %w", err)
+		}
+		resolvConf, ok := resolvConfCM.Data["resolv.conf"]
+		if !ok {
+			return nil, nil, fmt.Errorf("resolv conf config map is missing resolv.conf")
+		}
+		rke2AgentConfig.ResolvConf = "/etc/rancher/rke2/resolv.conf"
+		files = append(files, bootstrapv1.File{
+			Path:        rke2AgentConfig.ResolvConf,
+			Content:     resolvConf,
+			Owner:       "root:root",
+			Permissions: "0644",
+		})
+	}
+	rke2AgentConfig.RuntimeImage = opts.AgentConfig.RuntimeImage
+	rke2AgentConfig.Selinux = opts.AgentConfig.EnableContainerdSElinux
+	rke2AgentConfig.Server = opts.ServerURL
+	rke2AgentConfig.Snapshotter = opts.AgentConfig.Snapshotter
+	if opts.AgentConfig.KubeProxy != nil {
+		rke2AgentConfig.KubeProxyArgs = opts.AgentConfig.KubeProxy.ExtraArgs
+		rke2AgentConfig.KubeProxyImage = opts.AgentConfig.KubeProxy.OverrideImage
+		rke2AgentConfig.KubeProxyExtraMounts = opts.AgentConfig.KubeProxy.ExtraMounts
+		rke2AgentConfig.KubeProxyExtraEnv = opts.AgentConfig.KubeProxy.ExtraEnv
+	}
+	rke2AgentConfig.Token = opts.Token
+
+	return rke2AgentConfig, files, nil
 }
 
-func GenerateInitControlPlaneConfig(controlPlaneEndpoint string, token string, serverConfig controlplanev1.RKE2ServerConfig, agentConfig bootstrapv1.RKE2AgentConfig) RKE2ServerConfig {
-	rke2ServerConfig := RKE2ServerConfig{
-		// TODO: Add data to the struct
-		DisableCloudController: true,
-		TLSSan:                 append(serverConfig.TLSSan, controlPlaneEndpoint),
-		BindAddress:            serverConfig.BindAddress,
-		AdvertiseAddress:       serverConfig.AdvertiseAddress,
-		ClusterDNS:             serverConfig.ClusterDNS,
-		ClusterDomain:          serverConfig.ClusterDomain,
+func GenerateInitControlPlaneConfig(opts RKE2ServerConfigOpts) (*rke2ServerConfig, []bootstrapv1.File, error) {
+	if opts.Token == "" {
+		return nil, nil, fmt.Errorf("token is required")
 	}
 
-	rke2ServerConfig.RKE2AgentConfig = RKE2AgentConfig{
-		Token:      token,
-		NodeLabels: agentConfig.NodeLabels,
-		NodeTaints: agentConfig.NodeTaints,
+	rke2ServerConfig, serverFiles, err := newRKE2ServerConfig(opts)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to generate rke2 server config: %w", err)
 	}
 
-	return rke2ServerConfig
+	rke2AgentConfig, agentFiles, err := newRKE2AgentConfig(RKE2AgentConfigOpts{
+		AgentConfig: opts.AgentConfig,
+		Client:      opts.Client,
+		Ctx:         opts.Ctx,
+		Token:       opts.Token,
+	})
+
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to generate rke2 agent config: %w", err)
+	}
+
+	rke2ServerConfig.rke2AgentConfig = *rke2AgentConfig
+
+	return rke2ServerConfig, append(serverFiles, agentFiles...), nil
 }
 
-func GenerateJoinControlPlaneConfig(serverUrl string, token string, controlplaneendpoint string, serverConfig controlplanev1.RKE2ServerConfig, agentConfig bootstrapv1.RKE2AgentConfig) RKE2ServerConfig {
+func GenerateJoinControlPlaneConfig(opts RKE2ServerConfigOpts) (*rke2ServerConfig, []bootstrapv1.File, error) {
+	if opts.ServerURL == "" {
+		return nil, nil, fmt.Errorf("server url is required")
+	}
 
-	rke2ServerConfig := RKE2ServerConfig{
-		// TODO: Add data to the struct
-		DisableCloudController: true,
-		TLSSan:                 append(serverConfig.TLSSan, controlplaneendpoint),
-		BindAddress:            serverConfig.BindAddress,
-		ClusterDNS:             serverConfig.ClusterDNS,
-		ClusterDomain:          serverConfig.ClusterDomain,
+	if opts.Token == "" {
+		return nil, nil, fmt.Errorf("token is required")
 	}
 
-	rke2ServerConfig.RKE2AgentConfig = RKE2AgentConfig{
-		// TODO : Add data to the struct
-		Token:      token,
-		Server:     serverUrl,
-		NodeLabels: agentConfig.NodeLabels,
-		NodeTaints: agentConfig.NodeTaints,
+	rke2ServerConfig, serverFiles, err := newRKE2ServerConfig(opts)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to generate rke2 server config: %w", err)
+	}
+
+	rke2AgentConfig, agentFiles, err := newRKE2AgentConfig(RKE2AgentConfigOpts{
+		AgentConfig: opts.AgentConfig,
+		Client:      opts.Client,
+		Ctx:         opts.Ctx,
+		ServerURL:   opts.ServerURL,
+		Token:       opts.Token,
+	})
+
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to generate rke2 agent config: %w", err)
 	}
 
-	return rke2ServerConfig
+	rke2ServerConfig.rke2AgentConfig = *rke2AgentConfig
+
+	return rke2ServerConfig, append(serverFiles, agentFiles...), nil
 }
 
-func GenerateWorkerConfig(serverUrl string, token string, agentConfig bootstrapv1.RKE2AgentConfig) RKE2AgentConfig {
-	return RKE2AgentConfig{
-		// TODO : Add data to the struct
-		Server:     serverUrl,
-		Token:      token,
-		NodeLabels: agentConfig.NodeLabels,
-		NodeTaints: agentConfig.NodeTaints,
+func GenerateWorkerConfig(opts RKE2AgentConfigOpts) (*rke2AgentConfig, []bootstrapv1.File, error) {
+	if opts.ServerURL == "" {
+		return nil, nil, fmt.Errorf("server url is required")
+	}
+
+	if opts.Token == "" {
+		return nil, nil, fmt.Errorf("token is required")
 	}
+
+	rke2AgentConfig, agentFiles, err := newRKE2AgentConfig(opts)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to generate rke2 agent config: %w", err)
+	}
+
+	return rke2AgentConfig, agentFiles, nil
 }
diff --git a/pkg/rke2/control_plane.go b/pkg/rke2/control_plane.go
index e67fb06..c3ebbd4 100644
--- a/pkg/rke2/control_plane.go
+++ b/pkg/rke2/control_plane.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 SUSE.
+Copyright 2020 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/rke2/failure_domain.go b/pkg/rke2/failure_domain.go
index 07362d4..a207115 100644
--- a/pkg/rke2/failure_domain.go
+++ b/pkg/rke2/failure_domain.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 SUSE.
+Copyright 2020 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/rke2/machine_collection.go b/pkg/rke2/machine_collection.go
index f5221d7..36f5383 100644
--- a/pkg/rke2/machine_collection.go
+++ b/pkg/rke2/machine_collection.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 SUSE.
+Copyright 2020 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/rke2/management_cluster.go b/pkg/rke2/management_cluster.go
index ae928c0..ed3ef04 100644
--- a/pkg/rke2/management_cluster.go
+++ b/pkg/rke2/management_cluster.go
@@ -1,19 +1,3 @@
-/*
-Copyright 2022 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 rke2
 
 import (
diff --git a/pkg/rke2/workload_cluster.go b/pkg/rke2/workload_cluster.go
index 2ed6a7e..1bac498 100644
--- a/pkg/rke2/workload_cluster.go
+++ b/pkg/rke2/workload_cluster.go
@@ -1,19 +1,3 @@
-/*
-Copyright 2022 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 rke2
 
 import (
diff --git a/pkg/util/util.go b/pkg/util/util.go
index 1c9ca50..f0a43f9 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -1,19 +1,3 @@
-/*
-Copyright 2022 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 util
 
 import (
diff --git a/version/version.go b/version/version.go
index 30bb50c..62798a7 100644
--- a/version/version.go
+++ b/version/version.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 The Kubernetes Authors.
+Copyright 2020 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.


Test patch:
diff --git a/pkg/rke2/config_test.go b/pkg/rke2/config_test.go
new file mode 100644
index 0000000..4fd4e87
--- /dev/null
+++ b/pkg/rke2/config_test.go
@@ -0,0 +1,298 @@
+package rke2
+
+import (
+	"context"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	bootstrapv1 "github.com/rancher-sandbox/cluster-api-provider-rke2/bootstrap/api/v1alpha1"
+	controlplanev1 "github.com/rancher-sandbox/cluster-api-provider-rke2/controlplane/api/v1alpha1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+var _ = Describe("RKE2ServerConfig", func() {
+	var opts *RKE2ServerConfigOpts
+
+	BeforeEach(func() {
+		opts = &RKE2ServerConfigOpts{
+			ControlPlaneEndpoint: "testendpoint",
+			Ctx:                  context.Background(),
+			Client: fake.NewClientBuilder().WithObjects(
+				&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "test",
+						Namespace: "test",
+					},
+					Data: map[string][]byte{
+						"aws_access_key_id":     []byte("test_id"),
+						"aws_secret_access_key": []byte("test_secret"),
+						"ca.pem":                []byte("test_ca"),
+						"audit-policy.yaml":     []byte("test_audit"),
+					},
+				},
+				&corev1.ConfigMap{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "test",
+						Namespace: "test",
+					},
+					Data: map[string]string{
+						"cloud-config":                 "test_cloud_config",
+						"credential-config.yaml":       "test_credential_config",
+						"credential-provider-binaries": "test_credential_provider_binaries",
+						"resolv.conf":                  "test_resolv_conf",
+					},
+				},
+			).Build(),
+			ServerConfig: controlplanev1.RKE2ServerConfig{
+				AdvertiseAddress: "testaddress",
+				AuditPolicySecret: &corev1.ObjectReference{
+					Name:      "test",
+					Namespace: "test",
+				},
+
+				BindAddress:   "testbindaddress",
+				CNI:           controlplanev1.Cilium,
+				ClusterDNS:    "testdns",
+				ClusterDomain: "testdomain",
+				CloudProviderConfigMap: &corev1.ObjectReference{
+					Name:      "test",
+					Namespace: "test",
+				},
+				CloudProviderName: "testcloud",
+				DisableComponents: controlplanev1.DisableComponents{
+					PluginComponents: []controlplanev1.DisabledPluginComponent{
+						controlplanev1.CoreDNS,
+					},
+					KubernetesComponents: []controlplanev1.DisabledKubernetesComponent{
+						controlplanev1.KubeProxy,
+						controlplanev1.Scheduler,
+						controlplanev1.CloudController,
+					},
+				},
+				Etcd: controlplanev1.EtcdConfig{
+					ExposeMetrics: true,
+					BackupConfig: controlplanev1.EtcdBackupConfig{
+						EnableAutomaticSnapshots: true,
+						S3: &controlplanev1.EtcdS3{
+							S3CredentialSecret: corev1.ObjectReference{
+								Name:      "test",
+								Namespace: "test",
+							},
+							EndpointCASecret: &corev1.ObjectReference{
+								Name:      "test",
+								Namespace: "test",
+							},
+							Bucket:           "testbucket",
+							Region:           "testregion",
+							Endpoint:         "testendpoint",
+							EnforceSSLVerify: true,
+						},
+						Directory:    "testdir",
+						SnapshotName: "testsnapshot",
+						Retention:    "testretention",
+						ScheduleCron: "testschedule",
+					},
+					CustomConfig: &bootstrapv1.ComponentConfig{
+						ExtraArgs:     []string{"testarg"},
+						OverrideImage: "testimage",
+						ExtraMounts:   map[string]string{"testmount": "testmount"},
+						ExtraEnv:      map[string]string{"testenv": "testenv"},
+					},
+				},
+				ServiceNodePortRange: "testrange",
+				TLSSan:               []string{"testsan"},
+				KubeAPIServer: &bootstrapv1.ComponentConfig{
+					ExtraArgs:     []string{"testarg"},
+					OverrideImage: "testimage",
+					ExtraMounts:   map[string]string{"testmount": "testmount"},
+					ExtraEnv:      map[string]string{"testenv": "testenv"},
+				},
+				KubeScheduler: &bootstrapv1.ComponentConfig{
+					ExtraArgs:     []string{"testarg"},
+					OverrideImage: "testimage",
+					ExtraMounts:   map[string]string{"testmount": "testmount"},
+					ExtraEnv:      map[string]string{"testenv": "testenv"},
+				},
+				KubeControllerManager: &bootstrapv1.ComponentConfig{
+					ExtraArgs:     []string{"testarg"},
+					OverrideImage: "testimage",
+					ExtraEnv:      map[string]string{"testenv": "testenv"},
+					ExtraMounts:   map[string]string{"testmount": "testmount"},
+				},
+				CloudControllerManager: &bootstrapv1.ComponentConfig{
+					ExtraArgs:     []string{"testarg"},
+					OverrideImage: "testimage",
+					ExtraEnv:      map[string]string{"testenv": "testenv"},
+					ExtraMounts:   map[string]string{"testmount": "testmount"},
+				},
+			},
+		}
+	})
+
+	It("should succefully generate a server config", func() {
+		rke2ServerConfig, files, err := newRKE2ServerConfig(*opts)
+		Expect(err).ToNot(HaveOccurred())
+
+		serverConfig := opts.ServerConfig
+		Expect(rke2ServerConfig.AdvertiseAddress).To(Equal(serverConfig.AdvertiseAddress))
+		Expect(rke2ServerConfig.AuditPolicyFile).To(Equal("/etc/rancher/rke2/audit-policy.yaml"))
+		Expect(rke2ServerConfig.BindAddress).To(Equal(serverConfig.BindAddress))
+		Expect(rke2ServerConfig.CNI).To(Equal(string(serverConfig.CNI)))
+		Expect(rke2ServerConfig.ClusterDNS).To(Equal(serverConfig.ClusterDNS))
+		Expect(rke2ServerConfig.ClusterDomain).To(Equal(serverConfig.ClusterDomain))
+		Expect(rke2ServerConfig.CloudProviderConfig).To(Equal("/etc/rancher/rke2/cloud-provider-config"))
+		Expect(rke2ServerConfig.CloudProviderName).To(Equal(opts.ServerConfig.CloudProviderName))
+		Expect(rke2ServerConfig.DisableComponents).To(Equal([]string{string(controlplanev1.CoreDNS)}))
+		Expect(rke2ServerConfig.DisableKubeProxy).To(BeTrue())
+		Expect(rke2ServerConfig.DisableCloudController).To(BeTrue())
+		Expect(rke2ServerConfig.DisableScheduler).To(BeTrue())
+		Expect(rke2ServerConfig.EtcdDisableSnapshots).To(BeFalse())
+		Expect(rke2ServerConfig.EtcdExposeMetrics).To(Equal(serverConfig.Etcd.ExposeMetrics))
+		Expect(rke2ServerConfig.EtcdS3).To(BeTrue())
+		Expect(rke2ServerConfig.EtcdS3AccessKey).To(Equal("test_id"))
+		Expect(rke2ServerConfig.EtcdS3SecretKey).To(Equal("test_secret"))
+		Expect(rke2ServerConfig.EtcdS3Bucket).To(Equal(serverConfig.Etcd.BackupConfig.S3.Bucket))
+		Expect(rke2ServerConfig.EtcdS3Region).To(Equal(serverConfig.Etcd.BackupConfig.S3.Region))
+		Expect(rke2ServerConfig.EtcdS3Folder).To(Equal(serverConfig.Etcd.BackupConfig.S3.Folder))
+		Expect(rke2ServerConfig.EtcdS3Endpoint).To(Equal(serverConfig.Etcd.BackupConfig.S3.Endpoint))
+		Expect(rke2ServerConfig.EtcdS3EndpointCA).To(Equal("/etc/rancher/rke2/etcd-s3-ca.crt"))
+		Expect(rke2ServerConfig.EtcdSnapshotDir).To(Equal(serverConfig.Etcd.BackupConfig.Directory))
+		Expect(rke2ServerConfig.EtcdSnapshotName).To(Equal(serverConfig.Etcd.BackupConfig.SnapshotName))
+		Expect(rke2ServerConfig.EtcdSnapshotRetention).To(Equal(serverConfig.Etcd.BackupConfig.Retention))
+		Expect(rke2ServerConfig.EtcdSnapshotScheduleCron).To(Equal(serverConfig.Etcd.BackupConfig.ScheduleCron))
+		Expect(rke2ServerConfig.EtcdS3SkipSslVerify).To(BeFalse())
+		Expect(rke2ServerConfig.EtcdArgs).To(Equal(serverConfig.Etcd.CustomConfig.ExtraArgs))
+		Expect(rke2ServerConfig.EtcdImage).To(Equal(serverConfig.Etcd.CustomConfig.OverrideImage))
+		Expect(rke2ServerConfig.EtcdExtraMounts).To(Equal(serverConfig.Etcd.CustomConfig.ExtraMounts))
+		Expect(rke2ServerConfig.EtcdExtraEnv).To(Equal(serverConfig.Etcd.CustomConfig.ExtraEnv))
+		Expect(rke2ServerConfig.ServiceNodePortRange).To(Equal(rke2ServerConfig.ServiceNodePortRange))
+		Expect(rke2ServerConfig.TLSSan).To(Equal(append(serverConfig.TLSSan, opts.ControlPlaneEndpoint)))
+		Expect(rke2ServerConfig.KubeAPIServerArgs).To(Equal(serverConfig.KubeAPIServer.ExtraArgs))
+		Expect(rke2ServerConfig.KubeAPIserverImage).To(Equal(serverConfig.KubeAPIServer.OverrideImage))
+		Expect(rke2ServerConfig.KubeAPIserverExtraMounts).To(Equal(serverConfig.KubeAPIServer.ExtraMounts))
+		Expect(rke2ServerConfig.KubeAPIserverExtraEnv).To(Equal(serverConfig.KubeAPIServer.ExtraEnv))
+		Expect(rke2ServerConfig.KubeSchedulerArgs).To(Equal(serverConfig.KubeScheduler.ExtraArgs))
+		Expect(rke2ServerConfig.KubeSchedulerImage).To(Equal(serverConfig.KubeScheduler.OverrideImage))
+		Expect(rke2ServerConfig.KubeSchedulerExtraMounts).To(Equal(serverConfig.KubeScheduler.ExtraMounts))
+		Expect(rke2ServerConfig.KubeSchedulerExtraEnv).To(Equal(serverConfig.KubeScheduler.ExtraEnv))
+		Expect(rke2ServerConfig.KubeControllerManagerArgs).To(Equal(serverConfig.KubeControllerManager.ExtraArgs))
+		Expect(rke2ServerConfig.KubeControllerManagerImage).To(Equal(serverConfig.KubeControllerManager.OverrideImage))
+		Expect(rke2ServerConfig.KubeControllerManagerExtraMounts).To(Equal(serverConfig.KubeControllerManager.ExtraMounts))
+		Expect(rke2ServerConfig.KubeControllerManagerExtraEnv).To(Equal(serverConfig.KubeControllerManager.ExtraEnv))
+		Expect(rke2ServerConfig.CloudControllerManagerExtraMounts).To(Equal(serverConfig.CloudControllerManager.ExtraMounts))
+		Expect(rke2ServerConfig.CloudControllerManagerExtraEnv).To(Equal(serverConfig.CloudControllerManager.ExtraEnv))
+
+		Expect(files).To(HaveLen(3))
+
+		Expect(files[0].Path).To(Equal(rke2ServerConfig.AuditPolicyFile))
+		Expect(files[0].Content).To(Equal("test_audit"))
+		Expect(files[0].Owner).To(Equal("root:root"))
+		Expect(files[0].Permissions).To(Equal("0644"))
+
+		Expect(files[1].Path).To(Equal(rke2ServerConfig.CloudProviderConfig))
+		Expect(files[1].Content).To(Equal("test_cloud_config"))
+		Expect(files[1].Owner).To(Equal("root:root"))
+		Expect(files[1].Permissions).To(Equal("0644"))
+
+		Expect(files[2].Path).To(Equal(rke2ServerConfig.EtcdS3EndpointCA))
+		Expect(files[2].Content).To(Equal("test_ca"))
+		Expect(files[2].Owner).To(Equal("root:root"))
+		Expect(files[2].Permissions).To(Equal("0640"))
+	})
+})
+
+var _ = Describe("RKE2 Agent Config", func() {
+	var opts *RKE2AgentConfigOpts
+
+	BeforeEach(func() {
+		opts = &RKE2AgentConfigOpts{
+			ServerURL: "testurl",
+			Ctx:       context.Background(),
+			Token:     "testtoken",
+			Client: fake.NewClientBuilder().WithObjects(
+				&corev1.ConfigMap{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "test",
+						Namespace: "test",
+					},
+					Data: map[string]string{
+						"credential-config.yaml":       "test_credential_config",
+						"credential-provider-binaries": "test_credential_provider_binaries",
+						"resolv.conf":                  "test_resolv_conf",
+					},
+				},
+			).Build(),
+			AgentConfig: bootstrapv1.RKE2AgentConfig{
+				ContainerRuntimeEndpoint: "testendpoint",
+				DataDir:                  "testdir",
+				ImageCredentialProviderConfigMap: &corev1.ObjectReference{
+					Name:      "test",
+					Namespace: "test",
+				},
+				KubeletPath: "testpath",
+				Kubelet: &bootstrapv1.ComponentConfig{
+					ExtraArgs: []string{"testarg"},
+				},
+				LoadBalancerPort:      1234,
+				NodeLabels:            []string{"testlabel"},
+				NodeTaints:            []string{"testtaint"},
+				CISProfile:            bootstrapv1.CIS1_23,
+				ProtectKernelDefaults: true,
+				ResolvConf: &corev1.ObjectReference{
+					Name:      "test",
+					Namespace: "test",
+				},
+				RuntimeImage:            "testimage",
+				EnableContainerdSElinux: true,
+				Snapshotter:             "testsnapshotter",
+				KubeProxy: &bootstrapv1.ComponentConfig{
+					ExtraArgs:     []string{"testarg"},
+					OverrideImage: "testimage",
+					ExtraEnv:      map[string]string{"testenv": "testenv"},
+					ExtraMounts:   map[string]string{"testmount": "testmount"},
+				},
+			},
+		}
+	})
+
+	It("should succefully generate an agent config", func() {
+		agentConfig, files, err := newRKE2AgentConfig(*opts)
+		Expect(err).ToNot(HaveOccurred())
+
+		Expect(agentConfig.ContainerRuntimeEndpoint).To(Equal(opts.AgentConfig.ContainerRuntimeEndpoint))
+		Expect(agentConfig.DataDir).To(Equal(opts.AgentConfig.DataDir))
+		Expect(agentConfig.ImageCredentialProviderConfig).To(Equal("/etc/rancher/rke2/credential-config.yaml"))
+		Expect(agentConfig.ImageCredentialProviderBinDir).To(Equal("test_credential_provider_binaries"))
+		Expect(agentConfig.KubeletPath).To(Equal(opts.AgentConfig.KubeletPath))
+		Expect(agentConfig.KubeletArgs).To(Equal(opts.AgentConfig.Kubelet.ExtraArgs))
+		Expect(agentConfig.LbServerPort).To(Equal(opts.AgentConfig.LoadBalancerPort))
+		Expect(agentConfig.NodeLabels).To(Equal(opts.AgentConfig.NodeLabels))
+		Expect(agentConfig.NodeTaints).To(Equal(opts.AgentConfig.NodeTaints))
+		Expect(agentConfig.Profile).To(Equal(string(opts.AgentConfig.CISProfile)))
+		Expect(agentConfig.ProtectKernelDefaults).To(Equal(opts.AgentConfig.ProtectKernelDefaults))
+		Expect(agentConfig.ResolvConf).To(Equal("/etc/rancher/rke2/resolv.conf"))
+		Expect(agentConfig.RuntimeImage).To(Equal(opts.AgentConfig.RuntimeImage))
+		Expect(agentConfig.Selinux).To(Equal(opts.AgentConfig.EnableContainerdSElinux))
+		Expect(agentConfig.Server).To(Equal(opts.ServerURL))
+		Expect(agentConfig.Snapshotter).To(Equal(opts.AgentConfig.Snapshotter))
+		Expect(agentConfig.KubeProxyArgs).To(Equal(opts.AgentConfig.KubeProxy.ExtraArgs))
+		Expect(agentConfig.KubeProxyImage).To(Equal(opts.AgentConfig.KubeProxy.OverrideImage))
+		Expect(agentConfig.KubeProxyExtraMounts).To(Equal(opts.AgentConfig.KubeProxy.ExtraMounts))
+		Expect(agentConfig.KubeProxyExtraEnv).To(Equal(opts.AgentConfig.KubeProxy.ExtraEnv))
+		Expect(agentConfig.Token).To(Equal(opts.Token))
+
+		Expect(files).To(HaveLen(2))
+
+		Expect(files[0].Path).To(Equal(agentConfig.ImageCredentialProviderConfig))
+		Expect(files[0].Content).To(Equal("test_credential_config"))
+		Expect(files[0].Owner).To(Equal("root:root"))
+		Expect(files[0].Permissions).To(Equal("0644"))
+
+		Expect(files[1].Path).To(Equal(agentConfig.ResolvConf))
+		Expect(files[1].Content).To(Equal("test_resolv_conf"))
+		Expect(files[1].Owner).To(Equal("root:root"))
+		Expect(files[1].Permissions).To(Equal("0644"))
+	})
+})
diff --git a/pkg/rke2/suite_test.go b/pkg/rke2/suite_test.go
new file mode 100644
index 0000000..2958124
--- /dev/null
+++ b/pkg/rke2/suite_test.go
@@ -0,0 +1,19 @@
+package rke2
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
+	//+kubebuilder:scaffold:imports
+)
+
+func TestAPIs(t *testing.T) {
+	RegisterFailHandler(Fail)
+
+	RunSpecsWithDefaultAndCustomReporters(t,
+		"RKE2 Suite",
+		[]Reporter{printer.NewlineReporter{}})
+}