Skip to content

Commit

Permalink
draft: add namespace scoped operator mode
Browse files Browse the repository at this point in the history
This adds automation and docs for restricting the
operator scope from cluster wide to namespace restricted.

Signed-off-by: NymanRobin <[email protected]>
  • Loading branch information
NymanRobin committed Aug 9, 2024
1 parent efae71e commit b3a3c6e
Show file tree
Hide file tree
Showing 14 changed files with 228 additions and 36 deletions.
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,14 @@ manifests-generate: $(CONTROLLER_GEN)
manifests-kustomize: $(KUSTOMIZE)
$< build config/default > config/render/capm3.yaml

.PHONY: update-rbac-markers
update-rbac-markers:
python update_kubebuilder_rbac.py controllers/ single-ns-bmh

.PHONY: manifests-kustomize-namespaced
manifests-kustomize-namespaced: update-rbac-markers manifests-generate $(KUSTOMIZE)
$(KUSTOMIZE) build config/overlays/namespaced > config/render/capm3.yaml

.PHONY: set-manifest-image-bmo
set-manifest-image-bmo: $(KUSTOMIZE) manifests
$(info Updating container image for BMO to use ${MANIFEST_IMG}:${MANIFEST_TAG})
Expand Down
3 changes: 2 additions & 1 deletion config/base/rbac/role.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
kind: Role
metadata:
name: manager-role
namespace: single-ns-bmh
rules:
- apiGroups:
- ""
Expand Down
26 changes: 26 additions & 0 deletions config/overlays/namespaced/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

bases:
- ../../default


patches:
- patch: |
# Add a namespace to watch
- op: replace
path: /kind
value: RoleBinding
- op: replace
path: /roleRef/kind
value: Role
- op: add
path: /metadata/namespace
value: single-ns-bmh
target:
group: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: manager-rolebinding

patchesStrategicMerge:
- namespaced-manager-patch.yaml
13 changes: 13 additions & 0 deletions config/overlays/namespaced/namespaced-manager-patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: manager
env:
- name: WATCH_NAMESPACE
value: "single-ns-bmh"
13 changes: 13 additions & 0 deletions config/overlays/namespaced/rbac-patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: manager-rolebinding
namespace: $(WATCH_NAMESPACE) # Use the variable in the namespace
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: manager-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: $(WATCH_NAMESPACE) # Use the variable in the namespace
16 changes: 10 additions & 6 deletions config/render/capm3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2167,9 +2167,10 @@ rules:
- delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
kind: Role
metadata:
name: baremetal-operator-manager-role
namespace: baremetal-operator-system
rules:
- apiGroups:
- ""
Expand Down Expand Up @@ -2412,17 +2413,18 @@ subjects:
namespace: baremetal-operator-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
kind: RoleBinding
metadata:
name: baremetal-operator-manager-rolebinding
namespace: single-ns-bmh
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: baremetal-operator-manager-role
kind: Role
name: manager-role
subjects:
- kind: ServiceAccount
name: baremetal-operator-controller-manager
namespace: baremetal-operator-system
name: controller-manager
namespace: system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
Expand Down Expand Up @@ -2525,6 +2527,8 @@ spec:
command:
- /baremetal-operator
env:
- name: WATCH_NAMESPACE
value: single-ns-bmh
- name: POD_NAME
valueFrom:
fieldRef:
Expand Down
28 changes: 14 additions & 14 deletions controllers/metal3.io/baremetalhost_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,24 @@ func (info *reconcileInfo) publishEvent(reason, message string) {
info.events = append(info.events, info.host.NewEvent(reason, message))
}

// +kubebuilder:rbac:groups=metal3.io,resources=baremetalhosts,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=metal3.io,resources=baremetalhosts/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=metal3.io,resources=baremetalhosts/finalizers,verbs=update
// +kubebuilder:rbac:groups=metal3.io,resources=preprovisioningimages,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=metal3.io,resources=hardwaredata,verbs=get;list;watch;create;delete;patch;update
// +kubebuilder:rbac:groups=metal3.io,resources=hardware/finalizers,verbs=update
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;update;delete
// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=baremetalhosts,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=baremetalhosts/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=baremetalhosts/finalizers,verbs=update
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=preprovisioningimages,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=hardwaredata,verbs=get;list;watch;create;delete;patch;update
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=hardware/finalizers,verbs=update
// +kubebuilder:rbac:groups="",namespace="single-ns-bmh",resources=secrets,verbs=get;list;watch;update;delete
// +kubebuilder:rbac:groups="",namespace="single-ns-bmh",resources=events,verbs=get;list;watch;create;update;patch

// Allow for managing hostfirmwaresettings, firmwareschema, bmceventsubscriptions and hostfirmwarecomponents
//+kubebuilder:rbac:groups=metal3.io,resources=hostfirmwaresettings,verbs=get;list;watch;create;update;patch
//+kubebuilder:rbac:groups=metal3.io,resources=firmwareschemas,verbs=get;list;watch;create;update;patch
//+kubebuilder:rbac:groups=metal3.io,resources=bmceventsubscriptions,verbs=get;list;watch;create;update;patch
//+kubebuilder:rbac:groups=metal3.io,resources=hostfirmwarecomponents,verbs=get;list;watch;create;update;patch
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=hostfirmwaresettings,verbs=get;list;watch;create;update;patch
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=firmwareschemas,verbs=get;list;watch;create;update;patch
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=bmceventsubscriptions,verbs=get;list;watch;create;update;patch
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=hostfirmwarecomponents,verbs=get;list;watch;create;update;patch

// Allow for updating dataimage
// +kubebuilder:rbac:groups=metal3.io,resources=dataimages,verbs=get;list;watch;create;update;patch
// +kubebuilder:rbac:groups=metal3.io,resources=dataimages/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=dataimages,verbs=get;list;watch;create;update;patch
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=dataimages/status,verbs=get;update;patch

// Reconcile handles changes to BareMetalHost resources.
func (r *BareMetalHostReconciler) Reconcile(ctx context.Context, request ctrl.Request) (result ctrl.Result, err error) {
Expand Down
4 changes: 2 additions & 2 deletions controllers/metal3.io/bmceventsubscription_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ type BMCEventSubscriptionReconciler struct {
APIReader client.Reader
}

//+kubebuilder:rbac:groups=metal3.io,resources=bmceventsubscriptions,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,resources=bmceventsubscriptions/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=bmceventsubscriptions,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=bmceventsubscriptions/status,verbs=get;update;patch

func (r *BMCEventSubscriptionReconciler) Reconcile(ctx context.Context, request ctrl.Request) (result ctrl.Result, err error) {
reqLogger := r.Log.WithValues("bmceventsubscription", request.NamespacedName)
Expand Down
6 changes: 3 additions & 3 deletions controllers/metal3.io/dataimage_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ func (info *rdiInfo) publishEvent(reason, message string) {
info.events = append(info.events, dataImageEvent)
}

//+kubebuilder:rbac:groups=metal3.io,resources=dataimages,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,resources=dataimages/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=metal3.io,resources=dataimages/finalizers,verbs=update
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=dataimages,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=dataimages/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=dataimages/finalizers,verbs=update

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
Expand Down
6 changes: 3 additions & 3 deletions controllers/metal3.io/hostfirmwarecomponents_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ func (info *rhfcInfo) publishEvent(reason, message string) {
info.events = append(info.events, hfcEvent)
}

//+kubebuilder:rbac:groups=metal3.io,resources=hostfirmwarecomponents,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,resources=hostfirmwarecomponents/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=metal3.io,resources=hostfirmwarecomponents/finalizers,verbs=update
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=hostfirmwarecomponents,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=hostfirmwarecomponents/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=hostfirmwarecomponents/finalizers,verbs=update

// Reconcile handles changes to HostFirmwareComponents resources.
func (r *HostFirmwareComponentsReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) {
Expand Down
8 changes: 4 additions & 4 deletions controllers/metal3.io/hostfirmwaresettings_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ func (info *rInfo) publishEvent(reason, message string) {
info.events = append(info.events, hfsEvent)
}

//+kubebuilder:rbac:groups=metal3.io,resources=hostfirmwaresettings,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,resources=hostfirmwaresettings/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=metal3.io,resources=firmwareschemas,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,resources=firmwareschemas/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=hostfirmwaresettings,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=hostfirmwaresettings/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=firmwareschemas,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=firmwareschemas/status,verbs=get;update;patch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
Expand Down
6 changes: 3 additions & 3 deletions controllers/metal3.io/preprovisioningimage_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ const (
reasonImageBuildInvalid imageConditionReason = "ImageBuildInvalid"
)

// +kubebuilder:rbac:groups=metal3.io,resources=preprovisioningimages,verbs=get;list;watch;update;patch
// +kubebuilder:rbac:groups=metal3.io,resources=preprovisioningimages/status,verbs=get;update;patch
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;update
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=preprovisioningimages,verbs=get;list;watch;update;patch
// +kubebuilder:rbac:groups=metal3.io,namespace="single-ns-bmh",resources=preprovisioningimages/status,verbs=get;update;patch
// +kubebuilder:rbac:groups="",namespace="single-ns-bmh",resources=secrets,verbs=get;list;watch;update

func (r *PreprovisioningImageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("preprovisioningimage", req.NamespacedName)
Expand Down
92 changes: 92 additions & 0 deletions docs/single-ns-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# How to restrict BMO scope to a single namespace!

The guide is based on the instructions in this documentation
https://sdk.operatorframework.io/docs/building-operators/golang/operator-scope/

## To generate manifests for namespace scoped BMO

To generate namespace-scoped manifests, run the `manifests-kustomize-namespaced`
make target. This will create manifests configured for the namespace
`single-ns-bmh`. For further details on the steps involved, or if you
prefer to manually achieve similar results, please refer to the continuation
of the documentation.

## Watching resources in specific Namespaces

When setting up the manager, you can use the environment variable
`WATCH_NAMESPACE` to restrict the operator to a specific namespace. If
`WATCH_NAMESPACE` is unset or set to an empty string, the operator will
monitor all namespaces. To limit it to a specific namespace, set
`WATCH_NAMESPACE` to that namespace.

For example, to configure the operator to watch the same namespace where
it is deployed, update the `config/base/manager.yaml` file. Add the
following configuration under `spec.template.spec.containers.env`:

```yaml
- name: WATCH_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
```
## Restricting Roles and permissions
When BMO is restricted to a single namespace, the RBAC permissions need
to be updated accordingly. Instead of using `ClusterRole`, you will use
`Role`.

The `Role` is defined in the file `config/base/rbac/role_ns.yaml`. This
file is auto-generated based on Kubebuilder RBAC markers, specifically those
in `<some>_controller.go`. The default namespace marking is set to `""`,
which results in a `ClusterRole`. To restrict it to a specific namespace,
update this value accordingly.

You can automatically update the Kubebuilder RBAC markers by running:

```bash
python update_kubebuilder_rbac.py controllers/metal3.io/ your-namespace
```

The first argument specifies the directory to search, and the second is
the new namespace. To revert the change, simply set the namespace to `""`.

After updating the markers, generate the new manifests by running:

```bash
make manifests
```

Ensure that `config/base/rbac/role_ns.yaml` has been updated to a `Role`.

Due to limitations in Kubebuilder generation, the `RoleBinding` will not
be updated automatically. However, a Kustomization overlay is provided to
replace `ClusterRoleBinding` with `RoleBinding`. This overlay can be found
in `config/overlays/namespaced`.

Alternatively, you can manually update `config/base/rbac/role_binding.yaml`
to achieve the desired outcome. Below is an example of how to modify the
`role_binding.yaml` file:

```yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: manager-rolebinding
namespace: your-namespace
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: manager-role
namespace: your-namespace
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system
```
Replace `your-namespace` and other fields as necessary to match your
specific configuration.

After this you can run `make manifests-kustomize` to get correct RoleBinding generated

35 changes: 35 additions & 0 deletions update_kubebuilder_rbac.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import os
import re
import sys

def update_rbac_markers(directory, namespace):
rbac_pattern = re.compile(r'(\+kubebuilder:rbac:.*?namespace=")(.*?)(".*)')

# If the namespace is an empty string, it should look like `namespace=""`
new_namespace = namespace if namespace else ""

for root, _, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
if not file.endswith(".go"):
continue

with open(file_path, 'r') as f:
content = f.read()

new_content, count = rbac_pattern.subn(rf'\1{new_namespace}\3', content)

if count > 0:
with open(file_path, 'w') as f:
f.write(new_content)
print(f"Updated {count} RBAC markers in {file_path}")

if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python update_kubebuilder_rbac.py <directory> <namespace>")
sys.exit(1)

directory = sys.argv[1]
namespace = sys.argv[2]

update_rbac_markers(directory, namespace)

0 comments on commit b3a3c6e

Please sign in to comment.