diff --git a/README.md b/README.md
index 5561242ac..6171bfc00 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,21 @@ spec:
The operator will deploy Shipwright Builds in the provided `targetNamespace`.
When `.spec.targetNamespace` is not set, the namespace will default to `shipwright-build`.
+
+To deploy [Shipwright Triggers](https://github.com/shipwright-io/triggers), add the `triggers` field:
+
+```yaml
+---
+apiVersion: operator.shipwright.io/v1alpha1
+kind: ShipwrightBuild
+metadata:
+ name: shipwright-operator
+spec:
+ targetNamespace: shipwright-build
+ triggers:
+ deployment: Enabled
+```
+
Refer to the [ShipwrightBuild documentation](docs/shipwrightbuild.md) for more information about this custom resource.
The operator handles differents environment variables to customize Shiprwright controller installation:
@@ -52,6 +67,7 @@ The operator handles differents environment variables to customize Shiprwright c
- IMAGE_SHIPWRIGHT_BUNDLE_CONTAINER_IMAGE: defines the Shipwright Bundle Image to use
- IMAGE_SHIPWRIGHT_WAITER_CONTAINER_IMAGE: defines the Shipwright Waiter Image to use
- IMAGE_SHIPWRIGHT_SHIPWRIGHT_BUILD_WEBHOOK: defines the Shipwright Build Webhook Image to use
+- IMAGE_SHIPWRIGHT_SHIPWRIGHT_TRIGGERS: defines the Shipwright Triggers Image to use
For more information about the function of these images, please consider the Shipwright Build doc https://github.com/shipwright-io/build/blob/main/docs/configuration.md
diff --git a/api/v1alpha1/shipwrightbuild_types.go b/api/v1alpha1/shipwrightbuild_types.go
index f7910f147..3e7c87453 100644
--- a/api/v1alpha1/shipwrightbuild_types.go
+++ b/api/v1alpha1/shipwrightbuild_types.go
@@ -8,10 +8,47 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
+// TriggersDeployment indicates whether the Shipwright Triggers component
+// should be deployed.
+// +kubebuilder:validation:Enum=Enabled;Disabled
+type TriggersDeployment string
+
+const (
+ // TriggersDeploymentEnabled indicates that the Shipwright Triggers
+ // component should be deployed.
+ TriggersDeploymentEnabled TriggersDeployment = "Enabled"
+ // TriggersDeploymentDisabled indicates that the Shipwright Triggers
+ // component should not be deployed.
+ TriggersDeploymentDisabled TriggersDeployment = "Disabled"
+)
+
+// TriggersSpec defines the desired state of the Triggers component.
+type TriggersSpec struct {
+ // Deployment controls whether the triggers component is deployed.
+ // Defaults to "Disabled".
+ // +kubebuilder:default=Disabled
+ // +optional
+ Deployment TriggersDeployment `json:"deployment,omitempty"`
+}
+
// ShipwrightBuildSpec defines the configuration of a Shipwright Build deployment.
type ShipwrightBuildSpec struct {
// TargetNamespace is the target namespace where Shipwright's build controller will be deployed.
TargetNamespace string `json:"targetNamespace,omitempty"`
+
+ // Triggers configures the deployment of the Shipwright Triggers component.
+ // When omitted, triggers are not deployed.
+ // +optional
+ Triggers *TriggersSpec `json:"triggers,omitempty"`
+}
+
+// TriggersEnabled returns true if the Triggers component should be deployed.
+// Triggers are only deployed when spec.triggers.deployment is set to "Enabled".
+func (s *ShipwrightBuildSpec) TriggersEnabled() bool {
+ if s.Triggers == nil {
+ return false
+ }
+ return s.Triggers.Deployment == TriggersDeploymentEnabled
}
// ShipwrightBuildStatus defines the observed state of ShipwrightBuild
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 68890bb47..d56ea3972 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -18,7 +18,7 @@ func (in *ShipwrightBuild) DeepCopyInto(out *ShipwrightBuild) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
- out.Spec = in.Spec
+ in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
@@ -75,6 +75,11 @@ func (in *ShipwrightBuildList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ShipwrightBuildSpec) DeepCopyInto(out *ShipwrightBuildSpec) {
*out = *in
+ if in.Triggers != nil {
+ in, out := &in.Triggers, &out.Triggers
+ *out = new(TriggersSpec)
+ **out = **in
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShipwrightBuildSpec.
@@ -108,3 +113,18 @@ func (in *ShipwrightBuildStatus) DeepCopy() *ShipwrightBuildStatus {
in.DeepCopyInto(out)
return out
}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TriggersSpec) DeepCopyInto(out *TriggersSpec) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggersSpec.
+func (in *TriggersSpec) DeepCopy() *TriggersSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(TriggersSpec)
+ in.DeepCopyInto(out)
+ return out
+}
diff --git a/bundle/manifests/operator.shipwright.io_shipwrightbuilds.yaml b/bundle/manifests/operator.shipwright.io_shipwrightbuilds.yaml
index 90841897f..5491dd09a 100644
--- a/bundle/manifests/operator.shipwright.io_shipwrightbuilds.yaml
+++ b/bundle/manifests/operator.shipwright.io_shipwrightbuilds.yaml
@@ -45,6 +45,21 @@ spec:
description: TargetNamespace is the target namespace where Shipwright's
build controller will be deployed.
type: string
+ triggers:
+ description: |-
+ Triggers configures the deployment of the Shipwright Triggers component.
+ When omitted, triggers are not deployed.
+ properties:
+ deployment:
+ default: Disabled
+ description: |-
+ Deployment controls whether the triggers component is deployed.
+ Defaults to "Disabled".
+ enum:
+ - Enabled
+ - Disabled
+ type: string
+ type: object
type: object
status:
description: ShipwrightBuildStatus defines the observed state of ShipwrightBuild
diff --git a/bundle/manifests/shipwright-operator.clusterserviceversion.yaml b/bundle/manifests/shipwright-operator.clusterserviceversion.yaml
index 714270bc4..b4497db9d 100644
--- a/bundle/manifests/shipwright-operator.clusterserviceversion.yaml
+++ b/bundle/manifests/shipwright-operator.clusterserviceversion.yaml
@@ -325,6 +325,17 @@ spec:
- delete
- patch
- update
+ - apiGroups:
+ - ""
+ resourceNames:
+ - shipwright-triggers
+ resources:
+ - serviceaccounts
+ - services
+ verbs:
+ - delete
+ - patch
+ - update
- apiGroups:
- admissionregistration.k8s.io
- admissionregistration.k8s.io/v1beta1
@@ -389,6 +400,16 @@ spec:
- delete
- patch
- update
+ - apiGroups:
+ - apps
+ resourceNames:
+ - shipwright-triggers
+ resources:
+ - deployments
+ verbs:
+ - delete
+ - patch
+ - update
- apiGroups:
- apps
resourceNames:
@@ -405,6 +426,14 @@ spec:
- deployments/finalizers
verbs:
- update
+ - apiGroups:
+ - apps
+ resourceNames:
+ - shipwright-triggers
+ resources:
+ - deployments/finalizers
+ verbs:
+ - update
- apiGroups:
- cert-manager.io
resourceNames:
@@ -505,6 +534,19 @@ spec:
- delete
- patch
- update
+ - apiGroups:
+ - rbac.authorization.k8s.io
+ resourceNames:
+ - shipwright-triggers
+ resources:
+ - clusterrolebindings
+ - clusterroles
+ - rolebindings
+ - roles
+ verbs:
+ - delete
+ - patch
+ - update
- apiGroups:
- rbac.authorization.k8s.io
resourceNames:
@@ -549,6 +591,50 @@ spec:
- subjectaccessreviews
verbs:
- create
+ - apiGroups:
+ - shipwright.io
+ resources:
+ - buildruns
+ verbs:
+ - create
+ - get
+ - list
+ - update
+ - watch
+ - apiGroups:
+ - shipwright.io
+ resources:
+ - builds
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - tekton.dev
+ resources:
+ - customruns
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - tekton.dev
+ resources:
+ - customruns/finalizers
+ - customruns/status
+ verbs:
+ - patch
+ - update
+ - apiGroups:
+ - tekton.dev
+ resources:
+ - pipelineruns
+ verbs:
+ - get
+ - list
+ - patch
+ - update
+ - watch
serviceAccountName: shipwright-operator
deployments:
- label:
diff --git a/config/crd/bases/operator.shipwright.io_shipwrightbuilds.yaml b/config/crd/bases/operator.shipwright.io_shipwrightbuilds.yaml
index 8341ab0bd..862dc642d 100644
--- a/config/crd/bases/operator.shipwright.io_shipwrightbuilds.yaml
+++ b/config/crd/bases/operator.shipwright.io_shipwrightbuilds.yaml
@@ -45,6 +45,21 @@ spec:
description: TargetNamespace is the target namespace where Shipwright's
build controller will be deployed.
type: string
+ triggers:
+ description: |-
+ Triggers configures the deployment of the Shipwright Triggers component.
+ When omitted, triggers are not deployed.
+ properties:
+ deployment:
+ default: Disabled
+ description: |-
+ Deployment controls whether the triggers component is deployed.
+ Defaults to "Disabled".
+ enum:
+ - Enabled
+ - Disabled
+ type: string
+ type: object
type: object
status:
description: ShipwrightBuildStatus defines the observed state of ShipwrightBuild
diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml
index 4f6af73ff..5b41b67a7 100644
--- a/config/rbac/kustomization.yaml
+++ b/config/rbac/kustomization.yaml
@@ -20,3 +20,5 @@ resources:
- shipwright_build_aggregate_role_binding.yaml
- shipwright_build_webhook_role.yaml
- shipwright_build_webhook_role_binding.yaml
+- shipwright_triggers_controller_role.yaml
+- shipwright_triggers_controller_role_binding.yaml
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index fccf7b657..2d91e92eb 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -51,6 +51,17 @@ rules:
- delete
- patch
- update
+- apiGroups:
+ - ""
+ resourceNames:
+ - shipwright-triggers
+ resources:
+ - serviceaccounts
+ - services
+ verbs:
+ - delete
+ - patch
+ - update
- apiGroups:
- admissionregistration.k8s.io
- admissionregistration.k8s.io/v1beta1
@@ -115,6 +126,16 @@ rules:
- delete
- patch
- update
+- apiGroups:
+ - apps
+ resourceNames:
+ - shipwright-triggers
+ resources:
+ - deployments
+ verbs:
+ - delete
+ - patch
+ - update
- apiGroups:
- apps
resourceNames:
@@ -131,6 +152,14 @@ rules:
- deployments/finalizers
verbs:
- update
+- apiGroups:
+ - apps
+ resourceNames:
+ - shipwright-triggers
+ resources:
+ - deployments/finalizers
+ verbs:
+ - update
- apiGroups:
- cert-manager.io
resourceNames:
@@ -231,6 +260,19 @@ rules:
- delete
- patch
- update
+- apiGroups:
+ - rbac.authorization.k8s.io
+ resourceNames:
+ - shipwright-triggers
+ resources:
+ - clusterrolebindings
+ - clusterroles
+ - rolebindings
+ - roles
+ verbs:
+ - delete
+ - patch
+ - update
- apiGroups:
- rbac.authorization.k8s.io
resourceNames:
diff --git a/config/rbac/shipwright_triggers_controller_role.yaml b/config/rbac/shipwright_triggers_controller_role.yaml
new file mode 100644
index 000000000..cb2dd9e1d
--- /dev/null
+++ b/config/rbac/shipwright_triggers_controller_role.yaml
@@ -0,0 +1,20 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: triggers-controller
+rules:
+ - apiGroups: ['shipwright.io']
+ resources: ['buildruns']
+ verbs: ['create', 'get', 'list', 'update', 'watch']
+ - apiGroups: ['shipwright.io']
+ resources: ['builds']
+ verbs: ['get', 'list', 'watch']
+ - apiGroups: ['tekton.dev']
+ resources: ['customruns']
+ verbs: ['get', 'list', 'watch']
+ - apiGroups: ['tekton.dev']
+ resources: ['customruns/finalizers', 'customruns/status']
+ verbs: ['patch', 'update']
+ - apiGroups: ['tekton.dev']
+ resources: ['pipelineruns']
+ verbs: ['get', 'list', 'patch', 'update', 'watch']
diff --git a/config/rbac/shipwright_triggers_controller_role_binding.yaml b/config/rbac/shipwright_triggers_controller_role_binding.yaml
new file mode 100644
index 000000000..4b216b217
--- /dev/null
+++ b/config/rbac/shipwright_triggers_controller_role_binding.yaml
@@ -0,0 +1,12 @@
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: triggers-controller
+roleRef:
+ kind: ClusterRole
+ name: triggers-controller
+ apiGroup: rbac.authorization.k8s.io
+subjects:
+- kind: ServiceAccount
+ name: operator
+ namespace: system
diff --git a/config/samples/operator_v1alpha1_shipwrightbuild_with_triggers.yaml b/config/samples/operator_v1alpha1_shipwrightbuild_with_triggers.yaml
new file mode 100644
index 000000000..eec21f5d1
--- /dev/null
+++ b/config/samples/operator_v1alpha1_shipwrightbuild_with_triggers.yaml
@@ -0,0 +1,8 @@
+apiVersion: operator.shipwright.io/v1alpha1
+kind: ShipwrightBuild
+metadata:
+ name: shipwright-build
+spec:
+ targetNamespace: shipwright-build
+ triggers:
+ deployment: Enabled
diff --git a/controllers/buildstrategies_test.go b/controllers/buildstrategies_test.go
index 18099bf26..a3e970dee 100644
--- a/controllers/buildstrategies_test.go
+++ b/controllers/buildstrategies_test.go
@@ -20,7 +20,7 @@ var _ = Describe("Install embedded build strategies", func() {
BeforeEach(func(ctx SpecContext) {
setupTektonCRDs(ctx)
createTektonConfig(ctx)
- build = createShipwrightBuild(ctx, "shipwright")
+ build = createShipwrightBuild(ctx, "cluster", "shipwright")
test.CRDEventuallyExists(ctx, k8sClient, "clusterbuildstrategies.shipwright.io")
})
diff --git a/controllers/default_test.go b/controllers/default_test.go
index 56fcbab60..4c33cc46a 100644
--- a/controllers/default_test.go
+++ b/controllers/default_test.go
@@ -48,7 +48,7 @@ var _ = g.Describe("Reconcile default ShipwrightBuild installation", func() {
// setting up the namespaces, where Shipwright Controller will be deployed
setupTektonCRDs(ctx)
createTektonConfig(ctx)
- build = createShipwrightBuild(ctx, targetNamespace)
+ build = createShipwrightBuild(ctx, "cluster", targetNamespace)
})
g.AfterEach(func(ctx g.SpecContext) {
diff --git a/controllers/shipwrightbuild_controller.go b/controllers/shipwrightbuild_controller.go
index 6c7dfaa4b..c9b3ef704 100644
--- a/controllers/shipwrightbuild_controller.go
+++ b/controllers/shipwrightbuild_controller.go
@@ -32,6 +32,7 @@ import (
"github.com/shipwright-io/operator/pkg/certmanager"
"github.com/shipwright-io/operator/pkg/common"
"github.com/shipwright-io/operator/pkg/tekton"
+ "github.com/shipwright-io/operator/pkg/triggers"
)
const (
@@ -62,6 +63,7 @@ type ShipwrightBuildReconciler struct {
Manifest manifestival.Manifest // release manifests render
TektonManifest manifestival.Manifest // Tekton release manifest render
BuildStrategyManifest manifestival.Manifest // Build strategies manifest to render
+ TriggersManifest manifestival.Manifest // Triggers manifest to render
}
type TektonCheckResult struct {
@@ -312,6 +314,12 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ
logger.Info("Finalizers removed, deletion of manifests completed!")
return NoRequeue()
}
+ logger.Info("Deleting triggers resources")
+ if err := r.deleteTriggersManifest(targetNamespace); err != nil {
+ logger.Error(err, "deleting triggers resources")
+ return RequeueWithError(err)
+ }
+
logger.Info("Deleting cluster build strategies")
if err := r.BuildStrategyManifest.Delete(); err != nil {
logger.Error(err, "deleting cluster build strategies")
@@ -386,6 +394,46 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return Requeue()
}
+ // Reconcile triggers
+ if b.Spec.TriggersEnabled() {
+ triggersManifest, err := r.TriggersManifest.
+ Filter(manifestival.Not(manifestival.ByKind("Namespace"))).
+ Transform(
+ // TODO: Remove this when we remove the target namespace feature.
+ // See https://github.com/shipwright-io/operator/issues/241
+ manifestival.InjectNamespace(targetNamespace),
+ common.DeploymentImages(images),
+ )
+ if err != nil {
+ logger.Error(err, "transforming triggers manifests")
+ return RequeueWithError(err)
+ }
+
+ requeue, err = triggers.ReconcileTriggers(ctx, r.CRDClient, logger, triggersManifest)
+ if err != nil {
+ logger.Error(err, "reconcile triggers")
+ return RequeueWithError(err)
+ }
+ if requeue {
+ logger.Info("requeue waiting for triggers preconditions")
+ apimeta.SetStatusCondition(&b.Status.Conditions, metav1.Condition{
+ Type: ConditionReady,
+ Status: metav1.ConditionUnknown,
+ Reason: "TriggersWaiting",
+ Message: "Waiting for triggers preconditions to be met",
+ })
+ if updateErr := r.Client.Status().Update(ctx, b); updateErr != nil {
+ return RequeueWithError(updateErr)
+ }
+ return Requeue()
+ }
+ } else {
+ if err := r.deleteTriggersManifest(targetNamespace); err != nil {
+ logger.Error(err, "cleaning up triggers resources")
+ return RequeueWithError(err)
+ }
+ }
+
apimeta.SetStatusCondition(&b.Status.Conditions, metav1.Condition{
Type: ConditionReady,
Status: metav1.ConditionTrue,
@@ -400,6 +448,19 @@ func (r *ShipwrightBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return NoRequeue()
}
+// deleteTriggersManifest deletes the triggers resources in the given namespace.
+func (r *ShipwrightBuildReconciler) deleteTriggersManifest(targetNamespace string) error {
+ triggersManifest, err := r.TriggersManifest.
+ Filter(manifestival.Not(manifestival.ByKind("Namespace"))).
+ // TODO: Remove this when we remove the target namespace feature.
+ // See https://github.com/shipwright-io/operator/issues/241
+ Transform(manifestival.InjectNamespace(targetNamespace))
+ if err != nil {
+ return err
+ }
+ return triggersManifest.Delete()
+}
+
// setupManifestival instantiate manifestival with local controller attributes, as well as tekton prereqs.
func (r *ShipwrightBuildReconciler) setupManifestival() error {
var err error
@@ -411,6 +472,10 @@ func (r *ShipwrightBuildReconciler) setupManifestival() error {
if err != nil {
return err
}
+ r.TriggersManifest, err = common.SetupManifestival(r.Client, "triggers-release.yaml", false, r.Logger)
+ if err != nil {
+ return err
+ }
return nil
}
diff --git a/controllers/shipwrightbuild_rbac.go b/controllers/shipwrightbuild_rbac.go
index be7e9b89b..85b7b2f74 100644
--- a/controllers/shipwrightbuild_rbac.go
+++ b/controllers/shipwrightbuild_rbac.go
@@ -37,6 +37,14 @@ package controllers
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterrolebindings,resourceNames=shipwright-build-webhook,verbs=update;patch;delete
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,resourceNames=shipwright-build-webhook,verbs=update;patch;delete
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,resourceNames=shipwright-build-webhook,verbs=update;patch;delete
+// +kubebuilder:rbac:groups=apps,resources=deployments,resourceNames=shipwright-triggers,verbs=update;patch;delete
+// +kubebuilder:rbac:groups=apps,resources=deployments/finalizers,resourceNames=shipwright-triggers,verbs=update
+// +kubebuilder:rbac:groups=core,resources=serviceaccounts,resourceNames=shipwright-triggers,verbs=update;patch;delete
+// +kubebuilder:rbac:groups=core,resources=services,resourceNames=shipwright-triggers,verbs=update;patch;delete
+// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles,resourceNames=shipwright-triggers,verbs=update;patch;delete
+// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterrolebindings,resourceNames=shipwright-triggers,verbs=update;patch;delete
+// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,resourceNames=shipwright-triggers,verbs=update;patch;delete
+// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,resourceNames=shipwright-triggers,verbs=update;patch;delete
// +kubebuilder:rbac:groups=cert-manager.io,resources=issuers,verbs=get;list;watch;create
// +kubebuilder:rbac:groups=cert-manager.io,resources=issuers,resourceNames=selfsigned-issuer,verbs=update;patch;delete
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates,verbs=get;list;watch;create
diff --git a/controllers/suite_test.go b/controllers/suite_test.go
index a98e62d3e..456cbebc9 100644
--- a/controllers/suite_test.go
+++ b/controllers/suite_test.go
@@ -144,13 +144,13 @@ func setupTektonCRDs(ctx context.Context) {
Expect(err).NotTo(HaveOccurred())
}
-// createShipwrightBuild creates an instance of the ShipwrightBuild object with the given target
-// namespace.
-func createShipwrightBuild(ctx context.Context, targetNamespace string) *operatorv1alpha1.ShipwrightBuild {
+// createShipwrightBuild creates an instance of the ShipwrightBuild object with the given name and
+// target namespace.
+func createShipwrightBuild(ctx context.Context, name string, targetNamespace string) *operatorv1alpha1.ShipwrightBuild {
By("creating a ShipwrightBuild instance")
build := &operatorv1alpha1.ShipwrightBuild{
ObjectMeta: metav1.ObjectMeta{
- Name: "cluster",
+ Name: name,
},
Spec: operatorv1alpha1.ShipwrightBuildSpec{
TargetNamespace: targetNamespace,
@@ -166,6 +166,28 @@ func createShipwrightBuild(ctx context.Context, targetNamespace string) *operato
return build
}
+// createShipwrightBuildWithTriggers creates an instance of the ShipwrightBuild object with triggers enabled.
+func createShipwrightBuildWithTriggers(ctx context.Context, name string, targetNamespace string) *operatorv1alpha1.ShipwrightBuild {
+ By("creating a ShipwrightBuild instance with triggers enabled")
+ build := &operatorv1alpha1.ShipwrightBuild{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: name,
+ },
+ Spec: operatorv1alpha1.ShipwrightBuildSpec{
+ TargetNamespace: targetNamespace,
+ Triggers: &operatorv1alpha1.TriggersSpec{
+ Deployment: operatorv1alpha1.TriggersDeploymentEnabled,
+ },
+ },
+ }
+ err := k8sClient.Create(ctx, build, &client.CreateOptions{})
+ Expect(err).NotTo(HaveOccurred())
+
+ By("waiting for the finalizer to be set")
+ test.EventuallyContainFinalizer(ctx, k8sClient, build, FinalizerAnnotation)
+ return build
+}
+
// deleteShipwrightBuild tears down the given ShipwrightBuild instance.
func deleteShipwrightBuild(ctx context.Context, build *operatorv1alpha1.ShipwrightBuild) {
By("deleting the ShipwrightBuild instance")
diff --git a/controllers/triggers_test.go b/controllers/triggers_test.go
new file mode 100644
index 000000000..81f5841cd
--- /dev/null
+++ b/controllers/triggers_test.go
@@ -0,0 +1,145 @@
+package controllers
+
+import (
+ 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"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ "github.com/shipwright-io/operator/api/v1alpha1"
+ "github.com/shipwright-io/operator/test"
+)
+
+var _ = g.Describe("Reconcile ShipwrightBuild with Triggers", func() {
+
+ const targetNamespace = "triggers-test-ns"
+ var build *v1alpha1.ShipwrightBuild
+
+ triggersDeployment := &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: targetNamespace,
+ Name: "shipwright-triggers",
+ },
+ }
+
+ g.When("triggers are enabled", g.Ordered, func() {
+
+ g.BeforeAll(func(ctx g.SpecContext) {
+ setupTektonCRDs(ctx)
+ createTektonConfig(ctx)
+ build = createShipwrightBuildWithTriggers(ctx, "triggers-enabled", targetNamespace)
+ })
+
+ g.AfterAll(func(ctx g.SpecContext) {
+ deleteShipwrightBuild(ctx, build)
+ deleteTektonConfig(ctx)
+ })
+
+ g.It("creates triggers resources", func(ctx g.SpecContext) {
+ test.EventuallyExists(ctx, k8sClient, triggersDeployment.DeepCopy())
+ test.EventuallyExists(ctx, k8sClient, &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Namespace: targetNamespace, Name: "shipwright-triggers"},
+ })
+ test.EventuallyExists(ctx, k8sClient, &corev1.ServiceAccount{
+ ObjectMeta: metav1.ObjectMeta{Namespace: targetNamespace, Name: "shipwright-triggers"},
+ })
+ test.EventuallyExists(ctx, k8sClient, &rbacv1.ClusterRole{
+ ObjectMeta: metav1.ObjectMeta{Name: "shipwright-triggers"},
+ })
+ test.EventuallyExists(ctx, k8sClient, &rbacv1.ClusterRoleBinding{
+ ObjectMeta: metav1.ObjectMeta{Name: "shipwright-triggers"},
+ })
+ })
+ })
+
+ g.When("triggers are enabled and the ShipwrightBuild is deleted", g.Ordered, func() {
+ const deleteNamespace = "triggers-delete-ns"
+
+ g.BeforeAll(func(ctx g.SpecContext) {
+ setupTektonCRDs(ctx)
+ createTektonConfig(ctx)
+ build = createShipwrightBuildWithTriggers(ctx, "triggers-delete", deleteNamespace)
+ })
+
+ g.AfterAll(func(ctx g.SpecContext) {
+ deleteShipwrightBuild(ctx, build)
+ deleteTektonConfig(ctx)
+ })
+
+ g.It("removes the triggers deployment", func(ctx g.SpecContext) {
+ expectedDeployment := &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{Namespace: deleteNamespace, Name: "shipwright-triggers"},
+ }
+ test.EventuallyExists(ctx, k8sClient, expectedDeployment)
+
+ err := k8sClient.Delete(ctx, build, &client.DeleteOptions{})
+ o.Expect(err).NotTo(o.HaveOccurred())
+
+ test.EventuallyRemoved(ctx, k8sClient, expectedDeployment)
+ })
+ })
+
+ g.When("triggers are disabled after being enabled", g.Ordered, func() {
+ const disableNamespace = "triggers-disable-ns"
+
+ g.BeforeAll(func(ctx g.SpecContext) {
+ setupTektonCRDs(ctx)
+ createTektonConfig(ctx)
+ build = createShipwrightBuildWithTriggers(ctx, "triggers-disable", disableNamespace)
+ })
+
+ g.AfterAll(func(ctx g.SpecContext) {
+ deleteShipwrightBuild(ctx, build)
+ deleteTektonConfig(ctx)
+ })
+
+ g.It("removes triggers resources when disabled via spec update", func(ctx g.SpecContext) {
+ expectedDeployment := &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{Namespace: disableNamespace, Name: "shipwright-triggers"},
+ }
+ test.EventuallyExists(ctx, k8sClient, expectedDeployment)
+
+ g.By("disabling triggers")
+ err := k8sClient.Get(ctx, client.ObjectKeyFromObject(build), build)
+ o.Expect(err).NotTo(o.HaveOccurred())
+ build.Spec.Triggers.Deployment = v1alpha1.TriggersDeploymentDisabled
+ err = k8sClient.Update(ctx, build)
+ o.Expect(err).NotTo(o.HaveOccurred())
+
+ test.EventuallyRemoved(ctx, k8sClient, expectedDeployment)
+ })
+ })
+
+ g.When("triggers are not enabled", g.Ordered, func() {
+ const noTriggersNamespace = "no-triggers-test-ns"
+
+ g.BeforeAll(func(ctx g.SpecContext) {
+ setupTektonCRDs(ctx)
+ createTektonConfig(ctx)
+ build = createShipwrightBuild(ctx, "no-triggers", noTriggersNamespace)
+ })
+
+ g.AfterAll(func(ctx g.SpecContext) {
+ deleteShipwrightBuild(ctx, build)
+ deleteTektonConfig(ctx)
+ })
+
+ g.It("does not create the triggers deployment", func(ctx g.SpecContext) {
+ test.EventuallyExists(ctx, k8sClient, &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{Namespace: noTriggersNamespace, Name: "shipwright-build-controller"},
+ })
+
+ noTriggersDeployment := &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{Namespace: noTriggersNamespace, Name: "shipwright-triggers"},
+ }
+ o.Consistently(func() bool {
+ err := k8sClient.Get(ctx, client.ObjectKeyFromObject(noTriggersDeployment), noTriggersDeployment)
+ return err != nil
+ }, "3s", "500ms").Should(o.BeTrue(), "triggers deployment should not be created")
+ })
+ })
+})
diff --git a/docs/shipwrightbuild.md b/docs/shipwrightbuild.md
index 39e67206e..cb9dc1d10 100644
--- a/docs/shipwrightbuild.md
+++ b/docs/shipwrightbuild.md
@@ -10,6 +10,9 @@ When the `ShipwrightBuild` instance is created, the following components are ins
- The custom resources to run Shipwright builds (`ClusterBuildStrategy`, `BuildStrategy`, `Build`,
`BuildRun`).
- Shipwright Build's controller, conversion webhook, and associated CA certificates.
+- Optionally, [Shipwright Triggers](https://github.com/shipwright-io/triggers) — a controller that
+ automatically creates BuildRuns based on events (e.g., Tekton PipelineRun completions). Enabled
+ via `spec.triggers.deployment: Enabled`.
- The following example `ClusterBuildStrategies`:
- `buildah-shipwright-managed-push`
- `buildah-strategy-managed-push`
@@ -27,4 +30,5 @@ When the `ShipwrightBuild` instance is created, the following components are ins
| Field | Description |
| ----- | ----------- |
| spec.targetNamespace | The target namespace where Shipwright Build will be deployed. If omitted, this will default to `shipwright-build` |
+| spec.triggers.deployment | When set to `Enabled`, deploys Shipwright Triggers alongside Build. Triggers are not deployed when this field is omitted or set to `Disabled`. Defaults to `Disabled`. |
| status.conditions | Conditions which report the status of Shipwright Build. Current reported conditions:
- `Ready` |
diff --git a/kodata/triggers-release.yaml b/kodata/triggers-release.yaml
new file mode 100644
index 000000000..9f144ff8b
--- /dev/null
+++ b/kodata/triggers-release.yaml
@@ -0,0 +1,260 @@
+# Source: shipwright-triggers/templates/serviceaccount.yaml
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ namespace: shipwright-build
+ name: shipwright-triggers
+ labels:
+ helm.sh/chart: shipwright-triggers-0.0.1
+ meta.helm.sh/release-name: shipwright-triggers
+ meta.helm.sh/release-namespace: shipwright-build
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+ app.kubernetes.io/managed-by: Helm
+---
+# Source: shipwright-triggers/templates/role.yaml
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ helm.sh/chart: shipwright-triggers-0.0.1
+ meta.helm.sh/release-name: shipwright-triggers
+ meta.helm.sh/release-namespace: shipwright-build
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+ app.kubernetes.io/managed-by: Helm
+ name: shipwright-triggers
+rules:
+ - apiGroups:
+ - shipwright.io
+ resources:
+ - buildruns
+ verbs:
+ - create
+ - get
+ - list
+ - update
+ - watch
+ - apiGroups:
+ - shipwright.io
+ resources:
+ - builds
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - tekton.dev
+ resources:
+ - customruns
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - tekton.dev
+ resources:
+ - customruns/finalizers
+ - customruns/status
+ verbs:
+ - patch
+ - update
+ - apiGroups:
+ - tekton.dev
+ resources:
+ - pipelineruns
+ verbs:
+ - get
+ - list
+ - patch
+ - update
+ - watch
+---
+# Source: shipwright-triggers/templates/role-binding.yaml
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: shipwright-triggers
+ labels:
+ helm.sh/chart: shipwright-triggers-0.0.1
+ meta.helm.sh/release-name: shipwright-triggers
+ meta.helm.sh/release-namespace: shipwright-build
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+ app.kubernetes.io/managed-by: Helm
+subjects:
+ - kind: ServiceAccount
+ namespace: shipwright-build
+ name: shipwright-triggers
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: shipwright-triggers
+---
+# Source: shipwright-triggers/templates/role.yaml
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ namespace: shipwright-build
+ labels:
+ helm.sh/chart: shipwright-triggers-0.0.1
+ meta.helm.sh/release-name: shipwright-triggers
+ meta.helm.sh/release-namespace: shipwright-build
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+ app.kubernetes.io/managed-by: Helm
+ name: shipwright-triggers
+rules:
+ - apiGroups:
+ - shipwright.io
+ resources:
+ - buildruns
+ verbs:
+ - create
+ - get
+ - list
+ - update
+ - watch
+ - apiGroups:
+ - shipwright.io
+ resources:
+ - builds
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - tekton.dev
+ resources:
+ - customruns
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - tekton.dev
+ resources:
+ - customruns/finalizers
+ - customruns/status
+ verbs:
+ - patch
+ - update
+ - apiGroups:
+ - tekton.dev
+ resources:
+ - pipelineruns
+ verbs:
+ - get
+ - list
+ - patch
+ - update
+ - watch
+---
+# Source: shipwright-triggers/templates/role-binding.yaml
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ namespace: shipwright-build
+ name: shipwright-triggers
+ labels:
+ helm.sh/chart: shipwright-triggers-0.0.1
+ meta.helm.sh/release-name: shipwright-triggers
+ meta.helm.sh/release-namespace: shipwright-build
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+ app.kubernetes.io/managed-by: Helm
+subjects:
+ - kind: ServiceAccount
+ namespace: shipwright-build
+ name: shipwright-triggers
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: shipwright-triggers
+---
+# Source: shipwright-triggers/templates/service.yaml
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: shipwright-build
+ name: shipwright-triggers
+ labels:
+ helm.sh/chart: shipwright-triggers-0.0.1
+ meta.helm.sh/release-name: shipwright-triggers
+ meta.helm.sh/release-namespace: shipwright-build
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+ app.kubernetes.io/managed-by: Helm
+spec:
+ type: ClusterIP
+ ports:
+ - port: 80
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+---
+# Source: shipwright-triggers/templates/deployment.yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: shipwright-build
+ name: shipwright-triggers
+ labels:
+ helm.sh/chart: shipwright-triggers-0.0.1
+ meta.helm.sh/release-name: shipwright-triggers
+ meta.helm.sh/release-namespace: shipwright-build
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+ app.kubernetes.io/managed-by: Helm
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: shipwright-triggers
+ app.kubernetes.io/instance: shipwright-triggers
+ spec:
+ serviceAccountName: shipwright-triggers
+ securityContext:
+ runAsNonRoot: true
+ containers:
+ - name: shipwright-triggers
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: true
+ runAsGroup: 1000
+ runAsUser: 1000
+ seccompProfile:
+ type: RuntimeDefault
+ image: "ghcr.io/shipwright-io/triggers/triggers@sha256:66166ea3a992d251d72d8ae548b9d31f48ad41ac2f98f385641627a18e0c5666"
+ args:
+ - --health-probe-bind-address
+ - ":8081"
+ imagePullPolicy: IfNotPresent
+ ports:
+ - name: webhook
+ containerPort: 80
+ protocol: TCP
+ - name: probe
+ containerPort: 8081
+ protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: probe
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: probe
+
+---
diff --git a/pkg/tekton/tekton.go b/pkg/tekton/tekton.go
index b3ede3698..4f7ef0e1b 100644
--- a/pkg/tekton/tekton.go
+++ b/pkg/tekton/tekton.go
@@ -14,7 +14,9 @@ import (
// ReconcileTekton ensures that Tekton Pipelines has been installed.
// If Tekton Pipelines has not been installed, ReconcileTekton will create a TektonConfig object
-// so that the Tekton Operator deploys Tekton Pipelines.
+// with the "lite" profile so that the Tekton Operator deploys Tekton Pipelines.
+// If a TektonConfig already exists, ReconcileTekton leaves it untouched, regardless of its
+// profile, so that externally managed configurations are preserved.
func ReconcileTekton(ctx context.Context,
crdClient crdclientv1.ApiextensionsV1Interface,
tektonOperatorClient tektonoperatorclientv1alpha1.OperatorV1alpha1Interface) (*tektonoperatorv1alpha1.TektonConfig, bool, error) {
@@ -39,6 +41,7 @@ func ReconcileTekton(ctx context.Context,
if tektonVersion.Major() < common.TektonOpMinSupportedMajor+1 && tektonVersion.Minor() < common.TektonOpMinSupportedMinor {
return nil, true, fmt.Errorf("insufficient Tekton Operator version - must be greater than %s", common.TektonOpMinSupportedVersion)
}
+
tektonConfigPresent, err := IsTektonConfigPresent(ctx, tektonOperatorClient)
if err != nil {
return nil, true, err
@@ -46,10 +49,9 @@ func ReconcileTekton(ctx context.Context,
if tektonConfigPresent {
return nil, false, nil
}
- // 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
+
tektonConfig, err := CreateTektonConfigWithProfileAndTargetNamespace(ctx,
- tektonOperatorClient, "lite", "tekton-pipelines")
+ tektonOperatorClient, tektonoperatorv1alpha1.ProfileLite, "tekton-pipelines")
if err != nil {
return tektonConfig, true, err
}
diff --git a/pkg/tekton/tekton_test.go b/pkg/tekton/tekton_test.go
index ae1abe3e7..f18a8cc3e 100644
--- a/pkg/tekton/tekton_test.go
+++ b/pkg/tekton/tekton_test.go
@@ -111,6 +111,29 @@ func TestReconcileTekton(t *testing.T) {
expectRequeue: false,
expectError: false,
},
+ {
+ // Externally managed TektonConfig with a richer profile must not be
+ // touched by the operator.
+ name: "TektonConfig externally managed with basic profile",
+ tektonConfigCRD: &apiextensionsv1.CustomResourceDefinition{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "tektonconfigs.operator.tekton.dev",
+ Labels: map[string]string{
+ "operator.tekton.dev/release": common.TektonOpMinSupportedVersion,
+ },
+ },
+ },
+ tektonConfigObj: &tektonoperatorv1alpha1.TektonConfig{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: tektonoperatorv1alpha1.TektonConfigSpec{
+ Profile: tektonoperatorv1alpha1.ProfileBasic,
+ },
+ },
+ expectRequeue: false,
+ expectError: false,
+ },
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
@@ -146,12 +169,15 @@ func TestReconcileTekton(t *testing.T) {
if action.Matches("create", "tektonconfigs") {
createOccurred = true
}
+ // The operator must never update an existing TektonConfig.
+ g.Expect(action.Matches("update", "tektonconfigs")).To(o.BeFalse(),
+ "ReconcileTekton must not update an existing TektonConfig")
}
g.Expect(createOccurred).To(o.Equal(tc.expectTektonConfigCreateAction))
if tc.expectTektonConfigCreateAction && tc.createTektonConfigErr == nil {
g.Expect(tektonConfig).NotTo(o.BeNil())
g.Expect(tektonConfig.Name).To(o.Equal("config"))
- g.Expect(tektonConfig.Spec.Profile).To(o.Equal("lite"))
+ g.Expect(tektonConfig.Spec.Profile).To(o.Equal(tektonoperatorv1alpha1.ProfileLite))
g.Expect(tektonConfig.Spec.TargetNamespace).To(o.Equal("tekton-pipelines"))
g.Expect(tektonConfig.Spec.Pruner.Disabled).To(o.Equal(false))
g.Expect(tektonConfig.Spec.Pruner.Keep).NotTo(o.BeNil())
diff --git a/pkg/triggers/triggers.go b/pkg/triggers/triggers.go
new file mode 100644
index 000000000..fea6e21ed
--- /dev/null
+++ b/pkg/triggers/triggers.go
@@ -0,0 +1,31 @@
+package triggers
+
+import (
+ "context"
+
+ "github.com/go-logr/logr"
+ "github.com/manifestival/manifestival"
+ "github.com/shipwright-io/operator/pkg/common"
+ crdclientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
+)
+
+const buildsCRD = "builds.shipwright.io"
+
+// ReconcileTriggers reconciles the desired Triggers deployment on the cluster.
+// Returns `true` if triggers were not installed and a requeue is required.
+func ReconcileTriggers(ctx context.Context, crdClient crdclientv1.ApiextensionsV1Interface, log logr.Logger, manifest manifestival.Manifest) (bool, error) {
+ crdExists, err := common.CRDExist(ctx, crdClient, buildsCRD)
+ if err != nil {
+ return true, err
+ }
+ // If the CRD for Shipwright's builds is not installed yet, the reconciler should requeue.
+ if !crdExists {
+ return true, nil
+ }
+ // Apply the provided manifest containing the triggers resources
+ err = manifest.Apply()
+ if err != nil {
+ return true, err
+ }
+ return false, nil
+}
diff --git a/pkg/triggers/triggers_test.go b/pkg/triggers/triggers_test.go
new file mode 100644
index 000000000..1c602fc8f
--- /dev/null
+++ b/pkg/triggers/triggers_test.go
@@ -0,0 +1,87 @@
+package triggers
+
+import (
+ "context"
+ "testing"
+
+ . "github.com/onsi/gomega"
+
+ appsv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ apiextensionsfake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/kubernetes/scheme"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+ "github.com/shipwright-io/operator/pkg/common"
+)
+
+func TestReconcileTriggers(t *testing.T) {
+ cases := []struct {
+ name string
+ installCRDs bool
+ expectRequeue bool
+ expectResourcesInstalled bool
+ }{
+ {
+ name: "no builds CRD",
+ installCRDs: false,
+ expectRequeue: true,
+ },
+ {
+ name: "builds CRD exists",
+ installCRDs: true,
+ expectRequeue: false,
+ expectResourcesInstalled: true,
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ o := NewWithT(t)
+ ctx := context.Background()
+
+ objects := []runtime.Object{}
+ if tc.installCRDs {
+ objects = append(objects, &crdv1.CustomResourceDefinition{
+ ObjectMeta: v1.ObjectMeta{
+ Name: buildsCRD,
+ },
+ })
+ }
+ crdClient := apiextensionsfake.NewSimpleClientset(objects...)
+
+ k8sScheme := runtime.NewScheme()
+ err := scheme.AddToScheme(k8sScheme)
+ o.Expect(err).NotTo(HaveOccurred())
+
+ k8sClient := fake.NewClientBuilder().WithScheme(k8sScheme).Build()
+ log := zap.New()
+
+ manifests, err := common.SetupManifestival(k8sClient, "triggers-release.yaml", false, log)
+ o.Expect(err).NotTo(HaveOccurred(), "setting up Manifestival")
+
+ requeue, err := ReconcileTriggers(ctx, crdClient.ApiextensionsV1(), log, manifests)
+ o.Expect(err).NotTo(HaveOccurred(), "reconciling triggers")
+ o.Expect(requeue).To(BeEquivalentTo(tc.expectRequeue), "check reconcile requeue")
+
+ if tc.expectResourcesInstalled {
+ deployment := &appsv1.Deployment{}
+ err := k8sClient.Get(ctx, client.ObjectKey{Namespace: "shipwright-build", Name: "shipwright-triggers"}, deployment)
+ o.Expect(err).NotTo(HaveOccurred(), "triggers deployment should exist")
+
+ service := &corev1.Service{}
+ err = k8sClient.Get(ctx, client.ObjectKey{Namespace: "shipwright-build", Name: "shipwright-triggers"}, service)
+ o.Expect(err).NotTo(HaveOccurred(), "triggers service should exist")
+
+ sa := &corev1.ServiceAccount{}
+ err = k8sClient.Get(ctx, client.ObjectKey{Namespace: "shipwright-build", Name: "shipwright-triggers"}, sa)
+ o.Expect(err).NotTo(HaveOccurred(), "triggers service account should exist")
+ }
+ })
+ }
+}