Skip to content

Commit 610afe0

Browse files
committed
Remove delete requeue in DockerMachinePool controller and ensure DockerMachine ownerRef
1 parent 8003f3f commit 610afe0

File tree

2 files changed

+102
-47
lines changed

2 files changed

+102
-47
lines changed

test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller.go

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ package controllers
2020
import (
2121
"context"
2222
"fmt"
23-
"time"
2423

2524
"github.com/pkg/errors"
2625
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -43,6 +42,7 @@ import (
4342
"sigs.k8s.io/cluster-api/controllers/remote"
4443
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
4544
utilexp "sigs.k8s.io/cluster-api/exp/util"
45+
"sigs.k8s.io/cluster-api/internal/util/ssa"
4646
"sigs.k8s.io/cluster-api/test/infrastructure/container"
4747
infrav1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1beta1"
4848
infraexpv1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/exp/api/v1beta1"
@@ -57,8 +57,7 @@ const (
5757
// dockerMachinePoolLabel is the label used to identify the DockerMachinePool that is responsible for a Docker container.
5858
dockerMachinePoolLabel = "docker.cluster.x-k8s.io/machine-pool"
5959

60-
// requeueAfter is how long to wait before checking again to see if the DockerMachines are still provisioning or deleting.
61-
requeueAfter = 10 * time.Second
60+
dockerMachinePoolControllerName = "dockermachinepool-controller"
6261
)
6362

6463
// DockerMachinePoolReconciler reconciles a DockerMachinePool object.
@@ -71,6 +70,7 @@ type DockerMachinePoolReconciler struct {
7170
// WatchFilterValue is the label value used to filter events prior to reconciliation.
7271
WatchFilterValue string
7372

73+
ssaCache ssa.Cache
7474
recorder record.EventRecorder
7575
externalTracker external.ObjectTracker
7676
}
@@ -140,7 +140,7 @@ func (r *DockerMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Re
140140

141141
// Handle deleted machines
142142
if !dockerMachinePool.ObjectMeta.DeletionTimestamp.IsZero() {
143-
return r.reconcileDelete(ctx, cluster, machinePool, dockerMachinePool)
143+
return ctrl.Result{}, r.reconcileDelete(ctx, cluster, machinePool, dockerMachinePool)
144144
}
145145

146146
// Add finalizer and the InfrastructureMachineKind if they aren't already present, and requeue if either were added.
@@ -194,21 +194,22 @@ func (r *DockerMachinePoolReconciler) SetupWithManager(ctx context.Context, mgr
194194
return errors.Wrap(err, "failed setting up with a controller manager")
195195
}
196196

197-
r.recorder = mgr.GetEventRecorderFor("dockermachinepool-controller")
197+
r.recorder = mgr.GetEventRecorderFor(dockerMachinePoolControllerName)
198198
r.externalTracker = external.ObjectTracker{
199199
Controller: c,
200200
Cache: mgr.GetCache(),
201201
}
202+
r.ssaCache = ssa.NewCache()
202203

203204
return nil
204205
}
205206

206-
func (r *DockerMachinePoolReconciler) reconcileDelete(ctx context.Context, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool) (ctrl.Result, error) {
207+
func (r *DockerMachinePoolReconciler) reconcileDelete(ctx context.Context, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool) error {
207208
log := ctrl.LoggerFrom(ctx)
208209

209210
dockerMachineList, err := getDockerMachines(ctx, r.Client, *cluster, *machinePool, *dockerMachinePool)
210211
if err != nil {
211-
return ctrl.Result{}, err
212+
return err
212213
}
213214

214215
if len(dockerMachineList.Items) > 0 {
@@ -229,10 +230,9 @@ func (r *DockerMachinePoolReconciler) reconcileDelete(ctx context.Context, clust
229230
}
230231

231232
if len(errs) > 0 {
232-
return ctrl.Result{}, kerrors.NewAggregate(errs)
233+
return kerrors.NewAggregate(errs)
233234
}
234-
235-
return ctrl.Result{RequeueAfter: requeueAfter}, nil
235+
return nil
236236
}
237237

238238
// Once there are no DockerMachines left, ensure there are no Docker containers left behind.
@@ -243,21 +243,21 @@ func (r *DockerMachinePoolReconciler) reconcileDelete(ctx context.Context, clust
243243
// List Docker containers, i.e. external machines in the cluster.
244244
externalMachines, err := docker.ListMachinesByCluster(ctx, cluster, labelFilters)
245245
if err != nil {
246-
return ctrl.Result{}, errors.Wrapf(err, "failed to list all machines in the cluster with label \"%s:%s\"", dockerMachinePoolLabel, dockerMachinePool.Name)
246+
return errors.Wrapf(err, "failed to list all machines in the cluster with label \"%s:%s\"", dockerMachinePoolLabel, dockerMachinePool.Name)
247247
}
248248

249249
// Providers should similarly ensure that all infrastructure instances are deleted even if the InfraMachine has not been created yet.
250250
for _, externalMachine := range externalMachines {
251251
log.Info("Deleting Docker container", "container", externalMachine.Name())
252252
if err := externalMachine.Delete(ctx); err != nil {
253-
return ctrl.Result{}, errors.Wrapf(err, "failed to delete machine %s", externalMachine.Name())
253+
return errors.Wrapf(err, "failed to delete machine %s", externalMachine.Name())
254254
}
255255
}
256256

257257
// Once all DockerMachines and Docker containers are deleted, remove the finalizer.
258258
controllerutil.RemoveFinalizer(dockerMachinePool, infraexpv1.MachinePoolFinalizer)
259259

260-
return ctrl.Result{}, nil
260+
return nil
261261
}
262262

263263
func (r *DockerMachinePoolReconciler) reconcileNormal(ctx context.Context, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool) (ctrl.Result, error) {
@@ -318,11 +318,7 @@ func (r *DockerMachinePoolReconciler) reconcileNormal(ctx context.Context, clust
318318
return ctrl.Result{}, nil
319319
}
320320

321-
dockerMachinePool.Status.Ready = false
322-
conditions.MarkFalse(dockerMachinePool, expv1.ReplicasReadyCondition, expv1.WaitingForReplicasReadyReason, clusterv1.ConditionSeverityInfo, "")
323-
324-
// if some machine is still provisioning, force reconcile in few seconds to check again infrastructure.
325-
return ctrl.Result{RequeueAfter: requeueAfter}, nil
321+
return r.updateStatus(ctx, cluster, machinePool, dockerMachinePool, dockerMachineList.Items)
326322
}
327323

328324
func getDockerMachines(ctx context.Context, c client.Client, cluster clusterv1.Cluster, machinePool expv1.MachinePool, dockerMachinePool infraexpv1.DockerMachinePool) (*infrav1.DockerMachineList, error) {
@@ -380,6 +376,64 @@ func dockerMachineToDockerMachinePool(_ context.Context, o client.Object) []ctrl
380376
return nil
381377
}
382378

379+
// updateStatus updates the Status field for the MachinePool object.
380+
// It checks for the current state of the replicas and updates the Status of the MachineSet.
381+
func (r *DockerMachinePoolReconciler) updateStatus(ctx context.Context, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool, dockerMachines []infrav1.DockerMachine) (ctrl.Result, error) {
382+
log := ctrl.LoggerFrom(ctx)
383+
384+
// List the Docker containers. This corresponds to an InfraMachinePool instance for providers.
385+
labelFilters := map[string]string{dockerMachinePoolLabel: dockerMachinePool.Name}
386+
externalMachines, err := docker.ListMachinesByCluster(ctx, cluster, labelFilters)
387+
if err != nil {
388+
return ctrl.Result{}, errors.Wrapf(err, "failed to list all external machines in the cluster")
389+
}
390+
391+
externalMachineMap := make(map[string]*docker.Machine)
392+
for _, externalMachine := range externalMachines {
393+
externalMachineMap[externalMachine.Name()] = externalMachine
394+
}
395+
// We can use reuse getDeletionCandidates to get the list of ready DockerMachines and avoid another API call, even though we aren't deleting them here.
396+
_, readyMachines, err := r.getDeletionCandidates(ctx, dockerMachines, externalMachineMap, machinePool, dockerMachinePool)
397+
if err != nil {
398+
return ctrl.Result{}, err
399+
}
400+
401+
readyReplicaCount := len(readyMachines)
402+
desiredReplicas := int(*machinePool.Spec.Replicas)
403+
404+
switch {
405+
// We are scaling up
406+
case readyReplicaCount < desiredReplicas:
407+
conditions.MarkFalse(dockerMachinePool, clusterv1.ResizedCondition, clusterv1.ScalingUpReason, clusterv1.ConditionSeverityWarning, "Scaling up MachineSet to %d replicas (actual %d)", desiredReplicas, readyReplicaCount)
408+
// We are scaling down
409+
case readyReplicaCount > desiredReplicas:
410+
conditions.MarkFalse(dockerMachinePool, clusterv1.ResizedCondition, clusterv1.ScalingDownReason, clusterv1.ConditionSeverityWarning, "Scaling down MachineSet to %d replicas (actual %d)", desiredReplicas, readyReplicaCount)
411+
default:
412+
// Make sure last resize operation is marked as completed.
413+
// NOTE: we are checking the number of machines ready so we report resize completed only when the machines
414+
// are actually provisioned (vs reporting completed immediately after the last machine object is created). This convention is also used by KCP.
415+
if len(dockerMachines) == readyReplicaCount {
416+
if conditions.IsFalse(dockerMachinePool, clusterv1.ResizedCondition) {
417+
log.Info("All the replicas are ready", "replicas", readyReplicaCount)
418+
}
419+
conditions.MarkTrue(dockerMachinePool, clusterv1.ResizedCondition)
420+
}
421+
// This means that there was no error in generating the desired number of machine objects
422+
conditions.MarkTrue(dockerMachinePool, clusterv1.MachinesCreatedCondition)
423+
}
424+
425+
getters := make([]conditions.Getter, 0, len(dockerMachines))
426+
for i := range dockerMachines {
427+
getters = append(getters, &dockerMachines[i])
428+
}
429+
430+
// Aggregate the operational state of all the machines; while aggregating we are adding the
431+
// source ref (reason@machine/name) so the problem can be easily tracked down to its source machine.
432+
conditions.SetAggregate(dockerMachinePool, expv1.ReplicasReadyCondition, getters, conditions.AddSourceRef())
433+
434+
return ctrl.Result{}, nil
435+
}
436+
383437
func patchDockerMachinePool(ctx context.Context, patchHelper *patch.Helper, dockerMachinePool *infraexpv1.DockerMachinePool) error {
384438
conditions.SetSummary(dockerMachinePool,
385439
conditions.WithConditions(

test/infrastructure/docker/exp/internal/controllers/dockermachinepool_controller_phases.go

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333

3434
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3535
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
36+
"sigs.k8s.io/cluster-api/internal/util/ssa"
3637
infrav1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1beta1"
3738
infraexpv1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/exp/api/v1beta1"
3839
"sigs.k8s.io/cluster-api/test/infrastructure/docker/internal/docker"
@@ -142,16 +143,22 @@ func (r *DockerMachinePoolReconciler) reconcileDockerMachines(ctx context.Contex
142143
// Create a DockerMachine for each Docker container so we surface the information to the user. Use the same name as the Docker container for the Docker Machine for ease of lookup.
143144
// Providers should iterate through their infrastructure instances and ensure that each instance has a corresponding InfraMachine.
144145
for _, machine := range externalMachines {
145-
if _, ok := dockerMachineMap[machine.Name()]; !ok {
146+
if existingMachine, ok := dockerMachineMap[machine.Name()]; ok {
147+
log.V(2).Info("Patching existing DockerMachine", "name", existingMachine.Name)
148+
desiredMachine := computeDesiredDockerMachine(machine.Name(), cluster, machinePool, dockerMachinePool, &existingMachine)
149+
if err := ssa.Patch(ctx, r.Client, dockerMachinePoolControllerName, desiredMachine, ssa.WithCachingProxy{Cache: r.ssaCache, Original: &existingMachine}); err != nil {
150+
return errors.Wrapf(err, "failed to update DockerMachine %q", klog.KObj(desiredMachine))
151+
}
152+
153+
dockerMachineMap[desiredMachine.Name] = *desiredMachine
154+
} else {
146155
log.V(2).Info("Creating a new DockerMachine for Docker container", "container", machine.Name())
147-
dockerMachine, err := r.createDockerMachine(ctx, machine.Name(), cluster, machinePool, dockerMachinePool)
148-
if err != nil {
156+
desiredMachine := computeDesiredDockerMachine(machine.Name(), cluster, machinePool, dockerMachinePool, nil)
157+
if err := ssa.Patch(ctx, r.Client, dockerMachinePoolControllerName, desiredMachine); err != nil {
149158
return errors.Wrap(err, "failed to create a new docker machine")
150159
}
151160

152-
dockerMachineMap[dockerMachine.Name] = *dockerMachine
153-
} else {
154-
log.V(4).Info("DockerMachine already exists, nothing to do", "name", machine.Name())
161+
dockerMachineMap[desiredMachine.Name] = *desiredMachine
155162
}
156163
}
157164

@@ -233,30 +240,15 @@ func (r *DockerMachinePoolReconciler) reconcileDockerMachines(ctx context.Contex
233240
return nil
234241
}
235242

236-
// createDockerMachine creates a DockerMachine to represent a Docker container in a DockerMachinePool.
243+
// computeDesiredDockerMachine creates a DockerMachine to represent a Docker container in a DockerMachinePool.
237244
// These DockerMachines have the clusterv1.ClusterNameLabel and clusterv1.MachinePoolNameLabel to support MachinePool Machines.
238-
func (r *DockerMachinePoolReconciler) createDockerMachine(ctx context.Context, name string, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool) (*infrav1.DockerMachine, error) {
239-
log := ctrl.LoggerFrom(ctx)
240-
241-
labels := map[string]string{
242-
clusterv1.ClusterNameLabel: cluster.Name,
243-
clusterv1.MachinePoolNameLabel: format.MustFormatValue(machinePool.Name),
244-
}
245+
func computeDesiredDockerMachine(name string, cluster *clusterv1.Cluster, machinePool *expv1.MachinePool, dockerMachinePool *infraexpv1.DockerMachinePool, existingDockerMachine *infrav1.DockerMachine) *infrav1.DockerMachine {
245246
dockerMachine := &infrav1.DockerMachine{
246247
ObjectMeta: metav1.ObjectMeta{
247248
Namespace: dockerMachinePool.Namespace,
248249
Name: name,
249-
Labels: labels,
250+
Labels: make(map[string]string),
250251
Annotations: make(map[string]string),
251-
OwnerReferences: []metav1.OwnerReference{
252-
{
253-
APIVersion: dockerMachinePool.APIVersion,
254-
Kind: dockerMachinePool.Kind,
255-
Name: dockerMachinePool.Name,
256-
UID: dockerMachinePool.UID,
257-
},
258-
// Note: Since the MachinePool controller has not created its owner Machine yet, we want to set the DockerMachinePool as the owner so it's not orphaned.
259-
},
260252
},
261253
Spec: infrav1.DockerMachineSpec{
262254
CustomImage: dockerMachinePool.Spec.Template.CustomImage,
@@ -265,13 +257,22 @@ func (r *DockerMachinePoolReconciler) createDockerMachine(ctx context.Context, n
265257
},
266258
}
267259

268-
log.V(2).Info("Creating DockerMachine", "dockerMachine", dockerMachine.Name)
269-
270-
if err := r.Client.Create(ctx, dockerMachine); err != nil {
271-
return nil, errors.Wrap(err, "failed to create dockerMachine")
260+
if existingDockerMachine != nil {
261+
dockerMachine.SetUID(existingDockerMachine.UID)
262+
dockerMachine.SetOwnerReferences(existingDockerMachine.OwnerReferences)
272263
}
273264

274-
return dockerMachine, nil
265+
// Note: Since the MachinePool controller has not created its owner Machine yet, we want to set the DockerMachinePool as the owner so it's not orphaned.
266+
dockerMachine.SetOwnerReferences(util.EnsureOwnerRef(dockerMachine.OwnerReferences, metav1.OwnerReference{
267+
APIVersion: dockerMachinePool.APIVersion,
268+
Kind: dockerMachinePool.Kind,
269+
Name: dockerMachinePool.Name,
270+
UID: dockerMachinePool.UID,
271+
}))
272+
dockerMachine.Labels[clusterv1.ClusterNameLabel] = cluster.Name
273+
dockerMachine.Labels[clusterv1.MachinePoolNameLabel] = format.MustFormatValue(machinePool.Name)
274+
275+
return dockerMachine
275276
}
276277

277278
// deleteMachinePoolMachine attempts to delete a DockerMachine and its associated owner Machine if it exists.

0 commit comments

Comments
 (0)