From 4ef451c2bd122803f54d7000d04c531fee1cb534 Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Mon, 11 Nov 2024 15:17:55 -0500 Subject: [PATCH] v2: correct and test controller RBAC Prior to this commit the declared permissions for the RedpandaReconciler had become out of date. This went unnoticed due to tests utilizing admin permissions or the inflated permissions required for executing `rpk debug bundle`. This commit corrects the permission declaration of the RedpandaReconciler, updates its tests to use the ClusterRole and Role generated by controller-gen, and adds a test to statically assert the correctness of the permissions. --- operator/config/rbac/bases/operator/role.yaml | 4 + .../config/rbac/v2-manager-role/role.yaml | 39 +- .../redpanda/redpanda_controller.go | 12 +- .../redpanda/redpanda_controller_test.go | 191 ++++++++-- .../internal/controller/redpanda/role.yaml | 334 ++++++++++++++++++ operator/internal/testenv/testenv.go | 26 +- taskfiles/k8s.yml | 3 + 7 files changed, 558 insertions(+), 51 deletions(-) create mode 100644 operator/internal/controller/redpanda/role.yaml diff --git a/operator/config/rbac/bases/operator/role.yaml b/operator/config/rbac/bases/operator/role.yaml index e6549fefe..4ad5ccf09 100644 --- a/operator/config/rbac/bases/operator/role.yaml +++ b/operator/config/rbac/bases/operator/role.yaml @@ -155,6 +155,7 @@ rules: - clusterroles verbs: - create + - delete - get - list - patch @@ -215,6 +216,8 @@ rules: resources: - configmaps - pods + - rolebindings + - roles - secrets - serviceaccounts - services @@ -394,6 +397,7 @@ rules: - apiGroups: - monitoring.coreos.com resources: + - podmonitors - servicemonitors verbs: - create diff --git a/operator/config/rbac/v2-manager-role/role.yaml b/operator/config/rbac/v2-manager-role/role.yaml index d798b5af5..e53898f97 100644 --- a/operator/config/rbac/v2-manager-role/role.yaml +++ b/operator/config/rbac/v2-manager-role/role.yaml @@ -53,6 +53,19 @@ rules: - get - patch - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -63,15 +76,15 @@ rules: - apiGroups: - "" resources: - - events + - configmaps + - pods + - rolebindings + - roles + - secrets + - serviceaccounts + - services verbs: - create - - patch -- apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - delete - get - list @@ -81,12 +94,15 @@ rules: - apiGroups: - "" resources: - - pods - - secrets - - serviceaccounts - - services + - events verbs: - create + - patch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: - delete - get - list @@ -231,6 +247,7 @@ rules: - apiGroups: - monitoring.coreos.com resources: + - podmonitors - servicemonitors verbs: - create diff --git a/operator/internal/controller/redpanda/redpanda_controller.go b/operator/internal/controller/redpanda/redpanda_controller.go index 43b556d50..0c48ea592 100644 --- a/operator/internal/controller/redpanda/redpanda_controller.go +++ b/operator/internal/controller/redpanda/redpanda_controller.go @@ -104,22 +104,20 @@ type RedpandaReconciler struct { // any resource that Redpanda helm creates and flux controller needs to reconcile them // +kubebuilder:rbac:groups="",namespace=default,resources=pods,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,namespace=default,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,namespace=default,resources=roles,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles;clusterrolebindings,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,namespace=default,resources=roles;rolebindings,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=batch,namespace=default,resources=jobs,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=core,namespace=default,resources=secrets,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=core,namespace=default,resources=services,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=core,namespace=default,resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,namespace=default,resources=configmaps;roles;rolebindings;secrets;services;serviceaccounts,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=apps,namespace=default,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete; // +kubebuilder:rbac:groups=policy,namespace=default,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=apps,namespace=default,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=cert-manager.io,namespace=default,resources=certificates,verbs=get;create;update;patch;delete;list;watch // +kubebuilder:rbac:groups=cert-manager.io,namespace=default,resources=issuers,verbs=get;create;update;patch;delete;list;watch -// +kubebuilder:rbac:groups="monitoring.coreos.com",namespace=default,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=networking.k8s.io,namespace=default,resources=ingresses,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="monitoring.coreos.com",namespace=default,resources=podmonitors;servicemonitors,verbs=get;list;watch;create;update;patch;delete // Console chart // +kubebuilder:rbac:groups=autoscaling,namespace=default,resources=horizontalpodautoscalers,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=networking.k8s.io,namespace=default,resources=ingresses,verbs=get;list;watch;create;update;patch;delete // redpanda resources // +kubebuilder:rbac:groups=cluster.redpanda.com,namespace=default,resources=redpandas,verbs=get;list;watch;create;update;patch;delete diff --git a/operator/internal/controller/redpanda/redpanda_controller_test.go b/operator/internal/controller/redpanda/redpanda_controller_test.go index f1d7c0d73..27cfe21ec 100644 --- a/operator/internal/controller/redpanda/redpanda_controller_test.go +++ b/operator/internal/controller/redpanda/redpanda_controller_test.go @@ -11,9 +11,11 @@ package redpanda_test import ( "context" + _ "embed" "encoding/json" "fmt" "math/rand" + "slices" "sort" "strings" "testing" @@ -36,6 +38,7 @@ import ( "github.com/stretchr/testify/suite" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -47,6 +50,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) +// operatorRBAC is the ClusterRole and Role generated via controller-gen and +// goembeded so it can be used for tests. +// +//go:embed role.yaml +var operatorRBAC []byte + // NB: This test setup is largely incompatible with webhooks. Though we might // be able to figure something freaky out. func TestRedpandaController(t *testing.T) { @@ -254,7 +263,7 @@ func (s *RedpandaControllerSuite) TestManagedDecommission() { "operator.redpanda.com/managed-decommission": "2999-12-31T00:00:00Z", } - s.applyAndWaitFor(rp, func(o client.Object) bool { + s.applyAndWaitFor(func(o client.Object) bool { rp := o.(*redpandav1alpha2.Redpanda) for _, cond := range rp.Status.Conditions { @@ -263,7 +272,7 @@ func (s *RedpandaControllerSuite) TestManagedDecommission() { } } return false - }) + }, rp) s.waitFor(rp, func(o client.Object) bool { rp := o.(*redpandav1alpha2.Redpanda) @@ -318,7 +327,7 @@ func (s *RedpandaControllerSuite) TestClusterSettings() { rp.Spec.ClusterSpec.Config.Cluster = &runtime.RawExtension{Raw: asJson} s.applyAndWait(rp) - s.applyAndWaitFor(rp, func(o client.Object) bool { + s.applyAndWaitFor(func(o client.Object) bool { rp := o.(*redpandav1alpha2.Redpanda) for _, cond := range rp.Status.Conditions { if cond.Type == redpandav1alpha2.ClusterConfigSynced { @@ -326,7 +335,7 @@ func (s *RedpandaControllerSuite) TestClusterSettings() { } } return false - }) + }, rp) } s.applyAndWait(&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ @@ -426,14 +435,14 @@ func (s *RedpandaControllerSuite) SetupSuite() { // rest config given to the manager. s.ctx = context.Background() s.env = testenv.New(t, testenv.Options{ - Scheme: controller.UnifiedScheme, + Scheme: controller.V2Scheme, CRDs: crds.All(), Logger: testr.New(t), }) s.client = s.env.Client() - s.env.SetupManager(func(mgr ctrl.Manager) error { + s.env.SetupManager(s.setupRBAC(), func(mgr ctrl.Manager) error { controllers := flux.NewFluxControllers(mgr, fluxclient.Options{}, fluxclient.KubeConfigOptions{}) for _, controller := range controllers { if err := controller.SetupWithManager(s.ctx, mgr); err != nil { @@ -462,18 +471,81 @@ func (s *RedpandaControllerSuite) SetupSuite() { }) } -func (s *RedpandaControllerSuite) minimalRP(useFlux bool) *redpandav1alpha2.Redpanda { +func (s *RedpandaControllerSuite) setupRBAC() string { + roles, err := kube.DecodeYAML(operatorRBAC, s.client.Scheme()) + s.Require().NoError(err) + + role := roles[1].(*rbacv1.Role) + clusterRole := roles[0].(*rbacv1.ClusterRole) + + // Inject additional permissions required for running in testenv. + role.Rules = append(role.Rules, rbacv1.PolicyRule{ + APIGroups: []string{""}, + Resources: []string{"pods/portforward"}, + Verbs: []string{"*"}, + }) + + name := "testenv-" + s.randString(6) + + role.Name = name + role.Namespace = s.env.Namespace() + clusterRole.Name = name + clusterRole.Namespace = s.env.Namespace() + + s.applyAndWait(roles...) + s.applyAndWait( + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, + &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Subjects: []rbacv1.Subject{ + {Kind: "ServiceAccount", Namespace: s.env.Namespace(), Name: name}, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: role.Name, + }, + }, + &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Subjects: []rbacv1.Subject{ + {Kind: "ServiceAccount", Namespace: s.env.Namespace(), Name: name}, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: clusterRole.Name, + }, + }, + ) + + return name +} + +func (s *RedpandaControllerSuite) randString(length int) string { const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" - name := "rp-" + name := "" for i := 0; i < 6; i++ { //nolint:gosec // not meant to be a secure random string. name += string(alphabet[rand.Intn(len(alphabet))]) } + return name +} + +func (s *RedpandaControllerSuite) minimalRP(useFlux bool) *redpandav1alpha2.Redpanda { return &redpandav1alpha2.Redpanda{ ObjectMeta: metav1.ObjectMeta{ - Name: name, // GenerateName doesn't play nice with SSA. + Name: "rp-" + s.randString(6), // GenerateName doesn't play nice with SSA. }, Spec: redpandav1alpha2.RedpandaSpec{ ChartRef: redpandav1alpha2.ChartRef{ @@ -541,45 +613,50 @@ func (s *RedpandaControllerSuite) deleteAndWait(obj client.Object) { })) } -func (s *RedpandaControllerSuite) applyAndWait(obj client.Object) { - s.applyAndWaitFor(obj, func(o client.Object) bool { +func (s *RedpandaControllerSuite) applyAndWait(objs ...client.Object) { + s.applyAndWaitFor(func(obj client.Object) bool { switch obj := obj.(type) { case *redpandav1alpha2.Redpanda: ready := apimeta.IsStatusConditionTrue(obj.Status.Conditions, "Ready") upToDate := obj.Generation != 0 && obj.Generation == obj.Status.ObservedGeneration return upToDate && ready - case *corev1.Secret, *corev1.ConfigMap: + case *corev1.Secret, *corev1.ConfigMap, *corev1.ServiceAccount, + *rbacv1.ClusterRole, *rbacv1.Role, *rbacv1.RoleBinding, *rbacv1.ClusterRoleBinding: return true default: s.T().Fatalf("unhandled object %T in applyAndWait", obj) panic("unreachable") } - }) + }, objs...) } -func (s *RedpandaControllerSuite) applyAndWaitFor(obj client.Object, cond func(client.Object) bool) { - gvk, err := s.client.GroupVersionKindFor(obj) - s.NoError(err) +func (s *RedpandaControllerSuite) applyAndWaitFor(cond func(client.Object) bool, objs ...client.Object) { + for _, obj := range objs { + gvk, err := s.client.GroupVersionKindFor(obj) + s.NoError(err) - obj.SetManagedFields(nil) - obj.GetObjectKind().SetGroupVersionKind(gvk) + obj.SetManagedFields(nil) + obj.GetObjectKind().SetGroupVersionKind(gvk) - s.Require().NoError(s.client.Patch(s.ctx, obj, client.Apply, client.ForceOwnership, client.FieldOwner("tests"))) + s.Require().NoError(s.client.Patch(s.ctx, obj, client.Apply, client.ForceOwnership, client.FieldOwner("tests"))) + } - s.NoError(wait.PollUntilContextTimeout(s.ctx, 5*time.Second, 5*time.Minute, false, func(ctx context.Context) (done bool, err error) { - if err := s.client.Get(ctx, client.ObjectKeyFromObject(obj), obj); err != nil { - return false, err - } + for _, obj := range objs { + s.NoError(wait.PollUntilContextTimeout(s.ctx, 5*time.Second, 5*time.Minute, false, func(ctx context.Context) (done bool, err error) { + if err := s.client.Get(ctx, client.ObjectKeyFromObject(obj), obj); err != nil { + return false, err + } - if cond(obj) { - return true, nil - } + if cond(obj) { + return true, nil + } - s.T().Logf("waiting for %T %q to be ready", obj, obj.GetName()) - return false, nil - })) + s.T().Logf("waiting for %T %q to be ready", obj, obj.GetName()) + return false, nil + })) + } } func (s *RedpandaControllerSuite) waitFor(obj client.Object, cond func(client.Object) bool) { @@ -686,3 +763,59 @@ func TestPostInstallUpgradeJobIndex(t *testing.T) { // `clusterConfigfor` utilizes. require.Equal(t, "bootstrap-yaml-envsubst", job.Spec.Template.Spec.InitContainers[0].Name) } + +// TestControllerRBAC asserts that the declared Roles and ClusterRoles of the +// RedpandaReconciler line up with all the resource types it needs to manage. +func TestControllerRBAC(t *testing.T) { + scheme := controller.V2Scheme + + expectedVerbs := []string{"create", "delete", "get", "list", "patch", "update", "watch"} + + roles, err := kube.DecodeYAML(operatorRBAC, scheme) + require.NoError(t, err) + + role := roles[1].(*rbacv1.Role) + clusterRole := roles[0].(*rbacv1.ClusterRole) + + for _, typ := range redpandachart.Types() { + gkvs, _, err := scheme.ObjectKinds(typ) + require.NoError(t, err) + + require.Len(t, gkvs, 1) + gvk := gkvs[0] + + rules := role.Rules + if !isNamespaced(typ) { + rules = clusterRole.Rules + } + + group := gvk.Group + kind := pluralize(gvk.Kind) + + idx := slices.IndexFunc(rules, func(rule rbacv1.PolicyRule) bool { + return slices.Contains(rule.APIGroups, group) && slices.Contains(rule.Resources, kind) + }) + + require.NotEqual(t, -1, idx, "missing rules for %s %s", gvk.Group, kind) + require.EqualValues(t, expectedVerbs, rules[idx].Verbs, "incorrect verbs for %s %s", gvk.Group, kind) + } +} + +func isNamespaced(obj client.Object) bool { + switch obj.(type) { + case *corev1.Namespace, *rbacv1.ClusterRole, *rbacv1.ClusterRoleBinding: + return false + default: + return true + } +} + +func pluralize(kind string) string { + switch kind[len(kind)-1] { + case 's': + return strings.ToLower(kind) + "es" + + default: + return strings.ToLower(kind) + "s" + } +} diff --git a/operator/internal/controller/redpanda/role.yaml b/operator/internal/controller/redpanda/role.yaml new file mode 100644 index 000000000..e53898f97 --- /dev/null +++ b/operator/internal/controller/redpanda/role.yaml @@ -0,0 +1,334 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: v2-manager-role +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - cluster.redpanda.com + resources: + - schemas + - topics + - users + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - cluster.redpanda.com + resources: + - schemas/finalizers + - topics/finalizers + - users/finalizers + verbs: + - update +- apiGroups: + - cluster.redpanda.com + resources: + - schemas/status + - topics/status + - users/status + verbs: + - get + - patch + - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: v2-manager-role + namespace: default +rules: +- apiGroups: + - "" + resources: + - configmaps + - pods + - rolebindings + - roles + - secrets + - serviceaccounts + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/status + verbs: + - patch + - update +- apiGroups: + - apps + resources: + - deployments + - replicasets + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets/status + verbs: + - patch + - update +- apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - cert-manager.io + resources: + - certificates + - issuers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - cluster.redpanda.com + resources: + - redpandas + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - cluster.redpanda.com + resources: + - redpandas/finalizers + - schemas/finalizers + - topics/finalizers + - users/finalizers + verbs: + - update +- apiGroups: + - cluster.redpanda.com + resources: + - redpandas/status + - schemas/status + - topics/status + - users/status + verbs: + - get + - patch + - update +- apiGroups: + - cluster.redpanda.com + resources: + - schemas + - topics + - users + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases/finalizers + verbs: + - update +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases/status + verbs: + - get + - patch + - update +- apiGroups: + - monitoring.coreos.com + resources: + - podmonitors + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - buckets + - gitrepositories + - gitrepository + - helmcharts + - helmrepositories + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - gitrepository/finalizers + - helmcharts/finalizers + - helmrepositories/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - gitrepository/status + - helmcharts/status + - helmrepositories/status + verbs: + - get + - patch + - update diff --git a/operator/internal/testenv/testenv.go b/operator/internal/testenv/testenv.go index dedd8878c..af84d8a9d 100644 --- a/operator/internal/testenv/testenv.go +++ b/operator/internal/testenv/testenv.go @@ -11,6 +11,7 @@ package testenv import ( "context" + "fmt" "testing" "time" @@ -128,13 +129,23 @@ func (e *Env) Client() client.Client { return e.wrapClient(e.client()) } -func (e *Env) SetupManager(fn func(ctrl.Manager) error) { +func (e *Env) Namespace() string { + return e.namespace.Name +} + +func (e *Env) SetupManager(serviceAccount string, fn func(ctrl.Manager) error) { + // Bind the managers base config to a ServiceAccount via the "Impersonate" + // feature. This ensures that any permissions/RBAC issues get caught by + // theses tests as e.config has Admin permissions. + config := rest.CopyConfig(e.config) + config.Impersonate.UserName = fmt.Sprintf("system:serviceaccount:%s:%s", e.Namespace(), serviceAccount) + // TODO: Webhooks likely aren't going to place nicely with this method of // testing. The Kube API server will have to dial out of the cluster to the // local machine which could prove to be difficult across all docker/docker // in docker environments. // See also https://k3d.io/v5.4.6/faq/faq/?h=host#how-to-access-services-like-a-database-running-on-my-docker-host-machine - manager, err := ctrl.NewManager(e.config, ctrl.Options{ + manager, err := ctrl.NewManager(config, ctrl.Options{ Cache: cache.Options{ // Limit this manager to only interacting with objects within our // namespace. @@ -180,13 +191,20 @@ func (e *Env) client() client.Client { } func (e *Env) wrapClient(c client.Client) client.Client { + gvk, err := c.GroupVersionKindFor(e.namespace) + if err != nil { + panic(err) + } + + apiVersion, kind := gvk.ToAPIVersionAndKind() + // Bind all operations to this namespace. We'll delete it at the end of this test. c = client.NewNamespacedClient(c, e.namespace.Name) // For any non-namespaced resources, we'll attach an OwnerReference to our // Namespace to ensure they get cleaned up as well. c = newOwnedClient(c, metav1.OwnerReference{ - APIVersion: e.namespace.GetObjectKind().GroupVersionKind().GroupVersion().String(), - Kind: e.namespace.GetObjectKind().GroupVersionKind().Kind, + APIVersion: apiVersion, + Kind: kind, UID: e.namespace.UID, Name: e.namespace.Name, BlockOwnerDeletion: ptr.To(true), diff --git a/taskfiles/k8s.yml b/taskfiles/k8s.yml index 3210ba960..94a478fea 100644 --- a/taskfiles/k8s.yml +++ b/taskfiles/k8s.yml @@ -31,6 +31,9 @@ tasks: paths='./internal/controller/redpanda/...' \ rbac:roleName=v2-manager-role \ output:rbac:artifacts:config=config/rbac/v2-manager-role + # controller-gen won't output to multiple directories and we need a copy + # of resources for tests, so cp it is. + - cp ./config/rbac/v2-manager-role/*.yaml ./internal/controller/redpanda/ - | controller-gen \ paths='./internal/controller/vectorized/...' \