Repository: tektoncd/operator
Pull request #1086 (2022-10-03 16:26:32)

Bump helm/chart-releaser-action from 1.4.0 to 1.4.1
Bumps [helm/chart-releaser-action](https://github.com/helm/chart-releaser-action) from 1.4.0 to 1.4.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/helm/chart-releaser-action/releases">helm/chart-releaser-action's releases</a>.</em></p>
<blockquote>
<h2>v1.4.1</h2>
<h2>What's Changed</h2>
<ul>
<li>document required permissions by <a href="https://github.com/davidkarlsen"><code>@​davidkarlsen</code></a> in <a href="https://github-redirect.dependabot.com/helm/chart-releaser-action/pull/114">helm/chart-releaser-action#114</a></li>
<li>bump cr to v1.4.1 by <a href="https://github.com/cpanato"><code>@​cpanato</code></a> in <a href="https://github-redirect.dependabot.com/helm/chart-releaser-action/pull/121">helm/chart-releaser-action#121</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/helm/chart-releaser-action/compare/v1.4.0...v1.4.1">https://github.com/helm/chart-releaser-action/compare/v1.4.0...v1.4.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/helm/chart-releaser-action/commit/98bccfd32b0f76149d188912ac8e45ddd3f8695f"><code>98bccfd</code></a> bump cr to v1.4.1 (<a href="https://github-redirect.dependabot.com/helm/chart-releaser-action/issues/121">#121</a>)</li>
<li><a href="https://github.com/helm/chart-releaser-action/commit/6d70e8546cd3d35bc8981b932b83df0acc531bf4"><code>6d70e85</code></a> document required permissions (<a href="https://github-redirect.dependabot.com/helm/chart-releaser-action/issues/114">#114</a>)</li>
<li>See full diff in <a href="https://github.com/helm/chart-releaser-action/compare/v1.4.0...v1.4.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=helm/chart-releaser-action&package-manager=github_actions&previous-version=1.4.0&new-version=1.4.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

tekton-robot: @dependabot[bot]: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

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 **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 **houshengbo** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @houshengbo` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://prow.tekton.dev/command-help?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":["houshengbo"]} -->
tekton-robot: Hi @dependabot[bot]. Thanks for your PR.

I'm waiting for a [tektoncd](https://github.com/orgs/tektoncd/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://github.com/orgs/tektoncd/people) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=tektoncd%2Foperator).

<details>

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>


Patch:
diff --git a/.github/issue-template.md b/.github/issue-template.md
deleted file mode 100644
index e9044a11b..000000000
--- a/.github/issue-template.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-name: Issue Template
-about: Template for both bug reports and feature requests
----
-
-# Expected Behavior
-
-# Actual Behavior
-
-# Steps to Reproduce the Problem
-
-1.
-2.
-3.
-
-# Additional Info
diff --git a/.github/workflows/helm-release.yaml b/.github/workflows/helm-release.yaml
index 7182a0996..2db38b55c 100644
--- a/.github/workflows/helm-release.yaml
+++ b/.github/workflows/helm-release.yaml
@@ -27,7 +27,7 @@ jobs:
           version: v3.8.1
 
       - name: Run chart-releaser
-        uses: helm/chart-releaser-action@v1.4.0
+        uses: helm/chart-releaser-action@v1.4.1
         with:
           charts_dir: .
         env:
diff --git a/components.yaml b/components.yaml
index 6d936d9af..df629f300 100644
--- a/components.yaml
+++ b/components.yaml
@@ -9,7 +9,7 @@ hub:
   version: v1.10.0
 pipeline:
   github: tektoncd/pipeline
-  version: v0.40.1
+  version: v0.40.2
 pipelines-as-code:
   github: openshift-pipelines/pipelines-as-code
   version: v0.12.0
diff --git a/pkg/apis/operator/v1alpha1/common.go b/pkg/apis/operator/v1alpha1/common.go
index 029d4a474..621cf30a0 100644
--- a/pkg/apis/operator/v1alpha1/common.go
+++ b/pkg/apis/operator/v1alpha1/common.go
@@ -47,12 +47,23 @@ type TektonComponentSpec interface {
 
 // TektonComponentStatus is a common interface for status mutations of all known types.
 type TektonComponentStatus interface {
+	MarkNotReady(string)
+	MarkInstallerSetReady()
+
+	MarkInstallerSetNotReady(string)
+	MarkInstallerSetAvailable()
+
+	MarkPreReconcilerFailed(string)
+	MarkPostReconcilerFailed(string)
+
 	// GetVersion gets the currently installed version of the component.
 	GetVersion() string
 	// SetVersion sets the currently installed version of the component.
 	SetVersion(version string)
 	// IsReady return true if all conditions are satisfied
 	IsReady() bool
+	// ConditionAccessor Implement to interact with a condition
+	apis.ConditionAccessor
 }
 
 // CommonSpec unifies common fields and functions on the Spec.
diff --git a/pkg/apis/operator/v1alpha1/const.go b/pkg/apis/operator/v1alpha1/const.go
index d17609cbd..2843d99b7 100644
--- a/pkg/apis/operator/v1alpha1/const.go
+++ b/pkg/apis/operator/v1alpha1/const.go
@@ -51,6 +51,7 @@ const (
 	DbSecretHash           = "operator.tekton.dev/db-secret-hash"
 
 	UpgradePending = "upgrade pending"
+	Reinstalling   = "reinstalling"
 
 	RequeueDelay = 10 * time.Second
 )
diff --git a/pkg/apis/operator/v1alpha1/tektonaddon_types.go b/pkg/apis/operator/v1alpha1/tektonaddon_types.go
index 1157be7df..eea21c87e 100644
--- a/pkg/apis/operator/v1alpha1/tektonaddon_types.go
+++ b/pkg/apis/operator/v1alpha1/tektonaddon_types.go
@@ -66,6 +66,11 @@ type TektonAddonStatus struct {
 	AddonsInstallerSet map[string]string `json:"installerSets,omitempty"`
 }
 
+func (in *TektonAddonStatus) MarkInstallerSetAvailable() {
+	//TODO implement me
+	panic("implement me")
+}
+
 // Addon defines the field to customize Addon component
 type Addon struct {
 	// Params is the list of params passed for Addon customization
diff --git a/pkg/apis/operator/v1alpha1/tektonconfig_types.go b/pkg/apis/operator/v1alpha1/tektonconfig_types.go
index 1ad5e93f4..61fc6a629 100644
--- a/pkg/apis/operator/v1alpha1/tektonconfig_types.go
+++ b/pkg/apis/operator/v1alpha1/tektonconfig_types.go
@@ -114,6 +114,31 @@ type TektonConfigStatus struct {
 	TektonInstallerSet map[string]string `json:"tektonInstallerSets,omitempty"`
 }
 
+func (in *TektonConfigStatus) MarkInstallerSetReady() {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (in *TektonConfigStatus) MarkInstallerSetNotReady(s string) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (in *TektonConfigStatus) MarkInstallerSetAvailable() {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (in *TektonConfigStatus) MarkPreReconcilerFailed(s string) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (in *TektonConfigStatus) MarkPostReconcilerFailed(s string) {
+	//TODO implement me
+	panic("implement me")
+}
+
 // TektonConfigList contains a list of TektonConfig
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 type TektonConfigList struct {
diff --git a/pkg/apis/operator/v1alpha1/tektonhub_types.go b/pkg/apis/operator/v1alpha1/tektonhub_types.go
index 066764bd6..9d18595f2 100644
--- a/pkg/apis/operator/v1alpha1/tektonhub_types.go
+++ b/pkg/apis/operator/v1alpha1/tektonhub_types.go
@@ -93,6 +93,21 @@ type TektonHubStatus struct {
 	HubInstallerSet map[string]string `json:"hubInstallerSets,omitempty"`
 }
 
+func (in *TektonHubStatus) MarkInstallerSetReady() {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (in *TektonHubStatus) MarkInstallerSetNotReady(s string) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (in *TektonHubStatus) MarkInstallerSetAvailable() {
+	//TODO implement me
+	panic("implement me")
+}
+
 // TektonHubList contains a list of TektonHub
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 type TektonHubList struct {
diff --git a/pkg/apis/operator/v1alpha1/tektonresult_types.go b/pkg/apis/operator/v1alpha1/tektonresult_types.go
index 6f8d65be8..c7cad1a8e 100644
--- a/pkg/apis/operator/v1alpha1/tektonresult_types.go
+++ b/pkg/apis/operator/v1alpha1/tektonresult_types.go
@@ -67,6 +67,16 @@ type TektonResultStatus struct {
 	TektonInstallerSet string `json:"tektonInstallerSet,omitempty"`
 }
 
+func (in *TektonResultStatus) MarkPreReconcilerFailed(s string) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (in *TektonResultStatus) MarkPostReconcilerFailed(s string) {
+	//TODO implement me
+	panic("implement me")
+}
+
 // TektonResultsList contains a list of TektonResult
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 type TektonResultList struct {
diff --git a/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle.go b/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle.go
index 604131562..97a5567d0 100644
--- a/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle.go
+++ b/pkg/apis/operator/v1alpha1/tektontrigger_lifecycle.go
@@ -60,6 +60,10 @@ func (tts *TektonTriggerStatus) IsReady() bool {
 	return triggersCondSet.Manage(tts).IsHappy()
 }
 
+func (tts *TektonTriggerStatus) IsNewInstallation() bool {
+	return tts.Status.GetCondition(apis.ConditionReady).IsUnknown()
+}
+
 func (tts *TektonTriggerStatus) MarkPreReconcilerComplete() {
 	triggersCondSet.Manage(tts).MarkTrue(PreReconciler)
 }
@@ -135,18 +139,6 @@ func (tts *TektonTriggerStatus) MarkDependencyMissing(msg string) {
 		"Dependencies are missing: %s", msg)
 }
 
-// TODO: below methods are not required for TektonTrigger
-// but as extension implements TektonComponent we need to define them
-// this will be removed
-
-func (tts *TektonTriggerStatus) GetTektonInstallerSet() string {
-	return tts.TektonInstallerSet
-}
-
-func (tts *TektonTriggerStatus) SetTektonInstallerSet(installerSet string) {
-	tts.TektonInstallerSet = installerSet
-}
-
 func (tts *TektonTriggerStatus) GetVersion() string {
 	return tts.Version
 }
@@ -154,7 +146,3 @@ func (tts *TektonTriggerStatus) GetVersion() string {
 func (tts *TektonTriggerStatus) SetVersion(version string) {
 	tts.Version = version
 }
-
-func (tts *TektonTriggerStatus) MarkInstallSucceeded() {
-	panic("implement me")
-}
diff --git a/pkg/reconciler/common/common.go b/pkg/reconciler/common/common.go
index df48c1d22..1fb62b574 100644
--- a/pkg/reconciler/common/common.go
+++ b/pkg/reconciler/common/common.go
@@ -19,10 +19,12 @@ package common
 import (
 	"context"
 	"fmt"
+	"strings"
 
 	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
 	informer "github.com/tektoncd/operator/pkg/client/informers/externalversions/operator/v1alpha1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"knative.dev/pkg/apis"
 )
 
 var (
@@ -83,11 +85,7 @@ func TriggerReady(informer informer.TektonTriggerInformer) (*v1alpha1.TektonTrig
 		}
 		return nil, err
 	}
-	upgradePending, err := CheckUpgradePending(trigger)
-	if err != nil {
-		return nil, err
-	}
-	if upgradePending {
+	if strings.Contains(trigger.GetStatus().GetCondition(apis.ConditionReady).Message, v1alpha1.UpgradePending) {
 		return nil, v1alpha1.DEPENDENCY_UPGRADE_PENDING_ERR
 	}
 	if !trigger.Status.IsReady() {
diff --git a/pkg/reconciler/common/initcontroller.go b/pkg/reconciler/common/initcontroller.go
index ac2574f7d..6d957e086 100644
--- a/pkg/reconciler/common/initcontroller.go
+++ b/pkg/reconciler/common/initcontroller.go
@@ -22,7 +22,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/go-logr/zapr"
 	mfc "github.com/manifestival/client-go-client"
 	mf "github.com/manifestival/manifestival"
 	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
@@ -59,9 +58,8 @@ func (ctrl Controller) InitController(ctx context.Context, opts PayloadOptions)
 	if err != nil {
 		ctrl.Logger.Fatalw("Error creating client from injected config", zap.Error(err))
 	}
-	mflogger := zapr.NewLogger(ctrl.Logger.Named("manifestival").Desugar())
 
-	manifest, err := mf.ManifestFrom(mf.Slice{}, mf.UseClient(mfclient), mf.UseLogger(mflogger))
+	manifest, err := mf.ManifestFrom(mf.Slice{}, mf.UseClient(mfclient))
 	if err != nil {
 		ctrl.Logger.Fatalw("Error creating initial manifest", zap.Error(err))
 	}
@@ -76,7 +74,7 @@ func (ctrl Controller) InitController(ctx context.Context, opts PayloadOptions)
 	releaseVersion, err = FetchVersionFromConfigMap(manifest, ctrl.VersionConfigMap)
 	if err != nil {
 		if IsFetchVersionError(err) {
-			ctrl.Logger.Warnf("failed to read version information from ConfigMap %s", ctrl.VersionConfigMap, err)
+			ctrl.Logger.Warnf("failed to read version information from ConfigMap %s: %v", ctrl.VersionConfigMap, err)
 			releaseVersion = ReleaseVersionUnknown
 		} else {
 			ctrl.Logger.Fatalw("Error while reading ConfigMap", zap.Error(err))
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/check.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/check.go
new file mode 100644
index 000000000..4bdb323a7
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/check.go
@@ -0,0 +1,156 @@
+/*
+Copyright 2022 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 client
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"github.com/tektoncd/operator/pkg/reconciler/shared/hash"
+	"go.uber.org/zap"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/apimachinery/pkg/selection"
+	"knative.dev/pkg/logging"
+)
+
+func (i *InstallerSetClient) CheckSet(ctx context.Context, comp v1alpha1.TektonComponent, isType string) ([]v1alpha1.TektonInstallerSet, error) {
+	logger := logging.FromContext(ctx)
+
+	labelSelector := labels.NewSelector()
+	createdReq, _ := labels.NewRequirement(v1alpha1.CreatedByKey, selection.Equals, []string{i.resourceKind})
+	if createdReq != nil {
+		labelSelector = labelSelector.Add(*createdReq)
+	}
+	typeReq, _ := labels.NewRequirement(v1alpha1.InstallerSetType, selection.Equals, []string{isType})
+	if typeReq != nil {
+		labelSelector = labelSelector.Add(*typeReq)
+	}
+
+	logger.Infof("%v/%v: checking installer sets with labels: %v", i.resourceKind, isType, labelSelector.String())
+
+	is, err := i.clientSet.List(ctx, v1.ListOptions{LabelSelector: labelSelector.String()})
+	if err != nil {
+		return nil, err
+	}
+
+	logger.Infof("%v/%v: found %v installer sets", i.resourceKind, isType, len(is.Items))
+
+	iSets := is.Items
+
+	if len(iSets) == 0 {
+		logger.Infof("%v/%v: installer sets not found", i.resourceKind, isType)
+		return nil, ErrNotFound
+	}
+
+	if len(iSets) == 1 {
+		if iSets[0].DeletionTimestamp != nil {
+			return iSets, ErrSetsInDeletionState
+		}
+	} else {
+		if iSets[0].DeletionTimestamp != nil || iSets[1].DeletionTimestamp != nil {
+			return iSets, ErrSetsInDeletionState
+		}
+	}
+
+	switch isType {
+	case InstallerTypeMain:
+		if err := verifyMainInstallerSets(iSets); err != nil {
+			logger.Errorf("%v/%v: failed to verify main sets: %v", i.resourceKind, isType, err)
+			return iSets, err
+		}
+	case InstallerTypePre, InstallerTypePost:
+		if len(iSets) != 1 {
+			logger.Errorf("%v/%v: found multiple sets, expected one", i.resourceKind, isType)
+			return iSets, ErrInvalidState
+		}
+	case InstallerTypeCustom:
+		// TODO
+	default:
+		return nil, fmt.Errorf("invalid installerSet type")
+	}
+
+	if err := verifyMeta(i.resourceKind, isType, logger, iSets[0], comp, i.releaseVersion); err != nil {
+		logger.Errorf("%v/%v: meta check failed for installer type: %v", i.resourceKind, isType, err)
+		return iSets, err
+	}
+	logger.Infof("%v/%v: meta check passed", i.resourceKind, isType)
+
+	return iSets, nil
+}
+
+func verifyMainInstallerSets(iSets []v1alpha1.TektonInstallerSet) error {
+	if len(iSets) != 2 {
+		return ErrInvalidState
+	}
+	var static, deployment bool
+	if strings.Contains(iSets[0].GetName(), InstallerSubTypeStatic) ||
+		strings.Contains(iSets[1].GetName(), InstallerSubTypeStatic) {
+		static = true
+	}
+	if strings.Contains(iSets[0].GetName(), InstallerSubTypeDeployment) ||
+		strings.Contains(iSets[1].GetName(), InstallerSubTypeDeployment) {
+		deployment = true
+	}
+	if !(static && deployment) {
+		return ErrInvalidState
+	}
+	return nil
+}
+
+func verifyMeta(resourceKind, isType string, logger *zap.SugaredLogger, set v1alpha1.TektonInstallerSet, comp v1alpha1.TektonComponent, releaseVersion string) error {
+	// Release Version Check
+	logger.Infof("%v/%v: release version check", resourceKind, isType)
+
+	rVel, ok := set.GetLabels()[v1alpha1.ReleaseVersionKey]
+	if !ok {
+		return ErrInvalidState
+	}
+	if rVel != releaseVersion {
+		return ErrVersionDifferent
+	}
+
+	// Target namespace check
+	logger.Infof("%v/%v: target namespace check", resourceKind, isType)
+
+	targetNamespace, ok := set.GetAnnotations()[v1alpha1.TargetNamespaceKey]
+	if !ok {
+		return ErrInvalidState
+	}
+	if targetNamespace != comp.GetSpec().GetTargetNamespace() {
+		return ErrNsDifferent
+	}
+
+	// Spec Hash Check
+	logger.Infof("%v/%v: spec hash check", resourceKind, isType)
+
+	expectedHash, err := hash.Compute(comp.GetSpec())
+	if err != nil {
+		return err
+	}
+	onClusterHash, ok := set.GetAnnotations()[v1alpha1.LastAppliedHashKey]
+	if !ok {
+		return ErrInvalidState
+	}
+	if onClusterHash != expectedHash {
+		return ErrUpdateRequired
+	}
+
+	return nil
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/cleanup.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/cleanup.go
new file mode 100644
index 000000000..cbbfd333c
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/cleanup.go
@@ -0,0 +1,136 @@
+/*
+Copyright 2022 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 client
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/apimachinery/pkg/selection"
+	"knative.dev/pkg/logging"
+)
+
+var deletePropagationPolicy = v1.DeletePropagationForeground
+
+func (i *InstallerSetClient) CleanupAll(ctx context.Context) error {
+	labelSelector := labels.NewSelector()
+	createdReq, _ := labels.NewRequirement(v1alpha1.CreatedByKey, selection.Equals, []string{i.resourceKind})
+	if createdReq != nil {
+		labelSelector = labelSelector.Add(*createdReq)
+	}
+	err := i.clientSet.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{
+		LabelSelector: labelSelector.String(),
+	})
+	return err
+}
+
+func (i *InstallerSetClient) CleanupMainSet(ctx context.Context) error {
+	logger := logging.FromContext(ctx).With("kind", i.resourceKind, "type", InstallerTypeMain)
+
+	labelSelector := labels.NewSelector()
+	createdReq, _ := labels.NewRequirement(v1alpha1.CreatedByKey, selection.Equals, []string{i.resourceKind})
+	if createdReq != nil {
+		labelSelector = labelSelector.Add(*createdReq)
+	}
+	typeReq, _ := labels.NewRequirement(v1alpha1.InstallerSetType, selection.Equals, []string{InstallerTypeMain})
+	if typeReq != nil {
+		labelSelector = labelSelector.Add(*typeReq)
+	}
+
+	list, err := i.clientSet.List(ctx, metav1.ListOptions{LabelSelector: labelSelector.String()})
+	if err != nil {
+		return err
+	}
+
+	if len(list.Items) != 2 {
+		logger.Error("found more than 2 installerSet for main, something fishy, cleaning up all")
+		return i.CleanupAll(ctx)
+	}
+
+	var static, deploy *v1alpha1.TektonInstallerSet
+	for i, is := range list.Items {
+		if strings.Contains(is.GetName(), InstallerSubTypeStatic) {
+			static = &list.Items[i]
+		} else {
+			deploy = &list.Items[i]
+		}
+	}
+
+	// delete static installerSet first and then deployment one
+	logger.Infof("deleting main-static installer set: %s", static.GetName())
+	err = i.clientSet.Delete(ctx, static.GetName(), metav1.DeleteOptions{
+		PropagationPolicy: &deletePropagationPolicy,
+	})
+	if err != nil {
+		return fmt.Errorf("failed to delete main-static installer set for %s", static.GetName())
+	}
+
+	logger.Infof("deleting main-deployment installer set: %s", deploy.GetName())
+	err = i.clientSet.Delete(ctx, deploy.GetName(), metav1.DeleteOptions{
+		PropagationPolicy: &deletePropagationPolicy,
+	})
+	if err != nil {
+		return fmt.Errorf("failed to delete main-deployment installer set for %s", deploy.GetName())
+	}
+	return err
+}
+
+func (i *InstallerSetClient) CleanupPreSet(ctx context.Context) error {
+	return i.Cleanup(ctx, InstallerTypePre)
+}
+
+func (i *InstallerSetClient) CleanupPostSet(ctx context.Context) error {
+	return i.Cleanup(ctx, InstallerTypePost)
+}
+
+func (i *InstallerSetClient) Cleanup(ctx context.Context, isType string) error {
+	logger := logging.FromContext(ctx).With("kind", i.resourceKind, "type", isType)
+
+	labelSelector := labels.NewSelector()
+	createdReq, _ := labels.NewRequirement(v1alpha1.CreatedByKey, selection.Equals, []string{i.resourceKind})
+	if createdReq != nil {
+		labelSelector = labelSelector.Add(*createdReq)
+	}
+	typeReq, _ := labels.NewRequirement(v1alpha1.InstallerSetType, selection.Equals, []string{isType})
+	if typeReq != nil {
+		labelSelector = labelSelector.Add(*typeReq)
+	}
+
+	list, err := i.clientSet.List(ctx, metav1.ListOptions{LabelSelector: labelSelector.String()})
+	if err != nil {
+		return err
+	}
+
+	if len(list.Items) != 1 {
+		logger.Errorf("found more than 1 installerSet for %s something fishy, cleaning up all", isType)
+		return i.CleanupAll(ctx)
+	}
+
+	logger.Infof("deleting %s installer set: %s", isType, list.Items[0].GetName())
+	err = i.clientSet.Delete(ctx, list.Items[0].GetName(), metav1.DeleteOptions{
+		PropagationPolicy: &deletePropagationPolicy,
+	})
+	if err != nil {
+		return fmt.Errorf("failed to delete %s set: %s", isType, list.Items[0].GetName())
+	}
+	return err
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/client.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/client.go
new file mode 100644
index 000000000..f461ed0cf
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/client.go
@@ -0,0 +1,69 @@
+/*
+Copyright 2022 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 client
+
+import (
+	"context"
+	"fmt"
+
+	mf "github.com/manifestival/manifestival"
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	clientSet "github.com/tektoncd/operator/pkg/client/clientset/versioned/typed/operator/v1alpha1"
+)
+
+const (
+	InstallerSubTypeStatic     = "static"
+	InstallerSubTypeDeployment = "deployment"
+
+	InstallerTypeMain   = "main"
+	InstallerTypePre    = "pre"
+	InstallerTypePost   = "post"
+	InstallerTypeCustom = "custom"
+)
+
+var (
+	ErrInvalidState        = fmt.Errorf("installer sets in invalid state")
+	ErrNotFound            = fmt.Errorf("installer sets not found")
+	ErrVersionDifferent    = fmt.Errorf("installer sets release version doesn't match")
+	ErrNsDifferent         = fmt.Errorf("installer sets target namespace doesn't match")
+	ErrUpdateRequired      = fmt.Errorf("installer sets needs to be updated")
+	ErrSetsInDeletionState = fmt.Errorf("installer sets are in deletion state, will come back")
+)
+
+type FilterAndTransform func(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) (*mf.Manifest, error)
+
+type InstallerSetClient struct {
+	clientSet          clientSet.TektonInstallerSetInterface
+	releaseVersion     string
+	componentVersion   string
+	resourceKind       string
+	filterAndTransform FilterAndTransform
+	manifest           *mf.Manifest
+	metrics            Metrics
+}
+
+func NewInstallerSetClient(clientSet clientSet.TektonInstallerSetInterface, manifest *mf.Manifest, releaseVersion, componentVersion string, resourceKind string, filterAndTransform FilterAndTransform, metrics Metrics) *InstallerSetClient {
+	return &InstallerSetClient{
+		clientSet:          clientSet,
+		releaseVersion:     releaseVersion,
+		resourceKind:       resourceKind,
+		filterAndTransform: filterAndTransform,
+		manifest:           manifest,
+		metrics:            metrics,
+		componentVersion:   componentVersion,
+	}
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/create.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/create.go
new file mode 100644
index 000000000..2ad4d2576
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/create.go
@@ -0,0 +1,127 @@
+/*
+Copyright 2022 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 client
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	mf "github.com/manifestival/manifestival"
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"github.com/tektoncd/operator/pkg/reconciler/shared/hash"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"knative.dev/pkg/logging"
+)
+
+func (i *InstallerSetClient) Create(ctx context.Context, comp v1alpha1.TektonComponent, manifest *mf.Manifest, isType string) ([]v1alpha1.TektonInstallerSet, error) {
+	logger := logging.FromContext(ctx).With("kind", i.resourceKind, "type", isType)
+
+	switch isType {
+	case InstallerTypeMain:
+		sets, err := i.makeMainSets(ctx, comp, manifest)
+		if err != nil {
+			logger.Errorf("installer set creation failed for main type: %v", err)
+			return sets, err
+		}
+		return sets, nil
+
+	case InstallerTypePre, InstallerTypePost:
+		kind := strings.ToLower(strings.TrimPrefix(i.resourceKind, "Tekton"))
+		isName := fmt.Sprintf("%s-%s-", kind, isType)
+
+		iS, err := i.makeInstallerSet(ctx, comp, manifest, isName, isType)
+		if err != nil {
+			return nil, err
+		}
+
+		iS, err = i.clientSet.Create(ctx, iS, metav1.CreateOptions{})
+		if err != nil {
+			return nil, err
+		}
+		return []v1alpha1.TektonInstallerSet{*iS}, nil
+
+	case InstallerTypeCustom:
+	// TODO
+
+	default:
+		return nil, fmt.Errorf("invalid installer set type")
+	}
+
+	return nil, nil
+}
+
+func (i *InstallerSetClient) makeMainSets(ctx context.Context, comp v1alpha1.TektonComponent, manifest *mf.Manifest) ([]v1alpha1.TektonInstallerSet, error) {
+	staticManifest := manifest.Filter(mf.Not(mf.ByKind("Deployment")))
+	deploymentManifest := manifest.Filter(mf.ByKind("Deployment"))
+
+	kind := strings.ToLower(strings.TrimPrefix(i.resourceKind, "Tekton"))
+	staticName := fmt.Sprintf("%s-%s-%s-", kind, InstallerTypeMain, InstallerSubTypeStatic)
+
+	staticIS, err := i.makeInstallerSet(ctx, comp, &staticManifest, staticName, InstallerTypeMain)
+	if err != nil {
+		return nil, err
+	}
+	staticIS, err = i.clientSet.Create(ctx, staticIS, metav1.CreateOptions{})
+	if err != nil {
+		return nil, err
+	}
+
+	deployName := fmt.Sprintf("%s-%s-%s-", kind, InstallerTypeMain, InstallerSubTypeDeployment)
+	deploymentIS, err := i.makeInstallerSet(ctx, comp, &deploymentManifest, deployName, InstallerTypeMain)
+	if err != nil {
+		return nil, err
+	}
+
+	deploymentIS, err = i.clientSet.Create(ctx, deploymentIS, metav1.CreateOptions{})
+	if err != nil {
+		return nil, err
+	}
+	return []v1alpha1.TektonInstallerSet{*staticIS, *deploymentIS}, nil
+}
+
+func (i *InstallerSetClient) makeInstallerSet(ctx context.Context, comp v1alpha1.TektonComponent, manifest *mf.Manifest, isName, isType string) (*v1alpha1.TektonInstallerSet, error) {
+	specHash, err := hash.Compute(comp.GetSpec())
+	if err != nil {
+		return nil, err
+	}
+
+	transformedMf, err := i.filterAndTransform(ctx, manifest, comp)
+	if err != nil {
+		return nil, err
+	}
+
+	ownerRef := *metav1.NewControllerRef(comp, v1alpha1.SchemeGroupVersion.WithKind(i.resourceKind))
+	return &v1alpha1.TektonInstallerSet{
+		ObjectMeta: metav1.ObjectMeta{
+			GenerateName: isName,
+			Labels: map[string]string{
+				v1alpha1.CreatedByKey:      i.resourceKind,
+				v1alpha1.ReleaseVersionKey: i.releaseVersion,
+				v1alpha1.InstallerSetType:  isType,
+			},
+			Annotations: map[string]string{
+				v1alpha1.TargetNamespaceKey: comp.GetSpec().GetTargetNamespace(),
+				v1alpha1.LastAppliedHashKey: specHash,
+			},
+			OwnerReferences: []metav1.OwnerReference{ownerRef},
+		},
+		Spec: v1alpha1.TektonInstallerSetSpec{
+			Manifests: transformedMf.Resources(),
+		},
+	}, nil
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/fake/client.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/fake/client.go
new file mode 100644
index 000000000..48cf7676f
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/fake/client.go
@@ -0,0 +1,91 @@
+/*
+Copyright 2022 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 fake
+
+import (
+	"context"
+
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	v1alpha12 "github.com/tektoncd/operator/pkg/client/clientset/versioned/typed/operator/v1alpha1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/watch"
+)
+
+type fakeClient struct {
+	resource map[string]*v1alpha1.TektonInstallerSet
+}
+
+func NewFakeISClient() v1alpha12.TektonInstallerSetInterface {
+	return &fakeClient{
+		resource: map[string]*v1alpha1.TektonInstallerSet{},
+	}
+}
+
+func (f fakeClient) Create(ctx context.Context, tektonInstallerSet *v1alpha1.TektonInstallerSet, opts metav1.CreateOptions) (*v1alpha1.TektonInstallerSet, error) {
+	tektonInstallerSet.SetName(tektonInstallerSet.GenerateName + "test")
+	if _, ok := f.resource[tektonInstallerSet.GetName()]; ok {
+		return nil, errors.NewAlreadyExists(schema.GroupResource{
+			Group:    v1alpha1.GroupName,
+			Resource: v1alpha1.KindTektonInstallerSet,
+		}, tektonInstallerSet.GetName())
+	}
+	f.resource[tektonInstallerSet.GetName()] = tektonInstallerSet
+	return tektonInstallerSet, nil
+}
+
+func (f fakeClient) Update(ctx context.Context, tektonInstallerSet *v1alpha1.TektonInstallerSet, opts metav1.UpdateOptions) (*v1alpha1.TektonInstallerSet, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (f fakeClient) UpdateStatus(ctx context.Context, tektonInstallerSet *v1alpha1.TektonInstallerSet, opts metav1.UpdateOptions) (*v1alpha1.TektonInstallerSet, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (f fakeClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (f fakeClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (f fakeClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1alpha1.TektonInstallerSet, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (f fakeClient) List(ctx context.Context, opts metav1.ListOptions) (*v1alpha1.TektonInstallerSetList, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (f fakeClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (f fakeClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1alpha1.TektonInstallerSet, err error) {
+	//TODO implement me
+	panic("implement me")
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/main.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/main.go
new file mode 100644
index 000000000..fc930a80e
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/main.go
@@ -0,0 +1,98 @@
+/*
+Copyright 2022 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
+
+    hcompp://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 client
+
+import (
+	"context"
+
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"knative.dev/pkg/logging"
+)
+
+const (
+	metricsNew     = "NewInstall"
+	metricsUpgrade = "Upgrade"
+)
+
+func (i *InstallerSetClient) MainSet(ctx context.Context, comp v1alpha1.TektonComponent) error {
+	logger := logging.FromContext(ctx)
+	setType := InstallerTypeMain
+
+	sets, err := i.CheckSet(ctx, comp, InstallerTypeMain)
+	if err == nil {
+		logger.Infof("%v/%v: found %v installer sets", i.resourceKind, setType, len(sets))
+	}
+
+	switch err {
+	case ErrNotFound:
+		logger.Infof("%v/%v: installer set not found, creating", i.resourceKind, setType)
+		sets, err = i.Create(ctx, comp, i.manifest, InstallerTypeMain)
+		if err != nil {
+			return nil
+		}
+		if comp.GetStatus().GetCondition(v1alpha1.InstallerSetAvailable).IsUnknown() {
+			i.metrics.LogMetrics(metricsNew, i.componentVersion, logger)
+		}
+
+	case ErrInvalidState, ErrNsDifferent, ErrVersionDifferent:
+		logger.Infof("%v/%v: installer set not in valid state : %v, cleaning up!", i.resourceKind, setType, err)
+		if err := i.CleanupMainSet(ctx); err != nil {
+			logger.Errorf("%v/%v: failed to cleanup main installer set: %v", i.resourceKind, setType, err)
+			return nil
+		}
+		if err == ErrVersionDifferent {
+			i.metrics.LogMetrics(metricsUpgrade, i.componentVersion, logger)
+			markComponentStatus(comp, v1alpha1.UpgradePending)
+		} else {
+			markComponentStatus(comp, v1alpha1.Reinstalling)
+		}
+		logger.Infof("%v/%v: returning, will create main installer sets in further reconcile", i.resourceKind, setType)
+		return v1alpha1.REQUEUE_EVENT_AFTER
+
+	case ErrUpdateRequired:
+		logger.Infof("%v/%v: updating installer set", i.resourceKind, setType)
+		sets, err = i.Update(ctx, comp, sets, i.manifest, InstallerTypeMain)
+		if err != nil {
+			logger.Errorf("%v/%v: update failed : %v", i.resourceKind, setType, err)
+			return nil
+		}
+	case ErrSetsInDeletionState:
+		logger.Infof("%v/%v: %v", i.resourceKind, setType, err)
+		return v1alpha1.REQUEUE_EVENT_AFTER
+	}
+
+	//Mark InstallerSet Available
+	comp.GetStatus().MarkInstallerSetAvailable()
+
+	for _, set := range sets {
+		if !set.Status.IsReady() {
+			logger.Infof("%v/%v: installer set %v no yet ready, wait !", i.resourceKind, setType, set.GetName())
+			return nil
+		}
+	}
+
+	//Mark InstallerSet Ready
+	comp.GetStatus().MarkInstallerSetReady()
+	return nil
+}
+
+func markComponentStatus(comp v1alpha1.TektonComponent, status string) {
+	comp.GetStatus().MarkInstallerSetNotReady(status)
+	comp.GetStatus().MarkPreReconcilerFailed(status)
+	comp.GetStatus().MarkPostReconcilerFailed(status)
+	comp.GetStatus().MarkNotReady(status)
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/metrics.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/metrics.go
new file mode 100644
index 000000000..56989bd1b
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/metrics.go
@@ -0,0 +1,23 @@
+/*
+Copyright 2022 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 client
+
+import "go.uber.org/zap"
+
+type Metrics interface {
+	LogMetrics(string, string, *zap.SugaredLogger)
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/update.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/update.go
new file mode 100644
index 000000000..8dc1711a0
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/update.go
@@ -0,0 +1,127 @@
+/*
+Copyright 2022 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 client
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	mf "github.com/manifestival/manifestival"
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"github.com/tektoncd/operator/pkg/reconciler/shared/hash"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/util/retry"
+	"knative.dev/pkg/logging"
+)
+
+func (i *InstallerSetClient) Update(ctx context.Context, comp v1alpha1.TektonComponent, toBeUpdatedIS []v1alpha1.TektonInstallerSet, manifest *mf.Manifest, isType string) ([]v1alpha1.TektonInstallerSet, error) {
+	logger := logging.FromContext(ctx).With("kind", i.resourceKind, "type", isType)
+
+	switch isType {
+	case InstallerTypeMain:
+		sets, err := i.updateMainSets(ctx, comp, toBeUpdatedIS, manifest)
+		if err != nil {
+			logger.Errorf("installer set update failed for main type: %v", err)
+			return sets, err
+		}
+		return sets, nil
+
+	case InstallerTypePre, InstallerTypePost:
+		logger.Infof("updating installer set: %v", toBeUpdatedIS[0].GetName())
+		updatedSet, err := i.updateSet(ctx, comp, toBeUpdatedIS[0], manifest)
+		if err != nil {
+			return nil, fmt.Errorf("failed to update installerset : %v", err)
+		}
+		logger.Infof("updated installer set: %v", toBeUpdatedIS[0].GetName())
+		return []v1alpha1.TektonInstallerSet{*updatedSet}, nil
+
+	case InstallerTypeCustom:
+	// TODO
+
+	default:
+		return nil, fmt.Errorf("invalid installer set type")
+	}
+
+	return nil, nil
+}
+
+func (i *InstallerSetClient) updateMainSets(ctx context.Context, comp v1alpha1.TektonComponent, toBeUpdatedIS []v1alpha1.TektonInstallerSet, manifest *mf.Manifest) ([]v1alpha1.TektonInstallerSet, error) {
+	logger := logging.FromContext(ctx)
+	logger.Infof("updating main installersets for %v", i.resourceKind)
+
+	staticManifest := manifest.Filter(mf.Not(mf.ByKind("Deployment")))
+	deploymentManifest := manifest.Filter(mf.ByKind("Deployment"))
+
+	var updatedSets []v1alpha1.TektonInstallerSet
+
+	for _, is := range toBeUpdatedIS {
+		logger.Infof("updating installer set: %v", is.GetName())
+
+		var manifest *mf.Manifest
+		if strings.Contains(is.GetName(), InstallerSubTypeStatic) {
+			manifest = &staticManifest
+		} else {
+			manifest = &deploymentManifest
+		}
+
+		updatedSet, err := i.updateSet(ctx, comp, is, manifest)
+		if err != nil {
+			return nil, fmt.Errorf("failed to update installerset : %v", err)
+		}
+
+		logger.Infof("updated installer set: %v", is.GetName())
+		updatedSets = append(updatedSets, *updatedSet)
+	}
+	return updatedSets, nil
+}
+
+func (i *InstallerSetClient) updateSet(ctx context.Context, comp v1alpha1.TektonComponent, set v1alpha1.TektonInstallerSet, manifest *mf.Manifest) (*v1alpha1.TektonInstallerSet, error) {
+	var updatedSet *v1alpha1.TektonInstallerSet
+	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
+		onCluster, err := i.clientSet.Get(ctx, set.GetName(), metav1.GetOptions{})
+		if err != nil {
+			return err
+		}
+
+		manifest, err = i.filterAndTransform(ctx, manifest, comp)
+		if err != nil {
+			return err
+		}
+
+		specHash, err := hash.Compute(comp.GetSpec())
+		if err != nil {
+			return err
+		}
+
+		current := onCluster.GetAnnotations()
+		current[v1alpha1.LastAppliedHashKey] = specHash
+		onCluster.SetAnnotations(current)
+
+		onCluster.Spec.Manifests = manifest.Resources()
+
+		updatedSet, err = i.clientSet.Update(ctx, onCluster, metav1.UpdateOptions{})
+		if err != nil {
+			return err
+		}
+		return nil
+	})
+	if retryErr != nil {
+		return nil, retryErr
+	}
+	return updatedSet, nil
+}
diff --git a/pkg/reconciler/kubernetes/tektonpipeline/tektonpipeline.go b/pkg/reconciler/kubernetes/tektonpipeline/tektonpipeline.go
index 7f705e9df..61222de36 100644
--- a/pkg/reconciler/kubernetes/tektonpipeline/tektonpipeline.go
+++ b/pkg/reconciler/kubernetes/tektonpipeline/tektonpipeline.go
@@ -235,7 +235,8 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tp *v1alpha1.TektonPipel
 
 		if lastAppliedHash != expectedSpecHash {
 			manifest := r.manifest
-			if err := r.transform(ctx, &manifest, tp); err != nil {
+			manifest, err := r.transform(ctx, &manifest, tp)
+			if err != nil {
 				logger.Error("manifest transformation failed:  ", err)
 				return err
 			}
@@ -273,8 +274,8 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tp *v1alpha1.TektonPipel
 		return v1alpha1.REQUEUE_EVENT_AFTER
 	} else if ready.Status == corev1.ConditionFalse {
 		tp.Status.MarkInstallerSetNotReady(ready.Message)
-		manifest := r.manifest
-		if err := r.transform(ctx, &manifest, tp); err != nil {
+		manifest, err := r.transform(ctx, &r.manifest, tp)
+		if err != nil {
 			logger.Error("manifest transformation failed:  ", err)
 			return err
 		}
@@ -330,8 +331,8 @@ func (r *Reconciler) generateInstallerSet(ctx context.Context, tp *v1alpha1.Tekt
 	// Creates a new default installer
 	dis := installer.NewDefaultInstaller()
 
-	manifest := r.manifest
-	if err := r.transform(ctx, &manifest, tp); err != nil {
+	manifest, err := r.transform(ctx, &r.manifest, tp)
+	if err != nil {
 		tp.Status.MarkNotReady("transformation failed: " + err.Error())
 		return nil, err
 	}
@@ -381,8 +382,11 @@ func (r *Reconciler) targetNamespaceCheck(ctx context.Context, tp *v1alpha1.Tekt
 
 // 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 {
+func (r *Reconciler) transform(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) (mf.Manifest, error) {
 	pipeline := comp.(*v1alpha1.TektonPipeline)
+
+	filteredManifest := manifest.Filter(mf.Not(mf.ByKind("PodSecurityPolicy")), mf.Not(mf.ByKind("HorizontalPodAutoscaler")))
+
 	images := common.ToLowerCaseKeys(common.ImagesFromEnv(common.PipelinesImagePrefix))
 	instance := comp.(*v1alpha1.TektonPipeline)
 	// adding extension's transformers first to run them before `extra` transformers
@@ -398,7 +402,11 @@ func (r *Reconciler) transform(ctx context.Context, manifest *mf.Manifest, comp
 		common.AddConfiguration(pipeline.Spec.Config),
 	}
 	trns = append(trns, extra...)
-	return common.Transform(ctx, manifest, instance, trns...)
+
+	if err := common.Transform(ctx, &filteredManifest, instance, trns...); err != nil {
+		return mf.Manifest{}, err
+	}
+	return filteredManifest, nil
 }
 
 func (m *Recorder) logMetrics(status, version string, logger *zap.SugaredLogger) {
diff --git a/pkg/reconciler/kubernetes/tektontrigger/controller.go b/pkg/reconciler/kubernetes/tektontrigger/controller.go
index 177520373..4d72e4db6 100644
--- a/pkg/reconciler/kubernetes/tektontrigger/controller.go
+++ b/pkg/reconciler/kubernetes/tektontrigger/controller.go
@@ -19,10 +19,9 @@ package tektontrigger
 import (
 	"context"
 
-	kubeclient "knative.dev/pkg/client/injection/kube/client"
-
 	tektonInstallerinformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektoninstallerset"
 	tektonPipelineinformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektonpipeline"
+	"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client"
 	"k8s.io/client-go/tools/cache"
 
 	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
@@ -66,15 +65,16 @@ func NewExtendedController(generator common.ExtensionGenerator) injection.Contro
 			logger.Fatal(err)
 		}
 
+		tisClient := operatorclient.Get(ctx).OperatorV1alpha1().TektonInstallerSets()
+
 		c := &Reconciler{
-			kubeClientSet:     kubeclient.Get(ctx),
-			operatorClientSet: operatorclient.Get(ctx),
-			pipelineInformer:  tektonPipelineinformer.Get(ctx),
-			extension:         generator(ctx),
-			manifest:          manifest,
-			operatorVersion:   operatorVer,
-			triggersVersion:   triggersVer,
-			metrics:           metrics,
+			pipelineInformer: tektonPipelineinformer.Get(ctx),
+			installerSetClient: client.NewInstallerSetClient(tisClient, &manifest,
+				operatorVer, triggersVer, v1alpha1.KindTektonTrigger, filterAndTransform(generator(ctx)), metrics),
+			extension:       generator(ctx),
+			manifest:        manifest,
+			triggersVersion: triggersVer,
+			metrics:         metrics,
 		}
 		impl := tektonTriggerreconciler.NewImpl(ctx, c)
 
diff --git a/pkg/reconciler/kubernetes/tektontrigger/finalize.go b/pkg/reconciler/kubernetes/tektontrigger/finalize.go
new file mode 100644
index 000000000..ec51e383c
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektontrigger/finalize.go
@@ -0,0 +1,53 @@
+/*
+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"
+
+	mf "github.com/manifestival/manifestival"
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	tektontriggerreconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektontrigger"
+	"knative.dev/pkg/logging"
+	pkgreconciler "knative.dev/pkg/reconciler"
+)
+
+var _ tektontriggerreconciler.Finalizer = (*Reconciler)(nil)
+
+// FinalizeKind removes all resources after deletion of a TektonTrigger CR.
+func (r *Reconciler) FinalizeKind(ctx context.Context, original *v1alpha1.TektonTrigger) pkgreconciler.Event {
+	logger := logging.FromContext(ctx)
+
+	//Delete CRDs before deleting rest of resources so that any instance
+	//of CRDs which has finalizer set will get deleted before we remove
+	//the controller;s deployment for it
+	if err := r.manifest.Filter(mf.CRDs).Delete(); err != nil {
+		logger.Error("Failed to deleted CRDs for TektonTrigger")
+		return err
+	}
+
+	if err := r.installerSetClient.CleanupAll(ctx); err != nil {
+		logger.Error("failed to cleanup all installerset: ", err)
+		return err
+	}
+
+	if err := r.extension.Finalize(ctx, original); err != nil {
+		logger.Error("Failed to finalize platform resources", err)
+	}
+
+	return nil
+}
diff --git a/pkg/reconciler/kubernetes/tektontrigger/metrics.go b/pkg/reconciler/kubernetes/tektontrigger/metrics.go
index 8a99d04a9..9c2547e66 100644
--- a/pkg/reconciler/kubernetes/tektontrigger/metrics.go
+++ b/pkg/reconciler/kubernetes/tektontrigger/metrics.go
@@ -24,6 +24,7 @@ import (
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
+	"go.uber.org/zap"
 	"knative.dev/pkg/metrics"
 )
 
@@ -33,11 +34,6 @@ var (
 		stats.UnitDimensionless)
 )
 
-const (
-	metricsNew     = "NewInstall"
-	metricsUpgrade = "Upgrade"
-)
-
 // Recorder holds keys for Tekton metrics
 type Recorder struct {
 	initialized bool
@@ -107,3 +103,10 @@ func (r *Recorder) Count(status, version string) error {
 	metrics.Record(ctx, tReconcileCount.M(1))
 	return nil
 }
+
+func (m *Recorder) LogMetrics(status, version string, logger *zap.SugaredLogger) {
+	err := m.Count(status, version)
+	if err != nil {
+		logger.Warnf("%v: Failed to log the metrics : %v", resourceKind, err)
+	}
+}
diff --git a/pkg/reconciler/kubernetes/tektontrigger/reconcile.go b/pkg/reconciler/kubernetes/tektontrigger/reconcile.go
new file mode 100644
index 000000000..d551d1168
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektontrigger/reconcile.go
@@ -0,0 +1,110 @@
+/*
+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"
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	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"
+	"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client"
+	"knative.dev/pkg/logging"
+	pkgreconciler "knative.dev/pkg/reconciler"
+)
+
+const resourceKind = v1alpha1.KindTektonTrigger
+
+// Reconciler implements controller.Reconciler for TektonTrigger resources.
+type Reconciler struct {
+	// installer Set client to do CRUD operations for components
+	installerSetClient *client.InstallerSetClient
+	// pipelineInformer to query for TektonPipeline
+	pipelineInformer pipelineinformer.TektonPipelineInformer
+	// manifest has the source manifest of Tekton Triggers for a
+	// particular version
+	manifest mf.Manifest
+	// Platform-specific behavior to affect the transform
+	extension common.Extension
+	// metrics handles metrics for trigger install
+	metrics *Recorder
+	// version of triggers which we are installing
+	triggersVersion string
+}
+
+// Check that our Reconciler implements controller.Reconciler
+var _ tektontriggerreconciler.Interface = (*Reconciler)(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).With("name", tt.GetName())
+	tt.Status.InitializeConditions()
+	tt.Status.SetVersion(r.triggersVersion)
+
+	if tt.GetName() != v1alpha1.TriggerResourceName {
+		msg := fmt.Sprintf("Resource ignored, Expected Name: %s, Got Name: %s",
+			v1alpha1.TriggerResourceName,
+			tt.GetName(),
+		)
+		logger.Error(msg)
+		tt.Status.MarkNotReady(msg)
+		return nil
+	}
+
+	//Make sure TektonPipeline is installed before proceeding with
+	//TektonTrigger
+	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 v1alpha1.REQUEUE_EVENT_AFTER
+		}
+		// (tektonpipeline.operator.tekton.dev instance not available yet)
+		tt.Status.MarkDependencyMissing("tekton-pipelines does not exist")
+		return err
+	}
+	tt.Status.MarkDependenciesInstalled()
+
+	// Pass the object through defaulting
+	tt.SetDefaults(ctx)
+
+	if err := r.extension.PreReconcile(ctx, tt); err != nil {
+		tt.Status.MarkPreReconcilerFailed(fmt.Sprintf("PreReconciliation failed: %s", err.Error()))
+		return err
+	}
+
+	//Mark PreReconcile Complete
+	tt.Status.MarkPreReconcilerComplete()
+
+	if err := r.installerSetClient.MainSet(ctx, tt); err != nil {
+		logger.Errorf("failed for main set: %v", err)
+		return err
+	}
+
+	if err := r.extension.PostReconcile(ctx, tt); err != nil {
+		tt.Status.MarkPostReconcilerFailed(fmt.Sprintf("PostReconciliation failed: %s", err.Error()))
+		return err
+	}
+
+	// Mark PostReconcile Complete
+	tt.Status.MarkPostReconcilerComplete()
+	return nil
+}
diff --git a/pkg/reconciler/kubernetes/tektontrigger/tektontrigger.go b/pkg/reconciler/kubernetes/tektontrigger/tektontrigger.go
deleted file mode 100644
index eb7bf8c80..000000000
--- a/pkg/reconciler/kubernetes/tektontrigger/tektontrigger.go
+++ /dev/null
@@ -1,426 +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.
-*/
-
-package tektontrigger
-
-import (
-	"context"
-	"fmt"
-
-	mf "github.com/manifestival/manifestival"
-	"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"
-	"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset"
-	"github.com/tektoncd/operator/pkg/reconciler/shared/hash"
-	"go.uber.org/zap"
-	corev1 "k8s.io/api/core/v1"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/client-go/kubernetes"
-	"knative.dev/pkg/apis"
-	"knative.dev/pkg/logging"
-	pkgreconciler "knative.dev/pkg/reconciler"
-)
-
-// Triggers ConfigMap
-const (
-	ConfigDefaults = "config-defaults-triggers"
-	FeatureFlag    = "feature-flags-triggers"
-
-	createdByValue = "TektonTrigger"
-)
-
-// Reconciler implements controller.Reconciler for TektonTrigger resources.
-type Reconciler struct {
-	// operatorClientSet allows us to configure operator objects
-	operatorClientSet clientset.Interface
-	// manifest has the source manifest of Tekton Triggers for a
-	// particular version
-	manifest mf.Manifest
-	// Platform-specific behavior to affect the transform
-	extension common.Extension
-	// pipelineInformer to query for TektonPipeline
-	// metrics handles metrics for trigger install
-	metrics *Recorder
-
-	pipelineInformer pipelineinformer.TektonPipelineInformer
-	triggersVersion  string
-	operatorVersion  string
-	kubeClientSet    kubernetes.Interface
-}
-
-var (
-	ls = metav1.LabelSelector{
-		MatchLabels: map[string]string{
-			v1alpha1.CreatedByKey:     createdByValue,
-			v1alpha1.InstallerSetType: v1alpha1.TriggerResourceName,
-		},
-	}
-)
-
-// Check that our Reconciler implements controller.Reconciler
-var _ tektontriggerreconciler.Interface = (*Reconciler)(nil)
-var _ tektontriggerreconciler.Finalizer = (*Reconciler)(nil)
-
-// 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)
-
-	// Delete CRDs before deleting rest of resources so that any instance
-	// of CRDs which has finalizer set will get deleted before we remove
-	// the controller;s deployment for it
-	if err := r.manifest.Filter(mf.CRDs).Delete(); err != nil {
-		logger.Error("Failed to deleted CRDs for TektonTrigger")
-		return err
-	}
-
-	labelSelector, err := common.LabelSelector(ls)
-	if err != nil {
-		return err
-	}
-	if err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets().
-		DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{
-			LabelSelector: labelSelector,
-		}); err != nil {
-		logger.Error("Failed to delete installer set created by TektonTrigger", err)
-		return err
-	}
-
-	if err := r.extension.Finalize(ctx, original); 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()
-
-	if tt.GetName() != v1alpha1.TriggerResourceName {
-		msg := fmt.Sprintf("Resource ignored, Expected Name: %s, Got Name: %s",
-			v1alpha1.TriggerResourceName,
-			tt.GetName(),
-		)
-		logger.Error(msg)
-		tt.Status.MarkNotReady(msg)
-		return nil
-	}
-
-	// Make sure TektonPipeline is installed before proceeding with
-	// TektonTrigger
-	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 v1alpha1.REQUEUE_EVENT_AFTER
-		}
-		// (tektonpipeline.operator.tekton.dev instance not available yet)
-		tt.Status.MarkDependencyMissing("tekton-pipelines does not exist")
-		return err
-	}
-	tt.Status.MarkDependenciesInstalled()
-
-	// Pass the object through defaulting
-	tt.SetDefaults(ctx)
-	// Mark TektonTrigger Instance as Not Ready if an upgrade is needed
-	if err := r.markUpgrade(ctx, tt); err != nil {
-		return err
-	}
-
-	if err := tektoninstallerset.CleanUpObsoleteResources(ctx, r.operatorClientSet, createdByValue); err != nil {
-		return err
-	}
-
-	if err := r.extension.PreReconcile(ctx, tt); err != nil {
-		tt.Status.MarkPreReconcilerFailed(fmt.Sprintf("PreReconciliation failed: %s", err.Error()))
-		return err
-	}
-
-	// Mark PreReconcile Complete
-	tt.Status.MarkPreReconcilerComplete()
-
-	// Check if an tekton installer set already exists, if not then create
-	labelSelector, err := common.LabelSelector(ls)
-	if err != nil {
-		return err
-	}
-	existingInstallerSet, err := tektoninstallerset.CurrentInstallerSetName(ctx, r.operatorClientSet, labelSelector)
-	if err != nil {
-		return err
-	}
-	if existingInstallerSet == "" {
-		createdIs, err := r.createInstallerSet(ctx, tt)
-		if err != nil {
-			return err
-		}
-
-		// If there was no existing installer set, that means its a new install
-		r.metrics.logMetrics(metricsNew, r.triggersVersion, logger)
-
-		return r.updateTektonTriggerStatus(ctx, tt, createdIs)
-	}
-
-	// If exists, then fetch the TektonInstallerSet
-	installedTIS, err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets().
-		Get(ctx, existingInstallerSet, metav1.GetOptions{})
-	if err != nil {
-		if apierrors.IsNotFound(err) {
-			createdIs, err := r.createInstallerSet(ctx, tt)
-			if err != nil {
-				return err
-			}
-			// if there is version diff then its a call for upgrade
-			if tt.Status.Version != r.triggersVersion {
-				r.metrics.logMetrics(metricsUpgrade, r.triggersVersion, logger)
-			}
-			return r.updateTektonTriggerStatus(ctx, tt, createdIs)
-		}
-		logger.Error("failed to get InstallerSet: %s", err)
-		return err
-	}
-
-	installerSetTargetNamespace := installedTIS.Annotations[v1alpha1.TargetNamespaceKey]
-	installerSetReleaseVersion := installedTIS.Labels[v1alpha1.ReleaseVersionKey]
-
-	// Check if TargetNamespace of existing TektonInstallerSet is same as expected
-	// Check if Release Version in TektonInstallerSet is same as expected
-	// If any of the thing above is not same the delete the existing TektonInstallerSet
-	// and create a new with expected properties
-
-	if installerSetTargetNamespace != tt.Spec.TargetNamespace || installerSetReleaseVersion != r.operatorVersion {
-
-		// Delete the existing TektonInstallerSet
-		err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets().
-			Delete(ctx, existingInstallerSet, metav1.DeleteOptions{})
-		if err != nil {
-			logger.Error("failed to delete InstallerSet: %s", err)
-			return err
-		}
-
-		// Make sure the TektonInstallerSet is deleted
-		_, err = r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets().
-			Get(ctx, existingInstallerSet, metav1.GetOptions{})
-		if err == nil {
-			tt.Status.MarkNotReady("Waiting for previous installer set to get deleted")
-			return v1alpha1.REQUEUE_EVENT_AFTER
-		}
-		if !apierrors.IsNotFound(err) {
-			logger.Error("failed to get InstallerSet: %s", err)
-			return err
-		}
-
-		return nil
-
-	} else {
-		// If target namespace and version are not changed then check if spec
-		// of TektonTrigger is changed by checking hash stored as annotation on
-		// TektonInstallerSet with computing new hash of TektonTrigger Spec
-
-		// Hash of TektonPipeline Spec
-		expectedSpecHash, err := hash.Compute(tt.Spec)
-		if err != nil {
-			return err
-		}
-
-		// spec hash stored on installerSet
-		lastAppliedHash := installedTIS.GetAnnotations()[v1alpha1.LastAppliedHashKey]
-
-		if lastAppliedHash != expectedSpecHash {
-
-			manifest := r.manifest
-			if err := r.transform(ctx, &manifest, tt); err != nil {
-				logger.Error("manifest transformation failed:  ", err)
-				return err
-			}
-
-			// Update the spec hash
-			current := installedTIS.GetAnnotations()
-			current[v1alpha1.LastAppliedHashKey] = expectedSpecHash
-			installedTIS.SetAnnotations(current)
-
-			// Update the manifests
-			installedTIS.Spec.Manifests = manifest.Resources()
-
-			if _, err = r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets().
-				Update(ctx, installedTIS, metav1.UpdateOptions{}); err != nil {
-				return err
-			}
-
-			// after updating installer set enqueue after a duration
-			// to allow changes to get deployed
-			return v1alpha1.REQUEUE_EVENT_AFTER
-		}
-	}
-
-	// Mark InstallerSet Available
-	tt.Status.MarkInstallerSetAvailable()
-
-	ready := installedTIS.Status.GetCondition(apis.ConditionReady)
-	if ready == nil {
-		tt.Status.MarkInstallerSetNotReady("Waiting for installation")
-		return v1alpha1.REQUEUE_EVENT_AFTER
-	}
-
-	if ready.Status == corev1.ConditionUnknown {
-		tt.Status.MarkInstallerSetNotReady("Waiting for installation")
-		return v1alpha1.REQUEUE_EVENT_AFTER
-	} else if ready.Status == corev1.ConditionFalse {
-		tt.Status.MarkInstallerSetNotReady(ready.Message)
-		manifest := r.manifest
-		if err := r.transform(ctx, &manifest, tt); err != nil {
-			logger.Error("manifest transformation failed:  ", err)
-			return err
-		}
-		err = common.PreemptDeadlock(ctx, &manifest, r.kubeClientSet, v1alpha1.TriggerResourceName)
-		if err != nil {
-			logger.Error("preempt deadlock error: %v", err)
-		}
-		return v1alpha1.REQUEUE_EVENT_AFTER
-	}
-
-	// Mark InstallerSet Ready
-	tt.Status.MarkInstallerSetReady()
-
-	if err := r.extension.PostReconcile(ctx, tt); err != nil {
-		tt.Status.MarkPostReconcilerFailed(fmt.Sprintf("PostReconciliation failed: %s", err.Error()))
-		return err
-	}
-
-	// Mark PostReconcile Complete
-	tt.Status.MarkPostReconcilerComplete()
-
-	// Update the object for any spec changes
-	if _, err := r.operatorClientSet.OperatorV1alpha1().TektonTriggers().Update(ctx, tt, v1.UpdateOptions{}); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// 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 {
-	trigger := comp.(*v1alpha1.TektonTrigger)
-	triggerImages := common.ToLowerCaseKeys(common.ImagesFromEnv(common.TriggersImagePrefix))
-	// adding extension's transformers first to run them before `extra` transformers
-	trns := r.extension.Transformers(trigger)
-	extra := []mf.Transformer{
-		common.InjectOperandNameLabelOverwriteExisting(v1alpha1.OperandTektoncdTriggers),
-		common.AddConfigMapValues(ConfigDefaults, trigger.Spec.OptionalTriggersProperties),
-		common.AddConfigMapValues(FeatureFlag, trigger.Spec.TriggersProperties),
-		common.ApplyProxySettings,
-		common.DeploymentImages(triggerImages),
-		common.AddConfiguration(trigger.Spec.Config),
-	}
-	trns = append(trns, extra...)
-	return common.Transform(ctx, manifest, trigger, trns...)
-}
-
-func (r *Reconciler) updateTektonTriggerStatus(ctx context.Context, tt *v1alpha1.TektonTrigger, createdIs *v1alpha1.TektonInstallerSet) error {
-	// update the tt with TektonInstallerSet and releaseVersion
-	tt.Status.SetTektonInstallerSet(createdIs.Name)
-	tt.Status.SetVersion(r.triggersVersion)
-	return nil
-}
-
-func (r *Reconciler) createInstallerSet(ctx context.Context, tt *v1alpha1.TektonTrigger) (*v1alpha1.TektonInstallerSet, error) {
-
-	manifest := r.manifest
-	if err := r.transform(ctx, &manifest, tt); err != nil {
-		tt.Status.MarkNotReady("transformation failed: " + err.Error())
-		return nil, err
-	}
-
-	// compute the hash of tektontrigger spec and store as an annotation
-	// in further reconciliation we compute hash of tt spec and check with
-	// annotation, if they are same then we skip updating the object
-	// otherwise we update the manifest
-	specHash, err := hash.Compute(tt.Spec)
-	if err != nil {
-		return nil, err
-	}
-
-	// create installer set
-	tis := makeInstallerSet(tt, manifest, specHash, r.operatorVersion)
-	createdIs, err := r.operatorClientSet.OperatorV1alpha1().TektonInstallerSets().
-		Create(ctx, tis, metav1.CreateOptions{})
-	if err != nil {
-		return nil, err
-	}
-
-	return createdIs, nil
-}
-
-func makeInstallerSet(tt *v1alpha1.TektonTrigger, manifest mf.Manifest, ttSpecHash, releaseVersion string) *v1alpha1.TektonInstallerSet {
-	ownerRef := *metav1.NewControllerRef(tt, tt.GetGroupVersionKind())
-	return &v1alpha1.TektonInstallerSet{
-		ObjectMeta: metav1.ObjectMeta{
-			GenerateName: fmt.Sprintf("%s-", v1alpha1.TriggerResourceName),
-			Labels: map[string]string{
-				v1alpha1.CreatedByKey:      createdByValue,
-				v1alpha1.ReleaseVersionKey: releaseVersion,
-				v1alpha1.InstallerSetType:  v1alpha1.TriggerResourceName,
-			},
-			Annotations: map[string]string{
-				v1alpha1.TargetNamespaceKey: tt.Spec.TargetNamespace,
-				v1alpha1.LastAppliedHashKey: ttSpecHash,
-			},
-			OwnerReferences: []metav1.OwnerReference{ownerRef},
-		},
-		Spec: v1alpha1.TektonInstallerSetSpec{
-			Manifests: manifest.Resources(),
-		},
-	}
-}
-
-func (m *Recorder) logMetrics(status, version string, logger *zap.SugaredLogger) {
-	err := m.Count(status, version)
-	if err != nil {
-		logger.Warnf("Failed to log the metrics : %v", err)
-	}
-}
-
-func (r *Reconciler) markUpgrade(ctx context.Context, tt *v1alpha1.TektonTrigger) error {
-	labels := tt.GetLabels()
-	ver, ok := labels[v1alpha1.ReleaseVersionKey]
-	if ok && ver == r.operatorVersion {
-		return nil
-	}
-	if ok && ver != r.operatorVersion {
-		tt.Status.MarkInstallerSetNotReady(v1alpha1.UpgradePending)
-		tt.Status.MarkPreReconcilerFailed(v1alpha1.UpgradePending)
-		tt.Status.MarkPostReconcilerFailed(v1alpha1.UpgradePending)
-		tt.Status.MarkNotReady(v1alpha1.UpgradePending)
-	}
-	if labels == nil {
-		labels = map[string]string{}
-	}
-	labels[v1alpha1.ReleaseVersionKey] = r.operatorVersion
-	tt.SetLabels(labels)
-
-	if _, err := r.operatorClientSet.OperatorV1alpha1().TektonTriggers().Update(ctx,
-		tt, v1.UpdateOptions{}); err != nil {
-		return err
-	}
-	return v1alpha1.RECONCILE_AGAIN_ERR
-}
diff --git a/pkg/reconciler/kubernetes/tektontrigger/transform.go b/pkg/reconciler/kubernetes/tektontrigger/transform.go
new file mode 100644
index 000000000..f22e7b353
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektontrigger/transform.go
@@ -0,0 +1,57 @@
+/*
+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"
+
+	mf "github.com/manifestival/manifestival"
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"github.com/tektoncd/operator/pkg/reconciler/common"
+	"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client"
+)
+
+// Triggers ConfigMap
+const (
+	ConfigDefaults = "config-defaults-triggers"
+	FeatureFlag    = "feature-flags-triggers"
+)
+
+func filterAndTransform(extension common.Extension) client.FilterAndTransform {
+	return func(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) (*mf.Manifest, error) {
+		trigger := comp.(*v1alpha1.TektonTrigger)
+		triggerImages := common.ToLowerCaseKeys(common.ImagesFromEnv(common.TriggersImagePrefix))
+
+		filteredManifest := manifest.Filter(mf.Not(mf.ByKind("PodSecurityPolicy")))
+
+		// adding extension's transformers first to run them before `extra` transformers
+		trns := extension.Transformers(trigger)
+		extra := []mf.Transformer{
+			common.InjectOperandNameLabelOverwriteExisting(v1alpha1.OperandTektoncdTriggers),
+			common.AddConfigMapValues(ConfigDefaults, trigger.Spec.OptionalTriggersProperties),
+			common.AddConfigMapValues(FeatureFlag, trigger.Spec.TriggersProperties),
+			common.ApplyProxySettings,
+			common.DeploymentImages(triggerImages),
+			common.AddConfiguration(trigger.Spec.Config),
+		}
+		trns = append(trns, extra...)
+		if err := common.Transform(ctx, &filteredManifest, trigger, trns...); err != nil {
+			return nil, err
+		}
+		return &filteredManifest, nil
+	}
+}
diff --git a/pkg/reconciler/shared/tektonconfig/trigger/trigger.go b/pkg/reconciler/shared/tektonconfig/trigger/trigger.go
index 228173bc3..f5530829b 100644
--- a/pkg/reconciler/shared/tektonconfig/trigger/trigger.go
+++ b/pkg/reconciler/shared/tektonconfig/trigger/trigger.go
@@ -20,13 +20,13 @@ import (
 	"context"
 	"fmt"
 	"reflect"
+	"strings"
 
 	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
-
 	op "github.com/tektoncd/operator/pkg/client/clientset/versioned/typed/operator/v1alpha1"
-	"github.com/tektoncd/operator/pkg/reconciler/common"
 	apierrs "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"knative.dev/pkg/apis"
 )
 
 func EnsureTektonTriggerExists(ctx context.Context, clients op.TektonTriggerInterface, config *v1alpha1.TektonConfig) (*v1alpha1.TektonTrigger, error) {
@@ -123,12 +123,10 @@ func UpdateTrigger(ctx context.Context, ttCR *v1alpha1.TektonTrigger, config *v1
 
 // isTektonTriggerReady will check the status conditions of the TektonTrigger and return true if the TektonTrigger is ready.
 func isTektonTriggerReady(s *v1alpha1.TektonTrigger, err error) (bool, error) {
-	upgradePending, errInternal := common.CheckUpgradePending(s)
-	if err != nil {
-		return false, errInternal
-	}
-	if upgradePending {
-		return false, v1alpha1.DEPENDENCY_UPGRADE_PENDING_ERR
+	if s.GetStatus() != nil && s.GetStatus().GetCondition(apis.ConditionReady) != nil {
+		if strings.Contains(s.GetStatus().GetCondition(apis.ConditionReady).Message, v1alpha1.UpgradePending) {
+			return false, v1alpha1.DEPENDENCY_UPGRADE_PENDING_ERR
+		}
 	}
 	return s.Status.IsReady(), err
 }


Test patch:
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/check_test.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/check_test.go
new file mode 100644
index 000000000..1ea895465
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/check_test.go
@@ -0,0 +1,264 @@
+/*
+Copyright 2022 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 client
+
+import (
+	"context"
+	"testing"
+
+	mf "github.com/manifestival/manifestival"
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"github.com/tektoncd/operator/pkg/client/clientset/versioned/fake"
+	"github.com/tektoncd/operator/pkg/reconciler/common"
+	"gotest.tools/v3/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	testing2 "knative.dev/pkg/reconciler/testing"
+)
+
+func filterAndTransform(extension common.Extension) FilterAndTransform {
+	return func(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) (*mf.Manifest, error) {
+		return manifest, nil
+	}
+}
+
+func TestInstallerSetClient_Check(t *testing.T) {
+	releaseVersion := "devel"
+	comp := &v1alpha1.TektonTrigger{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "trigger",
+		},
+		Spec: v1alpha1.TektonTriggerSpec{
+			CommonSpec: v1alpha1.CommonSpec{TargetNamespace: "test"},
+		},
+	}
+
+	tests := []struct {
+		name      string
+		resources *v1alpha1.TektonInstallerSetList
+		setType   string
+		wantErr   error
+	}{
+		{
+			name:      "installer set not found",
+			setType:   InstallerTypeMain,
+			resources: &v1alpha1.TektonInstallerSetList{},
+			wantErr:   ErrNotFound,
+		},
+		{
+			name:    "main installer invalid state, exist only one instead of two",
+			setType: InstallerTypeMain,
+			resources: &v1alpha1.TektonInstallerSetList{
+				Items: []v1alpha1.TektonInstallerSet{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "abc-static",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:     v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType: InstallerTypeMain,
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "some-other",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:     v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType: InstallerTypeMain,
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+				},
+			},
+			wantErr: ErrInvalidState,
+		},
+		{
+			name:    "main installer set version different error",
+			setType: InstallerTypeMain,
+			resources: &v1alpha1.TektonInstallerSetList{
+				Items: []v1alpha1.TektonInstallerSet{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "trigger-main-static-asd",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:      v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType:  InstallerTypeMain,
+								v1alpha1.ReleaseVersionKey: "old",
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "trigger-main-deployment-asd",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:      v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType:  InstallerTypeMain,
+								v1alpha1.ReleaseVersionKey: "old",
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "abc-pre",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:      v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType:  InstallerTypePre,
+								v1alpha1.ReleaseVersionKey: "devel",
+							},
+							Annotations: map[string]string{
+								v1alpha1.TargetNamespaceKey: "different-than-expected",
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+				},
+			},
+			wantErr: ErrVersionDifferent,
+		},
+		{
+			name:    "pre set with different namespace error",
+			setType: InstallerTypePre,
+			resources: &v1alpha1.TektonInstallerSetList{
+				Items: []v1alpha1.TektonInstallerSet{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "abc-pre",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:      v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType:  InstallerTypePre,
+								v1alpha1.ReleaseVersionKey: "devel",
+							},
+							Annotations: map[string]string{
+								v1alpha1.TargetNamespaceKey: "different-than-expected",
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+				},
+			},
+			wantErr: ErrNsDifferent,
+		},
+		{
+			name:    "pre set more than one",
+			setType: InstallerTypePre,
+			resources: &v1alpha1.TektonInstallerSetList{
+				Items: []v1alpha1.TektonInstallerSet{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "abc-pre",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:      v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType:  InstallerTypePre,
+								v1alpha1.ReleaseVersionKey: "devel",
+							},
+							Annotations: map[string]string{
+								v1alpha1.TargetNamespaceKey: "different-than-expected",
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "abc1-pre",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:      v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType:  InstallerTypePre,
+								v1alpha1.ReleaseVersionKey: "devel",
+							},
+							Annotations: map[string]string{
+								v1alpha1.TargetNamespaceKey: "different-than-expected",
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+				},
+			},
+			wantErr: ErrInvalidState,
+		},
+		{
+			name:    "post set with update required error",
+			setType: InstallerTypePost,
+			resources: &v1alpha1.TektonInstallerSetList{
+				Items: []v1alpha1.TektonInstallerSet{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "abc-post",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:      v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType:  InstallerTypePost,
+								v1alpha1.ReleaseVersionKey: "devel",
+							},
+							Annotations: map[string]string{
+								v1alpha1.TargetNamespaceKey: comp.Spec.GetTargetNamespace(),
+								v1alpha1.LastAppliedHashKey: "abc",
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+				},
+			},
+			wantErr: ErrUpdateRequired,
+		},
+		{
+			name:    "no error",
+			setType: InstallerTypePost,
+			resources: &v1alpha1.TektonInstallerSetList{
+				Items: []v1alpha1.TektonInstallerSet{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "abc-post",
+							Labels: map[string]string{
+								v1alpha1.CreatedByKey:      v1alpha1.KindTektonTrigger,
+								v1alpha1.InstallerSetType:  InstallerTypePost,
+								v1alpha1.ReleaseVersionKey: "devel",
+							},
+							Annotations: map[string]string{
+								v1alpha1.TargetNamespaceKey: comp.Spec.GetTargetNamespace(),
+								v1alpha1.LastAppliedHashKey: "1ca67ca6f4dfe602f76233470d031976757fc5249c2d67887adb04a7988cfebc",
+							},
+						},
+						Spec: v1alpha1.TektonInstallerSetSpec{},
+					},
+				},
+			},
+			wantErr: nil,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ctx, _ := testing2.SetupFakeContext(t)
+
+			fakeclient := fake.NewSimpleClientset(tt.resources)
+			tisClient := fakeclient.OperatorV1alpha1().TektonInstallerSets()
+
+			client := NewInstallerSetClient(tisClient, nil, releaseVersion, "test-version", v1alpha1.KindTektonTrigger,
+				filterAndTransform(common.NoExtension(ctx)), &testMetrics{})
+
+			_, gotErr := client.CheckSet(ctx, comp, tt.setType)
+
+			if tt.wantErr != nil {
+				assert.Equal(t, gotErr, tt.wantErr)
+				return
+			}
+			assert.NilError(t, gotErr)
+		})
+	}
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/cleanup_test.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/cleanup_test.go
new file mode 100644
index 000000000..266c8510d
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/cleanup_test.go
@@ -0,0 +1,169 @@
+/*
+Copyright 2022 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 client
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"github.com/tektoncd/operator/pkg/client/clientset/versioned/fake"
+	"github.com/tektoncd/operator/pkg/reconciler/common"
+	"gotest.tools/v3/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	testing2 "knative.dev/pkg/reconciler/testing"
+)
+
+func TestInstallerSetClient_Cleanup(t *testing.T) {
+	releaseVersion := "devel"
+	tests := []struct {
+		name                   string
+		existingIS             []v1alpha1.TektonInstallerSet
+		setType                string
+		afterCleanupNumberOfIS int
+	}{
+		{
+			name:    "cleanup main",
+			setType: InstallerTypeMain,
+			existingIS: []v1alpha1.TektonInstallerSet{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "abc-main-static",
+						Labels: map[string]string{
+							v1alpha1.CreatedByKey:     v1alpha1.KindTektonTrigger,
+							v1alpha1.InstallerSetType: InstallerTypeMain,
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{},
+				},
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "abc-main-deployment",
+						Labels: map[string]string{
+							v1alpha1.CreatedByKey:     v1alpha1.KindTektonTrigger,
+							v1alpha1.InstallerSetType: InstallerTypeMain,
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{},
+				},
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "trigger-pre",
+						Labels: map[string]string{
+							v1alpha1.CreatedByKey:     v1alpha1.KindTektonTrigger,
+							v1alpha1.InstallerSetType: InstallerTypePre,
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{},
+				},
+			},
+			afterCleanupNumberOfIS: 1,
+		},
+		{
+			name:    "cleanup pre set",
+			setType: InstallerTypePre,
+			existingIS: []v1alpha1.TektonInstallerSet{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "abc-pre-sadsa",
+						Labels: map[string]string{
+							v1alpha1.CreatedByKey:     v1alpha1.KindTektonTrigger,
+							v1alpha1.InstallerSetType: InstallerTypePre,
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{},
+				},
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "abc-post-asdad",
+						Labels: map[string]string{
+							v1alpha1.CreatedByKey:     v1alpha1.KindTektonTrigger,
+							v1alpha1.InstallerSetType: InstallerTypePost,
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{},
+				},
+			},
+			afterCleanupNumberOfIS: 1,
+		},
+		{
+			name:    "cleanup post set",
+			setType: InstallerTypePost,
+			existingIS: []v1alpha1.TektonInstallerSet{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "abc-pre-sadsa",
+						Labels: map[string]string{
+							v1alpha1.CreatedByKey:     v1alpha1.KindTektonTrigger,
+							v1alpha1.InstallerSetType: InstallerTypePre,
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{},
+				},
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "abc-post-asdad",
+						Labels: map[string]string{
+							v1alpha1.CreatedByKey:     v1alpha1.KindTektonTrigger,
+							v1alpha1.InstallerSetType: InstallerTypePost,
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{},
+				},
+			},
+			afterCleanupNumberOfIS: 1,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ctx, _ := testing2.SetupFakeContext(t)
+
+			runObj := []runtime.Object{}
+			for i := range tt.existingIS {
+				runObj = append(runObj, &tt.existingIS[i])
+			}
+			fakeClient := fake.NewSimpleClientset(runObj...)
+			tisClient := fakeClient.OperatorV1alpha1().TektonInstallerSets()
+
+			client := NewInstallerSetClient(tisClient, nil, releaseVersion, "test-version", v1alpha1.KindTektonTrigger,
+				filterAndTransform(common.NoExtension(ctx)), &testMetrics{})
+
+			var gotErr error
+			switch tt.setType {
+			case InstallerTypeMain:
+				gotErr = client.CleanupMainSet(ctx)
+			case InstallerTypePre:
+				gotErr = client.CleanupPreSet(ctx)
+			case InstallerTypePost:
+				gotErr = client.CleanupPostSet(ctx)
+			default:
+				gotErr = client.CleanupAll(ctx)
+			}
+			assert.NilError(t, gotErr)
+
+			list, err := fakeClient.OperatorV1alpha1().TektonInstallerSets().List(ctx, metav1.ListOptions{})
+			assert.NilError(t, err)
+			assert.Equal(t, len(list.Items), tt.afterCleanupNumberOfIS)
+
+			if strings.Contains(list.Items[0].GetName(), tt.setType) {
+				t.Fatalf("installer set %s should have been deleted but", list.Items[0].GetName())
+			}
+		})
+	}
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/create_test.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/create_test.go
new file mode 100644
index 000000000..59a1c9136
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/create_test.go
@@ -0,0 +1,134 @@
+package client
+
+/*
+Copyright 2022 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.
+*/
+
+import (
+	"strings"
+	"testing"
+
+	mf "github.com/manifestival/manifestival"
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"github.com/tektoncd/operator/pkg/client/clientset/versioned/fake"
+	"github.com/tektoncd/operator/pkg/reconciler/common"
+	fake2 "github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client/fake"
+	"gotest.tools/v3/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	testing2 "knative.dev/pkg/reconciler/testing"
+)
+
+var (
+	serviceAccount = namespacedResource("v1", "ServiceAccount", "test", "test-service-account")
+	deployment     = namespacedResource("v1", "Deployment", "test", "test-deployment")
+)
+
+// namespacedResource is an unstructured resource with the given apiVersion, kind, ns and name.
+func namespacedResource(apiVersion, kind, ns, name string) unstructured.Unstructured {
+	resource := unstructured.Unstructured{}
+	resource.SetAPIVersion(apiVersion)
+	resource.SetKind(kind)
+	resource.SetNamespace(ns)
+	resource.SetName(name)
+	return resource
+}
+
+func TestInstallerSetClient_Create(t *testing.T) {
+	releaseVersion := "devel"
+	comp := &v1alpha1.TektonTrigger{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "trigger",
+		},
+		Spec: v1alpha1.TektonTriggerSpec{
+			CommonSpec: v1alpha1.CommonSpec{TargetNamespace: "test"},
+		},
+	}
+
+	tests := []struct {
+		name      string
+		resources []unstructured.Unstructured
+		setType   string
+		wantIS    int
+		wantErr   error
+	}{
+		{
+			name:      "create pre set",
+			setType:   InstallerTypePre,
+			resources: []unstructured.Unstructured{serviceAccount, deployment},
+			wantIS:    1,
+			wantErr:   nil,
+		},
+		{
+			name:      "create post set",
+			setType:   InstallerTypePost,
+			resources: []unstructured.Unstructured{serviceAccount, deployment},
+			wantIS:    1,
+			wantErr:   nil,
+		},
+		{
+			name:      "create main set",
+			setType:   InstallerTypeMain,
+			resources: []unstructured.Unstructured{serviceAccount, deployment},
+			wantIS:    2,
+			wantErr:   nil,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ctx, _ := testing2.SetupFakeContext(t)
+
+			fakeclient := fake.NewSimpleClientset()
+			tisClient := fakeclient.OperatorV1alpha1().TektonInstallerSets()
+
+			manifest, err := mf.ManifestFrom(mf.Slice(tt.resources))
+			if err != nil {
+				t.Fatalf("Failed to generate manifest: %v", err)
+			}
+
+			client := NewInstallerSetClient(tisClient, &manifest, releaseVersion, "test-version", v1alpha1.KindTektonTrigger,
+				filterAndTransform(common.NoExtension(ctx)), &testMetrics{})
+
+			// fake.NewSimpleClientset() doesn't consider generate name when creating a resources
+			// so we write a fake client to test
+			// if we create one installerSet, it saves the name as "", then for the second installeSet
+			// it tries save as "", and return already exist error
+
+			if tt.setType == InstallerTypeMain {
+				fakeClient := fake2.NewFakeISClient()
+				client = NewInstallerSetClient(fakeClient, &manifest, releaseVersion, "test-version", v1alpha1.KindTektonTrigger,
+					filterAndTransform(common.NoExtension(ctx)), &testMetrics{})
+			}
+
+			iSs, gotErr := client.Create(ctx, comp, &manifest, tt.setType)
+
+			if tt.wantErr != nil {
+				assert.Equal(t, gotErr, tt.wantErr)
+				return
+			}
+			assert.NilError(t, gotErr)
+			assert.Equal(t, len(iSs), tt.wantIS)
+
+			if !strings.Contains(iSs[0].GenerateName, tt.setType) {
+				t.Fatalf("expected installer set type in generate name")
+			}
+			if tt.setType == InstallerTypeMain {
+				assert.Assert(t, len(iSs[0].Spec.Manifests) != 0, "resource list must not be empty here")
+				assert.Assert(t, len(iSs[1].Spec.Manifests) != 0, "resource list must not be empty here")
+			}
+		})
+	}
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/metrics_test.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/metrics_test.go
new file mode 100644
index 000000000..f5e295d45
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/metrics_test.go
@@ -0,0 +1,25 @@
+/*
+Copyright 2022 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 client
+
+import "go.uber.org/zap"
+
+type testMetrics struct {
+}
+
+func (t *testMetrics) LogMetrics(string, string, *zap.SugaredLogger) {
+}
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/update_test.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/update_test.go
new file mode 100644
index 000000000..5967e24e7
--- /dev/null
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/update_test.go
@@ -0,0 +1,186 @@
+package client
+
+/*
+Copyright 2022 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.
+*/
+
+import (
+	"context"
+	"testing"
+
+	mf "github.com/manifestival/manifestival"
+	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
+	"github.com/tektoncd/operator/pkg/client/clientset/versioned/fake"
+	"github.com/tektoncd/operator/pkg/reconciler/common"
+	"github.com/tektoncd/operator/pkg/reconciler/shared/hash"
+	"gotest.tools/v3/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
+	testing2 "knative.dev/pkg/reconciler/testing"
+)
+
+func updateFilterAndTransform(extension common.Extension, updatedNs string) FilterAndTransform {
+	return func(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) (*mf.Manifest, error) {
+		updatedManifet, err := manifest.Transform(mf.InjectNamespace(updatedNs))
+		if err != nil {
+			return nil, err
+		}
+		return &updatedManifet, nil
+	}
+}
+
+func TestInstallerSetClient_Update(t *testing.T) {
+	updatedNs := "updated-ns"
+	releaseVersion := "devel"
+	comp := &v1alpha1.TektonTrigger{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "trigger",
+		},
+		Spec: v1alpha1.TektonTriggerSpec{
+			CommonSpec: v1alpha1.CommonSpec{TargetNamespace: "test"},
+		},
+	}
+
+	expectedHash, err := hash.Compute(comp.GetSpec())
+	assert.NilError(t, err)
+
+	tests := []struct {
+		name       string
+		existingIS []v1alpha1.TektonInstallerSet
+		resources  []unstructured.Unstructured
+		setType    string
+		wantIS     int
+		wantErr    error
+	}{
+		{
+			name:    "update pre set",
+			setType: InstallerTypePre,
+			existingIS: []v1alpha1.TektonInstallerSet{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "trigger-pre-8vue6",
+						Annotations: map[string]string{
+							v1alpha1.LastAppliedHashKey: "test",
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{
+						Manifests: []unstructured.Unstructured{
+							serviceAccount, deployment,
+						},
+					},
+				},
+			},
+			resources: []unstructured.Unstructured{serviceAccount, deployment},
+			wantErr:   nil,
+		},
+		{
+			name:    "update post set",
+			setType: InstallerTypePost,
+			existingIS: []v1alpha1.TektonInstallerSet{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "trigger-post-8vue6",
+						Annotations: map[string]string{
+							v1alpha1.LastAppliedHashKey: "test-post",
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{
+						Manifests: []unstructured.Unstructured{
+							serviceAccount, deployment,
+						},
+					},
+				},
+			},
+			resources: []unstructured.Unstructured{serviceAccount, deployment},
+			wantErr:   nil,
+		},
+		{
+			name:    "update main sets",
+			setType: InstallerTypeMain,
+			existingIS: []v1alpha1.TektonInstallerSet{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "trigger-main-static-8vue6",
+						Annotations: map[string]string{
+							v1alpha1.LastAppliedHashKey: "test-post",
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{
+						Manifests: []unstructured.Unstructured{
+							serviceAccount,
+						},
+					},
+				},
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "trigger-main-deployment-asd34",
+						Annotations: map[string]string{
+							v1alpha1.LastAppliedHashKey: "test-post",
+						},
+					},
+					Spec: v1alpha1.TektonInstallerSetSpec{
+						Manifests: []unstructured.Unstructured{
+							deployment,
+						},
+					},
+				},
+			},
+			resources: []unstructured.Unstructured{serviceAccount, deployment},
+			wantErr:   nil,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ctx, _ := testing2.SetupFakeContext(t)
+
+			runObj := []runtime.Object{}
+			for i := range tt.existingIS {
+				runObj = append(runObj, &tt.existingIS[i])
+			}
+
+			manifest, err := mf.ManifestFrom(mf.Slice(tt.resources))
+			if err != nil {
+				t.Fatalf("Failed to generate manifest: %v", err)
+			}
+
+			fakeclient := fake.NewSimpleClientset(runObj...)
+			tisClient := fakeclient.OperatorV1alpha1().TektonInstallerSets()
+
+			client := NewInstallerSetClient(tisClient, &manifest, releaseVersion, "test-version", v1alpha1.KindTektonTrigger,
+				updateFilterAndTransform(common.NoExtension(ctx), updatedNs), &testMetrics{})
+
+			updatedISs, gotErr := client.Update(ctx, comp, tt.existingIS, &manifest, tt.setType)
+			if tt.wantErr != nil {
+				assert.Equal(t, gotErr, tt.wantErr)
+				return
+			}
+			assert.NilError(t, gotErr)
+
+			// based on transformer all the resource namespace should be changed
+			if tt.setType != InstallerTypeMain {
+				assert.Equal(t, updatedISs[0].Spec.Manifests[0].GetNamespace(), updatedNs)
+				assert.Equal(t, updatedISs[0].Spec.Manifests[1].GetNamespace(), updatedNs)
+				assert.Equal(t, updatedISs[0].Annotations[v1alpha1.LastAppliedHashKey], expectedHash)
+			} else {
+				assert.Equal(t, updatedISs[0].Spec.Manifests[0].GetNamespace(), updatedNs)
+				assert.Equal(t, updatedISs[1].Spec.Manifests[0].GetNamespace(), updatedNs)
+				assert.Equal(t, updatedISs[0].Annotations[v1alpha1.LastAppliedHashKey], expectedHash)
+				assert.Equal(t, updatedISs[1].Annotations[v1alpha1.LastAppliedHashKey], expectedHash)
+			}
+		})
+	}
+}
diff --git a/pkg/reconciler/shared/tektonconfig/trigger/trigger_test.go b/pkg/reconciler/shared/tektonconfig/trigger/trigger_test.go
index a79c26fe8..3b094ed2e 100644
--- a/pkg/reconciler/shared/tektonconfig/trigger/trigger_test.go
+++ b/pkg/reconciler/shared/tektonconfig/trigger/trigger_test.go
@@ -43,7 +43,7 @@ func TestEnsureTektonTriggerExists(t *testing.T) {
 	// during second invocation instance exists but waiting on dependencies (pipeline, triggers)
 	// hence returns DEPENDENCY_UPGRADE_PENDING_ERR
 	_, err = EnsureTektonTriggerExists(ctx, c.OperatorV1alpha1().TektonTriggers(), tConfig)
-	util.AssertEqual(t, err, v1alpha1.DEPENDENCY_UPGRADE_PENDING_ERR)
+	util.AssertEqual(t, err, v1alpha1.RECONCILE_AGAIN_ERR)
 
 	// make upgrade checks pass
 	makeUpgradeCheckPass(t, ctx, c.OperatorV1alpha1().TektonTriggers())