Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1369,7 +1369,7 @@ func TestBootstrapTokenRotationMachinePool(t *testing.T) {

patchHelper, err := patch.NewHelper(workerMachinePool, myclient)
g.Expect(err).ShouldNot(HaveOccurred())
workerMachinePool.Status.InfrastructureReady = true
workerMachinePool.Status.Initialization = &expv1.MachinePoolInitializationStatus{InfrastructureProvisioned: true}
g.Expect(patchHelper.Patch(ctx, workerMachinePool, patch.WithStatusObservedGeneration{})).To(Succeed())

result, err = k.Reconcile(ctx, request)
Expand Down
11 changes: 2 additions & 9 deletions bootstrap/util/configowner.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,8 @@ type ConfigOwner struct {
*unstructured.Unstructured
}

// IsInfrastructureReady extracts infrastructure status from the config owner.
func (co ConfigOwner) IsInfrastructureReady() bool {
if co.IsMachinePool() {
infrastructureReady, _, err := unstructured.NestedBool(co.Object, "status", "infrastructureReady")
if err != nil {
return false
}
return infrastructureReady
}
// IsInfrastructureProvisioned extracts infrastructure status from the config owner.
func (co ConfigOwner) IsInfrastructureProvisioned() bool {
infrastructureReady, _, err := unstructured.NestedBool(co.Object, "status", "initialization", "infrastructureProvisioned")
if err != nil {
return false
Expand Down
8 changes: 5 additions & 3 deletions bootstrap/util/configowner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func TestGetConfigOwner(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(configOwner).ToNot(BeNil())
g.Expect(configOwner.ClusterName()).To(BeEquivalentTo("my-cluster"))
g.Expect(configOwner.IsInfrastructureReady()).To(BeTrue())
g.Expect(configOwner.IsInfrastructureProvisioned()).To(BeTrue())
g.Expect(configOwner.IsControlPlaneMachine()).To(BeTrue())
g.Expect(configOwner.IsMachinePool()).To(BeFalse())
g.Expect(configOwner.KubernetesVersion()).To(Equal("v1.19.6"))
Expand Down Expand Up @@ -110,7 +110,9 @@ func TestGetConfigOwner(t *testing.T) {
},
},
Status: expv1.MachinePoolStatus{
InfrastructureReady: true,
Initialization: &expv1.MachinePoolInitializationStatus{
InfrastructureProvisioned: true,
},
},
}

Expand All @@ -132,7 +134,7 @@ func TestGetConfigOwner(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(configOwner).ToNot(BeNil())
g.Expect(configOwner.ClusterName()).To(BeEquivalentTo("my-cluster"))
g.Expect(configOwner.IsInfrastructureReady()).To(BeTrue())
g.Expect(configOwner.IsInfrastructureProvisioned()).To(BeTrue())
g.Expect(configOwner.IsControlPlaneMachine()).To(BeFalse())
g.Expect(configOwner.IsMachinePool()).To(BeTrue())
g.Expect(configOwner.KubernetesVersion()).To(Equal("v1.19.6"))
Expand Down
25 changes: 18 additions & 7 deletions config/crd/bases/cluster.x-k8s.io_machinepools.yaml

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

15 changes: 15 additions & 0 deletions exp/api/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ func Convert_v1beta2_MachinePoolStatus_To_v1beta1_MachinePoolStatus(in *expv1.Ma
out.UnavailableReplicas = in.Deprecated.V1Beta1.UnavailableReplicas
}

// Move initialization to old fields
if in.Initialization != nil {
out.BootstrapReady = in.Initialization.BootstrapDataSecretCreated
out.InfrastructureReady = in.Initialization.InfrastructureProvisioned
}

// Move new conditions (v1beta2) and replica counters to the v1beta2 field.
if in.Conditions == nil && in.ReadyReplicas == nil && in.AvailableReplicas == nil && in.UpToDateReplicas == nil {
return nil
Expand Down Expand Up @@ -98,6 +104,15 @@ func Convert_v1beta1_MachinePoolStatus_To_v1beta2_MachinePoolStatus(in *MachineP
out.UpToDateReplicas = in.V1Beta2.UpToDateReplicas
}

// Move BootstrapReady and InfrastructureReady to Initialization
if in.BootstrapReady || in.InfrastructureReady {
if out.Initialization == nil {
out.Initialization = &expv1.MachinePoolInitializationStatus{}
}
out.Initialization.BootstrapDataSecretCreated = in.BootstrapReady
out.Initialization.InfrastructureProvisioned = in.InfrastructureReady
}

// Move legacy conditions (v1beta1), failureReason, failureMessage and replica counters to the deprecated field.
if out.Deprecated == nil {
out.Deprecated = &expv1.MachinePoolDeprecatedStatus{}
Expand Down
7 changes: 7 additions & 0 deletions exp/api/v1beta1/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ func hubMachinePoolStatus(in *expv1.MachinePoolStatus, c fuzz.Continue) {
if in.Deprecated.V1Beta1 == nil {
in.Deprecated.V1Beta1 = &expv1.MachinePoolV1Beta1DeprecatedStatus{}
}

// Drop empty structs with only omit empty fields.
if in.Initialization != nil {
if reflect.DeepEqual(in.Initialization, &expv1.MachinePoolInitializationStatus{}) {
in.Initialization = nil
}
}
}

func spokeMachinePoolStatus(in *MachinePoolStatus, c fuzz.Continue) {
Expand Down
7 changes: 3 additions & 4 deletions exp/api/v1beta1/zz_generated.conversion.go

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

29 changes: 21 additions & 8 deletions exp/api/v1beta2/machinepool_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ type MachinePoolStatus struct {
// +kubebuilder:validation:MaxItems=32
Conditions []metav1.Condition `json:"conditions,omitempty"`

// initialization provides observations of the MachinePool initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial MachinePool provisioning.
// +optional
Initialization *MachinePoolInitializationStatus `json:"initialization,omitempty"`

// nodeRefs will point to the corresponding Nodes if it they exist.
// +optional
// +kubebuilder:validation:MaxItems=10000
Expand All @@ -112,14 +117,6 @@ type MachinePoolStatus struct {
// +kubebuilder:validation:Enum=Pending;Provisioning;Provisioned;Running;ScalingUp;ScalingDown;Scaling;Deleting;Failed;Unknown
Phase string `json:"phase,omitempty"`

// bootstrapReady is the state of the bootstrap provider.
// +optional
BootstrapReady bool `json:"bootstrapReady"`

// infrastructureReady is the state of the infrastructure provider.
// +optional
InfrastructureReady bool `json:"infrastructureReady"`

// observedGeneration is the latest generation observed by the controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
Expand All @@ -129,6 +126,22 @@ type MachinePoolStatus struct {
Deprecated *MachinePoolDeprecatedStatus `json:"deprecated,omitempty"`
}

// MachinePoolInitializationStatus provides observations of the MachinePool initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial MachinePool provisioning.
type MachinePoolInitializationStatus struct {
// infrastructureProvisioned is true when the infrastructure provider reports that MachinePool's infrastructure is fully provisioned.
// NOTE: this field is part of the Cluster API contract, and it is used to orchestrate provisioning.
// The value of this field is never updated after provisioning is completed.
// +optional
InfrastructureProvisioned bool `json:"infrastructureProvisioned"`

// bootstrapDataSecretCreated is true when the bootstrap provider reports that the MachinePool's boostrap secret is created.
// NOTE: this field is part of the Cluster API contract, and it is used to orchestrate provisioning.
// The value of this field is never updated after provisioning is completed.
// +optional
BootstrapDataSecretCreated bool `json:"bootstrapDataSecretCreated"`
}

// MachinePoolDeprecatedStatus groups all the status fields that are deprecated and will be removed in a future version.
// See https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more context.
type MachinePoolDeprecatedStatus struct {
Expand Down
20 changes: 20 additions & 0 deletions exp/api/v1beta2/zz_generated.deepcopy.go

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

32 changes: 22 additions & 10 deletions exp/internal/controllers/machinepool_controller_phases.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (r *MachinePoolReconciler) reconcilePhase(mp *expv1.MachinePool) {
}

// Set the phase to "provisioning" if bootstrap is ready and the infrastructure isn't.
if mp.Status.BootstrapReady && !mp.Status.InfrastructureReady {
if mp.Status.Initialization != nil && mp.Status.Initialization.BootstrapDataSecretCreated && !mp.Status.Initialization.InfrastructureProvisioned {
mp.Status.SetTypedPhase(expv1.MachinePoolPhaseProvisioning)
}

Expand All @@ -75,12 +75,12 @@ func (r *MachinePoolReconciler) reconcilePhase(mp *expv1.MachinePool) {
if mp.Status.Deprecated != nil && mp.Status.Deprecated.V1Beta1 != nil {
readyReplicas = mp.Status.Deprecated.V1Beta1.ReadyReplicas
}
if mp.Status.InfrastructureReady && mp.Spec.Replicas != nil && *mp.Spec.Replicas == readyReplicas {
if mp.Status.Initialization != nil && mp.Status.Initialization.InfrastructureProvisioned && mp.Spec.Replicas != nil && *mp.Spec.Replicas == readyReplicas {
mp.Status.SetTypedPhase(expv1.MachinePoolPhaseRunning)
}

// Set the appropriate phase in response to the MachinePool replica count being greater than the observed infrastructure replicas.
if mp.Status.InfrastructureReady && mp.Spec.Replicas != nil && *mp.Spec.Replicas > readyReplicas {
if mp.Status.Initialization != nil && mp.Status.Initialization.InfrastructureProvisioned && mp.Spec.Replicas != nil && *mp.Spec.Replicas > readyReplicas {
// If we are being managed by an external autoscaler and can't predict scaling direction, set to "Scaling".
if annotations.ReplicasManagedByExternalAutoscaler(mp) {
mp.Status.SetTypedPhase(expv1.MachinePoolPhaseScaling)
Expand All @@ -91,7 +91,7 @@ func (r *MachinePoolReconciler) reconcilePhase(mp *expv1.MachinePool) {
}

// Set the appropriate phase in response to the MachinePool replica count being less than the observed infrastructure replicas.
if mp.Status.InfrastructureReady && mp.Spec.Replicas != nil && *mp.Spec.Replicas < readyReplicas {
if mp.Status.Initialization != nil && mp.Status.Initialization.InfrastructureProvisioned && mp.Spec.Replicas != nil && *mp.Spec.Replicas < readyReplicas {
// If we are being managed by an external autoscaler and can't predict scaling direction, set to "Scaling".
if annotations.ReplicasManagedByExternalAutoscaler(mp) {
mp.Status.SetTypedPhase(expv1.MachinePoolPhaseScaling)
Expand Down Expand Up @@ -231,7 +231,10 @@ func (r *MachinePoolReconciler) reconcileBootstrap(ctx context.Context, s *scope

if !dataSecretCreated {
log.Info("Waiting for bootstrap provider to generate data secret and report status.ready", bootstrapConfig.GetKind(), klog.KObj(bootstrapConfig))
m.Status.BootstrapReady = dataSecretCreated
if m.Status.Initialization == nil {
m.Status.Initialization = &expv1.MachinePoolInitializationStatus{}
}
m.Status.Initialization.BootstrapDataSecretCreated = dataSecretCreated
return ctrl.Result{}, nil
}

Expand All @@ -244,13 +247,19 @@ func (r *MachinePoolReconciler) reconcileBootstrap(ctx context.Context, s *scope
}

m.Spec.Template.Spec.Bootstrap.DataSecretName = secretName
m.Status.BootstrapReady = true
if m.Status.Initialization == nil {
m.Status.Initialization = &expv1.MachinePoolInitializationStatus{}
}
m.Status.Initialization.BootstrapDataSecretCreated = true
return ctrl.Result{}, nil
}

// If dataSecretName is set without a ConfigRef, this means the user brought their own bootstrap data.
if m.Spec.Template.Spec.Bootstrap.DataSecretName != nil {
m.Status.BootstrapReady = true
if m.Status.Initialization == nil {
m.Status.Initialization = &expv1.MachinePoolInitializationStatus{}
}
m.Status.Initialization.BootstrapDataSecretCreated = true
v1beta1conditions.MarkTrue(m, clusterv1.BootstrapReadyV1Beta1Condition)
return ctrl.Result{}, nil
}
Expand All @@ -269,7 +278,7 @@ func (r *MachinePoolReconciler) reconcileInfrastructure(ctx context.Context, s *
if err != nil {
if apierrors.IsNotFound(errors.Cause(err)) {
log.Error(err, "infrastructure reference could not be found")
if mp.Status.InfrastructureReady {
if mp.Status.Initialization != nil && mp.Status.Initialization.InfrastructureProvisioned {
// Infra object went missing after the machine pool was up and running
log.Error(err, "infrastructure reference has been deleted after being ready, setting failure state")
if mp.Status.Deprecated == nil {
Expand Down Expand Up @@ -297,7 +306,10 @@ func (r *MachinePoolReconciler) reconcileInfrastructure(ctx context.Context, s *
return ctrl.Result{}, err
}

mp.Status.InfrastructureReady = ready
if mp.Status.Initialization == nil {
mp.Status.Initialization = &expv1.MachinePoolInitializationStatus{}
}
mp.Status.Initialization.InfrastructureProvisioned = ready

// Report a summary of current status of the infrastructure object defined for this machine pool.
v1beta1conditions.SetMirror(mp, clusterv1.InfrastructureReadyV1Beta1Condition,
Expand All @@ -320,7 +332,7 @@ func (r *MachinePoolReconciler) reconcileInfrastructure(ctx context.Context, s *
return ctrl.Result{}, kerrors.NewAggregate([]error{errors.Wrapf(err, "failed to reconcile Machines for MachinePool %s", klog.KObj(mp)), errors.Wrapf(getNodeRefsErr, "failed to get nodeRefs for MachinePool %s", klog.KObj(mp))})
}

if !mp.Status.InfrastructureReady {
if mp.Status.Initialization == nil && !mp.Status.Initialization.InfrastructureProvisioned {
log.Info("Infrastructure provider is not yet ready", infraConfig.GetKind(), klog.KObj(infraConfig))
return ctrl.Result{}, nil
}
Expand Down
Loading