Skip to content

Commit

Permalink
Add MachinePool Machine implementation in core CAPI components
Browse files Browse the repository at this point in the history
  • Loading branch information
Jont828 committed Jun 19, 2023
1 parent ab1dd0e commit f688868
Show file tree
Hide file tree
Showing 10 changed files with 773 additions and 17 deletions.
4 changes: 4 additions & 0 deletions api/v1beta1/machine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ const (
// MachineDeploymentNameLabel is the label set on machines if they're controlled by MachineDeployment.
MachineDeploymentNameLabel = "cluster.x-k8s.io/deployment-name"

// MachinePoolNameLabel is the label indicating the name of the MachinePool a Machine is controlled 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"

// MachineControlPlaneNameLabel is the label set on machines if they're controlled by a ControlPlane.
// Note: The value of this label may be a hash if the control plane name is longer than 63 characters.
MachineControlPlaneNameLabel = "cluster.x-k8s.io/control-plane-name"
Expand Down
31 changes: 24 additions & 7 deletions api/v1beta1/machine_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,16 @@ 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) {
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 Down Expand Up @@ -143,3 +146,17 @@ func (m *Machine) validate(old *Machine) error {
}
return apierrors.NewInvalid(GroupVersion.WithKind("Machine").GroupKind(), m.Name, allErrs)
}

func isMachinePoolMachine(m *Machine) bool {
if m.OwnerReferences == nil {
return false
}

for _, owner := range m.OwnerReferences {
if owner.Kind == "MachinePool" {
return true
}
}

return false
}
44 changes: 44 additions & 0 deletions api/v1beta1/machine_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,50 @@ func TestMachineClusterNameImmutable(t *testing.T) {
}
}

func TestIsMachinePoolMachine(t *testing.T) {
tests := []struct {
name string
machine Machine
isMPM bool
}{
{
name: "machine is a MachinePoolMachine",
machine: Machine{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{
{
Kind: "MachinePool",
},
},
},
},
isMPM: true,
},
{
name: "machine is not a MachinePoolMachine",
machine: Machine{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{
{
Kind: "NotMachinePool",
},
},
},
},
isMPM: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

result := isMachinePoolMachine(&tt.machine)
g.Expect(result).To(Equal(tt.isMPM))
})
}
}

func TestMachineVersionValidation(t *testing.T) {
tests := []struct {
name string
Expand Down
17 changes: 16 additions & 1 deletion docs/book/src/developer/architecture/controllers/machine-pool.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# MachinePool Controller
# MachinePool Controller

![](../../../images/cluster-admission-machinepool-controller.png)

Expand Down Expand Up @@ -96,6 +96,16 @@ The `status` object **may** define several fields that do not affect functionali
* `failureReason` - is a string that explains why a fatal error has occurred, if possible.
* `failureMessage` - is a string that holds the message contained by the error.

#### MachinePool Machines

Infrastructure providers can support MachinePool Machines by having the InfraMachinePool create InfraMachines and setting
the following optional `status` fields. If both are set, the InfraMachinePool should create InfraMachines, and the MachinePool
will create Machines associated with each instance.

* `infrastructureMachineKind` - the kind of the InfraMachines.
* `infrastructureMachineSelector` - a `metav1.LabelSelector` for selecting the associated InfraMachines. The InfraMachine should be created with labels to match the selector.


Example:
```yaml
kind: MyMachinePool
Expand All @@ -106,6 +116,7 @@ spec:
- cloud:////my-cloud-provider-id-1
status:
ready: true
```

#### Externally Managed Autoscaler
Expand Down Expand Up @@ -133,6 +144,10 @@ spec:
status:
ready: true
phase: Scaling
infrastructureMachineKind: InfrastructreMachine
infrastructureMachineSelector:
matchLabels:
cluster.x-k8s.io/pool-name: myPool
```

It is the provider's responsibility to update Cluster API's `Spec.Replicas` property to the value observed in the underlying infra environment as it changes in response to external autoscaling behaviors. Once that is done, and the number of providerID items is equal to the `Spec.Replicas` property, the MachinePools's `Status.Phase` property will be set to `Running` by Cluster API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ maintainers of providers and consumers of our Go API.

### API Changes

-
- InfraMachinePools now include optional status fields for `infrastructureMachineKind` and `infrastructureMachineSelector`. If both are set, the InfraMachinePool should create InfraMachines, and the MachinePool will create Machines associated with each instance.

### Other

Expand Down
5 changes: 3 additions & 2 deletions docs/book/src/reference/labels_and_annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


| Label | Note |
|:------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| :---------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| cluster.x-k8s.io/cluster-name | It is set on machines linked to a cluster and external objects(bootstrap and infrastructure providers). |
| topology.cluster.x-k8s.io/owned | It is set on all the object which are managed as part of a ClusterTopology. |
| topology.cluster.x-k8s.io/deployment-name | It is set on the generated MachineDeployment objects to track the name of the MachineDeployment topology it represents. |
Expand All @@ -13,14 +13,15 @@
| cluster.x-k8s.io/set-name | It is set on machines if they're controlled by MachineSet. The value of this label may be a hash if the MachineSet name is longer than 63 characters. |
| cluster.x-k8s.io/control-plane-name | It is set on machines if they're controlled by a control plane. The value of this label may be a hash if the control plane name is longer than 63 characters. |
| cluster.x-k8s.io/deployment-name | It is set on machines if they're controlled by a MachineDeployment. |
| cluster.x-k8s.io/pool-name | It is set on machines if they're controlled by a MachinePool. |
| machine-template-hash | It is applied to Machines in a MachineDeployment containing the hash of the template. |
<br>


**Supported Annotations:**

| Annotation | Note |
|:-----------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| :--------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| clusterctl.cluster.x-k8s.io/skip-crd-name-preflight-check | Can be placed on provider CRDs, so that clusterctl doesn't emit an error if the CRD doesn't comply with Cluster APIs naming scheme. Only CRDs that are referenced by core Cluster API CRDs have to comply with the naming scheme. |
| clusterctl.cluster.x-k8s.io/delete-for-move | DeleteForMoveAnnotation will be set to objects that are going to be deleted from the source cluster after being moved to the target cluster during the clusterctl move operation. It will help any validation webhook to take decision based on it. |
| unsafe.topology.cluster.x-k8s.io/disable-update-class-name-check | It can be used to disable the webhook check on update that disallows a pre-existing Cluster to be populated with Topology information and Class. |
Expand Down
19 changes: 19 additions & 0 deletions exp/internal/controllers/machinepool_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,22 @@ func (r *MachinePoolReconciler) nodeToMachinePool(ctx context.Context, o client.

return nil
}

// infraMachineToMachinePoolMapper is a mapper function that maps an InfraMachine to the MachinePool that owns it.
// This is used to trigger an update of the MachinePool when a InfraMachine is changed.
func infraMachineToMachinePoolMapper(_ context.Context, o client.Object) []ctrl.Request {
labels := o.GetLabels()

if poolName, ok := labels[clusterv1.MachinePoolNameLabel]; ok {
return []ctrl.Request{
{
NamespacedName: client.ObjectKey{
Namespace: o.GetNamespace(),
Name: poolName,
},
},
}
}

return nil
}
Loading

0 comments on commit f688868

Please sign in to comment.