@@ -9,23 +9,25 @@ import (
9
9
10
10
o "github.com/onsi/gomega"
11
11
12
+ buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
13
+ "github.com/shipwright-io/operator/api/v1alpha1"
14
+ tektonoperatorv1alpha1 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
15
+ tektonoperatorv1alpha1client "github.com/tektoncd/operator/pkg/client/clientset/versioned/fake"
12
16
appsv1 "k8s.io/api/apps/v1"
13
17
corev1 "k8s.io/api/core/v1"
14
18
rbacv1 "k8s.io/api/rbac/v1"
19
+ crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
15
20
crdclientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
16
21
"k8s.io/apimachinery/pkg/api/errors"
17
22
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
23
"k8s.io/apimachinery/pkg/runtime"
19
24
"k8s.io/apimachinery/pkg/types"
25
+ "knative.dev/pkg/apis"
26
+ duckv1 "knative.dev/pkg/apis/duck/v1"
20
27
"sigs.k8s.io/controller-runtime/pkg/client"
21
28
"sigs.k8s.io/controller-runtime/pkg/client/fake"
22
29
"sigs.k8s.io/controller-runtime/pkg/log/zap"
23
30
"sigs.k8s.io/controller-runtime/pkg/reconcile"
24
-
25
- "github.com/shipwright-io/operator/api/v1alpha1"
26
- tektonoperatorv1alpha1 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
27
- tektonoperatorv1alpha1client "github.com/tektoncd/operator/pkg/client/clientset/versioned/fake"
28
- crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
29
31
)
30
32
31
33
// bootstrapShipwrightBuildReconciler start up a new instance of ShipwrightBuildReconciler which is
@@ -35,6 +37,7 @@ func bootstrapShipwrightBuildReconciler(
35
37
b * v1alpha1.ShipwrightBuild ,
36
38
tcfg * tektonoperatorv1alpha1.TektonConfig ,
37
39
tcrds []* crdv1.CustomResourceDefinition ,
40
+ statusObjects ... client.Object ,
38
41
) (client.Client , * crdclientv1.Clientset , * tektonoperatorv1alpha1client.Clientset , * ShipwrightBuildReconciler ) {
39
42
g := o .NewGomegaWithT (t )
40
43
@@ -44,10 +47,17 @@ func bootstrapShipwrightBuildReconciler(
44
47
s .AddKnownTypes (v1alpha1 .GroupVersion , & v1alpha1.ShipwrightBuild {})
45
48
s .AddKnownTypes (rbacv1 .SchemeGroupVersion , & rbacv1.ClusterRoleBinding {})
46
49
s .AddKnownTypes (rbacv1 .SchemeGroupVersion , & rbacv1.ClusterRole {})
50
+ s .AddKnownTypes (tektonoperatorv1alpha1 .SchemeGroupVersion , & tektonoperatorv1alpha1.TektonConfig {})
51
+ s .AddKnownTypes (buildv1alpha1 .SchemeGroupVersion , & buildv1alpha1.ClusterBuildStrategy {})
47
52
48
53
logger := zap .New ()
54
+ clientBuilder := fake .NewClientBuilder ().WithScheme (s ).WithObjects (b )
55
+ if len (statusObjects ) > 0 {
56
+ // the fake client does not support the status subresource by default.
57
+ clientBuilder = clientBuilder .WithStatusSubresource (statusObjects ... )
58
+ }
59
+ c := clientBuilder .Build ()
49
60
50
- c := fake .NewClientBuilder ().WithScheme (s ).WithObjects (b ).Build ()
51
61
var crdClient * crdclientv1.Clientset
52
62
var toClient * tektonoperatorv1alpha1client.Clientset
53
63
if len (tcrds ) > 0 {
@@ -245,3 +255,118 @@ func TestShipwrightBuildReconciler_Reconcile(t *testing.T) {
245
255
})
246
256
}
247
257
}
258
+
259
+ func TestShipwrightBuildReconciler_OperandReadiness (t * testing.T ) {
260
+ g := o .NewGomegaWithT (t )
261
+ ctx := context .TODO ()
262
+
263
+ // ShipwrightBuild object
264
+ b := & v1alpha1.ShipwrightBuild {
265
+ ObjectMeta : metav1.ObjectMeta {
266
+ Name : "name" ,
267
+ Namespace : "default" ,
268
+ },
269
+ Spec : v1alpha1.ShipwrightBuildSpec {
270
+ TargetNamespace : "namespace" ,
271
+ },
272
+ Status : v1alpha1.ShipwrightBuildStatus {
273
+ Conditions : []metav1.Condition {
274
+ {
275
+ Type : ConditionReady ,
276
+ Status : metav1 .ConditionTrue ,
277
+ },
278
+ },
279
+ },
280
+ }
281
+
282
+ // Mock TektonConfig to simulate Tekton Operator installation
283
+ tektonConfig := & tektonoperatorv1alpha1.TektonConfig {
284
+ ObjectMeta : metav1.ObjectMeta {
285
+ Name : "config" ,
286
+ },
287
+ Status : tektonoperatorv1alpha1.TektonConfigStatus {
288
+ Status : duckv1.Status {
289
+ Conditions : duckv1.Conditions {
290
+ {
291
+ Type : ConditionReady ,
292
+ Status : corev1 .ConditionFalse ,
293
+ Reason : "Installed" ,
294
+ Message : "TektonConfig is not ready" ,
295
+ },
296
+ },
297
+ },
298
+ },
299
+ }
300
+ // Preload the fake client with a valid ClusterBuildStrategy to pass buildstrategy reconciliation
301
+ cbs := & buildv1alpha1.ClusterBuildStrategy {
302
+ ObjectMeta : metav1.ObjectMeta {
303
+ Name : "buildah" ,
304
+ },
305
+ Spec : buildv1alpha1.BuildStrategySpec {
306
+ BuildSteps : []buildv1alpha1.BuildStep {
307
+ {
308
+ Container : corev1.Container {
309
+ Name : "build-step" ,
310
+ Image : "quay.io/buildah/stable" ,
311
+ },
312
+ },
313
+ },
314
+ },
315
+ }
316
+
317
+ // Prepare cr
318
+ crd1 := & crdv1.CustomResourceDefinition {}
319
+ crd1 .Name = "taskruns.tekton.dev"
320
+ crd2 := & crdv1.CustomResourceDefinition {}
321
+ crd2 .Name = "tektonconfigs.operator.tekton.dev"
322
+ crd2 .Labels = map [string ]string {"operator.tekton.dev/release" : common .TektonOpMinSupportedVersion }
323
+ crd3 := & crdv1.CustomResourceDefinition {}
324
+ crd3 .Name = "clusterbuildstrategies.shipwright.io"
325
+ crds := []* crdv1.CustomResourceDefinition {crd1 , crd2 , crd3 }
326
+
327
+ // Bootstrap the reconciler with the mock objects
328
+ c , _ , _ , r := bootstrapShipwrightBuildReconciler (t , b , nil , crds , & v1alpha1.ShipwrightBuild {})
329
+
330
+ // Inject a pre-created valid tektonconfig
331
+ _ , err := r .TektonOperatorClient .TektonConfigs ().Create (ctx , tektonConfig , metav1.CreateOptions {})
332
+ g .Expect (err ).To (o .BeNil ())
333
+
334
+ // Verify creation of tektonconfig
335
+ cfg , err := r .TektonOperatorClient .TektonConfigs ().Get (ctx , "config" , metav1.GetOptions {})
336
+ g .Expect (err ).To (o .BeNil ())
337
+ g .Expect (cfg .Name ).To (o .Equal ("config" ))
338
+ g .Expect (cfg .Status .Conditions [0 ].Type ).To (o .Equal (apis .ConditionReady ))
339
+
340
+ // Simulate reconciliation
341
+ namespacedName := types.NamespacedName {Namespace : "default" , Name : "name" }
342
+ req := reconcile.Request {NamespacedName : namespacedName }
343
+ res , err := r .Reconcile (ctx , req )
344
+ g .Expect (err ).To (o .BeNil ())
345
+ g .Expect (res .Requeue ).To (o .BeTrue (), "Reconciliation should requeue when TektonConfig is not ready" )
346
+
347
+ // Verify that the ShipwrightBuild is marked as not ready
348
+ updated := & v1alpha1.ShipwrightBuild {}
349
+ err = c .Get (ctx , req .NamespacedName , updated )
350
+ g .Expect (err ).To (o .BeNil ())
351
+ g .Expect (updated .Status .IsReady ()).To (o .BeFalse (), "ShipwrightBuild should not be ready when TektonConfig is not ready" )
352
+
353
+ // Simulate TektonConfig becoming ready
354
+ tektonConfig .Status .Conditions [0 ].Status = corev1 .ConditionTrue
355
+ tektonConfig .Status .Conditions [0 ].Reason = "Installed"
356
+ tektonConfig .Status .Conditions [0 ].Message = "TektonConfig is now ready"
357
+ _ , err = r .TektonOperatorClient .TektonConfigs ().Update (ctx , tektonConfig , metav1.UpdateOptions {})
358
+ g .Expect (err ).To (o .BeNil ())
359
+
360
+ // Inject a pre-created valid ClusterBuildStrategy
361
+ err = c .Create (ctx , cbs )
362
+ g .Expect (err ).To (o .BeNil ())
363
+ // Trigger reconciliation again
364
+ res , err = r .Reconcile (ctx , req )
365
+ g .Expect (err ).To (o .BeNil ())
366
+ g .Expect (res .Requeue ).To (o .BeFalse (), "Should not requeue after TektonConfig is ready" )
367
+
368
+ // Fetch and verify ShipwrightBuild is now ready
369
+ err = c .Get (ctx , req .NamespacedName , updated )
370
+ g .Expect (err ).To (o .BeNil ())
371
+ g .Expect (updated .Status .IsReady ()).To (o .BeTrue (), "ShipwrightBuild should be ready when TektonConfig is ready" )
372
+ }
0 commit comments