Repository: kubernetes-sigs/security-profiles-operator
Pull request #522 (2021-07-05 13:54:59)

Streamline container ID retrieval
#### What type of PR is this?

/kind cleanup

#### What this PR does / why we need it:
   
We now do not error any more if the cgroup path does not exist to reduce the log verbosity. This also fixes some smaller issues with the logger as well as cleans some unnecessarily complex code paths.

#### Which issue(s) this PR fixes:

None

#### Does this PR have test?

None

#### Special notes for your reviewer:
None
#### Does this PR introduce a user-facing change?

<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->

```release-note
None
```


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

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

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

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

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

- ~~[OWNERS](https://github.com/kubernetes-sigs/security-profiles-operator/blob/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: @saschagrunert: The following test **failed**, say `/retest` to rerun all failed tests:

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

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

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

<details>

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


```diff
@@            Coverage Diff             @@
##           master     #522      +/-   ##
==========================================
- Coverage   43.11%   42.90%   -0.22%     
==========================================
  Files          24       24              
  Lines        1285     1289       +4     
==========================================
- Hits          554      553       -1     
- Misses        692      696       +4     
- Partials       39       40       +1     
```


Patch:
diff --git a/api/server/api.pb.go b/api/server/api.pb.go
index 53300181b..06f30cd44 100644
--- a/api/server/api.pb.go
+++ b/api/server/api.pb.go
@@ -395,26 +395,26 @@ var file_api_server_api_proto_rawDesc = []byte{
 	0x10, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
 	0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x01, 0x20,
 	0x03, 0x28, 0x09, 0x52, 0x08, 0x73, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x22, 0x0f, 0x0a,
-	0x0d, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x97,
+	0x0d, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x99,
 	0x02, 0x0a, 0x18, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69,
-	0x6c, 0x65, 0x73, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x41, 0x0a, 0x0f, 0x4d,
+	0x6c, 0x65, 0x73, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x43, 0x0a, 0x0f, 0x4d,
 	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x41, 0x75, 0x64, 0x69, 0x74, 0x49, 0x6e, 0x63, 0x12, 0x18,
 	0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x41, 0x75, 0x64, 0x69,
 	0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45,
-	0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40,
-	0x0a, 0x0d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x12,
-	0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x53, 0x79, 0x73, 0x63,
-	0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x61, 0x70, 0x69,
-	0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
-	0x12, 0x39, 0x0a, 0x08, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x14, 0x2e, 0x61,
-	0x70, 0x69, 0x2e, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c,
-	0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0d, 0x52,
-	0x65, 0x73, 0x65, 0x74, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x14, 0x2e, 0x61,
-	0x70, 0x69, 0x2e, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x06, 0x5a, 0x04, 0x2f, 0x61, 0x70, 0x69,
-	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01,
+	0x12, 0x40, 0x0a, 0x0d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c,
+	0x6c, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x53, 0x79,
+	0x73, 0x63, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x61,
+	0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x22, 0x00, 0x12, 0x39, 0x0a, 0x08, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x14,
+	0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x79, 0x73, 0x63, 0x61,
+	0x6c, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a,
+	0x0d, 0x52, 0x65, 0x73, 0x65, 0x74, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x14,
+	0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x06, 0x5a, 0x04, 0x2f, 0x61,
+	0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/api/server/api.proto b/api/server/api.proto
index 3cc050d23..8b1c64012 100644
--- a/api/server/api.proto
+++ b/api/server/api.proto
@@ -20,7 +20,7 @@ package api;
 option go_package = "/api";
 
 service SecurityProfilesOperator {
-    rpc MetricsAuditInc(MetricsAuditRequest) returns (EmptyResponse) {}
+    rpc MetricsAuditInc(stream MetricsAuditRequest) returns (EmptyResponse) {}
     rpc RecordSyscall(RecordSyscallRequest) returns (EmptyResponse) {}
     rpc Syscalls(SyscallsRequest) returns (SyscallsResponse) {}
     rpc ResetSyscalls(SyscallsRequest) returns (EmptyResponse) {}
diff --git a/api/server/api_grpc.pb.go b/api/server/api_grpc.pb.go
index 9cfa6c7ab..a4dbdd4f1 100644
--- a/api/server/api_grpc.pb.go
+++ b/api/server/api_grpc.pb.go
@@ -18,7 +18,7 @@ const _ = grpc.SupportPackageIsVersion7
 //
 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
 type SecurityProfilesOperatorClient interface {
-	MetricsAuditInc(ctx context.Context, in *MetricsAuditRequest, opts ...grpc.CallOption) (*EmptyResponse, error)
+	MetricsAuditInc(ctx context.Context, opts ...grpc.CallOption) (SecurityProfilesOperator_MetricsAuditIncClient, error)
 	RecordSyscall(ctx context.Context, in *RecordSyscallRequest, opts ...grpc.CallOption) (*EmptyResponse, error)
 	Syscalls(ctx context.Context, in *SyscallsRequest, opts ...grpc.CallOption) (*SyscallsResponse, error)
 	ResetSyscalls(ctx context.Context, in *SyscallsRequest, opts ...grpc.CallOption) (*EmptyResponse, error)
@@ -32,13 +32,38 @@ func NewSecurityProfilesOperatorClient(cc grpc.ClientConnInterface) SecurityProf
 	return &securityProfilesOperatorClient{cc}
 }
 
-func (c *securityProfilesOperatorClient) MetricsAuditInc(ctx context.Context, in *MetricsAuditRequest, opts ...grpc.CallOption) (*EmptyResponse, error) {
-	out := new(EmptyResponse)
-	err := c.cc.Invoke(ctx, "/api.SecurityProfilesOperator/MetricsAuditInc", in, out, opts...)
+func (c *securityProfilesOperatorClient) MetricsAuditInc(ctx context.Context, opts ...grpc.CallOption) (SecurityProfilesOperator_MetricsAuditIncClient, error) {
+	stream, err := c.cc.NewStream(ctx, &SecurityProfilesOperator_ServiceDesc.Streams[0], "/api.SecurityProfilesOperator/MetricsAuditInc", opts...)
 	if err != nil {
 		return nil, err
 	}
-	return out, nil
+	x := &securityProfilesOperatorMetricsAuditIncClient{stream}
+	return x, nil
+}
+
+type SecurityProfilesOperator_MetricsAuditIncClient interface {
+	Send(*MetricsAuditRequest) error
+	CloseAndRecv() (*EmptyResponse, error)
+	grpc.ClientStream
+}
+
+type securityProfilesOperatorMetricsAuditIncClient struct {
+	grpc.ClientStream
+}
+
+func (x *securityProfilesOperatorMetricsAuditIncClient) Send(m *MetricsAuditRequest) error {
+	return x.ClientStream.SendMsg(m)
+}
+
+func (x *securityProfilesOperatorMetricsAuditIncClient) CloseAndRecv() (*EmptyResponse, error) {
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	m := new(EmptyResponse)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
 }
 
 func (c *securityProfilesOperatorClient) RecordSyscall(ctx context.Context, in *RecordSyscallRequest, opts ...grpc.CallOption) (*EmptyResponse, error) {
@@ -72,7 +97,7 @@ func (c *securityProfilesOperatorClient) ResetSyscalls(ctx context.Context, in *
 // All implementations must embed UnimplementedSecurityProfilesOperatorServer
 // for forward compatibility
 type SecurityProfilesOperatorServer interface {
-	MetricsAuditInc(context.Context, *MetricsAuditRequest) (*EmptyResponse, error)
+	MetricsAuditInc(SecurityProfilesOperator_MetricsAuditIncServer) error
 	RecordSyscall(context.Context, *RecordSyscallRequest) (*EmptyResponse, error)
 	Syscalls(context.Context, *SyscallsRequest) (*SyscallsResponse, error)
 	ResetSyscalls(context.Context, *SyscallsRequest) (*EmptyResponse, error)
@@ -83,8 +108,8 @@ type SecurityProfilesOperatorServer interface {
 type UnimplementedSecurityProfilesOperatorServer struct {
 }
 
-func (UnimplementedSecurityProfilesOperatorServer) MetricsAuditInc(context.Context, *MetricsAuditRequest) (*EmptyResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method MetricsAuditInc not implemented")
+func (UnimplementedSecurityProfilesOperatorServer) MetricsAuditInc(SecurityProfilesOperator_MetricsAuditIncServer) error {
+	return status.Errorf(codes.Unimplemented, "method MetricsAuditInc not implemented")
 }
 func (UnimplementedSecurityProfilesOperatorServer) RecordSyscall(context.Context, *RecordSyscallRequest) (*EmptyResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method RecordSyscall not implemented")
@@ -109,22 +134,30 @@ func RegisterSecurityProfilesOperatorServer(s grpc.ServiceRegistrar, srv Securit
 	s.RegisterService(&SecurityProfilesOperator_ServiceDesc, srv)
 }
 
-func _SecurityProfilesOperator_MetricsAuditInc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(MetricsAuditRequest)
-	if err := dec(in); err != nil {
+func _SecurityProfilesOperator_MetricsAuditInc_Handler(srv interface{}, stream grpc.ServerStream) error {
+	return srv.(SecurityProfilesOperatorServer).MetricsAuditInc(&securityProfilesOperatorMetricsAuditIncServer{stream})
+}
+
+type SecurityProfilesOperator_MetricsAuditIncServer interface {
+	SendAndClose(*EmptyResponse) error
+	Recv() (*MetricsAuditRequest, error)
+	grpc.ServerStream
+}
+
+type securityProfilesOperatorMetricsAuditIncServer struct {
+	grpc.ServerStream
+}
+
+func (x *securityProfilesOperatorMetricsAuditIncServer) SendAndClose(m *EmptyResponse) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+func (x *securityProfilesOperatorMetricsAuditIncServer) Recv() (*MetricsAuditRequest, error) {
+	m := new(MetricsAuditRequest)
+	if err := x.ServerStream.RecvMsg(m); err != nil {
 		return nil, err
 	}
-	if interceptor == nil {
-		return srv.(SecurityProfilesOperatorServer).MetricsAuditInc(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/api.SecurityProfilesOperator/MetricsAuditInc",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(SecurityProfilesOperatorServer).MetricsAuditInc(ctx, req.(*MetricsAuditRequest))
-	}
-	return interceptor(ctx, in, info, handler)
+	return m, nil
 }
 
 func _SecurityProfilesOperator_RecordSyscall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
@@ -188,10 +221,6 @@ var SecurityProfilesOperator_ServiceDesc = grpc.ServiceDesc{
 	ServiceName: "api.SecurityProfilesOperator",
 	HandlerType: (*SecurityProfilesOperatorServer)(nil),
 	Methods: []grpc.MethodDesc{
-		{
-			MethodName: "MetricsAuditInc",
-			Handler:    _SecurityProfilesOperator_MetricsAuditInc_Handler,
-		},
 		{
 			MethodName: "RecordSyscall",
 			Handler:    _SecurityProfilesOperator_RecordSyscall_Handler,
@@ -205,6 +234,12 @@ var SecurityProfilesOperator_ServiceDesc = grpc.ServiceDesc{
 			Handler:    _SecurityProfilesOperator_ResetSyscalls_Handler,
 		},
 	},
-	Streams:  []grpc.StreamDesc{},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "MetricsAuditInc",
+			Handler:       _SecurityProfilesOperator_MetricsAuditInc_Handler,
+			ClientStreams: true,
+		},
+	},
 	Metadata: "api/server/api.proto",
 }
diff --git a/internal/pkg/daemon/enricher/audit.go b/internal/pkg/daemon/enricher/audit.go
index 9dee739ce..2dee449a8 100644
--- a/internal/pkg/daemon/enricher/audit.go
+++ b/internal/pkg/daemon/enricher/audit.go
@@ -25,8 +25,9 @@ import (
 
 // type IDs are defined at https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/audit.h
 var (
-	//nolint:lll
-	seccompLineRegex = regexp.MustCompile(`(type=SECCOMP|audit:.+type=1326).+audit\((.+)\).+pid=(\b\d+\b).+exe="(.+)".+syscall=(\b\d+\b).*`)
+	seccompLineRegex = regexp.MustCompile(
+		`(type=SECCOMP|audit:.+type=1326).+audit\((.+)\).+pid=(\b\d+\b).+exe="(.+)".+syscall=(\b\d+\b).*`,
+	)
 	selinuxLineRegex = regexp.MustCompile(`type=AVC.+audit\((.+)\).+pid=(\b\d+\b).*`)
 )
 
@@ -56,7 +57,7 @@ func extractAuditLine(logLine string) (*auditLine, error) {
 		return selinux, nil
 	}
 
-	return nil, errors.Wrap(errUnsupportedLogLine, logLine)
+	return nil, errors.Errorf("unsupported log line: %s", logLine)
 }
 
 func extractSeccompLine(logLine string) *auditLine {
diff --git a/internal/pkg/daemon/enricher/container.go b/internal/pkg/daemon/enricher/container.go
index 46bf70858..2651b9f0b 100644
--- a/internal/pkg/daemon/enricher/container.go
+++ b/internal/pkg/daemon/enricher/container.go
@@ -24,7 +24,6 @@ import (
 	"strconv"
 
 	"github.com/ReneKroon/ttlcache/v2"
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
@@ -37,7 +36,7 @@ import (
 // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;
 
 func (e *Enricher) getContainerInfo(
-	logger logr.Logger, nodeName, containerID string,
+	nodeName, containerID string,
 ) (*containerInfo, error) {
 	// Check the cache first
 	if info, err := e.infoCache.Get(
@@ -56,6 +55,7 @@ func (e *Enricher) getContainerInfo(
 		return nil, errors.Wrap(err, "load in-cluster config")
 	}
 
+	errContainerIDEmpty := errors.New("container ID is empty")
 	if err := util.Retry(
 		func() (retryErr error) {
 			pods, err := e.impl.ListPods(clientset, nodeName)
@@ -72,7 +72,7 @@ func (e *Enricher) getContainerInfo(
 					containerName := containerStatus.Name
 
 					if containerID == "" {
-						logger.Info(
+						e.logger.Info(
 							"container ID is still empty, retrying",
 							"podName", pod.Name,
 							"containerName", containerName,
@@ -82,7 +82,7 @@ func (e *Enricher) getContainerInfo(
 
 					rawContainerID := regexID.FindString(containerID)
 					if rawContainerID == "" {
-						logger.Error(
+						e.logger.Error(
 							err, "unable to get container ID",
 							"podName", pod.Name,
 							"containerName", containerName,
@@ -123,6 +123,9 @@ func (e *Enricher) getContainerInfo(
 	return nil, errors.New("no container info for container ID")
 }
 
+// We assume that a container ID has a length of 64.
+var regexID = regexp.MustCompile(`[0-9a-f]{64}`)
+
 func (e *Enricher) getContainerID(processID int) (string, error) {
 	// Check the cache first
 	if id, err := e.containerIDCache.Get(
@@ -131,11 +134,13 @@ func (e *Enricher) getContainerID(processID int) (string, error) {
 		return id.(string), nil
 	}
 
-	cgroupFile := fmt.Sprintf("/proc/%d/cgroup", processID)
-	file, err := e.impl.Open(filepath.Clean(cgroupFile))
+	cgroupPath := fmt.Sprintf("/proc/%d/cgroup", processID)
+
+	file, err := e.impl.Open(filepath.Clean(cgroupPath))
 	if err != nil {
-		return "", errors.Errorf("could not open cgroup path %s", cgroupFile)
+		return "", errors.Wrap(err, "could not open cgroup path")
 	}
+
 	defer func() {
 		cerr := file.Close()
 		if err == nil {
@@ -144,9 +149,8 @@ func (e *Enricher) getContainerID(processID int) (string, error) {
 	}()
 
 	scanner := bufio.NewScanner(file)
-	scanner.Split(bufio.ScanLines)
 	for scanner.Scan() {
-		if containerID := extractID(scanner.Text()); containerID != "" {
+		if containerID := regexID.FindString(scanner.Text()); containerID != "" {
 			// Update the cache
 			if err := e.containerIDCache.Set(
 				strconv.Itoa(processID), containerID,
@@ -160,10 +164,3 @@ func (e *Enricher) getContainerID(processID int) (string, error) {
 
 	return "", errors.New("unable to find container ID from cgroup path")
 }
-
-// We assume that a container ID has a length of 64.
-var regexID = regexp.MustCompile(`[0-9a-f]{64}`)
-
-func extractID(cgroup string) string {
-	return regexID.FindString(cgroup)
-}
diff --git a/internal/pkg/daemon/enricher/enricher.go b/internal/pkg/daemon/enricher/enricher.go
index ce11ea1b0..fe029f890 100644
--- a/internal/pkg/daemon/enricher/enricher.go
+++ b/internal/pkg/daemon/enricher/enricher.go
@@ -82,6 +82,11 @@ func (e *Enricher) Run() error {
 	defer e.impl.Close(conn)
 	client := api.NewSecurityProfilesOperatorClient(conn)
 
+	metricsAuditIncClient, err := e.impl.MetricsAuditInc(client)
+	if err != nil {
+		return errors.Wrap(err, "create metrics audit client")
+	}
+
 	// If the file does not exist, then tail will wait for it to appear
 	tailFile, err := e.impl.TailFile(
 		config.AuditLogPath,
@@ -117,12 +122,19 @@ func (e *Enricher) Run() error {
 		}
 
 		cID, err := e.getContainerID(auditLine.processID)
+		if errors.Is(err, os.ErrNotExist) {
+			// We're probably in container creation or removal
+			continue
+		}
 		if err != nil {
-			e.logger.Error(err, "unable to get container ID", "processID", auditLine.processID)
+			e.logger.Error(
+				err, "unable to get container ID",
+				"processID", auditLine.processID,
+			)
 			continue
 		}
 
-		info, err := e.getContainerInfo(e.logger, nodeName, cID)
+		info, err := e.getContainerInfo(nodeName, cID)
 		if err != nil {
 			e.logger.Error(
 				err, "container ID not found in cluster",
@@ -150,8 +162,8 @@ func (e *Enricher) Run() error {
 		if auditLine.type_ == auditTypeSelinux {
 			metricsType = api.MetricsAuditRequest_SELINUX
 		}
-		if _, err := e.impl.MetricsAuditInc(
-			client,
+		if err := e.impl.SendMetric(
+			metricsAuditIncClient,
 			&api.MetricsAuditRequest{
 				Type:       metricsType,
 				Node:       nodeName,
diff --git a/internal/pkg/daemon/enricher/enricherfakes/fake_impl.go b/internal/pkg/daemon/enricher/enricherfakes/fake_impl.go
index e9a776a64..a1e8683a5 100644
--- a/internal/pkg/daemon/enricher/enricherfakes/fake_impl.go
+++ b/internal/pkg/daemon/enricher/enricherfakes/fake_impl.go
@@ -101,18 +101,17 @@ type FakeImpl struct {
 		result1 *v1.PodList
 		result2 error
 	}
-	MetricsAuditIncStub        func(api.SecurityProfilesOperatorClient, *api.MetricsAuditRequest) (*api.EmptyResponse, error)
+	MetricsAuditIncStub        func(api.SecurityProfilesOperatorClient) (api.SecurityProfilesOperator_MetricsAuditIncClient, error)
 	metricsAuditIncMutex       sync.RWMutex
 	metricsAuditIncArgsForCall []struct {
 		arg1 api.SecurityProfilesOperatorClient
-		arg2 *api.MetricsAuditRequest
 	}
 	metricsAuditIncReturns struct {
-		result1 *api.EmptyResponse
+		result1 api.SecurityProfilesOperator_MetricsAuditIncClient
 		result2 error
 	}
 	metricsAuditIncReturnsOnCall map[int]struct {
-		result1 *api.EmptyResponse
+		result1 api.SecurityProfilesOperator_MetricsAuditIncClient
 		result2 error
 	}
 	NewForConfigStub        func(*rest.Config) (*kubernetes.Clientset, error)
@@ -155,6 +154,18 @@ type FakeImpl struct {
 		result1 *api.EmptyResponse
 		result2 error
 	}
+	SendMetricStub        func(api.SecurityProfilesOperator_MetricsAuditIncClient, *api.MetricsAuditRequest) error
+	sendMetricMutex       sync.RWMutex
+	sendMetricArgsForCall []struct {
+		arg1 api.SecurityProfilesOperator_MetricsAuditIncClient
+		arg2 *api.MetricsAuditRequest
+	}
+	sendMetricReturns struct {
+		result1 error
+	}
+	sendMetricReturnsOnCall map[int]struct {
+		result1 error
+	}
 	TailFileStub        func(string, tail.Config) (*tail.Tail, error)
 	tailFileMutex       sync.RWMutex
 	tailFileArgsForCall []struct {
@@ -533,19 +544,18 @@ func (fake *FakeImpl) ListPodsReturnsOnCall(i int, result1 *v1.PodList, result2
 	}{result1, result2}
 }
 
-func (fake *FakeImpl) MetricsAuditInc(arg1 api.SecurityProfilesOperatorClient, arg2 *api.MetricsAuditRequest) (*api.EmptyResponse, error) {
+func (fake *FakeImpl) MetricsAuditInc(arg1 api.SecurityProfilesOperatorClient) (api.SecurityProfilesOperator_MetricsAuditIncClient, error) {
 	fake.metricsAuditIncMutex.Lock()
 	ret, specificReturn := fake.metricsAuditIncReturnsOnCall[len(fake.metricsAuditIncArgsForCall)]
 	fake.metricsAuditIncArgsForCall = append(fake.metricsAuditIncArgsForCall, struct {
 		arg1 api.SecurityProfilesOperatorClient
-		arg2 *api.MetricsAuditRequest
-	}{arg1, arg2})
+	}{arg1})
 	stub := fake.MetricsAuditIncStub
 	fakeReturns := fake.metricsAuditIncReturns
-	fake.recordInvocation("MetricsAuditInc", []interface{}{arg1, arg2})
+	fake.recordInvocation("MetricsAuditInc", []interface{}{arg1})
 	fake.metricsAuditIncMutex.Unlock()
 	if stub != nil {
-		return stub(arg1, arg2)
+		return stub(arg1)
 	}
 	if specificReturn {
 		return ret.result1, ret.result2
@@ -559,41 +569,41 @@ func (fake *FakeImpl) MetricsAuditIncCallCount() int {
 	return len(fake.metricsAuditIncArgsForCall)
 }
 
-func (fake *FakeImpl) MetricsAuditIncCalls(stub func(api.SecurityProfilesOperatorClient, *api.MetricsAuditRequest) (*api.EmptyResponse, error)) {
+func (fake *FakeImpl) MetricsAuditIncCalls(stub func(api.SecurityProfilesOperatorClient) (api.SecurityProfilesOperator_MetricsAuditIncClient, error)) {
 	fake.metricsAuditIncMutex.Lock()
 	defer fake.metricsAuditIncMutex.Unlock()
 	fake.MetricsAuditIncStub = stub
 }
 
-func (fake *FakeImpl) MetricsAuditIncArgsForCall(i int) (api.SecurityProfilesOperatorClient, *api.MetricsAuditRequest) {
+func (fake *FakeImpl) MetricsAuditIncArgsForCall(i int) api.SecurityProfilesOperatorClient {
 	fake.metricsAuditIncMutex.RLock()
 	defer fake.metricsAuditIncMutex.RUnlock()
 	argsForCall := fake.metricsAuditIncArgsForCall[i]
-	return argsForCall.arg1, argsForCall.arg2
+	return argsForCall.arg1
 }
 
-func (fake *FakeImpl) MetricsAuditIncReturns(result1 *api.EmptyResponse, result2 error) {
+func (fake *FakeImpl) MetricsAuditIncReturns(result1 api.SecurityProfilesOperator_MetricsAuditIncClient, result2 error) {
 	fake.metricsAuditIncMutex.Lock()
 	defer fake.metricsAuditIncMutex.Unlock()
 	fake.MetricsAuditIncStub = nil
 	fake.metricsAuditIncReturns = struct {
-		result1 *api.EmptyResponse
+		result1 api.SecurityProfilesOperator_MetricsAuditIncClient
 		result2 error
 	}{result1, result2}
 }
 
-func (fake *FakeImpl) MetricsAuditIncReturnsOnCall(i int, result1 *api.EmptyResponse, result2 error) {
+func (fake *FakeImpl) MetricsAuditIncReturnsOnCall(i int, result1 api.SecurityProfilesOperator_MetricsAuditIncClient, result2 error) {
 	fake.metricsAuditIncMutex.Lock()
 	defer fake.metricsAuditIncMutex.Unlock()
 	fake.MetricsAuditIncStub = nil
 	if fake.metricsAuditIncReturnsOnCall == nil {
 		fake.metricsAuditIncReturnsOnCall = make(map[int]struct {
-			result1 *api.EmptyResponse
+			result1 api.SecurityProfilesOperator_MetricsAuditIncClient
 			result2 error
 		})
 	}
 	fake.metricsAuditIncReturnsOnCall[i] = struct {
-		result1 *api.EmptyResponse
+		result1 api.SecurityProfilesOperator_MetricsAuditIncClient
 		result2 error
 	}{result1, result2}
 }
@@ -791,6 +801,68 @@ func (fake *FakeImpl) RecordSyscallReturnsOnCall(i int, result1 *api.EmptyRespon
 	}{result1, result2}
 }
 
+func (fake *FakeImpl) SendMetric(arg1 api.SecurityProfilesOperator_MetricsAuditIncClient, arg2 *api.MetricsAuditRequest) error {
+	fake.sendMetricMutex.Lock()
+	ret, specificReturn := fake.sendMetricReturnsOnCall[len(fake.sendMetricArgsForCall)]
+	fake.sendMetricArgsForCall = append(fake.sendMetricArgsForCall, struct {
+		arg1 api.SecurityProfilesOperator_MetricsAuditIncClient
+		arg2 *api.MetricsAuditRequest
+	}{arg1, arg2})
+	stub := fake.SendMetricStub
+	fakeReturns := fake.sendMetricReturns
+	fake.recordInvocation("SendMetric", []interface{}{arg1, arg2})
+	fake.sendMetricMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeImpl) SendMetricCallCount() int {
+	fake.sendMetricMutex.RLock()
+	defer fake.sendMetricMutex.RUnlock()
+	return len(fake.sendMetricArgsForCall)
+}
+
+func (fake *FakeImpl) SendMetricCalls(stub func(api.SecurityProfilesOperator_MetricsAuditIncClient, *api.MetricsAuditRequest) error) {
+	fake.sendMetricMutex.Lock()
+	defer fake.sendMetricMutex.Unlock()
+	fake.SendMetricStub = stub
+}
+
+func (fake *FakeImpl) SendMetricArgsForCall(i int) (api.SecurityProfilesOperator_MetricsAuditIncClient, *api.MetricsAuditRequest) {
+	fake.sendMetricMutex.RLock()
+	defer fake.sendMetricMutex.RUnlock()
+	argsForCall := fake.sendMetricArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeImpl) SendMetricReturns(result1 error) {
+	fake.sendMetricMutex.Lock()
+	defer fake.sendMetricMutex.Unlock()
+	fake.SendMetricStub = nil
+	fake.sendMetricReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeImpl) SendMetricReturnsOnCall(i int, result1 error) {
+	fake.sendMetricMutex.Lock()
+	defer fake.sendMetricMutex.Unlock()
+	fake.SendMetricStub = nil
+	if fake.sendMetricReturnsOnCall == nil {
+		fake.sendMetricReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.sendMetricReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
 func (fake *FakeImpl) TailFile(arg1 string, arg2 tail.Config) (*tail.Tail, error) {
 	fake.tailFileMutex.Lock()
 	ret, specificReturn := fake.tailFileReturnsOnCall[len(fake.tailFileArgsForCall)]
@@ -879,6 +951,8 @@ func (fake *FakeImpl) Invocations() map[string][][]interface{} {
 	defer fake.openMutex.RUnlock()
 	fake.recordSyscallMutex.RLock()
 	defer fake.recordSyscallMutex.RUnlock()
+	fake.sendMetricMutex.RLock()
+	defer fake.sendMetricMutex.RUnlock()
 	fake.tailFileMutex.RLock()
 	defer fake.tailFileMutex.RUnlock()
 	copiedInvocations := map[string][][]interface{}{}
diff --git a/internal/pkg/daemon/enricher/impl.go b/internal/pkg/daemon/enricher/impl.go
index 2bd6c7e55..0a54cd978 100644
--- a/internal/pkg/daemon/enricher/impl.go
+++ b/internal/pkg/daemon/enricher/impl.go
@@ -47,8 +47,11 @@ type impl interface {
 	ListPods(c *kubernetes.Clientset, nodeName string) (*v1.PodList, error)
 	MetricsAuditInc(
 		client api.SecurityProfilesOperatorClient,
+	) (api.SecurityProfilesOperator_MetricsAuditIncClient, error)
+	SendMetric(
+		client api.SecurityProfilesOperator_MetricsAuditIncClient,
 		in *api.MetricsAuditRequest,
-	) (*api.EmptyResponse, error)
+	) error
 	RecordSyscall(
 		client api.SecurityProfilesOperatorClient,
 		in *api.RecordSyscallRequest,
@@ -101,9 +104,15 @@ func (d *defaultImpl) ListPods(
 
 func (d *defaultImpl) MetricsAuditInc(
 	client api.SecurityProfilesOperatorClient,
+) (api.SecurityProfilesOperator_MetricsAuditIncClient, error) {
+	return client.MetricsAuditInc(context.Background())
+}
+
+func (d *defaultImpl) SendMetric(
+	client api.SecurityProfilesOperator_MetricsAuditIncClient,
 	in *api.MetricsAuditRequest,
-) (*api.EmptyResponse, error) {
-	return client.MetricsAuditInc(context.Background(), in)
+) error {
+	return client.Send(in)
 }
 
 func (d *defaultImpl) RecordSyscall(
diff --git a/internal/pkg/daemon/enricher/types.go b/internal/pkg/daemon/enricher/types.go
index 43a8292d7..9ece93ab8 100644
--- a/internal/pkg/daemon/enricher/types.go
+++ b/internal/pkg/daemon/enricher/types.go
@@ -16,13 +16,6 @@ limitations under the License.
 
 package enricher
 
-import "errors"
-
-var (
-	errUnsupportedLogLine = errors.New("unsupported log line")
-	errContainerIDEmpty   = errors.New("container ID is empty")
-)
-
 const (
 	auditTypeSeccomp = "seccomp"
 	auditTypeSelinux = "selinux"
diff --git a/internal/pkg/daemon/server/server.go b/internal/pkg/daemon/server/server.go
index 216231c5f..2f3b1e009 100644
--- a/internal/pkg/daemon/server/server.go
+++ b/internal/pkg/daemon/server/server.go
@@ -18,6 +18,7 @@ package server
 
 import (
 	"context"
+	"io"
 	"net"
 	"sync"
 
@@ -81,28 +82,38 @@ func Addr() string {
 // MetricSeccompProfileAuditInc updates the metrics for the seccomp audit
 // counter.
 func (s *Server) MetricsAuditInc(
-	ctx context.Context, r *api.MetricsAuditRequest,
-) (*api.EmptyResponse, error) {
-	if r.GetType() == api.MetricsAuditRequest_SECCOMP {
-		s.metrics.IncSeccompProfileAudit(
-			r.GetNode(),
-			r.GetNamespace(),
-			r.GetPod(),
-			r.GetContainer(),
-			r.GetExecutable(),
-			r.GetSyscall(),
-		)
-	} else if r.GetType() == api.MetricsAuditRequest_SELINUX {
-		s.metrics.IncSelinuxProfileAudit(
-			r.GetNode(),
-			r.GetNamespace(),
-			r.GetPod(),
-			r.GetContainer(),
-			r.GetExecutable(),
-			r.GetSyscall(),
-		)
+	stream api.SecurityProfilesOperator_MetricsAuditIncServer,
+) error {
+	for {
+		r, err := stream.Recv()
+		if errors.Is(err, io.EOF) {
+			return stream.SendAndClose(&api.EmptyResponse{})
+		}
+		if err != nil {
+			return errors.Wrap(err, "record syscalls")
+		}
+
+		switch r.GetType() {
+		case api.MetricsAuditRequest_SECCOMP:
+			s.metrics.IncSeccompProfileAudit(
+				r.GetNode(),
+				r.GetNamespace(),
+				r.GetPod(),
+				r.GetContainer(),
+				r.GetExecutable(),
+				r.GetSyscall(),
+			)
+		case api.MetricsAuditRequest_SELINUX:
+			s.metrics.IncSelinuxProfileAudit(
+				r.GetNode(),
+				r.GetNamespace(),
+				r.GetPod(),
+				r.GetContainer(),
+				r.GetExecutable(),
+				r.GetSyscall(),
+			)
+		}
 	}
-	return &api.EmptyResponse{}, nil
 }
 
 // RecordSyscall traces a single syscall for a provided profile.


Test patch:
diff --git a/internal/pkg/daemon/enricher/container_test.go b/internal/pkg/daemon/enricher/container_test.go
index ead15c507..2e5b4ca7c 100644
--- a/internal/pkg/daemon/enricher/container_test.go
+++ b/internal/pkg/daemon/enricher/container_test.go
@@ -31,8 +31,8 @@ func TestExtractContainerID(t *testing.T) {
 	}{
 		{
 			"Should extract crio ID",
-			//nolint:lll
-			`4:net_cls,net_prio:/kubepods/besteffort/pod26ba375c-2266-4ecc-bf2d-b626db8762af/crio-af208fd68bf39a07a439ed0c9b6609b9ae63ecd8a5f1a2af3e0db48b945b320a`,
+			`4:net_cls,net_prio:/kubepods/besteffort/pod26ba375c-2266-4ecc-bf2d-b626db8762af/` +
+				`crio-af208fd68bf39a07a439ed0c9b6609b9ae63ecd8a5f1a2af3e0db48b945b320a`,
 			"af208fd68bf39a07a439ed0c9b6609b9ae63ecd8a5f1a2af3e0db48b945b320a",
 		},
 		{
@@ -50,7 +50,7 @@ func TestExtractContainerID(t *testing.T) {
 		tt := tt
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
-			got := extractID(tt.cgroupLine)
+			got := regexID.FindString(tt.cgroupLine)
 			require.Equal(t, tt.want, got)
 		})
 	}
diff --git a/internal/pkg/daemon/enricher/enricher_test.go b/internal/pkg/daemon/enricher/enricher_test.go
index 47a8b19f7..e3e23d507 100644
--- a/internal/pkg/daemon/enricher/enricher_test.go
+++ b/internal/pkg/daemon/enricher/enricher_test.go
@@ -93,11 +93,11 @@ func TestRun(t *testing.T) {
 					Time: time.Now(),
 				}
 
-				for mock.MetricsAuditIncCallCount() != 1 {
+				for mock.SendMetricCallCount() != 1 {
 					// Wait for MetricsAuditIncCallCount to be called
 				}
 
-				_, res := mock.MetricsAuditIncArgsForCall(0)
+				_, res := mock.SendMetricArgsForCall(0)
 				require.Equal(t, node, res.Node)
 				require.Equal(t, namespace, res.Namespace)
 				require.Equal(t, pod, res.Pod)
diff --git a/test/e2e_test.go b/test/e2e_test.go
index 81b590c4f..c6027c645 100644
--- a/test/e2e_test.go
+++ b/test/e2e_test.go
@@ -126,9 +126,11 @@ func (e *e2e) TestSecurityProfilesOperator() {
 	e.Run("cluster-wide: Seccomp: Verify profile recording", func() {
 		e.testCaseProfileRecordingStaticPodHook()
 		e.testCaseProfileRecordingStaticPodLogs()
-		e.testCaseProfileRecordingKubectlRun()
-		e.testCaseProfileRecordingMultiContainer()
-		e.testCaseProfileRecordingDeployment()
+		e.testCaseProfileRecordingKubectlRunHook()
+		e.testCaseProfileRecordingMultiContainerHook()
+		e.testCaseProfileRecordingMultiContainerLogs()
+		e.testCaseProfileRecordingDeploymentHook()
+		e.testCaseProfileRecordingDeploymentLogs()
 	})
 
 	// Clean up cluster-wide deployment to prepare for namespace deployment
diff --git a/test/tc_profilerecordings_test.go b/test/tc_profilerecordings_test.go
index 48a4dac48..9e22672a4 100644
--- a/test/tc_profilerecordings_test.go
+++ b/test/tc_profilerecordings_test.go
@@ -19,6 +19,7 @@ package e2e_test
 import (
 	"io/ioutil"
 	"os"
+	"regexp"
 	"time"
 )
 
@@ -28,18 +29,45 @@ const (
 	recordingName            = "test-recording"
 )
 
+func (e *e2e) waitForEnricherLogs(since time.Time, conditions ...*regexp.Regexp) {
+	for i := 0; i < 10; i++ {
+		e.logf("Waiting for enricher to record syscalls")
+		logs := e.kubectlOperatorNS(
+			"logs",
+			"--since-time="+since.Format(time.RFC3339),
+			"ds/spod",
+			"log-enricher",
+		)
+
+		matchAll := true
+		for _, condition := range conditions {
+			if !condition.MatchString(logs) {
+				matchAll = false
+			}
+		}
+		if matchAll {
+			break
+		}
+
+		time.Sleep(3 * time.Second)
+	}
+}
+
 func (e *e2e) testCaseProfileRecordingStaticPodHook() {
 	e.profileRecordingTestCase()
-	e.profileRecordingStaticPod(exampleRecordingHookPath, 0)
+	e.profileRecordingStaticPod(exampleRecordingHookPath)
 }
 
 func (e *e2e) testCaseProfileRecordingStaticPodLogs() {
 	e.profileRecordingTestCase()
 	e.logEnricherOnlyTestCase()
-	e.profileRecordingStaticPod(exampleRecordingLogsPath, 10*time.Second)
+	e.profileRecordingStaticPod(
+		exampleRecordingLogsPath,
+		regexp.MustCompile(`(?m)"syscallName"="setuid"`),
+	)
 }
 
-func (e *e2e) profileRecordingStaticPod(recording string, sleepTime time.Duration) {
+func (e *e2e) profileRecordingStaticPod(recording string, waitConditions ...*regexp.Regexp) {
 	e.logf("Creating recording for static pod test")
 	e.kubectl("create", "-f", recording)
 
@@ -63,6 +91,8 @@ spec:
 	e.Nil(err)
 	err = testPodFile.Close()
 	e.Nil(err)
+
+	since := time.Now() // nolint: ifshort
 	e.kubectl("create", "-f", testPodFile.Name())
 
 	e.logf("Waiting for test pod to be initialized")
@@ -70,41 +100,61 @@ spec:
 	e.retryGet("pod", podName)
 	e.waitFor("condition=ready", "pod", podName)
 
-	time.Sleep(sleepTime)
+	if waitConditions != nil {
+		e.waitForEnricherLogs(since, waitConditions...)
+	}
+
 	e.kubectl("delete", "pod", podName)
 
 	resourceName := recordingName + "-nginx"
 	profile := e.retryGetSeccompProfile(resourceName)
-	e.Contains(profile, "close")
+	e.Contains(profile, "setuid")
 
 	e.kubectl("delete", "-f", recording)
 	e.Nil(os.Remove(testPodFile.Name()))
 	e.kubectl("delete", "sp", resourceName)
 }
 
-func (e *e2e) testCaseProfileRecordingKubectlRun() {
+func (e *e2e) testCaseProfileRecordingKubectlRunHook() {
 	e.profileRecordingTestCase()
 
 	e.logf("Creating recording for kubectl run test")
 	e.kubectl("create", "-f", exampleRecordingHookPath)
-	defer e.kubectl("delete", "-f", exampleRecordingHookPath)
 
 	e.logf("Creating test pod")
 	e.kubectlRun("--labels=app=alpine", "fedora", "--", "echo", "test")
 
 	resourceName := recordingName + "-fedora"
 	profile := e.retryGetSeccompProfile(resourceName)
-	defer e.kubectl("delete", "sp", resourceName)
 	e.Contains(profile, "prctl")
 	e.NotContains(profile, "mkdir")
+
+	e.kubectl("delete", "-f", exampleRecordingHookPath)
+	e.kubectl("delete", "sp", resourceName)
+}
+
+func (e *e2e) testCaseProfileRecordingMultiContainerHook() {
+	e.profileRecordingTestCase()
+	e.profileRecordingMultiContainer(exampleRecordingHookPath)
+}
+
+func (e *e2e) testCaseProfileRecordingMultiContainerLogs() {
+	e.profileRecordingTestCase()
+	e.logEnricherOnlyTestCase()
+	e.profileRecordingMultiContainer(
+		exampleRecordingLogsPath,
+		regexp.MustCompile(`(?m)"container"="nginx".*"syscallName"="setuid"`),
+		regexp.MustCompile(`(?m)"container"="redis".*"syscallName"="epoll_wait"`),
+	)
 }
 
-func (e *e2e) testCaseProfileRecordingMultiContainer() {
+func (e *e2e) profileRecordingMultiContainer(
+	recording string, waitConditions ...*regexp.Regexp,
+) {
 	e.profileRecordingTestCase()
 
 	e.logf("Creating recording for multi container test")
-	e.kubectl("create", "-f", exampleRecordingHookPath)
-	defer e.kubectl("delete", "-f", exampleRecordingHookPath)
+	e.kubectl("create", "-f", recording)
 
 	e.logf("Creating test pod")
 	const testPod = `
@@ -124,32 +174,61 @@ spec:
 `
 	testPodFile, err := ioutil.TempFile(os.TempDir(), "recording-pod*.yaml")
 	e.Nil(err)
-	defer os.Remove(testPodFile.Name())
 	_, err = testPodFile.Write([]byte(testPod))
 	e.Nil(err)
 	err = testPodFile.Close()
 	e.Nil(err)
+
+	since := time.Now() // nolint: ifshort
 	e.kubectl("create", "-f", testPodFile.Name())
 
 	e.logf("Waiting for test pod to be initialized")
 	const podName = "my-pod"
 	e.retryGet("pod", podName)
 	e.waitFor("condition=ready", "pod", podName)
+
+	if waitConditions != nil {
+		e.waitForEnricherLogs(since, waitConditions...)
+	}
+
 	e.kubectl("delete", "pod", podName)
 
-	profileRedis := e.retryGetSeccompProfile(recordingName + "-redis")
-	profileNginx := e.retryGetSeccompProfile(recordingName + "-nginx")
-	defer e.kubectl("delete", "sp", "--all")
-	e.Contains(profileNginx, "unlink")
-	e.Contains(profileRedis, "nanosleep")
+	const profileNameRedis = recordingName + "-redis"
+	profileRedis := e.retryGetSeccompProfile(profileNameRedis)
+	e.Contains(profileRedis, "epoll_wait")
+
+	const profileNameNginx = recordingName + "-nginx"
+	profileNginx := e.retryGetSeccompProfile(profileNameNginx)
+	e.Contains(profileNginx, "close")
+
+	e.kubectl("delete", "-f", recording)
+	e.Nil(os.Remove(testPodFile.Name()))
+	e.kubectl("delete", "sp", profileNameRedis, profileNameNginx)
+}
+
+func (e *e2e) testCaseProfileRecordingDeploymentHook() {
+	e.profileRecordingTestCase()
+	e.profileRecordingDeployment(exampleRecordingHookPath)
 }
 
-func (e *e2e) testCaseProfileRecordingDeployment() {
+func (e *e2e) testCaseProfileRecordingDeploymentLogs() {
+	e.profileRecordingTestCase()
+	e.logEnricherOnlyTestCase()
+	e.profileRecordingDeployment(
+		exampleRecordingLogsPath,
+		regexp.MustCompile(
+			`(?s)"container"="nginx".*"syscallName"="setuid"`+
+				`.*"container"="nginx".*"syscallName"="setuid"`),
+	)
+}
+
+func (e *e2e) profileRecordingDeployment(
+	recording string, waitConditions ...*regexp.Regexp,
+) {
 	e.profileRecordingTestCase()
 
 	e.logf("Creating recording for deployment test")
-	e.kubectl("create", "-f", exampleRecordingHookPath)
-	defer e.kubectl("delete", "-f", exampleRecordingHookPath)
+	e.kubectl("create", "-f", recording)
 
 	e.logf("Creating test deployment")
 	const testDeployment = `
@@ -173,23 +252,34 @@ spec:
 `
 	testFile, err := ioutil.TempFile(os.TempDir(), "recording-deployment*.yaml")
 	e.Nil(err)
-	defer os.Remove(testFile.Name())
 	_, err = testFile.Write([]byte(testDeployment))
 	e.Nil(err)
 	err = testFile.Close()
 	e.Nil(err)
+
+	since := time.Now() // nolint: ifshort
 	e.kubectl("create", "-f", testFile.Name())
+
 	const deployName = "my-deployment"
 	e.retryGet("deploy", deployName)
 	e.waitFor("condition=available", "deploy", deployName)
+
+	if waitConditions != nil {
+		e.waitForEnricherLogs(since, waitConditions...)
+	}
+
 	e.kubectl("delete", "deploy", deployName)
 
-	profile0 := e.retryGetSeccompProfile(recordingName + "-nginx-0")
-	profile1 := e.retryGetSeccompProfile(recordingName + "-nginx-1")
-	e.Contains(profile0, "unlink")
-	e.Contains(profile1, "unlink")
-	defer e.kubectl("delete", "sp", recordingName+"-nginx-0")
-	defer e.kubectl("delete", "sp", recordingName+"-nginx-1")
+	const profileName0 = recordingName + "-nginx-0"
+	const profileName1 = recordingName + "-nginx-1"
+	profile0 := e.retryGetSeccompProfile(profileName0)
+	profile1 := e.retryGetSeccompProfile(profileName1)
+	e.Contains(profile0, "setuid")
+	e.Contains(profile1, "setuid")
+
+	e.kubectl("delete", "-f", recording)
+	e.Nil(os.Remove(testFile.Name()))
+	e.kubectl("delete", "sp", profileName0, profileName1)
 }
 
 func (e *e2e) retryGetSeccompProfile(args ...string) string {