Repository: operator-framework/operator-sdk
Pull request #4693 (2021-03-24 02:01:24)

add new optional for bundle validate
**Description of the change:**

- Add a new optional flag to pass optional values to the validators.  For more info see the EP: https://github.com/operator-framework/enhancements/blob/master/enhancements/operator-hub-check.md

<img width="1067" alt="Screen Shot 2021-03-24 at 01 43 43" src="https://user-images.githubusercontent.com/7708031/112242908-8f9c9300-8c44-11eb-9a03-8297a3be470f.png">
<img width="1068" alt="Screen Shot 2021-03-24 at 01 44 06" src="https://user-images.githubusercontent.com/7708031/112242917-93301a00-8c44-11eb-83a4-782e40a5ec2e.png">

**Motivation for the change:**

Allow SDK to use the new checks added for OperatorHub.io

**NOTE** This PR is using my fork. We need to get merged first: https://github.com/operator-framework/api/pull/103 and follow-up: https://github.com/operator-framework/api/pull/111

**Checklist**

If the pull request includes user-facing changes, extra documentation is required:
- [x] Add a new changelog fragment in `changelog/fragments` (see [`changelog/fragments/00-template.yaml`](https://github.com/operator-framework/operator-sdk/tree/master/changelog/fragments/00-template.yaml))
- [x] Add or update relevant sections of the docs website in [`website/content/en/docs`](https://github.com/operator-framework/operator-sdk/tree/master/website/content/en/docs)


estroz: I'm not sure I see the value in supplying an optional value. Shouldn't the CSV validator easily be able to perform some checks if a CSV value is present, and skip them if not present?
estroz: For the sake of the argument, lets say this idea is accepted in general.

Lets also say two different validators have the same optional value key, but different semantics and/or value. How does `--optional-values` allow differentiation? The user may have to know about all validators that accept that key.

There are two ways around this:
1. Require optional value keys always have the validator's name as a key prefix, ex. `operatorhub-k8s=1.22`
2. Use some external configuration like [environment variables](https://github.com/operator-framework/api/pull/103#discussion_r606001123) to do (1).

I'd prefer (2) because it is less verbose and does not put the onus of optional value configuration onto operator-sdk, which merely imports and runs validators.
camilamacedo86: HI @estroz, 

> I'm not sure I see the value in supplying an optional value. Shouldn't the CSV validator easily be able to perform some checks if a CSV value is present, and skip them if not present?

I am not sure if I understand what you mean here. 

The idea of we provides the optional values is to allow us to send optional values to the validators. 
Regarding all needs that we could check so far, we need to pass the k8s/OCP versions to do the checks against the version that we have the intention to publish the project. It is important for the pipeline for example.  
camilamacedo86: Hi @jmrodri, @estroz, 

That would be very helpful if we are able to merge before the release. It will be helpful and collab with our efforts to identify and push the operator authors to use the latest API version. This check is used by the pipeline.   
camilamacedo86: Hi @estroz, 

Regarding your suggestion in https://github.com/operator-framework/operator-sdk/pull/4693#issuecomment-815252687 that was re-plied https://github.com/operator-framework/operator-sdk/pull/4693#issuecomment-815277896. See that it in fact has no relation with this PR at all. 

This PR, in SDK, only bumps the API lib and add the new flag which allows the users to inform optionally keys/values for the validators. In this way, any way, your suggestion would not fit here and ought to be addressed in the API. 

However, I still not thinking that we should move with that unless we have use case/scenarios that explicitly justify it since it will reduce usability and maintainability. So, in the future, if that be the case we can cange the API with you suggesting and that ought to not affect SDK at all. For now, IMO I think we can address the need as discussed and defined in the EP and implemented so far. 

c/c @estroz @fabianvf 
 
camilamacedo86: HI @estroz, @jmrodri, @fabianvf  

Now we have here only the validator impl. 
estroz: Before this goes in, I would really like to see docs about how this value is used in the api repo, and have them referenced in SDK docs. Otherwise we're just adding some nebulous, obscure feature.

/hold
marc-obrien: /hold totally agree with Eric's points
camilamacedo86: Hi @marc-obrien, @estroz,   

> Before this goes in, I would really like to see docs about how this value is used in the api repo, and have them referenced in SDK docs. Otherwise, we're just adding some nebulous, obscure feature.

The new option is doc here. See that the CLI commands are doc by default. 
Also, it is documented in the EP: https://github.com/operator-framework/enhancements/blob/master/enhancements/operator-hub-check.md

However, I totally agree that we should have all validators as their options **fully** documented and not only this one. But we have no documentation about that and it never was a blocker for we start to use them in the SDK or their options so far. 

See that doc the validators as their options it is a big task/epic that goes out of scope here and beyond. I raised an issue for we are able to address that. See: https://github.com/operator-framework/api/issues/109

Also, I raised an issue for OLM to add the SDK bundle validation options and doc that properly. We also have no info about that there. See: https://github.com/operator-framework/operator-lifecycle-manager/issues/2097 

And then, it is important to highlight that we cannot add any doc in OLM before this option exists and be released whcih means that if the request here is to do in OLM docs then, that cannot be a blocker either. However, not that I already pushed this info there as well. See https://github.com/operator-framework/olm-docs/pull/144 with the comment that we cannot merge that at all before it is released. https://github.com/operator-framework/olm-docs/pull/144#issuecomment-819015164

 





camilamacedo86: Hi @marc-obrien, 

As spoke with @estroz @jmrodri @fabianvf , the new optional-values flag (what is added here is doc via the CLI). Also, how that works is doc in the EP linked in the first comment. 

IHMO:

- we need to document all validators fully as their optional options in the API( https://github.com/operator-framework/api/issues/109)
- we need documentation in SDK that fully describes the operator bundle validation in the section over how to integrate their project with OLM. Currently, in POV that is not clear too. ( more https://github.com/operator-framework/operator-sdk/issues/4528 )
- Then, we can link the API docs to clarify its details and available options according to the tag/version bumped and used in the SDK project.
- Also, we need to add the SDK validation options in the OLM, OCP, partners, internal pipeline, etc docs. we have docs that suggest using the suite test to validate the bundles but that is not in all of them and then, it would be better if we could link when possible the SDK docs in these places instead of duplicating the info. See: https://github.com/operator-framework/operator-lifecycle-manager/issues/2097

In this way, I think that what we could doc so far is done and what eric request here is a big epic/tasks that go beyond this PR scope. See, that we cannot document its options with SDK on the OLM/OCP/partners(certified) docs, etc., before that exists and be released in SDK. 

Also, to clarifies **I am 100% in favor of having all validators as its options fully documented in the API as @estroz requested**, however, it is **not** today and, because of this, I tracked the issue ( https://github.com/operator-framework/api/issues/109 ). I am intended and happy to work on that as a low priority or help who can do it. 

Then, I cannot find any other extra place now to add any further doc about this new small option. However, please let me know where/how that should be added if you have it in mind.  

PS.: In POV, what was done until now shows enough to address the need and add the value that we need ( provide a helper to allow users/pipeline to check if their projects have or not deprecated APIs and will or not work against specific k8s 1.22) as the first step. 

camilamacedo86: Hi @marc-obrien, 

By @estroz 
> Before this goes in, I would really like to see docs about how this value is used in the api repo, and have them referenced in SDK docs. Otherwise we're just adding some nebulous, obscure feature.

By @marc-obrien 
> /hold totally agree with Eric's points

As spoke with @estroz @jmrodri @fabianvf , the new optional-values flag (what is added here is doc via the CLI). Also, how that works is doc in the EP linked in the first comment. 

IHMO:

- we need to document all validators fully as their optional options in the API( https://github.com/operator-framework/api/issues/109)
- we need documentation in SDK that fully describes the operator bundle validation in the section over how to integrate their project with OLM. Currently, in POV that is not clear too. ( more https://github.com/operator-framework/operator-sdk/issues/4528 )
- Then, we can link the API docs to clarify its details and available options according to the tag/version bumped and used in the SDK project.
- Also, we need to add the SDK validation options in the OLM, OCP, partners, internal pipeline, etc docs. we have places that suggested check the bundles via the suite before publishing such as (https://github.com/operator-framework/olm-docs/pull/1440) but that is not in all of them (e.g. https://github.com/operator-framework/operator-lifecycle-manager/issues/2097) and then, it would be better if we could link when possible the SDK docs in these places instead of duplicating the info.

See, that we cannot document its options with SDK on the OLM/OCP/partners(certified) docs, etc., before that exists and be released in SDK. See https://github.com/operator-framework/olm-docs/pull/144#issuecomment-819015164

Also, to clarifies **I am 100% in favor of having all validators as its options fully documented in the API as @estroz requested**, however, it is **not** today and, because of this, I tracked the issue ( https://github.com/operator-framework/api/issues/109 ). I am intended and happy to work on that as a low priority or help who can do it. However, that is a big epic/tasks that in POV goes beyond this PR scope.

Then, I cannot find any other extra place now to add any further doc about this new small option. However, please let me know where/how that should be added if you have it in mind.  

PS.: In POV, what was done until now shows enough to address the need and add the value that we need ( provide a helper to allow users/pipeline to check if their projects have or not deprecated APIs and will or not work against specific k8s 1.22) as the first step. But, we can always work with follow-ups and further improvements when/if required according to use cases and prioritization (agile development).

openshift-ci-robot: New changes are detected. LGTM label has been removed.
camilamacedo86: Hi @estroz, @jmrodri, @fabianvf;

- This pr is also doc the new option CLI/bundle validate
- The key k8s was renamed to k8s-version: https://github.com/operator-framework/api/pull/111
- The EP was supplement with the follow up suggestions for optional values ; https://github.com/operator-framework/enhancements/pull/79
- Also we have the PR to doc that in OLM: https://github.com/operator-framework/olm-docs/pull/144/files
- + issues tracked for the further gaps docs found. 

Please, let me know if has anything else that we ought to address. 

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 2a4c1881..c6e06bd7 100644
--- a/.github/workflows/deploy-manual.yml
+++ b/.github/workflows/deploy-manual.yml
@@ -55,16 +55,19 @@ jobs:
     # 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
+      id: update
       run: |
-        set -e
-        sed -i -E 's|FROM quay\.io/operator-framework/ansible-operator-base:.+|FROM '"${{ steps.tag.outputs.tag }}"'|' images/ansible-operator/Dockerfile
+        set -ex
+        sed -i -E 's|FROM quay\.io/operator-framework/ansible-operator-base:.+|FROM ${{ steps.tag.outputs.tag }}|g' images/ansible-operator/Dockerfile
         git diff --exit-code --quiet && echo "Failed to update images/ansible-operator/Dockerfile" && exit 1
+        REF="${{ github.event.ref }}"
+        echo ::set-output name=branch_name::"${REF##*/}"
 
     - 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 }}"
+        title: "[${{ steps.update.outputs.branch_name }}] image(ansible-operator): bump base to ${{ steps.tag.outputs.tag }}"
+        commit-message: "[${{ steps.update.outputs.branch_name }}] 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 e33d8698..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.6.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/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/new-option-validate.yaml b/changelog/fragments/new-option-validate.yaml
new file mode 100644
index 00000000..bbccb27a
--- /dev/null
+++ b/changelog/fragments/new-option-validate.yaml
@@ -0,0 +1,13 @@
+# entries is a list of entries to include in
+# release notes and/or the migration guide
+entries:
+  - description: >
+      Add new optional flag `--optional-values` to the command `operator-sdk bundle validate`. This option allow to inform a list of key and values to the validators. (e.g. `operator-sdk bundle validate ./bundle --optional-values=k8s-version=1.22 --select-optional suite=operatorframework`)
+
+    # kind is one of:
+    # - addition
+    # - change
+    # - deprecation
+    # - removal
+    # - bugfix
+    kind: "addition"
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 6c0338fb..6633644b 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.8.0
+	github.com/operator-framework/api v0.8.1-0.20210414192051-b51286920978
 	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.20210411191801-c7525d748153
+	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 aa1f88dd..4685f2e3 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.8.0 h1:S1R5BaPKeZoACbu0913mPnG33s7GOA2VrT9gvOeBjbU=
-github.com/operator-framework/api v0.8.0/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
+github.com/operator-framework/api v0.8.1-0.20210414192051-b51286920978 h1:I7bA53PwWaCr5x64vIjYNt384D0zQUwsZ8TA2DttRhQ=
+github.com/operator-framework/api v0.8.1-0.20210414192051-b51286920978/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.20210411191801-c7525d748153 h1:zNScO/WTy8ZTg3lxnjm1y/oHcZgKJx0IY4axJCHKSE0=
-sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210411191801-c7525d748153/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/bundle/validate/optional.go b/internal/cmd/operator-sdk/bundle/validate/optional.go
index d85019b8..c87c165c 100644
--- a/internal/cmd/operator-sdk/bundle/validate/optional.go
+++ b/internal/cmd/operator-sdk/bundle/validate/optional.go
@@ -47,8 +47,8 @@ var optionalValidators = validators{
 }
 
 // runOptionalValidators runs optional validators selected by sel on bundle.
-func runOptionalValidators(bundle *apimanifests.Bundle, sel labels.Selector) []apierrors.ManifestResult {
-	return optionalValidators.run(bundle, sel)
+func runOptionalValidators(bundle *apimanifests.Bundle, sel labels.Selector, optionalValues map[string]string) []apierrors.ManifestResult {
+	return optionalValidators.run(bundle, sel, optionalValues)
 }
 
 // listOptionalValidators lists all optional validators.
@@ -101,7 +101,7 @@ func (vals validators) checkMatches(sel labels.Selector) error {
 }
 
 // run runs optional validators selected by sel on bundle.
-func (vals validators) run(bundle *apimanifests.Bundle, sel labels.Selector) (results []apierrors.ManifestResult) {
+func (vals validators) run(bundle *apimanifests.Bundle, sel labels.Selector, optionalValues map[string]string) (results []apierrors.ManifestResult) {
 	// No selector set, do not run any optional validators.
 	if sel == nil || sel.String() == "" {
 		return results
@@ -116,6 +116,9 @@ func (vals validators) run(bundle *apimanifests.Bundle, sel labels.Selector) (re
 		objs = append(objs, obj)
 	}
 
+	// Pass the --optional-values. e.g. --optional-values="k8s-version=1.22"
+	objs = append(objs, optionalValues)
+
 	for _, v := range vals {
 		if sel.Matches(labels.Set(v.labels)) {
 			results = append(results, v.Validate(objs...)...)
diff --git a/internal/cmd/operator-sdk/bundle/validate/validate.go b/internal/cmd/operator-sdk/bundle/validate/validate.go
index 47a14599..913a454e 100644
--- a/internal/cmd/operator-sdk/bundle/validate/validate.go
+++ b/internal/cmd/operator-sdk/bundle/validate/validate.go
@@ -36,12 +36,13 @@ import (
 )
 
 type bundleValidateCmd struct {
-	directory    string
-	imageBuilder string
-	outputFormat string
-	selectorRaw  string
-	selector     labels.Selector
-	listOptional bool
+	directory      string
+	imageBuilder   string
+	outputFormat   string
+	selectorRaw    string
+	selector       labels.Selector
+	listOptional   bool
+	optionalValues map[string]string
 }
 
 // validate verifies the command args
@@ -79,6 +80,11 @@ func (c *bundleValidateCmd) addToFlagSet(fs *pflag.FlagSet) {
 	fs.BoolVar(&c.listOptional, "list-optional", false,
 		"List all optional validators available. When set, no validators will be run")
 
+	optionalValueEmpty := map[string]string{}
+	fs.StringToStringVarP(&c.optionalValues, "optional-values", "", optionalValueEmpty,
+		"Inform a []string map of key=values which can be used by the validator. e.g. to check the operator bundle "+
+			"against an Kubernetes version that it is intended to be distributed use `--optional-values=k8s-version=1.22`")
+
 	fs.StringVarP(&c.outputFormat, "output", "o", internal.Text,
 		"Result format for results. One of: [text, json-alpha1]. Note: output format types containing "+
 			"\"alphaX\" are subject to change and not covered by guarantees of stable APIs.")
@@ -144,7 +150,7 @@ func (c bundleValidateCmd) run(logger *log.Entry, bundleRaw string) (res *intern
 	res.AddManifestResults(results...)
 
 	// Run optional validators.
-	results = runOptionalValidators(bundle, c.selector)
+	results = runOptionalValidators(bundle, c.selector, c.optionalValues)
 	res.AddManifestResults(results...)
 
 	return res, nil
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/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/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/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/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/cli/operator-sdk_bundle_validate.md b/website/content/en/docs/cli/operator-sdk_bundle_validate.md
index 793fe054..066a019a 100644
--- a/website/content/en/docs/cli/operator-sdk_bundle_validate.md
+++ b/website/content/en/docs/cli/operator-sdk_bundle_validate.md
@@ -68,11 +68,12 @@ To validate a bundle against the validator for operatorhub.io specifically, in a
 ### Options
 
 ```
-  -h, --help                     help for validate
-  -b, --image-builder string     Tool to pull and unpack bundle images. Only used when validating a bundle image. One of: [docker, podman, none] (default "docker")
-      --list-optional            List all optional validators available. When set, no validators will be run
-  -o, --output string            Result format for results. One of: [text, json-alpha1]. Note: output format types containing "alphaX" are subject to change and not covered by guarantees of stable APIs. (default "text")
-      --select-optional string   Label selector to select optional validators to run. Run this command with '--list-optional' to list available optional validators
+  -h, --help                                                 help for validate
+  -b, --image-builder string                                 Tool to pull and unpack bundle images. Only used when validating a bundle image. One of: [docker, podman, none] (default "docker")
+      --list-optional                                        List all optional validators available. When set, no validators will be run
+      --optional-values --optional-values=k8s-version=1.22   Inform a []string map of key=values which can be used by the validator. e.g. to check the operator bundle against an Kubernetes version that it is intended to be distributed use --optional-values=k8s-version=1.22 (default [])
+  -o, --output string                                        Result format for results. One of: [text, json-alpha1]. Note: output format types containing "alphaX" are subject to change and not covered by guarantees of stable APIs. (default "text")
+      --select-optional string                               Label selector to select optional validators to run. Run this command with '--list-optional' to list available optional validators
 ```
 
 ### Options inherited from parent commands
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..73ed341d
--- /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
+
+Contributors are able to create their own plugin(s) using the same standards and approach described by this document. Following them facilitates in-tree (in other words, built with the `operator-sdk` binary) addition of such community-driven plugins to the Operator SDK project by making review easier and code maintainable. Currently, the SDK cannot discover and use plugins that are not in-tree. However, out-of-tree plugins have been discussed in [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 8d0db628..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.6.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/olm-integration/generation.md b/website/content/en/docs/olm-integration/generation.md
index 1a04634d..54a9eea5 100644
--- a/website/content/en/docs/olm-integration/generation.md
+++ b/website/content/en/docs/olm-integration/generation.md
@@ -237,6 +237,18 @@ bundle: ...
   operator-sdk bundle validate ./bundle --select-optional name=operatorhub
 ```
 
+Also, see that you can test the bundle against the suite of test to ensure it against all criteria:
+
+```sh 
+operator-sdk bundle validate ./bundle --select-optional suite=operatorframework 
+```  
+
+**Note**: The `OperatorHub.io` validator in the `operatorframework` optional suite allows you to validate that your manifests can work with a Kubernetes cluster of a particular version using the `k8s-version` optional key value:
+
+```sh 
+operator-sdk bundle validate ./bundle --select-optional suite=operatorframework --optional-values=k8s-version=1.22
+```
+
 Documentation on optional validators:
 - [`operatorhub`][operatorhub_validator]
 
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/cmd/operator-sdk/bundle/validate/optional_test.go b/internal/cmd/operator-sdk/bundle/validate/optional_test.go
index 83ea43f2..081c2777 100644
--- a/internal/cmd/operator-sdk/bundle/validate/optional_test.go
+++ b/internal/cmd/operator-sdk/bundle/validate/optional_test.go
@@ -46,14 +46,14 @@ var _ = Describe("Running optional validators", func() {
 		It("runs no validators for an empty selector", func() {
 			bundle = &apimanifests.Bundle{}
 			sel = labels.SelectorFromSet(map[string]string{})
-			Expect(vals.run(bundle, sel)).To(HaveLen(0))
+			Expect(vals.run(bundle, sel, nil)).To(HaveLen(0))
 		})
 		It("runs a validator for one selector on an empty bundle", func() {
 			bundle = &apimanifests.Bundle{}
 			sel = labels.SelectorFromSet(map[string]string{
 				nameKey: "operatorhub",
 			})
-			results = vals.run(bundle, sel)
+			results = vals.run(bundle, sel, map[string]string{"k8s-version": "1.22"})
 			Expect(results).To(HaveLen(1))
 			Expect(results[0].Errors).To(HaveLen(1))
 		})
@@ -63,7 +63,7 @@ var _ = Describe("Running optional validators", func() {
 			sel = labels.SelectorFromSet(map[string]string{
 				nameKey: "operatorhub",
 			})
-			results = vals.run(bundle, sel)
+			results = vals.run(bundle, sel, nil)
 			Expect(results).To(HaveLen(1))
 			// Only test that more than one error was returned than the empty bundle case, which
 			// indicates validation happening.
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/testdata/ansible/memcached-operator/Dockerfile b/testdata/ansible/memcached-operator/Dockerfile
index 7fe8d337..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.6.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 0625ecd8..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.6.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 102f82a7..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.6.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.6.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.6.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.6.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.6.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.6.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 82c03ac2..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.6.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 61942fa2..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.6.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.6.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.6.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.6.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.6.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 102f82a7..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.6.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.6.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.6.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.6.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.6.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.6.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 82c03ac2..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.6.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 61942fa2..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.6.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.6.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.6.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.6.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.6.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 102f82a7..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.6.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.6.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.6.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.6.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.6.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.6.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 82c03ac2..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.6.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 61942fa2..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.6.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.6.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.6.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.6.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.6.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 067a66fa..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.6.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 11928a2b..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.6.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 102f82a7..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.6.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.6.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.6.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.6.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.6.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.6.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 82c03ac2..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.6.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 61942fa2..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.6.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.6.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.6.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.6.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.6.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.