Repository: operator-framework/operator-sdk
Pull request #5092 (2021-07-27 15:25:05)

Add UniqueID to XML Output
Signed-off-by: Ish Shah <ishah@redhat.com>

Include optional Unique ID in XML output, change was withheld earlier as an API update was necessary 

estroz: @theishshah can you link the `api` repo PR, and add a changelog entry here
theishshah: https://github.com/operator-framework/api/pull/110

Here is the API PR, this one merged a while back. The type and name were chosen based on QE's requests for it in Xunit. 

Patch:
diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD
index d3c390a6..abfd8fa0 100644
--- a/CONTRIBUTING.MD
+++ b/CONTRIBUTING.MD
@@ -64,8 +64,13 @@ The first line is the subject and should be no longer than 70 characters, the se
 
 Most contributions involve some sort of documentation. See our [doc contribution guidelines][doc-contrib] for details.
 
+## Changelog
+
+The operator-sdk maintains a changelog and most contributions should involve a changelog entry. See our [changelog contribution guidelines][changelog-contrib] for details.
+
 [operator_framework]: https://groups.google.com/forum/#!forum/operator-framework
 [developer-guide]: https://sdk.operatorframework.io/docs/contribution-guidelines/developer-guide/
 [reporting-issues]: https://sdk.operatorframework.io/docs/contribution-guidelines/reporting-issues/
 [golang-style-doc]: https://github.com/golang/go/wiki/CodeReviewComments
-[doc-contrib]:https://sdk.operatorframework.io/docs/contribution-guidelines/documentation
+[doc-contrib]: https://sdk.operatorframework.io/docs/contribution-guidelines/documentation
+[changelog-contrib]:https://master.sdk.operatorframework.io/docs/contribution-guidelines/changelog
diff --git a/Makefile b/Makefile
index de6e426f..5ca53dfe 100644
--- a/Makefile
+++ b/Makefile
@@ -4,13 +4,14 @@ SHELL = /bin/bash
 # This value must be updated to the release tag of the most recent release, a change that must
 # occur in the release commit. IMAGE_VERSION will be removed once each subproject that uses this
 # version is moved to a separate repo and release process.
-export IMAGE_VERSION = v1.10.0
+export IMAGE_VERSION = v1.11.0
 # Build-time variables to inject into binaries
 export SIMPLE_VERSION = $(shell (test "$(shell git describe)" = "$(shell git describe --abbrev=0)" && echo $(shell git describe)) || echo $(shell git describe --abbrev=0)+git)
 export GIT_VERSION = $(shell git describe --dirty --tags --always)
 export GIT_COMMIT = $(shell git rev-parse HEAD)
+export K8S_VERSION = 1.21
 # TODO: bump this to 1.21, after kubectl `--generator` flag is removed from e2e tests.
-export K8S_VERSION = 1.20.2
+export ENVTEST_K8S_VERSION = 1.20.2
 
 # Build settings
 export TOOLS_DIR = tools/bin
@@ -146,11 +147,11 @@ e2e_targets := test-e2e $(e2e_tests)
 .PHONY: test-e2e-setup
 export KIND_CLUSTER := operator-sdk-e2e
 
-export KUBEBUILDER_ASSETS = $(PWD)/$(shell go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest && $(shell go env GOPATH)/bin/setup-envtest use $(K8S_VERSION) --bin-dir tools/bin/ -p path)
+export KUBEBUILDER_ASSETS = $(PWD)/$(shell go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest && $(shell go env GOPATH)/bin/setup-envtest use $(ENVTEST_K8S_VERSION) --bin-dir tools/bin/ -p path)
 test-e2e-setup: build
 	$(SCRIPTS_DIR)/fetch kind 0.11.0
-	$(SCRIPTS_DIR)/fetch kubectl $(K8S_VERSION) # Install kubectl AFTER envtest because envtest includes its own kubectl binary
-	[[ "`$(TOOLS_DIR)/kind get clusters`" =~ "$(KIND_CLUSTER)" ]] || $(TOOLS_DIR)/kind create cluster --image="kindest/node:v$(K8S_VERSION)" --name $(KIND_CLUSTER)
+	$(SCRIPTS_DIR)/fetch kubectl $(ENVTEST_K8S_VERSION) # Install kubectl AFTER envtest because envtest includes its own kubectl binary
+	[[ "`$(TOOLS_DIR)/kind get clusters`" =~ "$(KIND_CLUSTER)" ]] || $(TOOLS_DIR)/kind create cluster --image="kindest/node:v$(ENVTEST_K8S_VERSION)" --name $(KIND_CLUSTER)
 
 .PHONY: test-e2e-teardown
 test-e2e-teardown:
@@ -162,7 +163,7 @@ test-e2e-teardown:
 # Repeated rules are executed in the order they appear.
 $(e2e_targets):: test-e2e-setup image/scorecard-test
 test-e2e:: $(e2e_tests) ## Run e2e tests
-	
+
 test-e2e-go:: image/custom-scorecard-tests ## Run Go e2e tests
 	go test ./test/e2e/go -v -ginkgo.v
 test-e2e-ansible:: image/ansible-operator ## Run Ansible e2e tests
diff --git a/changelog/fragments/detect-request-ns.yaml b/changelog/fragments/detect-request-ns.yaml
deleted file mode 100644
index d6cdce2f..00000000
--- a/changelog/fragments/detect-request-ns.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-entries:
-  - description: >
-      For Ansible-based operators, if a request is sent without a body in
-      the metadata it will now be extracted from the request URL and properly
-      set owner references/dependent watches.
-    kind: "bugfix"
-    breaking: false
-
diff --git a/changelog/fragments/uid-output.yaml b/changelog/fragments/uid-output.yaml
new file mode 100644
index 00000000..18127490
--- /dev/null
+++ b/changelog/fragments/uid-output.yaml
@@ -0,0 +1,5 @@
+entries:
+  - description: >
+      If an optional UniqueID is provided by a user in the scorecard config and the user generates XML output the UID will be appended to the test result output.
+    kind: "addition"
+    breaking: false
diff --git a/changelog/generated/v1.11.0.md b/changelog/generated/v1.11.0.md
new file mode 100644
index 00000000..029383ba
--- /dev/null
+++ b/changelog/generated/v1.11.0.md
@@ -0,0 +1,20 @@
+## v1.11.0
+
+### Additions
+
+- (go/v3, ansible/v1, helm/v1) Added `containerPort` protocol field required for server-side apply in manifests. ([#4863](https://github.com/operator-framework/operator-sdk/pull/4863))
+- For helm based operators, added a predicate that filters resources based on selectors specified in `watches.yaml`. Only the resources that contain the labels specified by selectors will be reconciled. ([#4997](https://github.com/operator-framework/operator-sdk/pull/4997))
+- For helm-based operators, support go `text/template` expansion of override values. ([#5105](https://github.com/operator-framework/operator-sdk/pull/5105))
+
+### Changes
+
+- (go/v3) Fixed a bug in `make test` caused by incorrect envtest asset setup. ([#4863](https://github.com/operator-framework/operator-sdk/pull/4863))
+- (go/v3, ansible/v1, helm/v1) Increased resource limits for controller manager. ([#4863](https://github.com/operator-framework/operator-sdk/pull/4863))
+- Updated operator-framework dependency to 0.10.5. This specific update includes a fix for bundle validation when checking for invalid service accounts. ([#5119](https://github.com/operator-framework/operator-sdk/pull/5119))
+- For helm-based operators, reverted #5042 and #4937  which modified helm release equality comparison. ([#5097](https://github.com/operator-framework/operator-sdk/pull/5097))
+
+### Bug Fixes
+
+- (ansible/v1) Previously, when scaffolding an Ansible-based operator with both Roles and Playbooks, the roles imported by the playbook could not be found when running locally (`make run`). This change prepends the `ANSIBLE_ROLES_PATH` environment variable with the path to the local roles directory. ([#5118](https://github.com/operator-framework/operator-sdk/pull/5118))
+- For Ansible-based operators, if a request is sent without a body in the metadata it will now be extracted from the request URL and properly set owner references/dependent watches. ([#5064](https://github.com/operator-framework/operator-sdk/pull/5064))
+- In `generate bundle`, exclude ServiceAccounts already in a CSV from generated bundle. ([#5120](https://github.com/operator-framework/operator-sdk/pull/5120))
diff --git a/go.mod b/go.mod
index fafd3f49..12ba033a 100644
--- a/go.mod
+++ b/go.mod
@@ -6,15 +6,16 @@ require (
 	github.com/blang/semver/v4 v4.0.0
 	github.com/fatih/structtag v1.1.0
 	github.com/go-logr/logr v0.4.0
+	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
 	github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
 	github.com/kr/text v0.2.0
 	github.com/markbates/inflect v1.0.4
 	github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2
 	github.com/onsi/ginkgo v1.16.4
 	github.com/onsi/gomega v1.13.0
-	github.com/operator-framework/api v0.10.2
+	github.com/operator-framework/api v0.10.5
 	github.com/operator-framework/java-operator-plugins v0.0.0-20210708174638-463fb91f3d5e
-	github.com/operator-framework/operator-lib v0.5.0
+	github.com/operator-framework/operator-lib v0.6.0
 	github.com/operator-framework/operator-registry v1.17.4
 	github.com/prometheus/client_golang v1.11.0
 	github.com/prometheus/client_model v0.2.0
@@ -39,7 +40,7 @@ require (
 	k8s.io/kubectl v0.21.0
 	sigs.k8s.io/controller-runtime v0.9.2
 	sigs.k8s.io/controller-tools v0.6.0
-	sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210707171043-359e8aa70c2b
+	sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210803185103-51e4a9aa5055
 	sigs.k8s.io/yaml v1.2.0
 )
 
diff --git a/go.sum b/go.sum
index 5e52c8fc..2cd620c1 100644
--- a/go.sum
+++ b/go.sum
@@ -409,6 +409,7 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
 github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
 github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
@@ -823,12 +824,12 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
 github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
 github.com/operator-framework/api v0.7.1/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
 github.com/operator-framework/api v0.10.0/go.mod h1:tV0BUNvly7szq28ZPBXhjp1Sqg5yHCOeX19ui9K4vjI=
-github.com/operator-framework/api v0.10.2 h1:fo8Bhyx1v46NdJIz2rUzfzNUpe1KDNPtVpHVpuxZnk0=
-github.com/operator-framework/api v0.10.2/go.mod h1:tV0BUNvly7szq28ZPBXhjp1Sqg5yHCOeX19ui9K4vjI=
+github.com/operator-framework/api v0.10.5 h1:/WvLKOPo8zZMyEmuW0kLC0PJBt4Xal8HZkFioKIxqTA=
+github.com/operator-framework/api v0.10.5/go.mod h1:tV0BUNvly7szq28ZPBXhjp1Sqg5yHCOeX19ui9K4vjI=
 github.com/operator-framework/java-operator-plugins v0.0.0-20210708174638-463fb91f3d5e h1:LMsT59IJqaLn7kD6DnZFy0IouRufXyJHTT+mXQrl9Ps=
 github.com/operator-framework/java-operator-plugins v0.0.0-20210708174638-463fb91f3d5e/go.mod h1:sGKGELFkUeRqElcyvyPC89bC76YnCL7MPMa13P0AQcw=
-github.com/operator-framework/operator-lib v0.5.0 h1:Jmhz/WjcstEyBBM9IFUiHEgKg5bd43uF4ej/ZY2S0rM=
-github.com/operator-framework/operator-lib v0.5.0/go.mod h1:33Skl0vjauYx3nAS+cSFbHNkX8do7weQ6s5siIV/w1E=
+github.com/operator-framework/operator-lib v0.6.0 h1:srZoTL8P7OZUOovMkQkd4vwIbFzFNW413R/6V9N9rz4=
+github.com/operator-framework/operator-lib v0.6.0/go.mod h1:2Z32GTTJUz2/f+OKcoJXsVnAyRwcXx7mGmQsdhIAIIE=
 github.com/operator-framework/operator-registry v1.17.4 h1:bYoWevurGEUshSMu8QNcImhLuPZJ/a4MbsUuvBjFEzA=
 github.com/operator-framework/operator-registry v1.17.4/go.mod h1:k0rWVT23QoN1prs9tX8PHjRVXz6FMZfUJ5EIZSrqh9E=
 github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k=
@@ -1624,8 +1625,8 @@ sigs.k8s.io/controller-tools v0.6.0 h1:o2Fm1K7CmIp8OVaBtXsWB/ssBAzyoKZPPAGR3Vuxa
 sigs.k8s.io/controller-tools v0.6.0/go.mod h1:baRMVPrctU77F+rfAuH2uPqW93k6yQnZA2dhUOr7ihc=
 sigs.k8s.io/kind v0.10.0/go.mod h1:fb32zUw7ewC47bPwLnwhf47wd/vADtv3c38KP7sjIlo=
 sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210702145813-742983631190/go.mod h1:pUhjQx9f/+cn1OtSa5zMohY1lgk9s/9Mbcvwj82lrNk=
-sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210707171043-359e8aa70c2b h1:fGbw9og/lq/fNi4afnVwSinfaQXkonlbTar9nG95u44=
-sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210707171043-359e8aa70c2b/go.mod h1:pUhjQx9f/+cn1OtSa5zMohY1lgk9s/9Mbcvwj82lrNk=
+sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210803185103-51e4a9aa5055 h1:3f5m0xp0DEsg+Sdrt1Rld8TCEFOH5GKsoYrEpdOzA+s=
+sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210803185103-51e4a9aa5055/go.mod h1:pUhjQx9f/+cn1OtSa5zMohY1lgk9s/9Mbcvwj82lrNk=
 sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
 sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
 sigs.k8s.io/kustomize/api v0.8.5 h1:bfCXGXDAbFbb/Jv5AhMj2BB8a5VAJuuQ5/KU69WtDjQ=
diff --git a/internal/ansible/controller/controller.go b/internal/ansible/controller/controller.go
index 4c7a0d78..2e1b4188 100644
--- a/internal/ansible/controller/controller.go
+++ b/internal/ansible/controller/controller.go
@@ -17,6 +17,7 @@ package controller
 import (
 	"fmt"
 	"os"
+	"reflect"
 	"strings"
 	"time"
 
@@ -33,7 +34,6 @@ import (
 
 	"github.com/operator-framework/operator-sdk/internal/ansible/events"
 	"github.com/operator-framework/operator-sdk/internal/ansible/handler"
-	"github.com/operator-framework/operator-sdk/internal/ansible/predicate"
 	"github.com/operator-framework/operator-sdk/internal/ansible/runner"
 )
 
@@ -103,12 +103,17 @@ func Add(mgr manager.Manager, options Options) *controller.Controller {
 	predicates := []ctrlpredicate.Predicate{
 		ctrlpredicate.Or(ctrlpredicate.GenerationChangedPredicate{}, libpredicate.NoGenerationPredicate{}),
 	}
-	filterPredicate, err := predicate.NewResourceFilterPredicate(options.Selector)
+
+	p, err := parsePredicateSelector(options.Selector)
+
 	if err != nil {
-		log.Error(err, "Error creating resource filter predicate")
+		log.Error(err, "")
 		os.Exit(1)
 	}
-	predicates = append(predicates, filterPredicate)
+
+	if p != nil {
+		predicates = append(predicates, p)
+	}
 
 	u := &unstructured.Unstructured{}
 	u.SetGroupVersionKind(options.GVK)
@@ -120,3 +125,17 @@ func Add(mgr manager.Manager, options Options) *controller.Controller {
 
 	return &c
 }
+
+// parsePredicateSelector parses the selector in the WatchOptions and creates a predicate
+// that is used to filter resources based on the specified selector
+func parsePredicateSelector(selector metav1.LabelSelector) (ctrlpredicate.Predicate, error) {
+	// If a selector has been specified in watches.yaml, add it to the watch's predicates.
+	if !reflect.ValueOf(selector).IsZero() {
+		p, err := ctrlpredicate.LabelSelectorPredicate(selector)
+		if err != nil {
+			return nil, fmt.Errorf("error constructing predicate from watches selector: %v", err)
+		}
+		return p, nil
+	}
+	return nil, nil
+}
diff --git a/internal/ansible/predicate/predicate.go b/internal/ansible/predicate/predicate.go
deleted file mode 100644
index 9f0e3723..00000000
--- a/internal/ansible/predicate/predicate.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2018 The Operator-SDK 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 predicate
-
-import (
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/labels"
-	"sigs.k8s.io/controller-runtime/pkg/event"
-	"sigs.k8s.io/controller-runtime/pkg/predicate"
-)
-
-type resourceFilterPredicate struct {
-	predicate.Funcs
-	Selector labels.Selector
-}
-
-// Skips events that have labels matching selectors defined in watches.yaml
-func (r resourceFilterPredicate) eventFilter(eventLabels map[string]string) bool {
-	return r.Selector.Matches(labels.Set(eventLabels))
-}
-
-func NewResourceFilterPredicate(s metav1.LabelSelector) (predicate.Predicate, error) {
-	selectorSpecs, err := metav1.LabelSelectorAsSelector(&s)
-	requirements := resourceFilterPredicate{Selector: selectorSpecs}
-	return requirements, err
-
-}
-
-// Predicate functions that call the EventFilter Function
-func (r resourceFilterPredicate) Update(e event.UpdateEvent) bool {
-	return r.eventFilter(e.ObjectNew.GetLabels())
-}
-
-func (r resourceFilterPredicate) Create(e event.CreateEvent) bool {
-	return r.eventFilter(e.Object.GetLabels())
-}
-
-func (r resourceFilterPredicate) Delete(e event.DeleteEvent) bool {
-	return r.eventFilter(e.Object.GetLabels())
-}
-
-func (r resourceFilterPredicate) Generic(e event.GenericEvent) bool {
-	return r.eventFilter(e.Object.GetLabels())
-}
diff --git a/internal/ansible/watches/watches.go b/internal/ansible/watches/watches.go
index c42fae03..48204687 100644
--- a/internal/ansible/watches/watches.go
+++ b/internal/ansible/watches/watches.go
@@ -85,36 +85,6 @@ var (
 	ansibleVerbosityDefault        = 2
 )
 
-// Creates, populates, and returns a LabelSelector object. Used in Unmarshal().
-func parseLabelSelector(dls tempLabelSelector) metav1.LabelSelector {
-	obj := metav1.LabelSelector{}
-	obj.MatchLabels = dls.MatchLabels
-
-	for _, v := range dls.MatchExpressions {
-		requirement := metav1.LabelSelectorRequirement{
-			Key:      v.Key,
-			Operator: v.Operator,
-			Values:   v.Values,
-		}
-
-		obj.MatchExpressions = append(obj.MatchExpressions, requirement)
-	}
-
-	return obj
-}
-
-// Temporary structs created to store yaml parsing
-type tempLabelSelector struct {
-	MatchLabels      map[string]string `yaml:"matchLabels,omitempty"`
-	MatchExpressions []tempRequirement `json:"matchExpressions,omitempty"`
-}
-
-type tempRequirement struct {
-	Key      string                       `json:"key"`
-	Operator metav1.LabelSelectorOperator `json:"operator"`
-	Values   []string                     `json:"values,omitempty"`
-}
-
 // Use an alias struct to handle complex types
 type alias struct {
 	Group                       string                    `yaml:"group"`
@@ -132,7 +102,7 @@ type alias struct {
 	MarkUnsafe                  *bool                     `yaml:"markUnsafe"`
 	Blacklist                   []schema.GroupVersionKind `yaml:"blacklist,omitempty"`
 	Finalizer                   *Finalizer                `yaml:"finalizer"`
-	Selector                    tempLabelSelector         `yaml:"selector"`
+	Selector                    metav1.LabelSelector      `yaml:"selector"`
 }
 
 // buildWatch will build Watch based on the values parsed from alias
@@ -201,7 +171,7 @@ func (w *Watch) setValuesFromAlias(tmp alias) error {
 		return err
 	}
 	w.addRolePlaybookPaths(wd)
-	w.Selector = parseLabelSelector(tmp.Selector)
+	w.Selector = tmp.Selector
 
 	return nil
 }
diff --git a/internal/cmd/helm-operator/run/cmd.go b/internal/cmd/helm-operator/run/cmd.go
index ef367713..e909d27c 100644
--- a/internal/cmd/helm-operator/run/cmd.go
+++ b/internal/cmd/helm-operator/run/cmd.go
@@ -202,6 +202,7 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 			WatchDependentResources: *w.WatchDependentResources,
 			OverrideValues:          w.OverrideValues,
 			MaxConcurrentReconciles: f.MaxConcurrentReconciles,
+			Selector:                w.Selector,
 		})
 		if err != nil {
 			log.Error(err, "Failed to add manager factory to controller.")
diff --git a/internal/cmd/operator-sdk/generate/internal/manifests.go b/internal/cmd/operator-sdk/generate/internal/manifests.go
index 7e9ffb3a..5834684f 100644
--- a/internal/cmd/operator-sdk/generate/internal/manifests.go
+++ b/internal/cmd/operator-sdk/generate/internal/manifests.go
@@ -31,23 +31,6 @@ func GetManifestObjects(c *collector.Manifests, extraSAs []string) (objs []clien
 		objs = append(objs, &c.V1beta1CustomResourceDefinitions[i])
 	}
 
-	// All ServiceAccounts passed in should be written.
-	saSet := make(map[string]struct{}, len(extraSAs))
-	for _, saName := range extraSAs {
-		saSet[saName] = struct{}{}
-	}
-	for i := range c.ServiceAccounts {
-		sa := c.ServiceAccounts[i]
-		saSet[sa.GetName()] = struct{}{}
-		objs = append(objs, &sa)
-	}
-	extraSAs = make([]string, len(saSet))
-	i := 0
-	for saName := range saSet {
-		extraSAs[i] = saName
-		i++
-	}
-
 	// All Services passed in should be written.
 	for i := range c.Services {
 		objs = append(objs, &c.Services[i])
@@ -61,7 +44,7 @@ func GetManifestObjects(c *collector.Manifests, extraSAs []string) (objs []clien
 		}
 	}
 
-	// RBAC objects that are not a part of the CSV should be written.
+	// RBAC objects (including ServiceAccounts) that are not a part of the CSV should be written.
 	_, _, rbacObjs := c.SplitCSVPermissionsObjects(extraSAs)
 	objs = append(objs, rbacObjs...)
 
diff --git a/internal/cmd/operator-sdk/scorecard/cmd.go b/internal/cmd/operator-sdk/scorecard/cmd.go
index 98b18b93..a42a95a5 100644
--- a/internal/cmd/operator-sdk/scorecard/cmd.go
+++ b/internal/cmd/operator-sdk/scorecard/cmd.go
@@ -142,8 +142,11 @@ func (c *scorecardCmd) convertXunit(output v1alpha3.TestList) xunit.TestSuites {
 			}
 			tSuite.TestCases = append(tSuite.TestCases, tCase)
 			tSuite.URL = item.Spec.Image
-			//TODO: Add TestStuite ID when API updates version
-			//tSuite.ID = item.Spec.UniqueID
+			if item.Spec.UniqueID != "" {
+				tSuite.ID = item.Spec.UniqueID
+			} else {
+				tSuite.ID = res.Name
+			}
 			resultSuite.TestSuite = append(resultSuite.TestSuite, tSuite)
 		}
 	}
diff --git a/internal/generate/collector/clusterserviceversion.go b/internal/generate/collector/clusterserviceversion.go
index 46436235..c658aeea 100644
--- a/internal/generate/collector/clusterserviceversion.go
+++ b/internal/generate/collector/clusterserviceversion.go
@@ -38,16 +38,16 @@ const (
 // are added to out. Any bindings with some associations, but with non-associations, are added to out unmodified.
 func (c *Manifests) SplitCSVPermissionsObjects(extraSAs []string) (inPerms, inCPerms, out []client.Object) {
 	// Create a set of ServiceAccount names to match against below.
-	saNameSet := make(map[string]struct{})
+	csvSAs := make(map[string]struct{})
 	for _, dep := range c.Deployments {
 		saName := dep.Spec.Template.Spec.ServiceAccountName
 		if saName == "" {
 			saName = defaultServiceAccountName
 		}
-		saNameSet[saName] = struct{}{}
+		csvSAs[saName] = struct{}{}
 	}
 	for _, extraSA := range extraSAs {
-		saNameSet[extraSA] = struct{}{}
+		csvSAs[extraSA] = struct{}{}
 	}
 
 	// Construct sets for lookups.
@@ -86,9 +86,9 @@ func (c *Manifests) SplitCSVPermissionsObjects(extraSAs []string) (inPerms, inCP
 	// bound Subjects from partial bindings to easily find concrete bindings that bind non-CSV RBAC.
 	// Those with no Subjects left will be removed from the partial binding lists; their concrete counterparts should
 	// be added to out.
-	inRoleNames := pRoleBindings.getRolesBoundToPartialBindings(roleKind, roleNameMap, saNameSet)
-	inCRoleNamesNScope := pRoleBindings.getRolesBoundToPartialBindings(cRoleKind, cRoleNameMap, saNameSet)
-	inCRoleNamesCScope := pCRoleBindings.getRolesBoundToPartialBindings(cRoleKind, cRoleNameMap, saNameSet)
+	inRoleNames := pRoleBindings.getRolesBoundToPartialBindings(roleKind, roleNameMap, csvSAs)
+	inCRoleNamesNScope := pRoleBindings.getRolesBoundToPartialBindings(cRoleKind, cRoleNameMap, csvSAs)
+	inCRoleNamesCScope := pCRoleBindings.getRolesBoundToPartialBindings(cRoleKind, cRoleNameMap, csvSAs)
 
 	// Add {Cluster}Roles bound to a ServiceAccount to either namespace- or cluster-scoped permission sets.
 	for _, roleName := range inRoleNames {
@@ -120,6 +120,14 @@ func (c *Manifests) SplitCSVPermissionsObjects(extraSAs []string) (inPerms, inCP
 		out = append(out, cRoleBindingMap[pBinding.Name])
 	}
 
+	// All ServiceAccounts not in the CSV should be in out.
+	for i := range c.ServiceAccounts {
+		sa := c.ServiceAccounts[i]
+		if _, csvHasSA := csvSAs[sa.Name]; !csvHasSA {
+			out = append(out, &sa)
+		}
+	}
+
 	return inPerms, inCPerms, out
 }
 
diff --git a/internal/helm/controller/controller.go b/internal/helm/controller/controller.go
index 3870fa78..7b4e44ba 100644
--- a/internal/helm/controller/controller.go
+++ b/internal/helm/controller/controller.go
@@ -16,6 +16,7 @@ package controller
 
 import (
 	"fmt"
+	"reflect"
 	"strings"
 	"sync"
 	"time"
@@ -30,6 +31,7 @@ import (
 	crthandler "sigs.k8s.io/controller-runtime/pkg/handler"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
+	ctrlpredicate "sigs.k8s.io/controller-runtime/pkg/predicate"
 	"sigs.k8s.io/controller-runtime/pkg/source"
 	"sigs.k8s.io/yaml"
 
@@ -51,6 +53,7 @@ type WatchOptions struct {
 	WatchDependentResources bool
 	OverrideValues          map[string]string
 	MaxConcurrentReconciles int
+	Selector                metav1.LabelSelector
 }
 
 // Add creates a new helm operator controller and adds it to the manager
@@ -80,7 +83,19 @@ func Add(mgr manager.Manager, options WatchOptions) error {
 
 	o := &unstructured.Unstructured{}
 	o.SetGroupVersionKind(options.GVK)
-	if err := c.Watch(&source.Kind{Type: o}, &libhandler.InstrumentedEnqueueRequestForObject{}); err != nil {
+
+	var preds []ctrlpredicate.Predicate
+	p, err := parsePredicateSelector(options.Selector)
+
+	if err != nil {
+		return err
+	}
+
+	if p != nil {
+		preds = append(preds, p)
+	}
+
+	if err := c.Watch(&source.Kind{Type: o}, &libhandler.InstrumentedEnqueueRequestForObject{}, preds...); err != nil {
 		return err
 	}
 
@@ -93,6 +108,20 @@ func Add(mgr manager.Manager, options WatchOptions) error {
 	return nil
 }
 
+// parsePredicateSelector parses the selector in the WatchOptions and creates a predicate
+// that is used to filter resources based on the specified selector
+func parsePredicateSelector(selector metav1.LabelSelector) (ctrlpredicate.Predicate, error) {
+	// If a selector has been specified in watches.yaml, add it to the watch's predicates.
+	if !reflect.ValueOf(selector).IsZero() {
+		p, err := ctrlpredicate.LabelSelectorPredicate(selector)
+		if err != nil {
+			return nil, fmt.Errorf("error constructing predicate from watches selector: %v", err)
+		}
+		return p, nil
+	}
+	return nil, nil
+}
+
 // watchDependentResources adds a release hook function to the HelmOperatorReconciler
 // that adds watches for resources in released Helm charts.
 func watchDependentResources(mgr manager.Manager, r *HelmOperatorReconciler, c controller.Controller) {
diff --git a/internal/helm/release/manager.go b/internal/helm/release/manager.go
index ffeb40b2..07641ccc 100644
--- a/internal/helm/release/manager.go
+++ b/internal/helm/release/manager.go
@@ -20,7 +20,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"reflect"
 	"strings"
 
 	jsonpatch "gomodules.xyz/jsonpatch/v3"
@@ -33,7 +32,6 @@ import (
 	"helm.sh/helm/v3/pkg/storage/driver"
 	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
-	apiequality "k8s.io/apimachinery/pkg/api/equality"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -127,10 +125,15 @@ func (m *manager) Sync(ctx context.Context) error {
 	m.deployedRelease = deployedRelease
 	m.isInstalled = true
 
-	m.isUpgradeRequired, err = m.isUpgrade(deployedRelease)
+	// Get the next candidate release to determine if an upgrade is necessary.
+	candidateRelease, err := m.getCandidateRelease(m.namespace, m.releaseName, m.chart, m.values)
 	if err != nil {
-		return fmt.Errorf("failed to get upgrade status: %w", err)
+		return fmt.Errorf("failed to get candidate release: %w", err)
 	}
+	if deployedRelease.Manifest != candidateRelease.Manifest {
+		m.isUpgradeRequired = true
+	}
+
 	return nil
 }
 
@@ -138,43 +141,6 @@ func notFoundErr(err error) bool {
 	return err != nil && strings.Contains(err.Error(), "not found")
 }
 
-// This is caused by the different logic of loading from local and loading from secret
-// For example, the Raw field, which has the tag `json:"-"`, causes the Unmarshal to be lost when it into Release
-// We need to make them follow the JSON tag
-// see: https://github.com/helm/helm/blob/cf0c6fed519d48101cd69ce01a355125215ee46f/pkg/storage/driver/util.go#L81
-func equalJSONStruct(a, b interface{}) (bool, error) {
-	if reflect.ValueOf(a).IsNil() || reflect.ValueOf(b).IsNil() {
-		return apiequality.Semantic.DeepEqual(a, b), nil
-	}
-
-	aBuf, bBuf := &bytes.Buffer{}, &bytes.Buffer{}
-	err := json.NewEncoder(aBuf).Encode(a)
-	if err != nil {
-		return false, err
-	}
-	err = json.NewEncoder(bBuf).Encode(b)
-	return aBuf.String() == bBuf.String(), err
-}
-
-func (m manager) isUpgrade(deployedRelease *rpb.Release) (bool, error) {
-	if deployedRelease == nil {
-		return false, nil
-	}
-
-	// Judging whether to skip updates
-	skip := m.namespace == deployedRelease.Namespace
-	skip = skip && m.releaseName == deployedRelease.Name
-
-	ok, err := equalJSONStruct(m.chart, deployedRelease.Chart)
-	if err != nil {
-		return false, err
-	}
-	skip = skip && ok
-
-	ok, err = equalJSONStruct(m.values, deployedRelease.Config)
-	return !(skip && ok), err
-}
-
 func (m manager) getDeployedRelease() (*rpb.Release, error) {
 	deployedRelease, err := m.storageBackend.Deployed(m.releaseName)
 	if err != nil {
@@ -186,6 +152,14 @@ func (m manager) getDeployedRelease() (*rpb.Release, error) {
 	return deployedRelease, nil
 }
 
+func (m manager) getCandidateRelease(namespace, name string, chart *cpb.Chart,
+	values map[string]interface{}) (*rpb.Release, error) {
+	upgrade := action.NewUpgrade(m.actionConfig)
+	upgrade.Namespace = namespace
+	upgrade.DryRun = true
+	return upgrade.Run(name, chart, values)
+}
+
 // InstallRelease performs a Helm release install.
 func (m manager) InstallRelease(ctx context.Context, opts ...InstallOption) (*rpb.Release, error) {
 	install := action.NewInstall(m.actionConfig)
diff --git a/internal/helm/watches/watches.go b/internal/helm/watches/watches.go
index c02413ab..e2f43a1d 100644
--- a/internal/helm/watches/watches.go
+++ b/internal/helm/watches/watches.go
@@ -15,13 +15,17 @@
 package watches
 
 import (
+	"bytes"
 	"errors"
 	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
+	"text/template"
 
+	sprig "github.com/go-task/slim-sprig"
 	"helm.sh/helm/v3/pkg/chartutil"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"sigs.k8s.io/yaml"
 )
@@ -32,9 +36,10 @@ const WatchesFile = "watches.yaml"
 // custom resource.
 type Watch struct {
 	schema.GroupVersionKind `json:",inline"`
-	ChartDir                string            `json:"chart"`
-	WatchDependentResources *bool             `json:"watchDependentResources,omitempty"`
-	OverrideValues          map[string]string `json:"overrideValues,omitempty"`
+	ChartDir                string               `json:"chart"`
+	WatchDependentResources *bool                `json:"watchDependentResources,omitempty"`
+	OverrideValues          map[string]string    `json:"overrideValues,omitempty"`
+	Selector                metav1.LabelSelector `json:"selector"`
 }
 
 // UnmarshalYAML unmarshals an individual watch from the Helm watches.yaml file
@@ -110,21 +115,34 @@ func LoadReader(reader io.Reader) ([]Watch, error) {
 			trueVal := true
 			w.WatchDependentResources = &trueVal
 		}
-		w.OverrideValues = expandOverrideEnvs(w.OverrideValues)
+		w.OverrideValues, err = expandOverrideValues(w.OverrideValues)
+		if err != nil {
+			return nil, fmt.Errorf("failed to expand override values: %v", err)
+		}
 		watches[i] = w
 	}
 	return watches, nil
 }
 
-func expandOverrideEnvs(in map[string]string) map[string]string {
+func expandOverrideValues(in map[string]string) (map[string]string, error) {
 	if in == nil {
-		return nil
+		return nil, nil
 	}
 	out := make(map[string]string)
 	for k, v := range in {
-		out[k] = os.ExpandEnv(v)
+		envV := os.ExpandEnv(v)
+
+		v := &bytes.Buffer{}
+		tmplV, err := template.New(k).Funcs(sprig.TxtFuncMap()).Parse(envV)
+		if err != nil {
+			return nil, fmt.Errorf("invalid template string %q: %v", envV, err)
+		}
+		if err := tmplV.Execute(v, nil); err != nil {
+			return nil, fmt.Errorf("failed to execute template %q: %v", envV, err)
+		}
+		out[k] = v.String()
 	}
-	return out
+	return out, nil
 }
 
 func verifyGVK(gvk schema.GroupVersionKind) error {
diff --git a/internal/plugins/ansible/v1/init.go b/internal/plugins/ansible/v1/init.go
index 207c8dd4..680c7a9e 100644
--- a/internal/plugins/ansible/v1/init.go
+++ b/internal/plugins/ansible/v1/init.go
@@ -177,8 +177,8 @@ func addInitCustomizations(projectName string) error {
 	// todo: remove it when we solve the issue operator-framework/operator-sdk#3573
 	const resourcesLimitsFragment = `  resources:
           limits:
-            cpu: 100m
-            memory: 30Mi
+            cpu: 200m
+            memory: 100Mi
           requests:
             cpu: 100m
             memory: 20Mi
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/makefile.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/makefile.go
index f3f5d394..08d7ebca 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/makefile.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/makefile.go
@@ -89,7 +89,7 @@ help: ## Display this help.
 ##@ Build
 
 run: ansible-operator ## Run against the configured Kubernetes cluster in ~/.kube/config
-	$(ANSIBLE_OPERATOR) run
+	ANSIBLE_ROLES_PATH="$(ANSIBLE_ROLES_PATH):$(shell pwd)/roles" $(ANSIBLE_OPERATOR) run
 
 docker-build: ## Build docker image with the manager.
 	docker build -t ${IMG} .
diff --git a/internal/plugins/helm/v1/init.go b/internal/plugins/helm/v1/init.go
index f8ede16c..f2db1d4b 100644
--- a/internal/plugins/helm/v1/init.go
+++ b/internal/plugins/helm/v1/init.go
@@ -199,10 +199,6 @@ func addInitCustomizations(projectName string) error {
 	}
 
 	// Increase the default memory required.
-	err = sdkutil.ReplaceInFile(managerFile, "memory: 30Mi", "memory: 90Mi")
-	if err != nil {
-		return err
-	}
 	err = sdkutil.ReplaceInFile(managerFile, "memory: 20Mi", "memory: 60Mi")
 	if err != nil {
 		return err
diff --git a/website/config.toml b/website/config.toml
index 23e7e444..f8da20a1 100644
--- a/website/config.toml
+++ b/website/config.toml
@@ -95,6 +95,10 @@ algolia_docsearch = true
 
 ##RELEASE_ADDME##
 
+[[params.versions]]
+  version = "v1.11"
+  url = "https://v1-11-x.sdk.operatorframework.io"
+
 [[params.versions]]
   version = "v1.10"
   url = "https://v1-10-x.sdk.operatorframework.io"
diff --git a/website/content/en/docs/building-operators/ansible/installation.md b/website/content/en/docs/building-operators/ansible/installation.md
index 7840117f..04a4e305 100644
--- a/website/content/en/docs/building-operators/ansible/installation.md
+++ b/website/content/en/docs/building-operators/ansible/installation.md
@@ -13,7 +13,7 @@ Follow the steps in the [installation guide][install-guide] to learn how to inst
 - [docker][docker_tool] version 17.03+.
 - [python][python] version 3.8.6+
 - [ansible][ansible] version v2.9.0+
-- [ansible-runner][ansible-runner] version v1.1.0+
+- [ansible-runner][ansible-runner] version >=1.1.0,<2.0
 - [ansible-runner-http][ansible-runner-http-plugin] version v1.0.0+
 - [openshift][openshift-module] version v0.11.2+
 - [kubectl][kubectl_tool] and access to a Kubernetes cluster of a [compatible version][k8s-version-compat].
diff --git a/website/content/en/docs/building-operators/ansible/quickstart.md b/website/content/en/docs/building-operators/ansible/quickstart.md
index 9d762a85..3b002ed2 100644
--- a/website/content/en/docs/building-operators/ansible/quickstart.md
+++ b/website/content/en/docs/building-operators/ansible/quickstart.md
@@ -106,5 +106,5 @@ Read the [full tutorial][tutorial] for an in-depth walkthough of building a Ansi
 [ansible-link]:https://www.ansible.com/
 [install-guide]:/docs/building-operators/ansible/installation
 [image-reg-config]:/docs/olm-integration/cli-overview#private-bundle-and-catalog-image-registries
-[doc-olm]:/docs/olm-integration/quickstart-bundle/#enabling-olm
+[doc-olm]:/docs/olm-integration/tutorial-bundle/#enabling-olm
 [tutorial]:/docs/building-operators/ansible/tutorial/
diff --git a/website/content/en/docs/building-operators/ansible/reference/advanced_options.md b/website/content/en/docs/building-operators/ansible/reference/advanced_options.md
index 9aa859e1..cca0090b 100644
--- a/website/content/en/docs/building-operators/ansible/reference/advanced_options.md
+++ b/website/content/en/docs/building-operators/ansible/reference/advanced_options.md
@@ -242,7 +242,7 @@ Ansible-runner will perform the task relevant to the command specified by the us
         msg: The decrypted value is {{secret.the_secret}}
 ```
 
-Now, let's also assume that we have a password file, `pwd.yml`, that contains the password to decrypt the encrypted text. Then, by running the command `ansible-operator run --ansible-args='--vault-password-file pwd.yml'` the operator will read in the encrypted text from the file and perform decryption using the password stored in the `pwd.yml` file:
+Now, let's also assume that we have a password file, `pwd.yml`, that contains the password to decrypt the encrypted text. Then, by running the command `ansible-operator run --ansible-args='--vault-password-file /absolute/path/to/pwd.yml'` the operator will read in the encrypted text from the file and perform decryption using the password stored in the `pwd.yml` file:
 
 ```
 --------------------------- Ansible Task StdOut -------------------------------
diff --git a/website/content/en/docs/building-operators/ansible/tutorial.md b/website/content/en/docs/building-operators/ansible/tutorial.md
index a312398f..fcfd5080 100644
--- a/website/content/en/docs/building-operators/ansible/tutorial.md
+++ b/website/content/en/docs/building-operators/ansible/tutorial.md
@@ -164,7 +164,7 @@ There are three ways to run the operator:
 
 - As Go program outside a cluster
 - As a Deployment inside a Kubernetes cluster
-- Managed by the [Operator Lifecycle Manager (OLM)][doc-olm] in [bundle][quickstart-bundle] format
+- Managed by the [Operator Lifecycle Manager (OLM)][doc-olm] in [bundle][tutorial-bundle] format
 
 ### 1. Run locally outside the cluster
 
@@ -215,7 +215,7 @@ has a custom CA, these [configuration steps][image-reg-config] must be complete.
 operator-sdk run bundle example.com/memcached-operator-bundle:v0.0.1
 ```
 
-Check out the [docs][quickstart-bundle] for a deep dive into `operator-sdk`'s OLM integration.
+Check out the [docs][tutorial-bundle] for a deep dive into `operator-sdk`'s OLM integration.
 
 
 ## Create a Memcached CR
@@ -346,7 +346,7 @@ todo(camilamacedo86): Create an Ansible operator scope document.
 https://github.com/operator-framework/operator-sdk/issues/3447
 -->
 
-OLM will manage creation of most if not all resources required to run your operator, using a bit of setup from other operator-sdk commands. Check out the [OLM integration guide][quickstart-bundle].
+OLM will manage creation of most if not all resources required to run your operator, using a bit of setup from other operator-sdk commands. Check out the [OLM integration guide][tutorial-bundle].
 
 [legacy-quickstart-doc]:https://v0-19-x.sdk.operatorframework.io/docs/ansible/quickstart/
 [migration-guide]:/docs/building-operators/ansible/migration
@@ -357,5 +357,5 @@ OLM will manage creation of most if not all resources required to run your opera
 [custom-resources]:https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/
 [layout-doc]:/docs/building-operators/ansible/reference/scaffolding
 [doc-bundle]:https://github.com/operator-framework/operator-registry/blob/v1.16.1/docs/design/operator-bundle.md#operator-bundle
-[quickstart-bundle]:/docs/olm-integration/quickstart-bundle
-[doc-olm]:/docs/olm-integration/quickstart-bundle/#enabling-olm
+[tutorial-bundle]:/docs/olm-integration/tutorial-bundle
+[doc-olm]:/docs/olm-integration/tutorial-bundle/#enabling-olm
diff --git a/website/content/en/docs/building-operators/golang/operator-scope.md b/website/content/en/docs/building-operators/golang/operator-scope.md
index 1717562b..0b435f61 100644
--- a/website/content/en/docs/building-operators/golang/operator-scope.md
+++ b/website/content/en/docs/building-operators/golang/operator-scope.md
@@ -268,7 +268,7 @@ if strings.Contains(watchNamespace, ",") {
 If your operator is [integrated with OLM][olm-integration], you will want to update your [CSV base's][csv-base]
 `spec.installModes` list to support the desired namespacing requirements. Support for multiple types of namespacing
 is allowed, so supporting multiple install modes in a CSV is permitted. After doing so, update your
-[bundle][bundle-quickstart] or [package manifests][packagemanifests-quickstart] by following the linked guides.
+[bundle][bundle-quickstart] or [package manifests][packagemanifests-tutorial] by following the linked guides.
 
 ### Watching resources in all Namespaces (default)
 
@@ -311,4 +311,4 @@ If the operator can watch multiple namespaces, set the following in your `spec.i
 [olm-integration]: /docs/olm-integration
 [csv-base]: /docs/olm-integration/generation/#kustomize-files
 [bundle-quickstart]: /docs/olm-integration/quickstart-bundle
-[packagemanifests-quickstart]: /docs/olm-integration/quickstart-package-manifests
+[packagemanifests-tutorial]: /docs/olm-integration/tutorial-package-manifests
diff --git a/website/content/en/docs/building-operators/golang/quickstart.md b/website/content/en/docs/building-operators/golang/quickstart.md
index c38e143b..b8d70eb9 100644
--- a/website/content/en/docs/building-operators/golang/quickstart.md
+++ b/website/content/en/docs/building-operators/golang/quickstart.md
@@ -105,5 +105,5 @@ Read the [full tutorial][tutorial] for an in-depth walkthough of building a Go o
 
 [install-guide]:/docs/building-operators/golang/installation
 [image-reg-config]:/docs/olm-integration/cli-overview#private-bundle-and-catalog-image-registries
-[doc-olm]:/docs/olm-integration/quickstart-bundle/#enabling-olm
+[doc-olm]:/docs/olm-integration/tutorial-bundle/#enabling-olm
 [tutorial]:/docs/building-operators/golang/tutorial/
diff --git a/website/content/en/docs/building-operators/golang/tutorial.md b/website/content/en/docs/building-operators/golang/tutorial.md
index be05e3c8..f50745a2 100644
--- a/website/content/en/docs/building-operators/golang/tutorial.md
+++ b/website/content/en/docs/building-operators/golang/tutorial.md
@@ -345,7 +345,7 @@ has a custom CA, these [configuration steps][image-reg-config] must be complete.
 operator-sdk run bundle example.com/memcached-operator-bundle:v0.0.1
 ```
 
-Check out the [docs][quickstart-bundle] for a deep dive into `operator-sdk`'s OLM integration.
+Check out the [docs][tutorial-bundle] for a deep dive into `operator-sdk`'s OLM integration.
 
 
 ## Create a Memcached CR
@@ -478,8 +478,9 @@ Next, check out the following:
 [role-based-access-control]: https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control#iam-rolebinding-bootstrap
 [multigroup-kubebuilder-doc]: https://book.kubebuilder.io/migration/multi-group.html
 [doc-bundle]:https://github.com/operator-framework/operator-registry/blob/v1.16.1/docs/design/operator-bundle.md#operator-bundle
+[tutorial-bundle]:/docs/olm-integration/tutorial-bundle
 [quickstart-bundle]:/docs/olm-integration/quickstart-bundle
-[doc-olm]:/docs/olm-integration/quickstart-bundle/#enabling-olm
+[doc-olm]:/docs/olm-integration/tutorial-bundle/#enabling-olm
 [conditionals]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
 [kubernetes-extend-api]: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/
 [reconcile-godoc]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/reconcile
diff --git a/website/content/en/docs/building-operators/golang/webhook.md b/website/content/en/docs/building-operators/golang/webhook.md
index 50e26931..e1317ea7 100755
--- a/website/content/en/docs/building-operators/golang/webhook.md
+++ b/website/content/en/docs/building-operators/golang/webhook.md
@@ -72,7 +72,7 @@ After running the `create webhook` command the file structure would be:
 
 The scaffolded file `api/v1alpha1/memcached_webhook.go` has method signatures which need to be implemented for the validation webhook.
 
-Following this, there are a few steps which need to be done in your operator project to enable webhhoks. This will involve:
+Following this, there are a few steps which need to be done in your operator project to enable webhooks. This will involve:
 
 1. Implementing the required methods for Validating or Mutating webhook in `<kind>_webhook.go`. An example of such implementation is provided [here](https://book.kubebuilder.io/cronjob-tutorial/webhook-implementation.html).
 
@@ -96,4 +96,4 @@ Technically, the webhooks can be run locally, but for it to work you need to gen
 
 #### Run as a Deployment inside the cluster
 
-Adding webhooks does not alter deploying your operator. For instructions on deploying your operator into a cluster, refer to the [tutorial](https://sdk.operatorframework.io/docs/building-operators/golang/tutorial/#2-run-as-a-deployment-inside-the-cluster).
\ No newline at end of file
+Adding webhooks does not alter deploying your operator. For instructions on deploying your operator into a cluster, refer to the [tutorial](https://sdk.operatorframework.io/docs/building-operators/golang/tutorial/#2-run-as-a-deployment-inside-the-cluster).
diff --git a/website/content/en/docs/building-operators/helm/quickstart.md b/website/content/en/docs/building-operators/helm/quickstart.md
index 06d57804..be7afdbc 100644
--- a/website/content/en/docs/building-operators/helm/quickstart.md
+++ b/website/content/en/docs/building-operators/helm/quickstart.md
@@ -106,5 +106,5 @@ Read the [full tutorial][tutorial] for an in-depth walkthough of building a Helm
 [helm-official]:https://helm.sh/docs/
 [install-guide]:/docs/building-operators/helm/installation
 [image-reg-config]:/docs/olm-integration/cli-overview#private-bundle-and-catalog-image-registries
-[doc-olm]:/docs/olm-integration/quickstart-bundle/#enabling-olm
+[doc-olm]:/docs/olm-integration/tutorial-bundle/#enabling-olm
 [tutorial]:/docs/building-operators/helm/tutorial/
diff --git a/website/content/en/docs/building-operators/helm/reference/advanced_features/override_values.md b/website/content/en/docs/building-operators/helm/reference/advanced_features/override_values.md
index cbfce953..16b69d08 100644
--- a/website/content/en/docs/building-operators/helm/reference/advanced_features/override_values.md
+++ b/website/content/en/docs/building-operators/helm/reference/advanced_features/override_values.md
@@ -14,8 +14,8 @@ or the helm binary (helm v3) for security reasons.
 With the helm Operator this becomes possible by override values. This enforces that certain
 template values provided by the chart's default `values.yaml` or by a CR spec are always set
 when rendering the chart. If the value is set by a CR it gets overridden by the global override value.
-The override value can be static but can also refer to an environment variable. To pass down environment
-variables to the chart override values is currently the only way.
+The override value can be static but can also refer to an environment variable and use go templates.
+To pass down environment variables to the chart override values is currently the only way.
 
 An example use case of this is when your helm chart references container images by chart variables,
 which is a good practice.
@@ -27,6 +27,8 @@ versus individually per CR / chart release.
 > and then also associate these with an environment variable of your Operator like shown below.
 > This allows your Operator to be mirrored for offline usage when packaged for OLM.
 
+## Basic usage
+
 To configure your operator with override values, add an `overrideValues` map to your
 `watches.yaml` file for the GVK and chart you need to override. For example, to change
 the repository used by the nginx chart, you would update your `watches.yaml` to the
@@ -39,14 +41,16 @@ following:
   kind: Nginx
   chart: helm-charts/nginx
   overrideValues:
-    image.repository: quay.io/mycustomrepo
+    image.repository: quay.io/mycustomrepo/myimage
 ```
 
-By setting `image.repository` to `quay.io/mycustomrepo` you are ensuring that
-`quay.io/mycustomrepo` will always be used instead of the chart's default repository
+By setting `image.repository` to `quay.io/mycustomrepo/myimage` you are ensuring that
+`quay.io/mycustomrepo/myimage` will always be used instead of the chart's default repository
 (`nginx`). If the CR attempts to set this value, it will be ignored.
 
-It is now possible to reference environment variables in the `overrideValues` section:
+## Using environment variables
+
+It is also possible to reference environment variables in the `overrideValues` section:
 
 ```yaml
   overrideValues:
@@ -61,7 +65,7 @@ following snippet to the container spec:
 ```yaml
 env:
   - name: IMAGE_REPOSITORY
-    value: quay.io/mycustomrepo
+    value: quay.io/mycustomrepo/myimage
 ```
 
 If an environment variable reference is listed in `overrideValues`, but is not present
@@ -70,6 +74,32 @@ override all other values. Therefore, these environment variables should _always
 set. It is suggested to update the Dockerfile to set these environment variables to
 the same defaults that are defined by the chart.
 
+## Using Go templates
+
+Lastly, you can use Go `text/template` strings along with
+[slim-sprig](https://go-task.github.io/slim-sprig/) functions to provide even more
+flexibility when building override values.
+
+For example, consider a situation where your operator has an environment variable,
+`$IMAGE`, set to `quay.io/mycustomrepo/myimage:latest`. You can use sprig template
+functions to split that environment variable into its repo and tag:
+
+```yaml
+  overrideValues:
+    image.repository: '{{ ("$IMAGE" | split ":")._0 }}'
+    image.tag: '{{ ("$IMAGE" | split ":")._1 }}'
+```
+
+The resulting override values sent to the helm installation would look like:
+
+```yaml
+  overrideValues:
+    image.repository: quay.io/mycustomrepo/myimage
+    image.tag: latest
+```
+
+## Event generation
+
 To warn users that their CR settings may be ignored, the Helm operator creates events on
 the CR that include the name and value of each overridden value. For example:
 
diff --git a/website/content/en/docs/building-operators/helm/reference/watches.md b/website/content/en/docs/building-operators/helm/reference/watches.md
index de423dfd..f8447b68 100644
--- a/website/content/en/docs/building-operators/helm/reference/watches.md
+++ b/website/content/en/docs/building-operators/helm/reference/watches.md
@@ -19,6 +19,7 @@ The follow tables describes the fields in an entry in `watches.yaml`:
 | chart                   | The path to the helm chart to use when reconciling this GVK.  |
 | watchDependentResources | Enable watching resources that are created by helm (default: `true`). |
 | overrideValues          | Values to be used for overriding Helm chart's defaults. For additional information see the [reference doc][override-values]. |
+| selector                | The conditions that a resource's labels must satsify in order to get reconciled. For additional information see [labels and selectors documentation][label-selector-doc]. |
 
 
 For reference, here is an example of a simple `watches.yaml` file:
@@ -31,7 +32,11 @@ For reference, here is an example of a simple `watches.yaml` file:
   chart: helm-charts/foo
   overrideValues:
     image.repository: quay.io/mycustomrepo
-  watchDependentResources: false   
+  watchDependentResources: false
+  selector:
+    matchExpressions:
+     - {key: testLabel, operator: Exists, values: []}
 ```
 
 [override-values]: /docs/building-operators/helm/reference/advanced_features/override_values/
+[label-selector-doc]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
diff --git a/website/content/en/docs/building-operators/helm/tutorial.md b/website/content/en/docs/building-operators/helm/tutorial.md
index 81a93e50..7cca752d 100644
--- a/website/content/en/docs/building-operators/helm/tutorial.md
+++ b/website/content/en/docs/building-operators/helm/tutorial.md
@@ -197,7 +197,7 @@ There are three ways to run the operator:
 
 - As Go program outside a cluster
 - As a Deployment inside a Kubernetes cluster
-- Managed by the [Operator Lifecycle Manager (OLM)][doc-olm] in [bundle][quickstart-bundle] format
+- Managed by the [Operator Lifecycle Manager (OLM)][doc-olm] in [bundle][tutorial-bundle] format
 
 ### 1. Run locally outside the cluster
 
@@ -248,7 +248,7 @@ has a custom CA, these [configuration steps][image-reg-config] must be complete.
 operator-sdk run bundle example.com/nginx-operator-bundle:v0.0.1
 ```
 
-Check out the [docs][quickstart-bundle] for a deep dive into `operator-sdk`'s OLM integration.
+Check out the [docs][tutorial-bundle] for a deep dive into `operator-sdk`'s OLM integration.
 
 
 ## Create a Nginx CR
@@ -366,7 +366,7 @@ Next, check out the following:
 [helm-charts]:https://helm.sh/docs/topics/charts/
 [helm-values]:https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing
 [helm-official]:https://helm.sh/docs/
-[quickstart-bundle]:/docs/olm-integration/quickstart-bundle
-[doc-olm]:/docs/olm-integration/quickstart-bundle/#enabling-olm
+[tutorial-bundle]:/docs/olm-integration/tutorial-bundle
+[doc-olm]:/docs/olm-integration/tutorial-bundle/#enabling-olm
 [olm-integration]:/docs/olm-integration
 [advanced-features]:/docs/building-operators/helm/reference/advanced_features
diff --git a/website/content/en/docs/contribution-guidelines/changelog.md b/website/content/en/docs/contribution-guidelines/changelog.md
new file mode 100644
index 00000000..01c4f008
--- /dev/null
+++ b/website/content/en/docs/contribution-guidelines/changelog.md
@@ -0,0 +1,18 @@
+---
+title: Changelog
+weight: 25
+---
+
+The Operator SDK project tracks changes across releases by maintaining a live changelog. This changelog is compiled via fragments located in `changelog/fragments`
+
+Contributors are asked to add these changelog fragments when creating pull requests by following this [template][changelog-template].
+
+The changlog fragments document the following information:
+
+* Description of Changes
+* Types of Changes
+  * One of: addition, change, deprecation, removal, bugfix
+* Migration Guide for Changes
+  * These are required for breaking changes, and essential (but not required) for scaffolding-related changes.
+
+[changelog-template]: https://github.com/operator-framework/operator-sdk/blob/master/changelog/fragments/00-template.yaml
diff --git a/website/content/en/docs/contribution-guidelines/releasing.md b/website/content/en/docs/contribution-guidelines/releasing.md
index b913386a..43362b35 100755
--- a/website/content/en/docs/contribution-guidelines/releasing.md
+++ b/website/content/en/docs/contribution-guidelines/releasing.md
@@ -309,6 +309,13 @@ Prior to an Operator SDK release, add bindata (if required) for a new OLM versio
 3. Run `make bindata`.
 4. Update the list of supported OLM versions stated in the [`Overview`][overview] section of SDK documentation is updated.
 
+### Patch releases in parallel:
+
+  - Releasing in order is nice but not worth the inconvenience. Release order affects the order on GitHub releases, and which 
+    is labeled "latest release".
+  - Do not unlock v.* branches while other releases are in progress. Instead, have an admin do the merges.
+  - Release announcements should be consolidated.
+
 [netlify-deploy]:https://docs.netlify.com/site-deploys/overview/#deploy-summary
 [doc-owners]: https://github.com/operator-framework/operator-sdk/blob/master/OWNERS
 [release-page]:https://github.com/operator-framework/operator-sdk/releases
diff --git a/website/content/en/docs/faqs/_index.md b/website/content/en/docs/faqs/_index.md
index 6f197a6d..bfdc384c 100644
--- a/website/content/en/docs/faqs/_index.md
+++ b/website/content/en/docs/faqs/_index.md
@@ -20,7 +20,7 @@ For further context about the relationship between Kubebuilder and Operator SDK,
 ## Can I use the Kubebuilder docs?
 
 Yes, you can use [https://book.kubebuilder.io/](https://book.kubebuilder.io/). Just keep in mind that when you see an instruction such as:
-`$ kubebuilder <command>` you will use `$ operator-sdk <command>`. 
+`$ kubebuilder <command>` you will use `$ operator-sdk <command>`.
 
 ## Controller Runtime FAQ
 
@@ -154,5 +154,5 @@ SHELL := /bin/bash
 [owner-references-permission-enforcement]: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement
 [rbac-markers]: https://book.kubebuilder.io/reference/markers/rbac.html
 [rbac]:https://kubernetes.io/docs/reference/access-authn-authz/rbac/
-[scorecard-doc]: https://sdk.operatorframework.io/docs/advanced-topics/scorecard/
+[scorecard-doc]: https://sdk.operatorframework.io/docs/testing-operators/scorecard/
 [project-doc]: /docs/overview/project-layout
diff --git a/website/content/en/docs/installation/_index.md b/website/content/en/docs/installation/_index.md
index a9cd7e3a..bb53ce84 100644
--- a/website/content/en/docs/installation/_index.md
+++ b/website/content/en/docs/installation/_index.md
@@ -36,7 +36,7 @@ export OS=$(uname | awk '{print tolower($0)}')
 Download the binary for your platform:
 
 ```sh
-export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.10.0
+export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.11.0
 curl -LO ${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH}
 ```
 
diff --git a/website/content/en/docs/olm-integration/quickstart-bundle.md b/website/content/en/docs/olm-integration/quickstart-bundle.md
index 4dcc61c7..0f0fad8f 100644
--- a/website/content/en/docs/olm-integration/quickstart-bundle.md
+++ b/website/content/en/docs/olm-integration/quickstart-bundle.md
@@ -8,288 +8,59 @@ The [Operator Lifecycle Manager (OLM)][olm] is a set of cluster resources that m
 The Operator SDK supports both creating manifests for OLM deployment, and testing your Operator on an OLM-enabled
 Kubernetes cluster.
 
-This document succinctly walks through getting an Operator OLM-ready with [bundles][bundle], and glosses over
-explanations of certain steps for brevity. The following documents contain more detail on these steps:
-- All operator-framework manifest commands supported by the SDK: [CLI overview][doc-cli-overview].
-- Generating operator-framework manifests: [generation overview][doc-olm-generate].
-
-If you are working with package manifests, see the [package manifests quickstart][quickstart-package-manifests]
-once you have completed the *Setup* section below.
+This document is intended to quickly walk through the steps to generate an OLM bundle. For further explanation,
+or if you're using package manifests, see the [Bundle Tutorial][tutorial-bundle].
 
 **Important:** this guide assumes your project was scaffolded with `operator-sdk init --project-version=3`.
 These features are unavailable to projects of version `2` or less; this information can be found by inspecting
 your `PROJECT` file's `version` value.
 
-## Setup
-
-Let's first walk through creating an Operator for `memcached`, a distributed key-value store.
-
-Follow one of the user guides to develop the memcached-operator in either [Go][sdk-user-guide-go],
-[Ansible][sdk-user-guide-ansible], or [Helm][sdk-user-guide-helm], depending on which Operator type you are interested in.
-This guide assumes memcached-operator is on version `0.0.1`, which is set in the `Makefile` variable `VERSION`.
-
-### Enabling OLM
+## Prerequisites
+- Have a working operator that you have uploaded to a container registry. This guide assumes the simple Golang Memcached operator from [the building operators section][sdk-user-guid-go] at version `0.0.1`.
+- User authorized with 'cluster-admin' permissions.
+- OLM installed on your cluster. The command `operator-sdk olm install` will attempt to install a basic OLM deployment on your cluster.
 
-Ensure OLM is enabled on your cluster before following this guide. [`operator-sdk olm`][cli-olm]
-has several subcommands that can install, uninstall, and check the status of particular OLM versions in a cluster.
+## Steps
 
-**Note:** Certain cluster types may already have OLM enabled, but under a non-default (`"olm"`) namespace,
-which can be configured by setting `--olm-namespace=[non-default-olm-namespace]` for `operator-sdk olm status|uninstall` subcommands.
+1. Export environment variables
 
-You can check if OLM is already installed by running the following command,
-which will detect the installed OLM version automatically (0.15.1 in this example):
-
-```console
-$ operator-sdk olm status
-INFO[0000] Fetching CRDs for version "0.15.1"
-INFO[0002] Fetching resources for version "0.15.1"
-INFO[0002] Successfully got OLM status for version "0.15.1"
-
-NAME                                            NAMESPACE    KIND                        STATUS
-olm                                                          Namespace                   Installed
-operatorgroups.operators.coreos.com                          CustomResourceDefinition    Installed
-catalogsources.operators.coreos.com                          CustomResourceDefinition    Installed
-subscriptions.operators.coreos.com                           CustomResourceDefinition    Installed
-...
+```sh
+$ export USERNAME=<container-registry-username>
+$ export VERSON=0.0.1
+$ export IMG=docker.io/$USERNAME/memcached-operator:v$VERSION // location where your operator image is hosted
+$ export BUNDLE_IMG=docker.io/$USERNAME/memcached-operator-bundle:v$VERSION // location where your bundle will be hosted
 ```
 
-All resources listed should have status `Installed`.
-
-If OLM is not already installed, go ahead and install the latest version:
-
-```console
-$ operator-sdk olm install
-INFO[0000] Fetching CRDs for version "latest"
-INFO[0001] Fetching resources for version "latest"
-INFO[0007] Creating CRDs and resources
-INFO[0007]   Creating CustomResourceDefinition "clusterserviceversions.operators.coreos.com"
-INFO[0007]   Creating CustomResourceDefinition "installplans.operators.coreos.com"
-INFO[0007]   Creating CustomResourceDefinition "subscriptions.operators.coreos.com"
-...
-NAME                                            NAMESPACE    KIND                        STATUS
-clusterserviceversions.operators.coreos.com                  CustomResourceDefinition    Installed
-installplans.operators.coreos.com                            CustomResourceDefinition    Installed
-subscriptions.operators.coreos.com                           CustomResourceDefinition    Installed
-catalogsources.operators.coreos.com                          CustomResourceDefinition    Installed
-...
-```
-
-**Note:** By default, `olm status` and `olm uninstall` auto-detect the OLM version installed in your cluster.
-This can fail if the installation is broken in some way, so the version of OLM can be overridden using the
-`--version` flag provided with these commands.
-
-## Creating a bundle
-
-_If working with package manifests, see the [package manifests quickstart][quickstart-package-manifests]._
-
-We will now create bundle manifests by running `make bundle` in the root of the memcached-operator project.
+1. Create a bundle from the root directory of your project
 
-```console
+```sh
 $ make bundle
-/home/user/go/bin/controller-gen "crd:trivialVersions=true" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
-operator-sdk generate kustomize manifests -q
-kustomize build config/manifests | operator-sdk generate bundle -q --overwrite --version 0.0.1
-INFO[0000] Building annotations.yaml
-INFO[0000] Writing annotations.yaml in /home/user/go/src/github.com/test-org/memcached-operator/bundle/metadata
-INFO[0000] Building Dockerfile
-INFO[0000] Writing bundle.Dockerfile in /home/user/go/src/github.com/test-org/memcached-operator
-operator-sdk bundle validate ./bundle
-INFO[0000] Found annotations file                        bundle-dir=bundle container-tool=docker
-INFO[0000] Could not find optional dependencies file     bundle-dir=bundle container-tool=docker
-INFO[0000] All validation tests have completed successfully
 ```
 
-The above command will have created the following bundle artifacts: a manifests directory
-(`bundle/manifests`) containing a CSV and all CRDs from `config/crds`, [metadata][bundle-metadata]
-directory (`bundle/metadata`), and [`bundle.Dockerfile`][bundle-dockerfile] have been created in
-the Operator project. These files have been statically validated by `operator-sdk bundle validate`
-to ensure the on-disk bundle representation is correct.
+This will prompt you to enter basic information about your operator.
 
-## Deploying an Operator with OLM
+1. Build and push the bundle image
 
-At this point in development we've generated all files necessary to build the memcached-operator bundle.
-Now we're ready to test and deploy the Operator with OLM.
-
-**Note:** If testing a bundle whose image will be hosted in a registry that is private and/or
-has a custom CA, these [configuration steps][image-reg-config] must be complete.
-
-### Testing bundles
-
-Before proceeding, make sure you've [Installed OLM](#enabling-olm) onto your
-cluster.
-
-First, we need to build our bundle. To build a memcached-operator bundle, run:
-
-```console
-$ make bundle-build bundle-push BUNDLE_IMG=<some-registry>/memcached-operator-bundle:v0.0.1
+```sh
+$ make bundle-build bundle-push
 ```
 
-Now that the bundle image is present in a registry, [`operator-sdk run bundle`][cli-run-bundle]
-can create a pod to serve that bundle to OLM via a [`Subscription`][install-your-operator],
-along with other OLM objects, ephemerally.
+1. Validate the bundle
 
-```console
-$ operator-sdk run bundle <some-registry>/memcached-operator-bundle:v0.0.1
-INFO[0008] Successfully created registry pod: <some-registry>-memcached-operator-bundle-0-0-1
-INFO[0008] Created CatalogSource: memcached-operator-catalog
-INFO[0008] OperatorGroup "operator-sdk-og" created
-INFO[0008] Created Subscription: memcached-operator-v0-0-1-sub
-INFO[0019] Approved InstallPlan install-krv7q for the Subscription: memcached-operator-v0-0-1-sub
-INFO[0019] Waiting for ClusterServiceVersion "default/memcached-operator.v0.0.1" to reach 'Succeeded' phase
-INFO[0019]   Waiting for ClusterServiceVersion "default/memcached-operator.v0.0.1" to appear
-INFO[0031]   Found ClusterServiceVersion "default/memcached-operator.v0.0.1" phase: Pending
-INFO[0032]   Found ClusterServiceVersion "default/memcached-operator.v0.0.1" phase: Installing
-INFO[0040]   Found ClusterServiceVersion "default/memcached-operator.v0.0.1" phase: Succeeded
-INFO[0040] OLM has successfully installed "memcached-operator.v0.0.1"
+```sh
+$ operator-sdk bundle validate $BUNDLE_IMG
 ```
 
-<!-- TODO(jmccormick2001): add `scorecard` usage here -->
-
-### Upgrading a bundle to a newer version
+1. Install the bundle with OLM
 
-We can use the `operator-sdk run bundle-upgrade` command with a newer version of bundle image to upgrade
-an existing operator bundle deployed on cluster. The command automates the manual orchestration typically required to upgrade an operator
-from one version to another. It extracts the package name from bundle, finds the existing subscription, updates the catalog
-source, deletes the existing registry pod and creates a new registry pod with the version of bundle image provided in the command.
-
-Let's upgrade the previously deployed memcached-operator bundle from version `0.0.1` to `0.0.2`.
-
-```console
-$ operator-sdk run bundle-upgrade <some-registry>/memcached-operator-bundle:v0.0.2
-INFO[0002] Found existing subscription with name memcached-operator-bundle-0-0-1-sub and namespace default
-INFO[0002] Found existing catalog source with name memcached-operator-catalog and namespace default
-INFO[0007] Successfully created registry pod: <some-registry>-memcached-operator-bundle-0-0-2
-INFO[0007] Updated catalog source memcached-operator-catalog with address and annotations
-INFO[0008] Deleted previous registry pod with name "<some-registry>-memcached-operator-bundle-0-0-1"
-INFO[0050] Approved InstallPlan install-c8fkh for the Subscription: memcached-operator-bundle-0-0-1-sub
-INFO[0050] Waiting for ClusterServiceVersion "default/memcached-operator.v0.0.2" to reach 'Succeeded' phase
-INFO[0050]   Waiting for ClusterServiceVersion "default/memcached-operator.v0.0.2" to appear
-INFO[0052]   Found ClusterServiceVersion "default/memcached-operator.v0.0.2" phase: Pending
-INFO[0057]   Found ClusterServiceVersion "default/memcached-operator.v0.0.2" phase: InstallReady
-INFO[0058]   Found ClusterServiceVersion "default/memcached-operator.v0.0.2" phase: Installing
-INFO[0095]   Found ClusterServiceVersion "default/memcached-operator.v0.0.2" phase: Succeeded
-INFO[0095] Successfully upgraded to "memcached-operator.v0.0.2"
+```sh
+$ operator-sdk run bundle $BUNDLE_IMG
 ```
 
-#### Upgrading a bundle that was installed traditionally using OLM
-
-An operator bundle can be upgraded even if it was originally deployed using OLM without using the `run bundle` command.
-
-Let's see how to deploy an operator bundle traditionally using OLM and then upgrade the operator bundle to a newer version.
-
-First, create a CatalogSource by building the CatalogSource from a catalog.
-
-```console
-$ oc create -f catalogsource.yaml
-```
-
-```yaml
-# catalogsource.yaml
-apiVersion: operators.coreos.com/v1alpha1
-kind: CatalogSource
-metadata:
-  name: etcdoperator
-  namespace: default
-spec:
-  displayName: Etcd Operators
-  image: <some-registry>/etcd-catalog:latest
-  sourceType: grpc
-```
-
-Next, install the operator bundle by creating a subscription.
-
-```console
-$ oc create -f subscription.yaml
-```
-
-```yaml
-# subscription.yaml
-apiVersion: v1
-items:
-- apiVersion: operators.coreos.com/v1alpha1
-  kind: Subscription
-  metadata:
-    name: etcd
-    namespace: default
-  spec:
-    channel: "stable"
-    installPlanApproval: Manual
-    name: etcd
-    source: etcdoperator
-    sourceNamespace: default
-    startingCSV: etcdoperator.v0.0.1
-```
-
-Once the Operator bundle is deployed, you can use the `run bundle-upgrade` command by specifing the new bundle image that you want to upgrade to.
-
-```console
-$ operator-sdk run bundle-upgrade <some-registry>/etcd-bundle:v0.0.2
-INFO[0000] Found existing subscription with name etcd and namespace default
-INFO[0000] Found existing catalog source with name etcdoperator and namespace default
-INFO[0005] Successfully created registry pod: <some-registry>-etcd-bundle-0-0-2
-INFO[0005] Updated catalog source etcdoperator with address and annotations
-INFO[0005] Deleted previous registry pod with name "<some-registry>-etcd-bundle-0-0-1"
-INFO[0005] Approved InstallPlan install-6vrzh for the Subscription: etcd
-INFO[0005] Waiting for ClusterServiceVersion "default/etcdoperator.v0.0.2" to reach 'Succeeded' phase
-INFO[0005]   Waiting for ClusterServiceVersion "default/etcdoperator.v0.0.2" to appear
-INFO[0007]   Found ClusterServiceVersion "default/etcdoperator.v0.0.2" phase: Pending
-INFO[0008]   Found ClusterServiceVersion "default/etcdoperator.v0.0.2" phase: Installing
-INFO[0018]   Found ClusterServiceVersion "default/etcdoperator.v0.0.2" phase: Succeeded
-INFO[0018] Successfully upgraded to "etcdoperator.v0.0.2"
-```
-
-### Deploying bundles in production
-
-OLM and Operator Registry consumes Operator bundles via a catalog of Operators, implemented as an
-[index image][index-image], which are composed of one or more bundles. To build and push a
-memcached-operator bundle image for version v0.0.1, run:
-
-```console
-$ make bundle-build bundle-push BUNDLE_IMG=<some-registry>/memcached-operator-bundle:v0.0.1
-```
-
-Now you can build and push the catalog by running `catalog-*` Makfile targets, which use
-the Operator package manager tool [`opm`][opm] to [build][doc-index-build] the catalog:
-
-```console
-$ make catalog-build catalog-push CATALOG_IMG=<some-registry>/memcached-operator-catalog:v0.0.1
-```
-
-Assuming `IMAGE_TAG_BASE = <some-registry>/memcached-operator` has the desired tag base, you can inline
-the above two commands to:
-
-```console
-$ make bundle-build bundle-push catalog-build catalog-push
-```
-
-Which will build and push both `<some-registry>/memcached-operator-bundle:v0.0.1`
-and `<some-registry>/memcached-operator-catalog:v0.0.1`.
-
-## Further reading
-
-In-depth discussions of OLM concepts mentioned here:
-- [CatalogSource][catalogsource]
-- [Subscription][subscription]
-- [Install an Operator from a catalog][olm-install]
+## Next Steps
 
+Read the [full tutorial][tutorial-bundle] for a more in-depth look at creating and using a bundle.
 
+[tutorial-bundle]:/docs/olm-integration/tutorial-bundle
 [sdk-user-guide-go]:/docs/building-operators/golang/quickstart
-[sdk-user-guide-ansible]:/docs/building-operators/ansible/quickstart
-[sdk-user-guide-helm]:/docs/building-operators/helm/quickstart
-[quickstart-package-manifests]:/docs/olm-integration/quickstart-package-manifests
 [olm]:https://github.com/operator-framework/operator-lifecycle-manager/
-[bundle]:https://github.com/operator-framework/operator-registry/blob/v1.16.1/docs/design/operator-bundle.md
-[bundle-metadata]:https://github.com/operator-framework/operator-registry/blob/v1.12.6/docs/design/operator-bundle.md#bundle-annotations
-[bundle-dockerfile]:https://github.com/operator-framework/operator-registry/blob/v1.12.6/docs/design/operator-bundle.md#bundle-dockerfile
-[image-reg-config]:/docs/olm-integration/cli-overview#private-bundle-and-catalog-image-registries
-[cli-olm]:/docs/cli/operator-sdk_olm
-[cli-run-bundle]:/docs/cli/operator-sdk_run_bundle
-[doc-cli-overview]:/docs/olm-integration/cli-overview
-[doc-olm-generate]:/docs/olm-integration/generation
-[opm]:https://github.com/operator-framework/operator-registry/blob/master/docs/design/opm-tooling.md
-[index-image]:https://github.com/operator-framework/operator-registry/blob/master/docs/design/opm-tooling.md#index
-[doc-index-build]:https://github.com/operator-framework/operator-registry#building-an-index-of-operators-using-opm
-[install-your-operator]:https://olm.operatorframework.io/docs/tasks/install-operator-with-olm/#install-your-operator
-[catalogsource]:https://olm.operatorframework.io/docs/concepts/crds/catalogsource/
-[subscription]:https://olm.operatorframework.io/docs/concepts/crds/subscription/
-[olm-install]:https://olm.operatorframework.io/docs/tasks/install-operator-with-olm/
diff --git a/website/content/en/docs/olm-integration/tutorial-bundle.md b/website/content/en/docs/olm-integration/tutorial-bundle.md
new file mode 100644
index 00000000..fbdd782d
--- /dev/null
+++ b/website/content/en/docs/olm-integration/tutorial-bundle.md
@@ -0,0 +1,295 @@
+---
+title: OLM Integration Bundle Tutorial
+linkTitle: Bundle Tutorial
+weight: 1
+---
+
+The [Operator Lifecycle Manager (OLM)][olm] is a set of cluster resources that manage the lifecycle of an Operator.
+The Operator SDK supports both creating manifests for OLM deployment, and testing your Operator on an OLM-enabled
+Kubernetes cluster.
+
+This document succinctly walks through getting an Operator OLM-ready with [bundles][bundle], and glosses over
+explanations of certain steps for brevity. The following documents contain more detail on these steps:
+- All operator-framework manifest commands supported by the SDK: [CLI overview][doc-cli-overview].
+- Generating operator-framework manifests: [generation overview][doc-olm-generate].
+
+If you are working with package manifests, see the [package manifests tutorial][tutorial-package-manifests]
+once you have completed the *Setup* section below.
+
+**Important:** this guide assumes your project was scaffolded with `operator-sdk init --project-version=3`.
+These features are unavailable to projects of version `2` or less; this information can be found by inspecting
+your `PROJECT` file's `version` value.
+
+## Setup
+
+Let's first walk through creating an Operator for `memcached`, a distributed key-value store.
+
+Follow one of the user guides to develop the memcached-operator in either [Go][sdk-user-guide-go],
+[Ansible][sdk-user-guide-ansible], or [Helm][sdk-user-guide-helm], depending on which Operator type you are interested in.
+This guide assumes memcached-operator is on version `0.0.1`, which is set in the `Makefile` variable `VERSION`.
+
+### Enabling OLM
+
+Ensure OLM is enabled on your cluster before following this guide. [`operator-sdk olm`][cli-olm]
+has several subcommands that can install, uninstall, and check the status of particular OLM versions in a cluster.
+
+**Note:** Certain cluster types may already have OLM enabled, but under a non-default (`"olm"`) namespace,
+which can be configured by setting `--olm-namespace=[non-default-olm-namespace]` for `operator-sdk olm status|uninstall` subcommands.
+
+You can check if OLM is already installed by running the following command,
+which will detect the installed OLM version automatically (0.15.1 in this example):
+
+```console
+$ operator-sdk olm status
+INFO[0000] Fetching CRDs for version "0.15.1"
+INFO[0002] Fetching resources for version "0.15.1"
+INFO[0002] Successfully got OLM status for version "0.15.1"
+
+NAME                                            NAMESPACE    KIND                        STATUS
+olm                                                          Namespace                   Installed
+operatorgroups.operators.coreos.com                          CustomResourceDefinition    Installed
+catalogsources.operators.coreos.com                          CustomResourceDefinition    Installed
+subscriptions.operators.coreos.com                           CustomResourceDefinition    Installed
+...
+```
+
+All resources listed should have status `Installed`.
+
+If OLM is not already installed, go ahead and install the latest version:
+
+```console
+$ operator-sdk olm install
+INFO[0000] Fetching CRDs for version "latest"
+INFO[0001] Fetching resources for version "latest"
+INFO[0007] Creating CRDs and resources
+INFO[0007]   Creating CustomResourceDefinition "clusterserviceversions.operators.coreos.com"
+INFO[0007]   Creating CustomResourceDefinition "installplans.operators.coreos.com"
+INFO[0007]   Creating CustomResourceDefinition "subscriptions.operators.coreos.com"
+...
+NAME                                            NAMESPACE    KIND                        STATUS
+clusterserviceversions.operators.coreos.com                  CustomResourceDefinition    Installed
+installplans.operators.coreos.com                            CustomResourceDefinition    Installed
+subscriptions.operators.coreos.com                           CustomResourceDefinition    Installed
+catalogsources.operators.coreos.com                          CustomResourceDefinition    Installed
+...
+```
+
+**Note:** By default, `olm status` and `olm uninstall` auto-detect the OLM version installed in your cluster.
+This can fail if the installation is broken in some way, so the version of OLM can be overridden using the
+`--version` flag provided with these commands.
+
+## Creating a bundle
+
+_If working with package manifests, see the [package manifests tutorial][tutorial-package-manifests]._
+
+We will now create bundle manifests by running `make bundle` in the root of the memcached-operator project.
+
+```console
+$ make bundle
+/home/user/go/bin/controller-gen "crd:trivialVersions=true" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+operator-sdk generate kustomize manifests -q
+kustomize build config/manifests | operator-sdk generate bundle -q --overwrite --version 0.0.1
+INFO[0000] Building annotations.yaml
+INFO[0000] Writing annotations.yaml in /home/user/go/src/github.com/test-org/memcached-operator/bundle/metadata
+INFO[0000] Building Dockerfile
+INFO[0000] Writing bundle.Dockerfile in /home/user/go/src/github.com/test-org/memcached-operator
+operator-sdk bundle validate ./bundle
+INFO[0000] Found annotations file                        bundle-dir=bundle container-tool=docker
+INFO[0000] Could not find optional dependencies file     bundle-dir=bundle container-tool=docker
+INFO[0000] All validation tests have completed successfully
+```
+
+The above command will have created the following bundle artifacts: a manifests directory
+(`bundle/manifests`) containing a CSV and all CRDs from `config/crds`, [metadata][bundle-metadata]
+directory (`bundle/metadata`), and [`bundle.Dockerfile`][bundle-dockerfile] have been created in
+the Operator project. These files have been statically validated by `operator-sdk bundle validate`
+to ensure the on-disk bundle representation is correct.
+
+## Deploying an Operator with OLM
+
+At this point in development we've generated all files necessary to build the memcached-operator bundle.
+Now we're ready to test and deploy the Operator with OLM.
+
+**Note:** If testing a bundle whose image will be hosted in a registry that is private and/or
+has a custom CA, these [configuration steps][image-reg-config] must be complete.
+
+### Testing bundles
+
+Before proceeding, make sure you've [Installed OLM](#enabling-olm) onto your
+cluster.
+
+First, we need to build our bundle. To build a memcached-operator bundle, run:
+
+```console
+$ make bundle-build bundle-push BUNDLE_IMG=<some-registry>/memcached-operator-bundle:v0.0.1
+```
+
+Now that the bundle image is present in a registry, [`operator-sdk run bundle`][cli-run-bundle]
+can create a pod to serve that bundle to OLM via a [`Subscription`][install-your-operator],
+along with other OLM objects, ephemerally.
+
+```console
+$ operator-sdk run bundle <some-registry>/memcached-operator-bundle:v0.0.1
+INFO[0008] Successfully created registry pod: <some-registry>-memcached-operator-bundle-0-0-1
+INFO[0008] Created CatalogSource: memcached-operator-catalog
+INFO[0008] OperatorGroup "operator-sdk-og" created
+INFO[0008] Created Subscription: memcached-operator-v0-0-1-sub
+INFO[0019] Approved InstallPlan install-krv7q for the Subscription: memcached-operator-v0-0-1-sub
+INFO[0019] Waiting for ClusterServiceVersion "default/memcached-operator.v0.0.1" to reach 'Succeeded' phase
+INFO[0019]   Waiting for ClusterServiceVersion "default/memcached-operator.v0.0.1" to appear
+INFO[0031]   Found ClusterServiceVersion "default/memcached-operator.v0.0.1" phase: Pending
+INFO[0032]   Found ClusterServiceVersion "default/memcached-operator.v0.0.1" phase: Installing
+INFO[0040]   Found ClusterServiceVersion "default/memcached-operator.v0.0.1" phase: Succeeded
+INFO[0040] OLM has successfully installed "memcached-operator.v0.0.1"
+```
+
+<!-- TODO(jmccormick2001): add `scorecard` usage here -->
+
+### Upgrading a bundle to a newer version
+
+We can use the `operator-sdk run bundle-upgrade` command with a newer version of bundle image to upgrade
+an existing operator bundle deployed on cluster. The command automates the manual orchestration typically required to upgrade an operator
+from one version to another. It extracts the package name from bundle, finds the existing subscription, updates the catalog
+source, deletes the existing registry pod and creates a new registry pod with the version of bundle image provided in the command.
+
+Let's upgrade the previously deployed memcached-operator bundle from version `0.0.1` to `0.0.2`.
+
+```console
+$ operator-sdk run bundle-upgrade <some-registry>/memcached-operator-bundle:v0.0.2
+INFO[0002] Found existing subscription with name memcached-operator-bundle-0-0-1-sub and namespace default
+INFO[0002] Found existing catalog source with name memcached-operator-catalog and namespace default
+INFO[0007] Successfully created registry pod: <some-registry>-memcached-operator-bundle-0-0-2
+INFO[0007] Updated catalog source memcached-operator-catalog with address and annotations
+INFO[0008] Deleted previous registry pod with name "<some-registry>-memcached-operator-bundle-0-0-1"
+INFO[0050] Approved InstallPlan install-c8fkh for the Subscription: memcached-operator-bundle-0-0-1-sub
+INFO[0050] Waiting for ClusterServiceVersion "default/memcached-operator.v0.0.2" to reach 'Succeeded' phase
+INFO[0050]   Waiting for ClusterServiceVersion "default/memcached-operator.v0.0.2" to appear
+INFO[0052]   Found ClusterServiceVersion "default/memcached-operator.v0.0.2" phase: Pending
+INFO[0057]   Found ClusterServiceVersion "default/memcached-operator.v0.0.2" phase: InstallReady
+INFO[0058]   Found ClusterServiceVersion "default/memcached-operator.v0.0.2" phase: Installing
+INFO[0095]   Found ClusterServiceVersion "default/memcached-operator.v0.0.2" phase: Succeeded
+INFO[0095] Successfully upgraded to "memcached-operator.v0.0.2"
+```
+
+#### Upgrading a bundle that was installed traditionally using OLM
+
+An operator bundle can be upgraded even if it was originally deployed using OLM without using the `run bundle` command.
+
+Let's see how to deploy an operator bundle traditionally using OLM and then upgrade the operator bundle to a newer version.
+
+First, create a CatalogSource by building the CatalogSource from a catalog.
+
+```console
+$ oc create -f catalogsource.yaml
+```
+
+```yaml
+# catalogsource.yaml
+apiVersion: operators.coreos.com/v1alpha1
+kind: CatalogSource
+metadata:
+  name: etcdoperator
+  namespace: default
+spec:
+  displayName: Etcd Operators
+  image: <some-registry>/etcd-catalog:latest
+  sourceType: grpc
+```
+
+Next, install the operator bundle by creating a subscription.
+
+```console
+$ oc create -f subscription.yaml
+```
+
+```yaml
+# subscription.yaml
+apiVersion: v1
+items:
+- apiVersion: operators.coreos.com/v1alpha1
+  kind: Subscription
+  metadata:
+    name: etcd
+    namespace: default
+  spec:
+    channel: "stable"
+    installPlanApproval: Manual
+    name: etcd
+    source: etcdoperator
+    sourceNamespace: default
+    startingCSV: etcdoperator.v0.0.1
+```
+
+Once the Operator bundle is deployed, you can use the `run bundle-upgrade` command by specifing the new bundle image that you want to upgrade to.
+
+```console
+$ operator-sdk run bundle-upgrade <some-registry>/etcd-bundle:v0.0.2
+INFO[0000] Found existing subscription with name etcd and namespace default
+INFO[0000] Found existing catalog source with name etcdoperator and namespace default
+INFO[0005] Successfully created registry pod: <some-registry>-etcd-bundle-0-0-2
+INFO[0005] Updated catalog source etcdoperator with address and annotations
+INFO[0005] Deleted previous registry pod with name "<some-registry>-etcd-bundle-0-0-1"
+INFO[0005] Approved InstallPlan install-6vrzh for the Subscription: etcd
+INFO[0005] Waiting for ClusterServiceVersion "default/etcdoperator.v0.0.2" to reach 'Succeeded' phase
+INFO[0005]   Waiting for ClusterServiceVersion "default/etcdoperator.v0.0.2" to appear
+INFO[0007]   Found ClusterServiceVersion "default/etcdoperator.v0.0.2" phase: Pending
+INFO[0008]   Found ClusterServiceVersion "default/etcdoperator.v0.0.2" phase: Installing
+INFO[0018]   Found ClusterServiceVersion "default/etcdoperator.v0.0.2" phase: Succeeded
+INFO[0018] Successfully upgraded to "etcdoperator.v0.0.2"
+```
+
+### Deploying bundles in production
+
+OLM and Operator Registry consumes Operator bundles via a catalog of Operators, implemented as an
+[index image][index-image], which are composed of one or more bundles. To build and push a
+memcached-operator bundle image for version v0.0.1, run:
+
+```console
+$ make bundle-build bundle-push BUNDLE_IMG=<some-registry>/memcached-operator-bundle:v0.0.1
+```
+
+Now you can build and push the catalog by running `catalog-*` Makfile targets, which use
+the Operator package manager tool [`opm`][opm] to [build][doc-index-build] the catalog:
+
+```console
+$ make catalog-build catalog-push CATALOG_IMG=<some-registry>/memcached-operator-catalog:v0.0.1
+```
+
+Assuming `IMAGE_TAG_BASE = <some-registry>/memcached-operator` has the desired tag base, you can inline
+the above two commands to:
+
+```console
+$ make bundle-build bundle-push catalog-build catalog-push
+```
+
+Which will build and push both `<some-registry>/memcached-operator-bundle:v0.0.1`
+and `<some-registry>/memcached-operator-catalog:v0.0.1`.
+
+## Further reading
+
+In-depth discussions of OLM concepts mentioned here:
+- [CatalogSource][catalogsource]
+- [Subscription][subscription]
+- [Install an Operator from a catalog][olm-install]
+
+
+[sdk-user-guide-go]:/docs/building-operators/golang/quickstart
+[sdk-user-guide-ansible]:/docs/building-operators/ansible/quickstart
+[sdk-user-guide-helm]:/docs/building-operators/helm/quickstart
+[tutorial-package-manifests]:/docs/olm-integration/tutorial-package-manifests
+[olm]:https://github.com/operator-framework/operator-lifecycle-manager/
+[bundle]:https://github.com/operator-framework/operator-registry/blob/v1.16.1/docs/design/operator-bundle.md
+[bundle-metadata]:https://github.com/operator-framework/operator-registry/blob/v1.12.6/docs/design/operator-bundle.md#bundle-annotations
+[bundle-dockerfile]:https://github.com/operator-framework/operator-registry/blob/v1.12.6/docs/design/operator-bundle.md#bundle-dockerfile
+[image-reg-config]:/docs/olm-integration/cli-overview#private-bundle-and-catalog-image-registries
+[cli-olm]:/docs/cli/operator-sdk_olm
+[cli-run-bundle]:/docs/cli/operator-sdk_run_bundle
+[doc-cli-overview]:/docs/olm-integration/cli-overview
+[doc-olm-generate]:/docs/olm-integration/generation
+[opm]:https://github.com/operator-framework/operator-registry/blob/master/docs/design/opm-tooling.md
+[index-image]:https://github.com/operator-framework/operator-registry/blob/master/docs/design/opm-tooling.md#index
+[doc-index-build]:https://github.com/operator-framework/operator-registry#building-an-index-of-operators-using-opm
+[install-your-operator]:https://olm.operatorframework.io/docs/tasks/install-operator-with-olm/#install-your-operator
+[catalogsource]:https://olm.operatorframework.io/docs/concepts/crds/catalogsource/
+[subscription]:https://olm.operatorframework.io/docs/concepts/crds/subscription/
+[olm-install]:https://olm.operatorframework.io/docs/tasks/install-operator-with-olm/
diff --git a/website/content/en/docs/olm-integration/quickstart-package-manifests.md b/website/content/en/docs/olm-integration/tutorial-package-manifests.md
similarity index 100%
rename from website/content/en/docs/olm-integration/quickstart-package-manifests.md
rename to website/content/en/docs/olm-integration/tutorial-package-manifests.md
diff --git a/website/content/en/docs/overview/cheat-sheet.md b/website/content/en/docs/overview/cheat-sheet.md
index 50498fdd..7b856483 100644
--- a/website/content/en/docs/overview/cheat-sheet.md
+++ b/website/content/en/docs/overview/cheat-sheet.md
@@ -30,7 +30,7 @@ For further information check [Operator SDK Integration with Operator Lifecycle
 
 | Command   | Description  |
 |-------|-----------|
-| `make bundle`          | Create/update the [bundle][bundle] based on the project manifests in the `bundle/` directory. More info [Create a bundle][creating-a-bundle].      |
+| `make bundle`          | Create/update the [bundle][bundle] based on the project manifests in the `bundle/` directory. For more info see [Create a bundle][creating-a-bundle].      |
 | `operator-sdk bundle validate ./bundle`          | To validate your [bundle][bundle] spec definition.      |
 | `operator-sdk bundle validate ./bundle --select-optional suite=operatorframework` | Validate your bundle against [OperatorHub.io][operatorhub-io] criteria. For further information use the flag `--help`. |
 | `operator-sdk olm install` | To install OLM on your cluster for development purposes. |
@@ -66,7 +66,7 @@ make bundle CHANNELS=fast,preview DEFAULT_CHANNEL=stable VERSION=1.0.0 IMG=<some
 **NOTE:** This is not a comprehensive list of make targets or commands. Please see the scaffolded Makefile and `make help` for the full list of targets. Note that you can use `operator-sdk <command> --help` and check the [CLI][cli] section to check all options.
  
 [olm-integration]: /docs/olm-integration/
-[creating-a-bundle]: /docs/olm-integration/quickstart-bundle/#creating-a-bundle
+[creating-a-bundle]: /docs/olm-integration/tutorial-bundle/#creating-a-bundle
 [bundle]:https://github.com/operator-framework/operator-registry/blob/v1.16.1/docs/design/operator-bundle.md
 [operatorhub-io]: https://operatorhub.io/
 [upgrade-project]: /docs/olm-integration/generation/#upgrade-your-operator
diff --git a/website/content/en/docs/upgrading-sdk-version/v1.0.0.md b/website/content/en/docs/upgrading-sdk-version/v1.0.0.md
index ad32facb..fecefa13 100644
--- a/website/content/en/docs/upgrading-sdk-version/v1.0.0.md
+++ b/website/content/en/docs/upgrading-sdk-version/v1.0.0.md
@@ -31,7 +31,7 @@ The following subcommands were removed:
 | `operator-sdk migrate`        | Removed support for hybrid operators, no migration                                                       | [#3385](https://github.com/operator-framework/operator-sdk/pull/3385)
 | `operator-sdk print-deps`     | Removed, no migration                                                                                    | [#3385](https://github.com/operator-framework/operator-sdk/pull/3385)
 | `operator-sdk run local`      | Use `make run`                                                                                           | [#3406](https://github.com/operator-framework/operator-sdk/pull/3406)
-| `operator-sdk test`           | Use controller-runtime's [envtest]https://book.kubebuilder.io/reference/envtest.html) framework          | [#3409](https://github.com/operator-framework/operator-sdk/pull/3409)
+| `operator-sdk test`           | Use controller-runtime's [envtest](https://book.kubebuilder.io/reference/envtest.html) framework          | [#3409](https://github.com/operator-framework/operator-sdk/pull/3409)
 
 ### Library changes
 
diff --git a/website/content/en/docs/upgrading-sdk-version/v1.11.0.md b/website/content/en/docs/upgrading-sdk-version/v1.11.0.md
new file mode 100644
index 00000000..9999ef34
--- /dev/null
+++ b/website/content/en/docs/upgrading-sdk-version/v1.11.0.md
@@ -0,0 +1,58 @@
+---
+title: v1.11.0
+weight: 998989000
+---
+
+## (optional) Add local Ansible Roles path to Env in `make run`
+
+If you would like to run your operator locally using `make run`, modify the `run` target in the `Makefile` to: `ANSIBLE_ROLES_PATH="$(ANSIBLE_ROLES_PATH):$(shell pwd)/roles" $(ANSIBLE_OPERATOR) run`
+
+_See [#5118](https://github.com/operator-framework/operator-sdk/pull/5118) for more details._
+
+## (go/v3) Export `KUBEBUILDER_ASSETS` in `Makefile`
+
+In `go/v3` projects, after installing `setup-envtest` binary, the path to `KUBEBUILDER_ASSETS` needs to be exported. In `Makefile`, add the following:
+
+  ```diff
+    +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
+    +ENVTEST_K8S_VERSION = 1.21
+
+    test: manifests generate fmt vet envtest ## Run tests.
+    - go test ./... -coverprofile cover.out
+    + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out
+  ```
+
+_See [#4863](https://github.com/operator-framework/operator-sdk/pull/4863) for more details._
+
+## (go/v3, ansible/v1, helm/v1) Add `containerPort` protocol field in manifests.
+
+In `go/v3`, `ansible/v1`, `helm/v1` projects, specify the `containerPort` protocol in the following files: - `config/default/manager_auth_proxy_patch.yaml` - `config/rbac/auth_proxy_service.yaml`
+```diff
+  ports:
+ - containerPort: 8443
+ + protocol: TCP
+   name: https
+```
+Additionally, in `go/v3` projects, add the `containerPort` protocol to `config/webhook/service.yaml`:
+```diff
+   ports:
+    - port: 443
+    + protocol: TCP
+      targetPort: 9443
+```
+
+_See [#4863](https://github.com/operator-framework/operator-sdk/pull/4863) for more details._
+
+## (go/v3, ansible/v1, helm/v1) Increase resource limits for controller manager.
+
+In `go/v3`, `ansible/v1`, `helm/v1` projects, increase the `cpu` and `memory` resource limits to `200m` and `100Mi` respectively in `config/manager/manager.yaml`.
+```diff
+  resources:
+   limits:
+    - cpu: 100m
+    - memory: 30Mi
+    + cpu: 200m
+    + memory: 100Mi
+```
+
+_See [#4863](https://github.com/operator-framework/operator-sdk/pull/4863) for more details._


Test patch:
diff --git a/internal/ansible/controller/controller_test.go b/internal/ansible/controller/controller_test.go
new file mode 100644
index 00000000..968fdef1
--- /dev/null
+++ b/internal/ansible/controller/controller_test.go
@@ -0,0 +1,39 @@
+// Copyright 2021 The Operator-SDK 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 controller
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestFilterPredicate(t *testing.T) {
+	matchLabelPass := make(map[string]string)
+	matchLabelPass["testKey"] = "testValue"
+	selectorPass := metav1.LabelSelector{
+		MatchLabels: matchLabelPass,
+	}
+	noSelector := metav1.LabelSelector{}
+
+	passPredicate, err := parsePredicateSelector(selectorPass)
+	assert.Equal(t, nil, err, "Verify that no error is thrown on a valid populated selector")
+	assert.NotEqual(t, nil, passPredicate, "Verify that a predicate is constructed using a valid selector")
+
+	nilPredicate, err := parsePredicateSelector(noSelector)
+	assert.Equal(t, nil, err, "Verify that no error is thrown on a valid unpopulated selector")
+	assert.Equal(t, nil, nilPredicate, "Verify correct parsing of an unpopulated selector")
+}
diff --git a/internal/generate/clusterserviceversion/clusterserviceversion_updaters_test.go b/internal/generate/clusterserviceversion/clusterserviceversion_updaters_test.go
index 55fda9d3..5f20b476 100644
--- a/internal/generate/clusterserviceversion/clusterserviceversion_updaters_test.go
+++ b/internal/generate/clusterserviceversion/clusterserviceversion_updaters_test.go
@@ -379,7 +379,7 @@ var _ = Describe("findMatchingDeploymentAndServiceForWebhook", func() {
 		port1 := new(int32)
 		*port1 = 2311
 		crdToConfigPath := map[string]apiextv1.WebhookConversion{
-			"crd-test-1": apiextv1.WebhookConversion{
+			"crd-test-1": {
 				ClientConfig: &apiextv1.WebhookClientConfig{
 					Service: &apiextv1.ServiceReference{
 						Path: &path1,
@@ -388,7 +388,7 @@ var _ = Describe("findMatchingDeploymentAndServiceForWebhook", func() {
 				},
 			},
 
-			"crd-test-2": apiextv1.WebhookConversion{
+			"crd-test-2": {
 				ClientConfig: &apiextv1.WebhookClientConfig{
 					Service: &apiextv1.ServiceReference{
 						Path: &path1,
diff --git a/internal/generate/collector/clusterserviceversion_test.go b/internal/generate/collector/clusterserviceversion_test.go
index 8604c836..94a476f8 100644
--- a/internal/generate/collector/clusterserviceversion_test.go
+++ b/internal/generate/collector/clusterserviceversion_test.go
@@ -21,6 +21,7 @@ import (
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
@@ -160,6 +161,10 @@ var _ = Describe("SplitCSVPermissionsObjects", func() {
 
 		complexTestSetup := func() {
 			c.Deployments = []appsv1.Deployment{newDeploymentWithServiceAccount(depSA)}
+			c.ServiceAccounts = []corev1.ServiceAccount{
+				newServiceAccount(depSA),
+				newServiceAccount(extraSA),
+			}
 			role1, role2 := newRole(roleName1), newRole(roleName2)
 			c.Roles = []rbacv1.Role{role1, role2}
 			// Use the same names as for Roles to make sure Kind is respected
@@ -198,12 +203,14 @@ var _ = Describe("SplitCSVPermissionsObjects", func() {
 			Expect(getClusterRoleNames(inPerm)).To(Equal([]string{roleName1}))
 			Expect(inCPerm).To(HaveLen(1))
 			Expect(getClusterRoleNames(inCPerm)).To(Equal([]string{roleName1}))
-			Expect(out).To(HaveLen(5))
+			Expect(out).To(HaveLen(6))
 			Expect(getRoleNames(out)).To(Equal([]string{roleName2}))
 			Expect(getClusterRoleNames(out)).To(Equal([]string{roleName2}))
 			Expect(getRoleBindingNames(out)).To(Equal([]string{bindingName1, bindingName2}))
 			Expect(getClusterRoleBindingNames(out)).To(Equal([]string{bindingName2}))
+			Expect(getServiceAccountNames(out)).To(Equal([]string{extraSA}))
 		})
+
 		It("contains a Deployment serviceAccountName and extra ServiceAccount", func() {
 			complexTestSetup()
 			inPerm, inCPerm, out = c.SplitCSVPermissionsObjects([]string{extraSA})
@@ -233,6 +240,10 @@ func getClusterRoleBindingNames(objs []client.Object) []string {
 	return getNamesForKind("ClusterRoleBinding", objs)
 }
 
+func getServiceAccountNames(objs []client.Object) []string {
+	return getNamesForKind("ServiceAccount", objs)
+}
+
 func getNamesForKind(kind string, objs []client.Object) (names []string) {
 	for _, obj := range objs {
 		if obj.GetObjectKind().GroupVersionKind().Kind == kind {
@@ -300,3 +311,9 @@ func newSubject(name, kind string) (s rbacv1.Subject) {
 func newServiceAccountSubject(name string) (s rbacv1.Subject) {
 	return newSubject(name, "ServiceAccount")
 }
+
+func newServiceAccount(name string) (sa corev1.ServiceAccount) {
+	sa.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("ServiceAccount"))
+	sa.Name = name
+	return sa
+}
diff --git a/internal/helm/controller/controller_test.go b/internal/helm/controller/controller_test.go
new file mode 100644
index 00000000..968fdef1
--- /dev/null
+++ b/internal/helm/controller/controller_test.go
@@ -0,0 +1,39 @@
+// Copyright 2021 The Operator-SDK 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 controller
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestFilterPredicate(t *testing.T) {
+	matchLabelPass := make(map[string]string)
+	matchLabelPass["testKey"] = "testValue"
+	selectorPass := metav1.LabelSelector{
+		MatchLabels: matchLabelPass,
+	}
+	noSelector := metav1.LabelSelector{}
+
+	passPredicate, err := parsePredicateSelector(selectorPass)
+	assert.Equal(t, nil, err, "Verify that no error is thrown on a valid populated selector")
+	assert.NotEqual(t, nil, passPredicate, "Verify that a predicate is constructed using a valid selector")
+
+	nilPredicate, err := parsePredicateSelector(noSelector)
+	assert.Equal(t, nil, err, "Verify that no error is thrown on a valid unpopulated selector")
+	assert.Equal(t, nil, nilPredicate, "Verify correct parsing of an unpopulated selector")
+}
diff --git a/internal/helm/release/manager_test.go b/internal/helm/release/manager_test.go
index 70c5e5b5..466bb693 100644
--- a/internal/helm/release/manager_test.go
+++ b/internal/helm/release/manager_test.go
@@ -15,21 +15,18 @@
 package release
 
 import (
-	"bytes"
-	"encoding/json"
 	"testing"
 
-	"github.com/stretchr/testify/assert"
-	cpb "helm.sh/helm/v3/pkg/chart"
-	lpb "helm.sh/helm/v3/pkg/chart/loader"
-	rpb "helm.sh/helm/v3/pkg/release"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	apitypes "k8s.io/apimachinery/pkg/types"
+	"k8s.io/cli-runtime/pkg/resource"
+
 	appsv1 "k8s.io/api/apps/v1"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+
+	"github.com/stretchr/testify/assert"
 	"k8s.io/apimachinery/pkg/runtime"
-	apitypes "k8s.io/apimachinery/pkg/types"
-	"k8s.io/cli-runtime/pkg/resource"
 )
 
 func newTestUnstructured(containers []interface{}) *unstructured.Unstructured {
@@ -216,85 +213,3 @@ func TestManagerGenerateStrategicMergePatch(t *testing.T) {
 		assert.Equal(t, test.patch, string(diff))
 	}
 }
-
-func TestManagerisUpgrade(t *testing.T) {
-	tests := []struct {
-		name            string
-		releaseName     string
-		releaseNs       string
-		values          map[string]interface{}
-		chart           *cpb.Chart
-		deployedRelease *rpb.Release
-		want            bool
-	}{
-		{
-			name:            "ok",
-			releaseName:     "deployed",
-			releaseNs:       "deployed-ns",
-			values:          map[string]interface{}{"key": "value"},
-			chart:           newTestChart(t, "./testdata/simple"),
-			deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": "value"}, "deployed", "deployed-ns"),
-			want:            false,
-		},
-		{
-			name:            "different chart",
-			releaseName:     "deployed",
-			releaseNs:       "deployed-ns",
-			values:          map[string]interface{}{"key": "value"},
-			chart:           newTestChart(t, "./testdata/simple"),
-			deployedRelease: newTestRelease(newTestChart(t, "./testdata/simpledf"), map[string]interface{}{"key": "value"}, "deployed", "deployed-ns"),
-			want:            true,
-		},
-		{
-			name:            "different values",
-			releaseName:     "deployed",
-			releaseNs:       "deployed-ns",
-			values:          map[string]interface{}{"key": "1", "int": int32(1)},
-			chart:           newTestChart(t, "./testdata/simple"),
-			deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": "", "int": int64(1)}, "deployed", "deployed-ns"),
-			want:            true,
-		},
-		{
-			name:            "nil values",
-			releaseName:     "deployed",
-			releaseNs:       "deployed-ns",
-			values:          nil,
-			chart:           newTestChart(t, "./testdata/simple"),
-			deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{}, "deployed", "deployed-ns"),
-			want:            false,
-		},
-	}
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			m := manager{
-				releaseName: test.releaseName,
-				namespace:   test.releaseNs,
-				values:      test.values,
-				chart:       test.chart,
-			}
-			isUpgrade, err := m.isUpgrade(test.deployedRelease)
-			assert.Equal(t, test.want, isUpgrade)
-			assert.Equal(t, nil, err)
-		})
-	}
-}
-
-func newTestChart(t *testing.T, path string) *cpb.Chart {
-	chart, err := lpb.Load(path)
-	assert.Nil(t, err)
-	return chart
-}
-
-func newTestRelease(chart *cpb.Chart, values map[string]interface{}, name, namespace string) *rpb.Release { // nolint: unparam
-	release := rpb.Mock(&rpb.MockReleaseOptions{
-		Name:      name,
-		Namespace: namespace,
-		Version:   1,
-	})
-
-	buffer := &bytes.Buffer{}
-	_ = json.NewEncoder(buffer).Encode(chart)
-	_ = json.NewDecoder(buffer).Decode(release.Chart)
-	release.Config = values
-	return release
-}
diff --git a/internal/helm/release/testdata/simple/.helmignore b/internal/helm/release/testdata/simple/.helmignore
deleted file mode 100644
index f0c13194..00000000
--- a/internal/helm/release/testdata/simple/.helmignore
+++ /dev/null
@@ -1,21 +0,0 @@
-# Patterns to ignore when building packages.
-# This supports shell glob matching, relative path matching, and
-# negation (prefixed with !). Only one pattern per line.
-.DS_Store
-# Common VCS dirs
-.git/
-.gitignore
-.bzr/
-.bzrignore
-.hg/
-.hgignore
-.svn/
-# Common backup files
-*.swp
-*.bak
-*.tmp
-*~
-# Various IDEs
-.project
-.idea/
-*.tmproj
diff --git a/internal/helm/release/testdata/simple/Chart.yaml b/internal/helm/release/testdata/simple/Chart.yaml
deleted file mode 100644
index 786c7721..00000000
--- a/internal/helm/release/testdata/simple/Chart.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-apiVersion: v1
-description: a simple charts
-name: simple
-keywords:
-  - helm
-  - operator-sdk
-version: 0.0.1+master
-appVersion: "master"
diff --git a/internal/helm/release/testdata/simple/templates/NOTES.txt b/internal/helm/release/testdata/simple/templates/NOTES.txt
deleted file mode 100644
index da930300..00000000
--- a/internal/helm/release/testdata/simple/templates/NOTES.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-A simple charts
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simple/templates/_helpers.tpl b/internal/helm/release/testdata/simple/templates/_helpers.tpl
deleted file mode 100644
index e5fe9272..00000000
--- a/internal/helm/release/testdata/simple/templates/_helpers.tpl
+++ /dev/null
@@ -1,7 +0,0 @@
-{{/* vim: set filetype=mustache: */}}
-{{/*
-Expand the name of the chart.
-*/}}
-{{- define "simple.fullname" -}}
-{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
-{{- end -}}
diff --git a/internal/helm/release/testdata/simple/templates/secrets.yaml b/internal/helm/release/testdata/simple/templates/secrets.yaml
deleted file mode 100644
index 4b61fbb7..00000000
--- a/internal/helm/release/testdata/simple/templates/secrets.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-apiVersion: v1
-kind: Secret
-metadata:
-  name: {{ template "simple.fullname" . }}
-  namespace: {{ .Release.Namespace }}
-type: Opaque
-data:
-  password: {{ randAlphaNum 10 | b64enc | quote }}
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simple/values.yaml b/internal/helm/release/testdata/simple/values.yaml
deleted file mode 100644
index ca762273..00000000
--- a/internal/helm/release/testdata/simple/values.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-# simple values
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simpledf/.helmignore b/internal/helm/release/testdata/simpledf/.helmignore
deleted file mode 100644
index f0c13194..00000000
--- a/internal/helm/release/testdata/simpledf/.helmignore
+++ /dev/null
@@ -1,21 +0,0 @@
-# Patterns to ignore when building packages.
-# This supports shell glob matching, relative path matching, and
-# negation (prefixed with !). Only one pattern per line.
-.DS_Store
-# Common VCS dirs
-.git/
-.gitignore
-.bzr/
-.bzrignore
-.hg/
-.hgignore
-.svn/
-# Common backup files
-*.swp
-*.bak
-*.tmp
-*~
-# Various IDEs
-.project
-.idea/
-*.tmproj
diff --git a/internal/helm/release/testdata/simpledf/Chart.yaml b/internal/helm/release/testdata/simpledf/Chart.yaml
deleted file mode 100644
index 786c7721..00000000
--- a/internal/helm/release/testdata/simpledf/Chart.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-apiVersion: v1
-description: a simple charts
-name: simple
-keywords:
-  - helm
-  - operator-sdk
-version: 0.0.1+master
-appVersion: "master"
diff --git a/internal/helm/release/testdata/simpledf/templates/NOTES.txt b/internal/helm/release/testdata/simpledf/templates/NOTES.txt
deleted file mode 100644
index da930300..00000000
--- a/internal/helm/release/testdata/simpledf/templates/NOTES.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-A simple charts
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simpledf/templates/_helpers.tpl b/internal/helm/release/testdata/simpledf/templates/_helpers.tpl
deleted file mode 100644
index e5fe9272..00000000
--- a/internal/helm/release/testdata/simpledf/templates/_helpers.tpl
+++ /dev/null
@@ -1,7 +0,0 @@
-{{/* vim: set filetype=mustache: */}}
-{{/*
-Expand the name of the chart.
-*/}}
-{{- define "simple.fullname" -}}
-{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
-{{- end -}}
diff --git a/internal/helm/release/testdata/simpledf/templates/secrets.yaml b/internal/helm/release/testdata/simpledf/templates/secrets.yaml
deleted file mode 100644
index 4b61fbb7..00000000
--- a/internal/helm/release/testdata/simpledf/templates/secrets.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-apiVersion: v1
-kind: Secret
-metadata:
-  name: {{ template "simple.fullname" . }}
-  namespace: {{ .Release.Namespace }}
-type: Opaque
-data:
-  password: {{ randAlphaNum 10 | b64enc | quote }}
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simpledf/values.yaml b/internal/helm/release/testdata/simpledf/values.yaml
deleted file mode 100644
index a99c72dc..00000000
--- a/internal/helm/release/testdata/simpledf/values.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-# simple values
-name: simpledf
\ No newline at end of file
diff --git a/internal/helm/watches/watches_test.go b/internal/helm/watches/watches_test.go
index cdea650c..7f543f39 100644
--- a/internal/helm/watches/watches_test.go
+++ b/internal/helm/watches/watches_test.go
@@ -55,7 +55,7 @@ func TestLoadReader(t *testing.T) {
 			expectErr: false,
 		},
 		{
-			name: "valid with override expansion",
+			name: "valid with override env expansion",
 			data: `---
 - group: mygroup
   version: v1alpha1
@@ -76,6 +76,48 @@ func TestLoadReader(t *testing.T) {
 			},
 			expectErr: false,
 		},
+		{
+			name: "valid with override template expansion",
+			data: `---
+- group: mygroup
+  version: v1alpha1
+  kind: MyKind
+  chart: ../../../internal/plugins/helm/v1/chartutil/testdata/test-chart
+  watchDependentResources: false
+  overrideValues:
+    repo: '{{ ("$MY_IMAGE" | split ":")._0 }}'
+    tag: '{{ ("$MY_IMAGE" | split ":")._1 }}'
+`,
+			env: map[string]string{"MY_IMAGE": "quay.io/operator-framework/helm-operator:latest"},
+			expectWatches: []Watch{
+				{
+					GroupVersionKind:        schema.GroupVersionKind{Group: "mygroup", Version: "v1alpha1", Kind: "MyKind"},
+					ChartDir:                "../../../internal/plugins/helm/v1/chartutil/testdata/test-chart",
+					WatchDependentResources: &falseVal,
+					OverrideValues: map[string]string{
+						"repo": "quay.io/operator-framework/helm-operator",
+						"tag":  "latest",
+					},
+				},
+			},
+			expectErr: false,
+		},
+
+		{
+			name: "invalid with override template expansion",
+			data: `---
+- group: mygroup
+  version: v1alpha1
+  kind: MyKind
+  chart: ../../../internal/plugins/helm/v1/chartutil/testdata/test-chart
+  watchDependentResources: false
+  overrideValues:
+    repo: '{{ ("$MY_IMAGE" | split ":")._0 }}'
+    tag: '{{ ("$MY_IMAGE" | split ":")._1'
+`,
+			env:       map[string]string{"MY_IMAGE": "quay.io/operator-framework/helm-operator:latest"},
+			expectErr: true,
+		},
 		{
 			name: "multiple gvk",
 			data: `---
diff --git a/test/common/scorecard.go b/test/common/scorecard.go
index 3cb8d5f9..57b23820 100644
--- a/test/common/scorecard.go
+++ b/test/common/scorecard.go
@@ -91,5 +91,22 @@ func ScorecardSpec(tc *testutils.TestContext, operatorType string) func() {
 				Expect(results[0].State).To(Equal(expected[results[0].Name]))
 			}
 		})
+
+		It("should configure scorecard storage successfully", func() {
+			cmd = exec.Command(tc.BinaryName, "scorecard", "bundle",
+				"--selector", "suite=basic",
+				"--output", "json",
+				"--test-output", "/testdata",
+				"--wait-time", "4m")
+			outputBytes, err = tc.Run(cmd)
+			Expect(err).NotTo(HaveOccurred())
+			Expect(json.Unmarshal(outputBytes, &output)).To(Succeed())
+
+			Expect(output.Items).To(HaveLen(1))
+			results := output.Items[0].Status.Results
+			Expect(results).To(HaveLen(1))
+			Expect(results[0].Name).To(Equal("basic-check-spec"))
+			Expect(results[0].State).To(Equal(v1alpha3.PassState))
+		})
 	}
 }
diff --git a/testdata/ansible/memcached-operator/Dockerfile b/testdata/ansible/memcached-operator/Dockerfile
index 80f85352..9dff0cf8 100644
--- a/testdata/ansible/memcached-operator/Dockerfile
+++ b/testdata/ansible/memcached-operator/Dockerfile
@@ -1,4 +1,4 @@
-FROM quay.io/operator-framework/ansible-operator:v1.10.0
+FROM quay.io/operator-framework/ansible-operator:v1.11.0
 
 COPY requirements.yml ${HOME}/requirements.yml
 RUN ansible-galaxy collection install -r ${HOME}/requirements.yml \
diff --git a/testdata/ansible/memcached-operator/Makefile b/testdata/ansible/memcached-operator/Makefile
index 7c1ac731..ff22e88a 100644
--- a/testdata/ansible/memcached-operator/Makefile
+++ b/testdata/ansible/memcached-operator/Makefile
@@ -59,7 +59,7 @@ help: ## Display this help.
 ##@ Build
 
 run: ansible-operator ## Run against the configured Kubernetes cluster in ~/.kube/config
-	$(ANSIBLE_OPERATOR) run
+	ANSIBLE_ROLES_PATH="$(ANSIBLE_ROLES_PATH):$(shell pwd)/roles" $(ANSIBLE_OPERATOR) run
 
 docker-build: ## Build docker image with the manager.
 	docker build -t ${IMG} .
@@ -109,7 +109,7 @@ ifeq (,$(shell which ansible-operator 2>/dev/null))
 	@{ \
 	set -e ;\
 	mkdir -p $(dir $(ANSIBLE_OPERATOR)) ;\
-	curl -sSLo $(ANSIBLE_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.10.0/ansible-operator_$(OS)_$(ARCH) ;\
+	curl -sSLo $(ANSIBLE_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.11.0/ansible-operator_$(OS)_$(ARCH) ;\
 	chmod +x $(ANSIBLE_OPERATOR) ;\
 	}
 else
diff --git a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml b/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml
index 157a0cef..79085e5a 100644
--- a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml
+++ b/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml
@@ -9,6 +9,7 @@ spec:
   ports:
   - name: https
     port: 8443
+    protocol: TCP
     targetPort: https
   selector:
     control-plane: controller-manager
diff --git a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager_v1_serviceaccount.yaml b/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager_v1_serviceaccount.yaml
deleted file mode 100644
index adc7cf0c..00000000
--- a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager_v1_serviceaccount.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  creationTimestamp: null
-  name: memcached-operator-controller-manager
diff --git a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml b/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
index 985dcef3..e81ccabb 100644
--- a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
+++ b/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
@@ -115,6 +115,7 @@ spec:
                 ports:
                 - containerPort: 8443
                   name: https
+                  protocol: TCP
                 resources: {}
               - args:
                 - --health-probe-bind-address=:6789
diff --git a/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml b/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml
index 8cba919c..7b281f6a 100644
--- a/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml
+++ b/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml
@@ -8,7 +8,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: basic
       test: basic-check-spec-test
@@ -18,7 +18,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-bundle-validation-test
@@ -28,7 +28,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-validation-test
@@ -38,7 +38,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-resources-test
@@ -48,7 +48,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-spec-descriptors-test
@@ -58,7 +58,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/ansible/memcached-operator/config/default/manager_auth_proxy_patch.yaml b/testdata/ansible/memcached-operator/config/default/manager_auth_proxy_patch.yaml
index fabc2065..8fbf836e 100644
--- a/testdata/ansible/memcached-operator/config/default/manager_auth_proxy_patch.yaml
+++ b/testdata/ansible/memcached-operator/config/default/manager_auth_proxy_patch.yaml
@@ -18,6 +18,7 @@ spec:
         - "--v=10"
         ports:
         - containerPort: 8443
+          protocol: TCP
           name: https
       - name: manager
         args:
diff --git a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_service.yaml b/testdata/ansible/memcached-operator/config/rbac/auth_proxy_service.yaml
index 6cf656be..71f17972 100644
--- a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_service.yaml
+++ b/testdata/ansible/memcached-operator/config/rbac/auth_proxy_service.yaml
@@ -9,6 +9,7 @@ spec:
   ports:
   - name: https
     port: 8443
+    protocol: TCP
     targetPort: https
   selector:
     control-plane: controller-manager
diff --git a/testdata/ansible/memcached-operator/config/scorecard/patches/basic.config.yaml b/testdata/ansible/memcached-operator/config/scorecard/patches/basic.config.yaml
index ab07c06f..f3f0f34d 100644
--- a/testdata/ansible/memcached-operator/config/scorecard/patches/basic.config.yaml
+++ b/testdata/ansible/memcached-operator/config/scorecard/patches/basic.config.yaml
@@ -4,7 +4,7 @@
     entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: basic
       test: basic-check-spec-test
diff --git a/testdata/ansible/memcached-operator/config/scorecard/patches/olm.config.yaml b/testdata/ansible/memcached-operator/config/scorecard/patches/olm.config.yaml
index b1ba10d6..327800b2 100644
--- a/testdata/ansible/memcached-operator/config/scorecard/patches/olm.config.yaml
+++ b/testdata/ansible/memcached-operator/config/scorecard/patches/olm.config.yaml
@@ -4,7 +4,7 @@
     entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-bundle-validation-test
@@ -14,7 +14,7 @@
     entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-validation-test
@@ -24,7 +24,7 @@
     entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-resources-test
@@ -34,7 +34,7 @@
     entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-spec-descriptors-test
@@ -44,7 +44,7 @@
     entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/go/v2/memcached-operator/bundle/tests/scorecard/config.yaml b/testdata/go/v2/memcached-operator/bundle/tests/scorecard/config.yaml
index 8cba919c..7b281f6a 100644
--- a/testdata/go/v2/memcached-operator/bundle/tests/scorecard/config.yaml
+++ b/testdata/go/v2/memcached-operator/bundle/tests/scorecard/config.yaml
@@ -8,7 +8,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: basic
       test: basic-check-spec-test
@@ -18,7 +18,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-bundle-validation-test
@@ -28,7 +28,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-validation-test
@@ -38,7 +38,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-resources-test
@@ -48,7 +48,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-spec-descriptors-test
@@ -58,7 +58,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/go/v2/memcached-operator/config/scorecard/patches/basic.config.yaml b/testdata/go/v2/memcached-operator/config/scorecard/patches/basic.config.yaml
index ab07c06f..f3f0f34d 100644
--- a/testdata/go/v2/memcached-operator/config/scorecard/patches/basic.config.yaml
+++ b/testdata/go/v2/memcached-operator/config/scorecard/patches/basic.config.yaml
@@ -4,7 +4,7 @@
     entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: basic
       test: basic-check-spec-test
diff --git a/testdata/go/v2/memcached-operator/config/scorecard/patches/olm.config.yaml b/testdata/go/v2/memcached-operator/config/scorecard/patches/olm.config.yaml
index b1ba10d6..327800b2 100644
--- a/testdata/go/v2/memcached-operator/config/scorecard/patches/olm.config.yaml
+++ b/testdata/go/v2/memcached-operator/config/scorecard/patches/olm.config.yaml
@@ -4,7 +4,7 @@
     entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-bundle-validation-test
@@ -14,7 +14,7 @@
     entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-validation-test
@@ -24,7 +24,7 @@
     entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-resources-test
@@ -34,7 +34,7 @@
     entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-spec-descriptors-test
@@ -44,7 +44,7 @@
     entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/go/v3/memcached-operator/Makefile b/testdata/go/v3/memcached-operator/Makefile
index c8744e94..2cf8fbab 100644
--- a/testdata/go/v3/memcached-operator/Makefile
+++ b/testdata/go/v3/memcached-operator/Makefile
@@ -39,6 +39,8 @@ BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
 IMG ?= controller:latest
 # Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
 CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
+# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
+ENVTEST_K8S_VERSION = 1.21
 
 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
 ifeq (,$(shell go env GOBIN))
@@ -86,7 +88,7 @@ vet: ## Run go vet against code.
 	go vet ./...
 
 test: manifests generate fmt vet envtest ## Run tests.
-	go test ./... -coverprofile cover.out
+	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out
 
 ##@ Build
 
@@ -129,7 +131,6 @@ kustomize: ## Download kustomize locally if necessary.
 ENVTEST = $(shell pwd)/bin/setup-envtest
 envtest: ## Download envtest-setup locally if necessary.
 	$(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest)
-	$(ENVTEST) use 1.21
 
 # go-get-tool will 'go get' any package $2 and install it to $1.
 PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
diff --git a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml b/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml
index 157a0cef..79085e5a 100644
--- a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml
+++ b/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml
@@ -9,6 +9,7 @@ spec:
   ports:
   - name: https
     port: 8443
+    protocol: TCP
     targetPort: https
   selector:
     control-plane: controller-manager
diff --git a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-controller-manager_v1_serviceaccount.yaml b/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-controller-manager_v1_serviceaccount.yaml
deleted file mode 100644
index adc7cf0c..00000000
--- a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-controller-manager_v1_serviceaccount.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  creationTimestamp: null
-  name: memcached-operator-controller-manager
diff --git a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-webhook-service_v1_service.yaml b/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-webhook-service_v1_service.yaml
index 4c9ef443..3bb3965d 100644
--- a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-webhook-service_v1_service.yaml
+++ b/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator-webhook-service_v1_service.yaml
@@ -6,6 +6,7 @@ metadata:
 spec:
   ports:
   - port: 443
+    protocol: TCP
     targetPort: 9443
   selector:
     control-plane: controller-manager
diff --git a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml b/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
index 8d9dd284..b8bf2a34 100644
--- a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
+++ b/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
@@ -119,6 +119,7 @@ spec:
                 ports:
                 - containerPort: 8443
                   name: https
+                  protocol: TCP
                 resources: {}
               - args:
                 - --health-probe-bind-address=:8081
@@ -146,8 +147,8 @@ spec:
                   periodSeconds: 10
                 resources:
                   limits:
-                    cpu: 100m
-                    memory: 30Mi
+                    cpu: 200m
+                    memory: 100Mi
                   requests:
                     cpu: 100m
                     memory: 20Mi
diff --git a/testdata/go/v3/memcached-operator/bundle/tests/scorecard/config.yaml b/testdata/go/v3/memcached-operator/bundle/tests/scorecard/config.yaml
index 8cba919c..7b281f6a 100644
--- a/testdata/go/v3/memcached-operator/bundle/tests/scorecard/config.yaml
+++ b/testdata/go/v3/memcached-operator/bundle/tests/scorecard/config.yaml
@@ -8,7 +8,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: basic
       test: basic-check-spec-test
@@ -18,7 +18,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-bundle-validation-test
@@ -28,7 +28,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-validation-test
@@ -38,7 +38,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-resources-test
@@ -48,7 +48,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-spec-descriptors-test
@@ -58,7 +58,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/go/v3/memcached-operator/config/default/manager_auth_proxy_patch.yaml b/testdata/go/v3/memcached-operator/config/default/manager_auth_proxy_patch.yaml
index a224be19..4e2232fa 100644
--- a/testdata/go/v3/memcached-operator/config/default/manager_auth_proxy_patch.yaml
+++ b/testdata/go/v3/memcached-operator/config/default/manager_auth_proxy_patch.yaml
@@ -18,6 +18,7 @@ spec:
         - "--v=10"
         ports:
         - containerPort: 8443
+          protocol: TCP
           name: https
       - name: manager
         args:
diff --git a/testdata/go/v3/memcached-operator/config/manager/manager.yaml b/testdata/go/v3/memcached-operator/config/manager/manager.yaml
index 79adfe72..20262442 100644
--- a/testdata/go/v3/memcached-operator/config/manager/manager.yaml
+++ b/testdata/go/v3/memcached-operator/config/manager/manager.yaml
@@ -47,8 +47,8 @@ spec:
           periodSeconds: 10
         resources:
           limits:
-            cpu: 100m
-            memory: 30Mi
+            cpu: 200m
+            memory: 100Mi
           requests:
             cpu: 100m
             memory: 20Mi
diff --git a/testdata/go/v3/memcached-operator/config/rbac/auth_proxy_service.yaml b/testdata/go/v3/memcached-operator/config/rbac/auth_proxy_service.yaml
index 6cf656be..71f17972 100644
--- a/testdata/go/v3/memcached-operator/config/rbac/auth_proxy_service.yaml
+++ b/testdata/go/v3/memcached-operator/config/rbac/auth_proxy_service.yaml
@@ -9,6 +9,7 @@ spec:
   ports:
   - name: https
     port: 8443
+    protocol: TCP
     targetPort: https
   selector:
     control-plane: controller-manager
diff --git a/testdata/go/v3/memcached-operator/config/scorecard/patches/basic.config.yaml b/testdata/go/v3/memcached-operator/config/scorecard/patches/basic.config.yaml
index ab07c06f..f3f0f34d 100644
--- a/testdata/go/v3/memcached-operator/config/scorecard/patches/basic.config.yaml
+++ b/testdata/go/v3/memcached-operator/config/scorecard/patches/basic.config.yaml
@@ -4,7 +4,7 @@
     entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: basic
       test: basic-check-spec-test
diff --git a/testdata/go/v3/memcached-operator/config/scorecard/patches/olm.config.yaml b/testdata/go/v3/memcached-operator/config/scorecard/patches/olm.config.yaml
index b1ba10d6..327800b2 100644
--- a/testdata/go/v3/memcached-operator/config/scorecard/patches/olm.config.yaml
+++ b/testdata/go/v3/memcached-operator/config/scorecard/patches/olm.config.yaml
@@ -4,7 +4,7 @@
     entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-bundle-validation-test
@@ -14,7 +14,7 @@
     entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-validation-test
@@ -24,7 +24,7 @@
     entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-resources-test
@@ -34,7 +34,7 @@
     entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-spec-descriptors-test
@@ -44,7 +44,7 @@
     entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/go/v3/memcached-operator/config/webhook/service.yaml b/testdata/go/v3/memcached-operator/config/webhook/service.yaml
index 31e0f829..3f638bd9 100644
--- a/testdata/go/v3/memcached-operator/config/webhook/service.yaml
+++ b/testdata/go/v3/memcached-operator/config/webhook/service.yaml
@@ -7,6 +7,7 @@ metadata:
 spec:
   ports:
     - port: 443
+      protocol: TCP
       targetPort: 9443
   selector:
     control-plane: controller-manager
diff --git a/testdata/helm/memcached-operator/Dockerfile b/testdata/helm/memcached-operator/Dockerfile
index 583e3ec8..0ebd0864 100644
--- a/testdata/helm/memcached-operator/Dockerfile
+++ b/testdata/helm/memcached-operator/Dockerfile
@@ -1,5 +1,5 @@
 # Build the manager binary
-FROM quay.io/operator-framework/helm-operator:v1.10.0
+FROM quay.io/operator-framework/helm-operator:v1.11.0
 
 ENV HOME=/opt/helm
 COPY watches.yaml ${HOME}/watches.yaml
diff --git a/testdata/helm/memcached-operator/Makefile b/testdata/helm/memcached-operator/Makefile
index f25ab850..f1e88ed8 100644
--- a/testdata/helm/memcached-operator/Makefile
+++ b/testdata/helm/memcached-operator/Makefile
@@ -109,7 +109,7 @@ ifeq (,$(shell which helm-operator 2>/dev/null))
 	@{ \
 	set -e ;\
 	mkdir -p $(dir $(HELM_OPERATOR)) ;\
-	curl -sSLo $(HELM_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.10.0/helm-operator_$(OS)_$(ARCH) ;\
+	curl -sSLo $(HELM_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.11.0/helm-operator_$(OS)_$(ARCH) ;\
 	chmod +x $(HELM_OPERATOR) ;\
 	}
 else
diff --git a/testdata/helm/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml b/testdata/helm/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml
index 157a0cef..79085e5a 100644
--- a/testdata/helm/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml
+++ b/testdata/helm/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml
@@ -9,6 +9,7 @@ spec:
   ports:
   - name: https
     port: 8443
+    protocol: TCP
     targetPort: https
   selector:
     control-plane: controller-manager
diff --git a/testdata/helm/memcached-operator/bundle/manifests/memcached-operator-controller-manager_v1_serviceaccount.yaml b/testdata/helm/memcached-operator/bundle/manifests/memcached-operator-controller-manager_v1_serviceaccount.yaml
deleted file mode 100644
index adc7cf0c..00000000
--- a/testdata/helm/memcached-operator/bundle/manifests/memcached-operator-controller-manager_v1_serviceaccount.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  creationTimestamp: null
-  name: memcached-operator-controller-manager
diff --git a/testdata/helm/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml b/testdata/helm/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
index 7d025a25..6016a7cf 100644
--- a/testdata/helm/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
+++ b/testdata/helm/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
@@ -203,6 +203,7 @@ spec:
                 ports:
                 - containerPort: 8443
                   name: https
+                  protocol: TCP
                 resources: {}
               - args:
                 - --health-probe-bind-address=:8081
@@ -225,8 +226,8 @@ spec:
                   periodSeconds: 10
                 resources:
                   limits:
-                    cpu: 100m
-                    memory: 90Mi
+                    cpu: 200m
+                    memory: 100Mi
                   requests:
                     cpu: 100m
                     memory: 60Mi
diff --git a/testdata/helm/memcached-operator/bundle/tests/scorecard/config.yaml b/testdata/helm/memcached-operator/bundle/tests/scorecard/config.yaml
index 8cba919c..7b281f6a 100644
--- a/testdata/helm/memcached-operator/bundle/tests/scorecard/config.yaml
+++ b/testdata/helm/memcached-operator/bundle/tests/scorecard/config.yaml
@@ -8,7 +8,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: basic
       test: basic-check-spec-test
@@ -18,7 +18,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-bundle-validation-test
@@ -28,7 +28,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-validation-test
@@ -38,7 +38,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-resources-test
@@ -48,7 +48,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-spec-descriptors-test
@@ -58,7 +58,7 @@ stages:
   - entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/helm/memcached-operator/config/default/manager_auth_proxy_patch.yaml b/testdata/helm/memcached-operator/config/default/manager_auth_proxy_patch.yaml
index 33886eb0..92bc8851 100644
--- a/testdata/helm/memcached-operator/config/default/manager_auth_proxy_patch.yaml
+++ b/testdata/helm/memcached-operator/config/default/manager_auth_proxy_patch.yaml
@@ -18,6 +18,7 @@ spec:
         - "--v=10"
         ports:
         - containerPort: 8443
+          protocol: TCP
           name: https
       - name: manager
         args:
diff --git a/testdata/helm/memcached-operator/config/manager/manager.yaml b/testdata/helm/memcached-operator/config/manager/manager.yaml
index 6dde7ee4..cb2ea1ba 100644
--- a/testdata/helm/memcached-operator/config/manager/manager.yaml
+++ b/testdata/helm/memcached-operator/config/manager/manager.yaml
@@ -46,8 +46,8 @@ spec:
           periodSeconds: 10
         resources:
           limits:
-            cpu: 100m
-            memory: 90Mi
+            cpu: 200m
+            memory: 100Mi
           requests:
             cpu: 100m
             memory: 60Mi
diff --git a/testdata/helm/memcached-operator/config/rbac/auth_proxy_service.yaml b/testdata/helm/memcached-operator/config/rbac/auth_proxy_service.yaml
index 6cf656be..71f17972 100644
--- a/testdata/helm/memcached-operator/config/rbac/auth_proxy_service.yaml
+++ b/testdata/helm/memcached-operator/config/rbac/auth_proxy_service.yaml
@@ -9,6 +9,7 @@ spec:
   ports:
   - name: https
     port: 8443
+    protocol: TCP
     targetPort: https
   selector:
     control-plane: controller-manager
diff --git a/testdata/helm/memcached-operator/config/scorecard/patches/basic.config.yaml b/testdata/helm/memcached-operator/config/scorecard/patches/basic.config.yaml
index ab07c06f..f3f0f34d 100644
--- a/testdata/helm/memcached-operator/config/scorecard/patches/basic.config.yaml
+++ b/testdata/helm/memcached-operator/config/scorecard/patches/basic.config.yaml
@@ -4,7 +4,7 @@
     entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: basic
       test: basic-check-spec-test
diff --git a/testdata/helm/memcached-operator/config/scorecard/patches/olm.config.yaml b/testdata/helm/memcached-operator/config/scorecard/patches/olm.config.yaml
index b1ba10d6..327800b2 100644
--- a/testdata/helm/memcached-operator/config/scorecard/patches/olm.config.yaml
+++ b/testdata/helm/memcached-operator/config/scorecard/patches/olm.config.yaml
@@ -4,7 +4,7 @@
     entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-bundle-validation-test
@@ -14,7 +14,7 @@
     entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-validation-test
@@ -24,7 +24,7 @@
     entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-crds-have-resources-test
@@ -34,7 +34,7 @@
     entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-spec-descriptors-test
@@ -44,7 +44,7 @@
     entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.10.0
+    image: quay.io/operator-framework/scorecard-test:v1.11.0
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/website/content/en/docs/olm-integration/testing-deployment.md b/website/content/en/docs/olm-integration/testing-deployment.md
index d69eab15..83141766 100644
--- a/website/content/en/docs/olm-integration/testing-deployment.md
+++ b/website/content/en/docs/olm-integration/testing-deployment.md
@@ -174,6 +174,6 @@ Let's look at the anatomy of the `cleanup` configuration model:
 [csv-install-modes]:https://github.com/operator-framework/operator-lifecycle-manager/blob/master/doc/design/building-your-csv.md#operator-metadata
 [cli-olm-install]:/docs/cli/operator-sdk_olm_install
 [cli-olm-status]:/docs/cli/operator-sdk_olm_status
-[creating-bundles]:/docs/olm-integration/quickstart-bundle/#creating-a-bundle
+[creating-bundles]:/docs/olm-integration/tutorial-bundle/#creating-a-bundle
 [add-sa-secret]:https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account
 [image-reg-config]:/docs/olm-integration/cli-overview#private-bundle-and-catalog-image-registries
diff --git a/website/content/en/docs/testing-operators/scorecard/custom-tests.md b/website/content/en/docs/testing-operators/scorecard/custom-tests.md
index 0a72542f..76e60ca4 100644
--- a/website/content/en/docs/testing-operators/scorecard/custom-tests.md
+++ b/website/content/en/docs/testing-operators/scorecard/custom-tests.md
@@ -242,6 +242,16 @@ The `--skip-cleanup` flag can be used when executing the `operator-sdk scorecard
 This is useful when debugging or writing new tests so that you can view
 the test logs or the pod manifests.
 
+### Storing scorecard test output
+
+The `--test-output` flag can be used when executing the `operator-sdk scorecard` command with a config specifying [output persistence][storage] to store the output of the scorecard tests in a specific directory. Any persistent volume data will be stored in the specified local directory upon completion of the scorecard tests.
+
+```console
+$ operator-sdk scorecard ./bundle --test-output=/mytestoutput
+```
+
+**Note**: By default, the gathered test output will be stored in `$(pwd)/test-output`.
+
 ### Scorecard initContainer
 
 The scorecard inserts an `initContainer` into the test pods it creates. The
@@ -301,3 +311,4 @@ connection to invoke the Kube API.
 [sample_makefile]: https://github.com/operator-framework/operator-sdk/blob/09c3aa14625965af9f22f513cd5c891471dbded2/Makefile
 [kustomize-patchJson6902]: https://kubernetes-sigs.github.io/kustomize/api-reference/kustomization/patchesjson6902/
 [testresults]:https://github.com/operator-framework/api/blob/333d064/pkg/apis/scorecard/v1alpha3/test_types.go#L35
+[storage]:https://pkg.go.dev/github.com/operator-framework/api@v0.10.4/pkg/apis/scorecard/v1alpha3#Storage