Repository: tektoncd/operator
Pull request #153 (2020-09-21 13:53:37)

Add knative/pkg based TektonTrigger API and Reconciler
Part of https://github.com/tektoncd/operator/issues/150

# Changes

* Add TektonTrigger API

* Add TektonTrigger API with spec

* Add generated clients and deepcopy

* Add generated clientset, informers, injection clients, and listers

* Add generated deepcopy

/cc @nikhil-thomas 

<!-- 🎉🎉🎉 Thank you for the PR!!! 🎉🎉🎉 -->

<!-- Describe your changes here- ideally you can get that description straight from
your descriptive commit message(s)! -->

# Submitter Checklist

These are the criteria that every PR should meet, please check them off as you
review them:

- [ ] Includes [tests](https://github.com/tektoncd/community/blob/master/standards.md#principles) (if functionality changed/added)
- [ ] Includes [docs](https://github.com/tektoncd/community/blob/master/standards.md#principles) (if user facing)
- [ ] Commit messages follow [commit message best practices](https://github.com/tektoncd/community/blob/master/standards.md#commit-messages)

_See [the contribution guide](https://github.com/tektoncd/operator/blob/master/CONTRIBUTING.md) for more details._

# Release Notes

<!--
Describe any user facing changes here, or delete this block.

Examples of user facing changes:
- API changes
- Bug fixes
- Any changes in behavior
- Changes requiring upgrade notices or deprecation warnings

For pull requests with a release note:

    ```release-note
    Your release note here
    ```

For pull requests that require additional action from users switching to the new release, include the string "action required" (case insensitive) in the release note:

    ```release-note
    action required: your release note here
    ```

For pull requests that don't need to be mentioned at release time, use the `/release-note-none` Prow command to add the `release-note-none` label to the PR. You can also write the string "NONE" as a release note in your PR description:

    ```release-note
    NONE
    ```
-->


tekton-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **sthaha**
You can assign the PR to them by writing `/assign @sthaha` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=tektoncd%2Foperator).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/tektoncd/operator/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["sthaha"]} -->
tekton-robot: The following is the coverage report on the affected files.
Say `/test pull-tekton-operator-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/reconciler/common/common.go](https://storage.cloud.google.com/tekton-prow/pr-logs/pull/tektoncd_operator/153/pull-tekton-operator-go-coverage/1308041739520446465/artifacts/line-cov.html#file0) | Do not exist | 0.0% | 
[pkg/reconciler/common/deployments.go](https://storage.cloud.google.com/tekton-prow/pr-logs/pull/tektoncd_operator/153/pull-tekton-operator-go-coverage/1308041739520446465/artifacts/line-cov.html#file1) | Do not exist | 0.0% | 
[pkg/reconciler/common/extensions.go](https://storage.cloud.google.com/tekton-prow/pr-logs/pull/tektoncd_operator/153/pull-tekton-operator-go-coverage/1308041739520446465/artifacts/line-cov.html#file2) | Do not exist | 100.0% | 
[pkg/reconciler/common/install.go](https://storage.cloud.google.com/tekton-prow/pr-logs/pull/tektoncd_operator/153/pull-tekton-operator-go-coverage/1308041739520446465/artifacts/line-cov.html#file3) | Do not exist | 0.0% | 
[pkg/reconciler/common/releases.go](https://storage.cloud.google.com/tekton-prow/pr-logs/pull/tektoncd_operator/153/pull-tekton-operator-go-coverage/1308041739520446465/artifacts/line-cov.html#file4) | Do not exist | 0.0% | 
[pkg/reconciler/common/stages.go](https://storage.cloud.google.com/tekton-prow/pr-logs/pull/tektoncd_operator/153/pull-tekton-operator-go-coverage/1308041739520446465/artifacts/line-cov.html#file5) | Do not exist | 0.0% | 
[pkg/reconciler/common/transformers.go](https://storage.cloud.google.com/tekton-prow/pr-logs/pull/tektoncd_operator/153/pull-tekton-operator-go-coverage/1308041739520446465/artifacts/line-cov.html#file6) | Do not exist | 0.0% | 

nikhil-thomas: This looks good.

I have `question, should make the TektonTrigger reconciler respont to only on reosurce name(eg: https://github.com/tektoncd/operator/blob/7deb4ca47e723c32e58991ddf1b8bb95f839c252/pkg/reconciler/tektonpipeline/tektonpipeline.go#L96)

That will make sure that only one instance of triggers is installed on any cluster
savitaashture: > This looks good.
> 
> I have `question, should make the TektonTrigger reconciler respont to only on reosurce name(eg:
> 
> https://github.com/tektoncd/operator/blob/7deb4ca47e723c32e58991ddf1b8bb95f839c252/pkg/reconciler/tektonpipeline/tektonpipeline.go#L96
> 
> )
> That will make sure that only one instance of triggers is installed on any cluster

ya that also make sense

I din't add name check to avoid hard rule

But yes i will add that change :+1: 
nikhil-thomas: > I din't add name check to avoid hard rule
i appreciate it.
But let us keep the hard rule at present. 
Let us discuss this more in a work group meeting  before we make an official release. :)
tekton-robot: The following is the coverage report on the affected files.
Say `/test pull-tekton-operator-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/reconciler/common/common.go](https://storage.cloud.google.com/tekton-prow/pr-logs/pull/tektoncd_operator/153/pull-tekton-operator-go-coverage/1309373285108027395/artifacts/line-cov.html#file0) | Do not exist | 0.0% | 

vdemeester: /lgtm
nikhil-thomas: /lgtm
vdemeester: /approve
/meow
tekton-robot: @vdemeester: ![cat image](https://cdn2.thecatapi.com/images/MTU1Nzc3MQ.jpg)

<details>

In response to [this](https://github.com/tektoncd/operator/pull/153#issuecomment-698857625):

>/approve
>/meow


Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
tekton-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/tektoncd/operator/pull/153#issuecomment-698857625" title="Approved">vdemeester</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=tektoncd%2Foperator).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/tektoncd/operator/blob/main/OWNERS)~~ [vdemeester]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/cmd/operator/kodata/resources/addons/dashboard/openshift-v0.2.0/release.yaml b/cmd/operator/kodata/resources/addons/dashboard/openshift-v0.2.0/release.yaml
deleted file mode 100644
index 72ea588ca..000000000
--- a/cmd/operator/kodata/resources/addons/dashboard/openshift-v0.2.0/release.yaml
+++ /dev/null
@@ -1,283 +0,0 @@
-# ------------------- Dashboard Service Account ------------------- #
-
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  labels:
-    app: tekton-dashboard
-  name: tekton-dashboard
-  namespace: tekton-pipelines
-  annotations:
-    serviceaccounts.openshift.io/oauth-redirectreference.primary: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"tekton-dashboard"}}'
----
-# ------------------- Extension Resource Definition ------------------- #
-apiVersion: apiextensions.k8s.io/v1beta1
-kind: CustomResourceDefinition
-metadata:
-  name: extensions.dashboard.tekton.dev
-spec:
-  group: dashboard.tekton.dev
-  names:
-    kind: Extension
-    plural: extensions
-    categories:
-      - all
-      - tekton-pipelines
-  scope: Namespaced
-  # Opt into the status subresource so metadata.generation
-  # starts to increment
-  subresources:
-    status: {}
-  version: v1alpha1
----
-# ------------------- Dashboard Role & Role Binding ------------------- #
-kind: ClusterRole
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
-  name: tekton-dashboard-minimal
-  namespace: tekton-pipelines
-rules:
-  - apiGroups: ["security.openshift.io"]
-    resources: ["securitycontextconstraints"]
-    verbs: ["use"]
-  - apiGroups: ["route.openshift.io"]
-    resources: ["routes"]
-    verbs: ["get", "list"]
-  - apiGroups: ["extensions", "apps"]
-    resources: ["ingresses"]
-    verbs: ["get", "list", "watch"]
-  - apiGroups: [""]
-    resources: ["serviceaccounts"]
-    verbs: ["get", "list", "update", "patch"]
-  - apiGroups: [""]
-    resources: ["pods", "services"]
-    verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
-  - apiGroups: [""]
-    resources: ["pods/log", "namespaces", "events"]
-    verbs: ["get", "list", "watch"]
-  - apiGroups: [""]
-    resources: ["secrets", "configmaps"]
-    verbs: ["get", "list", "create", "update", "watch", "delete"]
-  - apiGroups: ["extensions", "apps"]
-    resources: ["deployments"]
-    verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
-  - apiGroups: ["tekton.dev"]
-    resources: ["tasks", "clustertasks", "taskruns", "pipelines", "pipelineruns", "pipelineresources"]
-    verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
-  - apiGroups: ["tekton.dev"]
-    resources: ["taskruns/finalizers", "pipelineruns/finalizers"]
-    verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
-  - apiGroups: ["tekton.dev"]
-    resources: ["tasks/status", "clustertasks/status", "taskruns/status", "pipelines/status", "pipelineruns/status"]
-    verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
-  - apiGroups: ["dashboard.tekton.dev"]
-    resources: ["extensions"]
-    verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
----
-
-# ------------------- Dashboard Role & Role Binding ------------------- #
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: tekton-dashboard-minimal
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: tekton-pipelines
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-minimal
----
-# ------------------- Dashboard Deployment ------------------- #
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: tekton-dashboard
-  namespace: tekton-pipelines
-  labels:
-    app: tekton-dashboard
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: tekton-dashboard
-  template:
-    metadata:
-      name: tekton-dashboard
-      labels:
-        app: tekton-dashboard
-    spec:
-      containers:
-      - name: oauth-proxy
-        image: openshift/oauth-proxy:latest@sha256:6bc1759a3202b4614739f12441461e344907f6b3f758c34314284debe36d4e15
-        imagePullPolicy: IfNotPresent
-        ports:
-        - containerPort: 8443
-          name: public
-        args:
-        - --https-address=:8443
-        - --provider=openshift
-        - --openshift-service-account=tekton-dashboard
-        - --upstream=http://localhost:9097
-        - --tls-cert=/etc/tls/private/tls.crt
-        - --tls-key=/etc/tls/private/tls.key
-        - --cookie-secret=SECRET
-        - --skip-auth-regex=^/v1/extensions/.*\.js
-        volumeMounts:
-        - mountPath: /etc/tls/private
-          name: proxy-tls
-      - name: tekton-dashboard
-        image: gcr.io/tekton-releases/github.com/tektoncd/dashboard/cmd/dashboard@sha256:d6d1572aff2036707339fdcdf6a89e2850750a1ac643cd033ecdba4fbe4c3f20
-        ports:
-        - containerPort: 9097
-        livenessProbe:
-          httpGet:
-            path: /health
-            port: 9097
-        readinessProbe:
-          httpGet:
-            path: /readiness
-            port: 9097
-        resources:
-        env:
-        - name: PORT
-          value: "9097"
-        - name: WEB_RESOURCES_DIR
-          value: /var/run/ko/web
-        - name: PIPELINE_RUN_SERVICE_ACCOUNT
-          value: ""
-        - name: INSTALLED_NAMESPACE
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
-      serviceAccountName: tekton-dashboard
-      volumes:
-      - name: proxy-tls
-        secret:
-          secretName: proxy-tls
----
-# ------------------- Dashboard Route ------------------- #
-apiVersion: route.openshift.io/v1
-kind: Route
-metadata:
-  name: tekton-dashboard
-  namespace: tekton-pipelines
-spec:
-  to:
-    kind: Service
-    name: tekton-dashboard
-  tls:
-    termination: Reencrypt
-    insecureEdgeTerminationPolicy: Redirect
----
-# ------------------- Dashboard Service ------------------- #
-kind: Service
-apiVersion: v1
-metadata:
-  name: tekton-dashboard
-  namespace: tekton-pipelines
-  labels:
-    app: tekton-dashboard
-  annotations:
-    service.alpha.openshift.io/serving-cert-secret-name: proxy-tls
-spec:
-  ports:
-    - name: http
-      protocol: TCP
-      port: 443
-      targetPort: 8443
-  selector:
-    app: tekton-dashboard
----
-# ------- Insecure Dashboard Service - Do Not Expose ------ #
-kind: Service
-apiVersion: v1
-metadata:
-  name: tekton-dashboard-internal
-  namespace: tekton-pipelines
-  labels:
-    app: tekton-dashboard-internal
-spec:
-  ports:
-    - name: http
-      protocol: TCP
-      port: 9097
-      targetPort: 9097
-  selector:
-    app: tekton-dashboard
----
-# ------------------- Pipeline0 --------------------------- #
-apiVersion: tekton.dev/v1alpha1
-kind: Pipeline
-metadata:
-  name: pipeline0
-  namespace: tekton-pipelines
-spec:
-  resources:
-  - name: git-source
-    type: git
-  params:
-  - name: pathToResourceFiles
-    description: The path to the resource files to apply
-    default: /workspace/git-source
-    type: string
-  - name: apply-directory
-    description: The directory from which resources are to be applied
-    default: "."
-    type: string
-  - name: target-namespace
-    description: The namespace in which to create the resources being imported
-    default: tekton-pipelines
-    type: string
-  tasks:
-  - name: pipeline0-task
-    taskRef:
-      name: pipeline0-task
-    params:
-    - name: pathToResourceFiles
-      value: $(params.pathToResourceFiles)
-    - name: apply-directory
-      value: $(params.apply-directory)
-    - name: target-namespace
-      value: $(params.target-namespace)
-    resources:
-      inputs:
-      - name: git-source
-        resource: git-source
----
-# ------------------- Pipeline0 task ---------------------- #
-apiVersion: tekton.dev/v1alpha1
-kind: Task
-metadata:
-  name: pipeline0-task
-  namespace: tekton-pipelines
-spec:
-  inputs:
-    resources:
-    - name: git-source
-      type: git
-    params:
-    - name: pathToResourceFiles
-      description: The path to the resource files to apply
-      default: /workspace/git-source
-      type: string
-    - name: apply-directory
-      description: The directory from which resources are to be applied
-      default: "."
-      type: string
-    - name: target-namespace
-      description: The namespace in which to create the resources being imported
-      default: tekton-pipelines
-      type: string
-  steps:
-  - name: kubectl-apply
-    image: lachlanevenson/k8s-kubectl@sha256:7f50d26d77ba7bd15ef9c0ebd29db88cf81d095287b63bd1e4054cdfb0d52417
-    command:
-    - kubectl
-    args:
-    - apply
-    - -f
-    - $(inputs.params.pathToResourceFiles)/$(inputs.params.apply-directory) 
-    - -n
-    - $(inputs.params.target-namespace)
diff --git a/cmd/operator/kodata/resources/addons/dashboard/openshift-v0.8.2/openshift-tekton-dashboard-release.yaml b/cmd/operator/kodata/resources/addons/dashboard/openshift-v0.8.2/openshift-tekton-dashboard-release.yaml
deleted file mode 100644
index 05ba9bce5..000000000
--- a/cmd/operator/kodata/resources/addons/dashboard/openshift-v0.8.2/openshift-tekton-dashboard-release.yaml
+++ /dev/null
@@ -1,578 +0,0 @@
-apiVersion: apiextensions.k8s.io/v1beta1
-kind: CustomResourceDefinition
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: extensions.dashboard.tekton.dev
-spec:
-  additionalPrinterColumns:
-  - JSONPath: .spec.apiVersion
-    name: API version
-    type: string
-  - JSONPath: .spec.name
-    name: Kind
-    type: string
-  - JSONPath: .spec.displayname
-    name: Display name
-    type: string
-  - JSONPath: .metadata.creationTimestamp
-    name: Age
-    type: date
-  group: dashboard.tekton.dev
-  names:
-    categories:
-    - tekton
-    - tekton-dashboard
-    kind: Extension
-    plural: extensions
-    shortNames:
-    - ext
-    - exts
-  preserveUnknownFields: false
-  scope: Namespaced
-  subresources:
-    status: {}
-  validation:
-    openAPIV3Schema:
-      type: object
-      x-kubernetes-preserve-unknown-fields: true
-  versions:
-  - name: v1alpha1
-    served: true
-    storage: true
----
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  annotations:
-    serviceaccounts.openshift.io/oauth-redirectreference.primary: '{"kind":"OAuthRedirectReference","apiVersion":"v1",
-      "reference":{"kind":"Route","name":"tekton-dashboard"}}'
-  labels:
-    app: tekton-dashboard
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard
-  namespace: openshift-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-backend
-rules:
-- apiGroups:
-  - apiextensions.k8s.io
-  resources:
-  - customresourcedefinitions
-  verbs:
-  - get
-  - list
-- apiGroups:
-  - security.openshift.io
-  resources:
-  - securitycontextconstraints
-  verbs:
-  - use
-- apiGroups:
-  - tekton.dev
-  resources:
-  - clustertasks
-  - clustertasks/status
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - clustertriggerbindings
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - dashboard.tekton.dev
-  resources:
-  - extensions
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - clustertasks
-  - clustertasks/status
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - clustertriggerbindings
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-  - add
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-dashboard
-rules:
-- apiGroups:
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - list
-- apiGroups:
-  - route.openshift.io
-  resources:
-  - routes
-  verbs:
-  - get
-  - list
-- apiGroups:
-  - extensions
-  - apps
-  resources:
-  - ingresses
-  verbs:
-  - get
-  - list
-  - watch
----
-aggregationRule:
-  clusterRoleSelectors:
-  - matchLabels:
-      rbac.dashboard.tekton.dev/aggregate-to-dashboard: "true"
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-extensions
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-pipelines
-rules:
-- apiGroups:
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - list
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-tenant
-rules:
-- apiGroups:
-  - ""
-  resources:
-  - services
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - dashboard.tekton.dev
-  resources:
-  - extensions
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - serviceaccounts
-  - pods/log
-  - namespaces
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks
-  - taskruns
-  - pipelines
-  - pipelineruns
-  - pipelineresources
-  - conditions
-  - tasks/status
-  - taskruns/status
-  - pipelines/status
-  - pipelineruns/status
-  - taskruns/finalizers
-  - pipelineruns/finalizers
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - eventlisteners
-  - triggerbindings
-  - triggertemplates
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - serviceaccounts
-  verbs:
-  - update
-  - patch
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - delete
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks
-  - taskruns
-  - pipelines
-  - pipelineruns
-  - pipelineresources
-  - conditions
-  - taskruns/finalizers
-  - pipelineruns/finalizers
-  - tasks/status
-  - taskruns/status
-  - pipelines/status
-  - pipelineruns/status
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - eventlisteners
-  - triggerbindings
-  - triggertemplates
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-  - add
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-triggers
-rules:
-- apiGroups:
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - list
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-backend
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-backend
-subjects:
-- kind: ServiceAccount
-  name: tekton-dashboard
-  namespace: openshift-pipelines
----
-apiVersion: v1
-kind: Service
-metadata:
-  annotations:
-    service.alpha.openshift.io/serving-cert-secret-name: proxy-tls
-  labels:
-    app: tekton-dashboard
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/name: dashboard
-    app.kubernetes.io/part-of: tekton-dashboard
-    app.kubernetes.io/version: v0.8.2
-    dashboard.tekton.dev/release: v0.8.2
-    version: v0.8.2
-  name: tekton-dashboard
-  namespace: openshift-pipelines
-spec:
-  ports:
-  - name: http
-    port: 443
-    protocol: TCP
-    targetPort: 8443
-  selector:
-    app: tekton-dashboard
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/name: dashboard
-    app.kubernetes.io/part-of: tekton-dashboard
----
-apiVersion: v1
-kind: Service
-metadata:
-  labels:
-    app: tekton-dashboard-internal
-    dashboard.tekton.dev/release: v0.8.2
-    version: v0.8.2
-  name: tekton-dashboard-internal
-  namespace: openshift-pipelines
-spec:
-  ports:
-  - name: http
-    port: 9097
-    protocol: TCP
-    targetPort: 9097
-  selector:
-    app: tekton-dashboard
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  labels:
-    app: tekton-dashboard
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/name: dashboard
-    app.kubernetes.io/part-of: tekton-dashboard
-    app.kubernetes.io/version: v0.8.2
-    dashboard.tekton.dev/release: v0.8.2
-    version: v0.8.2
-  name: tekton-dashboard
-  namespace: openshift-pipelines
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app.kubernetes.io/component: dashboard
-      app.kubernetes.io/instance: default
-      app.kubernetes.io/name: dashboard
-      app.kubernetes.io/part-of: tekton-dashboard
-  template:
-    metadata:
-      labels:
-        app: tekton-dashboard
-        app.kubernetes.io/component: dashboard
-        app.kubernetes.io/instance: default
-        app.kubernetes.io/name: dashboard
-        app.kubernetes.io/part-of: tekton-dashboard
-        app.kubernetes.io/version: v0.8.2
-      name: tekton-dashboard
-    spec:
-      containers:
-      - args:
-        - --port=9097
-        - --logout-url=/oauth/sign_out
-        - --pipelines-namespace=openshift-pipelines
-        - --triggers-namespace=openshift-pipelines
-        - --read-only=false
-        - --csrf-secure-cookie=false
-        - --log-level=info
-        - --log-format=json
-        - --namespace=
-        - --openshift=true
-        - --stream-logs=false
-        env:
-        - name: INSTALLED_NAMESPACE
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
-        image: gcr.io/tekton-releases/github.com/tektoncd/dashboard/cmd/dashboard@sha256:4175d6237de3f85d4cfa2e331e244f0b1c20776455af10dcccdb81a3f9ebb012
-        livenessProbe:
-          httpGet:
-            path: /health
-            port: 9097
-        name: tekton-dashboard
-        ports:
-        - containerPort: 9097
-        readinessProbe:
-          httpGet:
-            path: /readiness
-            port: 9097
-      - args:
-        - --https-address=:8443
-        - --provider=openshift
-        - --skip-provider-button=true
-        - --openshift-service-account=tekton-dashboard
-        - --upstream=http://localhost:9097
-        - --tls-cert=/etc/tls/private/tls.crt
-        - --tls-key=/etc/tls/private/tls.key
-        - --cookie-secret=SECRET
-        - --skip-auth-regex=^/v1/extensions/.*\.js
-        image: openshift/oauth-proxy@sha256:6bc1759a3202b4614739f12441461e344907f6b3f758c34314284debe36d4e15
-        imagePullPolicy: IfNotPresent
-        name: oauth-proxy
-        ports:
-        - containerPort: 8443
-          name: public
-        securityContext:
-          runAsUser: 1000
-        volumeMounts:
-        - mountPath: /etc/tls/private
-          name: proxy-tls
-      securityContext:
-        runAsNonRoot: true
-      serviceAccountName: tekton-dashboard
-      volumes:
-      - name: proxy-tls
-        secret:
-          secretName: proxy-tls
----
-apiVersion: route.openshift.io/v1
-kind: Route
-metadata:
-  name: tekton-dashboard
-  namespace: openshift-pipelines
-spec:
-  tls:
-    insecureEdgeTerminationPolicy: Redirect
-    termination: Reencrypt
-  to:
-    kind: Service
-    name: tekton-dashboard
-
----
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-pipelines
-  namespace: openshift-pipelines
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-pipelines
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: openshift-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-dashboard
-  namespace: openshift-pipelines
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-dashboard
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: openshift-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-triggers
-  namespace: openshift-pipelines
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-triggers
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: openshift-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-tenant
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-tenant
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: openshift-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-extensions
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-extensions
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: openshift-pipelines
diff --git a/cmd/operator/kodata/resources/addons/dashboard/v0.8.2/tekton-dashboard-release.yaml b/cmd/operator/kodata/resources/addons/dashboard/v0.8.2/tekton-dashboard-release.yaml
deleted file mode 100644
index 0d419b8d4..000000000
--- a/cmd/operator/kodata/resources/addons/dashboard/v0.8.2/tekton-dashboard-release.yaml
+++ /dev/null
@@ -1,516 +0,0 @@
-apiVersion: apiextensions.k8s.io/v1beta1
-kind: CustomResourceDefinition
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: extensions.dashboard.tekton.dev
-spec:
-  additionalPrinterColumns:
-  - JSONPath: .spec.apiVersion
-    name: API version
-    type: string
-  - JSONPath: .spec.name
-    name: Kind
-    type: string
-  - JSONPath: .spec.displayname
-    name: Display name
-    type: string
-  - JSONPath: .metadata.creationTimestamp
-    name: Age
-    type: date
-  group: dashboard.tekton.dev
-  names:
-    categories:
-    - tekton
-    - tekton-dashboard
-    kind: Extension
-    plural: extensions
-    shortNames:
-    - ext
-    - exts
-  preserveUnknownFields: false
-  scope: Namespaced
-  subresources:
-    status: {}
-  validation:
-    openAPIV3Schema:
-      type: object
-      x-kubernetes-preserve-unknown-fields: true
-  versions:
-  - name: v1alpha1
-    served: true
-    storage: true
----
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard
-  namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-backend
-rules:
-- apiGroups:
-  - apiextensions.k8s.io
-  resources:
-  - customresourcedefinitions
-  verbs:
-  - get
-  - list
-- apiGroups:
-  - security.openshift.io
-  resources:
-  - securitycontextconstraints
-  verbs:
-  - use
-- apiGroups:
-  - tekton.dev
-  resources:
-  - clustertasks
-  - clustertasks/status
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - clustertriggerbindings
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - dashboard.tekton.dev
-  resources:
-  - extensions
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - clustertasks
-  - clustertasks/status
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - clustertriggerbindings
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-  - add
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-dashboard
-rules:
-- apiGroups:
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - list
-- apiGroups:
-  - route.openshift.io
-  resources:
-  - routes
-  verbs:
-  - get
-  - list
-- apiGroups:
-  - extensions
-  - apps
-  resources:
-  - ingresses
-  verbs:
-  - get
-  - list
-  - watch
----
-aggregationRule:
-  clusterRoleSelectors:
-  - matchLabels:
-      rbac.dashboard.tekton.dev/aggregate-to-dashboard: "true"
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-extensions
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-pipelines
-rules:
-- apiGroups:
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - list
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-tenant
-rules:
-- apiGroups:
-  - ""
-  resources:
-  - services
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - dashboard.tekton.dev
-  resources:
-  - extensions
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - serviceaccounts
-  - pods/log
-  - namespaces
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks
-  - taskruns
-  - pipelines
-  - pipelineruns
-  - pipelineresources
-  - conditions
-  - tasks/status
-  - taskruns/status
-  - pipelines/status
-  - pipelineruns/status
-  - taskruns/finalizers
-  - pipelineruns/finalizers
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - eventlisteners
-  - triggerbindings
-  - triggertemplates
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - serviceaccounts
-  verbs:
-  - update
-  - patch
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - delete
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks
-  - taskruns
-  - pipelines
-  - pipelineruns
-  - pipelineresources
-  - conditions
-  - taskruns/finalizers
-  - pipelineruns/finalizers
-  - tasks/status
-  - taskruns/status
-  - pipelines/status
-  - pipelineruns/status
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - eventlisteners
-  - triggerbindings
-  - triggertemplates
-  verbs:
-  - create
-  - update
-  - delete
-  - patch
-  - add
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-triggers
-rules:
-- apiGroups:
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - list
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-backend
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-backend
-subjects:
-- kind: ServiceAccount
-  name: tekton-dashboard
-  namespace: tekton-pipelines
----
-apiVersion: v1
-kind: Service
-metadata:
-  labels:
-    app: tekton-dashboard
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/name: dashboard
-    app.kubernetes.io/part-of: tekton-dashboard
-    app.kubernetes.io/version: v0.8.2
-    dashboard.tekton.dev/release: v0.8.2
-    version: v0.8.2
-  name: tekton-dashboard
-  namespace: tekton-pipelines
-spec:
-  ports:
-  - name: http
-    port: 9097
-    protocol: TCP
-    targetPort: 9097
-  selector:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/name: dashboard
-    app.kubernetes.io/part-of: tekton-dashboard
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  labels:
-    app: tekton-dashboard
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/name: dashboard
-    app.kubernetes.io/part-of: tekton-dashboard
-    app.kubernetes.io/version: v0.8.2
-    dashboard.tekton.dev/release: v0.8.2
-    version: v0.8.2
-  name: tekton-dashboard
-  namespace: tekton-pipelines
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app.kubernetes.io/component: dashboard
-      app.kubernetes.io/instance: default
-      app.kubernetes.io/name: dashboard
-      app.kubernetes.io/part-of: tekton-dashboard
-  template:
-    metadata:
-      labels:
-        app: tekton-dashboard
-        app.kubernetes.io/component: dashboard
-        app.kubernetes.io/instance: default
-        app.kubernetes.io/name: dashboard
-        app.kubernetes.io/part-of: tekton-dashboard
-        app.kubernetes.io/version: v0.8.2
-      name: tekton-dashboard
-    spec:
-      containers:
-      - args:
-        - --port=9097
-        - --logout-url=
-        - --pipelines-namespace=tekton-pipelines
-        - --triggers-namespace=tekton-pipelines
-        - --read-only=false
-        - --csrf-secure-cookie=false
-        - --log-level=info
-        - --log-format=json
-        - --namespace=
-        - --openshift=false
-        - --stream-logs=false
-        env:
-        - name: INSTALLED_NAMESPACE
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
-        image: gcr.io/tekton-releases/github.com/tektoncd/dashboard/cmd/dashboard@sha256:4175d6237de3f85d4cfa2e331e244f0b1c20776455af10dcccdb81a3f9ebb012
-        livenessProbe:
-          httpGet:
-            path: /health
-            port: 9097
-        name: tekton-dashboard
-        ports:
-        - containerPort: 9097
-        readinessProbe:
-          httpGet:
-            path: /readiness
-            port: 9097
-      securityContext:
-        runAsNonRoot: true
-      serviceAccountName: tekton-dashboard
-      volumes: []
-
----
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-pipelines
-  namespace: tekton-pipelines
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-pipelines
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-dashboard
-  namespace: tekton-pipelines
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-dashboard
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-triggers
-  namespace: tekton-pipelines
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-triggers
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-tenant
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-tenant
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  labels:
-    app.kubernetes.io/component: dashboard
-    app.kubernetes.io/instance: default
-    app.kubernetes.io/part-of: tekton-dashboard
-  name: tekton-dashboard-extensions
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-dashboard-extensions
-subjects:
-  - kind: ServiceAccount
-    name: tekton-dashboard
-    namespace: tekton-pipelines
diff --git a/cmd/operator/kodata/resources/addons/extensionwebhooks/openshift-v0.2.0/release.yaml b/cmd/operator/kodata/resources/addons/extensionwebhooks/openshift-v0.2.0/release.yaml
deleted file mode 100644
index faadd8ba7..000000000
--- a/cmd/operator/kodata/resources/addons/extensionwebhooks/openshift-v0.2.0/release.yaml
+++ /dev/null
@@ -1,401 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  labels:
-    app: tekton-webhooks-extension
-  name: tekton-webhooks-extension
-  namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: tekton-webhooks-extension-minimal
-  namespace: tekton-pipelines
-rules:
-- apiGroups:
-  - security.openshift.io
-  resources:
-  - securitycontextconstraints
-  verbs:
-  - use
-- apiGroups:
-  - ""
-  resources:
-  - serviceaccounts
-  verbs:
-  - get
-  - list
-  - patch
-  - update
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - pods
-  - services
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - pods/log
-  - namespaces
-  - events
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  - configmaps
-  verbs:
-  - get
-  - list
-  - create
-  - delete
-  - update
-  - watch
-- apiGroups:
-  - extensions
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks
-  - clustertasks
-  - taskruns
-  - pipelines
-  - pipelineruns
-  - pipelineresources
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - taskruns/finalizers
-  - pipelineruns/finalizers
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks/status
-  - clustertasks/status
-  - taskruns/status
-  - pipelines/status
-  - pipelineruns/status
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - sources.eventing.knative.dev
-  resources:
-  - githubsources
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: tekton-webhooks-extension-minimal
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-webhooks-extension-minimal
-subjects:
-- kind: ServiceAccount
-  name: tekton-webhooks-extension
-  namespace: tekton-pipelines
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  labels:
-    app: webhooks-extension
-  name: webhooks-extension
-  namespace: tekton-pipelines
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: webhooks-extension
-  template:
-    metadata:
-      labels:
-        app: webhooks-extension
-    spec:
-      containers:
-      - env:
-        - name: PORT
-          value: "8080"
-        - name: INSTALLED_NAMESPACE
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
-        - name: DOCKER_REGISTRY_LOCATION
-          value: DOCKER_REPO
-        - name: WEB_RESOURCES_DIR
-          value: web
-        image: gcr.io/tekton-releases/github.com/tektoncd/experimental/webhooks-extension/cmd/extension@sha256:4b227d0cb2ff9f2bd5ac2746a51bec8eff55d29cfd4ae60afa8dd68d6c4fe2f0
-        imagePullPolicy: Always
-        livenessProbe:
-          httpGet:
-            path: /liveness
-            port: 8080
-        name: webhooks-extension
-        ports:
-        - containerPort: 8080
-        readinessProbe:
-          httpGet:
-            path: /readiness
-            port: 8080
-      serviceAccountName: tekton-webhooks-extension
-
----
-apiVersion: v1
-kind: Service
-metadata:
-  annotations:
-    tekton-dashboard-bundle-location: web/extension.9b61e1e9.js
-    tekton-dashboard-display-name: Webhooks
-    tekton-dashboard-endpoints: webhooks.web
-  labels:
-    app: webhooks-extension
-    tekton-dashboard-extension: "true"
-  name: webhooks-extension
-  namespace: tekton-pipelines
-spec:
-  ports:
-  - port: 8080
-    targetPort: 8080
-  selector:
-    app: webhooks-extension
-  type: NodePort
-
----
-apiVersion: serving.knative.dev/v1alpha1
-kind: Service
-metadata:
-  labels:
-    app: webhooks-extension-sink
-  name: webhooks-extension-sink
-  namespace: tekton-pipelines
-spec:
-  template:
-    spec:
-      containers:
-      - env:
-        - name: INSTALLED_NAMESPACE
-          value: tekton-pipelines
-        - name: SERVICEACCOUNT
-          value: tekton-webhooks-extension
-        - name: PLATFORM
-          value: openshift
-        image: gcr.io/tekton-releases/github.com/tektoncd/experimental/webhooks-extension/cmd/sink@sha256:b7ef6c779aa02a395459a08facd6d581e350179ecdf447681b207515ddf521b9
-        imagePullPolicy: Always
-        livenessProbe:
-          httpGet:
-            path: /liveness
-        ports:
-        - containerPort: 8080
-        readinessProbe:
-          httpGet:
-            path: /readiness
-      serviceAccountName: tekton-webhooks-extension
-
----
-# Monitor the pipelinerun status and update the pull request
-apiVersion: tekton.dev/v1alpha1
-kind: Task
-metadata:
-  name: monitor-result-task
-  namespace: tekton-pipelines
-spec:
-  inputs:
-    resources:
-      - name: pull-request
-        type: pullRequest
-    params:
-      - name: pipelineruns
-        description: The name of the pipelineruns (and their namespace) to be monitored in format pipelinerun1:namespace1, pipelinerun2:namespace2
-        default: default pipelinerun
-        type: string
-      - name: commentsuccess
-        description: The text of the success comment
-        default: "Success"
-        type: string
-      - name: commentfailure
-        description: The text of the failure comment
-        default: "Failed"
-        type: string
-      - name: dashboard-url
-        description: The URL to the pipelineruns page of the dashboard
-        default: "http://localhost:9097/"
-        type: string
-      # This can be deleted after pending status change issue is resolved, that being that AFAIK the pull request resource only modifies
-      # status once everything is complete, so we can only modify status via the pull request resource once.  To get around this we hit
-      # the github status URL to set the status into pending and use this secret to during that request.  
-      - name: secret
-        description: The secret containing the access token to access github
-        type: string
-      # Up to here
-  outputs:
-    resources:
-      - name: pull-request
-        type: pullRequest
-  steps:
-  - name: check
-    image: maiwj/kubernetes-python-client@sha256:74a868a0dff5c8ada64472db3efd09d205d4f877d14d2d3226511adbb25cfea3
-    env:
-      - name: PIPELINERUNS
-        value: $(inputs.params.pipelineruns)
-      - name: COMMENT_SUCCESS
-        value: $(inputs.params.commentsuccess)
-      - name: COMMENT_FAILURE
-        value: $(inputs.params.commentfailure)
-      - name: URL
-        value: $(inputs.params.dashboard-url)
-      # This can be deleted after any fix to the above mentioned pending status change
-      - name: GITHUBTOKEN
-        valueFrom:
-          secretKeyRef:
-            key: accessToken
-            name: $(inputs.params.secret)
-      # Up to here
-    command: ["/bin/bash"]
-    args:
-    - -ce
-    - |
-      set -e
-      cat <<EOF | python
-      import time, os, json, requests, pprint
-      from kubernetes import client, config
-      config.load_incluster_config()
-      api_instance = client.CustomObjectsApi(client.ApiClient(client.Configuration()))
-      gitPRcontext = "Tekton"
-      gitPRurl = ""
-      # This is the code thats puts the pullrequest into pending status, this is code to rip out later if there
-      # is a fix to the above mentioned update status to pending issue.
-      with open("/workspace/pull-request/github/pr.json") as fp:
-        rawdata = json.load(fp)
-        statusurl = rawdata['statuses_url']
-      pendingData = {
-        "state": "pending",
-        "description": "pipelines in progress",
-        "target_url": "",
-        "context": "Tekton"
-      }
-      print("Setting status to pending with URL : " + statusurl)
-      resp = requests.post(statusurl, json.dumps(pendingData), headers = {'Content-Type': 'application/json', 'Authorization': "Bearer $GITHUBTOKEN"})
-      print(resp)
-      # End of code to replace
-      if not "$URL".startswith("http"):
-        pipelineRunURLPrefix = "http://" + "$URL"
-      else:
-        pipelineRunURLPrefix = "$URL"
-      runsToCheck = "$PIPELINERUNS".split(",")
-      runsPassed = []
-      runsFailed = []
-      runsIncomplete = []
-      failed = 0
-      i = range(180)
-      for x in i:
-          time.sleep( 10 )
-          if len(runsToCheck) > 0:
-            for entry in runsToCheck:
-              pr, namespace, pipeline = entry.split(":")
-              pr = pr.strip()
-              namespace = namespace.strip()
-              pipeline = pipeline.strip()
-              link = pipelineRunURLPrefix + "/#/namespaces/" + namespace + "/pipelines/" + pipeline + "/runs/" + pr
-              print("Checking pipelinerun " + pr + " in namespace " + namespace)
-              output = api_instance.get_namespaced_custom_object("tekton.dev", "v1alpha1", namespace, "pipelineruns", pr)
-              if output["status"]["conditions"][0]["status"] == u'True' and output["status"]["conditions"][0]["type"] == u'Succeeded':
-                print("Success - pipelinerun " + pr + " in namespace " + namespace)
-                runsToCheck.remove(entry)
-                runsPassed.append("[**$COMMENT_SUCCESS**](" + link + ") | " + pipeline + " | " +  pr + " | " + namespace)
-              if output["status"]["conditions"][0]["status"] == u'False' and output["status"]["conditions"][0]["type"] == u'Succeeded':
-                failed =+ 1
-                print("Failed - pipelinerun " + pr + " in namespace " + namespace)
-                runsToCheck.remove(entry)
-                runsFailed.append("[**$COMMENT_FAILURE**](" + link + ") | " + pipeline + " | " + pr + " | " + namespace)
-          else:
-            break
-      gitPRdescription = "All pipelines succeeded!"
-      gitPRcode = "success"
-      if failed > 0:
-        gitPRdescription = str(failed) + " pipeline(s) failed!"
-        gitPRcode = "failure"
-      if len(runsToCheck) > 0:
-        print("Some pipelineruns had not completed when the monitor reached its timeout")
-        gitPRdescription = "timed out monitoring pipeline runs"
-        gitPRcode = "error"
-        for entry in runsToCheck:
-          pr, namespace, pipeline = entry.split(":")
-          pr = pr.strip()
-          namespace = namespace.strip()
-          pipeline = pipeline.strip()
-          print("Still Running - pipelinerun " + pr + " in namespace " + namespace)
-          link = pipelineRunURLPrefix + "/#/namespaces/" + namespace + "/pipelines/" + pipeline + "/runs/" + pr
-          runsIncomplete.append("[**??????**](" + link + ") | " + pipeline + " | " + pr + " | " + namespace)
-      results = runsPassed + runsFailed + runsIncomplete
-      comment = ("## Tekton Status Report \n\n"
-                 "Status | Pipeline | PipelineRun | Namespace\n"
-                 ":----- | :------- | :--------------- | :--------\n"
-                 ) + "\n".join(results)
-      handle = open("/workspace/pull-request/comments/newcomment.json", 'w')
-      handle.write(comment)
-      handle.close()
-      if not "$URL".startswith("http"):
-        detailsURL = "http://" + "$URL" + "/#/pipelineruns"
-      else:
-        detailsURL = "$URL" + "/#/pipelineruns"
-      print("Set details url to: " + detailsURL)
-      status = json.dumps(dict(code=gitPRcode, description=gitPRdescription, id=gitPRcontext, url=detailsURL))
-      handle = open("/workspace/pull-request/status/newstatus.json", 'w')
-      handle.write(status)
-      handle.close()
-      EOF
----
-
diff --git a/cmd/operator/kodata/resources/addons/extensionwebhooks/v0.2.0/release.yaml b/cmd/operator/kodata/resources/addons/extensionwebhooks/v0.2.0/release.yaml
deleted file mode 100644
index d96d8b478..000000000
--- a/cmd/operator/kodata/resources/addons/extensionwebhooks/v0.2.0/release.yaml
+++ /dev/null
@@ -1,388 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  labels:
-    app: tekton-webhooks-extension
-  name: tekton-webhooks-extension
-  namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: tekton-webhooks-extension-minimal
-  namespace: tekton-pipelines
-rules:
-- apiGroups:
-  - ""
-  resources:
-  - serviceaccounts
-  verbs:
-  - get
-  - list
-  - patch
-  - update
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - pods
-  - services
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - pods/log
-  - namespaces
-  - events
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  - configmaps
-  verbs:
-  - get
-  - list
-  - create
-  - delete
-  - update
-  - watch
-- apiGroups:
-  - extensions
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks
-  - clustertasks
-  - taskruns
-  - pipelines
-  - pipelineruns
-  - pipelineresources
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - taskruns/finalizers
-  - pipelineruns/finalizers
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks/status
-  - clustertasks/status
-  - taskruns/status
-  - pipelines/status
-  - pipelineruns/status
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - sources.eventing.knative.dev
-  resources:
-  - githubsources
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: tekton-webhooks-extension-minimal
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-webhooks-extension-minimal
-subjects:
-- kind: ServiceAccount
-  name: tekton-webhooks-extension
-  namespace: tekton-pipelines
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  labels:
-    app: webhooks-extension
-  name: webhooks-extension
-  namespace: tekton-pipelines
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: webhooks-extension
-  template:
-    metadata:
-      labels:
-        app: webhooks-extension
-    spec:
-      containers:
-      - env:
-        - name: PORT
-          value: "8080"
-        - name: INSTALLED_NAMESPACE
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
-        - name: DOCKER_REGISTRY_LOCATION
-          value: DOCKER_REPO
-        - name: WEB_RESOURCES_DIR
-          value: web
-        image: gcr.io/tekton-releases/github.com/tektoncd/experimental/webhooks-extension/cmd/extension@sha256:4b227d0cb2ff9f2bd5ac2746a51bec8eff55d29cfd4ae60afa8dd68d6c4fe2f0
-        imagePullPolicy: Always
-        livenessProbe:
-          httpGet:
-            path: /liveness
-            port: 8080
-        name: webhooks-extension
-        ports:
-        - containerPort: 8080
-        readinessProbe:
-          httpGet:
-            path: /readiness
-            port: 8080
-      serviceAccountName: tekton-webhooks-extension
-
----
-apiVersion: v1
-kind: Service
-metadata:
-  annotations:
-    tekton-dashboard-bundle-location: web/extension.9b61e1e9.js
-    tekton-dashboard-display-name: Webhooks
-    tekton-dashboard-endpoints: webhooks.web
-  labels:
-    app: webhooks-extension
-    tekton-dashboard-extension: "true"
-  name: webhooks-extension
-  namespace: tekton-pipelines
-spec:
-  ports:
-  - port: 8080
-    targetPort: 8080
-  selector:
-    app: webhooks-extension
-  type: NodePort
-
----
-apiVersion: serving.knative.dev/v1alpha1
-kind: Service
-metadata:
-  labels:
-    app: webhooks-extension-sink
-  name: webhooks-extension-sink
-  namespace: tekton-pipelines
-spec:
-  template:
-    spec:
-      containers:
-      - env:
-        - name: INSTALLED_NAMESPACE
-          value: tekton-pipelines
-        - name: SERVICEACCOUNT
-          value: tekton-webhooks-extension
-        image: gcr.io/tekton-releases/github.com/tektoncd/experimental/webhooks-extension/cmd/sink@sha256:b7ef6c779aa02a395459a08facd6d581e350179ecdf447681b207515ddf521b9
-        imagePullPolicy: Always
-        livenessProbe:
-          httpGet:
-            path: /liveness
-        ports:
-        - containerPort: 8080
-        readinessProbe:
-          httpGet:
-            path: /readiness
-      serviceAccountName: tekton-webhooks-extension
-
----
-apiVersion: tekton.dev/v1alpha1
-kind: Task
-metadata:
-  name: monitor-result-task
-  namespace: tekton-pipelines
-spec:
-  inputs:
-    params:
-    - default: default pipelinerun
-      description: The name of the pipelineruns (and their namespace) to be monitored
-        in format pipelinerun1:namespace1, pipelinerun2:namespace2
-      name: pipelineruns
-      type: string
-    - default: Success
-      description: The text of the success comment
-      name: commentsuccess
-      type: string
-    - default: Failed
-      description: The text of the failure comment
-      name: commentfailure
-      type: string
-    - default: http://localhost:9097/
-      description: The URL to the pipelineruns page of the dashboard
-      name: dashboard-url
-      type: string
-    - description: The secret containing the access token to access github
-      name: secret
-      type: string
-    resources:
-    - name: pull-request
-      type: pullRequest
-  outputs:
-    resources:
-    - name: pull-request
-      type: pullRequest
-  steps:
-  - args:
-    - -ce
-    - |
-      set -e
-      cat <<EOF | python
-      import time, os, json, requests, pprint
-      from kubernetes import client, config
-      config.load_incluster_config()
-      api_instance = client.CustomObjectsApi(client.ApiClient(client.Configuration()))
-      gitPRcontext = "Tekton"
-      gitPRurl = ""
-      # This is the code thats puts the pullrequest into pending status, this is code to rip out later if there
-      # is a fix to the above mentioned update status to pending issue.
-      with open("/workspace/pull-request/github/pr.json") as fp:
-        rawdata = json.load(fp)
-        statusurl = rawdata['statuses_url']
-      pendingData = {
-        "state": "pending",
-        "description": "pipelines in progress",
-        "target_url": "",
-        "context": "Tekton"
-      }
-      print("Setting status to pending with URL : " + statusurl)
-      resp = requests.post(statusurl, json.dumps(pendingData), headers = {'Content-Type': 'application/json', 'Authorization': "Bearer $GITHUBTOKEN"})
-      print(resp)
-      # End of code to replace
-      if not "$URL".startswith("http"):
-        pipelineRunURLPrefix = "http://" + "$URL"
-      else:
-        pipelineRunURLPrefix = "$URL"
-      runsToCheck = "$PIPELINERUNS".split(",")
-      runsPassed = []
-      runsFailed = []
-      runsIncomplete = []
-      failed = 0
-      i = range(180)
-      for x in i:
-          time.sleep( 10 )
-          if len(runsToCheck) > 0:
-            for entry in runsToCheck:
-              pr, namespace, pipeline = entry.split(":")
-              pr = pr.strip()
-              namespace = namespace.strip()
-              pipeline = pipeline.strip()
-              link = pipelineRunURLPrefix + "/#/namespaces/" + namespace + "/pipelines/" + pipeline + "/runs/" + pr
-              print("Checking pipelinerun " + pr + " in namespace " + namespace)
-              output = api_instance.get_namespaced_custom_object("tekton.dev", "v1alpha1", namespace, "pipelineruns", pr)
-              if output["status"]["conditions"][0]["status"] == u'True' and output["status"]["conditions"][0]["type"] == u'Succeeded':
-                print("Success - pipelinerun " + pr + " in namespace " + namespace)
-                runsToCheck.remove(entry)
-                runsPassed.append("[**$COMMENT_SUCCESS**](" + link + ") | " + pipeline + " | " +  pr + " | " + namespace)
-              if output["status"]["conditions"][0]["status"] == u'False' and output["status"]["conditions"][0]["type"] == u'Succeeded':
-                failed =+ 1
-                print("Failed - pipelinerun " + pr + " in namespace " + namespace)
-                runsToCheck.remove(entry)
-                runsFailed.append("[**$COMMENT_FAILURE**](" + link + ") | " + pipeline + " | " + pr + " | " + namespace)
-          else:
-            break
-      gitPRdescription = "All pipelines succeeded!"
-      gitPRcode = "success"
-      if failed > 0:
-        gitPRdescription = str(failed) + " pipeline(s) failed!"
-        gitPRcode = "failure"
-      if len(runsToCheck) > 0:
-        print("Some pipelineruns had not completed when the monitor reached its timeout")
-        gitPRdescription = "timed out monitoring pipeline runs"
-        gitPRcode = "error"
-        for entry in runsToCheck:
-          pr, namespace, pipeline = entry.split(":")
-          pr = pr.strip()
-          namespace = namespace.strip()
-          pipeline = pipeline.strip()
-          print("Still Running - pipelinerun " + pr + " in namespace " + namespace)
-          link = pipelineRunURLPrefix + "/#/namespaces/" + namespace + "/pipelines/" + pipeline + "/runs/" + pr
-          runsIncomplete.append("[**??????**](" + link + ") | " + pipeline + " | " + pr + " | " + namespace)
-      results = runsPassed + runsFailed + runsIncomplete
-      comment = ("## Tekton Status Report \n\n"
-                 "Status | Pipeline | PipelineRun | Namespace\n"
-                 ":----- | :------- | :--------------- | :--------\n"
-                 ) + "\n".join(results)
-      handle = open("/workspace/pull-request/comments/newcomment.json", 'w')
-      handle.write(comment)
-      handle.close()
-      if not "$URL".startswith("http"):
-        detailsURL = "http://" + "$URL" + "/#/pipelineruns"
-      else:
-        detailsURL = "$URL" + "/#/pipelineruns"
-      print("Set details url to: " + detailsURL)
-      status = json.dumps(dict(code=gitPRcode, description=gitPRdescription, id=gitPRcontext, url=detailsURL))
-      handle = open("/workspace/pull-request/status/newstatus.json", 'w')
-      handle.write(status)
-      handle.close()
-      EOF
-    command:
-    - /bin/bash
-    env:
-    - name: PIPELINERUNS
-      value: $(inputs.params.pipelineruns)
-    - name: COMMENT_SUCCESS
-      value: $(inputs.params.commentsuccess)
-    - name: COMMENT_FAILURE
-      value: $(inputs.params.commentfailure)
-    - name: URL
-      value: $(inputs.params.dashboard-url)
-    - name: GITHUBTOKEN
-      valueFrom:
-        secretKeyRef:
-          key: accessToken
-          name: $(inputs.params.secret)
-    image: maiwj/kubernetes-python-client:latest@sha256:74a868a0dff5c8ada64472db3efd09d205d4f877d14d2d3226511adbb25cfea3
-    name: check
-
----
diff --git a/cmd/operator/kodata/resources/addons/extensionwebhooks/v0.6.1/release.yaml b/cmd/operator/kodata/resources/addons/extensionwebhooks/v0.6.1/release.yaml
deleted file mode 100644
index 76358d675..000000000
--- a/cmd/operator/kodata/resources/addons/extensionwebhooks/v0.6.1/release.yaml
+++ /dev/null
@@ -1,656 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  labels:
-    app: tekton-webhooks-extension
-  name: tekton-webhooks-extension
-  namespace: tekton-pipelines
----
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  labels:
-    app: tekton-webhooks-extension
-  name: tekton-webhooks-extension-eventlistener
-  namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: Role
-metadata:
-  name: tekton-webhooks-extension-minimal
-  namespace: tekton-pipelines
-rules:
-- apiGroups:
-  - extensions
-  resources:
-  - ingresses
-  - ingresses/status
-  verbs:
-  - delete
-  - create
-  - patch
-  - get
-  - list
-  - update
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - pods
-  - services
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - pods/log
-  - namespaces
-  - events
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  - configmaps
-  verbs:
-  - get
-  - list
-  - create
-  - delete
-  - update
-  - watch
-- apiGroups:
-  - extensions
-  - apps
-  resources:
-  - deployments
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks
-  - clustertasks
-  - taskruns
-  - pipelines
-  - pipelineruns
-  - pipelineresources
-  - conditions
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - eventlisteners
-  - triggerbindings
-  - triggertemplates
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - taskruns/finalizers
-  - pipelineruns/finalizers
-  - tasks/status
-  - clustertasks/status
-  - taskruns/status
-  - pipelines/status
-  - pipelineruns/status
-  verbs:
-  - get
-  - list
-  - create
-  - update
-  - delete
-  - patch
-  - watch
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: tekton-triggers-minimal
-rules:
-- apiGroups:
-  - tekton.dev
-  resources:
-  - tasks
-  - taskruns
-  verbs:
-  - get
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - triggerbindings
-  - triggertemplates
-  - eventlisteners
-  verbs:
-  - get
-- apiGroups:
-  - tekton.dev
-  resources:
-  - pipelineruns
-  - pipelineresources
-  - taskruns
-  verbs:
-  - create
-- apiGroups:
-  - ""
-  resources:
-  - configmaps
-  verbs:
-  - list
-  - get
-  - watch
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: tekton-webhooks-extension-minimal-cluster-powers
-rules:
-- apiGroups:
-  - ""
-  resources:
-  - serviceaccounts
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - tekton.dev
-  resources:
-  - pipelines
-  - pipelineruns
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - triggers.tekton.dev
-  resources:
-  - pipelines
-  - pipelineruns
-  - tasks
-  - taskruns
-  verbs:
-  - get
-  - list
-  - watch
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
-  name: tekton-webhooks-extension-minimal
-  namespace: tekton-pipelines
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: Role
-  name: tekton-webhooks-extension-minimal
-subjects:
-- kind: ServiceAccount
-  name: tekton-webhooks-extension
-  namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: tekton-webhooks-extension-eventlistener-minimal
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-triggers-minimal
-subjects:
-- kind: ServiceAccount
-  name: tekton-webhooks-extension-eventlistener
-  namespace: tekton-pipelines
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: tekton-webhooks-extension-minimal-cluster-powers
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: tekton-webhooks-extension-minimal-cluster-powers
-subjects:
-- kind: ServiceAccount
-  name: tekton-webhooks-extension
-  namespace: tekton-pipelines
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: tekton-webhooks-extension-validator
-  namespace: tekton-pipelines
-spec:
-  ports:
-  - port: 80
-    protocol: TCP
-    targetPort: 8080
-  selector:
-    app: tekton-webhooks-extension-validator
-  type: ClusterIP
----
-apiVersion: v1
-kind: Service
-metadata:
-  annotations:
-    tekton-dashboard-bundle-location: web/extension.c591f714.js
-    tekton-dashboard-display-name: Webhooks
-    tekton-dashboard-endpoints: webhooks.web
-  labels:
-    app: webhooks-extension
-    tekton-dashboard-extension: "true"
-  name: webhooks-extension
-  namespace: tekton-pipelines
-spec:
-  ports:
-  - port: 8080
-    targetPort: 8080
-  selector:
-    app: webhooks-extension
-  type: NodePort
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: tekton-webhooks-extension-validator
-  namespace: tekton-pipelines
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: tekton-webhooks-extension-validator
-  template:
-    metadata:
-      labels:
-        app: tekton-webhooks-extension-validator
-    spec:
-      containers:
-      - env:
-        - name: INSTALLED_NAMESPACE
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
-        image: gcr.io/tekton-releases/github.com/tektoncd/experimental/webhooks-extension/cmd/interceptor@sha256:657d40a9116ef0b6f886f94fa7980755e3267dd34017f2fd9b713b63ddfc0d55
-        name: validate
-      serviceAccountName: tekton-webhooks-extension
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  labels:
-    app: webhooks-extension
-  name: webhooks-extension
-  namespace: tekton-pipelines
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: webhooks-extension
-  template:
-    metadata:
-      labels:
-        app: webhooks-extension
-    spec:
-      containers:
-      - env:
-        - name: PORT
-          value: "8080"
-        - name: INSTALLED_NAMESPACE
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
-        - name: DOCKER_REGISTRY_LOCATION
-          value: DOCKER_REPO
-        - name: WEB_RESOURCES_DIR
-          value: web
-        - name: WEBHOOK_CALLBACK_URL
-          value: http://listener.IPADDRESS.nip.io
-        - name: SSL_VERIFICATION_ENABLED
-          value: "false"
-        - name: SERVICE_ACCOUNT
-          valueFrom:
-            fieldRef:
-              fieldPath: spec.serviceAccountName
-        image: gcr.io/tekton-releases/github.com/tektoncd/experimental/webhooks-extension/cmd/extension@sha256:e7bcffbd2db6b874dbb4b4e71fc0c089acf7ccb803df896d9592063b649ac292
-        imagePullPolicy: Always
-        livenessProbe:
-          httpGet:
-            path: /liveness
-            port: 8080
-        name: webhooks-extension
-        ports:
-        - containerPort: 8080
-        readinessProbe:
-          httpGet:
-            path: /readiness
-            port: 8080
-      serviceAccountName: tekton-webhooks-extension
----
-apiVersion: tekton.dev/v1beta1
-kind: Task
-metadata:
-  name: monitor-task
-  namespace: tekton-pipelines
-spec:
-  params:
-  - description: The statuses url
-    name: statusesurl
-    type: string
-  - default: Success
-    description: The text to use in the situation where a PipelineRun has succeeded.
-    name: commentsuccess
-    type: string
-  - default: Failed
-    description: The text to use in the situation where a PipelineRun has failed.
-    name: commentfailure
-    type: string
-  - default: Unknown
-    description: The text to use in the situation where a PipelineRun has timed out.
-    name: commenttimeout
-    type: string
-  - default: Missing
-    description: The text to use in the situation where a PipelineRun cannot be found.
-    name: commentmissing
-    type: string
-  - default: http://localhost:9097/
-    description: The URL to the PipelineRuns page of the dashboard
-    name: dashboard-url
-    type: string
-  - default: github
-    description: The Git provider ("github" or "gitlab")
-    name: provider
-    type: string
-  - description: The Git API URL for the repository
-    name: apiurl
-    type: string
-  - default: "false"
-    description: Whether or not to verify SSL Certificates from the git server ("true"
-      or "false")
-    name: insecure-skip-tls-verify
-    type: string
-  - description: The secret containing the access token to access the git server
-    name: secret
-    type: string
-  resources:
-    inputs:
-    - name: pull-request
-      type: pullRequest
-    outputs:
-    - name: pull-request
-      type: pullRequest
-  steps:
-  - args:
-    - -ce
-    - "set -e\ncat <<EOF | python\nimport time, os, json, requests, pprint, shutil,
-      distutils.util\nfrom kubernetes import client, config\ndef diff(li1, li2): \n
-      \ li_dif = [i for i in li1 + li2 if i not in li1 or i not in li2] \n  return
-      li_dif\nconfig.load_incluster_config()\napi_instance = client.CustomObjectsApi(client.ApiClient(client.Configuration()))\ngitPRcontext
-      = \"Tekton\"\ngitPRurl = \"\"  \nif not \"$URL\".startswith(\"http\"):\n  pipelineRunURLPrefix
-      = \"http://\" + \"$URL\"\nelse:\n  pipelineRunURLPrefix = \"$URL\"    \nverifySSL
-      = not bool(distutils.util.strtobool(\"$SKIPSSLVERIFY\"))\nif \"$GITPROVIDER\"
-      == \"github\":\n  statusurl = \"$STATUSES_URL\"\n  pendingData = {\n    \"state\":
-      \"pending\",\n    \"description\": \"pipelines in progress\",\n    \"target_url\":
-      pipelineRunURLPrefix + \"/#/pipelineruns\",\n    \"context\": \"Tekton\"\n  }\n
-      \ resp = requests.post(statusurl, json.dumps(pendingData), headers = {'Content-Type':
-      'application/json', 'Authorization': \"Token $GITTOKEN\"}, verify=verifySSL)\n
-      \ print(resp)\nif \"$GITPROVIDER\" == \"gitlab\":\n  statusurl = \"$GITAPIURL\"
-      + \"/\" + \"$STATUSES_URL\" + \"?state=pending&name=Tekton&target_url=\" + pipelineRunURLPrefix
-      + \"/#/pipelineruns\"\n  resp = requests.post(statusurl, headers = {'Authorization':
-      \"Bearer $GITTOKEN\"}, verify=verifySSL)\n  print(resp)\nlabelToCheck = \"triggers.tekton.dev/triggers-eventid=$EVENTID\"\nrunsPassed
-      = []\nrunsFailed = []\nrunsIncomplete = []\nrunsMissing = []\nfailed = 0\ni
-      = range(180)\ninitial_runs = api_instance.list_cluster_custom_object(\"tekton.dev\",
-      \"v1beta1\", \"pipelineruns\", label_selector=labelToCheck)[\"items\"]\nfor
-      x in i:\n    time.sleep( 10 )\n    runsPassed = []\n    runsFailed = []\n    runsIncomplete
-      = []\n    # To test this we need a webhook that will kick off two Pipelines\n
-      \   # We will then delete one PipelineRun and observe it is correctly picked
-      up as missing\n    # This is easiest done by reopening an existing PullRequest\n
-      \   # It's important to delete the PipelineRun only after the monitor task is
-      already running because \n    # the first thing it's going to do is figure out
-      the PipelineRuns to watch over\n    failed = 0\n    \n    found_runs = api_instance.list_cluster_custom_object(\"tekton.dev\",
-      \"v1beta1\", \"pipelineruns\", label_selector=labelToCheck)[\"items\"]\n    missingRuns
-      = diff(initial_runs, found_runs)\n    if len(missingRuns) > 0:\n      for missingRun
-      in missingRuns:\n        pr = missingRun[\"metadata\"][\"name\"]\n        namespace
-      = missingRun[\"metadata\"][\"namespace\"]\n        pipeline = missingRun[\"spec\"][\"pipelineRef\"][\"name\"]\n
-      \       link = pipelineRunURLPrefix + \"/#/namespaces/\" + namespace + \"/pipelineruns/\"\n
-      \       data = \"[**$COMMENT_MISSING**](\" + link + \") | \" + pipeline + \"
-      | \" + pr + \" | \" + namespace\n        if data not in runsMissing:\n          #
-      Don't add duplicates. Fear not, once this run is found it'll be removed\n          runsMissing.append(data)\n
-      \   if len(found_runs) > 0:\n      for entry in found_runs:\n        pr = entry[\"metadata\"][\"name\"]\n
-      \       namespace = entry[\"metadata\"][\"namespace\"]\n        pipeline = entry[\"spec\"][\"pipelineRef\"][\"name\"]\n
-      \       link = pipelineRunURLPrefix + \"/#/namespaces/\" + namespace + \"/pipelineruns/\"
-      + pr\n        missingLink = pipelineRunURLPrefix + \"/#/namespaces/\" + namespace
-      + \"/pipelineruns/\"\n        missingDataEntry = \"[**$COMMENT_MISSING**](\"
-      + missingLink + \") | \" + pipeline + \" | \" + pr + \" | \" + namespace\n        if
-      missingDataEntry in runsMissing:\n          runsMissing.remove(missingDataEntry)\n
-      \       print(\"Checking PipelineRun \" + pr + \" in namespace \" + namespace)\n
-      \       if entry[\"status\"][\"conditions\"][0][\"status\"] == u'True' and entry[\"status\"][\"conditions\"][0][\"type\"]
-      == u'Succeeded':\n          print(\"Success - pipelinerun \" + pr + \" in namespace
-      \" + namespace)\n          runsPassed.append(\"[**$COMMENT_SUCCESS**](\" + link
-      + \") | \" + pipeline + \" | \" +  pr + \" | \" + namespace)\n          continue\n
-      \       if entry[\"status\"][\"conditions\"][0][\"status\"] == u'False' and
-      entry[\"status\"][\"conditions\"][0][\"type\"] == u'Succeeded':\n          failed
-      =+ 1\n          print(\"Failed - PipelineRun \" + pr + \" in namespace \" +
-      namespace)\n          runsFailed.append(\"[**$COMMENT_FAILURE**](\" + link +
-      \") | \" + pipeline + \" | \" + pr + \" | \" + namespace)\n          continue\n
-      \       link = pipelineRunURLPrefix + \"/#/namespaces/\" + namespace + \"/pipelineruns/\"
-      + pr\n        runsIncomplete.append(\"[**$COMMENT_TIMEOUT**](\" + link + \")
-      | \" + pipeline + \" | \" + pr + \" | \" + namespace)\n      if len(runsIncomplete)
-      == 0:\n        break\n    else:\n      break\ngitPRdescription = \"All pipelines
-      succeeded!\"\ngitPRcode = \"success\"\nif failed > 0:\n  gitPRdescription =
-      str(failed) + \" pipeline(s) failed!\"\n  gitPRcode = \"failure\"\nif len(runsMissing)
-      > 0:\n  gitPRdescription = \"Pipeline(s) missing!\"\n  gitPRcode = \"failure\"\nif
-      len(runsIncomplete) > 0:\n  print(\"Some PipelineRuns had not completed when
-      the monitor reached its timeout\")\n  gitPRdescription = \"timed out monitoring
-      PipelineRuns\"\n  gitPRcode = \"error\"\n\nresults = runsPassed + runsFailed
-      + runsIncomplete + runsMissing\n\nif (results == []):\n  gitPRdescription =
-      \"No PipelineRuns were ever found for my PullRequest!\"\n  gitPRcode = \"error\"\n
-      \ data = \"**$COMMENT_MISSING** | N/A | No PipelineRuns were ever detected,
-      failing the build | N/A\"\n  runsMissing.append(data)    \n       \n  results
-      = runsMissing\n\ncomment = (\"## Tekton Status Report \\n\\n\"\n           \"Status
-      | Pipeline | PipelineRun | Namespace\\n\"\n           \":----- | :------- |
-      :--------------- | :--------\\n\"\n           ) + \"\\n\".join(results)\n\nshutil.copyfile(\"/workspace/pull-request/pr.json\",\"/workspace/output/pull-request/pr.json\")\n#
-      Preserve existing comments\nshutil.copytree(\"/workspace/pull-request/comments\",\"/workspace/output/pull-request/comments\")\nhandle
-      = open(\"/workspace/output/pull-request/comments/newcomment.json\", 'w')\nhandle.write(comment)\nhandle.close()\nif
-      not \"$URL\".startswith(\"http\"):\n  detailsURL = \"http://\" + \"$URL\" +
-      \"/#/pipelineruns\"\nelse:\n  detailsURL = \"$URL\" + \"/#/pipelineruns\"\nprint(\"Set
-      details url to \" + detailsURL)\nstatus = json.dumps(dict(Label=gitPRcontext,state=gitPRcode,Desc=gitPRdescription,Target=detailsURL))\nprint(\"Setting
-      status to \" + status)\nif not os.path.exists(\"/workspace/output/pull-request/status\"):\n
-      \ os.makedirs(\"/workspace/output/pull-request/status\")\nhandle = open(\"/workspace/output/pull-request/status/Tekton.json\",
-      'w')\nhandle.write(status)\nhandle.close()\nif not os.path.exists(\"/workspace/output/pull-request/labels\"):\n
-      \ shutil.copytree(\"/workspace/pull-request/labels\",\"/workspace/output/pull-request/labels\")\nshutil.copyfile(\"/workspace/pull-request/base.json\",\"/workspace/output/pull-request/base.json\")
-      \nshutil.copyfile(\"/workspace/pull-request/head.json\",\"/workspace/output/pull-request/head.json\")\nEOF\n"
-    command:
-    - /bin/bash
-    env:
-    - name: EVENTID
-      valueFrom:
-        fieldRef:
-          fieldPath: metadata.labels['triggers.tekton.dev/triggers-eventid']
-    - name: COMMENT_SUCCESS
-      value: $(inputs.params.commentsuccess)
-    - name: COMMENT_FAILURE
-      value: $(inputs.params.commentfailure)
-    - name: COMMENT_TIMEOUT
-      value: $(inputs.params.commenttimeout)
-    - name: COMMENT_MISSING
-      value: $(inputs.params.commentmissing)
-    - name: URL
-      value: $(inputs.params.dashboard-url)
-    - name: STATUSES_URL
-      value: $(inputs.params.statusesurl)
-    - name: GITPROVIDER
-      value: $(inputs.params.provider)
-    - name: GITAPIURL
-      value: $(inputs.params.apiurl)
-    - name: SKIPSSLVERIFY
-      value: $(inputs.params.insecure-skip-tls-verify)
-    - name: GITTOKEN
-      valueFrom:
-        secretKeyRef:
-          key: accessToken
-          name: $(inputs.params.secret)
-    image: maiwj/kubernetes-python-client@sha256:74a868a0dff5c8ada64472db3efd09d205d4f877d14d2d3226511adbb25cfea3
-    name: check
----
-apiVersion: triggers.tekton.dev/v1alpha1
-kind: TriggerBinding
-metadata:
-  name: monitor-task-github-binding
-  namespace: tekton-pipelines
-spec:
-  params:
-  - name: pullrequesturl
-    value: $(body.pull_request.html_url)
-  - name: statusesurl
-    value: $(body.pull_request.statuses_url)
----
-apiVersion: triggers.tekton.dev/v1alpha1
-kind: TriggerBinding
-metadata:
-  name: monitor-task-gitlab-binding
-  namespace: tekton-pipelines
-spec:
-  params:
-  - name: pullrequesturl
-    value: $(body.object_attributes.url)
-  - name: statusesurl
-    value: projects/$(body.project.id)/statuses/$(body.object_attributes.last_commit.id)
----
-apiVersion: triggers.tekton.dev/v1alpha1
-kind: TriggerTemplate
-metadata:
-  name: monitor-task-template
-  namespace: tekton-pipelines
-spec:
-  params:
-  - description: The pull request url
-    name: pullrequesturl
-    type: string
-  - description: The statuses url
-    name: statusesurl
-    type: string
-  - default: github-secrets
-    description: The git secret name
-    name: gitsecretname
-    type: string
-  - default: token
-    description: The git secret key name
-    name: gitsecretkeyname
-    type: string
-  - default: Success
-    description: The text of the success comment
-    name: commentsuccess
-    type: string
-  - default: Failed
-    description: The text of the failure comment
-    name: commentfailure
-    type: string
-  - default: Unknown
-    description: The text of the timeout comment
-    name: commenttimeout
-    type: string
-  - default: Missing
-    description: The text of the missing comment
-    name: commentmissing
-    type: string
-  - default: http://localhost:9097/
-    description: The URL to the pipelineruns page of the dashboard
-    name: dashboardurl
-    type: string
-  - default: github
-    description: The git provider, "github" or "gitlab"
-    name: provider
-    type: string
-  - default: ""
-    description: The git api URL for the repository
-    name: apiurl
-    type: string
-  - default: "false"
-    description: Whether or not to skip SSL validation of certificates ("true" or
-      "false")
-    name: insecure-skip-tls-verify
-    type: string
-  resourcetemplates:
-  - apiVersion: tekton.dev/v1alpha1
-    kind: PipelineResource
-    metadata:
-      name: pull-request-$(uid)
-      namespace: tekton-pipelines
-    spec:
-      params:
-      - name: url
-        value: $(params.pullrequesturl)
-      - name: insecure-skip-tls-verify
-        value: $(params.insecure-skip-tls-verify)
-      secrets:
-      - fieldName: authToken
-        secretKey: $(params.gitsecretkeyname)
-        secretName: $(params.gitsecretname)
-      type: pullRequest
-  - apiVersion: tekton.dev/v1beta1
-    kind: TaskRun
-    metadata:
-      generateName: monitor-taskrun-
-      namespace: tekton-pipelines
-    spec:
-      params:
-      - name: commentsuccess
-        value: $(params.commentsuccess)
-      - name: commentfailure
-        value: $(params.commentfailure)
-      - name: commenttimeout
-        value: $(params.commenttimeout)
-      - name: dashboard-url
-        value: $(params.dashboardurl)
-      - name: secret
-        value: $(params.gitsecretname)
-      - name: statusesurl
-        value: $(params.statusesurl)
-      - name: provider
-        value: $(params.provider)
-      - name: apiurl
-        value: $(params.apiurl)
-      - name: insecure-skip-tls-verify
-        value: $(params.insecure-skip-tls-verify)
-      resources:
-        inputs:
-        - name: pull-request
-          resourceRef:
-            name: pull-request-$(uid)
-        outputs:
-        - name: pull-request
-          resourceRef:
-            name: pull-request-$(uid)
-      serviceAccountName: tekton-webhooks-extension
-      taskRef:
-        name: monitor-task
-
----
diff --git a/cmd/operator/kodata/resources/addons/trigger/v0.7.0/release.yaml b/cmd/operator/kodata/tekton-trigger/0.7.0/release.yaml
similarity index 100%
rename from cmd/operator/kodata/resources/addons/trigger/v0.7.0/release.yaml
rename to cmd/operator/kodata/tekton-trigger/0.7.0/release.yaml
diff --git a/cmd/operator/main.go b/cmd/operator/main.go
index 2725b7875..c421dfdae 100644
--- a/cmd/operator/main.go
+++ b/cmd/operator/main.go
@@ -1,24 +1,30 @@
 /*
 Copyright 2019 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
+
 package main
 
 import (
 	"github.com/tektoncd/operator/pkg/reconciler/tektonpipeline"
+	"github.com/tektoncd/operator/pkg/reconciler/tektontrigger"
 	"knative.dev/pkg/injection/sharedmain"
 )
 
 func main() {
 	sharedmain.Main("tekton-operator",
 		tektonpipeline.NewController,
+		tektontrigger.NewController,
 	)
 }
diff --git a/config/300-operator_v1alpha1_addon_crd.yaml b/config/300-operator_v1alpha1_addon_crd.yaml
deleted file mode 120000
index bb5ab15d7..000000000
--- a/config/300-operator_v1alpha1_addon_crd.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-crds/300-operator_v1alpha1_addon_crd.yaml
\ No newline at end of file
diff --git a/config/300-operator_v1alpha1_trigger_crd.yaml b/config/300-operator_v1alpha1_trigger_crd.yaml
new file mode 100644
index 000000000..3800b9cea
--- /dev/null
+++ b/config/300-operator_v1alpha1_trigger_crd.yaml
@@ -0,0 +1,119 @@
+# Copyright 2020 The Tekton Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: tektontriggers.operator.tekton.dev
+  labels:
+    version: "devel"
+    operator.tekton.dev/release: "devel"
+spec:
+  additionalPrinterColumns:
+    - JSONPath: .status.version
+      name: Version
+      type: string
+    - JSONPath: .status.conditions[?(@.type=="Ready")].status
+      name: Ready
+      type: string
+    - JSONPath: ".status.conditions[?(@.type==\"Ready\")].reason"
+      name: Reason
+      type: string
+  group: operator.tekton.dev
+  names:
+    kind: TektonTrigger
+    listKind: TektonTriggerList
+    plural: tektontriggers
+    singular: tektontrigger
+  preserveUnknownFields: false
+  scope: Cluster
+  subresources:
+    status: {}
+  validation:
+    openAPIV3Schema:
+      type: object
+      description: Schema for the tektontriggers API
+      properties:
+        apiVersion:
+          description: 'APIVersion defines the versioned schema of this representation
+            of an object. Servers should convert recognized schemas to the latest
+            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
+          type: string
+        kind:
+          description: 'Kind is a string value representing the REST resource this
+            object represents. Servers may infer this from the endpoint the client
+            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
+          type: string
+        metadata:
+          type: object
+        spec:
+          description: Spec defines the desired state of TektonTrigger
+          properties:
+            targetNamespace:
+              description: namespace where tekton triggers will be installed
+              type: string
+          type: object
+        status:
+          description: Status defines the observed state of TektonTrigger
+          properties:
+            observedGeneration:
+              description: The generation last processed by the controller
+              type: integer
+            conditions:
+              description: The latest available observations of a resource's current
+                state.
+              items:
+                properties:
+                  lastTransitionTime:
+                    description: LastTransitionTime is the last time the condition
+                      transitioned from one status to another. We use VolatileTime
+                      in place of metav1.Time to exclude this from creating equality.Semantic
+                      differences (all other things held constant).
+                    type: string
+                  message:
+                    description: A human readable message indicating details about
+                      the transition.
+                    type: string
+                  reason:
+                    description: The reason for the condition's last transition.
+                    type: string
+                  severity:
+                    description: Severity with which to treat failures of this type
+                      of condition. When this is not specified, it defaults to Error.
+                    type: string
+                  status:
+                    description: Status of the condition, one of True, False, Unknown.
+                    type: string
+                  type:
+                    description: Type of condition.
+                    type: string
+                required:
+                  - type
+                  - status
+                type: object
+              type: array
+            version:
+              description: The version of the installed release
+              type: string
+            manifests:
+              description: The list of serving manifests, which have been installed by the operator
+              type: array
+              items:
+                type: string
+          type: object
+  version: v1alpha1
+  versions:
+  - name: v1alpha1
+    served: true
+    storage: true
diff --git a/config/crds/300-operator_v1alpha1_addon_crd.yaml b/config/crds/300-operator_v1alpha1_addon_crd.yaml
deleted file mode 100644
index 2e89848ed..000000000
--- a/config/crds/300-operator_v1alpha1_addon_crd.yaml
+++ /dev/null
@@ -1,85 +0,0 @@
-# Copyright 2020 The Tekton Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-apiVersion: apiextensions.k8s.io/v1beta1
-kind: CustomResourceDefinition
-metadata:
-  name: tektonaddons.operator.tekton.dev
-  labels:
-    version: "devel"
-    operator.tekton.dev/release: "devel"
-spec:
-  group: operator.tekton.dev
-  names:
-    kind: TektonAddon
-    listKind: TektonAddonList
-    plural: tektonaddons
-    singular: tektonaddon
-  scope: Cluster
-  subresources:
-    status: {}
-  validation:
-    openAPIV3Schema:
-      properties:
-        apiVersion:
-          description: 'APIVersion defines the versioned schema of this representation
-            of an object. Servers should convert recognized schemas to the latest
-            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
-          type: string
-        kind:
-          description: 'Kind is a string value representing the REST resource this
-            object represents. Servers may infer this from the endpoint the client
-            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
-          type: string
-        metadata:
-          type: object
-        spec:
-          properties:
-            version:
-              description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
-                Important: Run "operator-sdk generate k8s" to regenerate code after
-                modifying this file Add custom validation using kubebuilder tags:
-                https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html'
-              type: string
-          required:
-          - version
-          type: object
-        status:
-          properties:
-            conditions:
-              description: installation status sorted in reverse chronological order
-              items:
-                properties:
-                  code:
-                    description: 'Code indicates the status of installation of tektonaddon
-                      resources Valid values are:   - "error"   - "installing"   -
-                      "installed"'
-                    type: string
-                  details:
-                    description: Additional details about the Code
-                    type: string
-                  version:
-                    description: The version of installed tektonaddon
-                    type: string
-                required:
-                - code
-                - version
-                type: object
-              type: array
-          type: object
-  version: v1alpha1
-  versions:
-  - name: v1alpha1
-    served: true
-    storage: true
diff --git a/config/crds/operator_v1alpha1_addon_dashboard_cr.yaml b/config/crds/operator_v1alpha1_addon_dashboard_cr.yaml
deleted file mode 100644
index 29c25f297..000000000
--- a/config/crds/operator_v1alpha1_addon_dashboard_cr.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-apiVersion: operator.tekton.dev/v1alpha1
-kind: TektonAddon
-metadata:
-  name: dashboard
-spec:
-  # Add fields here
-  version: v0.8.2
diff --git a/config/crds/operator_v1alpha1_addon_extensionwebhooks_cr.yaml b/config/crds/operator_v1alpha1_addon_extensionwebhooks_cr.yaml
deleted file mode 100644
index 292e627e1..000000000
--- a/config/crds/operator_v1alpha1_addon_extensionwebhooks_cr.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-apiVersion: operator.tekton.dev/v1alpha1
-kind: TektonAddon
-metadata:
-  name: extensionwebhooks
-spec:
-  # Add fields here
-  version: v0.6.1
\ No newline at end of file
diff --git a/config/crds/operator_v1alpha1_pipeline_cr.yaml b/config/crs/operator_v1alpha1_pipeline_cr.yaml
similarity index 100%
rename from config/crds/operator_v1alpha1_pipeline_cr.yaml
rename to config/crs/operator_v1alpha1_pipeline_cr.yaml
diff --git a/config/crds/operator_v1alpha1_addon_trigger_cr.yaml b/config/crs/operator_v1alpha1_trigger_cr.yaml
similarity index 56%
rename from config/crds/operator_v1alpha1_addon_trigger_cr.yaml
rename to config/crs/operator_v1alpha1_trigger_cr.yaml
index 2e8958d23..27beba81e 100644
--- a/config/crds/operator_v1alpha1_addon_trigger_cr.yaml
+++ b/config/crs/operator_v1alpha1_trigger_cr.yaml
@@ -1,7 +1,6 @@
 apiVersion: operator.tekton.dev/v1alpha1
-kind: TektonAddon
+kind: TektonTrigger
 metadata:
   name: trigger
 spec:
-  # Add fields here
-  version: v0.7.0
+  targetNamespace: tekton-pipelines
diff --git a/pkg/apis/operator/v1alpha1/common.go b/pkg/apis/operator/v1alpha1/common.go
index eba9f4734..5d98a7bab 100644
--- a/pkg/apis/operator/v1alpha1/common.go
+++ b/pkg/apis/operator/v1alpha1/common.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/pkg/apis/operator/v1alpha1/doc.go b/pkg/apis/operator/v1alpha1/doc.go
index 7a3cf0741..45601bc93 100644
--- a/pkg/apis/operator/v1alpha1/doc.go
+++ b/pkg/apis/operator/v1alpha1/doc.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/pkg/apis/operator/v1alpha1/register.go b/pkg/apis/operator/v1alpha1/register.go
index c432c7bb1..a8a08a29a 100644
--- a/pkg/apis/operator/v1alpha1/register.go
+++ b/pkg/apis/operator/v1alpha1/register.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -26,8 +29,11 @@ const (
 	// SchemaVersion is the current version of the API.
 	SchemaVersion = "v1alpha1"
 
-	// KindTektonPipeline is the Kind of Knative Serving in a GVK context.
+	// KindTektonPipeline is the Kind of Tekton Pipeline in a GVK context.
 	KindTektonPipeline = "TektonPipeline"
+
+	// KindTektonTrigger is the Kind of Tekton Trigger in a GVK context.
+	KindTektonTrigger = "TektonTrigger"
 )
 
 // Resource takes an unqualified resource and returns a Group qualified GroupResource
@@ -41,6 +47,8 @@ func addKnownTypes(s *runtime.Scheme) error {
 	s.AddKnownTypes(SchemeGroupVersion,
 		&TektonPipeline{},
 		&TektonPipelineList{},
+		&TektonTrigger{},
+		&TektonTriggerList{},
 	)
 	metav1.AddToGroupVersion(s, SchemeGroupVersion)
 	return nil
diff --git a/pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle.go b/pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle.go
index 23ab6b384..308db9130 100644
--- a/pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle.go
+++ b/pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/pkg/apis/operator/v1alpha1/tektonpipeline_types.go b/pkg/apis/operator/v1alpha1/tektonpipeline_types.go
index b6bb00dbb..5976e70d2 100644
--- a/pkg/apis/operator/v1alpha1/tektonpipeline_types.go
+++ b/pkg/apis/operator/v1alpha1/tektonpipeline_types.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle.go b/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle.go
new file mode 100644
index 000000000..70024864f
--- /dev/null
+++ b/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle.go
@@ -0,0 +1,127 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"knative.dev/pkg/apis"
+)
+
+var (
+	_ TektonComponentStatus = (*TektonTriggerStatus)(nil)
+
+	triggersCondSet = apis.NewLivingConditionSet(
+		DependenciesInstalled,
+		DeploymentsAvailable,
+		InstallSucceeded,
+	)
+)
+
+// GroupVersionKind returns SchemeGroupVersion of a TektonTrigger
+func (tp *TektonTrigger) GroupVersionKind() schema.GroupVersionKind {
+	return SchemeGroupVersion.WithKind(KindTektonTrigger)
+}
+
+// GetCondition returns the current condition of a given condition type
+func (tps *TektonTriggerStatus) GetCondition(t apis.ConditionType) *apis.Condition {
+	return triggersCondSet.Manage(tps).GetCondition(t)
+}
+
+// InitializeConditions initializes conditions of an TektonTriggerStatus
+func (tps *TektonTriggerStatus) InitializeConditions() {
+	triggersCondSet.Manage(tps).InitializeConditions()
+}
+
+// IsReady looks at the conditions returns true if they are all true.
+func (tps *TektonTriggerStatus) IsReady() bool {
+	return triggersCondSet.Manage(tps).IsHappy()
+}
+
+// MarkInstallSucceeded marks the InstallationSucceeded status as true.
+func (tps *TektonTriggerStatus) MarkInstallSucceeded() {
+	triggersCondSet.Manage(tps).MarkTrue(InstallSucceeded)
+	if tps.GetCondition(DependenciesInstalled).IsUnknown() {
+		// Assume deps are installed if we're not sure
+		tps.MarkDependenciesInstalled()
+	}
+}
+
+// MarkInstallFailed marks the InstallationSucceeded status as false with the given
+// message.
+func (tps *TektonTriggerStatus) MarkInstallFailed(msg string) {
+	triggersCondSet.Manage(tps).MarkFalse(
+		InstallSucceeded,
+		"Error",
+		"Install failed with message: %s", msg)
+}
+
+// MarkDeploymentsAvailable marks the DeploymentsAvailable status as true.
+func (tps *TektonTriggerStatus) MarkDeploymentsAvailable() {
+	triggersCondSet.Manage(tps).MarkTrue(DeploymentsAvailable)
+}
+
+// MarkDeploymentsNotReady marks the DeploymentsAvailable status as false and calls out
+// it's waiting for deployments.
+func (tps *TektonTriggerStatus) MarkDeploymentsNotReady() {
+	triggersCondSet.Manage(tps).MarkFalse(
+		DeploymentsAvailable,
+		"NotReady",
+		"Waiting on deployments")
+}
+
+// MarkDependenciesInstalled marks the DependenciesInstalled status as true.
+func (tps *TektonTriggerStatus) MarkDependenciesInstalled() {
+	triggersCondSet.Manage(tps).MarkTrue(DependenciesInstalled)
+}
+
+// MarkDependencyInstalling marks the DependenciesInstalled status as false with the
+// given message.
+func (tps *TektonTriggerStatus) MarkDependencyInstalling(msg string) {
+	triggersCondSet.Manage(tps).MarkFalse(
+		DependenciesInstalled,
+		"Installing",
+		"Dependency installing: %s", msg)
+}
+
+// MarkDependencyMissing marks the DependenciesInstalled status as false with the
+// given message.
+func (tps *TektonTriggerStatus) MarkDependencyMissing(msg string) {
+	triggersCondSet.Manage(tps).MarkFalse(
+		DependenciesInstalled,
+		"Error",
+		"Dependency missing: %s", msg)
+}
+
+// GetVersion gets the currently installed version of the component.
+func (tps *TektonTriggerStatus) GetVersion() string {
+	return tps.Version
+}
+
+// SetVersion sets the currently installed version of the component.
+func (tps *TektonTriggerStatus) SetVersion(version string) {
+	tps.Version = version
+}
+
+// GetManifests gets the url links of the manifests.
+func (tps *TektonTriggerStatus) GetManifests() []string {
+	return tps.Manifests
+}
+
+// SetVersion sets the url links of the manifests.
+func (tps *TektonTriggerStatus) SetManifests(manifests []string) {
+	tps.Manifests = manifests
+}
diff --git a/pkg/apis/operator/v1alpha1/tektontrigger_types.go b/pkg/apis/operator/v1alpha1/tektontrigger_types.go
new file mode 100644
index 000000000..e7470a86b
--- /dev/null
+++ b/pkg/apis/operator/v1alpha1/tektontrigger_types.go
@@ -0,0 +1,76 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	duckv1 "knative.dev/pkg/apis/duck/v1"
+)
+
+var (
+	_ TektonComponent     = (*TektonTrigger)(nil)
+	_ TektonComponentSpec = (*TektonTriggerSpec)(nil)
+)
+
+// TektonTrigger is the Schema for the tektontriggers API
+// +genclient
+// +genreconciler:krshapedlogic=false
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// +genclient:nonNamespaced
+type TektonTrigger struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   TektonTriggerSpec   `json:"spec,omitempty"`
+	Status TektonTriggerStatus `json:"status,omitempty"`
+}
+
+// GetSpec implements TektonComponent
+func (tp *TektonTrigger) GetSpec() TektonComponentSpec {
+	return &tp.Spec
+}
+
+// GetStatus implements TektonComponent
+func (tp *TektonTrigger) GetStatus() TektonComponentStatus {
+	return &tp.Status
+}
+
+// TektonTriggerSpec defines the desired state of TektonTrigger
+type TektonTriggerSpec struct {
+	CommonSpec `json:",inline"`
+}
+
+// TektonPipelineStatus defines the observed state of TektonTrigger
+type TektonTriggerStatus struct {
+	duckv1.Status `json:",inline"`
+
+	// The version of the installed release
+	// +optional
+	Version string `json:"version,omitempty"`
+
+	// The url links of the manifests, separated by comma
+	// +optional
+	Manifests []string `json:"manifests,omitempty"`
+}
+
+// TektonTriggersList contains a list of TektonTrigger
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type TektonTriggerList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []TektonTrigger `json:"items"`
+}
diff --git a/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go
index 823bd3417..5a35d992f 100644
--- a/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go
@@ -139,3 +139,103 @@ func (in *TektonPipelineStatus) DeepCopy() *TektonPipelineStatus {
 	in.DeepCopyInto(out)
 	return out
 }
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TektonTrigger) DeepCopyInto(out *TektonTrigger) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	out.Spec = in.Spec
+	in.Status.DeepCopyInto(&out.Status)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TektonTrigger.
+func (in *TektonTrigger) DeepCopy() *TektonTrigger {
+	if in == nil {
+		return nil
+	}
+	out := new(TektonTrigger)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *TektonTrigger) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TektonTriggerList) DeepCopyInto(out *TektonTriggerList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]TektonTrigger, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TektonTriggerList.
+func (in *TektonTriggerList) DeepCopy() *TektonTriggerList {
+	if in == nil {
+		return nil
+	}
+	out := new(TektonTriggerList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *TektonTriggerList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TektonTriggerSpec) DeepCopyInto(out *TektonTriggerSpec) {
+	*out = *in
+	out.CommonSpec = in.CommonSpec
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TektonTriggerSpec.
+func (in *TektonTriggerSpec) DeepCopy() *TektonTriggerSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(TektonTriggerSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TektonTriggerStatus) DeepCopyInto(out *TektonTriggerStatus) {
+	*out = *in
+	in.Status.DeepCopyInto(&out.Status)
+	if in.Manifests != nil {
+		in, out := &in.Manifests, &out.Manifests
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TektonTriggerStatus.
+func (in *TektonTriggerStatus) DeepCopy() *TektonTriggerStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(TektonTriggerStatus)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/pkg/client/clientset/versioned/typed/operator/v1alpha1/fake/fake_operator_client.go b/pkg/client/clientset/versioned/typed/operator/v1alpha1/fake/fake_operator_client.go
index 02ba3498b..c8cbb2598 100644
--- a/pkg/client/clientset/versioned/typed/operator/v1alpha1/fake/fake_operator_client.go
+++ b/pkg/client/clientset/versioned/typed/operator/v1alpha1/fake/fake_operator_client.go
@@ -32,6 +32,10 @@ func (c *FakeOperatorV1alpha1) TektonPipelines() v1alpha1.TektonPipelineInterfac
 	return &FakeTektonPipelines{c}
 }
 
+func (c *FakeOperatorV1alpha1) TektonTriggers() v1alpha1.TektonTriggerInterface {
+	return &FakeTektonTriggers{c}
+}
+
 // RESTClient returns a RESTClient that is used to communicate
 // with API server by this client implementation.
 func (c *FakeOperatorV1alpha1) RESTClient() rest.Interface {
diff --git a/pkg/client/clientset/versioned/typed/operator/v1alpha1/fake/fake_tektontrigger.go b/pkg/client/clientset/versioned/typed/operator/v1alpha1/fake/fake_tektontrigger.go
new file mode 100644
index 000000000..f6898a751
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/operator/v1alpha1/fake/fake_tektontrigger.go
@@ -0,0 +1,131 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	v1alpha1 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	labels "k8s.io/apimachinery/pkg/labels"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	testing "k8s.io/client-go/testing"
+)
+
+// FakeTektonTriggers implements TektonTriggerInterface
+type FakeTektonTriggers struct {
+	Fake *FakeOperatorV1alpha1
+}
+
+var tektontriggersResource = schema.GroupVersionResource{Group: "operator.tekton.dev", Version: "v1alpha1", Resource: "tektontriggers"}
+
+var tektontriggersKind = schema.GroupVersionKind{Group: "operator.tekton.dev", Version: "v1alpha1", Kind: "TektonTrigger"}
+
+// Get takes name of the tektonTrigger, and returns the corresponding tektonTrigger object, and an error if there is any.
+func (c *FakeTektonTriggers) Get(name string, options v1.GetOptions) (result *v1alpha1.TektonTrigger, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewRootGetAction(tektontriggersResource, name), &v1alpha1.TektonTrigger{})
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.TektonTrigger), err
+}
+
+// List takes label and field selectors, and returns the list of TektonTriggers that match those selectors.
+func (c *FakeTektonTriggers) List(opts v1.ListOptions) (result *v1alpha1.TektonTriggerList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewRootListAction(tektontriggersResource, tektontriggersKind, opts), &v1alpha1.TektonTriggerList{})
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v1alpha1.TektonTriggerList{ListMeta: obj.(*v1alpha1.TektonTriggerList).ListMeta}
+	for _, item := range obj.(*v1alpha1.TektonTriggerList).Items {
+		if label.Matches(labels.Set(item.Labels)) {
+			list.Items = append(list.Items, item)
+		}
+	}
+	return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested tektonTriggers.
+func (c *FakeTektonTriggers) Watch(opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewRootWatchAction(tektontriggersResource, opts))
+}
+
+// Create takes the representation of a tektonTrigger and creates it.  Returns the server's representation of the tektonTrigger, and an error, if there is any.
+func (c *FakeTektonTriggers) Create(tektonTrigger *v1alpha1.TektonTrigger) (result *v1alpha1.TektonTrigger, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewRootCreateAction(tektontriggersResource, tektonTrigger), &v1alpha1.TektonTrigger{})
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.TektonTrigger), err
+}
+
+// Update takes the representation of a tektonTrigger and updates it. Returns the server's representation of the tektonTrigger, and an error, if there is any.
+func (c *FakeTektonTriggers) Update(tektonTrigger *v1alpha1.TektonTrigger) (result *v1alpha1.TektonTrigger, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewRootUpdateAction(tektontriggersResource, tektonTrigger), &v1alpha1.TektonTrigger{})
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.TektonTrigger), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeTektonTriggers) UpdateStatus(tektonTrigger *v1alpha1.TektonTrigger) (*v1alpha1.TektonTrigger, error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewRootUpdateSubresourceAction(tektontriggersResource, "status", tektonTrigger), &v1alpha1.TektonTrigger{})
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.TektonTrigger), err
+}
+
+// Delete takes name of the tektonTrigger and deletes it. Returns an error if one occurs.
+func (c *FakeTektonTriggers) Delete(name string, options *v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewRootDeleteAction(tektontriggersResource, name), &v1alpha1.TektonTrigger{})
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeTektonTriggers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
+	action := testing.NewRootDeleteCollectionAction(tektontriggersResource, listOptions)
+
+	_, err := c.Fake.Invokes(action, &v1alpha1.TektonTriggerList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched tektonTrigger.
+func (c *FakeTektonTriggers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TektonTrigger, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewRootPatchSubresourceAction(tektontriggersResource, name, pt, data, subresources...), &v1alpha1.TektonTrigger{})
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.TektonTrigger), err
+}
diff --git a/pkg/client/clientset/versioned/typed/operator/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/operator/v1alpha1/generated_expansion.go
index 962f68e1f..dad89cb2a 100644
--- a/pkg/client/clientset/versioned/typed/operator/v1alpha1/generated_expansion.go
+++ b/pkg/client/clientset/versioned/typed/operator/v1alpha1/generated_expansion.go
@@ -19,3 +19,5 @@ limitations under the License.
 package v1alpha1
 
 type TektonPipelineExpansion interface{}
+
+type TektonTriggerExpansion interface{}
diff --git a/pkg/client/clientset/versioned/typed/operator/v1alpha1/operator_client.go b/pkg/client/clientset/versioned/typed/operator/v1alpha1/operator_client.go
index 36c2ba277..35e7843a0 100644
--- a/pkg/client/clientset/versioned/typed/operator/v1alpha1/operator_client.go
+++ b/pkg/client/clientset/versioned/typed/operator/v1alpha1/operator_client.go
@@ -27,6 +27,7 @@ import (
 type OperatorV1alpha1Interface interface {
 	RESTClient() rest.Interface
 	TektonPipelinesGetter
+	TektonTriggersGetter
 }
 
 // OperatorV1alpha1Client is used to interact with features provided by the operator.tekton.dev group.
@@ -38,6 +39,10 @@ func (c *OperatorV1alpha1Client) TektonPipelines() TektonPipelineInterface {
 	return newTektonPipelines(c)
 }
 
+func (c *OperatorV1alpha1Client) TektonTriggers() TektonTriggerInterface {
+	return newTektonTriggers(c)
+}
+
 // NewForConfig creates a new OperatorV1alpha1Client for the given config.
 func NewForConfig(c *rest.Config) (*OperatorV1alpha1Client, error) {
 	config := *c
diff --git a/pkg/client/clientset/versioned/typed/operator/v1alpha1/tektontrigger.go b/pkg/client/clientset/versioned/typed/operator/v1alpha1/tektontrigger.go
new file mode 100644
index 000000000..7e76e5e6b
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/operator/v1alpha1/tektontrigger.go
@@ -0,0 +1,180 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	"time"
+
+	v1alpha1 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	scheme "github.com/tektoncd/operator/pkg/client/clientset/versioned/scheme"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	rest "k8s.io/client-go/rest"
+)
+
+// TektonTriggersGetter has a method to return a TektonTriggerInterface.
+// A group's client should implement this interface.
+type TektonTriggersGetter interface {
+	TektonTriggers() TektonTriggerInterface
+}
+
+// TektonTriggerInterface has methods to work with TektonTrigger resources.
+type TektonTriggerInterface interface {
+	Create(*v1alpha1.TektonTrigger) (*v1alpha1.TektonTrigger, error)
+	Update(*v1alpha1.TektonTrigger) (*v1alpha1.TektonTrigger, error)
+	UpdateStatus(*v1alpha1.TektonTrigger) (*v1alpha1.TektonTrigger, error)
+	Delete(name string, options *v1.DeleteOptions) error
+	DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
+	Get(name string, options v1.GetOptions) (*v1alpha1.TektonTrigger, error)
+	List(opts v1.ListOptions) (*v1alpha1.TektonTriggerList, error)
+	Watch(opts v1.ListOptions) (watch.Interface, error)
+	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TektonTrigger, err error)
+	TektonTriggerExpansion
+}
+
+// tektonTriggers implements TektonTriggerInterface
+type tektonTriggers struct {
+	client rest.Interface
+}
+
+// newTektonTriggers returns a TektonTriggers
+func newTektonTriggers(c *OperatorV1alpha1Client) *tektonTriggers {
+	return &tektonTriggers{
+		client: c.RESTClient(),
+	}
+}
+
+// Get takes name of the tektonTrigger, and returns the corresponding tektonTrigger object, and an error if there is any.
+func (c *tektonTriggers) Get(name string, options v1.GetOptions) (result *v1alpha1.TektonTrigger, err error) {
+	result = &v1alpha1.TektonTrigger{}
+	err = c.client.Get().
+		Resource("tektontriggers").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do().
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of TektonTriggers that match those selectors.
+func (c *tektonTriggers) List(opts v1.ListOptions) (result *v1alpha1.TektonTriggerList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v1alpha1.TektonTriggerList{}
+	err = c.client.Get().
+		Resource("tektontriggers").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do().
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested tektonTriggers.
+func (c *tektonTriggers) Watch(opts v1.ListOptions) (watch.Interface, error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	opts.Watch = true
+	return c.client.Get().
+		Resource("tektontriggers").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch()
+}
+
+// Create takes the representation of a tektonTrigger and creates it.  Returns the server's representation of the tektonTrigger, and an error, if there is any.
+func (c *tektonTriggers) Create(tektonTrigger *v1alpha1.TektonTrigger) (result *v1alpha1.TektonTrigger, err error) {
+	result = &v1alpha1.TektonTrigger{}
+	err = c.client.Post().
+		Resource("tektontriggers").
+		Body(tektonTrigger).
+		Do().
+		Into(result)
+	return
+}
+
+// Update takes the representation of a tektonTrigger and updates it. Returns the server's representation of the tektonTrigger, and an error, if there is any.
+func (c *tektonTriggers) Update(tektonTrigger *v1alpha1.TektonTrigger) (result *v1alpha1.TektonTrigger, err error) {
+	result = &v1alpha1.TektonTrigger{}
+	err = c.client.Put().
+		Resource("tektontriggers").
+		Name(tektonTrigger.Name).
+		Body(tektonTrigger).
+		Do().
+		Into(result)
+	return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+
+func (c *tektonTriggers) UpdateStatus(tektonTrigger *v1alpha1.TektonTrigger) (result *v1alpha1.TektonTrigger, err error) {
+	result = &v1alpha1.TektonTrigger{}
+	err = c.client.Put().
+		Resource("tektontriggers").
+		Name(tektonTrigger.Name).
+		SubResource("status").
+		Body(tektonTrigger).
+		Do().
+		Into(result)
+	return
+}
+
+// Delete takes name of the tektonTrigger and deletes it. Returns an error if one occurs.
+func (c *tektonTriggers) Delete(name string, options *v1.DeleteOptions) error {
+	return c.client.Delete().
+		Resource("tektontriggers").
+		Name(name).
+		Body(options).
+		Do().
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *tektonTriggers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
+	var timeout time.Duration
+	if listOptions.TimeoutSeconds != nil {
+		timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
+	}
+	return c.client.Delete().
+		Resource("tektontriggers").
+		VersionedParams(&listOptions, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(options).
+		Do().
+		Error()
+}
+
+// Patch applies the patch and returns the patched tektonTrigger.
+func (c *tektonTriggers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TektonTrigger, err error) {
+	result = &v1alpha1.TektonTrigger{}
+	err = c.client.Patch(pt).
+		Resource("tektontriggers").
+		SubResource(subresources...).
+		Name(name).
+		Body(data).
+		Do().
+		Into(result)
+	return
+}
diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go
index 8551d123b..f48a91503 100644
--- a/pkg/client/informers/externalversions/generic.go
+++ b/pkg/client/informers/externalversions/generic.go
@@ -55,6 +55,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
 	// Group=operator.tekton.dev, Version=v1alpha1
 	case v1alpha1.SchemeGroupVersion.WithResource("tektonpipelines"):
 		return &genericInformer{resource: resource.GroupResource(), informer: f.Operator().V1alpha1().TektonPipelines().Informer()}, nil
+	case v1alpha1.SchemeGroupVersion.WithResource("tektontriggers"):
+		return &genericInformer{resource: resource.GroupResource(), informer: f.Operator().V1alpha1().TektonTriggers().Informer()}, nil
 
 	}
 
diff --git a/pkg/client/informers/externalversions/operator/v1alpha1/interface.go b/pkg/client/informers/externalversions/operator/v1alpha1/interface.go
index 6efbd8eda..94dcb28a8 100644
--- a/pkg/client/informers/externalversions/operator/v1alpha1/interface.go
+++ b/pkg/client/informers/externalversions/operator/v1alpha1/interface.go
@@ -26,6 +26,8 @@ import (
 type Interface interface {
 	// TektonPipelines returns a TektonPipelineInformer.
 	TektonPipelines() TektonPipelineInformer
+	// TektonTriggers returns a TektonTriggerInformer.
+	TektonTriggers() TektonTriggerInformer
 }
 
 type version struct {
@@ -43,3 +45,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
 func (v *version) TektonPipelines() TektonPipelineInformer {
 	return &tektonPipelineInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
 }
+
+// TektonTriggers returns a TektonTriggerInformer.
+func (v *version) TektonTriggers() TektonTriggerInformer {
+	return &tektonTriggerInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
+}
diff --git a/pkg/client/informers/externalversions/operator/v1alpha1/tektontrigger.go b/pkg/client/informers/externalversions/operator/v1alpha1/tektontrigger.go
new file mode 100644
index 000000000..45e1a4e7b
--- /dev/null
+++ b/pkg/client/informers/externalversions/operator/v1alpha1/tektontrigger.go
@@ -0,0 +1,88 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	time "time"
+
+	operatorv1alpha1 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	versioned "github.com/tektoncd/operator/pkg/client/clientset/versioned"
+	internalinterfaces "github.com/tektoncd/operator/pkg/client/informers/externalversions/internalinterfaces"
+	v1alpha1 "github.com/tektoncd/operator/pkg/client/listers/operator/v1alpha1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	watch "k8s.io/apimachinery/pkg/watch"
+	cache "k8s.io/client-go/tools/cache"
+)
+
+// TektonTriggerInformer provides access to a shared informer and lister for
+// TektonTriggers.
+type TektonTriggerInformer interface {
+	Informer() cache.SharedIndexInformer
+	Lister() v1alpha1.TektonTriggerLister
+}
+
+type tektonTriggerInformer struct {
+	factory          internalinterfaces.SharedInformerFactory
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// NewTektonTriggerInformer constructs a new informer for TektonTrigger type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewTektonTriggerInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+	return NewFilteredTektonTriggerInformer(client, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredTektonTriggerInformer constructs a new informer for TektonTrigger type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredTektonTriggerInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+	return cache.NewSharedIndexInformer(
+		&cache.ListWatch{
+			ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.OperatorV1alpha1().TektonTriggers().List(options)
+			},
+			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.OperatorV1alpha1().TektonTriggers().Watch(options)
+			},
+		},
+		&operatorv1alpha1.TektonTrigger{},
+		resyncPeriod,
+		indexers,
+	)
+}
+
+func (f *tektonTriggerInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+	return NewFilteredTektonTriggerInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *tektonTriggerInformer) Informer() cache.SharedIndexInformer {
+	return f.factory.InformerFor(&operatorv1alpha1.TektonTrigger{}, f.defaultInformer)
+}
+
+func (f *tektonTriggerInformer) Lister() v1alpha1.TektonTriggerLister {
+	return v1alpha1.NewTektonTriggerLister(f.Informer().GetIndexer())
+}
diff --git a/pkg/client/injection/informers/operator/v1alpha1/tektontrigger/fake/fake.go b/pkg/client/injection/informers/operator/v1alpha1/tektontrigger/fake/fake.go
new file mode 100644
index 000000000..69d0b2038
--- /dev/null
+++ b/pkg/client/injection/informers/operator/v1alpha1/tektontrigger/fake/fake.go
@@ -0,0 +1,40 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	context "context"
+
+	fake "github.com/tektoncd/operator/pkg/client/injection/informers/factory/fake"
+	tektontrigger "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektontrigger"
+	controller "knative.dev/pkg/controller"
+	injection "knative.dev/pkg/injection"
+)
+
+var Get = tektontrigger.Get
+
+func init() {
+	injection.Fake.RegisterInformer(withInformer)
+}
+
+func withInformer(ctx context.Context) (context.Context, controller.Informer) {
+	f := fake.Get(ctx)
+	inf := f.Operator().V1alpha1().TektonTriggers()
+	return context.WithValue(ctx, tektontrigger.Key{}, inf), inf.Informer()
+}
diff --git a/pkg/client/injection/informers/operator/v1alpha1/tektontrigger/tektontrigger.go b/pkg/client/injection/informers/operator/v1alpha1/tektontrigger/tektontrigger.go
new file mode 100644
index 000000000..f6c932631
--- /dev/null
+++ b/pkg/client/injection/informers/operator/v1alpha1/tektontrigger/tektontrigger.go
@@ -0,0 +1,52 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package tektontrigger
+
+import (
+	context "context"
+
+	v1alpha1 "github.com/tektoncd/operator/pkg/client/informers/externalversions/operator/v1alpha1"
+	factory "github.com/tektoncd/operator/pkg/client/injection/informers/factory"
+	controller "knative.dev/pkg/controller"
+	injection "knative.dev/pkg/injection"
+	logging "knative.dev/pkg/logging"
+)
+
+func init() {
+	injection.Default.RegisterInformer(withInformer)
+}
+
+// Key is used for associating the Informer inside the context.Context.
+type Key struct{}
+
+func withInformer(ctx context.Context) (context.Context, controller.Informer) {
+	f := factory.Get(ctx)
+	inf := f.Operator().V1alpha1().TektonTriggers()
+	return context.WithValue(ctx, Key{}, inf), inf.Informer()
+}
+
+// Get extracts the typed informer from the context.
+func Get(ctx context.Context) v1alpha1.TektonTriggerInformer {
+	untyped := ctx.Value(Key{})
+	if untyped == nil {
+		logging.FromContext(ctx).Panic(
+			"Unable to fetch github.com/tektoncd/operator/pkg/client/informers/externalversions/operator/v1alpha1.TektonTriggerInformer from context.")
+	}
+	return untyped.(v1alpha1.TektonTriggerInformer)
+}
diff --git a/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger/controller.go b/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger/controller.go
new file mode 100644
index 000000000..1b702189d
--- /dev/null
+++ b/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger/controller.go
@@ -0,0 +1,142 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package tektontrigger
+
+import (
+	context "context"
+	fmt "fmt"
+	reflect "reflect"
+	strings "strings"
+
+	versionedscheme "github.com/tektoncd/operator/pkg/client/clientset/versioned/scheme"
+	client "github.com/tektoncd/operator/pkg/client/injection/client"
+	tektontrigger "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektontrigger"
+	corev1 "k8s.io/api/core/v1"
+	labels "k8s.io/apimachinery/pkg/labels"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	scheme "k8s.io/client-go/kubernetes/scheme"
+	v1 "k8s.io/client-go/kubernetes/typed/core/v1"
+	record "k8s.io/client-go/tools/record"
+	kubeclient "knative.dev/pkg/client/injection/kube/client"
+	controller "knative.dev/pkg/controller"
+	logging "knative.dev/pkg/logging"
+	reconciler "knative.dev/pkg/reconciler"
+)
+
+const (
+	defaultControllerAgentName = "tektontrigger-controller"
+	defaultFinalizerName       = "tektontriggers.operator.tekton.dev"
+)
+
+// NewImpl returns a controller.Impl that handles queuing and feeding work from
+// the queue through an implementation of controller.Reconciler, delegating to
+// the provided Interface and optional Finalizer methods. OptionsFn is used to return
+// controller.Options to be used but the internal reconciler.
+func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsFn) *controller.Impl {
+	logger := logging.FromContext(ctx)
+
+	// Check the options function input. It should be 0 or 1.
+	if len(optionsFns) > 1 {
+		logger.Fatalf("up to one options function is supported, found %d", len(optionsFns))
+	}
+
+	tektontriggerInformer := tektontrigger.Get(ctx)
+
+	lister := tektontriggerInformer.Lister()
+
+	rec := &reconcilerImpl{
+		LeaderAwareFuncs: reconciler.LeaderAwareFuncs{
+			PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error {
+				all, err := lister.List(labels.Everything())
+				if err != nil {
+					return err
+				}
+				for _, elt := range all {
+					// TODO: Consider letting users specify a filter in options.
+					enq(bkt, types.NamespacedName{
+						Namespace: elt.GetNamespace(),
+						Name:      elt.GetName(),
+					})
+				}
+				return nil
+			},
+		},
+		Client:        client.Get(ctx),
+		Lister:        lister,
+		reconciler:    r,
+		finalizerName: defaultFinalizerName,
+	}
+
+	t := reflect.TypeOf(r).Elem()
+	queueName := fmt.Sprintf("%s.%s", strings.ReplaceAll(t.PkgPath(), "/", "-"), t.Name())
+
+	impl := controller.NewImpl(rec, logger, queueName)
+	agentName := defaultControllerAgentName
+
+	// Pass impl to the options. Save any optional results.
+	for _, fn := range optionsFns {
+		opts := fn(impl)
+		if opts.ConfigStore != nil {
+			rec.configStore = opts.ConfigStore
+		}
+		if opts.FinalizerName != "" {
+			rec.finalizerName = opts.FinalizerName
+		}
+		if opts.AgentName != "" {
+			agentName = opts.AgentName
+		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
+	}
+
+	rec.Recorder = createRecorder(ctx, agentName)
+
+	return impl
+}
+
+func createRecorder(ctx context.Context, agentName string) record.EventRecorder {
+	logger := logging.FromContext(ctx)
+
+	recorder := controller.GetEventRecorder(ctx)
+	if recorder == nil {
+		// Create event broadcaster
+		logger.Debug("Creating event broadcaster")
+		eventBroadcaster := record.NewBroadcaster()
+		watches := []watch.Interface{
+			eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof),
+			eventBroadcaster.StartRecordingToSink(
+				&v1.EventSinkImpl{Interface: kubeclient.Get(ctx).CoreV1().Events("")}),
+		}
+		recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName})
+		go func() {
+			<-ctx.Done()
+			for _, w := range watches {
+				w.Stop()
+			}
+		}()
+	}
+
+	return recorder
+}
+
+func init() {
+	versionedscheme.AddToScheme(scheme.Scheme)
+}
diff --git a/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger/reconciler.go b/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger/reconciler.go
new file mode 100644
index 000000000..e5fcba073
--- /dev/null
+++ b/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger/reconciler.go
@@ -0,0 +1,441 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package tektontrigger
+
+import (
+	context "context"
+	json "encoding/json"
+	fmt "fmt"
+	reflect "reflect"
+
+	v1alpha1 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	versioned "github.com/tektoncd/operator/pkg/client/clientset/versioned"
+	operatorv1alpha1 "github.com/tektoncd/operator/pkg/client/listers/operator/v1alpha1"
+	zap "go.uber.org/zap"
+	v1 "k8s.io/api/core/v1"
+	equality "k8s.io/apimachinery/pkg/api/equality"
+	errors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	labels "k8s.io/apimachinery/pkg/labels"
+	types "k8s.io/apimachinery/pkg/types"
+	sets "k8s.io/apimachinery/pkg/util/sets"
+	record "k8s.io/client-go/tools/record"
+	controller "knative.dev/pkg/controller"
+	kmp "knative.dev/pkg/kmp"
+	logging "knative.dev/pkg/logging"
+	reconciler "knative.dev/pkg/reconciler"
+)
+
+// Interface defines the strongly typed interfaces to be implemented by a
+// controller reconciling v1alpha1.TektonTrigger.
+type Interface interface {
+	// ReconcileKind implements custom logic to reconcile v1alpha1.TektonTrigger. Any changes
+	// to the objects .Status or .Finalizers will be propagated to the stored
+	// object. It is recommended that implementors do not call any update calls
+	// for the Kind inside of ReconcileKind, it is the responsibility of the calling
+	// controller to propagate those properties. The resource passed to ReconcileKind
+	// will always have an empty deletion timestamp.
+	ReconcileKind(ctx context.Context, o *v1alpha1.TektonTrigger) reconciler.Event
+}
+
+// Finalizer defines the strongly typed interfaces to be implemented by a
+// controller finalizing v1alpha1.TektonTrigger.
+type Finalizer interface {
+	// FinalizeKind implements custom logic to finalize v1alpha1.TektonTrigger. Any changes
+	// to the objects .Status or .Finalizers will be ignored. Returning a nil or
+	// Normal type reconciler.Event will allow the finalizer to be deleted on
+	// the resource. The resource passed to FinalizeKind will always have a set
+	// deletion timestamp.
+	FinalizeKind(ctx context.Context, o *v1alpha1.TektonTrigger) reconciler.Event
+}
+
+// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a
+// controller reconciling v1alpha1.TektonTrigger if they want to process resources for which
+// they are not the leader.
+type ReadOnlyInterface interface {
+	// ObserveKind implements logic to observe v1alpha1.TektonTrigger.
+	// This method should not write to the API.
+	ObserveKind(ctx context.Context, o *v1alpha1.TektonTrigger) reconciler.Event
+}
+
+// ReadOnlyFinalizer defines the strongly typed interfaces to be implemented by a
+// controller finalizing v1alpha1.TektonTrigger if they want to process tombstoned resources
+// even when they are not the leader.  Due to the nature of how finalizers are handled
+// there are no guarantees that this will be called.
+type ReadOnlyFinalizer interface {
+	// ObserveFinalizeKind implements custom logic to observe the final state of v1alpha1.TektonTrigger.
+	// This method should not write to the API.
+	ObserveFinalizeKind(ctx context.Context, o *v1alpha1.TektonTrigger) reconciler.Event
+}
+
+type doReconcile func(ctx context.Context, o *v1alpha1.TektonTrigger) reconciler.Event
+
+// reconcilerImpl implements controller.Reconciler for v1alpha1.TektonTrigger resources.
+type reconcilerImpl struct {
+	// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
+	reconciler.LeaderAwareFuncs
+
+	// Client is used to write back status updates.
+	Client versioned.Interface
+
+	// Listers index properties about resources
+	Lister operatorv1alpha1.TektonTriggerLister
+
+	// Recorder is an event recorder for recording Event resources to the
+	// Kubernetes API.
+	Recorder record.EventRecorder
+
+	// configStore allows for decorating a context with config maps.
+	// +optional
+	configStore reconciler.ConfigStore
+
+	// reconciler is the implementation of the business logic of the resource.
+	reconciler Interface
+
+	// finalizerName is the name of the finalizer to reconcile.
+	finalizerName string
+
+	// skipStatusUpdates configures whether or not this reconciler automatically updates
+	// the status of the reconciled resource.
+	skipStatusUpdates bool
+}
+
+// Check that our Reconciler implements controller.Reconciler
+var _ controller.Reconciler = (*reconcilerImpl)(nil)
+
+// Check that our generated Reconciler is always LeaderAware.
+var _ reconciler.LeaderAware = (*reconcilerImpl)(nil)
+
+func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versioned.Interface, lister operatorv1alpha1.TektonTriggerLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler {
+	// Check the options function input. It should be 0 or 1.
+	if len(options) > 1 {
+		logger.Fatalf("up to one options struct is supported, found %d", len(options))
+	}
+
+	// Fail fast when users inadvertently implement the other LeaderAware interface.
+	// For the typed reconcilers, Promote shouldn't take any arguments.
+	if _, ok := r.(reconciler.LeaderAware); ok {
+		logger.Fatalf("%T implements the incorrect LeaderAware interface.  Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r)
+	}
+	// TODO: Consider validating when folks implement ReadOnlyFinalizer, but not Finalizer.
+
+	rec := &reconcilerImpl{
+		LeaderAwareFuncs: reconciler.LeaderAwareFuncs{
+			PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error {
+				all, err := lister.List(labels.Everything())
+				if err != nil {
+					return err
+				}
+				for _, elt := range all {
+					// TODO: Consider letting users specify a filter in options.
+					enq(bkt, types.NamespacedName{
+						Namespace: elt.GetNamespace(),
+						Name:      elt.GetName(),
+					})
+				}
+				return nil
+			},
+		},
+		Client:        client,
+		Lister:        lister,
+		Recorder:      recorder,
+		reconciler:    r,
+		finalizerName: defaultFinalizerName,
+	}
+
+	for _, opts := range options {
+		if opts.ConfigStore != nil {
+			rec.configStore = opts.ConfigStore
+		}
+		if opts.FinalizerName != "" {
+			rec.finalizerName = opts.FinalizerName
+		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
+	}
+
+	return rec
+}
+
+// Reconcile implements controller.Reconciler
+func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
+	logger := logging.FromContext(ctx)
+
+	// Initialize the reconciler state. This will convert the namespace/name
+	// string into a distinct namespace and name, determin if this instance of
+	// the reconciler is the leader, and any additional interfaces implemented
+	// by the reconciler. Returns an error is the resource key is invalid.
+	s, err := newState(key, r)
+	if err != nil {
+		logger.Errorf("invalid resource key: %s", key)
+		return nil
+	}
+
+	// If we are not the leader, and we don't implement either ReadOnly
+	// observer interfaces, then take a fast-path out.
+	if s.isNotLeaderNorObserver() {
+		return nil
+	}
+
+	// If configStore is set, attach the frozen configuration to the context.
+	if r.configStore != nil {
+		ctx = r.configStore.ToContext(ctx)
+	}
+
+	// Add the recorder to context.
+	ctx = controller.WithEventRecorder(ctx, r.Recorder)
+
+	// Get the resource with this namespace/name.
+
+	getter := r.Lister
+
+	original, err := getter.Get(s.name)
+
+	if errors.IsNotFound(err) {
+		// The resource may no longer exist, in which case we stop processing.
+		logger.Debugf("resource %q no longer exists", key)
+		return nil
+	} else if err != nil {
+		return err
+	}
+
+	// Don't modify the informers copy.
+	resource := original.DeepCopy()
+
+	var reconcileEvent reconciler.Event
+
+	name, do := s.reconcileMethodFor(resource)
+	// Append the target method to the logger.
+	logger = logger.With(zap.String("targetMethod", name))
+	switch name {
+	case reconciler.DoReconcileKind:
+		// Append the target method to the logger.
+		logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
+
+		// Set and update the finalizer on resource if r.reconciler
+		// implements Finalizer.
+		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
+			return fmt.Errorf("failed to set finalizers: %w", err)
+		}
+
+		// Reconcile this copy of the resource and then write back any status
+		// updates regardless of whether the reconciliation errored out.
+		reconcileEvent = do(ctx, resource)
+
+	case reconciler.DoFinalizeKind:
+		// For finalizing reconcilers, if this resource being marked for deletion
+		// and reconciled cleanly (nil or normal event), remove the finalizer.
+		reconcileEvent = do(ctx, resource)
+
+		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
+			return fmt.Errorf("failed to clear finalizers: %w", err)
+		}
+
+	case reconciler.DoObserveKind, reconciler.DoObserveFinalizeKind:
+		// Observe any changes to this resource, since we are not the leader.
+		reconcileEvent = do(ctx, resource)
+
+	}
+
+	// Synchronize the status.
+	switch {
+	case r.skipStatusUpdates:
+		// This reconciler implementation is configured to skip resource updates.
+		// This may mean this reconciler does not observe spec, but reconciles external changes.
+	case equality.Semantic.DeepEqual(original.Status, resource.Status):
+		// If we didn't change anything then don't call updateStatus.
+		// This is important because the copy we loaded from the injectionInformer's
+		// cache may be stale and we don't want to overwrite a prior update
+		// to status with this stale state.
+	case !s.isLeader:
+		// High-availability reconcilers may have many replicas watching the resource, but only
+		// the elected leader is expected to write modifications.
+		logger.Warn("Saw status changes when we aren't the leader!")
+	default:
+		if err = r.updateStatus(ctx, original, resource); err != nil {
+			logger.Warnw("Failed to update resource status", zap.Error(err))
+			r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed",
+				"Failed to update status for %q: %v", resource.Name, err)
+			return err
+		}
+	}
+
+	// Report the reconciler event, if any.
+	if reconcileEvent != nil {
+		var event *reconciler.ReconcilerEvent
+		if reconciler.EventAs(reconcileEvent, &event) {
+			logger.Infow("Returned an event", zap.Any("event", reconcileEvent))
+			r.Recorder.Eventf(resource, event.EventType, event.Reason, event.Format, event.Args...)
+
+			// the event was wrapped inside an error, consider the reconciliation as failed
+			if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent {
+				return reconcileEvent
+			}
+			return nil
+		}
+
+		logger.Errorw("Returned an error", zap.Error(reconcileEvent))
+		r.Recorder.Event(resource, v1.EventTypeWarning, "InternalError", reconcileEvent.Error())
+		return reconcileEvent
+	}
+
+	return nil
+}
+
+func (r *reconcilerImpl) updateStatus(ctx context.Context, existing *v1alpha1.TektonTrigger, desired *v1alpha1.TektonTrigger) error {
+	existing = existing.DeepCopy()
+	return reconciler.RetryUpdateConflicts(func(attempts int) (err error) {
+		// The first iteration tries to use the injectionInformer's state, subsequent attempts fetch the latest state via API.
+		if attempts > 0 {
+
+			getter := r.Client.OperatorV1alpha1().TektonTriggers()
+
+			existing, err = getter.Get(desired.Name, metav1.GetOptions{})
+			if err != nil {
+				return err
+			}
+		}
+
+		// If there's nothing to update, just return.
+		if reflect.DeepEqual(existing.Status, desired.Status) {
+			return nil
+		}
+
+		if diff, err := kmp.SafeDiff(existing.Status, desired.Status); err == nil && diff != "" {
+			logging.FromContext(ctx).Debugf("Updating status with: %s", diff)
+		}
+
+		existing.Status = desired.Status
+
+		updater := r.Client.OperatorV1alpha1().TektonTriggers()
+
+		_, err = updater.UpdateStatus(existing)
+		return err
+	})
+}
+
+// updateFinalizersFiltered will update the Finalizers of the resource.
+// TODO: this method could be generic and sync all finalizers. For now it only
+// updates defaultFinalizerName or its override.
+func (r *reconcilerImpl) updateFinalizersFiltered(ctx context.Context, resource *v1alpha1.TektonTrigger) (*v1alpha1.TektonTrigger, error) {
+
+	getter := r.Lister
+
+	actual, err := getter.Get(resource.Name)
+	if err != nil {
+		return resource, err
+	}
+
+	// Don't modify the informers copy.
+	existing := actual.DeepCopy()
+
+	var finalizers []string
+
+	// If there's nothing to update, just return.
+	existingFinalizers := sets.NewString(existing.Finalizers...)
+	desiredFinalizers := sets.NewString(resource.Finalizers...)
+
+	if desiredFinalizers.Has(r.finalizerName) {
+		if existingFinalizers.Has(r.finalizerName) {
+			// Nothing to do.
+			return resource, nil
+		}
+		// Add the finalizer.
+		finalizers = append(existing.Finalizers, r.finalizerName)
+	} else {
+		if !existingFinalizers.Has(r.finalizerName) {
+			// Nothing to do.
+			return resource, nil
+		}
+		// Remove the finalizer.
+		existingFinalizers.Delete(r.finalizerName)
+		finalizers = existingFinalizers.List()
+	}
+
+	mergePatch := map[string]interface{}{
+		"metadata": map[string]interface{}{
+			"finalizers":      finalizers,
+			"resourceVersion": existing.ResourceVersion,
+		},
+	}
+
+	patch, err := json.Marshal(mergePatch)
+	if err != nil {
+		return resource, err
+	}
+
+	patcher := r.Client.OperatorV1alpha1().TektonTriggers()
+
+	resourceName := resource.Name
+	resource, err = patcher.Patch(resourceName, types.MergePatchType, patch)
+	if err != nil {
+		r.Recorder.Eventf(resource, v1.EventTypeWarning, "FinalizerUpdateFailed",
+			"Failed to update finalizers for %q: %v", resourceName, err)
+	} else {
+		r.Recorder.Eventf(resource, v1.EventTypeNormal, "FinalizerUpdate",
+			"Updated %q finalizers", resource.GetName())
+	}
+	return resource, err
+}
+
+func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1alpha1.TektonTrigger) (*v1alpha1.TektonTrigger, error) {
+	if _, ok := r.reconciler.(Finalizer); !ok {
+		return resource, nil
+	}
+
+	finalizers := sets.NewString(resource.Finalizers...)
+
+	// If this resource is not being deleted, mark the finalizer.
+	if resource.GetDeletionTimestamp().IsZero() {
+		finalizers.Insert(r.finalizerName)
+	}
+
+	resource.Finalizers = finalizers.List()
+
+	// Synchronize the finalizers filtered by r.finalizerName.
+	return r.updateFinalizersFiltered(ctx, resource)
+}
+
+func (r *reconcilerImpl) clearFinalizer(ctx context.Context, resource *v1alpha1.TektonTrigger, reconcileEvent reconciler.Event) (*v1alpha1.TektonTrigger, error) {
+	if _, ok := r.reconciler.(Finalizer); !ok {
+		return resource, nil
+	}
+	if resource.GetDeletionTimestamp().IsZero() {
+		return resource, nil
+	}
+
+	finalizers := sets.NewString(resource.Finalizers...)
+
+	if reconcileEvent != nil {
+		var event *reconciler.ReconcilerEvent
+		if reconciler.EventAs(reconcileEvent, &event) {
+			if event.EventType == v1.EventTypeNormal {
+				finalizers.Delete(r.finalizerName)
+			}
+		}
+	} else {
+		finalizers.Delete(r.finalizerName)
+	}
+
+	resource.Finalizers = finalizers.List()
+
+	// Synchronize the finalizers filtered by r.finalizerName.
+	return r.updateFinalizersFiltered(ctx, resource)
+}
diff --git a/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger/state.go b/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger/state.go
new file mode 100644
index 000000000..ea256befb
--- /dev/null
+++ b/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger/state.go
@@ -0,0 +1,106 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package tektontrigger
+
+import (
+	fmt "fmt"
+
+	v1alpha1 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	types "k8s.io/apimachinery/pkg/types"
+	cache "k8s.io/client-go/tools/cache"
+	reconciler "knative.dev/pkg/reconciler"
+)
+
+// state is used to track the state of a reconciler in a single run.
+type state struct {
+	// Key is the original reconciliation key from the queue.
+	key string
+	// Namespace is the namespace split from the reconciliation key.
+	namespace string
+	// Namespace is the name split from the reconciliation key.
+	name string
+	// reconciler is the reconciler.
+	reconciler Interface
+	// rof is the read only interface cast of the reconciler.
+	roi ReadOnlyInterface
+	// IsROI (Read Only Interface) the reconciler only observes reconciliation.
+	isROI bool
+	// rof is the read only finalizer cast of the reconciler.
+	rof ReadOnlyFinalizer
+	// IsROF (Read Only Finalizer) the reconciler only observes finalize.
+	isROF bool
+	// IsLeader the instance of the reconciler is the elected leader.
+	isLeader bool
+}
+
+func newState(key string, r *reconcilerImpl) (*state, error) {
+	// Convert the namespace/name string into a distinct namespace and name
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		return nil, fmt.Errorf("invalid resource key: %s", key)
+	}
+
+	roi, isROI := r.reconciler.(ReadOnlyInterface)
+	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
+
+	isLeader := r.IsLeaderFor(types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	})
+
+	return &state{
+		key:        key,
+		namespace:  namespace,
+		name:       name,
+		reconciler: r.reconciler,
+		roi:        roi,
+		isROI:      isROI,
+		rof:        rof,
+		isROF:      isROF,
+		isLeader:   isLeader,
+	}, nil
+}
+
+// isNotLeaderNorObserver checks to see if this reconciler with the current
+// state is enabled to do any work or not.
+// isNotLeaderNorObserver returns true when there is no work possible for the
+// reconciler.
+func (s *state) isNotLeaderNorObserver() bool {
+	if !s.isLeader && !s.isROI && !s.isROF {
+		// If we are not the leader, and we don't implement either ReadOnly
+		// interface, then take a fast-path out.
+		return true
+	}
+	return false
+}
+
+func (s *state) reconcileMethodFor(o *v1alpha1.TektonTrigger) (string, doReconcile) {
+	if o.GetDeletionTimestamp().IsZero() {
+		if s.isLeader {
+			return reconciler.DoReconcileKind, s.reconciler.ReconcileKind
+		} else if s.isROI {
+			return reconciler.DoObserveKind, s.roi.ObserveKind
+		}
+	} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
+		return reconciler.DoFinalizeKind, fin.FinalizeKind
+	} else if !s.isLeader && s.isROF {
+		return reconciler.DoObserveFinalizeKind, s.rof.ObserveFinalizeKind
+	}
+	return "unknown", nil
+}
diff --git a/pkg/client/listers/operator/v1alpha1/expansion_generated.go b/pkg/client/listers/operator/v1alpha1/expansion_generated.go
index 6498ee90e..03c475642 100644
--- a/pkg/client/listers/operator/v1alpha1/expansion_generated.go
+++ b/pkg/client/listers/operator/v1alpha1/expansion_generated.go
@@ -21,3 +21,7 @@ package v1alpha1
 // TektonPipelineListerExpansion allows custom methods to be added to
 // TektonPipelineLister.
 type TektonPipelineListerExpansion interface{}
+
+// TektonTriggerListerExpansion allows custom methods to be added to
+// TektonTriggerLister.
+type TektonTriggerListerExpansion interface{}
diff --git a/pkg/client/listers/operator/v1alpha1/tektontrigger.go b/pkg/client/listers/operator/v1alpha1/tektontrigger.go
new file mode 100644
index 000000000..475c9d969
--- /dev/null
+++ b/pkg/client/listers/operator/v1alpha1/tektontrigger.go
@@ -0,0 +1,65 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	v1alpha1 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/client-go/tools/cache"
+)
+
+// TektonTriggerLister helps list TektonTriggers.
+type TektonTriggerLister interface {
+	// List lists all TektonTriggers in the indexer.
+	List(selector labels.Selector) (ret []*v1alpha1.TektonTrigger, err error)
+	// Get retrieves the TektonTrigger from the index for a given name.
+	Get(name string) (*v1alpha1.TektonTrigger, error)
+	TektonTriggerListerExpansion
+}
+
+// tektonTriggerLister implements the TektonTriggerLister interface.
+type tektonTriggerLister struct {
+	indexer cache.Indexer
+}
+
+// NewTektonTriggerLister returns a new TektonTriggerLister.
+func NewTektonTriggerLister(indexer cache.Indexer) TektonTriggerLister {
+	return &tektonTriggerLister{indexer: indexer}
+}
+
+// List lists all TektonTriggers in the indexer.
+func (s *tektonTriggerLister) List(selector labels.Selector) (ret []*v1alpha1.TektonTrigger, err error) {
+	err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+		ret = append(ret, m.(*v1alpha1.TektonTrigger))
+	})
+	return ret, err
+}
+
+// Get retrieves the TektonTrigger from the index for a given name.
+func (s *tektonTriggerLister) Get(name string) (*v1alpha1.TektonTrigger, error) {
+	obj, exists, err := s.indexer.GetByKey(name)
+	if err != nil {
+		return nil, err
+	}
+	if !exists {
+		return nil, errors.NewNotFound(v1alpha1.Resource("tektontrigger"), name)
+	}
+	return obj.(*v1alpha1.TektonTrigger), nil
+}
diff --git a/pkg/reconciler/common/common.go b/pkg/reconciler/common/common.go
new file mode 100644
index 000000000..eec7cc189
--- /dev/null
+++ b/pkg/reconciler/common/common.go
@@ -0,0 +1,55 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package common
+
+import (
+	"fmt"
+
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	pipelineinformer "github.com/tektoncd/operator/pkg/client/informers/externalversions/operator/v1alpha1"
+	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+)
+
+var WatchedResourceName = "cluster"
+
+const (
+	PipelineNotReady = "tekton-pipelines not ready"
+	PipelineNotFound = "tekton-pipelines not installed"
+)
+
+func PipelineReady(informer pipelineinformer.TektonPipelineInformer) (*v1alpha1.TektonPipeline, error) {
+	ppln, err := getPipelineRes(informer)
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			return nil, fmt.Errorf(PipelineNotFound)
+		}
+		return nil, err
+	}
+
+	if len(ppln.Status.Conditions) != 0 {
+		if ppln.Status.Conditions[0].Status != corev1.ConditionTrue {
+			return nil, fmt.Errorf(PipelineNotReady)
+		}
+	}
+	return ppln, nil
+}
+
+func getPipelineRes(informer pipelineinformer.TektonPipelineInformer) (*v1alpha1.TektonPipeline, error) {
+	res, err := informer.Lister().Get(WatchedResourceName)
+	return res, err
+}
diff --git a/pkg/reconciler/common/deployments.go b/pkg/reconciler/common/deployments.go
index 60e5d603f..c40ca2584 100644
--- a/pkg/reconciler/common/deployments.go
+++ b/pkg/reconciler/common/deployments.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/pkg/reconciler/common/extensions.go b/pkg/reconciler/common/extensions.go
index fe05e9f0a..70286411d 100644
--- a/pkg/reconciler/common/extensions.go
+++ b/pkg/reconciler/common/extensions.go
@@ -1,15 +1,19 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
+
 package common
 
 import (
diff --git a/pkg/reconciler/common/install.go b/pkg/reconciler/common/install.go
index 4adda8ae1..a84f55505 100644
--- a/pkg/reconciler/common/install.go
+++ b/pkg/reconciler/common/install.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/pkg/reconciler/common/releases.go b/pkg/reconciler/common/releases.go
index 8ee8ec164..1449a8582 100644
--- a/pkg/reconciler/common/releases.go
+++ b/pkg/reconciler/common/releases.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -86,6 +89,8 @@ func componentDir(instance v1alpha1.TektonComponent) string {
 	switch instance.(type) {
 	case *v1alpha1.TektonPipeline:
 		return filepath.Join(koDataDir, "tekton-pipeline")
+	case *v1alpha1.TektonTrigger:
+		return filepath.Join(koDataDir, "tekton-trigger")
 	}
 	return ""
 }
diff --git a/pkg/reconciler/common/stages.go b/pkg/reconciler/common/stages.go
index 66bde12d4..69def7b91 100644
--- a/pkg/reconciler/common/stages.go
+++ b/pkg/reconciler/common/stages.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/pkg/reconciler/common/transformers.go b/pkg/reconciler/common/transformers.go
index 33847928a..f9019ba43 100644
--- a/pkg/reconciler/common/transformers.go
+++ b/pkg/reconciler/common/transformers.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,7 +18,6 @@ package common
 
 import (
 	"context"
-
 	mf "github.com/manifestival/manifestival"
 
 	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
diff --git a/pkg/reconciler/tektonpipeline/controller.go b/pkg/reconciler/tektonpipeline/controller.go
index 48a1a765f..d6e027b63 100644
--- a/pkg/reconciler/tektonpipeline/controller.go
+++ b/pkg/reconciler/tektonpipeline/controller.go
@@ -1,9 +1,12 @@
 /*
-Copyright 2019 The Knative Authors.
+Copyright 2019 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/pkg/reconciler/tektonpipeline/tektonpipeline.go b/pkg/reconciler/tektonpipeline/tektonpipeline.go
index 8b0883f94..569a99f55 100644
--- a/pkg/reconciler/tektonpipeline/tektonpipeline.go
+++ b/pkg/reconciler/tektonpipeline/tektonpipeline.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2019 The Knative Authors
+Copyright 2019 The Tekton Authors
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -32,8 +32,6 @@ import (
 	pkgreconciler "knative.dev/pkg/reconciler"
 )
 
-var watchedResourceName string = "cluster"
-
 // Reconciler implements controller.Reconciler for Knativeserving resources.
 type Reconciler struct {
 	// kubeClientSet allows us to talk to the k8s for core APIs
@@ -93,9 +91,9 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tp *v1alpha1.TektonPipel
 	tp.Status.ObservedGeneration = tp.Generation
 
 	logger.Infow("Reconciling TektonPipeline", "status", tp.Status)
-	if tp.GetName() != watchedResourceName {
+	if tp.GetName() != common.WatchedResourceName {
 		msg := fmt.Sprintf("Resource ignored, Expected Name: %s, Got Name: %s",
-			watchedResourceName,
+			common.WatchedResourceName,
 			tp.GetName(),
 		)
 		logger.Error(msg)
diff --git a/pkg/reconciler/tektontrigger/controller.go b/pkg/reconciler/tektontrigger/controller.go
new file mode 100644
index 000000000..4dbb1a3d8
--- /dev/null
+++ b/pkg/reconciler/tektontrigger/controller.go
@@ -0,0 +1,84 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tektontrigger
+
+import (
+	"context"
+
+	"github.com/go-logr/zapr"
+	mfc "github.com/manifestival/client-go-client"
+	mf "github.com/manifestival/manifestival"
+	"go.uber.org/zap"
+	"k8s.io/client-go/tools/cache"
+
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	operatorclient "github.com/tektoncd/operator/pkg/client/injection/client"
+	tektonPipelineinformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektonpipeline"
+	tektonTriggerinformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektontrigger"
+	tektonTriggerreconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger"
+	"github.com/tektoncd/operator/pkg/reconciler/common"
+	kubeclient "knative.dev/pkg/client/injection/kube/client"
+	deploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment"
+	"knative.dev/pkg/configmap"
+	"knative.dev/pkg/controller"
+	"knative.dev/pkg/injection"
+	"knative.dev/pkg/logging"
+)
+
+// NewController initializes the controller and is called by the generated code
+// Registers eventhandlers to enqueue events
+func NewController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
+	return NewExtendedController(common.NoExtension)(ctx, cmw)
+}
+
+// NewExtendedController returns a controller extended to a specific platform
+func NewExtendedController(generator common.ExtensionGenerator) injection.ControllerConstructor {
+	return func(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
+		tektonPipelineInformer := tektonPipelineinformer.Get(ctx)
+		tektonTriggersInformer := tektonTriggerinformer.Get(ctx)
+		deploymentInformer := deploymentinformer.Get(ctx)
+		kubeClient := kubeclient.Get(ctx)
+		logger := logging.FromContext(ctx)
+
+		mfclient, err := mfc.NewClient(injection.GetConfig(ctx))
+		if err != nil {
+			logger.Fatalw("Error creating client from injected config", zap.Error(err))
+		}
+		mflogger := zapr.NewLogger(logger.Named("manifestival").Desugar())
+		manifest, _ := mf.ManifestFrom(mf.Slice{}, mf.UseClient(mfclient), mf.UseLogger(mflogger))
+
+		c := &Reconciler{
+			kubeClientSet:     kubeClient,
+			operatorClientSet: operatorclient.Get(ctx),
+			extension:         generator(ctx),
+			manifest:          manifest,
+			pipelineInformer:  tektonPipelineInformer,
+		}
+		impl := tektonTriggerreconciler.NewImpl(ctx, c)
+
+		logger.Info("Setting up event handlers")
+
+		tektonTriggersInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
+
+		deploymentInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{
+			FilterFunc: controller.FilterControllerGVK(v1alpha1.SchemeGroupVersion.WithKind("TektonTriggers")),
+			Handler:    controller.HandleAll(impl.EnqueueControllerOf),
+		})
+
+		return impl
+	}
+}
diff --git a/pkg/reconciler/tektontrigger/tektontrigger.go b/pkg/reconciler/tektontrigger/tektontrigger.go
new file mode 100644
index 000000000..c2c136449
--- /dev/null
+++ b/pkg/reconciler/tektontrigger/tektontrigger.go
@@ -0,0 +1,152 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tektontrigger
+
+import (
+	"context"
+	"fmt"
+
+	mf "github.com/manifestival/manifestival"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/kubernetes"
+
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	clientset "github.com/tektoncd/operator/pkg/client/clientset/versioned"
+	pipelineinformer "github.com/tektoncd/operator/pkg/client/informers/externalversions/operator/v1alpha1"
+	tektontriggerreconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger"
+	"github.com/tektoncd/operator/pkg/reconciler/common"
+	"knative.dev/pkg/logging"
+	pkgreconciler "knative.dev/pkg/reconciler"
+)
+
+// Reconciler implements controller.Reconciler for Knativeserving resources.
+type Reconciler struct {
+	// kubeClientSet allows us to talk to the k8s for core APIs
+	kubeClientSet kubernetes.Interface
+	// operatorClientSet allows us to configure operator objects
+	operatorClientSet clientset.Interface
+	// manifest is empty, but with a valid client and logger. all
+	// manifests are immutable, and any created during reconcile are
+	// expected to be appended to this one, obviating the passing of
+	// client & logger
+	manifest mf.Manifest
+	// Platform-specific behavior to affect the transform
+	extension common.Extension
+
+	pipelineInformer pipelineinformer.TektonPipelineInformer
+}
+
+// Check that our Reconciler implements controller.Reconciler
+var _ tektontriggerreconciler.Interface = (*Reconciler)(nil)
+var _ tektontriggerreconciler.Finalizer = (*Reconciler)(nil)
+
+var watchedResourceName = "trigger"
+
+// FinalizeKind removes all resources after deletion of a TektonTriggers.
+func (r *Reconciler) FinalizeKind(ctx context.Context, original *v1alpha1.TektonTrigger) pkgreconciler.Event {
+	logger := logging.FromContext(ctx)
+
+	// List all TektonTriggers to determine if cluster-scoped resources should be deleted.
+	tps, err := r.operatorClientSet.OperatorV1alpha1().TektonTriggers().List(metav1.ListOptions{})
+	if err != nil {
+		return fmt.Errorf("failed to list all TektonTriggers: %w", err)
+	}
+
+	for _, tp := range tps.Items {
+		if tp.GetDeletionTimestamp().IsZero() {
+			// Not deleting all TektonTriggers. Nothing to do here.
+			return nil
+		}
+	}
+
+	if err := r.extension.Finalize(ctx, original); err != nil {
+		logger.Error("Failed to finalize platform resources", err)
+	}
+	logger.Info("Deleting cluster-scoped resources")
+	manifest, err := r.installed(ctx, original)
+	if err != nil {
+		logger.Error("Unable to fetch installed manifest; no cluster-scoped resources will be finalized", err)
+		return nil
+	}
+	if err := common.Uninstall(manifest); err != nil {
+		logger.Error("Failed to finalize platform resources", err)
+	}
+	return nil
+}
+
+// ReconcileKind compares the actual state with the desired, and attempts to
+// converge the two.
+func (r *Reconciler) ReconcileKind(ctx context.Context, tt *v1alpha1.TektonTrigger) pkgreconciler.Event {
+	logger := logging.FromContext(ctx)
+	tt.Status.InitializeConditions()
+	tt.Status.ObservedGeneration = tt.Generation
+
+	logger.Infow("Reconciling TektonTriggers", "status", tt.Status)
+
+	if tt.GetName() != watchedResourceName {
+		msg := fmt.Sprintf("Resource ignored, Expected Name: %s, Got Name: %s",
+			watchedResourceName,
+			tt.GetName(),
+		)
+		logger.Error(msg)
+		tt.GetStatus().MarkInstallFailed(msg)
+		return nil
+	}
+
+	//find the valid tekton-pipeline installation
+	if _, err := common.PipelineReady(r.pipelineInformer); err != nil {
+		if err.Error() == common.PipelineNotReady {
+			tt.Status.MarkDependencyInstalling("tekton-pipelines is still installing")
+			// wait for pipeline status to change
+			return fmt.Errorf(common.PipelineNotReady)
+		}
+		// (tektonpipeline.opeator.tekton.dev instance not available yet)
+		tt.Status.MarkDependencyMissing("tekton-pipelines does not exist")
+		return err
+	}
+	tt.Status.MarkDependenciesInstalled()
+
+	if err := r.extension.Reconcile(ctx, tt); err != nil {
+		return err
+	}
+	stages := common.Stages{
+		common.AppendTarget,
+		r.transform,
+		common.Install,
+		common.CheckDeployments,
+	}
+	manifest := r.manifest.Append()
+	return stages.Execute(ctx, &manifest, tt)
+}
+
+// transform mutates the passed manifest to one with common, component
+// and platform transformations applied
+func (r *Reconciler) transform(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error {
+	instance := comp.(*v1alpha1.TektonTrigger)
+	extra := []mf.Transformer{}
+	extra = append(extra, r.extension.Transformers(instance)...)
+	return common.Transform(ctx, manifest, instance, extra...)
+}
+
+func (r *Reconciler) installed(ctx context.Context, instance v1alpha1.TektonComponent) (*mf.Manifest, error) {
+	// Create new, empty manifest with valid client and logger
+	installed := r.manifest.Append()
+	// TODO: add ingress, etc
+	stages := common.Stages{common.AppendInstalled, r.transform}
+	err := stages.Execute(ctx, &installed, instance)
+	return &installed, err
+}


Test patch:
diff --git a/pkg/apis/operator/v1alpha1/register_test.go b/pkg/apis/operator/v1alpha1/register_test.go
index 2d8b5548e..a6e4b11ac 100644
--- a/pkg/apis/operator/v1alpha1/register_test.go
+++ b/pkg/apis/operator/v1alpha1/register_test.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,7 +24,7 @@ import (
 
 func TestRegisterHelpers(t *testing.T) {
 	if got, want := Resource("TektonPipeline"), "TektonPipeline."+GroupName; got.String() != want {
-		t.Errorf("Resource(PodAutoscaler) = %v, want %v", got.String(), want)
+		t.Errorf("Resource(TektonPipeline) = %v, want %v", got.String(), want)
 	}
 
 	if got, want := SchemeGroupVersion.String(), GroupName+"/v1alpha1"; got != want {
diff --git a/pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle_test.go b/pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle_test.go
index 6d4951b91..7a27a2b94 100644
--- a/pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle_test.go
+++ b/pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle_test.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2019 The Knative Authors
+Copyright 2020 The Tekton Authors
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle_test.go b/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle_test.go
new file mode 100644
index 000000000..6026a5dd4
--- /dev/null
+++ b/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle_test.go
@@ -0,0 +1,138 @@
+/*
+Copyright 2020 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+	"testing"
+
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	apistest "knative.dev/pkg/apis/testing"
+)
+
+func TestTektonTriggerGroupVersionKind(t *testing.T) {
+	r := &TektonTrigger{}
+	want := schema.GroupVersionKind{
+		Group:   GroupName,
+		Version: SchemaVersion,
+		Kind:    KindTektonTrigger,
+	}
+	if got := r.GroupVersionKind(); got != want {
+		t.Errorf("got: %v, want: %v", got, want)
+	}
+}
+
+func TestTektonTriggerHappyPath(t *testing.T) {
+	tt := &TektonTriggerStatus{}
+	tt.InitializeConditions()
+
+	apistest.CheckConditionOngoing(tt, DependenciesInstalled, t)
+	apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionOngoing(tt, InstallSucceeded, t)
+
+	// Install succeeds.
+	tt.MarkInstallSucceeded()
+	// Dependencies are assumed successful too.
+	apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t)
+	apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionSucceeded(tt, InstallSucceeded, t)
+
+	// Deployments are not available at first.
+	tt.MarkDeploymentsNotReady()
+	apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t)
+	apistest.CheckConditionFailed(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionSucceeded(tt, InstallSucceeded, t)
+	if ready := tt.IsReady(); ready {
+		t.Errorf("tt.IsReady() = %v, want false", ready)
+	}
+
+	// Deployments become ready and we're good.
+	tt.MarkDeploymentsAvailable()
+	apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t)
+	apistest.CheckConditionSucceeded(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionSucceeded(tt, InstallSucceeded, t)
+	if ready := tt.IsReady(); !ready {
+		t.Errorf("tt.IsReady() = %v, want true", ready)
+	}
+}
+
+func TestTektonTriggerErrorPath(t *testing.T) {
+	tt := &TektonTriggerStatus{}
+	tt.InitializeConditions()
+
+	apistest.CheckConditionOngoing(tt, DependenciesInstalled, t)
+	apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionOngoing(tt, InstallSucceeded, t)
+
+	// Install fails.
+	tt.MarkInstallFailed("test")
+	apistest.CheckConditionOngoing(tt, DependenciesInstalled, t)
+	apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionFailed(tt, InstallSucceeded, t)
+
+	// Dependencies are installing.
+	tt.MarkDependencyInstalling("testing")
+	apistest.CheckConditionFailed(tt, DependenciesInstalled, t)
+	apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionFailed(tt, InstallSucceeded, t)
+
+	// Install now succeeds.
+	tt.MarkInstallSucceeded()
+	apistest.CheckConditionFailed(tt, DependenciesInstalled, t)
+	apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionSucceeded(tt, InstallSucceeded, t)
+	if ready := tt.IsReady(); ready {
+		t.Errorf("tt.IsReady() = %v, want false", ready)
+	}
+
+	// Deployments become ready
+	tt.MarkDeploymentsAvailable()
+	apistest.CheckConditionFailed(tt, DependenciesInstalled, t)
+	apistest.CheckConditionSucceeded(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionSucceeded(tt, InstallSucceeded, t)
+	if ready := tt.IsReady(); ready {
+		t.Errorf("tt.IsReady() = %v, want false", ready)
+	}
+
+	// Finally, dependencies become available.
+	tt.MarkDependenciesInstalled()
+	apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t)
+	apistest.CheckConditionSucceeded(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionSucceeded(tt, InstallSucceeded, t)
+	if ready := tt.IsReady(); !ready {
+		t.Errorf("tt.IsReady() = %v, want true", ready)
+	}
+}
+
+func TestTektonTriggerExternalDependency(t *testing.T) {
+	tt := &TektonTriggerStatus{}
+	tt.InitializeConditions()
+
+	// External marks dependency as failed.
+	tt.MarkDependencyMissing("test")
+
+	// Install succeeds.
+	tt.MarkInstallSucceeded()
+	apistest.CheckConditionFailed(tt, DependenciesInstalled, t)
+	apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionSucceeded(tt, InstallSucceeded, t)
+
+	// Dependencies are now ready.
+	tt.MarkDependenciesInstalled()
+	apistest.CheckConditionSucceeded(tt, DependenciesInstalled, t)
+	apistest.CheckConditionOngoing(tt, DeploymentsAvailable, t)
+	apistest.CheckConditionSucceeded(tt, InstallSucceeded, t)
+}
diff --git a/pkg/reconciler/common/extensions_test.go b/pkg/reconciler/common/extensions_test.go
index 1ef5bade3..d78ec51fd 100644
--- a/pkg/reconciler/common/extensions_test.go
+++ b/pkg/reconciler/common/extensions_test.go
@@ -1,9 +1,12 @@
 /*
 Copyright 2020 The Tekton Authors
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.