Skip to content

Commit 5524458

Browse files
Herbaerttuunit
authored andcommitted
chore: trim comments to describe the current state
1 parent 9cff40b commit 5524458

8 files changed

Lines changed: 57 additions & 113 deletions

‎cloud/fake/client.go‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,9 @@ type Client struct {
5555
FailNextEnsureNodeSSH error
5656
FailNextDeleteNodeSSH error
5757

58-
// Before* hooks, if non-nil, run before the call they belong to does any
59-
// work. They let a test observe API server state at the exact moment a cloud
60-
// call is about to happen — for instance to assert a finalizer was persisted
61-
// before the first resource could be created. Unlike FailNext*, they are not
62-
// consumed and fire on every call.
58+
// Before* hooks, if non-nil, run before the call they belong to. They let a
59+
// test observe API server state at the exact moment a cloud call would
60+
// happen. Unlike FailNext*, they are not consumed.
6361
BeforeCreateServer func()
6462
BeforeGetNetwork func()
6563

‎controller/controller_test_helpers_test.go‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,7 @@ func createOwnerCluster(ctx context.Context, name string) {
8787
Expect(k8sClient.Create(ctx, cluster)).To(Succeed())
8888
}
8989

90-
// Machines are created without bootstrap data; the specs that need it attach
91-
// it afterwards via updateMachineBootstrapSecret.
9290
func createOwnerMachine(ctx context.Context, name, clusterName, stackitMachineName string) {
93-
bootstrapSecretName := new("")
9491
machine := &clusterv1.Machine{
9592
ObjectMeta: metav1.ObjectMeta{
9693
Name: name,
@@ -102,7 +99,8 @@ func createOwnerMachine(ctx context.Context, name, clusterName, stackitMachineNa
10299
Spec: clusterv1.MachineSpec{
103100
ClusterName: clusterName,
104101
Bootstrap: clusterv1.Bootstrap{
105-
DataSecretName: bootstrapSecretName,
102+
// Specs that need bootstrap data attach it with updateMachineBootstrapSecret.
103+
DataSecretName: new(""),
106104
},
107105
InfrastructureRef: clusterv1.ContractVersionedObjectReference{
108106
APIGroup: infrav1.GroupVersion.Group,

‎controller/stackitcluster_bastion.go‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,9 @@ func (r *StackitClusterReconciler) reconcileBastion(
4949
}
5050

5151
if !stackitCluster.Spec.Bastion.Enabled {
52-
// The condition carries this reason only after a cleanup has succeeded,
53-
// so anything else means we may still own bastion resources — including
54-
// the case where EnsureBastion succeeded but its status patch was lost.
55-
// Keying on it instead of on the status keeps the tag-based cleanup to
56-
// once per cluster rather than once per reconcile, which matters because
57-
// this path runs for every cluster without a bastion.
52+
// The reason is set only after a cleanup has succeeded, so anything else
53+
// means bastion resources may still exist. Keying on it rather than on
54+
// the status keeps the tag-based cleanup to once per cluster.
5855
condition := meta.FindStatusCondition(stackitCluster.Status.Conditions, infrav1.ClusterBastionReadyCondition)
5956
if condition == nil || condition.Reason != bastionDisabledReason {
6057
if err := cloudClient.DeleteNodeSSHAccess(ctx, bastionservice.NodeSSHAccessTags(stackitCluster)); err != nil {

‎controller/stackitcluster_controller_test.go‎

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,8 @@ var _ = Describe("StackitCluster Controller", func() {
203203
})
204204

205205
It("cleans up bastion resources during deletion even when bastion status was never persisted", func() {
206-
// The cloud-cleanup block used to be gated on persisted status for the
207-
// bastion, while the load balancer was gated on its spec flag. A bastion
208-
// created without its status patch landing (process restart, conflict)
209-
// therefore skipped cleanup entirely and leaked server, public IP and
210-
// security group.
206+
// A bastion whose status patch never landed must still be cleaned up, so
207+
// cleanup follows the spec flag rather than persisted status.
211208
createOwnerCluster(ctx, clusterName+"-nolb")
212209
defer deleteIfExists(ctx, &clusterv1.Cluster{
213210
ObjectMeta: metav1.ObjectMeta{Name: clusterName + "-nolb", Namespace: namespace},
@@ -247,10 +244,8 @@ var _ = Describe("StackitCluster Controller", func() {
247244
})
248245

249246
It("finalizes deletion when the credentials Secret is already gone", func() {
250-
// A missing credentials Secret cannot be recovered from, and it commonly
251-
// disappears first during namespace teardown. Broadening the delete gate
252-
// to spec.Bastion.Enabled made a working cloud client mandatory for every
253-
// bastion cluster, which would strand such a cluster in Terminating.
247+
// The Secret commonly disappears first during namespace teardown, and
248+
// without it no cloud client can be built at all.
254249
createOwnerCluster(ctx, clusterName+"-nocreds")
255250
defer deleteIfExists(ctx, &clusterv1.Cluster{
256251
ObjectMeta: metav1.ObjectMeta{Name: clusterName + "-nocreds", Namespace: namespace},
@@ -284,10 +279,8 @@ var _ = Describe("StackitCluster Controller", func() {
284279
})
285280

286281
It("tears the bastion down when disabled even if its status was never persisted", func() {
287-
// Counterpart to the deletion path: disabling the bastion used to be
288-
// gated on hasBastionStatus alone. With the status lost, nothing was torn
289-
// down while the condition reported "bastion disabled" — leaving port 22
290-
// open for the rest of the cluster's life.
282+
// With the status lost, a status-gated teardown would report the bastion
283+
// as disabled while leaving port 22 open.
291284
got := &infrav1.StackitCluster{}
292285
Expect(k8sClient.Get(ctx, stackitKey, got)).To(Succeed())
293286
got.Spec.Bastion = validBastionSpec()
@@ -316,8 +309,7 @@ var _ = Describe("StackitCluster Controller", func() {
316309

317310
It("tears the bastion down when disabled even if only its status was lost", func() {
318311
// Narrower than the case above: the condition survives and still reports
319-
// the bastion as available, only the bastion status fields are gone.
320-
// Gating the cleanup on hasBastionStatus left the server running here.
312+
// the bastion as available, only the status fields are gone.
321313
got := &infrav1.StackitCluster{}
322314
Expect(k8sClient.Get(ctx, stackitKey, got)).To(Succeed())
323315
got.Spec.Bastion = validBastionSpec()
@@ -345,10 +337,8 @@ var _ = Describe("StackitCluster Controller", func() {
345337
})
346338

347339
It("cleans up the load balancer during deletion when it was disabled and its status was lost", func() {
348-
// Counterpart to the bastion case: flipping apiServerLoadBalancer.enabled
349-
// off neither deletes the load balancer nor clears its ID, so with the
350-
// status patch lost the deletion gate matched nothing and the load
351-
// balancer stayed behind.
340+
// Flipping apiServerLoadBalancer.enabled off neither deletes the load
341+
// balancer nor clears its ID, so deletion cannot rely on either.
352342
_, err := reconciler.Reconcile(ctx, request)
353343
Expect(err).NotTo(HaveOccurred())
354344
Expect(fakeCloud.LoadBalancerCount()).To(Equal(1))
@@ -595,12 +585,8 @@ var _ = Describe("StackitCluster Controller", func() {
595585
}).Should(BeTrue())
596586
})
597587

598-
// AddFinalizer only mutated the object in memory, and the write to etcd
599-
// happened in the deferred PatchObject at the end of Reconcile — after the
600-
// load balancer and the bastion had been created. A process dying in between
601-
// left those resources behind an object with no finalizer to clean them up.
602-
// GetNetwork is the first cloud call of the reconcile, so a hook there
603-
// proves the ordering rather than only the end result.
588+
// GetNetwork is the first cloud call of the reconcile, so a hook there proves
589+
// the ordering rather than only the end result.
604590
It("persists the finalizer before the first cloud call", func() {
605591
var finalizersAtFirstCall []string
606592
fakeCloud.BeforeGetNetwork = func() {
@@ -617,13 +603,10 @@ var _ = Describe("StackitCluster Controller", func() {
617603
"cloud resources were created while the API server had no finalizer to clean them up")
618604
})
619605

620-
// The finalizer used to go away regardless of remaining Machines. Their
621-
// controllers reach credentials and project context through this
622-
// StackitCluster, so once it is gone they can neither delete their servers
623-
// nor drop their own finalizers — the VMs are orphaned and the Machines
624-
// hang. Cluster API orders this correctly when the deletion starts at the
625-
// Cluster, but a namespace teardown or a direct delete of this object
626-
// bypasses that ordering.
606+
// Machine controllers reach credentials and project context through this
607+
// StackitCluster, so it has to outlive them. Cluster API orders this
608+
// correctly when deletion starts at the Cluster, but a namespace teardown or
609+
// a direct delete of this object bypasses that ordering.
627610
It("keeps the finalizer while Machines still exist for the cluster", func() {
628611
_, err := reconciler.Reconcile(ctx, request)
629612
Expect(err).NotTo(HaveOccurred())

‎controller/stackitcluster_infrastructure.go‎

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,9 @@ func (r *StackitClusterReconciler) reconcileNormal(ctx context.Context, clusterS
3939

4040
if !controllerutil.ContainsFinalizer(stackitCluster, infrav1.ClusterFinalizer) {
4141
controllerutil.AddFinalizer(stackitCluster, infrav1.ClusterFinalizer)
42-
// Persisted immediately, before anything can create a cloud resource.
43-
// AddFinalizer only mutates the object in memory; it otherwise reaches
44-
// etcd through the deferred PatchObject at the end of Reconcile, and a
45-
// process that dies in between leaves a load balancer or a bastion
46-
// running behind an object that carries no finalizer to clean it up.
42+
// Persisted immediately, before any cloud resource can be created, to
43+
// ensure nothing is running behind an object that carries no finalizer to
44+
// clean it up.
4745
if err := clusterScope.PatchObject(ctx); err != nil {
4846
return ctrl.Result{}, fmt.Errorf("persist finalizer: %w", err)
4947
}
@@ -255,20 +253,15 @@ func (r *StackitClusterReconciler) reconcileDelete(ctx context.Context, clusterS
255253
return ctrl.Result{RequeueAfter: deleteRequeueAfter}, nil
256254
}
257255

258-
// Cleanup runs unconditionally. Neither spec nor status is a trustworthy
259-
// record of what exists in the cloud: a resource can be created before its
260-
// status patch lands, and disabling the load balancer or the bastion leaves
261-
// the running resource behind. ResolveID, DeleteBastion and
262-
// DeleteNodeSSHAccess all fall back to tag lookups and tolerate NotFound, so
263-
// asking for everything costs a handful of list calls once per cluster and
264-
// removes every combination in which a resource could be missed.
256+
// Cleanup runs unconditionally: neither spec nor status is a trustworthy
257+
// record of what exists in the cloud. ResolveID, DeleteBastion and
258+
// DeleteNodeSSHAccess fall back to tag lookups and tolerate NotFound.
265259
cloudClient, err := util.BuildCloudClient(ctx, r.Client, r.CloudClientFactory, stackitCluster)
266260
if err != nil {
267-
// A missing credentials Secret can never be recovered from — it commonly
268-
// disappears first during namespace teardown. Blocking here would strand
269-
// the cluster in Terminating forever, so finalize and make the possible
270-
// leak loud instead. Any other credentials problem is fixable, so keep
271-
// retrying for those.
261+
// A missing credentials Secret cannot be recovered from and commonly
262+
// disappears first during namespace teardown, so finalize and make the
263+
// possible leak loud rather than stranding the cluster in Terminating.
264+
// Every other credentials problem is fixable and keeps retrying.
272265
if apierrors.IsNotFound(err) {
273266
if r.Recorder != nil {
274267
r.Recorder.Eventf(stackitCluster, nil, corev1.EventTypeWarning, "CleanupSkipped", "Delete",

‎controller/stackitmachine_controller_test.go‎

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,9 @@ var _ = Describe("StackitMachine Controller", func() {
132132
})
133133

134134
It("does not silently recreate the server of an already-provisioned machine", func() {
135-
// When the backing server disappears out-of-band, ensureServer used to
136-
// call CreateServer again, replaying the original bootstrap data — which
137-
// is pinned to the previous identity. The replacement either never
138-
// rejoins (different IP) or rejoins while Machine and Node keep pointing
139-
// at the deleted server (same IP) — neither restores the cluster, and
140-
// both consume another VM unnoticed.
135+
// Recreating it would replay bootstrap data pinned to the previous
136+
// identity: the replacement either never rejoins or rejoins while Machine
137+
// and Node still point at the deleted server.
141138
updateMachineBootstrapSecret(ctx, machineName, bootstrapName)
142139
createBootstrapSecret(ctx, bootstrapName)
143140

@@ -398,12 +395,8 @@ var _ = Describe("StackitMachine Controller", func() {
398395
}).Should(BeTrue())
399396
})
400397

401-
// AddFinalizer only mutated the object in memory, and the write to etcd
402-
// happened in the deferred PatchObject at the end of Reconcile — after
403-
// CreateServer. A process dying in between left a running server behind an
404-
// object with no finalizer to clean it up. The hook observes API server
405-
// state from inside the cloud call, so it proves the ordering rather than
406-
// only the end result.
398+
// The hook observes API server state from inside the cloud call, so it proves
399+
// the ordering rather than only the end result.
407400
It("persists the finalizer before creating the server", func() {
408401
updateMachineBootstrapSecret(ctx, machineName, bootstrapName)
409402
createBootstrapSecret(ctx, bootstrapName)
@@ -423,10 +416,8 @@ var _ = Describe("StackitMachine Controller", func() {
423416
"the server was created while the API server had no finalizer to clean it up")
424417
})
425418

426-
// An empty status.instanceID used to be taken as proof that no VM had ever
427-
// been created, so the finalizer went away without a single cloud call. If
428-
// CreateServer had succeeded and the status patch had not, that server kept
429-
// running, tagged and unreferenced by any object.
419+
// A lost status patch leaves a running, tagged server that no field on the
420+
// object names, so an empty status.instanceID is not proof that none exists.
430421
It("deletes a tagged server whose instance ID was lost from the status", func() {
431422
updateMachineBootstrapSecret(ctx, machineName, bootstrapName)
432423
createBootstrapSecret(ctx, bootstrapName)
@@ -476,11 +467,9 @@ var _ = Describe("StackitMachine Controller", func() {
476467
Expect(requests).To(ConsistOf(request))
477468
})
478469

479-
// The mapper used to match Machine.spec.clusterName against the
480-
// StackitCluster name, so it enqueued nothing as soon as the two differed.
481-
// Every other spec here hides the bug because createOwnerCluster gives the
482-
// Cluster and its infrastructureRef the same name; a ClusterClass-generated
483-
// infrastructureRef never does.
470+
// Machine.spec.clusterName names the Cluster, not the StackitCluster, and a
471+
// ClusterClass-generated infrastructureRef never shares its Cluster's name.
472+
// Every other spec here uses matching names and would miss that.
484473
It("maps StackitCluster events when the StackitCluster name differs from the Cluster name", func() {
485474
suffix := time.Now().UnixNano()
486475
ownerClusterName := fmt.Sprintf("owner-%d", suffix)

‎controller/stackitmachine_infrastructure.go‎

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,8 @@ func (r *StackitMachineReconciler) reconcileNormal(ctx context.Context, machineS
3838

3939
if !controllerutil.ContainsFinalizer(stackitMachine, infrav1.MachineFinalizer) {
4040
controllerutil.AddFinalizer(stackitMachine, infrav1.MachineFinalizer)
41-
// Persisted immediately, before CreateServer can run. AddFinalizer only
42-
// mutates the object in memory; it otherwise reaches etcd through the
43-
// deferred PatchObject at the end of Reconcile, and a process that dies
44-
// in between leaves a server running behind an object that carries no
45-
// finalizer to clean it up.
41+
// Persisted immediately, before CreateServer can run, to ensure no servers
42+
// are running behind an object that carries no finalizer to clean it up.
4643
if err := machineScope.PatchObject(ctx); err != nil {
4744
return ctrl.Result{}, fmt.Errorf("persist finalizer: %w", err)
4845
}
@@ -171,16 +168,14 @@ func validateMachineAvailabilityZone(machineScope *scope.MachineScope) error {
171168
func (r *StackitMachineReconciler) reconcileDelete(ctx context.Context, machineScope *scope.MachineScope) error {
172169
stackitMachine := machineScope.StackitMachine
173170

174-
// The cloud client is built unconditionally: an empty status.instanceID is
175-
// not proof that no server exists, so every deletion has to ask the cloud
176-
// before it may drop the finalizer.
171+
// An empty status.instanceID is not proof that no server exists, so every
172+
// deletion asks the cloud before dropping the finalizer.
177173
cloudClient, err := util.BuildCloudClient(ctx, r.Client, r.CloudClientFactory, machineScope.StackitCluster)
178174
if err != nil {
179-
// A missing credentials Secret can never be recovered from, and it
180-
// commonly disappears first during namespace teardown. Blocking here
181-
// would strand the Machine in Terminating forever, so finalize and make
182-
// the possible leak loud instead — the same trade the cluster side
183-
// makes. Any other credentials problem is fixable, so keep retrying.
175+
// A missing credentials Secret cannot be recovered from and commonly
176+
// disappears first during namespace teardown, so finalize and make the
177+
// possible leak loud rather than stranding the Machine in Terminating.
178+
// Every other credentials problem is fixable and keeps retrying.
184179
if apierrors.IsNotFound(err) {
185180
if r.Recorder != nil {
186181
r.Recorder.Eventf(stackitMachine, nil, corev1.EventTypeWarning, "CleanupSkipped", "Delete",
@@ -223,11 +218,9 @@ func (r *StackitMachineReconciler) reconcileDelete(ctx context.Context, machineS
223218
}
224219

225220
// resolveServerForDeletion reports the ID of the server backing this machine, or
226-
// an empty string once the cloud confirms none exists.
227-
//
228-
// status.instanceID alone is not enough: CreateServer can succeed and the status
229-
// patch can be lost, leaving a tagged server that no field on the object refers
230-
// to. The tags carry the Machine UID and therefore still identify that server.
221+
// an empty string once the cloud confirms none exists. It falls back to the tags,
222+
// which carry the Machine UID, because a lost status patch can leave a running
223+
// server that status.instanceID no longer names.
231224
func (r *StackitMachineReconciler) resolveServerForDeletion(
232225
ctx context.Context,
233226
cloudClient cloud.Client,
@@ -288,13 +281,8 @@ func (r *StackitMachineReconciler) ensureServer(
288281
return nil, false, err
289282
}
290283

291-
// The machine had already been provisioned and its server has since
292-
// disappeared. Recreating it here would replay the original bootstrap data,
293-
// which is pinned to the previous identity: the replacement either never
294-
// rejoins (different IP) or rejoins while Machine/Node keep pointing at the
295-
// deleted server (same IP). Neither restores the cluster, and both consume
296-
// another VM silently. Surface it instead and let Cluster API decide to
297-
// replace the Machine.
284+
// Recreating the server would replay bootstrap data pinned to the previous
285+
// identity, so surface the loss and let Cluster API replace the Machine.
298286
if stackitMachine.Status.Initialization.Provisioned {
299287
return nil, false, fmt.Errorf(
300288
"%w: server %s for already-provisioned machine no longer exists; the Machine must be replaced",

‎controller/stackitmachine_watches.go‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ func (r *StackitMachineReconciler) stackitMachineRequestsForStackitCluster(ctx c
4040
}
4141

4242
// Machine.spec.clusterName names the owning Cluster, not the StackitCluster,
43-
// and nothing forces the two to share a name — a ClusterClass-generated
44-
// infrastructureRef carries a random suffix. Resolve the owning Cluster
45-
// first, then match against its name.
43+
// and a ClusterClass-generated infrastructureRef never shares its name.
4644
cluster, err := clusterutil.GetOwnerCluster(ctx, r.Client, stackitCluster.ObjectMeta)
4745
switch {
4846
case apierrors.IsNotFound(err) || cluster == nil:

0 commit comments

Comments
 (0)