@@ -138,6 +138,100 @@ func TestReconcile_when_cluster_ready(t *testing.T) {
138
138
}
139
139
}
140
140
141
+ func TestReconcile_when_cluster_ready_bootstrapped_with_same_config (t * testing.T ) {
142
+ bc := makeTestClusterBootstrapConfig (func (c * capiv1alpha1.ClusterBootstrapConfig ) {
143
+ c .Spec .RequireClusterReady = true
144
+ })
145
+ readyNode := makeNode (map [string ]string {
146
+ "node-role.kubernetes.io/control-plane" : "" ,
147
+ }, corev1.NodeCondition {
148
+ Type : "Ready" , Status : "True" , LastHeartbeatTime : metav1 .Now (), LastTransitionTime : metav1 .Now (), Reason : "KubeletReady" , Message : "kubelet is posting ready status" })
149
+
150
+ cl := makeTestCluster (func (c * gitopsv1alpha1.GitopsCluster ) {
151
+ c .ObjectMeta .Labels = bc .Spec .ClusterSelector .MatchLabels
152
+ c .Status .Conditions = append (c .Status .Conditions , makeReadyCondition ())
153
+ c .ObjectMeta .Annotations = map [string ]string {
154
+ capiv1alpha1 .BootstrappedAnnotation : "true" ,
155
+ capiv1alpha1 .BootstrapConfigsAnnotation : fmt .Sprintf ("%s/%s" , bc .Namespace , bc .Name ),
156
+ }
157
+ })
158
+ secret := makeTestSecret (types.NamespacedName {
159
+ Name : cl .GetName () + "-kubeconfig" ,
160
+ Namespace : cl .GetNamespace (),
161
+ }, map [string ][]byte {"value" : []byte ("testing" )})
162
+ // This cheats by using the local client as the remote client to simplify
163
+ // getting the value from the remote client.
164
+ reconciler := makeTestReconciler (t , bc , cl , secret , readyNode )
165
+ reconciler .configParser = func (b []byte ) (client.Client , error ) {
166
+ return reconciler .Client , nil
167
+ }
168
+
169
+ result , err := reconciler .Reconcile (context .TODO (), ctrl.Request {NamespacedName : types.NamespacedName {
170
+ Name : bc .GetName (),
171
+ Namespace : bc .GetNamespace (),
172
+ }})
173
+ if err != nil {
174
+ t .Fatal (err )
175
+ }
176
+ if ! result .IsZero () {
177
+ t .Fatalf ("want empty result, got %v" , result )
178
+ }
179
+ var jobs batchv1.JobList
180
+ if err := reconciler .List (context .TODO (), & jobs , client .InNamespace (testNamespace )); err != nil {
181
+ t .Fatal (err )
182
+ }
183
+ if l := len (jobs .Items ); l != 0 {
184
+ t .Fatalf ("found %d jobs, want %d" , l , 0 )
185
+ }
186
+ }
187
+
188
+ func TestReconcile_when_cluster_ready_bootstrapped_with_different_config (t * testing.T ) {
189
+ bc := makeTestClusterBootstrapConfig (func (c * capiv1alpha1.ClusterBootstrapConfig ) {
190
+ c .Spec .RequireClusterReady = true
191
+ })
192
+ readyNode := makeNode (map [string ]string {
193
+ "node-role.kubernetes.io/control-plane" : "" ,
194
+ }, corev1.NodeCondition {
195
+ Type : "Ready" , Status : "True" , LastHeartbeatTime : metav1 .Now (), LastTransitionTime : metav1 .Now (), Reason : "KubeletReady" , Message : "kubelet is posting ready status" })
196
+
197
+ cl := makeTestCluster (func (c * gitopsv1alpha1.GitopsCluster ) {
198
+ c .ObjectMeta .Labels = bc .Spec .ClusterSelector .MatchLabels
199
+ c .ObjectMeta .Annotations = map [string ]string {
200
+ capiv1alpha1 .BootstrappedAnnotation : "true" ,
201
+ capiv1alpha1 .BootstrapConfigsAnnotation : "unknown/unknown" ,
202
+ }
203
+ c .Status .Conditions = append (c .Status .Conditions , makeReadyCondition ())
204
+ })
205
+ secret := makeTestSecret (types.NamespacedName {
206
+ Name : cl .GetName () + "-kubeconfig" ,
207
+ Namespace : cl .GetNamespace (),
208
+ }, map [string ][]byte {"value" : []byte ("testing" )})
209
+ // This cheats by using the local client as the remote client to simplify
210
+ // getting the value from the remote client.
211
+ reconciler := makeTestReconciler (t , bc , cl , secret , readyNode )
212
+ reconciler .configParser = func (b []byte ) (client.Client , error ) {
213
+ return reconciler .Client , nil
214
+ }
215
+
216
+ result , err := reconciler .Reconcile (context .TODO (), ctrl.Request {NamespacedName : types.NamespacedName {
217
+ Name : bc .GetName (),
218
+ Namespace : bc .GetNamespace (),
219
+ }})
220
+ if err != nil {
221
+ t .Fatal (err )
222
+ }
223
+ if ! result .IsZero () {
224
+ t .Fatalf ("want empty result, got %v" , result )
225
+ }
226
+ var jobs batchv1.JobList
227
+ if err := reconciler .List (context .TODO (), & jobs , client .InNamespace (testNamespace )); err != nil {
228
+ t .Fatal (err )
229
+ }
230
+ if l := len (jobs .Items ); l != 1 {
231
+ t .Fatalf ("found %d jobs, want %d" , l , 1 )
232
+ }
233
+ }
234
+
141
235
func TestReconcile_when_cluster_provisioned (t * testing.T ) {
142
236
bc := makeTestClusterBootstrapConfig (func (c * capiv1alpha1.ClusterBootstrapConfig ) {
143
237
c .Spec .RequireClusterProvisioned = true
@@ -244,6 +338,56 @@ func TestReconcile_when_cluster_no_matching_labels(t *testing.T) {
244
338
assertNoJobsCreated (t , reconciler .Client )
245
339
}
246
340
341
+ func TestReconcile_when_cluster_ready_bootstrapped_with_multiple_config (t * testing.T ) {
342
+ // Multiple configs can bootstrap the same cluster
343
+ // If the reconciled cluster is in that list (anywhere) then we don't create
344
+ // jobs.
345
+ bc := makeTestClusterBootstrapConfig (func (c * capiv1alpha1.ClusterBootstrapConfig ) {
346
+ c .Spec .RequireClusterReady = true
347
+ })
348
+ readyNode := makeNode (map [string ]string {
349
+ "node-role.kubernetes.io/control-plane" : "" ,
350
+ }, corev1.NodeCondition {
351
+ Type : "Ready" , Status : "True" , LastHeartbeatTime : metav1 .Now (), LastTransitionTime : metav1 .Now (), Reason : "KubeletReady" , Message : "kubelet is posting ready status" })
352
+
353
+ cl := makeTestCluster (func (c * gitopsv1alpha1.GitopsCluster ) {
354
+ c .ObjectMeta .Labels = bc .Spec .ClusterSelector .MatchLabels
355
+ c .ObjectMeta .Annotations = map [string ]string {
356
+ capiv1alpha1 .BootstrappedAnnotation : "true" ,
357
+ capiv1alpha1 .BootstrapConfigsAnnotation : fmt .Sprintf ("%s,%s/%s" , "unknown/unknown" , bc .GetNamespace (), bc .GetName ()),
358
+ }
359
+ c .Status .Conditions = append (c .Status .Conditions , makeReadyCondition ())
360
+ })
361
+ secret := makeTestSecret (types.NamespacedName {
362
+ Name : cl .GetName () + "-kubeconfig" ,
363
+ Namespace : cl .GetNamespace (),
364
+ }, map [string ][]byte {"value" : []byte ("testing" )})
365
+ // This cheats by using the local client as the remote client to simplify
366
+ // getting the value from the remote client.
367
+ reconciler := makeTestReconciler (t , bc , cl , secret , readyNode )
368
+ reconciler .configParser = func (b []byte ) (client.Client , error ) {
369
+ return reconciler .Client , nil
370
+ }
371
+
372
+ result , err := reconciler .Reconcile (context .TODO (), ctrl.Request {NamespacedName : types.NamespacedName {
373
+ Name : bc .GetName (),
374
+ Namespace : bc .GetNamespace (),
375
+ }})
376
+ if err != nil {
377
+ t .Fatal (err )
378
+ }
379
+ if ! result .IsZero () {
380
+ t .Fatalf ("want empty result, got %v" , result )
381
+ }
382
+ var jobs batchv1.JobList
383
+ if err := reconciler .List (context .TODO (), & jobs , client .InNamespace (testNamespace )); err != nil {
384
+ t .Fatal (err )
385
+ }
386
+ if l := len (jobs .Items ); l != 0 {
387
+ t .Fatalf ("found %d jobs, want %d" , l , 0 )
388
+ }
389
+ }
390
+
247
391
func TestReconcile_when_empty_label_selector (t * testing.T ) {
248
392
// When the label selector is empty, we don't want any jobs created, rather
249
393
// than a job for all clusters.
0 commit comments