Repository: operator-framework/operator-sdk
Pull request #4772 (2021-04-15 14:13:26)

fix: ensure labels are ordered in generated bundle
**Description of the change:**

Sorting the OtherLabels in bundle command.

**Motivation for the change:**

OtherLabels stem from a map and will have random order between
executions causing the generated bundle Dockerfile to potentially
result in a dirty git state.



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/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/go.mod b/go.mod
index 6c0338fb..126b89aa 100644
--- a/go.mod
+++ b/go.mod
@@ -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..633ff930 100644
--- a/go.sum
+++ b/go.sum
@@ -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/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/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/installation/_index.md b/website/content/en/docs/installation/_index.md
index eff5fb72..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.1
+export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.6.1
 curl -LO ${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH}
 ```
 
diff --git a/website/content/en/docs/upgrading-sdk-version/version-upgrade-guide.md b/website/content/en/docs/upgrading-sdk-version/version-upgrade-guide.md
index dbba1715..2a9393ce 100644
--- a/website/content/en/docs/upgrading-sdk-version/version-upgrade-guide.md
+++ b/website/content/en/docs/upgrading-sdk-version/version-upgrade-guide.md
@@ -10,7 +10,6 @@ For some versions it might also be necessary to update the upstream Kubernetes a
 
 The full list of dependencies and their versions required by a particular version of the SDK can be viewed by using the [`operator-sdk print-deps`][print-deps-cli] command. Use this command to update the project `Gopkg.toml`/`go.mod` file accordingly as you upgrade to a new SDK version.
 
-
 For some SDK versions after `v0.1.0` there might be minor breaking changes in the controller-runtime APIs, `operator-sdk` CLI or the expected project layout and file names. These breaking changes will usually be outlined in the [CHANGELOG][changelog]/[release-notes][release-notes] for each release version.
 
 For releases that have a large number of breaking changes or involve a significant refactoring of the APIs and project layout there will be a migration guide similar to the [v0.1.0 migration guide][v0.1.0-migration-guide].
@@ -32,27 +31,27 @@ The following sections outline the upgrade steps for each SDK version along with
   ```TOML
   [[override]]
     name = "k8s.io/code-generator"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "3dcf91f64f638563e5106f21f50c31fa361c918d"
 
   [[override]]
     name = "k8s.io/api"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "b503174bad5991eb66f18247f52e41c3258f6348"
 
   [[override]]
     name = "k8s.io/apiextensions-apiserver"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "0cd23ebeb6882bd1cdc2cb15fc7b2d72e8a86a5b"
 
   [[override]]
     name = "k8s.io/apimachinery"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "eddba98df674a16931d2d4ba75edc3a389bf633a"
 
   [[override]]
     name = "k8s.io/client-go"
-    **revision for tag "kubernetes-1.12.3"**
+    # **revision for tag "kubernetes-1.12.3"**
     revision = "d082d5923d3cc0bfbb066ee5fbdea3d0ca79acf8"
 
   [[override]]
@@ -79,27 +78,27 @@ The following sections outline the upgrade steps for each SDK version along with
   ```TOML
   [[override]]
     name = "k8s.io/code-generator"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "c2090bec4d9b1fb25de3812f868accc2bc9ecbae"
 
   [[override]]
     name = "k8s.io/api"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "05914d821849570fba9eacfb29466f2d8d3cd229"
 
   [[override]]
     name = "k8s.io/apiextensions-apiserver"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "0fe22c71c47604641d9aa352c785b7912c200562"
 
   [[override]]
     name = "k8s.io/apimachinery"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "2b1284ed4c93a43499e781493253e2ac5959c4fd"
 
   [[override]]
     name = "k8s.io/client-go"
-    **revision for tag "kubernetes-1.13.1"**
+    # **revision for tag "kubernetes-1.13.1"**
     revision = "8d9ed539ba3134352c586810e749e58df4e94e4f"
 
   [[override]]
@@ -286,19 +285,19 @@ Upon updating the project to `v0.8.2` the following breaking changes apply:
   ```TOML
   [[override]]
     name = "k8s.io/api"
-    **revision for tag "kubernetes-1.13.4"**
+    # **revision for tag "kubernetes-1.13.4"**
     revision = "5cb15d34447165a97c76ed5a60e4e99c8a01ecfe"
   [[override]]
     name = "k8s.io/apiextensions-apiserver"
-    **revision for tag "kubernetes-1.13.4"**
+    # **revision for tag "kubernetes-1.13.4"**
     revision = "d002e88f6236312f0289d9d1deab106751718ff0"
   [[override]]
     name = "k8s.io/apimachinery"
-    **revision for tag "kubernetes-1.13.4"**
+    # **revision for tag "kubernetes-1.13.4"**
     revision = "86fb29eff6288413d76bd8506874fddd9fccdff0"
   [[override]]
     name = "k8s.io/client-go"
-    **revision for tag "kubernetes-1.13.4"**
+    # **revision for tag "kubernetes-1.13.4"**
     revision = "b40b2a5939e43f7ffe0028ad67586b7ce50bb675"
   [[override]]
     name = "github.com/coreos/prometheus-operator"
@@ -380,19 +379,19 @@ Upon updating the project to `v0.8.2` the following breaking changes apply:
     ```TOML
     [[override]]
       name = "k8s.io/api"
-      **revision for tag "kubernetes-1.14.1"**
+      # **revision for tag "kubernetes-1.14.1"**
       revision = "6e4e0e4f393bf5e8bbff570acd13217aa5a770cd"
     [[override]]
       name = "k8s.io/apiextensions-apiserver"
-      **revision for tag "kubernetes-1.14.1"**
+      # **revision for tag "kubernetes-1.14.1"**
       revision = "727a075fdec8319bf095330e344b3ccc668abc73"
     [[override]]
       name = "k8s.io/apimachinery"
-      **revision for tag "kubernetes-1.14.1"**
+      # **revision for tag "kubernetes-1.14.1"**
       revision = "6a84e37a896db9780c75367af8d2ed2bb944022e"
     [[override]]
       name = "k8s.io/client-go"
-      **revision for tag "kubernetes-1.14.1"**
+      # **revision for tag "kubernetes-1.14.1"**
       revision = "1a26190bd76a9017e289958b9fba936430aa3704"
     [[override]]
       name = "github.com/coreos/prometheus-operator"
@@ -465,7 +464,7 @@ All method signatures for [`sigs.k8s.io/controller-runtime/pkg/client.Client`](h
     with:
     ```go
     listOpts := []client.ListOption{
-      client.InNamespace("namespace"),        
+      client.InNamespace("namespace"),
     }
     err = r.client.List(context.TODO(), podList, listOpts...)
     ```
@@ -634,7 +633,7 @@ Replace `*` per verbs in order to solve the issue [671](https://github.com/opera
 
 - **Deprecated:** Deprecated the `operator-sdk generate openapi` command. CRD generation is still supported with `operator-sdk generate crds`. It is now recommended to use [openapi-gen](https://github.com/kubernetes/kube-openapi/tree/master/cmd/openapi-gen) directly for OpenAPI code generation. The `generate openapi` subcommand will be removed in a future release.
 - **Breaking change:** An existing CSV's `spec.customresourcedefinitions.owned` is now always overwritten except for each name, version, and kind on invoking olm-catalog gen-csv when Go API code annotations are present.
-- **Potentially Breaking change:** Be aware that there are potentially other breaking changes due to the controller-runtime and Kubernetes version be upgraded from `v0.4.0` to `v1.16.2, respectively. There may be breaking changes to Go client code due to both of those changes.
+- **Potentially Breaking change:** Be aware that there are potentially other breaking changes due to the controller-runtime and Kubernetes version be upgraded from `v0.4.0` to `v1.16.2`, respectively. There may be breaking changes to Go client code due to both of those changes.
 
 For further detailed information see [CHANGELOG](https://github.com/operator-framework/operator-sdk/blob/master/CHANGELOG.md#v0130)
 
@@ -1341,7 +1340,7 @@ install:
   - pip3 install docker molecule ansible-lint yamllint flake8 openshift jmespath
 ```
 
-**NOTE** To know more about how to upgrade your project to use the V3 Molecule version see [here](https://github.com/ansible-community/molecule/issues/2560).  
+**NOTE** To know more about how to upgrade your project to use the V3 Molecule version see [here](https://github.com/ansible-community/molecule/issues/2560).
 
 **Deprecations**
 


Test patch:
diff --git a/hack/generate/samples/generate_testdata.go b/hack/generate/samples/generate_testdata.go
index 69125cdc..b5fd758c 100644
--- a/hack/generate/samples/generate_testdata.go
+++ b/hack/generate/samples/generate_testdata.go
@@ -28,31 +28,37 @@ import (
 )
 
 func main() {
-	// testdata is the path where all samples should be generate
-	const testdata = "/testdata/"
-
-	// binaryName allow inform the binary that should be used.
+	// binaryPath allow inform the binary that should be used.
 	// By default it is operator-sdk
-	var binaryName string
+	var binaryPath string
 
-	flag.StringVar(&binaryName, "bin", testutils.BinaryName, "Binary path that should be used")
+	flag.StringVar(&binaryPath, "bin", testutils.BinaryName, "Binary path that should be used")
 	flag.Parse()
 
-	currentPath, err := os.Getwd()
+	// Make the binary path absolute if pathed, for reproducibility and debugging purposes.
+	if dir, _ := filepath.Split(binaryPath); dir != "" {
+		tmp, err := filepath.Abs(binaryPath)
+		if err != nil {
+			log.Fatalf("Failed to make binary path %q absolute: %v", binaryPath, err)
+		}
+		binaryPath = tmp
+	}
+
+	wd, err := os.Getwd()
 	if err != nil {
-		log.Error(err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
-	samplesPath := filepath.Join(currentPath, testdata)
-	log.Infof("using the path: (%v)", samplesPath)
+	// samplesPath is the path where all samples should be generated
+	samplesPath := filepath.Join(wd, "testdata")
+	log.Infof("writing sample directories under %s", samplesPath)
 
 	log.Infof("creating Helm Memcached Sample")
-	helm.GenerateMemcachedHelmSample(samplesPath)
+	helm.GenerateMemcachedSamples(binaryPath, samplesPath)
 
 	log.Infof("creating Ansible Memcached Sample")
-	ansible.GenerateMemcachedAnsibleSample(samplesPath)
+	ansible.GenerateMemcachedSamples(binaryPath, samplesPath)
 
 	log.Infof("creating Go Memcached Sample with Webhooks")
-	golang.GenerateMemcachedGoWithWebhooksSample(samplesPath)
+	golang.GenerateMemcachedSamples(binaryPath, samplesPath)
 }
diff --git a/images/custom-scorecard-tests/Dockerfile b/images/custom-scorecard-tests/Dockerfile
index 07f1a411..deb1ff98 100644
--- a/images/custom-scorecard-tests/Dockerfile
+++ b/images/custom-scorecard-tests/Dockerfile
@@ -17,7 +17,7 @@ COPY . .
 RUN GOOS=linux GOARCH=$TARGETARCH make build/custom-scorecard-tests
 
 # Final image.
-FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-230
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-298
 
 ENV HOME=/opt/custom-scorecard-tests \
     USER_NAME=custom-scorecard-tests \
diff --git a/images/scorecard-test/Dockerfile b/images/scorecard-test/Dockerfile
index 996970c5..ec014a2b 100644
--- a/images/scorecard-test/Dockerfile
+++ b/images/scorecard-test/Dockerfile
@@ -17,7 +17,7 @@ COPY . .
 RUN GOOS=linux GOARCH=$TARGETARCH make build/scorecard-test
 
 # Final image.
-FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-230
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-298
 
 ENV HOME=/opt/scorecard-test \
     USER_NAME=scorecard-test \
diff --git a/internal/ansible/flags/flag_test.go b/internal/ansible/flags/flag_test.go
new file mode 100644
index 00000000..095918ea
--- /dev/null
+++ b/internal/ansible/flags/flag_test.go
@@ -0,0 +1,72 @@
+// Copyright 2021 The Operator-SDK Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package flags_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/spf13/pflag"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+
+	"github.com/operator-framework/operator-sdk/internal/ansible/flags"
+)
+
+var _ = Describe("Flags", func() {
+	Describe("ToManagerOptions", func() {
+		var (
+			f       *flags.Flags
+			flagSet *pflag.FlagSet
+			options manager.Options
+		)
+		BeforeEach(func() {
+			f = &flags.Flags{}
+			flagSet = pflag.NewFlagSet("test", pflag.ExitOnError)
+			f.AddTo(flagSet)
+		})
+
+		When("the flag is set", func() {
+			It("uses the flag value when corresponding option value is empty", func() {
+				expOptionValue := ":5678"
+				options.MetricsBindAddress = ""
+				parseArgs(flagSet, "--metrics-bind-address", expOptionValue)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+			It("uses the flag value when corresponding option value is not empty", func() {
+				expOptionValue := ":5678"
+				options.MetricsBindAddress = ":1234"
+				parseArgs(flagSet, "--metrics-bind-address", expOptionValue)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+		})
+		When("the flag is not set", func() {
+			It("uses the default flag value when corresponding option value is empty", func() {
+				expOptionValue := ":8080"
+				options.MetricsBindAddress = ""
+				parseArgs(flagSet)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+			It("uses the option value when corresponding option value is not empty", func() {
+				expOptionValue := ":1234"
+				options.MetricsBindAddress = expOptionValue
+				parseArgs(flagSet)
+				Expect(f.ToManagerOptions(options).MetricsBindAddress).To(Equal(expOptionValue))
+			})
+		})
+	})
+})
+
+func parseArgs(fs *pflag.FlagSet, extraArgs ...string) {
+	Expect(fs.Parse(extraArgs)).To(Succeed())
+}
diff --git a/internal/ansible/flags/suite_test.go b/internal/ansible/flags/suite_test.go
new file mode 100644
index 00000000..09916d7f
--- /dev/null
+++ b/internal/ansible/flags/suite_test.go
@@ -0,0 +1,27 @@
+// Copyright 2021 The Operator-SDK Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package flags_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+func TestFlags(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Flags Suite")
+}
diff --git a/internal/helm/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/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/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