Skip to content

Commit

Permalink
Implement MachinePool Machines in CAPI, CAPD, and clusterctl
Browse files Browse the repository at this point in the history
  • Loading branch information
Jont828 committed Feb 22, 2023
1 parent 744f3ff commit eb4eaeb
Show file tree
Hide file tree
Showing 29 changed files with 1,474 additions and 208 deletions.
12 changes: 12 additions & 0 deletions api/v1beta1/common_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ const (
// update that disallows a pre-existing Cluster to be populated with Topology information and Class.
ClusterTopologyUnsafeUpdateClassNameAnnotation = "unsafe.topology.cluster.x-k8s.io/disable-update-class-name-check"

// MachinePoolOwnedLabel is the label set on Machines and InfraMachines linked to a MachinePool.
MachinePoolOwnedLabel = "machinepool.cluster.x-k8s.io/owned"

// FallbackMachineLabel indicates that a Machine belongs to a MachinePool that does not support MachinePool Machines.
// As such, these Machines exist to create a consistent user experience and will not have an infrastructure reference. The user will
// also be prevented from deleting these Machines.
FallbackMachineLabel = "machinepool.cluster.x-k8s.io/fallback-machine"

// AllowFallbackMachineDeletionAnnotation is used to bypass the prevention of deletion of fallback machines. The controller will
// apply this annotation before deletion.
AllowFallbackMachineDeletionAnnotation = "machinepool.cluster.x-k8s.io/allow-fallback-deletion"

// ProviderNameLabel is the label set on components in the provider manifest.
// This label allows to easily identify all the components belonging to a provider; the clusterctl
// tool uses this label for implementing provider's lifecycle operations.
Expand Down
52 changes: 33 additions & 19 deletions api/v1beta1/machine_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ func (m *Machine) SetupWebhookWithManager(mgr ctrl.Manager) error {
Complete()
}

// +kubebuilder:webhook:verbs=create;update,path=/validate-cluster-x-k8s-io-v1beta1-machine,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=cluster.x-k8s.io,resources=machines,versions=v1beta1,name=validation.machine.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1
// +kubebuilder:webhook:verbs=create;update,path=/mutate-cluster-x-k8s-io-v1beta1-machine,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=cluster.x-k8s.io,resources=machines,versions=v1beta1,name=default.machine.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1
// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-cluster-x-k8s-io-v1beta1-machine,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=cluster.x-k8s.io,resources=machines,versions=v1beta1,name=validation.machine.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1
// +kubebuilder:webhook:verbs=create;update;delete,path=/mutate-cluster-x-k8s-io-v1beta1-machine,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=cluster.x-k8s.io,resources=machines,versions=v1beta1,name=default.machine.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1

var _ webhook.Validator = &Machine{}
var _ webhook.Defaulter = &Machine{}
Expand All @@ -57,7 +57,10 @@ func (m *Machine) Default() {
}

if m.Spec.InfrastructureRef.Namespace == "" {
m.Spec.InfrastructureRef.Namespace = m.Namespace
// Don't autofill namespace for MachinePool Machines since the infraRef will be populated by the MachinePool controller.
if _, isMachinePoolMachine := m.Labels[MachinePoolOwnedLabel]; !isMachinePoolMachine {
m.Spec.InfrastructureRef.Namespace = m.Namespace
}
}

if m.Spec.Version != nil && !strings.HasPrefix(*m.Spec.Version, "v") {
Expand Down Expand Up @@ -86,20 +89,29 @@ func (m *Machine) ValidateUpdate(old runtime.Object) error {

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type.
func (m *Machine) ValidateDelete() error {
if _, isFallbackMachine := m.Labels[FallbackMachineLabel]; isFallbackMachine {
if _, allowDelete := m.Annotations[AllowFallbackMachineDeletionAnnotation]; !allowDelete {
return apierrors.NewBadRequest("fallback MachinePool Machine cannot be deleted by user")
}
}

return nil
}

func (m *Machine) validate(old *Machine) error {
var allErrs field.ErrorList
specPath := field.NewPath("spec")
if m.Spec.Bootstrap.ConfigRef == nil && m.Spec.Bootstrap.DataSecretName == nil {
allErrs = append(
allErrs,
field.Required(
specPath.Child("bootstrap", "data"),
"expected either spec.bootstrap.dataSecretName or spec.bootstrap.configRef to be populated",
),
)
// MachinePool Machines don't have a bootstrap configRef, so don't require it. The bootstrap config is instead owned by the MachinePool.
if _, isMachinePoolMachine := m.Labels[MachinePoolOwnedLabel]; !isMachinePoolMachine {
allErrs = append(
allErrs,
field.Required(
specPath.Child("bootstrap", "data"),
"expected either spec.bootstrap.dataSecretName or spec.bootstrap.configRef to be populated",
),
)
}
}

if m.Spec.Bootstrap.ConfigRef != nil && m.Spec.Bootstrap.ConfigRef.Namespace != m.Namespace {
Expand All @@ -113,15 +125,17 @@ func (m *Machine) validate(old *Machine) error {
)
}

if m.Spec.InfrastructureRef.Namespace != m.Namespace {
allErrs = append(
allErrs,
field.Invalid(
specPath.Child("infrastructureRef", "namespace"),
m.Spec.InfrastructureRef.Namespace,
"must match metadata.namespace",
),
)
if _, isMachinePoolMachine := m.Labels[MachinePoolOwnedLabel]; !isMachinePoolMachine {
if m.Spec.InfrastructureRef.Namespace != m.Namespace {
allErrs = append(
allErrs,
field.Invalid(
specPath.Child("infrastructureRef", "namespace"),
m.Spec.InfrastructureRef.Namespace,
"must match metadata.namespace",
),
)
}
}

if old != nil && old.Spec.ClusterName != m.Spec.ClusterName {
Expand Down
48 changes: 28 additions & 20 deletions cmd/clusterctl/client/tree/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,10 @@ func Discovery(ctx context.Context, c client.Client, namespace, name string, opt
tree.Add(m, machineInfra, ObjectMetaName("MachineInfrastructure"), NoEcho(true))
}

if machineBootstrap, err := external.Get(ctx, c, m.Spec.Bootstrap.ConfigRef, cluster.Namespace); err == nil {
tree.Add(m, machineBootstrap, ObjectMetaName("BootstrapConfig"), NoEcho(true))
if m.Spec.Bootstrap.ConfigRef != nil {
if machineBootstrap, err := external.Get(ctx, c, m.Spec.Bootstrap.ConfigRef, cluster.Namespace); err == nil {
tree.Add(m, machineBootstrap, ObjectMetaName("BootstrapConfig"), NoEcho(true))
}
}
}
}
Expand Down Expand Up @@ -141,25 +143,25 @@ func Discovery(ctx context.Context, c client.Client, namespace, name string, opt
if err != nil {
return nil, err
}

// Handles orphan machines.
if len(machineMap) < len(machinesList.Items) {
other := VirtualObject(cluster.Namespace, "OtherGroup", "Other")
tree.Add(workers, other)

for i := range machinesList.Items {
m := &machinesList.Items[i]
if _, ok := machineMap[m.Name]; ok {
continue
}
addMachineFunc(other, m)
}
}
}

if len(machinePoolList.Items) > 0 { // Add MachinePool objects
tree.Add(cluster, workers)
addMachinePoolsToObjectTree(ctx, c, cluster.Namespace, workers, machinePoolList, tree)
addMachinePoolsToObjectTree(ctx, c, cluster.Namespace, workers, machinePoolList, machinesList, tree, addMachineFunc)
}

// Handles orphan machines.
if len(machineMap) < len(machinesList.Items) {
other := VirtualObject(cluster.Namespace, "OtherGroup", "Other")
tree.Add(workers, other)

for i := range machinesList.Items {
m := &machinesList.Items[i]
if _, ok := machineMap[m.Name]; ok {
continue
}
addMachineFunc(other, m)
}
}

return tree, nil
Expand Down Expand Up @@ -263,20 +265,26 @@ func addMachineDeploymentToObjectTree(ctx context.Context, c client.Client, clus
return nil
}

func addMachinePoolsToObjectTree(ctx context.Context, c client.Client, namespace string, workers *unstructured.Unstructured, machinePoolList *expv1.MachinePoolList, tree *ObjectTree) {
func addMachinePoolsToObjectTree(ctx context.Context, c client.Client, namespace string, workers *unstructured.Unstructured, machinePoolList *expv1.MachinePoolList, machinesList *clusterv1.MachineList, tree *ObjectTree, addMachineFunc func(parent client.Object, m *clusterv1.Machine)) {
for i := range machinePoolList.Items {
mp := &machinePoolList.Items[i]
_, visible := tree.Add(workers, mp)
_, visible := tree.Add(workers, mp, GroupingObject(true))

if visible {
if machinePoolBootstrap, err := external.Get(ctx, c, mp.Spec.Template.Spec.Bootstrap.ConfigRef, namespace); err == nil {
tree.Add(mp, machinePoolBootstrap, ObjectMetaName("BootstrapConfig"), NoEcho(true))
}

if machinePoolInfra, err := external.Get(ctx, c, &mp.Spec.Template.Spec.InfrastructureRef, namespace); err == nil {
tree.Add(mp, machinePoolInfra, ObjectMetaName("MachineInfrastructure"), NoEcho(true))
tree.Add(mp, machinePoolInfra, ObjectMetaName("MachinePoolInfrastructure"), NoEcho(true))
}
}

machines := selectMachinesControlledBy(machinesList, mp)

for _, m := range machines {
addMachineFunc(mp, m)
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ webhooks:
operations:
- CREATE
- UPDATE
- DELETE
resources:
- machines
sideEffects: None
Expand Down Expand Up @@ -229,6 +230,7 @@ webhooks:
operations:
- CREATE
- UPDATE
- DELETE
resources:
- machines
sideEffects: None
Expand Down
4 changes: 4 additions & 0 deletions exp/api/v1beta1/machinepool_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ import (
const (
// MachinePoolFinalizer is used to ensure deletion of dependencies (nodes, infra).
MachinePoolFinalizer = "machinepool.cluster.x-k8s.io"

// MachinePoolNameLabel is the label indicating the name of the MachinePool a Machine is controleld by.
// Note: The value of this label may be a hash if the MachinePool name is longer than 63 characters.
MachinePoolNameLabel = "cluster.x-k8s.io/pool-name"
)

// ANCHOR: MachinePoolSpec
Expand Down
Loading

0 comments on commit eb4eaeb

Please sign in to comment.