From 8594f4aafa1a2791d962b1a2e05027ce8c552566 Mon Sep 17 00:00:00 2001 From: gabemontero Date: Mon, 23 Aug 2021 18:06:01 -0400 Subject: [PATCH] test for TaskRun CRD presence, create/configure tekton operator as needed --- cmd/operator/main.go | 22 +++++- controllers/shipwrightbuild_controller.go | 68 +++++++++++++++++-- .../shipwrightbuild_controller_test.go | 14 +++- controllers/suite_test.go | 13 +++- 4 files changed, 101 insertions(+), 16 deletions(-) diff --git a/cmd/operator/main.go b/cmd/operator/main.go index a222cf9a4..c19dc6854 100644 --- a/cmd/operator/main.go +++ b/cmd/operator/main.go @@ -13,6 +13,7 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + crdclientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -20,6 +21,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" + tektonoperatorv1alpha1client "github.com/tektoncd/operator/pkg/client/clientset/versioned/typed/operator/v1alpha1" + operatorv1alpha1 "github.com/shipwright-io/operator/api/v1alpha1" "github.com/shipwright-io/operator/controllers" // +kubebuilder:scaffold:imports @@ -79,10 +82,23 @@ func main() { os.Exit(1) } + crdClient, err := crdclientv1.NewForConfig(mgr.GetConfig()) + if err != nil { + setupLog.Error(err, "unable to get crd client") + os.Exit(1) + } + tektonOperatorClient, err := tektonoperatorv1alpha1client.NewForConfig(mgr.GetConfig()) + if err != nil { + setupLog.Error(err, "unable to get tekton operator client") + os.Exit(1) + } + if err = (&controllers.ShipwrightBuildReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Logger: ctrl.Log.WithName("controllers").WithName("ShipwrightBuild"), + CRDClient: crdClient, + TektonOperatorClient: tektonOperatorClient, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Logger: ctrl.Log.WithName("controllers").WithName("ShipwrightBuild"), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ShipwrightBuild") os.Exit(1) diff --git a/controllers/shipwrightbuild_controller.go b/controllers/shipwrightbuild_controller.go index a787700b3..52314ea27 100644 --- a/controllers/shipwrightbuild_controller.go +++ b/controllers/shipwrightbuild_controller.go @@ -12,7 +12,12 @@ import ( "github.com/go-logr/logr" mfc "github.com/manifestival/controller-runtime-client" "github.com/manifestival/manifestival" + tektonoperatorv1alpha1 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" + tektonoperatorv1alpha1client "github.com/tektoncd/operator/pkg/client/clientset/versioned/typed/operator/v1alpha1" + + crdclientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" @@ -32,11 +37,14 @@ const ( // ShipwrightBuildReconciler reconciles a ShipwrightBuild object type ShipwrightBuildReconciler struct { - client.Client // controller kubernetes client - - Logger logr.Logger // decorated logger - Scheme *runtime.Scheme // runtime scheme - Manifest manifestival.Manifest // release manifests render + client.Client // controller kubernetes client + CRDClient crdclientv1.ApiextensionsV1Interface + TektonOperatorClient tektonoperatorv1alpha1client.OperatorV1alpha1Interface + + Logger logr.Logger // decorated logger + Scheme *runtime.Scheme // runtime scheme + Manifest manifestival.Manifest // release manifests render + TektonManifest manifestival.Manifest // Tekton release manifest render } // setFinalizer append finalizer on the resource, and uses local client to update it immediately. @@ -69,7 +77,43 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ logger := r.Logger.WithValues("namespace", req.Namespace, "name", req.Name) logger.Info("Starting resource reconciliation...") - // retrieving the ShipwrightBuild instance requested for reconciliation + // See if tekton is there + _, err := r.CRDClient.CustomResourceDefinitions().Get(ctx, "taskruns.tekton.dev", metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + // CRD not there, install tekton via operator; as we have specified the TaskRun CRD as required in + // our operator's CSV, if the TaskRun CRD is not there, that means the operator is not present, as + // OLM should have provisioned the Tekton operator based on our dependency declaration. + if err := r.TektonManifest.Apply(); err != nil { + logger.Error(err, "Rolling out Tekton Operator release 0.49.0 resources") + RequeueOnError(err) + } + logger.Info("The Tekton Operator 0.49.0 release has been applied to the cluster") + // the tekton operator 'lite' profile is all Shipwright currently needs, so configure that up; + // when Shipwright starts leveraging triggers, we will want to bump up to a 'base' or higher + list, err := r.TektonOperatorClient.TektonConfigs().List(ctx, metav1.ListOptions{}) + if err != nil { + logger.Error(err, "Listing Tekton Configs") + RequeueOnError(err) + } + if list == nil || len(list.Items) == 0 { + tektonOperatorCfg := &tektonoperatorv1alpha1.TektonConfig{} + tektonOperatorCfg.Name = "config" + tektonOperatorCfg.Spec.TargetNamespace = "tekton-pipelines" + tektonOperatorCfg.Spec.Profile = "lite" + if _, err := r.TektonOperatorClient.TektonConfigs().Create(ctx, tektonOperatorCfg, metav1.CreateOptions{}); err != nil { + logger.Error(err, "Creating Tekton Operator lite config ") + RequeueOnError(err) + } + logger.Info("A Tekton Operator config with the 'lite' profile has been applied to the cluster") + } + } else { + logger.Error(err, "Retrieving TaskRun CRD instance") + return RequeueOnError(err) + } + } + + // retrieving the ShipwrightBuild instance requested for reconciliatio b := &v1alpha1.ShipwrightBuild{} if err := r.Get(ctx, req.NamespacedName, b); err != nil { if errors.IsNotFound(err) { @@ -142,7 +186,7 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ return NoRequeue() } -// setupManifestival instantiate manifestival with local controller attributes. +// setupManifestival instantiate manifestival with local controller attributes, as well as tekton prereqs. func (r *ShipwrightBuildReconciler) setupManifestival(managerLogger logr.Logger) error { client := mfc.NewClient(r.Client) logger := managerLogger.WithName("manifestival") @@ -158,6 +202,16 @@ func (r *ShipwrightBuildReconciler) setupManifestival(managerLogger logr.Logger) manifestival.UseClient(client), manifestival.UseLogger(logger), ) + if err != nil { + return err + } + + tektonOperatorManifest := "https://storage.googleapis.com/tekton-releases/operator/previous/v0.49.0/release.yaml" + r.TektonManifest, err = manifestival.NewManifest( + tektonOperatorManifest, + manifestival.UseClient(client), + manifestival.UseLogger(logger), + ) return err } diff --git a/controllers/shipwrightbuild_controller_test.go b/controllers/shipwrightbuild_controller_test.go index c433d74a9..c382bac85 100644 --- a/controllers/shipwrightbuild_controller_test.go +++ b/controllers/shipwrightbuild_controller_test.go @@ -9,6 +9,7 @@ import ( o "github.com/onsi/gomega" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + crdclientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -19,6 +20,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/shipwright-io/operator/api/v1alpha1" + tektonoperatorv1alpha1client "github.com/tektoncd/operator/pkg/client/clientset/versioned/fake" + crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) func init() { @@ -32,6 +35,7 @@ func init() { func bootstrapShipwrightBuildReconciler( t *testing.T, b *v1alpha1.ShipwrightBuild, + tcrd *crdv1.CustomResourceDefinition, ) (client.Client, *ShipwrightBuildReconciler) { g := o.NewGomegaWithT(t) @@ -43,7 +47,9 @@ func bootstrapShipwrightBuildReconciler( logger := zap.New() c := fake.NewFakeClientWithScheme(s, b) - r := &ShipwrightBuildReconciler{Client: c, Scheme: s, Logger: logger} + crdClient := crdclientv1.NewSimpleClientset(tcrd) + toClient := tektonoperatorv1alpha1client.NewSimpleClientset() + r := &ShipwrightBuildReconciler{CRDClient: crdClient.ApiextensionsV1(), TektonOperatorClient: toClient.OperatorV1alpha1(), Client: c, Scheme: s, Logger: logger} // creating targetNamespace on which Shipwright-Build will be deployed against, before the other // tests takes place @@ -74,7 +80,7 @@ func TestShipwrightBuildReconciler_Finalizers(t *testing.T) { g := o.NewGomegaWithT(t) b := &v1alpha1.ShipwrightBuild{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "default"}} - _, r := bootstrapShipwrightBuildReconciler(t, b) + _, r := bootstrapShipwrightBuildReconciler(t, b, &crdv1.CustomResourceDefinition{}) // adding one entry on finalizers slice, making sure it's registered t.Run("setFinalizer", func(t *testing.T) { @@ -114,7 +120,9 @@ func testShipwrightBuildReconcilerReconcile(t *testing.T, targetNamespace string TargetNamespace: targetNamespace, }, } - c, r := bootstrapShipwrightBuildReconciler(t, b) + crd := &crdv1.CustomResourceDefinition{} + crd.Name = "taskruns.tekton.dev" + c, r := bootstrapShipwrightBuildReconciler(t, b, crd) t.Logf("Deploying Shipwright Controller against '%s' namespace", targetNamespace) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 2f9dd0f97..2a0b662d6 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -6,6 +6,8 @@ package controllers import ( "context" + tektonoperatorv1alpha1client "github.com/tektoncd/operator/pkg/client/clientset/versioned/typed/operator/v1alpha1" + crdclientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" "path/filepath" "testing" "time" @@ -70,10 +72,15 @@ var _ = BeforeSuite(func() { Scheme: scheme.Scheme, }) Expect(err).NotTo(HaveOccurred()) + crdClient, err := crdclientv1.NewForConfig(mgr.GetConfig()) + Expect(err).NotTo(HaveOccurred()) + toClient, err := tektonoperatorv1alpha1client.NewForConfig(mgr.GetConfig()) err = (&ShipwrightBuildReconciler{ - Client: mgr.GetClient(), - Scheme: scheme.Scheme, - Logger: ctrl.Log.WithName("controllers").WithName("shipwrightbuild"), + CRDClient: crdClient, + TektonOperatorClient: toClient, + Client: mgr.GetClient(), + Scheme: scheme.Scheme, + Logger: ctrl.Log.WithName("controllers").WithName("shipwrightbuild"), }).SetupWithManager(mgr) Expect(err).NotTo(HaveOccurred())