@@ -9,23 +9,25 @@ import (
99
1010 o "github.com/onsi/gomega"
1111
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"
1216 appsv1 "k8s.io/api/apps/v1"
1317 corev1 "k8s.io/api/core/v1"
1418 rbacv1 "k8s.io/api/rbac/v1"
19+ crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1520 crdclientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
1621 "k8s.io/apimachinery/pkg/api/errors"
1722 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1823 "k8s.io/apimachinery/pkg/runtime"
1924 "k8s.io/apimachinery/pkg/types"
25+ "knative.dev/pkg/apis"
26+ duckv1 "knative.dev/pkg/apis/duck/v1"
2027 "sigs.k8s.io/controller-runtime/pkg/client"
2128 "sigs.k8s.io/controller-runtime/pkg/client/fake"
2229 "sigs.k8s.io/controller-runtime/pkg/log/zap"
2330 "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"
2931)
3032
3133// bootstrapShipwrightBuildReconciler start up a new instance of ShipwrightBuildReconciler which is
@@ -35,6 +37,7 @@ func bootstrapShipwrightBuildReconciler(
3537 b * v1alpha1.ShipwrightBuild ,
3638 tcfg * tektonoperatorv1alpha1.TektonConfig ,
3739 tcrds []* crdv1.CustomResourceDefinition ,
40+ statusObjects ... client.Object ,
3841) (client.Client , * crdclientv1.Clientset , * tektonoperatorv1alpha1client.Clientset , * ShipwrightBuildReconciler ) {
3942 g := o .NewGomegaWithT (t )
4043
@@ -44,10 +47,17 @@ func bootstrapShipwrightBuildReconciler(
4447 s .AddKnownTypes (v1alpha1 .GroupVersion , & v1alpha1.ShipwrightBuild {})
4548 s .AddKnownTypes (rbacv1 .SchemeGroupVersion , & rbacv1.ClusterRoleBinding {})
4649 s .AddKnownTypes (rbacv1 .SchemeGroupVersion , & rbacv1.ClusterRole {})
50+ s .AddKnownTypes (tektonoperatorv1alpha1 .SchemeGroupVersion , & tektonoperatorv1alpha1.TektonConfig {})
51+ s .AddKnownTypes (buildv1alpha1 .SchemeGroupVersion , & buildv1alpha1.ClusterBuildStrategy {})
4752
4853 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 ()
4960
50- c := fake .NewClientBuilder ().WithScheme (s ).WithObjects (b ).Build ()
5161 var crdClient * crdclientv1.Clientset
5262 var toClient * tektonoperatorv1alpha1client.Clientset
5363 if len (tcrds ) > 0 {
@@ -245,3 +255,118 @@ func TestShipwrightBuildReconciler_Reconcile(t *testing.T) {
245255 })
246256 }
247257}
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