Fixes for PR #60

Signed-off-by: Mohamed Belgaied Hassine <belgaied2@hotmail.com>
This commit is contained in:
Mohamed Belgaied Hassine 2023-01-17 18:52:14 +00:00
parent 87e50626c9
commit e2c134c69b
No known key found for this signature in database
GPG Key ID: 391F62FCFA48CCD9
8 changed files with 132 additions and 110 deletions

View File

@ -73,8 +73,6 @@ type RKE2AgentConfig struct {
//+optional
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.
//+optional
ContainerRuntimeEndpoint string `json:"containerRuntimeEndpoint,omitempty"`
@ -83,16 +81,6 @@ type RKE2AgentConfig struct {
//+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.
//+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
// +kubebuilder:validation:Enum=cis-1.23
//+optional
@ -143,13 +131,6 @@ type RKE2AgentConfig struct {
// 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.
// 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"`
}

View File

@ -20,36 +20,37 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// RKE2ConfigTemplateSpec defines the desired state of RKE2ConfigTemplate
// RKE2ConfigTemplateSpec defines the specification of RKE2ConfigTemplate.
type RKE2ConfigTemplateSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Foo is an example field of RKE2ConfigTemplate. Edit RKE2configtemplate_types.go to remove/update
// Template references a RKE2ConfigTemplate, which is used to include an RKE2ConfigSpec struct. This is used to include a desired RKE2ConfigSpec configuration
// when an RKE2Config resource is generated by a MachineDeployment resource.
Template RKE2ConfigTemplateResource `json:"template"`
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// RKE2ConfigTemplate is the Schema for the RKE2configtemplates API
// RKE2ConfigTemplate is the Schema for the RKE2configtemplates API.
type RKE2ConfigTemplate struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec RKE2ConfigTemplateSpec `json:"spec,omitempty"`
// Spec details the RKE2ConfigTemplate specification.
Spec RKE2ConfigTemplateSpec `json:"spec"`
}
//+kubebuilder:object:root=true
// RKE2ConfigTemplateList contains a list of RKE2ConfigTemplate
// RKE2ConfigTemplateList contains a list of RKE2ConfigTemplate.
type RKE2ConfigTemplateList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []RKE2ConfigTemplate `json:"items"`
}
// RKE2ConfigTemplateResource is a struct that wraps the desired spec for the RKE2ConfigSpec inside the template field.
type RKE2ConfigTemplateResource struct {
// Spec is the RKE2ConfigSpec that should be used for the template.
Spec RKE2ConfigSpec `json:"spec"`
}

View File

@ -39,15 +39,9 @@ spec:
description: AgentConfig specifies configuration for the agent nodes.
properties:
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
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'
registries and RKE2 install scripts are not reachable.
type: boolean
cisProfile:
description: CISProfile activates CIS compliance of RKE2 for a

View File

@ -19,7 +19,7 @@ spec:
schema:
openAPIV3Schema:
description: RKE2ConfigTemplate is the Schema for the RKE2configtemplates
API
API.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
@ -34,30 +34,27 @@ spec:
metadata:
type: object
spec:
description: RKE2ConfigTemplateSpec defines the desired state of RKE2ConfigTemplate
description: Spec details the RKE2ConfigTemplate specification.
properties:
template:
description: Foo is an example field of RKE2ConfigTemplate. Edit RKE2configtemplate_types.go
to remove/update
description: Template references a RKE2ConfigTemplate, which is used
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:
spec:
description: RKE2ConfigSpec defines the desired state of RKE2Config.
description: Spec is the RKE2ConfigSpec that should be used for
the template.
properties:
agentConfig:
description: AgentConfig specifies configuration for the agent
nodes.
properties:
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
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'
are not reachable.
type: boolean
cisProfile:
description: CISProfile activates CIS compliance of RKE2
@ -511,6 +508,8 @@ spec:
required:
- template
type: object
required:
- spec
type: object
served: true
storage: true

View File

@ -49,6 +49,13 @@ import (
"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
type RKE2ConfigReconciler struct {
RKE2InitLock RKE2InitLock
@ -106,7 +113,7 @@ func (r *RKE2ConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{}, err
}
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
} else {
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
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 {
scope.Logger.Info("Requeuing because this machine is not a Control Plane machine")
return ctrl.Result{RequeueAfter: 1 * time.Minute}, nil
@ -267,13 +268,12 @@ func (r *RKE2ConfigReconciler) handleClusterNotInitialized(ctx context.Context,
}()
certificates := secret.NewCertificatesForInitialControlPlane()
err := certificates.LookupOrGenerate(
if err := certificates.LookupOrGenerate(
ctx,
r.Client,
util.ObjectKey(scope.Cluster),
*metav1.NewControllerRef(scope.Config, bootstrapv1.GroupVersion.WithKind("RKE2Config")),
)
if err != nil {
); err != nil {
conditions.MarkFalse(scope.Config, bootstrapv1.CertificatesAvailableCondition, bootstrapv1.CertificatesGenerationFailedReason, clusterv1.ConditionSeverityWarning, err.Error())
return ctrl.Result{}, err
}
@ -290,7 +290,7 @@ func (r *RKE2ConfigReconciler) handleClusterNotInitialized(ctx context.Context,
rke2.RKE2ServerConfigOpts{
ControlPlaneEndpoint: scope.Cluster.Spec.ControlPlaneEndpoint.Host,
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,
AgentConfig: scope.Config.Spec.AgentConfig,
Ctx: ctx,
@ -311,8 +311,8 @@ func (r *RKE2ConfigReconciler) handleClusterNotInitialized(ctx context.Context,
initConfigFile := bootstrapv1.File{
Path: rke2.DefaultRKE2ConfigLocation,
Content: string(b),
Owner: "root:root",
Permissions: "0640",
Owner: fileOwner,
Permissions: filePermissions,
}
// 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
}
// 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) {
tokenSecret := &corev1.Secret{}
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
}
token := string(tokenSecret.Data["value"])
@ -363,7 +364,7 @@ func (r *RKE2ConfigReconciler) joinControlplane(ctx context.Context, scope *Scop
Cluster: *scope.Cluster,
Token: token,
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,
AgentConfig: scope.Config.Spec.AgentConfig,
Ctx: ctx,
@ -388,11 +389,11 @@ func (r *RKE2ConfigReconciler) joinControlplane(ctx context.Context, scope *Scop
initConfigFile := bootstrapv1.File{
Path: rke2.DefaultRKE2ConfigLocation,
Content: string(b),
Owner: "root:root",
Permissions: "0640",
Owner: fileOwner,
Permissions: filePermissions,
}
// TODO: Implement adding additional files through API
// TODO: Implement adding additional files through API, coming in future PR
cpinput := &cloudinit.ControlPlaneInput{
BaseUserData: cloudinit.BaseUserData{
@ -416,7 +417,8 @@ func (r *RKE2ConfigReconciler) joinControlplane(ctx context.Context, scope *Scop
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) {
tokenSecret := &corev1.Secret{}
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(
rke2.RKE2AgentConfigOpts{
ServerURL: "https://" + scope.Cluster.Spec.ControlPlaneEndpoint.Host + ":9345",
ServerURL: fmt.Sprintf(serverURLFormat, scope.Cluster.Spec.ControlPlaneEndpoint.Host, registrationPort),
Token: token,
AgentConfig: scope.Config.Spec.AgentConfig,
Ctx: ctx,
@ -449,8 +451,8 @@ func (r *RKE2ConfigReconciler) joinWorker(ctx context.Context, scope *Scope) (re
wkJoinConfigFile := bootstrapv1.File{
Path: rke2.DefaultRKE2ConfigLocation,
Content: string(b),
Owner: "root:root",
Permissions: "0640",
Owner: fileOwner,
Permissions: filePermissions,
}
// 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
}
// 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) {
tokn, err := bsutil.Random(16)
token, err := bsutil.Random(16)
if err != nil {
return "", err
}
@ -503,24 +506,15 @@ func (r *RKE2ConfigReconciler) generateAndStoreToken(ctx context.Context, scope
},
},
Data: map[string][]byte{
"value": []byte(tokn),
"value": []byte(token),
},
Type: clusterv1.ClusterSecretType,
}
// as secret creation and scope.Config status patch are not atomic operations
// it is possible that secret creation happens but the config.Status patches are not applied
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)
if err := r.createOrUpdateSecretFromObject(*secret, ctx, scope.Logger, "token", *scope.Config); err != nil {
return "", err
}
// 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 tokn, nil
return token, nil
}
// 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{
{
APIVersion: bootstrapv1.GroupVersion.String(),
Kind: "RKE2Config",
APIVersion: scope.Config.APIVersion,
Kind: scope.Config.Kind,
Name: scope.Config.Name,
UID: scope.Config.UID,
Controller: pointer.BoolPtr(true),
@ -549,20 +543,25 @@ func (r *RKE2ConfigReconciler) storeBootstrapData(ctx context.Context, scope *Sc
Type: clusterv1.ClusterSecretType,
}
// as secret creation and scope.Config status patch are not atomic operations
// it is possible that secret creation happens but the config.Status patches are not applied
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)
if err := r.createOrUpdateSecretFromObject(*secret, ctx, scope.Logger, "bootstrap data", *scope.Config); err != nil {
return err
}
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.Ready = true
// conditions.MarkTrue(scope.Config, bootstrapv1.DataSecretAvailableCondition)
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
}

View File

@ -36,14 +36,9 @@ spec:
description: RKE2ControlPlaneSpec defines the desired state of RKE2ControlPlane
properties:
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
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'
registries and RKE2 install scripts are not reachable.
type: boolean
cisProfile:
description: CISProfile activates CIS compliance of RKE2 for a certain

View File

@ -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/))
- 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)))
@ -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
<mark>Question: Should the use be able to uninstall ?</mark>
### First configuration section: config.yaml
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
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

View File

@ -21,7 +21,7 @@ func matchesRCPConfiguration(infraConfigs map[string]*unstructured.Unstructured,
// matchesRKE2BootstrapConfig checks if machine's RKE2ConfigSpec is equivalent with RCP's RKE2ConfigSpec.
func matchesRKE2BootstrapConfig(machineConfigs map[string]*bootstrapv1.RKE2Config, rcp *controlplanev1.RKE2ControlPlane) collections.Func {
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
}
}