@@ -35,13 +35,21 @@ import (
35
35
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
36
36
"k8s.io/apimachinery/pkg/runtime"
37
37
"k8s.io/apimachinery/pkg/types"
38
+ clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
38
39
clusterutilv1 "sigs.k8s.io/cluster-api/util"
40
+ "sigs.k8s.io/cluster-api/util/patch"
39
41
ctrl "sigs.k8s.io/controller-runtime"
40
42
"sigs.k8s.io/controller-runtime/pkg/client"
43
+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
41
44
)
42
45
43
46
const (
44
47
agentClusterDependenciesWaitTime = 5 * time .Second
48
+ AgentClusterRefLabel = "agentClusterRef"
49
+ )
50
+
51
+ var (
52
+ agentClusterFinalizer = "agentcluster" + capiproviderv1 .GroupVersion .Group + "/deprovision"
45
53
)
46
54
47
55
// AgentClusterReconciler reconciles a AgentCluster object
@@ -66,16 +74,13 @@ type ControlPlane struct {
66
74
//+kubebuilder:rbac:groups=extensions.hive.openshift.io,resources=agentclusterinstalls,verbs=get;list;watch;create;update;patch;delete
67
75
//+kubebuilder:rbac:groups=hypershift.openshift.io,resources=hostedcontrolplanes,verbs=get;list;watch;
68
76
69
- func (r * AgentClusterReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
77
+ func (r * AgentClusterReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (_ ctrl.Result , rerr error ) {
70
78
log := r .Log .WithFields (
71
79
logrus.Fields {
72
80
"agent_cluster" : req .Name ,
73
81
"agent_cluster_namespace" : req .Namespace ,
74
82
})
75
83
76
- defer func () {
77
- log .Info ("AgentCluster Reconcile ended" )
78
- }()
79
84
log .Info ("AgentCluster Reconcile start" )
80
85
81
86
agentCluster := & capiproviderv1.AgentCluster {}
@@ -84,17 +89,54 @@ func (r *AgentClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request
84
89
return ctrl.Result {}, client .IgnoreNotFound (err )
85
90
}
86
91
87
- // If the agentCluster has no reference to a ClusterDeployment, create one
92
+ patchHelper , err := patch .NewHelper (agentCluster , r .Client )
93
+ if err != nil {
94
+ return ctrl.Result {}, err
95
+ }
96
+ defer func () {
97
+ if rerr := patchHelper .Patch (ctx , agentCluster ); rerr != nil {
98
+ log .WithError (err ).Errorf ("failed patching AgentCluster" )
99
+ }
100
+ log .Info ("AgentCluster Reconcile ended" )
101
+ }()
102
+
103
+ if ! agentCluster .DeletionTimestamp .IsZero () {
104
+ if err = r .handleDeletion (ctx , agentCluster ); err != nil {
105
+ log .WithError (err ).Errorf ("failed to remove AgentCluster" )
106
+ return ctrl.Result {}, err
107
+ }
108
+ return ctrl.Result {}, nil
109
+ }
110
+
111
+ if ! controllerutil .ContainsFinalizer (agentCluster , agentClusterFinalizer ) {
112
+ controllerutil .AddFinalizer (agentCluster , agentClusterFinalizer )
113
+ }
114
+
115
+ if paused := agentCluster .Annotations [clusterv1 .PausedAnnotation ]; paused == "true" {
116
+ log .Info ("Skipping reconcile of AgentCluster as it's paused, but orphan its resources" )
117
+ if err = r .orphanClusterDeployment (ctx , agentCluster ); err != nil {
118
+ return ctrl.Result {}, err
119
+ }
120
+ return ctrl.Result {}, nil
121
+ }
122
+
123
+ // If the agentCluster has no reference to a ClusterDeployment, find or create one
88
124
if agentCluster .Status .ClusterDeploymentRef .Name == "" {
89
- return r .createClusterDeployment (ctx , log , agentCluster )
125
+ return r .findOrCreateClusterDeployment (ctx , log , agentCluster )
90
126
}
127
+
91
128
clusterDeployment := & hivev1.ClusterDeployment {}
92
- err := r .Get (ctx , types.NamespacedName {Namespace : agentCluster .Status .ClusterDeploymentRef .Namespace , Name : agentCluster .Status .ClusterDeploymentRef .Name }, clusterDeployment )
93
- if err != nil {
129
+ if err = r .Get (ctx , types.NamespacedName {Namespace : agentCluster .Status .ClusterDeploymentRef .Namespace , Name : agentCluster .Status .ClusterDeploymentRef .Name }, clusterDeployment ); err != nil {
94
130
log .WithError (err ).Error ("Failed to get ClusterDeployment" )
95
131
return ctrl.Result {}, err
96
132
}
97
133
134
+ err = r .ensureOwnedClusterDeployment (ctx , agentCluster , clusterDeployment )
135
+ if err != nil {
136
+ log .WithError (err ).Errorf ("failed to ensure ClusterDeployment %s is owned by AgentCluster %s" , clusterDeployment .Name , agentCluster .Name )
137
+ return ctrl.Result {}, err
138
+ }
139
+
98
140
err = r .ensureAgentClusterInstall (ctx , log , clusterDeployment , agentCluster )
99
141
if err != nil {
100
142
return ctrl.Result {}, err
@@ -175,6 +217,38 @@ func (r *AgentClusterReconciler) getControlPlane(ctx context.Context, log logrus
175
217
return & controlPlane , nil
176
218
}
177
219
220
+ func (r * AgentClusterReconciler ) findOrCreateClusterDeployment (ctx context.Context , log logrus.FieldLogger , agentCluster * capiproviderv1.AgentCluster ) (ctrl.Result , error ) {
221
+ clusterDeployment , err := r .findClusterDeployment (ctx , agentCluster )
222
+ if err != nil {
223
+ return ctrl.Result {}, err
224
+ }
225
+ if clusterDeployment != nil {
226
+ log .Infof ("Found previously created clusterDeployment referencing agentCluster %s. Re-adding agentCluster status to reference clusterDeployment %s" , agentCluster .Name , clusterDeployment .Name )
227
+ agentCluster .Status .ClusterDeploymentRef .Name = clusterDeployment .Name
228
+ agentCluster .Status .ClusterDeploymentRef .Namespace = clusterDeployment .Namespace
229
+ return ctrl.Result {}, nil
230
+ }
231
+ return r .createClusterDeployment (ctx , log , agentCluster )
232
+ }
233
+
234
+ func (r * AgentClusterReconciler ) findClusterDeployment (ctx context.Context , agentCluster * capiproviderv1.AgentCluster ) (* hivev1.ClusterDeployment , error ) {
235
+ labelSelector := metav1.LabelSelector {MatchLabels : map [string ]string {AgentClusterRefLabel : agentCluster .Name }}
236
+ selector , err := metav1 .LabelSelectorAsSelector (& labelSelector )
237
+ if err != nil {
238
+ return nil , err
239
+ }
240
+
241
+ clusterDeployments := & hivev1.ClusterDeploymentList {}
242
+ if err := r .Client .List (ctx , clusterDeployments , & client.ListOptions {LabelSelector : selector }); err != nil {
243
+ return nil , err
244
+ }
245
+
246
+ if len (clusterDeployments .Items ) == 0 {
247
+ return nil , nil
248
+ }
249
+ return & clusterDeployments .Items [0 ], nil
250
+ }
251
+
178
252
func (r * AgentClusterReconciler ) createClusterDeploymentObject (agentCluster * capiproviderv1.AgentCluster ,
179
253
controlPlane * ControlPlane ) * hivev1.ClusterDeployment {
180
254
var kubeadminPassword * corev1.LocalObjectReference
@@ -193,6 +267,9 @@ func (r *AgentClusterReconciler) createClusterDeploymentObject(agentCluster *cap
193
267
Name : agentCluster .Name ,
194
268
UID : agentCluster .UID ,
195
269
}},
270
+ Labels : map [string ]string {
271
+ AgentClusterRefLabel : agentCluster .Name ,
272
+ },
196
273
},
197
274
Spec : hivev1.ClusterDeploymentSpec {
198
275
Installed : true ,
@@ -232,7 +309,6 @@ func (r *AgentClusterReconciler) createClusterDeployment(ctx context.Context, lo
232
309
clusterDeployment := r .createClusterDeploymentObject (agentCluster , controlPlane )
233
310
234
311
r .labelControlPlaneSecrets (ctx , controlPlane , agentCluster .Namespace )
235
-
236
312
agentCluster .Status .ClusterDeploymentRef .Name = clusterDeployment .Name
237
313
agentCluster .Status .ClusterDeploymentRef .Namespace = clusterDeployment .Namespace
238
314
if err = r .Client .Create (ctx , clusterDeployment ); err != nil {
@@ -243,13 +319,31 @@ func (r *AgentClusterReconciler) createClusterDeployment(ctx context.Context, lo
243
319
return ctrl.Result {}, err
244
320
}
245
321
}
246
- if err = r .Client .Status ().Update (ctx , agentCluster ); err != nil {
247
- log .WithError (err ).Error ("Failed to update status" )
248
- return ctrl.Result {}, err
249
- }
250
322
return ctrl.Result {}, nil
251
323
}
252
324
325
+ // ensureOwnedClusterDeployment makes sure that the ClusterDeployment has its owner set to this AgentCluster
326
+ // and that the ClusterDeployment has a label referencing this AgentCluster.
327
+ func (r * AgentClusterReconciler ) ensureOwnedClusterDeployment (ctx context.Context , agentCluster * capiproviderv1.AgentCluster , clusterDeployment * hivev1.ClusterDeployment ) error {
328
+ alreadyOwned := clusterutilv1 .IsOwnedByObject (clusterDeployment , agentCluster )
329
+ agentClusterRef := clusterDeployment .ObjectMeta .Labels [AgentClusterRefLabel ]
330
+ if alreadyOwned && agentClusterRef != "" && agentClusterRef == agentCluster .Name {
331
+ return nil
332
+ }
333
+ patch := client .MergeFrom (clusterDeployment .DeepCopy ())
334
+ if err := controllerutil .SetOwnerReference (agentCluster , clusterDeployment , r .Scheme ); err != nil {
335
+ return err
336
+ }
337
+ if clusterDeployment .ObjectMeta .Labels == nil {
338
+ clusterDeployment .ObjectMeta .Labels = make (map [string ]string )
339
+ }
340
+ clusterDeployment .ObjectMeta .Labels [AgentClusterRefLabel ] = agentCluster .Name
341
+ if err := r .Client .Patch (ctx , clusterDeployment , patch ); err != nil {
342
+ return err
343
+ }
344
+ return nil
345
+ }
346
+
253
347
func (r * AgentClusterReconciler ) ensureAgentClusterInstall (ctx context.Context , log logrus.FieldLogger , clusterDeployment * hivev1.ClusterDeployment , agentCluster * capiproviderv1.AgentCluster ) error {
254
348
log .Info ("Setting AgentClusterInstall" )
255
349
agentClusterInstall := & hiveext.AgentClusterInstall {}
@@ -348,3 +442,48 @@ func (r *AgentClusterReconciler) ensureSecretLabel(ctx context.Context, name, na
348
442
r .Log .WithError (err ).Warn ("Failed labeling secret" )
349
443
}
350
444
}
445
+
446
+ func (r * AgentClusterReconciler ) handleDeletion (ctx context.Context , agentCluster * capiproviderv1.AgentCluster ) error {
447
+ if paused := agentCluster .Annotations [clusterv1 .PausedAnnotation ]; paused == "true" {
448
+ // unset finalizer, remove owner from ClusterDeployment to orphan it and return
449
+ if err := r .orphanClusterDeployment (ctx , agentCluster ); err != nil {
450
+ return err
451
+ }
452
+ }
453
+ controllerutil .RemoveFinalizer (agentCluster , agentClusterFinalizer )
454
+ return nil
455
+ }
456
+
457
+ // orphanClusterDeployment removes this AgentCluster as the owner of this ClusterDeployment. This ensures that there's
458
+ // no cascade deletion of the ClusterDeployment (and its AgentClusterInstall) if the AgentCluster is deleted.
459
+ func (r * AgentClusterReconciler ) orphanClusterDeployment (ctx context.Context , agentCluster * capiproviderv1.AgentCluster ) error {
460
+ if agentCluster .Status .ClusterDeploymentRef .Name == "" {
461
+ return nil
462
+ }
463
+ clusterDeployment := & hivev1.ClusterDeployment {}
464
+ if err := r .Get (ctx , types.NamespacedName {Namespace : agentCluster .Status .ClusterDeploymentRef .Namespace , Name : agentCluster .Status .ClusterDeploymentRef .Name }, clusterDeployment ); err != nil {
465
+ if apierrors .IsNotFound (err ) {
466
+ return nil
467
+ }
468
+ return err
469
+ }
470
+
471
+ if ! clusterutilv1 .IsOwnedByObject (clusterDeployment , agentCluster ) {
472
+ return nil
473
+ }
474
+
475
+ var newOwners []metav1.OwnerReference
476
+ for _ , owner := range clusterDeployment .GetOwnerReferences () {
477
+ if owner .Kind == agentCluster .Kind && owner .Name == agentCluster .Name && owner .APIVersion == agentCluster .APIVersion && owner .UID == agentCluster .UID {
478
+ continue
479
+ }
480
+ newOwners = append (newOwners , owner )
481
+ }
482
+
483
+ patch := client .MergeFrom (clusterDeployment .DeepCopy ())
484
+ clusterDeployment .SetOwnerReferences (newOwners )
485
+ if err := r .Patch (ctx , clusterDeployment , patch ); err != nil {
486
+ return err
487
+ }
488
+ return nil
489
+ }
0 commit comments