Repository: kubernetes-sigs/security-profiles-operator
Pull request #349 (2021-03-05 06:51:21)

Use Downward API to get operator namespace
#### What type of PR is this?

/kind cleanup

#### What this PR does / why we need it:

This uses the downward API to fetch the operator's namespace.

#### Which issue(s) this PR fixes:

None

#### Does this PR have test?

N/A.

#### Special notes for your reviewer:

This was based on #228

Credit to @naveensrinivasan for starting this work. Thanks!

#### Does this PR introduce a user-facing change?

```release-note
NONE
```

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/349#" title="Author self-approved">JAORMX</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **pjbgf** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @pjbgf` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fsecurity-profiles-operator).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/security-profiles-operator/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["pjbgf"]} -->
k8s-ci-robot: @JAORMX: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-verify | 3951a19bca0e4024f7eacddd3c5318992d2b30a7 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/349/pull-security-profiles-operator-verify/1367729432168501248) | `/test pull-security-profiles-operator-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=349). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AJAORMX). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/security-profiles-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
JAORMX: Supersedes #228 
k8s-ci-robot: @JAORMX: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-verify | 4b476752811afaf1e44fe484707cba798aa3570a | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/349/pull-security-profiles-operator-verify/1367730314817835008) | `/test pull-security-profiles-operator-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=349). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AJAORMX). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/security-profiles-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @JAORMX: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-verify | 4b476752811afaf1e44fe484707cba798aa3570a | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/349/pull-security-profiles-operator-verify/1367730314817835008) | `/test pull-security-profiles-operator-verify`
pull-security-profiles-operator-test-e2e | 4b476752811afaf1e44fe484707cba798aa3570a | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/349/pull-security-profiles-operator-test-e2e/1367730314914304000) | `/test pull-security-profiles-operator-test-e2e`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=349). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AJAORMX). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/security-profiles-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
codecov-io: # [Codecov](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/349?src=pr&el=h1) Report
> Merging [#349](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/349?src=pr&el=desc) (fc1ee1b) into [master](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/commit/b9403a11d988b13e88b28a5a913073c264560a3c?el=desc) (b9403a1) will **increase** coverage by `0.06%`.
> The diff coverage is `33.33%`.

[![Impacted file tree graph](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/349/graphs/tree.svg?width=650&height=150&src=pr&token=37VIWSZ1ZT)](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/349?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #349      +/-   ##
==========================================
+ Coverage   29.66%   29.72%   +0.06%     
==========================================
  Files           9       10       +1     
  Lines         691      703      +12     
==========================================
+ Hits          205      209       +4     
- Misses        467      475       +8     
  Partials       19       19              
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `29.72% <33.33%> (+0.06%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/349?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/pkg/config/config.go](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/349/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGtnL2NvbmZpZy9jb25maWcuZ28=) | `33.33% <33.33%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/349?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/349?src=pr&el=footer). Last update [b9403a1...fc1ee1b](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/349?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/349#" title="Author self-approved">JAORMX</a>*, *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/349#pullrequestreview-604901615" title="LGTM">saschagrunert</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fsecurity-profiles-operator).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes-sigs/security-profiles-operator/blob/master/OWNERS)~~ [saschagrunert]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/api/spod/v1alpha1/spod_types.go b/api/spod/v1alpha1/spod_types.go
index 0be494b43..ae83ce5a8 100644
--- a/api/spod/v1alpha1/spod_types.go
+++ b/api/spod/v1alpha1/spod_types.go
@@ -39,13 +39,15 @@ type SPODSpec struct {
 type SPODState string
 
 const (
-	// The policy is pending installation.
+	// The SPOD instance is pending installation.
 	SPODStatePending SPODState = "PENDING"
-	// The policy is being installed.
+	// The SPOD instance is being created.
 	SPODStateCreating SPODState = "CREATING"
-	// The policy was installed successfully.
+	// The SPOD instance is being updated.
+	SPODStateUpdating SPODState = "UPDATING"
+	// The SPOD instance was installed successfully.
 	SPODStateRunning SPODState = "RUNNING"
-	// The policy couldn't be installed.
+	// The SPOD instance couldn't be installed.
 	SPODStateError SPODState = "ERROR"
 )
 
@@ -95,6 +97,16 @@ func (s *SPODStatus) StateCreating() {
 	s.ConditionedStatus.SetConditions(rcommonv1.Creating())
 }
 
+func (s *SPODStatus) StateUpdating() {
+	s.State = SPODStateUpdating
+	s.ConditionedStatus.SetConditions(rcommonv1.Condition{
+		Type:               rcommonv1.TypeReady,
+		Status:             corev1.ConditionFalse,
+		LastTransitionTime: metav1.Now(),
+		Reason:             "Updating",
+	})
+}
+
 func (s *SPODStatus) StateRunning() {
 	s.State = SPODStateRunning
 	s.ConditionedStatus.SetConditions(rcommonv1.Available())
diff --git a/deploy/base/operator.yaml b/deploy/base/operator.yaml
index 06bf881b9..0afb2d696 100644
--- a/deploy/base/operator.yaml
+++ b/deploy/base/operator.yaml
@@ -29,6 +29,10 @@ spec:
           env:
             - name: RELATED_IMAGE_SELINUXD
               value: quay.io/jaosorior/selinuxd
+            - name: OPERATOR_NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
       tolerations:
         - key: "node-role.kubernetes.io/master"
           operator: "Exists"
diff --git a/deploy/base/role.yaml b/deploy/base/role.yaml
index a063fa509..4093f815a 100644
--- a/deploy/base/role.yaml
+++ b/deploy/base/role.yaml
@@ -25,7 +25,6 @@ rules:
   - create
   - get
   - list
-  - patch
   - update
   - watch
 - apiGroups:
@@ -62,6 +61,7 @@ rules:
   - create
   - get
   - list
+  - patch
   - update
   - watch
 - apiGroups:
diff --git a/deploy/namespace-operator.yaml b/deploy/namespace-operator.yaml
index 9b4b2abfd..d46d4bef2 100644
--- a/deploy/namespace-operator.yaml
+++ b/deploy/namespace-operator.yaml
@@ -461,7 +461,6 @@ rules:
   - create
   - get
   - list
-  - patch
   - update
   - watch
 - apiGroups:
@@ -499,6 +498,7 @@ rules:
   - create
   - get
   - list
+  - patch
   - update
   - watch
 - apiGroups:
@@ -740,6 +740,10 @@ spec:
           value: NS_REPLACE
         - name: RELATED_IMAGE_SELINUXD
           value: quay.io/jaosorior/selinuxd
+        - name: OPERATOR_NAMESPACE
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.namespace
         image: gcr.io/k8s-staging-sp-operator/security-profiles-operator:latest
         imagePullPolicy: Always
         name: security-profiles-operator
diff --git a/deploy/operator.yaml b/deploy/operator.yaml
index 6ba563f28..71a152758 100644
--- a/deploy/operator.yaml
+++ b/deploy/operator.yaml
@@ -461,7 +461,6 @@ rules:
   - create
   - get
   - list
-  - patch
   - update
   - watch
 - apiGroups:
@@ -499,6 +498,7 @@ rules:
   - create
   - get
   - list
+  - patch
   - update
   - watch
 - apiGroups:
@@ -738,6 +738,10 @@ spec:
         env:
         - name: RELATED_IMAGE_SELINUXD
           value: quay.io/jaosorior/selinuxd
+        - name: OPERATOR_NAMESPACE
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.namespace
         image: gcr.io/k8s-staging-sp-operator/security-profiles-operator:latest
         imagePullPolicy: Always
         name: security-profiles-operator
diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go
index e71f44b30..4aa1608bf 100644
--- a/internal/pkg/config/config.go
+++ b/internal/pkg/config/config.go
@@ -17,8 +17,11 @@ limitations under the License.
 package config
 
 import (
+	"errors"
 	"os"
 	"path/filepath"
+
+	"k8s.io/release/pkg/util"
 )
 
 const (
@@ -61,15 +64,24 @@ const (
 // therefore have a limited lifetime.
 var ProfileRecordingOutputPath = filepath.Join(os.TempDir(), "security-profiles-operator-recordings")
 
+var ErrPodNamespaceEnvNotFound = errors.New("the env variable OPERATOR_NAMESPACE hasn't been set")
+
 // GetOperatorNamespace gets the namespace that the operator is currently running on.
+// Failure to get the namespace results in a panic.
 func GetOperatorNamespace() string {
-	// TODO(jaosorior): Get a method to return the current operator
-	// namespace.
-	//
-	// operatorNs, err := k8sutil.GetOperatorNamespace()
-	// if err != nil {
-	// 	return "security-profiles-operator"
-	// }
-	// return operatorNs
-	return "security-profiles-operator"
+	ns, err := TryToGetOperatorNamespace()
+	if err != nil {
+		panic(err)
+	}
+	return ns
+}
+
+func TryToGetOperatorNamespace() (string, error) {
+	// This is OPERATOR_NAMESPACE should have been set by the downward API to identify
+	// the namespace which this controller is running from
+	operatorNS := util.EnvDefault("OPERATOR_NAMESPACE", "")
+	if operatorNS == "" {
+		return "", ErrPodNamespaceEnvNotFound
+	}
+	return operatorNS, nil
 }
diff --git a/internal/pkg/manager/spod/bindata/spod.go b/internal/pkg/manager/spod/bindata/spod.go
index a3eccef3b..1bd1d184c 100644
--- a/internal/pkg/manager/spod/bindata/spod.go
+++ b/internal/pkg/manager/spod/bindata/spod.go
@@ -272,12 +272,12 @@ var Manifest = &appsv1.DaemonSet{
 						Resources: v1.ResourceRequirements{
 							Requests: v1.ResourceList{
 								v1.ResourceMemory:           resource.MustParse("512Mi"),
-								v1.ResourceCPU:              resource.MustParse("1000m"),
+								v1.ResourceCPU:              resource.MustParse("100m"),
 								v1.ResourceEphemeralStorage: resource.MustParse("200Mi"),
 							},
 							Limits: v1.ResourceList{
 								v1.ResourceMemory:           resource.MustParse("1024Mi"),
-								v1.ResourceCPU:              resource.MustParse("2000m"),
+								v1.ResourceCPU:              resource.MustParse("1000m"),
 								v1.ResourceEphemeralStorage: resource.MustParse("400Mi"),
 							},
 						},
diff --git a/internal/pkg/manager/spod/spod_controller.go b/internal/pkg/manager/spod/spod_controller.go
index 9b67d0bd8..7b29a859b 100644
--- a/internal/pkg/manager/spod/spod_controller.go
+++ b/internal/pkg/manager/spod/spod_controller.go
@@ -25,6 +25,7 @@ import (
 	"github.com/pkg/errors"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
+	apiequality "k8s.io/apimachinery/pkg/api/equality"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
@@ -32,6 +33,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
+	sccmpv1alpha1 "sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1alpha1"
 	spodv1alpha1 "sigs.k8s.io/security-profiles-operator/api/spod/v1alpha1"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/manager/spod/bindata"
@@ -39,6 +41,7 @@ import (
 
 const (
 	reasonCannotCreateSPOD event.Reason = "CannotCreateSPOD"
+	reasonCannotUpdateSPOD event.Reason = "CannotUpdateSPOD"
 )
 
 // blank assignment to verify that ReconcileSPOd implements `reconcile.Reconciler`.
@@ -63,9 +66,9 @@ type ReconcileSPOd struct {
 // +kubebuilder:rbac:groups=core,namespace="security-profiles-operator",resources=configmaps;events,verbs=get;list;watch;create;update;patch
 //
 // Operand:
-// +kubebuilder:rbac:groups=apps,namespace="security-profiles-operator",resources=daemonsets,verbs=get;list;watch;create;update
+// +kubebuilder:rbac:groups=apps,namespace="security-profiles-operator",resources=daemonsets,verbs=get;list;watch;create;update;patch
 // +kubebuilder:rbac:groups=apps,namespace="security-profiles-operator",resources=daemonsets/finalizers,verbs=delete;get;update;patch
-// +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=securityprofilesoperatordaemons,verbs=get;list;watch;create;update;patch
+// +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=securityprofilesoperatordaemons,verbs=get;list;watch;create;update
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=securityprofilesoperatordaemons/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=securityprofilesoperatordaemons/finalizers,verbs=delete;get;update;patch
 // Helpers:
@@ -112,14 +115,17 @@ func (r *ReconcileSPOd) Reconcile(_ context.Context, req reconcile.Request) (rec
 	image := foundDeployment.Spec.Template.Spec.Containers[0].Image
 	pullPolicy := foundDeployment.Spec.Template.Spec.Containers[0].ImagePullPolicy
 
+	configuredSPOd := r.getConfiguredSPOd(spod, image, pullPolicy)
+
 	spodKey := types.NamespacedName{
 		Name:      spod.GetName(),
 		Namespace: config.GetOperatorNamespace(),
 	}
+
 	foundSPOd := &appsv1.DaemonSet{}
 	if err := r.client.Get(ctx, spodKey, foundSPOd); err != nil {
 		if kerrors.IsNotFound(err) {
-			createErr := r.handleCreate(ctx, spod, image, pullPolicy)
+			createErr := r.handleCreate(ctx, spod, configuredSPOd)
 			if createErr != nil {
 				r.record.Event(spod, event.Warning(reasonCannotCreateSPOD, createErr))
 				return reconcile.Result{}, createErr
@@ -129,10 +135,23 @@ func (r *ReconcileSPOd) Reconcile(_ context.Context, req reconcile.Request) (rec
 		return reconcile.Result{}, errors.Wrap(err, "getting spod DaemonSet")
 	}
 
-	// NOTE(jaosorior): We gotta handle updates
+	if spodNeedsUpdate(configuredSPOd, foundSPOd) {
+		updatedSPod := foundSPOd.DeepCopy()
+		updatedSPod.Spec.Template = configuredSPOd.Spec.Template
+		updateErr := r.handleUpdate(ctx, updatedSPod)
+		if updateErr != nil {
+			r.record.Event(spod, event.Warning(reasonCannotUpdateSPOD, updateErr))
+			return reconcile.Result{}, updateErr
+		}
+		return r.handleUpdatingStatus(ctx, spod, logger)
+	}
 
 	if foundSPOd.Status.NumberReady == foundSPOd.Status.DesiredNumberScheduled {
-		return r.handleRunningStatus(ctx, spod, logger)
+		condready := spod.Status.GetCondition("Ready")
+		// Don't pollute the logs. Let's only update when needed.
+		if condready.Status != corev1.ConditionTrue {
+			return r.handleRunningStatus(ctx, spod, logger)
+		}
 	}
 	return reconcile.Result{}, nil
 }
@@ -167,6 +186,21 @@ func (r *ReconcileSPOd) handleCreatingStatus(
 	return reconcile.Result{}, nil
 }
 
+func (r *ReconcileSPOd) handleUpdatingStatus(
+	ctx context.Context,
+	spod *spodv1alpha1.SecurityProfilesOperatorDaemon,
+	l logr.Logger,
+) (res reconcile.Result, err error) {
+	l.Info("Adding 'Updating' status to the SPOD Instance")
+	sCopy := spod.DeepCopy()
+	sCopy.Status.StateUpdating()
+	updateErr := r.client.Status().Update(ctx, sCopy)
+	if updateErr != nil {
+		return reconcile.Result{}, errors.Wrap(updateErr, "updating spod status to 'updating'")
+	}
+	return reconcile.Result{}, nil
+}
+
 func (r *ReconcileSPOd) handleRunningStatus(
 	ctx context.Context,
 	spod *spodv1alpha1.SecurityProfilesOperatorDaemon,
@@ -185,11 +219,9 @@ func (r *ReconcileSPOd) handleRunningStatus(
 func (r *ReconcileSPOd) handleCreate(
 	ctx context.Context,
 	cfg *spodv1alpha1.SecurityProfilesOperatorDaemon,
-	image string,
-	pullPolicy corev1.PullPolicy,
+	newSPOd *appsv1.DaemonSet,
 ) error {
 	r.log.Info("Creating operator resources")
-	newSPOd := r.getConfiguredSPOd(cfg, image, pullPolicy)
 
 	if err := controllerutil.SetControllerReference(cfg, newSPOd, r.scheme); err != nil {
 		return errors.Wrap(err, "setting spod controller reference")
@@ -212,7 +244,7 @@ func (r *ReconcileSPOd) handleCreate(
 
 		if err := r.client.Create(ctx, profile); err != nil {
 			if kerrors.IsAlreadyExists(err) {
-				return nil
+				continue
 			}
 			return errors.Wrapf(
 				err, "creating operator default profile %s", profile.Name,
@@ -223,13 +255,72 @@ func (r *ReconcileSPOd) handleCreate(
 	return nil
 }
 
+func (r *ReconcileSPOd) handleUpdate(
+	ctx context.Context,
+	spodInstance *appsv1.DaemonSet,
+) error {
+	r.log.Info("Updating operator daemonset")
+	if err := r.client.Patch(ctx, spodInstance, client.Merge); err != nil {
+		if kerrors.IsAlreadyExists(err) {
+			return nil
+		}
+		return errors.Wrap(err, "creating operator DaemonSet")
+	}
+
+	r.log.Info("Updating operator default profiles")
+	for _, profile := range bindata.DefaultProfiles() {
+		// Adapt the namespace if we watch only a single one
+		if r.watchNamespace != "" {
+			profile.Namespace = r.watchNamespace
+		}
+
+		pKey := types.NamespacedName{
+			Name:      profile.GetName(),
+			Namespace: profile.GetNamespace(),
+		}
+		foundProfile := &sccmpv1alpha1.SeccompProfile{}
+		var err error
+		if err = r.client.Get(ctx, pKey, foundProfile); err == nil {
+			if updateErr := r.client.Update(ctx, profile); updateErr != nil {
+				return errors.Wrapf(
+					updateErr, "updating operator default profile %s", profile.Name,
+				)
+			}
+			continue
+		}
+
+		if kerrors.IsNotFound(err) {
+			// Handle new default profile
+			if createErr := r.client.Create(ctx, profile); err != nil {
+				if kerrors.IsAlreadyExists(createErr) {
+					return nil
+				}
+				return errors.Wrapf(
+					createErr, "creating operator default profile %s", profile.Name,
+				)
+			}
+			continue
+		}
+
+		return errors.Wrapf(
+			err, "getting operator default profile %s", profile.Name,
+		)
+	}
+
+	return nil
+}
+
+// getConfiguredSPOd gets a fully configured SPOd instance from a desired
+// configuration and the reference base SPOd.
 func (r *ReconcileSPOd) getConfiguredSPOd(
 	cfg *spodv1alpha1.SecurityProfilesOperatorDaemon,
 	image string,
 	pullPolicy corev1.PullPolicy,
 ) *appsv1.DaemonSet {
 	newSPOd := r.baseSPOd.DeepCopy()
+
 	newSPOd.SetName(cfg.GetName())
+	newSPOd.SetNamespace(config.GetOperatorNamespace())
 	templateSpec := &newSPOd.Spec.Template.Spec
 
 	// Set Images
@@ -253,14 +344,14 @@ func (r *ReconcileSPOd) getConfiguredSPOd(
 
 	// Log enricher parameters
 	if cfg.Spec.EnableLogEnricher {
-		r.baseSPOd.Spec.Template.Spec.Containers[2].Image = image
-		newSPOd.Spec.Template.Spec.Containers = append(
-			newSPOd.Spec.Template.Spec.Containers,
+		templateSpec.Containers = append(
+			templateSpec.Containers,
 			r.baseSPOd.Spec.Template.Spec.Containers[2])
+		templateSpec.Containers[2].Image = image
 
 		// HostPID is only required for the log-enricher
 		// and is used to access cgroup files to map Process IDs to Pod IDs
-		newSPOd.Spec.Template.Spec.HostPID = true
+		templateSpec.HostPID = true
 	}
 
 	// Set image pull policy
@@ -276,3 +367,11 @@ func (r *ReconcileSPOd) getConfiguredSPOd(
 
 	return newSPOd
 }
+
+func spodNeedsUpdate(configured, found *appsv1.DaemonSet) bool {
+	// If the length of the containers don't match, we clearly need an update.
+	// This way we avoid the expensive DeepDerivative check.
+	return (len(configured.Spec.Template.Spec.InitContainers) != len(found.Spec.Template.Spec.InitContainers) ||
+		len(configured.Spec.Template.Spec.Containers) != len(found.Spec.Template.Spec.Containers) ||
+		!apiequality.Semantic.DeepDerivative(configured.Spec.Template, found.Spec.Template))
+}


Test patch:
diff --git a/internal/pkg/config/config_test.go b/internal/pkg/config/config_test.go
new file mode 100644
index 000000000..74f2c0cdf
--- /dev/null
+++ b/internal/pkg/config/config_test.go
@@ -0,0 +1,61 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package config
+
+import (
+	"os"
+	"testing"
+)
+
+func TestGetOperatorNamespace(t *testing.T) {
+	t.Parallel()
+	tests := []struct {
+		name    string
+		want    string
+		wantErr bool
+	}{
+		{
+			name:    "Valid one",
+			want:    "default",
+			wantErr: false,
+		},
+		{
+			name:    "invalid one",
+			want:    "",
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+			if tt.wantErr {
+				os.Unsetenv("OPERATOR_NAMESPACE")
+			} else {
+				os.Setenv("OPERATOR_NAMESPACE", tt.want)
+			}
+			got, err := TryToGetOperatorNamespace()
+			if (err != nil) != tt.wantErr {
+				t.Errorf("GetOperatorNamespace() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != tt.want {
+				t.Errorf("GetOperatorNamespace() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/test/e2e_test.go b/test/e2e_test.go
index e7c00f3f3..c769883a5 100644
--- a/test/e2e_test.go
+++ b/test/e2e_test.go
@@ -85,6 +85,10 @@ func (e *e2e) TestSecurityProfilesOperator() {
 			"SELinux: base case (install policy, run pod and delete)",
 			e.testCaseSelinuxBaseUsage,
 		},
+		{
+			"SPOD: Update SELinux flag",
+			e.testCaseSPODUpdateSelinux,
+		},
 	}
 	for _, testCase := range testCases {
 		tc := testCase
diff --git a/test/suite_test.go b/test/suite_test.go
index ae3303a3a..3e127e0bc 100644
--- a/test/suite_test.go
+++ b/test/suite_test.go
@@ -423,8 +423,8 @@ func (e *e2e) kubectlFailure(args ...string) string {
 	return e.runFailure(e.kubectlPath, args...)
 }
 
-func (e *e2e) kubectlOperatorNS(args ...string) {
-	e.kubectl(
+func (e *e2e) kubectlOperatorNS(args ...string) string {
+	return e.kubectl(
 		append([]string{"-n", config.OperatorName}, args...)...,
 	)
 }
diff --git a/test/tc_spod_update_selinux_test.go b/test/tc_spod_update_selinux_test.go
new file mode 100644
index 000000000..079c48138
--- /dev/null
+++ b/test/tc_spod_update_selinux_test.go
@@ -0,0 +1,51 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e_test
+
+import "time"
+
+func (e *e2e) testCaseSPODUpdateSelinux(nodes []string) {
+	e.selinuxtOnlyTestCase()
+
+	e.logf("assert selinux is enabled in the spod object")
+	selinuxEnabledInSPODObj := e.kubectl("get", "spod", "spod", "-o", "jsonpath={.spec.enableSelinux}")
+	e.Equal(selinuxEnabledInSPODObj, "true")
+
+	e.logf("assert selinux is enabled in the spod DS")
+	selinuxEnabledInSPODDS := e.kubectlOperatorNS("get", "ds", "spod", "-o", "yaml")
+	e.Contains(selinuxEnabledInSPODDS, "--with-selinux=true")
+
+	e.logf("Disable selinux from SPOD")
+	e.kubectl("patch", "spod", "spod", "-p", `{"spec":{"enableSelinux": false}}`, "--type=merge")
+
+	time.Sleep(defaultWaitTime)
+	e.waitInOperatorNSFor("condition=ready", "spod", "spod")
+
+	e.logf("assert selinux is disabled in the spod DS")
+	selinuxDisabledInSPODDS := e.kubectlOperatorNS("get", "ds", "spod", "-o", "yaml")
+	e.NotContains(selinuxDisabledInSPODDS, "--with-selinux=true")
+
+	e.logf("Re-enable selinux in SPOD")
+	e.kubectl("patch", "spod", "spod", "-p", `{"spec":{"enableSelinux": true}}`, "--type=merge")
+
+	time.Sleep(defaultWaitTime)
+	e.waitInOperatorNSFor("condition=ready", "spod", "spod")
+
+	e.logf("assert selinux is enabled in the spod DS")
+	selinuxEnabledInSPODDS = e.kubectlOperatorNS("get", "ds", "spod", "-o", "yaml")
+	e.Contains(selinuxEnabledInSPODDS, "--with-selinux=true")
+}