Repository: kubernetes-sigs/security-profiles-operator
Pull request #884 (2022-04-05 10:08:11)

bundle: Add an icon
<!--  Thanks for sending a pull request!  Here are some tips for you:

- If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
- Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. For reference on required PR/issue labels, read here:
https://git.k8s.io/community/contributors/devel/sig-release/release.md#issuepr-kind-label
- If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
- If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

#### What type of PR is this?
/kind cleanup

<!--
Add one of the following kinds:
/kind bug
/kind documentation
/kind feature
/kind design

Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:
Per the operatorhub.io docs:
    https://k8s-operatorhub.github.io/community-operators/packaging-operator/
"If you don't have own icon, you should use default one". This patch
adds the default icon so that SPO passes operatorhub.io tests.

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

<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.

Fixes #

or

None
-->

#### Does this PR have test?
N/A covered by bundle tests

<!--
If tests aren't applicable just write N/A.
-->

#### Special notes for your reviewer:
N/A

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

<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->

```release-note
NONE
```


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/884#" title="Author self-approved">jhrozek</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/main/OWNERS)~~ [jhrozek]

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":[]} -->
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/884#" title="Author self-approved">jhrozek</a>*, *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/884#pullrequestreview-931621388" title="Approved">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/main/OWNERS)~~ [jhrozek,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":[]} -->
saschagrunert: @jhrozek do we have to release a new version after that?
jhrozek: /retest

Patch:
diff --git a/api/profilebinding/v1alpha1/profilebinding_types.go b/api/profilebinding/v1alpha1/profilebinding_types.go
index 8e555e657..8f92cddab 100644
--- a/api/profilebinding/v1alpha1/profilebinding_types.go
+++ b/api/profilebinding/v1alpha1/profilebinding_types.go
@@ -24,6 +24,7 @@ type ProfileBindingKind string
 
 const (
 	ProfileBindingKindSeccompProfile ProfileBindingKind = "SeccompProfile"
+	ProfileBindingKindSelinuxProfile ProfileBindingKind = "SelinuxProfile"
 )
 
 // ProfileBindingSpec defines the desired state of ProfileBinding.
@@ -37,7 +38,7 @@ type ProfileBindingSpec struct {
 // ProfileRef contains information that points to the profile being used.
 type ProfileRef struct {
 	// Kind of object to be bound.
-	// +kubebuilder:validation:Enum=SeccompProfile
+	// +kubebuilder:validation:Enum=SeccompProfile;SelinuxProfile
 	Kind ProfileBindingKind `json:"kind"` // TODO(cmurphy) add SelinuxProfile
 	// Name of the profile within the current namespace to which to bind the selected pods.
 	Name string `json:"name"`
diff --git a/api/selinuxprofile/v1alpha2/selinuxprofile_types.go b/api/selinuxprofile/v1alpha2/selinuxprofile_types.go
index 7a39c4616..d6254bad3 100644
--- a/api/selinuxprofile/v1alpha2/selinuxprofile_types.go
+++ b/api/selinuxprofile/v1alpha2/selinuxprofile_types.go
@@ -73,7 +73,8 @@ type SelinuxProfileStatus struct {
 	profilebasev1alpha1.StatusBase `json:",inline"`
 	// Represents the string that the SelinuxProfile object can be
 	// referenced as in a pod seLinuxOptions section.
-	Usage string `json:"usage,omitempty"`
+	Usage           string   `json:"usage,omitempty"`
+	ActiveWorkloads []string `json:"activeWorkloads,omitempty"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
diff --git a/api/selinuxprofile/v1alpha2/zz_generated.deepcopy.go b/api/selinuxprofile/v1alpha2/zz_generated.deepcopy.go
index 687fdbff0..734c3f3bc 100644
--- a/api/selinuxprofile/v1alpha2/zz_generated.deepcopy.go
+++ b/api/selinuxprofile/v1alpha2/zz_generated.deepcopy.go
@@ -280,6 +280,11 @@ func (in *SelinuxProfileSpec) DeepCopy() *SelinuxProfileSpec {
 func (in *SelinuxProfileStatus) DeepCopyInto(out *SelinuxProfileStatus) {
 	*out = *in
 	in.StatusBase.DeepCopyInto(&out.StatusBase)
+	if in.ActiveWorkloads != nil {
+		in, out := &in.ActiveWorkloads, &out.ActiveWorkloads
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SelinuxProfileStatus.
diff --git a/bundle/manifests/security-profiles-operator.clusterserviceversion.yaml b/bundle/manifests/security-profiles-operator.clusterserviceversion.yaml
index 2ecba1e71..f440956bc 100644
--- a/bundle/manifests/security-profiles-operator.clusterserviceversion.yaml
+++ b/bundle/manifests/security-profiles-operator.clusterserviceversion.yaml
@@ -152,8 +152,8 @@ spec:
     seccomp and AppArmor in Kubernetes clusters
   displayName: Security Profiles Operator
   icon:
-  - base64data: ""
-    mediatype: ""
+  - base64data: PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTguNTEgMjU4LjUxIj48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2QxZDFkMTt9LmNscy0ye2ZpbGw6IzhkOGQ4Zjt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPkFzc2V0IDQ8L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMTI5LjI1LDIwQTEwOS4xLDEwOS4xLDAsMCwxLDIwNi40LDIwNi40LDEwOS4xLDEwOS4xLDAsMSwxLDUyLjExLDUyLjExLDEwOC40NSwxMDguNDUsMCwwLDEsMTI5LjI1LDIwbTAtMjBoMEM1OC4xNiwwLDAsNTguMTYsMCwxMjkuMjVIMGMwLDcxLjA5LDU4LjE2LDEyOS4yNiwxMjkuMjUsMTI5LjI2aDBjNzEuMDksMCwxMjkuMjYtNTguMTcsMTI5LjI2LTEyOS4yNmgwQzI1OC41MSw1OC4xNiwyMDAuMzQsMCwxMjkuMjUsMFoiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0xNzcuNTQsMTAzLjQxSDE0MS42NkwxNTQuOSw2NS43NmMxLjI1LTQuNC0yLjMzLTguNzYtNy4yMS04Ljc2SDEwMi45M2E3LjMyLDcuMzIsMCwwLDAtNy40LDZsLTEwLDY5LjYxYy0uNTksNC4xNywyLjg5LDcuODksNy40LDcuODloMzYuOUwxMTUuNTUsMTk3Yy0xLjEyLDQuNDEsMi40OCw4LjU1LDcuMjQsOC41NWE3LjU4LDcuNTgsMCwwLDAsNi40Ny0zLjQ4TDE4NCwxMTMuODVDMTg2Ljg2LDEwOS4yNCwxODMuMjksMTAzLjQxLDE3Ny41NCwxMDMuNDFaIi8+PC9nPjwvZz48L3N2Zz4=
+    mediatype: image/svg+xml
   install:
     spec:
       clusterPermissions:
diff --git a/bundle/manifests/security-profiles-operator.x-k8s.io_profilebindings.yaml b/bundle/manifests/security-profiles-operator.x-k8s.io_profilebindings.yaml
index 6c402131a..61ae90629 100644
--- a/bundle/manifests/security-profiles-operator.x-k8s.io_profilebindings.yaml
+++ b/bundle/manifests/security-profiles-operator.x-k8s.io_profilebindings.yaml
@@ -47,6 +47,7 @@ spec:
                     description: Kind of object to be bound.
                     enum:
                     - SeccompProfile
+                    - SelinuxProfile
                     type: string
                   name:
                     description: Name of the profile within the current namespace
diff --git a/bundle/manifests/security-profiles-operator.x-k8s.io_rawselinuxprofiles.yaml b/bundle/manifests/security-profiles-operator.x-k8s.io_rawselinuxprofiles.yaml
index c55911fec..e309eb20c 100644
--- a/bundle/manifests/security-profiles-operator.x-k8s.io_rawselinuxprofiles.yaml
+++ b/bundle/manifests/security-profiles-operator.x-k8s.io_rawselinuxprofiles.yaml
@@ -49,6 +49,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
diff --git a/bundle/manifests/security-profiles-operator.x-k8s.io_selinuxprofiles.yaml b/bundle/manifests/security-profiles-operator.x-k8s.io_selinuxprofiles.yaml
index a50c6b3a5..62c9bf0fc 100644
--- a/bundle/manifests/security-profiles-operator.x-k8s.io_selinuxprofiles.yaml
+++ b/bundle/manifests/security-profiles-operator.x-k8s.io_selinuxprofiles.yaml
@@ -81,6 +81,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
diff --git a/bundle/manifests/spo-webhook_rbac.authorization.k8s.io_v1_clusterrole.yaml b/bundle/manifests/spo-webhook_rbac.authorization.k8s.io_v1_clusterrole.yaml
index ec1fdeefc..c8a61dbb2 100644
--- a/bundle/manifests/spo-webhook_rbac.authorization.k8s.io_v1_clusterrole.yaml
+++ b/bundle/manifests/spo-webhook_rbac.authorization.k8s.io_v1_clusterrole.yaml
@@ -89,3 +89,11 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - security-profiles-operator.x-k8s.io
+  resources:
+  - selinuxprofiles
+  verbs:
+  - get
+  - list
+  - watch
diff --git a/cmd/security-profiles-operator/main.go b/cmd/security-profiles-operator/main.go
index 3bb96ce52..40c15d202 100644
--- a/cmd/security-profiles-operator/main.go
+++ b/cmd/security-profiles-operator/main.go
@@ -473,6 +473,9 @@ func runWebhook(ctx *cli.Context, info *version.Info) error {
 	if err := seccompprofileapi.AddToScheme(mgr.GetScheme()); err != nil {
 		return fmt.Errorf("add seccompprofile API to scheme: %w", err)
 	}
+	if err := selxv1alpha2.AddToScheme(mgr.GetScheme()); err != nil {
+		return fmt.Errorf("add selinuxprofile API to scheme: %w", err)
+	}
 	if err := profilerecording1alpha1.AddToScheme(mgr.GetScheme()); err != nil {
 		return fmt.Errorf("add profilerecording API to scheme: %w", err)
 	}
diff --git a/dependencies.yaml b/dependencies.yaml
index f35e50eb7..2ca3bb45e 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -52,7 +52,7 @@ dependencies:
       match: OLM_VERSION
 
   - name: cert-manager
-    version: 1.7.2
+    version: 1.8.0
     refPaths:
     - path: test/e2e_test.go
       match: jetstack/cert-manager
diff --git a/deploy/base/clusterserviceversion.yaml b/deploy/base/clusterserviceversion.yaml
index f7e70bd83..b58104058 100644
--- a/deploy/base/clusterserviceversion.yaml
+++ b/deploy/base/clusterserviceversion.yaml
@@ -53,8 +53,8 @@ spec:
   description: SPO is an operator which aims to make it easier for users to use SELinux, seccomp and AppArmor in Kubernetes clusters
   displayName: Security Profiles Operator
   icon:
-  - base64data: ""
-    mediatype: ""
+  - base64data: "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTguNTEgMjU4LjUxIj48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2QxZDFkMTt9LmNscy0ye2ZpbGw6IzhkOGQ4Zjt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPkFzc2V0IDQ8L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMTI5LjI1LDIwQTEwOS4xLDEwOS4xLDAsMCwxLDIwNi40LDIwNi40LDEwOS4xLDEwOS4xLDAsMSwxLDUyLjExLDUyLjExLDEwOC40NSwxMDguNDUsMCwwLDEsMTI5LjI1LDIwbTAtMjBoMEM1OC4xNiwwLDAsNTguMTYsMCwxMjkuMjVIMGMwLDcxLjA5LDU4LjE2LDEyOS4yNiwxMjkuMjUsMTI5LjI2aDBjNzEuMDksMCwxMjkuMjYtNTguMTcsMTI5LjI2LTEyOS4yNmgwQzI1OC41MSw1OC4xNiwyMDAuMzQsMCwxMjkuMjUsMFoiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0xNzcuNTQsMTAzLjQxSDE0MS42NkwxNTQuOSw2NS43NmMxLjI1LTQuNC0yLjMzLTguNzYtNy4yMS04Ljc2SDEwMi45M2E3LjMyLDcuMzIsMCwwLDAtNy40LDZsLTEwLDY5LjYxYy0uNTksNC4xNywyLjg5LDcuODksNy40LDcuODloMzYuOUwxMTUuNTUsMTk3Yy0xLjEyLDQuNDEsMi40OCw4LjU1LDcuMjQsOC41NWE3LjU4LDcuNTgsMCwwLDAsNi40Ny0zLjQ4TDE4NCwxMTMuODVDMTg2Ljg2LDEwOS4yNCwxODMuMjksMTAzLjQxLDE3Ny41NCwxMDMuNDFaIi8+PC9nPjwvZz48L3N2Zz4="
+    mediatype: "image/svg+xml"
   install:
     spec:
       deployments: null
diff --git a/deploy/base/crd.yaml b/deploy/base/crd.yaml
index b0329c6da..90b876322 100644
--- a/deploy/base/crd.yaml
+++ b/deploy/base/crd.yaml
@@ -505,6 +505,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
diff --git a/deploy/base/crds/profilebinding.yaml b/deploy/base/crds/profilebinding.yaml
index 04779443c..1f72ca0a7 100644
--- a/deploy/base/crds/profilebinding.yaml
+++ b/deploy/base/crds/profilebinding.yaml
@@ -45,6 +45,7 @@ spec:
                     description: Kind of object to be bound.
                     enum:
                     - SeccompProfile
+                    - SelinuxProfile
                     type: string
                   name:
                     description: Name of the profile within the current namespace
diff --git a/deploy/base/crds/selinuxpolicy.yaml b/deploy/base/crds/selinuxpolicy.yaml
index 843c09f08..5f612e20d 100644
--- a/deploy/base/crds/selinuxpolicy.yaml
+++ b/deploy/base/crds/selinuxpolicy.yaml
@@ -47,6 +47,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
@@ -184,6 +188,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
diff --git a/deploy/base/role.yaml b/deploy/base/role.yaml
index fda373bb2..5e966fd7b 100644
--- a/deploy/base/role.yaml
+++ b/deploy/base/role.yaml
@@ -544,6 +544,14 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - security-profiles-operator.x-k8s.io
+  resources:
+  - selinuxprofiles
+  verbs:
+  - get
+  - list
+  - watch
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
diff --git a/deploy/namespace-operator.yaml b/deploy/namespace-operator.yaml
index d583070b4..e70d1ca0a 100644
--- a/deploy/namespace-operator.yaml
+++ b/deploy/namespace-operator.yaml
@@ -47,6 +47,7 @@ spec:
                     description: Kind of object to be bound.
                     enum:
                     - SeccompProfile
+                    - SelinuxProfile
                     type: string
                   name:
                     description: Name of the profile within the current namespace
@@ -776,6 +777,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
@@ -915,6 +920,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
@@ -1617,6 +1626,14 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - security-profiles-operator.x-k8s.io
+  resources:
+  - selinuxprofiles
+  verbs:
+  - get
+  - list
+  - watch
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
diff --git a/deploy/openshift-dev.yaml b/deploy/openshift-dev.yaml
index 5e2e27f74..198319581 100644
--- a/deploy/openshift-dev.yaml
+++ b/deploy/openshift-dev.yaml
@@ -47,6 +47,7 @@ spec:
                     description: Kind of object to be bound.
                     enum:
                     - SeccompProfile
+                    - SelinuxProfile
                     type: string
                   name:
                     description: Name of the profile within the current namespace
@@ -776,6 +777,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
@@ -915,6 +920,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
@@ -1617,6 +1626,14 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - security-profiles-operator.x-k8s.io
+  resources:
+  - selinuxprofiles
+  verbs:
+  - get
+  - list
+  - watch
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
diff --git a/deploy/operator.yaml b/deploy/operator.yaml
index a3faaebc0..ed45cf4ff 100644
--- a/deploy/operator.yaml
+++ b/deploy/operator.yaml
@@ -47,6 +47,7 @@ spec:
                     description: Kind of object to be bound.
                     enum:
                     - SeccompProfile
+                    - SelinuxProfile
                     type: string
                   name:
                     description: Name of the profile within the current namespace
@@ -776,6 +777,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
@@ -915,6 +920,10 @@ spec:
           status:
             description: SelinuxProfileStatus defines the observed state of SelinuxProfile.
             properties:
+              activeWorkloads:
+                items:
+                  type: string
+                type: array
               conditions:
                 description: Conditions of the resource.
                 items:
@@ -1617,6 +1626,14 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - security-profiles-operator.x-k8s.io
+  resources:
+  - selinuxprofiles
+  verbs:
+  - get
+  - list
+  - watch
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
diff --git a/installation-usage.md b/installation-usage.md
index ee29173f0..100bb4a42 100644
--- a/installation-usage.md
+++ b/installation-usage.md
@@ -1,6 +1,7 @@
 # Installation and Usage
 
 <!-- toc -->
+
 - [Features](#features)
 - [Tutorials and Demos](#tutorials-and-demos)
 - [Install operator](#install-operator)
@@ -51,7 +52,7 @@ cert-manager via `kubectl`, if you're **not** running on
 [OpenShift](https://www.redhat.com/en/technologies/cloud-computing/openshift):
 
 ```sh
-$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.7.2/cert-manager.yaml
+$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.8.0/cert-manager.yaml
 $ kubectl --namespace cert-manager wait --for condition=ready pod -l app.kubernetes.io/instance=cert-manager
 ```
 
@@ -82,7 +83,7 @@ I1111 15:13:16.942837       1 main.go:182]  "msg"="Set logging verbosity to 1"
 
 ## Configure the SELinux type
 
-The operator uses by default the `spc_t` SELinux type in the security context of the daemon pod. This can be easily 
+The operator uses by default the `spc_t` SELinux type in the security context of the daemon pod. This can be easily
 changed to a different SELinux type by patching the spod config as follows:
 
 ```
diff --git a/internal/pkg/manager/workloadannotator/setup.go b/internal/pkg/manager/workloadannotator/setup.go
index 25a63500d..deb55edb8 100644
--- a/internal/pkg/manager/workloadannotator/setup.go
+++ b/internal/pkg/manager/workloadannotator/setup.go
@@ -28,6 +28,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	seccompprofileapi "sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1beta1"
+	selinuxprofileapi "sigs.k8s.io/security-profiles-operator/api/selinuxprofile/v1alpha2"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/metrics"
 )
 
@@ -53,6 +54,17 @@ func (r *PodReconciler) Setup(
 		return fmt.Errorf("creating pod index: %w", err)
 	}
 
+	// Index Pods using selinux profiles
+	if err := mgr.GetFieldIndexer().IndexField(ctx, &corev1.Pod{}, seOwnerKey, func(rawObj client.Object) []string {
+		pod, ok := rawObj.(*corev1.Pod)
+		if !ok {
+			return []string{}
+		}
+		return getSelinuxProfilesFromPod(r, pod)
+	}); err != nil {
+		return fmt.Errorf("creating pod index: %w", err)
+	}
+
 	// Index SeccompProfiles with active pods
 	if err := mgr.GetFieldIndexer().IndexField(
 		ctx, &seccompprofileapi.SeccompProfile{}, linkedPodsKey, func(rawObj client.Object) []string {
@@ -60,16 +72,29 @@ func (r *PodReconciler) Setup(
 			if !ok {
 				return []string{}
 			}
+
 			return sp.Status.ActiveWorkloads
 		}); err != nil {
 		return fmt.Errorf("creating seccomp profile index: %w", err)
 	}
 
+	// Index SelinuxProfile with active pods
+	if err := mgr.GetFieldIndexer().IndexField(
+		ctx, &selinuxprofileapi.SelinuxProfile{}, linkedPodsKey, func(rawObj client.Object) []string {
+			sp, ok := rawObj.(*selinuxprofileapi.SelinuxProfile)
+			if !ok {
+				return []string{}
+			}
+			return sp.Status.ActiveWorkloads
+		}); err != nil {
+		return fmt.Errorf("creating selinux profile index: %w", err)
+	}
+
 	// Register a special reconciler for pod events
 	return ctrl.NewControllerManagedBy(mgr).
 		Named(name).
 		For(&corev1.Pod{}).
-		WithEventFilter(resource.NewPredicates(hasSeccompProfile)).
+		WithEventFilter(resource.NewPredicates(r.hasValidProfile)).
 		Complete(r)
 }
 
@@ -81,3 +106,16 @@ func hasSeccompProfile(obj runtime.Object) bool {
 
 	return len(getSeccompProfilesFromPod(pod)) > 0
 }
+
+func hasSelinuxProfile(r *PodReconciler, obj runtime.Object) bool {
+	pod, ok := obj.(*corev1.Pod)
+	if !ok {
+		return false
+	}
+
+	return len(getSelinuxProfilesFromPod(r, pod)) > 0
+}
+
+func (r *PodReconciler) hasValidProfile(obj runtime.Object) bool {
+	return hasSeccompProfile(obj) || hasSelinuxProfile(r, obj)
+}
diff --git a/internal/pkg/manager/workloadannotator/workloadannotator.go b/internal/pkg/manager/workloadannotator/workloadannotator.go
index 02747d427..7907c080a 100644
--- a/internal/pkg/manager/workloadannotator/workloadannotator.go
+++ b/internal/pkg/manager/workloadannotator/workloadannotator.go
@@ -32,14 +32,17 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/scheme"
 
 	seccompprofileapi "sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1beta1"
+	selinuxprofileapi "sigs.k8s.io/security-profiles-operator/api/selinuxprofile/v1alpha2"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/controller"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/util"
 )
 
 const (
-	spOwnerKey       = ".metadata.seccompProfileOwner"
-	linkedPodsKey    = ".metadata.activeWorkloads"
-	reconcileTimeout = 1 * time.Minute
+	spOwnerKey        = ".metadata.seccompProfileOwner"
+	seOwnerKey        = ".metadata.selinuxProfileOwner"
+	linkedPodsKey     = ".metadata.activeWorkloads"
+	StatusToProfLabel = "spo.x-k8s.io/profile-id"
+	reconcileTimeout  = 1 * time.Minute
 )
 
 // NewController returns a new empty controller instance.
@@ -72,7 +75,7 @@ func (r *PodReconciler) Healthz(*http.Request) error {
 // Namespace scoped
 // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
 
-// Reconcile reacts to pod events and updates SeccompProfiles if in use or no longer in use by a pod.
+// Reconcile reacts to pod events and updates SeccompProfiles or SelinuxProfiles if in use or no longer in use by a pod.
 func (r *PodReconciler) Reconcile(_ context.Context, req reconcile.Request) (reconcile.Result, error) {
 	logger := r.log.WithValues("pod", req.Name, "namespace", req.Namespace)
 
@@ -87,19 +90,33 @@ func (r *PodReconciler) Reconcile(_ context.Context, req reconcile.Request) (rec
 		logger.Error(err, "could not get pod")
 		return reconcile.Result{}, fmt.Errorf("looking up pod in pod reconciler: %w", err)
 	}
-	if errors.IsNotFound(err) { // this is a pod deletion, so update all seccomp profiles that were using it
+	if errors.IsNotFound(err) { // this is a pod deletion, so update all seccomp/selinux profiles that were using it
 		seccompProfiles := &seccompprofileapi.SeccompProfileList{}
+		selinuxProfiles := &selinuxprofileapi.SelinuxProfileList{}
+
 		if err = r.client.List(ctx, seccompProfiles, client.MatchingFields{linkedPodsKey: podID}); err != nil {
 			return reconcile.Result{}, fmt.Errorf("listing SeccompProfiles for deleted pod: %w", err)
 		}
+
+		if err = r.client.List(ctx, selinuxProfiles, client.MatchingFields{linkedPodsKey: podID}); err != nil {
+			return reconcile.Result{}, fmt.Errorf("listing SelinuxProfiles for deleted pod: %w", err)
+		}
+
 		for i := range seccompProfiles.Items {
-			if err = r.updatePodReferences(ctx, &seccompProfiles.Items[i]); err != nil {
+			if err = r.updatePodReferencesForSeccomp(ctx, &seccompProfiles.Items[i]); err != nil {
 				return reconcile.Result{}, fmt.Errorf("updating SeccompProfile for deleted pod: %w", err)
 			}
 		}
+
+		for j := range selinuxProfiles.Items {
+			if err = r.updatePodReferencesForSelinux(ctx, &selinuxProfiles.Items[j]); err != nil {
+				return reconcile.Result{}, fmt.Errorf("updating SelinuxProfile for deleted pod: %w", err)
+			}
+		}
 		return reconcile.Result{}, nil
 	}
-	// pod is being created or updated so ensure it is linked to a seccomp profile
+
+	// pod is being created or updated so ensure it is linked to a seccomp/selinux profile
 	for _, profileIndex := range getSeccompProfilesFromPod(pod) {
 		profileElements := strings.Split(profileIndex, "/")
 		profileNamespace := profileElements[1]
@@ -109,17 +126,33 @@ func (r *PodReconciler) Reconcile(_ context.Context, req reconcile.Request) (rec
 			logger.Error(err, "could not get seccomp profile for pod")
 			return reconcile.Result{}, fmt.Errorf("looking up SeccompProfile for new or updated pod: %w", err)
 		}
-		if err := r.updatePodReferences(ctx, seccompProfile); err != nil {
+		if err := r.updatePodReferencesForSeccomp(ctx, seccompProfile); err != nil {
 			logger.Error(err, "could not update seccomp profile for pod")
 			return reconcile.Result{}, fmt.Errorf("updating SeccompProfile pod references for new or updated pod: %w", err)
 		}
 	}
+
+	// pod is being created or updated so ensure it is linked to a selinux profile
+	for _, profileIndex := range getSelinuxProfilesFromPod(r, pod) {
+		profileSuffix := "_" + pod.GetNamespace() + ".process"
+		profileName := strings.TrimSuffix(profileIndex, profileSuffix)
+
+		selinuxProfile := &selinuxprofileapi.SelinuxProfile{}
+		if err := r.client.Get(ctx, util.NamespacedName(profileName, pod.GetNamespace()), selinuxProfile); err != nil {
+			logger.Error(err, "could not get selinux profile for pod")
+			return reconcile.Result{}, fmt.Errorf("looking up SelinuxProfile for new or updated pod: %w", err)
+		}
+		if err := r.updatePodReferencesForSelinux(ctx, selinuxProfile); err != nil {
+			logger.Error(err, "could not update selinux profile for pod")
+			return reconcile.Result{}, fmt.Errorf("updating SelinuxProfile pod references for new or updated pod: %w", err)
+		}
+	}
 	return reconcile.Result{}, nil
 }
 
-// updatePodReferences updates a SeccompProfile with the identifiers of pods using it and ensures
+// updatePodReferencesForSeccomp updates a SeccompProfile with the identifiers of pods using it and ensures
 // it has a finalizer indicating it is in use to prevent it from being deleted.
-func (r *PodReconciler) updatePodReferences(ctx context.Context, sp *seccompprofileapi.SeccompProfile) error {
+func (r *PodReconciler) updatePodReferencesForSeccomp(ctx context.Context, sp *seccompprofileapi.SeccompProfile) error {
 	linkedPods := &corev1.PodList{}
 	profileReference := fmt.Sprintf("operator/%s/%s.json", sp.GetNamespace(), sp.GetName())
 	err := r.client.List(ctx, linkedPods, client.MatchingFields{spOwnerKey: profileReference})
@@ -162,6 +195,49 @@ func (r *PodReconciler) updatePodReferences(ctx context.Context, sp *seccompprof
 	return nil
 }
 
+// updatePodReferencesForSelinux updates a SelinuxProfile with the identifiers of pods using it and ensures
+// it has a finalizer indicating it is in use to prevent it from being deleted.
+func (r *PodReconciler) updatePodReferencesForSelinux(ctx context.Context, se *selinuxprofileapi.SelinuxProfile) error {
+	linkedPods := &corev1.PodList{}
+	profileReference := se.GetPolicyUsage()
+	err := r.client.List(ctx, linkedPods, client.MatchingFields{seOwnerKey: profileReference})
+	if util.IgnoreNotFound(err) != nil {
+		return fmt.Errorf("listing pods to update selinuxProfile: %w", err)
+	}
+	podList := make([]string, len(linkedPods.Items))
+	for i := range linkedPods.Items {
+		pod := linkedPods.Items[i]
+		podList[i] = pod.ObjectMeta.Namespace + "/" + pod.ObjectMeta.Name
+	}
+	if err := util.Retry(func() error {
+		se.Status.ActiveWorkloads = podList
+		updateErr := r.client.Status().Update(ctx, se)
+		if updateErr != nil {
+			if err := r.client.Get(ctx, util.NamespacedName(se.GetName(), se.GetNamespace()), se); err != nil {
+				return fmt.Errorf("retrieving profile: %w", err)
+			}
+			return fmt.Errorf("updating profile: %w", updateErr)
+		}
+		return nil
+	}, util.IsNotFoundOrConflict); err != nil {
+		return fmt.Errorf("updating SelinuxProfile status: %w", err)
+	}
+	if len(linkedPods.Items) > 0 {
+		if err := util.Retry(func() error {
+			return util.AddFinalizer(ctx, r.client, se, util.HasActivePodsFinalizerString)
+		}, util.IsNotFoundOrConflict); err != nil {
+			return fmt.Errorf("adding finalizer: %w", err)
+		}
+	} else {
+		if err := util.Retry(func() error {
+			return util.RemoveFinalizer(ctx, r.client, se, util.HasActivePodsFinalizerString)
+		}, util.IsNotFoundOrConflict); err != nil {
+			return fmt.Errorf("removing finalizer: %w", err)
+		}
+	}
+	return nil
+}
+
 // getSeccompProfilesFromPod returns a slice of strings representing seccomp profiles required by the pod.
 // It looks first at the pod spec level, then in each container and init container, then in the annotations.
 func getSeccompProfilesFromPod(pod *corev1.Pod) []string {
@@ -194,6 +270,34 @@ func getSeccompProfilesFromPod(pod *corev1.Pod) []string {
 	return profiles
 }
 
+// getSelinuxProfilesFromPod returns a slice of strings representing selinux profiles required by the pod.
+// It looks first at the pod spec level, then in each container.
+func getSelinuxProfilesFromPod(r *PodReconciler, pod *corev1.Pod) []string {
+	profiles := []string{}
+	// try to get profile from pod securityContext
+	sc := pod.Spec.SecurityContext
+	if sc != nil {
+		if isOperatorSelinuxType(r, sc.SELinuxOptions, pod.GetNamespace()) {
+			profiles = append(profiles, sc.SELinuxOptions.Type)
+		}
+	}
+	// try to get profile(s) from securityContext in containers
+	containers := pod.Spec.Containers
+	containers = append(containers, pod.Spec.InitContainers...)
+	for i := range containers {
+		sc := containers[i].SecurityContext
+		if sc != nil {
+			if isOperatorSelinuxType(r, sc.SELinuxOptions, pod.GetNamespace()) {
+				profileString := containers[i].SecurityContext.SELinuxOptions.Type
+				if !util.Contains(profiles, profileString) {
+					profiles = append(profiles, profileString)
+				}
+			}
+		}
+	}
+	return profiles
+}
+
 // isOperatorSeccompProfile checks whether a corev1.SeccompProfile object belongs to the operator.
 // SeccompProfiles controlled by the operator are of type "Localhost" and have a path of the form
 // "operator/namespace/profile-name.json".
@@ -210,3 +314,29 @@ func isOperatorSeccompProfile(sp *corev1.SeccompProfile) bool {
 	const pathParts = 3
 	return len(strings.Split(*sp.LocalhostProfile, "/")) == pathParts
 }
+
+// isOperatorSelinuxType checks whether Selinux Type is created by the operator.
+// Selinux Type controlled by the operator has the form
+// "selinuxprofilename_namespace.process".
+func isOperatorSelinuxType(r *PodReconciler, se *corev1.SELinuxOptions, ns string) bool {
+	if se == nil {
+		return false
+	}
+	if se.Type == "" {
+		return false
+	}
+	suffix := "_" + ns + ".process"
+	selinuxProfileName := strings.TrimSuffix(se.Type, suffix)
+
+	if selinuxProfileName != se.Type {
+		selinuxProfile := &selinuxprofileapi.SelinuxProfile{}
+		err := r.client.Get(context.TODO(), util.NamespacedName(strings.TrimSuffix(se.Type, suffix), ns), selinuxProfile)
+		if err != nil {
+			return false
+		}
+
+		_, hasLabel := selinuxProfile.GetLabels()[StatusToProfLabel]
+		return hasLabel
+	}
+	return false
+}
diff --git a/internal/pkg/webhooks/binding/binding.go b/internal/pkg/webhooks/binding/binding.go
index 628909259..729c250f8 100644
--- a/internal/pkg/webhooks/binding/binding.go
+++ b/internal/pkg/webhooks/binding/binding.go
@@ -36,6 +36,7 @@ import (
 
 	profilebindingv1alpha1 "sigs.k8s.io/security-profiles-operator/api/profilebinding/v1alpha1"
 	seccompprofileapi "sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1beta1"
+	selinuxprofileapi "sigs.k8s.io/security-profiles-operator/api/selinuxprofile/v1alpha2"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/util"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/webhooks/utils"
 )
@@ -44,7 +45,7 @@ const finalizer = "active-workload-lock"
 
 var ErrProfWithoutStatus = errors.New("profile hasn't been initialized with status")
 
-type podSeccompBinder struct {
+type podBinder struct {
 	impl
 	log logr.Logger
 }
@@ -53,7 +54,7 @@ func RegisterWebhook(server *webhook.Server, c client.Client) {
 	server.Register(
 		"/mutate-v1-pod-binding",
 		&webhook.Admission{
-			Handler: &podSeccompBinder{
+			Handler: &podBinder{
 				impl: &defaultImpl{client: c},
 				log:  logf.Log.WithName("binding"),
 			},
@@ -92,6 +93,7 @@ func initContainerMap(m *sync.Map, spec *corev1.PodSpec) {
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=profilebindings/status,verbs=get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=profilebindings/finalizers,verbs=delete;get;update;patch
 // +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=seccompprofiles,verbs=get;list;watch
+// +kubebuilder:rbac:groups=security-profiles-operator.x-k8s.io,resources=selinuxprofiles,verbs=get;list;watch
 // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
 
 // Leader election
@@ -101,7 +103,7 @@ func initContainerMap(m *sync.Map, spec *corev1.PodSpec) {
 // +kubebuilder:rbac:groups=coordination.k8s.io,namespace="security-profiles-operator",resources=leases,verbs=create;get;update;
 
 // nolint:gocritic
-func (p *podSeccompBinder) Handle(ctx context.Context, req admission.Request) admission.Response {
+func (p *podBinder) Handle(ctx context.Context, req admission.Request) admission.Response {
 	profileBindings, err := p.ListProfileBindings(ctx, client.InNamespace(req.Namespace))
 	if err != nil {
 		p.log.Error(err, "could not list profile bindings")
@@ -123,11 +125,14 @@ func (p *podSeccompBinder) Handle(ctx context.Context, req admission.Request) ad
 	}
 
 	for i := range profilebindings {
-		// TODO(cmurphy): handle profiles kinds other than SeccompProfile
-		if profilebindings[i].Spec.ProfileRef.Kind != profilebindingv1alpha1.ProfileBindingKindSeccompProfile {
-			p.log.Info(fmt.Sprintf("profile kind %s not yet supported", profilebindings[i].Spec.ProfileRef.Kind))
-			continue
+		profileKind := profilebindings[i].Spec.ProfileRef.Kind
+		if profileKind != profilebindingv1alpha1.ProfileBindingKindSeccompProfile {
+			if profileKind != profilebindingv1alpha1.ProfileBindingKindSelinuxProfile {
+				p.log.Info(fmt.Sprintf("profile kind %s not yet supported", profileKind))
+				continue
+			}
 		}
+
 		profileName := profilebindings[i].Spec.ProfileRef.Name
 		if req.Operation == "DELETE" {
 			if err := p.removePodFromBinding(ctx, podID, &profilebindings[i]); err != nil {
@@ -145,14 +150,24 @@ func (p *podSeccompBinder) Handle(ctx context.Context, req admission.Request) ad
 		}
 
 		namespacedName := types.NamespacedName{Namespace: req.Namespace, Name: profileName}
-		seccompProfile, err := p.getSeccompProfile(ctx, namespacedName)
+		var bindProfile interface{}
+		var err error
+
+		if profileKind == profilebindingv1alpha1.ProfileBindingKindSeccompProfile {
+			bindProfile, err = p.getSeccompProfile(ctx, namespacedName)
+		}
+
+		if profileKind == profilebindingv1alpha1.ProfileBindingKindSelinuxProfile {
+			bindProfile, err = p.getSelinuxProfile(ctx, namespacedName)
+		}
+
 		if err != nil {
-			p.log.Error(err, fmt.Sprintf("failed to get SeccompProfile %#v", namespacedName))
+			p.log.Error(err, fmt.Sprintf("failed to get %v %#v", profileKind, namespacedName))
 			return admission.Errored(http.StatusInternalServerError, err)
 		}
 
 		for j := range containers {
-			podChanged = p.addSecurityContext(containers[j], seccompProfile)
+			podChanged = p.addSecurityContext(containers[j], bindProfile)
 		}
 		if podChanged {
 			if err := p.addPodToBinding(ctx, podID, &profilebindings[i]); err != nil {
@@ -172,7 +187,7 @@ func (p *podSeccompBinder) Handle(ctx context.Context, req admission.Request) ad
 	return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod)
 }
 
-func (p *podSeccompBinder) getSeccompProfile(
+func (p *podBinder) getSeccompProfile(
 	ctx context.Context,
 	key types.NamespacedName,
 ) (seccompProfile *seccompprofileapi.SeccompProfile, err error) {
@@ -193,7 +208,45 @@ func (p *podSeccompBinder) getSeccompProfile(
 	return seccompProfile, err
 }
 
-func (p *podSeccompBinder) addSecurityContext(
+func (p *podBinder) getSelinuxProfile(
+	ctx context.Context,
+	key types.NamespacedName,
+) (selinuxProfile *selinuxprofileapi.SelinuxProfile, err error) {
+	err = util.Retry(
+		func() (retryErr error) {
+			selinuxProfile, retryErr = p.GetSelinuxProfile(ctx, key)
+			if retryErr != nil {
+				return fmt.Errorf("getting profile: %w", retryErr)
+			}
+			if selinuxProfile.Status.Status == "" {
+				return fmt.Errorf("getting profile:	%w", ErrProfWithoutStatus)
+			}
+			return nil
+		}, func(inErr error) bool {
+			return errors.Is(inErr, ErrProfWithoutStatus) || kerrors.IsNotFound(inErr)
+		})
+	// nolint:wrapcheck // error is already wrapped
+	return selinuxProfile, err
+}
+
+func (p *podBinder) addSecurityContext(
+	c *corev1.Container, bindProfile interface{},
+) bool {
+	var podChanged bool
+
+	switch v := bindProfile.(type) {
+	case *seccompprofileapi.SeccompProfile:
+		podChanged = p.addSeccompContext(c, v)
+	case *selinuxprofileapi.SelinuxProfile:
+		podChanged = p.addSelinuxContext(c, v)
+	default:
+		p.log.Info("Unexpected Profile Type")
+		return false
+	}
+	return podChanged
+}
+
+func (p *podBinder) addSeccompContext(
 	c *corev1.Container, seccompProfile *seccompprofileapi.SeccompProfile,
 ) bool {
 	podChanged := false
@@ -214,7 +267,28 @@ func (p *podSeccompBinder) addSecurityContext(
 	return podChanged
 }
 
-func (p *podSeccompBinder) addPodToBinding(
+func (p *podBinder) addSelinuxContext(
+	c *corev1.Container, selinuxProfile *selinuxprofileapi.SelinuxProfile,
+) bool {
+	podChanged := false
+	usage := selinuxProfile.Status.Usage
+	sl := corev1.SELinuxOptions{
+		Type: usage,
+	}
+
+	if c.SecurityContext == nil {
+		c.SecurityContext = &corev1.SecurityContext{}
+	}
+	if c.SecurityContext.SELinuxOptions != nil {
+		p.log.Info("cannot override existing selinux profile for pod or container")
+	} else {
+		c.SecurityContext.SELinuxOptions = &sl
+		podChanged = true
+	}
+	return podChanged
+}
+
+func (p *podBinder) addPodToBinding(
 	ctx context.Context,
 	podID string,
 	pb *profilebindingv1alpha1.ProfileBinding,
@@ -229,7 +303,7 @@ func (p *podSeccompBinder) addPodToBinding(
 	return p.impl.UpdateResource(ctx, p.log, pb, "profilebinding")
 }
 
-func (p *podSeccompBinder) removePodFromBinding(
+func (p *podBinder) removePodFromBinding(
 	ctx context.Context,
 	podID string,
 	pb *profilebindingv1alpha1.ProfileBinding,
@@ -245,7 +319,7 @@ func (p *podSeccompBinder) removePodFromBinding(
 	return p.impl.UpdateResource(ctx, p.log, pb, "profilebinding")
 }
 
-func (p *podSeccompBinder) InjectDecoder(decoder *admission.Decoder) error {
+func (p *podBinder) InjectDecoder(decoder *admission.Decoder) error {
 	p.impl.SetDecoder(decoder)
 	return nil
 }
diff --git a/internal/pkg/webhooks/binding/bindingfakes/fake_impl.go b/internal/pkg/webhooks/binding/bindingfakes/fake_impl.go
index 541158952..e85afdc1c 100644
--- a/internal/pkg/webhooks/binding/bindingfakes/fake_impl.go
+++ b/internal/pkg/webhooks/binding/bindingfakes/fake_impl.go
@@ -28,6 +28,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 	"sigs.k8s.io/security-profiles-operator/api/profilebinding/v1alpha1"
 	"sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1beta1"
+	"sigs.k8s.io/security-profiles-operator/api/selinuxprofile/v1alpha2"
 )
 
 type FakeImpl struct {
@@ -58,6 +59,20 @@ type FakeImpl struct {
 		result1 *v1beta1.SeccompProfile
 		result2 error
 	}
+	GetSelinuxProfileStub        func(context.Context, types.NamespacedName) (*v1alpha2.SelinuxProfile, error)
+	getSelinuxProfileMutex       sync.RWMutex
+	getSelinuxProfileArgsForCall []struct {
+		arg1 context.Context
+		arg2 types.NamespacedName
+	}
+	getSelinuxProfileReturns struct {
+		result1 *v1alpha2.SelinuxProfile
+		result2 error
+	}
+	getSelinuxProfileReturnsOnCall map[int]struct {
+		result1 *v1alpha2.SelinuxProfile
+		result2 error
+	}
 	ListProfileBindingsStub        func(context.Context, ...client.ListOption) (*v1alpha1.ProfileBindingList, error)
 	listProfileBindingsMutex       sync.RWMutex
 	listProfileBindingsArgsForCall []struct {
@@ -238,6 +253,71 @@ func (fake *FakeImpl) GetSeccompProfileReturnsOnCall(i int, result1 *v1beta1.Sec
 	}{result1, result2}
 }
 
+func (fake *FakeImpl) GetSelinuxProfile(arg1 context.Context, arg2 types.NamespacedName) (*v1alpha2.SelinuxProfile, error) {
+	fake.getSelinuxProfileMutex.Lock()
+	ret, specificReturn := fake.getSelinuxProfileReturnsOnCall[len(fake.getSelinuxProfileArgsForCall)]
+	fake.getSelinuxProfileArgsForCall = append(fake.getSelinuxProfileArgsForCall, struct {
+		arg1 context.Context
+		arg2 types.NamespacedName
+	}{arg1, arg2})
+	stub := fake.GetSelinuxProfileStub
+	fakeReturns := fake.getSelinuxProfileReturns
+	fake.recordInvocation("GetSelinuxProfile", []interface{}{arg1, arg2})
+	fake.getSelinuxProfileMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *FakeImpl) GetSelinuxProfileCallCount() int {
+	fake.getSelinuxProfileMutex.RLock()
+	defer fake.getSelinuxProfileMutex.RUnlock()
+	return len(fake.getSelinuxProfileArgsForCall)
+}
+
+func (fake *FakeImpl) GetSelinuxProfileCalls(stub func(context.Context, types.NamespacedName) (*v1alpha2.SelinuxProfile, error)) {
+	fake.getSelinuxProfileMutex.Lock()
+	defer fake.getSelinuxProfileMutex.Unlock()
+	fake.GetSelinuxProfileStub = stub
+}
+
+func (fake *FakeImpl) GetSelinuxProfileArgsForCall(i int) (context.Context, types.NamespacedName) {
+	fake.getSelinuxProfileMutex.RLock()
+	defer fake.getSelinuxProfileMutex.RUnlock()
+	argsForCall := fake.getSelinuxProfileArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeImpl) GetSelinuxProfileReturns(result1 *v1alpha2.SelinuxProfile, result2 error) {
+	fake.getSelinuxProfileMutex.Lock()
+	defer fake.getSelinuxProfileMutex.Unlock()
+	fake.GetSelinuxProfileStub = nil
+	fake.getSelinuxProfileReturns = struct {
+		result1 *v1alpha2.SelinuxProfile
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeImpl) GetSelinuxProfileReturnsOnCall(i int, result1 *v1alpha2.SelinuxProfile, result2 error) {
+	fake.getSelinuxProfileMutex.Lock()
+	defer fake.getSelinuxProfileMutex.Unlock()
+	fake.GetSelinuxProfileStub = nil
+	if fake.getSelinuxProfileReturnsOnCall == nil {
+		fake.getSelinuxProfileReturnsOnCall = make(map[int]struct {
+			result1 *v1alpha2.SelinuxProfile
+			result2 error
+		})
+	}
+	fake.getSelinuxProfileReturnsOnCall[i] = struct {
+		result1 *v1alpha2.SelinuxProfile
+		result2 error
+	}{result1, result2}
+}
+
 func (fake *FakeImpl) ListProfileBindings(arg1 context.Context, arg2 ...client.ListOption) (*v1alpha1.ProfileBindingList, error) {
 	fake.listProfileBindingsMutex.Lock()
 	ret, specificReturn := fake.listProfileBindingsReturnsOnCall[len(fake.listProfileBindingsArgsForCall)]
@@ -470,6 +550,8 @@ func (fake *FakeImpl) Invocations() map[string][][]interface{} {
 	defer fake.decodePodMutex.RUnlock()
 	fake.getSeccompProfileMutex.RLock()
 	defer fake.getSeccompProfileMutex.RUnlock()
+	fake.getSelinuxProfileMutex.RLock()
+	defer fake.getSelinuxProfileMutex.RUnlock()
 	fake.listProfileBindingsMutex.RLock()
 	defer fake.listProfileBindingsMutex.RUnlock()
 	fake.setDecoderMutex.RLock()
diff --git a/internal/pkg/webhooks/binding/impl.go b/internal/pkg/webhooks/binding/impl.go
index fc169ed33..83e637fdf 100644
--- a/internal/pkg/webhooks/binding/impl.go
+++ b/internal/pkg/webhooks/binding/impl.go
@@ -28,6 +28,7 @@ import (
 
 	"sigs.k8s.io/security-profiles-operator/api/profilebinding/v1alpha1"
 	seccompprofileapi "sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1beta1"
+	selinuxprofileapi "sigs.k8s.io/security-profiles-operator/api/selinuxprofile/v1alpha2"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/webhooks/utils"
 )
 
@@ -45,6 +46,7 @@ type impl interface {
 	SetDecoder(*admission.Decoder)
 	DecodePod(admission.Request) (*corev1.Pod, error)
 	GetSeccompProfile(context.Context, types.NamespacedName) (*seccompprofileapi.SeccompProfile, error)
+	GetSelinuxProfile(context.Context, types.NamespacedName) (*selinuxprofileapi.SelinuxProfile, error)
 }
 
 func (d *defaultImpl) ListProfileBindings(
@@ -97,3 +99,14 @@ func (d *defaultImpl) GetSeccompProfile(
 	}
 	return seccompProfile, nil
 }
+
+func (d *defaultImpl) GetSelinuxProfile(
+	ctx context.Context, key types.NamespacedName,
+) (*selinuxprofileapi.SelinuxProfile, error) {
+	selinuxProfile := &selinuxprofileapi.SelinuxProfile{}
+	err := d.client.Get(ctx, key, selinuxProfile)
+	if err != nil {
+		return nil, fmt.Errorf("get selinux profile: %w", err)
+	}
+	return selinuxProfile, nil
+}


Test patch:
diff --git a/hack/ci/e2e-olm.sh b/hack/ci/e2e-olm.sh
index 0e867078e..819fb731e 100755
--- a/hack/ci/e2e-olm.sh
+++ b/hack/ci/e2e-olm.sh
@@ -23,74 +23,74 @@ BUNDLE_IMG=${REPO}/security-profiles-operator-bundle:v${GITHUB_SHA}
 CATALOG_IMG=${REPO}/security-profiles-operator-catalog:v${GITHUB_SHA}
 
 function build_and_push_spo() {
-        make image IMAGE=${IMG}
-        podman push --tls-verify=false ${IMG}
+    make image IMAGE=${IMG}
+    podman push --tls-verify=false ${IMG}
 }
 
 function build_and_push_packages() {
-        OPERATOR_MANIFEST=deploy/operator-ci.yaml
-
-        # Create a manifest with local image
-        pushd deploy/base
-        kustomize edit set image security-profiles-operator=$IMG
-        popd
-        kustomize build --reorder=none deploy/overlays/cluster -o ${OPERATOR_MANIFEST}
-
-        # this is a kludge, we need to make sure kustomize can be overwritten
-        rm -f build/kustomize
-
-        # create bundle, bundle image, push bundle using our manifest created earlier
-        make bundle BUNDLE_OPERATOR_MANIFEST=${OPERATOR_MANIFEST}
-        # GH CI workers have pretty limited CPU and won't be able to run SPO, OLM and cert-manager at the same time
-        sed -i '/cpu\:/d' bundle/manifests/security-profiles-operator.clusterserviceversion.yaml
-        make bundle-build BUNDLE_IMG=${BUNDLE_IMG}
-        podman push --tls-verify=false ${BUNDLE_IMG}
-
-        # create catalog image, push catalog
-        make catalog-build OPM_EXTRA_ARGS=" --skip-tls" BUNDLE_IMGS=${BUNDLE_IMG} CATALOG_IMG=${CATALOG_IMG}
-        podman push --tls-verify=false ${CATALOG_IMG}
+    OPERATOR_MANIFEST=deploy/operator-ci.yaml
+
+    # Create a manifest with local image
+    pushd deploy/base
+    kustomize edit set image security-profiles-operator=$IMG
+    popd
+    kustomize build --reorder=none deploy/overlays/cluster -o ${OPERATOR_MANIFEST}
+
+    # this is a kludge, we need to make sure kustomize can be overwritten
+    rm -f build/kustomize
+
+    # create bundle, bundle image, push bundle using our manifest created earlier
+    make bundle BUNDLE_OPERATOR_MANIFEST=${OPERATOR_MANIFEST}
+    # GH CI workers have pretty limited CPU and won't be able to run SPO, OLM and cert-manager at the same time
+    sed -i '/cpu\:/d' bundle/manifests/security-profiles-operator.clusterserviceversion.yaml
+    make bundle-build BUNDLE_IMG=${BUNDLE_IMG}
+    podman push --tls-verify=false ${BUNDLE_IMG}
+
+    # create catalog image, push catalog
+    make catalog-build OPM_EXTRA_ARGS=" --skip-tls" BUNDLE_IMGS=${BUNDLE_IMG} CATALOG_IMG=${CATALOG_IMG}
+    podman push --tls-verify=false ${CATALOG_IMG}
 }
 
 function deploy_olm() {
-        operator-sdk olm install --version ${OLM_VERSION} --timeout 6m
+    operator-sdk olm install --version ${OLM_VERSION} --timeout 6m
 }
 
 function deploy_spo() {
-        # cert-manager first. This should be done using dependencies in the
-        # future
-        kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.7.2/cert-manager.yaml
-        kubectl -ncert-manager wait --for condition=ready pod -l app.kubernetes.io/instance=cert-manager
-
-        # let's roll..
-        sed -i "s#quay.io/security-profiles-operator/security-profiles-operator-catalog:latest#${CATALOG_IMG}#g" examples/olm/install-resources.yaml
-        kubectl create -f examples/olm/install-resources.yaml
+    # cert-manager first. This should be done using dependencies in the
+    # future
+    kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.8.0/cert-manager.yaml
+    kubectl -ncert-manager wait --for condition=ready pod -l app.kubernetes.io/instance=cert-manager
+
+    # let's roll..
+    sed -i "s#quay.io/security-profiles-operator/security-profiles-operator-catalog:latest#${CATALOG_IMG}#g" examples/olm/install-resources.yaml
+    kubectl create -f examples/olm/install-resources.yaml
 }
 
 function check_spo_is_running() {
-        # Useful in case the CatalogSource is fubar. We retry several times
-        # because on transient errors (which are for some reason common even
-        # if the catalog is local) the pod gets restarted
-        for i in $(seq 1 5); do
-            kubectl -nolm wait --for=condition=ready pods -lolm.catalogSource=security-profiles-operator
-            catalog_logs=$(kubectl -nolm logs $(kubectl -nolm get pods --no-headers -lolm.catalogSource=security-profiles-operator | awk '{print $1}') 2>/dev/null)
-            if [[ -n "$catalog_logs" ]]; then
-                echo $catalog_logs
-                break
-            fi
-        done
-
-        # wait a bit for CSV to appear
-        # (jhrozek): I didn't find a useful condition or status to wait for..
-        # ..if only there was a way to check if ANY installedCSV is set..
-        sleep 30
-        CSV=$(kubectl -nsecurity-profiles-operator get sub security-profiles-operator-sub -ojsonpath='{.status.installedCSV}')
-        # wait for the CSV to be actually installed
-        kubectl -nsecurity-profiles-operator wait --for=jsonpath='{.status.phase}'=Succeeded csv $CSV
-
-        # wait for the operator to be ready
-        kubectl -nsecurity-profiles-operator wait --for=condition=ready pod -lname=security-profiles-operator
-        kubectl -nsecurity-profiles-operator wait --for=condition=ready pod -lname=security-profiles-operator-webhook
-        kubectl -nsecurity-profiles-operator wait --for=condition=ready pod -lname=spod
+    # Useful in case the CatalogSource is fubar. We retry several times
+    # because on transient errors (which are for some reason common even
+    # if the catalog is local) the pod gets restarted
+    for i in $(seq 1 5); do
+        kubectl -nolm wait --for=condition=ready pods -lolm.catalogSource=security-profiles-operator
+        catalog_logs=$(kubectl -nolm logs $(kubectl -nolm get pods --no-headers -lolm.catalogSource=security-profiles-operator | awk '{print $1}') 2>/dev/null)
+        if [[ -n "$catalog_logs" ]]; then
+            echo $catalog_logs
+            break
+        fi
+    done
+
+    # wait a bit for CSV to appear
+    # (jhrozek): I didn't find a useful condition or status to wait for..
+    # ..if only there was a way to check if ANY installedCSV is set..
+    sleep 30
+    CSV=$(kubectl -nsecurity-profiles-operator get sub security-profiles-operator-sub -ojsonpath='{.status.installedCSV}')
+    # wait for the CSV to be actually installed
+    kubectl -nsecurity-profiles-operator wait --for=jsonpath='{.status.phase}'=Succeeded csv $CSV
+
+    # wait for the operator to be ready
+    kubectl -nsecurity-profiles-operator wait --for=condition=ready pod -lname=security-profiles-operator
+    kubectl -nsecurity-profiles-operator wait --for=condition=ready pod -lname=security-profiles-operator-webhook
+    kubectl -nsecurity-profiles-operator wait --for=condition=ready pod -lname=spod
 }
 
 # The actual script begins here
diff --git a/internal/pkg/webhooks/binding/binding_test.go b/internal/pkg/webhooks/binding/binding_test.go
index c5dafa3b6..b28d37c68 100644
--- a/internal/pkg/webhooks/binding/binding_test.go
+++ b/internal/pkg/webhooks/binding/binding_test.go
@@ -36,6 +36,7 @@ import (
 	"sigs.k8s.io/security-profiles-operator/api/profilebinding/v1alpha1"
 	seccompprofileapi "sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1beta1"
 	secprofnodestatusv1alpha1 "sigs.k8s.io/security-profiles-operator/api/secprofnodestatus/v1alpha1"
+	selinuxprofileapi "sigs.k8s.io/security-profiles-operator/api/selinuxprofile/v1alpha2"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/webhooks/binding/bindingfakes"
 )
 
@@ -129,6 +130,44 @@ func TestHandle(t *testing.T) {
 				require.Len(t, resp.Patches, 1)
 			},
 		},
+		{ // selinux success pod changed
+			prepare: func(mock *bindingfakes.FakeImpl) {
+				mock.ListProfileBindingsReturns(&v1alpha1.ProfileBindingList{
+					Items: []v1alpha1.ProfileBinding{
+						{
+							Spec: v1alpha1.ProfileBindingSpec{
+								ProfileRef: v1alpha1.ProfileRef{
+									Kind: v1alpha1.ProfileBindingKindSelinuxProfile,
+								},
+							},
+						},
+					},
+				}, nil)
+				mock.DecodePodReturns(testPod.DeepCopy(), nil)
+				mock.GetSelinuxProfileReturns(&selinuxprofileapi.SelinuxProfile{
+					Status: selinuxprofileapi.SelinuxProfileStatus{
+						StatusBase: profilebasev1alpha1.StatusBase{
+							Status: "Installed",
+						},
+					},
+				}, nil)
+			},
+			request: admission.Request{
+				AdmissionRequest: admissionv1.AdmissionRequest{
+					Object: runtime.RawExtension{
+						Raw: func() []byte {
+							b, err := json.Marshal(testPod.DeepCopy())
+							require.Nil(t, err)
+							return b
+						}(),
+					},
+				},
+			},
+			assert: func(resp admission.Response) {
+				require.True(t, resp.AdmissionResponse.Allowed)
+				require.Len(t, resp.Patches, 1)
+			},
+		},
 		{ // success unsupported kind
 			prepare: func(mock *bindingfakes.FakeImpl) {
 				mock.ListProfileBindingsReturns(&v1alpha1.ProfileBindingList{
@@ -368,7 +407,7 @@ func TestHandle(t *testing.T) {
 		mock := &bindingfakes.FakeImpl{}
 		tc.prepare(mock)
 
-		binder := podSeccompBinder{impl: mock, log: logr.Discard()}
+		binder := podBinder{impl: mock, log: logr.Discard()}
 		resp := binder.Handle(context.Background(), tc.request)
 		tc.assert(resp)
 	}
diff --git a/test/e2e_test.go b/test/e2e_test.go
index 079a16aae..d0cafde61 100644
--- a/test/e2e_test.go
+++ b/test/e2e_test.go
@@ -32,7 +32,7 @@ import (
 )
 
 const (
-	certmanager       = "https://github.com/jetstack/cert-manager/releases/download/v1.7.2/cert-manager.yaml"
+	certmanager       = "https://github.com/jetstack/cert-manager/releases/download/v1.8.0/cert-manager.yaml"
 	manifest          = "deploy/operator.yaml"
 	namespaceManifest = "deploy/namespace-operator.yaml"
 	testNamespace     = "test-ns"
@@ -129,7 +129,11 @@ func (e *e2e) TestSecurityProfilesOperator() {
 	// TODO(jaosorior): Re-introduce this to the namespaced tests once we
 	// fix the issue with the certs.
 	e.Run("cluster-wide: Seccomp: Verify profile binding", func() {
-		e.testCaseProfileBinding(nodes)
+		e.testCaseSeccompProfileBinding(nodes)
+	})
+
+	e.Run("cluster-wide: Selinux: Verify profile binding", func() {
+		e.testCaseSelinuxProfileBinding(nodes)
 	})
 
 	e.Run("cluster-wide: Seccomp: Verify profile recording hook", func() {
diff --git a/test/tc_profilebindings_test.go b/test/tc_seccomp_profilebindings_test.go
similarity index 98%
rename from test/tc_profilebindings_test.go
rename to test/tc_seccomp_profilebindings_test.go
index de5cfe7f8..4ceec4ed0 100644
--- a/test/tc_profilebindings_test.go
+++ b/test/tc_seccomp_profilebindings_test.go
@@ -22,7 +22,7 @@ import (
 	"strings"
 )
 
-func (e *e2e) testCaseProfileBinding([]string) {
+func (e *e2e) testCaseSeccompProfileBinding([]string) {
 	e.seccompOnlyTestCase()
 
 	const exampleProfilePath = "examples/seccompprofile.yaml"
diff --git a/test/tc_selinux_profilebindings_test.go b/test/tc_selinux_profilebindings_test.go
new file mode 100644
index 000000000..0a2915d8c
--- /dev/null
+++ b/test/tc_selinux_profilebindings_test.go
@@ -0,0 +1,156 @@
+/*
+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 (
+	"fmt"
+	"strings"
+)
+
+func (e *e2e) testCaseSelinuxProfileBinding([]string) {
+	e.selinuxOnlyTestCase()
+	const selinuxTestProfileName = "testpolicy"
+	const selinuxBindingName = "profile-binding-selinux"
+	const testPodName = "selinux-profile-test-pod"
+	selinuxTestProfile := fmt.Sprintf(`
+apiVersion: security-profiles-operator.x-k8s.io/v1alpha2
+kind: SelinuxProfile
+metadata:
+  name: %s
+spec:
+  allow:
+    var_log_t:
+      dir:
+        - open
+        - read
+        - getattr
+        - lock
+        - search
+        - ioctl
+        - add_name
+        - remove_name
+        - write
+      file:
+        - getattr
+        - read
+        - write
+        - append
+        - ioctl
+        - lock
+        - map
+        - open
+        - create
+      sock_file:
+        - getattr
+        - read
+        - write
+        - append
+        - open
+`, selinuxTestProfileName)
+
+	selinuxBinding := fmt.Sprintf(`
+apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
+kind: ProfileBinding
+metadata:
+  name: %s
+spec:
+  profileRef:
+    kind: SelinuxProfile
+    name: %s
+  image: busybox:latest
+`, selinuxBindingName, selinuxTestProfileName)
+
+	testPod := fmt.Sprintf(`
+apiVersion: v1
+kind: Pod
+metadata:
+  name: %s
+spec:
+  initContainers:
+  - image: busybox:latest
+    name: errorloggerbinding
+    command: ["sh"]
+    args: ["-c", "echo \"Time: $(date). Some error info.\" >> /var/log/test.log || /bin/true"]
+    volumeMounts:
+    - name: varlog
+      mountPath: /var/log
+  containers:
+  - name: pauser
+    image: "gcr.io/google_containers/pause:latest"
+  restartPolicy: Never
+  volumes:
+  - name: varlog
+    hostPath:
+      path: /var/log
+      type: Directory
+`, testPodName)
+
+	e.logf("creating policy")
+	e.writeAndCreate(selinuxTestProfile, "selinuxProfile-test.yml")
+
+	// Let's wait for the policy to be processed
+	e.kubectl("wait", "--timeout", defaultSelinuxOpTimeout,
+		"--for", "condition=ready", "selinuxprofile", selinuxTestProfileName)
+	defer e.kubectl("delete", "selinuxprofile", selinuxTestProfileName)
+
+	e.logf("Creating test profile binding")
+	e.writeAndCreate(selinuxBinding, "selinuxPolicyBinding-test.yml")
+	defer e.kubectl("delete", "profilebinding", selinuxBindingName)
+
+	e.logf("Creating test pod")
+	e.writeAndCreate(testPod, "selinuxBindingPod-test.yml")
+	defer e.kubectl("delete", "pod", testPodName)
+
+	output := e.kubectl("get", "pod", testPodName)
+	for strings.Contains(output, "ContainerCreating") {
+		output = e.kubectl("get", "pod", testPodName)
+	}
+
+	e.waitFor("condition=ready", "pod", testPodName)
+
+	e.logf("the workload should not have errored")
+	log := e.kubectl("logs", testPodName, "-c", "errorloggerbinding")
+	e.Emptyf(log, "container should not have returned a 'Permissions Denied' error")
+
+	namespace := e.getCurrentContextNamespace(defaultNamespace)
+
+	e.logf("Getting selinux profile usage")
+	selinuxUsage := e.getSELinuxPolicyUsage(selinuxTestProfileName)
+
+	e.logf("Testing that pod has securityContext")
+	output = e.kubectl(
+		"get", "pod", testPodName,
+		"--output", "jsonpath={.spec.initContainers[0].securityContext.seLinuxOptions.type}",
+	)
+	e.Equal(selinuxUsage, output)
+
+	e.logf("Testing that profile binding has pod reference")
+	output = e.kubectl("get", "profilebinding", selinuxBindingName, "--output", "jsonpath={.status.activeWorkloads[0]}")
+	e.Equal(fmt.Sprintf("%s/%s", namespace, testPodName), output)
+	output = e.kubectl("get", "profilebinding", selinuxBindingName, "--output", "jsonpath={.metadata.finalizers[0]}")
+	e.Equal("active-workload-lock", output)
+
+	e.logf("Testing that profile has pod reference")
+	output = e.kubectl("get", "selinuxprofile", selinuxTestProfileName,
+		"--output", "jsonpath={.status.activeWorkloads[0]}")
+
+	e.Equal(fmt.Sprintf("%s/%s", namespace, testPodName), output)
+	output = e.kubectl("get", "selinuxprofile", selinuxTestProfileName,
+		"--output", "jsonpath={.metadata.finalizers[*]}")
+
+	e.Contains(output, "in-use-by-active-pods")
+}