Skip to content

Commit 76ab73d

Browse files
author
Xin Li
authored
Add validated namespace members in status (#47)
1 parent 215d8c5 commit 76ab73d

File tree

8 files changed

+136
-74
lines changed

8 files changed

+136
-74
lines changed

api/v1/namespacescope_types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type NamespaceScopeSpec struct {
5050
type NamespaceScopeStatus struct {
5151
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
5252
// Important: Run "make" to regenerate code after modifying this file
53+
ValidatedMembers []string `json:"validatedMembers,omitempty"`
5354
}
5455

5556
// +kubebuilder:object:root=true

api/v1/zz_generated.deepcopy.go

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bundle-restricted/manifests/operator.ibm.com_namespacescopes.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,20 @@ spec:
4848
type: string
4949
description: Restart pods with the following labels when the namspace list changes
5050
type: object
51+
serviceAccountMembers:
52+
description: ServiceAccountMembers are extra service accounts will be bond the roles from other namespaces
53+
items:
54+
type: string
55+
type: array
5156
type: object
5257
status:
5358
description: NamespaceScopeStatus defines the observed state of NamespaceScope
59+
properties:
60+
validatedMembers:
61+
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file'
62+
items:
63+
type: string
64+
type: array
5465
type: object
5566
type: object
5667
version: v1

bundle/manifests/operator.ibm.com_namespacescopes.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,20 @@ spec:
4848
type: string
4949
description: Restart pods with the following labels when the namspace list changes
5050
type: object
51+
serviceAccountMembers:
52+
description: ServiceAccountMembers are extra service accounts will be bond the roles from other namespaces
53+
items:
54+
type: string
55+
type: array
5156
type: object
5257
status:
5358
description: NamespaceScopeStatus defines the observed state of NamespaceScope
59+
properties:
60+
validatedMembers:
61+
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file'
62+
items:
63+
type: string
64+
type: array
5465
type: object
5566
type: object
5667
version: v1

config/crd/bases/operator.ibm.com_namespacescopes.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ spec:
6969
type: object
7070
status:
7171
description: NamespaceScopeStatus defines the observed state of NamespaceScope
72+
properties:
73+
validatedMembers:
74+
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
75+
of cluster Important: Run "make" to regenerate code after modifying
76+
this file'
77+
items:
78+
type: string
79+
type: array
7280
type: object
7381
type: object
7482
version: v1

controllers/common/util.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ func CheckListDifference(slice1 []string, slice2 []string) bool {
6969
return false
7070
}
7171

72+
//StringSliceContentEqual checks if the contant from two string slice are the same
73+
func StringSliceContentEqual(slice1, slice2 []string) bool {
74+
set1 := MakeSet(slice1)
75+
set2 := MakeSet(slice2)
76+
return set1.Equal(set2)
77+
}
78+
7279
func GetOwnerReferenceUIDs(ownerRefs []metav1.OwnerReference) []types.UID {
7380
var ownerRefUIDs []types.UID
7481
for _, ref := range ownerRefs {

controllers/namespacescope_controller.go

Lines changed: 85 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ func (r *NamespaceScopeReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
9393
instance = r.setDefaults(instance)
9494

9595
klog.Infof("Reconciling NamespaceScope: %s", req.NamespacedName)
96-
if err := r.InitConfigMap(instance); err != nil {
96+
97+
if err := r.UpdateStatus(instance); err != nil {
9798
return ctrl.Result{}, err
9899
}
99100

@@ -123,23 +124,41 @@ func (r *NamespaceScopeReconciler) addFinalizer(nss *operatorv1.NamespaceScope)
123124
return nil
124125
}
125126

126-
func (r *NamespaceScopeReconciler) InitConfigMap(instance *operatorv1.NamespaceScope) error {
127+
func (r *NamespaceScopeReconciler) UpdateStatus(instance *operatorv1.NamespaceScope) error {
128+
// Get validated namespaces
129+
validatedNamespaces, err := r.getValidatedNamespaces(instance)
130+
if err != nil {
131+
klog.Errorf("Failed to get validated namespaces: %v", err)
132+
return err
133+
}
134+
// Update instance status with the validated namespaces
135+
if !util.StringSliceContentEqual(instance.Status.ValidatedMembers, validatedNamespaces) {
136+
instance.Status.ValidatedMembers = validatedNamespaces
137+
if err := r.Status().Update(ctx, instance); err != nil {
138+
klog.Errorf("Failed to update instance %s/%s: %v", instance.Namespace, instance.Name, err)
139+
return err
140+
}
141+
}
142+
143+
return nil
144+
}
145+
146+
func (r *NamespaceScopeReconciler) UpdateConfigMap(instance *operatorv1.NamespaceScope) error {
127147
cm := &corev1.ConfigMap{}
128148
cmName := instance.Spec.ConfigmapName
129149
cmNamespace := instance.Namespace
150+
cmKey := types.NamespacedName{Name: cmName, Namespace: cmNamespace}
151+
validatedMembers, err := r.getAllValidatedNamespaceMembers(instance)
152+
if err != nil {
153+
klog.Errorf("Failed to get all validated namespace members: %v", err)
154+
return err
155+
}
130156

131-
if err := r.Get(ctx, types.NamespacedName{Name: cmName, Namespace: cmNamespace}, cm); err != nil {
132-
// If ConfigMap does not exist, create it
157+
if err := r.Get(ctx, cmKey, cm); err != nil {
133158
if errors.IsNotFound(err) {
134-
cm.Name = cmName
135-
cm.Namespace = cmNamespace
136159
cm.Labels = map[string]string{constant.NamespaceScopeLabel: "true"}
137160
cm.Data = make(map[string]string)
138-
nsMembers, err := r.getNamespaceList(instance)
139-
if err != nil {
140-
return err
141-
}
142-
cm.Data["namespaces"] = strings.Join(nsMembers, ",")
161+
cm.Data["namespaces"] = strings.Join(validatedMembers, ",")
143162
// Set NamespaceScope instance as the owner of the ConfigMap.
144163
if err := controllerutil.SetOwnerReference(instance, cm, r.Scheme); err != nil {
145164
klog.Errorf("Failed to set owner reference for ConfigMap %s/%s: %v", cmNamespace, cmName, err)
@@ -158,33 +177,13 @@ func (r *NamespaceScopeReconciler) InitConfigMap(instance *operatorv1.NamespaceS
158177
return err
159178
}
160179

161-
return nil
162-
}
163-
164-
func (r *NamespaceScopeReconciler) UpdateConfigMap(instance *operatorv1.NamespaceScope) error {
165-
cm := &corev1.ConfigMap{}
166-
cmKey := types.NamespacedName{Name: instance.Spec.ConfigmapName, Namespace: instance.Namespace}
167-
if err := r.Get(ctx, cmKey, cm); err != nil {
168-
if errors.IsNotFound(err) {
169-
klog.Infof("ConfigMap %s not found ", cmKey.String())
170-
return nil
171-
}
172-
return err
173-
}
174-
175-
// If NamespaceMembers changed, update ConfigMap
176-
nsMembers, err := r.getNamespaceList(instance)
177-
if err != nil {
178-
return err
179-
}
180-
181180
// Get owner uids
182181
ownerRefUIDs := util.GetOwnerReferenceUIDs(cm.GetOwnerReferences())
183182

184-
if util.CheckListDifference(nsMembers, strings.Split(cm.Data["namespaces"], ",")) || !util.UIDContains(ownerRefUIDs, instance.UID) {
185-
restartpod := util.CheckListDifference(nsMembers, strings.Split(cm.Data["namespaces"], ","))
183+
if util.CheckListDifference(validatedMembers, strings.Split(cm.Data["namespaces"], ",")) || !util.UIDContains(ownerRefUIDs, instance.UID) {
184+
restartpod := util.CheckListDifference(validatedMembers, strings.Split(cm.Data["namespaces"], ","))
186185
if restartpod {
187-
cm.Data["namespaces"] = strings.Join(nsMembers, ",")
186+
cm.Data["namespaces"] = strings.Join(validatedMembers, ",")
188187
}
189188

190189
if err := controllerutil.SetOwnerReference(instance, cm, r.Scheme); err != nil {
@@ -220,7 +219,7 @@ func (r *NamespaceScopeReconciler) PushRbacToNamespace(instance *operatorv1.Name
220219
return err
221220
}
222221

223-
for _, toNs := range instance.Spec.NamespaceMembers {
222+
for _, toNs := range instance.Status.ValidatedMembers {
224223
if toNs == operatorNs {
225224
continue
226225
}
@@ -246,7 +245,7 @@ func (r *NamespaceScopeReconciler) DeleteRbacFromUnmanagedNamespace(instance *op
246245
if cm.Data["namespaces"] != "" {
247246
nsInCm = strings.Split(cm.Data["namespaces"], ",")
248247
}
249-
nsInCr, err := r.getNamespaceList(instance)
248+
nsInCr, err := r.getAllValidatedNamespaceMembers(instance)
250249
if err != nil {
251250
return err
252251
}
@@ -286,7 +285,6 @@ func (r *NamespaceScopeReconciler) DeleteRbacFromUnmanagedNamespace(instance *op
286285

287286
// When delete NamespaceScope instance, cleanup all RBAC resources
288287
func (r *NamespaceScopeReconciler) DeleteAllRbac(instance *operatorv1.NamespaceScope) error {
289-
instance = r.setDefaults(instance)
290288
labels := map[string]string{
291289
"namespace-scope-configmap": instance.Namespace + "-" + instance.Spec.ConfigmapName,
292290
}
@@ -297,7 +295,7 @@ func (r *NamespaceScopeReconciler) DeleteAllRbac(instance *operatorv1.NamespaceS
297295
return err
298296
}
299297

300-
usingMembers, err := r.getNamespaceList(instance)
298+
usingMembers, err := r.getAllValidatedNamespaceMembers(instance)
301299
if err != nil {
302300
return err
303301
}
@@ -618,21 +616,13 @@ func (r *NamespaceScopeReconciler) setDefaults(instance *operatorv1.NamespaceSco
618616
constant.DefaultRestartLabelsKey: constant.DefaultRestartLabelsValue,
619617
}
620618
}
621-
if r.checkGetNSAuth() {
622-
if validatedNs, err := r.getValidatedNamespaces(instance); err != nil {
623-
klog.Errorf("Failed to validate namespace: %v", err)
624-
} else {
625-
instance.Spec.NamespaceMembers = validatedNs
626-
}
627-
}
628-
629619
return instance
630620
}
631621

632-
func (r *NamespaceScopeReconciler) getNamespaceList(instance *operatorv1.NamespaceScope) ([]string, error) {
622+
func (r *NamespaceScopeReconciler) getAllValidatedNamespaceMembers(instance *operatorv1.NamespaceScope) ([]string, error) {
633623
// List the instance using the same configmap
634624
crList := &operatorv1.NamespaceScopeList{}
635-
namespaceMembersList := util.MakeSet([]string{})
625+
namespaceMembers := []string{}
636626
if err := r.List(ctx, crList, &client.ListOptions{Namespace: instance.Namespace}); err != nil {
637627
klog.Errorf("Cannot list namespacescope with in namespace %s: %v", instance.Namespace, err)
638628
return nil, err
@@ -643,16 +633,13 @@ func (r *NamespaceScopeReconciler) getNamespaceList(instance *operatorv1.Namespa
643633
continue
644634
}
645635
if instance.Spec.ConfigmapName == cr.Spec.ConfigmapName {
646-
for _, ns := range cr.Spec.NamespaceMembers {
647-
namespaceMembersList.Add(ns)
648-
}
636+
namespaceMembers = append(namespaceMembers, cr.Status.ValidatedMembers...)
649637
}
650638
}
651-
return util.ToStringSlice(namespaceMembersList), nil
639+
return util.ToStringSlice(util.MakeSet(namespaceMembers)), nil
652640
}
653641

654642
func (r *NamespaceScopeReconciler) checkGetNSAuth() bool {
655-
// List the instance using the same configmap
656643
sar := &authorizationv1.SelfSubjectAccessReview{
657644
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
658645
ResourceAttributes: &authorizationv1.ResourceAttributes{
@@ -667,27 +654,59 @@ func (r *NamespaceScopeReconciler) checkGetNSAuth() bool {
667654
klog.Errorf("Failed to check if operator has permission to get namespace: %v", err)
668655
return false
669656
}
657+
klog.V(2).Infof("Get Namespace permission, Allowed: %t, Denied: %t, Reason: %s", sar.Status.Allowed, sar.Status.Denied, sar.Status.Reason)
658+
return sar.Status.Allowed
659+
}
660+
661+
// Check if operator has namespace admin permission
662+
func (r *NamespaceScopeReconciler) checkNamespaceAdminAuth(namespace string) bool {
663+
sar := &authorizationv1.SelfSubjectAccessReview{
664+
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
665+
ResourceAttributes: &authorizationv1.ResourceAttributes{
666+
Namespace: namespace,
667+
Verb: "*",
668+
Group: "*",
669+
Resource: "*",
670+
},
671+
},
672+
}
673+
674+
if err := r.Create(ctx, sar); err != nil {
675+
klog.Errorf("Failed to check operator namespace admin permission: %v", err)
676+
return false
677+
}
678+
679+
klog.V(2).Infof("Namespace admin permission in namesapce %s, Allowed: %t, Denied: %t, Reason: %s", namespace, sar.Status.Allowed, sar.Status.Denied, sar.Status.Reason)
670680
return sar.Status.Allowed
671681
}
672682

673683
func (r *NamespaceScopeReconciler) getValidatedNamespaces(instance *operatorv1.NamespaceScope) ([]string, error) {
674684
var validatedNs []string
675685
for _, nsMem := range instance.Spec.NamespaceMembers {
676-
ns := &corev1.Namespace{}
677-
key := types.NamespacedName{Name: nsMem}
678-
if err := r.Get(ctx, key, ns); err != nil {
679-
if errors.IsNotFound(err) {
680-
klog.Infof("Namespace %s does not exist and will be ignored", nsMem)
681-
continue
682-
} else {
683-
return nil, err
686+
// Check if operator has target namespace admin permission
687+
if r.checkNamespaceAdminAuth(nsMem) {
688+
// Check if operator has permission to get namespace resource
689+
if r.checkGetNSAuth() {
690+
ns := &corev1.Namespace{}
691+
key := types.NamespacedName{Name: nsMem}
692+
if err := r.Get(ctx, key, ns); err != nil {
693+
if errors.IsNotFound(err) {
694+
klog.Infof("Namespace %s does not exist and will be ignored", nsMem)
695+
continue
696+
} else {
697+
return nil, err
698+
}
699+
}
700+
if ns.Status.Phase == corev1.NamespaceTerminating {
701+
klog.Infof("Namespace %s is terminating. Ignore this namespace ", nsMem)
702+
continue
703+
}
684704
}
705+
validatedNs = append(validatedNs, nsMem)
706+
} else {
707+
klog.Infof("ibm-namespace-scope-operator has not admin permission in namespace %s", nsMem)
708+
r.Recorder.Eventf(instance, corev1.EventTypeWarning, "Forbidden", "ibm-namespace-scope-operator has not admin permission in namespace %s", nsMem)
685709
}
686-
if ns.Status.Phase == corev1.NamespaceTerminating {
687-
klog.Infof("Namespace %s is terminating. Ignore this namespace ", nsMem)
688-
continue
689-
}
690-
validatedNs = append(validatedNs, nsMem)
691710
}
692711
return validatedNs, nil
693712
}

scripts/authorize-namespace.sh

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,11 @@ fi
130130
#
131131
# Define a role for service accounts
132132
#
133-
cat <<EOF | oc apply -n $TARGETNS -f -
133+
cat <<EOF | oc apply -n $TONS -f -
134134
apiVersion: rbac.authorization.k8s.io/v1
135135
kind: Role
136136
metadata:
137-
name: nss-managed-role-from-$TONS
137+
name: nss-managed-role-from-$TARGETNS
138138
rules:
139139
- apiGroups:
140140
- "*"
@@ -147,17 +147,17 @@ EOF
147147
#
148148
# Bind the service account in the TO namespace to the Role in the target namespace
149149
#
150-
cat <<EOF | oc apply -n $TARGETNS -f -
150+
cat <<EOF | oc apply -n $TONS -f -
151151
kind: RoleBinding
152152
apiVersion: rbac.authorization.k8s.io/v1
153153
metadata:
154-
name: nss-managed-role-from-$TONS
154+
name: nss-managed-rolebinding-from-$TARGETNS
155155
subjects:
156156
- kind: ServiceAccount
157157
name: ibm-namespace-scope-operator
158-
namespace: $TONS
158+
namespace: $TARGETNS
159159
roleRef:
160160
kind: Role
161-
name: nss-managed-role-from-$TONS
161+
name: nss-managed-role-from-$TARGETNS
162162
apiGroup: rbac.authorization.k8s.io
163-
EOF
163+
EOF

0 commit comments

Comments
 (0)