Skip to content

Commit

Permalink
Merge pull request #171 from adambkaplan/install-build-strategies
Browse files Browse the repository at this point in the history
Install build strategies via operator
  • Loading branch information
openshift-merge-bot[bot] authored Nov 7, 2023
2 parents 5fe8cc0 + d423d71 commit bed9d49
Show file tree
Hide file tree
Showing 260 changed files with 11,044 additions and 3,296 deletions.
12 changes: 12 additions & 0 deletions bundle/manifests/shipwright-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,18 @@ spec:
- delete
- patch
- update
- apiGroups:
- shipwright.io
resources:
- clusterbuildstrategies
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- authentication.k8s.io
resources:
Expand Down
12 changes: 12 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,15 @@ rules:
- delete
- patch
- update
- apiGroups:
- shipwright.io
resources:
- clusterbuildstrategies
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
48 changes: 48 additions & 0 deletions controllers/buildstrategies_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package controllers

import (
"fmt"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
"github.com/shipwright-io/operator/api/v1alpha1"
"github.com/shipwright-io/operator/test"
)

var _ = Describe("Install embedded build strategies", func() {

var build *v1alpha1.ShipwrightBuild

BeforeEach(func(ctx SpecContext) {
setupTektonCRDs(ctx)
build = createShipwrightBuild(ctx, "shipwright")
test.CRDEventuallyExists(ctx, k8sClient, "clusterbuildstrategies.shipwright.io")
})

When("the install build strategies feature is enabled", func() {

It("applies the embedded build strategy manifests to the cluster", func(ctx SpecContext) {
expectedBuildStrategies, err := test.ParseBuildStrategyNames()
Expect(err).NotTo(HaveOccurred())
for _, strategy := range expectedBuildStrategies {
strategyObj := &buildv1alpha1.ClusterBuildStrategy{
ObjectMeta: metav1.ObjectMeta{
Name: strategy,
},
}
By(fmt.Sprintf("checking for build strategy %q", strategy))
test.EventuallyExists(ctx, k8sClient, strategyObj)
}

})
})

AfterEach(func(ctx SpecContext) {
deleteShipwrightBuild(ctx, build)
})

})
119 changes: 3 additions & 116 deletions controllers/default_test.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,21 @@
package controllers

import (
"context"

g "github.com/onsi/ginkgo/v2"
o "github.com/onsi/gomega"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/shipwright-io/operator/api/v1alpha1"
"github.com/shipwright-io/operator/pkg/common"
"github.com/shipwright-io/operator/test"
)

// createNamespace creates the namespace informed.
func createNamespace(ctx context.Context, name string) {
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}}
err := k8sClient.Get(ctx, types.NamespacedName{Name: ns.Name}, ns)
if errors.IsNotFound(err) {
err = k8sClient.Create(ctx, ns, &client.CreateOptions{})
}
o.Expect(err).NotTo(o.HaveOccurred())
}

var _ = g.Describe("Reconcile default ShipwrightBuild installation", func() {

// namespace where ShipwrightBuild instance will be located
const namespace = "namespace"
// targetNamespace namespace where shipwright Controller and dependencies will be located
const targetNamespace = "target-namespace"
// build Build instance employed during testing
Expand Down Expand Up @@ -62,109 +44,14 @@ var _ = g.Describe("Reconcile default ShipwrightBuild installation", func() {
},
}

truePtr := true
g.BeforeEach(func(ctx g.SpecContext) {
// setting up the namespaces, where Shipwright Controller will be deployed
createNamespace(ctx, namespace)

g.By("does tekton taskrun crd exist")
err := k8sClient.Get(ctx, types.NamespacedName{Name: "taskruns.tekton.dev"}, &crdv1.CustomResourceDefinition{})
if errors.IsNotFound(err) {
g.By("creating tekton taskrun crd")
taskRunCRD := &crdv1.CustomResourceDefinition{}
taskRunCRD.Name = "taskruns.tekton.dev"
taskRunCRD.Spec.Group = "tekton.dev"
taskRunCRD.Spec.Scope = crdv1.NamespaceScoped
taskRunCRD.Spec.Versions = []crdv1.CustomResourceDefinitionVersion{
{
Name: "v1beta1",
Storage: true,
Schema: &crdv1.CustomResourceValidation{
OpenAPIV3Schema: &crdv1.JSONSchemaProps{
Type: "object",
XPreserveUnknownFields: &truePtr,
},
},
},
}
taskRunCRD.Spec.Names.Plural = "taskruns"
taskRunCRD.Spec.Names.Singular = "taskrun"
taskRunCRD.Spec.Names.Kind = "TaskRun"
taskRunCRD.Spec.Names.ListKind = "TaskRunList"
taskRunCRD.Status.StoredVersions = []string{"v1beta1"}
err = k8sClient.Create(ctx, taskRunCRD, &client.CreateOptions{})
o.Expect(err).NotTo(o.HaveOccurred())

}
o.Expect(err).NotTo(o.HaveOccurred())

g.By("does tektonconfig crd exist")
err = k8sClient.Get(ctx, types.NamespacedName{Name: "tektonconfigs.operator.tekton.dev"}, &crdv1.CustomResourceDefinition{})
if errors.IsNotFound(err) {
tektonOpCRD := &crdv1.CustomResourceDefinition{}
tektonOpCRD.Name = "tektonconfigs.operator.tekton.dev"
tektonOpCRD.Labels = map[string]string{"operator.tekton.dev/release": common.TektonOpMinSupportedVersion}
tektonOpCRD.Spec.Group = "operator.tekton.dev"
tektonOpCRD.Spec.Scope = crdv1.ClusterScoped
tektonOpCRD.Spec.Versions = []crdv1.CustomResourceDefinitionVersion{
{
Name: "v1alpha1",
Storage: true,
Schema: &crdv1.CustomResourceValidation{
OpenAPIV3Schema: &crdv1.JSONSchemaProps{
Type: "object",
XPreserveUnknownFields: &truePtr,
},
},
},
}
tektonOpCRD.Spec.Names.Plural = "tektonconfigs"
tektonOpCRD.Spec.Names.Singular = "tektonconfig"
tektonOpCRD.Spec.Names.Kind = "TektonConfig"
tektonOpCRD.Spec.Names.ListKind = "TektonConfigList"
tektonOpCRD.Status.StoredVersions = []string{"v1alpha1"}
err = k8sClient.Create(ctx, tektonOpCRD, &client.CreateOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
}
o.Expect(err).NotTo(o.HaveOccurred())

g.By("creating a ShipwrightBuild instance")
build = &v1alpha1.ShipwrightBuild{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: "cluster",
},
Spec: v1alpha1.ShipwrightBuildSpec{
TargetNamespace: targetNamespace,
},
}
err = k8sClient.Create(ctx, build, &client.CreateOptions{})
o.Expect(err).NotTo(o.HaveOccurred())

// when the finalizer is in place, the deployment of manifest elements is done, and therefore
// functional testing can proceed
g.By("waiting for the finalizer to be set")
test.EventuallyContainFinalizer(ctx, k8sClient, build, FinalizerAnnotation)
setupTektonCRDs(ctx)
build = createShipwrightBuild(ctx, targetNamespace)
})

g.AfterEach(func(ctx g.SpecContext) {
g.By("deleting the ShipwrightBuild instance")
namespacedName := types.NamespacedName{Namespace: namespace, Name: build.Name}
err := k8sClient.Get(ctx, namespacedName, build)
if errors.IsNotFound(err) {
return
}
o.Expect(err).NotTo(o.HaveOccurred())

err = k8sClient.Delete(ctx, build, &client.DeleteOptions{})
// the delete e2e's can delete this object before this AfterEach runs
if errors.IsNotFound(err) {
return
}
o.Expect(err).NotTo(o.HaveOccurred())

g.By("waiting for ShipwrightBuild instance to be completely removed")
test.EventuallyRemoved(ctx, k8sClient, build)
deleteShipwrightBuild(ctx, build)

g.By("checking that the shipwright-build-controller deployment has been removed")
deployment := baseDeployment.DeepCopy()
Expand Down
50 changes: 44 additions & 6 deletions controllers/shipwrightbuild_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package controllers
import (
"context"
"fmt"
"path/filepath"

"github.com/go-logr/logr"
"github.com/manifestival/manifestival"
Expand All @@ -25,6 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"

"github.com/shipwright-io/operator/api/v1alpha1"
"github.com/shipwright-io/operator/pkg/buildstrategy"
"github.com/shipwright-io/operator/pkg/certmanager"
"github.com/shipwright-io/operator/pkg/common"
"github.com/shipwright-io/operator/pkg/tekton"
Expand Down Expand Up @@ -53,10 +55,11 @@ type ShipwrightBuildReconciler struct {
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
Logger logr.Logger // decorated logger
Scheme *runtime.Scheme // runtime scheme
Manifest manifestival.Manifest // release manifests render
TektonManifest manifestival.Manifest // Tekton release manifest render
BuildStrategyManifest manifestival.Manifest // Build strategies manifest to render
}

// setFinalizer append finalizer on the resource, and uses local client to update it immediately.
Expand Down Expand Up @@ -195,6 +198,11 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ
logger.Info("Finalizers removed, deletion of manifests completed!")
return NoRequeue()
}
logger.Info("Deleting cluster build strategies")
if err := r.BuildStrategyManifest.Delete(); err != nil {
logger.Error(err, "deleting cluster build strategies")
return RequeueWithError(err)
}

logger.Info("Deleting manifests...")
if err := manifest.Delete(); err != nil {
Expand Down Expand Up @@ -229,6 +237,29 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ
logger.Error(err, "setting the finalizer")
return RequeueWithError(err)
}

requeue, err = buildstrategy.ReconcileBuildStrategies(ctx,
r.CRDClient,
logger,
r.BuildStrategyManifest)
if err != nil {
logger.Error(err, "reconcile cluster build strategies")
return RequeueWithError(err)
}
if requeue {
logger.Info("requeue waiting for cluster build strategy preconditions")
apimeta.SetStatusCondition(&b.Status.Conditions, metav1.Condition{
Type: ConditionReady,
Status: metav1.ConditionUnknown,
Reason: "ClusterBuildStrategiesWaiting",
Message: "Waiting for cluster build strategies to be deployed",
})
if updateErr := r.Client.Status().Update(ctx, b); updateErr != nil {
return RequeueWithError(err)
}
return Requeue()
}

apimeta.SetStatusCondition(&b.Status.Conditions, metav1.Condition{
Type: ConditionReady,
Status: metav1.ConditionTrue,
Expand All @@ -246,8 +277,15 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ
// setupManifestival instantiate manifestival with local controller attributes, as well as tekton prereqs.
func (r *ShipwrightBuildReconciler) setupManifestival() error {
var err error
r.Manifest, err = common.SetupManifestival(r.Client, "release.yaml", r.Logger)
return err
r.Manifest, err = common.SetupManifestival(r.Client, "release.yaml", false, r.Logger)
if err != nil {
return err
}
r.BuildStrategyManifest, err = common.SetupManifestival(r.Client, filepath.Join("samples", "buildstrategy"), true, r.Logger)
if err != nil {
return err
}
return nil
}

// SetupWithManager sets up the controller with the Manager, by instantiating Manifestival and
Expand Down
22 changes: 18 additions & 4 deletions controllers/shipwrightbuild_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,18 @@ func testShipwrightBuildReconcilerReconcile(t *testing.T, targetNamespace string
ctx := context.TODO()
res, err := r.Reconcile(ctx, req)
g.Expect(err).To(o.BeNil())
g.Expect(res.Requeue).To(o.BeFalse())
// TODO: Code technically uses two different clientsets that don't talk to each other.
// This makes testing brittle and unable to capture the behavior on a real cluster.
// Requeue can return "true" because the tests think the CRD for ClusterBuildStrategies
// do not exist yet.
g.Expect(res.Requeue).To(o.BeTrue(), "checking requeue for Reconcile")
err = c.Get(ctx, deploymentName, &appsv1.Deployment{})
g.Expect(err).To(o.BeNil())
err = c.Get(ctx, namespacedName, b)
g.Expect(err).To(o.BeNil())
g.Expect(b.Status.IsReady()).To(o.BeTrue())
// Likewise, the ShipwrightBuild object will not report itself ready because it is waiting
// for the ClusterBuildStrategy CRD to be created first.
g.Expect(b.Status.IsReady()).To(o.BeFalse(), "checking ShipwrightBuild readiness")
})

t.Run("rollout-manifests-with-images-env-vars", func(t *testing.T) {
Expand All @@ -174,14 +180,20 @@ func testShipwrightBuildReconcilerReconcile(t *testing.T, targetNamespace string
deployment := &appsv1.Deployment{}
res, err := r.Reconcile(ctx, req)
g.Expect(err).To(o.BeNil())
g.Expect(res.Requeue).To(o.BeFalse())
// TODO: Code technically uses two different clientsets that don't talk to each other.
// This makes testing brittle and unable to capture the behavior on a real cluster.
// Requeue can return "true" because the tests think the CRD for ClusterBuildStrategies
// do not exist yet.
g.Expect(res.Requeue).To(o.BeTrue())
err = c.Get(ctx, deploymentName, deployment)
g.Expect(err).To(o.BeNil())
containers := deployment.Spec.Template.Spec.Containers
g.Expect(containers[0].Image).To(o.Equal("ghcr.io/shipwright-io/build/shipwright-build-controller:nightly-2023-05-05-1683263383"))
err = c.Get(ctx, namespacedName, b)
g.Expect(err).To(o.BeNil())
g.Expect(b.Status.IsReady()).To(o.BeTrue())
// Likewise, the ShipwrightBuild object will not report itself ready because it is waiting
// for the ClusterBuildStrategy CRD to be created first.
g.Expect(b.Status.IsReady()).To(o.BeFalse())
})

// rolling back all changes, making sure the main deployment is also not found afterwards
Expand All @@ -193,6 +205,8 @@ func testShipwrightBuildReconcilerReconcile(t *testing.T, targetNamespace string

// setting a deletion timestemp on the build object, it triggers the rollback logic so the
// reconciliation should remove the objects previously deployed

// TODO: Refactor to use owner references so the rollback is handled by Kubernetes itself.
b.SetDeletionTimestamp(&metav1.Time{Time: time.Now()})
err = r.Update(ctx, b, &client.UpdateOptions{})
g.Expect(err).To(o.BeNil())
Expand Down
Loading

0 comments on commit bed9d49

Please sign in to comment.