diff --git a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_types.go b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_types.go index 1a9ccf073831..9c1abde9ff68 100644 --- a/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_types.go +++ b/controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_types.go @@ -117,6 +117,11 @@ type KubeadmControlPlaneSpec struct { // The RemediationStrategy that controls how control plane machine remediation happens. // +optional RemediationStrategy *RemediationStrategy `json:"remediationStrategy,omitempty"` + + // MachineNamingStrategy allows changing the naming pattern used when creating Machines. + // Note: InfraMachines & KubeadmConfigs will use the same name as the corresponding Machines. + // +optional + MachineNamingStrategy *MachineNamingStrategy `json:"machineNamingStrategy,omitempty"` } // KubeadmControlPlaneMachineTemplate defines the template for Machines @@ -228,6 +233,20 @@ type RemediationStrategy struct { MinHealthyPeriod *metav1.Duration `json:"minHealthyPeriod,omitempty"` } +// MachineNamingStrategy allows changing the naming pattern used when creating Machines. +// Note: InfraMachines & KubeadmConfigs will use the same name as the corresponding Machines. +type MachineNamingStrategy struct { + // Template defines the template to use for generating the names of the Machine objects. + // If not defined, it will fallback to `{{ .kubeadmControlPlane.name }}-{{ .random }}`. + // If the templated string exceeds 63 characters, it will be trimmed to 58 characters and will + // get concatenated with a random suffix of length 5. + // The templating mechanism provides the following arguments: + // * `.kubeadmControlPlane.name`: The name of the KubeadmControlPlane object. + // * `.random`: A random alphanumeric string, without vowels, of length 5. + // +optional + Template *string `json:"template,omitempty"` +} + // KubeadmControlPlaneStatus defines the observed state of KubeadmControlPlane. type KubeadmControlPlaneStatus struct { // Selector is the label selector in string format to avoid introspection diff --git a/controlplane/kubeadm/api/v1beta1/zz_generated.deepcopy.go b/controlplane/kubeadm/api/v1beta1/zz_generated.deepcopy.go index fdef3a38dfbd..76dbe68ebfe8 100644 --- a/controlplane/kubeadm/api/v1beta1/zz_generated.deepcopy.go +++ b/controlplane/kubeadm/api/v1beta1/zz_generated.deepcopy.go @@ -147,6 +147,11 @@ func (in *KubeadmControlPlaneSpec) DeepCopyInto(out *KubeadmControlPlaneSpec) { *out = new(RemediationStrategy) (*in).DeepCopyInto(*out) } + if in.MachineNamingStrategy != nil { + in, out := &in.MachineNamingStrategy, &out.MachineNamingStrategy + *out = new(MachineNamingStrategy) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneSpec. @@ -374,6 +379,26 @@ func (in *LastRemediationStatus) DeepCopy() *LastRemediationStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineNamingStrategy) DeepCopyInto(out *MachineNamingStrategy) { + *out = *in + if in.Template != nil { + in, out := &in.Template, &out.Template + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineNamingStrategy. +func (in *MachineNamingStrategy) DeepCopy() *MachineNamingStrategy { + if in == nil { + return nil + } + out := new(MachineNamingStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RemediationStrategy) DeepCopyInto(out *RemediationStrategy) { *out = *in diff --git a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml index cb4668d56d03..49d91df85c30 100644 --- a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml +++ b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml @@ -4185,6 +4185,22 @@ spec: format: int32 type: integer type: object + machineNamingStrategy: + description: |- + MachineNamingStrategy allows changing the naming pattern used when creating Machines. + Note: InfraMachines & KubeadmConfigs will use the same name as the corresponding Machines. + properties: + template: + description: |- + Template defines the template to use for generating the names of the Machine objects. + If not defined, it will fallback to `{{ .kubeadmControlPlane.name }}-{{ .random }}`. + If the templated string exceeds 63 characters, it will be trimmed to 58 characters and will + get concatenated with a random suffix of length 5. + The templating mechanism provides the following arguments: + * `.kubeadmControlPlane.name`: The name of the KubeadmControlPlane object. + * `.random`: A random alphanumeric string, without vowels, of length 5. + type: string + type: object machineTemplate: description: |- MachineTemplate contains information about how machines diff --git a/controlplane/kubeadm/internal/controllers/helpers.go b/controlplane/kubeadm/internal/controllers/helpers.go index f7e3a93f758d..8d304bc964a4 100644 --- a/controlplane/kubeadm/internal/controllers/helpers.go +++ b/controlplane/kubeadm/internal/controllers/helpers.go @@ -37,6 +37,7 @@ import ( "sigs.k8s.io/cluster-api/controllers/external" controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" "sigs.k8s.io/cluster-api/controlplane/kubeadm/internal" + topologynames "sigs.k8s.io/cluster-api/internal/topology/names" "sigs.k8s.io/cluster-api/internal/util/ssa" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/certs" @@ -184,6 +185,7 @@ func (r *KubeadmControlPlaneReconciler) cloneConfigsAndGenerateMachine(ctx conte if r.DeprecatedInfraMachineNaming { infraMachineName = names.SimpleNameGenerator.GenerateName(kcp.Spec.MachineTemplate.InfrastructureRef.Name + "-") } + // Clone the infrastructure template infraRef, err := external.CreateFromTemplate(ctx, &external.CreateFromTemplateInput{ Client: r.Client, @@ -349,7 +351,15 @@ func (r *KubeadmControlPlaneReconciler) computeDesiredMachine(kcp *controlplanev annotations := map[string]string{} if existingMachine == nil { // Creating a new machine - machineName = names.SimpleNameGenerator.GenerateName(kcp.Name + "-") + nameTemplate := "{{ .kubeadmcontrolplane.name }}-{{ .random }}" + if kcp.Spec.MachineNamingStrategy != nil && kcp.Spec.MachineNamingStrategy.Template != nil { + nameTemplate = *kcp.Spec.MachineNamingStrategy.Template + } + generatedMachineName, err := topologynames.KCPMachineNameGenerator(nameTemplate, kcp.Name).GenerateName() + if err != nil { + return nil, errors.Wrap(err, "failed to generate name for KCP machine") + } + machineName = generatedMachineName version = &kcp.Spec.Version // Machine's bootstrap config may be missing ClusterConfiguration if it is not the first machine in the control plane. diff --git a/controlplane/kubeadm/internal/controllers/helpers_test.go b/controlplane/kubeadm/internal/controllers/helpers_test.go index eb5d8ffb6539..f828ce216f98 100644 --- a/controlplane/kubeadm/internal/controllers/helpers_test.go +++ b/controlplane/kubeadm/internal/controllers/helpers_test.go @@ -340,6 +340,7 @@ func TestCloneConfigsAndGenerateMachine(t *testing.T) { } g.Expect(env.CreateAndWait(ctx, genericInfrastructureMachineTemplate)).To(Succeed()) + namingTemplateKey := "-testkcp" kcp := &controlplanev1.KubeadmControlPlane{ ObjectMeta: metav1.ObjectMeta{ Name: "kcp-foo", @@ -356,6 +357,9 @@ func TestCloneConfigsAndGenerateMachine(t *testing.T) { }, }, Version: "v1.16.6", + MachineNamingStrategy: &controlplanev1.MachineNamingStrategy{ + Template: ptr.To("{{ .kubeadmcontrolplane.name }}" + namingTemplateKey + "-{{ .random }}"), + }, }, } @@ -378,7 +382,7 @@ func TestCloneConfigsAndGenerateMachine(t *testing.T) { m := machineList.Items[i] g.Expect(m.Namespace).To(Equal(cluster.Namespace)) g.Expect(m.Name).NotTo(BeEmpty()) - g.Expect(m.Name).To(HavePrefix(kcp.Name)) + g.Expect(m.Name).To(HavePrefix(kcp.Name + namingTemplateKey)) infraObj, err := external.Get(ctx, r.Client, &m.Spec.InfrastructureRef, m.Spec.InfrastructureRef.Namespace) g.Expect(err).ToNot(HaveOccurred()) @@ -487,6 +491,7 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { }, } kcpMachineTemplateObjectMetaCopy := kcpMachineTemplateObjectMeta.DeepCopy() + namingTemplateKey := "-testkcp" kcp := &controlplanev1.KubeadmControlPlane{ ObjectMeta: metav1.ObjectMeta{ Name: "testControlPlane", @@ -505,6 +510,9 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { ClusterName: "testCluster", }, }, + MachineNamingStrategy: &controlplanev1.MachineNamingStrategy{ + Template: ptr.To("{{ .kubeadmcontrolplane.name }}" + namingTemplateKey + "-{{ .random }}"), + }, }, } clusterConfigurationString := "{\"etcd\":{},\"networking\":{},\"apiServer\":{},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}" @@ -540,7 +548,7 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { NodeDeletionTimeout: kcp.Spec.MachineTemplate.NodeDeletionTimeout, NodeVolumeDetachTimeout: kcp.Spec.MachineTemplate.NodeVolumeDetachTimeout, } - g.Expect(createdMachine.Name).To(HavePrefix(kcp.Name)) + g.Expect(createdMachine.Name).To(HavePrefix(kcp.Name + namingTemplateKey)) g.Expect(createdMachine.Namespace).To(Equal(kcp.Namespace)) g.Expect(createdMachine.OwnerReferences).To(HaveLen(1)) g.Expect(createdMachine.OwnerReferences).To(ContainElement(*metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind("KubeadmControlPlane")))) diff --git a/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane.go b/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane.go index d848d4616bba..3b47bdfd48f7 100644 --- a/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane.go +++ b/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane.go @@ -234,6 +234,8 @@ func (webhook *KubeadmControlPlane) ValidateUpdate(_ context.Context, oldObj, ne {spec, "version"}, {spec, "remediationStrategy"}, {spec, "remediationStrategy", "*"}, + {spec, "machineNamingStrategy"}, + {spec, "machineNamingStrategy", "*"}, {spec, "rolloutAfter"}, {spec, "rolloutBefore"}, {spec, "rolloutBefore", "*"}, diff --git a/internal/apis/controlplane/kubeadm/v1alpha3/zz_generated.conversion.go b/internal/apis/controlplane/kubeadm/v1alpha3/zz_generated.conversion.go index 9dcb33c824b2..f3621923c185 100644 --- a/internal/apis/controlplane/kubeadm/v1alpha3/zz_generated.conversion.go +++ b/internal/apis/controlplane/kubeadm/v1alpha3/zz_generated.conversion.go @@ -213,6 +213,7 @@ func autoConvert_v1beta1_KubeadmControlPlaneSpec_To_v1alpha3_KubeadmControlPlane // WARNING: in.RolloutAfter requires manual conversion: does not exist in peer-type out.RolloutStrategy = (*RolloutStrategy)(unsafe.Pointer(in.RolloutStrategy)) // WARNING: in.RemediationStrategy requires manual conversion: does not exist in peer-type + // WARNING: in.MachineNamingStrategy requires manual conversion: does not exist in peer-type return nil } diff --git a/internal/apis/controlplane/kubeadm/v1alpha4/zz_generated.conversion.go b/internal/apis/controlplane/kubeadm/v1alpha4/zz_generated.conversion.go index 4efbb1b7b795..325fe2564ad2 100644 --- a/internal/apis/controlplane/kubeadm/v1alpha4/zz_generated.conversion.go +++ b/internal/apis/controlplane/kubeadm/v1alpha4/zz_generated.conversion.go @@ -317,6 +317,7 @@ func autoConvert_v1beta1_KubeadmControlPlaneSpec_To_v1alpha4_KubeadmControlPlane out.RolloutAfter = (*v1.Time)(unsafe.Pointer(in.RolloutAfter)) out.RolloutStrategy = (*RolloutStrategy)(unsafe.Pointer(in.RolloutStrategy)) // WARNING: in.RemediationStrategy requires manual conversion: does not exist in peer-type + // WARNING: in.MachineNamingStrategy requires manual conversion: does not exist in peer-type return nil } diff --git a/internal/topology/names/names.go b/internal/topology/names/names.go index 33e419c3584a..6b4775d8d7e3 100644 --- a/internal/topology/names/names.go +++ b/internal/topology/names/names.go @@ -86,6 +86,12 @@ func MachinePoolNameGenerator(templateString, clusterName, topologyName string) }) } +// KCPMachineNameGenerator returns a generator for creating a kcp machine name. +func KCPMachineNameGenerator(templateString, kubeadmControlPlaneName string) NameGenerator { + return newTemplateGeneratorWithKCP(templateString, kubeadmControlPlaneName, + map[string]interface{}{}) +} + // templateGenerator parses the template string as text/template and executes it using // the passed data to generate a name. type templateGenerator struct { @@ -105,6 +111,18 @@ func newTemplateGenerator(template, clusterName string, data map[string]interfac } } +func newTemplateGeneratorWithKCP(template, kubeadmControlPlaneName string, data map[string]interface{}) NameGenerator { + data["kubeadmcontrolplane"] = map[string]interface{}{ + "name": kubeadmControlPlaneName, + } + data["random"] = utilrand.String(randomLength) + + return &templateGenerator{ + template: template, + data: data, + } +} + func (g *templateGenerator) GenerateName() (string, error) { tpl, err := template.New("template name generator").Option("missingkey=error").Parse(g.template) if err != nil {