Repository: operator-framework/operator-sdk
Pull request #4402 (2021-01-18 16:37:22)

Align SDK with Kubebuilder - Project version stable
**Description of the change:**
- Update kb dependency from `v2.3.2-0.20201214213149-0a807f4e9428` to `v3.0.0-alpha.0.0.20210203175028-3c8e370d49c5`. See: https://github.com/kubernetes-sigs/kubebuilder/compare/0a807f4e...3c8e370d49c5 and fix modules to point out v3.
- Upgrade gcr.io/kubebuilder/kube-rbac-proxy image to 0.5.0 to 0.8.0. (fix security concern)
- Get Project version stable 
 
**Motivation for the change:**

- Partial: https://github.com/operator-framework/operator-sdk/issues/3925
- Closes: https://github.com/operator-framework/operator-sdk/issues/4453
- Solve tech debts and allow upstream and downstream still be compatible and maintainable.
- Allow SDK still getting bug fixes and improvements done in upstream
- Solve security concerns
- Avoid breaking changes in the future that impacts users and maintainers. 

**Checklist**

If the pull request includes user-facing changes, extra documentation is required:
- [x] Add a new changelog fragment in `changelog/fragments` (see [`changelog/fragments/00-template.yaml`](https://github.com/operator-framework/operator-sdk/tree/master/changelog/fragments/00-template.yaml))
- [ ] Add or update relevant sections of the docs website in [`website/content/en/docs`](https://github.com/operator-framework/operator-sdk/tree/master/website/content/en/docs)


camilamacedo86: Hi @estroz, 

> Personally I think the kubebuilder dep should not be upgraded until v3.0.0 is released and project version 3-alpha is stable, in case other changes are made to the project file spec. That way the migration is guaranteed to be a one time thing. 

I am afraid that this boat shipped already :-) with the 1.3.0 release. See that we have a lot of changes that we need to address and how long more we wait for that harder it will be.

However, if we have the requirement for we ship 1.4.0 then, I think we need to add that in the milestone and block the 1.4.0 until there. I hope that we are close to make the project version stable too. However, to address the changes required for that will be another big PR. WDYT?
estroz: > I am afraid that this boat sailed already with the 1.3.0 release

The 3-alpha spec was compatible between operator-sdk 1.2 and 1.3, so I don’t know what you mean by this.

operator-sdk doesn’t need any changes made in kubebuilder right now so there is no urgency to upgrade. Once again this is my opinion, I will let others chime in.
camilamacedo86: Hi @estroz,

> The 3-alpha spec was compatible between operator-sdk 1.2 and 1.3, so I don’t know what you mean by this.
the project file over go/v3 SDK and upstream are not the same and the further content of the [link added in its migration guide](https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.3.0/#upgrade-your-go-project-from-gokubebuilderiov2-to-gokubebuilderiov3) will not compatible with upstream [#1916](https://github.com/kubernetes-sigs/kubebuilder/pull/1916)

> operator-sdk doesn’t need any changes made in kubebuilder right now so there is no urgency to upgrade.
- besides the above, see that we would need to get https://github.com/kubernetes-sigs/kubebuilder/pull/1955 as soon as possible as well. 
- and then, because it is very behind upstream it has been very hard we check and discuss the final changes in wip by the community for we make the project-version stable. 

so, my proposal here would we keep both sync and track for the milestone 1.4.0 the project-version stable as a requirement. I hope that clarifies. 

camilamacedo86: HI @estroz, 

See that we have here a lot of changes already (202). And for we align SDK with the next follow-ups to make the project-file stable (https://github.com/kubernetes-sigs/kubebuilder/pull/1911) it will more changes. My idea is we merge this one and then do a PR now only to address the changes of https://github.com/kubernetes-sigs/kubebuilder/pull/1911 because it will change many files and will require a good review.  
openshift-ci-robot: @camilamacedo86: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
openshift-ci-robot: @camilamacedo86: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
camilamacedo86: HI @estroz, 

Could you please check this one? All the discussed points are addressed now.  
camilamacedo86: HI @estroz, 

All points discussed are done and solved. Could you please give a hand here for we are able to move forward?  
camilamacedo86: HI @estroz, 

Your suggestions/requests regarding this pr are all done.  
Can we move forward with it now? 

c/c @jmrodri 
Adirio: > After discussing with the parties in this PR, I'm good with this and having a hook to migrate v3-alpha to v3.

Just to clarify, the migration hook he is talking about is to migrate the project configuration (`PROJECT` file) version from `3-alpha` to `"3"`.
openshift-ci-robot: New changes are detected. LGTM label has been removed.
jmrodri: /lgtm
jmrodri: /retest
openshift-ci-robot: New changes are detected. LGTM label has been removed.
openshift-ci-robot: @camilamacedo86: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

Patch:
diff --git a/changelog/fragments/align-kb.yaml b/changelog/fragments/align-kb.yaml
new file mode 100644
index 00000000..b8459971
--- /dev/null
+++ b/changelog/fragments/align-kb.yaml
@@ -0,0 +1,113 @@
+# entries is a list of entries to include in
+# release notes and/or the migration guide
+entries:
+  - description: >
+      For Go-based operators, migrate your project to use the project version stable.
+    kind: "change"
+    breaking: true
+    migration:
+      header: For Go-based operators, migrate your project to use the project version stable.
+      body: >
+        The PROJECT version config file represents the project configuration. It reach the maturate stability and it will
+        store the data used to do the scaffolds. The motivation for this behaviour is to allow tools and helpers in the future such as to make easier the process to update the projects to use upper versions.
+        More info: TBD. //TODO: add link for docs (see the PR kubernetes-sigs/kubebuilder#1916)
+  - description: >
+      For Ansible/Helm-based operators, usage of project config stable version.
+    kind: "change"
+    breaking: true
+    migration:
+      header: For Ansible/Helm-based operators, update the PROJECT file.
+      body: >
+        Update the PROJECT file by replacing the `version: 3-alpha` with `version: "3"` and then, for each API scaffolded in your project:
+        - Add the `- api.crdVersion: <crdVersion>` and `api.namespaced: true`.
+        - Add the resource `domain` which will be the same value of the project domain unless it is an external type
+
+        Ensure that the PROJECT file results are like:
+
+        ```yml
+        domain: example.com
+        projectName: memcached-operator
+        resources:
+        - api:
+            crdVersion: v1
+            namespaced: true
+          domain: example.com
+          group: cache
+          kind: Memcached
+          version: v1alpha1
+        version: "3"
+        ```
+  - description: >
+      Add support for markers for files with the `yml` extension. More info: [#kubernetes-sigs/kubebuilder#1907](https://github.com/kubernetes-sigs/kubebuilder/pull/1907).
+    kind: "addition"
+    breaking: false
+  - description: >
+      (go/v3) Added the `--force` option to the `create webhook` command. More info: [#kubernetes-sigs/kubebuilder#1903](https://github.com/kubernetes-sigs/kubebuilder/pull/1903).
+    kind: "addition"
+    breaking: false
+  - description: >
+      For Go-based operators, fix `--force` option to recreate the files via `create api` command. More info: [#kubernetes-sigs/kubebuilder#1903](https://github.com/kubernetes-sigs/kubebuilder/pull/1903).
+    kind: "bugfix"
+    breaking: false
+  - description: >
+      For Go-based operators using go/v3 plugin only, upgrade the sigs.k8s.io/kubebuilder-declarative-pattern dependency (used only to gen api with --pattern=addon). More info: [#kubernetes-sigs/kubebuilder#1946](https://github.com/kubernetes-sigs/kubebuilder/pull/1946).
+    kind: "change"
+    breaking: false
+  - description: >
+      For Go-based operators, fix the name of the files to be edit in the comments of `[kind]_types.go` files. More info: [#kubernetes-sigs/kubebuilder#1927](https://github.com/kubernetes-sigs/kubebuilder/pull/1927).
+    kind: "bugfix"
+    breaking: false
+    header: Fix the name of the files in the comments on the `<kind>_types.go` files
+    body: >
+      When we use `$operator-sdk create api` or `$operator-sdk create webhook`, it generates a file like `[kind]_types.go`. In this file, there is a comment that indicates which file to change when adding a field.
+      You can update it with the correct value if you wich by replacing the Kind for lower case, such as replace `Edit Memcached_types.go to remove/update` with `Edit memcached_types.go to remove/update`
+  - description: >
+      Upgrade the `kube-rbac-proxy` image version from 0.5.0 to 0.8.0 to address security concerns. More info [#kubernetes-sigs/kubebuilder#1955](https://github.com/kubernetes-sigs/kubebuilder/pull/1955).
+    kind: "bugfix"
+    breaking: false
+    header: Upgrade the `kube-rbac-proxy` from 0.5.0 to 0.8.0 to solve security concerns
+    body: >
+      In the `config/default/manager_auth_proxy_patch.yaml` file, replace `gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0` with `gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0`.
+  - description: >
+      For Go-based operators, fix the endpoint names. More info [#kubernetes-sigs/kubebuilder#1910](https://github.com/kubernetes-sigs/kubebuilder/pull/1910).
+    kind: "bugfix"
+    breaking: false
+    header: For Go-based operators, fix the endpoint names
+    body: >
+      In the `main.go` file, replace `health` with `healthz` and `ready` with `readyz` then, it would look like:
+      ```go
+      if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
+        setupLog.Error(err, "unable to set up health check")
+        os.Exit(1)
+      }
+      if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
+        setupLog.Error(err, "unable to set up ready check")
+        os.Exit(1)
+      }
+      ```
+  - description: >
+      For Go-based operators, add `ErrorIfCRDPathMissing` config by default to the `suite_tests.go`. More info [#kubernetes-sigs/kubebuilder#1910](https://github.com/kubernetes-sigs/kubebuilder/pull/1910).
+    kind: "addition"
+    breaking: false
+    header: For Go-based operators, add `ErrorIfCRDPathMissing` config option if please you.
+    body: >
+      In the `controllers/<kind>/suite_test.go` file(s), add the option `ErrorIfCRDPathMissing` to raise an issue if the CRDs path be missing which would like:
+      ```go
+      By("bootstrapping test environment")
+      testEnv = &envtest.Environment{
+        CRDDirectoryPaths:     []string{filepath.Join("..", "..", "config", "crd", "bases")},
+        ErrorIfCRDPathMissing: true,
+      }
+      ```
+  - description: >
+      Allows initialize the projects with directories with `.`(dot) prefix names. More info [#kubernetes-sigs/kubebuilder#1944](https://github.com/kubernetes-sigs/kubebuilder/pull/1944).
+    kind: "bugfix"
+    breaking: false
+  - description: >
+      For Go-based operators, add Makefile help
+    kind: "change"
+    breaking: false
+  - description: >
+      For Go-based operators, Makefile scaffold was improved
+    kind: "change"
+    breaking: false
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 0f2f81c9..18630b76 100644
--- a/go.mod
+++ b/go.mod
@@ -34,7 +34,7 @@ require (
 	rsc.io/letsencrypt v0.0.3 // indirect
 	sigs.k8s.io/controller-runtime v0.8.0
 	sigs.k8s.io/controller-tools v0.4.1
-	sigs.k8s.io/kubebuilder/v2 v2.3.2-0.20201214213149-0a807f4e9428
+	sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210211121616-05abe25e7215
 	sigs.k8s.io/yaml v1.2.0
 )
 
diff --git a/go.sum b/go.sum
index 057aefe6..590e7347 100644
--- a/go.sum
+++ b/go.sum
@@ -104,8 +104,10 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/
 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
 github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
@@ -1318,6 +1320,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
 google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1457,13 +1460,12 @@ sigs.k8s.io/controller-runtime v0.8.0/go.mod h1:v9Lbj5oX443uR7GXYY46E0EE2o7k2YxQ
 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/kubebuilder/v2 v2.3.2-0.20201214213149-0a807f4e9428 h1:VUvP/g8pDUdqBHgKPoSX6WA/ZH1Y1+FBOk7khgyTLJw=
-sigs.k8s.io/kubebuilder/v2 v2.3.2-0.20201214213149-0a807f4e9428/go.mod h1:J/D/179LBZhQOhRvmMRNbje/Bk+PjbN0/fzUupmO7+U=
+sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210211121616-05abe25e7215 h1:pwEHSu3/ODNEr33DNrKt2DovGV84LhRBLt4QyrT6FJA=
+sigs.k8s.io/kubebuilder/v3 v3.0.0-alpha.0.0.20210211121616-05abe25e7215/go.mod h1:b1WkCy5t/3VSRBCffSfPV1WbH+f45ls69d4ic37sW6w=
 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/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
 sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
diff --git a/hack/generate/samples/internal/ansible/advanced_molecule.go b/hack/generate/samples/internal/ansible/advanced_molecule.go
index e066339f..aab99747 100644
--- a/hack/generate/samples/internal/ansible/advanced_molecule.go
+++ b/hack/generate/samples/internal/ansible/advanced_molecule.go
@@ -21,7 +21,7 @@ import (
 	"strings"
 
 	log "github.com/sirupsen/logrus"
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	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"
@@ -145,10 +145,10 @@ func (ma *AdvancedMolecule) updateConfig() {
       - patch
       - update
       - watch
-# +kubebuilder:scaffold:rules`
+#+kubebuilder:scaffold:rules`
 	err := testutils.ReplaceInFile(
 		filepath.Join(ma.ctx.Dir, "config", "rbac", "role.yaml"),
-		"# +kubebuilder:scaffold:rules",
+		"#+kubebuilder:scaffold:rules",
 		cmRolesFragment)
 	pkg.CheckError("adding customized roles", err)
 
diff --git a/hack/generate/samples/internal/ansible/constants.go b/hack/generate/samples/internal/ansible/constants.go
index 6bdc7100..bbe525c6 100644
--- a/hack/generate/samples/internal/ansible/constants.go
+++ b/hack/generate/samples/internal/ansible/constants.go
@@ -379,5 +379,5 @@ const rolesForBaseOperator = `
       - patch
       - update
       - watch
-# +kubebuilder:scaffold:rules
+#+kubebuilder:scaffold:rules
 `
diff --git a/hack/generate/samples/internal/ansible/memcached.go b/hack/generate/samples/internal/ansible/memcached.go
index c191d6c1..84d4b9de 100644
--- a/hack/generate/samples/internal/ansible/memcached.go
+++ b/hack/generate/samples/internal/ansible/memcached.go
@@ -20,7 +20,7 @@ import (
 	"strings"
 
 	log "github.com/sirupsen/logrus"
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	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"
diff --git a/hack/generate/samples/internal/ansible/memcached_molecule.go b/hack/generate/samples/internal/ansible/memcached_molecule.go
index bf00dc11..8af3d003 100644
--- a/hack/generate/samples/internal/ansible/memcached_molecule.go
+++ b/hack/generate/samples/internal/ansible/memcached_molecule.go
@@ -20,7 +20,7 @@ import (
 	"path/filepath"
 	"strings"
 
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	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"
@@ -81,7 +81,7 @@ func (ma *MoleculeAnsible) Run() {
 
 	log.Infof("adding RBAC permissions")
 	err = testutils.ReplaceInFile(filepath.Join(ma.ctx.Dir, "config", "rbac", "role.yaml"),
-		"# +kubebuilder:scaffold:rules", rolesForBaseOperator)
+		"#+kubebuilder:scaffold:rules", rolesForBaseOperator)
 	pkg.CheckError("replacing in role.yml", err)
 
 	log.Infof("adding Memcached mock task to the role with black list")
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 f920219c..958b498a 100644
--- a/hack/generate/samples/internal/go/v2/memcached_with_webhooks.go
+++ b/hack/generate/samples/internal/go/v2/memcached_with_webhooks.go
@@ -21,7 +21,7 @@ import (
 	"strings"
 
 	log "github.com/sirupsen/logrus"
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	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"
@@ -59,8 +59,7 @@ func (mh *MemcachedGoWithWebhooks) Prepare() {
 func (mh *MemcachedGoWithWebhooks) Run() {
 	log.Infof("creating the project")
 	err := mh.ctx.Init(
-		// TODO(estroz): change this to 3 when stabilized.
-		"--project-version", "3-alpha",
+		"--project-version", "3",
 		"--plugins", "go/v2",
 		"--repo", "github.com/example/memcached-operator",
 		"--domain",
@@ -231,6 +230,7 @@ func (mh *MemcachedGoWithWebhooks) implementingController() {
 	pkg.CheckError("replacing reconcile", err)
 }
 
+// nolint:gosec
 // implementingAPI will customize the API
 func (mh *MemcachedGoWithWebhooks) implementingAPI() {
 	typeFilePath := filepath.Join(mh.ctx.Dir, "api", mh.ctx.Version, fmt.Sprintf("%s_types.go", strings.ToLower(mh.ctx.Kind)))
@@ -278,9 +278,9 @@ func GenerateMemcachedGoWithWebhooksSample(samplesPath string) {
 }
 
 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
-// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;`
+//+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
+//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;`
 
 const reconcileFragment = `// Fetch the Memcached instance
 	memcached := &cachev1alpha1.Memcached{}
@@ -433,7 +433,7 @@ const watchCustomizedFragment = `return ctrl.NewControllerManagedBy(mgr).
 
 const webhooksFragment = `
 // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
-// +kubebuilder:webhook:verbs=create;update,path=/validate-cache-example-com-v1alpha1-memcached,mutating=false,failurePolicy=fail,groups=cache.example.com,resources=memcacheds,versions=v1alpha1,name=vmemcached.kb.io
+//+kubebuilder:webhook:verbs=create;update,path=/validate-cache-example-com-v1alpha1-memcached,mutating=false,failurePolicy=fail,groups=cache.example.com,resources=memcacheds,versions=v1alpha1,name=vmemcached.kb.io
 
 var _ webhook.Validator = &Memcached{}
 
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 7fa47552..525abae2 100644
--- a/hack/generate/samples/internal/go/v3/memcached_with_webhooks.go
+++ b/hack/generate/samples/internal/go/v3/memcached_with_webhooks.go
@@ -21,7 +21,7 @@ import (
 	"strings"
 
 	log "github.com/sirupsen/logrus"
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	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"
@@ -59,8 +59,7 @@ func (mh *MemcachedGoWithWebhooks) Prepare() {
 func (mh *MemcachedGoWithWebhooks) Run() {
 	log.Infof("creating the project")
 	err := mh.ctx.Init(
-		// TODO(estroz): change this to 3 when stabilized.
-		"--project-version", "3-alpha",
+		"--project-version", "3",
 		"--plugins", "go/v3",
 		"--repo", "github.com/example/memcached-operator",
 		"--domain",
@@ -229,6 +228,7 @@ func (mh *MemcachedGoWithWebhooks) implementingController() {
 	pkg.CheckError("replacing reconcile", err)
 }
 
+// nolint:gosec
 // implementingAPI will customize the API
 func (mh *MemcachedGoWithWebhooks) implementingAPI() {
 	err := kbtestutils.InsertCode(
@@ -273,8 +273,8 @@ func GenerateMemcachedGoWithWebhooksSample(samplesPath string) {
 }
 
 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;`
+//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;`
 
 const reconcileFragment = `// Fetch the Memcached instance
 	memcached := &cachev1alpha1.Memcached{}
@@ -427,7 +427,7 @@ const watchCustomizedFragment = `return ctrl.NewControllerManagedBy(mgr).
 
 const webhooksFragment = `
 // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
-// +kubebuilder:webhook:path=/validate-cache-example-com-v1alpha1-memcached,mutating=false,failurePolicy=fail,sideEffects=None,groups=cache.example.com,resources=memcacheds,verbs=create;update,versions=v1alpha1,name=vmemcached.kb.io,admissionReviewVersions={v1,v1beta1}
+//+kubebuilder:webhook:path=/validate-cache-example-com-v1alpha1-memcached,mutating=false,failurePolicy=fail,sideEffects=None,groups=cache.example.com,resources=memcacheds,verbs=create;update,versions=v1alpha1,name=vmemcached.kb.io,admissionReviewVersions={v1,v1beta1}
 
 var _ webhook.Validator = &Memcached{}
 
diff --git a/hack/generate/samples/internal/helm/memcached.go b/hack/generate/samples/internal/helm/memcached.go
index c677c166..83f81e1b 100644
--- a/hack/generate/samples/internal/helm/memcached.go
+++ b/hack/generate/samples/internal/helm/memcached.go
@@ -95,7 +95,7 @@ func (mh *MemcachedHelm) Run() {
 
 	log.Infof("adding customized roles")
 	err = testutils.ReplaceInFile(filepath.Join(mh.ctx.Dir, "config", "rbac", "role.yaml"),
-		"# +kubebuilder:scaffold:rules", policyRolesFragment)
+		"#+kubebuilder:scaffold:rules", policyRolesFragment)
 	pkg.CheckError("adding customized roles", err)
 
 	log.Infof("creating the bundle")
@@ -151,5 +151,5 @@ const policyRolesFragment = `
   - update
   - watch
 
-# +kubebuilder:scaffold:rules
+#+kubebuilder:scaffold:rules
 `
diff --git a/internal/cmd/operator-sdk/cli/cli.go b/internal/cmd/operator-sdk/cli/cli.go
index 4d587611..cab6264f 100644
--- a/internal/cmd/operator-sdk/cli/cli.go
+++ b/internal/cmd/operator-sdk/cli/cli.go
@@ -15,6 +15,13 @@
 package cli
 
 import (
+	log "github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
+	"sigs.k8s.io/kubebuilder/v3/pkg/cli"
+	cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
+	cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
+
 	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/bundle"
 	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/cleanup"
 	"github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/completion"
@@ -28,12 +35,6 @@ import (
 	golangv3 "github.com/operator-framework/operator-sdk/internal/plugins/golang/v3"
 	helmv1 "github.com/operator-framework/operator-sdk/internal/plugins/helm/v1"
 	"github.com/operator-framework/operator-sdk/internal/util/projutil"
-
-	log "github.com/sirupsen/logrus"
-	"github.com/spf13/cobra"
-	"github.com/spf13/viper"
-	"sigs.k8s.io/kubebuilder/v2/pkg/cli"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
 )
 
 var commands = []*cobra.Command{
@@ -58,15 +59,15 @@ func GetPluginsCLIAndRoot() (cli.CLI, *cobra.Command) {
 	c, err := cli.New(
 		cli.WithCommandName("operator-sdk"),
 		cli.WithVersion(makeVersionString()),
-		cli.WithDefaultProjectVersion(config.Version3Alpha),
+		cli.WithDefaultProjectVersion(cfgv3.Version),
 		cli.WithPlugins(
 			&golangv2.Plugin{},
 			&golangv3.Plugin{},
 			&helmv1.Plugin{},
 			&ansiblev1.Plugin{},
 		),
-		cli.WithDefaultPlugins(config.Version2, &golangv2.Plugin{}),
-		cli.WithDefaultPlugins(config.Version3Alpha, &golangv3.Plugin{}),
+		cli.WithDefaultPlugins(cfgv2.Version, &golangv2.Plugin{}),
+		cli.WithDefaultPlugins(cfgv3.Version, &golangv3.Plugin{}),
 		cli.WithExtraCommands(commands...),
 	)
 	if err != nil {
diff --git a/internal/cmd/operator-sdk/generate/internal/genutil.go b/internal/cmd/operator-sdk/generate/internal/genutil.go
index d36e1185..03cbe285 100644
--- a/internal/cmd/operator-sdk/generate/internal/genutil.go
+++ b/internal/cmd/operator-sdk/generate/internal/genutil.go
@@ -28,6 +28,7 @@ import (
 	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+	cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
 	"sigs.k8s.io/yaml"
 )
 
@@ -171,17 +172,17 @@ func GetPackageNameAndLayout(defaultPackageName string) (packageName string, lay
 		}
 		if packageName == "" {
 			switch {
-			case cfg.IsV2():
+			case cfg.GetVersion().Compare(cfgv2.Version) == 0:
 				wd, err := os.Getwd()
 				if err != nil {
 					return "", "", err
 				}
 				packageName = strings.ToLower(filepath.Base(wd))
 			default:
-				if cfg.ProjectName == "" {
+				packageName = cfg.GetProjectName()
+				if packageName == "" {
 					return "", "", errors.New("--package-name must be set if \"projectName\" is not set in the PROJECT config file")
 				}
-				packageName = cfg.ProjectName
 			}
 		}
 		layout = projutil.GetProjectLayout(cfg)
diff --git a/internal/cmd/operator-sdk/generate/kustomize/manifests.go b/internal/cmd/operator-sdk/generate/kustomize/manifests.go
index cc510e48..6a95f143 100644
--- a/internal/cmd/operator-sdk/generate/kustomize/manifests.go
+++ b/internal/cmd/operator-sdk/generate/kustomize/manifests.go
@@ -26,7 +26,8 @@ import (
 	"github.com/spf13/pflag"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/util/validation"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
 	"sigs.k8s.io/yaml"
 
 	genutil "github.com/operator-framework/operator-sdk/internal/cmd/operator-sdk/generate/internal"
@@ -138,9 +139,9 @@ func (c *manifestsCmd) addFlagsTo(fs *pflag.FlagSet) {
 var defaultDir = filepath.Join("config", "manifests")
 
 // setDefaults sets command defaults.
-func (c *manifestsCmd) setDefaults(cfg *config.Config) error {
+func (c *manifestsCmd) setDefaults(cfg config.Config) error {
 	if c.packageName == "" {
-		c.packageName = cfg.ProjectName
+		c.packageName = cfg.GetProjectName()
 	}
 
 	if c.inputDir == "" {
@@ -151,7 +152,7 @@ func (c *manifestsCmd) setDefaults(cfg *config.Config) error {
 	}
 
 	if c.apisDir == "" {
-		if cfg.MultiGroup {
+		if cfg.IsMultiGroup() {
 			c.apisDir = "apis"
 		} else {
 			c.apisDir = "api"
@@ -169,7 +170,7 @@ const manifestsKustomization = `resources:
 `
 
 // run generates kustomize bundle bases and a kustomization.yaml if one does not exist.
-func (c manifestsCmd) run(cfg *config.Config) error {
+func (c manifestsCmd) run(cfg config.Config) error {
 
 	if !c.quiet {
 		fmt.Println("Generating kustomize files in", c.outputDir)
@@ -189,12 +190,16 @@ func (c manifestsCmd) run(cfg *config.Config) error {
 
 	relBasePath := filepath.Join("bases", c.packageName+".clusterserviceversion.yaml")
 	basePath := filepath.Join(c.inputDir, relBasePath)
+	gvks, err := getGVKs(cfg)
+	if err != nil {
+		return err
+	}
 	base := bases.ClusterServiceVersion{
 		OperatorName: c.packageName,
-		OperatorType: projutil.PluginKeyToOperatorType(cfg.Layout),
+		OperatorType: projutil.PluginKeyToOperatorType(cfg.GetLayout()),
 		APIsDir:      c.apisDir,
 		Interactive:  requiresInteraction(basePath, c.interactiveLevel),
-		GVKs:         getGVKs(cfg),
+		GVKs:         gvks,
 	}
 	// Set BasePath only if it exists. If it doesn't, a new base will be generated
 	// if BasePath is empty.
@@ -236,12 +241,21 @@ func requiresInteraction(basePath string, ilvl projutil.InteractiveLevel) bool {
 	return (ilvl == projutil.InteractiveSoftOff && genutil.IsNotExist(basePath)) || ilvl == projutil.InteractiveOnAll
 }
 
-func getGVKs(cfg *config.Config) []schema.GroupVersionKind {
-	gvks := make([]schema.GroupVersionKind, len(cfg.Resources))
-	for i, gvk := range cfg.Resources {
-		gvks[i].Group = fmt.Sprintf("%s.%s", gvk.Group, cfg.Domain)
+func getGVKs(cfg config.Config) ([]schema.GroupVersionKind, error) {
+	resources, err := cfg.GetResources()
+	if err != nil {
+		return nil, err
+	}
+	gvks := make([]schema.GroupVersionKind, len(resources))
+	for i, gvk := range resources {
+		// check if the resource has an specific domain
+		// otherwise use the config.Domain.
+		if cfg.GetVersion().Compare(cfgv2.Version) == 0 {
+			gvk.Domain = cfg.GetDomain()
+		}
+		gvks[i].Group = gvk.QualifiedGroup()
 		gvks[i].Version = gvk.Version
 		gvks[i].Kind = gvk.Kind
 	}
-	return gvks
+	return gvks, nil
 }
diff --git a/internal/generate/clusterserviceversion/bases/definitions/markers.go b/internal/generate/clusterserviceversion/bases/definitions/markers.go
index 7bdb24a5..27842d9f 100644
--- a/internal/generate/clusterserviceversion/bases/definitions/markers.go
+++ b/internal/generate/clusterserviceversion/bases/definitions/markers.go
@@ -38,9 +38,9 @@ const (
 )
 
 var (
-	// +operator-sdk:csv:customresourcedefinitions:displayName="string",resources={ {kind,version,name} , ... }
+	//+operator-sdk:csv:customresourcedefinitions:displayName="string",resources={ {kind,version,name} , ... }
 	typeDefinition = markers.Must(markers.MakeDefinition(crdMarkerName, markers.DescribesType, Description{}))
-	// +operator-sdk:csv:customresourcedefinitions:type=<spec|status>,displayName="name",xDescriptors="ui:elements:foo:bar"
+	//+operator-sdk:csv:customresourcedefinitions:type=<spec|status>,displayName="name",xDescriptors="ui:elements:foo:bar"
 	fieldDefinition = markers.Must(markers.MakeDefinition(crdMarkerName, markers.DescribesField, Descriptor{}))
 )
 
diff --git a/internal/kubebuilder/machinery/errors.go b/internal/kubebuilder/machinery/errors.go
index 31dcc101..faba57a1 100644
--- a/internal/kubebuilder/machinery/errors.go
+++ b/internal/kubebuilder/machinery/errors.go
@@ -20,7 +20,7 @@ import (
 	"errors"
 	"fmt"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 // This file contains the errors returned by the scaffolding machinery
diff --git a/internal/kubebuilder/machinery/scaffold.go b/internal/kubebuilder/machinery/scaffold.go
index 9d653784..bcd55af1 100644
--- a/internal/kubebuilder/machinery/scaffold.go
+++ b/internal/kubebuilder/machinery/scaffold.go
@@ -26,8 +26,8 @@ import (
 	"text/template"
 
 	"golang.org/x/tools/imports"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/filesystem"
 )
@@ -69,7 +69,7 @@ func (s *scaffold) Execute(universe *model.Universe, files ...file.Builder) erro
 
 	// Set the repo as the local prefix so that it knows how to group imports
 	if universe.Config != nil {
-		imports.LocalPrefix = universe.Config.Repo
+		imports.LocalPrefix = universe.Config.GetRepository()
 	}
 
 	for _, f := range files {
diff --git a/internal/plugins/ansible/v1/api.go b/internal/plugins/ansible/v1/api.go
index 059afc4e..fed6d81c 100644
--- a/internal/plugins/ansible/v1/api.go
+++ b/internal/plugins/ansible/v1/api.go
@@ -19,9 +19,9 @@ import (
 	"strings"
 
 	"github.com/spf13/pflag"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/resource"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/cmdutil"
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds"
@@ -40,7 +40,7 @@ const (
 )
 
 type createAPIPSubcommand struct {
-	config        *config.Config
+	config        config.Config
 	createOptions scaffolds.CreateOptions
 }
 
@@ -91,7 +91,6 @@ func (p *createAPIPSubcommand) UpdateContext(ctx *plugin.Context) {
 
 func (p *createAPIPSubcommand) BindFlags(fs *pflag.FlagSet) {
 	fs.SortFlags = false
-
 	fs.StringVar(&p.createOptions.GVK.Group, groupFlag, "", "resource group")
 	fs.StringVar(&p.createOptions.GVK.Version, versionFlag, "", "resource version")
 	fs.StringVar(&p.createOptions.GVK.Kind, kindFlag, "", "resource kind")
@@ -100,7 +99,7 @@ func (p *createAPIPSubcommand) BindFlags(fs *pflag.FlagSet) {
 	fs.BoolVarP(&p.createOptions.GenerateRole, "generate-role", "", false, "Generate an Ansible role skeleton.")
 }
 
-func (p *createAPIPSubcommand) InjectConfig(c *config.Config) {
+func (p *createAPIPSubcommand) InjectConfig(c config.Config) {
 	p.config = c
 }
 
@@ -120,7 +119,7 @@ func (p *createAPIPSubcommand) Run() error {
 // SDK phase 2 plugins.
 func (p *createAPIPSubcommand) runPhase2() error {
 	ogvk := p.createOptions.GVK
-	gvk := config.GVK{Group: ogvk.Group, Version: ogvk.Version, Kind: ogvk.Kind}
+	gvk := resource.GVK{Group: ogvk.Group, Version: ogvk.Version, Kind: ogvk.Kind}
 
 	// Initially the ansible/v1 plugin was written to not create a "plugins" config entry
 	// for any phase 2 plugin because they did not have their own keys. Now there are phase 2
@@ -155,13 +154,9 @@ func (p *createAPIPSubcommand) Validate() error {
 	}
 
 	// Validate the resource.
-	r := resource.Options{
-		Namespaced: true,
-		Group:      p.createOptions.GVK.Group,
-		Version:    p.createOptions.GVK.Version,
-		Kind:       p.createOptions.GVK.Kind,
-	}
-	if err := r.Validate(); err != nil {
+	ogvk := p.createOptions.GVK
+	gvk := resource.GVK{Group: ogvk.Group, Version: ogvk.Version, Kind: ogvk.Kind}
+	if err := gvk.Validate(); err != nil {
 		return err
 	}
 
diff --git a/internal/plugins/ansible/v1/init.go b/internal/plugins/ansible/v1/init.go
index 89b7d50b..158b5913 100644
--- a/internal/plugins/ansible/v1/init.go
+++ b/internal/plugins/ansible/v1/init.go
@@ -22,8 +22,9 @@ import (
 
 	"github.com/spf13/pflag"
 	"k8s.io/apimachinery/pkg/util/validation"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/cmdutil"
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/scaffolds"
@@ -32,14 +33,15 @@ import (
 )
 
 type initSubcommand struct {
-	config    *config.Config
+	config    config.Config
 	apiPlugin createAPIPSubcommand
-
-	// If true, run the `create api` plugin.
-	doCreateAPI bool
+	options   *golang.Options
 
 	// For help text.
 	commandName string
+
+	// Flags
+	projectName string
 }
 
 var (
@@ -92,13 +94,14 @@ Optionally creates a new API, using the same flags as "create api"
 
 func (p *initSubcommand) BindFlags(fs *pflag.FlagSet) {
 	fs.SortFlags = false
-	fs.StringVar(&p.config.Domain, "domain", "my.domain", "domain for groups")
-	fs.StringVar(&p.config.ProjectName, "project-name", "", "name of this project, the default being directory name")
+	p.options = &golang.Options{}
+	fs.StringVar(&p.options.Domain, "domain", "my.domain", "domain for groups")
+	fs.StringVar(&p.projectName, "project-name", "", "name of this project, the default being directory name")
 	p.apiPlugin.BindFlags(fs)
 }
 
-func (p *initSubcommand) InjectConfig(c *config.Config) {
-	c.Layout = pluginKey
+func (p *initSubcommand) InjectConfig(c config.Config) {
+	_ = c.SetLayout(pluginKey)
 	p.config = c
 	p.apiPlugin.config = p.config
 }
@@ -125,7 +128,7 @@ func (p *initSubcommand) runPhase2() error {
 		return err
 	}
 
-	if p.doCreateAPI {
+	if p.options.DoAPI {
 		if err := p.apiPlugin.runPhase2(); err != nil {
 			return err
 		}
@@ -135,21 +138,32 @@ func (p *initSubcommand) runPhase2() error {
 }
 
 func (p *initSubcommand) Validate() error {
+	// Set values in the config
+	if err := p.config.SetProjectName(p.projectName); err != nil {
+		return err
+	}
+	if err := p.config.SetDomain(p.options.Domain); err != nil {
+		return err
+	}
+
 	// Check if the project name is a valid k8s namespace (DNS 1123 label).
-	if p.config.ProjectName == "" {
+	if p.config.GetProjectName() == "" {
 		dir, err := os.Getwd()
 		if err != nil {
 			return fmt.Errorf("error getting current directory: %v", err)
 		}
-		p.config.ProjectName = strings.ToLower(filepath.Base(dir))
+
+		if err := p.config.SetProjectName(strings.ToLower(filepath.Base(dir))); err != nil {
+			return err
+		}
 	}
-	if err := validation.IsDNS1123Label(p.config.ProjectName); err != nil {
-		return fmt.Errorf("project name (%s) is invalid: %v", p.config.ProjectName, err)
+	if err := validation.IsDNS1123Label(p.config.GetProjectName()); err != nil {
+		return fmt.Errorf("project name (%s) is invalid: %v", p.config.GetProjectName(), err)
 	}
 
 	defaultOpts := scaffolds.CreateOptions{CRDVersion: "v1"}
 	if !p.apiPlugin.createOptions.GVK.Empty() || p.apiPlugin.createOptions != defaultOpts {
-		p.doCreateAPI = true
+		p.options.DoAPI = true
 		return p.apiPlugin.Validate()
 	}
 	return nil
@@ -160,7 +174,7 @@ func (p *initSubcommand) GetScaffolder() (cmdutil.Scaffolder, error) {
 		apiScaffolder cmdutil.Scaffolder
 		err           error
 	)
-	if p.doCreateAPI {
+	if p.options.DoAPI {
 		apiScaffolder, err = p.apiPlugin.GetScaffolder()
 		if err != nil {
 			return nil, err
@@ -170,7 +184,7 @@ func (p *initSubcommand) GetScaffolder() (cmdutil.Scaffolder, error) {
 }
 
 func (p *initSubcommand) PostScaffold() error {
-	if !p.doCreateAPI {
+	if !p.options.DoAPI {
 		fmt.Printf("Next: define a resource with:\n$ %s create api\n", p.commandName)
 	}
 
diff --git a/internal/plugins/ansible/v1/plugin.go b/internal/plugins/ansible/v1/plugin.go
index fd7f8f67..5b25a85e 100644
--- a/internal/plugins/ansible/v1/plugin.go
+++ b/internal/plugins/ansible/v1/plugin.go
@@ -15,16 +15,17 @@
 package ansible
 
 import (
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins"
+	cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
 )
 
 const pluginName = "ansible" + plugins.DefaultNameQualifier
 
 var (
-	supportedProjectVersions = []string{config.Version3Alpha}
+	supportedProjectVersions = []config.Version{cfgv3.Version}
 	pluginVersion            = plugin.Version{Number: 1}
 	pluginKey                = plugin.KeyFor(Plugin{})
 )
@@ -42,6 +43,6 @@ type Plugin struct {
 
 func (Plugin) Name() string                                         { return pluginName }
 func (Plugin) Version() plugin.Version                              { return pluginVersion }
-func (Plugin) SupportedProjectVersions() []string                   { return supportedProjectVersions }
+func (Plugin) SupportedProjectVersions() []config.Version           { return supportedProjectVersions }
 func (p Plugin) GetInitSubcommand() plugin.InitSubcommand           { return &p.initSubcommand }
 func (p Plugin) GetCreateAPISubcommand() plugin.CreateAPISubcommand { return &p.createAPIPSubcommand }
diff --git a/internal/plugins/ansible/v1/scaffolds/api.go b/internal/plugins/ansible/v1/scaffolds/api.go
index 57a727a9..f86ef278 100644
--- a/internal/plugins/ansible/v1/scaffolds/api.go
+++ b/internal/plugins/ansible/v1/scaffolds/api.go
@@ -21,10 +21,11 @@ import (
 	"errors"
 
 	"k8s.io/apimachinery/pkg/runtime/schema"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/cmdutil"
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/machinery"
@@ -49,12 +50,12 @@ type CreateOptions struct {
 }
 
 type apiScaffolder struct {
-	config *config.Config
+	config config.Config
 	opts   CreateOptions
 }
 
 // NewCreateAPIScaffolder returns a new Scaffolder for project initialization operations
-func NewCreateAPIScaffolder(config *config.Config, opts CreateOptions) cmdutil.Scaffolder {
+func NewCreateAPIScaffolder(config config.Config, opts CreateOptions) cmdutil.Scaffolder {
 	return &apiScaffolder{
 		config: config,
 		opts:   opts,
@@ -74,24 +75,44 @@ func (s *apiScaffolder) Scaffold() error {
 }
 
 func (s *apiScaffolder) scaffold() error {
-
-	resourceOptions := resource.Options{
-		Group:   s.opts.GVK.Group,
-		Version: s.opts.GVK.Version,
-		Kind:    s.opts.GVK.Kind,
+	resourceOptions := &golang.Options{}
+	resourceOptions.DoAPI = true
+	resourceOptions.Group = s.opts.GVK.Group
+	resourceOptions.Version = s.opts.GVK.Version
+	resourceOptions.Kind = s.opts.GVK.Kind
+
+	//todo(camilamacedo86): replace the options by kubernetes-sigs/kubebuilder#1974
+	if err := resourceOptions.Validate(); err != nil {
+		return err
 	}
 
+	// Check that resource doesn't exist
 	if s.config.HasResource(resourceOptions.GVK()) {
 		return errors.New("the API resource already exists")
 	}
 
 	// Check that the provided group can be added to the project
-	if !s.config.MultiGroup && len(s.config.Resources) != 0 && !s.config.HasGroup(resourceOptions.Group) {
+	if !s.config.IsMultiGroup() && s.config.ResourcesLength() != 0 && !s.config.HasGroup(resourceOptions.Group) {
 		return errors.New("multiple groups are not allowed by default, to enable multi-group set 'multigroup: true' in your PROJECT file")
 	}
 
-	resource := resourceOptions.NewResource(s.config, true)
-	s.config.UpdateResources(resource.GVK())
+	resource := resourceOptions.NewResource(s.config)
+
+	resource.Domain = s.config.GetDomain()
+
+	// remove the path since is not a Golang project
+	resource.Path = ""
+
+	// add the resource API info to complain with project-version=3
+	// todo: ensure that this information is properly returned from
+	// resource.newResource in upstream ( see kubernetes-sigs/kubebuilder#1974)
+	// and then, remove it.
+	resource.API.Namespaced = true
+	resource.API.CRDVersion = s.opts.CRDVersion
+
+	if err := s.config.UpdateResource(resource); err != nil {
+		return err
+	}
 
 	var createAPITemplates []file.Builder
 	createAPITemplates = append(createAPITemplates,
@@ -123,7 +144,7 @@ func (s *apiScaffolder) scaffold() error {
 			&playbooks.Playbook{GenerateRole: s.opts.GenerateRole})
 	}
 	return machinery.NewScaffold().Execute(
-		s.newUniverse(resource),
+		s.newUniverse(&resource),
 		createAPITemplates...,
 	)
 }
diff --git a/internal/plugins/ansible/v1/scaffolds/init.go b/internal/plugins/ansible/v1/scaffolds/init.go
index 09e1c006..e3c0c4d3 100644
--- a/internal/plugins/ansible/v1/scaffolds/init.go
+++ b/internal/plugins/ansible/v1/scaffolds/init.go
@@ -18,8 +18,8 @@ limitations under the License.
 package scaffolds
 
 import (
-	"sigs.k8s.io/kubebuilder/v2/pkg/model"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/cmdutil"
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/machinery"
@@ -50,12 +50,12 @@ var ansibleOperatorVersion = version.ImageVersion
 var _ cmdutil.Scaffolder = &initScaffolder{}
 
 type initScaffolder struct {
-	config        *config.Config
+	config        config.Config
 	apiScaffolder cmdutil.Scaffolder
 }
 
 // NewInitScaffolder returns a new Scaffolder for project initialization operations
-func NewInitScaffolder(config *config.Config, apiScaffolder cmdutil.Scaffolder) cmdutil.Scaffolder {
+func NewInitScaffolder(config config.Config, apiScaffolder cmdutil.Scaffolder) cmdutil.Scaffolder {
 	return &initScaffolder{
 		config:        config,
 		apiScaffolder: apiScaffolder,
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/crd/crd.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/crd/crd.go
index a8921873..842a1ae0 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/crd/crd.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/crd/crd.go
@@ -20,7 +20,7 @@ import (
 	"path/filepath"
 
 	"github.com/kr/text"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &CRD{}
@@ -36,7 +36,7 @@ type CRD struct {
 // SetTemplateDefaults implements input.Template
 func (f *CRD) SetTemplateDefaults() error {
 	if f.Path == "" {
-		f.Path = filepath.Join("config", "crd", "bases", fmt.Sprintf("%s_%%[plural].yaml", f.Resource.Domain))
+		f.Path = filepath.Join("config", "crd", "bases", fmt.Sprintf("%s_%%[plural].yaml", f.Resource.QualifiedGroup()))
 	}
 	f.Path = f.Resource.Replacer().Replace(f.Path)
 
@@ -58,9 +58,9 @@ const crdTemplate = `---
 apiVersion: apiextensions.k8s.io/{{ .CRDVersion }}
 kind: CustomResourceDefinition
 metadata:
-  name: {{ .Resource.Plural }}.{{ .Resource.Domain }}
+  name: {{ .Resource.Plural }}.{{ .Resource.QualifiedGroup }}
 spec:
-  group: {{ .Resource.Domain }}
+  group: {{ .Resource.QualifiedGroup }}
   names:
     kind: {{ .Resource.Kind }}
     listKind: {{ .Resource.Kind }}List
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 a3281e96..7c0cc71d 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
@@ -21,7 +21,7 @@ import (
 	"fmt"
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
@@ -69,7 +69,7 @@ func (f *Kustomization) GetCodeFragments() file.CodeFragmentsMap {
 
 	// Generate resource code fragments
 	res := make([]string, 0)
-	res = append(res, fmt.Sprintf(resourceCodeFragment, f.Resource.Domain, f.Resource.Plural))
+	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 {
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go
index c01e7ed5..5c3a1086 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go
@@ -20,7 +20,7 @@ package kdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &AuthProxyPatch{}
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
index 3fe665b7..d989e14e 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault/kustomization.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/kdefault/kustomization.go
@@ -20,7 +20,7 @@ package kdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomize{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/manager/kustomization.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/manager/kustomization.go
index 1318302e..93dcfafa 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/manager/kustomization.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/manager/kustomization.go
@@ -19,7 +19,7 @@ package manager
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/manager/manager.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/manager/manager.go
index 463ded84..5b5a08cf 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/manager/manager.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/manager/manager.go
@@ -20,7 +20,7 @@ package manager
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Manager{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/prometheus/kustomization.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/prometheus/kustomization.go
index c72539da..a794eea1 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/prometheus/kustomization.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/prometheus/kustomization.go
@@ -19,7 +19,7 @@ package prometheus
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/prometheus/monitor.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/prometheus/monitor.go
index 23e309c3..2055b94b 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/prometheus/monitor.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/prometheus/monitor.go
@@ -19,7 +19,7 @@ package prometheus
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &ServiceMonitor{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_client_clusterrole.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_client_clusterrole.go
index a3cbb04c..6553210c 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_client_clusterrole.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_client_clusterrole.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &ClientClusterRole{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role.go
index 761ea8bc..2fb69db5 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &AuthProxyRole{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role_binding.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role_binding.go
index 8342bf58..5c52caec 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role_binding.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role_binding.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &AuthProxyRoleBinding{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_service.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_service.go
index 03857ba1..25a0a52a 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_service.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/auth_proxy_service.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &AuthProxyService{}
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
index 39f18cb7..44b02767 100644
--- 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
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &CRDEditorRole{}
@@ -49,7 +49,7 @@ metadata:
   name: {{ lower .Resource.Kind }}-editor-role
 rules:
 - apiGroups:
-  - {{ .Resource.Domain }}
+  - {{ .Resource.QualifiedGroup }}
   resources:
   - {{ .Resource.Plural }}
   verbs:
@@ -61,7 +61,7 @@ rules:
   - update
   - watch
 - apiGroups:
-  - {{ .Resource.Domain }}
+  - {{ .Resource.QualifiedGroup }}
   resources:
   - {{ .Resource.Plural }}/status
   verbs:
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
index fb8efa12..3dee9614 100644
--- 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
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &CRDViewerRole{}
@@ -49,7 +49,7 @@ metadata:
   name: {{ lower .Resource.Kind }}-viewer-role
 rules:
 - apiGroups:
-  - {{ .Resource.Domain }}
+  - {{ .Resource.QualifiedGroup }}
   resources:
   - {{ .Resource.Plural }}
   verbs:
@@ -57,7 +57,7 @@ rules:
   - list
   - watch
 - apiGroups:
-  - {{ .Resource.Domain }}
+  - {{ .Resource.QualifiedGroup }}
   resources:
   - {{ .Resource.Plural }}/status
   verbs:
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/kustomization.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/kustomization.go
index 9d5711cd..74c3ef6e 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/kustomization.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/kustomization.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/leader_election_role.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/leader_election_role.go
index b220d947..f029a428 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/leader_election_role.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/leader_election_role.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &LeaderElectionRole{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/leader_election_role_binding.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/leader_election_role_binding.go
index 00124b77..994c3179 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/leader_election_role_binding.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/leader_election_role_binding.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &LeaderElectionRoleBinding{}
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 fa04ccb8..8ef6408b 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
@@ -20,7 +20,7 @@ import (
 	"path/filepath"
 	"text/template"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &ManagerRole{}
@@ -138,10 +138,10 @@ rules:
 `
 
 const rulesFragment = `  ##
-  ## Rules for {{.Resource.Domain}}/{{.Resource.Version}}, Kind: {{.Resource.Kind}}
+  ## Rules for {{.Resource.QualifiedGroup}}/{{.Resource.Version}}, Kind: {{.Resource.Kind}}
   ##
   - apiGroups:
-      - {{.Resource.Domain}}
+      - {{.Resource.QualifiedGroup}}
     resources:
       - {{.Resource.Plural}}
       - {{.Resource.Plural}}/status
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/role_binding.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/role_binding.go
index 0e83dfce..0e236df0 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/role_binding.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/rbac/role_binding.go
@@ -20,7 +20,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &RoleBinding{}
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
index 54fb2958..d3356e54 100644
--- 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
@@ -22,7 +22,7 @@ import (
 	"strings"
 	"text/template"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &CR{}
@@ -67,7 +67,7 @@ func (f *CR) GetFuncMap() template.FuncMap {
 
 const defaultSpecTemplate = `foo: bar`
 
-const crSampleTemplate = `apiVersion: {{ .Resource.Domain }}/{{ .Resource.Version }}
+const crSampleTemplate = `apiVersion: {{ .Resource.QualifiedGroup }}/{{ .Resource.Version }}
 kind: {{ .Resource.Kind }}
 metadata:
   name: {{ lower .Resource.Kind }}-sample
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/dockerfile.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/dockerfile.go
index 974d360a..e430bc29 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/dockerfile.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/dockerfile.go
@@ -17,7 +17,7 @@ package templates
 import (
 	"errors"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/gitignore.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/gitignore.go
index 745db8da..64cc86d9 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/gitignore.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/gitignore.go
@@ -18,7 +18,7 @@ limitations under the License.
 package templates
 
 import (
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &GitIgnore{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/makefile.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/makefile.go
index f5a4c6c2..f304723d 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/makefile.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/makefile.go
@@ -20,7 +20,7 @@ package templates
 import (
 	"errors"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Makefile{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/converge.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/converge.go
index 04f2d42b..efd51727 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/converge.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/converge.go
@@ -17,7 +17,7 @@ package mdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Converge{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/create.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/create.go
index 270347d6..e61a23d5 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/create.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/create.go
@@ -17,7 +17,7 @@ package mdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Create{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/destroy.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/destroy.go
index 476bfde2..69cbf44e 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/destroy.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/destroy.go
@@ -17,7 +17,7 @@ package mdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Destroy{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/kustomize.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/kustomize.go
index 41d27279..8e2f07bd 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/kustomize.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/kustomize.go
@@ -17,7 +17,7 @@ package mdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomize{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/molecule.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/molecule.go
index e98b60cf..5da3e3fd 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/molecule.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/molecule.go
@@ -17,7 +17,7 @@ package mdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Molecule{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/prepare.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/prepare.go
index bd4da756..877fb985 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/prepare.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/prepare.go
@@ -17,7 +17,7 @@ package mdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Prepare{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/verify.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/verify.go
index 2370425c..b7cb2bc0 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/verify.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/verify.go
@@ -17,7 +17,7 @@ package mdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Verify{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/converge.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/converge.go
index f0669170..97ce83b1 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/converge.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/converge.go
@@ -17,7 +17,7 @@ package mkind
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Converge{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/create.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/create.go
index 1221e190..c553ac6a 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/create.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/create.go
@@ -17,7 +17,7 @@ package mkind
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Create{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/destroy.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/destroy.go
index 68f1fb8a..87ecced8 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/destroy.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/destroy.go
@@ -17,7 +17,7 @@ package mkind
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Destroy{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/molecule.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/molecule.go
index 7a2f789d..9ea33cb2 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/molecule.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mkind/molecule.go
@@ -17,7 +17,7 @@ package mkind
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Molecule{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/playbooks/placeholder.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/playbooks/placeholder.go
index 6dec7eef..05ec3d17 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/playbooks/placeholder.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/playbooks/placeholder.go
@@ -15,7 +15,7 @@
 package playbooks
 
 import (
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/playbooks/playbook.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/playbooks/playbook.go
index 8fd47cee..c7df1d9b 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/playbooks/playbook.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/playbooks/playbook.go
@@ -17,7 +17,7 @@ package playbooks
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 type Playbook struct {
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/requirements.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/requirements.go
index b2e1e7f0..5b3762a1 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/requirements.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/requirements.go
@@ -15,7 +15,7 @@
 package templates
 
 import (
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 // RequirementsYml - A requirements file for Ansible collection dependencies
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/defaults_main.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/defaults_main.go
index 457ad0c1..a4bf7819 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/defaults_main.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/defaults_main.go
@@ -17,7 +17,7 @@ package roles
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/files_dir.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/files_dir.go
index b06c3834..119a3216 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/files_dir.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/files_dir.go
@@ -17,7 +17,7 @@ package roles
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/handlers_main.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/handlers_main.go
index 88cf11ec..306948a8 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/handlers_main.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/handlers_main.go
@@ -17,7 +17,7 @@ package roles
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/meta_main.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/meta_main.go
index 2a73731c..00059e3e 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/meta_main.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/meta_main.go
@@ -17,7 +17,7 @@ package roles
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/placeholder.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/placeholder.go
index ffcfd197..dd7f33a0 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/placeholder.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/placeholder.go
@@ -15,7 +15,7 @@
 package roles
 
 import (
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/readme.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/readme.go
index b5cf1d61..73c786d9 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/readme.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/readme.go
@@ -17,7 +17,7 @@ package roles
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/tasks_main.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/tasks_main.go
index 60c596e0..bdb73c28 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/tasks_main.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/tasks_main.go
@@ -17,7 +17,7 @@ package roles
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/templates_dir.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/templates_dir.go
index cdb38fdf..d5c771cc 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/templates_dir.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/templates_dir.go
@@ -17,7 +17,7 @@ package roles
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/vars_main.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/vars_main.go
index 10cf23c3..7db0f372 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/vars_main.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/roles/vars_main.go
@@ -17,7 +17,7 @@ package roles
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/ansible/v1/constants"
 )
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/watches.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/watches.go
index cca37d2f..157931ce 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/watches.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/watches.go
@@ -19,7 +19,7 @@ import (
 	"fmt"
 	"text/template"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Watches{}
@@ -104,7 +104,7 @@ const watchesTemplate = `---
 `
 
 const watchFragment = `- version: {{.Resource.Version}}
-  group: {{.Resource.Domain}}
+  group: {{.Resource.QualifiedGroup}}
   kind: {{.Resource.Kind}}
   {{- if .GeneratePlaybook }}
   playbook: {{ .PlaybooksDir }}/{{ .Resource.Kind | lower }}.yml
diff --git a/internal/plugins/golang/v2/api.go b/internal/plugins/golang/v2/api.go
index 605f03bf..3ed69247 100644
--- a/internal/plugins/golang/v2/api.go
+++ b/internal/plugins/golang/v2/api.go
@@ -16,8 +16,9 @@ package v2
 
 import (
 	"github.com/spf13/pflag"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/manifests"
 	manifestsv2 "github.com/operator-framework/operator-sdk/internal/plugins/manifests/v2"
@@ -26,7 +27,7 @@ import (
 type createAPISubcommand struct {
 	plugin.CreateAPISubcommand
 
-	config *config.Config
+	config config.Config
 }
 
 var _ plugin.CreateAPISubcommand = &createAPISubcommand{}
@@ -36,16 +37,16 @@ func (p *createAPISubcommand) UpdateContext(ctx *plugin.Context) {
 }
 func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) { p.CreateAPISubcommand.BindFlags(fs) }
 
-func (p *createAPISubcommand) InjectConfig(c *config.Config) {
+func (p *createAPISubcommand) InjectConfig(c config.Config) {
 	p.CreateAPISubcommand.InjectConfig(c)
 	p.config = c
 }
 
 func (p *createAPISubcommand) Run() error {
 	// Run() may add a new resource to the config, so we can compare resources before/after to get the new resource.
-	oldResources := make(map[config.GVK]struct{}, len(p.config.Resources))
-	for _, r := range p.config.Resources {
-		oldResources[r] = struct{}{}
+	oldResources, err := p.config.GetResources()
+	if err != nil {
+		return err
 	}
 
 	if err := p.CreateAPISubcommand.Run(); err != nil {
@@ -54,20 +55,27 @@ func (p *createAPISubcommand) Run() error {
 
 	// Find the new resource. Here we shouldn't worry about checking if one was found,
 	// since downstream plugins will do so.
-	var newResource config.GVK
-	for _, r := range p.config.Resources {
-		if _, hasResource := oldResources[r]; !hasResource {
-			newResource = r
-			break
+	newResources, err := p.config.GetResources()
+	if err != nil {
+		return err
+	}
+	var newResource resource.Resource
+	for _, newR := range newResources {
+		newResource = newR
+		for _, oldR := range oldResources {
+			if !oldR.GVK.IsEqualTo(newR.GVK) {
+				newResource = newR
+				break
+			}
 		}
 	}
 
 	// Run SDK phase 2 plugins.
-	return p.runPhase2(newResource)
+	return p.runPhase2(newResource.GVK)
 }
 
 // SDK phase 2 plugins.
-func (p *createAPISubcommand) runPhase2(gvk config.GVK) error {
+func (p *createAPISubcommand) runPhase2(gvk resource.GVK) error {
 	// Check if the generic "go" operator-sdk plugin (legacy) exists first.
 	if hasPluginConfig(p.config) {
 		if err := manifests.RunCreateAPI(p.config, gvk); err != nil {
diff --git a/internal/plugins/golang/v2/config.go b/internal/plugins/golang/v2/config.go
index d0bed614..f6467a06 100644
--- a/internal/plugins/golang/v2/config.go
+++ b/internal/plugins/golang/v2/config.go
@@ -14,17 +14,16 @@
 
 package v2
 
-import "sigs.k8s.io/kubebuilder/v2/pkg/model/config"
+import (
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+)
 
 // Config configures this plugin, and is saved in the project config file.
 // Deprecated: use scorecard and manifests plugin configs directly.
 type Config struct{}
 
-// hasPluginConfig returns true if cfg.Plugins contains an exact match for this plugin's key.
-func hasPluginConfig(cfg *config.Config) bool {
-	if !cfg.IsV3() || len(cfg.Plugins) == 0 {
-		return false
-	}
-	_, hasKey := cfg.Plugins[pluginConfigKey]
-	return hasKey
+// hasPluginConfig returns true if cfg.Layout contains an exact match for this plugin's key.
+func hasPluginConfig(cfg config.Config) bool {
+	info := Config{}
+	return cfg.DecodePluginConfig(pluginConfigKey, &info) == nil
 }
diff --git a/internal/plugins/golang/v2/init.go b/internal/plugins/golang/v2/init.go
index dfd5c80c..bf2914b1 100644
--- a/internal/plugins/golang/v2/init.go
+++ b/internal/plugins/golang/v2/init.go
@@ -16,8 +16,8 @@ package v2
 
 import (
 	"github.com/spf13/pflag"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/envtest"
 	manifestsv2 "github.com/operator-framework/operator-sdk/internal/plugins/manifests/v2"
@@ -27,7 +27,7 @@ import (
 type initSubcommand struct {
 	plugin.InitSubcommand
 
-	config *config.Config
+	config config.Config
 }
 
 var _ plugin.InitSubcommand = &initSubcommand{}
@@ -35,7 +35,7 @@ var _ plugin.InitSubcommand = &initSubcommand{}
 func (p *initSubcommand) UpdateContext(ctx *plugin.Context) { p.InitSubcommand.UpdateContext(ctx) }
 func (p *initSubcommand) BindFlags(fs *pflag.FlagSet)       { p.InitSubcommand.BindFlags(fs) }
 
-func (p *initSubcommand) InjectConfig(c *config.Config) {
+func (p *initSubcommand) InjectConfig(c config.Config) {
 	p.InitSubcommand.InjectConfig(c)
 	p.config = c
 }
diff --git a/internal/plugins/golang/v2/plugin.go b/internal/plugins/golang/v2/plugin.go
index ec7b7f51..1404fbcf 100644
--- a/internal/plugins/golang/v2/plugin.go
+++ b/internal/plugins/golang/v2/plugin.go
@@ -15,10 +15,12 @@
 package v2
 
 import (
-	"github.com/operator-framework/operator-sdk/internal/plugins"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/stage"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
+	kbgov2 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v2"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
-	kbgov2 "sigs.k8s.io/kubebuilder/v2/pkg/plugins/golang/v2"
+	"github.com/operator-framework/operator-sdk/internal/plugins"
 )
 
 // Plugin name/version used in this file will also be used in phase 2 plugins when we can
@@ -30,7 +32,7 @@ const (
 )
 
 var (
-	pluginVersion   = plugin.Version{Number: 2, Stage: plugin.AlphaStage}
+	pluginVersion   = plugin.Version{Number: 2, Stage: stage.Alpha}
 	pluginConfigKey = plugin.Key(pluginName, pluginVersion.String())
 )
 
@@ -45,7 +47,7 @@ type Plugin struct{}
 
 func (Plugin) Name() string            { return (kbgov2.Plugin{}).Name() }
 func (Plugin) Version() plugin.Version { return (kbgov2.Plugin{}).Version() }
-func (Plugin) SupportedProjectVersions() []string {
+func (Plugin) SupportedProjectVersions() []config.Version {
 	return (kbgov2.Plugin{}).SupportedProjectVersions()
 }
 
diff --git a/internal/plugins/golang/v3/api.go b/internal/plugins/golang/v3/api.go
index 6f625d31..5b9caaf5 100644
--- a/internal/plugins/golang/v3/api.go
+++ b/internal/plugins/golang/v3/api.go
@@ -16,8 +16,9 @@ package v3
 
 import (
 	"github.com/spf13/pflag"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
 
 	manifestsv2 "github.com/operator-framework/operator-sdk/internal/plugins/manifests/v2"
 )
@@ -25,7 +26,7 @@ import (
 type createAPISubcommand struct {
 	plugin.CreateAPISubcommand
 
-	config *config.Config
+	config config.Config
 }
 
 var _ plugin.CreateAPISubcommand = &createAPISubcommand{}
@@ -35,36 +36,44 @@ func (p *createAPISubcommand) UpdateContext(ctx *plugin.Context) {
 }
 func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) { p.CreateAPISubcommand.BindFlags(fs) }
 
-func (p *createAPISubcommand) InjectConfig(c *config.Config) {
+func (p *createAPISubcommand) InjectConfig(c config.Config) {
 	p.CreateAPISubcommand.InjectConfig(c)
 	p.config = c
 }
 
 func (p *createAPISubcommand) Run() error {
 	// Run() may add a new resource to the config, so we can compare resources before/after to get the new resource.
-	oldResources := make(map[config.GVK]struct{}, len(p.config.Resources))
-	for _, r := range p.config.Resources {
-		oldResources[r] = struct{}{}
+	oldResources, err := p.config.GetResources()
+	if err != nil {
+		return err
 	}
+
 	if err := p.CreateAPISubcommand.Run(); err != nil {
 		return err
 	}
 
 	// Find the new resource. Here we shouldn't worry about checking if one was found,
 	// since downstream plugins will do so.
-	var newResource config.GVK
-	for _, r := range p.config.Resources {
-		if _, hasResource := oldResources[r]; !hasResource {
-			newResource = r
-			break
+	newResources, err := p.config.GetResources()
+	if err != nil {
+		return err
+	}
+	var newResource resource.Resource
+	for _, newR := range newResources {
+		newResource = newR
+		for _, oldR := range oldResources {
+			if !oldR.GVK.IsEqualTo(newR.GVK) {
+				newResource = newR
+				break
+			}
 		}
 	}
 
 	// Run SDK phase 2 plugins.
-	return p.runPhase2(newResource)
+	return p.runPhase2(newResource.GVK)
 }
 
 // SDK phase 2 plugins.
-func (p *createAPISubcommand) runPhase2(gvk config.GVK) error {
+func (p *createAPISubcommand) runPhase2(gvk resource.GVK) error {
 	return manifestsv2.RunCreateAPI(p.config, gvk)
 }
diff --git a/internal/plugins/golang/v3/init.go b/internal/plugins/golang/v3/init.go
index eed75977..cda5af2c 100644
--- a/internal/plugins/golang/v3/init.go
+++ b/internal/plugins/golang/v3/init.go
@@ -16,8 +16,8 @@ package v3
 
 import (
 	"github.com/spf13/pflag"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
 
 	manifestsv2 "github.com/operator-framework/operator-sdk/internal/plugins/manifests/v2"
 	scorecardv2 "github.com/operator-framework/operator-sdk/internal/plugins/scorecard/v2"
@@ -26,7 +26,7 @@ import (
 type initSubcommand struct {
 	plugin.InitSubcommand
 
-	config *config.Config
+	config config.Config
 }
 
 var _ plugin.InitSubcommand = &initSubcommand{}
@@ -34,7 +34,7 @@ var _ plugin.InitSubcommand = &initSubcommand{}
 func (p *initSubcommand) UpdateContext(ctx *plugin.Context) { p.InitSubcommand.UpdateContext(ctx) }
 func (p *initSubcommand) BindFlags(fs *pflag.FlagSet)       { p.InitSubcommand.BindFlags(fs) }
 
-func (p *initSubcommand) InjectConfig(c *config.Config) {
+func (p *initSubcommand) InjectConfig(c config.Config) {
 	p.InitSubcommand.InjectConfig(c)
 	p.config = c
 }
diff --git a/internal/plugins/golang/v3/plugin.go b/internal/plugins/golang/v3/plugin.go
index a6be68a5..54f178b4 100644
--- a/internal/plugins/golang/v3/plugin.go
+++ b/internal/plugins/golang/v3/plugin.go
@@ -15,8 +15,9 @@
 package v3
 
 import (
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
-	kbgov3 "sigs.k8s.io/kubebuilder/v2/pkg/plugins/golang/v3"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
+	kbgov3 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3"
 )
 
 var (
@@ -30,7 +31,7 @@ type Plugin struct{}
 
 func (Plugin) Name() string            { return (kbgov3.Plugin{}).Name() }
 func (Plugin) Version() plugin.Version { return (kbgov3.Plugin{}).Version() }
-func (Plugin) SupportedProjectVersions() []string {
+func (Plugin) SupportedProjectVersions() []config.Version {
 	return (kbgov3.Plugin{}).SupportedProjectVersions()
 }
 
diff --git a/internal/plugins/helm/v1/api.go b/internal/plugins/helm/v1/api.go
index 52ddc6c2..3463692e 100644
--- a/internal/plugins/helm/v1/api.go
+++ b/internal/plugins/helm/v1/api.go
@@ -19,9 +19,9 @@ import (
 	"strings"
 
 	"github.com/spf13/pflag"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/resource"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/cmdutil"
 	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/chartutil"
@@ -31,8 +31,7 @@ import (
 )
 
 type createAPISubcommand struct {
-	config *config.Config
-
+	config        config.Config
 	createOptions chartutil.CreateOptions
 }
 
@@ -117,7 +116,7 @@ func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) {
 }
 
 // InjectConfig will inject the PROJECT file/config in the plugin
-func (p *createAPISubcommand) InjectConfig(c *config.Config) {
+func (p *createAPISubcommand) InjectConfig(c config.Config) {
 	p.config = c
 }
 
@@ -138,7 +137,7 @@ func (p *createAPISubcommand) Run() error {
 // SDK phase 2 plugins.
 func (p *createAPISubcommand) runPhase2() error {
 	ogvk := p.createOptions.GVK
-	gvk := config.GVK{Group: ogvk.Group, Version: ogvk.Version, Kind: ogvk.Kind}
+	gvk := resource.GVK{Group: ogvk.Group, Version: ogvk.Version, Kind: ogvk.Kind}
 
 	// Initially the helm/v1 plugin was written to not create a "plugins" config entry
 	// for any phase 2 plugin because they did not have their own keys. Now there are phase 2
@@ -183,13 +182,9 @@ func (p *createAPISubcommand) Validate() error {
 		}
 
 		// Validate the resource.
-		r := resource.Options{
-			Namespaced: true,
-			Group:      p.createOptions.GVK.Group,
-			Version:    p.createOptions.GVK.Version,
-			Kind:       p.createOptions.GVK.Kind,
-		}
-		if err := r.Validate(); err != nil {
+		ogvk := p.createOptions.GVK
+		gvk := resource.GVK{Group: ogvk.Group, Version: ogvk.Version, Kind: ogvk.Kind}
+		if err := gvk.Validate(); err != nil {
 			return err
 		}
 	}
diff --git a/internal/plugins/helm/v1/chartutil/chart.go b/internal/plugins/helm/v1/chartutil/chart.go
index 55d9d2a9..671a1cce 100644
--- a/internal/plugins/helm/v1/chartutil/chart.go
+++ b/internal/plugins/helm/v1/chartutil/chart.go
@@ -32,7 +32,7 @@ import (
 	"helm.sh/helm/v3/pkg/getter"
 	"helm.sh/helm/v3/pkg/repo"
 	"k8s.io/apimachinery/pkg/runtime/schema"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang"
 )
 
 const (
@@ -66,6 +66,9 @@ type CreateOptions struct {
 
 	// CRDVersion is the version of the `apiextensions.k8s.io` API which will be used to generate the CRD.
 	CRDVersion string
+
+	// Domain is the domain of the project
+	Domain string
 }
 
 // CreateChart scaffolds a new helm chart for the project rooted in projectDir
@@ -114,7 +117,7 @@ type CreateOptions struct {
 //
 // CreateChart returns an error if an error occurs creating the scaffold.Resource or
 // creating the chart.
-func CreateChart(projectDir string, opts CreateOptions) (*resource.Options, *chart.Chart, error) {
+func CreateChart(projectDir string, opts CreateOptions) (*golang.Options, *chart.Chart, error) {
 	chartsDir := filepath.Join(projectDir, HelmChartsDir)
 	err := os.MkdirAll(chartsDir, 0755)
 	if err != nil {
@@ -122,14 +125,14 @@ func CreateChart(projectDir string, opts CreateOptions) (*resource.Options, *cha
 	}
 
 	var (
-		r *resource.Options
+		r *golang.Options
 		c *chart.Chart
 	)
 
 	// If we don't have a helm chart reference, scaffold the default chart
 	// from Helm's default template. Otherwise, fetch it.
 	if len(opts.Chart) == 0 {
-		r, c, err = scaffoldChart(chartsDir, opts.GVK.Group, opts.GVK.Version, opts.GVK.Kind)
+		r, c, err = scaffoldChart(chartsDir, opts)
 		if err != nil {
 			return nil, nil, fmt.Errorf("failed to scaffold default chart: %v", err)
 		}
@@ -156,13 +159,13 @@ func CreateChart(projectDir string, opts CreateOptions) (*resource.Options, *cha
 	return r, c, nil
 }
 
-func scaffoldChart(destDir, group, version, kind string) (*resource.Options, *chart.Chart, error) {
-	r := &resource.Options{
-		Namespaced: true,
-		Group:      group,
-		Version:    version,
-		Kind:       kind,
-	}
+func scaffoldChart(destDir string, opts CreateOptions) (*golang.Options, *chart.Chart, error) {
+	r := &golang.Options{}
+	r.Namespaced = true
+	r.Domain = opts.Domain
+	r.Group = opts.GVK.Group
+	r.Version = opts.GVK.Version
+	r.Kind = opts.GVK.Kind
 
 	chartPath, err := chartutil.Create(strings.ToLower(r.Kind), destDir)
 	if err != nil {
@@ -176,7 +179,7 @@ func scaffoldChart(destDir, group, version, kind string) (*resource.Options, *ch
 	return r, chart, nil
 }
 
-func fetchChart(destDir string, opts CreateOptions) (*resource.Options, *chart.Chart, error) {
+func fetchChart(destDir string, opts CreateOptions) (*golang.Options, *chart.Chart, error) {
 	var (
 		chart *chart.Chart
 		err   error
@@ -202,12 +205,13 @@ func fetchChart(destDir string, opts CreateOptions) (*resource.Options, *chart.C
 		opts.GVK.Kind = strcase.ToCamel(chartName)
 	}
 
-	r := &resource.Options{
-		Namespaced: true,
-		Group:      opts.GVK.Group,
-		Version:    opts.GVK.Version,
-		Kind:       opts.GVK.Kind,
-	}
+	r := &golang.Options{}
+	r.Namespaced = true
+	r.Domain = opts.Domain
+	r.Group = opts.GVK.Group
+	r.Version = opts.GVK.Version
+	r.Kind = opts.GVK.Kind
+
 	return r, chart, nil
 }
 
diff --git a/internal/plugins/helm/v1/init.go b/internal/plugins/helm/v1/init.go
index 719e46f8..9b75c74f 100644
--- a/internal/plugins/helm/v1/init.go
+++ b/internal/plugins/helm/v1/init.go
@@ -22,8 +22,9 @@ import (
 
 	"github.com/spf13/pflag"
 	"k8s.io/apimachinery/pkg/util/validation"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/cmdutil"
 	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/chartutil"
@@ -33,14 +34,15 @@ import (
 )
 
 type initSubcommand struct {
-	config    *config.Config
+	config    config.Config
+	options   *golang.Options
 	apiPlugin createAPISubcommand
 
-	// If true, run the `create api` plugin.
-	doCreateAPI bool
-
 	// For help text.
 	commandName string
+
+	// Flags
+	projectName string
 }
 
 var (
@@ -118,15 +120,16 @@ Writes the following files:
 // BindFlags will set the flags for the plugin
 func (p *initSubcommand) BindFlags(fs *pflag.FlagSet) {
 	fs.SortFlags = false
-	fs.StringVar(&p.config.Domain, "domain", "my.domain", "domain for groups")
-	fs.StringVar(&p.config.ProjectName, "project-name", "", "name of this project, the default being directory name")
+	p.options = &golang.Options{}
+	fs.StringVar(&p.options.Domain, "domain", "my.domain", "domain for groups")
+	fs.StringVar(&p.projectName, "project-name", "", "name of this project, the default being directory name")
 	p.apiPlugin.BindFlags(fs)
 }
 
 // InjectConfig will inject the PROJECT file/config in the plugin
-func (p *initSubcommand) InjectConfig(c *config.Config) {
+func (p *initSubcommand) InjectConfig(c config.Config) {
 	// v3 project configs get a 'layout' value.
-	c.Layout = pluginKey
+	_ = c.SetLayout(pluginKey)
 	p.config = c
 	p.apiPlugin.config = p.config
 }
@@ -154,7 +157,7 @@ func (p *initSubcommand) runPhase2() error {
 		return err
 	}
 
-	if p.doCreateAPI {
+	if p.options.DoAPI {
 		if err := p.apiPlugin.runPhase2(); err != nil {
 			return err
 		}
@@ -165,22 +168,32 @@ func (p *initSubcommand) runPhase2() error {
 
 // Validate perform the required validations for this plugin
 func (p *initSubcommand) Validate() error {
+	// Set values in the config
+	if err := p.config.SetProjectName(p.projectName); err != nil {
+		return err
+	}
+	if err := p.config.SetDomain(p.options.Domain); err != nil {
+		return err
+	}
 
 	// Check if the project name is a valid k8s namespace (DNS 1123 label).
-	if p.config.ProjectName == "" {
+	if p.config.GetProjectName() == "" {
 		dir, err := os.Getwd()
 		if err != nil {
 			return fmt.Errorf("error getting current directory: %v", err)
 		}
-		p.config.ProjectName = strings.ToLower(filepath.Base(dir))
+		if err = p.config.SetProjectName(strings.ToLower(filepath.Base(dir))); err != nil {
+			return err
+		}
 	}
-	if err := validation.IsDNS1123Label(p.config.ProjectName); err != nil {
-		return fmt.Errorf("project name (%s) is invalid: %v", p.config.ProjectName, err)
+
+	if err := validation.IsDNS1123Label(p.config.GetProjectName()); err != nil {
+		return fmt.Errorf("project name (%s) is invalid: %v", p.config.GetProjectName(), err)
 	}
 
 	defaultOpts := chartutil.CreateOptions{CRDVersion: "v1"}
 	if !p.apiPlugin.createOptions.GVK.Empty() || p.apiPlugin.createOptions != defaultOpts {
-		p.doCreateAPI = true
+		p.options.DoAPI = true
 		return p.apiPlugin.Validate()
 	}
 
@@ -193,7 +206,7 @@ func (p *initSubcommand) GetScaffolder() (cmdutil.Scaffolder, error) {
 		apiScaffolder cmdutil.Scaffolder
 		err           error
 	)
-	if p.doCreateAPI {
+	if p.options.DoAPI {
 		apiScaffolder, err = p.apiPlugin.GetScaffolder()
 		if err != nil {
 			return nil, err
@@ -205,7 +218,7 @@ func (p *initSubcommand) GetScaffolder() (cmdutil.Scaffolder, error) {
 // PostScaffold will run the required actions after the default plugin scaffold
 func (p *initSubcommand) PostScaffold() error {
 
-	if p.doCreateAPI {
+	if p.options.DoAPI {
 		return p.apiPlugin.PostScaffold()
 	}
 
diff --git a/internal/plugins/helm/v1/plugin.go b/internal/plugins/helm/v1/plugin.go
index 563e01df..6f956f6a 100644
--- a/internal/plugins/helm/v1/plugin.go
+++ b/internal/plugins/helm/v1/plugin.go
@@ -15,8 +15,9 @@
 package v1
 
 import (
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins"
 )
@@ -24,7 +25,7 @@ import (
 const pluginName = "helm" + plugins.DefaultNameQualifier
 
 var (
-	supportedProjectVersions = []string{config.Version3Alpha}
+	supportedProjectVersions = []config.Version{cfgv3.Version}
 	pluginVersion            = plugin.Version{Number: 1}
 	pluginKey                = plugin.KeyFor(Plugin{})
 )
@@ -42,6 +43,6 @@ type Plugin struct {
 
 func (Plugin) Name() string                                         { return pluginName }
 func (Plugin) Version() plugin.Version                              { return pluginVersion }
-func (Plugin) SupportedProjectVersions() []string                   { return supportedProjectVersions }
+func (Plugin) SupportedProjectVersions() []config.Version           { return supportedProjectVersions }
 func (p Plugin) GetInitSubcommand() plugin.InitSubcommand           { return &p.initSubcommand }
 func (p Plugin) GetCreateAPISubcommand() plugin.CreateAPISubcommand { return &p.createAPISubcommand }
diff --git a/internal/plugins/helm/v1/scaffolds/api.go b/internal/plugins/helm/v1/scaffolds/api.go
index de443830..29c7db31 100644
--- a/internal/plugins/helm/v1/scaffolds/api.go
+++ b/internal/plugins/helm/v1/scaffolds/api.go
@@ -23,9 +23,9 @@ import (
 	"os"
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/cmdutil"
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/machinery"
@@ -41,12 +41,12 @@ var _ cmdutil.Scaffolder = &apiScaffolder{}
 // apiScaffolder contains configuration for generating scaffolding for Go type
 // representing the API and controller that implements the behavior for the API.
 type apiScaffolder struct {
-	config *config.Config
+	config config.Config
 	opts   chartutil.CreateOptions
 }
 
 // NewAPIScaffolder returns a new Scaffolder for API/controller creation operations
-func NewAPIScaffolder(config *config.Config, opts chartutil.CreateOptions) cmdutil.Scaffolder {
+func NewAPIScaffolder(config config.Config, opts chartutil.CreateOptions) cmdutil.Scaffolder {
 	return &apiScaffolder{
 		config: config,
 		opts:   opts,
@@ -70,26 +70,48 @@ func (s *apiScaffolder) scaffold() error {
 	if err != nil {
 		return err
 	}
-	r, chrt, err := chartutil.CreateChart(projectDir, s.opts)
+	resourceOptions, chrt, err := chartutil.CreateChart(projectDir, s.opts)
 	if err != nil {
 		return err
 	}
 
+	resourceOptions.DoAPI = true
+	//todo(camilamacedo86): replace the options by kubernetes-sigs/kubebuilder#1974
+	if err := resourceOptions.Validate(); err != nil {
+		return err
+	}
+
 	// Check that resource doesn't exist
-	if s.config.HasResource(r.GVK()) {
+	if s.config.HasResource(resourceOptions.GVK()) {
 		return errors.New("the API resource already exists")
 	}
+
 	// Check that the provided group can be added to the project
-	if !s.config.MultiGroup && len(s.config.Resources) != 0 && !s.config.HasGroup(r.Group) {
+	if !s.config.IsMultiGroup() && s.config.ResourcesLength() != 0 && !s.config.HasGroup(resourceOptions.Group) {
 		return errors.New("multiple groups are not allowed by default, to enable multi-group set 'multigroup: true' in your PROJECT file")
 	}
 
-	res := r.NewResource(s.config, true)
-	s.config.UpdateResources(res.GVK())
+	resource := resourceOptions.NewResource(s.config)
+
+	resource.Domain = s.config.GetDomain()
+
+	// remove the path since is not a Golang project
+	resource.Path = ""
+
+	// add the resource API info to complain with project-version=3
+	// todo: ensure that this information is properly returned from
+	// resource.newResource in upstream ( see kubernetes-sigs/kubebuilder#1974)
+	// and then, remove it.
+	resource.API.Namespaced = true
+	resource.API.CRDVersion = s.opts.CRDVersion
+
+	if err := s.config.UpdateResource(resource); err != nil {
+		return err
+	}
 
 	chartPath := filepath.Join(chartutil.HelmChartsDir, chrt.Metadata.Name)
 	if err := machinery.NewScaffold().Execute(
-		s.newUniverse(res),
+		s.newUniverse(&resource),
 		&templates.WatchesUpdater{ChartPath: chartPath},
 		&crd.CRD{CRDVersion: s.opts.CRDVersion},
 		&crd.Kustomization{},
diff --git a/internal/plugins/helm/v1/scaffolds/init.go b/internal/plugins/helm/v1/scaffolds/init.go
index f9b1e6d4..a696f681 100644
--- a/internal/plugins/helm/v1/scaffolds/init.go
+++ b/internal/plugins/helm/v1/scaffolds/init.go
@@ -20,8 +20,8 @@ package scaffolds
 import (
 	"os"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/cmdutil"
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/machinery"
@@ -47,12 +47,12 @@ var helmOperatorVersion = version.ImageVersion
 var _ cmdutil.Scaffolder = &initScaffolder{}
 
 type initScaffolder struct {
-	config        *config.Config
+	config        config.Config
 	apiScaffolder cmdutil.Scaffolder
 }
 
 // NewInitScaffolder returns a new Scaffolder for project initialization operations
-func NewInitScaffolder(config *config.Config, apiScaffolder cmdutil.Scaffolder) cmdutil.Scaffolder {
+func NewInitScaffolder(config config.Config, apiScaffolder cmdutil.Scaffolder) cmdutil.Scaffolder {
 	return &initScaffolder{
 		config:        config,
 		apiScaffolder: apiScaffolder,
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/crd/crd.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/crd/crd.go
index a8921873..842a1ae0 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/crd/crd.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/crd/crd.go
@@ -20,7 +20,7 @@ import (
 	"path/filepath"
 
 	"github.com/kr/text"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &CRD{}
@@ -36,7 +36,7 @@ type CRD struct {
 // SetTemplateDefaults implements input.Template
 func (f *CRD) SetTemplateDefaults() error {
 	if f.Path == "" {
-		f.Path = filepath.Join("config", "crd", "bases", fmt.Sprintf("%s_%%[plural].yaml", f.Resource.Domain))
+		f.Path = filepath.Join("config", "crd", "bases", fmt.Sprintf("%s_%%[plural].yaml", f.Resource.QualifiedGroup()))
 	}
 	f.Path = f.Resource.Replacer().Replace(f.Path)
 
@@ -58,9 +58,9 @@ const crdTemplate = `---
 apiVersion: apiextensions.k8s.io/{{ .CRDVersion }}
 kind: CustomResourceDefinition
 metadata:
-  name: {{ .Resource.Plural }}.{{ .Resource.Domain }}
+  name: {{ .Resource.Plural }}.{{ .Resource.QualifiedGroup }}
 spec:
-  group: {{ .Resource.Domain }}
+  group: {{ .Resource.QualifiedGroup }}
   names:
     kind: {{ .Resource.Kind }}
     listKind: {{ .Resource.Kind }}List
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 a3281e96..7c0cc71d 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
@@ -21,7 +21,7 @@ import (
 	"fmt"
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
@@ -69,7 +69,7 @@ func (f *Kustomization) GetCodeFragments() file.CodeFragmentsMap {
 
 	// Generate resource code fragments
 	res := make([]string, 0)
-	res = append(res, fmt.Sprintf(resourceCodeFragment, f.Resource.Domain, f.Resource.Plural))
+	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 {
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go
index c01e7ed5..5c3a1086 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go
@@ -20,7 +20,7 @@ package kdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &AuthProxyPatch{}
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
index afbe8e0b..a5745e7c 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault/kustomization.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/kdefault/kustomization.go
@@ -20,7 +20,7 @@ package kdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/manager/kustomization.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/manager/kustomization.go
index 1318302e..93dcfafa 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/manager/kustomization.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/manager/kustomization.go
@@ -19,7 +19,7 @@ package manager
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/manager/manager.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/manager/manager.go
index beb54f70..8c7ca135 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/manager/manager.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/manager/manager.go
@@ -20,7 +20,7 @@ package manager
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Manager{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/prometheus/kustomization.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/prometheus/kustomization.go
index c72539da..a794eea1 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/prometheus/kustomization.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/prometheus/kustomization.go
@@ -19,7 +19,7 @@ package prometheus
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/prometheus/monitor.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/prometheus/monitor.go
index 54a2bcab..d092c6b8 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/prometheus/monitor.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/prometheus/monitor.go
@@ -19,7 +19,7 @@ package prometheus
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &ServiceMonitor{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role.go
index 761ea8bc..2fb69db5 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &AuthProxyRole{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role_binding.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role_binding.go
index 8342bf58..5c52caec 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role_binding.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_role_binding.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &AuthProxyRoleBinding{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_service.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_service.go
index 03857ba1..25a0a52a 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_service.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/auth_proxy_service.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &AuthProxyService{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/client_cluster_role.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/client_cluster_role.go
index a3cbb04c..6553210c 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/client_cluster_role.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/client_cluster_role.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &ClientClusterRole{}
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
index 39f18cb7..44b02767 100644
--- 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
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &CRDEditorRole{}
@@ -49,7 +49,7 @@ metadata:
   name: {{ lower .Resource.Kind }}-editor-role
 rules:
 - apiGroups:
-  - {{ .Resource.Domain }}
+  - {{ .Resource.QualifiedGroup }}
   resources:
   - {{ .Resource.Plural }}
   verbs:
@@ -61,7 +61,7 @@ rules:
   - update
   - watch
 - apiGroups:
-  - {{ .Resource.Domain }}
+  - {{ .Resource.QualifiedGroup }}
   resources:
   - {{ .Resource.Plural }}/status
   verbs:
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
index fb8efa12..3dee9614 100644
--- 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
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &CRDViewerRole{}
@@ -49,7 +49,7 @@ metadata:
   name: {{ lower .Resource.Kind }}-viewer-role
 rules:
 - apiGroups:
-  - {{ .Resource.Domain }}
+  - {{ .Resource.QualifiedGroup }}
   resources:
   - {{ .Resource.Plural }}
   verbs:
@@ -57,7 +57,7 @@ rules:
   - list
   - watch
 - apiGroups:
-  - {{ .Resource.Domain }}
+  - {{ .Resource.QualifiedGroup }}
   resources:
   - {{ .Resource.Plural }}/status
   verbs:
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/kustomization.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/kustomization.go
index 9d5711cd..74c3ef6e 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/kustomization.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/kustomization.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/leader_election_role.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/leader_election_role.go
index b220d947..f029a428 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/leader_election_role.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/leader_election_role.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &LeaderElectionRole{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/leader_election_role_binding.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/leader_election_role_binding.go
index 00124b77..994c3179 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/leader_election_role_binding.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/leader_election_role_binding.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &LeaderElectionRoleBinding{}
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 14488342..8472ff19 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
@@ -32,7 +32,7 @@ import (
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/client-go/discovery"
 	crconfig "sigs.k8s.io/controller-runtime/pkg/client/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 	"sigs.k8s.io/yaml"
 )
 
@@ -152,10 +152,10 @@ rules:
 `
 
 const rulesFragment = `##
-## Rules for {{.Resource.Domain}}/{{.Resource.Version}}, Kind: {{.Resource.Kind}}
+## Rules for {{.Resource.QualifiedGroup}}/{{.Resource.Version}}, Kind: {{.Resource.Kind}}
 ##
 - apiGroups:
-  - {{.Resource.Domain}}
+  - {{.Resource.QualifiedGroup}}
   resources:
   - {{.Resource.Plural}}
   - {{.Resource.Plural}}/status
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/manager_role_binding.go b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/manager_role_binding.go
index fd02baa9..544f4656 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/manager_role_binding.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/rbac/manager_role_binding.go
@@ -19,7 +19,7 @@ package rbac
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &ManagerRoleBinding{}
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/crd_sample.go
index c858a06c..b33681fb 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/config/samples/crd_sample.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/config/samples/crd_sample.go
@@ -24,7 +24,7 @@ import (
 	"text/template"
 
 	"helm.sh/helm/v3/pkg/chart"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 	"sigs.k8s.io/yaml"
 )
 
@@ -84,7 +84,7 @@ func (f *CRDSample) GetFuncMap() template.FuncMap {
 const defaultSpecTemplate = `foo: bar
 `
 
-const crdSampleTemplate = `apiVersion: {{ .Resource.Domain }}/{{ .Resource.Version }}
+const crdSampleTemplate = `apiVersion: {{ .Resource.QualifiedGroup }}/{{ .Resource.Version }}
 kind: {{ .Resource.Kind }}
 metadata:
   name: {{ lower .Resource.Kind }}-sample
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/dockerfile.go b/internal/plugins/helm/v1/scaffolds/internal/templates/dockerfile.go
index 26a16949..14f17dfc 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/dockerfile.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/dockerfile.go
@@ -17,7 +17,7 @@ package templates
 import (
 	"errors"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Dockerfile{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/gitignore.go b/internal/plugins/helm/v1/scaffolds/internal/templates/gitignore.go
index 745db8da..64cc86d9 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/gitignore.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/gitignore.go
@@ -18,7 +18,7 @@ limitations under the License.
 package templates
 
 import (
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &GitIgnore{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/makefile.go b/internal/plugins/helm/v1/scaffolds/internal/templates/makefile.go
index 7fa3fa3a..309a7aad 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/makefile.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/makefile.go
@@ -20,7 +20,7 @@ package templates
 import (
 	"errors"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Makefile{}
diff --git a/internal/plugins/helm/v1/scaffolds/internal/templates/watches.go b/internal/plugins/helm/v1/scaffolds/internal/templates/watches.go
index 7e8256bd..5269ae1f 100644
--- a/internal/plugins/helm/v1/scaffolds/internal/templates/watches.go
+++ b/internal/plugins/helm/v1/scaffolds/internal/templates/watches.go
@@ -17,7 +17,7 @@ package templates
 import (
 	"fmt"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Watches{}
@@ -79,7 +79,7 @@ func (f *WatchesUpdater) GetCodeFragments() file.CodeFragmentsMap {
 	// Generate watch fragments
 	watches := make([]string, 0)
 	watches = append(watches,
-		fmt.Sprintf(watchFragment, f.Resource.Domain, f.Resource.Version, f.Resource.Kind, f.ChartPath))
+		fmt.Sprintf(watchFragment, f.Resource.QualifiedGroup(), f.Resource.Version, f.Resource.Kind, f.ChartPath))
 
 	if len(watches) != 0 {
 		fragments[file.NewMarkerFor(defaultWatchesFile, watchMarker)] = watches
diff --git a/internal/plugins/manifests/api.go b/internal/plugins/manifests/api.go
index 20f02eff..91ad9c1c 100644
--- a/internal/plugins/manifests/api.go
+++ b/internal/plugins/manifests/api.go
@@ -18,17 +18,20 @@ package manifests
 import (
 	"fmt"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/machinery"
 )
 
 // RunCreateAPI runs the manifests SDK phase 2 plugin.
-func RunCreateAPI(cfg *config.Config, gvk config.GVK) error {
+func RunCreateAPI(cfg config.Config, gvk resource.GVK) error {
 	// Only run these if project version is v3.
-	if !cfg.IsV3() {
+	isV3 := cfg.GetVersion().Compare(cfgv3.Version) == 0
+	if !isV3 {
 		return nil
 	}
 
@@ -40,11 +43,11 @@ func RunCreateAPI(cfg *config.Config, gvk config.GVK) error {
 }
 
 type apiScaffolder struct {
-	config *config.Config
-	gvk    config.GVK
+	config config.Config
+	gvk    resource.GVK
 }
 
-func newAPIScaffolder(config *config.Config, gvk config.GVK) *apiScaffolder {
+func newAPIScaffolder(config config.Config, gvk resource.GVK) *apiScaffolder {
 	return &apiScaffolder{
 		config: config,
 		gvk:    gvk,
diff --git a/internal/plugins/manifests/init.go b/internal/plugins/manifests/init.go
index 7061dd02..cdf877de 100644
--- a/internal/plugins/manifests/init.go
+++ b/internal/plugins/manifests/init.go
@@ -20,15 +20,17 @@ import (
 	"io/ioutil"
 	"os"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
 
 	"github.com/operator-framework/operator-sdk/internal/util/projutil"
 )
 
 // RunInit modifies the project scaffolded by kubebuilder's Init plugin.
-func RunInit(cfg *config.Config) error {
+func RunInit(cfg config.Config) error {
 	// Only run these if project version is v3.
-	if !cfg.IsV3() {
+	isV3 := cfg.GetVersion().Compare(cfgv3.Version) == 0
+	if !isV3 {
 		return nil
 	}
 
@@ -40,7 +42,7 @@ func RunInit(cfg *config.Config) error {
 }
 
 // initUpdateMakefile updates a vanilla kubebuilder Makefile with operator-sdk recipes.
-func initUpdateMakefile(cfg *config.Config, filePath string) error {
+func initUpdateMakefile(cfg config.Config, filePath string) error {
 	makefileBytes, err := ioutil.ReadFile(filePath)
 	if err != nil {
 		return err
@@ -50,10 +52,10 @@ func initUpdateMakefile(cfg *config.Config, filePath string) error {
 	makefileBytes = append([]byte(makefileBundleVarFragment), makefileBytes...)
 
 	// Append bundle recipes.
-	operatorType := projutil.PluginKeyToOperatorType(cfg.Layout)
+	operatorType := projutil.PluginKeyToOperatorType(cfg.GetLayout())
 	switch operatorType {
 	case projutil.OperatorTypeUnknown:
-		return fmt.Errorf("unsupported plugin key %q", cfg.Layout)
+		return fmt.Errorf("unsupported plugin key %q", cfg.GetLayout())
 	case projutil.OperatorTypeGo:
 		makefileBytes = append(makefileBytes, []byte(makefileBundleFragmentGo)...)
 	default:
@@ -103,8 +105,7 @@ BUNDLE_IMG ?= controller-bundle:$(VERSION)
 `
 
 	makefileBundleFragmentGo = `
-# Generate bundle manifests and metadata, then validate generated files.
-.PHONY: bundle
+.PHONY: bundle ## Generate bundle manifests and metadata, then validate generated files.
 bundle: manifests kustomize
 	operator-sdk generate kustomize manifests -q
 	cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
@@ -113,8 +114,7 @@ bundle: manifests kustomize
 `
 
 	makefileBundleFragmentNonGo = `
-# Generate bundle manifests and metadata, then validate generated files.
-.PHONY: bundle
+.PHONY: bundle ## Generate bundle manifests and metadata, then validate generated files.
 bundle: kustomize
 	operator-sdk generate kustomize manifests -q
 	cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
@@ -123,8 +123,7 @@ bundle: kustomize
 `
 
 	makefileBundleBuildFragment = `
-# Build the bundle image.
-.PHONY: bundle-build
+.PHONY: bundle-build ## Build the bundle image.
 bundle-build:
 	docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
 `
diff --git a/internal/plugins/manifests/samples.go b/internal/plugins/manifests/samples.go
index 9aae7840..9c7c3b2e 100644
--- a/internal/plugins/manifests/samples.go
+++ b/internal/plugins/manifests/samples.go
@@ -18,8 +18,8 @@ import (
 	"path/filepath"
 	"strings"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
 )
 
 var _ file.Template = &kustomization{}
@@ -30,7 +30,7 @@ type kustomization struct {
 	file.TemplateMixin
 
 	// GroupVersionKind is the sample's gvk to add to this scaffold.
-	GroupVersionKind config.GVK
+	GroupVersionKind resource.GVK
 }
 
 // SetTemplateDefaults implements file.Template
@@ -58,7 +58,7 @@ const samplesCodeFragment = `- %s
 
 // makeCRFileName returns a Custom Resource example file name in the same format
 // as kubebuilder's CreateAPI plugin for a gvk.
-func makeCRFileName(gvk config.GVK) string {
+func makeCRFileName(gvk resource.GVK) string {
 	return fmt.Sprintf("%s_%s_%s.yaml", gvk.Group, gvk.Version, strings.ToLower(gvk.Kind))
 }
 
diff --git a/internal/plugins/manifests/v2/plugin.go b/internal/plugins/manifests/v2/plugin.go
index 1827fdc4..2365431d 100644
--- a/internal/plugins/manifests/v2/plugin.go
+++ b/internal/plugins/manifests/v2/plugin.go
@@ -17,11 +17,12 @@ package v2
 import (
 	"fmt"
 
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
+
 	"github.com/operator-framework/operator-sdk/internal/plugins"
 	"github.com/operator-framework/operator-sdk/internal/plugins/manifests"
-
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
 )
 
 const (
@@ -36,17 +37,14 @@ var (
 // Config configures this plugin, and is saved in the project config file.
 type Config struct{}
 
-// HasPluginConfig returns true if cfg.Plugins contains an exact match for this plugin's key.
-func HasPluginConfig(cfg *config.Config) bool {
-	if !cfg.IsV3() || len(cfg.Plugins) == 0 {
-		return false
-	}
-	_, hasKey := cfg.Plugins[pluginConfigKey]
-	return hasKey
+// HasPluginConfig returns true if cfg.Layout contains an exact match for this plugin's key.
+func HasPluginConfig(cfg config.Config) bool {
+	info := Config{}
+	return cfg.DecodePluginConfig(pluginConfigKey, &info) == nil
 }
 
 // RunInit modifies the project scaffolded by kubebuilder's Init plugin.
-func RunInit(cfg *config.Config) error {
+func RunInit(cfg config.Config) error {
 	// Only run these if project version is v3.
 	if err := manifests.RunInit(cfg); err != nil {
 		return err
@@ -62,7 +60,7 @@ func RunInit(cfg *config.Config) error {
 }
 
 // RunCreateAPI runs the manifests SDK phase 2 plugin.
-func RunCreateAPI(cfg *config.Config, gvk config.GVK) error {
+func RunCreateAPI(cfg config.Config, gvk resource.GVK) error {
 	if !HasPluginConfig(cfg) {
 		return nil
 	}
diff --git a/internal/plugins/scorecard/init.go b/internal/plugins/scorecard/init.go
index 977bf8f6..d8cd46bf 100644
--- a/internal/plugins/scorecard/init.go
+++ b/internal/plugins/scorecard/init.go
@@ -20,8 +20,9 @@ import (
 	"os"
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/util/kustomize"
 	"github.com/operator-framework/operator-sdk/internal/scorecard"
@@ -37,9 +38,10 @@ var (
 )
 
 // RunInit scaffolds kustomize files for kustomizing a scorecard componentconfig.
-func RunInit(cfg *config.Config) error {
+func RunInit(cfg config.Config) error {
 	// Only run these if project version is v3.
-	if !cfg.IsV3() {
+	isV3 := cfg.GetVersion().Compare(cfgv3.Version) == 0
+	if !isV3 {
 		return nil
 	}
 
diff --git a/internal/plugins/scorecard/v2/plugin.go b/internal/plugins/scorecard/v2/plugin.go
index 80147698..fbce6c3e 100644
--- a/internal/plugins/scorecard/v2/plugin.go
+++ b/internal/plugins/scorecard/v2/plugin.go
@@ -17,11 +17,11 @@ package v2
 import (
 	"fmt"
 
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
+
 	"github.com/operator-framework/operator-sdk/internal/plugins"
 	"github.com/operator-framework/operator-sdk/internal/plugins/scorecard"
-
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
-	"sigs.k8s.io/kubebuilder/v2/pkg/plugin"
 )
 
 const (
@@ -37,7 +37,7 @@ var (
 type Config struct{}
 
 // RunInit scaffolds kustomize files for kustomizing a scorecard componentconfig.
-func RunInit(cfg *config.Config) error {
+func RunInit(cfg config.Config) error {
 
 	if err := scorecard.RunInit(cfg); err != nil {
 		return err
diff --git a/internal/util/projutil/project_util.go b/internal/util/projutil/project_util.go
index a0a500b5..c40ad137 100644
--- a/internal/util/projutil/project_util.go
+++ b/internal/util/projutil/project_util.go
@@ -22,7 +22,12 @@ import (
 	"strings"
 
 	log "github.com/sirupsen/logrus"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
+	"github.com/spf13/afero"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	_ "sigs.k8s.io/kubebuilder/v3/pkg/config/v2" // Register config/v2 for `config.New`
+	_ "sigs.k8s.io/kubebuilder/v3/pkg/config/v3" // Register config/v3 for `config.New`
+	cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
+	"sigs.k8s.io/yaml"
 )
 
 const (
@@ -78,17 +83,36 @@ func HasProjectFile() bool {
 	return true
 }
 
+type versionedConfig struct {
+	Version config.Version
+}
+
 // ReadConfig returns a configuration if a file containing one exists at the
 // default path (project root).
-func ReadConfig() (*config.Config, error) {
-	b, err := ioutil.ReadFile(configFile)
+func ReadConfig() (config.Config, error) {
+	// Read the file
+	in, err := afero.ReadFile(afero.NewOsFs(), configFile) //nolint:gosec
 	if err != nil {
 		return nil, err
 	}
-	c := &config.Config{}
-	if err = c.Unmarshal(b); err != nil {
+
+	// Check the file version
+	var versioned versionedConfig
+	if err := yaml.Unmarshal(in, &versioned); err != nil {
 		return nil, err
 	}
+
+	// Create the config object
+	c, err := config.New(versioned.Version)
+	if err != nil {
+		return nil, err
+	}
+
+	// Unmarshal the file content
+	if err := c.Unmarshal(in); err != nil {
+		return nil, err
+	}
+
 	return c, nil
 }
 
@@ -108,11 +132,12 @@ func PluginKeyToOperatorType(pluginKey string) OperatorType {
 
 // GetProjectLayout returns the `layout` field in PROJECT file that is v3.
 // If not, it will return "go" because that was the only project type supported for project versions < v3.
-func GetProjectLayout(cfg *config.Config) string {
-	if cfg == nil || !cfg.IsV3() || cfg.Layout == "" {
+func GetProjectLayout(cfg config.Config) string {
+	isV3 := cfg.GetVersion().Compare(cfgv3.Version) == 0
+	if cfg == nil || !isV3 || cfg.GetLayout() == "" {
 		return "go"
 	}
-	return cfg.Layout
+	return cfg.GetLayout()
 }
 
 var flagRe = regexp.MustCompile("(.* )?-v(.* )?")
diff --git a/website/content/en/docs/building-operators/ansible/migration.md b/website/content/en/docs/building-operators/ansible/migration.md
index 4ec8b191..b163e822 100644
--- a/website/content/en/docs/building-operators/ansible/migration.md
+++ b/website/content/en/docs/building-operators/ansible/migration.md
@@ -101,7 +101,7 @@ In our example, we will replace `# FIXME: Specify the role or playbook for this
   group: cache.example.com
   kind: Memcached
   role: memcached
-# +kubebuilder:scaffold:watch
+#+kubebuilder:scaffold:watch
 ```
 
 **NOTE**: Do not remove the `+kubebuilder:scaffold:watch` [marker][marker]. It allows the tool to update the watches file when new APIs are created.
diff --git a/website/content/en/docs/building-operators/golang/advanced-topics.md b/website/content/en/docs/building-operators/golang/advanced-topics.md
index 306b5857..2c34f841 100644
--- a/website/content/en/docs/building-operators/golang/advanced-topics.md
+++ b/website/content/en/docs/building-operators/golang/advanced-topics.md
@@ -45,7 +45,7 @@ func init() {
 
     // Setup Scheme for all resources
     utilruntime.Must(cachev1alpha1.AddToScheme(scheme))
-    // +kubebuilder:scaffold:scheme
+    //+kubebuilder:scaffold:scheme
 }
 ```
 
@@ -67,7 +67,7 @@ func init() {
     utilruntime.Must(clientgoscheme.AddToScheme(scheme))
 
     utilruntime.Must(routev1.AddToScheme(scheme))
-    // +kubebuilder:scaffold:scheme
+    //+kubebuilder:scaffold:scheme
 
     ...
 }
diff --git a/website/content/en/docs/building-operators/golang/crds-scope.md b/website/content/en/docs/building-operators/golang/crds-scope.md
index fd4a5308..b312609a 100644
--- a/website/content/en/docs/building-operators/golang/crds-scope.md
+++ b/website/content/en/docs/building-operators/golang/crds-scope.md
@@ -13,7 +13,7 @@ The CRD manifests are generated in `config/crd/bases`. For each CRD that needs t
 should specify `spec.scope: Cluster`.
 
 To ensure that the CRD is always generated with `scope: Cluster`, add the marker 
-`// +kubebuilder:resource:path=<resource>,scope=Cluster`, or if already present replace `scope={Namespaced -> Cluster}`, 
+`//+kubebuilder:resource:path=<resource>,scope=Cluster`, or if already present replace `scope={Namespaced -> Cluster}`, 
 above the CRD's Go type definition in `api/<version>/<kind>_types.go` or `apis/<group>/<version>/<kind>_types.go` 
 if you are using the [multigroup][multigroup-kubebuilder-doc] layout. Note that the `<resource>` 
 element must be the same lower-case plural value of the CRD's Kind, `spec.names.plural`.  
@@ -51,13 +51,13 @@ spec:
 ``` 
 
 - Update the `apis/<version>/<kind>_types.go` by adding the 
-marker `// +kubebuilder:resource:path=<resource>,scope=Cluster`
+marker `//+kubebuilder:resource:path=<resource>,scope=Cluster`
 
 * `api/v1alpha1/memcached_types.go`
 
 ```Go
 // Memcached is the Schema for the memcacheds API
-// +kubebuilder:resource:path=memcacheds,scope=Cluster
+//+kubebuilder:resource:path=memcacheds,scope=Cluster
 type Memcached struct {
   metav1.TypeMeta   `json:",inline"`
   metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/website/content/en/docs/building-operators/golang/migration.md b/website/content/en/docs/building-operators/golang/migration.md
index a1521759..ebaab0c0 100644
--- a/website/content/en/docs/building-operators/golang/migration.md
+++ b/website/content/en/docs/building-operators/golang/migration.md
@@ -116,13 +116,13 @@ type MemcachedStatus struct {
 	Nodes []string `json:"nodes"`
 }
 
-// +kubebuilder:object:root=true
-// +kubebuilder:subresource:status
+//+kubebuilder:object:root=true
+//+kubebuilder:subresource:status
 
 // Memcached is the Schema for the memcacheds API
 type Memcached struct {...}
 
-// +kubebuilder:object:root=true
+//+kubebuilder:object:root=true
 
 // MemcachedList contains a list of Memcached
 type MemcachedList struct {...}
@@ -154,10 +154,10 @@ The RBAC permissions are now configured via [RBAC markers][rbac_markers], which
 In the Memcached example, they look like the following:
 
 ```go
-// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
-// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list
+//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
+//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list
 ```
 
 To update `config/rbac/role.yaml` after changing the markers, run `make manifests`.  
diff --git a/website/content/en/docs/building-operators/golang/operator-scope.md b/website/content/en/docs/building-operators/golang/operator-scope.md
index 3a402e14..1fa77a99 100644
--- a/website/content/en/docs/building-operators/golang/operator-scope.md
+++ b/website/content/en/docs/building-operators/golang/operator-scope.md
@@ -130,8 +130,8 @@ are used to generate the operator's [RBAC ClusterRole][rbac-clusterrole] (e.g `c
 Update the [`RBAC markers`][rbac-markers] in `<kind>_controller.go` with `namespace=<namespace>` where the `Role` is to be applied, such as:
 
 ```go
-// +kubebuilder:rbac:groups=cache.example.com,namespace=memcached-operator-system,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=cache.example.com,namespace=memcached-operator-system,resources=memcacheds/status,verbs=get;update;patch
+//+kubebuilder:rbac:groups=cache.example.com,namespace=memcached-operator-system,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=cache.example.com,namespace=memcached-operator-system,resources=memcacheds/status,verbs=get;update;patch
 ```
 
 - Run `make manifests` to update `config/rbac/role.yaml`. In our example it would like:
diff --git a/website/content/en/docs/building-operators/golang/references/logging.md b/website/content/en/docs/building-operators/golang/references/logging.md
index 2daab30c..b82a01e2 100644
--- a/website/content/en/docs/building-operators/golang/references/logging.md
+++ b/website/content/en/docs/building-operators/golang/references/logging.md
@@ -145,7 +145,7 @@ Make sure to have your `run` target to take `ARGS` as shown below in `Makefile`.
 
 ```makefile
 # Run against the configured Kubernetes cluster in ~/.kube/config
-run: generate fmt vet manifests
+run: manifests generate fmt vet
 	go run ./main.go $(ARGS)
 ```
 
diff --git a/website/content/en/docs/building-operators/golang/references/markers.md b/website/content/en/docs/building-operators/golang/references/markers.md
index ac76d2e4..7e395e10 100644
--- a/website/content/en/docs/building-operators/golang/references/markers.md
+++ b/website/content/en/docs/building-operators/golang/references/markers.md
@@ -52,7 +52,7 @@ These examples assume `Memcached`, `MemcachedSpec`, and `MemcachedStatus` are th
 1. Set a `displayName` and `resources` for a `customresourcedefinitions` kind entry:
 
 	```go
-	// +operator-sdk:csv:customresourcedefinitions:displayName="Memcached App",resources={{Pod,v1,memcached-runner},{Deployment,v1,memcached-deployment}}
+	//+operator-sdk:csv:customresourcedefinitions:displayName="Memcached App",resources={{Pod,v1,memcached-runner},{Deployment,v1,memcached-deployment}}
 	type Memcached struct {
 		metav1.TypeMeta   `json:",inline"`
 		metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -67,7 +67,7 @@ These examples assume `Memcached`, `MemcachedSpec`, and `MemcachedStatus` are th
 	```go
 	type MemcachedSpec struct {
 		// Size is the size of the memcached deployment. <-- This will become Size's specDescriptors.description.
-		// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Number of pods",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:podCount","urn:alm:descriptor:io.kubernetes:custom"}
+		//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Number of pods",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:podCount","urn:alm:descriptor:io.kubernetes:custom"}
 		Size int32 `json:"size"` // <-- Size's specDescriptors.path is inferred from this JSON tag.
 	}
 	```
@@ -77,7 +77,7 @@ These examples assume `Memcached`, `MemcachedSpec`, and `MemcachedStatus` are th
 	```go
 	type MemcachedSpec struct {
 		// Size is the size of the memcached deployment.
-		// +operator-sdk:csv:customresourcedefinitions:type=spec
+		//+operator-sdk:csv:customresourcedefinitions:type=spec
 		Size int32 `json:"size"`
 	}
 	```
@@ -90,7 +90,7 @@ These examples assume `Memcached`, `MemcachedSpec`, and `MemcachedStatus` are th
 
 	```go
 	// Represents a cluster of Memcached apps
-	// +operator-sdk:csv:customresourcedefinitions:displayName="Memcached App",resources={{Pod,v1,memcached-runner},{Deployment,v1,memcached-deployment}}
+	//+operator-sdk:csv:customresourcedefinitions:displayName="Memcached App",resources={{Pod,v1,memcached-runner},{Deployment,v1,memcached-deployment}}
 	type Memcached struct {
 		metav1.TypeMeta   `json:",inline"`
 		metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -105,14 +105,14 @@ These examples assume `Memcached`, `MemcachedSpec`, and `MemcachedStatus` are th
 
 	type MemcachedStatus struct {
 		Pods 		 MemcachedPods `json:"podStatuses"`
-		// +operator-sdk:csv:customresourcedefinitions:type=status,displayName="Pod Count",xDescriptors="urn:alm:descriptor:com.tectonic.ui:podCount"
+		//+operator-sdk:csv:customresourcedefinitions:type=status,displayName="Pod Count",xDescriptors="urn:alm:descriptor:com.tectonic.ui:podCount"
 		PodCount int 					 `json:"podCount"`
 	}
 
 	type MemcachedPods struct {
 		// Size is the size of the memcached deployment.
-		// +operator-sdk:csv:customresourcedefinitions:type=spec
-		// +operator-sdk:csv:customresourcedefinitions.type=status
+		//+operator-sdk:csv:customresourcedefinitions:type=spec
+		//+operator-sdk:csv:customresourcedefinitions.type=status
 		Size int32 `json:"size"`
 	}
 	```
diff --git a/website/content/en/docs/building-operators/golang/tutorial.md b/website/content/en/docs/building-operators/golang/tutorial.md
index 14e13bec..65300627 100644
--- a/website/content/en/docs/building-operators/golang/tutorial.md
+++ b/website/content/en/docs/building-operators/golang/tutorial.md
@@ -93,7 +93,7 @@ Define the API for the Memcached Custom Resource(CR) by modifying the Go type de
 ```Go
 // MemcachedSpec defines the desired state of Memcached
 type MemcachedSpec struct {
-	// +kubebuilder:validation:Minimum=0
+	//+kubebuilder:validation:Minimum=0
 	// Size is the size of the memcached deployment
 	Size int32 `json:"size"`
 }
@@ -109,7 +109,7 @@ Add the `+kubebuilder:subresource:status` [marker][status_marker] to add a [stat
 
 ```Go
 // Memcached is the Schema for the memcacheds API
-// +kubebuilder:subresource:status
+//+kubebuilder:subresource:status
 type Memcached struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -240,11 +240,11 @@ For more details, check the Reconcile and its [Reconcile godoc][reconcile-godoc]
 The controller needs certain [RBAC][rbac-k8s-doc] permissions to interact with the resources it manages. These are specified via [RBAC markers][rbac_markers] like the following:
 
 ```Go
-// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
-// +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
-// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
+//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
+//+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
+//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
 
 func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
   ...
diff --git a/website/content/en/docs/building-operators/helm/migration.md b/website/content/en/docs/building-operators/helm/migration.md
index fdefacfa..5864f593 100644
--- a/website/content/en/docs/building-operators/helm/migration.md
+++ b/website/content/en/docs/building-operators/helm/migration.md
@@ -85,7 +85,7 @@ Check if you have custom options in the `watches.yaml` file of your existing pro
   version: v1alpha1
   kind: Nginx
   chart: helm-charts/nginx
-# +kubebuilder:scaffold:watch
+#+kubebuilder:scaffold:watch
 ```
 
 **NOTE**: Do not remove the `+kubebuilder:scaffold:watch` [marker][marker]. It allows the tool to update the watches file when new APIs are created.
diff --git a/website/content/en/docs/building-operators/helm/tutorial.md b/website/content/en/docs/building-operators/helm/tutorial.md
index 6b87dcfe..a6f916b8 100644
--- a/website/content/en/docs/building-operators/helm/tutorial.md
+++ b/website/content/en/docs/building-operators/helm/tutorial.md
@@ -106,7 +106,7 @@ in `watches.yaml` and executes Helm releases using the specified chart:
   version: v1alpha1
   kind: Nginx
   chart: helm-charts/nginx
-# +kubebuilder:scaffold:watch
+#+kubebuilder:scaffold:watch
 ```
 
 ### Reviewing the Nginx Helm Chart
diff --git a/website/content/en/docs/cli/operator-sdk.md b/website/content/en/docs/cli/operator-sdk.md
index 2a8e1d75..5f5e8bc5 100644
--- a/website/content/en/docs/cli/operator-sdk.md
+++ b/website/content/en/docs/cli/operator-sdk.md
@@ -3,30 +3,11 @@ title: "operator-sdk"
 ---
 ## operator-sdk
 
-Development kit for building Kubernetes extensions and tools.
 
-### Synopsis
-
-Development kit for building Kubernetes extensions and tools.
-
-Provides libraries and tools to create new projects, APIs and controllers.
-Includes tools for packaging artifacts into an installer container.
-
-Typical project lifecycle:
-
-- initialize a project:
-
-  operator-sdk init --domain example.com --license apache2 --owner "The Kubernetes authors"
 
-- create one or more a new resource APIs and add your code to them:
-
-  operator-sdk create api --group <group> --version <version> --kind <Kind>
-
-Create resource will prompt the user for if it should scaffold the Resource and / or Controller. To only
-scaffold a Controller for an existing Resource, select "n" for Resource. To only define
-the schema for a Resource without writing a Controller, select "n" for Controller.
+### Synopsis
 
-After the scaffold is written, api will run make on the project.
+CLI tool for building Kubernetes extensions and tools.
 
 
 ```
@@ -36,32 +17,38 @@ operator-sdk [flags]
 ### Examples
 
 ```
+The first step is to initialize your project:
+    operator-sdk init --project-version=<PROJECT VERSION> --plugins=<PLUGIN KEYS>
 
-  # Initialize your project
-  operator-sdk init --domain example.com --license apache2 --owner "The Kubernetes authors"
-
-  # Create a frigates API with Group: ship, Version: v1beta1 and Kind: Frigate
-  operator-sdk create api --group ship --version v1beta1 --kind Frigate
-
-  # Edit the API Scheme
-  nano api/v1beta1/frigate_types.go
+<PLUGIN KEYS> is a comma-separated list of plugin keys from the following table
+and <PROJECT VERSION> a supported project version for these plugins.
 
-  # Edit the Controller
-  nano controllers/frigate_controller.go
+                         Plugin keys | Supported project versions
+-------------------------------------+----------------------------
+ ansible.sdk.operatorframework.io/v1 |                          3
+                go.kubebuilder.io/v2 |                       2, 3
+                go.kubebuilder.io/v3 |                          3
+    helm.sdk.operatorframework.io/v1 |                          3
 
-  # Install CRDs into the Kubernetes cluster using kubectl apply
-  make install
+For more specific help for the init command of a certain plugins and project version
+configuration please run:
+    operator-sdk init --help --project-version=<PROJECT VERSION> --plugins=<PLUGIN KEYS>
 
-  # Regenerate code and run against the Kubernetes cluster configured by ~/.kube/config
-  make run
+Default project version: 3
+Default plugin keys: "go.kubebuilder.io/v3"
 
+After the project has been initialized, run
+    operator-sdk --help
+to obtain further info about available commands.
 ```
 
 ### Options
 
 ```
-  -h, --help      help for operator-sdk
-      --verbose   Enable verbose logging
+  -h, --help                     help for operator-sdk
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_bundle.md b/website/content/en/docs/cli/operator-sdk_bundle.md
index 5ca8d3b0..752caaf7 100644
--- a/website/content/en/docs/cli/operator-sdk_bundle.md
+++ b/website/content/en/docs/cli/operator-sdk_bundle.md
@@ -27,11 +27,13 @@ https://sdk.operatorframework.io/docs/olm-integration
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 * [operator-sdk bundle validate](../operator-sdk_bundle_validate)	 - Validate an operator bundle
 
diff --git a/website/content/en/docs/cli/operator-sdk_bundle_validate.md b/website/content/en/docs/cli/operator-sdk_bundle_validate.md
index 247debea..7f0e57b1 100644
--- a/website/content/en/docs/cli/operator-sdk_bundle_validate.md
+++ b/website/content/en/docs/cli/operator-sdk_bundle_validate.md
@@ -77,7 +77,9 @@ To validate a bundle with a superset of requirements for operatorhub.io specific
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_cleanup.md b/website/content/en/docs/cli/operator-sdk_cleanup.md
index dc8ac9eb..89b74ffe 100644
--- a/website/content/en/docs/cli/operator-sdk_cleanup.md
+++ b/website/content/en/docs/cli/operator-sdk_cleanup.md
@@ -25,10 +25,12 @@ operator-sdk cleanup <operatorPackageName> [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 
diff --git a/website/content/en/docs/cli/operator-sdk_completion.md b/website/content/en/docs/cli/operator-sdk_completion.md
index 1ced5f6c..fc7aaf3f 100644
--- a/website/content/en/docs/cli/operator-sdk_completion.md
+++ b/website/content/en/docs/cli/operator-sdk_completion.md
@@ -14,12 +14,14 @@ Generators for shell completions
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 * [operator-sdk completion bash](../operator-sdk_completion_bash)	 - Generate bash completions
 * [operator-sdk completion zsh](../operator-sdk_completion_zsh)	 - Generate zsh completions
 
diff --git a/website/content/en/docs/cli/operator-sdk_completion_bash.md b/website/content/en/docs/cli/operator-sdk_completion_bash.md
index 1aec4b8c..0e19d004 100644
--- a/website/content/en/docs/cli/operator-sdk_completion_bash.md
+++ b/website/content/en/docs/cli/operator-sdk_completion_bash.md
@@ -18,7 +18,9 @@ operator-sdk completion bash [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_completion_zsh.md b/website/content/en/docs/cli/operator-sdk_completion_zsh.md
index fc8b1802..d6a37a67 100644
--- a/website/content/en/docs/cli/operator-sdk_completion_zsh.md
+++ b/website/content/en/docs/cli/operator-sdk_completion_zsh.md
@@ -18,7 +18,9 @@ operator-sdk completion zsh [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_create.md b/website/content/en/docs/cli/operator-sdk_create.md
index 432a7899..46f97d0e 100644
--- a/website/content/en/docs/cli/operator-sdk_create.md
+++ b/website/content/en/docs/cli/operator-sdk_create.md
@@ -18,12 +18,14 @@ Scaffold a Kubernetes API or webhook.
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 * [operator-sdk create api](../operator-sdk_create_api)	 - Scaffold a Kubernetes API
 * [operator-sdk create webhook](../operator-sdk_create_webhook)	 - Scaffold a webhook for an API resource
 
diff --git a/website/content/en/docs/cli/operator-sdk_create_api.md b/website/content/en/docs/cli/operator-sdk_create_api.md
index 009e523f..54a99272 100644
--- a/website/content/en/docs/cli/operator-sdk_create_api.md
+++ b/website/content/en/docs/cli/operator-sdk_create_api.md
@@ -24,7 +24,9 @@ operator-sdk create api [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_create_webhook.md b/website/content/en/docs/cli/operator-sdk_create_webhook.md
index 60983e33..80d10749 100644
--- a/website/content/en/docs/cli/operator-sdk_create_webhook.md
+++ b/website/content/en/docs/cli/operator-sdk_create_webhook.md
@@ -24,7 +24,9 @@ operator-sdk create webhook [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_edit.md b/website/content/en/docs/cli/operator-sdk_edit.md
index 63c908c3..9d79d0b1 100644
--- a/website/content/en/docs/cli/operator-sdk_edit.md
+++ b/website/content/en/docs/cli/operator-sdk_edit.md
@@ -24,10 +24,12 @@ operator-sdk edit [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 
diff --git a/website/content/en/docs/cli/operator-sdk_generate.md b/website/content/en/docs/cli/operator-sdk_generate.md
index c54e6a1c..0c15a9c4 100644
--- a/website/content/en/docs/cli/operator-sdk_generate.md
+++ b/website/content/en/docs/cli/operator-sdk_generate.md
@@ -19,12 +19,14 @@ code or manifests.
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 * [operator-sdk generate bundle](../operator-sdk_generate_bundle)	 - Generates bundle data for the operator
 * [operator-sdk generate kustomize](../operator-sdk_generate_kustomize)	 - Contains subcommands that generate operator-framework kustomize data for the operator
 * [operator-sdk generate packagemanifests](../operator-sdk_generate_packagemanifests)	 - Generates package manifests data for the operator
diff --git a/website/content/en/docs/cli/operator-sdk_generate_bundle.md b/website/content/en/docs/cli/operator-sdk_generate_bundle.md
index 8a0fe806..02d4215b 100644
--- a/website/content/en/docs/cli/operator-sdk_generate_bundle.md
+++ b/website/content/en/docs/cli/operator-sdk_generate_bundle.md
@@ -99,7 +99,9 @@ operator-sdk generate bundle [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_generate_kustomize.md b/website/content/en/docs/cli/operator-sdk_generate_kustomize.md
index 7578b8a9..9f307f46 100644
--- a/website/content/en/docs/cli/operator-sdk_generate_kustomize.md
+++ b/website/content/en/docs/cli/operator-sdk_generate_kustomize.md
@@ -14,7 +14,9 @@ Contains subcommands that generate operator-framework kustomize data for the ope
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_generate_kustomize_manifests.md b/website/content/en/docs/cli/operator-sdk_generate_kustomize_manifests.md
index 2f64a236..a9384f1e 100644
--- a/website/content/en/docs/cli/operator-sdk_generate_kustomize_manifests.md
+++ b/website/content/en/docs/cli/operator-sdk_generate_kustomize_manifests.md
@@ -59,7 +59,9 @@ operator-sdk generate kustomize manifests [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_generate_packagemanifests.md b/website/content/en/docs/cli/operator-sdk_generate_packagemanifests.md
index 47b04ab3..5db37486 100644
--- a/website/content/en/docs/cli/operator-sdk_generate_packagemanifests.md
+++ b/website/content/en/docs/cli/operator-sdk_generate_packagemanifests.md
@@ -80,7 +80,9 @@ operator-sdk generate packagemanifests [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_init.md b/website/content/en/docs/cli/operator-sdk_init.md
index 7e1d0d25..89727024 100644
--- a/website/content/en/docs/cli/operator-sdk_init.md
+++ b/website/content/en/docs/cli/operator-sdk_init.md
@@ -19,8 +19,6 @@ Writes the following files:
 - a Patch file for enabling prometheus metrics
 - a main.go to run
 
-project will prompt the user to run 'dep ensure' after writing the project files.
-
 
 ```
 operator-sdk init [flags]
@@ -37,26 +35,26 @@ operator-sdk init [flags]
 ### Options
 
 ```
-      --component-config         create a versioned ComponentConfig file, may be 'true' or 'false'
-      --domain string            domain for groups (default "my.domain")
-      --fetch-deps               ensure dependencies are downloaded (default true)
-  -h, --help                     help for init
-      --license string           license to use to boilerplate, may be one of 'apache2', 'none' (default "apache2")
-      --owner string             owner to add to the copyright
-      --plugins strings          Name and optionally version of the plugin to initialize the project with. Available plugins: ("ansible.sdk.operatorframework.io/v1", "go.kubebuilder.io/v2", "go.kubebuilder.io/v3", "helm.sdk.operatorframework.io/v1")
-      --project-name string      name of this project
-      --project-version string   project version, possible values: ("2", "3-alpha") (default "3-alpha")
-      --repo string              name to use for go module (e.g., github.com/user/repo), defaults to the go package of the current working directory.
-      --skip-go-version-check    if specified, skip checking the Go version
+      --component-config        create a versioned ComponentConfig file, may be 'true' or 'false'
+      --domain string           domain for groups (default "my.domain")
+      --fetch-deps              ensure dependencies are downloaded (default true)
+  -h, --help                    help for init
+      --license string          license to use to boilerplate, may be one of 'apache2', 'none' (default "apache2")
+      --owner string            owner to add to the copyright
+      --project-name string     name of this project
+      --repo string             name to use for go module (e.g., github.com/user/repo), defaults to the go package of the current working directory.
+      --skip-go-version-check   if specified, skip checking the Go version
 ```
 
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 
diff --git a/website/content/en/docs/cli/operator-sdk_olm.md b/website/content/en/docs/cli/operator-sdk_olm.md
index 7f54ef16..5447cf0f 100644
--- a/website/content/en/docs/cli/operator-sdk_olm.md
+++ b/website/content/en/docs/cli/operator-sdk_olm.md
@@ -14,12 +14,14 @@ Manage the Operator Lifecycle Manager installation in your cluster
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 * [operator-sdk olm install](../operator-sdk_olm_install)	 - Install Operator Lifecycle Manager in your cluster
 * [operator-sdk olm status](../operator-sdk_olm_status)	 - Get the status of the Operator Lifecycle Manager installation in your cluster
 * [operator-sdk olm uninstall](../operator-sdk_olm_uninstall)	 - Uninstall Operator Lifecycle Manager from your cluster
diff --git a/website/content/en/docs/cli/operator-sdk_olm_install.md b/website/content/en/docs/cli/operator-sdk_olm_install.md
index 9772858c..f7e5c6b8 100644
--- a/website/content/en/docs/cli/operator-sdk_olm_install.md
+++ b/website/content/en/docs/cli/operator-sdk_olm_install.md
@@ -20,7 +20,9 @@ operator-sdk olm install [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_olm_status.md b/website/content/en/docs/cli/operator-sdk_olm_status.md
index c377a18c..a453503d 100644
--- a/website/content/en/docs/cli/operator-sdk_olm_status.md
+++ b/website/content/en/docs/cli/operator-sdk_olm_status.md
@@ -21,7 +21,9 @@ operator-sdk olm status [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_olm_uninstall.md b/website/content/en/docs/cli/operator-sdk_olm_uninstall.md
index 1d9ff59b..0646ec9a 100644
--- a/website/content/en/docs/cli/operator-sdk_olm_uninstall.md
+++ b/website/content/en/docs/cli/operator-sdk_olm_uninstall.md
@@ -21,7 +21,9 @@ operator-sdk olm uninstall [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_run.md b/website/content/en/docs/cli/operator-sdk_run.md
index c6963489..c53d925d 100644
--- a/website/content/en/docs/cli/operator-sdk_run.md
+++ b/website/content/en/docs/cli/operator-sdk_run.md
@@ -18,12 +18,14 @@ This command has subcommands that will deploy your Operator with OLM.
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 * [operator-sdk run bundle](../operator-sdk_run_bundle)	 - Deploy an Operator in the bundle format with OLM
 * [operator-sdk run bundle-upgrade](../operator-sdk_run_bundle-upgrade)	 - Upgrade an Operator previously installed in the bundle format with OLM
 * [operator-sdk run packagemanifests](../operator-sdk_run_packagemanifests)	 - Deploy an Operator in the package manifests format with OLM
diff --git a/website/content/en/docs/cli/operator-sdk_run_bundle-upgrade.md b/website/content/en/docs/cli/operator-sdk_run_bundle-upgrade.md
index 60a7b527..09666185 100644
--- a/website/content/en/docs/cli/operator-sdk_run_bundle-upgrade.md
+++ b/website/content/en/docs/cli/operator-sdk_run_bundle-upgrade.md
@@ -21,7 +21,9 @@ operator-sdk run bundle-upgrade <bundle-image> [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_run_bundle.md b/website/content/en/docs/cli/operator-sdk_run_bundle.md
index 539dd2da..960a9661 100644
--- a/website/content/en/docs/cli/operator-sdk_run_bundle.md
+++ b/website/content/en/docs/cli/operator-sdk_run_bundle.md
@@ -23,7 +23,9 @@ operator-sdk run bundle <bundle-image> [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_run_packagemanifests.md b/website/content/en/docs/cli/operator-sdk_run_packagemanifests.md
index c21257dc..72d08510 100644
--- a/website/content/en/docs/cli/operator-sdk_run_packagemanifests.md
+++ b/website/content/en/docs/cli/operator-sdk_run_packagemanifests.md
@@ -29,7 +29,9 @@ operator-sdk run packagemanifests [packagemanifests-root-dir] [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
diff --git a/website/content/en/docs/cli/operator-sdk_scorecard.md b/website/content/en/docs/cli/operator-sdk_scorecard.md
index 429d3852..b2e23f5d 100644
--- a/website/content/en/docs/cli/operator-sdk_scorecard.md
+++ b/website/content/en/docs/cli/operator-sdk_scorecard.md
@@ -33,10 +33,12 @@ operator-sdk scorecard [flags]
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 
diff --git a/website/content/en/docs/cli/operator-sdk_version.md b/website/content/en/docs/cli/operator-sdk_version.md
index 1ba4c720..f2d22ad1 100644
--- a/website/content/en/docs/cli/operator-sdk_version.md
+++ b/website/content/en/docs/cli/operator-sdk_version.md
@@ -28,10 +28,12 @@ operator-sdk version
 ### Options inherited from parent commands
 
 ```
-      --verbose   Enable verbose logging
+      --plugins strings          plugin keys of the plugin to initialize the project with
+      --project-version string   project version
+      --verbose                  Enable verbose logging
 ```
 
 ### SEE ALSO
 
-* [operator-sdk](../operator-sdk)	 - Development kit for building Kubernetes extensions and tools.
+* [operator-sdk](../operator-sdk)	 - 
 
diff --git a/website/content/en/docs/faqs/_index.md b/website/content/en/docs/faqs/_index.md
index b1fc6a80..58ec0579 100644
--- a/website/content/en/docs/faqs/_index.md
+++ b/website/content/en/docs/faqs/_index.md
@@ -69,7 +69,7 @@ E0320 15:42:17.676888       1 reflector.go:280] pkg/mod/k8s.io/client-go@v0.0.0-
 Using controller-runtime's split client means that read operations (gets and lists) are read from a cache, and write operations are written directly to the API server. To populate the cache for reads, controller-runtime initiates a `list` and then a `watch` even when your operator is only attempting to `get` a single resource. The above scenario occurs when the operator does not have an [RBAC][rbac] permission to `watch` the resource. The solution is to add an RBAC directive to generate a `config/rbac/role.yaml` with `watch` privileges:
 
 ```go
-// +kubebuilder:rbac:groups=some.group.com,resources=myresources,verbs=watch
+//+kubebuilder:rbac:groups=some.group.com,resources=myresources,verbs=watch
 ```
 
 Alternatively, if the resource you're attempting to cannot be watched (like `v1.ImageStreamTag` above), you can specify that objects of this type should not be cached by adding the following to `main.go`:
@@ -102,7 +102,7 @@ func main() {
 Then in your controller file, add an RBAC directive to generate a `config/rbac/role.yaml` with `get` privileges:
 
 ```go
-// +kubebuilder:rbac:groups=image.openshift.io,resources=imagestreamtags,verbs=get
+//+kubebuilder:rbac:groups=image.openshift.io,resources=imagestreamtags,verbs=get
 ```
 
 Now run `make manifests` to update your `role.yaml`.
@@ -116,7 +116,7 @@ For Helm and Ansible operators, this permission is configured by default. Howeve
 by adding an RBAC directive to generate a `config/rbac/role.yaml` with `update` privileges on your CR's finalizers:
 
 ```go
-// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update
+//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update
 ```
 
 Now run `make manifests` to update your `role.yaml`.
diff --git a/website/content/en/docs/olm-integration/quickstart-bundle.md b/website/content/en/docs/olm-integration/quickstart-bundle.md
index b7754411..d0d37046 100644
--- a/website/content/en/docs/olm-integration/quickstart-bundle.md
+++ b/website/content/en/docs/olm-integration/quickstart-bundle.md
@@ -16,7 +16,7 @@ explanations of certain steps for brevity. The following documents contain more
 If you are working with package manifests, see the [package manifests quickstart][quickstart-package-manifests]
 once you have completed the *Setup* section below.
 
-**Important:** this guide assumes your project was scaffolded with `operator-sdk init --project-version=3-alpha`.
+**Important:** this guide assumes your project was scaffolded with `operator-sdk init --project-version=3`.
 These features are unavailable to projects of version `2` or less; this information can be found by inspecting
 your `PROJECT` file's `version` value.
 
diff --git a/website/content/en/docs/olm-integration/quickstart-package-manifests.md b/website/content/en/docs/olm-integration/quickstart-package-manifests.md
index 32f4ac34..928484af 100644
--- a/website/content/en/docs/olm-integration/quickstart-package-manifests.md
+++ b/website/content/en/docs/olm-integration/quickstart-package-manifests.md
@@ -7,7 +7,7 @@ weight: 2
 This guide assumes you have followed the introduction and *Setup* section of the [bundle quickstart][quickstart-bundle],
 and have added the `packagemanifests` target to your `Makefile` as described [here][doc-packagemanifests-makefile].
 
-**Important:** this guide assumes your project was scaffolded with `operator-sdk init --project-version=3-alpha`.
+**Important:** this guide assumes your project was scaffolded with `operator-sdk init --project-version=3`.
 These features are unavailable to projects of version `2` or less; this information can be found by inspecting
 your `PROJECT` file's `version` value.
 
diff --git a/website/content/en/docs/upgrading-sdk-version/v1.0.0.md b/website/content/en/docs/upgrading-sdk-version/v1.0.0.md
index bf1be0c1..c23c1c57 100644
--- a/website/content/en/docs/upgrading-sdk-version/v1.0.0.md
+++ b/website/content/en/docs/upgrading-sdk-version/v1.0.0.md
@@ -97,7 +97,7 @@ Add the `+kubebuilder:scaffold:manifestskustomizesamples` to your `config/sample
 ```yaml
 resources:
 - cache_v1alpha1_memcached.yaml
-# +kubebuilder:scaffold:manifestskustomizesamples
+#+kubebuilder:scaffold:manifestskustomizesamples
 ```
 
 _See [#3645](https://github.com/operator-framework/operator-sdk/pull/3645) for more details._
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 3460d8f8..dbba1715 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
@@ -235,23 +235,23 @@ Upon updating the project to `v0.8.2` the following breaking changes apply:
   ```Go
   // MemcachedSpec defines the desired state of Memcached
   type MemcachedSpec struct {
-    // +kubebuilder:validation:Maximum=5
-    // +kubebuilder:validation:Minimum=1
+    //+kubebuilder:validation:Maximum=5
+    //+kubebuilder:validation:Minimum=1
     Size int32 `json:"size"`
   }
 
   // MemcachedStatus defines the observed state of Memcached
   type MemcachedStatus struct {
-    // +kubebuilder:validation:MaxItems=5
-    // +kubebuilder:validation:MinItems=1
+    //+kubebuilder:validation:MaxItems=5
+    //+kubebuilder:validation:MinItems=1
     Nodes []string `json:"nodes"`
   }
 
   // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
   // Memcached is the Schema for the memcacheds API
-  // +kubebuilder:subresource:status
-  // +kubebuilder:resource:shortName="mc"
+  //+kubebuilder:subresource:status
+  //+kubebuilder:resource:shortName="mc"
   // +genclient:nonNamespaced
   type Memcached struct {
     metav1.TypeMeta   `json:",inline"`


Test patch:
diff --git a/hack/generate/samples/internal/ansible/testdata/watches.yaml b/hack/generate/samples/internal/ansible/testdata/watches.yaml
index 2f9392da..5969e711 100644
--- a/hack/generate/samples/internal/ansible/testdata/watches.yaml
+++ b/hack/generate/samples/internal/ansible/testdata/watches.yaml
@@ -60,4 +60,4 @@
   playbook: playbooks/reconciliationtest.yml
   vars:
     meta: '{{ ansible_operator_meta }}'
-# +kubebuilder:scaffold:watch
+#+kubebuilder:scaffold:watch
diff --git a/internal/cmd/operator-sdk/generate/packagemanifests/packagemanifests_test.go b/internal/cmd/operator-sdk/generate/packagemanifests/packagemanifests_test.go
index 86e6a077..78bab8d9 100644
--- a/internal/cmd/operator-sdk/generate/packagemanifests/packagemanifests_test.go
+++ b/internal/cmd/operator-sdk/generate/packagemanifests/packagemanifests_test.go
@@ -228,7 +228,7 @@ var _ = Describe("Running a generate packagemanifests command", func() {
 			It("reads the data from the project file", func() {
 				err := c.setDefaults()
 				Expect(err).To(HaveOccurred())
-				Expect(err.Error()).To(ContainSubstring("error unmarshalling project configuration"))
+				Expect(err.Error()).To(ContainSubstring("did not find expected key"))
 			})
 		})
 	})
diff --git a/internal/generate/clusterserviceversion/bases/definitions/crd_test.go b/internal/generate/clusterserviceversion/bases/definitions/crd_test.go
index 193b48eb..01bad43f 100644
--- a/internal/generate/clusterserviceversion/bases/definitions/crd_test.go
+++ b/internal/generate/clusterserviceversion/bases/definitions/crd_test.go
@@ -27,7 +27,7 @@ import (
 	"sigs.k8s.io/controller-tools/pkg/crd"
 	"sigs.k8s.io/controller-tools/pkg/loader"
 	"sigs.k8s.io/controller-tools/pkg/markers"
-	"sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	"sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
 )
 
 var _ = Describe("getTypedDescriptors", func() {
diff --git a/internal/generate/testdata/go/api/v1alpha1/memcached_types.go b/internal/generate/testdata/go/api/v1alpha1/memcached_types.go
index d6bcb6ce..f79eb9dd 100644
--- a/internal/generate/testdata/go/api/v1alpha1/memcached_types.go
+++ b/internal/generate/testdata/go/api/v1alpha1/memcached_types.go
@@ -23,11 +23,11 @@ import (
 // MemcachedSpec defines the desired state of Memcached
 type MemcachedSpec struct {
 	// Size is the size of the memcached deployment
-	// +operator-sdk:csv:customresourcedefinitions:type=spec
+	//+operator-sdk:csv:customresourcedefinitions:type=spec
 	Size int32 `json:"size"`
 
 	// List of Providers
-	// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Providers"
+	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Providers"
 	Providers []Provider `json:"providers,omitempty"`
 
 	// A useful shared type.
@@ -37,47 +37,47 @@ type MemcachedSpec struct {
 // Provider represents the container for a single provider
 type Provider struct {
 	// Foo represents the Foo provider
-	// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Foo Provider"
+	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Foo Provider"
 	Foo *FooProvider `json:"foo,omitempty"`
 }
 
 // FooProvider represents integration with Foo
 type FooProvider struct {
 	// CredentialsSecret is a reference to a secret containing authentication details for the Foo server
-	// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Secret Containing the Credentials",xDescriptors="urn:alm:descriptor:io.kubernetes:Secret"
-	// +kubebuilder:validation:Required
+	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Secret Containing the Credentials",xDescriptors="urn:alm:descriptor:io.kubernetes:Secret"
+	//+kubebuilder:validation:Required
 	CredentialsSecret *SecretRef `json:"credentialsSecret"`
 }
 
 // SecretRef represents a reference to an item within a Secret
 type SecretRef struct {
 	// Name represents the name of the secret
-	// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Name of the secret",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:advanced","urn:alm:descriptor:com.tectonic.ui:text"}
+	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Name of the secret",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:advanced","urn:alm:descriptor:com.tectonic.ui:text"}
 	Name string `json:"name"`
 
 	// Namespace represents the namespace containing the secret
-	// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Namespace containing the secret",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:advanced","urn:alm:descriptor:com.tectonic.ui:text"}
+	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Namespace containing the secret",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:advanced","urn:alm:descriptor:com.tectonic.ui:text"}
 	Namespace string `json:"namespace"`
 
 	// Key represents the specific key to reference from the secret
-	// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Key within the secret",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:advanced","urn:alm:descriptor:com.tectonic.ui:text"}
+	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Key within the secret",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:advanced","urn:alm:descriptor:com.tectonic.ui:text"}
 	Key string `json:"key,omitempty"`
 }
 
 // MemcachedStatus defines the observed state of Memcached
 type MemcachedStatus struct {
 	// Nodes are the names of the memcached pods
-	// +operator-sdk:csv:customresourcedefinitions:type=status
+	//+operator-sdk:csv:customresourcedefinitions:type=status
 	Nodes []string `json:"nodes"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Memcached is the Schema for the memcacheds API
-// +kubebuilder:subresource:status
-// +kubebuilder:resource:path=memcacheds,scope=Namespaced
-// +kubebuilder:storageversion
-// +operator-sdk:csv:customresourcedefinitions:displayName="Memcached App Display Name"
+//+kubebuilder:subresource:status
+//+kubebuilder:resource:path=memcacheds,scope=Namespaced
+//+kubebuilder:storageversion
+//+operator-sdk:csv:customresourcedefinitions:displayName="Memcached App Display Name"
 type Memcached struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/internal/generate/testdata/go/api/v1alpha2/dummy_types.go b/internal/generate/testdata/go/api/v1alpha2/dummy_types.go
index 60a9c5ac..f5696a55 100644
--- a/internal/generate/testdata/go/api/v1alpha2/dummy_types.go
+++ b/internal/generate/testdata/go/api/v1alpha2/dummy_types.go
@@ -26,8 +26,8 @@ import (
 // +k8s:openapi-gen=false
 type NoKindSpec struct {
 	// Not included in anything, no kind type
-	// +operator-sdk:csv:customresourcedefinitions:type=spec
-	// +operator-sdk:csv:customresourcedefinitions:type=status
+	//+operator-sdk:csv:customresourcedefinitions:type=spec
+	//+operator-sdk:csv:customresourcedefinitions:type=status
 	Size int32 `json:"size"`
 	// Not included in anything, no kind type
 	Boss Hog `json:"hog"`
@@ -37,8 +37,8 @@ type NoKindSpec struct {
 // +k8s:openapi-gen=false
 type NoKindStatus struct {
 	// Not included in anything, no kind type
-	// +operator-sdk:csv:customresourcedefinitions:type=spec
-	// +operator-sdk:csv:customresourcedefinitions:type=status
+	//+operator-sdk:csv:customresourcedefinitions:type=spec
+	//+operator-sdk:csv:customresourcedefinitions:type=status
 	Nodes []string `json:"nodes"`
 }
 
@@ -46,10 +46,10 @@ type NoKindStatus struct {
 // +k8s:openapi-gen=false
 type DummySpec struct {
 	// Should be in spec
-	// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="dummy-size",xDescriptors="urn:alm:descriptor:com.tectonic.ui:podCount"
+	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="dummy-size",xDescriptors="urn:alm:descriptor:com.tectonic.ui:podCount"
 	Size int32 `json:"size"`
 	// Should be in spec, but should not have array index in path
-	// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Wheels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
+	//+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Wheels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
 	Wheels []Wheel `json:"wheels"`
 	// A useful shared type.
 	Useful foo.UsefulType `json:"useful"`
@@ -61,8 +61,8 @@ type DummySpec struct {
 // +k8s:openapi-gen=false
 type DummyStatus struct {
 	// Should be in status but not spec, since DummyStatus isn't in DummySpec
-	// +operator-sdk:csv:customresourcedefinitions:type=spec
-	// +operator-sdk:csv:customresourcedefinitions:type=status
+	//+operator-sdk:csv:customresourcedefinitions:type=spec
+	//+operator-sdk:csv:customresourcedefinitions:type=status
 	Nodes []string `json:"nodes"`
 	// Not included in status but children should be
 	Boss Hog `json:"hog"`
@@ -72,18 +72,18 @@ type DummyStatus struct {
 // +k8s:openapi-gen=false
 type OtherDummyStatus struct {
 	// Should be in status but not spec, since this isn't a spec type
-	// +operator-sdk:csv:customresourcedefinitions:type=spec
-	// +operator-sdk:csv:customresourcedefinitions:type=status
+	//+operator-sdk:csv:customresourcedefinitions:type=spec
+	//+operator-sdk:csv:customresourcedefinitions:type=status
 	Nothing string `json:"nothing"`
 }
 
 // Dummy is the Schema for the dummy API
 // +k8s:deepcopy-gen=false
 // +k8s:openapi-gen=false
-// +kubebuilder:subresource:status
-// +kubebuilder:resource:path=dummys,scope=Namespaced
-// +operator-sdk:csv:customresourcedefinitions:displayName="Dummy App"
-// +operator-sdk:csv:customresourcedefinitions:resources={{Deployment,v1,"dummy-deployment"},{ReplicaSet,v1beta2,"dummy-replicaset"},{Pod,v1,"dummy-pod"}}
+//+kubebuilder:subresource:status
+//+kubebuilder:resource:path=dummys,scope=Namespaced
+//+operator-sdk:csv:customresourcedefinitions:displayName="Dummy App"
+//+operator-sdk:csv:customresourcedefinitions:resources={{Deployment,v1,"dummy-deployment"},{ReplicaSet,v1beta2,"dummy-replicaset"},{Pod,v1,"dummy-pod"}}
 type Dummy struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -95,8 +95,8 @@ type Dummy struct {
 // OtherDummy is the Schema for the other dummy API
 // +k8s:deepcopy-gen=false
 // +k8s:openapi-gen=false
-// +operator-sdk:csv:customresourcedefinitions:displayName="Other Dummy App"
-// +operator-sdk:csv:customresourcedefinitions:resources={{Service,v1,"other-dummy-service"},{Pod,v1,"other-dummy-pod"}}
+//+operator-sdk:csv:customresourcedefinitions:displayName="Other Dummy App"
+//+operator-sdk:csv:customresourcedefinitions:resources={{Service,v1,"other-dummy-service"},{Pod,v1,"other-dummy-pod"}}
 type OtherDummy struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/internal/generate/testdata/go/api/v1alpha2/memcached_types.go b/internal/generate/testdata/go/api/v1alpha2/memcached_types.go
index c26a3cb3..21b27701 100644
--- a/internal/generate/testdata/go/api/v1alpha2/memcached_types.go
+++ b/internal/generate/testdata/go/api/v1alpha2/memcached_types.go
@@ -22,24 +22,24 @@ import (
 // MemcachedSpec defines the desired state of Memcached
 type MemcachedSpec struct {
 	// Size is the size of the memcached deployment
-	// +operator-sdk:csv:customresourcedefinitions:type=spec
+	//+operator-sdk:csv:customresourcedefinitions:type=spec
 	Size int32 `json:"size"`
 }
 
 // MemcachedStatus defines the observed state of Memcached
 type MemcachedStatus struct {
 	// Nodes are the names of the memcached pods
-	// +operator-sdk:csv:customresourcedefinitions:type=status
+	//+operator-sdk:csv:customresourcedefinitions:type=status
 	Nodes []string `json:"nodes"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Memcached is the Schema for the memcacheds API
-// +kubebuilder:subresource:status
-// +kubebuilder:resource:path=memcacheds,scope=Namespaced
-// +kubebuilder:storageversion
-// +operator-sdk:csv:customresourcedefinitions:displayName="Memcached App"
+//+kubebuilder:subresource:status
+//+kubebuilder:resource:path=memcacheds,scope=Namespaced
+//+kubebuilder:storageversion
+//+operator-sdk:csv:customresourcedefinitions:displayName="Memcached App"
 type Memcached struct {
 	foo.TypeMeta   `json:",inline"`
 	foo.ObjectMeta `json:"metadata,omitempty"`
diff --git a/internal/generate/testdata/go/api/v1alpha2/memcachedrs_types.go b/internal/generate/testdata/go/api/v1alpha2/memcachedrs_types.go
index 196ff59c..11624c72 100644
--- a/internal/generate/testdata/go/api/v1alpha2/memcachedrs_types.go
+++ b/internal/generate/testdata/go/api/v1alpha2/memcachedrs_types.go
@@ -20,23 +20,23 @@ import (
 
 // MemcachedRSSpec defines the desired state of MemcachedRS
 type MemcachedRSSpec struct {
-	// +operator-sdk:csv:customresourcedefinitions:type=spec
+	//+operator-sdk:csv:customresourcedefinitions:type=spec
 	NumNodes int32 `json:"numNodes"`
 }
 
 // MemcachedRSStatus defines the observed state of MemcachedRS
 type MemcachedRSStatus struct {
-	// +operator-sdk:csv:customresourcedefinitions:type=status
+	//+operator-sdk:csv:customresourcedefinitions:type=status
 	NodeList []string `json:"nodeList"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // MemcachedRS is the Schema for the memcachedrs API
-// +kubebuilder:subresource:status
-// +kubebuilder:resource:path=memcachedrs,scope=Namespaced
-// +kubebuilder:storageversion
-// +operator-sdk:csv:customresourcedefinitions:displayName="MemcachedRS App"
+//+kubebuilder:subresource:status
+//+kubebuilder:resource:path=memcachedrs,scope=Namespaced
+//+kubebuilder:storageversion
+//+operator-sdk:csv:customresourcedefinitions:displayName="MemcachedRS App"
 type MemcachedRS struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/internal/kubebuilder/machinery/scaffold_test.go b/internal/kubebuilder/machinery/scaffold_test.go
index e553f9ab..d33f36b1 100644
--- a/internal/kubebuilder/machinery/scaffold_test.go
+++ b/internal/kubebuilder/machinery/scaffold_test.go
@@ -21,8 +21,8 @@ import (
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/ginkgo/extensions/table"
 	. "github.com/onsi/gomega"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 
 	"github.com/operator-framework/operator-sdk/internal/kubebuilder/filesystem"
 )
@@ -166,12 +166,12 @@ var _ = Describe("Scaffold", func() {
 			},
 			Entry("should insert lines for go files",
 				`
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `,
 				`
 1
 2
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `,
 				fakeInserter{codeFragments: file.CodeFragmentsMap{
 					file.NewMarkerFor("file.go", "-"): {"1\n", "2\n"}},
@@ -179,12 +179,12 @@ var _ = Describe("Scaffold", func() {
 			),
 			Entry("should insert lines for yaml files",
 				`
-# +kubebuilder:scaffold:-
+#+kubebuilder:scaffold:-
 `,
 				`
 1
 2
-# +kubebuilder:scaffold:-
+#+kubebuilder:scaffold:-
 `,
 				fakeInserter{codeFragments: file.CodeFragmentsMap{
 					file.NewMarkerFor("file.yaml", "-"): {"1\n", "2\n"}},
@@ -195,10 +195,10 @@ var _ = Describe("Scaffold", func() {
 				`
 1
 2
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `,
 				fakeTemplate{fakeBuilder: fakeBuilder{ifExistsAction: file.Overwrite}, body: `
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `},
 				fakeInserter{codeFragments: file.CodeFragmentsMap{
 					file.NewMarkerFor("file.go", "-"): {"1\n", "2\n"}},
@@ -209,10 +209,10 @@ var _ = Describe("Scaffold", func() {
 				`
 1
 2
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `,
 				fakeTemplate{fakeBuilder: fakeBuilder{ifExistsAction: file.Overwrite}, body: `
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `},
 				fakeInserter{codeFragments: file.CodeFragmentsMap{
 					file.NewMarkerFor("file.go", "-"): {"1\n", "2\n"}},
@@ -220,12 +220,12 @@ var _ = Describe("Scaffold", func() {
 			),
 			Entry("should use files over optional models",
 				`
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `,
 				`
 1
 2
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `,
 				fakeTemplate{body: fileContent},
 				fakeInserter{
@@ -236,14 +236,14 @@ var _ = Describe("Scaffold", func() {
 			),
 			Entry("should filter invalid markers",
 				`
-// +kubebuilder:scaffold:-
-// +kubebuilder:scaffold:*
+//+kubebuilder:scaffold:-
+//+kubebuilder:scaffold:*
 `,
 				`
 1
 2
-// +kubebuilder:scaffold:-
-// +kubebuilder:scaffold:*
+//+kubebuilder:scaffold:-
+//+kubebuilder:scaffold:*
 `,
 				fakeInserter{
 					markers: []file.Marker{file.NewMarkerFor("file.go", "-")},
@@ -256,18 +256,18 @@ var _ = Describe("Scaffold", func() {
 			Entry("should filter already existing one-line code fragments",
 				`
 1
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 3
 4
-// +kubebuilder:scaffold:*
+//+kubebuilder:scaffold:*
 `,
 				`
 1
 2
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 3
 4
-// +kubebuilder:scaffold:*
+//+kubebuilder:scaffold:*
 `,
 				fakeInserter{
 					codeFragments: file.CodeFragmentsMap{
@@ -279,10 +279,10 @@ var _ = Describe("Scaffold", func() {
 			Entry("should not insert anything if no code fragment",
 				"", // input is provided through a template as mock fs doesn't copy it to the output buffer if no-op
 				`
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `,
 				fakeTemplate{body: `
-// +kubebuilder:scaffold:-
+//+kubebuilder:scaffold:-
 `},
 				fakeInserter{
 					codeFragments: file.CodeFragmentsMap{
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/debug_logs_patch.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/debug_logs_patch.go
index f4622e5d..a93be5a0 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/debug_logs_patch.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/debug_logs_patch.go
@@ -17,7 +17,7 @@ package testing
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &DebugLogsPatch{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/kustomization.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/kustomization.go
index 37dc7ca4..c4894b2c 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/kustomization.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/kustomization.go
@@ -17,7 +17,7 @@ package testing
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &Kustomization{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/manager_image.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/manager_image.go
index d25d8c77..2c6d2e50 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/manager_image.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/manager_image.go
@@ -17,7 +17,7 @@ package testing
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &ManagerImage{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/always_pull_patch.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/always_pull_patch.go
index c0b5ce5a..f592b42e 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/always_pull_patch.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/always_pull_patch.go
@@ -17,7 +17,7 @@ package pullpolicy
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &AlwaysPullPatch{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/ifnotpresent_pull_patch.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/ifnotpresent_pull_patch.go
index 0a107451..61ef319a 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/ifnotpresent_pull_patch.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/ifnotpresent_pull_patch.go
@@ -17,7 +17,7 @@ package pullpolicy
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &IfNotPresentPullPatch{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/never_pull_patch.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/never_pull_patch.go
index 8d762e2b..5e65ed76 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/never_pull_patch.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/config/testing/pullpolicy/never_pull_patch.go
@@ -17,7 +17,7 @@ package pullpolicy
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &NeverPullPatch{}
diff --git a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/tasks_test_resource.go b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/tasks_test_resource.go
index d6092541..0ce1afc5 100644
--- a/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/tasks_test_resource.go
+++ b/internal/plugins/ansible/v1/scaffolds/internal/templates/molecule/mdefault/tasks_test_resource.go
@@ -17,7 +17,7 @@ package mdefault
 import (
 	"path/filepath"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/file"
+	"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
 )
 
 var _ file.Template = &ResourceTest{}
@@ -42,7 +42,7 @@ func (f *ResourceTest) SetTemplateDefaults() error {
 }
 
 const resourceTestTemplate = `---
-- name: Create the {{.Resource.Domain}}/{{.Resource.Version}}.{{.Resource.Kind}}
+- name: Create the {{.Resource.QualifiedGroup}}/{{.Resource.Version}}.{{.Resource.Kind}}
   k8s:
     state: present
     namespace: '{{ "{{ namespace }}" }}'
diff --git a/internal/plugins/envtest/init.go b/internal/plugins/envtest/init.go
index c0b599cb..f13d0ca5 100644
--- a/internal/plugins/envtest/init.go
+++ b/internal/plugins/envtest/init.go
@@ -22,16 +22,19 @@ import (
 	"os"
 	"strings"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
+	cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
+
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
 )
 
 // controllerRuntimeVersion version to be used to download the envtest setup script
 const controllerRuntimeVersion = "v0.6.3"
 
 // RunInit modifies the project scaffolded by kubebuilder's Init plugin.
-func RunInit(cfg *config.Config) error {
+func RunInit(cfg config.Config) error {
 	// Only run these if project version is v3.
-	if !cfg.IsV3() {
+	isV3 := cfg.GetVersion().Compare(cfgv3.Version) == 0
+	if !isV3 {
 		return nil
 	}
 
@@ -50,7 +53,7 @@ func initUpdateMakefile(filePath string) error {
 	}
 
 	makefileBytes = []byte(strings.Replace(string(makefileBytes),
-		"# Run tests\ntest: generate fmt vet manifests\n\tgo test ./... -coverprofile cover.out",
+		"# Run tests\ntest: manifests generate fmt vet\n\tgo test ./... -coverprofile cover.out",
 		fmt.Sprintf(makefileTestTarget, controllerRuntimeVersion), 1))
 
 	var mode os.FileMode = 0644
@@ -62,7 +65,7 @@ func initUpdateMakefile(filePath string) error {
 
 const makefileTestTarget = `# Run tests
 ENVTEST_ASSETS_DIR = $(shell pwd)/testbin
-test: generate fmt vet manifests
+test: manifests generate fmt vet
 	mkdir -p $(ENVTEST_ASSETS_DIR)
 	test -f $(ENVTEST_ASSETS_DIR)/setup-envtest.sh || curl -sSLo $(ENVTEST_ASSETS_DIR)/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/%s/hack/setup-envtest.sh
 	source $(ENVTEST_ASSETS_DIR)/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out`
diff --git a/internal/plugins/helm/v1/chartutil/chart_test.go b/internal/plugins/helm/v1/chartutil/chart_test.go
index f0df663d..bd12af23 100644
--- a/internal/plugins/helm/v1/chartutil/chart_test.go
+++ b/internal/plugins/helm/v1/chartutil/chart_test.go
@@ -24,7 +24,7 @@ import (
 	"helm.sh/helm/v3/pkg/chart/loader"
 	"helm.sh/helm/v3/pkg/repo/repotest"
 	"k8s.io/apimachinery/pkg/runtime/schema"
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/resource"
+	"sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang"
 
 	"github.com/operator-framework/operator-sdk/internal/plugins/helm/v1/chartutil"
 )
@@ -202,14 +202,14 @@ type createChartTestCase struct {
 	helmChartVersion string
 	helmChartRepo    string
 
-	expectResource     *resource.Options
+	expectResource     *golang.Options
 	expectChartName    string
 	expectChartVersion string
 	expectErr          bool
 }
 
-func mustNewResource(group, version, kind string) *resource.Options {
-	r := &resource.Options{
+func mustNewResource(group, version, kind string) *golang.Options {
+	r := &golang.Options{
 		Namespaced: true,
 		Group:      group,
 		Version:    version,
diff --git a/internal/testutils/olm.go b/internal/testutils/olm.go
index 151994eb..bf912013 100644
--- a/internal/testutils/olm.go
+++ b/internal/testutils/olm.go
@@ -20,7 +20,10 @@ import (
 	"path/filepath"
 	"strings"
 
-	"sigs.k8s.io/kubebuilder/v2/pkg/model/config"
+	"sigs.k8s.io/kubebuilder/v3/pkg/config"
+	_ "sigs.k8s.io/kubebuilder/v3/pkg/config/v2" // Register config/v2 for `config.New`
+	_ "sigs.k8s.io/kubebuilder/v3/pkg/config/v3" // Register config/v3 for `config.New`
+	"sigs.k8s.io/yaml"
 )
 
 const (
@@ -47,6 +50,10 @@ packagemanifests: kustomize %s
 	$(KUSTOMIZE) build config/manifests | operator-sdk generate packagemanifests -q --version $(VERSION) $(PKG_MAN_OPTS)
 `
 
+type versionedConfig struct {
+	Version config.Version
+}
+
 // AddPackagemanifestsTarget will append the packagemanifests target to the makefile
 // in order to test the steps described in the docs.
 // More info:  https://v1-0-x.sdk.operatorframework.io/docs/olm-integration/generation/#package-manifests-formats
@@ -60,14 +67,25 @@ func (tc TestContext) AddPackagemanifestsTarget() error {
 	if err != nil {
 		return err
 	}
-	c := &config.Config{}
-	if err = c.Unmarshal(b); err != nil {
+
+	var versioned versionedConfig
+	if err := yaml.Unmarshal(b, &versioned); err != nil {
+		return err
+	}
+	// Create the config object
+	c, err := config.New(versioned.Version)
+	if err != nil {
+		return err
+	}
+
+	// Unmarshal the file content
+	if err := c.Unmarshal(b); err != nil {
 		return err
 	}
 
 	// add the manifests target when is a Go project.
 	replaceTarget := ""
-	if strings.HasPrefix(c.Layout, "go") {
+	if strings.HasPrefix(c.GetLayout(), "go") {
 		replaceTarget = "manifests"
 	}
 	makefilePackagemanifestsFragment = fmt.Sprintf(makefilePackagemanifestsFragment, replaceTarget)
diff --git a/internal/testutils/utils.go b/internal/testutils/utils.go
index adb8cf65..0bb33215 100644
--- a/internal/testutils/utils.go
+++ b/internal/testutils/utils.go
@@ -28,7 +28,7 @@ import (
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	kbtestutils "sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
 )
 
 const BinaryName = "operator-sdk"
diff --git a/test/e2e/ansible/cluster_test.go b/test/e2e/ansible/cluster_test.go
index 60e4d861..70a7f0e1 100644
--- a/test/e2e/ansible/cluster_test.go
+++ b/test/e2e/ansible/cluster_test.go
@@ -23,7 +23,7 @@ import (
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	kbtestutils "sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
 
 	"github.com/operator-framework/operator-sdk/internal/testutils"
 )
diff --git a/test/e2e/ansible/suite_test.go b/test/e2e/ansible/suite_test.go
index 813dadb9..d5d1bb27 100644
--- a/test/e2e/ansible/suite_test.go
+++ b/test/e2e/ansible/suite_test.go
@@ -21,7 +21,7 @@ import (
 	"strings"
 	"testing"
 
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	kbtestutils "sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
@@ -108,7 +108,7 @@ var _ = BeforeSuite(func() {
 
 	By("adding RBAC permissions for the Memcached Kind")
 	err = testutils.ReplaceInFile(filepath.Join(tc.Dir, "config", "rbac", "role.yaml"),
-		"# +kubebuilder:scaffold:rules", rolesForBaseOperator)
+		"#+kubebuilder:scaffold:rules", rolesForBaseOperator)
 	Expect(err).NotTo(HaveOccurred())
 
 	By("building the project image")
@@ -213,5 +213,5 @@ const rolesForBaseOperator = `
       - patch
       - update
       - watch
-# +kubebuilder:scaffold:rules
+#+kubebuilder:scaffold:rules
 `
diff --git a/test/e2e/go/cluster_test.go b/test/e2e/go/cluster_test.go
index 01aafbdd..58c441a8 100644
--- a/test/e2e/go/cluster_test.go
+++ b/test/e2e/go/cluster_test.go
@@ -27,7 +27,7 @@ import (
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	kbtestutils "sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
 
 	"github.com/operator-framework/operator-sdk/internal/testutils"
 )
diff --git a/test/e2e/helm/cluster_test.go b/test/e2e/helm/cluster_test.go
index 05f60087..fe966e10 100644
--- a/test/e2e/helm/cluster_test.go
+++ b/test/e2e/helm/cluster_test.go
@@ -23,7 +23,7 @@ import (
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-	kbtestutils "sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	kbtestutils "sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
 
 	"github.com/operator-framework/operator-sdk/internal/testutils"
 )
diff --git a/test/integration/suite_test.go b/test/integration/suite_test.go
index 3b6a98f1..e1f7072b 100644
--- a/test/integration/suite_test.go
+++ b/test/integration/suite_test.go
@@ -25,7 +25,7 @@ import (
 	. "github.com/onsi/gomega"
 	"github.com/operator-framework/api/pkg/operators/v1alpha1"
 	"github.com/operator-framework/operator-registry/pkg/lib/bundle"
-	"sigs.k8s.io/kubebuilder/v2/test/e2e/utils"
+	"sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
 	"sigs.k8s.io/yaml"
 
 	"github.com/operator-framework/operator-sdk/internal/testutils"
diff --git a/testdata/ansible/memcached-operator/Makefile b/testdata/ansible/memcached-operator/Makefile
index e9f22276..b0ae3dc7 100644
--- a/testdata/ansible/memcached-operator/Makefile
+++ b/testdata/ansible/memcached-operator/Makefile
@@ -101,15 +101,13 @@ ANSIBLE_OPERATOR = $(shell which ansible-operator)
 endif
 endif
 
-# Generate bundle manifests and metadata, then validate generated files.
-.PHONY: bundle
+.PHONY: bundle ## Generate bundle manifests and metadata, then validate generated files.
 bundle: kustomize
 	operator-sdk generate kustomize manifests --interactive=false -q
 	cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
 	$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
 	operator-sdk bundle validate ./bundle
 
-# Build the bundle image.
-.PHONY: bundle-build
+.PHONY: bundle-build ## Build the bundle image.
 bundle-build:
 	docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
diff --git a/testdata/ansible/memcached-operator/PROJECT b/testdata/ansible/memcached-operator/PROJECT
index 2eeaedf5..b2e2c54a 100644
--- a/testdata/ansible/memcached-operator/PROJECT
+++ b/testdata/ansible/memcached-operator/PROJECT
@@ -1,11 +1,15 @@
 domain: example.com
 layout: ansible.sdk.operatorframework.io/v1
+plugins:
+  manifests.sdk.operatorframework.io/v2: {}
+  scorecard.sdk.operatorframework.io/v2: {}
 projectName: memcached-operator
 resources:
-- group: cache
+- api:
+    crdVersion: v1
+    namespaced: true
+  domain: example.com
+  group: cache
   kind: Memcached
   version: v1alpha1
-version: 3-alpha
-plugins:
-  manifests.sdk.operatorframework.io/v2: {}
-  scorecard.sdk.operatorframework.io/v2: {}
+version: "3"
diff --git a/testdata/ansible/memcached-operator/config/crd/kustomization.yaml b/testdata/ansible/memcached-operator/config/crd/kustomization.yaml
index f7aed174..c8033c37 100644
--- a/testdata/ansible/memcached-operator/config/crd/kustomization.yaml
+++ b/testdata/ansible/memcached-operator/config/crd/kustomization.yaml
@@ -3,4 +3,4 @@
 # It should be run by config/default
 resources:
 - bases/cache.example.com_memcacheds.yaml
-# +kubebuilder:scaffold:crdkustomizeresource
+#+kubebuilder:scaffold:crdkustomizeresource
diff --git a/testdata/ansible/memcached-operator/config/rbac/role.yaml b/testdata/ansible/memcached-operator/config/rbac/role.yaml
index 2cc2d4b6..27958f81 100644
--- a/testdata/ansible/memcached-operator/config/rbac/role.yaml
+++ b/testdata/ansible/memcached-operator/config/rbac/role.yaml
@@ -54,4 +54,4 @@ rules:
       - patch
       - update
       - watch
-# +kubebuilder:scaffold:rules
+#+kubebuilder:scaffold:rules
diff --git a/testdata/ansible/memcached-operator/config/samples/kustomization.yaml b/testdata/ansible/memcached-operator/config/samples/kustomization.yaml
index 62e56248..42654aa2 100644
--- a/testdata/ansible/memcached-operator/config/samples/kustomization.yaml
+++ b/testdata/ansible/memcached-operator/config/samples/kustomization.yaml
@@ -1,4 +1,4 @@
 ## Append samples you want in your CSV to this file as resources ##
 resources:
 - cache_v1alpha1_memcached.yaml
-# +kubebuilder:scaffold:manifestskustomizesamples
+#+kubebuilder:scaffold:manifestskustomizesamples
diff --git a/testdata/ansible/memcached-operator/config/scorecard/kustomization.yaml b/testdata/ansible/memcached-operator/config/scorecard/kustomization.yaml
index d73509ee..50cd2d08 100644
--- a/testdata/ansible/memcached-operator/config/scorecard/kustomization.yaml
+++ b/testdata/ansible/memcached-operator/config/scorecard/kustomization.yaml
@@ -13,4 +13,4 @@ patchesJson6902:
     version: v1alpha3
     kind: Configuration
     name: config
-# +kubebuilder:scaffold:patchesJson6902
+#+kubebuilder:scaffold:patchesJson6902
diff --git a/testdata/ansible/memcached-operator/watches.yaml b/testdata/ansible/memcached-operator/watches.yaml
index ca49be17..ec0cb28f 100644
--- a/testdata/ansible/memcached-operator/watches.yaml
+++ b/testdata/ansible/memcached-operator/watches.yaml
@@ -4,4 +4,4 @@
   group: cache.example.com
   kind: Memcached
   playbook: playbooks/memcached.yml
-# +kubebuilder:scaffold:watch
+#+kubebuilder:scaffold:watch
diff --git a/testdata/go/v2/memcached-operator/Makefile b/testdata/go/v2/memcached-operator/Makefile
index aa657472..087f6d92 100644
--- a/testdata/go/v2/memcached-operator/Makefile
+++ b/testdata/go/v2/memcached-operator/Makefile
@@ -40,63 +40,75 @@ else
 GOBIN=$(shell go env GOBIN)
 endif
 
-all: manager
+all: build
 
-# Run tests
-ENVTEST_ASSETS_DIR = $(shell pwd)/testbin
-test: generate fmt vet manifests
-	mkdir -p $(ENVTEST_ASSETS_DIR)
-	test -f $(ENVTEST_ASSETS_DIR)/setup-envtest.sh || curl -sSLo $(ENVTEST_ASSETS_DIR)/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.6.3/hack/setup-envtest.sh
-	source $(ENVTEST_ASSETS_DIR)/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out
+##@ General
 
-# Build manager binary
-manager: generate fmt vet
-	go build -o bin/manager main.go
-
-# Run against the configured Kubernetes cluster in ~/.kube/config
-run: generate fmt vet manifests
-	go run ./main.go
+# The help target prints out all targets with their descriptions organized
+# beneath their categories. The categories are represented by '##@' and the
+# target descriptions by '##'. The awk commands is responsible for reading the
+# entire set of makefiles included in this invocation, looking for lines of the
+# file as xyz: ## something, and then pretty-format the target and help. Then,
+# if there's a line with ##@ something, that gets pretty-printed as a category.
+# More info on the usage of ANSI control characters for terminal formatting:
+# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
+# More info on the awk command:
+# http://linuxcommand.org/lc3_adv_awk.php
 
-# Install CRDs into a cluster
-install: manifests kustomize
-	$(KUSTOMIZE) build config/crd | kubectl apply -f -
+help: ## Display this help.
+	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
 
-# Uninstall CRDs from a cluster
-uninstall: manifests kustomize
-	$(KUSTOMIZE) build config/crd | kubectl delete -f -
-
-# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
-deploy: manifests kustomize
-	cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
-	$(KUSTOMIZE) build config/default | kubectl apply -f -
+##@ Development
 
-# Generate manifests e.g. CRD, RBAC etc.
-manifests: controller-gen
+manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
 	$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
 
-# Run go fmt against code
-fmt:
+generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
+	$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
+
+fmt: ## Run go fmt against code.
 	go fmt ./...
 
-# Run go vet against code
-vet:
+vet: ## Run go vet against code.
 	go vet ./...
 
-# Generate code
-generate: controller-gen
-	$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
+test: manifests generate fmt vet ## Run tests.
+	go test ./... -coverprofile cover.out
+
+##@ Build
+
+build: generate fmt vet ## Build manager binary.
+	go build -o bin/manager main.go
 
-# Build the docker image
-docker-build: test
-	docker build . -t ${IMG}
+# Backwards compatibility
+manager: build ## Build manager binary (alias for build target).
 
-# Push the docker image
-docker-push:
+run: manifests generate fmt vet ## Run a controller from your host.
+	go run ./main.go
+
+docker-build: test ## Build docker image with the manager.
+	docker build -t ${IMG} .
+
+docker-push: ## Push docker image with the manager.
 	docker push ${IMG}
 
-# find or download controller-gen
-# download controller-gen if necessary
-controller-gen:
+##@ Deployment
+
+install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
+	$(KUSTOMIZE) build config/crd | kubectl apply -f -
+
+uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config.
+	$(KUSTOMIZE) build config/crd | kubectl delete -f -
+
+deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
+	cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
+	$(KUSTOMIZE) build config/default | kubectl apply -f -
+
+undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config.
+	$(KUSTOMIZE) build config/default | kubectl delete -f -
+
+
+controller-gen: ## Download controller-gen locally if necessary.
 ifeq (, $(shell which controller-gen))
 	@{ \
 	set -e ;\
@@ -111,7 +123,7 @@ else
 CONTROLLER_GEN=$(shell which controller-gen)
 endif
 
-kustomize:
+kustomize: ## Download kustomize locally if necessary.
 ifeq (, $(shell which kustomize))
 	@{ \
 	set -e ;\
@@ -126,15 +138,13 @@ else
 KUSTOMIZE=$(shell which kustomize)
 endif
 
-# Generate bundle manifests and metadata, then validate generated files.
-.PHONY: bundle
+.PHONY: bundle ## Generate bundle manifests and metadata, then validate generated files.
 bundle: manifests kustomize
 	operator-sdk generate kustomize manifests --interactive=false -q
 	cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
 	$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
 	operator-sdk bundle validate ./bundle
 
-# Build the bundle image.
-.PHONY: bundle-build
+.PHONY: bundle-build ## Build the bundle image.
 bundle-build:
 	docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
diff --git a/testdata/go/v2/memcached-operator/PROJECT b/testdata/go/v2/memcached-operator/PROJECT
index cef09ae8..b5b8ede3 100644
--- a/testdata/go/v2/memcached-operator/PROJECT
+++ b/testdata/go/v2/memcached-operator/PROJECT
@@ -1,12 +1,21 @@
 domain: example.com
 layout: go.kubebuilder.io/v2
+plugins:
+  manifests.sdk.operatorframework.io/v2: {}
+  scorecard.sdk.operatorframework.io/v2: {}
 projectName: memcached-operator
 repo: github.com/example/memcached-operator
 resources:
-- group: cache
+- api:
+    crdVersion: v1beta1
+    namespaced: true
+  controller: true
+  domain: example.com
+  group: cache
   kind: Memcached
+  path: github.com/example/memcached-operator/api/v1alpha1
   version: v1alpha1
-version: 3-alpha
-plugins:
-  manifests.sdk.operatorframework.io/v2: {}
-  scorecard.sdk.operatorframework.io/v2: {}
+  webhooks:
+    defaulting: true
+    webhookVersion: v1beta1
+version: "3"
diff --git a/testdata/go/v2/memcached-operator/api/v1alpha1/groupversion_info.go b/testdata/go/v2/memcached-operator/api/v1alpha1/groupversion_info.go
index 48db6c39..fcc60d06 100644
--- a/testdata/go/v2/memcached-operator/api/v1alpha1/groupversion_info.go
+++ b/testdata/go/v2/memcached-operator/api/v1alpha1/groupversion_info.go
@@ -15,8 +15,8 @@ limitations under the License.
 */
 
 // Package v1alpha1 contains API Schema definitions for the cache v1alpha1 API group
-// +kubebuilder:object:generate=true
-// +groupName=cache.example.com
+//+kubebuilder:object:generate=true
+//+groupName=cache.example.com
 package v1alpha1
 
 import (
diff --git a/testdata/go/v2/memcached-operator/api/v1alpha1/memcached_types.go b/testdata/go/v2/memcached-operator/api/v1alpha1/memcached_types.go
index 2affdfb7..5d69eec0 100644
--- a/testdata/go/v2/memcached-operator/api/v1alpha1/memcached_types.go
+++ b/testdata/go/v2/memcached-operator/api/v1alpha1/memcached_types.go
@@ -31,7 +31,7 @@ type MemcachedSpec struct {
 	// Size defines the number of Memcached instances
 	Size int32 `json:"size,omitempty"`
 
-	// Foo is an example field of Memcached. Edit Memcached_types.go to remove/update
+	// Foo is an example field of Memcached. Edit memcached_types.go to remove/update
 	Foo string `json:"foo,omitempty"`
 }
 
@@ -44,8 +44,8 @@ type MemcachedStatus struct {
 	Nodes []string `json:"nodes,omitempty"`
 }
 
-// +kubebuilder:object:root=true
-// +kubebuilder:subresource:status
+//+kubebuilder:object:root=true
+//+kubebuilder:subresource:status
 
 // Memcached is the Schema for the memcacheds API
 type Memcached struct {
@@ -56,7 +56,7 @@ type Memcached struct {
 	Status MemcachedStatus `json:"status,omitempty"`
 }
 
-// +kubebuilder:object:root=true
+//+kubebuilder:object:root=true
 
 // MemcachedList contains a list of Memcached
 type MemcachedList struct {
diff --git a/testdata/go/v2/memcached-operator/api/v1alpha1/memcached_webhook.go b/testdata/go/v2/memcached-operator/api/v1alpha1/memcached_webhook.go
index db297094..2286ce7d 100644
--- a/testdata/go/v2/memcached-operator/api/v1alpha1/memcached_webhook.go
+++ b/testdata/go/v2/memcached-operator/api/v1alpha1/memcached_webhook.go
@@ -36,7 +36,7 @@ func (r *Memcached) SetupWebhookWithManager(mgr ctrl.Manager) error {
 
 // EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
 
-// +kubebuilder:webhook:path=/mutate-cache-example-com-v1alpha1-memcached,mutating=true,failurePolicy=fail,groups=cache.example.com,resources=memcacheds,verbs=create;update,versions=v1alpha1,name=mmemcached.kb.io
+//+kubebuilder:webhook:path=/mutate-cache-example-com-v1alpha1-memcached,mutating=true,failurePolicy=fail,groups=cache.example.com,resources=memcacheds,verbs=create;update,versions=v1alpha1,name=mmemcached.kb.io
 
 var _ webhook.Defaulter = &Memcached{}
 
@@ -50,7 +50,7 @@ func (r *Memcached) Default() {
 }
 
 // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
-// +kubebuilder:webhook:verbs=create;update,path=/validate-cache-example-com-v1alpha1-memcached,mutating=false,failurePolicy=fail,groups=cache.example.com,resources=memcacheds,versions=v1alpha1,name=vmemcached.kb.io
+//+kubebuilder:webhook:verbs=create;update,path=/validate-cache-example-com-v1alpha1-memcached,mutating=false,failurePolicy=fail,groups=cache.example.com,resources=memcacheds,versions=v1alpha1,name=vmemcached.kb.io
 
 var _ webhook.Validator = &Memcached{}
 
diff --git a/testdata/go/v2/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml b/testdata/go/v2/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml
index 1c31e731..dd45d996 100644
--- a/testdata/go/v2/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml
+++ b/testdata/go/v2/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml
@@ -31,7 +31,7 @@ spec:
           description: MemcachedSpec defines the desired state of Memcached
           properties:
             foo:
-              description: Foo is an example field of Memcached. Edit Memcached_types.go to remove/update
+              description: Foo is an example field of Memcached. Edit memcached_types.go to remove/update
               type: string
             size:
               description: Size defines the number of Memcached instances
diff --git a/testdata/go/v2/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml b/testdata/go/v2/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml
index 666a403d..d2b016f6 100644
--- a/testdata/go/v2/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml
+++ b/testdata/go/v2/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml
@@ -37,7 +37,7 @@ spec:
           description: MemcachedSpec defines the desired state of Memcached
           properties:
             foo:
-              description: Foo is an example field of Memcached. Edit Memcached_types.go
+              description: Foo is an example field of Memcached. Edit memcached_types.go
                 to remove/update
               type: string
             size:
diff --git a/testdata/go/v2/memcached-operator/config/crd/kustomization.yaml b/testdata/go/v2/memcached-operator/config/crd/kustomization.yaml
index 4acd9f4b..f6e957df 100644
--- a/testdata/go/v2/memcached-operator/config/crd/kustomization.yaml
+++ b/testdata/go/v2/memcached-operator/config/crd/kustomization.yaml
@@ -3,18 +3,18 @@
 # It should be run by config/default
 resources:
 - bases/cache.example.com_memcacheds.yaml
-# +kubebuilder:scaffold:crdkustomizeresource
+#+kubebuilder:scaffold:crdkustomizeresource
 
 patchesStrategicMerge:
 # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
 # patches here are for enabling the conversion webhook for each CRD
 #- patches/webhook_in_memcacheds.yaml
-# +kubebuilder:scaffold:crdkustomizewebhookpatch
+#+kubebuilder:scaffold:crdkustomizewebhookpatch
 
 # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
 # patches here are for enabling the CA injection for each CRD
 #- patches/cainjection_in_memcacheds.yaml
-# +kubebuilder:scaffold:crdkustomizecainjectionpatch
+#+kubebuilder:scaffold:crdkustomizecainjectionpatch
 
 # the following config is for teaching kustomize how to do kustomization for CRDs.
 configurations:
diff --git a/testdata/go/v2/memcached-operator/config/samples/kustomization.yaml b/testdata/go/v2/memcached-operator/config/samples/kustomization.yaml
index 62e56248..42654aa2 100644
--- a/testdata/go/v2/memcached-operator/config/samples/kustomization.yaml
+++ b/testdata/go/v2/memcached-operator/config/samples/kustomization.yaml
@@ -1,4 +1,4 @@
 ## Append samples you want in your CSV to this file as resources ##
 resources:
 - cache_v1alpha1_memcached.yaml
-# +kubebuilder:scaffold:manifestskustomizesamples
+#+kubebuilder:scaffold:manifestskustomizesamples
diff --git a/testdata/go/v2/memcached-operator/config/scorecard/kustomization.yaml b/testdata/go/v2/memcached-operator/config/scorecard/kustomization.yaml
index d73509ee..50cd2d08 100644
--- a/testdata/go/v2/memcached-operator/config/scorecard/kustomization.yaml
+++ b/testdata/go/v2/memcached-operator/config/scorecard/kustomization.yaml
@@ -13,4 +13,4 @@ patchesJson6902:
     version: v1alpha3
     kind: Configuration
     name: config
-# +kubebuilder:scaffold:patchesJson6902
+#+kubebuilder:scaffold:patchesJson6902
diff --git a/testdata/go/v2/memcached-operator/controllers/memcached_controller.go b/testdata/go/v2/memcached-operator/controllers/memcached_controller.go
index 1f5f0f3d..020ec0ef 100644
--- a/testdata/go/v2/memcached-operator/controllers/memcached_controller.go
+++ b/testdata/go/v2/memcached-operator/controllers/memcached_controller.go
@@ -41,12 +41,21 @@ type MemcachedReconciler struct {
 	Scheme *runtime.Scheme
 }
 
-// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
-// +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
-// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
-
+//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
+//+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
+//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
+
+// Reconcile is part of the main kubernetes reconciliation loop which aims to
+// move the current state of the cluster closer to the desired state.
+// TODO(user): Modify the Reconcile function to compare the state specified by
+// the Memcached object against the actual cluster state, and then
+// perform operations to make the cluster state reflect the state specified by
+// the user.
+//
+// For more details, check Reconcile and its Result here:
+// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.6.4/pkg/reconcile
 func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	ctx := context.Background()
 	log := r.Log.WithValues("memcached", req.NamespacedName)
@@ -178,6 +187,7 @@ func getPodNames(pods []corev1.Pod) []string {
 	return podNames
 }
 
+// SetupWithManager sets up the controller with the Manager.
 func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewControllerManagedBy(mgr).
 		For(&cachev1alpha1.Memcached{}).
diff --git a/testdata/go/v2/memcached-operator/controllers/suite_test.go b/testdata/go/v2/memcached-operator/controllers/suite_test.go
index 349107d3..fac02bc4 100644
--- a/testdata/go/v2/memcached-operator/controllers/suite_test.go
+++ b/testdata/go/v2/memcached-operator/controllers/suite_test.go
@@ -31,7 +31,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 
 	cachev1alpha1 "github.com/example/memcached-operator/api/v1alpha1"
-	// +kubebuilder:scaffold:imports
+	//+kubebuilder:scaffold:imports
 )
 
 // These tests use Ginkgo (BDD-style Go testing framework). Refer to
@@ -65,7 +65,7 @@ var _ = BeforeSuite(func(done Done) {
 	err = cachev1alpha1.AddToScheme(scheme.Scheme)
 	Expect(err).NotTo(HaveOccurred())
 
-	// +kubebuilder:scaffold:scheme
+	//+kubebuilder:scaffold:scheme
 
 	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
 	Expect(err).ToNot(HaveOccurred())
diff --git a/testdata/go/v2/memcached-operator/go.mod b/testdata/go/v2/memcached-operator/go.mod
index 009ec6f6..0f76028f 100644
--- a/testdata/go/v2/memcached-operator/go.mod
+++ b/testdata/go/v2/memcached-operator/go.mod
@@ -4,8 +4,6 @@ go 1.13
 
 require (
 	github.com/go-logr/logr v0.1.0
-	github.com/onsi/ginkgo v1.12.1
-	github.com/onsi/gomega v1.10.1
 	k8s.io/api v0.18.6
 	k8s.io/apimachinery v0.18.6
 	k8s.io/client-go v0.18.6
diff --git a/testdata/go/v2/memcached-operator/main.go b/testdata/go/v2/memcached-operator/main.go
index 4e36eb8e..9ffffff8 100644
--- a/testdata/go/v2/memcached-operator/main.go
+++ b/testdata/go/v2/memcached-operator/main.go
@@ -29,7 +29,7 @@ import (
 
 	cachev1alpha1 "github.com/example/memcached-operator/api/v1alpha1"
 	"github.com/example/memcached-operator/controllers"
-	// +kubebuilder:scaffold:imports
+	//+kubebuilder:scaffold:imports
 )
 
 var (
@@ -41,7 +41,7 @@ func init() {
 	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
 
 	utilruntime.Must(cachev1alpha1.AddToScheme(scheme))
-	// +kubebuilder:scaffold:scheme
+	//+kubebuilder:scaffold:scheme
 }
 
 func main() {
@@ -79,7 +79,7 @@ func main() {
 		setupLog.Error(err, "unable to create webhook", "webhook", "Memcached")
 		os.Exit(1)
 	}
-	// +kubebuilder:scaffold:builder
+	//+kubebuilder:scaffold:builder
 
 	setupLog.Info("starting manager")
 	if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
diff --git a/testdata/go/v3/memcached-operator/Makefile b/testdata/go/v3/memcached-operator/Makefile
index 4d63bd71..61813510 100644
--- a/testdata/go/v3/memcached-operator/Makefile
+++ b/testdata/go/v3/memcached-operator/Makefile
@@ -40,72 +40,80 @@ else
 GOBIN=$(shell go env GOBIN)
 endif
 
-all: manager
+all: build
+
+##@ General
+
+# The help target prints out all targets with their descriptions organized
+# beneath their categories. The categories are represented by '##@' and the
+# target descriptions by '##'. The awk commands is responsible for reading the
+# entire set of makefiles included in this invocation, looking for lines of the
+# file as xyz: ## something, and then pretty-format the target and help. Then,
+# if there's a line with ##@ something, that gets pretty-printed as a category.
+# More info on the usage of ANSI control characters for terminal formatting:
+# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
+# More info on the awk command:
+# http://linuxcommand.org/lc3_adv_awk.php
+
+help: ## Display this help.
+	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
+
+##@ Development
+
+manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
+	$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+
+generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
+	$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
+
+fmt: ## Run go fmt against code.
+	go fmt ./...
+
+vet: ## Run go vet against code.
+	go vet ./...
 
-# Run tests
 ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
-test: generate fmt vet manifests
+test: manifests generate fmt vet ## Run tests.
 	mkdir -p ${ENVTEST_ASSETS_DIR}
 	test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.0/hack/setup-envtest.sh
 	source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out
 
-# Build manager binary
-manager: generate fmt vet
+##@ Build
+
+build: generate fmt vet ## Build manager binary.
 	go build -o bin/manager main.go
 
-# Run against the configured Kubernetes cluster in ~/.kube/config
-run: generate fmt vet manifests
+run: manifests generate fmt vet ## Run a controller from your host.
 	go run ./main.go
 
-# Install CRDs into a cluster
-install: manifests kustomize
+docker-build: test ## Build docker image with the manager.
+	docker build -t ${IMG} .
+
+docker-push: ## Push docker image with the manager.
+	docker push ${IMG}
+
+##@ Deployment
+
+install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
 	$(KUSTOMIZE) build config/crd | kubectl apply -f -
 
-# Uninstall CRDs from a cluster
-uninstall: manifests kustomize
+uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config.
 	$(KUSTOMIZE) build config/crd | kubectl delete -f -
 
-# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
-deploy: manifests kustomize
+deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
 	cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
 	$(KUSTOMIZE) build config/default | kubectl apply -f -
 
-# UnDeploy controller from the configured Kubernetes cluster in ~/.kube/config
-undeploy:
+undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config.
 	$(KUSTOMIZE) build config/default | kubectl delete -f -
 
-# Generate manifests e.g. CRD, RBAC etc.
-manifests: controller-gen
-	$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
-
-# Run go fmt against code
-fmt:
-	go fmt ./...
-
-# Run go vet against code
-vet:
-	go vet ./...
-
-# Generate code
-generate: controller-gen
-	$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
-
-# Build the docker image
-docker-build: test
-	docker build -t ${IMG} .
-
-# Push the docker image
-docker-push:
-	docker push ${IMG}
 
-# Download controller-gen locally if necessary
 CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
-controller-gen:
+controller-gen: ## Download controller-gen locally if necessary.
 	$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1)
 
-# Download kustomize locally if necessary
 KUSTOMIZE = $(shell pwd)/bin/kustomize
-kustomize:
+kustomize: ## Download kustomize locally if necessary.
 	$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
 
 # go-get-tool will 'go get' any package $2 and install it to $1.
@@ -122,15 +130,13 @@ rm -rf $$TMP_DIR ;\
 }
 endef
 
-# Generate bundle manifests and metadata, then validate generated files.
-.PHONY: bundle
+.PHONY: bundle ## Generate bundle manifests and metadata, then validate generated files.
 bundle: manifests kustomize
 	operator-sdk generate kustomize manifests --interactive=false -q
 	cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
 	$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
 	operator-sdk bundle validate ./bundle
 
-# Build the bundle image.
-.PHONY: bundle-build
+.PHONY: bundle-build ## Build the bundle image.
 bundle-build:
 	docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
diff --git a/testdata/go/v3/memcached-operator/PROJECT b/testdata/go/v3/memcached-operator/PROJECT
index aa02ce8b..96a8ba9a 100644
--- a/testdata/go/v3/memcached-operator/PROJECT
+++ b/testdata/go/v3/memcached-operator/PROJECT
@@ -1,14 +1,21 @@
 domain: example.com
 layout: go.kubebuilder.io/v3
+plugins:
+  manifests.sdk.operatorframework.io/v2: {}
+  scorecard.sdk.operatorframework.io/v2: {}
 projectName: memcached-operator
 repo: github.com/example/memcached-operator
 resources:
-- crdVersion: v1
+- api:
+    crdVersion: v1
+    namespaced: true
+  controller: true
+  domain: example.com
   group: cache
   kind: Memcached
+  path: github.com/example/memcached-operator/api/v1alpha1
   version: v1alpha1
-  webhookVersion: v1
-version: 3-alpha
-plugins:
-  manifests.sdk.operatorframework.io/v2: {}
-  scorecard.sdk.operatorframework.io/v2: {}
+  webhooks:
+    defaulting: true
+    webhookVersion: v1
+version: "3"
diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/groupversion_info.go b/testdata/go/v3/memcached-operator/api/v1alpha1/groupversion_info.go
index 48db6c39..fcc60d06 100644
--- a/testdata/go/v3/memcached-operator/api/v1alpha1/groupversion_info.go
+++ b/testdata/go/v3/memcached-operator/api/v1alpha1/groupversion_info.go
@@ -15,8 +15,8 @@ limitations under the License.
 */
 
 // Package v1alpha1 contains API Schema definitions for the cache v1alpha1 API group
-// +kubebuilder:object:generate=true
-// +groupName=cache.example.com
+//+kubebuilder:object:generate=true
+//+groupName=cache.example.com
 package v1alpha1
 
 import (
diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_types.go b/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_types.go
index 2affdfb7..5d69eec0 100644
--- a/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_types.go
+++ b/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_types.go
@@ -31,7 +31,7 @@ type MemcachedSpec struct {
 	// Size defines the number of Memcached instances
 	Size int32 `json:"size,omitempty"`
 
-	// Foo is an example field of Memcached. Edit Memcached_types.go to remove/update
+	// Foo is an example field of Memcached. Edit memcached_types.go to remove/update
 	Foo string `json:"foo,omitempty"`
 }
 
@@ -44,8 +44,8 @@ type MemcachedStatus struct {
 	Nodes []string `json:"nodes,omitempty"`
 }
 
-// +kubebuilder:object:root=true
-// +kubebuilder:subresource:status
+//+kubebuilder:object:root=true
+//+kubebuilder:subresource:status
 
 // Memcached is the Schema for the memcacheds API
 type Memcached struct {
@@ -56,7 +56,7 @@ type Memcached struct {
 	Status MemcachedStatus `json:"status,omitempty"`
 }
 
-// +kubebuilder:object:root=true
+//+kubebuilder:object:root=true
 
 // MemcachedList contains a list of Memcached
 type MemcachedList struct {
diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_webhook.go b/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_webhook.go
index 672a3a68..897d94af 100644
--- a/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_webhook.go
+++ b/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_webhook.go
@@ -36,7 +36,7 @@ func (r *Memcached) SetupWebhookWithManager(mgr ctrl.Manager) error {
 
 // EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
 
-// +kubebuilder:webhook:path=/mutate-cache-example-com-v1alpha1-memcached,mutating=true,failurePolicy=fail,sideEffects=None,groups=cache.example.com,resources=memcacheds,verbs=create;update,versions=v1alpha1,name=mmemcached.kb.io,admissionReviewVersions={v1,v1beta1}
+//+kubebuilder:webhook:path=/mutate-cache-example-com-v1alpha1-memcached,mutating=true,failurePolicy=fail,sideEffects=None,groups=cache.example.com,resources=memcacheds,verbs=create;update,versions=v1alpha1,name=mmemcached.kb.io,admissionReviewVersions={v1,v1beta1}
 
 var _ webhook.Defaulter = &Memcached{}
 
@@ -50,7 +50,7 @@ func (r *Memcached) Default() {
 }
 
 // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
-// +kubebuilder:webhook:path=/validate-cache-example-com-v1alpha1-memcached,mutating=false,failurePolicy=fail,sideEffects=None,groups=cache.example.com,resources=memcacheds,verbs=create;update,versions=v1alpha1,name=vmemcached.kb.io,admissionReviewVersions={v1,v1beta1}
+//+kubebuilder:webhook:path=/validate-cache-example-com-v1alpha1-memcached,mutating=false,failurePolicy=fail,sideEffects=None,groups=cache.example.com,resources=memcacheds,verbs=create;update,versions=v1alpha1,name=vmemcached.kb.io,admissionReviewVersions={v1,v1beta1}
 
 var _ webhook.Validator = &Memcached{}
 
diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/webhook_suite_test.go b/testdata/go/v3/memcached-operator/api/v1alpha1/webhook_suite_test.go
index 69c487c6..b419fb7e 100644
--- a/testdata/go/v3/memcached-operator/api/v1alpha1/webhook_suite_test.go
+++ b/testdata/go/v3/memcached-operator/api/v1alpha1/webhook_suite_test.go
@@ -29,7 +29,7 @@ import (
 	. "github.com/onsi/gomega"
 
 	admissionv1beta1 "k8s.io/api/admission/v1beta1"
-	// +kubebuilder:scaffold:imports
+	//+kubebuilder:scaffold:imports
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/rest"
 	ctrl "sigs.k8s.io/controller-runtime"
@@ -64,7 +64,8 @@ var _ = BeforeSuite(func() {
 
 	By("bootstrapping test environment")
 	testEnv = &envtest.Environment{
-		CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
+		CRDDirectoryPaths:     []string{filepath.Join("..", "..", "config", "crd", "bases")},
+		ErrorIfCRDPathMissing: false,
 		WebhookInstallOptions: envtest.WebhookInstallOptions{
 			Paths: []string{filepath.Join("..", "..", "config", "webhook")},
 		},
@@ -81,7 +82,7 @@ var _ = BeforeSuite(func() {
 	err = admissionv1beta1.AddToScheme(scheme)
 	Expect(err).NotTo(HaveOccurred())
 
-	// +kubebuilder:scaffold:scheme
+	//+kubebuilder:scaffold:scheme
 
 	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
 	Expect(err).NotTo(HaveOccurred())
@@ -102,7 +103,7 @@ var _ = BeforeSuite(func() {
 	err = (&Memcached{}).SetupWebhookWithManager(mgr)
 	Expect(err).NotTo(HaveOccurred())
 
-	// +kubebuilder:scaffold:webhook
+	//+kubebuilder:scaffold:webhook
 
 	go func() {
 		err = mgr.Start(ctx)
diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go b/testdata/go/v3/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go
index 06a3fb07..7730cf55 100644
--- a/testdata/go/v3/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go
+++ b/testdata/go/v3/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go
@@ -30,7 +30,7 @@ func (in *Memcached) DeepCopyInto(out *Memcached) {
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
 	out.Spec = in.Spec
-	in.Status.DeepCopyInto(&out.Status)
+	out.Status = in.Status
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Memcached.
@@ -101,11 +101,6 @@ func (in *MemcachedSpec) DeepCopy() *MemcachedSpec {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *MemcachedStatus) DeepCopyInto(out *MemcachedStatus) {
 	*out = *in
-	if in.Nodes != nil {
-		in, out := &in.Nodes, &out.Nodes
-		*out = make([]string, len(*in))
-		copy(*out, *in)
-	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemcachedStatus.
diff --git a/testdata/go/v3/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml b/testdata/go/v3/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml
index 3af607fe..ddffa55e 100644
--- a/testdata/go/v3/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml
+++ b/testdata/go/v3/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml
@@ -31,7 +31,7 @@ spec:
             description: MemcachedSpec defines the desired state of Memcached
             properties:
               foo:
-                description: Foo is an example field of Memcached. Edit Memcached_types.go to remove/update
+                description: Foo is an example field of Memcached. Edit memcached_types.go to remove/update
                 type: string
               size:
                 description: Size defines the number of Memcached instances
diff --git a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml b/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
index 227fc73d..bc44fa96 100644
--- a/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
+++ b/testdata/go/v3/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml
@@ -113,7 +113,7 @@ spec:
                 - --upstream=http://127.0.0.1:8080/
                 - --logtostderr=true
                 - --v=10
-                image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
+                image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0
                 name: kube-rbac-proxy
                 ports:
                 - containerPort: 8443
@@ -157,7 +157,7 @@ spec:
                   name: cert
                   readOnly: true
               securityContext:
-                runAsUser: 65532
+                runAsNonRoot: true
               terminationGracePeriodSeconds: 10
               volumes:
               - name: cert
diff --git a/testdata/go/v3/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml b/testdata/go/v3/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml
index af219aaf..0be073b7 100644
--- a/testdata/go/v3/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml
+++ b/testdata/go/v3/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml
@@ -37,7 +37,7 @@ spec:
             description: MemcachedSpec defines the desired state of Memcached
             properties:
               foo:
-                description: Foo is an example field of Memcached. Edit Memcached_types.go
+                description: Foo is an example field of Memcached. Edit memcached_types.go
                   to remove/update
                 type: string
               size:
diff --git a/testdata/go/v3/memcached-operator/config/crd/kustomization.yaml b/testdata/go/v3/memcached-operator/config/crd/kustomization.yaml
index 4acd9f4b..f6e957df 100644
--- a/testdata/go/v3/memcached-operator/config/crd/kustomization.yaml
+++ b/testdata/go/v3/memcached-operator/config/crd/kustomization.yaml
@@ -3,18 +3,18 @@
 # It should be run by config/default
 resources:
 - bases/cache.example.com_memcacheds.yaml
-# +kubebuilder:scaffold:crdkustomizeresource
+#+kubebuilder:scaffold:crdkustomizeresource
 
 patchesStrategicMerge:
 # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
 # patches here are for enabling the conversion webhook for each CRD
 #- patches/webhook_in_memcacheds.yaml
-# +kubebuilder:scaffold:crdkustomizewebhookpatch
+#+kubebuilder:scaffold:crdkustomizewebhookpatch
 
 # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
 # patches here are for enabling the CA injection for each CRD
 #- patches/cainjection_in_memcacheds.yaml
-# +kubebuilder:scaffold:crdkustomizecainjectionpatch
+#+kubebuilder:scaffold:crdkustomizecainjectionpatch
 
 # the following config is for teaching kustomize how to do kustomization for CRDs.
 configurations:
diff --git a/testdata/go/v3/memcached-operator/config/default/manager_auth_proxy_patch.yaml b/testdata/go/v3/memcached-operator/config/default/manager_auth_proxy_patch.yaml
index 49b1f1ab..a224be19 100644
--- a/testdata/go/v3/memcached-operator/config/default/manager_auth_proxy_patch.yaml
+++ b/testdata/go/v3/memcached-operator/config/default/manager_auth_proxy_patch.yaml
@@ -10,7 +10,7 @@ spec:
     spec:
       containers:
       - name: kube-rbac-proxy
-        image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
+        image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0
         args:
         - "--secure-listen-address=0.0.0.0:8443"
         - "--upstream=http://127.0.0.1:8080/"
diff --git a/testdata/go/v3/memcached-operator/config/manager/manager.yaml b/testdata/go/v3/memcached-operator/config/manager/manager.yaml
index 70e3f6ac..46a82393 100644
--- a/testdata/go/v3/memcached-operator/config/manager/manager.yaml
+++ b/testdata/go/v3/memcached-operator/config/manager/manager.yaml
@@ -23,7 +23,7 @@ spec:
         control-plane: controller-manager
     spec:
       securityContext:
-        runAsUser: 65532
+        runAsNonRoot: true
       containers:
       - command:
         - /manager
diff --git a/testdata/go/v3/memcached-operator/config/samples/kustomization.yaml b/testdata/go/v3/memcached-operator/config/samples/kustomization.yaml
index 62e56248..42654aa2 100644
--- a/testdata/go/v3/memcached-operator/config/samples/kustomization.yaml
+++ b/testdata/go/v3/memcached-operator/config/samples/kustomization.yaml
@@ -1,4 +1,4 @@
 ## Append samples you want in your CSV to this file as resources ##
 resources:
 - cache_v1alpha1_memcached.yaml
-# +kubebuilder:scaffold:manifestskustomizesamples
+#+kubebuilder:scaffold:manifestskustomizesamples
diff --git a/testdata/go/v3/memcached-operator/config/scorecard/kustomization.yaml b/testdata/go/v3/memcached-operator/config/scorecard/kustomization.yaml
index d73509ee..50cd2d08 100644
--- a/testdata/go/v3/memcached-operator/config/scorecard/kustomization.yaml
+++ b/testdata/go/v3/memcached-operator/config/scorecard/kustomization.yaml
@@ -13,4 +13,4 @@ patchesJson6902:
     version: v1alpha3
     kind: Configuration
     name: config
-# +kubebuilder:scaffold:patchesJson6902
+#+kubebuilder:scaffold:patchesJson6902
diff --git a/testdata/go/v3/memcached-operator/controllers/memcached_controller.go b/testdata/go/v3/memcached-operator/controllers/memcached_controller.go
index 61d2c759..6fe590eb 100644
--- a/testdata/go/v3/memcached-operator/controllers/memcached_controller.go
+++ b/testdata/go/v3/memcached-operator/controllers/memcached_controller.go
@@ -41,11 +41,11 @@ type MemcachedReconciler struct {
 	Scheme *runtime.Scheme
 }
 
-// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
-// +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
-// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
+//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
+//+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
+//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
 
 // Reconcile is part of the main kubernetes reconciliation loop which aims to
 // move the current state of the cluster closer to the desired state.
diff --git a/testdata/go/v3/memcached-operator/controllers/suite_test.go b/testdata/go/v3/memcached-operator/controllers/suite_test.go
index 2d7e4635..97d4bfbf 100644
--- a/testdata/go/v3/memcached-operator/controllers/suite_test.go
+++ b/testdata/go/v3/memcached-operator/controllers/suite_test.go
@@ -31,7 +31,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 
 	cachev1alpha1 "github.com/example/memcached-operator/api/v1alpha1"
-	// +kubebuilder:scaffold:imports
+	//+kubebuilder:scaffold:imports
 )
 
 // These tests use Ginkgo (BDD-style Go testing framework). Refer to
@@ -54,7 +54,8 @@ var _ = BeforeSuite(func() {
 
 	By("bootstrapping test environment")
 	testEnv = &envtest.Environment{
-		CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
+		CRDDirectoryPaths:     []string{filepath.Join("..", "config", "crd", "bases")},
+		ErrorIfCRDPathMissing: true,
 	}
 
 	cfg, err := testEnv.Start()
@@ -64,7 +65,7 @@ var _ = BeforeSuite(func() {
 	err = cachev1alpha1.AddToScheme(scheme.Scheme)
 	Expect(err).NotTo(HaveOccurred())
 
-	// +kubebuilder:scaffold:scheme
+	//+kubebuilder:scaffold:scheme
 
 	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
 	Expect(err).NotTo(HaveOccurred())
diff --git a/testdata/go/v3/memcached-operator/go.mod b/testdata/go/v3/memcached-operator/go.mod
index 64a36fc9..5087da1d 100644
--- a/testdata/go/v3/memcached-operator/go.mod
+++ b/testdata/go/v3/memcached-operator/go.mod
@@ -4,8 +4,6 @@ go 1.15
 
 require (
 	github.com/go-logr/logr v0.3.0
-	github.com/onsi/ginkgo v1.14.1
-	github.com/onsi/gomega v1.10.2
 	k8s.io/api v0.19.2
 	k8s.io/apimachinery v0.19.2
 	k8s.io/client-go v0.19.2
diff --git a/testdata/go/v3/memcached-operator/main.go b/testdata/go/v3/memcached-operator/main.go
index 44e9804e..382e2ce1 100644
--- a/testdata/go/v3/memcached-operator/main.go
+++ b/testdata/go/v3/memcached-operator/main.go
@@ -33,7 +33,7 @@ import (
 
 	cachev1alpha1 "github.com/example/memcached-operator/api/v1alpha1"
 	"github.com/example/memcached-operator/controllers"
-	// +kubebuilder:scaffold:imports
+	//+kubebuilder:scaffold:imports
 )
 
 var (
@@ -45,7 +45,7 @@ func init() {
 	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
 
 	utilruntime.Must(cachev1alpha1.AddToScheme(scheme))
-	// +kubebuilder:scaffold:scheme
+	//+kubebuilder:scaffold:scheme
 }
 
 func main() {
@@ -90,13 +90,13 @@ func main() {
 		setupLog.Error(err, "unable to create webhook", "webhook", "Memcached")
 		os.Exit(1)
 	}
-	// +kubebuilder:scaffold:builder
+	//+kubebuilder:scaffold:builder
 
-	if err := mgr.AddHealthzCheck("health", healthz.Ping); err != nil {
+	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
 		setupLog.Error(err, "unable to set up health check")
 		os.Exit(1)
 	}
-	if err := mgr.AddReadyzCheck("check", healthz.Ping); err != nil {
+	if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
 		setupLog.Error(err, "unable to set up ready check")
 		os.Exit(1)
 	}
diff --git a/testdata/helm/memcached-operator/Makefile b/testdata/helm/memcached-operator/Makefile
index b9dbb0eb..0015500f 100644
--- a/testdata/helm/memcached-operator/Makefile
+++ b/testdata/helm/memcached-operator/Makefile
@@ -101,15 +101,13 @@ HELM_OPERATOR = $(shell which helm-operator)
 endif
 endif
 
-# Generate bundle manifests and metadata, then validate generated files.
-.PHONY: bundle
+.PHONY: bundle ## Generate bundle manifests and metadata, then validate generated files.
 bundle: kustomize
 	operator-sdk generate kustomize manifests --interactive=false -q
 	cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
 	$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
 	operator-sdk bundle validate ./bundle
 
-# Build the bundle image.
-.PHONY: bundle-build
+.PHONY: bundle-build ## Build the bundle image.
 bundle-build:
 	docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
diff --git a/testdata/helm/memcached-operator/PROJECT b/testdata/helm/memcached-operator/PROJECT
index b6dbee32..d41963fd 100644
--- a/testdata/helm/memcached-operator/PROJECT
+++ b/testdata/helm/memcached-operator/PROJECT
@@ -1,11 +1,15 @@
 domain: example.com
 layout: helm.sdk.operatorframework.io/v1
+plugins:
+  manifests.sdk.operatorframework.io/v2: {}
+  scorecard.sdk.operatorframework.io/v2: {}
 projectName: memcached-operator
 resources:
-- group: cache
+- api:
+    crdVersion: v1
+    namespaced: true
+  domain: example.com
+  group: cache
   kind: Memcached
   version: v1alpha1
-version: 3-alpha
-plugins:
-  manifests.sdk.operatorframework.io/v2: {}
-  scorecard.sdk.operatorframework.io/v2: {}
+version: "3"
diff --git a/testdata/helm/memcached-operator/config/crd/kustomization.yaml b/testdata/helm/memcached-operator/config/crd/kustomization.yaml
index f7aed174..c8033c37 100644
--- a/testdata/helm/memcached-operator/config/crd/kustomization.yaml
+++ b/testdata/helm/memcached-operator/config/crd/kustomization.yaml
@@ -3,4 +3,4 @@
 # It should be run by config/default
 resources:
 - bases/cache.example.com_memcacheds.yaml
-# +kubebuilder:scaffold:crdkustomizeresource
+#+kubebuilder:scaffold:crdkustomizeresource
diff --git a/testdata/helm/memcached-operator/config/rbac/role.yaml b/testdata/helm/memcached-operator/config/rbac/role.yaml
index ac20f3c8..6746dc22 100644
--- a/testdata/helm/memcached-operator/config/rbac/role.yaml
+++ b/testdata/helm/memcached-operator/config/rbac/role.yaml
@@ -111,5 +111,5 @@ rules:
   - update
   - watch
 
-# +kubebuilder:scaffold:rules
+#+kubebuilder:scaffold:rules
 
diff --git a/testdata/helm/memcached-operator/config/samples/kustomization.yaml b/testdata/helm/memcached-operator/config/samples/kustomization.yaml
index 62e56248..42654aa2 100644
--- a/testdata/helm/memcached-operator/config/samples/kustomization.yaml
+++ b/testdata/helm/memcached-operator/config/samples/kustomization.yaml
@@ -1,4 +1,4 @@
 ## Append samples you want in your CSV to this file as resources ##
 resources:
 - cache_v1alpha1_memcached.yaml
-# +kubebuilder:scaffold:manifestskustomizesamples
+#+kubebuilder:scaffold:manifestskustomizesamples
diff --git a/testdata/helm/memcached-operator/config/scorecard/kustomization.yaml b/testdata/helm/memcached-operator/config/scorecard/kustomization.yaml
index d73509ee..50cd2d08 100644
--- a/testdata/helm/memcached-operator/config/scorecard/kustomization.yaml
+++ b/testdata/helm/memcached-operator/config/scorecard/kustomization.yaml
@@ -13,4 +13,4 @@ patchesJson6902:
     version: v1alpha3
     kind: Configuration
     name: config
-# +kubebuilder:scaffold:patchesJson6902
+#+kubebuilder:scaffold:patchesJson6902
diff --git a/testdata/helm/memcached-operator/watches.yaml b/testdata/helm/memcached-operator/watches.yaml
index 742c795a..6cb4c94d 100644
--- a/testdata/helm/memcached-operator/watches.yaml
+++ b/testdata/helm/memcached-operator/watches.yaml
@@ -3,4 +3,4 @@
   version: v1alpha1
   kind: Memcached
   chart: helm-charts/memcached
-# +kubebuilder:scaffold:watch
+#+kubebuilder:scaffold:watch
diff --git a/website/content/en/docs/building-operators/golang/references/envtest-setup.md b/website/content/en/docs/building-operators/golang/references/envtest-setup.md
index a1a0c4de..bc9381b0 100644
--- a/website/content/en/docs/building-operators/golang/references/envtest-setup.md
+++ b/website/content/en/docs/building-operators/golang/references/envtest-setup.md
@@ -10,7 +10,7 @@ By default, Go-based operators are scaffolded to make use of controller-runtime'
 ```sh
 # Run tests
 ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
-test: generate fmt vet manifests
+test: manifests generate fmt vet
         mkdir -p ${ENVTEST_ASSETS_DIR}
         test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/master/hack/setup-envtest.sh
         source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out