Skip to content

Commit

Permalink
Add NamingStrategy to MachineDeployment
Browse files Browse the repository at this point in the history
Signed-off-by: Muhammad Adil Ghaffar <[email protected]>
  • Loading branch information
adilGhaffarDev committed Sep 11, 2024
1 parent d4a8f10 commit e03d550
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 8 deletions.
19 changes: 19 additions & 0 deletions api/v1beta1/machinedeployment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ type MachineDeploymentStrategy struct {
// and how remediating operations should occur during the lifecycle of the dependant MachineSets.
// +optional
Remediation *RemediationStrategy `json:"remediation,omitempty"`

// MachineNamingStrategy allows changing the naming pattern used when creating Machines.
// Note: InfraMachines will use the same name as the corresponding Machines.
// +optional
MachineNamingStrategy *MachineNamingStrategy `json:"machineNamingStrategy,omitempty"`
}

// ANCHOR_END: MachineDeploymentStrategy
Expand Down Expand Up @@ -246,6 +251,20 @@ type RemediationStrategy struct {

// ANCHOR_END: RemediationStrategy

// MachineNamingStrategy allows changing the naming pattern used when creating Machines.
// Note: InfraMachines 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 `{{ .machinedeployment.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:
// * `.machinedeployment.name`: The name of the MachineDeployment object.
// * `.random`: A random alphanumeric string, without vowels, of length 5.
// +optional
Template string `json:"template,omitempty"`
}

// ANCHOR: MachineDeploymentStatus

// MachineDeploymentStatus defines the observed state of MachineDeployment.
Expand Down
20 changes: 20 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 28 additions & 1 deletion api/v1beta1/zz_generated.openapi.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions config/crd/bases/cluster.x-k8s.io_clusterclasses.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions config/crd/bases/cluster.x-k8s.io_clusters.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions config/crd/bases/cluster.x-k8s.io_machinedeployments.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/apis/core/v1alpha3/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/apis/core/v1alpha4/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 32 additions & 6 deletions internal/controllers/machineset/machineset_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
"sigs.k8s.io/cluster-api/controllers/remote"
"sigs.k8s.io/cluster-api/internal/contract"
"sigs.k8s.io/cluster-api/internal/controllers/machine"
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/annotations"
Expand Down Expand Up @@ -399,8 +400,11 @@ func (r *Reconciler) syncMachines(ctx context.Context, machineSet *clusterv1.Mac
}

// Update Machine to propagate in-place mutable fields from the MachineSet.
updatedMachine := r.computeDesiredMachine(machineSet, m)
err := ssa.Patch(ctx, r.Client, machineSetManagerName, updatedMachine, ssa.WithCachingProxy{Cache: r.ssaCache, Original: m})
updatedMachine, err := r.computeDesiredMachine(ctx, machineSet, m)
if err != nil {
return errors.Wrap(err, "failed to update Machine: failed to compute desired Machine")
}
err = ssa.Patch(ctx, r.Client, machineSetManagerName, updatedMachine, ssa.WithCachingProxy{Cache: r.ssaCache, Original: m})
if err != nil {
log.Error(err, "Failed to update Machine", "Machine", klog.KObj(updatedMachine))
return errors.Wrapf(err, "failed to update Machine %q", klog.KObj(updatedMachine))
Expand Down Expand Up @@ -486,7 +490,10 @@ func (r *Reconciler) syncReplicas(ctx context.Context, cluster *clusterv1.Cluste
for i := range diff {
// Create a new logger so the global logger is not modified.
log := log
machine := r.computeDesiredMachine(ms, nil)
machine, computeMachineErr := r.computeDesiredMachine(ctx, ms, nil)
if computeMachineErr != nil {
return ctrl.Result{}, errors.Wrap(computeMachineErr, "failed to create Machine: failed to compute desired Machine")
}
// Clone and set the infrastructure and bootstrap references.
var (
infraRef, bootstrapRef *corev1.ObjectReference
Expand Down Expand Up @@ -621,14 +628,33 @@ func (r *Reconciler) syncReplicas(ctx context.Context, cluster *clusterv1.Cluste
// There are small differences in how we calculate the Machine depending on if it
// is a create or update. Example: for a new Machine we have to calculate a new name,
// while for an existing Machine we have to use the name of the existing Machine.
func (r *Reconciler) computeDesiredMachine(machineSet *clusterv1.MachineSet, existingMachine *clusterv1.Machine) *clusterv1.Machine {
func (r *Reconciler) computeDesiredMachine(ctx context.Context, machineSet *clusterv1.MachineSet, existingMachine *clusterv1.Machine) (*clusterv1.Machine, error) {
machineName := names.SimpleNameGenerator.GenerateName(fmt.Sprintf("%s-", machineSet.Name))
// If the MachineSet is part of a MachineDeployment, check MachineNamingStrategy
// and name Machine accordingly.
if r.isDeploymentChild(machineSet) {
owner, err := r.getOwnerMachineDeployment(ctx, machineSet)
if err != nil {
return nil, err
}
nameTemplate := "{{ .machinedeployment.name }}-{{ .random }}"
if owner.Spec.Strategy.MachineNamingStrategy != nil && owner.Spec.Strategy.MachineNamingStrategy.Template != "" {
nameTemplate = owner.Spec.Strategy.MachineNamingStrategy.Template
}
generatedMachineName, err := topologynames.MachineDeploymentMachineNameGenerator(nameTemplate, owner.Name).GenerateName()
if err != nil {
return nil, errors.Wrap(err, "failed to generate name for MachineDeployment machine")
}
machineName = generatedMachineName
}

desiredMachine := &clusterv1.Machine{
TypeMeta: metav1.TypeMeta{
APIVersion: clusterv1.GroupVersion.String(),
Kind: "Machine",
},
ObjectMeta: metav1.ObjectMeta{
Name: names.SimpleNameGenerator.GenerateName(fmt.Sprintf("%s-", machineSet.Name)),
Name: machineName,
Namespace: machineSet.Namespace,
// Note: By setting the ownerRef on creation we signal to the Machine controller that this is not a stand-alone Machine.
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(machineSet, machineSetKind)},
Expand Down Expand Up @@ -685,7 +711,7 @@ func (r *Reconciler) computeDesiredMachine(machineSet *clusterv1.MachineSet, exi
desiredMachine.Spec.NodeDeletionTimeout = machineSet.Spec.Template.Spec.NodeDeletionTimeout
desiredMachine.Spec.NodeVolumeDetachTimeout = machineSet.Spec.Template.Spec.NodeVolumeDetachTimeout

return desiredMachine
return desiredMachine, nil
}

// updateExternalObject updates the external object passed in with the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2091,7 +2091,7 @@ func TestComputeDesiredMachine(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
got := (&Reconciler{}).computeDesiredMachine(ms, tt.existingMachine)
got, _ := (&Reconciler{}).computeDesiredMachine(ctx, ms, tt.existingMachine)
assertMachine(g, got, tt.want)
})
}
Expand Down
18 changes: 18 additions & 0 deletions internal/topology/names/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ func MachinePoolNameGenerator(templateString, clusterName, topologyName string)
})
}

// MachineDeploymentMachineNameGenerator returns a generator for creating a kcp machine name.
func MachineDeploymentMachineNameGenerator(templateString, machineDeploymentName string) NameGenerator {
return newTemplateGeneratorWithMachineDeployment(templateString, machineDeploymentName,
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 {
Expand All @@ -105,6 +111,18 @@ func newTemplateGenerator(template, clusterName string, data map[string]interfac
}
}

func newTemplateGeneratorWithMachineDeployment(template, machineDeploymentName string, data map[string]interface{}) NameGenerator {
data["machinedeployment"] = map[string]interface{}{
"name": machineDeploymentName,
}
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 {
Expand Down

0 comments on commit e03d550

Please sign in to comment.