sigs.k8s.io/cluster-api@v1.6.3/exp/internal/controllers/machinepool_controller_phases.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controllers
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"reflect"
    23  	"time"
    24  
    25  	"github.com/pkg/errors"
    26  	corev1 "k8s.io/api/core/v1"
    27  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    30  	kerrors "k8s.io/apimachinery/pkg/util/errors"
    31  	"k8s.io/apimachinery/pkg/util/wait"
    32  	"k8s.io/apiserver/pkg/storage/names"
    33  	"k8s.io/klog/v2"
    34  	"k8s.io/utils/pointer"
    35  	ctrl "sigs.k8s.io/controller-runtime"
    36  	"sigs.k8s.io/controller-runtime/pkg/client"
    37  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    38  	"sigs.k8s.io/controller-runtime/pkg/handler"
    39  
    40  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    41  	"sigs.k8s.io/cluster-api/controllers/external"
    42  	capierrors "sigs.k8s.io/cluster-api/errors"
    43  	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
    44  	utilexp "sigs.k8s.io/cluster-api/exp/util"
    45  	"sigs.k8s.io/cluster-api/internal/util/ssa"
    46  	"sigs.k8s.io/cluster-api/util"
    47  	"sigs.k8s.io/cluster-api/util/annotations"
    48  	"sigs.k8s.io/cluster-api/util/conditions"
    49  	utilconversion "sigs.k8s.io/cluster-api/util/conversion"
    50  	"sigs.k8s.io/cluster-api/util/labels"
    51  	"sigs.k8s.io/cluster-api/util/labels/format"
    52  	"sigs.k8s.io/cluster-api/util/patch"
    53  )
    54  
    55  func (r *MachinePoolReconciler) reconcilePhase(mp *expv1.MachinePool) {
    56  	// Set the phase to "pending" if nil.
    57  	if mp.Status.Phase == "" {
    58  		mp.Status.SetTypedPhase(expv1.MachinePoolPhasePending)
    59  	}
    60  
    61  	// Set the phase to "provisioning" if bootstrap is ready and the infrastructure isn't.
    62  	if mp.Status.BootstrapReady && !mp.Status.InfrastructureReady {
    63  		mp.Status.SetTypedPhase(expv1.MachinePoolPhaseProvisioning)
    64  	}
    65  
    66  	// Set the phase to "provisioned" if the infrastructure is ready.
    67  	if len(mp.Status.NodeRefs) != 0 {
    68  		mp.Status.SetTypedPhase(expv1.MachinePoolPhaseProvisioned)
    69  	}
    70  
    71  	// Set the phase to "running" if the number of ready replicas is equal to desired replicas.
    72  	if mp.Status.InfrastructureReady && *mp.Spec.Replicas == mp.Status.ReadyReplicas {
    73  		mp.Status.SetTypedPhase(expv1.MachinePoolPhaseRunning)
    74  	}
    75  
    76  	// Set the appropriate phase in response to the MachinePool replica count being greater than the observed infrastructure replicas.
    77  	if mp.Status.InfrastructureReady && *mp.Spec.Replicas > mp.Status.ReadyReplicas {
    78  		// If we are being managed by an external autoscaler and can't predict scaling direction, set to "Scaling".
    79  		if annotations.ReplicasManagedByExternalAutoscaler(mp) {
    80  			mp.Status.SetTypedPhase(expv1.MachinePoolPhaseScaling)
    81  		} else {
    82  			// Set the phase to "ScalingUp" if we are actively scaling the infrastructure out.
    83  			mp.Status.SetTypedPhase(expv1.MachinePoolPhaseScalingUp)
    84  		}
    85  	}
    86  
    87  	// Set the appropriate phase in response to the MachinePool replica count being less than the observed infrastructure replicas.
    88  	if mp.Status.InfrastructureReady && *mp.Spec.Replicas < mp.Status.ReadyReplicas {
    89  		// If we are being managed by an external autoscaler and can't predict scaling direction, set to "Scaling".
    90  		if annotations.ReplicasManagedByExternalAutoscaler(mp) {
    91  			mp.Status.SetTypedPhase(expv1.MachinePoolPhaseScaling)
    92  		} else {
    93  			// Set the phase to "ScalingDown" if we are actively scaling the infrastructure in.
    94  			mp.Status.SetTypedPhase(expv1.MachinePoolPhaseScalingDown)
    95  		}
    96  	}
    97  
    98  	// Set the phase to "failed" if any of Status.FailureReason or Status.FailureMessage is not-nil.
    99  	if mp.Status.FailureReason != nil || mp.Status.FailureMessage != nil {
   100  		mp.Status.SetTypedPhase(expv1.MachinePoolPhaseFailed)
   101  	}
   102  
   103  	// Set the phase to "deleting" if the deletion timestamp is set.
   104  	if !mp.DeletionTimestamp.IsZero() {
   105  		mp.Status.SetTypedPhase(expv1.MachinePoolPhaseDeleting)
   106  	}
   107  }
   108  
   109  // reconcileExternal handles generic unstructured objects referenced by a MachinePool.
   110  func (r *MachinePoolReconciler) reconcileExternal(ctx context.Context, cluster *clusterv1.Cluster, m *expv1.MachinePool, ref *corev1.ObjectReference) (external.ReconcileOutput, error) {
   111  	log := ctrl.LoggerFrom(ctx)
   112  
   113  	if err := utilconversion.UpdateReferenceAPIContract(ctx, r.Client, ref); err != nil {
   114  		return external.ReconcileOutput{}, err
   115  	}
   116  
   117  	obj, err := external.Get(ctx, r.Client, ref, m.Namespace)
   118  	if err != nil {
   119  		if apierrors.IsNotFound(errors.Cause(err)) {
   120  			return external.ReconcileOutput{}, errors.Wrapf(err, "could not find %v %q for MachinePool %q in namespace %q, requeuing",
   121  				ref.GroupVersionKind(), ref.Name, m.Name, m.Namespace)
   122  		}
   123  		return external.ReconcileOutput{}, err
   124  	}
   125  
   126  	// Ensure we add a watch to the external object, if there isn't one already.
   127  	if err := r.externalTracker.Watch(log, obj, handler.EnqueueRequestForOwner(r.Client.Scheme(), r.Client.RESTMapper(), &expv1.MachinePool{})); err != nil {
   128  		return external.ReconcileOutput{}, err
   129  	}
   130  
   131  	// if external ref is paused, return error.
   132  	if annotations.IsPaused(cluster, obj) {
   133  		log.V(3).Info("External object referenced is paused")
   134  		return external.ReconcileOutput{Paused: true}, nil
   135  	}
   136  
   137  	// Initialize the patch helper.
   138  	patchHelper, err := patch.NewHelper(obj, r.Client)
   139  	if err != nil {
   140  		return external.ReconcileOutput{}, err
   141  	}
   142  
   143  	// Set external object ControllerReference to the MachinePool.
   144  	if err := controllerutil.SetControllerReference(m, obj, r.Client.Scheme()); err != nil {
   145  		return external.ReconcileOutput{}, err
   146  	}
   147  
   148  	// Set the Cluster label.
   149  	labels := obj.GetLabels()
   150  	if labels == nil {
   151  		labels = make(map[string]string)
   152  	}
   153  	labels[clusterv1.ClusterNameLabel] = m.Spec.ClusterName
   154  	obj.SetLabels(labels)
   155  
   156  	// Always attempt to Patch the external object.
   157  	if err := patchHelper.Patch(ctx, obj); err != nil {
   158  		return external.ReconcileOutput{}, err
   159  	}
   160  
   161  	// Set failure reason and message, if any.
   162  	failureReason, failureMessage, err := external.FailuresFrom(obj)
   163  	if err != nil {
   164  		return external.ReconcileOutput{}, err
   165  	}
   166  	if failureReason != "" {
   167  		machineStatusFailure := capierrors.MachinePoolStatusFailure(failureReason)
   168  		m.Status.FailureReason = &machineStatusFailure
   169  	}
   170  	if failureMessage != "" {
   171  		m.Status.FailureMessage = pointer.String(
   172  			fmt.Sprintf("Failure detected from referenced resource %v with name %q: %s",
   173  				obj.GroupVersionKind(), obj.GetName(), failureMessage),
   174  		)
   175  	}
   176  
   177  	return external.ReconcileOutput{Result: obj}, nil
   178  }
   179  
   180  // reconcileBootstrap reconciles the Spec.Bootstrap.ConfigRef object on a MachinePool.
   181  func (r *MachinePoolReconciler) reconcileBootstrap(ctx context.Context, cluster *clusterv1.Cluster, m *expv1.MachinePool) (ctrl.Result, error) {
   182  	log := ctrl.LoggerFrom(ctx)
   183  
   184  	// Call generic external reconciler if we have an external reference.
   185  	var bootstrapConfig *unstructured.Unstructured
   186  	if m.Spec.Template.Spec.Bootstrap.ConfigRef != nil {
   187  		bootstrapReconcileResult, err := r.reconcileExternal(ctx, cluster, m, m.Spec.Template.Spec.Bootstrap.ConfigRef)
   188  		if err != nil {
   189  			return ctrl.Result{}, err
   190  		}
   191  		// if the external object is paused, return without any further processing
   192  		if bootstrapReconcileResult.Paused {
   193  			return ctrl.Result{}, nil
   194  		}
   195  		bootstrapConfig = bootstrapReconcileResult.Result
   196  
   197  		// If the bootstrap config is being deleted, return early.
   198  		if !bootstrapConfig.GetDeletionTimestamp().IsZero() {
   199  			return ctrl.Result{}, nil
   200  		}
   201  
   202  		// Determine if the bootstrap provider is ready.
   203  		ready, err := external.IsReady(bootstrapConfig)
   204  		if err != nil {
   205  			return ctrl.Result{}, err
   206  		}
   207  
   208  		// Report a summary of current status of the bootstrap object defined for this machine pool.
   209  		conditions.SetMirror(m, clusterv1.BootstrapReadyCondition,
   210  			conditions.UnstructuredGetter(bootstrapConfig),
   211  			conditions.WithFallbackValue(ready, clusterv1.WaitingForDataSecretFallbackReason, clusterv1.ConditionSeverityInfo, ""),
   212  		)
   213  
   214  		if !ready {
   215  			log.Info("Waiting for bootstrap provider to generate data secret and report status.ready", bootstrapConfig.GetKind(), klog.KObj(bootstrapConfig))
   216  			m.Status.BootstrapReady = ready
   217  			return ctrl.Result{}, nil
   218  		}
   219  
   220  		// Get and set the name of the secret containing the bootstrap data.
   221  		secretName, _, err := unstructured.NestedString(bootstrapConfig.Object, "status", "dataSecretName")
   222  		if err != nil {
   223  			return ctrl.Result{}, errors.Wrapf(err, "failed to retrieve dataSecretName from bootstrap provider for MachinePool %q in namespace %q", m.Name, m.Namespace)
   224  		} else if secretName == "" {
   225  			return ctrl.Result{}, errors.Errorf("retrieved empty dataSecretName from bootstrap provider for MachinePool %q in namespace %q", m.Name, m.Namespace)
   226  		}
   227  
   228  		m.Spec.Template.Spec.Bootstrap.DataSecretName = pointer.String(secretName)
   229  		m.Status.BootstrapReady = true
   230  		return ctrl.Result{}, nil
   231  	}
   232  
   233  	// If dataSecretName is set without a ConfigRef, this means the user brought their own bootstrap data.
   234  	if m.Spec.Template.Spec.Bootstrap.DataSecretName != nil {
   235  		m.Status.BootstrapReady = true
   236  		conditions.MarkTrue(m, clusterv1.BootstrapReadyCondition)
   237  		return ctrl.Result{}, nil
   238  	}
   239  
   240  	// This should never happen because the MachinePool webhook would not allow neither ConfigRef nor DataSecretName to be set.
   241  	return ctrl.Result{}, errors.Errorf("neither .spec.bootstrap.configRef nor .spec.bootstrap.dataSecretName are set for MachinePool %q in namespace %q", m.Name, m.Namespace)
   242  }
   243  
   244  // reconcileInfrastructure reconciles the Spec.InfrastructureRef object on a MachinePool.
   245  func (r *MachinePoolReconciler) reconcileInfrastructure(ctx context.Context, cluster *clusterv1.Cluster, mp *expv1.MachinePool) (ctrl.Result, error) {
   246  	log := ctrl.LoggerFrom(ctx)
   247  
   248  	// Call generic external reconciler.
   249  	infraReconcileResult, err := r.reconcileExternal(ctx, cluster, mp, &mp.Spec.Template.Spec.InfrastructureRef)
   250  	if err != nil {
   251  		if apierrors.IsNotFound(errors.Cause(err)) {
   252  			log.Error(err, "infrastructure reference could not be found")
   253  			if mp.Status.InfrastructureReady {
   254  				// Infra object went missing after the machine pool was up and running
   255  				log.Error(err, "infrastructure reference has been deleted after being ready, setting failure state")
   256  				mp.Status.FailureReason = capierrors.MachinePoolStatusErrorPtr(capierrors.InvalidConfigurationMachinePoolError)
   257  				mp.Status.FailureMessage = pointer.String(fmt.Sprintf("MachinePool infrastructure resource %v with name %q has been deleted after being ready",
   258  					mp.Spec.Template.Spec.InfrastructureRef.GroupVersionKind(), mp.Spec.Template.Spec.InfrastructureRef.Name))
   259  			}
   260  			conditions.MarkFalse(mp, clusterv1.InfrastructureReadyCondition, clusterv1.IncorrectExternalRefReason, clusterv1.ConditionSeverityError, fmt.Sprintf("could not find infra reference of kind %s with name %s", mp.Spec.Template.Spec.InfrastructureRef.Kind, mp.Spec.Template.Spec.InfrastructureRef.Name))
   261  		}
   262  		return ctrl.Result{}, err
   263  	}
   264  	// if the external object is paused, return without any further processing
   265  	if infraReconcileResult.Paused {
   266  		return ctrl.Result{}, nil
   267  	}
   268  	infraConfig := infraReconcileResult.Result
   269  
   270  	if !infraConfig.GetDeletionTimestamp().IsZero() {
   271  		return ctrl.Result{}, nil
   272  	}
   273  
   274  	ready, err := external.IsReady(infraConfig)
   275  	if err != nil {
   276  		return ctrl.Result{}, err
   277  	}
   278  
   279  	mp.Status.InfrastructureReady = ready
   280  
   281  	// Report a summary of current status of the infrastructure object defined for this machine pool.
   282  	conditions.SetMirror(mp, clusterv1.InfrastructureReadyCondition,
   283  		conditions.UnstructuredGetter(infraConfig),
   284  		conditions.WithFallbackValue(ready, clusterv1.WaitingForInfrastructureFallbackReason, clusterv1.ConditionSeverityInfo, ""),
   285  	)
   286  
   287  	if err := r.reconcileMachines(ctx, mp, infraConfig); err != nil {
   288  		return ctrl.Result{}, errors.Wrapf(err, "failed to reconcile Machines for MachinePool %s", klog.KObj(mp))
   289  	}
   290  
   291  	if !mp.Status.InfrastructureReady {
   292  		log.Info("Infrastructure provider is not yet ready", infraConfig.GetKind(), klog.KObj(infraConfig))
   293  		return ctrl.Result{}, nil
   294  	}
   295  
   296  	var providerIDList []string
   297  	// Get Spec.ProviderIDList from the infrastructure provider.
   298  	if err := util.UnstructuredUnmarshalField(infraConfig, &providerIDList, "spec", "providerIDList"); err != nil && !errors.Is(err, util.ErrUnstructuredFieldNotFound) {
   299  		return ctrl.Result{}, errors.Wrapf(err, "failed to retrieve data from infrastructure provider for MachinePool %q in namespace %q", mp.Name, mp.Namespace)
   300  	}
   301  
   302  	// Get and set Status.Replicas from the infrastructure provider.
   303  	err = util.UnstructuredUnmarshalField(infraConfig, &mp.Status.Replicas, "status", "replicas")
   304  	if err != nil {
   305  		if err != util.ErrUnstructuredFieldNotFound {
   306  			return ctrl.Result{}, errors.Wrapf(err, "failed to retrieve replicas from infrastructure provider for MachinePool %q in namespace %q", mp.Name, mp.Namespace)
   307  		}
   308  	}
   309  
   310  	if len(providerIDList) == 0 && mp.Status.Replicas != 0 {
   311  		log.Info("Retrieved empty spec.providerIDList from infrastructure provider but status.replicas is not zero.", "replicas", mp.Status.Replicas)
   312  		return ctrl.Result{}, nil
   313  	}
   314  
   315  	if !reflect.DeepEqual(mp.Spec.ProviderIDList, providerIDList) {
   316  		mp.Spec.ProviderIDList = providerIDList
   317  		mp.Status.ReadyReplicas = 0
   318  		mp.Status.AvailableReplicas = 0
   319  		mp.Status.UnavailableReplicas = mp.Status.Replicas
   320  	}
   321  
   322  	return ctrl.Result{}, nil
   323  }
   324  
   325  // reconcileMachines reconciles Machines associated with a MachinePool.
   326  //
   327  // Note: In the case of MachinePools the machines are created in order to surface in CAPI what exists in the
   328  // infrastructure while instead on MachineDeployments, machines are created in CAPI first and then the
   329  // infrastructure is created accordingly.
   330  // Note: When supported by the cloud provider implementation of the MachinePool, machines will provide a means to interact
   331  // with the corresponding infrastructure (e.g. delete a specific machine in case MachineHealthCheck detects it is unhealthy).
   332  func (r *MachinePoolReconciler) reconcileMachines(ctx context.Context, mp *expv1.MachinePool, infraMachinePool *unstructured.Unstructured) error {
   333  	log := ctrl.LoggerFrom(ctx)
   334  
   335  	var infraMachineKind string
   336  	if err := util.UnstructuredUnmarshalField(infraMachinePool, &infraMachineKind, "status", "infrastructureMachineKind"); err != nil {
   337  		if errors.Is(err, util.ErrUnstructuredFieldNotFound) {
   338  			log.V(4).Info("MachinePool Machines not supported, no infraMachineKind found")
   339  			return nil
   340  		}
   341  
   342  		return errors.Wrapf(err, "failed to retrieve infraMachineKind from infrastructure provider for MachinePool %s", klog.KObj(mp))
   343  	}
   344  
   345  	infraMachineSelector := metav1.LabelSelector{
   346  		MatchLabels: map[string]string{
   347  			clusterv1.MachinePoolNameLabel: format.MustFormatValue(mp.Name),
   348  			clusterv1.ClusterNameLabel:     mp.Spec.ClusterName,
   349  		},
   350  	}
   351  
   352  	log.V(4).Info("Reconciling MachinePool Machines", "infrastructureMachineKind", infraMachineKind, "infrastructureMachineSelector", infraMachineSelector)
   353  	var infraMachineList unstructured.UnstructuredList
   354  
   355  	// Get the list of infraMachines, which are maintained by the InfraMachinePool controller.
   356  	infraMachineList.SetAPIVersion(infraMachinePool.GetAPIVersion())
   357  	infraMachineList.SetKind(infraMachineKind + "List")
   358  	if err := r.Client.List(ctx, &infraMachineList, client.InNamespace(mp.Namespace), client.MatchingLabels(infraMachineSelector.MatchLabels)); err != nil {
   359  		return errors.Wrapf(err, "failed to list infra machines for MachinePool %q in namespace %q", mp.Name, mp.Namespace)
   360  	}
   361  
   362  	// Add watcher for infraMachine, if there isn't one already; this will allow this controller to reconcile
   363  	// immediately changes made by the InfraMachinePool controller.
   364  	sampleInfraMachine := &unstructured.Unstructured{}
   365  	sampleInfraMachine.SetAPIVersion(infraMachinePool.GetAPIVersion())
   366  	sampleInfraMachine.SetKind(infraMachineKind)
   367  
   368  	// Add watcher for infraMachine, if there isn't one already.
   369  	if err := r.externalTracker.Watch(log, sampleInfraMachine, handler.EnqueueRequestsFromMapFunc(r.infraMachineToMachinePoolMapper)); err != nil {
   370  		return err
   371  	}
   372  
   373  	// Get the list of machines managed by this controller, and align it with the infra machines managed by
   374  	// the InfraMachinePool controller.
   375  	machineList := &clusterv1.MachineList{}
   376  	if err := r.Client.List(ctx, machineList, client.InNamespace(mp.Namespace), client.MatchingLabels(infraMachineSelector.MatchLabels)); err != nil {
   377  		return err
   378  	}
   379  
   380  	if err := r.createOrUpdateMachines(ctx, mp, machineList.Items, infraMachineList.Items); err != nil {
   381  		return errors.Wrapf(err, "failed to create machines for MachinePool %q in namespace %q", mp.Name, mp.Namespace)
   382  	}
   383  
   384  	return nil
   385  }
   386  
   387  // createOrUpdateMachines creates a MachinePool Machine for each infraMachine if it doesn't already exist and sets the owner reference and infraRef.
   388  func (r *MachinePoolReconciler) createOrUpdateMachines(ctx context.Context, mp *expv1.MachinePool, machines []clusterv1.Machine, infraMachines []unstructured.Unstructured) error {
   389  	log := ctrl.LoggerFrom(ctx)
   390  
   391  	// Construct a set of names of infraMachines that already have a Machine.
   392  	infraMachineToMachine := map[string]clusterv1.Machine{}
   393  	for _, machine := range machines {
   394  		infraRef := machine.Spec.InfrastructureRef
   395  		infraMachineToMachine[infraRef.Name] = machine
   396  	}
   397  
   398  	createdMachines := []clusterv1.Machine{}
   399  	var errs []error
   400  	for i := range infraMachines {
   401  		infraMachine := &infraMachines[i]
   402  		// If infraMachine already has a Machine, update it if needed.
   403  		if existingMachine, ok := infraMachineToMachine[infraMachine.GetName()]; ok {
   404  			log.V(2).Info("Patching existing Machine for infraMachine", "infraMachine", klog.KObj(infraMachine), "machine", klog.KObj(&existingMachine))
   405  
   406  			desiredMachine := computeDesiredMachine(mp, infraMachine, &existingMachine)
   407  			if err := ssa.Patch(ctx, r.Client, MachinePoolControllerName, desiredMachine, ssa.WithCachingProxy{Cache: r.ssaCache, Original: &existingMachine}); err != nil {
   408  				log.Error(err, "failed to update Machine", "Machine", klog.KObj(desiredMachine))
   409  				errs = append(errs, errors.Wrapf(err, "failed to update Machine %q", klog.KObj(desiredMachine)))
   410  			}
   411  		} else {
   412  			// Otherwise create a new Machine for the infraMachine.
   413  			log.Info("Creating new Machine for infraMachine", "infraMachine", klog.KObj(infraMachine))
   414  			machine := computeDesiredMachine(mp, infraMachine, nil)
   415  
   416  			if err := ssa.Patch(ctx, r.Client, MachinePoolControllerName, machine); err != nil {
   417  				errs = append(errs, errors.Wrapf(err, "failed to create new Machine for infraMachine %q in namespace %q", infraMachine.GetName(), infraMachine.GetNamespace()))
   418  				continue
   419  			}
   420  
   421  			createdMachines = append(createdMachines, *machine)
   422  		}
   423  	}
   424  	if err := r.waitForMachineCreation(ctx, createdMachines); err != nil {
   425  		errs = append(errs, errors.Wrapf(err, "failed to wait for machines to be created"))
   426  	}
   427  	if len(errs) > 0 {
   428  		return kerrors.NewAggregate(errs)
   429  	}
   430  
   431  	return nil
   432  }
   433  
   434  // computeDesiredMachine constructs the desired Machine for an infraMachine.
   435  // If the Machine exists, it ensures the Machine always owned by the MachinePool.
   436  func computeDesiredMachine(mp *expv1.MachinePool, infraMachine *unstructured.Unstructured, existingMachine *clusterv1.Machine) *clusterv1.Machine {
   437  	infraRef := corev1.ObjectReference{
   438  		APIVersion: infraMachine.GetAPIVersion(),
   439  		Kind:       infraMachine.GetKind(),
   440  		Name:       infraMachine.GetName(),
   441  		Namespace:  infraMachine.GetNamespace(),
   442  	}
   443  
   444  	machine := &clusterv1.Machine{
   445  		ObjectMeta: metav1.ObjectMeta{
   446  			Name: names.SimpleNameGenerator.GenerateName(fmt.Sprintf("%s-", mp.Name)),
   447  			// Note: by setting the ownerRef on creation we signal to the Machine controller that this is not a stand-alone Machine.
   448  			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(mp, machinePoolKind)},
   449  			Namespace:       mp.Namespace,
   450  			Labels:          make(map[string]string),
   451  			Annotations:     make(map[string]string),
   452  		},
   453  		Spec: clusterv1.MachineSpec{
   454  			ClusterName:       mp.Spec.ClusterName,
   455  			InfrastructureRef: infraRef,
   456  		},
   457  	}
   458  
   459  	if existingMachine != nil {
   460  		machine.SetName(existingMachine.Name)
   461  		machine.SetUID(existingMachine.UID)
   462  	}
   463  
   464  	for k, v := range mp.Spec.Template.Annotations {
   465  		machine.Annotations[k] = v
   466  	}
   467  
   468  	// Set the labels from machinePool.Spec.Template.Labels as labels for the new Machine.
   469  	// Note: We can't just set `machinePool.Spec.Template.Labels` directly and thus "share" the labels
   470  	// map between Machine and machinePool.Spec.Template.Labels. This would mean that adding the
   471  	// MachinePoolNameLabel later on the Machine would also add the labels to machinePool.Spec.Template.Labels
   472  	// and thus modify the labels of the MachinePool.
   473  	for k, v := range mp.Spec.Template.Labels {
   474  		machine.Labels[k] = v
   475  	}
   476  
   477  	// Enforce that the MachinePoolNameLabel and ClusterNameLabel are present on the Machine.
   478  	machine.Labels[clusterv1.MachinePoolNameLabel] = format.MustFormatValue(mp.Name)
   479  	machine.Labels[clusterv1.ClusterNameLabel] = mp.Spec.ClusterName
   480  
   481  	return machine
   482  }
   483  
   484  // infraMachineToMachinePoolMapper is a mapper function that maps an InfraMachine to the MachinePool that owns it.
   485  // This is used to trigger an update of the MachinePool when a InfraMachine is changed.
   486  func (r *MachinePoolReconciler) infraMachineToMachinePoolMapper(ctx context.Context, o client.Object) []ctrl.Request {
   487  	log := ctrl.LoggerFrom(ctx)
   488  
   489  	if labels.IsMachinePoolOwned(o) {
   490  		machinePool, err := utilexp.GetMachinePoolByLabels(ctx, r.Client, o.GetNamespace(), o.GetLabels())
   491  		if err != nil {
   492  			log.Error(err, "failed to get MachinePool for InfraMachine", "infraMachine", klog.KObj(o), "labels", o.GetLabels())
   493  			return nil
   494  		}
   495  		if machinePool != nil {
   496  			return []ctrl.Request{
   497  				{
   498  					NamespacedName: client.ObjectKey{
   499  						Namespace: machinePool.Namespace,
   500  						Name:      machinePool.Name,
   501  					},
   502  				},
   503  			}
   504  		}
   505  	}
   506  
   507  	return nil
   508  }
   509  
   510  func (r *MachinePoolReconciler) waitForMachineCreation(ctx context.Context, machineList []clusterv1.Machine) error {
   511  	_ = ctrl.LoggerFrom(ctx)
   512  
   513  	// waitForCacheUpdateTimeout is the amount of time allowed to wait for desired state.
   514  	const waitForCacheUpdateTimeout = 10 * time.Second
   515  
   516  	// waitForCacheUpdateInterval is the amount of time between polling for the desired state.
   517  	// The polling is against a local memory cache.
   518  	const waitForCacheUpdateInterval = 100 * time.Millisecond
   519  
   520  	for i := 0; i < len(machineList); i++ {
   521  		machine := machineList[i]
   522  		pollErr := wait.PollUntilContextTimeout(ctx, waitForCacheUpdateInterval, waitForCacheUpdateTimeout, true, func(ctx context.Context) (bool, error) {
   523  			key := client.ObjectKey{Namespace: machine.Namespace, Name: machine.Name}
   524  			if err := r.Client.Get(ctx, key, &clusterv1.Machine{}); err != nil {
   525  				if apierrors.IsNotFound(err) {
   526  					return false, nil
   527  				}
   528  				return false, err
   529  			}
   530  
   531  			return true, nil
   532  		})
   533  
   534  		if pollErr != nil {
   535  			return errors.Wrapf(pollErr, "failed waiting for machine object %v to be created", klog.KObj(&machine))
   536  		}
   537  	}
   538  
   539  	return nil
   540  }