Repository: operator-framework/operator-sdk
Pull request #4717 (2021-04-05 16:41:45)

doc: add operator best practices
**Description**

Add the content from [community-operators](https://github.com/operator-framework/community-operators/blob/master/docs/best-practices.md) to SDK docs. Note that this comment will be removed from there and we will link the SDK docs instead of. 

See the preview : https://deploy-preview-4717--operator-sdk.netlify.app/docs/best-practices/best-practices/

**Motivation**

Centralize here the docs as requested by @dmesser 

camilamacedo86: Hi @marc-obrien, 

All suggestions addressed. Thank you. 
Could we get this one merged before the release for we are able to move forward with https://github.com/operator-framework/community-operators/pull/3491?

c/c @jmrodri 
 
openshift-ci-robot: New changes are detected. LGTM label has been removed.
camilamacedo86: All done @varshaprasad96. 
Thank you.   

Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..85bd4820
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,26 @@
+version: 2
+updates:
+  - package-ecosystem: docker
+    directory: "/images/ansible-operator"
+    schedule:
+      interval: daily
+  - package-ecosystem: docker
+    directory: "/images/custom-scorecard-tests"
+    schedule:
+      interval: daily
+  - package-ecosystem: docker
+    directory: "/images/helm-operator"
+    schedule:
+      interval: daily
+  - package-ecosystem: docker
+    directory: "/images/operator-sdk"
+    schedule:
+      interval: daily
+  - package-ecosystem: docker
+    directory: "/images/scorecard-test"
+    schedule:
+      interval: daily
+  - package-ecosystem: docker
+    directory: "/images/scorecard-test-kuttl"
+    schedule:
+      interval: daily
diff --git a/.github/workflows/deploy-manual.yml b/.github/workflows/deploy-manual.yml
index 37132228..2a4c1881 100644
--- a/.github/workflows/deploy-manual.yml
+++ b/.github/workflows/deploy-manual.yml
@@ -51,3 +51,20 @@ jobs:
         platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x
         push: true
         tags: ${{ steps.tag.outputs.tag }}
+
+    # This change will be staged and committed in the PR pushed below.
+    # The script below will fail if no change was made.
+    - name: update ansible-operator base
+      run: |
+        set -e
+        sed -i -E 's|FROM quay\.io/operator-framework/ansible-operator-base:.+|FROM '"${{ steps.tag.outputs.tag }}"'|' images/ansible-operator/Dockerfile
+        git diff --exit-code --quiet && echo "Failed to update images/ansible-operator/Dockerfile" && exit 1
+
+    - name: create PR
+      uses: peter-evans/create-pull-request@v3
+      with:
+        title: "[auto] image(ansible-operator): bump base to ${{ steps.tag.outputs.tag }}"
+        commit-message: "[auto] image(ansible-operator): bump base to ${{ steps.tag.outputs.tag }}"
+        body: "New ansible-operator-base image built by https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
+        delete-branch: true
+        branch-suffix: -ansible-operator-base
diff --git a/Makefile b/Makefile
index f747d7c3..cafb7234 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ 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.5.0
+export IMAGE_VERSION = v1.6.1
 # 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)
diff --git a/changelog/fragments/add-componentconfig-flag.yaml b/changelog/fragments/add-componentconfig-flag.yaml
new file mode 100644
index 00000000..3bb0f39f
--- /dev/null
+++ b/changelog/fragments/add-componentconfig-flag.yaml
@@ -0,0 +1,28 @@
+entries:
+  - description: >
+      For Ansible- and Helm-based operators, add the `--config` flag, which was mistakenly
+      not added to either ansible-/helm-operator binary when file support was originally added.
+    kind: bugfix
+    migration:
+      header: Add the manager config patch to config/default/kustomization.yaml
+      body: >
+        The scaffolded `--config` flag was not added to either ansible-/helm-operator binary
+        when [config file](https://master.book.kubebuilder.io/component-config-tutorial/tutorial.html)
+        support was originally added, so does not currently work.
+        The `--config` flag supports configuration of both binaries by file;
+        this method of configuration only applies to the underlying
+        [controller manager](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Manager),
+        not the operator as a whole. To optionally configure the operator's Deployment
+        with a config file, make the following update to config/default/kustomization.yaml:
+
+        ```diff
+        # If you want your controller-manager to expose the /metrics
+        # endpoint w/o any authn/z, please comment the following line.
+        - manager_auth_proxy_patch.yaml
+        +
+        +# Mount the controller config file for loading manager configurations
+        +# through a ComponentConfig type
+        +- manager_config_patch.yaml
+        ```
+
+        This feature is opt-in: flags can be used as-is or to override config file values.
diff --git a/changelog/fragments/add-declarative.yaml b/changelog/fragments/add-declarative.yaml
deleted file mode 100644
index ceec21f0..00000000
--- a/changelog/fragments/add-declarative.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-# entries is a list of entries to include in
-# release notes and/or the migration guide
-entries:
-  - description: >
-      For Golang-based operators, added the `declarative.go/v1` plugin which customizes initialized projects with patterns from [kubernetes-sigs/kubebuilder-declarative-pattern](https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern). (e.g `operator-sdk create api --plugins=go/v3,declarative`)
-    kind: "addition"
-    breaking: false
diff --git a/changelog/fragments/add-plugins.yaml b/changelog/fragments/add-plugins.yaml
deleted file mode 100644
index fc39ab0d..00000000
--- a/changelog/fragments/add-plugins.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-# entries is a list of entries to include in
-# release notes and/or the migration guide
-entries:
-  - description: >
-      Added `kustomize.common/v1` plugin which scaffolds the a commonly used project base that leverages `kustomize`.
-    kind: "addition"
-    breaking: false
diff --git a/changelog/fragments/add_common_kb.yaml b/changelog/fragments/add_common_kb.yaml
deleted file mode 100644
index 30b40888..00000000
--- a/changelog/fragments/add_common_kb.yaml
+++ /dev/null
@@ -1,60 +0,0 @@
-# entries is a list of entries to include in
-# release notes and/or the migration guide
-entries:
-  - description: >
-      (ansible/v1,helm/v1) Add componentConfig option. For further information check [here](https://master.book.kubebuilder.io/component-config-tutorial/tutorial.html)
-    kind: "addition"
-    breaking: false
-    migration:
-      header: (ansible/v1,helm/v1) Add componentConfig option.
-      body: >
-        To add this option your project you will need to:
-        - Create the file [/config/default/manager_config_patch.yaml](https://github.com/operator-framework/operator-sdk/blob/v1.6.0/testdata/ansible/memcached-operator/config/default/manager_config_patch.yaml).
-        - Create the file [/config/default/manager_config_patch.yaml](https://github.com/operator-framework/operator-sdk/blob/v1.6.0/testdata/ansible/memcached-operator/config/manager/controller_manager_config.yaml) .
-        - Update the `config/default/kustomization.yaml` by adding:
-          ```yaml
-          # Mount the controller config file for loading manager configurations
-          # through a ComponentConfig type
-          #- manager_config_patch.yaml
-          ```
-        - Update the `config/manager/kustomization.yaml` by adding:
-          ```yaml
-          generatorOptions:
-            disableNameSuffixHash: true
-
-          configMapGenerator:
-          - files:
-            - controller_manager_config.yaml
-            name: manager-config
-          apiVersion: kustomize.config.k8s.io/v1beta1
-          kind: Kustomization
-          images:
-          - name: controller
-            newName: quay.io/example/memcached-operator
-            newTag: v0.0.1
-          ```
-  - description: >
-      (golang/v3) Not scaffold the specific webhooks config manifests by default 
-    kind: "bugfix"
-
-    # Is this a breaking change?
-    breaking: false
-  - description: >
-      (ansible/v1,helm/v1) Add rules for leader election
-    kind: "addition"
-
-    # Is this a breaking change?
-    breaking: false
-    migration:
-      header: (ansible/v1,helm/v1) Add rules for leader election.
-      body: >
-        Add the rule for the `apiGroups` `coordination.k8s.io` and the resource `leases` in config/rbac/leader_election_role.yaml:
-        ```yaml
-        rules:
-          - apiGroups:
-              - ""
-              - coordination.k8s.io
-            resources:
-              - configmaps
-              - leases
-        ```
diff --git a/changelog/fragments/alpha-config-gen.yaml b/changelog/fragments/alpha-config-gen.yaml
deleted file mode 100644
index 78676f21..00000000
--- a/changelog/fragments/alpha-config-gen.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-entries:
-  - description: >
-      Added [`alpha config-gen`](https://github.com/kubernetes-sigs/kubebuilder/tree/master/pkg/cli/alpha/config-gen),
-      a kustomize plugin to specialize configuration for kubebuilder-style projects. This feature is *alpha*
-      and subject to breaking changes.
-    kind: addition
diff --git a/changelog/fragments/ansible-2.10.7.yaml b/changelog/fragments/ansible-2.10.7.yaml
deleted file mode 100644
index ed142a4d..00000000
--- a/changelog/fragments/ansible-2.10.7.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-entries:
-  - description: >
-      Bumped ansible and ansible-base in ansible-operator-base and ansible-operator images
-      to 2.10.7 for a security fix.
-    kind: change
diff --git a/changelog/fragments/ansible-dep-bump.yaml b/changelog/fragments/ansible-dep-bump.yaml
deleted file mode 100644
index 5f5c8348..00000000
--- a/changelog/fragments/ansible-dep-bump.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-entries:
-  - description: |
-      For Ansible-based Operators: Update Python dependencies.
-        - openshift (0.11.2 -> 0.12.0)
-        - kubernetes (11.0.0 -> 12.0.1)
-        - ansible-runner (1.4.6 -> 1.4.7)
-        - ansible (2.9.15 -> 2.9.19)
-    kind: "change"
-    breaking: false
-
-  - description: |
-      (ansible/v1) Update scaffolded requirements.yml to pull in newer versions of the Ansible collections.
-        - community.kubernetes (1.1.1 -> 1.2.1)
-        - operator_sdk.util (0.1.0 -> 0.2.0)
-    kind: "change"
-    breaking: false
-    migration:
-      header: (ansible/v1) Update Ansible collections
-      body: >
-        In your requirements.yml, change the `version` field for community.kubernetes to `1.2.1`,
-        and the `version` field for `operator_sdk.util` to `0.2.0`.
diff --git a/changelog/fragments/ansible-helm-flags.yaml b/changelog/fragments/ansible-helm-flags.yaml
deleted file mode 100644
index d2defe30..00000000
--- a/changelog/fragments/ansible-helm-flags.yaml
+++ /dev/null
@@ -1,109 +0,0 @@
-# entries is a list of entries to include in
-# release notes and/or the migration guide
-entries:
-  - description: >
-      (ansible/v1, helm/v1) The flags `--enable-leader-election` and `--metrics-addr` were deprecated in favor of `--leader-elect` and `--metrics-bind-address`, respectively, to follow upstream conventions.
-
-    # kind is one of:
-    # - addition
-    # - change
-    # - deprecation
-    # - removal
-    # - bugfix
-    kind: "deprecation"
-
-    # Is this a breaking change?
-    breaking: false
-
-    # NOTE: ONLY USE `pull_request_override` WHEN ADDING THIS
-    # FILE FOR A PREVIOUSLY MERGED PULL_REQUEST!
-    #
-    # The generator auto-detects the PR number from the commit
-    # message in which this file was originally added.
-    #
-    # What is the pull request number (without the "#")?
-    # pull_request_override: 0
-
-
-    # Migration can be defined to automatically add a section to
-    # the migration guide. This is required for breaking changes.
-    migration:
-      header: (helm/v1) Replace deprecated leader election and metrics address flags
-      body: >
-        Replace deprecated flags `--enable-leader-election` and `--metrics-addr` with `--leader-elect` and `--metrics-bind-address`, respectively.
-  - description: >
-      (helm/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch.
-
-    # kind is one of:
-    # - addition
-    # - change
-    # - deprecation
-    # - removal
-    # - bugfix
-    kind: "change"
-
-    # Is this a breaking change?
-    breaking: false
-
-    # NOTE: ONLY USE `pull_request_override` WHEN ADDING THIS
-    # FILE FOR A PREVIOUSLY MERGED PULL_REQUEST!
-    #
-    # The generator auto-detects the PR number from the commit
-    # message in which this file was originally added.
-    #
-    # What is the pull request number (without the "#")?
-    # pull_request_override: 0
-
-
-    # Migration can be defined to automatically add a section to
-    # the migration guide. This is required for breaking changes.
-    migration:
-      header: (helm/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch.
-      body: >
-        Add the arg `--health-probe-bind-address=:8081` to the `config/default/manager_auth_proxy_patch.yaml`:
-        ```yaml
-        - "--health-probe-bind-address=:8081"
-        - "--metrics-bind-address=127.0.0.1:8080"
-        - "--leader-elect"
-        ```
-  - description: >
-      (ansible/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch.
-
-    # kind is one of:
-    # - addition
-    # - change
-    # - deprecation
-    # - removal
-    # - bugfix
-    kind: "change"
-
-    # Is this a breaking change?
-    breaking: false
-
-    # NOTE: ONLY USE `pull_request_override` WHEN ADDING THIS
-    # FILE FOR A PREVIOUSLY MERGED PULL_REQUEST!
-    #
-    # The generator auto-detects the PR number from the commit
-    # message in which this file was originally added.
-    #
-    # What is the pull request number (without the "#")?
-    # pull_request_override: 0
-
-
-    # Migration can be defined to automatically add a section to
-    # the migration guide. This is required for breaking changes.
-    migration:
-      header: (ansible/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch.
-      body: >
-        Add the arg `--health-probe-bind-address=:8081` to the `config/default/manager_auth_proxy_patch.yaml`:
-        ```yaml
-        - "--health-probe-bind-address=:6789"
-        - "--metrics-bind-address=127.0.0.1:6789"
-        - "--leader-elect"
-        ```
-
-
-
-
-
-
diff --git a/changelog/fragments/ansible-helm-makefile-help.yaml b/changelog/fragments/ansible-helm-makefile-help.yaml
deleted file mode 100644
index fed0ba39..00000000
--- a/changelog/fragments/ansible-helm-makefile-help.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-# entries is a list of entries to include in
-# release notes and/or the migration guide
-entries:
-  - description: >
-      (helm/v1, ansible/v1) Added Makefile `help` target
-
-    # kind is one of:
-    # - addition
-    # - change
-    # - deprecation
-    # - removal
-    # - bugfix
-    kind: "addition"
-
-    # Is this a breaking change?
-    breaking: false
-
-    # NOTE: ONLY USE `pull_request_override` WHEN ADDING THIS
-    # FILE FOR A PREVIOUSLY MERGED PULL_REQUEST!
-    #
-    # The generator auto-detects the PR number from the commit
-    # message in which this file was originally added.
-    #
-    # What is the pull request number (without the "#")?
-    # pull_request_override: 0
-
-
-    # Migration can be defined to automatically add a section to
-    # the migration guide. This is required for breaking changes.
-    migration:
-      header: (helm/v1, ansible/v1) Add `help` target to Makefile.
-      body: >
-        Ansible/Helm projects now provide a Makefile `help` target, similar to a `--help` flag.
-        You can copy and paste this target from the relevant sample's Makefile
-        ([helm]((https://github.com/operator-framework/operator-sdk/blob/v1.5.0/testdata/helm/memcached-operator/Makefile), [ansible]((https://github.com/operator-framework/operator-sdk/blob/v1.5.0/testdata/ansible/memcached-operator/Makefile)).
diff --git a/changelog/fragments/ansible-helm-ssc.yaml b/changelog/fragments/ansible-helm-ssc.yaml
deleted file mode 100644
index 28e1035d..00000000
--- a/changelog/fragments/ansible-helm-ssc.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-# entries is a list of entries to include in
-# release notes and/or the migration guide
-entries:
-  - description: >
-      ansible/v1, helm/v1) Added `securityContext`'s to the manager's Deployment to disallow running as root user.
-
-    # kind is one of:
-    # - addition
-    # - change
-    # - deprecation
-    # - removal
-    # - bugfix
-    kind: "addition"
-
-    # Is this a breaking change?
-    breaking: false
-
-    # NOTE: ONLY USE `pull_request_override` WHEN ADDING THIS
-    # FILE FOR A PREVIOUSLY MERGED PULL_REQUEST!
-    #
-    # The generator auto-detects the PR number from the commit
-    # message in which this file was originally added.
-    #
-    # What is the pull request number (without the "#")?
-    # pull_request_override: 0
-
-
-    # Migration can be defined to automatically add a section to
-    # the migration guide. This is required for breaking changes.
-    migration:
-      header: (ansible/v1, helm/v1) Add `securityContext`'s to your manager's Deployment.
-      body: >
-        In `config/manager/manager.yaml`, add the following security contexts:
-        ```yaml
-        spec:
-          ...
-          template:
-            ...
-            spec:
-              securityContext:
-                runAsNonRoot: true
-              containers:
-              - name: manager
-                securityContext:
-                  allowPrivilegeEscalation: false
-        ```
diff --git a/changelog/fragments/bugfix-csv-webhooks.yaml b/changelog/fragments/bugfix-csv-webhooks.yaml
deleted file mode 100644
index 6d519f56..00000000
--- a/changelog/fragments/bugfix-csv-webhooks.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-entries:
-  - description: >
-      (manifests/v2) Added a `config/manifests` kustomize patch to remove the cert-manager
-      volume and volumeMount from manifests destined for `generate <bundle|packagemanifests>`
-    kind: bugfix
-    migration:
-      header: (manifests/v2) Add a kustomize patch to remove the cert-manager volume/volumeMount from your CSV
-      body: >
-        OLM does [not yet support cert-manager](https://olm.operatorframework.io/docs/advanced-tasks/adding-admission-and-conversion-webhooks/#certificate-authority-requirements),
-        so a JSON patch was added to remove this volume and mount such that
-        OLM can itself create and manage certs for your Operator.
-
-        In `config/manifests/kustomization.yaml`, add the following:
-
-        ```yaml
-        patchesJson6902:
-        - target:
-            group: apps
-            version: v1
-            kind: Deployment
-            name: controller-manager
-            namespace: system
-          patch: |-
-            # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs.
-            # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment.
-            - op: remove
-              path: /spec/template/spec/containers/1/volumeMounts/0
-            # Remove the "cert" volume, since OLM will create and mount a set of certs.
-            # Update the indices in this path if adding or removing volumes in the manager's Deployment.
-            - op: remove
-              path: /spec/template/spec/volumes/0
-        ```
diff --git a/changelog/fragments/ca-secret-name-run-bundle.yaml b/changelog/fragments/ca-secret-name-run-bundle.yaml
deleted file mode 100644
index 222abf84..00000000
--- a/changelog/fragments/ca-secret-name-run-bundle.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-entries:
-  - description: >
-      Added `--ca-secret-name` to `run bundle` and `run bundle-upgrade` to configure
-      the registry Pod with an in-cluster certificate Secret to use TLS with a private registry.
-    kind: addition
diff --git a/changelog/fragments/change-scImagePolicy.yaml b/changelog/fragments/change-scImagePolicy.yaml
new file mode 100644
index 00000000..57bab779
--- /dev/null
+++ b/changelog/fragments/change-scImagePolicy.yaml
@@ -0,0 +1,5 @@
+entries:
+  - description: >
+      "Changed default image Pull Policy to Always for scorecard"
+    kind: change
+    breaking: false
diff --git a/changelog/fragments/helm-handle-list-kind.yaml b/changelog/fragments/helm-handle-list-kind.yaml
deleted file mode 100644
index 6192990f..00000000
--- a/changelog/fragments/helm-handle-list-kind.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-# entries is a list of entries to include in
-# release notes and/or the migration guide
-entries:
-  - description: >
-      For Helm-based operators, fixes handling of `kind: List` whereby the 
-      operator fails when trying to set watch on the object. The fix now sets
-      the watch on the objects of the list instead. 
-
-    kind: "bugfix"
-
-    breaking: false
diff --git a/changelog/fragments/kubebuilder-646f742.yaml b/changelog/fragments/kubebuilder-646f742.yaml
deleted file mode 100644
index adfe352b..00000000
--- a/changelog/fragments/kubebuilder-646f742.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-entries:
-  - description: >
-      (go/v2, go/v3, ansible/v1, helm/v1) Fixed the Prometheus `ServiceMonitor` metrics endpoint,
-      which was not configured to be scraped correctly.
-    kind: bugfix
-    migration:
-      header: (go/v2, go/v3, ansible/v1, helm/v1) Add scheme, token, and TLS config to the Prometheus `ServiceMonitor` metrics endpoint.
-      body: >
-        The `/metrics` endpoint, while specifying the `https` port on the manager Pod, was not actually configured
-        to serve over https because no tlsConfig was set. Since kube-rbac-proxy secures this endpoint as a
-        manager sidecar, using the service account token mounted into the Pod by default corrects this problem.
-
-        The changes should look like:
-
-        ```diff
-        # config/prometheus/monitor.yaml
-
-        spec:
-           endpoints:
-             - path: /metrics
-               port: https
-        +      scheme: https
-        +      bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
-        +      tlsConfig:
-        +        insecureSkipVerify: true
-           selector:
-             matchLabels:
-               control-plane: controller-manager
-        ```
-
-        **Note:** if you have removed kube-rbac-proxy from your project, make sure to secure the `/metrics`
-        endpoint using a proper [TLS configuration](https://prometheus.io/docs/guides/tls-encryption/).
diff --git a/changelog/fragments/make-catalog-build.yaml b/changelog/fragments/make-catalog-build.yaml
deleted file mode 100644
index 02787af1..00000000
--- a/changelog/fragments/make-catalog-build.yaml
+++ /dev/null
@@ -1,72 +0,0 @@
-entries:
-  - description: >
-      (go/v2, go/v3, ansible/v1, helm/v1) Added the `opm` and `catalog-build` Makefile targets to
-      download [`opm`](https://github.com/operator-framework/operator-registry/blob/v1.15.1/docs/design/opm-tooling.md)
-      and build operator catalogs either from scratch or an existing catalog.
-    kind: addition
-    migration:
-      header: (go/v2, go/v3, ansible/v1, helm/v1) Add `opm` and `catalog-build` Makefile targets
-      body: >
-        The `opm` and `catalog-build` Makefile targets were added so operator developers who
-        want to create their own catalogs for their operator or add their operator's bundle(s) to an
-        existing catalog can do so. If this sounds like you, add the following lines to the bottom
-        of your Makefile:
-
-        ```make
-        .PHONY: opm
-        OPM = ./bin/opm
-        opm:
-        ifeq (,$(wildcard $(OPM)))
-        ifeq (,$(shell which opm 2>/dev/null))
-        	@{ \
-        	set -e ;\
-        	mkdir -p $(dir $(OPM)) ;\
-        	curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.1/$(OS)-$(ARCH)-opm ;\
-        	chmod +x $(OPM) ;\
-        	}
-        else
-        OPM = $(shell which opm)
-        endif
-        endif
-
-        BUNDLE_IMGS ?= $(BUNDLE_IMG)
-        CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION)
-        ifneq ($(origin CATALOG_BASE_IMG), undefined)
-        FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG)
-        endif
-        .PHONY: catalog-build
-        catalog-build: opm
-        	$(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)
-
-        # Push the catalog image.
-        .PHONY: catalog-push
-        catalog-push:
-        	$(MAKE) docker-push IMG=$(CATALOG_IMG)
-        ```
-
-        If updating a Go operator project, additionally add the following Makefile variables:
-
-        ```make
-        OS = $(shell go env GOOS)
-        ARCH = $(shell go env GOARCH)
-        ```
-  - description: >
-      (go/v2, go/v3, ansible/v1, helm/v1) Changed `BUNDLE_IMG` and added `IMAGE_TAG_BASE` Makefile variables
-      to allow one line bundle and catalog image builds.
-    kind: change
-    migration:
-      header: (go/v2, go/v3, ansible/v1, helm/v1) Changed `BUNDLE_IMG` and added `IMAGE_TAG_BASE` Makefile variables
-      body: >
-        The following Makefile changes were made to allow `make bundle-build bundle-push catalog-build catalog-push`
-        and encode image repo/namespace information in the Makefile by default:
-
-        ```diff
-        +IMAGE_TAG_BASE ?= <registry>/<operator name>
-        +
-        -BUNDLE_IMG ?= controller-bundle:$(VERSION)
-        +BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
-        ```
-
-        For example, if `IMAGE_TAG_BASE ?= foo/bar-operator` then running `make bundle-build bundle-push catalog-build catalog-push`
-        would build `foo/bar-operator-bundle:v0.0.1` and `foo/bar-operator-catalog:v0.0.1` then push them to the
-        `docker.io/foo` namespaced registry.
diff --git a/changelog/fragments/mark-variables-unsafe.yaml b/changelog/fragments/mark-variables-unsafe.yaml
deleted file mode 100644
index 933b6266..00000000
--- a/changelog/fragments/mark-variables-unsafe.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-entries:
-  - description: >
-      Mark the input variables from custom resources as unsafe by default in Ansible operators. 
-    kind: bugfix
-    breaking: false
diff --git a/changelog/fragments/optional-flags-cleanup.yaml b/changelog/fragments/optional-flags-cleanup.yaml
deleted file mode 100644
index 67f03584..00000000
--- a/changelog/fragments/optional-flags-cleanup.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-entries:
-  - description: >
-      Added new optional flags `--delete-all`, `--delete-crds` and `--delete-operator-groups` to the cleanup command
-    kind: "addition"
-    breaking: false
diff --git a/changelog/fragments/pin-ansible-galaxy-collections.yaml b/changelog/fragments/pin-ansible-galaxy-collections.yaml
deleted file mode 100644
index 3f4cadf7..00000000
--- a/changelog/fragments/pin-ansible-galaxy-collections.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-# entries is a list of entries to include in
-# release notes and/or the migration guide
-entries:
-  - description: >
-      For Ansible-based operators, collections as main dependencies for the operator installed with ansible-galaxy are
-      pinned to specific versions to prevent hard to track bugs.
-
-    kind: change
-
-    # Is this a breaking change?
-    breaking: false
diff --git a/changelog/fragments/run-bundle-with-secret.yaml b/changelog/fragments/run-bundle-with-secret.yaml
deleted file mode 100644
index 9ecb4f26..00000000
--- a/changelog/fragments/run-bundle-with-secret.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-entries:
-  - description: >
-      Added `--service-account` to `run bundle` and `run bundle-upgrade` to bind
-      registry objects to a non-default service account.
-    kind: addition
-  - description: >
-      Added `--pull-secret-name` to `run bundle` and `run bundle-upgrade` to configure
-      the registry Pod with an in-cluster docker config Secret
-      to pull bundle images from private registries.
-    kind: addition
diff --git a/changelog/fragments/service_account.yaml b/changelog/fragments/service_account.yaml
deleted file mode 100644
index a593b6b8..00000000
--- a/changelog/fragments/service_account.yaml
+++ /dev/null
@@ -1,86 +0,0 @@
-# entries is a list of entries to include in
-# release notes and/or the migration guide
-entries:
-    - description: >
-          (ansible/v1, helm/v1) Create and bind to a non-default service account ([kubebuilder#2070](https://github.com/kubernetes-sigs/kubebuilder/pull/2070))
-      kind: "addition"
-      breaking: false
-
-        # NOTE: ONLY USE `pull_request_override` WHEN ADDING THIS
-        # FILE FOR A PREVIOUSLY MERGED PULL_REQUEST!
-        #
-        # The generator auto-detects the PR number from the commit
-        # message in which this file was originally added.
-        #
-        # What is the pull request number (without the "#")?
-        # pull_request_override: 0
-
-
-        # Migration can be defined to automatically add a section to
-        # the migration guide. This is required for breaking changes.
-      migration:
-          header: (ansible/v1, helm/v1) Add a `system:controller-manager` ServiceAccount to your project.
-          body: >
-              A non-default ServiceAccount `controller-manager` is scaffolded on `operator-sdk init`,
-              to improve security for operators installed in shared namespaces. To add this ServiceAccount
-              to your project, do the following:
-              ```sh
-              # Create the ServiceAccount.
-              cat <<EOF > config/rbac/service_account.yaml
-              apiVersion: v1
-              kind: ServiceAccount
-              metadata:
-                name: controller-manager
-                namespace: system
-              EOF
-              # Add it to the list of RBAC resources.
-              echo "- service_account.yaml" >> config/rbac/kustomization.yaml
-              # Update all RoleBinding and ClusterRoleBinding subjects that reference the operator's ServiceAccount.
-              find config/rbac -name *_binding.yaml -exec sed -i -E 's/  name: default/  name: controller-manager/g' {} \;
-              # Add the ServiceAccount name to the manager Deployment's spec.template.spec.serviceAccountName.
-              sed -i -E 's/([ ]+)(terminationGracePeriodSeconds:)/\1serviceAccountName: controller-manager\n\1\2/g' config/manager/manager.yaml
-              ```
-              The changes should look like:
-              ```diff
-              # config/manager/manager.yaml
-                         requests:
-                           cpu: 100m
-                           memory: 20Mi
-              +      serviceAccountName: controller-manager
-                     terminationGracePeriodSeconds: 10
-              # config/rbac/auth_proxy_role_binding.yaml
-                 name: proxy-role
-               subjects:
-               - kind: ServiceAccount
-              -  name: default
-              +  name: controller-manager
-                 namespace: system
-              # config/rbac/kustomization.yaml
-               resources:
-              +- service_account.yaml
-               - role.yaml
-               - role_binding.yaml
-               - leader_election_role.yaml
-              # config/rbac/leader_election_role_binding.yaml
-                 name: leader-election-role
-               subjects:
-               - kind: ServiceAccount
-              -  name: default
-              +  name: controller-manager
-                 namespace: system
-              # config/rbac/role_binding.yaml
-                 name: manager-role
-               subjects:
-               - kind: ServiceAccount
-              -  name: default
-              +  name: controller-manager
-                 namespace: system
-              # config/rbac/service_account.yaml
-              +apiVersion: v1
-              +kind: ServiceAccount
-              +metadata:
-              +  name: controller-manager
-              +  namespace: system
-              ```
-
-
diff --git a/changelog/fragments/update-ansible-collections.yaml b/changelog/fragments/update-ansible-collections.yaml
deleted file mode 100644
index 18f59e50..00000000
--- a/changelog/fragments/update-ansible-collections.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-entries:
-  - description: >
-      Update community Kubernetes Ansible collection to version 1.1.1
-    kind: "change"
-    breaking: false
diff --git a/changelog/fragments/urllib3-1.26.4.yaml b/changelog/fragments/urllib3-1.26.4.yaml
deleted file mode 100644
index 7bfdf21c..00000000
--- a/changelog/fragments/urllib3-1.26.4.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-entries:
-  - description: >
-      Bumped urllib3 in ansible-operator-base and ansible-operator images to 1.26.4
-      for a security fix.
-    kind: change
diff --git a/changelog/fragments/use-pipenv-for-python-package-installation.yaml b/changelog/fragments/use-pipenv-for-python-package-installation.yaml
deleted file mode 100644
index 8d33c195..00000000
--- a/changelog/fragments/use-pipenv-for-python-package-installation.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-entries:
-  - description: >
-      For Ansible-based operators, Python package installation in the Docker
-      image is delegated to a pipenv managed Pipfile and Pipfile.lock, pinning
-      both the main installed packages and their subdependencies. This prevents
-      installing conflicting (sub)dependencies
-
-    kind: change
-
-    breaking: false
-
-  - description: >
-      For Ansible-based operators, the Python following packages were updated:
-      `openshift` was updated to 0.11.2, `ansible-runner` was updated to 1.4.6.
-
-    kind: change
-
-    breaking: false
diff --git a/changelog/generated/v1.6.0.md b/changelog/generated/v1.6.0.md
new file mode 100644
index 00000000..3d416a28
--- /dev/null
+++ b/changelog/generated/v1.6.0.md
@@ -0,0 +1,49 @@
+## v1.6.0
+
+### Additions
+
+- For Golang-based operators, added the `declarative.go/v1` plugin which customizes initialized projects with patterns from [kubernetes-sigs/kubebuilder-declarative-pattern](https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern). (e.g `operator-sdk create api --plugins=go/v3,declarative`). ([#4731](https://github.com/operator-framework/operator-sdk/pull/4731))
+- Added `kustomize.common/v1` plugin which scaffolds the a commonly used project base that leverages `kustomize`. ([#4730](https://github.com/operator-framework/operator-sdk/pull/4730))
+- (ansible/v1, helm/v1) Added the option to configure `ansible-operator` and `helm-operator` with a [component config](https://master.book.kubebuilder.io/component-config-tutorial/tutorial.html). ([#4701](https://github.com/operator-framework/operator-sdk/pull/4701))
+- (ansible/v1, helm/v1) Add rules for leader election. ([#4701](https://github.com/operator-framework/operator-sdk/pull/4701))
+- Added [`alpha config-gen`](https://github.com/kubernetes-sigs/kubebuilder/tree/master/pkg/cli/alpha/config-gen), a kustomize plugin to specialize configuration for kubebuilder-style projects. This feature is *alpha* and subject to breaking changes. ([#4670](https://github.com/operator-framework/operator-sdk/pull/4670))
+- (helm/v1, ansible/v1) Added Makefile `help` target. ([#4660](https://github.com/operator-framework/operator-sdk/pull/4660))
+- (ansible/v1, helm/v1) Added `securityContext`'s to the manager's Deployment to disallow running as root user. ([#4655](https://github.com/operator-framework/operator-sdk/pull/4655))
+- Added `--ca-secret-name` to `run bundle` and `run bundle-upgrade` to configure the registry Pod with an in-cluster certificate Secret to use TLS with a private registry. ([#4703](https://github.com/operator-framework/operator-sdk/pull/4703))
+- For Helm based-operators, added annotation `helm.sdk.operatorframework.io/uninstall-wait: "true"` to allow all resources to be deleted before removing the custom resource's finalizer. ([#4487](https://github.com/operator-framework/operator-sdk/pull/4487))
+- (go/v2, go/v3, ansible/v1, helm/v1) Added the `opm` and `catalog-build` Makefile targets to download [`opm`](https://github.com/operator-framework/operator-registry/blob/v1.15.1/docs/design/opm-tooling.md) and build operator catalogs either from scratch or an existing catalog. ([#4406](https://github.com/operator-framework/operator-sdk/pull/4406))
+- Added new optional flags `--delete-all`, `--delete-crds` and `--delete-operator-groups` to the cleanup command. ([#4619](https://github.com/operator-framework/operator-sdk/pull/4619))
+- Added `--service-account` to `run bundle` and `run bundle-upgrade` to bind registry objects to a non-default service account. ([#4694](https://github.com/operator-framework/operator-sdk/pull/4694))
+- Added `--pull-secret-name` to `run bundle` and `run bundle-upgrade` to configure the registry Pod with an in-cluster docker config Secret to pull bundle images from private registries. ([#4694](https://github.com/operator-framework/operator-sdk/pull/4694))
+- (ansible/v1, helm/v1) Create and bind controller-manager to a non-default service account ([kubebuilder#2070](https://github.com/kubernetes-sigs/kubebuilder/pull/2070)). ([#4653](https://github.com/operator-framework/operator-sdk/pull/4653))
+
+### Changes
+
+- For Ansible-based Operators: Update Python dependencies.
+  - openshift (0.11.2 -> 0.12.0)
+  - kubernetes (11.0.0 -> 12.0.1)
+  - ansible-runner (1.4.6 -> 1.4.7)
+  - ansible (2.9.15 -> 2.9.19). ([#4734](https://github.com/operator-framework/operator-sdk/pull/4734))
+- (ansible/v1) Update scaffolded requirements.yml to pull in newer versions of the Ansible collections.
+  - community.kubernetes (1.1.1 -> 1.2.1)
+  - operator_sdk.util (0.1.0 -> 0.2.0). ([#4734](https://github.com/operator-framework/operator-sdk/pull/4734))
+- (helm/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch. ([#4654](https://github.com/operator-framework/operator-sdk/pull/4654))
+- (ansible/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch. ([#4654](https://github.com/operator-framework/operator-sdk/pull/4654))
+- (go/v2, go/v3, ansible/v1, helm/v1) Changed `BUNDLE_IMG` and added `IMAGE_TAG_BASE` Makefile variables to allow one line bundle and catalog image builds. ([#4406](https://github.com/operator-framework/operator-sdk/pull/4406))
+- For Ansible-based operators, collections as main dependencies for the operator installed with ansible-galaxy are pinned to specific versions to prevent hard to track bugs. ([#4529](https://github.com/operator-framework/operator-sdk/pull/4529))
+- Update community Kubernetes Ansible collection to version 1.1.1. ([#4594](https://github.com/operator-framework/operator-sdk/pull/4594))
+- Bumped urllib3 in ansible-operator-base and ansible-operator images to 1.26.4 for a security fix. ([#4723](https://github.com/operator-framework/operator-sdk/pull/4723))
+- For Ansible-based operators, Python package installation in the Docker image is delegated to a pipenv managed Pipfile and Pipfile.lock, pinning both the main installed packages and their subdependencies. This prevents installing conflicting (sub)dependencies. ([#4543](https://github.com/operator-framework/operator-sdk/pull/4543))
+- For Ansible-based operators, Python package installation in the Docker image is delegated to a pipenv managed Pipfile and Pipfile.lock, pinning both the main installed packages and their subdependencies. This prevents installing conflicting (sub)dependencies. ([#4543](https://github.com/operator-framework/operator-sdk/pull/4543))
+
+### Deprecations
+
+- (ansible/v1, helm/v1) The flags `--enable-leader-election` and `--metrics-addr` were deprecated in favor of `--leader-elect` and `--metrics-bind-address`, respectively, to follow upstream conventions. ([#4654](https://github.com/operator-framework/operator-sdk/pull/4654))
+
+### Bug Fixes
+
+- (go/v3) Create webhook manifests in `config/` on running `create webhook`, not `init`. ([#4701](https://github.com/operator-framework/operator-sdk/pull/4701))
+- (manifests/v2) Added a `config/manifests` kustomize patch to remove the cert-manager volume and volumeMount from manifests destined for `generate <bundle|packagemanifests>`. ([#4623](https://github.com/operator-framework/operator-sdk/pull/4623))
+- For Helm-based operators, fixed handling of `kind: List` whereby the operator fails when trying to set watch on the object. Watches are created for objects in the list instead. ([#4682](https://github.com/operator-framework/operator-sdk/pull/4682))
+- (go/v2, go/v3, ansible/v1, helm/v1) Fixed the Prometheus `ServiceMonitor` metrics endpoint, which was not configured to be scraped correctly. ([#4680](https://github.com/operator-framework/operator-sdk/pull/4680))
+- In Ansible-based operators, mark the input variables from custom resources as unsafe by default. ([#4566](https://github.com/operator-framework/operator-sdk/pull/4566))
diff --git a/changelog/generated/v1.6.1.md b/changelog/generated/v1.6.1.md
new file mode 100644
index 00000000..68cd7adc
--- /dev/null
+++ b/changelog/generated/v1.6.1.md
@@ -0,0 +1,49 @@
+## v1.6.1
+
+### Additions
+
+- For Golang-based operators, added the `declarative.go/v1` plugin which customizes initialized projects with patterns from [kubernetes-sigs/kubebuilder-declarative-pattern](https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern). (e.g `operator-sdk create api --plugins=go/v3,declarative`). ([#4731](https://github.com/operator-framework/operator-sdk/pull/4731))
+- Added `kustomize.common/v1` plugin which scaffolds the a commonly used project base that leverages `kustomize`. ([#4730](https://github.com/operator-framework/operator-sdk/pull/4730))
+- (ansible/v1, helm/v1) Added the option to configure `ansible-operator` and `helm-operator` with a [component config](https://master.book.kubebuilder.io/component-config-tutorial/tutorial.html). ([#4701](https://github.com/operator-framework/operator-sdk/pull/4701))
+- (ansible/v1, helm/v1) Add rules for leader election. ([#4701](https://github.com/operator-framework/operator-sdk/pull/4701))
+- Added [`alpha config-gen`](https://github.com/kubernetes-sigs/kubebuilder/tree/master/pkg/cli/alpha/config-gen), a kustomize plugin to specialize configuration for kubebuilder-style projects. This feature is *alpha* and subject to breaking changes. ([#4670](https://github.com/operator-framework/operator-sdk/pull/4670))
+- (helm/v1, ansible/v1) Added Makefile `help` target. ([#4660](https://github.com/operator-framework/operator-sdk/pull/4660))
+- (ansible/v1, helm/v1) Added `securityContext`'s to the manager's Deployment to disallow running as root user. ([#4655](https://github.com/operator-framework/operator-sdk/pull/4655))
+- Added `--ca-secret-name` to `run bundle` and `run bundle-upgrade` to configure the registry Pod with an in-cluster certificate Secret to use TLS with a private registry. ([#4703](https://github.com/operator-framework/operator-sdk/pull/4703))
+- For Helm based-operators, added annotation `helm.sdk.operatorframework.io/uninstall-wait: "true"` to allow all resources to be deleted before removing the custom resource's finalizer. ([#4487](https://github.com/operator-framework/operator-sdk/pull/4487))
+- (go/v2, go/v3, ansible/v1, helm/v1) Added the `opm` and `catalog-build` Makefile targets to download [`opm`](https://github.com/operator-framework/operator-registry/blob/v1.15.1/docs/design/opm-tooling.md) and build operator catalogs either from scratch or an existing catalog. ([#4406](https://github.com/operator-framework/operator-sdk/pull/4406))
+- Added new optional flags `--delete-all`, `--delete-crds` and `--delete-operator-groups` to the cleanup command. ([#4619](https://github.com/operator-framework/operator-sdk/pull/4619))
+- Added `--service-account` to `run bundle` and `run bundle-upgrade` to bind registry objects to a non-default service account. ([#4694](https://github.com/operator-framework/operator-sdk/pull/4694))
+- Added `--pull-secret-name` to `run bundle` and `run bundle-upgrade` to configure the registry Pod with an in-cluster docker config Secret to pull bundle images from private registries. ([#4694](https://github.com/operator-framework/operator-sdk/pull/4694))
+- (ansible/v1, helm/v1) Create and bind controller-manager to a non-default service account ([kubebuilder#2070](https://github.com/kubernetes-sigs/kubebuilder/pull/2070)). ([#4653](https://github.com/operator-framework/operator-sdk/pull/4653))
+
+### Changes
+
+- For Ansible-based Operators: Update Python dependencies.
+  - openshift (0.11.2 -> 0.12.0)
+  - kubernetes (11.0.0 -> 12.0.1)
+  - ansible-runner (1.4.6 -> 1.4.7)
+  - ansible (2.9.15 -> 2.9.19). ([#4734](https://github.com/operator-framework/operator-sdk/pull/4734))
+- (ansible/v1) Update scaffolded requirements.yml to pull in newer versions of the Ansible collections.
+  - community.kubernetes (1.1.1 -> 1.2.1)
+  - operator_sdk.util (0.1.0 -> 0.2.0). ([#4734](https://github.com/operator-framework/operator-sdk/pull/4734))
+- (helm/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch. ([#4654](https://github.com/operator-framework/operator-sdk/pull/4654))
+- (ansible/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch. ([#4654](https://github.com/operator-framework/operator-sdk/pull/4654))
+- (go/v2, go/v3, ansible/v1, helm/v1) Changed `BUNDLE_IMG` and added `IMAGE_TAG_BASE` Makefile variables to allow one line bundle and catalog image builds. ([#4406](https://github.com/operator-framework/operator-sdk/pull/4406))
+- For Ansible-based operators, collections as main dependencies for the operator installed with ansible-galaxy are pinned to specific versions to prevent hard to track bugs. ([#4529](https://github.com/operator-framework/operator-sdk/pull/4529))
+- Update community Kubernetes Ansible collection to version 1.1.1. ([#4594](https://github.com/operator-framework/operator-sdk/pull/4594))
+- Bumped urllib3 in ansible-operator-base and ansible-operator images to 1.26.4 for a security fix. ([#4723](https://github.com/operator-framework/operator-sdk/pull/4723))
+- For Ansible-based operators, Python package installation in the Docker image is delegated to a pipenv managed Pipfile and Pipfile.lock, pinning both the main installed packages and their subdependencies. This prevents installing conflicting (sub)dependencies. ([#4543](https://github.com/operator-framework/operator-sdk/pull/4543))
+- For Ansible-based operators, Python package installation in the Docker image is delegated to a pipenv managed Pipfile and Pipfile.lock, pinning both the main installed packages and their subdependencies. This prevents installing conflicting (sub)dependencies. ([#4543](https://github.com/operator-framework/operator-sdk/pull/4543))
+
+### Deprecations
+
+- (ansible/v1, helm/v1) The flags `--enable-leader-election` and `--metrics-addr` were deprecated in favor of `--leader-elect` and `--metrics-bind-address`, respectively, to follow upstream conventions. ([#4654](https://github.com/operator-framework/operator-sdk/pull/4654))
+
+### Bug Fixes
+
+- (go/v3) Create webhook manifests in `config/` on running `create webhook`, not `init`. ([#4701](https://github.com/operator-framework/operator-sdk/pull/4701))
+- (manifests/v2) Added a `config/manifests` kustomize patch to remove the cert-manager volume and volumeMount from manifests destined for `generate <bundle|packagemanifests>`. ([#4623](https://github.com/operator-framework/operator-sdk/pull/4623))
+- For Helm-based operators, fixed handling of `kind: List` whereby the operator fails when trying to set watch on the object. Watches are created for objects in the list instead. ([#4682](https://github.com/operator-framework/operator-sdk/pull/4682))
+- (go/v2, go/v3, ansible/v1, helm/v1) Fixed the Prometheus `ServiceMonitor` metrics endpoint, which was not configured to be scraped correctly. ([#4680](https://github.com/operator-framework/operator-sdk/pull/4680))
+- In Ansible-based operators, mark the input variables from custom resources as unsafe by default. ([#4566](https://github.com/operator-framework/operator-sdk/pull/4566))
\ No newline at end of file
diff --git a/go.mod b/go.mod
index f74f0e6f..126b89aa 100644
--- a/go.mod
+++ b/go.mod
@@ -11,7 +11,7 @@ require (
 	github.com/markbates/inflect v1.0.4
 	github.com/onsi/ginkgo v1.15.0
 	github.com/onsi/gomega v1.10.5
-	github.com/operator-framework/api v0.5.3
+	github.com/operator-framework/api v0.8.0
 	github.com/operator-framework/operator-lib v0.4.0
 	github.com/operator-framework/operator-registry v1.15.3
 	github.com/prometheus/client_golang v1.7.1
@@ -34,9 +34,9 @@ require (
 	k8s.io/client-go v0.20.2
 	k8s.io/kubectl v0.20.2
 	rsc.io/letsencrypt v0.0.3 // indirect
-	sigs.k8s.io/controller-runtime v0.8.2
+	sigs.k8s.io/controller-runtime v0.8.3
 	sigs.k8s.io/controller-tools v0.5.0
-	sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210407152553-d5e70a57ccb0
+	sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210415223022-9f90d1a3c3cc
 	sigs.k8s.io/yaml v1.2.0
 )
 
diff --git a/go.sum b/go.sum
index d615f8dd..633ff930 100644
--- a/go.sum
+++ b/go.sum
@@ -802,8 +802,8 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
 github.com/operator-framework/api v0.3.22/go.mod h1:GVNiB6AQucwdZz3ZFXNv9HtcLOzcFnr6O/QldzKG93g=
 github.com/operator-framework/api v0.5.2 h1:NLgOoi70+iyz4vVJeeJUKaFT8wZaCbCHzS1eExCqX7A=
 github.com/operator-framework/api v0.5.2/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
-github.com/operator-framework/api v0.5.3 h1:1RDw2FbuEDtSC7ONKLKgAxtq+iRsV7rRTumD4RLQfSI=
-github.com/operator-framework/api v0.5.3/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
+github.com/operator-framework/api v0.8.0 h1:S1R5BaPKeZoACbu0913mPnG33s7GOA2VrT9gvOeBjbU=
+github.com/operator-framework/api v0.8.0/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
 github.com/operator-framework/operator-lib v0.4.0 h1:g7tGRo+FikHgFZDmRdHkOxyTv3sViI+Ujiqbfd9Tfsk=
 github.com/operator-framework/operator-lib v0.4.0/go.mod h1:kOjV7h01DCSw3RZAqYdHyHyVwuJL8hvG53tSZoDZfsQ=
 github.com/operator-framework/operator-registry v1.15.3 h1:C+u+zjDh6yQAKN+DbUvPeLjojZtJftvp/J28rRqiWWU=
@@ -1550,15 +1550,15 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyz
 sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
 sigs.k8s.io/controller-runtime v0.8.0 h1:s0dYdo7lQgJiAf+alP82PRwbz+oAqL3oSyMQ18XRDOc=
 sigs.k8s.io/controller-runtime v0.8.0/go.mod h1:v9Lbj5oX443uR7GXYY46E0EE2o7k2YxQ58GxVNeXSW4=
-sigs.k8s.io/controller-runtime v0.8.2 h1:SBWmI0b3uzMIUD/BIXWNegrCeZmPJ503pOtwxY0LPHM=
-sigs.k8s.io/controller-runtime v0.8.2/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU=
+sigs.k8s.io/controller-runtime v0.8.3 h1:GMHvzjTmaWHQB8HadW+dIvBoJuLvZObYJ5YoZruPRao=
+sigs.k8s.io/controller-runtime v0.8.3/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU=
 sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
 sigs.k8s.io/controller-tools v0.4.1 h1:VkuV0MxlRPmRu5iTgBZU4UxUX2LiR99n3sdQGRxZF4w=
 sigs.k8s.io/controller-tools v0.4.1/go.mod h1:G9rHdZMVlBDocIxGkK3jHLWqcTMNvveypYJwrvYKjWU=
 sigs.k8s.io/controller-tools v0.5.0 h1:3u2RCwOlp0cjCALAigpOcbAf50pE+kHSdueUosrC/AE=
 sigs.k8s.io/controller-tools v0.5.0/go.mod h1:JTsstrMpxs+9BUj6eGuAaEb6SDSPTeVtUyp0jmnAM/I=
-sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210407152553-d5e70a57ccb0 h1:7xXfjzq8NfOwBEo3ZGa/+G5uABPfHIDXdoy3ERn5kxQ=
-sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210407152553-d5e70a57ccb0/go.mod h1:eVtLdWzmvL1ixDYLlVrvQe8wjpikJVoSOg5PghTk2Lw=
+sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210415223022-9f90d1a3c3cc h1:laR4sYUQabBCltK1WnyvA5Bde+f/0aiPogArXWfnBBs=
+sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210415223022-9f90d1a3c3cc/go.mod h1:KJLAKkOvgXZ2+1REJqFmoseez1tgg5Qoz0zFeJorrSo=
 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/kyaml v0.10.10 h1:caAxDDkaXZp+0kDsZVik4leFJV8LCy09PdVqpaoNeF4=
diff --git a/hack/generate/samples/internal/ansible/advanced_molecule.go b/hack/generate/samples/internal/ansible/advanced_molecule.go
index 951ef1ae..43165f77 100644
--- a/hack/generate/samples/internal/ansible/advanced_molecule.go
+++ b/hack/generate/samples/internal/ansible/advanced_molecule.go
@@ -24,7 +24,6 @@ import (
 	kbtestutils "sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
 
 	"github.com/operator-framework/operator-sdk/hack/generate/samples/internal/pkg"
-	"github.com/operator-framework/operator-sdk/internal/testutils"
 	"github.com/operator-framework/operator-sdk/internal/util"
 )
 
@@ -33,9 +32,18 @@ type AdvancedMolecule struct {
 	ctx *pkg.SampleContext
 }
 
-// NewMoleculeAnsible return a MoleculeAnsible
-func NewAdvancedMolecule(ctx *pkg.SampleContext) AdvancedMolecule {
-	return AdvancedMolecule{ctx}
+// GenerateAdvancedMoleculeSample will call all actions to create the directory and generate the sample
+// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT
+// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files
+// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext.
+func GenerateAdvancedMoleculeSample(binaryPath, samplesPath string) {
+	ctx, err := pkg.NewSampleContext(binaryPath, filepath.Join(samplesPath, "advanced-molecule-operator"),
+		"GO111MODULE=on")
+	pkg.CheckError("generating Ansible Molecule Advanced Operator context", err)
+
+	molecule := AdvancedMolecule{&ctx}
+	molecule.Prepare()
+	molecule.Run()
 }
 
 // Prepare the Context for the Memcached Ansible Sample
@@ -518,17 +526,3 @@ const originalPlaybookFragment = `---
 const inventorysampleFragment = `name: inventorytest-sample
   annotations:
     "ansible.sdk.operatorframework.io/verbosity": "0"`
-
-// GenerateMoleculeAnsibleSample will call all actions to create the directory and generate the sample
-// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT
-// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files
-// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext.
-func GenerateMoleculeAdvancedAnsibleSample(path string) {
-	ctx, err := pkg.NewSampleContext(testutils.BinaryName, filepath.Join(path, "advanced-molecule-operator"),
-		"GO111MODULE=on")
-	pkg.CheckError("generating Ansible Molecule Advanced Operator context", err)
-
-	molecule := NewAdvancedMolecule(&ctx)
-	molecule.Prepare()
-	molecule.Run()
-}
diff --git a/hack/generate/samples/internal/ansible/generate.go b/hack/generate/samples/internal/ansible/generate.go
new file mode 100644
index 00000000..2d0321e0
--- /dev/null
+++ b/hack/generate/samples/internal/ansible/generate.go
@@ -0,0 +1,23 @@
+// 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 ansible
+
+import (
+	"path/filepath"
+)
+
+func GenerateMemcachedSamples(binaryPath, rootPath string) {
+	GenerateMemcachedSample(binaryPath, filepath.Join(rootPath, "ansible"))
+}
diff --git a/hack/generate/samples/internal/ansible/memcached.go b/hack/generate/samples/internal/ansible/memcached.go
index b3263bf5..cd9a5af3 100644
--- a/hack/generate/samples/internal/ansible/memcached.go
+++ b/hack/generate/samples/internal/ansible/memcached.go
@@ -27,20 +27,29 @@ import (
 	"github.com/operator-framework/operator-sdk/internal/util"
 )
 
-// MemcachedAnsible defines the context for the sample
-type MemcachedAnsible struct {
+// Memcached defines the context for the sample
+type Memcached struct {
 	ctx *pkg.SampleContext
 }
 
-// NewMemcachedAnsible return a MemcachedAnsible
-func NewMemcachedAnsible(ctx *pkg.SampleContext) MemcachedAnsible {
-	return MemcachedAnsible{ctx}
+// GenerateMemcachedSample will call all actions to create the directory and generate the sample
+// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT
+// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files
+// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext.
+func GenerateMemcachedSample(binaryPath, samplesPath string) {
+	ctx, err := pkg.NewSampleContext(binaryPath, filepath.Join(samplesPath, "memcached-operator"),
+		"GO111MODULE=on")
+	pkg.CheckError("generating Ansible memcached context", err)
+
+	memcached := Memcached{&ctx}
+	memcached.Prepare()
+	memcached.Run()
 }
 
 // Prepare the Context for the Memcached Ansible Sample
 // Note that sample directory will be re-created and the context data for the sample
 // will be set such as the domain and GVK.
-func (ma *MemcachedAnsible) Prepare() {
+func (ma *Memcached) Prepare() {
 	log.Infof("destroying directory for memcached Ansible samples")
 	ma.ctx.Destroy()
 
@@ -56,7 +65,7 @@ func (ma *MemcachedAnsible) Prepare() {
 }
 
 // Run the steps to create the Memcached Ansible Sample
-func (ma *MemcachedAnsible) Run() {
+func (ma *Memcached) Run() {
 	log.Infof("creating the project")
 	err := ma.ctx.Init(
 		"--plugins", "ansible",
@@ -87,7 +96,7 @@ func (ma *MemcachedAnsible) Run() {
 }
 
 // addingMoleculeMockData will customize the molecule data
-func (ma *MemcachedAnsible) addingMoleculeMockData() {
+func (ma *Memcached) addingMoleculeMockData() {
 	log.Infof("adding molecule test for Ansible task")
 	moleculeTaskPath := filepath.Join(ma.ctx.Dir, "molecule", "default", "tasks",
 		fmt.Sprintf("%s_test.yml", strings.ToLower(ma.ctx.Kind)))
@@ -98,7 +107,7 @@ func (ma *MemcachedAnsible) addingMoleculeMockData() {
 }
 
 // addingAnsibleTask will add the Ansible Task and update the sample
-func (ma *MemcachedAnsible) addingAnsibleTask() {
+func (ma *Memcached) addingAnsibleTask() {
 	log.Infof("adding Ansible task and variable")
 	err := kbtestutils.InsertCode(filepath.Join(ma.ctx.Dir, "roles", strings.ToLower(ma.ctx.Kind),
 		"tasks", "main.yml"),
@@ -117,17 +126,3 @@ func (ma *MemcachedAnsible) addingAnsibleTask() {
 		"foo: bar", "size: 1")
 	pkg.CheckError("updating sample CR", err)
 }
-
-// GenerateMemcachedAnsibleSample will call all actions to create the directory and generate the sample
-// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT
-// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files
-// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext.
-func GenerateMemcachedAnsibleSample(samplesPath string) {
-	ctx, err := pkg.NewSampleContext(testutils.BinaryName, filepath.Join(samplesPath, "ansible", "memcached-operator"),
-		"GO111MODULE=on")
-	pkg.CheckError("generating Ansible memcached context", err)
-
-	memcached := NewMemcachedAnsible(&ctx)
-	memcached.Prepare()
-	memcached.Run()
-}
diff --git a/hack/generate/samples/internal/ansible/memcached_molecule.go b/hack/generate/samples/internal/ansible/memcached_molecule.go
index add57b9a..5faad2f1 100644
--- a/hack/generate/samples/internal/ansible/memcached_molecule.go
+++ b/hack/generate/samples/internal/ansible/memcached_molecule.go
@@ -24,24 +24,33 @@ import (
 	kbtestutils "sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
 
 	"github.com/operator-framework/operator-sdk/hack/generate/samples/internal/pkg"
-	"github.com/operator-framework/operator-sdk/internal/testutils"
 	"github.com/operator-framework/operator-sdk/internal/util"
 )
 
-// MoleculeAnsible defines the context for the sample
-type MoleculeAnsible struct {
-	ctx *pkg.SampleContext
+// MemcachedMolecule defines the context for the sample
+type MemcachedMolecule struct {
+	ctx  *pkg.SampleContext
+	Base Memcached
 }
 
-// NewMoleculeAnsible return a MoleculeAnsible
-func NewMoleculeAnsible(ctx *pkg.SampleContext) MoleculeAnsible {
-	return MoleculeAnsible{ctx}
+// GenerateMoleculeSample will call all actions to create the directory and generate the sample
+// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT
+// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files
+// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext.
+func GenerateMoleculeSample(binaryPath, samplesPath string) {
+	ctx, err := pkg.NewSampleContext(binaryPath, filepath.Join(samplesPath, "memcached-molecule-operator"),
+		"GO111MODULE=on")
+	pkg.CheckError("generating Ansible Moleule memcached context", err)
+
+	molecule := MemcachedMolecule{ctx: &ctx, Base: Memcached{&ctx}}
+	molecule.Prepare()
+	molecule.Run()
 }
 
 // Prepare the Context for the Memcached Ansible Sample
 // Note that sample directory will be re-created and the context data for the sample
 // will be set such as the domain and GVK.
-func (ma *MoleculeAnsible) Prepare() {
+func (ma *MemcachedMolecule) Prepare() {
 	log.Infof("destroying directory for memcached Ansible samples")
 	ma.ctx.Destroy()
 
@@ -57,9 +66,8 @@ func (ma *MoleculeAnsible) Prepare() {
 }
 
 // Run the steps to create the Memcached Ansible Sample
-func (ma *MoleculeAnsible) Run() {
-	memcached := NewMemcachedAnsible(ma.ctx)
-	memcached.Run()
+func (ma *MemcachedMolecule) Run() {
+	ma.Base.Run()
 
 	moleculeTaskPath := filepath.Join(ma.ctx.Dir, "molecule", "default", "tasks",
 		fmt.Sprintf("%s_test.yml", strings.ToLower(ma.ctx.Kind)))
@@ -163,17 +171,3 @@ func (ma *MoleculeAnsible) Run() {
 		fixmeAssert, "")
 	pkg.CheckError("replacing foo_test.yml", err)
 }
-
-// GenerateMoleculeAnsibleSample will call all actions to create the directory and generate the sample
-// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT
-// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files
-// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext.
-func GenerateMoleculeAnsibleSample(path string) {
-	ctx, err := pkg.NewSampleContext(testutils.BinaryName, filepath.Join(path, "memcached-molecule-operator"),
-		"GO111MODULE=on")
-	pkg.CheckError("generating Ansible Moleule memcached context", err)
-
-	molecule := NewMoleculeAnsible(&ctx)
-	molecule.Prepare()
-	molecule.Run()
-}
diff --git a/hack/generate/samples/internal/go/generate.go b/hack/generate/samples/internal/go/generate.go
index 2969a04e..df8895f1 100644
--- a/hack/generate/samples/internal/go/generate.go
+++ b/hack/generate/samples/internal/go/generate.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package pkg
+package golang
 
 import (
 	"path/filepath"
@@ -21,7 +21,7 @@ import (
 	golangv3 "github.com/operator-framework/operator-sdk/hack/generate/samples/internal/go/v3"
 )
 
-func GenerateMemcachedGoWithWebhooksSample(rootPath string) {
-	golangv2.GenerateMemcachedGoWithWebhooksSample(filepath.Join(rootPath, "go", "v2"))
-	golangv3.GenerateMemcachedGoWithWebhooksSample(filepath.Join(rootPath, "go", "v3"))
+func GenerateMemcachedSamples(binaryPath, rootPath string) {
+	golangv2.GenerateMemcachedSample(binaryPath, filepath.Join(rootPath, "go", "v2"))
+	golangv3.GenerateMemcachedSample(binaryPath, filepath.Join(rootPath, "go", "v3"))
 }
diff --git a/hack/generate/samples/internal/go/v2/memcached_with_webhooks.go b/hack/generate/samples/internal/go/v2/memcached_with_webhooks.go
index e6b6c8c4..110a7f31 100644
--- a/hack/generate/samples/internal/go/v2/memcached_with_webhooks.go
+++ b/hack/generate/samples/internal/go/v2/memcached_with_webhooks.go
@@ -28,20 +28,27 @@ import (
 	"github.com/operator-framework/operator-sdk/internal/util"
 )
 
-// MemcachedGoWithWebhooks defines the Memcached Sample in GO using webhooks
-type MemcachedGoWithWebhooks struct {
+// Memcached defines the Memcached Sample in GO using webhooks
+type Memcached struct {
 	ctx *pkg.SampleContext
 }
 
-// MemcachedGoWithWebhooks return a MemcachedGoWithWebhooks
-func NewMemcachedGoWithWebhooks(ctx *pkg.SampleContext) MemcachedGoWithWebhooks {
-	return MemcachedGoWithWebhooks{ctx}
+// GenerateMemcachedSample will call all actions to create the directory and generate the sample
+// Note that it should NOT be called in the e2e tests.
+func GenerateMemcachedSample(binaryPath, samplesPath string) {
+	log.Infof("starting to generate Go memcached sample with webhooks")
+	ctx, err := pkg.NewSampleContext(binaryPath, filepath.Join(samplesPath, "memcached-operator"), "GO111MODULE=on")
+	pkg.CheckError("generating Go memcached with webhooks context", err)
+
+	memcached := Memcached{&ctx}
+	memcached.Prepare()
+	memcached.Run()
 }
 
 // Prepare the Context for the Memcached with WebHooks Go Sample
 // Note that sample directory will be re-created and the context data for the sample
 // will be set such as the domain and GVK.
-func (mh *MemcachedGoWithWebhooks) Prepare() {
+func (mh *Memcached) Prepare() {
 	log.Infof("destroying directory for Memcached with Webhooks Go samples")
 	mh.ctx.Destroy()
 
@@ -57,7 +64,7 @@ func (mh *MemcachedGoWithWebhooks) Prepare() {
 }
 
 // Run the steps to create the Memcached with Webhooks Go Sample
-func (mh *MemcachedGoWithWebhooks) Run() {
+func (mh *Memcached) Run() {
 	log.Infof("creating the project")
 	err := mh.ctx.Init(
 		"--plugins", "go/v2",
@@ -108,7 +115,7 @@ func (mh *MemcachedGoWithWebhooks) Run() {
 }
 
 // uncommentDefaultKustomization will uncomment code in config/default/kustomization.yaml
-func (mh *MemcachedGoWithWebhooks) uncommentDefaultKustomization() {
+func (mh *Memcached) uncommentDefaultKustomization() {
 	var err error
 	kustomization := filepath.Join(mh.ctx.Dir, "config", "default", "kustomization.yaml")
 	log.Info("uncommenting config/default/kustomization.yaml to enable webhooks and ca injection")
@@ -159,7 +166,7 @@ func (mh *MemcachedGoWithWebhooks) uncommentDefaultKustomization() {
 }
 
 // uncommentManifestsKustomization will uncomment code in config/manifests/kustomization.yaml
-func (mh *MemcachedGoWithWebhooks) uncommentManifestsKustomization() {
+func (mh *Memcached) uncommentManifestsKustomization() {
 	var err error
 	kustomization := filepath.Join(mh.ctx.Dir, "config", "manifests", "kustomization.yaml")
 	log.Info("uncommenting config/manifests/kustomization.yaml to enable webhooks in OLM")
@@ -185,7 +192,7 @@ func (mh *MemcachedGoWithWebhooks) uncommentManifestsKustomization() {
 }
 
 // implementingWebhooks will customize the kind wekbhok file
-func (mh *MemcachedGoWithWebhooks) implementingWebhooks() {
+func (mh *Memcached) implementingWebhooks() {
 	log.Infof("implementing webhooks")
 	webhookPath := filepath.Join(mh.ctx.Dir, "api", mh.ctx.Version, fmt.Sprintf("%s_webhook.go",
 		strings.ToLower(mh.ctx.Kind)))
@@ -208,7 +215,7 @@ func (mh *MemcachedGoWithWebhooks) implementingWebhooks() {
 }
 
 // implementingController will customize the Controller
-func (mh *MemcachedGoWithWebhooks) implementingController() {
+func (mh *Memcached) implementingController() {
 	controllerPath := filepath.Join(mh.ctx.Dir, "controllers", fmt.Sprintf("%s_controller.go",
 		strings.ToLower(mh.ctx.Kind)))
 
@@ -252,7 +259,7 @@ func (mh *MemcachedGoWithWebhooks) implementingController() {
 
 // nolint:gosec
 // implementingAPI will customize the API
-func (mh *MemcachedGoWithWebhooks) implementingAPI() {
+func (mh *Memcached) implementingAPI() {
 	typeFilePath := filepath.Join(mh.ctx.Dir, "api", mh.ctx.Version, fmt.Sprintf("%s_types.go", strings.ToLower(mh.ctx.Kind)))
 
 	log.Infof("implementing api spec")
@@ -285,18 +292,6 @@ func (mh *MemcachedGoWithWebhooks) implementingAPI() {
 	pkg.CheckError("updating sample", err)
 }
 
-// GenerateMemcachedGoWithWebhooksSample will call all actions to create the directory and generate the sample
-// Note that it should NOT be called in the e2e tests.
-func GenerateMemcachedGoWithWebhooksSample(samplesPath string) {
-	log.Infof("starting to generate Go memcached sample with webhooks")
-	ctx, err := pkg.NewSampleContext(testutils.BinaryName, filepath.Join(samplesPath, "memcached-operator"), "GO111MODULE=on")
-	pkg.CheckError("generating Go memcached with webhooks context", err)
-
-	memcached := NewMemcachedGoWithWebhooks(&ctx)
-	memcached.Prepare()
-	memcached.Run()
-}
-
 const rbacFragment = `
 //+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update
 //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
diff --git a/hack/generate/samples/internal/go/v3/memcached_with_webhooks.go b/hack/generate/samples/internal/go/v3/memcached_with_webhooks.go
index cd2a210a..67e8283f 100644
--- a/hack/generate/samples/internal/go/v3/memcached_with_webhooks.go
+++ b/hack/generate/samples/internal/go/v3/memcached_with_webhooks.go
@@ -28,20 +28,27 @@ import (
 	"github.com/operator-framework/operator-sdk/internal/util"
 )
 
-// MemcachedGoWithWebhooks defines the Memcached Sample in GO using webhooks
-type MemcachedGoWithWebhooks struct {
+// Memcached defines the Memcached Sample in GO using webhooks
+type Memcached struct {
 	ctx *pkg.SampleContext
 }
 
-// MemcachedGoWithWebhooks return a MemcachedGoWithWebhooks
-func NewMemcachedGoWithWebhooks(ctx *pkg.SampleContext) MemcachedGoWithWebhooks {
-	return MemcachedGoWithWebhooks{ctx}
+// GenerateMemcachedSample will call all actions to create the directory and generate the sample
+// Note that it should NOT be called in the e2e tests.
+func GenerateMemcachedSample(binaryPath, samplesPath string) {
+	log.Infof("starting to generate Go memcached sample with webhooks")
+	ctx, err := pkg.NewSampleContext(binaryPath, filepath.Join(samplesPath, "memcached-operator"), "GO111MODULE=on")
+	pkg.CheckError("generating Go memcached with webhooks context", err)
+
+	memcached := Memcached{&ctx}
+	memcached.Prepare()
+	memcached.Run()
 }
 
 // Prepare the Context for the Memcached with WebHooks Go Sample
 // Note that sample directory will be re-created and the context data for the sample
 // will be set such as the domain and GVK.
-func (mh *MemcachedGoWithWebhooks) Prepare() {
+func (mh *Memcached) Prepare() {
 	log.Infof("destroying directory for Memcached with Webhooks Go samples")
 	mh.ctx.Destroy()
 
@@ -57,7 +64,7 @@ func (mh *MemcachedGoWithWebhooks) Prepare() {
 }
 
 // Run the steps to create the Memcached with Webhooks Go Sample
-func (mh *MemcachedGoWithWebhooks) Run() {
+func (mh *Memcached) Run() {
 	log.Infof("creating the project")
 	err := mh.ctx.Init(
 		"--plugins", "go/v3",
@@ -108,7 +115,7 @@ func (mh *MemcachedGoWithWebhooks) Run() {
 }
 
 // uncommentDefaultKustomization will uncomment code in config/default/kustomization.yaml
-func (mh *MemcachedGoWithWebhooks) uncommentDefaultKustomization() {
+func (mh *Memcached) uncommentDefaultKustomization() {
 	var err error
 	kustomization := filepath.Join(mh.ctx.Dir, "config", "default", "kustomization.yaml")
 	log.Info("uncommenting config/default/kustomization.yaml to enable webhooks and ca injection")
@@ -159,7 +166,7 @@ func (mh *MemcachedGoWithWebhooks) uncommentDefaultKustomization() {
 }
 
 // uncommentManifestsKustomization will uncomment code in config/manifests/kustomization.yaml
-func (mh *MemcachedGoWithWebhooks) uncommentManifestsKustomization() {
+func (mh *Memcached) uncommentManifestsKustomization() {
 	var err error
 	kustomization := filepath.Join(mh.ctx.Dir, "config", "manifests", "kustomization.yaml")
 	log.Info("uncommenting config/manifests/kustomization.yaml to enable webhooks in OLM")
@@ -185,7 +192,7 @@ func (mh *MemcachedGoWithWebhooks) uncommentManifestsKustomization() {
 }
 
 // implementingWebhooks will customize the kind wekbhok file
-func (mh *MemcachedGoWithWebhooks) implementingWebhooks() {
+func (mh *Memcached) implementingWebhooks() {
 	log.Infof("implementing webhooks")
 	webhookPath := filepath.Join(mh.ctx.Dir, "api", mh.ctx.Version, fmt.Sprintf("%s_webhook.go",
 		strings.ToLower(mh.ctx.Kind)))
@@ -209,7 +216,7 @@ func (mh *MemcachedGoWithWebhooks) implementingWebhooks() {
 }
 
 // implementingController will customize the Controller
-func (mh *MemcachedGoWithWebhooks) implementingController() {
+func (mh *Memcached) implementingController() {
 	controllerPath := filepath.Join(mh.ctx.Dir, "controllers", fmt.Sprintf("%s_controller.go",
 		strings.ToLower(mh.ctx.Kind)))
 
@@ -250,7 +257,7 @@ func (mh *MemcachedGoWithWebhooks) implementingController() {
 
 // nolint:gosec
 // implementingAPI will customize the API
-func (mh *MemcachedGoWithWebhooks) implementingAPI() {
+func (mh *Memcached) implementingAPI() {
 	err := kbtestutils.InsertCode(
 		filepath.Join(mh.ctx.Dir, "api", mh.ctx.Version, fmt.Sprintf("%s_types.go", strings.ToLower(mh.ctx.Kind))),
 		fmt.Sprintf("type %sSpec struct {\n\t// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster\n\t// Important: Run \"make\" to regenerate code after modifying this file", mh.ctx.Kind),
@@ -280,18 +287,6 @@ func (mh *MemcachedGoWithWebhooks) implementingAPI() {
 	pkg.CheckError("updating sample", err)
 }
 
-// GenerateMemcachedGoWithWebhooksSample will call all actions to create the directory and generate the sample
-// Note that it should NOT be called in the e2e tests.
-func GenerateMemcachedGoWithWebhooksSample(samplesPath string) {
-	log.Infof("starting to generate Go memcached sample with webhooks")
-	ctx, err := pkg.NewSampleContext(testutils.BinaryName, filepath.Join(samplesPath, "memcached-operator"), "GO111MODULE=on")
-	pkg.CheckError("generating Go memcached with webhooks context", err)
-
-	memcached := NewMemcachedGoWithWebhooks(&ctx)
-	memcached.Prepare()
-	memcached.Run()
-}
-
 const rbacFragment = `
 //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
 //+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch`
diff --git a/hack/generate/samples/internal/helm/generate.go b/hack/generate/samples/internal/helm/generate.go
new file mode 100644
index 00000000..771bd367
--- /dev/null
+++ b/hack/generate/samples/internal/helm/generate.go
@@ -0,0 +1,23 @@
+// 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 helm
+
+import (
+	"path/filepath"
+)
+
+func GenerateMemcachedSamples(binaryPath, rootPath string) {
+	GenerateMemcachedSample(binaryPath, filepath.Join(rootPath, "helm"))
+}
diff --git a/hack/generate/samples/internal/helm/memcached.go b/hack/generate/samples/internal/helm/memcached.go
index eaa917ed..7888a4ff 100644
--- a/hack/generate/samples/internal/helm/memcached.go
+++ b/hack/generate/samples/internal/helm/memcached.go
@@ -25,20 +25,29 @@ import (
 	"github.com/operator-framework/operator-sdk/internal/util"
 )
 
-// MemcachedHelm defines the Memcached Sample in Helm
-type MemcachedHelm struct {
+// Memcached defines the Memcached Sample in Helm
+type Memcached struct {
 	ctx *pkg.SampleContext
 }
 
-// NewMemcachedHelm return a MemcachedHelm
-func NewMemcachedHelm(ctx *pkg.SampleContext) MemcachedHelm {
-	return MemcachedHelm{ctx}
+// GenerateMemcachedSample will call all actions to create the directory and generate the sample
+// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT
+// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files
+// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext.
+func GenerateMemcachedSample(binaryPath, samplesPath string) {
+	ctx, err := pkg.NewSampleContext(binaryPath, filepath.Join(samplesPath, "memcached-operator"),
+		"GO111MODULE=on")
+	pkg.CheckError("generating Helm memcached context", err)
+
+	memcached := Memcached{&ctx}
+	memcached.Prepare()
+	memcached.Run()
 }
 
 // Prepare the Context for the Memcached Helm Sample
 // Note that sample directory will be re-created and the context data for the sample
 // will be set such as the domain and GVK.
-func (mh *MemcachedHelm) Prepare() {
+func (mh *Memcached) Prepare() {
 	log.Infof("destroying directory for memcached helm samples")
 	mh.ctx.Destroy()
 
@@ -54,7 +63,7 @@ func (mh *MemcachedHelm) Prepare() {
 }
 
 // Run runs the steps to generate the sample
-func (mh *MemcachedHelm) Run() {
+func (mh *Memcached) Run() {
 	// When operator-sdk scaffolds Helm projects, it tries to use the discovery API of a Kubernetes
 	// cluster to intelligently build the RBAC rules that the operator will require based on the
 	// content of the helm chart.
@@ -103,19 +112,6 @@ func (mh *MemcachedHelm) Run() {
 	pkg.CheckError("striping bundle annotations", err)
 }
 
-// GenerateMemcachedHelmSample will call all actions to create the directory and generate the sample
-// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT
-// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files
-// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext.
-func GenerateMemcachedHelmSample(samplesPath string) {
-	ctx, err := pkg.NewSampleContext(testutils.BinaryName, filepath.Join(samplesPath, "helm", "memcached-operator"), "GO111MODULE=on")
-	pkg.CheckError("generating Helm memcached context", err)
-
-	memcached := NewMemcachedHelm(&ctx)
-	memcached.Prepare()
-	memcached.Run()
-}
-
 const policyRolesFragment = `
 ##
 ## Rules customized for cache.example.com/v1alpha1, Kind: Memcached
diff --git a/hack/generate/samples/molecule/generate.go b/hack/generate/samples/molecule/generate.go
index 69f33026..5deceedb 100644
--- a/hack/generate/samples/molecule/generate.go
+++ b/hack/generate/samples/molecule/generate.go
@@ -29,46 +29,50 @@ import (
 // This generate is used to run the e2e molecule tests
 func main() {
 	var (
-		// binaryName allow inform the binary that should be used.
+		// binaryPath allow inform the binary that should be used.
 		// By default it is operator-sdk
-		binaryName string
+		binaryPath string
 
-		// path is the path provided to generate the molecule sample
-		path string
+		// samplesRoot is the path provided to generate the molecule sample
+		samplesRoot string
 
 		// sample is the name of the mock was selected to be generated
 		sample string
 	)
 
-	// testdata is the path where all samples are generate
-	const testdata = "/testdata/"
-
-	flag.StringVar(&binaryName, "bin", testutils.BinaryName, "Binary path that should be used")
-	flag.StringVar(&path, "path", "", "Path where the molecule should be called")
-	flag.StringVar(&sample, "sample", "", "To generate only the selected option. Options: [advanced,memcached]")
+	flag.StringVar(&binaryPath, "bin", testutils.BinaryName, "Binary path that should be used")
+	flag.StringVar(&samplesRoot, "samples-root", "", "Path where molecule samples should be generated")
+	flag.StringVar(&sample, "sample", "", "To generate only the selected option. Options: [advanced, memcached]")
 
 	flag.Parse()
 
+	// Make the binary path absolute if pathed, for reproducibility and debugging purposes.
+	if dir, _ := filepath.Split(binaryPath); dir != "" {
+		tmp, err := filepath.Abs(binaryPath)
+		if err != nil {
+			log.Fatalf("Failed to make binary path %q absolute: %v", binaryPath, err)
+		}
+		binaryPath = tmp
+	}
+
 	// If no path be provided then the Molecule sample will be create in the testdata/ansible dir
 	// It can be helpful to check the mock data used in the e2e molecule tests as to develop this sample
 	// By default this mock is ignored in the .gitignore
-	if strings.TrimSpace(path) == "" {
+	if strings.TrimSpace(samplesRoot) == "" {
 		currentPath, err := os.Getwd()
 		if err != nil {
-			log.Error(err)
-			os.Exit(1)
+			log.Fatal(err)
 		}
-		path = filepath.Join(currentPath, testdata, "ansible")
+		samplesRoot = filepath.Join(currentPath, "testdata", "ansible")
 	}
 
-	log.Infof("creating Ansible Molecule Mock Sample")
-	log.Infof("using the path: (%v)", path)
+	log.Infof("creating Ansible Molecule Mock Samples under %s", samplesRoot)
 
 	if sample == "" || sample == "memcached" {
-		ansible.GenerateMoleculeAnsibleSample(path)
+		ansible.GenerateMoleculeSample(binaryPath, samplesRoot)
 	}
 
 	if sample == "" || sample == "advanced" {
-		ansible.GenerateMoleculeAdvancedAnsibleSample(path)
+		ansible.GenerateAdvancedMoleculeSample(binaryPath, samplesRoot)
 	}
 }
diff --git a/images/ansible-operator/base.Dockerfile b/images/ansible-operator/base.Dockerfile
index f91c9eba..c908219f 100644
--- a/images/ansible-operator/base.Dockerfile
+++ b/images/ansible-operator/base.Dockerfile
@@ -2,7 +2,7 @@
 # It is built with dependencies that take a while to download, thus speeding
 # up ansible deploy jobs.
 
-FROM registry.access.redhat.com/ubi8/ubi:8.3-227
+FROM registry.access.redhat.com/ubi8/ubi:8.3-297
 ARG TARGETARCH
 
 RUN mkdir -p /etc/ansible \
diff --git a/images/helm-operator/Dockerfile b/images/helm-operator/Dockerfile
index 7b674974..90bf1ca6 100644
--- a/images/helm-operator/Dockerfile
+++ b/images/helm-operator/Dockerfile
@@ -17,7 +17,7 @@ COPY . .
 RUN GOOS=linux GOARCH=$TARGETARCH make build/helm-operator
 
 # Final image.
-FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-230
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-298
 
 ENV HOME=/opt/helm \
     USER_NAME=helm \
diff --git a/images/operator-sdk/Dockerfile b/images/operator-sdk/Dockerfile
index 71dbde57..3d29d371 100644
--- a/images/operator-sdk/Dockerfile
+++ b/images/operator-sdk/Dockerfile
@@ -17,7 +17,7 @@ COPY . .
 RUN GOOS=linux GOARCH=$TARGETARCH make build/operator-sdk
 
 # Final image.
-FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-230
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-298
 
 RUN microdnf install -y golang make which
 
diff --git a/internal/ansible/flags/flag.go b/internal/ansible/flags/flag.go
index 49cadfba..23daa77c 100644
--- a/internal/ansible/flags/flag.go
+++ b/internal/ansible/flags/flag.go
@@ -19,6 +19,8 @@ import (
 	"time"
 
 	"github.com/spf13/pflag"
+	"k8s.io/client-go/tools/leaderelection/resourcelock"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
 )
 
 // Flags - Options to be used by an ansible operator
@@ -37,18 +39,26 @@ type Flags struct {
 	LeaderElectionNamespace string
 	GracefulShutdownTimeout time.Duration
 	AnsibleArgs             string
+
+	// Path to a controller-runtime componentconfig file.
+	// If this is empty, use default values.
+	ManagerConfigPath string
+
+	// If not nil, used to deduce which flags were set in the CLI.
+	flagSet *pflag.FlagSet
 }
 
-const AnsibleRolesPathEnvVar = "ANSIBLE_ROLES_PATH"
-const AnsibleCollectionsPathEnvVar = "ANSIBLE_COLLECTIONS_PATH"
+const (
+	AnsibleRolesPathEnvVar       = "ANSIBLE_ROLES_PATH"
+	AnsibleCollectionsPathEnvVar = "ANSIBLE_COLLECTIONS_PATH"
+)
 
 // AddTo - Add the ansible operator flags to the the flagset
 func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
-	flagSet.DurationVar(&f.ReconcilePeriod,
-		"reconcile-period",
-		time.Minute,
-		"Default reconcile period for controllers",
-	)
+	// Store flagset internally to be used for lookups later.
+	f.flagSet = flagSet
+
+	// Ansible flags.
 	flagSet.StringVar(&f.WatchesFile,
 		"watches-file",
 		"./watches.yaml",
@@ -59,11 +69,6 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
 		true,
 		"The ansible operator will inject owner references unless this flag is false",
 	)
-	flagSet.IntVar(&f.MaxConcurrentReconciles,
-		"max-concurrent-reconciles",
-		runtime.NumCPU(),
-		"Maximum number of concurrent reconciles for controllers. Overridden by environment variable.",
-	)
 	flagSet.IntVar(&f.AnsibleVerbosity,
 		"ansible-verbosity",
 		2,
@@ -77,9 +82,36 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
 	flagSet.StringVar(&f.AnsibleCollectionsPath,
 		"ansible-collections-path",
 		"",
-		"Path to installed Ansible Collections. If set, collections should be located in {{value}}/ansible_collections/. If unset, collections are assumed to be in ~/.ansible/collections or /usr/share/ansible/collections.",
+		"Path to installed Ansible Collections. If set, collections should be located in {{value}}/ansible_collections/. "+
+			"If unset, collections are assumed to be in ~/.ansible/collections or /usr/share/ansible/collections.",
+	)
+	flagSet.StringVar(&f.AnsibleArgs,
+		"ansible-args",
+		"",
+		"Ansible args. Allows user to specify arbitrary arguments for ansible-based operators.",
+	)
+
+	// Controller flags.
+	flagSet.DurationVar(&f.ReconcilePeriod,
+		"reconcile-period",
+		time.Minute,
+		"Default reconcile period for controllers",
+	)
+	flagSet.IntVar(&f.MaxConcurrentReconciles,
+		"max-concurrent-reconciles",
+		runtime.NumCPU(),
+		"Maximum number of concurrent reconciles for controllers. Overridden by environment variable.",
+	)
+
+	// Controller manager flags.
+	flagSet.StringVar(&f.ManagerConfigPath,
+		"config",
+		"",
+		"The controller will load its initial configuration from this file. "+
+			"Omit this flag to use the default configuration values. "+
+			"Command-line flags override configuration from this file.",
 	)
-	// todo:remove it for 2.0.0
+	// TODO(2.0.0): remove
 	flagSet.StringVar(&f.MetricsBindAddress,
 		"metrics-addr",
 		":8080",
@@ -91,15 +123,14 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
 		":8080",
 		"The address the metric endpoint binds to",
 	)
-	// todo: for Go/Helm the port used is: 8081
+	// TODO(2.0.0): for Go/Helm the port used is: 8081
 	// update it to keep the project aligned to the other
-	// types for 2.0
 	flagSet.StringVar(&f.ProbeAddr,
 		"health-probe-bind-address",
 		":6789",
 		"The address the probe endpoint binds to.",
 	)
-	// todo:remove it for 2.0.0
+	// TODO(2.0.0): remove
 	flagSet.BoolVar(&f.LeaderElection,
 		"enable-leader-election",
 		false,
@@ -131,9 +162,43 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
 		"The amount of time that will be spent waiting"+
 			" for runners to gracefully exit.",
 	)
-	flagSet.StringVar(&f.AnsibleArgs,
-		"ansible-args",
-		"",
-		"Ansible args. Allows user to specify arbitrary arguments for ansible-based operators.",
-	)
+}
+
+// ToManagerOptions uses the flag set in f to configure options.
+// Values of options take precedence over flag defaults,
+// as values are assume to have been explicitly set.
+func (f *Flags) ToManagerOptions(options manager.Options) manager.Options {
+	// Alias FlagSet.Changed so options are still updated when fields are empty.
+	changed := func(flagName string) bool {
+		return f.flagSet.Changed(flagName)
+	}
+	if f.flagSet == nil {
+		changed = func(flagName string) bool { return false }
+	}
+
+	// TODO(2.0.0): remove metrics-addr
+	if changed("metrics-bind-address") || changed("metrics-addr") || options.MetricsBindAddress == "" {
+		options.MetricsBindAddress = f.MetricsBindAddress
+	}
+	if changed("health-probe-bind-address") || options.HealthProbeBindAddress == "" {
+		options.HealthProbeBindAddress = f.ProbeAddr
+	}
+	// TODO(2.0.0): remove enable-leader-election
+	if changed("leader-elect") || changed("enable-leader-election") || !options.LeaderElection {
+		options.LeaderElection = f.LeaderElection
+	}
+	if changed("leader-election-id") || options.LeaderElectionID == "" {
+		options.LeaderElectionID = f.LeaderElectionID
+	}
+	if changed("leader-election-namespace") || options.LeaderElectionNamespace == "" {
+		options.LeaderElectionNamespace = f.LeaderElectionNamespace
+	}
+	if options.LeaderElectionResourceLock == "" {
+		options.LeaderElectionResourceLock = resourcelock.ConfigMapsResourceLock
+	}
+	if changed("graceful-shutdown-timeout") || options.GracefulShutdownTimeout == nil {
+		options.GracefulShutdownTimeout = &f.GracefulShutdownTimeout
+	}
+
+	return options
 }
diff --git a/internal/cmd/ansible-operator/run/cmd.go b/internal/cmd/ansible-operator/run/cmd.go
index a1c17f53..88ca079c 100644
--- a/internal/cmd/ansible-operator/run/cmd.go
+++ b/internal/cmd/ansible-operator/run/cmd.go
@@ -25,7 +25,7 @@ import (
 
 	"github.com/spf13/cobra"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/client-go/tools/leaderelection/resourcelock"
+	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/cache"
 	"sigs.k8s.io/controller-runtime/pkg/client/config"
 	"sigs.k8s.io/controller-runtime/pkg/healthz"
@@ -82,25 +82,43 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 	printVersion()
 	metrics.RegisterBuildInfo(crmetrics.Registry)
 
+	// Load config options from the config at f.ManagerConfigPath.
+	// These options will not override those set by flags.
+	var (
+		options manager.Options
+		err     error
+	)
+	if f.ManagerConfigPath != "" {
+		cfgLoader := ctrl.ConfigFile().AtPath(f.ManagerConfigPath)
+		if options, err = options.AndFrom(cfgLoader); err != nil {
+			log.Error(err, "Unable to load the manager config file")
+			os.Exit(1)
+		}
+	}
+	exitIfUnsupported(options)
+
 	cfg, err := config.GetConfig()
 	if err != nil {
 		log.Error(err, "Failed to get config.")
 		os.Exit(1)
 	}
 
+	// TODO(2.0.0): remove
 	// Deprecated: OPERATOR_NAME environment variable is an artifact of the
 	// legacy operator-sdk project scaffolding. Flag `--leader-election-id`
 	// should be used instead.
 	if operatorName, found := os.LookupEnv("OPERATOR_NAME"); found {
 		log.Info("Environment variable OPERATOR_NAME has been deprecated, use --leader-election-id instead.")
-		if cmd.Flags().Lookup("leader-election-id").Changed {
+		if cmd.Flags().Changed("leader-election-id") {
 			log.Info("Ignoring OPERATOR_NAME environment variable since --leader-election-id is set")
-		} else {
-			f.LeaderElectionID = operatorName
+		} else if options.LeaderElectionID == "" {
+			// Only set leader election ID using OPERATOR_NAME if unset everywhere else,
+			// since this env var is deprecated.
+			options.LeaderElectionID = operatorName
 		}
 	}
 
-	//todo: remove the following checks for 2.0.0 they are required just because of the flags deprecation
+	//TODO(2.0.0): remove the following checks. they are required just because of the flags deprecation
 	if cmd.Flags().Changed("leader-elect") && cmd.Flags().Changed("enable-leader-election") {
 		log.Error(errors.New("only one of --leader-elect and --enable-leader-election may be set"), "invalid flags usage")
 		os.Exit(1)
@@ -113,20 +131,15 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 
 	// Set default manager options
 	// TODO: probably should expose the host & port as an environment variables
-	options := manager.Options{
-		MetricsBindAddress:         f.MetricsBindAddress,
-		HealthProbeBindAddress:     f.ProbeAddr,
-		LeaderElection:             f.LeaderElection,
-		LeaderElectionID:           f.LeaderElectionID,
-		LeaderElectionResourceLock: resourcelock.ConfigMapsResourceLock,
-		LeaderElectionNamespace:    f.LeaderElectionNamespace,
-		ClientBuilder:              clientbuilder.NewUnstructedCached(),
-		GracefulShutdownTimeout:    &f.GracefulShutdownTimeout,
+	options = f.ToManagerOptions(options)
+	if options.ClientBuilder == nil {
+		options.ClientBuilder = clientbuilder.NewUnstructedCached()
 	}
 
 	namespace, found := os.LookupEnv(k8sutil.WatchNamespaceEnvVar)
 	log = log.WithValues("Namespace", namespace)
 	if found {
+		log.V(1).Info("Setting namespace with value in %s", k8sutil.WatchNamespaceEnvVar)
 		if namespace == metav1.NamespaceAll {
 			log.Info("Watching all namespaces.")
 			options.Namespace = metav1.NamespaceAll
@@ -139,9 +152,9 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 				options.Namespace = namespace
 			}
 		}
-	} else {
-		log.Info(fmt.Sprintf("%v environment variable not set. Watching all namespaces.",
-			k8sutil.WatchNamespaceEnvVar))
+	} else if options.Namespace == "" {
+		log.Info(fmt.Sprintf("Watch namespaces not configured by environment variable %s or file. "+
+			"Watching all namespaces.", k8sutil.WatchNamespaceEnvVar))
 		options.Namespace = metav1.NamespaceAll
 	}
 
@@ -202,7 +215,7 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 		}, w.Blacklist)
 	}
 
-	// todo: remove when a upper version be bumped
+	// TODO(2.0.0): remove
 	err = mgr.AddHealthzCheck("ping", healthz.Ping)
 	if err != nil {
 		log.Error(err, "Failed to add Healthz check.")
@@ -219,7 +232,7 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 		RESTMapper:        mgr.GetRESTMapper(),
 		ControllerMap:     cMap,
 		OwnerInjection:    f.InjectOwnerRef,
-		WatchedNamespaces: []string{namespace},
+		WatchedNamespaces: strings.Split(namespace, ","),
 	})
 	if err != nil {
 		log.Error(err, "Error starting proxy.")
@@ -240,6 +253,27 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 	log.Info("Exiting.")
 }
 
+// exitIfUnsupported prints an error containing unsupported field names and exits
+// if any of those fields are not their default values.
+func exitIfUnsupported(options manager.Options) {
+	var keys []string
+	// The below options are webhook-specific, which is not supported by ansible.
+	if options.CertDir != "" {
+		keys = append(keys, "certDir")
+	}
+	if options.Host != "" {
+		keys = append(keys, "host")
+	}
+	if options.Port != 0 {
+		keys = append(keys, "port")
+	}
+
+	if len(keys) > 0 {
+		log.Error(fmt.Errorf("%s set in manager options", strings.Join(keys, ", ")), "unsupported fields")
+		os.Exit(1)
+	}
+}
+
 // getAnsibleDebugLog return the value from the ANSIBLE_DEBUG_LOGS it order to
 // print the full Ansible logs
 func getAnsibleDebugLog() bool {
diff --git a/internal/cmd/helm-operator/run/cmd.go b/internal/cmd/helm-operator/run/cmd.go
index c3742d3b..198313bf 100644
--- a/internal/cmd/helm-operator/run/cmd.go
+++ b/internal/cmd/helm-operator/run/cmd.go
@@ -24,7 +24,7 @@ import (
 
 	"github.com/spf13/cobra"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/client-go/tools/leaderelection/resourcelock"
+	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/cache"
 	"sigs.k8s.io/controller-runtime/pkg/client/config"
 	"sigs.k8s.io/controller-runtime/pkg/healthz"
@@ -79,25 +79,43 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 	printVersion()
 	metrics.RegisterBuildInfo(crmetrics.Registry)
 
+	// Load config options from the config at f.ManagerConfigPath.
+	// These options will not override those set by flags.
+	var (
+		options manager.Options
+		err     error
+	)
+	if f.ManagerConfigPath != "" {
+		cfgLoader := ctrl.ConfigFile().AtPath(f.ManagerConfigPath)
+		if options, err = options.AndFrom(cfgLoader); err != nil {
+			log.Error(err, "Unable to load the manager config file")
+			os.Exit(1)
+		}
+	}
+	exitIfUnsupported(options)
+
 	cfg, err := config.GetConfig()
 	if err != nil {
 		log.Error(err, "Failed to get config.")
 		os.Exit(1)
 	}
 
+	// TODO(2.0.0): remove
 	// Deprecated: OPERATOR_NAME environment variable is an artifact of the
 	// legacy operator-sdk project scaffolding. Flag `--leader-election-id`
 	// should be used instead.
 	if operatorName, found := os.LookupEnv("OPERATOR_NAME"); found {
 		log.Info("Environment variable OPERATOR_NAME has been deprecated, use --leader-election-id instead.")
-		if cmd.Flags().Lookup("leader-election-id").Changed {
+		if cmd.Flags().Changed("leader-election-id") {
 			log.Info("Ignoring OPERATOR_NAME environment variable since --leader-election-id is set")
-		} else {
-			f.LeaderElectionID = operatorName
+		} else if options.LeaderElectionID == "" {
+			// Only set leader election ID using OPERATOR_NAME if unset everywhere else,
+			// since this env var is deprecated.
+			options.LeaderElectionID = operatorName
 		}
 	}
 
-	//todo: remove the following checks for 2.0.0 they are required just because of the flags deprecation
+	//TODO(2.0.0): remove the following checks. they are required just because of the flags deprecation
 	if cmd.Flags().Changed("leader-elect") && cmd.Flags().Changed("enable-leader-election") {
 		log.Error(errors.New("only one of --leader-elect and --enable-leader-election may be set"), "invalid flags usage")
 		os.Exit(1)
@@ -109,19 +127,15 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 	}
 
 	// Set default manager options
-	options := manager.Options{
-		MetricsBindAddress:         f.MetricsBindAddress,
-		HealthProbeBindAddress:     f.ProbeAddr,
-		LeaderElection:             f.LeaderElection,
-		LeaderElectionID:           f.LeaderElectionID,
-		LeaderElectionResourceLock: resourcelock.ConfigMapsResourceLock,
-		LeaderElectionNamespace:    f.LeaderElectionNamespace,
-		ClientBuilder:              clientbuilder.NewUnstructedCached(),
+	options = f.ToManagerOptions(options)
+	if options.ClientBuilder == nil {
+		options.ClientBuilder = clientbuilder.NewUnstructedCached()
 	}
 
 	namespace, found := os.LookupEnv(k8sutil.WatchNamespaceEnvVar)
 	log = log.WithValues("Namespace", namespace)
 	if found {
+		log.V(1).Info("Setting namespace with value in %s", k8sutil.WatchNamespaceEnvVar)
 		if namespace == metav1.NamespaceAll {
 			log.Info("Watching all namespaces.")
 			options.Namespace = metav1.NamespaceAll
@@ -134,9 +148,9 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 				options.Namespace = namespace
 			}
 		}
-	} else {
-		log.Info(fmt.Sprintf("%v environment variable not set. Watching all namespaces.",
-			k8sutil.WatchNamespaceEnvVar))
+	} else if options.Namespace == "" {
+		log.Info(fmt.Sprintf("Watch namespaces not configured by environment variable %s or file. "+
+			"Watching all namespaces.", k8sutil.WatchNamespaceEnvVar))
 		options.Namespace = metav1.NamespaceAll
 	}
 
@@ -183,3 +197,24 @@ func run(cmd *cobra.Command, f *flags.Flags) {
 		os.Exit(1)
 	}
 }
+
+// exitIfUnsupported prints an error containing unsupported field names and exits
+// if any of those fields are not their default values.
+func exitIfUnsupported(options manager.Options) {
+	var keys []string
+	// The below options are webhook-specific, which is not supported by ansible.
+	if options.CertDir != "" {
+		keys = append(keys, "certDir")
+	}
+	if options.Host != "" {
+		keys = append(keys, "host")
+	}
+	if options.Port != 0 {
+		keys = append(keys, "port")
+	}
+
+	if len(keys) > 0 {
+		log.Error(fmt.Errorf("%s set in manager options", strings.Join(keys, ", ")), "unsupported fields")
+		os.Exit(1)
+	}
+}
diff --git a/internal/cmd/operator-sdk/alpha/config3alphato3/convert_config_3-alpha_to_3.go b/internal/cmd/operator-sdk/alpha/config3alphato3/convert_config_3-alpha_to_3.go
index 23abf241..fd19c6cf 100644
--- a/internal/cmd/operator-sdk/alpha/config3alphato3/convert_config_3-alpha_to_3.go
+++ b/internal/cmd/operator-sdk/alpha/config3alphato3/convert_config_3-alpha_to_3.go
@@ -34,8 +34,14 @@ var (
 	versionRe = regexp.MustCompile(`version:[ ]*(?:")?3-alpha(?:")?`)
 )
 
+type templObj struct {
+	IsGo      bool
+	Resources []resource.Resource
+}
+
 // convertConfig3AlphaTo3 returns cfgBytes converted to 3 iff cfgBytes is version 3-alpha.
 func convertConfig3AlphaTo3(cfgBytes []byte) (_ []byte, err error) {
+	tObj := templObj{}
 	cfgObj := make(map[string]interface{}, 5)
 	if err := yaml.Unmarshal(cfgBytes, &cfgObj); err != nil {
 		return nil, err
@@ -109,9 +115,12 @@ func convertConfig3AlphaTo3(cfgBytes []byte) (_ []byte, err error) {
 			}
 		}
 
+		tObj.Resources = resources
+		tObj.IsGo = isGo
+
 		out := bytes.Buffer{}
 		t := template.Must(template.New("").Parse(tmpl))
-		if err := t.Execute(&out, resources); err != nil {
+		if err := t.Execute(&out, tObj); err != nil {
 			return nil, err
 		}
 
@@ -152,7 +161,8 @@ var getModulePath = func() (string, error) {
 
 // Comment-heavy "resources" template.
 const tmpl = `resources:
-{{- range $i, $res := . }}
+{{- $isGo := .IsGo }}
+{{- range $i, $res := .Resources }}
 -{{- if $res.API }} api:
     {{- if $res.API.CRDVersion }}
     crdVersion: {{ $res.API.CRDVersion }}
@@ -163,8 +173,10 @@ const tmpl = `resources:
     # TODO(user): Uncomment the below line if this resource's CRD is namespace scoped, else delete it.
     # namespaced: true
   {{- end }}
+  {{- if $isGo }}
   # TODO(user): Uncomment the below line if this resource implements a controller, else delete it.
   # controller: true
+  {{- end }}
   {{- if $res.Domain }}
   domain: {{ $res.Domain }}
   {{- end }}
diff --git a/internal/cmd/operator-sdk/cli/cli.go b/internal/cmd/operator-sdk/cli/cli.go
index 15b5375a..ad634338 100644
--- a/internal/cmd/operator-sdk/cli/cli.go
+++ b/internal/cmd/operator-sdk/cli/cli.go
@@ -69,13 +69,6 @@ func Run() error {
 // This CLI can run kubebuilder commands and certain SDK specific commands that are aligned for
 // the kubebuilder project layout
 func GetPluginsCLIAndRoot() (*cli.CLI, *cobra.Command) {
-	ansibleBundle, _ := plugin.NewBundle("ansible"+plugins.DefaultNameQualifier, plugin.Version{Number: 1},
-		kustomizev1.Plugin{},
-		ansiblev1.Plugin{},
-		manifestsv2.Plugin{},
-		scorecardv2.Plugin{},
-	)
-
 	// todo: Export the bundles KB and then change here to use the bundles exported instead
 	// more info: https://github.com/kubernetes-sigs/kubebuilder/pull/2112
 	gov2Bundle, _ := plugin.NewBundle(golang.DefaultNameQualifier, golangv2.Plugin{}.Version(),
@@ -90,6 +83,12 @@ func GetPluginsCLIAndRoot() (*cli.CLI, *cobra.Command) {
 		manifestsv2.Plugin{},
 		scorecardv2.Plugin{},
 	)
+	ansibleBundle, _ := plugin.NewBundle("ansible"+plugins.DefaultNameQualifier, plugin.Version{Number: 1},
+		kustomizev1.Plugin{},
+		ansiblev1.Plugin{},
+		manifestsv2.Plugin{},
+		scorecardv2.Plugin{},
+	)
 	helmBundle, _ := plugin.NewBundle("helm"+plugins.DefaultNameQualifier, plugin.Version{Number: 1},
 		kustomizev1.Plugin{},
 		helmv1.Plugin{},
diff --git a/internal/cmd/operator-sdk/cli/cli.go.orig b/internal/cmd/operator-sdk/cli/cli.go.orig
deleted file mode 100644
index 15174e4b..00000000
--- a/internal/cmd/operator-sdk/cli/cli.go.orig
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2019 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 cli
-
-import (
-	log "github.com/sirupsen/logrus"
-	"github.com/spf13/cobra"
-	"github.com/spf13/viper"
-	"sigs.k8s.io/kubebuilder/v3/pkg/cli"
-	cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
-	cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
-	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
-	kustomizev1 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/common/kustomize/v1"
-	declarativev1 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/declarative/v1"
-
-	"sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang"
-	golangv2 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v2"
-	golangv3 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3"
-
-	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/alpha/config3alphato3"
-	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/bundle"
-	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/cleanup"
-	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/generate"
-	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/olm"
-	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/run"
-	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/scorecard"
-	"github.com/operator-framework/operator-sdk/internal/flags"
-	"github.com/operator-framework/operator-sdk/internal/plugins"
-	ansiblev1 "github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1"
-	envtestv1 "github.com/operator-framework/operator-sdk/internal/plugins/envtest/v1"
-	helmv1 "github.com/operator-framework/operator-sdk/internal/plugins/helm/v1"
-	manifestsv2 "github.com/operator-framework/operator-sdk/internal/plugins/manifests/v2"
-	scorecardv2 "github.com/operator-framework/operator-sdk/internal/plugins/scorecard/v2"
-	"github.com/operator-framework/operator-sdk/internal/util/projutil"
-)
-
-var (
-	commands = []*cobra.Command{
-		bundle.NewCmd(),
-		cleanup.NewCmd(),
-		generate.NewCmd(),
-		olm.NewCmd(),
-		run.NewCmd(),
-		scorecard.NewCmd(),
-	}
-	alphaCommands = []*cobra.Command{
-		config3alphato3.NewCmd(),
-	}
-)
-
-func Run() error {
-	c, _ := GetPluginsCLIAndRoot()
-	return c.Run()
-}
-
-// GetPluginsCLIAndRoot returns the plugins based CLI configured to use operator-sdk as the root command
-// This CLI can run kubebuilder commands and certain SDK specific commands that are aligned for
-// the kubebuilder project layout
-func GetPluginsCLIAndRoot() (*cli.CLI, *cobra.Command) {
-	ansibleBundle, _ := plugin.NewBundle("ansible"+plugins.DefaultNameQualifier, plugin.Version{Number: 1},
-		kustomizev1.Plugin{},
-		ansiblev1.Plugin{},
-		manifestsv2.Plugin{},
-		scorecardv2.Plugin{},
-	)
-
-	// todo: Export the bundles KB and then change here to use the bundles exported instead
-	// more info: https://github.com/kubernetes-sigs/kubebuilder/pull/2112
-	gov2Bundle, _ := plugin.NewBundle(golang.DefaultNameQualifier, golangv2.Plugin{}.Version(),
-		golangv2.Plugin{},
-		envtestv1.Plugin{},
-		manifestsv2.Plugin{},
-		scorecardv2.Plugin{},
-	)
-	gov3Bundle, _ := plugin.NewBundle(golang.DefaultNameQualifier, golangv3.Plugin{}.Version(),
-		kustomizev1.Plugin{},
-		golangv3.Plugin{},
-		manifestsv2.Plugin{},
-		scorecardv2.Plugin{},
-	)
-	helmBundle, _ := plugin.NewBundle("helm"+plugins.DefaultNameQualifier, plugin.Version{Number: 1},
-		kustomizev1.Plugin{},
-		helmv1.Plugin{},
-		manifestsv2.Plugin{},
-		scorecardv2.Plugin{},
-	)
-	c, err := cli.New(
-		cli.WithCommandName("operator-sdk"),
-		cli.WithVersion(makeVersionString()),
-		cli.WithPlugins(
-			ansibleBundle,
-			gov2Bundle,
-			gov3Bundle,
-			helmBundle,
-<<<<<<< HEAD
-			kustomizev1.Plugin{},
-=======
-			declarativev1.Plugin{},
->>>>>>> plugins: add the optional kb declative plugin
-		),
-		cli.WithDefaultPlugins(cfgv2.Version, gov2Bundle),
-		cli.WithDefaultPlugins(cfgv3.Version, gov3Bundle),
-		cli.WithDefaultProjectVersion(cfgv3.Version),
-		cli.WithExtraCommands(commands...),
-		cli.WithExtraAlphaCommands(alphaCommands...),
-		cli.WithCompletion(),
-	)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	// We can get the whole CLI for doc-gen/completion from the root of any
-	// command added to a CLI.
-	root := commands[0].Root()
-
-	// Configure --verbose globally.
-	// TODO(estroz): upstream PR for global --verbose.
-	root.PersistentFlags().Bool(flags.VerboseOpt, false, "Enable verbose logging")
-	if err := viper.BindPFlags(root.PersistentFlags()); err != nil {
-		log.Fatalf("Failed to bind %s flags: %v", root.Name(), err)
-	}
-	root.PersistentPreRun = rootPersistentPreRun
-
-	return c, root
-}
-
-func rootPersistentPreRun(cmd *cobra.Command, args []string) {
-	if viper.GetBool(flags.VerboseOpt) {
-		if err := projutil.SetGoVerbose(); err != nil {
-			log.Fatalf("Could not set GOFLAGS: (%v)", err)
-		}
-		log.SetLevel(log.DebugLevel)
-		log.Debug("Debug logging is set")
-	}
-
-	config3alphato3.RootPersistentPreRun(cmd, args)
-}
diff --git a/internal/cmd/operator-sdk/generate/bundle/bundle.go b/internal/cmd/operator-sdk/generate/bundle/bundle.go
index b731e71e..0986cc0b 100644
--- a/internal/cmd/operator-sdk/generate/bundle/bundle.go
+++ b/internal/cmd/operator-sdk/generate/bundle/bundle.go
@@ -20,6 +20,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"sort"
 	"strings"
 	"text/template"
 
@@ -286,6 +287,7 @@ func (c bundleCmd) runMetadata() error {
 	for key, value := range metricsannotations.MakeBundleMetadataLabels(c.layout) {
 		values.OtherLabels = append(values.OtherLabels, fmt.Sprintf("%s=%s", key, value))
 	}
+	sort.Strings(values.OtherLabels)
 
 	// Write each file.
 	metadataDir := filepath.Join(c.outputDir, "metadata")
diff --git a/internal/cmd/operator-sdk/generate/kustomize/manifests.go b/internal/cmd/operator-sdk/generate/kustomize/manifests.go
index 092f2cf9..84a41842 100644
--- a/internal/cmd/operator-sdk/generate/kustomize/manifests.go
+++ b/internal/cmd/operator-sdk/generate/kustomize/manifests.go
@@ -15,6 +15,7 @@
 package kustomize
 
 import (
+	"bytes"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -210,6 +211,11 @@ func (c manifestsCmd) run(cfg config.Config) error {
 	if err != nil {
 		return fmt.Errorf("error marshaling CSV base: %v", err)
 	}
+
+	// todo: remove it when the OLM starts to support https://github.com/operator-framework/api/pull/100
+	const cleanup = "cleanup:\n    enabled: false\n  "
+	csvBytes = bytes.ReplaceAll(csvBytes, []byte(cleanup), []byte(""))
+
 	if err = os.MkdirAll(filepath.Join(c.outputDir, "bases"), 0755); err != nil {
 		return err
 	}
diff --git a/internal/generate/internal/genutil.go b/internal/generate/internal/genutil.go
index 2a046de9..630fece9 100644
--- a/internal/generate/internal/genutil.go
+++ b/internal/generate/internal/genutil.go
@@ -15,6 +15,7 @@
 package genutil
 
 import (
+	"bytes"
 	"errors"
 	"fmt"
 	"io"
@@ -61,6 +62,11 @@ func WriteObject(w io.Writer, obj interface{}) error {
 	if err != nil {
 		return err
 	}
+
+	// todo: remove it when the OLM starts to support https://github.com/operator-framework/api/pull/100
+	const cleanup = "cleanup:\n    enabled: false\n  "
+	b = bytes.ReplaceAll(b, []byte(cleanup), []byte(""))
+
 	return write(w, b)
 }
 
diff --git a/internal/helm/controller/reconcile.go b/internal/helm/controller/reconcile.go
index 8fb55224..3917f86d 100644
--- a/internal/helm/controller/reconcile.go
+++ b/internal/helm/controller/reconcile.go
@@ -60,6 +60,9 @@ const (
 	uninstallFinalizer = "helm.sdk.operatorframework.io/uninstall-release"
 	// Deprecated: use uninstallFinalizer. This will be removed in operator-sdk v2.0.0.
 	uninstallFinalizerLegacy = "uninstall-helm-release"
+
+	helmUpgradeForceAnnotation  = "helm.sdk.operatorframework.io/upgrade-force"
+	helmUninstallWaitAnnotation = "helm.sdk.operatorframework.io/uninstall-wait"
 )
 
 // Reconcile reconciles the requested resource by installing, updating, or
@@ -122,25 +125,58 @@ func (r HelmOperatorReconciler) Reconcile(ctx context.Context, request reconcile
 		}
 		status.RemoveCondition(types.ConditionReleaseFailed)
 
+		wait := hasAnnotation(helmUninstallWaitAnnotation, o)
 		if errors.Is(err, driver.ErrReleaseNotFound) {
-			log.Info("Release not found, removing finalizer")
+			log.Info("Release not found")
 		} else {
 			log.Info("Uninstalled release")
 			if log.V(0).Enabled() && uninstalledRelease != nil {
 				fmt.Println(diff.Generate(uninstalledRelease.Manifest, ""))
 			}
+			if !wait {
+				status.SetCondition(types.HelmAppCondition{
+					Type:   types.ConditionDeployed,
+					Status: types.StatusFalse,
+					Reason: types.ReasonUninstallSuccessful,
+				})
+				status.DeployedRelease = nil
+			}
+		}
+		if wait {
 			status.SetCondition(types.HelmAppCondition{
-				Type:   types.ConditionDeployed,
-				Status: types.StatusFalse,
-				Reason: types.ReasonUninstallSuccessful,
+				Type:    types.ConditionDeployed,
+				Status:  types.StatusFalse,
+				Reason:  types.ReasonUninstallSuccessful,
+				Message: "Waiting until all resources are deleted.",
 			})
-			status.DeployedRelease = nil
 		}
 		if err := r.updateResourceStatus(ctx, o, status); err != nil {
 			log.Info("Failed to update CR status")
 			return reconcile.Result{}, err
 		}
 
+		if wait && status.DeployedRelease != nil && status.DeployedRelease.Manifest != "" {
+			log.Info("Uninstall wait")
+			isAllResourcesDeleted, err := manager.CleanupRelease(ctx, status.DeployedRelease.Manifest)
+			if err != nil {
+				log.Error(err, "Failed to cleanup release")
+				status.SetCondition(types.HelmAppCondition{
+					Type:    types.ConditionReleaseFailed,
+					Status:  types.StatusTrue,
+					Reason:  types.ReasonUninstallError,
+					Message: err.Error(),
+				})
+				_ = r.updateResourceStatus(ctx, o, status)
+				return reconcile.Result{}, err
+			}
+			if !isAllResourcesDeleted {
+				log.Info("Waiting until all resources are deleted")
+				return reconcile.Result{RequeueAfter: r.ReconcilePeriod}, nil
+			}
+			status.RemoveCondition(types.ConditionReleaseFailed)
+		}
+
+		log.Info("Removing finalizer")
 		controllerutil.RemoveFinalizer(o, uninstallFinalizer)
 		controllerutil.RemoveFinalizer(o, uninstallFinalizerLegacy)
 		if err := r.updateResource(ctx, o); err != nil {
@@ -254,7 +290,7 @@ func (r HelmOperatorReconciler) Reconcile(ctx context.Context, request reconcile
 			r.EventRecorder.Eventf(o, "Warning", "OverrideValuesInUse",
 				"Chart value %q overridden to %q by operator's watches.yaml", k, v)
 		}
-		force := hasHelmUpgradeForceAnnotation(o)
+		force := hasAnnotation(helmUpgradeForceAnnotation, o)
 		previousRelease, upgradedRelease, err := manager.UpgradeRelease(ctx, release.ForceUpgrade(force))
 		if err != nil {
 			log.Error(err, "Release failed")
@@ -357,16 +393,15 @@ func (r HelmOperatorReconciler) Reconcile(ctx context.Context, request reconcile
 
 // returns the boolean representation of the annotation string
 // will return false if annotation is not set
-func hasHelmUpgradeForceAnnotation(o *unstructured.Unstructured) bool {
-	const helmUpgradeForceAnnotation = "helm.sdk.operatorframework.io/upgrade-force"
-	force := o.GetAnnotations()[helmUpgradeForceAnnotation]
-	if force == "" {
+func hasAnnotation(anno string, o *unstructured.Unstructured) bool {
+	boolStr := o.GetAnnotations()[anno]
+	if boolStr == "" {
 		return false
 	}
 	value := false
-	if i, err := strconv.ParseBool(force); err != nil {
+	if i, err := strconv.ParseBool(boolStr); err != nil {
 		log.Info("Could not parse annotation as a boolean",
-			"annotation", helmUpgradeForceAnnotation, "value informed", force)
+			"annotation", anno, "value informed", boolStr)
 	} else {
 		value = i
 	}
diff --git a/internal/helm/flags/flag.go b/internal/helm/flags/flag.go
index b64a2f71..bddc0a35 100644
--- a/internal/helm/flags/flag.go
+++ b/internal/helm/flags/flag.go
@@ -19,6 +19,8 @@ import (
 	"time"
 
 	"github.com/spf13/pflag"
+	"k8s.io/client-go/tools/leaderelection/resourcelock"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
 )
 
 // Flags - Options to be used by a helm operator
@@ -31,21 +33,48 @@ type Flags struct {
 	LeaderElectionNamespace string
 	MaxConcurrentReconciles int
 	ProbeAddr               string
+
+	// Path to a controller-runtime componentconfig file.
+	// If this is empty, use default values.
+	ManagerConfigPath string
+
+	// If not nil, used to deduce which flags were set in the CLI.
+	flagSet *pflag.FlagSet
 }
 
 // AddTo - Add the helm operator flags to the the flagset
 func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
+	// Store flagset internally to be used for lookups later.
+	f.flagSet = flagSet
+
+	// Helm flags.
+	flagSet.StringVar(&f.WatchesFile,
+		"watches-file",
+		"./watches.yaml",
+		"Path to the watches file to use",
+	)
+
+	// Controller flags.
 	flagSet.DurationVar(&f.ReconcilePeriod,
 		"reconcile-period",
 		time.Minute,
 		"Default reconcile period for controllers",
 	)
-	flagSet.StringVar(&f.WatchesFile,
-		"watches-file",
-		"./watches.yaml",
-		"Path to the watches file to use",
+	flagSet.IntVar(&f.MaxConcurrentReconciles,
+		"max-concurrent-reconciles",
+		runtime.NumCPU(),
+		"Maximum number of concurrent reconciles for controllers.",
 	)
-	// todo:remove it for 2.0.0
+
+	// Controller manager flags.
+	flagSet.StringVar(&f.ManagerConfigPath,
+		"config",
+		"",
+		"The controller will load its initial configuration from this file. "+
+			"Omit this flag to use the default configuration values. "+
+			"Command-line flags override configuration from this file.",
+	)
+	// TODO(2.0.0): remove
 	flagSet.StringVar(&f.MetricsBindAddress,
 		"metrics-addr",
 		":8080",
@@ -57,15 +86,14 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
 		":8080",
 		"The address the metric endpoint binds to",
 	)
-	// todo: for Go/Helm the port used is: 8081
+	// TODO(2.0.0): for Go/Helm the port used is: 8081
 	// update it to keep the project aligned to the other
-	// types for 2.0
 	flagSet.StringVar(&f.ProbeAddr,
 		"health-probe-bind-address",
 		":8081",
 		"The address the probe endpoint binds to.",
 	)
-	// todo:remove it for 2.0.0
+	// TODO(2.0.0): remove
 	flagSet.BoolVar(&f.LeaderElection,
 		"enable-leader-election",
 		false,
@@ -91,9 +119,40 @@ func (f *Flags) AddTo(flagSet *pflag.FlagSet) {
 			" holding the leader lock (required if running locally with leader"+
 			" election enabled).",
 	)
-	flagSet.IntVar(&f.MaxConcurrentReconciles,
-		"max-concurrent-reconciles",
-		runtime.NumCPU(),
-		"Maximum number of concurrent reconciles for controllers.",
-	)
+}
+
+// ToManagerOptions uses the flag set in f to configure options.
+// Values of options take precedence over flag defaults,
+// as values are assume to have been explicitly set.
+func (f *Flags) ToManagerOptions(options manager.Options) manager.Options {
+	// Alias FlagSet.Changed so options are still updated when fields are empty.
+	changed := func(flagName string) bool {
+		return f.flagSet.Changed(flagName)
+	}
+	if f.flagSet == nil {
+		changed = func(flagName string) bool { return false }
+	}
+
+	// TODO(2.0.0): remove metrics-addr
+	if changed("metrics-bind-address") || changed("metrics-addr") || options.MetricsBindAddress == "" {
+		options.MetricsBindAddress = f.MetricsBindAddress
+	}
+	if changed("health-probe-bind-address") || options.HealthProbeBindAddress == "" {
+		options.HealthProbeBindAddress = f.ProbeAddr
+	}
+	// TODO(2.0.0): remove enable-leader-election
+	if changed("leader-elect") || changed("enable-leader-election") || !options.LeaderElection {
+		options.LeaderElection = f.LeaderElection
+	}
+	if changed("leader-election-id") || options.LeaderElectionID == "" {
+		options.LeaderElectionID = f.LeaderElectionID
+	}
+	if changed("leader-election-namespace") || options.LeaderElectionNamespace == "" {
+		options.LeaderElectionNamespace = f.LeaderElectionNamespace
+	}
+	if options.LeaderElectionResourceLock == "" {
+		options.LeaderElectionResourceLock = resourcelock.ConfigMapsResourceLock
+	}
+
+	return options
 }
diff --git a/internal/helm/manifestutil/resource_policy.go b/internal/helm/manifestutil/resource_policy.go
new file mode 100644
index 00000000..320d9f4d
--- /dev/null
+++ b/internal/helm/manifestutil/resource_policy.go
@@ -0,0 +1,44 @@
+/*
+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 manifestutil
+
+import (
+	"strings"
+
+	"helm.sh/helm/v3/pkg/kube"
+	"helm.sh/helm/v3/pkg/releaseutil"
+)
+
+// Source from https://github.com/helm/helm/blob/v3.4.2/pkg/action/resource_policy.go
+func FilterManifestsToKeep(manifests []releaseutil.Manifest) (keep, remaining []releaseutil.Manifest) {
+	for _, m := range manifests {
+		if m.Head.Metadata == nil || m.Head.Metadata.Annotations == nil || len(m.Head.Metadata.Annotations) == 0 {
+			remaining = append(remaining, m)
+			continue
+		}
+
+		resourcePolicyType, ok := m.Head.Metadata.Annotations[kube.ResourcePolicyAnno]
+		if !ok {
+			remaining = append(remaining, m)
+			continue
+		}
+
+		resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType))
+		if resourcePolicyType == kube.KeepPolicy {
+			keep = append(keep, m)
+		}
+
+	}
+	return keep, remaining
+}
diff --git a/internal/helm/release/manager.go b/internal/helm/release/manager.go
index c2968e4d..2d494f42 100644
--- a/internal/helm/release/manager.go
+++ b/internal/helm/release/manager.go
@@ -26,8 +26,8 @@ import (
 	"helm.sh/helm/v3/pkg/action"
 	cpb "helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/kube"
-	helmkube "helm.sh/helm/v3/pkg/kube"
 	rpb "helm.sh/helm/v3/pkg/release"
+	"helm.sh/helm/v3/pkg/releaseutil"
 	"helm.sh/helm/v3/pkg/storage"
 	"helm.sh/helm/v3/pkg/storage/driver"
 	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -36,10 +36,13 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	apitypes "k8s.io/apimachinery/pkg/types"
+	apiutilerrors "k8s.io/apimachinery/pkg/util/errors"
 	"k8s.io/apimachinery/pkg/util/strategicpatch"
 	"k8s.io/cli-runtime/pkg/resource"
+	"k8s.io/client-go/discovery"
 
 	"github.com/operator-framework/operator-sdk/internal/helm/internal/types"
+	"github.com/operator-framework/operator-sdk/internal/helm/manifestutil"
 )
 
 // Manager manages a Helm release. It can install, upgrade, reconcile,
@@ -53,6 +56,7 @@ type Manager interface {
 	UpgradeRelease(context.Context, ...UpgradeOption) (*rpb.Release, *rpb.Release, error)
 	ReconcileRelease(context.Context) (*rpb.Release, error)
 	UninstallRelease(context.Context, ...UninstallOption) (*rpb.Release, error)
+	CleanupRelease(context.Context, string) (bool, error)
 }
 
 type manager struct {
@@ -293,7 +297,7 @@ func createPatch(existing runtime.Object, expected *resource.Info) ([]byte, apit
 	}
 
 	// Get a versioned object
-	versionedObject := helmkube.AsVersioned(expected)
+	versionedObject := kube.AsVersioned(expected)
 
 	// Unstructured objects, such as CRDs, may not have an not registered error
 	// returned from ConvertToVersion. Anything that's unstructured should
@@ -363,3 +367,50 @@ func (m manager) UninstallRelease(ctx context.Context, opts ...UninstallOption)
 	}
 	return uninstallResponse.Release, err
 }
+
+// CleanupRelease deletes resources if they are not deleted already.
+// Return true if all the resources are deleted, false otherwise.
+func (m manager) CleanupRelease(ctx context.Context, manifest string) (bool, error) {
+	dc, err := m.actionConfig.RESTClientGetter.ToDiscoveryClient()
+	if err != nil {
+		return false, fmt.Errorf("failed to get Kubernetes discovery client: %w", err)
+	}
+	apiVersions, err := action.GetVersionSet(dc)
+	if err != nil && !discovery.IsGroupDiscoveryFailedError(err) {
+		return false, fmt.Errorf("failed to get apiVersions from Kubernetes: %w", err)
+	}
+	manifests := releaseutil.SplitManifests(manifest)
+	_, files, err := releaseutil.SortManifests(manifests, apiVersions, releaseutil.UninstallOrder)
+	if err != nil {
+		return false, fmt.Errorf("failed to sort manifests: %w", err)
+	}
+	// do not delete resources that are annotated with the Helm resource policy 'keep'
+	_, filesToDelete := manifestutil.FilterManifestsToKeep(files)
+	var builder strings.Builder
+	for _, file := range filesToDelete {
+		builder.WriteString("\n---\n" + file.Content)
+	}
+	resources, err := m.kubeClient.Build(strings.NewReader(builder.String()), false)
+	if err != nil {
+		return false, fmt.Errorf("failed to build resources from manifests: %w", err)
+	}
+	if resources == nil || len(resources) <= 0 {
+		return true, nil
+	}
+	for _, resource := range resources {
+		err = resource.Get()
+		if err != nil {
+			if apierrors.IsNotFound(err) {
+				continue // resource is already delete, check the next one.
+			}
+			return false, fmt.Errorf("failed to get resource: %w", err)
+		}
+		// found at least one resource that is not deleted so just delete everything again.
+		_, errs := m.kubeClient.Delete(resources)
+		if len(errs) > 0 {
+			return false, fmt.Errorf("failed to delete resources: %v", apiutilerrors.NewAggregate(errs))
+		}
+		return false, nil
+	}
+	return true, nil
+}
diff --git a/internal/plugins/ansible/v1/api.go b/internal/plugins/ansible/v1/api.go
index 89db9c8a..5f1e8d77 100644
--- a/internal/plugins/ansible/v1/api.go
+++ b/internal/plugins/ansible/v1/api.go
@@ -26,6 +26,7 @@ import (
 	pluginutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds"
+	"github.com/operator-framework/operator-sdk/internal/plugins/util"
 )
 
 const (
@@ -135,6 +136,13 @@ func (p *createAPISubcommand) InjectResource(res *resource.Resource) error {
 }
 
 func (p *createAPISubcommand) Scaffold(fs machinery.Filesystem) error {
+	if err := util.RemoveKustomizeCRDManifests(); err != nil {
+		return fmt.Errorf("error removing kustomization CRD manifests: %v", err)
+	}
+	if err := util.UpdateKustomizationsCreateAPI(); err != nil {
+		return fmt.Errorf("error updating kustomization.yaml files: %v", err)
+	}
+
 	scaffolder := scaffolds.NewCreateAPIScaffolder(p.config, *p.resource, p.options.DoRole, p.options.DoPlaybook)
 	scaffolder.InjectFS(fs)
 	if err := scaffolder.Scaffold(); err != nil {
diff --git a/internal/plugins/ansible/v1/init.go b/internal/plugins/ansible/v1/init.go
index 8b67e8ab..207c8dd4 100644
--- a/internal/plugins/ansible/v1/init.go
+++ b/internal/plugins/ansible/v1/init.go
@@ -26,6 +26,7 @@ import (
 	"sigs.k8s.io/kubebuilder/v3/pkg/plugin/util"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds"
+	sdkpluginutil "github.com/operator-framework/operator-sdk/internal/plugins/util"
 	sdkutil "github.com/operator-framework/operator-sdk/internal/util"
 )
 
@@ -109,7 +110,7 @@ func (p *initSubcommand) InjectConfig(c config.Config) error {
 
 func (p *initSubcommand) Scaffold(fs machinery.Filesystem) error {
 	if err := addInitCustomizations(p.config.GetProjectName()); err != nil {
-		return fmt.Errorf("unable to scaffold the ansible customizations : %s", err)
+		return fmt.Errorf("error updating init manifests: %s", err)
 	}
 
 	scaffolder := scaffolds.NewInitScaffolder(p.config)
@@ -164,7 +165,8 @@ func addInitCustomizations(projectName string) error {
 	if err != nil {
 		return err
 	}
-	err = sdkutil.InsertCode("config/default/manager_auth_proxy_patch.yaml",
+	managerProxyPatchFile := filepath.Join("config", "default", "manager_auth_proxy_patch.yaml")
+	err = sdkutil.InsertCode(managerProxyPatchFile,
 		"- \"--leader-elect\"",
 		fmt.Sprintf("\n        - \"--leader-election-id=%s\"", projectName))
 	if err != nil {
@@ -203,17 +205,18 @@ func addInitCustomizations(projectName string) error {
 	if err != nil {
 		return err
 	}
-	err = sdkutil.ReplaceInFile("config/default/manager_auth_proxy_patch.yaml", "8081", "6789")
+	err = sdkutil.ReplaceInFile(managerProxyPatchFile, "8081", "6789")
 	if err != nil {
 		return err
 	}
-	err = sdkutil.ReplaceInFile("config/manager/controller_manager_config.yaml", "8081", "6789")
+
+	managerConfigFile := filepath.Join("config", "manager", "controller_manager_config.yaml")
+	err = sdkutil.ReplaceInFile(managerConfigFile, "8081", "6789")
 	if err != nil {
 		return err
 	}
-
 	// Remove the webhook option for the componentConfig since webhooks are not supported by ansible
-	err = sdkutil.ReplaceInFile("config/manager/controller_manager_config.yaml", "webhook:\n  port: 9443", "")
+	err = sdkutil.ReplaceInFile(managerConfigFile, "webhook:\n  port: 9443", "")
 	if err != nil {
 		return err
 	}
@@ -228,5 +231,9 @@ func addInitCustomizations(projectName string) error {
 		return err
 	}
 
+	if err := sdkpluginutil.UpdateKustomizationsInit(); err != nil {
+		return fmt.Errorf("error updating kustomization.yaml files: %v", err)
+	}
+
 	return nil
 }
diff --git a/internal/plugins/ansible/v1/scaffolds/api.go b/internal/plugins/ansible/v1/scaffolds/api.go
index a3529f3d..f6902bf2 100644
--- a/internal/plugins/ansible/v1/scaffolds/api.go
+++ b/internal/plugins/ansible/v1/scaffolds/api.go
@@ -27,7 +27,6 @@ import (
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates"
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/config/crd"
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac"
-	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/config/samples"
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault"
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/playbooks"
 	ansibleroles "github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/roles"
@@ -74,22 +73,17 @@ func (s *apiScaffolder) Scaffold() error {
 		machinery.WithResource(&s.resource),
 	)
 
-	var createAPITemplates []machinery.Builder
-	createAPITemplates = append(createAPITemplates,
-		&rbac.CRDViewerRole{},
-		&rbac.CRDEditorRole{},
+	createAPITemplates := []machinery.Builder{
 		&rbac.ManagerRoleUpdater{},
-
 		&crd.CRD{},
 		&crd.Kustomization{},
-		&samples.CR{},
 		&templates.WatchesUpdater{
 			GeneratePlaybook: s.doPlaybook,
 			GenerateRole:     s.doRole,
 			PlaybooksDir:     constants.PlaybooksDir,
 		},
 		&mdefault.ResourceTest{},
-	)
+	}
 
 	if s.doRole {
 		createAPITemplates = append(createAPITemplates,
diff --git a/internal/plugins/ansible/v1/scaffolds/init.go b/internal/plugins/ansible/v1/scaffolds/init.go
index 661585f9..0b3a15d7 100644
--- a/internal/plugins/ansible/v1/scaffolds/init.go
+++ b/internal/plugins/ansible/v1/scaffolds/init.go
@@ -23,7 +23,6 @@ import (
 	"sigs.k8s.io/kubebuilder/v3/pkg/plugins"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates"
-	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault"
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac"
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing"
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy"
@@ -85,10 +84,8 @@ func (s *initScaffolder) Scaffold() error {
 		&templates.RequirementsYml{},
 		&templates.Watches{},
 		&rbac.ManagerRole{},
-		&kdefault.Kustomization{},
 		&roles.Placeholder{},
 		&playbooks.Placeholder{},
-
 		&mdefault.Converge{},
 		&mdefault.Create{},
 		&mdefault.Destroy{},
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/crd/kustomization.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/crd/kustomization.go
index 246c5c78..871c00e1 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/crd/kustomization.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/crd/kustomization.go
@@ -40,11 +40,8 @@ func (f *Kustomization) SetTemplateDefaults() error {
 	if f.Path == "" {
 		f.Path = filepath.Join("config", "crd", "kustomization.yaml")
 	}
-	f.Path = f.Resource.Replacer().Replace(f.Path)
 
-	f.TemplateBody = fmt.Sprintf(kustomizationTemplate,
-		machinery.NewMarkerFor(f.Path, resourceMarker),
-	)
+	f.TemplateBody = fmt.Sprintf(kustomizationTemplate, machinery.NewMarkerFor(f.Path, resourceMarker))
 
 	return nil
 }
@@ -53,7 +50,7 @@ const (
 	resourceMarker = "crdkustomizeresource"
 )
 
-// GetMarkers implements file.Inserter
+// GetMarkers implements machinery.Inserter
 func (f *Kustomization) GetMarkers() []machinery.Marker {
 	return []machinery.Marker{
 		machinery.NewMarkerFor(f.Path, resourceMarker),
@@ -65,20 +62,14 @@ const (
 `
 )
 
-// GetCodeFragments implements file.Inserter
+// GetCodeFragments implements machinery.Inserter
 func (f *Kustomization) GetCodeFragments() machinery.CodeFragmentsMap {
-	fragments := make(machinery.CodeFragmentsMap, 3)
-
-	// Generate resource code fragments
-	res := make([]string, 0)
-	res = append(res, fmt.Sprintf(resourceCodeFragment, f.Resource.QualifiedGroup(), f.Resource.Plural))
-
-	// Only store code fragments in the map if the slices are non-empty
-	if len(res) != 0 {
-		fragments[machinery.NewMarkerFor(f.Path, resourceMarker)] = res
+	return machinery.CodeFragmentsMap{
+		// Generate resource code fragments
+		machinery.NewMarkerFor(f.Path, resourceMarker): []string{
+			fmt.Sprintf(resourceCodeFragment, f.Resource.QualifiedGroup(), f.Resource.Plural),
+		},
 	}
-
-	return fragments
 }
 
 var kustomizationTemplate = `# This kustomization.yaml is not intended to be run by itself,
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault/kustomization.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault/kustomization.go
deleted file mode 100644
index 869347d5..00000000
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault/kustomization.go
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-Copyright 2018 The Kubernetes Authors.
-Modifications copyright 2020 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 kdefault
-
-import (
-	"path/filepath"
-
-	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
-)
-
-var _ machinery.Template = &Kustomization{}
-
-// Kustomization scaffolds the kustomization file for the default overlay
-type Kustomization struct {
-	machinery.TemplateMixin
-	machinery.ProjectNameMixin
-}
-
-// SetTemplateDefaults implements machinery.Template
-func (f *Kustomization) SetTemplateDefaults() error {
-	if f.Path == "" {
-		f.Path = filepath.Join("config", "default", "kustomization.yaml")
-	}
-
-	f.TemplateBody = kustomizeTemplate
-
-	// For Anible/Helm is no supported webhooks then, we customize
-	// it in the init
-	f.IfExistsAction = machinery.OverwriteFile
-
-	return nil
-}
-
-const kustomizeTemplate = `# Adds namespace to all resources.
-namespace: {{ .ProjectName }}-system
-
-# Value of this field is prepended to the
-# names of all resources, e.g. a deployment named
-# "wordpress" becomes "alices-wordpress".
-# Note that it should also match with the prefix (text before '-') of the namespace
-# field above.
-namePrefix: {{ .ProjectName }}-
-
-# Labels to add to all resources and selectors.
-#commonLabels:
-#  someName: someValue
-
-bases:
-- ../crd
-- ../rbac
-- ../manager
-# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
-#- ../prometheus
-
-patchesStrategicMerge:
-# Protect the /metrics endpoint by putting it behind auth.
-# If you want your controller-manager to expose the /metrics
-# endpoint w/o any authn/z, please comment the following line.
-- manager_auth_proxy_patch.yaml
-`
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/crd_editor_rbac.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/crd_editor_rbac.go
deleted file mode 100644
index 13a868b8..00000000
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/crd_editor_rbac.go
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-Copyright 2018 The Kubernetes 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 rbac
-
-import (
-	"path/filepath"
-
-	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
-)
-
-var _ machinery.Template = &CRDEditorRole{}
-
-// CRDEditorRole scaffolds the config/rbac/<kind>_editor_role.yaml
-type CRDEditorRole struct {
-	machinery.TemplateMixin
-	machinery.ResourceMixin
-}
-
-// SetTemplateDefaults implements machinery.Template
-func (f *CRDEditorRole) SetTemplateDefaults() error {
-	if f.Path == "" {
-		f.Path = filepath.Join("config", "rbac", "%[kind]_editor_role.yaml")
-	}
-	f.Path = f.Resource.Replacer().Replace(f.Path)
-
-	f.TemplateBody = crdRoleEditorTemplate
-
-	return nil
-}
-
-const crdRoleEditorTemplate = `# permissions for end users to edit {{ .Resource.Plural }}.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: {{ lower .Resource.Kind }}-editor-role
-rules:
-- apiGroups:
-  - {{ .Resource.QualifiedGroup }}
-  resources:
-  - {{ .Resource.Plural }}
-  verbs:
-  - create
-  - delete
-  - get
-  - list
-  - patch
-  - update
-  - watch
-- apiGroups:
-  - {{ .Resource.QualifiedGroup }}
-  resources:
-  - {{ .Resource.Plural }}/status
-  verbs:
-  - get
-`
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/crd_viewer_rbac.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/crd_viewer_rbac.go
deleted file mode 100644
index b0fe36d5..00000000
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/crd_viewer_rbac.go
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-Copyright 2018 The Kubernetes 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 rbac
-
-import (
-	"path/filepath"
-
-	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
-)
-
-var _ machinery.Template = &CRDViewerRole{}
-
-// CRDViewerRole scaffolds the config/rbac/<kind>_viewer_role.yaml
-type CRDViewerRole struct {
-	machinery.TemplateMixin
-	machinery.ResourceMixin
-}
-
-// SetTemplateDefaults implements machinery.Template
-func (f *CRDViewerRole) SetTemplateDefaults() error {
-	if f.Path == "" {
-		f.Path = filepath.Join("config", "rbac", "%[kind]_viewer_role.yaml")
-	}
-	f.Path = f.Resource.Replacer().Replace(f.Path)
-
-	f.TemplateBody = crdRoleViewerTemplate
-
-	return nil
-}
-
-const crdRoleViewerTemplate = `# permissions for end users to view {{ .Resource.Plural }}.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: {{ lower .Resource.Kind }}-viewer-role
-rules:
-- apiGroups:
-  - {{ .Resource.QualifiedGroup }}
-  resources:
-  - {{ .Resource.Plural }}
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - {{ .Resource.QualifiedGroup }}
-  resources:
-  - {{ .Resource.Plural }}/status
-  verbs:
-  - get
-`
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/role.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/role.go
index d1e18324..e7cc3bea 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/role.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/role.go
@@ -38,9 +38,8 @@ func (f *ManagerRole) SetTemplateDefaults() error {
 		f.Path = defaultRoleFile
 	}
 
-	f.TemplateBody = fmt.Sprintf(roleTemplate,
-		machinery.NewMarkerFor(f.Path, rulesMarker),
-	)
+	f.TemplateBody = fmt.Sprintf(roleTemplate, machinery.NewMarkerFor(f.Path, rulesMarker))
+
 	return nil
 }
 
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/samples/cr_sample.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/samples/cr_sample.go
deleted file mode 100644
index 54156fa9..00000000
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/samples/cr_sample.go
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-Copyright 2018 The Kubernetes Authors.
-Modifications copyright 2020 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 samples
-
-import (
-	"path/filepath"
-	"strings"
-	"text/template"
-
-	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
-)
-
-var (
-	_ machinery.Template         = &CR{}
-	_ machinery.UseCustomFuncMap = &CR{}
-)
-
-// CR scaffolds a sample manifest for a CRD.
-type CR struct {
-	machinery.TemplateMixin
-	machinery.ResourceMixin
-
-	Spec string
-}
-
-// SetTemplateDefaults implements machinery.Template
-func (f *CR) SetTemplateDefaults() error {
-	if f.Path == "" {
-		f.Path = filepath.Join("config", "samples", "%[group]_%[version]_%[kind].yaml")
-	}
-	f.Path = f.Resource.Replacer().Replace(f.Path)
-
-	f.IfExistsAction = machinery.Error
-
-	if len(f.Spec) == 0 {
-		f.Spec = defaultSpecTemplate
-	}
-
-	f.TemplateBody = crSampleTemplate
-	return nil
-}
-
-func indent(spaces int, v string) string {
-	pad := strings.Repeat(" ", spaces)
-	return pad + strings.Replace(v, "\n", "\n"+pad, -1)
-}
-
-// GetFuncMap implements machinery.UseCustomFuncMap
-func (f *CR) GetFuncMap() template.FuncMap {
-	fm := machinery.DefaultFuncMap()
-	fm["indent"] = indent
-	return fm
-}
-
-const defaultSpecTemplate = `foo: bar`
-
-const crSampleTemplate = `apiVersion: {{ .Resource.QualifiedGroup }}/{{ .Resource.Version }}
-kind: {{ .Resource.Kind }}
-metadata:
-  name: {{ lower .Resource.Kind }}-sample
-spec:
-{{ .Spec | indent 2 }}
-`
diff --git a/internal/plugins/helm/v1/api.go b/internal/plugins/helm/v1/api.go
index 1403be1b..9191ea57 100644
--- a/internal/plugins/helm/v1/api.go
+++ b/internal/plugins/helm/v1/api.go
@@ -30,6 +30,7 @@ import (
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/chartutil"
 	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/scaffolds"
+	"github.com/operator-framework/operator-sdk/internal/plugins/util"
 )
 
 const (
@@ -200,6 +201,13 @@ func (p *createAPISubcommand) InjectResource(res *resource.Resource) error {
 }
 
 func (p *createAPISubcommand) Scaffold(fs machinery.Filesystem) error {
+	if err := util.RemoveKustomizeCRDManifests(); err != nil {
+		return fmt.Errorf("error removing kustomization CRD manifests: %v", err)
+	}
+	if err := util.UpdateKustomizationsCreateAPI(); err != nil {
+		return fmt.Errorf("error updating kustomization.yaml files: %v", err)
+	}
+
 	scaffolder := scaffolds.NewAPIScaffolder(p.config, *p.resource, p.chart)
 	scaffolder.InjectFS(fs)
 	if err := scaffolder.Scaffold(); err != nil {
diff --git a/internal/plugins/helm/v1/init.go b/internal/plugins/helm/v1/init.go
index d86be6d8..bb5bdae7 100644
--- a/internal/plugins/helm/v1/init.go
+++ b/internal/plugins/helm/v1/init.go
@@ -26,6 +26,7 @@ import (
 	"sigs.k8s.io/kubebuilder/v3/pkg/plugin/util"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/scaffolds"
+	sdkpluginutil "github.com/operator-framework/operator-sdk/internal/plugins/util"
 	sdkutil "github.com/operator-framework/operator-sdk/internal/util"
 )
 
@@ -131,7 +132,7 @@ func (p *initSubcommand) InjectConfig(c config.Config) error {
 
 func (p *initSubcommand) Scaffold(fs machinery.Filesystem) error {
 	if err := addInitCustomizations(p.config.GetProjectName()); err != nil {
-		return fmt.Errorf("unable to scaffold the helm customizations : %s", err)
+		return fmt.Errorf("error updating init manifests: %s", err)
 	}
 
 	scaffolder := scaffolds.NewInitScaffolder(p.config)
@@ -190,8 +191,7 @@ func addInitCustomizations(projectName string) error {
 	if err != nil {
 		return err
 	}
-
-	err = sdkutil.InsertCode("config/default/manager_auth_proxy_patch.yaml",
+	err = sdkutil.InsertCode(filepath.Join("config", "default", "manager_auth_proxy_patch.yaml"),
 		"- \"--leader-elect\"",
 		fmt.Sprintf("\n        - \"--leader-election-id=%s\"", projectName))
 	if err != nil {
@@ -208,8 +208,9 @@ func addInitCustomizations(projectName string) error {
 		return err
 	}
 
-	// Remove the webhook option for the componentConfig since webhooks are not supported by helm
-	err = sdkutil.ReplaceInFile("config/manager/controller_manager_config.yaml", "webhook:\n  port: 9443", "")
+	// Remove the webhook option for the componentConfig since webhooks are not supported by ansible
+	err = sdkutil.ReplaceInFile(filepath.Join("config", "manager", "controller_manager_config.yaml"),
+		"webhook:\n  port: 9443", "")
 	if err != nil {
 		return err
 	}
@@ -224,5 +225,9 @@ func addInitCustomizations(projectName string) error {
 		return err
 	}
 
+	if err := sdkpluginutil.UpdateKustomizationsInit(); err != nil {
+		return fmt.Errorf("error updating kustomization.yaml files: %v", err)
+	}
+
 	return nil
 }
diff --git a/internal/plugins/helm/v1/scaffolds/api.go b/internal/plugins/helm/v1/scaffolds/api.go
index 536864be..194668f5 100644
--- a/internal/plugins/helm/v1/scaffolds/api.go
+++ b/internal/plugins/helm/v1/scaffolds/api.go
@@ -93,10 +93,8 @@ func (s *apiScaffolder) Scaffold() error {
 		&templates.WatchesUpdater{ChartPath: chartPath},
 		&crd.CRD{},
 		&crd.Kustomization{},
-		&rbac.CRDEditorRole{},
-		&rbac.CRDViewerRole{},
 		&rbac.ManagerRoleUpdater{Chart: s.chrt},
-		&samples.CRDSample{ChartPath: chartPath, Chart: s.chrt},
+		&samples.CustomResource{ChartPath: chartPath, Chart: s.chrt},
 	); err != nil {
 		return fmt.Errorf("error scaffolding APIs: %w", err)
 	}
diff --git a/internal/plugins/helm/v1/scaffolds/init.go b/internal/plugins/helm/v1/scaffolds/init.go
index 69e2c77c..c1a7eec5 100644
--- a/internal/plugins/helm/v1/scaffolds/init.go
+++ b/internal/plugins/helm/v1/scaffolds/init.go
@@ -26,7 +26,6 @@ import (
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/chartutil"
 	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/scaffolds/internal/templates"
-	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault"
 	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac"
 	"github.com/operator-framework/operator-sdk/internal/version"
 )
@@ -86,6 +85,5 @@ func (s *initScaffolder) Scaffold() error {
 		},
 		&templates.Watches{},
 		&rbac.ManagerRole{},
-		&kdefault.Kustomization{},
 	)
 }
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/crd/kustomization.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/crd/kustomization.go
index 246c5c78..871c00e1 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/crd/kustomization.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/crd/kustomization.go
@@ -40,11 +40,8 @@ func (f *Kustomization) SetTemplateDefaults() error {
 	if f.Path == "" {
 		f.Path = filepath.Join("config", "crd", "kustomization.yaml")
 	}
-	f.Path = f.Resource.Replacer().Replace(f.Path)
 
-	f.TemplateBody = fmt.Sprintf(kustomizationTemplate,
-		machinery.NewMarkerFor(f.Path, resourceMarker),
-	)
+	f.TemplateBody = fmt.Sprintf(kustomizationTemplate, machinery.NewMarkerFor(f.Path, resourceMarker))
 
 	return nil
 }
@@ -53,7 +50,7 @@ const (
 	resourceMarker = "crdkustomizeresource"
 )
 
-// GetMarkers implements file.Inserter
+// GetMarkers implements machinery.Inserter
 func (f *Kustomization) GetMarkers() []machinery.Marker {
 	return []machinery.Marker{
 		machinery.NewMarkerFor(f.Path, resourceMarker),
@@ -65,20 +62,14 @@ const (
 `
 )
 
-// GetCodeFragments implements file.Inserter
+// GetCodeFragments implements machinery.Inserter
 func (f *Kustomization) GetCodeFragments() machinery.CodeFragmentsMap {
-	fragments := make(machinery.CodeFragmentsMap, 3)
-
-	// Generate resource code fragments
-	res := make([]string, 0)
-	res = append(res, fmt.Sprintf(resourceCodeFragment, f.Resource.QualifiedGroup(), f.Resource.Plural))
-
-	// Only store code fragments in the map if the slices are non-empty
-	if len(res) != 0 {
-		fragments[machinery.NewMarkerFor(f.Path, resourceMarker)] = res
+	return machinery.CodeFragmentsMap{
+		// Generate resource code fragments
+		machinery.NewMarkerFor(f.Path, resourceMarker): []string{
+			fmt.Sprintf(resourceCodeFragment, f.Resource.QualifiedGroup(), f.Resource.Plural),
+		},
 	}
-
-	return fragments
 }
 
 var kustomizationTemplate = `# This kustomization.yaml is not intended to be run by itself,
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault/kustomization.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault/kustomization.go
deleted file mode 100644
index 42e9e380..00000000
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault/kustomization.go
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-Copyright 2018 The Kubernetes Authors.
-Modifications copyright 2020 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 kdefault
-
-import (
-	"path/filepath"
-
-	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
-)
-
-var _ machinery.Template = &Kustomization{}
-
-// Kustomization scaffolds the Kustomization file for the default overlay
-type Kustomization struct {
-	machinery.TemplateMixin
-	machinery.ProjectNameMixin
-}
-
-// SetTemplateDefaults implements machinery.Template
-func (f *Kustomization) SetTemplateDefaults() error {
-	if f.Path == "" {
-		f.Path = filepath.Join("config", "default", "kustomization.yaml")
-	}
-
-	f.TemplateBody = kustomizeTemplate
-
-	// For Anible/Helm is no supported webhooks then, we customize
-	// it in the init
-	f.IfExistsAction = machinery.OverwriteFile
-
-	return nil
-}
-
-const kustomizeTemplate = `# Adds namespace to all resources.
-namespace: {{ .ProjectName }}-system
-
-# Value of this field is prepended to the
-# names of all resources, e.g. a deployment named
-# "wordpress" becomes "alices-wordpress".
-# Note that it should also match with the prefix (text before '-') of the namespace
-# field above.
-namePrefix: {{ .ProjectName }}-
-
-# Labels to add to all resources and selectors.
-#commonLabels:
-#  someName: someValue
-
-bases:
-- ../crd
-- ../rbac
-- ../manager
-# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
-#- ../prometheus
-
-patchesStrategicMerge:
-# Protect the /metrics endpoint by putting it behind auth.
-# If you want your controller-manager to expose the /metrics
-# endpoint w/o any authn/z, please comment the following line.
-- manager_auth_proxy_patch.yaml
-`
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/crd_editor_rbac.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/crd_editor_rbac.go
deleted file mode 100644
index 13a868b8..00000000
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/crd_editor_rbac.go
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-Copyright 2018 The Kubernetes 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 rbac
-
-import (
-	"path/filepath"
-
-	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
-)
-
-var _ machinery.Template = &CRDEditorRole{}
-
-// CRDEditorRole scaffolds the config/rbac/<kind>_editor_role.yaml
-type CRDEditorRole struct {
-	machinery.TemplateMixin
-	machinery.ResourceMixin
-}
-
-// SetTemplateDefaults implements machinery.Template
-func (f *CRDEditorRole) SetTemplateDefaults() error {
-	if f.Path == "" {
-		f.Path = filepath.Join("config", "rbac", "%[kind]_editor_role.yaml")
-	}
-	f.Path = f.Resource.Replacer().Replace(f.Path)
-
-	f.TemplateBody = crdRoleEditorTemplate
-
-	return nil
-}
-
-const crdRoleEditorTemplate = `# permissions for end users to edit {{ .Resource.Plural }}.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: {{ lower .Resource.Kind }}-editor-role
-rules:
-- apiGroups:
-  - {{ .Resource.QualifiedGroup }}
-  resources:
-  - {{ .Resource.Plural }}
-  verbs:
-  - create
-  - delete
-  - get
-  - list
-  - patch
-  - update
-  - watch
-- apiGroups:
-  - {{ .Resource.QualifiedGroup }}
-  resources:
-  - {{ .Resource.Plural }}/status
-  verbs:
-  - get
-`
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/crd_viewer_rbac.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/crd_viewer_rbac.go
deleted file mode 100644
index b0fe36d5..00000000
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/crd_viewer_rbac.go
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-Copyright 2018 The Kubernetes 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 rbac
-
-import (
-	"path/filepath"
-
-	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
-)
-
-var _ machinery.Template = &CRDViewerRole{}
-
-// CRDViewerRole scaffolds the config/rbac/<kind>_viewer_role.yaml
-type CRDViewerRole struct {
-	machinery.TemplateMixin
-	machinery.ResourceMixin
-}
-
-// SetTemplateDefaults implements machinery.Template
-func (f *CRDViewerRole) SetTemplateDefaults() error {
-	if f.Path == "" {
-		f.Path = filepath.Join("config", "rbac", "%[kind]_viewer_role.yaml")
-	}
-	f.Path = f.Resource.Replacer().Replace(f.Path)
-
-	f.TemplateBody = crdRoleViewerTemplate
-
-	return nil
-}
-
-const crdRoleViewerTemplate = `# permissions for end users to view {{ .Resource.Plural }}.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: {{ lower .Resource.Kind }}-viewer-role
-rules:
-- apiGroups:
-  - {{ .Resource.QualifiedGroup }}
-  resources:
-  - {{ .Resource.Plural }}
-  verbs:
-  - get
-  - list
-  - watch
-- apiGroups:
-  - {{ .Resource.QualifiedGroup }}
-  resources:
-  - {{ .Resource.Plural }}/status
-  verbs:
-  - get
-`
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/manager_role.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/manager_role.go
index a3ee2b51..e5140183 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/manager_role.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/manager_role.go
@@ -51,9 +51,8 @@ func (f *ManagerRole) SetTemplateDefaults() error {
 		f.Path = defaultRoleFile
 	}
 
-	f.TemplateBody = fmt.Sprintf(roleTemplate,
-		machinery.NewMarkerFor(f.Path, rulesMarker),
-	)
+	f.TemplateBody = fmt.Sprintf(roleTemplate, machinery.NewMarkerFor(f.Path, rulesMarker))
+
 	return nil
 }
 
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/samples/crd_sample.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/samples/custom_resource.go
similarity index 80%
rename from internal/plugins/helm/v1/scaffolds/internal/templates/config/samples/crd_sample.go
rename to internal/plugins/helm/v1/scaffolds/internal/templates/config/samples/custom_resource.go
index 04aa6627..ec757433 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/samples/crd_sample.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/samples/custom_resource.go
@@ -29,12 +29,12 @@ import (
 )
 
 var (
-	_ machinery.Template         = &CRDSample{}
-	_ machinery.UseCustomFuncMap = &CRDSample{}
+	_ machinery.Template         = &CustomResource{}
+	_ machinery.UseCustomFuncMap = &CustomResource{}
 )
 
-// CRDSample scaffolds a manifest for CRD sample.
-type CRDSample struct {
+// CustomResource scaffolds a custom resource sample manifest.
+type CustomResource struct {
 	machinery.TemplateMixin
 	machinery.ResourceMixin
 
@@ -44,13 +44,13 @@ type CRDSample struct {
 }
 
 // SetTemplateDefaults implements machinery.Template
-func (f *CRDSample) SetTemplateDefaults() error {
+func (f *CustomResource) SetTemplateDefaults() error {
 	if f.Path == "" {
 		f.Path = filepath.Join("config", "samples", "%[group]_%[version]_%[kind].yaml")
 	}
 	f.Path = f.Resource.Replacer().Replace(f.Path)
 
-	f.IfExistsAction = machinery.Error
+	f.IfExistsAction = machinery.OverwriteFile
 
 	if len(f.Spec) == 0 {
 		f.Spec = defaultSpecTemplate
@@ -67,7 +67,7 @@ func (f *CRDSample) SetTemplateDefaults() error {
 		}
 	}
 
-	f.TemplateBody = crdSampleTemplate
+	f.TemplateBody = customResourceTemplate
 	return nil
 }
 
@@ -77,7 +77,7 @@ func indent(spaces int, v string) string {
 }
 
 // GetFuncMap implements machinery.UseCustomFuncMap
-func (f *CRDSample) GetFuncMap() template.FuncMap {
+func (f *CustomResource) GetFuncMap() template.FuncMap {
 	fm := machinery.DefaultFuncMap()
 	fm["indent"] = indent
 	return fm
@@ -86,7 +86,7 @@ func (f *CRDSample) GetFuncMap() template.FuncMap {
 const defaultSpecTemplate = `foo: bar
 `
 
-const crdSampleTemplate = `apiVersion: {{ .Resource.QualifiedGroup }}/{{ .Resource.Version }}
+const customResourceTemplate = `apiVersion: {{ .Resource.QualifiedGroup }}/{{ .Resource.Version }}
 kind: {{ .Resource.Kind }}
 metadata:
   name: {{ lower .Resource.Kind }}-sample
diff --git a/internal/plugins/util/cleanup.go b/internal/plugins/util/cleanup.go
new file mode 100644
index 00000000..0bda6c13
--- /dev/null
+++ b/internal/plugins/util/cleanup.go
@@ -0,0 +1,139 @@
+// 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 util
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+
+	log "github.com/sirupsen/logrus"
+
+	"github.com/operator-framework/operator-sdk/internal/util"
+)
+
+// RemoveKustomizeCRDManifests removes items in config/crd relating to CRD conversion webhooks.
+func RemoveKustomizeCRDManifests() error {
+
+	pathsToRemove := []string{
+		filepath.Join("config", "crd", "kustomizeconfig.yaml"),
+	}
+	configPatchesDir := filepath.Join("config", "crd", "patches")
+	webhookPatchMatches, err := filepath.Glob(filepath.Join(configPatchesDir, "webhook_in_*.yaml"))
+	if err != nil {
+		return err
+	}
+	pathsToRemove = append(pathsToRemove, webhookPatchMatches...)
+	cainjectionPatchMatches, err := filepath.Glob(filepath.Join(configPatchesDir, "cainjection_in_*.yaml"))
+	if err != nil {
+		return err
+	}
+	pathsToRemove = append(pathsToRemove, cainjectionPatchMatches...)
+	for _, p := range pathsToRemove {
+		if err := os.RemoveAll(p); err != nil {
+			return err
+		}
+	}
+	children, err := ioutil.ReadDir(configPatchesDir)
+	if err == nil && len(children) == 0 {
+		if err := os.RemoveAll(configPatchesDir); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// UpdateKustomizationsInit updates certain parts of or removes entire kustomization.yaml files
+// that are either not used by certain Init plugins or are created by preceding Init plugins.
+func UpdateKustomizationsInit() error {
+
+	defaultKFile := filepath.Join("config", "default", "kustomization.yaml")
+	if err := util.ReplaceInFile(defaultKFile,
+		`
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
+# crd/kustomization.yaml
+#- ../webhook
+# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
+#- ../certmanager`, ""); err != nil {
+		return fmt.Errorf("remove %s resources: %v", defaultKFile, err)
+	}
+
+	if err := util.ReplaceInFile(defaultKFile,
+		`
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
+# crd/kustomization.yaml
+#- manager_webhook_patch.yaml
+
+# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
+# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
+# 'CERTMANAGER' needs to be enabled to use ca injection
+#- webhookcainjection_patch.yaml
+
+# the following config is for teaching kustomize how to do var substitution
+vars:
+# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
+#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
+#  objref:
+#    kind: Certificate
+#    group: cert-manager.io
+#    version: v1
+#    name: serving-cert # this name should match the one in certificate.yaml
+#  fieldref:
+#    fieldpath: metadata.namespace
+#- name: CERTIFICATE_NAME
+#  objref:
+#    kind: Certificate
+#    group: cert-manager.io
+#    version: v1
+#    name: serving-cert # this name should match the one in certificate.yaml
+#- name: SERVICE_NAMESPACE # namespace of the service
+#  objref:
+#    kind: Service
+#    version: v1
+#    name: webhook-service
+#  fieldref:
+#    fieldpath: metadata.namespace
+#- name: SERVICE_NAME
+#  objref:
+#    kind: Service
+#    version: v1
+#    name: webhook-service
+`, ""); err != nil {
+		return fmt.Errorf("remove %s patch and vars blocks: %v", defaultKFile, err)
+	}
+
+	return nil
+}
+
+// UpdateKustomizationsCreateAPI updates certain parts of or removes entire kustomization.yaml files
+// that are either not used by certain CreateAPI plugins or are created by preceding CreateAPI plugins.
+func UpdateKustomizationsCreateAPI() error {
+
+	crdKFile := filepath.Join("config", "crd", "kustomization.yaml")
+	if crdKBytes, err := ioutil.ReadFile(crdKFile); err != nil && !errors.Is(err, os.ErrNotExist) {
+		log.Debugf("Error reading kustomization for substitution: %v", err)
+	} else if err == nil {
+		if bytes.Contains(crdKBytes, []byte("[WEBHOOK]")) || bytes.Contains(crdKBytes, []byte("[CERTMANAGER]")) {
+			if err := os.RemoveAll(crdKFile); err != nil {
+				log.Debugf("Error removing file prior to scaffold: %v", err)
+			}
+		}
+	}
+
+	return nil
+}
diff --git a/release/Makefile b/release/Makefile
index cd7da91a..15a404b1 100644
--- a/release/Makefile
+++ b/release/Makefile
@@ -24,7 +24,7 @@ release: ## Publish an operator-sdk release, with option for a dry run with DRY_
 ifeq (,$(GIT_VERSION))
 	$(error "GIT_VERSION must be set to a git tag")
 endif
-	$(SCRIPTS_DIR)/fetch goreleaser 0.147.2
+	$(SCRIPTS_DIR)/fetch goreleaser 0.156.0
 	GORELEASER_CURRENT_TAG=$(GIT_VERSION) $(TOOLS_DIR)/goreleaser $(SNAPSHOT_FLAGS) --release-notes=$(CHANGELOG) --parallelism 5
 ifneq ($(DRY_RUN),)
 	rm $(CHANGELOG)
diff --git a/website/config.toml b/website/config.toml
index 74ce4bdd..52f7410e 100644
--- a/website/config.toml
+++ b/website/config.toml
@@ -95,6 +95,10 @@ algolia_docsearch = true
 
 ##RELEASE_ADDME##
 
+[[params.versions]]
+  version = "v1.6"
+  url = "https://v1-6-x.sdk.operatorframework.io"
+
 [[params.versions]]
   version = "v1.5"
   url = "https://v1-5-x.sdk.operatorframework.io"
diff --git a/website/content/en/docs/advanced-topics/scorecard/scorecard.md b/website/content/en/docs/advanced-topics/scorecard/scorecard.md
index 99c1a393..17a4ec13 100644
--- a/website/content/en/docs/advanced-topics/scorecard/scorecard.md
+++ b/website/content/en/docs/advanced-topics/scorecard/scorecard.md
@@ -52,8 +52,8 @@ for an overview of command invocation.
 ## Configuration
 
 The scorecard test execution is driven by a configuration file named `config.yaml`, generated by `make bundle`.  Note that if run `make bundle` that any
-changes you have made to `config.yaml` will be overwritten.  To persist 
-any changes to `config.yaml` you can update the kustomize templates found in the 
+changes you have made to `config.yaml` will be overwritten.  To persist
+any changes to `config.yaml` you can update the kustomize templates found in the
 `config/scorecard` directory.
 The configuration file is located at the following location within your bundle directory (`bundle/` by default):
 ```sh
@@ -114,7 +114,7 @@ $ operator-sdk scorecard <bundle_dir_or_image> [flags]
 
 The scorecard requires a positional argument that holds either the
 on-disk path to your operator bundle or the name of a bundle image.  Note
-that the scorecard does not run your operator but merely uses 
+that the scorecard does not run your operator but merely uses
 the scorecard configuration within the bundle contents to know which tests
 to execute.
 
@@ -180,7 +180,7 @@ The scorecard ships with pre-defined tests that are arranged into suites.
 | --------    | -------- | -------- |
 | Bundle Validation | This test validates the bundle manifests found in the bundle that is passed into scorecard.  If the bundle contents contain errors, then the test result output will include the validator log as well as error messages from the validation library.  See this [document][olm-bundle] for details on bundles.| olm-bundle-validation-test |
 | Provided APIs have validation |This test verifies that the CRDs for the provided CRs contain a validation section and that there is validation for each spec and status field detected in the CR. | olm-crds-have-validation-test |
-| Owned CRDs Have Resources Listed | This test makes sure that the CRDs for each CR provided via the `cr-manifest` option have a `resources` subsection in the [`owned` CRDs section][owned-crds] of the CSV. If the test detects used resources that are not listed in the resources section, it will list them in the suggestions at the end of the test. Users are required to fill out the resources section after initial code generation for this test to pass.  | olm-crds-have-resources-test |
+| Owned CRDs Have Resources Listed | This test makes sure that the CRDs for each CR provided via the `cr-manifest` option have a `resources` subsection in the [`owned` CRDs section][owned-crds] of the CSV. If the test detects used resources that are not listed in the resources section, it will list them in the suggestions at the end of the test. Users are required to fill out the resources section after initial code generation for this test to pass. For Go-based operators, use ClusterServiceVersion [API Markers](/docs/building-operators/golang/references/markers) to add resources. | olm-crds-have-resources-test |
 | Spec Fields With Descriptors | This test verifies that every field in the Custom Resources' spec sections have a corresponding descriptor listed in the CSV.| olm-spec-descriptors-test |
 | Status Fields With Descriptors | This test verifies that every field in the Custom Resources' status sections have a corresponding descriptor listed in the CSV.| olm-status-descriptors-test |
 
diff --git a/website/content/en/docs/best-practices/best-practices.md b/website/content/en/docs/best-practices/best-practices.md
new file mode 100644
index 00000000..a7f6af22
--- /dev/null
+++ b/website/content/en/docs/best-practices/best-practices.md
@@ -0,0 +1,100 @@
+---
+title: "Operator Best Practices"
+linkTitle: "Best practices"
+weight: 1
+description: This guide describes the best practices concepts to write operators.
+---
+
+## Development
+
+Considerations for Operator developers:
+
+- An Operator should manage a single type of application, essentially following the UNIX principle: do one thing and do it well.
+
+- If an application consists of multiple tiers or components, multiple Operators should be written one for each of them. For example, if the application consists of Redis, AMQ and MySQL, there should be 3 Operators, not one.
+
+- If there is significant orchestration and sequencing involved, an Operator should be written that represents the entire stack, in turn delegating to other Operators for orchestrating their part of it.
+
+- Operators should own a CRD and only one Operator should control a CRD on a cluster. Two Operators managing the same CRD is not a recommended best practice. In the case where an API exists but with multiple implementations, this is typically an example of a no-op Operator because it doesn't have any deployment or reconciliation loop to define the shared API and other Operators depend on this Operator to provide one implementation of the API, e.g. similar to PVCs or Ingress.
+
+- Inside an Operator, multiple controllers should be used if multiple CRDs are managed. This helps in separation of concerns and code readability. Note that this doesn't necessarily mean that we need to have one container image per controller, but rather one reconciliation loop (which could be running as part of the same Operator binary) per CRD.
+
+- An Operator shouldn't deploy or manage other operators (such patterns are known as meta or super operators). It's the Operator Lifecycle Manager's job to manage the deployment and lifecycle of operators. For further information check [Dependency Resolution][Dependency Resolution].
+
+
+- If multiple operators are packaged and shipped as a single entity by the same CSV for example, then it is recommended to add all owned and required CRDs, as well as all deployments for operators that manage the owned CRDs, to the same CSV.
+
+- Writing an Operator involves using the Kubernetes API, which in most scenarios will be built using same boilerplate code. Use a framework like the Operator SDK to save yourself time with this and to also get a suite of tooling to ease development and testing.
+
+- Operators shouldn’t make any assumptions about the namespace they are deployed in and they should not use hard-coded names of resources that they expect to already exist.
+
+- Operators shouldn’t hard code the namespaces they are watching. This should be configurable - having no namespace supplied is interpreted as watching all namespaces
+
+- Semantic versioning (aka semver) should be used to version an Operator. Operators are long-running workloads on the cluster and its APIs are potentially in need of support over a longer period of time. Use the [semver.org guidelines](https://semver.org) to help determine when and how to bump versions when there are breaking or non-breaking changes.
+
+- Kubernetes API versioning guidelines should be used to version Operator CRDs. Use the [Kubernetes sig-architecture guidelines](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api_changes.md#so-you-want-to-change-the-api) to get best practices on when to bump versions and when breaking changes are acceptable.
+
+- When defining CRDs, you should use OpenAPI spec to create a structural schema for your CRDs.
+
+- Operators are instrumented to provide useful, actionable metrics to external systems (e.g. monitoring/alerting platforms).  Minimally, metrics should represent the software's health and key performance indicators, as well as support the creation of [service levels indicators](https://en.wikipedia.org/wiki/Service_level_indicator) such as throughput, latency, availability, errors, capacity, etc.
+
+### Summary
+
+- One Operator per managed application
+- Multiple operators should be used for complex, multi-tier application stacks
+- CRD can only be owned by a single Operator, shared CRDs should be owned by a separate Operator
+- One controller per custom resource definition
+- Use a tool like Operator SDK
+- Do not hard-code namespaces or resources names
+- Make watch namespace configurable
+- Use semver / observe Kubernetes guidelines on versioning APIs
+- Use OpenAPI spec with structural schema on CRDs
+- Operators expose metrics to external systems
+
+## Running On-Cluster
+
+Considerations for on-cluster behavior
+
+- Like all containers on Kubernetes, Operators need not run as root unless absolutely necessary. Operators should come with their own ServiceAccount and not rely on the `default`.
+
+- Operators should not self-register their CRDs. These are global resources and careful consideration needs to be taken when setting those up. Also this requires the Operator to have global privileges which is potentially dangerous compared to that little extra convenience.
+
+- Operators use CRs as the primary interface to the cluster user. As such, at all times, meaningful status information should be written to those objects unless they are solely used to store data in a structured schema.
+
+- Operators should be updated frequently according to server versioning.
+
+- Operators need to support updating managed applications (Operands) that were set up by an older version of the Operator. There are multiple models for this:
+
+| Model | Description | 
+| ------ | ----- |
+| **Operator fan-out** | where the Operator allows the user to specify the version in the custom resource |
+| **single version** | where the Operator is tied to the version of the operand. |
+| **hybrid approach** | where the Operator is tied to a range of versions, and the user can select some level of the version. |
+
+- An Operator should not deploy another Operator - an additional component on cluster should take care of this (OLM).
+
+- When Operators change their APIs, CRD conversion (webhooks) should be used to deal with potentially older instances of them using the previous API version.
+
+- Operators should make it easy for users to use their APIs - validating and rejecting malformed requests via extensive Open API validation schema on CRDs or via an admission webhook is good practice.
+
+- The Operator itself should be really modest in its requirements - it should always be able to deploy by deploying its controllers, no user input should be required to start up the Operator.
+
+- If user input is required to change the configuration of the Operator itself, a Configuration CRD should be used. Init-containers as part of the Operator deployments can be used to create a default instance of those CRs and then the Operator manages their lifecycle.
+
+### Summary:
+
+On the cluster, an Operator...
+
+- Does not run as root
+- Does not self-register CRDs
+- Does not install other Operators
+- Does rely on dependencies via package manager (OLM)
+- Writes meaningful status information on Custom Resources objects unless pure data structure
+- Should be capable of updating from a previous version of the Operator
+- Should be capable of managing an Operand from an older Operator version
+- Uses CRD conversion (webhooks) if API/CRDs change
+- Uses OpenAPI validation / Admission Webhooks to reject invalid CRs
+- Should always be able to deploy and come up without user input
+- Offers (pre)configuration via a `“Configuration CR”` instantiated by InitContainers
+
+[Dependency Resolution]: https://olm.operatorframework.io/docs/concepts/olm-architecture/dependency-resolution/
\ No newline at end of file
diff --git a/website/content/en/docs/best-practices/common-recommendation.md b/website/content/en/docs/best-practices/common-recommendation.md
index 5550188c..5d5e6151 100644
--- a/website/content/en/docs/best-practices/common-recommendation.md
+++ b/website/content/en/docs/best-practices/common-recommendation.md
@@ -7,7 +7,7 @@ description: Common recommendations and suggestions to built solutions with Oper
 
 ## Overview
 
-Any recommendations or good practices suggested by the Kubernetes community, such as how to to [develop Operator pattern solutions][operator-pattern] or how to [use controller-runtime][controller-runtime] are good recommendations for those who are looking to build operator projects with operator-sdk. Also, see [Operator Best Practices][operator-best-practices]. However, here are some common recommendations.
+Any recommendations or best practices suggested by the Kubernetes community, such as how to [develop Operator pattern solutions][operator-pattern] or how to [use controller-runtime][controller-runtime] are good recommendations for those who are looking to build operator projects with operator-sdk. Also, see [Operator Best Practices][operator-best-practices]. However, here are some common recommendations.
 
 ## Common Recommendations
 
@@ -62,7 +62,7 @@ spec:
 [project-layout]: /docs/overview/project-layout
 [controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
 [k8s-crd-doc]: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/
-[operator-best-practices]: https://operator-framework.github.io/community-operators/best-practices/
+[operator-best-practices]: /docs/best-practices/best-practices
 [kb-gkv]: https://book.kubebuilder.io/cronjob-tutorial/gvks.html
 [operator-pattern]: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
 [molecule]: https://molecule.readthedocs.io/en/latest/
diff --git a/website/content/en/docs/building-operators/golang/references/markers.md b/website/content/en/docs/building-operators/golang/references/markers.md
index 7e395e10..7d14a68d 100644
--- a/website/content/en/docs/building-operators/golang/references/markers.md
+++ b/website/content/en/docs/building-operators/golang/references/markers.md
@@ -112,7 +112,7 @@ These examples assume `Memcached`, `MemcachedSpec`, and `MemcachedStatus` are th
 	type MemcachedPods struct {
 		// Size is the size of the memcached deployment.
 		//+operator-sdk:csv:customresourcedefinitions:type=spec
-		//+operator-sdk:csv:customresourcedefinitions.type=status
+		//+operator-sdk:csv:customresourcedefinitions:type=status
 		Size int32 `json:"size"`
 	}
 	```
diff --git a/website/content/en/docs/building-operators/helm/reference/advanced_features/annotations.md b/website/content/en/docs/building-operators/helm/reference/advanced_features/annotations.md
index 09445546..604bd059 100644
--- a/website/content/en/docs/building-operators/helm/reference/advanced_features/annotations.md
+++ b/website/content/en/docs/building-operators/helm/reference/advanced_features/annotations.md
@@ -33,3 +33,115 @@ log message when an upgrade succeeds:
 ```
 {"level":"info","ts":1591198931.1703992,"logger":"helm.controller","msg":"Upgraded release","namespace":"helm-nginx","name":"example-nginx","apiVersion":"cache.example.com/v1alpha1","kind":"Nginx","release":"example-nginx","force":true}
 ```
+
+## `helm.sdk.operatorframework.io/uninstall-wait`
+
+This annotation can be set to `"true"` on custom resources to enable the deletion to wait until all the resources in the
+`status.deployedRelease.manifest` are deleted. 
+
+**Example**
+
+```yaml
+apiVersion: example.com/v1alpha1
+kind: Nginx
+metadata:
+  name: nginx-sample
+  annotations:
+    helm.sdk.operatorframework.io/uninstall-wait: "true"
+spec:
+...
+status:
+  ...
+  deployedRelease:
+    manifest: |
+      ---
+      # Source: nginx/templates/serviceaccount.yaml
+      apiVersion: v1
+      kind: ServiceAccount
+      metadata:
+        name: nginx-sample
+        labels:
+          helm.sh/chart: nginx-0.1.0
+          app.kubernetes.io/name: nginx
+          app.kubernetes.io/instance: nginx-sample
+          app.kubernetes.io/version: "1.16.0"
+          app.kubernetes.io/managed-by: Helm
+      ---
+      # Source: nginx/templates/service.yaml
+      apiVersion: v1
+      kind: Service
+      metadata:
+        name: nginx-sample
+        labels:
+          helm.sh/chart: nginx-0.1.0
+          app.kubernetes.io/name: nginx
+          app.kubernetes.io/instance: nginx-sample
+          app.kubernetes.io/version: "1.16.0"
+          app.kubernetes.io/managed-by: Helm
+      spec:
+        type: ClusterIP
+        ports:
+          - port: 80
+            targetPort: http
+            protocol: TCP
+            name: http
+        selector:
+          app.kubernetes.io/name: nginx
+          app.kubernetes.io/instance: nginx-sample
+      ---
+      # Source: nginx/templates/deployment.yaml
+      apiVersion: apps/v1
+      kind: Deployment
+      metadata:
+        name: nginx-sample
+        labels:
+          helm.sh/chart: nginx-0.1.0
+          app.kubernetes.io/name: nginx
+          app.kubernetes.io/instance: nginx-sample
+          app.kubernetes.io/version: "1.16.0"
+          app.kubernetes.io/managed-by: Helm
+      spec:
+        replicas: 1
+        selector:
+          matchLabels:
+            app.kubernetes.io/name: nginx
+            app.kubernetes.io/instance: nginx-sample
+        template:
+          metadata:
+            labels:
+              app.kubernetes.io/name: nginx
+              app.kubernetes.io/instance: nginx-sample
+          spec:
+            serviceAccountName: nginx-sample
+            securityContext:
+              {}
+            containers:
+              - name: nginx
+                securityContext:
+                  {}
+                image: "nginx:1.16.0"
+                imagePullPolicy: IfNotPresent
+                ports:
+                  - name: http
+                    containerPort: 80
+                    protocol: TCP
+                livenessProbe:
+                  httpGet:
+                    path: /
+                    port: http
+                readinessProbe:
+                  httpGet:
+                    path: /
+                    port: http
+                resources:
+                  {}
+```
+
+Setting this annotation to `true` and deleting the custom resource will cause the custom resource to be reconciled
+continuously until all the resources in `status.deployedRelease.manifest` are deleted. This can be verified in the
+log message when a delete has been triggered:
+
+```
+{"level":"info","ts":1612294054.5845876,"logger":"helm.controller","msg":"Uninstall wait","namespace":"default","name":"nginx-sample","apiVersion":"example.com/v1alpha1","kind":"Nginx","release":"nginx-sample"}
+
+```
diff --git a/website/content/en/docs/contribution-guidelines/documentation.md b/website/content/en/docs/contribution-guidelines/documentation.md
index 4ee2a7a9..04f31a26 100644
--- a/website/content/en/docs/contribution-guidelines/documentation.md
+++ b/website/content/en/docs/contribution-guidelines/documentation.md
@@ -33,12 +33,10 @@ hugo server
 
 Any changes will be included in real time.
 
-
 ## Check Docs
 
 `make test-docs` will validate changelog fragments, build doc HTML in a container, and check its links.
 Please consider running this locally before creating a PR to save CI resources.
 
-
 [hugo]:https://gohugo.io/
 [docsy-install]:https://www.docsy.dev/docs/getting-started/#prerequisites-and-installation
diff --git a/website/content/en/docs/contribution-guidelines/plugins.md b/website/content/en/docs/contribution-guidelines/plugins.md
new file mode 100644
index 00000000..e8ab3392
--- /dev/null
+++ b/website/content/en/docs/contribution-guidelines/plugins.md
@@ -0,0 +1,80 @@
+---
+title: Plugins
+linkTitle: Plugins
+weight: 7
+---
+
+## Overview
+
+SDK uses the project [Kubebuilder][kubebuilder] as a library for the CLI and plugins features. For further information see the [Plugins][kb-plugins-doc] document. To better understand its motivations, check the design documentation [Integrating Kubebuilder and Operator SDK][kb-int-sdk].
+
+## SDK Language-based plugins
+
+All language-based operator projects provided by SDK (Ansible/Helm/Golang) follow the Kubebuilder standard and have a common base. The common base is generated using [kustomize][kustomize] which is a project maintained and adopted by Kubernetes community to help work with the Kubernertes manifests (YAML files) that are used to configure the resources on the clusters.
+
+The specific files for each language are scaffolded for the language's plugins. The Operator SDK CLI tool can provide custom plugins and helpers which are common and useful for all language types. To check the common base, you can also run:
+
+```sh
+operator-sdk init --plugins=kustomize
+```
+
+Also, see the topic [Language-based Plugins][kb-language-plugins] to understand how it works.
+
+### Common scaffolds 
+
+Following the default common scaffolds for the projects which are built with SDK.
+
+| File/Directory | Description | 
+| ------ | ----- |
+|  Dockerfile |  Defines the operator(manager) image |  
+|  Makefile |  Provides the helpers and options for the users. (e.g. `make bundle` which generate/update the OLM [bundle][bundle] manifests ) |  
+|  PROJECT |  Project configuration. Stores the data used to do the scaffolds. For further information see [Project Config][kb-project] | 
+|  bundle.Dockerfile | Docker image which is used to provide the helpers to integrate the project with OLM. (e.g. [operator-sdk run ./bundle][sdk-cli-run-bundle]) | 
+|  config/ |  Directory which has all [kustomize's][kustomize] manifest to configure and test the project | 
+
+You can check the [Project Layout][project-layout] to better understand the files and directories scaffolded by SDK CLI, which may be common for each language-type.
+
+## Custom Plugins
+
+By default plugins are used by the Operator SDK to provide the following features:
+
+- [manifests.sdk.operatorframework.io][plugin-manifest]: perform the required scaffolds to provide the helpers to allow the projects to be integrated with OLM. 
+- [scorecard.sdk.operatorframework.io][plugin-scorecard]: perform the required scaffolds to provide the [Scorecard][scorecard] feature.
+
+### Optional/custom plugins
+
+Users can also use custom plugins when they execute the SDK CLI sub-commands as a helper which includes the following options:
+
+```sh
+operator-sdk create api --plugins="go/v3,declarative"
+```
+
+The above example will scaffold custom code in the controllers after an API is created to allow the users to develop solutions using the [kubebuilder declarative pattern][kubebuilder-declarative-pattern]. (e.g [default scaffold][default-scaffold] versus [example][kubebuilder-declarative-pattern-example]).
+
+Note that custom plugins are called via the init sub-command to work as global plugins, and will be added in the `layout` field of the PROJECT file. Any sub-command executed will then also be called.
+
+## Plugins Vision
+
+Maintainers and contributors are able to create their own plugin(s) using the same standards and approaches. These contributions in the future could be added as solutions to be compatible and used with the Operator SDK CLI. Currently, the SDK cannot discover and use plugins that are not shipped or built with its binary. However, it has been discussed in the issue [kubebuilder/issues/1378][kb-issue].
+
+## How to create your own plugins
+
+If you are looking to develop similar solutions to allow users for example to create projects using other languages or that could work as helpers for the projects built with SDK/Kubebuilder projects, then see the [Creating your own plugins][create-your-own-plugins] to see how you can benefit and apply this approach.
+
+[kb-plugins-doc]: https://master.book.kubebuilder.io/plugins/plugins.html
+[kb-int-sdk]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/integrating-kubebuilder-and-osdk.md
+[kb-language-plugins]:https://master.book.kubebuilder.io/plugins/creating-plugins.html#language-based-plugins
+[kustomize]: https://github.com/kubernetes-sigs/kustomize
+[bundle]: https://github.com/operator-framework/operator-registry/blob/v1.16.1/docs/design/operator-bundle.md#operator-bundle
+[kb-project]: https://master.book.kubebuilder.io/reference/project-config.html
+[sdk-cli-run-bundle]: /docs/cli/operator-sdk_run
+[project-layout]: /docs/overview/project-layout
+[plugin-manifest]: https://github.com/operator-framework/operator-sdk/tree/master/internal/plugins/manifests/v2
+[plugin-scorecard]: https://github.com/operator-framework/operator-sdk/tree/master/internal/plugins/scorecard/v2
+[kubebuilder-declarative-pattern]: https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern
+[kubebuilder-declarative-pattern-example]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/testdata/project-v3/controllers/firstmate_controller.go
+[default-scaffold]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/testdata/project-v3/controllers/admiral_controller.go
+[kb-issue]: https://github.com/kubernetes-sigs/kubebuilder/issues/1378
+[create-your-own-plugins]: https://master.book.kubebuilder.io/plugins/creating-plugins.html
+[scorecard]: /docs/advanced-topics/scorecard/scorecard/
+[kubebuilder]: https://github.com/kubernetes-sigs/kubebuilder 
diff --git a/website/content/en/docs/contribution-guidelines/releasing.md b/website/content/en/docs/contribution-guidelines/releasing.md
index 715111f3..0a596c18 100644
--- a/website/content/en/docs/contribution-guidelines/releasing.md
+++ b/website/content/en/docs/contribution-guidelines/releasing.md
@@ -1,7 +1,7 @@
 ---
 title: Release Guide
 linkTitle: Releasing
-weight: 30
+weight: 4
 ---
 
 These steps describe how to conduct a release of the operator-sdk repo using example versions.
diff --git a/website/content/en/docs/contribution-guidelines/reporting-issues.md b/website/content/en/docs/contribution-guidelines/reporting-issues.md
index 7ea456bd..52cd02f9 100644
--- a/website/content/en/docs/contribution-guidelines/reporting-issues.md
+++ b/website/content/en/docs/contribution-guidelines/reporting-issues.md
@@ -1,7 +1,7 @@
 ---
 title: Reporting Issues
 linkTitle: Reporting Issues
-weight: 40
+weight: 5
 ---
 
 If any part of the operator-sdk project has bugs or documentation mistakes, please let us know by [opening an issue][operator-sdk-issue]. We treat bugs and mistakes very seriously and believe no issue is too small. Before creating a bug report, please check that an issue reporting the same problem does not already exist.
diff --git a/website/content/en/docs/installation/_index.md b/website/content/en/docs/installation/_index.md
index 6b162acd..cab1dc97 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/v1.5.0
+export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.6.1
 curl -LO ${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH}
 ```
 
diff --git a/website/content/en/docs/upgrading-sdk-version/v1.6.0.md b/website/content/en/docs/upgrading-sdk-version/v1.6.0.md
new file mode 100644
index 00000000..490a793b
--- /dev/null
+++ b/website/content/en/docs/upgrading-sdk-version/v1.6.0.md
@@ -0,0 +1,278 @@
+---
+title: v1.6.0
+weight: 998994000
+---
+
+## (ansible/v1, helm/v1) Optional: configure `ansible-operator` and `helm-operator` with a component config.
+
+To add this option your project you will need to: 
+- Create the file [config/default/manager_config_patch.yaml](https://github.com/operator-framework/operator-sdk/blob/2a1bd03/testdata/ansible/memcached-operator/config/default/manager_config_patch.yaml). 
+- Create the file [config/manager/controller_manager_config.yaml](https://github.com/operator-framework/operator-sdk/blob/2a1bd03/testdata/ansible/memcached-operator/config/manager/controller_manager_config.yaml). 
+- Update the `config/default/kustomization.yaml` by adding the following to `resources`:
+  ```yaml
+  resources:
+  ...
+  - manager_config_patch.yaml
+  ```
+- Update the `config/manager/kustomization.yaml` by adding:
+```yaml
+  generatorOptions:
+    disableNameSuffixHash: true
+
+  configMapGenerator:
+  - files:
+    - controller_manager_config.yaml
+    name: manager-config
+  apiVersion: kustomize.config.k8s.io/v1beta1
+  kind: Kustomization
+  images:
+  - name: controller
+    newName: quay.io/example/memcached-operator
+    newTag: v0.0.1
+```
+
+_See [#4701](https://github.com/operator-framework/operator-sdk/pull/4701) for more details._
+
+## (ansible/v1, helm/v1) Add Role rules for leader election.
+
+Add the rule for the `apiGroups` `coordination.k8s.io` and the resource `leases` in config/rbac/leader_election_role.yaml:
+```yaml
+rules:
+  - apiGroups:
+      - ""
+      - coordination.k8s.io
+    resources:
+      - configmaps
+      - leases
+```
+
+_See [#4701](https://github.com/operator-framework/operator-sdk/pull/4701) for more details._
+
+## (ansible/v1) Update Ansible collections
+
+In your requirements.yml, change the `version` field for community.kubernetes to `1.2.1`, and the `version` field for `operator_sdk.util` to `0.2.0`.
+
+_See [#4734](https://github.com/operator-framework/operator-sdk/pull/4734) for more details._
+
+## (helm/v1) Replace deprecated leader election and metrics address flags
+
+Replace deprecated flags `--enable-leader-election` and `--metrics-addr` with `--leader-elect` and `--metrics-bind-address`, respectively.
+
+_See [#4654](https://github.com/operator-framework/operator-sdk/pull/4654) for more details._
+
+## (helm/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch.
+
+Add the arg `--health-probe-bind-address=:8081` to the `config/default/manager_auth_proxy_patch.yaml`:
+```yaml
+spec:
+  template:
+    spec:
+      containers:
+      - name: manager
+        args:
+        - "--health-probe-bind-address=:8081"
+        ...
+```
+
+_See [#4654](https://github.com/operator-framework/operator-sdk/pull/4654) for more details._
+
+## (ansible/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch.
+
+Add the arg `--health-probe-bind-address=:6789` to the `config/default/manager_auth_proxy_patch.yaml`:
+```yaml
+spec:
+  template:
+    spec:
+      containers:
+      - name: manager
+        args:
+        - "--health-probe-bind-address=:6789"
+        ...
+```
+
+_See [#4654](https://github.com/operator-framework/operator-sdk/pull/4654) for more details._
+
+## (helm/v1, ansible/v1) Add `help` target to Makefile.
+
+Ansible/Helm projects now provide a Makefile `help` target, similar to a `--help` flag. You can copy and paste this target from the relevant sample's Makefile ([helm](https://github.com/operator-framework/operator-sdk/blob/v1.5.0/testdata/helm/memcached-operator/Makefile), [ansible](https://github.com/operator-framework/operator-sdk/blob/v1.5.0/testdata/ansible/memcached-operator/Makefile)).
+
+_See [#4660](https://github.com/operator-framework/operator-sdk/pull/4660) for more details._
+
+## (ansible/v1, helm/v1) Add `securityContext`'s to your manager's Deployment.
+
+In `config/manager/manager.yaml`, add the following security contexts:
+```yaml
+spec:
+  ...
+  template:
+    ...
+    spec:
+      securityContext:
+        runAsNonRoot: true
+      containers:
+      - name: manager
+        securityContext:
+          allowPrivilegeEscalation: false
+```
+
+_See [#4655](https://github.com/operator-framework/operator-sdk/pull/4655) for more details._
+
+## (manifests/v2) Add a kustomize patch to remove the cert-manager volume/volumeMount from your CSV
+
+OLM does [not yet support cert-manager](https://olm.operatorframework.io/docs/advanced-tasks/adding-admission-and-conversion-webhooks/#certificate-authority-requirements), so a JSON patch was added to remove this volume and mount such that OLM can itself create and manage certs for your Operator.
+In `config/manifests/kustomization.yaml`, add the following:
+```yaml
+patchesJson6902: 
+- target:
+    group: apps
+    version: v1
+    kind: Deployment
+    name: controller-manager
+    namespace: system
+  patch: |-
+    # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs.
+    # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment.
+    - op: remove
+      path: /spec/template/spec/containers/1/volumeMounts/0
+    # Remove the "cert" volume, since OLM will create and mount a set of certs.
+    # Update the indices in this path if adding or removing volumes in the manager's Deployment.
+    - op: remove
+      path: /spec/template/spec/volumes/0
+```
+
+_See [#4623](https://github.com/operator-framework/operator-sdk/pull/4623) for more details._
+
+## (go/v2, go/v3, ansible/v1, helm/v1) Add scheme, token, and TLS config to the Prometheus `ServiceMonitor` metrics endpoint.
+
+The `/metrics` endpoint, while specifying the `https` port on the manager Pod, was not actually configured to serve over https because no tlsConfig was set. Since kube-rbac-proxy secures this endpoint as a manager sidecar, using the service account token mounted into the Pod by default corrects this problem.
+The changes should look like:
+```diff
+# config/prometheus/monitor.yaml
+spec:
+   endpoints:
+     - path: /metrics
+       port: https
++      scheme: https
++      bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
++      tlsConfig:
++        insecureSkipVerify: true
+   selector:
+     matchLabels:
+       control-plane: controller-manager
+```
+**Note:** if you have removed kube-rbac-proxy from your project, make sure to secure the `/metrics` endpoint using a proper [TLS configuration](https://prometheus.io/docs/guides/tls-encryption/).
+
+_See [#4680](https://github.com/operator-framework/operator-sdk/pull/4680) for more details._
+
+## (go/v2, go/v3, ansible/v1, helm/v1) Add `opm` and `catalog-build` Makefile targets
+
+The `opm` and `catalog-build` Makefile targets were added so operator developers who want to create their own catalogs for their operator or add their operator's bundle(s) to an existing catalog can do so. If this sounds like you, add the following lines to the bottom of your Makefile:
+```make
+.PHONY: opm
+OPM = ./bin/opm
+opm:
+ifeq (,$(wildcard $(OPM)))
+ifeq (,$(shell which opm 2>/dev/null))
+	@{ \
+	set -e ;\
+	mkdir -p $(dir $(OPM)) ;\
+	curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.1/$(OS)-$(ARCH)-opm ;\
+	chmod +x $(OPM) ;\
+	}
+else 
+OPM = $(shell which opm)
+endif
+endif
+BUNDLE_IMGS ?= $(BUNDLE_IMG) 
+CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) ifneq ($(origin CATALOG_BASE_IMG), undefined) FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) endif 
+.PHONY: catalog-build
+catalog-build: opm
+	$(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)
+
+.PHONY: catalog-push
+catalog-push: ## Push the catalog image.
+	$(MAKE) docker-push IMG=$(CATALOG_IMG)
+```
+If updating a Go operator project, additionally add the following Makefile variables:
+```make
+OS = $(shell go env GOOS)
+ARCH = $(shell go env GOARCH)
+```
+
+_See [#4406](https://github.com/operator-framework/operator-sdk/pull/4406) for more details._
+
+## (go/v2, go/v3, ansible/v1, helm/v1) Changed `BUNDLE_IMG` and added `IMAGE_TAG_BASE` Makefile variables
+
+The following Makefile changes were made to allow `make bundle-build bundle-push catalog-build catalog-push` and encode image repo/namespace information in the Makefile by default:
+```diff
++IMAGE_TAG_BASE ?= <registry>/<operator name>
++ 
+-BUNDLE_IMG ?= controller-bundle:$(VERSION)
++BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
+```
+
+For example, if `IMAGE_TAG_BASE ?= foo/bar-operator` then running `make bundle-build bundle-push catalog-build catalog-push` would build `foo/bar-operator-bundle:v0.0.1` and `foo/bar-operator-catalog:v0.0.1` then push them to the `docker.io/foo` namespaced registry.
+
+_See [#4406](https://github.com/operator-framework/operator-sdk/pull/4406) for more details._
+
+## (ansible/v1, helm/v1) Add the `controller-manager` ServiceAccount to your project.
+
+A non-default ServiceAccount `controller-manager` is scaffolded on `operator-sdk init`, to improve security for operators installed in shared namespaces. To add this ServiceAccount to your project, do the following: ```sh
+# Create the ServiceAccount.
+cat <<EOF > config/rbac/service_account.yaml apiVersion: v1 
+kind: ServiceAccount
+metadata:
+  name: controller-manager
+  namespace: system
+EOF
+# Add it to the list of RBAC resources.
+echo "- service_account.yaml" >> config/rbac/kustomization.yaml
+# Update all RoleBinding and ClusterRoleBinding subjects that reference the operator's ServiceAccount.
+find config/rbac -name *_binding.yaml -exec sed -i -E 's/  name: default/  name: controller-manager/g' {} \; # Add the ServiceAccount name to the manager Deployment's spec.template.spec.serviceAccountName. sed -i -E 's/([ ]+)(terminationGracePeriodSeconds:)/\1serviceAccountName: controller-manager\n\1\2/g' config/manager/manager.yaml
+``` 
+
+The changes should look like: 
+
+```diff 
+# config/manager/manager.yaml
+           requests:
+             cpu: 100m
+             memory: 20Mi
++      serviceAccountName: controller-manager
+       terminationGracePeriodSeconds: 10
+# config/rbac/auth_proxy_role_binding.yaml
+   name: proxy-role
+ subjects:
+ - kind: ServiceAccount
+-  name: default 
++  name: controller-manager
+   namespace: system
+# config/rbac/kustomization.yaml
+ resources:
++- service_account.yaml
+ - role.yaml
+ - role_binding.yaml
+ - leader_election_role.yaml
+# config/rbac/leader_election_role_binding.yaml
+   name: leader-election-role
+ subjects:
+ - kind: ServiceAccount
+-  name: default
++  name: controller-manager
+   namespace: system
+# config/rbac/role_binding.yaml
+   name: manager-role
+ subjects:
+ - kind: ServiceAccount
+-  name: default 
++  name: controller-manager
+   namespace: system
+# config/rbac/service_account.yaml 
++apiVersion: v1 
++kind: ServiceAccount 
++metadata: 
++  name: controller-manager 
++  namespace: system
+```
+
+_See [#4653](https://github.com/operator-framework/operator-sdk/pull/4653) for more details._
diff --git a/website/content/en/docs/upgrading-sdk-version/v1.6.1.md b/website/content/en/docs/upgrading-sdk-version/v1.6.1.md
new file mode 100644
index 00000000..1a9f8265
--- /dev/null
+++ b/website/content/en/docs/upgrading-sdk-version/v1.6.1.md
@@ -0,0 +1,280 @@
+---
+title: v1.6.1
+weight: 998994000
+---
+
+## (ansible/v1, helm/v1) Optional: configure `ansible-operator` and `helm-operator` with a component config.
+
+To add this option your project you will need to: 
+- Create the file [config/default/manager_config_patch.yaml](https://github.com/operator-framework/operator-sdk/blob/2a1bd03/testdata/ansible/memcached-operator/config/default/manager_config_patch.yaml). 
+- Create the file [config/manager/controller_manager_config.yaml](https://github.com/operator-framework/operator-sdk/blob/2a1bd03/testdata/ansible/memcached-operator/config/manager/controller_manager_config.yaml). 
+- Update the `config/default/kustomization.yaml` by adding the following to `resources`:
+  ```yaml
+  resources:
+  ...
+  - manager_config_patch.yaml
+  ```
+- Update the `config/manager/kustomization.yaml` by adding:
+```yaml
+  generatorOptions:
+    disableNameSuffixHash: true
+
+  configMapGenerator:
+  - files:
+    - controller_manager_config.yaml
+    name: manager-config
+  apiVersion: kustomize.config.k8s.io/v1beta1
+  kind: Kustomization
+  images:
+  - name: controller
+    newName: quay.io/example/memcached-operator
+    newTag: v0.0.1
+```
+
+_See [#4701](https://github.com/operator-framework/operator-sdk/pull/4701) for more details._
+
+## (ansible/v1, helm/v1) Add Role rules for leader election.
+
+Add the rule for the `apiGroups` `coordination.k8s.io` and the resource `leases` in config/rbac/leader_election_role.yaml:
+```yaml
+rules:
+  - apiGroups:
+      - ""
+      - coordination.k8s.io
+    resources:
+      - configmaps
+      - leases
+```
+
+_See [#4701](https://github.com/operator-framework/operator-sdk/pull/4701) for more details._
+
+## (ansible/v1) Update Ansible collections
+
+In your requirements.yml, change the `version` field for community.kubernetes to `1.2.1`, and the `version` field for `operator_sdk.util` to `0.2.0`.
+
+_See [#4734](https://github.com/operator-framework/operator-sdk/pull/4734) for more details._
+
+## (helm/v1) Replace deprecated leader election and metrics address flags
+
+Replace deprecated flags `--enable-leader-election` and `--metrics-addr` with `--leader-elect` and `--metrics-bind-address`, respectively.
+
+_See [#4654](https://github.com/operator-framework/operator-sdk/pull/4654) for more details._
+
+## (helm/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch.
+
+Add the arg `--health-probe-bind-address=:8081` to the `config/default/manager_auth_proxy_patch.yaml`:
+```yaml
+spec:
+  template:
+    spec:
+      containers:
+      - name: manager
+        args:
+        - "--health-probe-bind-address=:8081"
+        ...
+```
+
+_See [#4654](https://github.com/operator-framework/operator-sdk/pull/4654) for more details._
+
+## (ansible/v1) Explicitly set `--health-probe-bind-address` in the manager's auth proxy patch.
+
+Add the arg `--health-probe-bind-address=:6789` to the `config/default/manager_auth_proxy_patch.yaml`:
+```yaml
+spec:
+  template:
+    spec:
+      containers:
+      - name: manager
+        args:
+        - "--health-probe-bind-address=:6789"
+        ...
+```
+
+_See [#4654](https://github.com/operator-framework/operator-sdk/pull/4654) for more details._
+
+## (helm/v1, ansible/v1) Add `help` target to Makefile.
+
+Ansible/Helm projects now provide a Makefile `help` target, similar to a `--help` flag. You can copy and paste this target from the relevant sample's Makefile ([helm](https://github.com/operator-framework/operator-sdk/blob/v1.5.0/testdata/helm/memcached-operator/Makefile), [ansible](https://github.com/operator-framework/operator-sdk/blob/v1.5.0/testdata/ansible/memcached-operator/Makefile)).
+
+_See [#4660](https://github.com/operator-framework/operator-sdk/pull/4660) for more details._
+
+## (ansible/v1, helm/v1) Add `securityContext`'s to your manager's Deployment.
+
+In `config/manager/manager.yaml`, add the following security contexts:
+```yaml
+spec:
+  ...
+  template:
+    ...
+    spec:
+      securityContext:
+        runAsNonRoot: true
+      containers:
+      - name: manager
+        securityContext:
+          allowPrivilegeEscalation: false
+```
+
+_See [#4655](https://github.com/operator-framework/operator-sdk/pull/4655) for more details._
+
+## (manifests/v2) Add a kustomize patch to remove the cert-manager volume/volumeMount from your CSV
+
+OLM does [not yet support cert-manager](https://olm.operatorframework.io/docs/advanced-tasks/adding-admission-and-conversion-webhooks/#certificate-authority-requirements), so a JSON patch was added to remove this volume and mount such that OLM can itself create and manage certs for your Operator.
+In `config/manifests/kustomization.yaml`, add the following:
+```yaml
+patchesJson6902: 
+- target:
+    group: apps
+    version: v1
+    kind: Deployment
+    name: controller-manager
+    namespace: system
+  patch: |-
+    # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs.
+    # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment.
+    - op: remove
+      path: /spec/template/spec/containers/1/volumeMounts/0
+    # Remove the "cert" volume, since OLM will create and mount a set of certs.
+    # Update the indices in this path if adding or removing volumes in the manager's Deployment.
+    - op: remove
+      path: /spec/template/spec/volumes/0
+```
+
+_See [#4623](https://github.com/operator-framework/operator-sdk/pull/4623) for more details._
+
+## (go/v2, go/v3, ansible/v1, helm/v1) Add scheme, token, and TLS config to the Prometheus `ServiceMonitor` metrics endpoint.
+
+The `/metrics` endpoint, while specifying the `https` port on the manager Pod, was not actually configured to serve over https because no tlsConfig was set. Since kube-rbac-proxy secures this endpoint as a manager sidecar, using the service account token mounted into the Pod by default corrects this problem.
+The changes should look like:
+```diff
+# config/prometheus/monitor.yaml
+spec:
+   endpoints:
+     - path: /metrics
+       port: https
++      scheme: https
++      bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
++      tlsConfig:
++        insecureSkipVerify: true
+   selector:
+     matchLabels:
+       control-plane: controller-manager
+```
+**Note:** if you have removed kube-rbac-proxy from your project, make sure to secure the `/metrics` endpoint using a proper [TLS configuration](https://prometheus.io/docs/guides/tls-encryption/).
+
+_See [#4680](https://github.com/operator-framework/operator-sdk/pull/4680) for more details._
+
+## (go/v2, go/v3, ansible/v1, helm/v1) Add `opm` and `catalog-build` Makefile targets
+
+The `opm` and `catalog-build` Makefile targets were added so operator developers who want to create their own catalogs for their operator or add their operator's bundle(s) to an existing catalog can do so. If this sounds like you, add the following lines to the bottom of your Makefile:
+```make
+.PHONY: opm
+OPM = ./bin/opm
+opm:
+ifeq (,$(wildcard $(OPM)))
+ifeq (,$(shell which opm 2>/dev/null))
+	@{ \
+	set -e ;\
+	mkdir -p $(dir $(OPM)) ;\
+	curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.1/$(OS)-$(ARCH)-opm ;\
+	chmod +x $(OPM) ;\
+	}
+else 
+OPM = $(shell which opm)
+endif
+endif
+BUNDLE_IMGS ?= $(BUNDLE_IMG) 
+CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) ifneq ($(origin CATALOG_BASE_IMG), undefined) FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) endif 
+.PHONY: catalog-build
+catalog-build: opm
+	$(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)
+
+.PHONY: catalog-push
+catalog-push: ## Push the catalog image.
+	$(MAKE) docker-push IMG=$(CATALOG_IMG)
+```
+If updating a Go operator project, additionally add the following Makefile variables:
+```make
+OS = $(shell go env GOOS)
+ARCH = $(shell go env GOARCH)
+```
+
+_See [#4406](https://github.com/operator-framework/operator-sdk/pull/4406) for more details._
+
+## (go/v2, go/v3, ansible/v1, helm/v1) Changed `BUNDLE_IMG` and added `IMAGE_TAG_BASE` Makefile variables
+
+The following Makefile changes were made to allow `make bundle-build bundle-push catalog-build catalog-push` and encode image repo/namespace information in the Makefile by default:
+```diff
++IMAGE_TAG_BASE ?= <registry>/<operator name>
++ 
+-BUNDLE_IMG ?= controller-bundle:$(VERSION)
++BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
+```
+
+For example, if `IMAGE_TAG_BASE ?= foo/bar-operator` then running `make bundle-build bundle-push catalog-build catalog-push` would build `foo/bar-operator-bundle:v0.0.1` and `foo/bar-operator-catalog:v0.0.1` then push them to the `docker.io/foo` namespaced registry.
+
+_See [#4406](https://github.com/operator-framework/operator-sdk/pull/4406) for more details._
+
+## (ansible/v1, helm/v1) Add the `controller-manager` ServiceAccount to your project.
+
+A non-default ServiceAccount `controller-manager` is scaffolded on `operator-sdk init`, to improve security for operators installed in shared namespaces. To add this ServiceAccount to your project, do the following: ```sh
+# Create the ServiceAccount.
+cat <<EOF > config/rbac/service_account.yaml apiVersion: v1 
+kind: ServiceAccount
+metadata:
+  name: controller-manager
+  namespace: system
+EOF
+# Add it to the list of RBAC resources.
+echo "- service_account.yaml" >> config/rbac/kustomization.yaml
+# Update all RoleBinding and ClusterRoleBinding subjects that reference the operator's ServiceAccount.
+find config/rbac -name *_binding.yaml -exec sed -i -E 's/  name: default/  name: controller-manager/g' {} \; 
+# Add the ServiceAccount name to the manager Deployment's spec.template.spec.serviceAccountName. 
+sed -i -E 's/([ ]+)(terminationGracePeriodSeconds:)/\1serviceAccountName: controller-manager\n\1\2/g' config/manager/manager.yaml
+```
+
+The changes should look like: 
+
+```diff 
+# config/manager/manager.yaml
+           requests:
+             cpu: 100m
+             memory: 20Mi
++      serviceAccountName: controller-manager
+       terminationGracePeriodSeconds: 10
+# config/rbac/auth_proxy_role_binding.yaml
+   name: proxy-role
+ subjects:
+ - kind: ServiceAccount
+-  name: default 
++  name: controller-manager
+   namespace: system
+# config/rbac/kustomization.yaml
+ resources:
++- service_account.yaml
+ - role.yaml
+ - role_binding.yaml
+ - leader_election_role.yaml
+# config/rbac/leader_election_role_binding.yaml
+   name: leader-election-role
+ subjects:
+ - kind: ServiceAccount
+-  name: default
++  name: controller-manager
+   namespace: system
+# config/rbac/role_binding.yaml
+   name: manager-role
+ subjects:
+ - kind: ServiceAccount
+-  name: default 
++  name: controller-manager
+   namespace: system
+# config/rbac/service_account.yaml 
++apiVersion: v1 
++kind: ServiceAccount 
++metadata: 
++  name: controller-manager 
++  namespace: system
+```
+
+_See [#4653](https://github.com/operator-framework/operator-sdk/pull/4653) for more details._
\ No newline at end of file
diff --git a/website/content/en/docs/upgrading-sdk-version/version-upgrade-guide.md b/website/content/en/docs/upgrading-sdk-version/version-upgrade-guide.md
index dbba1715..2a9393ce 100644
--- a/website/content/en/docs/upgrading-sdk-version/version-upgrade-guide.md
+++ b/website/content/en/docs/upgrading-sdk-version/version-upgrade-guide.md
@@ -10,7 +10,6 @@ For some versions it might also be necessary to update the upstream Kubernetes a
 
 The full list of dependencies and their versions required by a particular version of the SDK can be viewed by using the [`operator-sdk print-deps`][print-deps-cli] command. Use this command to update the project `Gopkg.toml`/`go.mod` file accordingly as you upgrade to a new SDK version.
 
-
 For some SDK versions after `v0.1.0` there might be minor breaking changes in the controller-runtime APIs, `operator-sdk` CLI or the expected project layout and file names. These breaking changes will usually be outlined in the [CHANGELOG][changelog]/[release-notes][release-notes] for each release version.
 
 For releases that have a large number of breaking changes or involve a significant refactoring of the APIs and project layout there will be a migration guide similar to the [v0.1.0 migration guide][v0.1.0-migration-guide].
@@ -32,27 +31,27 @@ The following sections outline the upgrade steps for each SDK version along with
   ```TOML
   [[override]]
     name = "k8s.io/code-generator"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "3dcf91f64f638563e5106f21f50c31fa361c918d"
 
   [[override]]
     name = "k8s.io/api"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "b503174bad5991eb66f18247f52e41c3258f6348"
 
   [[override]]
     name = "k8s.io/apiextensions-apiserver"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "0cd23ebeb6882bd1cdc2cb15fc7b2d72e8a86a5b"
 
   [[override]]
     name = "k8s.io/apimachinery"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "eddba98df674a16931d2d4ba75edc3a389bf633a"
 
   [[override]]
     name = "k8s.io/client-go"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "d082d5923d3cc0bfbb066ee5fbdea3d0ca79acf8"
 
   [[override]]
@@ -79,27 +78,27 @@ The following sections outline the upgrade steps for each SDK version along with
   ```TOML
   [[override]]
     name = "k8s.io/code-generator"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "c2090bec4d9b1fb25de3812f868accc2bc9ecbae"
 
   [[override]]
     name = "k8s.io/api"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "05914d821849570fba9eacfb29466f2d8d3cd229"
 
   [[override]]
     name = "k8s.io/apiextensions-apiserver"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "0fe22c71c47604641d9aa352c785b7912c200562"
 
   [[override]]
     name = "k8s.io/apimachinery"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "2b1284ed4c93a43499e781493253e2ac5959c4fd"
 
   [[override]]
     name = "k8s.io/client-go"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "8d9ed539ba3134352c586810e749e58df4e94e4f"
 
   [[override]]
@@ -286,19 +285,19 @@ Upon updating the project to `v0.8.2` the following breaking changes apply:
   ```TOML
   [[override]]
     name = "k8s.io/api"
-    **revision for tag "kubernetes-1.13.4"**
+    # **revision for tag "kubernetes-1.13.4"**
     revision = "5cb15d34447165a97c76ed5a60e4e99c8a01ecfe"
   [[override]]
     name = "k8s.io/apiextensions-apiserver"
-    **revision for tag "kubernetes-1.13.4"**
+    # **revision for tag "kubernetes-1.13.4"**
     revision = "d002e88f6236312f0289d9d1deab106751718ff0"
   [[override]]
     name = "k8s.io/apimachinery"
-    **revision for tag "kubernetes-1.13.4"**
+    # **revision for tag "kubernetes-1.13.4"**
     revision = "86fb29eff6288413d76bd8506874fddd9fccdff0"
   [[override]]
     name = "k8s.io/client-go"
-    **revision for tag "kubernetes-1.13.4"**
+    # **revision for tag "kubernetes-1.13.4"**
     revision = "b40b2a5939e43f7ffe0028ad67586b7ce50bb675"
   [[override]]
     name = "github.com/coreos/prometheus-operator"
@@ -380,19 +379,19 @@ Upon updating the project to `v0.8.2` the following breaking changes apply:
     ```TOML
     [[override]]
       name = "k8s.io/api"
-      **revision for tag "kubernetes-1.14.1"**
+      # **revision for tag "kubernetes-1.14.1"**
       revision = "6e4e0e4f393bf5e8bbff570acd13217aa5a770cd"
     [[override]]
       name = "k8s.io/apiextensions-apiserver"
-      **revision for tag "kubernetes-1.14.1"**
+      # **revision for tag "kubernetes-1.14.1"**
       revision = "727a075fdec8319bf095330e344b3ccc668abc73"
     [[override]]
       name = "k8s.io/apimachinery"
-      **revision for tag "kubernetes-1.14.1"**
+      # **revision for tag "kubernetes-1.14.1"**
       revision = "6a84e37a896db9780c75367af8d2ed2bb944022e"
     [[override]]
       name = "k8s.io/client-go"
-      **revision for tag "kubernetes-1.14.1"**
+      # **revision for tag "kubernetes-1.14.1"**
       revision = "1a26190bd76a9017e289958b9fba936430aa3704"
     [[override]]
       name = "github.com/coreos/prometheus-operator"
@@ -465,7 +464,7 @@ All method signatures for [`sigs.k8s.io/controller-runtime/pkg/client.Client`](h
     with:
     ```go
     listOpts := []client.ListOption{
-      client.InNamespace("namespace"),        
+      client.InNamespace("namespace"),
     }
     err = r.client.List(context.TODO(), podList, listOpts...)
     ```
@@ -634,7 +633,7 @@ Replace `*` per verbs in order to solve the issue [671](https://github.com/opera
 
 - **Deprecated:** Deprecated the `operator-sdk generate openapi` command. CRD generation is still supported with `operator-sdk generate crds`. It is now recommended to use [openapi-gen](https://github.com/kubernetes/kube-openapi/tree/master/cmd/openapi-gen) directly for OpenAPI code generation. The `generate openapi` subcommand will be removed in a future release.
 - **Breaking change:** An existing CSV's `spec.customresourcedefinitions.owned` is now always overwritten except for each name, version, and kind on invoking olm-catalog gen-csv when Go API code annotations are present.
-- **Potentially Breaking change:** Be aware that there are potentially other breaking changes due to the controller-runtime and Kubernetes version be upgraded from `v0.4.0` to `v1.16.2, respectively. There may be breaking changes to Go client code due to both of those changes.
+- **Potentially Breaking change:** Be aware that there are potentially other breaking changes due to the controller-runtime and Kubernetes version be upgraded from `v0.4.0` to `v1.16.2`, respectively. There may be breaking changes to Go client code due to both of those changes.
 
 For further detailed information see [CHANGELOG](https://github.com/operator-framework/operator-sdk/blob/master/CHANGELOG.md#v0130)
 
@@ -1341,7 +1340,7 @@ install:
   - pip3 install docker molecule ansible-lint yamllint flake8 openshift jmespath
 ```
 
-**NOTE** To know more about how to upgrade your project to use the V3 Molecule version see [here](https://github.com/ansible-community/molecule/issues/2560).  
+**NOTE** To know more about how to upgrade your project to use the V3 Molecule version see [here](https://github.com/ansible-community/molecule/issues/2560).
 
 **Deprecations**
 


Test patch:
diff --git a/hack/generate/samples/generate_testdata.go b/hack/generate/samples/generate_testdata.go
index 69125cdc..b5fd758c 100644
--- a/hack/generate/samples/generate_testdata.go
+++ b/hack/generate/samples/generate_testdata.go
@@ -28,31 +28,37 @@ import (
 )
 
 func main() {
-	// testdata is the path where all samples should be generate
-	const testdata = "/testdata/"
-
-	// binaryName allow inform the binary that should be used.
+	// binaryPath allow inform the binary that should be used.
 	// By default it is operator-sdk
-	var binaryName string
+	var binaryPath string
 
-	flag.StringVar(&binaryName, "bin", testutils.BinaryName, "Binary path that should be used")
+	flag.StringVar(&binaryPath, "bin", testutils.BinaryName, "Binary path that should be used")
 	flag.Parse()
 
-	currentPath, err := os.Getwd()
+	// Make the binary path absolute if pathed, for reproducibility and debugging purposes.
+	if dir, _ := filepath.Split(binaryPath); dir != "" {
+		tmp, err := filepath.Abs(binaryPath)
+		if err != nil {
+			log.Fatalf("Failed to make binary path %q absolute: %v", binaryPath, err)
+		}
+		binaryPath = tmp
+	}
+
+	wd, err := os.Getwd()
 	if err != nil {
-		log.Error(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
-	samplesPath := filepath.Join(currentPath, testdata)
-	log.Infof("using the path: (%v)", samplesPath)
+	// samplesPath is the path where all samples should be generated
+	samplesPath := filepath.Join(wd, "testdata")
+	log.Infof("writing sample directories under %s", samplesPath)
 
 	log.Infof("creating Helm Memcached Sample")
-	helm.GenerateMemcachedHelmSample(samplesPath)
+	helm.GenerateMemcachedSamples(binaryPath, samplesPath)
 
 	log.Infof("creating Ansible Memcached Sample")
-	ansible.GenerateMemcachedAnsibleSample(samplesPath)
+	ansible.GenerateMemcachedSamples(binaryPath, samplesPath)
 
 	log.Infof("creating Go Memcached Sample with Webhooks")
-	golang.GenerateMemcachedGoWithWebhooksSample(samplesPath)
+	golang.GenerateMemcachedSamples(binaryPath, samplesPath)
 }
diff --git a/images/custom-scorecard-tests/Dockerfile b/images/custom-scorecard-tests/Dockerfile
index 07f1a411..deb1ff98 100644
--- a/images/custom-scorecard-tests/Dockerfile
+++ b/images/custom-scorecard-tests/Dockerfile
@@ -17,7 +17,7 @@ COPY . .
 RUN GOOS=linux GOARCH=$TARGETARCH make build/custom-scorecard-tests
 
 # Final image.
-FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-230
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-298
 
 ENV HOME=/opt/custom-scorecard-tests \
     USER_NAME=custom-scorecard-tests \
diff --git a/images/scorecard-test/Dockerfile b/images/scorecard-test/Dockerfile
index 996970c5..ec014a2b 100644
--- a/images/scorecard-test/Dockerfile
+++ b/images/scorecard-test/Dockerfile
@@ -17,7 +17,7 @@ COPY . .
 RUN GOOS=linux GOARCH=$TARGETARCH make build/scorecard-test
 
 # Final image.
-FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-230
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-298
 
 ENV HOME=/opt/scorecard-test \
     USER_NAME=scorecard-test \
diff --git a/internal/ansible/flags/flag_test.go b/internal/ansible/flags/flag_test.go
new file mode 100644
index 00000000..095918ea
--- /dev/null
+++ b/internal/ansible/flags/flag_test.go
@@ -0,0 +1,72 @@
+// 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 flags_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/spf13/pflag"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+
+	"github.com/operator-framework/operator-sdk/internal/ansible/flags"
+)
+
+var _ = Describe("Flags", func() {
+	Describe("ToManagerOptions", func() {
+		var (
+			f       *flags.Flags
+			flagSet *pflag.FlagSet
+			options manager.Options
+		)
+		BeforeEach(func() {
+			f = &flags.Flags{}
+			flagSet = pflag.NewFlagSet("test", pflag.ExitOnError)
+			f.AddTo(flagSet)
+		})
+
+		When("the flag is set", func() {
+			It("uses the flag value when corresponding option value is empty", func() {
+				expOptionValue := ":5678"
+				options.MetricsBindAddress = ""
+				parseArgs(flagSet, "--metrics-bind-address", expOptionValue)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+			It("uses the flag value when corresponding option value is not empty", func() {
+				expOptionValue := ":5678"
+				options.MetricsBindAddress = ":1234"
+				parseArgs(flagSet, "--metrics-bind-address", expOptionValue)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+		})
+		When("the flag is not set", func() {
+			It("uses the default flag value when corresponding option value is empty", func() {
+				expOptionValue := ":8080"
+				options.MetricsBindAddress = ""
+				parseArgs(flagSet)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+			It("uses the option value when corresponding option value is not empty", func() {
+				expOptionValue := ":1234"
+				options.MetricsBindAddress = expOptionValue
+				parseArgs(flagSet)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+		})
+	})
+})
+
+func parseArgs(fs *pflag.FlagSet, extraArgs ...string) {
+	Expect(fs.Parse(extraArgs)).To(Succeed())
+}
diff --git a/internal/ansible/flags/suite_test.go b/internal/ansible/flags/suite_test.go
new file mode 100644
index 00000000..09916d7f
--- /dev/null
+++ b/internal/ansible/flags/suite_test.go
@@ -0,0 +1,27 @@
+// 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 flags_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+func TestFlags(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Flags Suite")
+}
diff --git a/internal/helm/controller/reconcile_test.go b/internal/helm/controller/reconcile_test.go
index 130b1d5f..188e936f 100644
--- a/internal/helm/controller/reconcile_test.go
+++ b/internal/helm/controller/reconcile_test.go
@@ -21,8 +21,8 @@ import (
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
-func TestHasHelmUpgradeForceAnnotation(t *testing.T) {
-	tests := []struct {
+func TestHasAnnotation(t *testing.T) {
+	upgradeForceTests := []struct {
 		input       map[string]interface{}
 		expectedVal bool
 		expectedOut string
@@ -33,47 +33,101 @@ func TestHasHelmUpgradeForceAnnotation(t *testing.T) {
 				"helm.sdk.operatorframework.io/upgrade-force": "True",
 			},
 			expectedVal: true,
-			name:        "base case true",
+			name:        "upgrade force base case true",
 		},
 		{
 			input: map[string]interface{}{
 				"helm.sdk.operatorframework.io/upgrade-force": "False",
 			},
 			expectedVal: false,
-			name:        "base case false",
+			name:        "upgrade force base case false",
 		},
 		{
 			input: map[string]interface{}{
 				"helm.sdk.operatorframework.io/upgrade-force": "1",
 			},
 			expectedVal: true,
-			name:        "true as int",
+			name:        "upgrade force true as int",
 		},
 		{
 			input: map[string]interface{}{
 				"helm.sdk.operatorframework.io/upgrade-force": "0",
 			},
 			expectedVal: false,
-			name:        "false as int",
+			name:        "upgrade force false as int",
 		},
 		{
 			input: map[string]interface{}{
 				"helm.sdk.operatorframework.io/wrong-annotation": "true",
 			},
 			expectedVal: false,
-			name:        "annotation not set",
+			name:        "upgrade force annotation not set",
 		},
 		{
 			input: map[string]interface{}{
 				"helm.sdk.operatorframework.io/upgrade-force": "invalid",
 			},
 			expectedVal: false,
-			name:        "invalid value",
+			name:        "upgrade force invalid value",
 		},
 	}
 
-	for _, test := range tests {
-		assert.Equal(t, test.expectedVal, hasHelmUpgradeForceAnnotation(annotations(test.input)), test.name)
+	for _, test := range upgradeForceTests {
+		assert.Equal(t, test.expectedVal, hasAnnotation(helmUpgradeForceAnnotation, annotations(test.input)), test.name)
+	}
+
+	uninstallWaitTests := []struct {
+		input       map[string]interface{}
+		expectedVal bool
+		expectedOut string
+		name        string
+	}{
+		{
+			input: map[string]interface{}{
+				"helm.sdk.operatorframework.io/uninstall-wait": "True",
+			},
+			expectedVal: true,
+			name:        "uninstall wait base case true",
+		},
+		{
+			input: map[string]interface{}{
+				"helm.sdk.operatorframework.io/uninstall-wait": "False",
+			},
+			expectedVal: false,
+			name:        "uninstall wait base case false",
+		},
+		{
+			input: map[string]interface{}{
+				"helm.sdk.operatorframework.io/uninstall-wait": "1",
+			},
+			expectedVal: true,
+			name:        "uninstall wait true as int",
+		},
+		{
+			input: map[string]interface{}{
+				"helm.sdk.operatorframework.io/uninstall-wait": "0",
+			},
+			expectedVal: false,
+			name:        "uninstall wait false as int",
+		},
+		{
+			input: map[string]interface{}{
+				"helm.sdk.operatorframework.io/wrong-annotation": "true",
+			},
+			expectedVal: false,
+			name:        "uninstall wait annotation not set",
+		},
+		{
+			input: map[string]interface{}{
+				"helm.sdk.operatorframework.io/uninstall-wait": "invalid",
+			},
+			expectedVal: false,
+			name:        "uninstall wait invalid value",
+		},
+	}
+
+	for _, test := range uninstallWaitTests {
+		assert.Equal(t, test.expectedVal, hasAnnotation(helmUninstallWaitAnnotation, annotations(test.input)), test.name)
 	}
 }
 
diff --git a/internal/helm/flags/flag_test.go b/internal/helm/flags/flag_test.go
new file mode 100644
index 00000000..5b9fdd9c
--- /dev/null
+++ b/internal/helm/flags/flag_test.go
@@ -0,0 +1,72 @@
+// 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 flags_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/spf13/pflag"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+
+	"github.com/operator-framework/operator-sdk/internal/helm/flags"
+)
+
+var _ = Describe("Flags", func() {
+	Describe("ToManagerOptions", func() {
+		var (
+			f       *flags.Flags
+			flagSet *pflag.FlagSet
+			options manager.Options
+		)
+		BeforeEach(func() {
+			f = &flags.Flags{}
+			flagSet = pflag.NewFlagSet("test", pflag.ExitOnError)
+			f.AddTo(flagSet)
+		})
+
+		When("the flag is set", func() {
+			It("uses the flag value when corresponding option value is empty", func() {
+				expOptionValue := ":5678"
+				options.MetricsBindAddress = ""
+				parseArgs(flagSet, "--metrics-bind-address", expOptionValue)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+			It("uses the flag value when corresponding option value is not empty", func() {
+				expOptionValue := ":5678"
+				options.MetricsBindAddress = ":1234"
+				parseArgs(flagSet, "--metrics-bind-address", expOptionValue)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+		})
+		When("the flag is not set", func() {
+			It("uses the default flag value when corresponding option value is empty", func() {
+				expOptionValue := ":8080"
+				options.MetricsBindAddress = ""
+				parseArgs(flagSet)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+			It("uses the option value when corresponding option value is not empty", func() {
+				expOptionValue := ":1234"
+				options.MetricsBindAddress = expOptionValue
+				parseArgs(flagSet)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+		})
+	})
+})
+
+func parseArgs(fs *pflag.FlagSet, extraArgs ...string) {
+	Expect(fs.Parse(extraArgs)).To(Succeed())
+}
diff --git a/internal/helm/flags/suite_test.go b/internal/helm/flags/suite_test.go
new file mode 100644
index 00000000..09916d7f
--- /dev/null
+++ b/internal/helm/flags/suite_test.go
@@ -0,0 +1,27 @@
+// 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 flags_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+func TestFlags(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Flags Suite")
+}
diff --git a/internal/scorecard/testdata/pod.yaml b/internal/scorecard/testdata/pod.yaml
index 3b36f466..b55f50fb 100644
--- a/internal/scorecard/testdata/pod.yaml
+++ b/internal/scorecard/testdata/pod.yaml
@@ -12,7 +12,7 @@ spec:
           apiVersion: v1
           fieldPath: metadata.namespace
     image: quay.io/operator-framework/scorecard-test:dev 
-    imagePullPolicy: IfNotPresent
+    imagePullPolicy: Always
     name: scorecard-test
     command: ["/usr/local/bin/scorecard-test"]
     args: ["basic-check-spec"]
diff --git a/test/e2e/helm/cluster_test.go b/test/e2e/helm/cluster_test.go
index b3213efe..f9bb2ab5 100644
--- a/test/e2e/helm/cluster_test.go
+++ b/test/e2e/helm/cluster_test.go
@@ -276,17 +276,84 @@ var _ = Describe("Running Helm projects", func() {
 				tc.Version)
 			Eventually(getCurlLogs, time.Minute, time.Second).Should(ContainSubstring(metricExportedCR))
 
+			By("annotate CR with uninstall-wait")
+			cmdOpts = []string{
+				"annotate", tc.Kind, fmt.Sprintf("%s-sample", strings.ToLower(tc.Kind)),
+				"helm.sdk.operatorframework.io/uninstall-wait=true",
+			}
+			_, err = tc.Kubectl.Command(cmdOpts...)
+			Expect(err).NotTo(HaveOccurred())
+
+			By("adding a finalizer to statefulset")
+			cmdOpts = []string{
+				"patch", "statefulset", releaseName, "-p",
+				"{\"metadata\":{\"finalizers\":[\"helm.sdk.operatorframework.io/fake-finalizer\"]}}",
+				"--type=merge",
+			}
+			_, err = tc.Kubectl.Command(cmdOpts...)
+			Expect(err).NotTo(HaveOccurred())
+
 			By("deleting CR manifest")
+			_, err = tc.Kubectl.Delete(false, "-f", sampleFile, "--wait=false")
+			Expect(err).NotTo(HaveOccurred())
+
+			By("ensuring the CR gets reconciled and uninstall-wait is enabled")
+			managerContainerLogsAfterDeleteCR := func() string {
+				logOutput, err := tc.Kubectl.Logs(controllerPodName, "-c", "manager")
+				Expect(err).NotTo(HaveOccurred())
+				return logOutput
+			}
+			Eventually(managerContainerLogsAfterDeleteCR, time.Minute, time.Second).Should(ContainSubstring("Uninstall wait"))
+			Eventually(managerContainerLogsAfterDeleteCR, time.Minute, time.Second).Should(ContainSubstring("Waiting until all resources are deleted"))
+
+			By("removing the finalizer from statefulset")
+			cmdOpts = []string{
+				"patch", "statefulset", releaseName, "-p",
+				"{\"metadata\":{\"finalizers\":[]}}",
+				"--type=merge",
+			}
+			_, err = tc.Kubectl.Command(cmdOpts...)
+			Expect(err).NotTo(HaveOccurred())
+
+			By("ensuring the CR gets reconciled and CR finalizer is removed")
+			Eventually(managerContainerLogsAfterDeleteCR, 2*time.Minute, time.Second).Should(ContainSubstring("Removing finalizer"))
+
+			By("ensuring the CR is deleted")
+			verifyDeletedCR := func() error {
+				_, err := tc.Kubectl.Get(
+					true,
+					tc.Kind, fmt.Sprintf("%s-sample", strings.ToLower(tc.Kind)))
+				if err == nil {
+					return fmt.Errorf("the %s CR is not deleted", tc.Kind)
+				}
+				return nil
+			}
+			Eventually(verifyDeletedCR, time.Minute, time.Second).Should(Succeed())
+
+			By("creating an instance of release(CR) again after a delete with uninstall-wait")
+			_, err = tc.Kubectl.Apply(false, "-f", sampleFile)
+			Expect(err).NotTo(HaveOccurred())
+
+			By("ensuring the CR gets reconciled and the release was Installed again")
+			managerContainerLogs = func() string {
+				logOutput, err := tc.Kubectl.Logs(controllerPodName, "-c", "manager")
+				Expect(err).NotTo(HaveOccurred())
+				return logOutput
+			}
+			Eventually(managerContainerLogs, time.Minute, time.Second).Should(ContainSubstring("Installed release"))
+
+			By("deleting CR manifest again without uninstall-wait")
 			_, err = tc.Kubectl.Delete(false, "-f", sampleFile)
 			Expect(err).NotTo(HaveOccurred())
 
 			By("ensuring the CR gets reconciled and the release was Uninstalled")
-			managerContainerLogsAfterDeleteCR := func() string {
+			managerContainerLogsAfterDeleteCR = func() string {
 				logOutput, err := tc.Kubectl.Logs(controllerPodName, "-c", "manager")
 				Expect(err).NotTo(HaveOccurred())
 				return logOutput
 			}
 			Eventually(managerContainerLogsAfterDeleteCR, time.Minute, time.Second).Should(ContainSubstring("Uninstalled release"))
+			Eventually(managerContainerLogsAfterDeleteCR, time.Minute, time.Second).Should(ContainSubstring("Removing finalizer"))
 		})
 	})
 })
diff --git a/testdata/ansible/memcached-operator/Dockerfile b/testdata/ansible/memcached-operator/Dockerfile
index 464a197d..4c52ea0f 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.5.0
+FROM quay.io/operator-framework/ansible-operator:v1.6.1
 
 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 7c4c38c7..54feb3cb 100644
--- a/testdata/ansible/memcached-operator/Makefile
+++ b/testdata/ansible/memcached-operator/Makefile
@@ -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.5.0/ansible-operator_$(OS)_$(ARCH) ;\
+	curl -sSLo $(ANSIBLE_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.6.1/ansible-operator_$(OS)_$(ARCH) ;\
 	chmod +x $(ANSIBLE_OPERATOR) ;\
 	}
 else
diff --git a/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml b/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml
index fa798691..6cd486d5 100644
--- a/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml
+++ b/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml
@@ -8,42 +8,42 @@ stages:
   - entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: basic
       test: basic-check-spec-test
   - entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-bundle-validation-test
   - entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-crds-have-validation-test
   - entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-crds-have-resources-test
   - entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-spec-descriptors-test
   - entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/ansible/memcached-operator/config/default/kustomization.yaml b/testdata/ansible/memcached-operator/config/default/kustomization.yaml
index a7a68300..7a8160b5 100644
--- a/testdata/ansible/memcached-operator/config/default/kustomization.yaml
+++ b/testdata/ansible/memcached-operator/config/default/kustomization.yaml
@@ -24,3 +24,7 @@ patchesStrategicMerge:
 # If you want your controller-manager to expose the /metrics
 # endpoint w/o any authn/z, please comment the following line.
 - manager_auth_proxy_patch.yaml
+
+# Mount the controller config file for loading manager configurations
+# through a ComponentConfig type
+#- manager_config_patch.yaml
diff --git a/testdata/ansible/memcached-operator/config/samples/cache_v1alpha1_memcached.yaml b/testdata/ansible/memcached-operator/config/samples/cache_v1alpha1_memcached.yaml
index aa7a58d5..9e263db9 100644
--- a/testdata/ansible/memcached-operator/config/samples/cache_v1alpha1_memcached.yaml
+++ b/testdata/ansible/memcached-operator/config/samples/cache_v1alpha1_memcached.yaml
@@ -3,4 +3,5 @@ kind: Memcached
 metadata:
   name: memcached-sample
 spec:
+  # Add fields here
   size: 1
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 9a2562c8..ec3f34a7 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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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 f2284ce8..9e095537 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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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 fa798691..6cd486d5 100644
--- a/testdata/go/v2/memcached-operator/bundle/tests/scorecard/config.yaml
+++ b/testdata/go/v2/memcached-operator/bundle/tests/scorecard/config.yaml
@@ -8,42 +8,42 @@ stages:
   - entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: basic
       test: basic-check-spec-test
   - entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-bundle-validation-test
   - entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-crds-have-validation-test
   - entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-crds-have-resources-test
   - entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-spec-descriptors-test
   - entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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 9a2562c8..ec3f34a7 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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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 f2284ce8..9e095537 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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-status-descriptors-test
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 fa798691..6cd486d5 100644
--- a/testdata/go/v3/memcached-operator/bundle/tests/scorecard/config.yaml
+++ b/testdata/go/v3/memcached-operator/bundle/tests/scorecard/config.yaml
@@ -8,42 +8,42 @@ stages:
   - entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: basic
       test: basic-check-spec-test
   - entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-bundle-validation-test
   - entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-crds-have-validation-test
   - entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-crds-have-resources-test
   - entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-spec-descriptors-test
   - entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-status-descriptors-test
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 9a2562c8..ec3f34a7 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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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 f2284ce8..9e095537 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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/helm/memcached-operator/Dockerfile b/testdata/helm/memcached-operator/Dockerfile
index f4fea722..5f4dce1e 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.5.0
+FROM quay.io/operator-framework/helm-operator:v1.6.1
 
 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 693d5d8c..2b618ad3 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.5.0/helm-operator_$(OS)_$(ARCH) ;\
+	curl -sSLo $(HELM_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.6.1/helm-operator_$(OS)_$(ARCH) ;\
 	chmod +x $(HELM_OPERATOR) ;\
 	}
 else
diff --git a/testdata/helm/memcached-operator/bundle/tests/scorecard/config.yaml b/testdata/helm/memcached-operator/bundle/tests/scorecard/config.yaml
index fa798691..6cd486d5 100644
--- a/testdata/helm/memcached-operator/bundle/tests/scorecard/config.yaml
+++ b/testdata/helm/memcached-operator/bundle/tests/scorecard/config.yaml
@@ -8,42 +8,42 @@ stages:
   - entrypoint:
     - scorecard-test
     - basic-check-spec
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: basic
       test: basic-check-spec-test
   - entrypoint:
     - scorecard-test
     - olm-bundle-validation
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-bundle-validation-test
   - entrypoint:
     - scorecard-test
     - olm-crds-have-validation
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-crds-have-validation-test
   - entrypoint:
     - scorecard-test
     - olm-crds-have-resources
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-crds-have-resources-test
   - entrypoint:
     - scorecard-test
     - olm-spec-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-spec-descriptors-test
   - entrypoint:
     - scorecard-test
     - olm-status-descriptors
-    image: quay.io/operator-framework/scorecard-test:v1.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/testdata/helm/memcached-operator/config/default/kustomization.yaml b/testdata/helm/memcached-operator/config/default/kustomization.yaml
index a7a68300..7a8160b5 100644
--- a/testdata/helm/memcached-operator/config/default/kustomization.yaml
+++ b/testdata/helm/memcached-operator/config/default/kustomization.yaml
@@ -24,3 +24,7 @@ patchesStrategicMerge:
 # If you want your controller-manager to expose the /metrics
 # endpoint w/o any authn/z, please comment the following line.
 - manager_auth_proxy_patch.yaml
+
+# Mount the controller config file for loading manager configurations
+# through a ComponentConfig type
+#- manager_config_patch.yaml
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 9a2562c8..ec3f34a7 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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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 f2284ce8..9e095537 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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     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.5.0
+    image: quay.io/operator-framework/scorecard-test:v1.6.1
     labels:
       suite: olm
       test: olm-status-descriptors-test
diff --git a/website/content/en/docs/contribution-guidelines/testing.md b/website/content/en/docs/contribution-guidelines/testing.md
index 5064d892..af011955 100644
--- a/website/content/en/docs/contribution-guidelines/testing.md
+++ b/website/content/en/docs/contribution-guidelines/testing.md
@@ -1,7 +1,7 @@
 ---
 title: Testing
 linkTitle: Testing
-weight: 10
+weight: 6
 ---
 
 On all PRs, a suite of static and cluster tests is run against your changes in a CI environment.