diff --git a/.changes/unreleased/charts-redpanda-Fixed-20261009-120000.yaml b/.changes/unreleased/charts-redpanda-Fixed-20261009-120000.yaml new file mode 100644 index 0000000000..cfab2c3cdd --- /dev/null +++ b/.changes/unreleased/charts-redpanda-Fixed-20261009-120000.yaml @@ -0,0 +1,7 @@ +project: charts/redpanda +kind: Fixed +body: |- + `ClusterRole` and `ClusterRoleBinding` names are no longer truncated to 63 + characters. When `-` reached that limit, every + `ClusterRole` rendered under the same name, overwriting each other's rules. +time: 2026-10-09T12:00:00.000000-04:00 diff --git a/.changes/unreleased/operator-Changed-20260916-153000.yaml b/.changes/unreleased/operator-Changed-20260916-153000.yaml new file mode 100644 index 0000000000..55a2dbdfb3 --- /dev/null +++ b/.changes/unreleased/operator-Changed-20260916-153000.yaml @@ -0,0 +1,14 @@ +project: operator +kind: Changed +body: |- + StretchCluster's per-pool sidecar `Role` no longer grants read on `pods` or + `apps/statefulsets`, and its rack-awareness `ClusterRole` no longer grants + `list` or `watch` on `nodes`. The operator's own `ClusterRole` drops the same + `nodes` verbs on multicluster installs. + + The sidecar only ever ran the broker probe there, which talks to the Redpanda + Admin API rather than the Kubernetes one; the controllers that read those + resources are gated behind flags the StretchCluster renderer doesn't pass. + Rack awareness only ever `get`s the broker's own `Node`. The operator owns + these objects, so they narrow on upgrade with no action needed. +time: 2026-09-16T15:30:00-04:00 diff --git a/.changes/unreleased/operator-Fixed-20261009-120000.yaml b/.changes/unreleased/operator-Fixed-20261009-120000.yaml new file mode 100644 index 0000000000..91e084a9ed --- /dev/null +++ b/.changes/unreleased/operator-Fixed-20261009-120000.yaml @@ -0,0 +1,8 @@ +project: operator +kind: Fixed +body: |- + StretchCluster `ClusterRole` and `ClusterRoleBinding` names are no longer + truncated to 63 characters. When `-` reached that + limit, a pool's `ClusterRole`s rendered under the same name, overwriting each + other's rules. +time: 2026-10-09T12:00:00.000000-04:00 diff --git a/charts/redpanda/chart/chart.go b/charts/redpanda/chart/chart.go index 61c1d74c1d..7e94f55299 100644 --- a/charts/redpanda/chart/chart.go +++ b/charts/redpanda/chart/chart.go @@ -151,21 +151,7 @@ func renderResources(state *RenderState) []kube.Object { manifests = append(manifests, obj) } - for _, obj := range Roles(state) { - manifests = append(manifests, obj) - } - - for _, obj := range ClusterRoles(state) { - manifests = append(manifests, obj) - } - - for _, obj := range RoleBindings(state) { - manifests = append(manifests, obj) - } - - for _, obj := range ClusterRoleBindings(state) { - manifests = append(manifests, obj) - } + manifests = append(manifests, RoleSet(state).Render()...) for _, obj := range LoadBalancerServices(state) { manifests = append(manifests, obj) @@ -195,7 +181,6 @@ func renderResources(state *RenderState) []kube.Object { // //go:embed Chart.lock //go:embed Chart.yaml -//go:embed files/* //go:embed templates/* //go:embed values.schema.json //go:embed values.yaml diff --git a/charts/redpanda/chart/rbac.go b/charts/redpanda/chart/rbac.go index 6b50e1f7c7..47a061697f 100644 --- a/charts/redpanda/chart/rbac.go +++ b/charts/redpanda/chart/rbac.go @@ -11,156 +11,37 @@ package chart import ( - "fmt" - - rbacv1 "k8s.io/api/rbac/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - + "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25" "github.com/redpanda-data/redpanda-operator/gotohelm/helmette" ) -func Roles(state *RenderState) []*rbacv1.Role { - // path of static role definition -> Enabled - mapping := map[string]bool{ - "files/sidecar.Role.yaml": state.Values.RBAC.Enabled, - "files/pvcunbinder.Role.yaml": state.Values.RBAC.Enabled && state.Values.Statefulset.SideCars.PVCUnbinder.Enabled, - "files/decommission.Role.yaml": state.Values.RBAC.Enabled && state.Values.Statefulset.SideCars.BrokerDecommissioner.Enabled, - "files/rpk-debug-bundle.Role.yaml": state.Values.RBAC.Enabled && state.Values.RBAC.RPKDebugBundle, - } - - var roles []*rbacv1.Role - for file, enabled := range helmette.SortedMap(mapping) { - if !enabled { - continue - } - - role := helmette.FromYaml[rbacv1.Role](state.Dot.Files.Get(file)) - - // Populated all chart values on the loaded static Role. - role.ObjectMeta.Name = fmt.Sprintf("%s-%s", Fullname(state), role.ObjectMeta.Name) - role.ObjectMeta.Namespace = state.Release.Namespace - role.ObjectMeta.Labels = FullLabels(state) - role.ObjectMeta.Annotations = helmette.Merge( - map[string]string{}, - state.Values.ServiceAccount.Annotations, // For backwards compatibility - state.Values.RBAC.Annotations, - FullAnnotations(state), - ) - - roles = append(roles, &role) +// RoleSet resolves this chart's values into the shared RBAC set, which never +// sees chart values. It is the chart's one translation function for everything +// RBAC shaped. +func RoleSet(state *RenderState) *redpanda.RoleSet { + annotations := helmette.Merge( + map[string]string{}, + state.Values.ServiceAccount.Annotations, // For backwards compatibility + state.Values.RBAC.Annotations, + FullAnnotations(state), + ) + + return &redpanda.RoleSet{ + Prefix: Fullname(state), + Namespace: state.Release.Namespace, + Labels: FullLabels(state), + Annotations: annotations, + ServiceAccount: ServiceAccountName(state), + Roles: redpanda.Roles{ + Decommission: state.Values.RBAC.Enabled && state.Values.Statefulset.SideCars.BrokerDecommissioner.Enabled, + PVCUnbinder: state.Values.RBAC.Enabled && state.Values.Statefulset.SideCars.PVCUnbinder.Enabled, + RPKDebugBundle: state.Values.RBAC.Enabled && state.Values.RBAC.RPKDebugBundle, + Sidecar: state.Values.RBAC.Enabled, + }, + ClusterRoles: redpanda.ClusterRoles{ + Decommission: state.Values.RBAC.Enabled && state.Values.Statefulset.SideCars.BrokerDecommissioner.Enabled, + PVCUnbinder: state.Values.RBAC.Enabled && state.Values.Statefulset.SideCars.PVCUnbinder.Enabled, + RackAwareness: state.Values.RBAC.Enabled && state.Values.RackAwareness.Enabled, + }, } - - return roles -} - -func ClusterRoles(state *RenderState) []*rbacv1.ClusterRole { - // path of static ClusterRole definition -> Enabled - mapping := map[string]bool{ - "files/pvcunbinder.ClusterRole.yaml": state.Values.RBAC.Enabled && state.Values.Statefulset.SideCars.PVCUnbinder.Enabled, - "files/decommission.ClusterRole.yaml": state.Values.RBAC.Enabled && state.Values.Statefulset.SideCars.BrokerDecommissioner.Enabled, - "files/rack-awareness.ClusterRole.yaml": state.Values.RBAC.Enabled && state.Values.RackAwareness.Enabled, - } - - var clusterRoles []*rbacv1.ClusterRole - for file, enabled := range helmette.SortedMap(mapping) { - if !enabled { - continue - } - - role := helmette.FromYaml[rbacv1.ClusterRole](state.Dot.Files.Get(file)) - - // Populated all chart values on the loaded static Role. - // For ClusterScoped resources, we include the Namespace to permit - // installing multiple releases with the same names into the same - // cluster. - role.ObjectMeta.Name = cleanForK8s(fmt.Sprintf("%s-%s-%s", Fullname(state), state.Release.Namespace, role.ObjectMeta.Name)) - role.ObjectMeta.Labels = FullLabels(state) - role.ObjectMeta.Annotations = helmette.Merge( - map[string]string{}, - state.Values.ServiceAccount.Annotations, // For backwards compatibility - state.Values.RBAC.Annotations, - FullAnnotations(state), - ) - - clusterRoles = append(clusterRoles, &role) - } - - return clusterRoles -} - -func RoleBindings(state *RenderState) []*rbacv1.RoleBinding { - // Rather than worrying about syncing logic, we just generate the list of - // bindings from the roles that we create. - var roleBindings []*rbacv1.RoleBinding - for _, role := range Roles(state) { - roleBindings = append(roleBindings, &rbacv1.RoleBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "RoleBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: role.ObjectMeta.Name, - Labels: FullLabels(state), - Namespace: state.Release.Namespace, - Annotations: helmette.Merge( - map[string]string{}, - state.Values.ServiceAccount.Annotations, // For backwards compatibility - state.Values.RBAC.Annotations, - FullAnnotations(state), - ), - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "Role", - Name: role.ObjectMeta.Name, - }, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: ServiceAccountName(state), - Namespace: state.Release.Namespace, - }, - }, - }) - } - - return roleBindings -} - -func ClusterRoleBindings(state *RenderState) []*rbacv1.ClusterRoleBinding { - // Rather than worrying about syncing logic, we just generate the list of - // bindings from the roles that we create. - var crbs []*rbacv1.ClusterRoleBinding - for _, clusterRole := range ClusterRoles(state) { - crbs = append(crbs, &rbacv1.ClusterRoleBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "ClusterRoleBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: clusterRole.ObjectMeta.Name, - Labels: FullLabels(state), - Annotations: helmette.Merge( - map[string]string{}, - state.Values.ServiceAccount.Annotations, // For backwards compatibility - state.Values.RBAC.Annotations, - FullAnnotations(state), - ), - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "ClusterRole", - Name: clusterRole.ObjectMeta.Name, - }, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: ServiceAccountName(state), - Namespace: state.Release.Namespace, - }, - }, - }) - } - - return crbs } diff --git a/charts/redpanda/chart/serviceaccount.go b/charts/redpanda/chart/serviceaccount.go index a742a4dc59..3ba643d5ae 100644 --- a/charts/redpanda/chart/serviceaccount.go +++ b/charts/redpanda/chart/serviceaccount.go @@ -12,9 +12,8 @@ package chart import ( corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" + "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25" "github.com/redpanda-data/redpanda-operator/gotohelm/helmette" ) @@ -38,17 +37,10 @@ func ServiceAccount(state *RenderState) *corev1.ServiceAccount { return nil } - return &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: ServiceAccountName(state), - Namespace: state.Release.Namespace, - Labels: FullLabels(state), - Annotations: helmette.Merge(state.Values.ServiceAccount.Annotations, FullAnnotations(state)), - }, - AutomountServiceAccountToken: ptr.To(false), - } + return redpanda.ServiceAccount( + ServiceAccountName(state), + state.Release.Namespace, + FullLabels(state), + helmette.Merge(state.Values.ServiceAccount.Annotations, FullAnnotations(state)), + ) } diff --git a/charts/redpanda/chart/templates/__redpanda.rbac.tpl b/charts/redpanda/chart/templates/__redpanda.rbac.tpl new file mode 100644 index 0000000000..116f59fd38 --- /dev/null +++ b/charts/redpanda/chart/templates/__redpanda.rbac.tpl @@ -0,0 +1,193 @@ +{{- /* GENERATED FILE DO NOT EDIT */ -}} +{{- /* Transpiled by gotohelm from "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25/rbac.go" */ -}} + +{{- define "_redpanda.RoleSet.Render" -}} +{{- $r := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $roles := (get (fromJson (include "_redpanda.RoleSet.renderRoles" (dict "a" (list (deepCopy $r))))) "r") -}} +{{- $clusterRoles := (get (fromJson (include "_redpanda.RoleSet.renderClusterRoles" (dict "a" (list (deepCopy $r))))) "r") -}} +{{- $objs := (coalesce nil) -}} +{{- range $_, $obj := $roles -}} +{{- $objs = (concat (default (list) $objs) (list $obj)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- range $_, $obj := $clusterRoles -}} +{{- $objs = (concat (default (list) $objs) (list $obj)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- range $_, $obj := (get (fromJson (include "_redpanda.RoleSet.renderRoleBindings" (dict "a" (list (deepCopy $r) $roles)))) "r") -}} +{{- $objs = (concat (default (list) $objs) (list $obj)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- range $_, $obj := (get (fromJson (include "_redpanda.RoleSet.renderClusterRoleBindings" (dict "a" (list (deepCopy $r) $clusterRoles)))) "r") -}} +{{- $objs = (concat (default (list) $objs) (list $obj)) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $objs) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.RoleSet.renderRoles" -}} +{{- $r := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $catalog := (get (fromJson (include "_redpanda.roleRules" (dict "a" (list)))) "r") -}} +{{- $toggles := (get (fromJson (include "_redpanda.RoleSet.roleToggles" (dict "a" (list (deepCopy $r))))) "r") -}} +{{- $roles := (coalesce nil) -}} +{{- range $_, $name := (get (fromJson (include "_shims.slices_Sorted" (dict "a" (list (keys $toggles))))) "r") -}} +{{- if (not (ternary (index $toggles $name) false (hasKey $toggles $name))) -}} +{{- continue -}} +{{- end -}} +{{- $roles = (concat (default (list) $roles) (list (mustMergeOverwrite (dict "metadata" (dict) "rules" (coalesce nil)) (mustMergeOverwrite (dict) (dict "apiVersion" "rbac.authorization.k8s.io/v1" "kind" "Role")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" (get (fromJson (include "_redpanda.RoleSet.RoleName" (dict "a" (list (deepCopy $r) $name)))) "r") "namespace" $r.Namespace "labels" (get (fromJson (include "_shims.maps_Clone" (dict "a" (list $r.Labels)))) "r") "annotations" (get (fromJson (include "_shims.maps_Clone" (dict "a" (list $r.Annotations)))) "r"))) "rules" (index $catalog $name))))) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $roles) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.RoleSet.roleToggles" -}} +{{- $r := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_is_returning = true -}} +{{- (dict "r" (dict "decommission" $r.Roles.Decommission "pvcunbinder" $r.Roles.PVCUnbinder "rpk-debug-bundle" $r.Roles.RPKDebugBundle "sidecar" $r.Roles.Sidecar)) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.RoleSet.RoleName" -}} +{{- $r := (index .a 0) -}} +{{- $name := (index .a 1) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_is_returning = true -}} +{{- (dict "r" (printf "%s-%s" $r.Prefix $name)) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.RoleSet.renderClusterRoles" -}} +{{- $r := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $catalog := (get (fromJson (include "_redpanda.clusterRoleRules" (dict "a" (list)))) "r") -}} +{{- $toggles := (get (fromJson (include "_redpanda.RoleSet.clusterRoleToggles" (dict "a" (list (deepCopy $r))))) "r") -}} +{{- $clusterRoles := (coalesce nil) -}} +{{- range $_, $name := (get (fromJson (include "_shims.slices_Sorted" (dict "a" (list (keys $toggles))))) "r") -}} +{{- if (not (ternary (index $toggles $name) false (hasKey $toggles $name))) -}} +{{- continue -}} +{{- end -}} +{{- $clusterRoles = (concat (default (list) $clusterRoles) (list (mustMergeOverwrite (dict "metadata" (dict) "rules" (coalesce nil)) (mustMergeOverwrite (dict) (dict "apiVersion" "rbac.authorization.k8s.io/v1" "kind" "ClusterRole")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" (get (fromJson (include "_redpanda.RoleSet.ClusterRoleName" (dict "a" (list (deepCopy $r) $name)))) "r") "labels" (get (fromJson (include "_shims.maps_Clone" (dict "a" (list $r.Labels)))) "r") "annotations" (get (fromJson (include "_shims.maps_Clone" (dict "a" (list $r.Annotations)))) "r"))) "rules" (index $catalog $name))))) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $clusterRoles) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.RoleSet.clusterRoleToggles" -}} +{{- $r := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_is_returning = true -}} +{{- (dict "r" (dict "decommission" $r.ClusterRoles.Decommission "metrics-reader" $r.ClusterRoles.MetricsReader "pvcunbinder" $r.ClusterRoles.PVCUnbinder "rack-awareness" $r.ClusterRoles.RackAwareness)) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.RoleSet.ClusterRoleName" -}} +{{- $r := (index .a 0) -}} +{{- $name := (index .a 1) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_is_returning = true -}} +{{- (dict "r" (printf "%s-%s-%s" $r.Prefix $r.Namespace $name)) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.RoleSet.renderRoleBindings" -}} +{{- $r := (index .a 0) -}} +{{- $roles := (index .a 1) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $roleBindings := (coalesce nil) -}} +{{- range $_, $role := $roles -}} +{{- $roleBindings = (concat (default (list) $roleBindings) (list (mustMergeOverwrite (dict "metadata" (dict) "roleRef" (dict "apiGroup" "" "kind" "" "name" "")) (mustMergeOverwrite (dict) (dict "apiVersion" "rbac.authorization.k8s.io/v1" "kind" "RoleBinding")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" $role.metadata.name "namespace" $r.Namespace "labels" (get (fromJson (include "_shims.maps_Clone" (dict "a" (list $r.Labels)))) "r") "annotations" (get (fromJson (include "_shims.maps_Clone" (dict "a" (list $r.Annotations)))) "r"))) "roleRef" (mustMergeOverwrite (dict "apiGroup" "" "kind" "" "name" "") (dict "apiGroup" "rbac.authorization.k8s.io" "kind" "Role" "name" $role.metadata.name)) "subjects" (list (mustMergeOverwrite (dict "kind" "" "name" "") (dict "kind" "ServiceAccount" "name" $r.ServiceAccount "namespace" $r.Namespace))))))) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $roleBindings) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.RoleSet.renderClusterRoleBindings" -}} +{{- $r := (index .a 0) -}} +{{- $clusterRoles := (index .a 1) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $crbs := (coalesce nil) -}} +{{- range $_, $clusterRole := $clusterRoles -}} +{{- $crbs = (concat (default (list) $crbs) (list (mustMergeOverwrite (dict "metadata" (dict) "roleRef" (dict "apiGroup" "" "kind" "" "name" "")) (mustMergeOverwrite (dict) (dict "apiVersion" "rbac.authorization.k8s.io/v1" "kind" "ClusterRoleBinding")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" $clusterRole.metadata.name "labels" (get (fromJson (include "_shims.maps_Clone" (dict "a" (list $r.Labels)))) "r") "annotations" (get (fromJson (include "_shims.maps_Clone" (dict "a" (list $r.Annotations)))) "r"))) "roleRef" (mustMergeOverwrite (dict "apiGroup" "" "kind" "" "name" "") (dict "apiGroup" "rbac.authorization.k8s.io" "kind" "ClusterRole" "name" $clusterRole.metadata.name)) "subjects" (list (mustMergeOverwrite (dict "kind" "" "name" "") (dict "kind" "ServiceAccount" "name" $r.ServiceAccount "namespace" $r.Namespace))))))) -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $crbs) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.ServiceAccount" -}} +{{- $name := (index .a 0) -}} +{{- $namespace := (index .a 1) -}} +{{- $labels := (index .a 2) -}} +{{- $annotations := (index .a 3) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_is_returning = true -}} +{{- (dict "r" (mustMergeOverwrite (dict "metadata" (dict)) (mustMergeOverwrite (dict) (dict "apiVersion" "v1" "kind" "ServiceAccount")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" $name "namespace" $namespace "labels" $labels "annotations" $annotations)) "automountServiceAccountToken" false))) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.roleRules" -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_is_returning = true -}} +{{- (dict "r" (dict "decommission" (list (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "events") "verbs" (list "create" "patch"))) (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "persistentvolumeclaims") "verbs" (list "delete" "get" "list" "watch"))) (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "pods" "secrets") "verbs" (list "get" "list" "watch"))) (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "apps") "resources" (list "statefulsets") "verbs" (list "get" "list" "watch")))) "pvcunbinder" (list (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "persistentvolumeclaims" "pods") "verbs" (list "delete" "get" "list" "watch")))) "rpk-debug-bundle" (list (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "configmaps" "endpoints" "events" "limitranges" "persistentvolumeclaims" "pods" "pods/log" "replicationcontrollers" "resourcequotas" "serviceaccounts" "services") "verbs" (list "get" "list")))) "sidecar" (list (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "coordination.k8s.io") "resources" (list "leases") "verbs" (list "create" "delete" "get" "list" "patch" "update" "watch")))))) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + +{{- define "_redpanda.clusterRoleRules" -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $_is_returning = true -}} +{{- (dict "r" (dict "decommission" (list (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "persistentvolumes") "verbs" (list "patch")))) "metrics-reader" (list (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "nonResourceURLs" (list "/metrics") "verbs" (list "get")))) "pvcunbinder" (list (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "nodes") "verbs" (list "get" "list"))) (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "persistentvolumes") "verbs" (list "get" "list" "patch" "watch"))) (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "pods") "verbs" (list "list" "watch"))) (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "cluster.redpanda.com") "resources" (list "redpandas" "stretchclusters") "verbs" (list "get" "list" "watch"))) (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "events.k8s.io") "resources" (list "events") "verbs" (list "create" "patch"))) (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "redpanda.vectorized.io") "resources" (list "clusters") "verbs" (list "get" "list" "watch"))) (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "storage.k8s.io") "resources" (list "storageclasses") "verbs" (list "get")))) "rack-awareness" (list (mustMergeOverwrite (dict "verbs" (coalesce nil)) (dict "apiGroups" (list "") "resources" (list "nodes") "verbs" (list "get")))))) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + diff --git a/charts/redpanda/chart/templates/_chart.go.tpl b/charts/redpanda/chart/templates/_chart.go.tpl index 7d4f40a0df..8f96141e76 100644 --- a/charts/redpanda/chart/templates/_chart.go.tpl +++ b/charts/redpanda/chart/templates/_chart.go.tpl @@ -53,30 +53,7 @@ {{- if $_is_returning -}} {{- break -}} {{- end -}} -{{- range $_, $obj := (get (fromJson (include "redpanda.Roles" (dict "a" (list $state)))) "r") -}} -{{- $manifests = (concat (default (list) $manifests) (list $obj)) -}} -{{- end -}} -{{- if $_is_returning -}} -{{- break -}} -{{- end -}} -{{- range $_, $obj := (get (fromJson (include "redpanda.ClusterRoles" (dict "a" (list $state)))) "r") -}} -{{- $manifests = (concat (default (list) $manifests) (list $obj)) -}} -{{- end -}} -{{- if $_is_returning -}} -{{- break -}} -{{- end -}} -{{- range $_, $obj := (get (fromJson (include "redpanda.RoleBindings" (dict "a" (list $state)))) "r") -}} -{{- $manifests = (concat (default (list) $manifests) (list $obj)) -}} -{{- end -}} -{{- if $_is_returning -}} -{{- break -}} -{{- end -}} -{{- range $_, $obj := (get (fromJson (include "redpanda.ClusterRoleBindings" (dict "a" (list $state)))) "r") -}} -{{- $manifests = (concat (default (list) $manifests) (list $obj)) -}} -{{- end -}} -{{- if $_is_returning -}} -{{- break -}} -{{- end -}} +{{- $manifests = (concat (default (list) $manifests) (default (list) (get (fromJson (include "_redpanda.RoleSet.Render" (dict "a" (list (deepCopy (get (fromJson (include "redpanda.RoleSet" (dict "a" (list $state)))) "r")))))) "r"))) -}} {{- range $_, $obj := (get (fromJson (include "redpanda.LoadBalancerServices" (dict "a" (list $state)))) "r") -}} {{- $manifests = (concat (default (list) $manifests) (list $obj)) -}} {{- end -}} diff --git a/charts/redpanda/chart/templates/_rbac.go.tpl b/charts/redpanda/chart/templates/_rbac.go.tpl index a4cd6188c0..1f8a85bfc4 100644 --- a/charts/redpanda/chart/templates/_rbac.go.tpl +++ b/charts/redpanda/chart/templates/_rbac.go.tpl @@ -1,87 +1,13 @@ {{- /* GENERATED FILE DO NOT EDIT */ -}} {{- /* Transpiled by gotohelm from "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25/chart/rbac.go" */ -}} -{{- define "redpanda.Roles" -}} +{{- define "redpanda.RoleSet" -}} {{- $state := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $mapping := (dict "files/sidecar.Role.yaml" $state.Values.rbac.enabled "files/pvcunbinder.Role.yaml" (and $state.Values.rbac.enabled $state.Values.statefulset.sideCars.pvcUnbinder.enabled) "files/decommission.Role.yaml" (and $state.Values.rbac.enabled $state.Values.statefulset.sideCars.brokerDecommissioner.enabled) "files/rpk-debug-bundle.Role.yaml" (and $state.Values.rbac.enabled $state.Values.rbac.rpkDebugBundle)) -}} -{{- $roles := (coalesce nil) -}} -{{- range $file, $enabled := $mapping -}} -{{- if (not $enabled) -}} -{{- continue -}} -{{- end -}} -{{- $role := (get (fromJson (include "_shims.fromYaml" (dict "a" (list ($state.Dot.Files.Get $file))))) "r") -}} -{{- $_ := (set $role.metadata "name" (printf "%s-%s" (get (fromJson (include "redpanda.Fullname" (dict "a" (list $state)))) "r") $role.metadata.name)) -}} -{{- $_ := (set $role.metadata "namespace" $state.Release.Namespace) -}} -{{- $_ := (set $role.metadata "labels" (get (fromJson (include "redpanda.FullLabels" (dict "a" (list $state)))) "r")) -}} -{{- $_ := (set $role.metadata "annotations" (merge (dict) (dict) $state.Values.serviceAccount.annotations $state.Values.rbac.annotations (get (fromJson (include "redpanda.FullAnnotations" (dict "a" (list $state)))) "r"))) -}} -{{- $roles = (concat (default (list) $roles) (list $role)) -}} -{{- end -}} -{{- if $_is_returning -}} -{{- break -}} -{{- end -}} -{{- $_is_returning = true -}} -{{- (dict "r" $roles) | toJson -}} -{{- break -}} -{{- end -}} -{{- end -}} - -{{- define "redpanda.ClusterRoles" -}} -{{- $state := (index .a 0) -}} -{{- range $_ := (list 1) -}} -{{- $_is_returning := false -}} -{{- $mapping := (dict "files/pvcunbinder.ClusterRole.yaml" (and $state.Values.rbac.enabled $state.Values.statefulset.sideCars.pvcUnbinder.enabled) "files/decommission.ClusterRole.yaml" (and $state.Values.rbac.enabled $state.Values.statefulset.sideCars.brokerDecommissioner.enabled) "files/rack-awareness.ClusterRole.yaml" (and $state.Values.rbac.enabled $state.Values.rackAwareness.enabled)) -}} -{{- $clusterRoles := (coalesce nil) -}} -{{- range $file, $enabled := $mapping -}} -{{- if (not $enabled) -}} -{{- continue -}} -{{- end -}} -{{- $role := (get (fromJson (include "_shims.fromYaml" (dict "a" (list ($state.Dot.Files.Get $file))))) "r") -}} -{{- $_ := (set $role.metadata "name" (get (fromJson (include "redpanda.cleanForK8s" (dict "a" (list (printf "%s-%s-%s" (get (fromJson (include "redpanda.Fullname" (dict "a" (list $state)))) "r") $state.Release.Namespace $role.metadata.name))))) "r")) -}} -{{- $_ := (set $role.metadata "labels" (get (fromJson (include "redpanda.FullLabels" (dict "a" (list $state)))) "r")) -}} -{{- $_ := (set $role.metadata "annotations" (merge (dict) (dict) $state.Values.serviceAccount.annotations $state.Values.rbac.annotations (get (fromJson (include "redpanda.FullAnnotations" (dict "a" (list $state)))) "r"))) -}} -{{- $clusterRoles = (concat (default (list) $clusterRoles) (list $role)) -}} -{{- end -}} -{{- if $_is_returning -}} -{{- break -}} -{{- end -}} -{{- $_is_returning = true -}} -{{- (dict "r" $clusterRoles) | toJson -}} -{{- break -}} -{{- end -}} -{{- end -}} - -{{- define "redpanda.RoleBindings" -}} -{{- $state := (index .a 0) -}} -{{- range $_ := (list 1) -}} -{{- $_is_returning := false -}} -{{- $roleBindings := (coalesce nil) -}} -{{- range $_, $role := (get (fromJson (include "redpanda.Roles" (dict "a" (list $state)))) "r") -}} -{{- $roleBindings = (concat (default (list) $roleBindings) (list (mustMergeOverwrite (dict "metadata" (dict) "roleRef" (dict "apiGroup" "" "kind" "" "name" "")) (mustMergeOverwrite (dict) (dict "apiVersion" "rbac.authorization.k8s.io/v1" "kind" "RoleBinding")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" $role.metadata.name "labels" (get (fromJson (include "redpanda.FullLabels" (dict "a" (list $state)))) "r") "namespace" $state.Release.Namespace "annotations" (merge (dict) (dict) $state.Values.serviceAccount.annotations $state.Values.rbac.annotations (get (fromJson (include "redpanda.FullAnnotations" (dict "a" (list $state)))) "r")))) "roleRef" (mustMergeOverwrite (dict "apiGroup" "" "kind" "" "name" "") (dict "apiGroup" "rbac.authorization.k8s.io" "kind" "Role" "name" $role.metadata.name)) "subjects" (list (mustMergeOverwrite (dict "kind" "" "name" "") (dict "kind" "ServiceAccount" "name" (get (fromJson (include "redpanda.ServiceAccountName" (dict "a" (list $state)))) "r") "namespace" $state.Release.Namespace))))))) -}} -{{- end -}} -{{- if $_is_returning -}} -{{- break -}} -{{- end -}} -{{- $_is_returning = true -}} -{{- (dict "r" $roleBindings) | toJson -}} -{{- break -}} -{{- end -}} -{{- end -}} - -{{- define "redpanda.ClusterRoleBindings" -}} -{{- $state := (index .a 0) -}} -{{- range $_ := (list 1) -}} -{{- $_is_returning := false -}} -{{- $crbs := (coalesce nil) -}} -{{- range $_, $clusterRole := (get (fromJson (include "redpanda.ClusterRoles" (dict "a" (list $state)))) "r") -}} -{{- $crbs = (concat (default (list) $crbs) (list (mustMergeOverwrite (dict "metadata" (dict) "roleRef" (dict "apiGroup" "" "kind" "" "name" "")) (mustMergeOverwrite (dict) (dict "apiVersion" "rbac.authorization.k8s.io/v1" "kind" "ClusterRoleBinding")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" $clusterRole.metadata.name "labels" (get (fromJson (include "redpanda.FullLabels" (dict "a" (list $state)))) "r") "annotations" (merge (dict) (dict) $state.Values.serviceAccount.annotations $state.Values.rbac.annotations (get (fromJson (include "redpanda.FullAnnotations" (dict "a" (list $state)))) "r")))) "roleRef" (mustMergeOverwrite (dict "apiGroup" "" "kind" "" "name" "") (dict "apiGroup" "rbac.authorization.k8s.io" "kind" "ClusterRole" "name" $clusterRole.metadata.name)) "subjects" (list (mustMergeOverwrite (dict "kind" "" "name" "") (dict "kind" "ServiceAccount" "name" (get (fromJson (include "redpanda.ServiceAccountName" (dict "a" (list $state)))) "r") "namespace" $state.Release.Namespace))))))) -}} -{{- end -}} -{{- if $_is_returning -}} -{{- break -}} -{{- end -}} +{{- $annotations := (merge (dict) (dict) $state.Values.serviceAccount.annotations $state.Values.rbac.annotations (get (fromJson (include "redpanda.FullAnnotations" (dict "a" (list $state)))) "r")) -}} {{- $_is_returning = true -}} -{{- (dict "r" $crbs) | toJson -}} +{{- (dict "r" (mustMergeOverwrite (dict "Prefix" "" "Namespace" "" "Labels" (coalesce nil) "Annotations" (coalesce nil) "ServiceAccount" "" "Roles" (dict "Decommission" false "PVCUnbinder" false "RPKDebugBundle" false "Sidecar" false) "ClusterRoles" (dict "Decommission" false "MetricsReader" false "PVCUnbinder" false "RackAwareness" false)) (dict "Prefix" (get (fromJson (include "redpanda.Fullname" (dict "a" (list $state)))) "r") "Namespace" $state.Release.Namespace "Labels" (get (fromJson (include "redpanda.FullLabels" (dict "a" (list $state)))) "r") "Annotations" $annotations "ServiceAccount" (get (fromJson (include "redpanda.ServiceAccountName" (dict "a" (list $state)))) "r") "Roles" (mustMergeOverwrite (dict "Decommission" false "PVCUnbinder" false "RPKDebugBundle" false "Sidecar" false) (dict "Decommission" (and $state.Values.rbac.enabled $state.Values.statefulset.sideCars.brokerDecommissioner.enabled) "PVCUnbinder" (and $state.Values.rbac.enabled $state.Values.statefulset.sideCars.pvcUnbinder.enabled) "RPKDebugBundle" (and $state.Values.rbac.enabled $state.Values.rbac.rpkDebugBundle) "Sidecar" $state.Values.rbac.enabled)) "ClusterRoles" (mustMergeOverwrite (dict "Decommission" false "MetricsReader" false "PVCUnbinder" false "RackAwareness" false) (dict "Decommission" (and $state.Values.rbac.enabled $state.Values.statefulset.sideCars.brokerDecommissioner.enabled) "PVCUnbinder" (and $state.Values.rbac.enabled $state.Values.statefulset.sideCars.pvcUnbinder.enabled) "RackAwareness" (and $state.Values.rbac.enabled $state.Values.rackAwareness.enabled)))))) | toJson -}} {{- break -}} {{- end -}} {{- end -}} diff --git a/charts/redpanda/chart/templates/_serviceaccount.go.tpl b/charts/redpanda/chart/templates/_serviceaccount.go.tpl index 27df2651f9..87843d4b5d 100644 --- a/charts/redpanda/chart/templates/_serviceaccount.go.tpl +++ b/charts/redpanda/chart/templates/_serviceaccount.go.tpl @@ -37,7 +37,7 @@ {{- break -}} {{- end -}} {{- $_is_returning = true -}} -{{- (dict "r" (mustMergeOverwrite (dict "metadata" (dict)) (mustMergeOverwrite (dict) (dict "apiVersion" "v1" "kind" "ServiceAccount")) (dict "metadata" (mustMergeOverwrite (dict) (dict "name" (get (fromJson (include "redpanda.ServiceAccountName" (dict "a" (list $state)))) "r") "namespace" $state.Release.Namespace "labels" (get (fromJson (include "redpanda.FullLabels" (dict "a" (list $state)))) "r") "annotations" (merge (dict) $state.Values.serviceAccount.annotations (get (fromJson (include "redpanda.FullAnnotations" (dict "a" (list $state)))) "r")))) "automountServiceAccountToken" false))) | toJson -}} +{{- (dict "r" (get (fromJson (include "_redpanda.ServiceAccount" (dict "a" (list (get (fromJson (include "redpanda.ServiceAccountName" (dict "a" (list $state)))) "r") $state.Release.Namespace (get (fromJson (include "redpanda.FullLabels" (dict "a" (list $state)))) "r") (merge (dict) $state.Values.serviceAccount.annotations (get (fromJson (include "redpanda.FullAnnotations" (dict "a" (list $state)))) "r")))))) "r")) | toJson -}} {{- break -}} {{- end -}} {{- end -}} diff --git a/charts/redpanda/rbac.go b/charts/redpanda/rbac.go new file mode 100644 index 0000000000..63e5da1d09 --- /dev/null +++ b/charts/redpanda/rbac.go @@ -0,0 +1,381 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + +package redpanda + +import ( + "fmt" + "maps" + "slices" + + "github.com/redpanda-data/common-go/kube" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +// RoleSet is the RBAC generator for a Redpanda deployment. +type RoleSet struct { + // Prefix leads every object name. + Prefix string + Namespace string + + Labels map[string]string + Annotations map[string]string + + // ServiceAccount is the name the bindings' subject points at. Resolved, + // not derived: the chart falls back to "default", the operator to its + // per-pool fullname. + ServiceAccount string + + Roles Roles + ClusterRoles ClusterRoles +} + +type Roles struct { + Decommission bool + PVCUnbinder bool + RPKDebugBundle bool + Sidecar bool +} + +type ClusterRoles struct { + Decommission bool + MetricsReader bool + PVCUnbinder bool + RackAwareness bool +} + +// Render returns the Roles, ClusterRoles, RoleBindings, and +// ClusterRoleBindings required for a redpanda deployment, each group in +// catalog-key order. +func (r RoleSet) Render() []kube.Object { + roles := r.renderRoles() + clusterRoles := r.renderClusterRoles() + + var objs []kube.Object + + for _, obj := range roles { + objs = append(objs, obj) + } + + for _, obj := range clusterRoles { + objs = append(objs, obj) + } + + for _, obj := range r.renderRoleBindings(roles) { + objs = append(objs, obj) + } + + for _, obj := range r.renderClusterRoleBindings(clusterRoles) { + objs = append(objs, obj) + } + + return objs +} + +func (r RoleSet) renderRoles() []*rbacv1.Role { + catalog := roleRules() + toggles := r.roleToggles() + + var roles []*rbacv1.Role + for _, name := range slices.Sorted(maps.Keys(toggles)) { + if !toggles[name] { + continue + } + + roles = append(roles, &rbacv1.Role{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "rbac.authorization.k8s.io/v1", + Kind: "Role", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: r.RoleName(name), + Namespace: r.Namespace, + Labels: maps.Clone(r.Labels), + Annotations: maps.Clone(r.Annotations), + }, + Rules: catalog[name], + }) + } + + return roles +} + +// roleToggles and clusterRoleToggles key the toggles by catalog name. This is +// the only place those names are spelled outside the catalog itself, so +// callers deal in fields; TestToggleFieldsCoverCatalog holds the two key sets +// equal. +func (r RoleSet) roleToggles() map[string]bool { + return map[string]bool{ + "decommission": r.Roles.Decommission, + "pvcunbinder": r.Roles.PVCUnbinder, + "rpk-debug-bundle": r.Roles.RPKDebugBundle, + "sidecar": r.Roles.Sidecar, + } +} + +// RoleName is the namespaced name of a catalog entry. +func (r RoleSet) RoleName(name string) string { + return fmt.Sprintf("%s-%s", r.Prefix, name) +} + +func (r RoleSet) renderClusterRoles() []*rbacv1.ClusterRole { + catalog := clusterRoleRules() + toggles := r.clusterRoleToggles() + + var clusterRoles []*rbacv1.ClusterRole + for _, name := range slices.Sorted(maps.Keys(toggles)) { + if !toggles[name] { + continue + } + + clusterRoles = append(clusterRoles, &rbacv1.ClusterRole{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "rbac.authorization.k8s.io/v1", + Kind: "ClusterRole", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: r.ClusterRoleName(name), + Labels: maps.Clone(r.Labels), + Annotations: maps.Clone(r.Annotations), + }, + Rules: catalog[name], + }) + } + + return clusterRoles +} + +func (r RoleSet) clusterRoleToggles() map[string]bool { + return map[string]bool{ + "decommission": r.ClusterRoles.Decommission, + "metrics-reader": r.ClusterRoles.MetricsReader, + "pvcunbinder": r.ClusterRoles.PVCUnbinder, + "rack-awareness": r.ClusterRoles.RackAwareness, + } +} + +// ClusterRoleName is the cluster-scoped name of a catalog entry. The namespace +// is folded in so multiple releases with the same name can be installed into +// one cluster. +func (r RoleSet) ClusterRoleName(name string) string { + // NB: Not truncated to 63. ClusterRole names are path segments, not DNS + // labels, and a cut at 63 can drop the suffix, collapsing every + // ClusterRole onto one name. + return fmt.Sprintf("%s-%s-%s", r.Prefix, r.Namespace, name) +} + +func (r RoleSet) renderRoleBindings(roles []*rbacv1.Role) []*rbacv1.RoleBinding { + var roleBindings []*rbacv1.RoleBinding + for _, role := range roles { + roleBindings = append(roleBindings, &rbacv1.RoleBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "rbac.authorization.k8s.io/v1", + Kind: "RoleBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: role.ObjectMeta.Name, + Namespace: r.Namespace, + Labels: maps.Clone(r.Labels), + Annotations: maps.Clone(r.Annotations), + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: role.ObjectMeta.Name, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: r.ServiceAccount, + Namespace: r.Namespace, + }, + }, + }) + } + + return roleBindings +} + +func (r RoleSet) renderClusterRoleBindings(clusterRoles []*rbacv1.ClusterRole) []*rbacv1.ClusterRoleBinding { + var crbs []*rbacv1.ClusterRoleBinding + for _, clusterRole := range clusterRoles { + crbs = append(crbs, &rbacv1.ClusterRoleBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "rbac.authorization.k8s.io/v1", + Kind: "ClusterRoleBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: clusterRole.ObjectMeta.Name, + Labels: maps.Clone(r.Labels), + Annotations: maps.Clone(r.Annotations), + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: clusterRole.ObjectMeta.Name, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: r.ServiceAccount, + Namespace: r.Namespace, + }, + }, + }) + } + + return crbs +} + +// ServiceAccount is the ServiceAccount a [RoleSet]'s bindings references. +func ServiceAccount(name, namespace string, labels, annotations map[string]string) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + Annotations: annotations, + }, + AutomountServiceAccountToken: ptr.To(false), + } +} + +// roleRules is the catalog of Roles a redpanda deployment MAY require. It's a +// hand coded version of what is generated from our controllers' markers. +func roleRules() map[string][]rbacv1.PolicyRule { + return map[string][]rbacv1.PolicyRule{ + "decommission": { + { + APIGroups: []string{""}, + Resources: []string{"events"}, + Verbs: []string{"create", "patch"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"persistentvolumeclaims"}, + Verbs: []string{"delete", "get", "list", "watch"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"pods", "secrets"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"apps"}, + Resources: []string{"statefulsets"}, + Verbs: []string{"get", "list", "watch"}, + }, + }, + "pvcunbinder": { + { + APIGroups: []string{""}, + Resources: []string{"persistentvolumeclaims", "pods"}, + Verbs: []string{"delete", "get", "list", "watch"}, + }, + }, + "rpk-debug-bundle": { + { + APIGroups: []string{""}, + Resources: []string{ + "configmaps", + "endpoints", + "events", + "limitranges", + "persistentvolumeclaims", + "pods", + "pods/log", + "replicationcontrollers", + "resourcequotas", + "serviceaccounts", + "services", + }, + Verbs: []string{"get", "list"}, + }, + }, + "sidecar": { + { + APIGroups: []string{"coordination.k8s.io"}, + Resources: []string{"leases"}, + Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, + }, + }, + } +} + +// clusterRoleRules is the catalog of ClusterRoles a redpanda deployment MAY +// require. It's a hand coded version of what is generated from our +// controllers' markers. +func clusterRoleRules() map[string][]rbacv1.PolicyRule { + return map[string][]rbacv1.PolicyRule{ + "decommission": { + { + APIGroups: []string{""}, + Resources: []string{"persistentvolumes"}, + Verbs: []string{"patch"}, + }, + }, + "metrics-reader": { + { + NonResourceURLs: []string{"/metrics"}, + Verbs: []string{"get"}, + }, + }, + "pvcunbinder": { + { + APIGroups: []string{""}, + Resources: []string{"nodes"}, + Verbs: []string{"get", "list"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"persistentvolumes"}, + Verbs: []string{"get", "list", "patch", "watch"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"list", "watch"}, + }, + { + APIGroups: []string{"cluster.redpanda.com"}, + Resources: []string{"redpandas", "stretchclusters"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"events.k8s.io"}, + Resources: []string{"events"}, + Verbs: []string{"create", "patch"}, + }, + { + APIGroups: []string{"redpanda.vectorized.io"}, + Resources: []string{"clusters"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"storage.k8s.io"}, + Resources: []string{"storageclasses"}, + Verbs: []string{"get"}, + }, + }, + "rack-awareness": { + { + APIGroups: []string{""}, + Resources: []string{"nodes"}, + Verbs: []string{"get"}, + }, + }, + } +} diff --git a/charts/redpanda/rbac_test.go b/charts/redpanda/rbac_test.go new file mode 100644 index 0000000000..50168e6d8b --- /dev/null +++ b/charts/redpanda/rbac_test.go @@ -0,0 +1,141 @@ +// Copyright 2026 Redpanda Data, Inc. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.md +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0 + +package redpanda + +import ( + "embed" + "fmt" + "io/fs" + "maps" + "path" + "slices" + "strings" + "testing" + + "github.com/stretchr/testify/require" + rbacv1 "k8s.io/api/rbac/v1" + "sigs.k8s.io/yaml" + + "github.com/redpanda-data/redpanda-operator/pkg/testutil" +) + +// generatedRules is controller-gen's output, one object per file named +// "..yaml". The catalog in rbac.go is hand-written as +// gotohelm doesn't (easily) support go:embed in packages outside of the root +// chart. +// +//go:embed testdata/rbac/*.Role.yaml +//go:embed testdata/rbac/*.ClusterRole.yaml +var generatedRules embed.FS + +// TestRoleRulesMatchGenerated asserts both catalogs equal the generated +// fixtures. The comparison is whole-map, so neither a new bundle nor a new +// catalog entry can land without the other. +func TestRoleRulesMatchGenerated(t *testing.T) { + require.Equal(t, fixtureRules(t, "*.Role.yaml"), roleRules()) + require.Equal(t, fixtureRules(t, "*.ClusterRole.yaml"), clusterRoleRules()) +} + +// fixtureRules reads the fixtures matching pattern, keyed by the catalog key +// their filename leads with. +func fixtureRules(t *testing.T, pattern string) map[string][]rbacv1.PolicyRule { + t.Helper() + + matches, err := fs.Glob(generatedRules, path.Join("testdata", "rbac", pattern)) + require.NoError(t, err) + require.NotEmpty(t, matches, "no fixtures matched %q", pattern) + + suffix := strings.TrimPrefix(pattern, "*") + + rules := map[string][]rbacv1.PolicyRule{} + for _, match := range matches { + contents, err := generatedRules.ReadFile(match) + require.NoError(t, err) + + var role rbacv1.Role + require.NoError(t, yaml.Unmarshal(contents, &role)) + + rules[strings.TrimSuffix(path.Base(match), suffix)] = role.Rules + } + + return rules +} + +// TestToggleFieldsCoverCatalog asserts every catalog entry has a toggle field +// and vice versa. Without it, adding a rule set and a field but forgetting to +// pair them renders nothing, silently. +func TestToggleFieldsCoverCatalog(t *testing.T) { + var rs RoleSet + + require.Equal(t, slices.Sorted(maps.Keys(roleRules())), slices.Sorted(maps.Keys(rs.roleToggles()))) + require.Equal(t, slices.Sorted(maps.Keys(clusterRoleRules())), slices.Sorted(maps.Keys(rs.clusterRoleToggles()))) +} + +// TestRoleSetRender pins naming, labels, annotations, binding derivation, and +// emission order in one artifact. Order is load-bearing: it's what both +// callers' goldens are recorded against. +func TestRoleSetRender(t *testing.T) { + cases := map[string]RoleSet{ + "chart": { + Prefix: "release", + Namespace: "ns", + Labels: map[string]string{"app.kubernetes.io/name": "redpanda"}, + Annotations: map[string]string{"eks.amazonaws.com/role-arn": "arn:aws:iam::1:role/rp"}, + ServiceAccount: "release-sa", + Roles: Roles{ + RPKDebugBundle: true, + Sidecar: true, + }, + ClusterRoles: ClusterRoles{ + MetricsReader: true, + RackAwareness: true, + }, + }, + "long-names": { + Prefix: strings.Repeat("p", 40), + Namespace: strings.Repeat("n", 30), + ServiceAccount: "sa", + ClusterRoles: ClusterRoles{ + MetricsReader: true, + RackAwareness: true, + }, + }, + } + + for name, rs := range cases { + t.Run(name, func(t *testing.T) { + objs := rs.Render() + + names := map[string]bool{} + for _, obj := range objs { + key := fmt.Sprintf("%T/%s", obj, obj.GetName()) + require.False(t, names[key], "duplicate object %s", key) + names[key] = true + } + + rendered, err := yaml.Marshal(objs) + require.NoError(t, err) + + testutil.AssertGolden(t, testutil.YAML, fmt.Sprintf("./testdata/roleset-%s.golden", name), rendered) + + // Consumers, such as the syncer, mutate metadata maps in place. + for _, m := range []map[string]string{objs[0].GetLabels(), objs[0].GetAnnotations()} { + if m == nil { + continue + } + m["mutated"] = "true" + for _, obj := range objs[1:] { + require.NotContains(t, obj.GetLabels(), "mutated") + require.NotContains(t, obj.GetAnnotations(), "mutated") + } + } + }) + } +} diff --git a/charts/redpanda/chart/files/decommission.ClusterRole.yaml b/charts/redpanda/testdata/rbac/decommission.ClusterRole.yaml similarity index 100% rename from charts/redpanda/chart/files/decommission.ClusterRole.yaml rename to charts/redpanda/testdata/rbac/decommission.ClusterRole.yaml diff --git a/charts/redpanda/chart/files/decommission.Role.yaml b/charts/redpanda/testdata/rbac/decommission.Role.yaml similarity index 100% rename from charts/redpanda/chart/files/decommission.Role.yaml rename to charts/redpanda/testdata/rbac/decommission.Role.yaml diff --git a/charts/redpanda/testdata/rbac/metrics-reader.ClusterRole.yaml b/charts/redpanda/testdata/rbac/metrics-reader.ClusterRole.yaml new file mode 100644 index 0000000000..9d8145f77e --- /dev/null +++ b/charts/redpanda/testdata/rbac/metrics-reader.ClusterRole.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: + - nonResourceURLs: ["/metrics"] + verbs: ["get"] diff --git a/charts/redpanda/chart/files/pvcunbinder.ClusterRole.yaml b/charts/redpanda/testdata/rbac/pvcunbinder.ClusterRole.yaml similarity index 100% rename from charts/redpanda/chart/files/pvcunbinder.ClusterRole.yaml rename to charts/redpanda/testdata/rbac/pvcunbinder.ClusterRole.yaml diff --git a/charts/redpanda/chart/files/pvcunbinder.Role.yaml b/charts/redpanda/testdata/rbac/pvcunbinder.Role.yaml similarity index 100% rename from charts/redpanda/chart/files/pvcunbinder.Role.yaml rename to charts/redpanda/testdata/rbac/pvcunbinder.Role.yaml diff --git a/charts/redpanda/chart/files/rack-awareness.ClusterRole.yaml b/charts/redpanda/testdata/rbac/rack-awareness.ClusterRole.yaml similarity index 100% rename from charts/redpanda/chart/files/rack-awareness.ClusterRole.yaml rename to charts/redpanda/testdata/rbac/rack-awareness.ClusterRole.yaml diff --git a/charts/redpanda/chart/files/rpk-debug-bundle.Role.yaml b/charts/redpanda/testdata/rbac/rpk-debug-bundle.Role.yaml similarity index 100% rename from charts/redpanda/chart/files/rpk-debug-bundle.Role.yaml rename to charts/redpanda/testdata/rbac/rpk-debug-bundle.Role.yaml diff --git a/charts/redpanda/chart/files/sidecar.Role.yaml b/charts/redpanda/testdata/rbac/sidecar.Role.yaml similarity index 100% rename from charts/redpanda/chart/files/sidecar.Role.yaml rename to charts/redpanda/testdata/rbac/sidecar.Role.yaml diff --git a/charts/redpanda/testdata/roleset-chart.golden b/charts/redpanda/testdata/roleset-chart.golden new file mode 100644 index 0000000000..cf9b8ce1a3 --- /dev/null +++ b/charts/redpanda/testdata/roleset-chart.golden @@ -0,0 +1,143 @@ +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::1:role/rp + labels: + app.kubernetes.io/name: redpanda + name: release-rpk-debug-bundle + namespace: ns + rules: + - apiGroups: + - "" + resources: + - configmaps + - endpoints + - events + - limitranges + - persistentvolumeclaims + - pods + - pods/log + - replicationcontrollers + - resourcequotas + - serviceaccounts + - services + verbs: + - get + - list +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::1:role/rp + labels: + app.kubernetes.io/name: redpanda + name: release-sidecar + namespace: ns + rules: + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::1:role/rp + labels: + app.kubernetes.io/name: redpanda + name: release-ns-metrics-reader + rules: + - nonResourceURLs: + - /metrics + verbs: + - get +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::1:role/rp + labels: + app.kubernetes.io/name: redpanda + name: release-ns-rack-awareness + rules: + - apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::1:role/rp + labels: + app.kubernetes.io/name: redpanda + name: release-rpk-debug-bundle + namespace: ns + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: release-rpk-debug-bundle + subjects: + - kind: ServiceAccount + name: release-sa + namespace: ns +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::1:role/rp + labels: + app.kubernetes.io/name: redpanda + name: release-sidecar + namespace: ns + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: release-sidecar + subjects: + - kind: ServiceAccount + name: release-sa + namespace: ns +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::1:role/rp + labels: + app.kubernetes.io/name: redpanda + name: release-ns-metrics-reader + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: release-ns-metrics-reader + subjects: + - kind: ServiceAccount + name: release-sa + namespace: ns +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::1:role/rp + labels: + app.kubernetes.io/name: redpanda + name: release-ns-rack-awareness + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: release-ns-rack-awareness + subjects: + - kind: ServiceAccount + name: release-sa + namespace: ns diff --git a/charts/redpanda/testdata/roleset-long-names.golden b/charts/redpanda/testdata/roleset-long-names.golden new file mode 100644 index 0000000000..10573cb3ed --- /dev/null +++ b/charts/redpanda/testdata/roleset-long-names.golden @@ -0,0 +1,44 @@ +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: pppppppppppppppppppppppppppppppppppppppp-nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn-metrics-reader + rules: + - nonResourceURLs: + - /metrics + verbs: + - get +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: pppppppppppppppppppppppppppppppppppppppp-nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn-rack-awareness + rules: + - apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: pppppppppppppppppppppppppppppppppppppppp-nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn-metrics-reader + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: pppppppppppppppppppppppppppppppppppppppp-nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn-metrics-reader + subjects: + - kind: ServiceAccount + name: sa + namespace: nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: pppppppppppppppppppppppppppppppppppppppp-nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn-rack-awareness + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: pppppppppppppppppppppppppppppppppppppppp-nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn-rack-awareness + subjects: + - kind: ServiceAccount + name: sa + namespace: nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn diff --git a/operator/chart/chart_test.go b/operator/chart/chart_test.go index 4907cfc578..72e6878fd7 100644 --- a/operator/chart/chart_test.go +++ b/operator/chart/chart_test.go @@ -342,29 +342,6 @@ func TestRBACBindings(t *testing.T) { } } -// TestRBACStretchRackAwarenessScoped asserts that the operator only claims -// the nodes list/watch grant needed for StretchCluster's per-pool rack -// awareness ClusterRole (operator/multicluster/rbac.go) on multicluster -// installs. -func TestRBACStretchRackAwarenessScoped(t *testing.T) { - multiclusterObjs, err := Chart.Render(nil, helmette.Release{Name: "operator"}, PartialValues{ - Multicluster: &PartialMulticluster{ - Enabled: ptr.To(true), - Name: ptr.To("cluster-1"), - KubernetesAPIExternalAddress: ptr.To("cluster-1.example.com"), - Peers: []PartialPeer{ - {Name: ptr.To("cluster-2"), Address: ptr.To("cluster-2.example.com")}, - }, - }, - }) - require.NoError(t, err) - - multiclusterClusterRoleRules, _ := ExtractRules(multiclusterObjs) - multiclusterNodeVerbs := multiclusterClusterRoleRules["#nodes"] - require.Contains(t, multiclusterNodeVerbs, "list", "multicluster install should hold nodes:list") - require.Contains(t, multiclusterNodeVerbs, "watch", "multicluster install should hold nodes:watch") -} - func TestRBACIsSuperSetOfRedpanda(t *testing.T) { testCases := []struct { Name string diff --git a/operator/chart/files/rbac/stretch-rack-awareness.ClusterRole.yaml b/operator/chart/files/rbac/stretch-rack-awareness.ClusterRole.yaml deleted file mode 100644 index ec0343e184..0000000000 --- a/operator/chart/files/rbac/stretch-rack-awareness.ClusterRole.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: stretch-rack-awareness -rules: - - apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch diff --git a/operator/chart/rbac.go b/operator/chart/rbac.go index f13de4d8e8..4e6e3d5007 100644 --- a/operator/chart/rbac.go +++ b/operator/chart/rbac.go @@ -64,15 +64,6 @@ func rbacBundles(dot *helmette.Dot) []RBACBundle { "files/rbac/old-decommission.Role.yaml": true, // Deprecated but not yet removed. "files/rbac/pvcunbinder.ClusterRole.yaml": true, "files/rbac/pvcunbinder.Role.yaml": true, - // The StretchCluster renderer grants each broker pool a - // rack-awareness ClusterRole with nodes get/list/watch; - // RBAC escalation prevention requires the operator to - // hold those verbs itself (the always-on - // rack-awareness.ClusterRole.yaml carries `get` only). - // StretchCluster is multicluster-only, so scope this grant - // to multicluster installs rather than every default - // single-cluster ServiceAccount. - "files/rbac/stretch-rack-awareness.ClusterRole.yaml": values.Multicluster.Enabled, }, }, // ClusterRole for the CRD installation Job. diff --git a/operator/chart/templates/_rbac.go.tpl b/operator/chart/templates/_rbac.go.tpl index 708bd3beaf..9d481b7be6 100644 --- a/operator/chart/templates/_rbac.go.tpl +++ b/operator/chart/templates/_rbac.go.tpl @@ -6,7 +6,7 @@ {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} {{- $values := $dot.Values.AsMap -}} -{{- $bundles := (list (mustMergeOverwrite (dict "Enabled" false "Name" "" "Subject" "" "RuleFiles" (coalesce nil) "Annotations" (coalesce nil)) (dict "Name" (get (fromJson (include "operator.Fullname" (dict "a" (list $dot)))) "r") "Enabled" true "Subject" (get (fromJson (include "operator.ServiceAccountName" (dict "a" (list $dot)))) "r") "RuleFiles" (dict "files/rbac/console.ClusterRole.yaml" true "files/rbac/leader-election.ClusterRole.yaml" true "files/rbac/leader-election.Role.yaml" true "files/rbac/pvcunbinder.ClusterRole.yaml" true "files/rbac/pvcunbinder.Role.yaml" true "files/rbac/rack-awareness.ClusterRole.yaml" true "files/rbac/rpk-debug-bundle.Role.yaml" true "files/rbac/sidecar.Role.yaml" true "files/rbac/v1-manager.ClusterRole.yaml" $values.vectorizedControllers.enabled "files/rbac/v1-manager.Role.yaml" $values.vectorizedControllers.enabled "files/rbac/v2-manager.ClusterRole.yaml" true "files/rbac/pipeline.ClusterRole.yaml" $values.connectController.enabled "files/rbac/multicluster-manager.ClusterRole.yaml" $values.multicluster.enabled "files/rbac/endpoint-steering.ClusterRole.yaml" (not $values.multicluster.enabled)))) (mustMergeOverwrite (dict "Enabled" false "Name" "" "Subject" "" "RuleFiles" (coalesce nil) "Annotations" (coalesce nil)) (dict "Name" (get (fromJson (include "operator.cleanForK8sWithSuffix" (dict "a" (list (get (fromJson (include "operator.Fullname" (dict "a" (list $dot)))) "r") "additional-controllers")))) "r") "Enabled" $values.rbac.createAdditionalControllerCRs "Subject" (get (fromJson (include "operator.ServiceAccountName" (dict "a" (list $dot)))) "r") "RuleFiles" (dict "files/rbac/decommission.ClusterRole.yaml" true "files/rbac/decommission.Role.yaml" true "files/rbac/old-decommission.ClusterRole.yaml" true "files/rbac/old-decommission.Role.yaml" true "files/rbac/pvcunbinder.ClusterRole.yaml" true "files/rbac/pvcunbinder.Role.yaml" true "files/rbac/stretch-rack-awareness.ClusterRole.yaml" $values.multicluster.enabled))) (mustMergeOverwrite (dict "Enabled" false "Name" "" "Subject" "" "RuleFiles" (coalesce nil) "Annotations" (coalesce nil)) (dict "Name" (get (fromJson (include "operator.CRDJobServiceAccountName" (dict "a" (list $dot)))) "r") "Enabled" (or $values.crds.enabled $values.crds.experimental) "Subject" (get (fromJson (include "operator.CRDJobServiceAccountName" (dict "a" (list $dot)))) "r") "Annotations" (dict "helm.sh/hook" "pre-install,pre-upgrade" "helm.sh/hook-delete-policy" "before-hook-creation,hook-succeeded,hook-failed" "helm.sh/hook-weight" "-10") "RuleFiles" (dict "files/rbac/crd-installation.ClusterRole.yaml" true)))) -}} +{{- $bundles := (list (mustMergeOverwrite (dict "Enabled" false "Name" "" "Subject" "" "RuleFiles" (coalesce nil) "Annotations" (coalesce nil)) (dict "Name" (get (fromJson (include "operator.Fullname" (dict "a" (list $dot)))) "r") "Enabled" true "Subject" (get (fromJson (include "operator.ServiceAccountName" (dict "a" (list $dot)))) "r") "RuleFiles" (dict "files/rbac/console.ClusterRole.yaml" true "files/rbac/leader-election.ClusterRole.yaml" true "files/rbac/leader-election.Role.yaml" true "files/rbac/pvcunbinder.ClusterRole.yaml" true "files/rbac/pvcunbinder.Role.yaml" true "files/rbac/rack-awareness.ClusterRole.yaml" true "files/rbac/rpk-debug-bundle.Role.yaml" true "files/rbac/sidecar.Role.yaml" true "files/rbac/v1-manager.ClusterRole.yaml" $values.vectorizedControllers.enabled "files/rbac/v1-manager.Role.yaml" $values.vectorizedControllers.enabled "files/rbac/v2-manager.ClusterRole.yaml" true "files/rbac/pipeline.ClusterRole.yaml" $values.connectController.enabled "files/rbac/multicluster-manager.ClusterRole.yaml" $values.multicluster.enabled "files/rbac/endpoint-steering.ClusterRole.yaml" (not $values.multicluster.enabled)))) (mustMergeOverwrite (dict "Enabled" false "Name" "" "Subject" "" "RuleFiles" (coalesce nil) "Annotations" (coalesce nil)) (dict "Name" (get (fromJson (include "operator.cleanForK8sWithSuffix" (dict "a" (list (get (fromJson (include "operator.Fullname" (dict "a" (list $dot)))) "r") "additional-controllers")))) "r") "Enabled" $values.rbac.createAdditionalControllerCRs "Subject" (get (fromJson (include "operator.ServiceAccountName" (dict "a" (list $dot)))) "r") "RuleFiles" (dict "files/rbac/decommission.ClusterRole.yaml" true "files/rbac/decommission.Role.yaml" true "files/rbac/old-decommission.ClusterRole.yaml" true "files/rbac/old-decommission.Role.yaml" true "files/rbac/pvcunbinder.ClusterRole.yaml" true "files/rbac/pvcunbinder.Role.yaml" true))) (mustMergeOverwrite (dict "Enabled" false "Name" "" "Subject" "" "RuleFiles" (coalesce nil) "Annotations" (coalesce nil)) (dict "Name" (get (fromJson (include "operator.CRDJobServiceAccountName" (dict "a" (list $dot)))) "r") "Enabled" (or $values.crds.enabled $values.crds.experimental) "Subject" (get (fromJson (include "operator.CRDJobServiceAccountName" (dict "a" (list $dot)))) "r") "Annotations" (dict "helm.sh/hook" "pre-install,pre-upgrade" "helm.sh/hook-delete-policy" "before-hook-creation,hook-succeeded,hook-failed" "helm.sh/hook-weight" "-10") "RuleFiles" (dict "files/rbac/crd-installation.ClusterRole.yaml" true)))) -}} {{- $bundles = (concat (default (list) $bundles) (list (mustMergeOverwrite (dict "Enabled" false "Name" "" "Subject" "" "RuleFiles" (coalesce nil) "Annotations" (coalesce nil)) (dict "Name" (get (fromJson (include "operator.MigrationJobServiceAccountName" (dict "a" (list $dot)))) "r") "Enabled" true "Subject" (get (fromJson (include "operator.MigrationJobServiceAccountName" (dict "a" (list $dot)))) "r") "Annotations" (dict "helm.sh/hook" "post-upgrade" "helm.sh/hook-delete-policy" "before-hook-creation,hook-succeeded,hook-failed" "helm.sh/hook-weight" "-10") "RuleFiles" (index $bundles (0 | int)).RuleFiles)))) -}} {{- $_is_returning = true -}} {{- (dict "r" $bundles) | toJson -}} diff --git a/operator/chart/testdata/template-cases.golden.txtar b/operator/chart/testdata/template-cases.golden.txtar index 50aacfbe44..229c7ffd48 100644 --- a/operator/chart/testdata/template-cases.golden.txtar +++ b/operator/chart/testdata/template-cases.golden.txtar @@ -125975,14 +125975,6 @@ rules: - get - list - watch -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch --- # Source: operator/templates/entry-point.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -138498,14 +138490,6 @@ rules: - get - list - watch -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch --- # Source: operator/templates/entry-point.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -140383,14 +140367,6 @@ rules: - get - list - watch -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch --- # Source: operator/templates/entry-point.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -142323,14 +142299,6 @@ rules: - get - list - watch -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch --- # Source: operator/templates/entry-point.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -144257,14 +144225,6 @@ rules: - get - list - watch -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch --- # Source: operator/templates/entry-point.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -146124,14 +146084,6 @@ rules: - get - list - watch -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch --- # Source: operator/templates/entry-point.yaml apiVersion: rbac.authorization.k8s.io/v1 diff --git a/operator/config/rbac/itemized/stretch-rack-awareness.yaml b/operator/config/rbac/itemized/stretch-rack-awareness.yaml deleted file mode 100644 index ae5ac9847e..0000000000 --- a/operator/config/rbac/itemized/stretch-rack-awareness.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: stretch-rack-awareness -rules: -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch diff --git a/operator/internal/controller/stretchrackawareness/doc.go b/operator/internal/controller/stretchrackawareness/doc.go deleted file mode 100644 index 633e79ad59..0000000000 --- a/operator/internal/controller/stretchrackawareness/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2026 Redpanda Data, Inc. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.md -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0 - -// Package stretchrackawareness is an intentionally empty package that houses -// the kubebuilder annotations for generating the ClusterRole the operator -// needs to deploy a StretchCluster with rack awareness enabled. The -// multicluster renderer creates a per-pool rack-awareness ClusterRole -// granting nodes get/list/watch (operator/multicluster/rbac.go), and -// Kubernetes RBAC escalation prevention only lets the operator grant verbs -// it already holds — the plain rack-awareness package above carries `get` -// only, which is all the non-stretch flow needs. -package stretchrackawareness - -// +kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch diff --git a/operator/internal/lifecycle/testdata/stretch-cluster-cases.resources.golden.txtar b/operator/internal/lifecycle/testdata/stretch-cluster-cases.resources.golden.txtar index b412b565e1..a0a61738c4 100644 --- a/operator/internal/lifecycle/testdata/stretch-cluster-cases.resources.golden.txtar +++ b/operator/internal/lifecycle/testdata/stretch-cluster-cases.resources.golden.txtar @@ -1403,28 +1403,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -2057,28 +2041,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -2899,28 +2867,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -3767,28 +3719,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -4589,28 +4525,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -5243,28 +5163,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/operator/multicluster/rbac.go b/operator/multicluster/rbac.go index 6b5d26b975..bcd4aba649 100644 --- a/operator/multicluster/rbac.go +++ b/operator/multicluster/rbac.go @@ -10,234 +10,46 @@ package multicluster import ( - "fmt" - - rbacv1 "k8s.io/api/rbac/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/redpanda-data/common-go/kube" "k8s.io/utils/ptr" + "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25" redpandav1alpha2 "github.com/redpanda-data/redpanda-operator/operator/api/redpanda/v1alpha2" - "github.com/redpanda-data/redpanda-operator/operator/pkg/tplutil" ) -// roles returns Roles across every local pool. -func roles(state *RenderState) []*rbacv1.Role { - var out []*rbacv1.Role - for _, pool := range state.inClusterPools { - out = append(out, rolesForPool(state, pool)...) - } - return out -} - -// rolesForPool returns Roles for a single local pool. RBAC config (enabled, -// RPKDebugBundle) is per-pool, so each pool gets its own Roles named -// --. -func rolesForPool(state *RenderState, pool *redpandav1alpha2.RedpandaBrokerPool) []*rbacv1.Role { - if !pool.Spec.RBAC.IsEnabled() { - return nil - } - - poolFullname := state.poolFullname(pool) - var roles []*rbacv1.Role - - // Sidecar role: allows the sidecar to manage leases, get/list/watch pods and statefulsets. - roles = append(roles, &rbacv1.Role{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "Role", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-sidecar", poolFullname), - Namespace: state.namespace, - Labels: state.commonLabels(), - }, - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"coordination.k8s.io"}, - Resources: []string{"leases"}, - Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"pods"}, - Verbs: []string{"get", "list", "watch"}, - }, - { - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - Verbs: []string{"get", "list", "watch"}, - }, - }, - }) - - // RPK debug bundle role: allows rpk debug bundle to collect cluster diagnostics. - if ptr.Deref(pool.Spec.RBAC.RPKDebugBundle, false) { - roles = append(roles, &rbacv1.Role{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "Role", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-rpk-debug-bundle", poolFullname), - Namespace: state.namespace, - Labels: state.commonLabels(), - }, - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{ - "configmaps", "endpoints", "events", "limitranges", - "persistentvolumeclaims", "pods", "pods/log", - "replicationcontrollers", "resourcequotas", - "serviceaccounts", "services", - }, - Verbs: []string{"get", "list"}, - }, - }, - }) - } - - return roles -} - -// clusterRoles returns ClusterRoles across every local pool. -func clusterRoles(state *RenderState) []*rbacv1.ClusterRole { - var out []*rbacv1.ClusterRole +// rbac returns the RBAC objects across every local pool. +func rbac(state *RenderState) []kube.Object { + var out []kube.Object for _, pool := range state.inClusterPools { - out = append(out, clusterRolesForPool(state, pool)...) + out = append(out, rbacForPool(state, pool).Render()...) } return out } -// clusterRolesForPool returns ClusterRoles for a single local pool. RBAC / -// RackAwareness toggles are per-pool. Cluster-scoped objects insert -// to disambiguate StretchClusters with the same name in -// different namespaces. -func clusterRolesForPool(state *RenderState, pool *redpandav1alpha2.RedpandaBrokerPool) []*rbacv1.ClusterRole { - if !pool.Spec.RBAC.IsEnabled() { - return nil - } - - poolFullname := state.poolFullname(pool) - var clusterRoles []*rbacv1.ClusterRole - - // Self-metrics ClusterRole. - clusterRoles = append(clusterRoles, &rbacv1.ClusterRole{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "ClusterRole", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: tplutil.CleanForK8s(fmt.Sprintf("%s-%s-metrics-reader", poolFullname, state.namespace)), - Labels: state.commonLabels(), +// rbacForPool resolves a single local pool into the shared RBAC set, which +// never sees the CRD types. It is the operator's one translation function for +// everything RBAC shaped. +// +// RBAC and RackAwareness toggles are per-pool, so each pool gets its own +// objects named --. Cluster-scoped names additionally +// fold in to disambiguate StretchClusters sharing a name across +// namespaces. +func rbacForPool(state *RenderState, pool *redpandav1alpha2.RedpandaBrokerPool) redpanda.RoleSet { + enabled := pool.Spec.RBAC.IsEnabled() + fullname := state.poolFullname(pool) + + return redpanda.RoleSet{ + Prefix: fullname, + Namespace: state.namespace, + Labels: state.commonLabels(), + ServiceAccount: pool.Spec.GetServiceAccountName(fullname), + Roles: redpanda.Roles{ + RPKDebugBundle: enabled && ptr.Deref(pool.Spec.RBAC.RPKDebugBundle, false), + Sidecar: enabled, }, - Rules: []rbacv1.PolicyRule{ - { - NonResourceURLs: []string{"/metrics"}, - Verbs: []string{"get"}, - }, + ClusterRoles: redpanda.ClusterRoles{ + MetricsReader: enabled, + RackAwareness: enabled && pool.Spec.RackAwareness.IsEnabled(), }, - }) - - if pool.Spec.RackAwareness.IsEnabled() { - clusterRoles = append(clusterRoles, &rbacv1.ClusterRole{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "ClusterRole", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: tplutil.CleanForK8s(fmt.Sprintf("%s-%s-rack-awareness", poolFullname, state.namespace)), - Labels: state.commonLabels(), - }, - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"nodes"}, - Verbs: []string{"get", "list", "watch"}, - }, - }, - }) - } - - return clusterRoles -} - -// roleBindings returns RoleBindings across every local pool. -func roleBindings(state *RenderState) []*rbacv1.RoleBinding { - var out []*rbacv1.RoleBinding - for _, pool := range state.inClusterPools { - out = append(out, roleBindingsForPool(state, pool)...) - } - return out -} - -// roleBindingsForPool binds the pool's Roles to the pool's ServiceAccount. -func roleBindingsForPool(state *RenderState, pool *redpandav1alpha2.RedpandaBrokerPool) []*rbacv1.RoleBinding { - var roleBindings []*rbacv1.RoleBinding - for _, role := range rolesForPool(state, pool) { - roleBindings = append(roleBindings, &rbacv1.RoleBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "RoleBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: role.ObjectMeta.Name, - Labels: state.commonLabels(), - Namespace: state.namespace, - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "Role", - Name: role.ObjectMeta.Name, - }, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: pool.Spec.GetServiceAccountName(state.poolFullname(pool)), - Namespace: state.namespace, - }, - }, - }) - } - return roleBindings -} - -// clusterRoleBindings returns ClusterRoleBindings across every local pool. -func clusterRoleBindings(state *RenderState) []*rbacv1.ClusterRoleBinding { - var out []*rbacv1.ClusterRoleBinding - for _, pool := range state.inClusterPools { - out = append(out, clusterRoleBindingsForPool(state, pool)...) - } - return out -} - -// clusterRoleBindingsForPool binds the pool's ClusterRoles to the pool's -// ServiceAccount. -func clusterRoleBindingsForPool(state *RenderState, pool *redpandav1alpha2.RedpandaBrokerPool) []*rbacv1.ClusterRoleBinding { - var crbs []*rbacv1.ClusterRoleBinding - for _, clusterRole := range clusterRolesForPool(state, pool) { - crbs = append(crbs, &rbacv1.ClusterRoleBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "ClusterRoleBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: clusterRole.ObjectMeta.Name, - Labels: state.commonLabels(), - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "ClusterRole", - Name: clusterRole.ObjectMeta.Name, - }, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: pool.Spec.GetServiceAccountName(state.poolFullname(pool)), - Namespace: state.namespace, - }, - }, - }) } - return crbs } diff --git a/operator/multicluster/render.go b/operator/multicluster/render.go index 7dd9da30e7..b14d66f886 100644 --- a/operator/multicluster/render.go +++ b/operator/multicluster/render.go @@ -90,10 +90,7 @@ func RenderResources(state *RenderState) ([]kube.Object, error) { manifests = appendIfNotNil(manifests, cm...) manifests = appendIfNotNil(manifests, certIssuers(state)...) manifests = appendIfNotNil(manifests, certs...) - manifests = appendIfNotNil(manifests, roles(state)...) - manifests = appendIfNotNil(manifests, clusterRoles(state)...) - manifests = appendIfNotNil(manifests, roleBindings(state)...) - manifests = appendIfNotNil(manifests, clusterRoleBindings(state)...) + manifests = append(manifests, rbac(state)...) manifests = appendIfNotNil(manifests, lbServices...) manifests = appendIfNotNil(manifests, secretObjs...) manifests = appendIfNotNil(manifests, ppServices...) diff --git a/operator/multicluster/render_buckets.go b/operator/multicluster/render_buckets.go index b0a45a68d6..69a4a7f7f1 100644 --- a/operator/multicluster/render_buckets.go +++ b/operator/multicluster/render_buckets.go @@ -55,9 +55,8 @@ func RenderClusterResources(state *RenderState) ([]kube.Object, error) { // - serviceMonitorForPool — per-pool ServiceMonitor // - nodePortServiceForPool — per-pool external NodePort // - certificatesForPool — per-pool leaf certs (different DNS SANs per pool) -// - rolesForPool / roleBindingsForPool — per-pool RBAC -// - clusterRolesForPool / clusterRoleBindingsForPool — per-pool RBAC -// (cluster-scoped objects but per-pool driven) +// - rbacForPool — per-pool Roles, ClusterRoles, and both binding sets. +// The cluster-scoped halves are per-pool driven despite their scope. func RenderInClusterPoolResources(state *RenderState, pool *redpandav1alpha2.RedpandaBrokerPool) ([]kube.Object, error) { cm, err := redpandaConfigMap(state, pool) if err != nil { @@ -83,10 +82,7 @@ func RenderInClusterPoolResources(state *RenderState, pool *redpandav1alpha2.Red manifests = appendIfNotNil(manifests, serviceMonitorForPool(state, pool)) manifests = appendIfNotNil(manifests, nodePortServiceForPool(state, pool)) manifests = appendIfNotNil(manifests, certs...) - manifests = appendIfNotNil(manifests, rolesForPool(state, pool)...) - manifests = appendIfNotNil(manifests, clusterRolesForPool(state, pool)...) - manifests = appendIfNotNil(manifests, roleBindingsForPool(state, pool)...) - manifests = appendIfNotNil(manifests, clusterRoleBindingsForPool(state, pool)...) + manifests = append(manifests, rbacForPool(state, pool).Render()...) return manifests, nil } diff --git a/operator/multicluster/serviceaccount.go b/operator/multicluster/serviceaccount.go index 6d851c2fc2..77c7b3b39e 100644 --- a/operator/multicluster/serviceaccount.go +++ b/operator/multicluster/serviceaccount.go @@ -11,9 +11,8 @@ package multicluster import ( corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" + "github.com/redpanda-data/redpanda-operator/charts/redpanda/v25" redpandav1alpha2 "github.com/redpanda-data/redpanda-operator/operator/api/redpanda/v1alpha2" ) @@ -40,17 +39,10 @@ func serviceAccountForPool(state *RenderState, pool *redpandav1alpha2.RedpandaBr return nil } - return &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: pool.Spec.GetServiceAccountName(state.poolFullname(pool)), - Namespace: state.namespace, - Labels: state.commonLabels(), - Annotations: sa.Annotations, - }, - AutomountServiceAccountToken: ptr.To(false), - } + return redpanda.ServiceAccount( + pool.Spec.GetServiceAccountName(state.poolFullname(pool)), + state.namespace, + state.commonLabels(), + sa.Annotations, + ) } diff --git a/operator/multicluster/testdata/render-cases.resources.golden.txtar b/operator/multicluster/testdata/render-cases.resources.golden.txtar index a7172aa790..253dd61377 100644 --- a/operator/multicluster/testdata/render-cases.resources.golden.txtar +++ b/operator/multicluster/testdata/render-cases.resources.golden.txtar @@ -449,28 +449,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -1345,28 +1329,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -2235,28 +2203,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -2267,7 +2219,7 @@ app.kubernetes.io/instance: custom-cluster-domain app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: custom-cluster-domain-pool-a-custom-cluster-domain-metrics-read + name: custom-cluster-domain-pool-a-custom-cluster-domain-metrics-reader rules: - nonResourceURLs: - /metrics @@ -2301,11 +2253,11 @@ app.kubernetes.io/instance: custom-cluster-domain app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: custom-cluster-domain-pool-a-custom-cluster-domain-metrics-read + name: custom-cluster-domain-pool-a-custom-cluster-domain-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: custom-cluster-domain-pool-a-custom-cluster-domain-metrics-read + name: custom-cluster-domain-pool-a-custom-cluster-domain-metrics-reader subjects: - kind: ServiceAccount name: custom-cluster-domain-pool-a @@ -3110,28 +3062,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -3984,28 +3920,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -4858,28 +4778,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -5732,28 +5636,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -5764,7 +5652,7 @@ app.kubernetes.io/instance: custom-resources-explicit app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: custom-resources-explicit-pool-a-custom-resources-explicit-metr + name: custom-resources-explicit-pool-a-custom-resources-explicit-metrics-reader rules: - nonResourceURLs: - /metrics @@ -5798,11 +5686,11 @@ app.kubernetes.io/instance: custom-resources-explicit app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: custom-resources-explicit-pool-a-custom-resources-explicit-metr + name: custom-resources-explicit-pool-a-custom-resources-explicit-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: custom-resources-explicit-pool-a-custom-resources-explicit-metr + name: custom-resources-explicit-pool-a-custom-resources-explicit-metrics-reader subjects: - kind: ServiceAccount name: custom-resources-explicit-pool-a @@ -6606,28 +6494,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -7480,28 +7352,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -7512,7 +7368,7 @@ app.kubernetes.io/instance: external-loadbalancer app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: external-loadbalancer-pool-a-external-loadbalancer-metrics-read + name: external-loadbalancer-pool-a-external-loadbalancer-metrics-reader rules: - nonResourceURLs: - /metrics @@ -7546,11 +7402,11 @@ app.kubernetes.io/instance: external-loadbalancer app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: external-loadbalancer-pool-a-external-loadbalancer-metrics-read + name: external-loadbalancer-pool-a-external-loadbalancer-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: external-loadbalancer-pool-a-external-loadbalancer-metrics-read + name: external-loadbalancer-pool-a-external-loadbalancer-metrics-reader subjects: - kind: ServiceAccount name: external-loadbalancer-pool-a @@ -8354,28 +8210,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -9228,28 +9068,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -10090,28 +9914,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -11038,28 +10846,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -12587,69 +12379,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - - get - - list - - watch -- apiVersion: rbac.authorization.k8s.io/v1 - kind: Role - metadata: - labels: - app.kubernetes.io/cluster-name: test - app.kubernetes.io/component: redpanda - app.kubernetes.io/instance: full-featured - app.kubernetes.io/managed-by: redpanda-operator - app.kubernetes.io/name: redpanda - team: data-platform - name: full-featured-pool-hot-sidecar - namespace: full-featured - rules: - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - get - list - - watch - - create - - update - patch - - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - - get - - list + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -12668,7 +12403,7 @@ verbs: - get - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole + kind: RoleBinding metadata: labels: app.kubernetes.io/cluster-name: test @@ -12677,14 +12412,18 @@ app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda team: data-platform - name: full-featured-pool-hot-full-featured-metrics-reader - rules: - - nonResourceURLs: - - /metrics - verbs: - - get + name: full-featured-pool-cold-sidecar + namespace: full-featured + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: full-featured-pool-cold-sidecar + subjects: + - kind: ServiceAccount + name: full-featured-pool-cold + namespace: full-featured - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding + kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/cluster-name: test @@ -12693,18 +12432,17 @@ app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda team: data-platform - name: full-featured-pool-cold-sidecar - namespace: full-featured + name: full-featured-pool-cold-full-featured-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io - kind: Role - name: full-featured-pool-cold-sidecar + kind: ClusterRole + name: full-featured-pool-cold-full-featured-metrics-reader subjects: - kind: ServiceAccount name: full-featured-pool-cold namespace: full-featured - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding + kind: Role metadata: labels: app.kubernetes.io/cluster-name: test @@ -12715,16 +12453,21 @@ team: data-platform name: full-featured-pool-hot-sidecar namespace: full-featured - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: full-featured-pool-hot-sidecar - subjects: - - kind: ServiceAccount - name: full-featured-pool-hot - namespace: full-featured + rules: + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRoleBinding + kind: ClusterRole metadata: labels: app.kubernetes.io/cluster-name: test @@ -12733,14 +12476,31 @@ app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda team: data-platform - name: full-featured-pool-cold-full-featured-metrics-reader + name: full-featured-pool-hot-full-featured-metrics-reader + rules: + - nonResourceURLs: + - /metrics + verbs: + - get +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/cluster-name: test + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: full-featured + app.kubernetes.io/managed-by: redpanda-operator + app.kubernetes.io/name: redpanda + team: data-platform + name: full-featured-pool-hot-sidecar + namespace: full-featured roleRef: apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: full-featured-pool-cold-full-featured-metrics-reader + kind: Role + name: full-featured-pool-hot-sidecar subjects: - kind: ServiceAccount - name: full-featured-pool-cold + name: full-featured-pool-hot namespace: full-featured - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -13953,28 +13713,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -14827,28 +14571,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -15753,28 +15481,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -16623,28 +16335,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -17536,28 +17232,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -18410,28 +18090,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -19312,28 +18976,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -20572,68 +20220,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - - get - - list - - watch -- apiVersion: rbac.authorization.k8s.io/v1 - kind: Role - metadata: - labels: - app.kubernetes.io/cluster-name: test - app.kubernetes.io/component: redpanda - app.kubernetes.io/instance: multi-pool - app.kubernetes.io/managed-by: redpanda-operator - app.kubernetes.io/name: redpanda - name: multi-pool-pool-b-sidecar - namespace: multi-pool - rules: - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - get - list - - watch - - create - - update - patch - - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - - get - - list + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -20650,21 +20242,6 @@ - /metrics verbs: - get -- apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - labels: - app.kubernetes.io/cluster-name: test - app.kubernetes.io/component: redpanda - app.kubernetes.io/instance: multi-pool - app.kubernetes.io/managed-by: redpanda-operator - app.kubernetes.io/name: redpanda - name: multi-pool-pool-b-multi-pool-metrics-reader - rules: - - nonResourceURLs: - - /metrics - verbs: - - get - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -20685,7 +20262,7 @@ name: multi-pool-pool-a namespace: multi-pool - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding + kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/cluster-name: test @@ -20693,18 +20270,17 @@ app.kubernetes.io/instance: multi-pool app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-pool-b-sidecar - namespace: multi-pool + name: multi-pool-pool-a-multi-pool-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io - kind: Role - name: multi-pool-pool-b-sidecar + kind: ClusterRole + name: multi-pool-pool-a-multi-pool-metrics-reader subjects: - kind: ServiceAccount - name: multi-pool-pool-b + name: multi-pool-pool-a namespace: multi-pool - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRoleBinding + kind: Role metadata: labels: app.kubernetes.io/cluster-name: test @@ -20712,14 +20288,54 @@ app.kubernetes.io/instance: multi-pool app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-pool-a-multi-pool-metrics-reader + name: multi-pool-pool-b-sidecar + namespace: multi-pool + rules: + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + labels: + app.kubernetes.io/cluster-name: test + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: multi-pool + app.kubernetes.io/managed-by: redpanda-operator + app.kubernetes.io/name: redpanda + name: multi-pool-pool-b-multi-pool-metrics-reader + rules: + - nonResourceURLs: + - /metrics + verbs: + - get +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/cluster-name: test + app.kubernetes.io/component: redpanda + app.kubernetes.io/instance: multi-pool + app.kubernetes.io/managed-by: redpanda-operator + app.kubernetes.io/name: redpanda + name: multi-pool-pool-b-sidecar + namespace: multi-pool roleRef: apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: multi-pool-pool-a-multi-pool-metrics-reader + kind: Role + name: multi-pool-pool-b-sidecar subjects: - kind: ServiceAccount - name: multi-pool-pool-a + name: multi-pool-pool-b namespace: multi-pool - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -22515,31 +22131,15 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 - kind: Role + kind: ClusterRole metadata: labels: app.kubernetes.io/cluster-name: test @@ -22547,39 +22147,14 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-b-sidecar - namespace: multi-pool-heterogeneous-ports + name: multi-pool-heterogeneous-ports-pool-a-multi-pool-heterogeneous-ports-metrics-reader rules: - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets + - nonResourceURLs: + - /metrics verbs: - get - - list - - watch - apiVersion: rbac.authorization.k8s.io/v1 - kind: Role + kind: RoleBinding metadata: labels: app.kubernetes.io/cluster-name: test @@ -22587,39 +22162,18 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-c-sidecar + name: multi-pool-heterogeneous-ports-pool-a-sidecar + namespace: multi-pool-heterogeneous-ports + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: multi-pool-heterogeneous-ports-pool-a-sidecar + subjects: + - kind: ServiceAccount + name: multi-pool-heterogeneous-ports-pool-a namespace: multi-pool-heterogeneous-ports - rules: - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - - get - - list - - watch - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole + kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/cluster-name: test @@ -22627,14 +22181,17 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-a-multi-pool-heterogeneous - rules: - - nonResourceURLs: - - /metrics - verbs: - - get + name: multi-pool-heterogeneous-ports-pool-a-multi-pool-heterogeneous-ports-metrics-reader + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: multi-pool-heterogeneous-ports-pool-a-multi-pool-heterogeneous-ports-metrics-reader + subjects: + - kind: ServiceAccount + name: multi-pool-heterogeneous-ports-pool-a + namespace: multi-pool-heterogeneous-ports - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole + kind: Role metadata: labels: app.kubernetes.io/cluster-name: test @@ -22642,12 +22199,21 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-b-multi-pool-heterogeneous + name: multi-pool-heterogeneous-ports-pool-b-sidecar + namespace: multi-pool-heterogeneous-ports rules: - - nonResourceURLs: - - /metrics + - apiGroups: + - coordination.k8s.io + resources: + - leases verbs: + - create + - delete - get + - list + - patch + - update + - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -22657,7 +22223,7 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-c-multi-pool-heterogeneous + name: multi-pool-heterogeneous-ports-pool-b-multi-pool-heterogeneous-ports-metrics-reader rules: - nonResourceURLs: - /metrics @@ -22672,18 +22238,18 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-a-sidecar + name: multi-pool-heterogeneous-ports-pool-b-sidecar namespace: multi-pool-heterogeneous-ports roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: multi-pool-heterogeneous-ports-pool-a-sidecar + name: multi-pool-heterogeneous-ports-pool-b-sidecar subjects: - kind: ServiceAccount - name: multi-pool-heterogeneous-ports-pool-a + name: multi-pool-heterogeneous-ports-pool-b namespace: multi-pool-heterogeneous-ports - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding + kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/cluster-name: test @@ -22691,18 +22257,17 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-b-sidecar - namespace: multi-pool-heterogeneous-ports + name: multi-pool-heterogeneous-ports-pool-b-multi-pool-heterogeneous-ports-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io - kind: Role - name: multi-pool-heterogeneous-ports-pool-b-sidecar + kind: ClusterRole + name: multi-pool-heterogeneous-ports-pool-b-multi-pool-heterogeneous-ports-metrics-reader subjects: - kind: ServiceAccount name: multi-pool-heterogeneous-ports-pool-b namespace: multi-pool-heterogeneous-ports - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding + kind: Role metadata: labels: app.kubernetes.io/cluster-name: test @@ -22712,16 +22277,21 @@ app.kubernetes.io/name: redpanda name: multi-pool-heterogeneous-ports-pool-c-sidecar namespace: multi-pool-heterogeneous-ports - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: multi-pool-heterogeneous-ports-pool-c-sidecar - subjects: - - kind: ServiceAccount - name: multi-pool-heterogeneous-ports-pool-c - namespace: multi-pool-heterogeneous-ports + rules: + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRoleBinding + kind: ClusterRole metadata: labels: app.kubernetes.io/cluster-name: test @@ -22729,17 +22299,14 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-a-multi-pool-heterogeneous - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: multi-pool-heterogeneous-ports-pool-a-multi-pool-heterogeneous - subjects: - - kind: ServiceAccount - name: multi-pool-heterogeneous-ports-pool-a - namespace: multi-pool-heterogeneous-ports + name: multi-pool-heterogeneous-ports-pool-c-multi-pool-heterogeneous-ports-metrics-reader + rules: + - nonResourceURLs: + - /metrics + verbs: + - get - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRoleBinding + kind: RoleBinding metadata: labels: app.kubernetes.io/cluster-name: test @@ -22747,14 +22314,15 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-b-multi-pool-heterogeneous + name: multi-pool-heterogeneous-ports-pool-c-sidecar + namespace: multi-pool-heterogeneous-ports roleRef: apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: multi-pool-heterogeneous-ports-pool-b-multi-pool-heterogeneous + kind: Role + name: multi-pool-heterogeneous-ports-pool-c-sidecar subjects: - kind: ServiceAccount - name: multi-pool-heterogeneous-ports-pool-b + name: multi-pool-heterogeneous-ports-pool-c namespace: multi-pool-heterogeneous-ports - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -22765,11 +22333,11 @@ app.kubernetes.io/instance: multi-pool-heterogeneous-ports app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: multi-pool-heterogeneous-ports-pool-c-multi-pool-heterogeneous + name: multi-pool-heterogeneous-ports-pool-c-multi-pool-heterogeneous-ports-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: multi-pool-heterogeneous-ports-pool-c-multi-pool-heterogeneous + name: multi-pool-heterogeneous-ports-pool-c-multi-pool-heterogeneous-ports-metrics-reader subjects: - kind: ServiceAccount name: multi-pool-heterogeneous-ports-pool-c @@ -23975,28 +23543,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -24007,7 +23559,7 @@ app.kubernetes.io/instance: per-pod-service-overrides app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: per-pod-service-overrides-pool-a-per-pod-service-overrides-metr + name: per-pod-service-overrides-pool-a-per-pod-service-overrides-metrics-reader rules: - nonResourceURLs: - /metrics @@ -24041,11 +23593,11 @@ app.kubernetes.io/instance: per-pod-service-overrides app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: per-pod-service-overrides-pool-a-per-pod-service-overrides-metr + name: per-pod-service-overrides-pool-a-per-pod-service-overrides-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: per-pod-service-overrides-pool-a-per-pod-service-overrides-metr + name: per-pod-service-overrides-pool-a-per-pod-service-overrides-metrics-reader subjects: - kind: ServiceAccount name: per-pod-service-overrides-pool-a @@ -24739,28 +24291,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -24771,7 +24307,7 @@ app.kubernetes.io/instance: per-pod-service-remote-disabled app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: per-pod-service-remote-disabled-pool-a-per-pod-service-remote-d + name: per-pod-service-remote-disabled-pool-a-per-pod-service-remote-disabled-metrics-reader rules: - nonResourceURLs: - /metrics @@ -24805,11 +24341,11 @@ app.kubernetes.io/instance: per-pod-service-remote-disabled app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: per-pod-service-remote-disabled-pool-a-per-pod-service-remote-d + name: per-pod-service-remote-disabled-pool-a-per-pod-service-remote-disabled-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: per-pod-service-remote-disabled-pool-a-per-pod-service-remote-d + name: per-pod-service-remote-disabled-pool-a-per-pod-service-remote-disabled-metrics-reader subjects: - kind: ServiceAccount name: per-pod-service-remote-disabled-pool-a @@ -25524,28 +25060,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -25556,7 +25076,7 @@ app.kubernetes.io/instance: pool-overrides-cluster app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: pool-overrides-cluster-pool-a-pool-overrides-cluster-metrics-re + name: pool-overrides-cluster-pool-a-pool-overrides-cluster-metrics-reader rules: - nonResourceURLs: - /metrics @@ -25590,11 +25110,11 @@ app.kubernetes.io/instance: pool-overrides-cluster app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: pool-overrides-cluster-pool-a-pool-overrides-cluster-metrics-re + name: pool-overrides-cluster-pool-a-pool-overrides-cluster-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: pool-overrides-cluster-pool-a-pool-overrides-cluster-metrics-re + name: pool-overrides-cluster-pool-a-pool-overrides-cluster-metrics-reader subjects: - kind: ServiceAccount name: pool-overrides-cluster-pool-a @@ -26398,28 +25918,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -27272,28 +26776,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -28150,28 +27638,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -29043,28 +28515,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -29075,7 +28531,7 @@ app.kubernetes.io/instance: sasl-scram512-with-tls app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: sasl-scram512-with-tls-pool-a-sasl-scram512-with-tls-metrics-re + name: sasl-scram512-with-tls-pool-a-sasl-scram512-with-tls-metrics-reader rules: - nonResourceURLs: - /metrics @@ -29109,11 +28565,11 @@ app.kubernetes.io/instance: sasl-scram512-with-tls app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: sasl-scram512-with-tls-pool-a-sasl-scram512-with-tls-metrics-re + name: sasl-scram512-with-tls-pool-a-sasl-scram512-with-tls-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: sasl-scram512-with-tls-pool-a-sasl-scram512-with-tls-metrics-re + name: sasl-scram512-with-tls-pool-a-sasl-scram512-with-tls-metrics-reader subjects: - kind: ServiceAccount name: sasl-scram512-with-tls-pool-a @@ -29908,28 +29364,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -30693,28 +30133,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -31567,28 +30991,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -32441,28 +31849,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -32473,7 +31865,7 @@ app.kubernetes.io/instance: tiered-storage-emptydir app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: tiered-storage-emptydir-pool-a-tiered-storage-emptydir-metrics + name: tiered-storage-emptydir-pool-a-tiered-storage-emptydir-metrics-reader rules: - nonResourceURLs: - /metrics @@ -32507,11 +31899,11 @@ app.kubernetes.io/instance: tiered-storage-emptydir app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: tiered-storage-emptydir-pool-a-tiered-storage-emptydir-metrics + name: tiered-storage-emptydir-pool-a-tiered-storage-emptydir-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: tiered-storage-emptydir-pool-a-tiered-storage-emptydir-metrics + name: tiered-storage-emptydir-pool-a-tiered-storage-emptydir-metrics-reader subjects: - kind: ServiceAccount name: tiered-storage-emptydir-pool-a @@ -33315,28 +32707,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -33347,7 +32723,7 @@ app.kubernetes.io/instance: tiered-storage-hostpath app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: tiered-storage-hostpath-pool-a-tiered-storage-hostpath-metrics + name: tiered-storage-hostpath-pool-a-tiered-storage-hostpath-metrics-reader rules: - nonResourceURLs: - /metrics @@ -33381,11 +32757,11 @@ app.kubernetes.io/instance: tiered-storage-hostpath app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: tiered-storage-hostpath-pool-a-tiered-storage-hostpath-metrics + name: tiered-storage-hostpath-pool-a-tiered-storage-hostpath-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: tiered-storage-hostpath-pool-a-tiered-storage-hostpath-metrics + name: tiered-storage-hostpath-pool-a-tiered-storage-hostpath-metrics-reader subjects: - kind: ServiceAccount name: tiered-storage-hostpath-pool-a @@ -34189,28 +33565,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -34881,28 +34241,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -35755,28 +35099,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -36629,28 +35957,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -37503,28 +36815,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -38488,28 +37784,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -38520,7 +37800,7 @@ app.kubernetes.io/instance: tls-multi-cert-listener-order app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: tls-multi-cert-listener-order-pool-a-tls-multi-cert-listener-or + name: tls-multi-cert-listener-order-pool-a-tls-multi-cert-listener-order-metrics-reader rules: - nonResourceURLs: - /metrics @@ -38554,11 +37834,11 @@ app.kubernetes.io/instance: tls-multi-cert-listener-order app.kubernetes.io/managed-by: redpanda-operator app.kubernetes.io/name: redpanda - name: tls-multi-cert-listener-order-pool-a-tls-multi-cert-listener-or + name: tls-multi-cert-listener-order-pool-a-tls-multi-cert-listener-order-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: tls-multi-cert-listener-order-pool-a-tls-multi-cert-listener-or + name: tls-multi-cert-listener-order-pool-a-tls-multi-cert-listener-order-metrics-reader subjects: - kind: ServiceAccount name: tls-multi-cert-listener-order-pool-a @@ -39362,28 +38642,12 @@ resources: - leases verbs: - - get - - list - - watch - create - - update - - patch - delete - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - get - list + - patch + - update - watch - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/taskfiles/k8s.yml b/taskfiles/k8s.yml index e5aff27bd7..646ef1af6c 100644 --- a/taskfiles/k8s.yml +++ b/taskfiles/k8s.yml @@ -88,7 +88,6 @@ tasks: deps: - { task: generate:rbac:bundle, vars: { NAME: rpk-debug-bundle, PACKAGE: ./internal/controller/rpkdebugbundle } } - { task: generate:rbac:bundle, vars: { NAME: rack-awareness, PACKAGE: ./internal/controller/rackawareness } } - - { task: generate:rbac:bundle, vars: { NAME: stretch-rack-awareness, PACKAGE: ./internal/controller/stretchrackawareness } } - { task: generate:rbac:bundle, vars: { NAME: old-decommission, PACKAGE: ./internal/controller/olddecommission } } - { task: generate:rbac:bundle, vars: { NAME: pvcunbinder, PACKAGE: ./internal/controller/pvcunbinder } } - { task: generate:rbac:bundle, vars: { NAME: v2-manager, PACKAGE: ./internal/controller/redpanda } } @@ -121,27 +120,32 @@ tasks: rbac:roleName={{ .NAME }} \ output:rbac:stdout > config/rbac/itemized/{{ .NAME }}.yaml - # Collect all permissions relevant for the redpanda chart and drop them into ./files, separated by kind. - # The chart embeds these permissions so they can't get out of sync with the operator. + # Collect all permissions relevant for the redpanda chart and drop them into + # ../charts/redpanda/testdata, separated by kind. The chart hand-writes these + # rules so they're reachable from transpiled template land; TestRoleRulesMatchGenerated + # holds the hand-written catalog to these fixtures. generate:rbac:embedded: internal: true dir: 'operator' sources: - ./config/rbac/itemized/*.yaml + - ./config/rbac/bases/auth_proxy/*.yaml generates: - - ../charts/redpanda/chart/files/*.yaml + - ../charts/redpanda/testdata/rbac/*.yaml - ./chart/files/rbac/*.yaml cmds: - - rm ../charts/redpanda/chart/files/*.yaml + - rm -rf ../charts/redpanda/testdata/rbac/ + - mkdir -p ../charts/redpanda/testdata/rbac/ - | yq e \ - --split-exp '"../charts/redpanda/chart/files/\( .metadata.name ).\( .kind ).yaml"' \ + --split-exp '"../charts/redpanda/testdata/rbac/\( .metadata.name ).\( .kind ).yaml"' \ '.' \ ./config/rbac/itemized/decommission.yaml \ ./config/rbac/itemized/pvcunbinder.yaml \ ./config/rbac/itemized/sidecar.yaml \ ./config/rbac/itemized/rpk-debug-bundle.yaml \ - ./config/rbac/itemized/rack-awareness.yaml + ./config/rbac/itemized/rack-awareness.yaml \ + ./config/rbac/bases/auth_proxy/metrics_reader_role.yaml # And once more for the Operator's chart. - mkdir -p chart/files/rbac/ && (rm chart/files/rbac/*.yaml || true) @@ -154,7 +158,6 @@ tasks: ./config/rbac/itemized/old-decommission.yaml \ ./config/rbac/itemized/pvcunbinder.yaml \ ./config/rbac/itemized/rack-awareness.yaml \ - ./config/rbac/itemized/stretch-rack-awareness.yaml \ ./config/rbac/itemized/rpk-debug-bundle.yaml \ ./config/rbac/itemized/sidecar.yaml \ ./config/rbac/itemized/crd-installation.yaml \