Fixes for PR #60
Signed-off-by: Mohamed Belgaied Hassine <belgaied2@hotmail.com>
This commit is contained in:
parent
87e50626c9
commit
e2c134c69b
|
|
@ -73,8 +73,6 @@ type RKE2AgentConfig struct {
|
||||||
//+optional
|
//+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?
|
|
||||||
|
|
||||||
// ContainerRuntimeEndpoint Disable embedded containerd and use alternative CRI implementation.
|
// ContainerRuntimeEndpoint Disable embedded containerd and use alternative CRI implementation.
|
||||||
//+optional
|
//+optional
|
||||||
ContainerRuntimeEndpoint string `json:"containerRuntimeEndpoint,omitempty"`
|
ContainerRuntimeEndpoint string `json:"containerRuntimeEndpoint,omitempty"`
|
||||||
|
|
@ -83,16 +81,6 @@ type RKE2AgentConfig struct {
|
||||||
//+optional
|
//+optional
|
||||||
Snapshotter string `json:"snapshotter,omitempty"`
|
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.
|
|
||||||
//+optional.
|
|
||||||
//NodeIp string `json:"nodeIp,omitempty"`
|
|
||||||
|
|
||||||
// NodeExternalIp IPv4/IPv6 external IP addresses to advertise for node.
|
|
||||||
//+optional
|
|
||||||
// NodeExternalIp string `json:"nodeExternalIp,omitempty"`
|
|
||||||
|
|
||||||
// CISProfile activates CIS compliance of RKE2 for a certain profile
|
// CISProfile activates CIS compliance of RKE2 for a certain profile
|
||||||
// +kubebuilder:validation:Enum=cis-1.23
|
// +kubebuilder:validation:Enum=cis-1.23
|
||||||
//+optional
|
//+optional
|
||||||
|
|
@ -143,13 +131,6 @@ type RKE2AgentConfig struct {
|
||||||
|
|
||||||
// AirGapped is a boolean value to define if the bootstrapping should be air-gapped,
|
// AirGapped is a boolean value to define if the bootstrapping should be air-gapped,
|
||||||
// basically supposing that online container registries and RKE2 install scripts are not reachable.
|
// basically supposing that online container registries and RKE2 install scripts are not reachable.
|
||||||
// NOTE: Make sure when using this option that you are using a custom machine image in the InfraMachineTemplates, this image needs to contain the necessary RKE2 files
|
|
||||||
// which are:
|
|
||||||
// - install.sh
|
|
||||||
// - rke2-images.linux-amd64.tar.zst
|
|
||||||
// - rke2.linux-amd64.tar.gz
|
|
||||||
// - sha256sum-amd64.txt
|
|
||||||
// from the RKE2 release page, these files should be the same as the ones from RKE2AgentConfig
|
|
||||||
AirGapped bool `json:"airGapped,omitempty"`
|
AirGapped bool `json:"airGapped,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,36 +20,37 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RKE2ConfigTemplateSpec defines the desired state of RKE2ConfigTemplate
|
// RKE2ConfigTemplateSpec defines the specification of RKE2ConfigTemplate.
|
||||||
type RKE2ConfigTemplateSpec struct {
|
type RKE2ConfigTemplateSpec struct {
|
||||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
// Template references a RKE2ConfigTemplate, which is used to include an RKE2ConfigSpec struct. This is used to include a desired RKE2ConfigSpec configuration
|
||||||
// Important: Run "make" to regenerate code after modifying this file
|
// when an RKE2Config resource is generated by a MachineDeployment resource.
|
||||||
|
|
||||||
// Foo is an example field of RKE2ConfigTemplate. Edit RKE2configtemplate_types.go to remove/update
|
|
||||||
Template RKE2ConfigTemplateResource `json:"template"`
|
Template RKE2ConfigTemplateResource `json:"template"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//+kubebuilder:object:root=true
|
//+kubebuilder:object:root=true
|
||||||
//+kubebuilder:subresource:status
|
//+kubebuilder:subresource:status
|
||||||
|
|
||||||
// RKE2ConfigTemplate is the Schema for the RKE2configtemplates API
|
// RKE2ConfigTemplate is the Schema for the RKE2configtemplates API.
|
||||||
type RKE2ConfigTemplate struct {
|
type RKE2ConfigTemplate struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
Spec RKE2ConfigTemplateSpec `json:"spec,omitempty"`
|
// Spec details the RKE2ConfigTemplate specification.
|
||||||
|
Spec RKE2ConfigTemplateSpec `json:"spec"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//+kubebuilder:object:root=true
|
//+kubebuilder:object:root=true
|
||||||
|
|
||||||
// RKE2ConfigTemplateList contains a list of RKE2ConfigTemplate
|
// RKE2ConfigTemplateList contains a list of RKE2ConfigTemplate.
|
||||||
type RKE2ConfigTemplateList struct {
|
type RKE2ConfigTemplateList struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
Items []RKE2ConfigTemplate `json:"items"`
|
Items []RKE2ConfigTemplate `json:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RKE2ConfigTemplateResource is a struct that wraps the desired spec for the RKE2ConfigSpec inside the template field.
|
||||||
type RKE2ConfigTemplateResource struct {
|
type RKE2ConfigTemplateResource struct {
|
||||||
|
// Spec is the RKE2ConfigSpec that should be used for the template.
|
||||||
Spec RKE2ConfigSpec `json:"spec"`
|
Spec RKE2ConfigSpec `json:"spec"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,15 +39,9 @@ spec:
|
||||||
description: AgentConfig specifies configuration for the agent nodes.
|
description: AgentConfig specifies configuration for the agent nodes.
|
||||||
properties:
|
properties:
|
||||||
airGapped:
|
airGapped:
|
||||||
description: 'AirGapped is a boolean value to define if the bootstrapping
|
description: AirGapped is a boolean value to define if the bootstrapping
|
||||||
should be air-gapped, basically supposing that online container
|
should be air-gapped, basically supposing that online container
|
||||||
registries and RKE2 install scripts are not reachable. NOTE:
|
registries and RKE2 install scripts are not reachable.
|
||||||
Make sure when using this option that you are using a custom
|
|
||||||
machine image in the InfraMachineTemplates, this image needs
|
|
||||||
to contain the necessary RKE2 files which are: - install.sh
|
|
||||||
- rke2-images.linux-amd64.tar.zst - rke2.linux-amd64.tar.gz
|
|
||||||
- sha256sum-amd64.txt from the RKE2 release page, these files
|
|
||||||
should be the same as the ones from RKE2AgentConfig'
|
|
||||||
type: boolean
|
type: boolean
|
||||||
cisProfile:
|
cisProfile:
|
||||||
description: CISProfile activates CIS compliance of RKE2 for a
|
description: CISProfile activates CIS compliance of RKE2 for a
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ spec:
|
||||||
schema:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
description: RKE2ConfigTemplate is the Schema for the RKE2configtemplates
|
description: RKE2ConfigTemplate is the Schema for the RKE2configtemplates
|
||||||
API
|
API.
|
||||||
properties:
|
properties:
|
||||||
apiVersion:
|
apiVersion:
|
||||||
description: 'APIVersion defines the versioned schema of this representation
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
|
@ -34,30 +34,27 @@ spec:
|
||||||
metadata:
|
metadata:
|
||||||
type: object
|
type: object
|
||||||
spec:
|
spec:
|
||||||
description: RKE2ConfigTemplateSpec defines the desired state of RKE2ConfigTemplate
|
description: Spec details the RKE2ConfigTemplate specification.
|
||||||
properties:
|
properties:
|
||||||
template:
|
template:
|
||||||
description: Foo is an example field of RKE2ConfigTemplate. Edit RKE2configtemplate_types.go
|
description: Template references a RKE2ConfigTemplate, which is used
|
||||||
to remove/update
|
to include an RKE2ConfigSpec struct. This is used to include a desired
|
||||||
|
RKE2ConfigSpec configuration when an RKE2Config resource is generated
|
||||||
|
by a MachineDeployment resource.
|
||||||
properties:
|
properties:
|
||||||
spec:
|
spec:
|
||||||
description: RKE2ConfigSpec defines the desired state of RKE2Config.
|
description: Spec is the RKE2ConfigSpec that should be used for
|
||||||
|
the template.
|
||||||
properties:
|
properties:
|
||||||
agentConfig:
|
agentConfig:
|
||||||
description: AgentConfig specifies configuration for the agent
|
description: AgentConfig specifies configuration for the agent
|
||||||
nodes.
|
nodes.
|
||||||
properties:
|
properties:
|
||||||
airGapped:
|
airGapped:
|
||||||
description: 'AirGapped is a boolean value to define if
|
description: AirGapped is a boolean value to define if
|
||||||
the bootstrapping should be air-gapped, basically supposing
|
the bootstrapping should be air-gapped, basically supposing
|
||||||
that online container registries and RKE2 install scripts
|
that online container registries and RKE2 install scripts
|
||||||
are not reachable. NOTE: Make sure when using this option
|
are not reachable.
|
||||||
that you are using a custom machine image in the InfraMachineTemplates,
|
|
||||||
this image needs to contain the necessary RKE2 files
|
|
||||||
which are: - install.sh - rke2-images.linux-amd64.tar.zst
|
|
||||||
- rke2.linux-amd64.tar.gz - sha256sum-amd64.txt from
|
|
||||||
the RKE2 release page, these files should be the same
|
|
||||||
as the ones from RKE2AgentConfig'
|
|
||||||
type: boolean
|
type: boolean
|
||||||
cisProfile:
|
cisProfile:
|
||||||
description: CISProfile activates CIS compliance of RKE2
|
description: CISProfile activates CIS compliance of RKE2
|
||||||
|
|
@ -511,6 +508,8 @@ spec:
|
||||||
required:
|
required:
|
||||||
- template
|
- template
|
||||||
type: object
|
type: object
|
||||||
|
required:
|
||||||
|
- spec
|
||||||
type: object
|
type: object
|
||||||
served: true
|
served: true
|
||||||
storage: true
|
storage: true
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,13 @@ import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileOwner string = "root:root"
|
||||||
|
filePermissions string = "0640"
|
||||||
|
registrationPort int = 9345
|
||||||
|
serverURLFormat string = "https://%v:%v"
|
||||||
|
)
|
||||||
|
|
||||||
// RKE2ConfigReconciler reconciles a Rke2Config object
|
// RKE2ConfigReconciler reconciles a Rke2Config object
|
||||||
type RKE2ConfigReconciler struct {
|
type RKE2ConfigReconciler struct {
|
||||||
RKE2InitLock RKE2InitLock
|
RKE2InitLock RKE2InitLock
|
||||||
|
|
@ -106,7 +113,7 @@ func (r *RKE2ConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
if cp == nil {
|
if cp == nil {
|
||||||
logger.Info("This config is for a worker node")
|
logger.V(5).Info("This config is for a worker node")
|
||||||
scope.HasControlPlaneOwner = false
|
scope.HasControlPlaneOwner = false
|
||||||
} else {
|
} else {
|
||||||
logger.Info("This config is for a ControlPlane node")
|
logger.Info("This config is for a ControlPlane node")
|
||||||
|
|
@ -242,12 +249,6 @@ func (r *RKE2ConfigReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
// handleClusterNotInitialized handles the first control plane node
|
// handleClusterNotInitialized handles the first control plane node
|
||||||
func (r *RKE2ConfigReconciler) handleClusterNotInitialized(ctx context.Context, scope *Scope) (res ctrl.Result, reterr error) {
|
func (r *RKE2ConfigReconciler) handleClusterNotInitialized(ctx context.Context, scope *Scope) (res ctrl.Result, reterr error) {
|
||||||
|
|
||||||
// initialize the DataSecretAvailableCondition if missing.
|
|
||||||
// this is required in order to avoid the condition's LastTransitionTime to flicker in case of errors surfacing
|
|
||||||
// using the DataSecretGeneratedFailedReason
|
|
||||||
// if conditions.GetReason(scope.Config, bootstrapv1.DataSecretAvailableCondition) != bootstrapv1.DataSecretGenerationFailedReason {
|
|
||||||
// conditions.MarkFalse(scope.Config, bootstrapv1.DataSecretAvailableCondition, clusterv1.WaitingForControlPlaneAvailableReason, clusterv1.ConditionSeverityInfo, "")
|
|
||||||
// }
|
|
||||||
if !scope.HasControlPlaneOwner {
|
if !scope.HasControlPlaneOwner {
|
||||||
scope.Logger.Info("Requeuing because this machine is not a Control Plane machine")
|
scope.Logger.Info("Requeuing because this machine is not a Control Plane machine")
|
||||||
return ctrl.Result{RequeueAfter: 1 * time.Minute}, nil
|
return ctrl.Result{RequeueAfter: 1 * time.Minute}, nil
|
||||||
|
|
@ -267,13 +268,12 @@ func (r *RKE2ConfigReconciler) handleClusterNotInitialized(ctx context.Context,
|
||||||
}()
|
}()
|
||||||
|
|
||||||
certificates := secret.NewCertificatesForInitialControlPlane()
|
certificates := secret.NewCertificatesForInitialControlPlane()
|
||||||
err := certificates.LookupOrGenerate(
|
if err := certificates.LookupOrGenerate(
|
||||||
ctx,
|
ctx,
|
||||||
r.Client,
|
r.Client,
|
||||||
util.ObjectKey(scope.Cluster),
|
util.ObjectKey(scope.Cluster),
|
||||||
*metav1.NewControllerRef(scope.Config, bootstrapv1.GroupVersion.WithKind("RKE2Config")),
|
*metav1.NewControllerRef(scope.Config, bootstrapv1.GroupVersion.WithKind("RKE2Config")),
|
||||||
)
|
); err != nil {
|
||||||
if err != nil {
|
|
||||||
conditions.MarkFalse(scope.Config, bootstrapv1.CertificatesAvailableCondition, bootstrapv1.CertificatesGenerationFailedReason, clusterv1.ConditionSeverityWarning, err.Error())
|
conditions.MarkFalse(scope.Config, bootstrapv1.CertificatesAvailableCondition, bootstrapv1.CertificatesGenerationFailedReason, clusterv1.ConditionSeverityWarning, err.Error())
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +290,7 @@ func (r *RKE2ConfigReconciler) handleClusterNotInitialized(ctx context.Context,
|
||||||
rke2.RKE2ServerConfigOpts{
|
rke2.RKE2ServerConfigOpts{
|
||||||
ControlPlaneEndpoint: scope.Cluster.Spec.ControlPlaneEndpoint.Host,
|
ControlPlaneEndpoint: scope.Cluster.Spec.ControlPlaneEndpoint.Host,
|
||||||
Token: token,
|
Token: token,
|
||||||
ServerURL: "https://" + scope.Cluster.Spec.ControlPlaneEndpoint.Host + ":9345",
|
ServerURL: fmt.Sprintf(serverURLFormat, scope.Cluster.Spec.ControlPlaneEndpoint.Host, registrationPort),
|
||||||
ServerConfig: scope.ControlPlane.Spec.ServerConfig,
|
ServerConfig: scope.ControlPlane.Spec.ServerConfig,
|
||||||
AgentConfig: scope.Config.Spec.AgentConfig,
|
AgentConfig: scope.Config.Spec.AgentConfig,
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
|
|
@ -311,8 +311,8 @@ func (r *RKE2ConfigReconciler) handleClusterNotInitialized(ctx context.Context,
|
||||||
initConfigFile := bootstrapv1.File{
|
initConfigFile := bootstrapv1.File{
|
||||||
Path: rke2.DefaultRKE2ConfigLocation,
|
Path: rke2.DefaultRKE2ConfigLocation,
|
||||||
Content: string(b),
|
Content: string(b),
|
||||||
Owner: "root:root",
|
Owner: fileOwner,
|
||||||
Permissions: "0640",
|
Permissions: filePermissions,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement adding additional files through API
|
// TODO: Implement adding additional files through API
|
||||||
|
|
@ -346,12 +346,13 @@ type RKE2InitLock interface {
|
||||||
Lock(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine) bool
|
Lock(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement these functions
|
// joinControlPlane implements the part of the Reconciler which bootstraps a secondary
|
||||||
|
// Control Plane machine joining a cluster that is already initialized
|
||||||
func (r *RKE2ConfigReconciler) joinControlplane(ctx context.Context, scope *Scope) (res ctrl.Result, rerr error) {
|
func (r *RKE2ConfigReconciler) joinControlplane(ctx context.Context, scope *Scope) (res ctrl.Result, rerr error) {
|
||||||
|
|
||||||
tokenSecret := &corev1.Secret{}
|
tokenSecret := &corev1.Secret{}
|
||||||
if err := r.Client.Get(ctx, types.NamespacedName{Namespace: scope.Cluster.Namespace, Name: scope.Cluster.Name + "-token"}, tokenSecret); err != nil {
|
if err := r.Client.Get(ctx, types.NamespacedName{Namespace: scope.Cluster.Namespace, Name: scope.Cluster.Name + "-token"}, tokenSecret); err != nil {
|
||||||
scope.Logger.Info("Token for already initialized RKE2 Cluster not found", "token-namespace", scope.Cluster.Namespace, "token-name", scope.Cluster.Name+"-token")
|
scope.Logger.Error(err, "Token for already initialized RKE2 Cluster not found", "token-namespace", scope.Cluster.Namespace, "token-name", scope.Cluster.Name+"-token")
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
token := string(tokenSecret.Data["value"])
|
token := string(tokenSecret.Data["value"])
|
||||||
|
|
@ -363,7 +364,7 @@ func (r *RKE2ConfigReconciler) joinControlplane(ctx context.Context, scope *Scop
|
||||||
Cluster: *scope.Cluster,
|
Cluster: *scope.Cluster,
|
||||||
Token: token,
|
Token: token,
|
||||||
ControlPlaneEndpoint: scope.Cluster.Spec.ControlPlaneEndpoint.Host,
|
ControlPlaneEndpoint: scope.Cluster.Spec.ControlPlaneEndpoint.Host,
|
||||||
ServerURL: "https://" + scope.Cluster.Spec.ControlPlaneEndpoint.Host + ":9345",
|
ServerURL: fmt.Sprintf(serverURLFormat, scope.Cluster.Spec.ControlPlaneEndpoint.Host, registrationPort),
|
||||||
ServerConfig: scope.ControlPlane.Spec.ServerConfig,
|
ServerConfig: scope.ControlPlane.Spec.ServerConfig,
|
||||||
AgentConfig: scope.Config.Spec.AgentConfig,
|
AgentConfig: scope.Config.Spec.AgentConfig,
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
|
|
@ -388,11 +389,11 @@ func (r *RKE2ConfigReconciler) joinControlplane(ctx context.Context, scope *Scop
|
||||||
initConfigFile := bootstrapv1.File{
|
initConfigFile := bootstrapv1.File{
|
||||||
Path: rke2.DefaultRKE2ConfigLocation,
|
Path: rke2.DefaultRKE2ConfigLocation,
|
||||||
Content: string(b),
|
Content: string(b),
|
||||||
Owner: "root:root",
|
Owner: fileOwner,
|
||||||
Permissions: "0640",
|
Permissions: filePermissions,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement adding additional files through API
|
// TODO: Implement adding additional files through API, coming in future PR
|
||||||
|
|
||||||
cpinput := &cloudinit.ControlPlaneInput{
|
cpinput := &cloudinit.ControlPlaneInput{
|
||||||
BaseUserData: cloudinit.BaseUserData{
|
BaseUserData: cloudinit.BaseUserData{
|
||||||
|
|
@ -416,7 +417,8 @@ func (r *RKE2ConfigReconciler) joinControlplane(ctx context.Context, scope *Scop
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement these functions
|
// joinWorker implements the part of the Reconciler which bootstraps a worker node
|
||||||
|
// after the cluster has been initialized
|
||||||
func (r *RKE2ConfigReconciler) joinWorker(ctx context.Context, scope *Scope) (res ctrl.Result, rerr error) {
|
func (r *RKE2ConfigReconciler) joinWorker(ctx context.Context, scope *Scope) (res ctrl.Result, rerr error) {
|
||||||
tokenSecret := &corev1.Secret{}
|
tokenSecret := &corev1.Secret{}
|
||||||
if err := r.Client.Get(ctx, types.NamespacedName{Namespace: scope.Cluster.Namespace, Name: scope.Cluster.Name + "-token"}, tokenSecret); err != nil {
|
if err := r.Client.Get(ctx, types.NamespacedName{Namespace: scope.Cluster.Namespace, Name: scope.Cluster.Name + "-token"}, tokenSecret); err != nil {
|
||||||
|
|
@ -428,7 +430,7 @@ func (r *RKE2ConfigReconciler) joinWorker(ctx context.Context, scope *Scope) (re
|
||||||
|
|
||||||
configStruct, files, err := rke2.GenerateWorkerConfig(
|
configStruct, files, err := rke2.GenerateWorkerConfig(
|
||||||
rke2.RKE2AgentConfigOpts{
|
rke2.RKE2AgentConfigOpts{
|
||||||
ServerURL: "https://" + scope.Cluster.Spec.ControlPlaneEndpoint.Host + ":9345",
|
ServerURL: fmt.Sprintf(serverURLFormat, scope.Cluster.Spec.ControlPlaneEndpoint.Host, registrationPort),
|
||||||
Token: token,
|
Token: token,
|
||||||
AgentConfig: scope.Config.Spec.AgentConfig,
|
AgentConfig: scope.Config.Spec.AgentConfig,
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
|
|
@ -449,8 +451,8 @@ func (r *RKE2ConfigReconciler) joinWorker(ctx context.Context, scope *Scope) (re
|
||||||
wkJoinConfigFile := bootstrapv1.File{
|
wkJoinConfigFile := bootstrapv1.File{
|
||||||
Path: rke2.DefaultRKE2ConfigLocation,
|
Path: rke2.DefaultRKE2ConfigLocation,
|
||||||
Content: string(b),
|
Content: string(b),
|
||||||
Owner: "root:root",
|
Owner: fileOwner,
|
||||||
Permissions: "0640",
|
Permissions: filePermissions,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement adding additional files through API
|
// TODO: Implement adding additional files through API
|
||||||
|
|
@ -477,8 +479,9 @@ func (r *RKE2ConfigReconciler) joinWorker(ctx context.Context, scope *Scope) (re
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateAndStoreToken generates a random token with 16 characters then stores it in a Secret in the API
|
||||||
func (r *RKE2ConfigReconciler) generateAndStoreToken(ctx context.Context, scope *Scope) (string, error) {
|
func (r *RKE2ConfigReconciler) generateAndStoreToken(ctx context.Context, scope *Scope) (string, error) {
|
||||||
tokn, err := bsutil.Random(16)
|
token, err := bsutil.Random(16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -503,24 +506,15 @@ func (r *RKE2ConfigReconciler) generateAndStoreToken(ctx context.Context, scope
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"value": []byte(tokn),
|
"value": []byte(token),
|
||||||
},
|
},
|
||||||
Type: clusterv1.ClusterSecretType,
|
Type: clusterv1.ClusterSecretType,
|
||||||
}
|
}
|
||||||
|
|
||||||
// as secret creation and scope.Config status patch are not atomic operations
|
if err := r.createOrUpdateSecretFromObject(*secret, ctx, scope.Logger, "token", *scope.Config); err != nil {
|
||||||
// it is possible that secret creation happens but the config.Status patches are not applied
|
return "", err
|
||||||
if err := r.Client.Create(ctx, secret); err != nil {
|
|
||||||
if !apierrors.IsAlreadyExists(err) {
|
|
||||||
return "", errors.Wrapf(err, "failed to create token for RKE2Config %s/%s", scope.Config.Namespace, scope.Config.Name)
|
|
||||||
}
|
|
||||||
// r.Log.Info("bootstrap data secret for RKE2Config already exists, updating", "secret", secret.Name, "RKE2Config", scope.Config.Name)
|
|
||||||
if err := r.Client.Update(ctx, secret); err != nil {
|
|
||||||
return "", errors.Wrapf(err, "failed to update bootstrap token secret for RKE2Config %s/%s", scope.Config.Namespace, scope.Config.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return token, nil
|
||||||
return tokn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// storeBootstrapData creates a new secret with the data passed in as input,
|
// storeBootstrapData creates a new secret with the data passed in as input,
|
||||||
|
|
@ -535,8 +529,8 @@ func (r *RKE2ConfigReconciler) storeBootstrapData(ctx context.Context, scope *Sc
|
||||||
},
|
},
|
||||||
OwnerReferences: []metav1.OwnerReference{
|
OwnerReferences: []metav1.OwnerReference{
|
||||||
{
|
{
|
||||||
APIVersion: bootstrapv1.GroupVersion.String(),
|
APIVersion: scope.Config.APIVersion,
|
||||||
Kind: "RKE2Config",
|
Kind: scope.Config.Kind,
|
||||||
Name: scope.Config.Name,
|
Name: scope.Config.Name,
|
||||||
UID: scope.Config.UID,
|
UID: scope.Config.UID,
|
||||||
Controller: pointer.BoolPtr(true),
|
Controller: pointer.BoolPtr(true),
|
||||||
|
|
@ -549,20 +543,25 @@ func (r *RKE2ConfigReconciler) storeBootstrapData(ctx context.Context, scope *Sc
|
||||||
Type: clusterv1.ClusterSecretType,
|
Type: clusterv1.ClusterSecretType,
|
||||||
}
|
}
|
||||||
|
|
||||||
// as secret creation and scope.Config status patch are not atomic operations
|
if err := r.createOrUpdateSecretFromObject(*secret, ctx, scope.Logger, "bootstrap data", *scope.Config); err != nil {
|
||||||
// it is possible that secret creation happens but the config.Status patches are not applied
|
return err
|
||||||
if err := r.Client.Create(ctx, secret); err != nil {
|
|
||||||
if !apierrors.IsAlreadyExists(err) {
|
|
||||||
return errors.Wrapf(err, "failed to create bootstrap data secret for RKE2Config %s/%s", scope.Config.Namespace, scope.Config.Name)
|
|
||||||
}
|
|
||||||
scope.Logger.Info("bootstrap data secret for RKE2Config already exists, updating", "secret", secret.Name, "RKE2Config", scope.Config.Name)
|
|
||||||
if err := r.Client.Update(ctx, secret); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to update bootstrap data secret for RKE2Config %s/%s", scope.Config.Namespace, scope.Config.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Config.Status.DataSecretName = pointer.StringPtr(secret.Name)
|
scope.Config.Status.DataSecretName = pointer.StringPtr(secret.Name)
|
||||||
scope.Config.Status.Ready = true
|
scope.Config.Status.Ready = true
|
||||||
// conditions.MarkTrue(scope.Config, bootstrapv1.DataSecretAvailableCondition)
|
// conditions.MarkTrue(scope.Config, bootstrapv1.DataSecretAvailableCondition)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createOrUpdateSecret tries to create the given secret in the API, if that secret exists it will update it.
|
||||||
|
func (r *RKE2ConfigReconciler) createOrUpdateSecretFromObject(secret corev1.Secret, ctx context.Context, logger logr.Logger, secretType string, config bootstrapv1.RKE2Config) (reterr error) {
|
||||||
|
if err := r.Client.Create(ctx, &secret); err != nil {
|
||||||
|
if !apierrors.IsAlreadyExists(err) {
|
||||||
|
return errors.Wrapf(err, "failed to create %s secret for %s: %s/%s", secretType, config.Kind, config.Name, config.Namespace)
|
||||||
|
}
|
||||||
|
logger.Info("%s secret for %s %s already exists, updating", secretType, config.Kind, config.Name)
|
||||||
|
if err := r.Client.Update(ctx, &secret); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to update %s secret for %s: %s/%s", secretType, config.Kind, config.Namespace, config.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,9 @@ spec:
|
||||||
description: RKE2ControlPlaneSpec defines the desired state of RKE2ControlPlane
|
description: RKE2ControlPlaneSpec defines the desired state of RKE2ControlPlane
|
||||||
properties:
|
properties:
|
||||||
airGapped:
|
airGapped:
|
||||||
description: 'AirGapped is a boolean value to define if the bootstrapping
|
description: AirGapped is a boolean value to define if the bootstrapping
|
||||||
should be air-gapped, basically supposing that online container
|
should be air-gapped, basically supposing that online container
|
||||||
registries and RKE2 install scripts are not reachable. NOTE: Make
|
registries and RKE2 install scripts are not reachable.
|
||||||
sure when using this option that you are using a custom machine
|
|
||||||
image in the InfraMachineTemplates, this image needs to contain
|
|
||||||
the necessary RKE2 files which are: - install.sh - rke2-images.linux-amd64.tar.zst
|
|
||||||
- rke2.linux-amd64.tar.gz - sha256sum-amd64.txt from the RKE2 release
|
|
||||||
page, these files should be the same as the ones from RKE2AgentConfig'
|
|
||||||
type: boolean
|
type: boolean
|
||||||
cisProfile:
|
cisProfile:
|
||||||
description: CISProfile activates CIS compliance of RKE2 for a certain
|
description: CISProfile activates CIS compliance of RKE2 for a certain
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ RKE2 is a very configurable Kubernetes distribution. The main ways to configure
|
||||||
|
|
||||||
- config.yaml file (default location at /etc/rancher/rke2/): configuration options for RKE2 that are described in this [documentation page]([Server Configuration Reference - RKE2 - Rancher's Next Generation Kubernetes Distribution](https://docs.rke2.io/install/install_options/server_config/))
|
- config.yaml file (default location at /etc/rancher/rke2/): configuration options for RKE2 that are described in this [documentation page]([Server Configuration Reference - RKE2 - Rancher's Next Generation Kubernetes Distribution](https://docs.rke2.io/install/install_options/server_config/))
|
||||||
|
|
||||||
- registries.yaml ()
|
- registries.yaml (default location at /etc/rancher/rke2/): Container Image registry configuration for the cluster (mirrors, rewrites, etc.), documentation available in this [RKE2 Documentation page](https://docs.rke2.io/install/containerd_registry_configuration)
|
||||||
|
|
||||||
- Environement variables for versions, etc. (options documented [here]([Overview - RKE2 - Rancher's Next Generation Kubernetes Distribution](https://docs.rke2.io/install/install_options/install_options/#configuring-the-linux-installation-script)))
|
- Environement variables for versions, etc. (options documented [here]([Overview - RKE2 - Rancher's Next Generation Kubernetes Distribution](https://docs.rke2.io/install/install_options/install_options/#configuring-the-linux-installation-script)))
|
||||||
|
|
||||||
|
|
@ -22,9 +22,6 @@ RKE2 is a very configurable Kubernetes distribution. The main ways to configure
|
||||||
|
|
||||||
- Should be possible to deploy in **Air-Gapped** mode
|
- Should be possible to deploy in **Air-Gapped** mode
|
||||||
|
|
||||||
|
|
||||||
<mark>Question: Should the use be able to uninstall ?</mark>
|
|
||||||
|
|
||||||
### First configuration section: config.yaml
|
### First configuration section: config.yaml
|
||||||
|
|
||||||
In order to make RKE2 installation sufficiently configurable, we rely on the documentation page above and implement all options.
|
In order to make RKE2 installation sufficiently configurable, we rely on the documentation page above and implement all options.
|
||||||
|
|
@ -516,3 +513,59 @@ type CisProfile string
|
||||||
// +kubebuilder:validation:enum=none;calico;canal;cilium
|
// +kubebuilder:validation:enum=none;calico;canal;cilium
|
||||||
type Cni string
|
type Cni string
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Further improvement
|
||||||
|
After generating the previous Go Data types, some improvements were done by nesting some configurations that are relevant to the same components, as well as making some parts re-usable.
|
||||||
|
|
||||||
|
Now, the `RKE2ControlPlaneSpec` struct (`spec` field of the `rke2controlplane` resource ) embeds a `RKE2ConfigSpec` struct (inline) which contains the fields of the `spec` field of `rke2config` resource. This makes it possible to embed Agent configuration inside the control plane resource.
|
||||||
|
|
||||||
|
This is an extract of the `RKE2ControlPlaneSpec` struct:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// RKE2ControlPlaneSpec defines the desired state of RKE2ControlPlane
|
||||||
|
type RKE2ControlPlaneSpec struct {
|
||||||
|
// Replicas is the number of replicas for the Control Plane
|
||||||
|
Replicas *int32 `json:"replicas,omitempty"`
|
||||||
|
|
||||||
|
// RKE2AgentConfig references fields from the Agent Configuration in the Bootstrap Provider because an RKE2 Server node also has an agent
|
||||||
|
bootstrapv1.RKE2ConfigSpec `json:",inline"`
|
||||||
|
|
||||||
|
// ServerConfig specifies configuration for the agent nodes.
|
||||||
|
//+optional
|
||||||
|
ServerConfig RKE2ServerConfig `json:"serverConfig,omitempty"`
|
||||||
|
|
||||||
|
// ManifestsConfigMapReference references a ConfigMap which contains Kubernetes manifests to be deployed automatically on the cluster
|
||||||
|
// Each data entry in the ConfigMap will be will be copied to a folder on the control plane nodes that RKE2 scans and uses to deploy manifests.
|
||||||
|
//+optional
|
||||||
|
ManifestsConfigMapReference corev1.ObjectReference `json:"manifestsConfigMapReference,omitempty"`
|
||||||
|
|
||||||
|
// InfrastructureRef is a required reference to a custom resource
|
||||||
|
// offered by an infrastructure provider.
|
||||||
|
InfrastructureRef corev1.ObjectReference `json:"infrastructureRef"`
|
||||||
|
|
||||||
|
// NodeDrainTimeout is the total amount of time that the controller will spend on draining a controlplane node
|
||||||
|
// The default value is 0, meaning that the node can be drained without any time limitations.
|
||||||
|
// NOTE: NodeDrainTimeout is different from `kubectl drain --timeout`
|
||||||
|
// +optional
|
||||||
|
NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `manifestsConfigMapReference` Field
|
||||||
|
This field has been added to the `RKE2ControlPlaneSpec` struct in order to make it easier for the user to provider manifests to deploy on the cluster. This is an RKE2 feature, that might be useful for deploying a custom CNI for instance during a cluster bootstrap. We will monitor feedback on this feature since it might be duplicated by the `ResourceSet` feature of upstream CAPI.
|
||||||
|
|
||||||
|
#### `privateRegistryConfig` field in `RKE2ConfigSpec` struct
|
||||||
|
This field has been added to simplify defining custom registries or mirror configurations for RKE2. It relies on a similar structure than the native RKE2's `registries.yaml` but references Secrets to store Trust Certificates.
|
||||||
|
|
||||||
|
#### Air-gapped mode
|
||||||
|
The Bootstrap provider for RKE2 implements an Air-Gapped mode, which is based on [Tarball-based Air-Gapped installation procedure of RKE2](https://docs.rke2.io/install/airgap#tarball-method). If the `rke2config.spec.agentConfig.airGapped` field (`AirGapped` field of the `RKE2AgentConfig` struct) is set to `true`, the bootstrap provider will:
|
||||||
|
- Assume the usage of a custom VM Image that contains the necessary RKE2 assets.
|
||||||
|
- Execute the above tarball installation of RKE2, executing `/opt/install.sh` which it assumes would be available in the VM image. This `install.sh` is nothing more than the downloaded script from `https://get.rke2.io` URL.
|
||||||
|
|
||||||
|
The necessary files should be at these locations:
|
||||||
|
- Those who don't depend on RKE2's version:
|
||||||
|
- /opt/install.sh
|
||||||
|
- Those which depend on RKE2's version, available in RKE2's release page assets:
|
||||||
|
- /opt/rke2-artifacts/rke2-images.linux-amd64.tar.zst
|
||||||
|
- /opt/rke2-artifacts/rke2.linux-amd64.tar.gz
|
||||||
|
- /opt/rke2-artifacts/sha256sum-amd64.txt
|
||||||
|
|
@ -21,7 +21,7 @@ func matchesRCPConfiguration(infraConfigs map[string]*unstructured.Unstructured,
|
||||||
// matchesRKE2BootstrapConfig checks if machine's RKE2ConfigSpec is equivalent with RCP's RKE2ConfigSpec.
|
// matchesRKE2BootstrapConfig checks if machine's RKE2ConfigSpec is equivalent with RCP's RKE2ConfigSpec.
|
||||||
func matchesRKE2BootstrapConfig(machineConfigs map[string]*bootstrapv1.RKE2Config, rcp *controlplanev1.RKE2ControlPlane) collections.Func {
|
func matchesRKE2BootstrapConfig(machineConfigs map[string]*bootstrapv1.RKE2Config, rcp *controlplanev1.RKE2ControlPlane) collections.Func {
|
||||||
return func(machine *clusterv1.Machine) bool {
|
return func(machine *clusterv1.Machine) bool {
|
||||||
return true // TODO: implement
|
return true // TODO: implement by using inspiration from https://github.com/kubernetes-sigs/cluster-api/blob/v1.3.2/controlplane/kubeadm/internal/filters.go#L81
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue