@@ -22,21 +22,17 @@ import (
2222 "errors"
2323 "fmt"
2424 "strings"
25- "time"
2625
2726 topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1"
2827 corev1 "k8s.io/api/core/v1"
2928 rbacv1 "k8s.io/api/rbac/v1"
30- k8s_errors "k8s.io/apimachinery/pkg/api/errors"
3129 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3230 ctrl "sigs.k8s.io/controller-runtime"
33- "sigs.k8s.io/controller-runtime/pkg/client "
31+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil "
3432
3533 "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
3634 "github.com/openstack-k8s-operators/lib-common/modules/common/helper"
3735 common_rbac "github.com/openstack-k8s-operators/lib-common/modules/common/rbac"
38- common_role "github.com/openstack-k8s-operators/lib-common/modules/common/role"
39- common_rolebinding "github.com/openstack-k8s-operators/lib-common/modules/common/rolebinding"
4036 "github.com/openstack-k8s-operators/lib-common/modules/common/secret"
4137)
4238
@@ -207,34 +203,34 @@ func ensureConsoleNamespace(
207203 ns := & corev1.Namespace {
208204 ObjectMeta : metav1.ObjectMeta {
209205 Name : consolesNamespace ,
210- Labels : map [string ]string {
211- "app" : "ironic" ,
212- },
213206 },
214207 }
215208
216- err := h .GetClient ().Get (ctx , client.ObjectKey {Name : consolesNamespace }, ns )
217- if err != nil {
218- if k8s_errors .IsNotFound (err ) {
219- // Namespace doesn't exist, create it
220- err = h .GetClient ().Create (ctx , ns )
221- if err != nil {
222- return fmt .Errorf ("failed to create console namespace %s: %w" , consolesNamespace , err )
223- }
224- h .GetLogger ().Info (fmt .Sprintf ("Created console namespace: %s" , consolesNamespace ))
225- return nil
209+ op , err := controllerutil .CreateOrPatch (ctx , h .GetClient (), ns , func () error {
210+ // Set labels
211+ if ns .Labels == nil {
212+ ns .Labels = make (map [string ]string )
226213 }
227- return fmt .Errorf ("failed to get console namespace %s: %w" , consolesNamespace , err )
214+ ns .Labels ["app" ] = "ironic"
215+ return nil
216+ })
217+
218+ if err != nil {
219+ return fmt .Errorf ("failed to reconcile console namespace %s: %w" , consolesNamespace , err )
220+ }
221+
222+ if op != controllerutil .OperationResultNone {
223+ h .GetLogger ().Info (fmt .Sprintf ("Namespace %s %s" , consolesNamespace , op ))
228224 }
229225
230- // Namespace already exists
231226 return nil
232227}
233228
234229// reconcileGraphicalConsoleRbac creates a Role and RoleBinding in the console namespace
235230// that grants the ServiceAccount from the service namespace permissions to create console pods.
236231// This enables cross-namespace RBAC where the ironic ServiceAccount in the 'openstack' namespace
237232// can create pods and secrets in the 'openstack-ironic-consoles' namespace.
233+ // Note: These resources cannot have owner references since cross-namespace ownership is not allowed.
238234func reconcileGraphicalConsoleRbac (
239235 ctx context.Context ,
240236 h * helper.Helper ,
@@ -244,81 +240,91 @@ func reconcileGraphicalConsoleRbac(
244240 rules []rbacv1.PolicyRule ,
245241) (ctrl.Result , error ) {
246242 serviceNamespace := instance .RbacNamespace ()
247-
248- // Create Role in the console namespace
249243 roleName := serviceAccountName + "-console-role"
250- role := common_role .NewRole (
251- & rbacv1.Role {
252- ObjectMeta : metav1.ObjectMeta {
253- Name : roleName ,
254- Namespace : consoleNamespace ,
255- },
256- Rules : rules ,
244+ roleBindingName := serviceAccountName + "-console-rolebinding"
245+
246+ labels := map [string ]string {
247+ "app" : "ironic" ,
248+ "ironic.openstack.org/service" : serviceAccountName ,
249+ }
250+
251+ // Create or update Role in the console namespace without owner references
252+ role := & rbacv1.Role {
253+ ObjectMeta : metav1.ObjectMeta {
254+ Name : roleName ,
255+ Namespace : consoleNamespace ,
257256 },
258- time .Duration (10 ),
259- )
260- roleResult , err := role .CreateOrPatch (ctx , h )
257+ }
258+
259+ op , err := controllerutil .CreateOrPatch (ctx , h .GetClient (), role , func () error {
260+ // Set labels
261+ role .Labels = labels
262+ // Set rules
263+ role .Rules = rules
264+ return nil
265+ })
266+
261267 if err != nil {
262268 instance .RbacConditionsSet (condition .FalseCondition (
263269 condition .RoleReadyCondition ,
264270 condition .ErrorReason ,
265271 condition .SeverityWarning ,
266272 condition .RoleReadyErrorMessage ,
267273 err .Error ()))
268- return roleResult , err
269- } else if (roleResult != ctrl.Result {}) {
270- instance .RbacConditionsSet (condition .FalseCondition (
271- condition .RoleReadyCondition ,
272- condition .RequestedReason ,
273- condition .SeverityInfo ,
274- condition .RoleCreatingMessage ))
275- return roleResult , nil
274+ return ctrl.Result {}, err
276275 }
276+
277+ if op != controllerutil .OperationResultNone {
278+ h .GetLogger ().Info (fmt .Sprintf ("Role %s %s in namespace %s" , roleName , op , consoleNamespace ))
279+ }
280+
277281 instance .RbacConditionsSet (condition .TrueCondition (
278282 condition .RoleReadyCondition ,
279283 condition .RoleReadyMessage ))
280284
281- // Create RoleBinding in the console namespace that references the ServiceAccount
282- // from the service namespace
283- roleBindingName := serviceAccountName + "-console-rolebinding"
284- rolebinding := common_rolebinding .NewRoleBinding (
285- & rbacv1.RoleBinding {
286- ObjectMeta : metav1.ObjectMeta {
287- Name : roleBindingName ,
288- Namespace : consoleNamespace ,
289- },
290- RoleRef : rbacv1.RoleRef {
291- APIGroup : "rbac.authorization.k8s.io" ,
292- Kind : "Role" ,
293- Name : roleName ,
294- },
295- Subjects : []rbacv1.Subject {
296- {
297- Kind : "ServiceAccount" ,
298- Name : serviceAccountName ,
299- Namespace : serviceNamespace , // Cross-namespace reference
300- },
301- },
285+ // Create or update RoleBinding in the console namespace that references the ServiceAccount
286+ // from the service namespace (cross-namespace reference)
287+ roleBinding := & rbacv1.RoleBinding {
288+ ObjectMeta : metav1.ObjectMeta {
289+ Name : roleBindingName ,
290+ Namespace : consoleNamespace ,
302291 },
303- time .Duration (10 ),
304- )
305- roleBindingResult , err := rolebinding .CreateOrPatch (ctx , h )
292+ }
293+
294+ op , err = controllerutil .CreateOrPatch (ctx , h .GetClient (), roleBinding , func () error {
295+ // Set labels
296+ roleBinding .Labels = labels
297+ // Set RoleRef (immutable, but safe to set on create)
298+ roleBinding .RoleRef = rbacv1.RoleRef {
299+ APIGroup : "rbac.authorization.k8s.io" ,
300+ Kind : "Role" ,
301+ Name : roleName ,
302+ }
303+ // Set Subjects
304+ roleBinding .Subjects = []rbacv1.Subject {
305+ {
306+ Kind : "ServiceAccount" ,
307+ Name : serviceAccountName ,
308+ Namespace : serviceNamespace ,
309+ },
310+ }
311+ return nil
312+ })
313+
306314 if err != nil {
307315 instance .RbacConditionsSet (condition .FalseCondition (
308316 condition .RoleBindingReadyCondition ,
309317 condition .ErrorReason ,
310318 condition .SeverityWarning ,
311319 condition .RoleBindingReadyErrorMessage ,
312320 err .Error ()))
313- return roleBindingResult , err
314- } else if (roleBindingResult != ctrl.Result {}) {
315- instance .RbacConditionsSet (condition .FalseCondition (
316- condition .RoleBindingReadyCondition ,
317- condition .RequestedReason ,
318- condition .SeverityInfo ,
319- condition .RoleBindingCreatingMessage ))
320- return roleBindingResult , nil
321+ return ctrl.Result {}, err
322+ }
323+
324+ if op != controllerutil .OperationResultNone {
325+ h .GetLogger ().Info (fmt .Sprintf ("RoleBinding %s %s in namespace %s" , roleBindingName , op , consoleNamespace ))
321326 }
327+
322328 instance .RbacConditionsSet (condition .TrueCondition (
323329 condition .RoleBindingReadyCondition ,
324330 condition .RoleBindingReadyMessage ))
0 commit comments