Skip to content

Commit 0a870c1

Browse files
committed
Automate helm ClusterRole RBAC sync from kubebuilder
1 parent ed454f5 commit 0a870c1

File tree

4 files changed

+325
-57
lines changed

4 files changed

+325
-57
lines changed

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ deploy: manifests
8181
manifests: controller-gen kustomize
8282
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=controller-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
8383
yq eval '.metadata.name = "webhook"' -i config/webhook/manifests.yaml
84+
hack/sync-rbac-to-helm.sh
8485

8586
crds: manifests
8687
$(MOVE_GATEWAY_CRDS)
@@ -204,7 +205,7 @@ lint:
204205
echo "TODO"
205206

206207
.PHONY: quick-ci
207-
quick-ci: verify-versions verify-generate verify-crds
208+
quick-ci: verify-versions verify-generate verify-crds verify-rbac-sync
208209
echo "Done!"
209210

210211
.PHONY: verify-generate
@@ -215,6 +216,10 @@ verify-generate:
215216
verify-crds:
216217
hack/verify-crds.sh
217218

219+
.PHONY: verify-rbac-sync
220+
verify-rbac-sync:
221+
hack/verify-rbac-sync.sh
222+
218223
.PHONY: verify-versions
219224
verify-versions:
220225
hack/verify-versions.sh

hack/sync-rbac-to-helm.sh

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright 2025 The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# This script syncs RBAC rules from the kubebuilder-generated role.yaml
18+
# into the helm chart's rbac.yaml template, keeping helm-specific sections
19+
# (leader election, bindings, conditionals) intact.
20+
21+
set -o errexit
22+
set -o nounset
23+
set -o pipefail
24+
25+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26+
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
27+
28+
KUBEBUILDER_ROLE="${ROOT_DIR}/config/rbac/role.yaml"
29+
HELM_RBAC="${ROOT_DIR}/helm/aws-load-balancer-controller/templates/rbac.yaml"
30+
31+
if [ ! -f "${KUBEBUILDER_ROLE}" ]; then
32+
echo "Error: kubebuilder role not found at ${KUBEBUILDER_ROLE}"
33+
echo "Run 'make manifests' first."
34+
exit 1
35+
fi
36+
37+
if [ ! -f "${HELM_RBAC}" ]; then
38+
echo "Error: helm rbac template not found at ${HELM_RBAC}"
39+
exit 1
40+
fi
41+
42+
# Extract rules from kubebuilder role.yaml and convert to helm flow style
43+
# We use python3 to parse the kubebuilder YAML (simple structure, no external deps)
44+
generate_helm_clusterrole_rules() {
45+
python3 -c '
46+
import sys
47+
48+
def parse_role_yaml(filepath):
49+
"""Parse kubebuilder role.yaml without external YAML library."""
50+
with open(filepath) as f:
51+
lines = f.readlines()
52+
53+
rules = []
54+
current_rule = None
55+
current_key = None
56+
in_rules = False
57+
58+
for line in lines:
59+
raw = line.rstrip("\n")
60+
stripped = raw.strip()
61+
62+
if not stripped or stripped.startswith("#") or stripped.startswith("---"):
63+
continue
64+
65+
# Detect top-level keys
66+
if not raw.startswith(" ") and not raw.startswith("-"):
67+
if stripped == "rules:":
68+
in_rules = True
69+
else:
70+
in_rules = False
71+
continue
72+
73+
if not in_rules:
74+
continue
75+
76+
# New rule: line starts with "- " at rule level (typically 0 or minimal indent)
77+
if raw.startswith("- "):
78+
if current_rule:
79+
rules.append(current_rule)
80+
current_rule = {}
81+
# Handle "- apiGroups:" on the same line
82+
key_part = stripped[2:] # remove "- "
83+
if key_part.endswith(":"):
84+
current_key = key_part[:-1]
85+
current_rule[current_key] = []
86+
continue
87+
88+
# Key at rule level (indented, no dash)
89+
if not stripped.startswith("-") and stripped.endswith(":"):
90+
current_key = stripped[:-1].strip()
91+
if current_rule is not None and current_key not in current_rule:
92+
current_rule[current_key] = []
93+
continue
94+
95+
# List item
96+
if stripped.startswith("- "):
97+
value = stripped[2:].strip().strip("\"").strip("'\''")
98+
if current_rule is not None and current_key:
99+
current_rule[current_key].append(value)
100+
continue
101+
102+
if current_rule:
103+
rules.append(current_rule)
104+
105+
return rules
106+
107+
def format_list(items):
108+
return "[" + ", ".join(str(i) for i in items) + "]"
109+
110+
rules = parse_role_yaml(sys.argv[1])
111+
for rule in rules:
112+
api_groups = rule.get("apiGroups", [])
113+
resources = rule.get("resources", [])
114+
verbs = rule.get("verbs", [])
115+
resource_names = rule.get("resourceNames", None)
116+
117+
formatted_groups = []
118+
for g in api_groups:
119+
if g == "":
120+
formatted_groups.append("\"\"")
121+
else:
122+
formatted_groups.append("\"" + g + "\"")
123+
124+
print("- apiGroups: [{}]".format(", ".join(formatted_groups)))
125+
print(" resources: {}".format(format_list(resources)))
126+
if resource_names:
127+
print(" resourceNames: {}".format(format_list(resource_names)))
128+
print(" verbs: {}".format(format_list(verbs)))
129+
' "${KUBEBUILDER_ROLE}"
130+
}
131+
132+
# Build the new helm rbac.yaml
133+
# We preserve the leader election Role, RoleBinding, and ClusterRoleBinding
134+
# from the existing template, and only replace the ClusterRole rules.
135+
136+
GENERATED_RULES=$(generate_helm_clusterrole_rules)
137+
138+
cat > "${HELM_RBAC}" << 'HELM_HEADER'
139+
{{- if .Values.rbac.create }}
140+
apiVersion: rbac.authorization.k8s.io/v1
141+
kind: Role
142+
metadata:
143+
name: {{ template "aws-load-balancer-controller.fullname" . }}-leader-election-role
144+
namespace: {{ .Release.Namespace }}
145+
labels:
146+
{{- include "aws-load-balancer-controller.labels" . | nindent 4 }}
147+
rules:
148+
- apiGroups: [""]
149+
resources: [configmaps]
150+
verbs: [create]
151+
- apiGroups: [""]
152+
resources: [configmaps]
153+
resourceNames: [aws-load-balancer-controller-leader]
154+
verbs: [get, patch, update]
155+
- apiGroups:
156+
- "coordination.k8s.io"
157+
resources:
158+
- leases
159+
verbs:
160+
- create
161+
- apiGroups:
162+
- "coordination.k8s.io"
163+
resources:
164+
- leases
165+
resourceNames:
166+
- aws-load-balancer-controller-leader
167+
verbs:
168+
- get
169+
- update
170+
- patch
171+
---
172+
apiVersion: rbac.authorization.k8s.io/v1
173+
kind: RoleBinding
174+
metadata:
175+
name: {{ template "aws-load-balancer-controller.fullname" . }}-leader-election-rolebinding
176+
namespace: {{ .Release.Namespace }}
177+
labels:
178+
{{- include "aws-load-balancer-controller.labels" . | nindent 4 }}
179+
roleRef:
180+
apiGroup: rbac.authorization.k8s.io
181+
kind: Role
182+
name: {{ template "aws-load-balancer-controller.fullname" . }}-leader-election-role
183+
subjects:
184+
- kind: ServiceAccount
185+
name: {{ template "aws-load-balancer-controller.serviceAccountName" . }}
186+
namespace: {{ .Release.Namespace }}
187+
---
188+
apiVersion: rbac.authorization.k8s.io/v1
189+
kind: ClusterRole
190+
metadata:
191+
name: {{ template "aws-load-balancer-controller.fullname" . }}-role
192+
labels:
193+
{{- include "aws-load-balancer-controller.labels" . | nindent 4 }}
194+
rules:
195+
HELM_HEADER
196+
197+
# Append the generated rules (from kubebuilder source of truth)
198+
echo "# AUTO-GENERATED from config/rbac/role.yaml by hack/sync-rbac-to-helm.sh" >> "${HELM_RBAC}"
199+
echo "# Do not edit these rules manually. Run 'make manifests' to update." >> "${HELM_RBAC}"
200+
echo "${GENERATED_RULES}" >> "${HELM_RBAC}"
201+
202+
# Append helm-specific conditional rules and the ClusterRoleBinding
203+
cat >> "${HELM_RBAC}" << 'HELM_FOOTER'
204+
{{- if .Values.clusterSecretsPermissions.allowAllSecrets }}
205+
- apiGroups: [""]
206+
resources: [secrets]
207+
verbs: [get, list, watch]
208+
{{- end }}
209+
---
210+
apiVersion: rbac.authorization.k8s.io/v1
211+
kind: ClusterRoleBinding
212+
metadata:
213+
name: {{ template "aws-load-balancer-controller.fullname" . }}-rolebinding
214+
labels:
215+
{{- include "aws-load-balancer-controller.labels" . | nindent 4 }}
216+
roleRef:
217+
apiGroup: rbac.authorization.k8s.io
218+
kind: ClusterRole
219+
name: {{ template "aws-load-balancer-controller.fullname" . }}-role
220+
subjects:
221+
- kind: ServiceAccount
222+
name: {{ template "aws-load-balancer-controller.serviceAccountName" . }}
223+
namespace: {{ .Release.Namespace }}
224+
{{- end }}
225+
HELM_FOOTER
226+
227+
echo "Synced RBAC rules from ${KUBEBUILDER_ROLE} to ${HELM_RBAC}"

hack/verify-rbac-sync.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright 2025 The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# Verifies that the helm chart RBAC rules are in sync with the
18+
# kubebuilder-generated role.yaml. Fails if they have drifted.
19+
20+
set -o errexit
21+
set -o nounset
22+
set -o pipefail
23+
24+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25+
26+
"${SCRIPT_DIR}/sync-rbac-to-helm.sh"
27+
28+
changed_files=$(git status --porcelain --untracked-files=no -- helm/aws-load-balancer-controller/templates/rbac.yaml || true)
29+
if [ -n "${changed_files}" ]; then
30+
echo "Detected that helm RBAC is out of sync with kubebuilder RBAC; run 'make sync-rbac'"
31+
echo "changed files:"
32+
printf "%s\n" "${changed_files}"
33+
echo "git diff:"
34+
git --no-pager diff -- helm/aws-load-balancer-controller/templates/rbac.yaml
35+
echo "To fix: run 'make sync-rbac'"
36+
exit 1
37+
fi

0 commit comments

Comments
 (0)