diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/controllers/controller_suitetest.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/controllers/controller_suitetest.go index e2f8796bc5f..b28bd11a326 100644 --- a/pkg/plugins/golang/v4/scaffolds/internal/templates/controllers/controller_suitetest.go +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/controllers/controller_suitetest.go @@ -136,6 +136,7 @@ package controller {{end}} import ( + "context" "fmt" "path/filepath" "runtime" diff --git a/test/testdata/generate.sh b/test/testdata/generate.sh index bc16751b992..f97674d66b7 100755 --- a/test/testdata/generate.sh +++ b/test/testdata/generate.sh @@ -68,6 +68,10 @@ function scaffold_test_project { $kb create api --group apps --version v1 --kind Deployment --controller=true --resource=false --make=false $kb create api --group foo --version v1 --kind Bar --controller=true --resource=true --make=false $kb create api --group fiz --version v1 --kind Bar --controller=true --resource=true --make=false + + # Create controller for resources which has empty group + $kb create api --group "" --version v1 --kind Pod --controller=true --resource=false --make=false + $kb create api --group "" --version v1 --kind ConfigMap --controller=true --resource=false --make=false fi if [[ $project =~ multigroup ]] || [[ $project =~ with-plugins ]] ; then diff --git a/testdata/project-v4-multigroup/PROJECT b/testdata/project-v4-multigroup/PROJECT index ea5536b0583..5c92371511d 100644 --- a/testdata/project-v4-multigroup/PROJECT +++ b/testdata/project-v4-multigroup/PROJECT @@ -125,6 +125,14 @@ resources: kind: Bar path: sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/api/fiz/v1 version: v1 +- controller: true + domain: testproject.org + kind: Pod + version: v1 +- controller: true + domain: testproject.org + kind: ConfigMap + version: v1 - api: crdVersion: v1 namespaced: true diff --git a/testdata/project-v4-multigroup/cmd/main.go b/testdata/project-v4-multigroup/cmd/main.go index e7dc7e0a55e..69ae3c135af 100644 --- a/testdata/project-v4-multigroup/cmd/main.go +++ b/testdata/project-v4-multigroup/cmd/main.go @@ -45,6 +45,7 @@ import ( shipv1 "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/api/ship/v1" shipv1beta1 "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/api/ship/v1beta1" shipv2alpha1 "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/api/ship/v2alpha1" + "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller" appscontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/apps" crewcontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/crew" examplecomcontroller "sigs.k8s.io/kubebuilder/testdata/project-v4-multigroup/internal/controller/example.com" @@ -267,6 +268,20 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Bar") os.Exit(1) } + if err = (&controller.PodReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Pod") + os.Exit(1) + } + if err = (&controller.ConfigMapReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ConfigMap") + os.Exit(1) + } if err = (&examplecomcontroller.MemcachedReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), diff --git a/testdata/project-v4-multigroup/config/rbac/role.yaml b/testdata/project-v4-multigroup/config/rbac/role.yaml index 3bf7c0bed2f..b640a3fbe64 100644 --- a/testdata/project-v4-multigroup/config/rbac/role.yaml +++ b/testdata/project-v4-multigroup/config/rbac/role.yaml @@ -216,3 +216,32 @@ rules: - get - patch - update +- apiGroups: + - testproject.org + resources: + - configmaps + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - testproject.org + resources: + - configmaps/finalizers + - pods/finalizers + verbs: + - update +- apiGroups: + - testproject.org + resources: + - configmaps/status + - pods/status + verbs: + - get + - patch + - update diff --git a/testdata/project-v4-multigroup/dist/install.yaml b/testdata/project-v4-multigroup/dist/install.yaml index 05a31522eeb..c43d3715cd7 100644 --- a/testdata/project-v4-multigroup/dist/install.yaml +++ b/testdata/project-v4-multigroup/dist/install.yaml @@ -1347,6 +1347,35 @@ rules: - get - patch - update +- apiGroups: + - testproject.org + resources: + - configmaps + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - testproject.org + resources: + - configmaps/finalizers + - pods/finalizers + verbs: + - update +- apiGroups: + - testproject.org + resources: + - configmaps/status + - pods/status + verbs: + - get + - patch + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/testdata/project-v4-multigroup/internal/controller/configmap_controller.go b/testdata/project-v4-multigroup/internal/controller/configmap_controller.go new file mode 100644 index 00000000000..d1265b3b34b --- /dev/null +++ b/testdata/project-v4-multigroup/internal/controller/configmap_controller.go @@ -0,0 +1,62 @@ +/* +Copyright 2024 The Kubernetes authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// ConfigMapReconciler reconciles a ConfigMap object +type ConfigMapReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=testproject.org,resources=configmaps,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=testproject.org,resources=configmaps/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=testproject.org,resources=configmaps/finalizers,verbs=update + +// 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 ConfigMap 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.19.0/pkg/reconcile +func (r *ConfigMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ConfigMapReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + // Uncomment the following line adding a pointer to an instance of the controlled resource as an argument + // For(). + Named("configmap"). + Complete(r) +} diff --git a/testdata/project-v4-multigroup/internal/controller/configmap_controller_test.go b/testdata/project-v4-multigroup/internal/controller/configmap_controller_test.go new file mode 100644 index 00000000000..7120fd64028 --- /dev/null +++ b/testdata/project-v4-multigroup/internal/controller/configmap_controller_test.go @@ -0,0 +1,32 @@ +/* +Copyright 2024 The Kubernetes authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("ConfigMap Controller", func() { + Context("When reconciling a resource", func() { + + It("should successfully reconcile the resource", func() { + + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/testdata/project-v4-multigroup/internal/controller/pod_controller.go b/testdata/project-v4-multigroup/internal/controller/pod_controller.go new file mode 100644 index 00000000000..f3e2a844307 --- /dev/null +++ b/testdata/project-v4-multigroup/internal/controller/pod_controller.go @@ -0,0 +1,62 @@ +/* +Copyright 2024 The Kubernetes authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// PodReconciler reconciles a Pod object +type PodReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=testproject.org,resources=pods,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=testproject.org,resources=pods/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=testproject.org,resources=pods/finalizers,verbs=update + +// 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 Pod 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.19.0/pkg/reconcile +func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *PodReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + // Uncomment the following line adding a pointer to an instance of the controlled resource as an argument + // For(). + Named("pod"). + Complete(r) +} diff --git a/testdata/project-v4-multigroup/internal/controller/pod_controller_test.go b/testdata/project-v4-multigroup/internal/controller/pod_controller_test.go new file mode 100644 index 00000000000..ebcfc7174c1 --- /dev/null +++ b/testdata/project-v4-multigroup/internal/controller/pod_controller_test.go @@ -0,0 +1,32 @@ +/* +Copyright 2024 The Kubernetes authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("Pod Controller", func() { + Context("When reconciling a resource", func() { + + It("should successfully reconcile the resource", func() { + + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/testdata/project-v4-multigroup/internal/controller/suite_test.go b/testdata/project-v4-multigroup/internal/controller/suite_test.go new file mode 100644 index 00000000000..e871cf888d2 --- /dev/null +++ b/testdata/project-v4-multigroup/internal/controller/suite_test.go @@ -0,0 +1,91 @@ +/* +Copyright 2024 The Kubernetes authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "fmt" + "path/filepath" + "runtime" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment +var ctx context.Context +var cancel context.CancelFunc + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: false, + + // The BinaryAssetsDirectory is only required if you want to run the tests directly + // without call the makefile target test. If not informed it will look for the + // default path defined in controller-runtime which is /usr/local/kubebuilder/. + // Note that you must have the required binaries setup under the bin directory to perform + // the tests directly. When we run make test it will be setup and used automatically. + BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", + fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + // +kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +})