Repository: kubernetes-sigs/security-profiles-operator
Pull request #138 (2020-10-01 17:20:44)

Remove support for ConfigMap seccomp profiles
Remove the ability to use ConfigMaps to define seccomp profiles, and instead only support the SeccompProfile custom resource.

<!--  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?

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/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
-->
/kind feature
/kind api-change

#### What this PR does / why we need it:

#### Which issue(s) this PR fixes:

<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.

Fixes #131

or

None
-->

#### Special notes for your reviewer:

#### 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
Seccomp profiles can now no longer be configured using the ConfigMap native resource, and instead may now only be defined using the provided SeccompProfile custom resource.
```


pjbgf: > `SeccompProfiles` right now are written under `<namespace>/custom-profiles` which doesn't feel right for "default" profiles, any thoughts on a better way to handle the directory structure? Should there be a `directory` field in the `spec` object, or some kind of annotation?

By removing `ConfigMap` support we can also remove the `custom-profiles`, which would then lead to have the same directory structure as we had before - nice and clean.
cmurphy: [**@pjbgf**](https://github.com/pjbgf) commented on [Oct 8, 2020, 12:32 PM PDT](https://github.com/kubernetes-sigs/security-profiles-operator/pull/138#issuecomment-705778265 "2020-10-08T19:32:09Z - Replied by Github Reply Comments"):
> > `SeccompProfiles` right now are written under `<namespace>/custom-profiles` which doesn't feel right for "default" profiles, any thoughts on a better way to handle the directory structure? Should there be a `directory` field in the `spec` object, or some kind of annotation?
> 
> By removing `ConfigMap` support we can also remove the `custom-profiles`, which would then lead to have the same directory structure as we had before - nice and clean.

It's not quite the same directory structure. The directory structure with ConfigMap is `<namespace>/<configmap name>/<configmap key name>`. With SeccompProfile the structure is `<namespace>/custom-profiles/<seccompprofile name>.json`. So removing `custom-profiles` moves the `.json` file one level up.
pjbgf: > It's not quite the same directory structure. The directory structure with ConfigMap is `<namespace>/<configmap name>/<configmap key name>`. With SeccompProfile the structure is `<namespace>/custom-profiles/<seccompprofile name>.json`. So removing `custom-profiles` moves the `.json` file one level up.

You are absolutely right, I completely disregarded the fact that each CRD will only store a single profile. 
Considering that, and connecting this to the "auto-apply" of profiles, I think we could have a way to define what workload we are talking about and use that within the directory structure. 

For example, if we had a new field called (target-workload? target-image?) which defined what image this profile is for, we could then build the entire path: `<namespace>/<workload>/<seccompprofile name>.json`

kube-proxy:
`kube-system/k8s.gcr.io/kube-proxy/kube-proxy-v1.18.json`

nginx:
`default/nginx/nginx-1.19.1.json`

what are your thoughts @cmurphy  @saschagrunert @hasheddan ?
cmurphy: Sounds fine to me :+1: 
cmurphy: So you're envisioning that there could be multiple profiles per workload, e.g. `default/nginx/nginx-1.19.1.json` and `default/nginx/nginx-1.18.0.json`?
pjbgf: > So you're envisioning that there could be multiple profiles per workload, e.g. `default/nginx/nginx-1.19.1.json` and `default/nginx/nginx-1.18.0.json`?

Not always, but it is highly likely that workloads in active development would experience a change in their seccomp profile as new features start (or stop) depending on other system calls. Therefore, restricting profile to workload as `1:N` a mapping may break workloads.

Conversely, mapping them as `1:1` would lead to too many profiles. The auto apply feature will flash this out a bit further, but as a first thought I was thinking that the map could be based on `target-workload-image` + `target-tag regex`, so it is flexible enough to cover both `1:N` and `1:1` mappings.
saschagrunert: The general direction sounds good to me. We should block the case where people can overwrite the profiles form other namespaces than they're permitted to act on.
cmurphy: Okay the latest set of revisions fully remove `ConfigMap` support, including adding a `TargetWorkload` attribute to the CRD
to help with grouping profiles and create a consistent directory structure as discussed.

Fully pivoting from ConfigMap to SeccompProfile is a little tricky, because 1) there is a very slight delay between registering a new CRD with the kubernetes API and it being available to POST a new resource to, so if both the CRD and the default SeccompProfile resources are included in the deployment manifest, creating the default profiles might fail and usually failed for me, and 2) there is a chicken/egg problem with creating the seccomp-operator container which reconciles the seccomp-operator profile, because the controller can't start without the profile already there. To fix both of these, I added another init container which reads a special ConfigMap which contains the default SeccompProfile resources, serializes the seccomp-operator profile to disk, and creates the default SeccompProfile resources which the main container will later reconcile. It seems like init containers do not need to adhere to the pod profile, at least in the annotation - not sure if that's intentional but it was advantageous here. The new init container is based on an external dockerhub image and is just a proof of concept, but if this approach is acceptable I can transform it into its own app so that it doesn't rely on an external image.

I will also update the usage guide, and will need to double check whether this works properly with the namespaced deployment. This will also need to be rebased on top of #139.
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/138#" title="Author self-approved">cmurphy</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: @cmurphy: PR needs rebase.

<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.
</details>
k8s-ci-robot: @cmurphy: The following test **failed**, say `/retest` to rerun all failed tests:

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

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-verify | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-verify/1317234620139311104) | `/test pull-security-profiles-operator-verify`
pull-security-profiles-operator-build-image | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build-image/1317234620181254144) | `/test pull-security-profiles-operator-build-image`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-verify | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-verify/1317234620139311104) | `/test pull-security-profiles-operator-verify`
pull-security-profiles-operator-build-image | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build-image/1317234620181254144) | `/test pull-security-profiles-operator-build-image`
pull-security-profiles-operator-test-e2e | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1317234620227391488) | `/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=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-verify | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-verify/1317234620139311104) | `/test pull-security-profiles-operator-verify`
pull-security-profiles-operator-build-image | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build-image/1317234620181254144) | `/test pull-security-profiles-operator-build-image`
pull-security-profiles-operator-test-e2e | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1317234620227391488) | `/test pull-security-profiles-operator-test-e2e`
pull-security-profiles-operator-build | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build/1317234620122533888) | `/test pull-security-profiles-operator-build`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-verify | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-verify/1317234620139311104) | `/test pull-security-profiles-operator-verify`
pull-security-profiles-operator-build-image | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build-image/1317234620181254144) | `/test pull-security-profiles-operator-build-image`
pull-security-profiles-operator-test-e2e | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1317234620227391488) | `/test pull-security-profiles-operator-test-e2e`
pull-security-profiles-operator-build | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build/1317234620122533888) | `/test pull-security-profiles-operator-build`
pull-security-profiles-operator-test-unit | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-unit/1317234620160282624) | `/test pull-security-profiles-operator-test-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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 -->
cmurphy: Fixed the namespaced deployment, reimplemented the init containers in Go, and fixed nits. Still needs a rebase/rename and docs.
saschagrunert: > Fixed the namespaced deployment, reimplemented the init containers in Go, and fixed nits. Still needs a rebase/rename and docs.

Thanks! From my side this looks good so far. Sorry for the rebase, I hope this does not introduce to much trouble on your side.
k8s-ci-robot: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-build-image | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build-image/1317234620181254144) | `/test pull-security-profiles-operator-build-image`
pull-security-profiles-operator-test-e2e | fa710dc8b92c30b2fea7ae377bf10ddf44bded4d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1317234620227391488) | `/test pull-security-profiles-operator-test-e2e`
pull-security-profiles-operator-verify | 4b4f10088d934cfc6a6e236334400b865301c601 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-verify/1318277163970990080) | `/test pull-security-profiles-operator-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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 -->
cmurphy: /retest
k8s-ci-robot: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

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

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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 -->
cmurphy: This is pretty much ready now. The verify test was failing on the generated namespace-operator.yaml manifest though I couldn't reproduce locally. I can clean up the fixup commits after you guys have had a chance to take a look.
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/138#" title="Author self-approved">cmurphy</a>*, *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/138#pullrequestreview-512400362" 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":[]} -->
k8s-ci-robot: New changes are detected. LGTM label has been removed.
k8s-ci-robot: @cmurphy: The following test **failed**, say `/retest` to rerun all failed tests:

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

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: @cmurphy: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | 555279894d52643ae27a5de5c9ddf52c2e1984d1 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1318987500751949824) | `/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=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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 -->
cmurphy: /retest
cmurphy: @pjbgf thanks for the hint, I didn't notice that that ConfigMap was special and not annotated. Keeping that will be much easier.

> and the nit from @saschagrunert 

I think I already addressed the filemode nit but let me know if I missed something else.
k8s-ci-robot: @cmurphy: PR needs rebase.

<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.
</details>
k8s-ci-robot: @cmurphy: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1320907764662276096) | `/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=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1320907764662276096) | `/test pull-security-profiles-operator-test-e2e`
pull-security-profiles-operator-build | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build/1320907764553224192) | `/test pull-security-profiles-operator-build`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1320907764662276096) | `/test pull-security-profiles-operator-test-e2e`
pull-security-profiles-operator-build | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build/1320907764553224192) | `/test pull-security-profiles-operator-build`
pull-security-profiles-operator-verify | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-verify/1320907764590972928) | `/test pull-security-profiles-operator-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1320907764662276096) | `/test pull-security-profiles-operator-test-e2e`
pull-security-profiles-operator-build | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build/1320907764553224192) | `/test pull-security-profiles-operator-build`
pull-security-profiles-operator-verify | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-verify/1320907764590972928) | `/test pull-security-profiles-operator-verify`
pull-security-profiles-operator-build-image | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build-image/1320907764641304576) | `/test pull-security-profiles-operator-build-image`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: @cmurphy: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1320907764662276096) | `/test pull-security-profiles-operator-test-e2e`
pull-security-profiles-operator-build | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build/1320907764553224192) | `/test pull-security-profiles-operator-build`
pull-security-profiles-operator-verify | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-verify/1320907764590972928) | `/test pull-security-profiles-operator-verify`
pull-security-profiles-operator-build-image | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-build-image/1320907764641304576) | `/test pull-security-profiles-operator-build-image`
pull-security-profiles-operator-test-unit | 21574f1541668e5a41552686d24ebafdb4fd0f68 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-unit/1320907764616138752) | `/test pull-security-profiles-operator-test-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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 -->
cmurphy: Updated to remove the unnecessary init container. Added the following to the main commit message, will copy here for visibility:

```
    This requires modifications to the default profiles. The
    security-profiles-operator profile remains a ConfigMap because the
    controller's init container needs to mount and copy it in order to
    bootstrap the controller, but the special annotation is no longer
    needed. This has the consequence that this particular profile cannot be
    managed as a SeccompProfile resource, it can only be managed as a
    ConfigMap. The nginx-1.19.1 profile is transformed to a SeccompProfile
    resource. Since there is a slight delay between a CRD being registered
    with the API server and it being available to POST to, the CRD
    definition is split into its own manifest so it can be deployed first
    before the default profiles.
```
k8s-ci-robot: @cmurphy: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-security-profiles-operator-test-e2e | 3ea0a5b73bc38d1eb41174773583849f46090f9d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/138/pull-security-profiles-operator-test-e2e/1320908879155957760) | `/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=138). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Acmurphy). 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: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/138#" title="Author self-approved">cmurphy</a>*, *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/138#pullrequestreview-517431148" 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":[]} -->
saschagrunert: Ah there seems an issue in the e2e test:
```
I1027 02:08:39.689955    6163 suite_test.go:164]  "msg"="Verifying security-profiles-operator-profile profile on node so-e2e-1603764250-worker"  
    tc_default_profiles_test.go:82: 
        	Error Trace:	tc_default_profiles_test.go:82
        	            				tc_default_profiles_test.go:53
        	            				e2e_test.go:62
        	Error:      	Expected nil, but got: &errors.errorString{s:"invalid syntax"}
        	Test:       	TestSuite/TestSeccompOperator
```
k8s-ci-robot: New changes are detected. LGTM label has been removed.
cmurphy: [**@saschagrunert**](https://github.com/saschagrunert) commented on [Oct 27, 2020, 1:05 AM PDT](https://github.com/kubernetes-sigs/security-profiles-operator/pull/138#issuecomment-717063154 "2020-10-27T08:05:59Z - Replied by Github Reply Comments"):
> Ah there seems an issue in the e2e test:

Oops, pushed a little too soon. Should be fixed now.
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/138#" title="Author self-approved">cmurphy</a>*, *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/138#pullrequestreview-518083989" title="LGTM">pjbgf</a>*, *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/138#pullrequestreview-517431148" 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)~~ [pjbgf,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":[]} -->
pjbgf: Based on the thread, I don't think there is anything else holding this to be merged.

/unhold

Patch:
diff --git a/Makefile b/Makefile
index eafc5cd43..456e46031 100644
--- a/Makefile
+++ b/Makefile
@@ -140,7 +140,7 @@ test-e2e: ## Run the end-to-end tests
 
 # Generate CRD manifest
 manifests:
-	$(GO) run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen $(CRD_OPTIONS) paths="./api/..." output:crd:stdout > deploy/base/crd.yaml
+	$(GO) run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen $(CRD_OPTIONS) paths="./api/..." output:crd:stdout > deploy/crd.yaml
 
 # Generate deepcopy code
 generate:
diff --git a/api/v1alpha1/seccompprofile_types.go b/api/v1alpha1/seccompprofile_types.go
index 30e0c43cf..260e24610 100644
--- a/api/v1alpha1/seccompprofile_types.go
+++ b/api/v1alpha1/seccompprofile_types.go
@@ -23,6 +23,10 @@ import (
 
 // SeccompProfileSpec defines the desired state of SeccompProfile.
 type SeccompProfileSpec struct {
+	// the type of application workload for which the profile is targeted. Will be a subdirectory in the profile path on disk.
+	//nolint:lll
+	TargetWorkload string `json:"targetWorkload"`
+
 	// Properties from containers/common/pkg/seccomp.Seccomp type
 
 	// the default action for seccomp
diff --git a/cmd/security-profiles-operator/main.go b/cmd/security-profiles-operator/main.go
index 0e1183abe..0f7888aed 100644
--- a/cmd/security-profiles-operator/main.go
+++ b/cmd/security-profiles-operator/main.go
@@ -25,7 +25,7 @@ import (
 	"k8s.io/klog/v2/klogr"
 	ctrl "sigs.k8s.io/controller-runtime"
 
-	operator_v1alpha1 "sigs.k8s.io/security-profiles-operator/api/v1alpha1"
+	"sigs.k8s.io/security-profiles-operator/api/v1alpha1"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/controllers/profile"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/version"
@@ -115,7 +115,7 @@ func run(*cli.Context) error {
 		return errors.Wrap(err, "create manager")
 	}
 
-	if err := operator_v1alpha1.AddToScheme(mgr.GetScheme()); err != nil {
+	if err := v1alpha1.AddToScheme(mgr.GetScheme()); err != nil {
 		return errors.Wrap(err, "add core operator APIs to scheme")
 	}
 
diff --git a/deploy/base/crd.yaml b/deploy/base/crd.yaml
index e09db0341..944a3ef9b 100644
--- a/deploy/base/crd.yaml
+++ b/deploy/base/crd.yaml
@@ -146,8 +146,12 @@ spec:
                   - names
                   type: object
                 type: array
+              targetWorkload:
+                description: the type of application workload for which the profile is targeted. Will be a subdirectory in the profile path on disk.
+                type: string
             required:
             - defaultAction
+            - targetWorkload
             type: object
           status:
             description: SeccompProfileStatus contains the host path of the deployed SeccompProfile.
diff --git a/deploy/base/kustomization.yaml b/deploy/base/kustomization.yaml
index bc41ba9f6..8dd03c257 100644
--- a/deploy/base/kustomization.yaml
+++ b/deploy/base/kustomization.yaml
@@ -19,16 +19,12 @@ commonLabels:
 resources:
   - rbac.yaml
   - operator.yaml
-  - crd.yaml
+  - profiles/nginx-1.19.1.yaml
 
 configMapGenerator:
   - name: security-profiles-operator-profile
     files:
       - profiles/security-profiles-operator.json
 
-  - name: default-profiles
-    files:
-      - profiles/nginx-1.19.1.json
-
 generatorOptions:
   disableNameSuffixHash: true
diff --git a/deploy/base/profiles/nginx-1.19.1.json b/deploy/base/profiles/nginx-1.19.1.json
deleted file mode 100644
index d2b02b344..000000000
--- a/deploy/base/profiles/nginx-1.19.1.json
+++ /dev/null
@@ -1,109 +0,0 @@
-{
-  "defaultAction": "SCMP_ACT_ERRNO",
-  "architectures":[
-      "SCMP_ARCH_X86_64",
-      "SCMP_ARCH_X86",
-      "SCMP_ARCH_X32"
-  ],
-  "syscalls": [
-    {
-      "names": [
-        "accept4",
-        "access",
-        "arch_prctl",
-        "bind",
-        "brk",
-        "capget",
-        "capset",
-        "chdir",
-        "chown",
-        "clone",
-        "close",
-        "connect",
-        "dup2",
-        "epoll_create",
-        "epoll_ctl",
-        "epoll_pwait",
-        "epoll_wait",
-        "eventfd2",
-        "execve",
-        "exit",
-        "exit_group",
-        "faccessat",
-        "fadvise64",
-        "fchdir",
-        "fchown",
-        "fcntl",
-        "fgetxattr",
-        "fsetxattr",
-        "fstat",
-        "fstatfs",
-        "futex",
-        "getcwd",
-        "getdents",
-        "getdents64",
-        "getegid",
-        "geteuid",
-        "getgid",
-        "getpid",
-        "getppid",
-        "getrlimit",
-        "getuid",
-        "ioctl",
-        "io_setup",
-        "listen",
-        "lseek",
-        "mkdir",
-        "mmap",
-        "mprotect",
-        "munmap",
-        "nanosleep",
-        "newfstatat",
-        "open",
-        "openat",
-        "pipe",
-        "prctl",
-        "pread64",
-        "prlimit64",
-        "pwrite64",
-        "read",
-        "recvfrom",
-        "recvmsg",
-        "rename",
-        "rt_sigaction",
-        "rt_sigprocmask",
-        "rt_sigreturn",
-        "rt_sigsuspend",
-        "sched_getaffinity",
-        "seccomp",
-        "sendfile",
-        "sendmsg",
-        "setgid",
-        "setgroups",
-        "setitimer",
-        "set_robust_list",
-        "setsockopt",
-        "set_tid_address",
-        "setuid",
-        "sigaltstack",
-        "socket",
-        "socketpair",
-        "stat",
-        "statfs",
-        "sysinfo",
-        "umask",
-        "uname",
-        "unlink",
-        "utimensat",
-        "wait4",
-        "write",
-        "writev"
-      ],
-      "action": "SCMP_ACT_ALLOW",
-      "args": [],
-      "comment": "",
-      "includes": {},
-      "excludes": {}
-    }
-  ]
-}
\ No newline at end of file
diff --git a/deploy/base/profiles/nginx-1.19.1.yaml b/deploy/base/profiles/nginx-1.19.1.yaml
new file mode 100644
index 000000000..32e02bdd6
--- /dev/null
+++ b/deploy/base/profiles/nginx-1.19.1.yaml
@@ -0,0 +1,106 @@
+---
+apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
+kind: SeccompProfile
+metadata:
+  name: nginx-1.19.1
+  namespace: security-profiles-operator
+spec:
+  defaultAction: SCMP_ACT_ERRNO
+  targetWorkload: default-profiles
+  architectures:
+  - SCMP_ARCH_X86_64
+  - SCMP_ARCH_X86
+  - SCMP_ARCH_X32
+  syscalls:
+  - names:
+    - accept4
+    - access
+    - arch_prctl
+    - bind
+    - brk
+    - capget
+    - capset
+    - chdir
+    - chown
+    - clone
+    - close
+    - connect
+    - dup2
+    - epoll_create
+    - epoll_ctl
+    - epoll_pwait
+    - epoll_wait
+    - eventfd2
+    - execve
+    - exit
+    - exit_group
+    - faccessat
+    - fadvise64
+    - fchdir
+    - fchown
+    - fcntl
+    - fgetxattr
+    - fsetxattr
+    - fstat
+    - fstatfs
+    - futex
+    - getcwd
+    - getdents
+    - getdents64
+    - getegid
+    - geteuid
+    - getgid
+    - getpid
+    - getppid
+    - getrlimit
+    - getuid
+    - ioctl
+    - io_setup
+    - listen
+    - lseek
+    - mkdir
+    - mmap
+    - mprotect
+    - munmap
+    - nanosleep
+    - newfstatat
+    - open
+    - openat
+    - pipe
+    - prctl
+    - pread64
+    - prlimit64
+    - pwrite64
+    - read
+    - recvfrom
+    - recvmsg
+    - rename
+    - rt_sigaction
+    - rt_sigprocmask
+    - rt_sigreturn
+    - rt_sigsuspend
+    - sched_getaffinity
+    - seccomp
+    - sendfile
+    - sendmsg
+    - setgid
+    - setgroups
+    - setitimer
+    - set_robust_list
+    - setsockopt
+    - set_tid_address
+    - setuid
+    - sigaltstack
+    - socket
+    - socketpair
+    - stat
+    - statfs
+    - sysinfo
+    - umask
+    - uname
+    - unlink
+    - utimensat
+    - wait4
+    - write
+    - writev
+    action: SCMP_ACT_ALLOW
diff --git a/deploy/crd.yaml b/deploy/crd.yaml
new file mode 100644
index 000000000..944a3ef9b
--- /dev/null
+++ b/deploy/crd.yaml
@@ -0,0 +1,172 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.2.4
+  creationTimestamp: null
+  name: seccompprofiles.security-profiles-operator.x-k8s.io
+spec:
+  group: security-profiles-operator.x-k8s.io
+  names:
+    kind: SeccompProfile
+    listKind: SeccompProfileList
+    plural: seccompprofiles
+    shortNames:
+    - sp
+    singular: seccompprofile
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: SeccompProfile is a cluster level specification for a seccomp profile. See https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#seccomp
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: SeccompProfileSpec defines the desired state of SeccompProfile.
+            properties:
+              architectures:
+                description: the architecture used for system calls
+                items:
+                  enum:
+                  - SCMP_ARCH_X86
+                  - SCMP_ARCH_X86_64
+                  - SCMP_ARCH_X32
+                  - SCMP_ARCH_ARM
+                  - SCMP_ARCH_AARCH64
+                  - SCMP_ARCH_MIPS
+                  - SCMP_ARCH_MIPS64
+                  - SCMP_ARCH_MIPS64N32
+                  - SCMP_ARCH_MIPSEL
+                  - SCMP_ARCH_MIPSEL64
+                  - SCMP_ARCH_MIPSEL64N32
+                  - SCMP_ARCH_PPC
+                  - SCMP_ARCH_PPC64
+                  - SCMP_ARCH_PPC64LE
+                  - SCMP_ARCH_S390
+                  - SCMP_ARCH_S390X
+                  - SCMP_ARCH_PARISC
+                  - SCMP_ARCH_PARISC64
+                  - SCMP_ARCH_RISCV64
+                  type: string
+                type: array
+              defaultAction:
+                description: the default action for seccomp
+                enum:
+                - SCMP_ACT_KILL
+                - SCMP_ACT_KILL_PROCESS
+                - SCMP_ACT_KILL_THREAD
+                - SCMP_ACT_TRAP
+                - SCMP_ACT_ERRNO
+                - SCMP_ACT_TRACE
+                - SCMP_ACT_ALLOW
+                - SCMP_ACT_LOG
+                type: string
+              flags:
+                description: list of flags to use with seccomp(2)
+                items:
+                  enum:
+                  - SECCOMP_FILTER_FLAG_TSYNC
+                  - SECCOMP_FILTER_FLAG_LOG
+                  - SECCOMP_FILTER_FLAG_SPEC_ALLOW
+                  type: string
+                type: array
+              syscalls:
+                description: match a syscall in seccomp. While this property is OPTIONAL, some values of defaultAction are not useful without syscalls entries. For example, if defaultAction is SCMP_ACT_KILL and syscalls is empty or unset, the kernel will kill the container process on its first syscall
+                items:
+                  description: Syscall defines a syscall in seccomp.
+                  properties:
+                    action:
+                      description: the action for seccomp rules
+                      enum:
+                      - SCMP_ACT_KILL
+                      - SCMP_ACT_KILL_PROCESS
+                      - SCMP_ACT_KILL_THREAD
+                      - SCMP_ACT_TRAP
+                      - SCMP_ACT_ERRNO
+                      - SCMP_ACT_TRACE
+                      - SCMP_ACT_ALLOW
+                      - SCMP_ACT_LOG
+                      type: string
+                    args:
+                      description: the specific syscall in seccomp
+                      items:
+                        description: Arg defines the specific syscall in seccomp.
+                        properties:
+                          index:
+                            description: the index for syscall arguments in seccomp
+                            minimum: 0
+                            type: integer
+                          op:
+                            description: the operator for syscall arguments in seccomp
+                            enum:
+                            - SCMP_CMP_NE
+                            - SCMP_CMP_LT
+                            - SCMP_CMP_LE
+                            - SCMP_CMP_EQ
+                            - SCMP_CMP_GE
+                            - SCMP_CMP_GT
+                            - SCMP_CMP_MASKED_EQ
+                            type: string
+                          value:
+                            description: the value for syscall arguments in seccomp
+                            format: int64
+                            minimum: 0
+                            type: integer
+                          valueTwo:
+                            description: the value for syscall arguments in seccomp
+                            format: int64
+                            minimum: 0
+                            type: integer
+                        required:
+                        - index
+                        - op
+                        type: object
+                      maxItems: 6
+                      type: array
+                    errnoRet:
+                      description: the errno return code to use. Some actions like SCMP_ACT_ERRNO and SCMP_ACT_TRACE allow to specify the errno code to return
+                      type: string
+                    names:
+                      description: the names of the syscalls
+                      items:
+                        type: string
+                      type: array
+                  required:
+                  - action
+                  - names
+                  type: object
+                type: array
+              targetWorkload:
+                description: the type of application workload for which the profile is targeted. Will be a subdirectory in the profile path on disk.
+                type: string
+            required:
+            - defaultAction
+            - targetWorkload
+            type: object
+          status:
+            description: SeccompProfileStatus contains the host path of the deployed SeccompProfile.
+            properties:
+              path:
+                type: string
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/deploy/namespace-operator.yaml b/deploy/namespace-operator.yaml
index 3b961cc8f..f66d86a40 100644
--- a/deploy/namespace-operator.yaml
+++ b/deploy/namespace-operator.yaml
@@ -191,174 +191,113 @@ spec:
           name: security-profiles-operator-profile
         name: profile-configmap-volume
 ---
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
+apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
+kind: SeccompProfile
 metadata:
-  annotations:
-    controller-gen.kubebuilder.io/version: v0.2.4
-  creationTimestamp: null
   labels:
     app: security-profiles-operator
-  name: seccompprofiles.security-profiles-operator.x-k8s.io
+  name: nginx-1.19.1
+  namespace: NS_REPLACE
 spec:
-  group: security-profiles-operator.x-k8s.io
-  names:
-    kind: SeccompProfile
-    listKind: SeccompProfileList
-    plural: seccompprofiles
-    shortNames:
-    - sp
-    singular: seccompprofile
-  scope: Namespaced
-  versions:
-  - name: v1alpha1
-    schema:
-      openAPIV3Schema:
-        description: SeccompProfile is a cluster level specification for a seccomp profile. See https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#seccomp
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: SeccompProfileSpec defines the desired state of SeccompProfile.
-            properties:
-              architectures:
-                description: the architecture used for system calls
-                items:
-                  enum:
-                  - SCMP_ARCH_X86
-                  - SCMP_ARCH_X86_64
-                  - SCMP_ARCH_X32
-                  - SCMP_ARCH_ARM
-                  - SCMP_ARCH_AARCH64
-                  - SCMP_ARCH_MIPS
-                  - SCMP_ARCH_MIPS64
-                  - SCMP_ARCH_MIPS64N32
-                  - SCMP_ARCH_MIPSEL
-                  - SCMP_ARCH_MIPSEL64
-                  - SCMP_ARCH_MIPSEL64N32
-                  - SCMP_ARCH_PPC
-                  - SCMP_ARCH_PPC64
-                  - SCMP_ARCH_PPC64LE
-                  - SCMP_ARCH_S390
-                  - SCMP_ARCH_S390X
-                  - SCMP_ARCH_PARISC
-                  - SCMP_ARCH_PARISC64
-                  - SCMP_ARCH_RISCV64
-                  type: string
-                type: array
-              defaultAction:
-                description: the default action for seccomp
-                enum:
-                - SCMP_ACT_KILL
-                - SCMP_ACT_KILL_PROCESS
-                - SCMP_ACT_KILL_THREAD
-                - SCMP_ACT_TRAP
-                - SCMP_ACT_ERRNO
-                - SCMP_ACT_TRACE
-                - SCMP_ACT_ALLOW
-                - SCMP_ACT_LOG
-                type: string
-              flags:
-                description: list of flags to use with seccomp(2)
-                items:
-                  enum:
-                  - SECCOMP_FILTER_FLAG_TSYNC
-                  - SECCOMP_FILTER_FLAG_LOG
-                  - SECCOMP_FILTER_FLAG_SPEC_ALLOW
-                  type: string
-                type: array
-              syscalls:
-                description: match a syscall in seccomp. While this property is OPTIONAL, some values of defaultAction are not useful without syscalls entries. For example, if defaultAction is SCMP_ACT_KILL and syscalls is empty or unset, the kernel will kill the container process on its first syscall
-                items:
-                  description: Syscall defines a syscall in seccomp.
-                  properties:
-                    action:
-                      description: the action for seccomp rules
-                      enum:
-                      - SCMP_ACT_KILL
-                      - SCMP_ACT_KILL_PROCESS
-                      - SCMP_ACT_KILL_THREAD
-                      - SCMP_ACT_TRAP
-                      - SCMP_ACT_ERRNO
-                      - SCMP_ACT_TRACE
-                      - SCMP_ACT_ALLOW
-                      - SCMP_ACT_LOG
-                      type: string
-                    args:
-                      description: the specific syscall in seccomp
-                      items:
-                        description: Arg defines the specific syscall in seccomp.
-                        properties:
-                          index:
-                            description: the index for syscall arguments in seccomp
-                            minimum: 0
-                            type: integer
-                          op:
-                            description: the operator for syscall arguments in seccomp
-                            enum:
-                            - SCMP_CMP_NE
-                            - SCMP_CMP_LT
-                            - SCMP_CMP_LE
-                            - SCMP_CMP_EQ
-                            - SCMP_CMP_GE
-                            - SCMP_CMP_GT
-                            - SCMP_CMP_MASKED_EQ
-                            type: string
-                          value:
-                            description: the value for syscall arguments in seccomp
-                            format: int64
-                            minimum: 0
-                            type: integer
-                          valueTwo:
-                            description: the value for syscall arguments in seccomp
-                            format: int64
-                            minimum: 0
-                            type: integer
-                        required:
-                        - index
-                        - op
-                        type: object
-                      maxItems: 6
-                      type: array
-                    errnoRet:
-                      description: the errno return code to use. Some actions like SCMP_ACT_ERRNO and SCMP_ACT_TRACE allow to specify the errno code to return
-                      type: string
-                    names:
-                      description: the names of the syscalls
-                      items:
-                        type: string
-                      type: array
-                  required:
-                  - action
-                  - names
-                  type: object
-                type: array
-            required:
-            - defaultAction
-            type: object
-          status:
-            description: SeccompProfileStatus contains the host path of the deployed SeccompProfile.
-            properties:
-              path:
-                type: string
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
+  architectures:
+  - SCMP_ARCH_X86_64
+  - SCMP_ARCH_X86
+  - SCMP_ARCH_X32
+  defaultAction: SCMP_ACT_ERRNO
+  syscalls:
+  - action: SCMP_ACT_ALLOW
+    names:
+    - accept4
+    - access
+    - arch_prctl
+    - bind
+    - brk
+    - capget
+    - capset
+    - chdir
+    - chown
+    - clone
+    - close
+    - connect
+    - dup2
+    - epoll_create
+    - epoll_ctl
+    - epoll_pwait
+    - epoll_wait
+    - eventfd2
+    - execve
+    - exit
+    - exit_group
+    - faccessat
+    - fadvise64
+    - fchdir
+    - fchown
+    - fcntl
+    - fgetxattr
+    - fsetxattr
+    - fstat
+    - fstatfs
+    - futex
+    - getcwd
+    - getdents
+    - getdents64
+    - getegid
+    - geteuid
+    - getgid
+    - getpid
+    - getppid
+    - getrlimit
+    - getuid
+    - ioctl
+    - io_setup
+    - listen
+    - lseek
+    - mkdir
+    - mmap
+    - mprotect
+    - munmap
+    - nanosleep
+    - newfstatat
+    - open
+    - openat
+    - pipe
+    - prctl
+    - pread64
+    - prlimit64
+    - pwrite64
+    - read
+    - recvfrom
+    - recvmsg
+    - rename
+    - rt_sigaction
+    - rt_sigprocmask
+    - rt_sigreturn
+    - rt_sigsuspend
+    - sched_getaffinity
+    - seccomp
+    - sendfile
+    - sendmsg
+    - setgid
+    - setgroups
+    - setitimer
+    - set_robust_list
+    - setsockopt
+    - set_tid_address
+    - setuid
+    - sigaltstack
+    - socket
+    - socketpair
+    - stat
+    - statfs
+    - sysinfo
+    - umask
+    - uname
+    - unlink
+    - utimensat
+    - wait4
+    - write
+    - writev
+  targetWorkload: default-profiles
 ---
 apiVersion: v1
 data:
@@ -436,124 +375,3 @@ metadata:
     app: security-profiles-operator
   name: security-profiles-operator-profile
   namespace: security-profiles-operator
----
-apiVersion: v1
-data:
-  nginx-1.19.1.json: |-
-    {
-      "defaultAction": "SCMP_ACT_ERRNO",
-      "architectures":[
-          "SCMP_ARCH_X86_64",
-          "SCMP_ARCH_X86",
-          "SCMP_ARCH_X32"
-      ],
-      "syscalls": [
-        {
-          "names": [
-            "accept4",
-            "access",
-            "arch_prctl",
-            "bind",
-            "brk",
-            "capget",
-            "capset",
-            "chdir",
-            "chown",
-            "clone",
-            "close",
-            "connect",
-            "dup2",
-            "epoll_create",
-            "epoll_ctl",
-            "epoll_pwait",
-            "epoll_wait",
-            "eventfd2",
-            "execve",
-            "exit",
-            "exit_group",
-            "faccessat",
-            "fadvise64",
-            "fchdir",
-            "fchown",
-            "fcntl",
-            "fgetxattr",
-            "fsetxattr",
-            "fstat",
-            "fstatfs",
-            "futex",
-            "getcwd",
-            "getdents",
-            "getdents64",
-            "getegid",
-            "geteuid",
-            "getgid",
-            "getpid",
-            "getppid",
-            "getrlimit",
-            "getuid",
-            "ioctl",
-            "io_setup",
-            "listen",
-            "lseek",
-            "mkdir",
-            "mmap",
-            "mprotect",
-            "munmap",
-            "nanosleep",
-            "newfstatat",
-            "open",
-            "openat",
-            "pipe",
-            "prctl",
-            "pread64",
-            "prlimit64",
-            "pwrite64",
-            "read",
-            "recvfrom",
-            "recvmsg",
-            "rename",
-            "rt_sigaction",
-            "rt_sigprocmask",
-            "rt_sigreturn",
-            "rt_sigsuspend",
-            "sched_getaffinity",
-            "seccomp",
-            "sendfile",
-            "sendmsg",
-            "setgid",
-            "setgroups",
-            "setitimer",
-            "set_robust_list",
-            "setsockopt",
-            "set_tid_address",
-            "setuid",
-            "sigaltstack",
-            "socket",
-            "socketpair",
-            "stat",
-            "statfs",
-            "sysinfo",
-            "umask",
-            "uname",
-            "unlink",
-            "utimensat",
-            "wait4",
-            "write",
-            "writev"
-          ],
-          "action": "SCMP_ACT_ALLOW",
-          "args": [],
-          "comment": "",
-          "includes": {},
-          "excludes": {}
-        }
-      ]
-    }
-kind: ConfigMap
-metadata:
-  annotations:
-    seccomp.security.kubernetes.io/profile: "true"
-  labels:
-    app: security-profiles-operator
-  name: default-profiles
-  namespace: NS_REPLACE
diff --git a/deploy/operator.yaml b/deploy/operator.yaml
index d794b4924..92f317ec5 100644
--- a/deploy/operator.yaml
+++ b/deploy/operator.yaml
@@ -189,174 +189,113 @@ spec:
           name: security-profiles-operator-profile
         name: profile-configmap-volume
 ---
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
+apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
+kind: SeccompProfile
 metadata:
-  annotations:
-    controller-gen.kubebuilder.io/version: v0.2.4
-  creationTimestamp: null
   labels:
     app: security-profiles-operator
-  name: seccompprofiles.security-profiles-operator.x-k8s.io
+  name: nginx-1.19.1
+  namespace: security-profiles-operator
 spec:
-  group: security-profiles-operator.x-k8s.io
-  names:
-    kind: SeccompProfile
-    listKind: SeccompProfileList
-    plural: seccompprofiles
-    shortNames:
-    - sp
-    singular: seccompprofile
-  scope: Namespaced
-  versions:
-  - name: v1alpha1
-    schema:
-      openAPIV3Schema:
-        description: SeccompProfile is a cluster level specification for a seccomp profile. See https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#seccomp
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: SeccompProfileSpec defines the desired state of SeccompProfile.
-            properties:
-              architectures:
-                description: the architecture used for system calls
-                items:
-                  enum:
-                  - SCMP_ARCH_X86
-                  - SCMP_ARCH_X86_64
-                  - SCMP_ARCH_X32
-                  - SCMP_ARCH_ARM
-                  - SCMP_ARCH_AARCH64
-                  - SCMP_ARCH_MIPS
-                  - SCMP_ARCH_MIPS64
-                  - SCMP_ARCH_MIPS64N32
-                  - SCMP_ARCH_MIPSEL
-                  - SCMP_ARCH_MIPSEL64
-                  - SCMP_ARCH_MIPSEL64N32
-                  - SCMP_ARCH_PPC
-                  - SCMP_ARCH_PPC64
-                  - SCMP_ARCH_PPC64LE
-                  - SCMP_ARCH_S390
-                  - SCMP_ARCH_S390X
-                  - SCMP_ARCH_PARISC
-                  - SCMP_ARCH_PARISC64
-                  - SCMP_ARCH_RISCV64
-                  type: string
-                type: array
-              defaultAction:
-                description: the default action for seccomp
-                enum:
-                - SCMP_ACT_KILL
-                - SCMP_ACT_KILL_PROCESS
-                - SCMP_ACT_KILL_THREAD
-                - SCMP_ACT_TRAP
-                - SCMP_ACT_ERRNO
-                - SCMP_ACT_TRACE
-                - SCMP_ACT_ALLOW
-                - SCMP_ACT_LOG
-                type: string
-              flags:
-                description: list of flags to use with seccomp(2)
-                items:
-                  enum:
-                  - SECCOMP_FILTER_FLAG_TSYNC
-                  - SECCOMP_FILTER_FLAG_LOG
-                  - SECCOMP_FILTER_FLAG_SPEC_ALLOW
-                  type: string
-                type: array
-              syscalls:
-                description: match a syscall in seccomp. While this property is OPTIONAL, some values of defaultAction are not useful without syscalls entries. For example, if defaultAction is SCMP_ACT_KILL and syscalls is empty or unset, the kernel will kill the container process on its first syscall
-                items:
-                  description: Syscall defines a syscall in seccomp.
-                  properties:
-                    action:
-                      description: the action for seccomp rules
-                      enum:
-                      - SCMP_ACT_KILL
-                      - SCMP_ACT_KILL_PROCESS
-                      - SCMP_ACT_KILL_THREAD
-                      - SCMP_ACT_TRAP
-                      - SCMP_ACT_ERRNO
-                      - SCMP_ACT_TRACE
-                      - SCMP_ACT_ALLOW
-                      - SCMP_ACT_LOG
-                      type: string
-                    args:
-                      description: the specific syscall in seccomp
-                      items:
-                        description: Arg defines the specific syscall in seccomp.
-                        properties:
-                          index:
-                            description: the index for syscall arguments in seccomp
-                            minimum: 0
-                            type: integer
-                          op:
-                            description: the operator for syscall arguments in seccomp
-                            enum:
-                            - SCMP_CMP_NE
-                            - SCMP_CMP_LT
-                            - SCMP_CMP_LE
-                            - SCMP_CMP_EQ
-                            - SCMP_CMP_GE
-                            - SCMP_CMP_GT
-                            - SCMP_CMP_MASKED_EQ
-                            type: string
-                          value:
-                            description: the value for syscall arguments in seccomp
-                            format: int64
-                            minimum: 0
-                            type: integer
-                          valueTwo:
-                            description: the value for syscall arguments in seccomp
-                            format: int64
-                            minimum: 0
-                            type: integer
-                        required:
-                        - index
-                        - op
-                        type: object
-                      maxItems: 6
-                      type: array
-                    errnoRet:
-                      description: the errno return code to use. Some actions like SCMP_ACT_ERRNO and SCMP_ACT_TRACE allow to specify the errno code to return
-                      type: string
-                    names:
-                      description: the names of the syscalls
-                      items:
-                        type: string
-                      type: array
-                  required:
-                  - action
-                  - names
-                  type: object
-                type: array
-            required:
-            - defaultAction
-            type: object
-          status:
-            description: SeccompProfileStatus contains the host path of the deployed SeccompProfile.
-            properties:
-              path:
-                type: string
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
+  architectures:
+  - SCMP_ARCH_X86_64
+  - SCMP_ARCH_X86
+  - SCMP_ARCH_X32
+  defaultAction: SCMP_ACT_ERRNO
+  syscalls:
+  - action: SCMP_ACT_ALLOW
+    names:
+    - accept4
+    - access
+    - arch_prctl
+    - bind
+    - brk
+    - capget
+    - capset
+    - chdir
+    - chown
+    - clone
+    - close
+    - connect
+    - dup2
+    - epoll_create
+    - epoll_ctl
+    - epoll_pwait
+    - epoll_wait
+    - eventfd2
+    - execve
+    - exit
+    - exit_group
+    - faccessat
+    - fadvise64
+    - fchdir
+    - fchown
+    - fcntl
+    - fgetxattr
+    - fsetxattr
+    - fstat
+    - fstatfs
+    - futex
+    - getcwd
+    - getdents
+    - getdents64
+    - getegid
+    - geteuid
+    - getgid
+    - getpid
+    - getppid
+    - getrlimit
+    - getuid
+    - ioctl
+    - io_setup
+    - listen
+    - lseek
+    - mkdir
+    - mmap
+    - mprotect
+    - munmap
+    - nanosleep
+    - newfstatat
+    - open
+    - openat
+    - pipe
+    - prctl
+    - pread64
+    - prlimit64
+    - pwrite64
+    - read
+    - recvfrom
+    - recvmsg
+    - rename
+    - rt_sigaction
+    - rt_sigprocmask
+    - rt_sigreturn
+    - rt_sigsuspend
+    - sched_getaffinity
+    - seccomp
+    - sendfile
+    - sendmsg
+    - setgid
+    - setgroups
+    - setitimer
+    - set_robust_list
+    - setsockopt
+    - set_tid_address
+    - setuid
+    - sigaltstack
+    - socket
+    - socketpair
+    - stat
+    - statfs
+    - sysinfo
+    - umask
+    - uname
+    - unlink
+    - utimensat
+    - wait4
+    - write
+    - writev
+  targetWorkload: default-profiles
 ---
 apiVersion: v1
 data:
@@ -434,124 +373,3 @@ metadata:
     app: security-profiles-operator
   name: security-profiles-operator-profile
   namespace: security-profiles-operator
----
-apiVersion: v1
-data:
-  nginx-1.19.1.json: |-
-    {
-      "defaultAction": "SCMP_ACT_ERRNO",
-      "architectures":[
-          "SCMP_ARCH_X86_64",
-          "SCMP_ARCH_X86",
-          "SCMP_ARCH_X32"
-      ],
-      "syscalls": [
-        {
-          "names": [
-            "accept4",
-            "access",
-            "arch_prctl",
-            "bind",
-            "brk",
-            "capget",
-            "capset",
-            "chdir",
-            "chown",
-            "clone",
-            "close",
-            "connect",
-            "dup2",
-            "epoll_create",
-            "epoll_ctl",
-            "epoll_pwait",
-            "epoll_wait",
-            "eventfd2",
-            "execve",
-            "exit",
-            "exit_group",
-            "faccessat",
-            "fadvise64",
-            "fchdir",
-            "fchown",
-            "fcntl",
-            "fgetxattr",
-            "fsetxattr",
-            "fstat",
-            "fstatfs",
-            "futex",
-            "getcwd",
-            "getdents",
-            "getdents64",
-            "getegid",
-            "geteuid",
-            "getgid",
-            "getpid",
-            "getppid",
-            "getrlimit",
-            "getuid",
-            "ioctl",
-            "io_setup",
-            "listen",
-            "lseek",
-            "mkdir",
-            "mmap",
-            "mprotect",
-            "munmap",
-            "nanosleep",
-            "newfstatat",
-            "open",
-            "openat",
-            "pipe",
-            "prctl",
-            "pread64",
-            "prlimit64",
-            "pwrite64",
-            "read",
-            "recvfrom",
-            "recvmsg",
-            "rename",
-            "rt_sigaction",
-            "rt_sigprocmask",
-            "rt_sigreturn",
-            "rt_sigsuspend",
-            "sched_getaffinity",
-            "seccomp",
-            "sendfile",
-            "sendmsg",
-            "setgid",
-            "setgroups",
-            "setitimer",
-            "set_robust_list",
-            "setsockopt",
-            "set_tid_address",
-            "setuid",
-            "sigaltstack",
-            "socket",
-            "socketpair",
-            "stat",
-            "statfs",
-            "sysinfo",
-            "umask",
-            "uname",
-            "unlink",
-            "utimensat",
-            "wait4",
-            "write",
-            "writev"
-          ],
-          "action": "SCMP_ACT_ALLOW",
-          "args": [],
-          "comment": "",
-          "includes": {},
-          "excludes": {}
-        }
-      ]
-    }
-kind: ConfigMap
-metadata:
-  annotations:
-    seccomp.security.kubernetes.io/profile: "true"
-  labels:
-    app: security-profiles-operator
-  name: default-profiles
-  namespace: security-profiles-operator
diff --git a/deploy/overlays/cluster/default-profiles.yaml b/deploy/overlays/cluster/default-profiles.yaml
deleted file mode 100644
index 805f9dc6e..000000000
--- a/deploy/overlays/cluster/default-profiles.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-- op: replace
-  path: /metadata/namespace
-  value: security-profiles-operator
-- op: add
-  path: /metadata/annotations
-  value:
-    seccomp.security.kubernetes.io/profile: "true"
diff --git a/deploy/overlays/cluster/kustomization.yaml b/deploy/overlays/cluster/kustomization.yaml
index 6787602e8..a84017889 100644
--- a/deploy/overlays/cluster/kustomization.yaml
+++ b/deploy/overlays/cluster/kustomization.yaml
@@ -7,12 +7,6 @@ bases:
   - ../../base
 
 patchesJson6902:
-  - target:
-      version: v1
-      kind: ConfigMap
-      name: default-profiles
-    path: default-profiles.yaml
-
   - target:
       version: v1
       kind: ConfigMap
diff --git a/deploy/overlays/namespaced/kustomization.yaml b/deploy/overlays/namespaced/kustomization.yaml
index 809157374..953810def 100644
--- a/deploy/overlays/namespaced/kustomization.yaml
+++ b/deploy/overlays/namespaced/kustomization.yaml
@@ -25,8 +25,9 @@ patchesJson6902:
     path: clusterrolebinding.yaml
 
   - target:
-      version: v1
-      kind: ConfigMap
-      name: default-profiles
+      group: security-profiles-operator.x-k8s.io
+      version: v1alpha1
+      kind: SeccompProfile
+      name: nginx-1.19.1
       namespace: security-profiles-operator
-    path: configmap.yaml
+    path: seccompprofile.yaml
diff --git a/deploy/overlays/namespaced/configmap.yaml b/deploy/overlays/namespaced/seccompprofile.yaml
similarity index 100%
rename from deploy/overlays/namespaced/configmap.yaml
rename to deploy/overlays/namespaced/seccompprofile.yaml
diff --git a/examples/pod.yaml b/examples/pod.yaml
index c05f96df0..303aa07c8 100644
--- a/examples/pod.yaml
+++ b/examples/pod.yaml
@@ -8,7 +8,7 @@ metadata:
     # Or one example within this directory
     # Please note the syntax:
     # `localhost/operator/{namespace}/{configmap-name}/{profile-name}`
-    # seccomp.security.alpha.kubernetes.io/pod: 'localhost/operator/default/test-profile/profile-allow.json'
+    # seccomp.security.alpha.kubernetes.io/pod: 'localhost/operator/default/example-profiles/profile-allow.json'
 spec:
   containers:
   - name: test-container
diff --git a/examples/profile.yaml b/examples/profile.yaml
deleted file mode 100644
index f9ce6e17e..000000000
--- a/examples/profile.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: test-profile
-  annotations:
-    seccomp.security.kubernetes.io/profile: "true"
-data:
-  profile-block.json: |-
-    { "defaultAction": "SCMP_ACT_ERRNO" }
-  profile-complain.json: |-
-    { "defaultAction": "SCMP_ACT_LOG" }
-  profile-allow.json: |-
-    { "defaultAction": "SCMP_ACT_ALLOW" }
diff --git a/examples/seccompprofile.yaml b/examples/seccompprofile.yaml
index df61ce150..fe1d54a82 100644
--- a/examples/seccompprofile.yaml
+++ b/examples/seccompprofile.yaml
@@ -5,6 +5,7 @@ metadata:
   name: profile-block
 spec:
   defaultAction: "SCMP_ACT_ERRNO"
+  targetWorkload: "example-profiles"
 ---
 apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
 kind: SeccompProfile
@@ -12,6 +13,7 @@ metadata:
   name: profile-complain
 spec:
   defaultAction: "SCMP_ACT_LOG"
+  targetWorkload: "example-profiles"
 ---
 apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
 kind: SeccompProfile
@@ -19,3 +21,4 @@ metadata:
   name: profile-allow
 spec:
   defaultAction: "SCMP_ACT_ALLOW"
+  targetWorkload: "example-profiles"
diff --git a/installation-usage.md b/installation-usage.md
index bdb6f30d7..16647c774 100644
--- a/installation-usage.md
+++ b/installation-usage.md
@@ -4,14 +4,9 @@
 
 The feature scope of the security-profiles-operator is right now limited to:
 
-- Enable `ConfigMap`s to store seccomp profiles.
+- Adds a `SeccompProfile` CRD (alpha) to store seccomp profiles.
 - Synchronize seccomp profiles across all worker nodes.
 - Validate if a node supports seccomp and do not synchronize if not.
-- Validate if a profile is syntactically correct and do not synchronize if not.
-
-There is now also a `SeccompProfile` Custom Resource Definition available to
-validate and store seccomp profiles. This custom resource is in Alpha status and
-may change at any time.
 
 ## Tutorials and Demos
 
@@ -23,44 +18,13 @@ may change at any time.
 ### 1. Install operator
 
 ```sh
+$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/security-profiles-operator/master/deploy/crd.yaml
 $ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/security-profiles-operator/master/deploy/operator.yaml
 ```
 
 ### 2. Create Profile
 
-#### ConfigMap
-
-ConfigMaps with profiles will be separated by their target namespace and must be
-annotated with `seccomp.security.kubernetes.io/profile: "true"`. As per below:
-
-```yaml
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  namespace: my-namespace
-  name: cfg-map-name
-  annotations:
-    seccomp.security.kubernetes.io/profile: "true"
-data:
-  profile1.json: |-
-    { "defaultAction": "SCMP_ACT_ERRNO" }
-  profile2.json: |-
-    { "defaultAction": "SCMP_ACT_LOG" }
-```
-
-The operator will get that ConfigMap and save all its profiles into the
-directory:
-
-`/var/lib/kubelet/seccomp/operator/my-namespace/cfg-map-name/`.
-
-An init container will setup the root directory of the operator to be able to
-run it without root G/UID. This will be done by creating a symlink from the
-rootless profile storage `/var/lib/security-profiles-operator` to the default seccomp root
-path inside of the kubelet root `/var/lib/kubelet/seccomp/operator`.
-
-#### SeccompProfile
-
-A `SeccompProfile` can also be used to create profiles. Example:
+Use the `SeccompProfile` kind to create profiles. Example:
 
 ```yaml
 apiVersion: v1alpha1
@@ -70,12 +34,18 @@ metadata:
   name: profile1
 spec:
   defaultAction: SCMP_ACT_LOG
+  targetWorkload: custom-profiles
 ```
 
 This seccomp profile will be saved at the path:
 
 `/var/lib/kubelet/seccomp/operator/my-namespace/custom-profiles/profile1.json`.
 
+An init container will set up the root directory of the operator to be able to
+run it without root G/UID. This will be done by creating a symlink from the
+rootless profile storage `/var/lib/security-profiles-operator` to the default seccomp root
+path inside of the kubelet root `/var/lib/kubelet/seccomp/operator`.
+
 ### 3. Apply profile to pod
 
 Create a pod using one of the created profiles. On Kubernetes >= 1.19, the
@@ -90,9 +60,7 @@ spec:
   securityContext:
     seccompProfile:
       type: Localhost
-      localhostProfile: operator/my-namespace/cfg-map-name/profile1.json
-      # if using SeccompProfile:
-      # localhostProfile: operator/my-namespace/custom-profiles/profile1.json
+      localhostProfile: operator/my-namespace/custom-profiles/profile1.json
   containers:
     - name: test-container
       image: nginx
@@ -106,9 +74,7 @@ kind: Pod
 metadata:
   name: test-pod
   annotations:
-    seccomp.security.alpha.kubernetes.io/pod: "localhost/operator/my-namespace/cfg-map-name/profile1.json"
-    # if using SeccompProfile:
-    # seccomp.security.alpha.kubernetes.io/pod: "localhost/operator/my-namespace/custom-profiles/profile1.json"
+    seccomp.security.alpha.kubernetes.io/pod: "localhost/operator/my-namespace/custom-profiles/profile1.json"
 spec:
   containers:
     - name: test-container
@@ -117,11 +83,11 @@ spec:
 
 ## Restricting to a Single Namespace
 
-The security-profiles-operator can optionally be run to watch ConfigMaps in a single
-namespace. This is advantageous because it allows for tightening the RBAC
-permissions required by the operator's ServiceAccount. To modify the operator
-deployment to run in a single namespace, use the `namespace-operator.yaml`
-manifest with your namespace of choice:
+The security-profiles-operator can optionally be run to watch SeccompProfiles in
+a single namespace. This is advantageous because it allows for tightening the
+RBAC permissions required by the operator's ServiceAccount. To modify the
+operator deployment to run in a single namespace, use the
+`namespace-operator.yaml` manifest with your namespace of choice:
 
 ```sh
 NAMESPACE=<your-namespace>
@@ -134,26 +100,22 @@ curl https://raw.githubusercontent.com/kubernetes-sigs/security-profiles-operato
 Confirm that the profile is being reconciled:
 
 ```sh
-$ kubectl logs -n security-profiles-operator security-profiles-operator-v6p2h
-
-I1009 21:47:54.491462       1 main.go:90] setup "msg"="starting security-profiles-operator"  "buildDate"="2020-09-30T14:37:39Z" "compiler"="gc" "gitCommit"="unknown" "gitTreeState"="clean" "goVersion"="go1.15.2" "platform"="linux/amd64" "version"="0.2.0-dev"
-I1009 21:47:54.900650       1 listener.go:44] controller-runtime/metrics "msg"="metrics server is starting to listen"  "addr"=":8080"
-I1009 21:47:54.902267       1 main.go:126] setup "msg"="starting manager"
-I1009 21:47:54.902854       1 internal.go:391] controller-runtime/manager "msg"="starting metrics server"  "path"="/metrics"
-I1009 21:47:54.903193       1 controller.go:142] controller "msg"="Starting EventSource" "controller"="profile" "reconcilerGroup"="" "reconcilerKind"="ConfigMap" "source"={"Type":{"metadata":{"creationTimestamp":null}}}
-I1009 21:47:54.903342       1 controller.go:142] controller "msg"="Starting EventSource" "controller"="profile" "reconcilerGroup"="security-profiles-operator.x-k8s.io" "reconcilerKind"="SeccompProfile" "source"={"Type":{"metadata":{"creationTimestamp":null},"spec":{"defaultAction":""}}}
-I1009 21:47:55.003608       1 controller.go:149] controller "msg"="Starting Controller" "controller"="profile" "reconcilerGroup"="" "reconcilerKind"="ConfigMap"
-I1009 21:47:55.003765       1 controller.go:149] controller "msg"="Starting Controller" "controller"="profile" "reconcilerGroup"="security-profiles-operator.x-k8s.io" "reconcilerKind"="SeccompProfile"
-I1009 21:47:55.003915       1 controller.go:176] controller "msg"="Starting workers" "controller"="profile" "reconcilerGroup"="security-profiles-operator.x-k8s.io" "reconcilerKind"="SeccompProfile" "worker count"=1
-I1009 21:47:55.003923       1 controller.go:176] controller "msg"="Starting workers" "controller"="profile" "reconcilerGroup"="" "reconcilerKind"="ConfigMap" "worker count"=1
-E1009 21:47:55.004175       1 profile.go:133] profile "msg"="unable to fetch SeccompProfile" "error"="SeccompProfile.security-profiles-operator.x-k8s.io \"default-profiles\" not found" "namespace"="security-profiles-operator" "profile"="default-profiles"
-I1009 21:47:55.005805       1 profile.go:232] profile "msg"="Reconciled profile from ConfigMap" "namespace"="security-profiles-operator" "profile"="default-profiles" "name"="default-profiles" "resource version"="9907"
+$ kubectl -n security-profiles-operator logs security-profiles-operator-mzw9t
+I1019 19:34:14.942464       1 main.go:90] setup "msg"="starting security-profiles-operator"  "buildDate"="2020-10-19T19:31:24Z" "compiler"="gc" "gitCommit"="a3ef0e1ea6405092268c18f240b62015c247dd9d" "gitTreeState"="dirty" "goVersion"="go1.15.1" "platform"="linux/amd64" "version"="0.2.0-dev"
+I1019 19:34:15.348389       1 listener.go:44] controller-runtime/metrics "msg"="metrics server is starting to listen"  "addr"=":8080"
+I1019 19:34:15.349076       1 main.go:126] setup "msg"="starting manager"
+I1019 19:34:15.349449       1 internal.go:391] controller-runtime/manager "msg"="starting metrics server"  "path"="/metrics"
+I1019 19:34:15.350201       1 controller.go:142] controller "msg"="Starting EventSource" "controller"="profile" "reconcilerGroup"="security-profiles-operator.x-k8s.io" "reconcilerKind"="SeccompProfile" "source"={"Type":{"metadata":{"creationTimestamp":null},"spec":{"targetWorkload":"","defaultAction":""}}}
+I1019 19:34:15.450674       1 controller.go:149] controller "msg"="Starting Controller" "controller"="profile" "reconcilerGroup"="security-profiles-operator.x-k8s.io" "reconcilerKind"="SeccompProfile"
+I1019 19:34:15.450757       1 controller.go:176] controller "msg"="Starting workers" "controller"="profile" "reconcilerGroup"="security-profiles-operator.x-k8s.io" "reconcilerKind"="SeccompProfile" "worker count"=1
+I1019 19:34:15.453102       1 profile.go:148] profile "msg"="Reconciled profile from SeccompProfile" "namespace"="security-profiles-operator" "profile"="nginx-1.19.1" "name"="nginx-1.19.1" "resource version"="728"
+I1019 19:34:15.453618       1 profile.go:148] profile "msg"="Reconciled profile from SeccompProfile" "namespace"="security-profiles-operator" "profile"="security-profiles-operator" "name"="security-profiles-operator" "resource version"="729"
 ```
 
 Confirm that the seccomp profiles are saved into the correct path:
 
 ```sh
-$ kubectl exec -t -n security-profiles-operator security-profiles-operator-v6p2h -- ls /var/lib/kubelet/seccomp/operator/my-namespace/test-profile
+$ kubectl exec -t -n security-profiles-operator security-profiles-operator-v6p2h -- ls /var/lib/kubelet/seccomp/operator/my-namespace/my-workload
 profile-block.json
 profile-complain.json
 ```
diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go
index e864b81a5..7988791af 100644
--- a/internal/pkg/config/config.go
+++ b/internal/pkg/config/config.go
@@ -28,14 +28,6 @@ const (
 	// profiles.
 	ProfilesRootPath = kubeletSeccompRootPath + "/operator"
 
-	// DefaultProfilesConfigMapName is the configMap name for the default
-	// profiles.
-	DefaultProfilesConfigMapName = "default-profiles"
-
-	// CustomProfilesDirectoryName is the directory where profiles from the
-	// SeccompProfile CRD are stored.
-	CustomProfilesDirectoryName = "custom-profiles"
-
 	// NodeNameEnvKey is the default environment variable key for retrieving
 	// the name of the current node.
 	NodeNameEnvKey = "NODE_NAME"
diff --git a/internal/pkg/controllers/profile/profile.go b/internal/pkg/controllers/profile/profile.go
index 2a22a52b0..5bf6658ad 100644
--- a/internal/pkg/controllers/profile/profile.go
+++ b/internal/pkg/controllers/profile/profile.go
@@ -28,18 +28,14 @@ import (
 
 	"github.com/containers/common/pkg/seccomp"
 	"github.com/crossplane/crossplane-runtime/pkg/event"
-	"github.com/crossplane/crossplane-runtime/pkg/resource"
 	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
-	corev1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/types"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
-	seccompoperatorv1alpha1 "sigs.k8s.io/security-profiles-operator/api/v1alpha1"
+	"sigs.k8s.io/security-profiles-operator/api/v1alpha1"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
 )
 
@@ -50,7 +46,6 @@ const (
 	wait = 30 * time.Second
 
 	errGetProfile          = "cannot get profile"
-	errConfigMapNil        = "config map cannot be nil"
 	errSeccompProfileNil   = "seccomp profile cannot be nil"
 	errSavingProfile       = "cannot save profile"
 	errCreatingOperatorDir = "cannot create operator directory"
@@ -61,51 +56,27 @@ const (
 	// https://github.com/golang/go/issues/22323#issuecomment-340568811
 	dirPermissionMode os.FileMode = 0o744
 
-	// seccompProfileAnnotation is the annotation on a ConfigMap that specifies
-	// its intention to be treated as a seccomp profile.
-	seccompProfileAnnotation = "seccomp.security.kubernetes.io/profile"
-
 	reasonSeccompNotSupported   event.Reason = "SeccompNotSupportedOnNode"
 	reasonInvalidSeccompProfile event.Reason = "InvalidSeccompProfile"
 	reasonCannotGetProfilePath  event.Reason = "CannotGetSeccompProfilePath"
 	reasonCannotSaveProfile     event.Reason = "CannotSaveSeccompProfile"
 
 	reasonSavedProfile event.Reason = "SavedSeccompProfile"
-)
 
-// isProfile checks if a ConfigMap has been designated as a seccomp profile.
-func isProfile(obj runtime.Object) bool {
-	r, ok := obj.(*corev1.ConfigMap)
-	if !ok {
-		return false
-	}
-
-	return r.Annotations[seccompProfileAnnotation] == "true"
-}
+	extJSON = ".json"
+)
 
 // Setup adds a controller that reconciles seccomp profiles.
 func Setup(mgr ctrl.Manager, l logr.Logger) error {
 	const name = "profile"
 
-	if err := ctrl.NewControllerManagedBy(mgr).
-		Named(name).
-		For(&corev1.ConfigMap{}).
-		WithEventFilter(resource.NewPredicates(isProfile)).
-		Complete(&Reconciler{
-			client: mgr.GetClient(),
-			log:    l,
-			record: event.NewAPIRecorder(mgr.GetEventRecorderFor("profile")),
-			save:   saveProfileOnDisk,
-		}); err != nil {
-		return err
-	}
 	return ctrl.NewControllerManagedBy(mgr).
 		Named(name).
-		For(&seccompoperatorv1alpha1.SeccompProfile{}).
+		For(&v1alpha1.SeccompProfile{}).
 		Complete(&Reconciler{
 			client: mgr.GetClient(),
 			log:    l,
-			record: event.NewAPIRecorder(mgr.GetEventRecorderFor("profile")),
+			record: event.NewAPIRecorder(mgr.GetEventRecorderFor(name)),
 			save:   saveProfileOnDisk,
 		})
 }
@@ -120,33 +91,18 @@ type Reconciler struct {
 	save   saver
 }
 
-// Reconcile reconciles a SeccompProfile or a ConfigMap representing a seccomp profile.
+// Reconcile reconciles a SeccompProfile.
 func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) {
 	logger := r.log.WithValues("profile", req.Name, "namespace", req.Namespace)
 
 	ctx, cancel := context.WithTimeout(context.Background(), reconcileTimeout)
 	defer cancel()
 
-	// Look for the SeccompProfile Kind first
-	seccompProfile := &seccompoperatorv1alpha1.SeccompProfile{}
-	if err := r.client.Get(ctx, req.NamespacedName, seccompProfile); err != nil {
-		logger.Error(err, "unable to fetch SeccompProfile")
-		seccompProfile = nil
-	}
-	// If no SeccompProfile, look for a ConfigMap
-	configMap := &corev1.ConfigMap{}
-	if seccompProfile == nil {
-		if err := r.client.Get(ctx, types.NamespacedName{Name: req.Name, Namespace: req.Namespace}, configMap); err != nil {
-			// Returning an error means we will be requeued implicitly.
-			return reconcile.Result{}, errors.Wrap(ignoreNotFound(err), errGetProfile)
-		}
-	}
-
 	// Pre-check if the node supports seccomp
 	if !seccomp.IsSupported() {
 		err := errors.New("profile not added")
 		logger.Error(err, fmt.Sprintf("node %q does not support seccomp", os.Getenv(config.NodeNameEnvKey)))
-		r.record.Event(seccompProfile,
+		r.record.Event(&v1alpha1.SeccompProfile{},
 			event.Warning(reasonSeccompNotSupported, err, os.Getenv(config.NodeNameEnvKey),
 				"node does not support seccomp"))
 
@@ -156,15 +112,16 @@ func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error)
 		return reconcile.Result{}, nil
 	}
 
-	if seccompProfile != nil {
-		return r.reconcileSeccompProfile(ctx, seccompProfile, logger)
+	seccompProfile := &v1alpha1.SeccompProfile{}
+	if err := r.client.Get(ctx, req.NamespacedName, seccompProfile); err != nil {
+		// Expected to find a SeccompProfile, return an error and requeue
+		return reconcile.Result{}, errors.Wrap(ignoreNotFound(err), errGetProfile)
 	}
-
-	return r.reconcileConfigMap(configMap, logger)
+	return r.reconcileSeccompProfile(ctx, seccompProfile, logger)
 }
 
 func (r *Reconciler) reconcileSeccompProfile(
-	ctx context.Context, sp *seccompoperatorv1alpha1.SeccompProfile, l logr.Logger) (reconcile.Result, error) {
+	ctx context.Context, sp *v1alpha1.SeccompProfile, l logr.Logger) (reconcile.Result, error) {
 	if sp == nil {
 		return reconcile.Result{}, errors.New(errSeccompProfileNil)
 	}
@@ -177,7 +134,7 @@ func (r *Reconciler) reconcileSeccompProfile(
 		return reconcile.Result{RequeueAfter: wait}, nil
 	}
 
-	profilePath, err := GetProfilePath(profileName, sp.ObjectMeta.Namespace, config.CustomProfilesDirectoryName)
+	profilePath, err := GetProfilePath(profileName, sp.ObjectMeta.Namespace, sp.Spec.TargetWorkload)
 	if err != nil {
 		l.Error(err, "cannot get profile path")
 		r.record.Event(sp, event.Warning(reasonCannotGetProfilePath, err))
@@ -202,47 +159,6 @@ func (r *Reconciler) reconcileSeccompProfile(
 	return reconcile.Result{}, nil
 }
 
-func (r *Reconciler) reconcileConfigMap(cm *corev1.ConfigMap, l logr.Logger) (reconcile.Result, error) {
-	if cm == nil {
-		return reconcile.Result{}, errors.New(errConfigMapNil)
-	}
-	for profileName, profileContent := range cm.Data {
-		if err := validateProfile(profileContent); err != nil {
-			l.Error(err, "cannot validate profile "+profileName)
-			r.record.Event(cm, event.Warning(reasonInvalidSeccompProfile, err))
-
-			// it might be possible that other profiles in the cm are
-			// valid
-			continue
-		}
-
-		profilePath, err := GetProfilePath(profileName, cm.ObjectMeta.Namespace, cm.ObjectMeta.Name)
-		if err != nil {
-			l.Error(err, "cannot get profile path")
-			r.record.Event(cm, event.Warning(reasonCannotGetProfilePath, err))
-			return reconcile.Result{RequeueAfter: wait}, nil
-		}
-
-		if err = r.save(profilePath, []byte(profileContent)); err != nil {
-			l.Error(err, "cannot save profile into disk")
-			r.record.Event(cm, event.Warning(reasonCannotSaveProfile, err))
-			return reconcile.Result{RequeueAfter: wait}, nil
-		}
-		if err = r.save(profilePath, []byte(profileContent)); err != nil {
-			l.Error(err, "cannot save profile into disk")
-			r.record.Event(cm, event.Warning(reasonCannotSaveProfile, err))
-			return reconcile.Result{RequeueAfter: wait}, nil
-		}
-	}
-	l.Info(
-		"Reconciled profile from ConfigMap",
-		"resource version", cm.GetResourceVersion(),
-		"name", cm.GetName(),
-	)
-	r.record.Event(cm, event.Normal(reasonSavedProfile, "Successfully saved profile to disk"))
-	return reconcile.Result{}, nil
-}
-
 func saveProfileOnDisk(fileName string, contents []byte) error {
 	if err := os.MkdirAll(path.Dir(fileName), dirPermissionMode); err != nil {
 		return errors.Wrap(err, errCreatingOperatorDir)
@@ -256,8 +172,8 @@ func saveProfileOnDisk(fileName string, contents []byte) error {
 
 // GetProfilePath returns the full path for the provided profile name and config.
 func GetProfilePath(profileName, namespace, subdir string) (string, error) {
-	if filepath.Ext(profileName) == "" {
-		profileName += ".json"
+	if filepath.Ext(profileName) != extJSON {
+		profileName += extJSON
 	}
 	return path.Join(
 		config.ProfilesRootPath,
@@ -273,15 +189,3 @@ func ignoreNotFound(err error) error {
 	}
 	return err
 }
-
-// validateProfile does a basic validation for the provided seccomp profile
-// string.
-func validateProfile(content string) error {
-	profile := &seccomp.Seccomp{}
-	if err := json.Unmarshal([]byte(content), &profile); err != nil {
-		return errors.Wrap(err, "decoding seccomp profile")
-	}
-
-	// TODO: consider further validation steps
-	return nil
-}


Test patch:
diff --git a/internal/pkg/controllers/profile/profile_test.go b/internal/pkg/controllers/profile/profile_test.go
index 73b3cd761..74d7bc2db 100644
--- a/internal/pkg/controllers/profile/profile_test.go
+++ b/internal/pkg/controllers/profile/profile_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package profile
 
 import (
-	"context"
 	"io/ioutil"
 	"os"
 	"path"
@@ -27,51 +26,17 @@ import (
 	"github.com/crossplane/crossplane-runtime/pkg/test"
 	"github.com/pkg/errors"
 	"github.com/stretchr/testify/require"
-	corev1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 
+	"sigs.k8s.io/security-profiles-operator/api/v1alpha1"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
 )
 
-func TestIsProfile(t *testing.T) {
-	cases := map[string]struct {
-		obj  runtime.Object
-		want bool
-	}{
-		"NotAProfile": {
-			obj:  &corev1.ConfigMap{},
-			want: false,
-		},
-		"NotAConfigMap": {
-			obj:  &corev1.Secret{},
-			want: false,
-		},
-		"IsProfile": {
-			obj: &corev1.ConfigMap{
-				ObjectMeta: metav1.ObjectMeta{
-					Annotations: map[string]string{
-						seccompProfileAnnotation: "true",
-					},
-				},
-			},
-			want: true,
-		},
-	}
-
-	for name, tc := range cases {
-		t.Run(name, func(t *testing.T) {
-			got := isProfile(tc.obj)
-			require.Equal(t, tc.want, got)
-		})
-	}
-}
-
 func TestReconcile(t *testing.T) {
 	name := "cool-profile"
 	namespace := "cool-namespace"
@@ -95,7 +60,7 @@ func TestReconcile(t *testing.T) {
 			wantResult: reconcile.Result{},
 			wantErr:    nil,
 		},
-		"ConfigMapErrGetProfile": {
+		"ErrGetProfile": {
 			rec: &Reconciler{
 				client: &test.MockClient{
 					MockGet: test.NewMockGetFn(errOops),
@@ -106,44 +71,7 @@ func TestReconcile(t *testing.T) {
 			wantResult: reconcile.Result{},
 			wantErr:    errors.Wrap(errOops, errGetProfile),
 		},
-		"ConfigMapGotProfile": {
-			rec: &Reconciler{
-				client: &test.MockClient{
-					MockGet: func(_ context.Context, n types.NamespacedName, o runtime.Object) error {
-						switch o.(type) {
-						case *corev1.ConfigMap:
-							return nil
-						default:
-							return kerrors.NewNotFound(schema.GroupResource{}, name)
-						}
-					},
-				},
-				log:    log.Log,
-				record: event.NewNopRecorder(),
-			},
-			req:        reconcile.Request{NamespacedName: types.NamespacedName{Namespace: namespace, Name: name}},
-			wantResult: reconcile.Result{},
-			wantErr:    nil,
-		},
-		"CRDErrGetProfile": {
-			rec: &Reconciler{
-				client: &test.MockClient{
-					MockGet: func(_ context.Context, n types.NamespacedName, o runtime.Object) error {
-						switch o.(type) {
-						case *corev1.ConfigMap:
-							return errOops
-						default:
-							return kerrors.NewNotFound(schema.GroupResource{}, name)
-						}
-					},
-				},
-				log: log.Log,
-			},
-			req:        reconcile.Request{NamespacedName: types.NamespacedName{Namespace: namespace, Name: name}},
-			wantResult: reconcile.Result{},
-			wantErr:    errors.Wrap(errOops, errGetProfile),
-		},
-		"CRDGotProfile": {
+		"GotProfile": {
 			rec: &Reconciler{
 				client: &test.MockClient{
 					MockGet:          test.NewMockGetFn(nil),
@@ -190,7 +118,7 @@ func TestSaveProfileOnDisk(t *testing.T) {
 		fileCreated bool
 	}{
 		"CreateDirsAndWriteFile": {
-			fileName:    path.Join(dir, "/seccomp/operator/namespace/configmap/filename.json"),
+			fileName:    path.Join(dir, "/seccomp/operator/namespace/sp/filename.json"),
 			contents:    "some content",
 			fileCreated: true,
 		},
@@ -240,56 +168,75 @@ func TestSaveProfileOnDisk(t *testing.T) {
 
 func TestGetProfilePath(t *testing.T) {
 	cases := map[string]struct {
-		want        string
-		wantErr     string
-		profileName string
-		config      *corev1.ConfigMap
+		want    string
+		wantErr string
+		sp      *v1alpha1.SeccompProfile
 	}{
-		"AppendNamespaceConfigNameAndProfile": {
-			want:        path.Join(config.ProfilesRootPath, "config-namespace", "config-name", "file.js"),
-			profileName: "file.js",
-			config: &corev1.ConfigMap{
+		"AppendNamespaceAndProfile": {
+			want: path.Join(config.ProfilesRootPath, "config-namespace", "config-target", "file.json"),
+			sp: &v1alpha1.SeccompProfile{
 				ObjectMeta: metav1.ObjectMeta{
-					Name:      "config-name",
+					Name:      "file.json",
 					Namespace: "config-namespace",
 				},
+				Spec: v1alpha1.SeccompProfileSpec{
+					TargetWorkload: "config-target",
+				},
 			},
 		},
 		"BlockTraversalAtProfileName": {
-			want:        path.Join(config.ProfilesRootPath, "ns", "cfg", "file.js"),
-			profileName: "../../../../../file.js",
-			config: &corev1.ConfigMap{
+			want: path.Join(config.ProfilesRootPath, "ns", "config-target", "file.json"),
+			sp: &v1alpha1.SeccompProfile{
 				ObjectMeta: metav1.ObjectMeta{
-					Name:      "cfg",
+					Name:      "../../../../../file.json",
 					Namespace: "ns",
 				},
+				Spec: v1alpha1.SeccompProfileSpec{
+					TargetWorkload: "config-target",
+				},
 			},
 		},
-		"BlockTraversalAtConfigName": {
-			want:        path.Join(config.ProfilesRootPath, "ns", "cfg", "file.js"),
-			profileName: "file.js",
-			config: &corev1.ConfigMap{
+		"BlockTraversalAtTargetName": {
+			want: path.Join(config.ProfilesRootPath, "ns", "config-target", "file.json"),
+			sp: &v1alpha1.SeccompProfile{
 				ObjectMeta: metav1.ObjectMeta{
-					Name:      "../../../../../cfg",
+					Name:      "file.json",
 					Namespace: "ns",
 				},
+				Spec: v1alpha1.SeccompProfileSpec{
+					TargetWorkload: "../../../../../config-target",
+				},
 			},
 		},
-		"BlockTraversalAtConfigNamespace": {
-			want:        path.Join(config.ProfilesRootPath, "ns", "cfg", "file.js"),
-			profileName: "file.js",
-			config: &corev1.ConfigMap{
+		"BlockTraversalAtSPNamespace": {
+			want: path.Join(config.ProfilesRootPath, "ns", "config-target", "file.json"),
+			sp: &v1alpha1.SeccompProfile{
 				ObjectMeta: metav1.ObjectMeta{
-					Name:      "cfg",
+					Name:      "file.json",
 					Namespace: "../../../../../ns",
 				},
+				Spec: v1alpha1.SeccompProfileSpec{
+					TargetWorkload: "config-target",
+				},
+			},
+		},
+		"AppendExtension": {
+			want: path.Join(config.ProfilesRootPath, "config-namespace", "config-target", "file.json"),
+			sp: &v1alpha1.SeccompProfile{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "file",
+					Namespace: "config-namespace",
+				},
+				Spec: v1alpha1.SeccompProfileSpec{
+					TargetWorkload: "config-target",
+				},
 			},
 		},
 	}
 
 	for name, tc := range cases {
 		t.Run(name, func(t *testing.T) {
-			got, gotErr := GetProfilePath(tc.profileName, tc.config.ObjectMeta.Namespace, tc.config.ObjectMeta.Name)
+			got, gotErr := GetProfilePath(tc.sp.ObjectMeta.Name, tc.sp.ObjectMeta.Namespace, tc.sp.Spec.TargetWorkload)
 			if tc.wantErr == "" {
 				require.NoError(t, gotErr)
 			} else {
@@ -326,25 +273,3 @@ func TestIgnoreNotFound(t *testing.T) {
 		})
 	}
 }
-
-func TestValidateProfile(t *testing.T) {
-	for _, tc := range []struct {
-		profile       string
-		shouldSucceed bool
-	}{
-		{"wrong", false},
-		{`{"defaultAction": true}`, false},
-		{`{"syscalls": "wrong"}`, false},
-		{`{}`, true},
-		{`{"defaultAction": "SCMP_ACT_ALLOW"}`, true},
-		{`{"syscalls": []}`, true},
-	} {
-		msg := "Profile content: " + tc.profile
-		err := validateProfile(tc.profile)
-		if tc.shouldSucceed {
-			require.Nil(t, err, msg)
-		} else {
-			require.NotNil(t, err, msg)
-		}
-	}
-}
diff --git a/test/e2e_test.go b/test/e2e_test.go
index 3a18c72c6..d6bd28b22 100644
--- a/test/e2e_test.go
+++ b/test/e2e_test.go
@@ -23,12 +23,13 @@ import (
 	"fmt"
 	"strings"
 
-	v1 "k8s.io/api/core/v1"
-
-	seccompoperatorv1alpha1 "sigs.k8s.io/security-profiles-operator/api/v1alpha1"
+	"sigs.k8s.io/security-profiles-operator/api/v1alpha1"
 )
 
-const manifest = "deploy/operator.yaml"
+const (
+	crd      = "deploy/crd.yaml"
+	manifest = "deploy/operator.yaml"
+)
 
 func (e *e2e) TestSeccompOperator() {
 	// Deploy the operator
@@ -56,14 +57,6 @@ func (e *e2e) TestSeccompOperator() {
 			"Re-deploy the operator",
 			e.testCaseReDeployOperator,
 		},
-		{
-			"Deploy invalid profile",
-			e.testCaseDeployInvalidProfile,
-		},
-		{
-			"Verify example CRD profiles",
-			e.testCaseCRDExampleProfiles,
-		},
 	} {
 		e.logf("> Running testcase: %s", testCase.description)
 		testCase.fn(nodes)
@@ -85,6 +78,10 @@ func (e *e2e) deployOperator(manifest string) {
 		manifest,
 	)
 
+	// Deploy the CRD
+	e.logf("Deploying CRD")
+	e.kubectl("create", "-f", crd)
+
 	// Deploy the operator
 	e.logf("Deploying operator")
 	e.kubectl("create", "-f", manifest)
@@ -107,20 +104,11 @@ func (e *e2e) getWorkerNodes() []string {
 	return nodes
 }
 
-func (e *e2e) getConfigMap(name, namespace string) *v1.ConfigMap {
-	configMapJSON := e.kubectl(
-		"-n", namespace, "get", "configmap", name, "-o", "json",
-	)
-	configMap := &v1.ConfigMap{}
-	e.Nil(json.Unmarshal([]byte(configMapJSON), configMap))
-	return configMap
-}
-
-func (e *e2e) getSeccompProfile(name, namespace string) *seccompoperatorv1alpha1.SeccompProfile {
+func (e *e2e) getSeccompProfile(name, namespace string) *v1alpha1.SeccompProfile {
 	seccompProfileJSON := e.kubectl(
 		"-n", namespace, "get", "seccompprofile", name, "-o", "json",
 	)
-	seccompProfile := &seccompoperatorv1alpha1.SeccompProfile{}
+	seccompProfile := &v1alpha1.SeccompProfile{}
 	e.Nil(json.Unmarshal([]byte(seccompProfileJSON), seccompProfile))
 	return seccompProfile
 }
diff --git a/test/tc_crd_example_profiles_test.go b/test/tc_crd_example_profiles_test.go
deleted file mode 100644
index 3d9c07308..000000000
--- a/test/tc_crd_example_profiles_test.go
+++ /dev/null
@@ -1,63 +0,0 @@
-// +build e2e
-
-/*
-Copyright 2020 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 (
-	"encoding/json"
-
-	operator_v1alpha1 "sigs.k8s.io/security-profiles-operator/api/v1alpha1"
-	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
-	"sigs.k8s.io/security-profiles-operator/internal/pkg/controllers/profile"
-)
-
-func (e *e2e) testCaseCRDExampleProfiles(nodes []string) {
-	const exampleProfilePath = "examples/seccompprofile.yaml"
-	exampleProfileNames := [3]string{"profile-allow", "profile-complain", "profile-block"}
-	e.kubectl("create", "-f", exampleProfilePath)
-	defer e.kubectl("delete", "-f", exampleProfilePath)
-
-	e.logf("Retrieving deployed example profile")
-
-	// Content verification
-	for _, node := range nodes {
-		// General path verification
-		e.logf("Verifying security profiles operator directory on node: %s", node)
-		statOutput := e.execNode(
-			node, "stat", "-L", "-c", `%a,%u,%g`, config.ProfilesRootPath,
-		)
-		e.Contains(statOutput, "744,2000,2000")
-
-		// Example profile verification
-		for _, name := range exampleProfileNames {
-			sp := e.getSeccompProfile(name, "default")
-			e.verifyCRDProfileContent(node, sp)
-		}
-	}
-}
-
-func (e *e2e) verifyCRDProfileContent(node string, sp *operator_v1alpha1.SeccompProfile) {
-	e.logf("Verifying %s profile on node %s", sp.Name, node)
-	name := sp.Name
-	expected, err := json.Marshal(sp.Spec)
-	e.Nil(err)
-	profilePath, err := profile.GetProfilePath(name, sp.ObjectMeta.Namespace, "custom-profiles")
-	e.Nil(err)
-	catOutput := e.execNode(node, "cat", profilePath)
-	e.Contains(catOutput, string(expected))
-}
diff --git a/test/tc_default_profiles_test.go b/test/tc_default_profiles_test.go
index 9109a3695..8a6693638 100644
--- a/test/tc_default_profiles_test.go
+++ b/test/tc_default_profiles_test.go
@@ -19,29 +19,23 @@ limitations under the License.
 package e2e_test
 
 import (
+	"encoding/json"
+	"path"
+
 	v1 "k8s.io/api/core/v1"
 
+	v1alpha1 "sigs.k8s.io/security-profiles-operator/api/v1alpha1"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/controllers/profile"
 )
 
 func (e *e2e) testCaseDefaultAndExampleProfiles(nodes []string) {
-	const (
-		exampleProfilePath = "examples/profile.yaml"
-		exampleProfileName = "test-profile"
-	)
+	const exampleProfilePath = "examples/seccompprofile.yaml"
+	exampleProfileNames := [3]string{"profile-allow", "profile-complain", "profile-block"}
+	defaultProfileNames := [1]string{"nginx-1.19.1"}
 	e.kubectl("create", "-f", exampleProfilePath)
 	defer e.kubectl("delete", "-f", exampleProfilePath)
 
-	e.logf("Retrieving deployed example profile")
-	exampleProfiles := e.getConfigMap(exampleProfileName, "default")
-
-	// Get the default profiles
-	e.logf("Retrieving default profiles from configmap: %s", config.DefaultProfilesConfigMapName)
-	defaultProfiles := e.getConfigMap(
-		config.DefaultProfilesConfigMapName, config.OperatorName,
-	)
-
 	// Content verification
 	for _, node := range nodes {
 		// General path verification
@@ -51,20 +45,51 @@ func (e *e2e) testCaseDefaultAndExampleProfiles(nodes []string) {
 		)
 		e.Contains(statOutput, "744,2000,2000")
 
+		// security-profiles-operator.json init verification
+		cm := e.getConfigMap(
+			"security-profiles-operator-profile", config.OperatorName,
+		)
+		e.verifyBaseProfileContent(node, cm)
+
 		// Default profile verification
-		e.verifyProfilesContent(node, defaultProfiles)
+		for _, name := range defaultProfileNames {
+			sp := e.getSeccompProfile(name, "security-profiles-operator")
+			e.verifyCRDProfileContent(node, sp)
+		}
 
 		// Example profile verification
-		e.verifyProfilesContent(node, exampleProfiles)
+		for _, name := range exampleProfileNames {
+			sp := e.getSeccompProfile(name, "default")
+			e.verifyCRDProfileContent(node, sp)
+		}
 	}
 }
 
-func (e *e2e) verifyProfilesContent(node string, cm *v1.ConfigMap) {
+func (e *e2e) getConfigMap(name, namespace string) *v1.ConfigMap {
+	configMapJSON := e.kubectl(
+		"-n", namespace, "get", "configmap", name, "-o", "json",
+	)
+	configMap := &v1.ConfigMap{}
+	e.Nil(json.Unmarshal([]byte(configMapJSON), configMap))
+	return configMap
+}
+
+func (e *e2e) verifyBaseProfileContent(node string, cm *v1.ConfigMap) {
 	e.logf("Verifying %s profile on node %s", cm.Name, node)
-	for name, content := range cm.Data {
-		profilePath, err := profile.GetProfilePath(name, cm.ObjectMeta.Namespace, cm.ObjectMeta.Name)
-		e.Nil(err)
-		catOutput := e.execNode(node, "cat", profilePath)
-		e.Contains(catOutput, content)
-	}
+	name := "security-profiles-operator.json"
+	content := cm.Data[name]
+	profilePath := path.Join("/var/lib/kubelet/seccomp", name)
+	catOutput := e.execNode(node, "cat", profilePath)
+	e.Contains(content, catOutput)
+}
+
+func (e *e2e) verifyCRDProfileContent(node string, sp *v1alpha1.SeccompProfile) {
+	e.logf("Verifying %s profile on node %s", sp.Name, node)
+	name := sp.Name
+	expected, err := json.Marshal(sp.Spec)
+	e.Nil(err)
+	profilePath, err := profile.GetProfilePath(name, sp.ObjectMeta.Namespace, sp.Spec.TargetWorkload)
+	e.Nil(err)
+	catOutput := e.execNode(node, "cat", profilePath)
+	e.Contains(catOutput, string(expected))
 }
diff --git a/test/tc_invalid_profile_test.go b/test/tc_invalid_profile_test.go
deleted file mode 100644
index c7466495e..000000000
--- a/test/tc_invalid_profile_test.go
+++ /dev/null
@@ -1,92 +0,0 @@
-// +build e2e
-
-/*
-Copyright 2020 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"
-	"io/ioutil"
-	"os"
-
-	"sigs.k8s.io/security-profiles-operator/internal/pkg/controllers/profile"
-)
-
-func (e *e2e) testCaseDeployInvalidProfile(nodes []string) {
-	const (
-		configMapName = "invalid-profile"
-		profileName   = "profile-invalid.json"
-	)
-
-	newProfile := func(content string) string {
-		configMapString := fmt.Sprintf(`---
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: %s
-  annotations:
-    seccomp.security.kubernetes.io/profile: "true"
-data:
-  %s: |-
-    %s
-`, configMapName, profileName, content)
-		configMapFile, err := ioutil.TempFile("", configMapName)
-		e.Nil(err)
-		_, err = configMapFile.WriteString(configMapString)
-		e.Nil(err)
-		return configMapFile.Name()
-	}
-
-	e.logf("Deploying invalid configMap")
-	invalidProfile := newProfile(`{ "defaultAction": true }`)
-	e.kubectl("create", "-f", invalidProfile)
-	defer func() {
-		e.kubectl("delete", "-f", invalidProfile)
-		e.Nil(os.RemoveAll(invalidProfile))
-	}()
-
-	// Verify the event
-	e.logf("Verifying events")
-	eventsOutput := e.kubectl("get", "events")
-	for _, s := range []string{
-		"Warning",
-		"InvalidSeccompProfile",
-		"configmap/" + configMapName,
-		"decoding seccomp profile: json: cannot unmarshal bool into " +
-			"Go struct field Seccomp.defaultAction of type seccomp.Action",
-	} {
-		e.Contains(eventsOutput, s)
-	}
-
-	// Check that the profile is not reconciled to the node
-	e.logf("Verifying node content")
-	configMap := e.getConfigMap(configMapName, "default")
-	profilePath, err := profile.GetProfilePath(profileName, configMap.ObjectMeta.Namespace, configMap.ObjectMeta.Name)
-	e.Nil(err)
-	for _, node := range nodes {
-		e.execNode(node, "bash", "-c", fmt.Sprintf("[ ! -f %s ]", profilePath))
-	}
-
-	// Make the profile valid
-	e.logf("Patching invalid configMap to be valid again")
-	validProfile := newProfile(`{ "defaultAction": "SCMP_ACT_ALLOW" }`)
-	e.kubectl("apply", "-f", validProfile)
-	defer e.Nil(os.RemoveAll(validProfile))
-	for _, node := range nodes {
-		e.execNode(node, "bash", "-c", fmt.Sprintf("[ -f %s ]", profilePath))
-	}
-}
diff --git a/test/tc_redeploy_operator_test.go b/test/tc_redeploy_operator_test.go
index ebc44cb9b..adb8c9497 100644
--- a/test/tc_redeploy_operator_test.go
+++ b/test/tc_redeploy_operator_test.go
@@ -30,4 +30,5 @@ func (e *e2e) cleanupOperator(manifest string) {
 	// Clean up the operator
 	e.logf("Cleaning up operator")
 	e.kubectl("delete", "-f", manifest)
+	e.kubectl("delete", "-f", crd)
 }