Repository: aquasecurity/starboard
Pull request #533 (2021-04-23 22:34:03)

refactor: Use builder for config audit scan jobs
Signed-off-by: Daniel Pacak <pacak.daniel@gmail.com>

codecov[bot]: # [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/533?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) Report
> Merging [#533](https://codecov.io/gh/aquasecurity/starboard/pull/533?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (f7fffc0) into [main](https://codecov.io/gh/aquasecurity/starboard/commit/3d7ad49ecf97d01cb77de01fd8ef602805eb6953?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (3d7ad49) will **decrease** coverage by `34.14%`.
> The diff coverage is `3.06%`.

[![Impacted file tree graph](https://codecov.io/gh/aquasecurity/starboard/pull/533/graphs/tree.svg?width=650&height=150&src=pr&token=qqTGKAJ0Wb&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)](https://codecov.io/gh/aquasecurity/starboard/pull/533?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)

```diff
@@             Coverage Diff             @@
##             main     #533       +/-   ##
===========================================
- Coverage   70.82%   36.67%   -34.15%     
===========================================
  Files          63       62        -1     
  Lines        4045     3915      -130     
===========================================
- Hits         2865     1436     -1429     
- Misses        866     2391     +1525     
+ Partials      314       88      -226     
```


| [Impacted Files](https://codecov.io/gh/aquasecurity/starboard/pull/533?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) | Coverage Δ | |
|---|---|---|
| [pkg/configauditreport/builder.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2NvbmZpZ2F1ZGl0cmVwb3J0L2J1aWxkZXIuZ28=) | `36.26% <0.00%> (-44.23%)` | :arrow_down: |
| [pkg/configauditreport/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2NvbmZpZ2F1ZGl0cmVwb3J0L3NjYW5uZXIuZ28=) | `0.00% <0.00%> (-82.20%)` | :arrow_down: |
| [pkg/operator/controller/ciskubebenchreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvY2lza3ViZWJlbmNocmVwb3J0Lmdv) | `0.00% <0.00%> (-66.12%)` | :arrow_down: |
| [pkg/operator/controller/configauditreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvY29uZmlnYXVkaXRyZXBvcnQuZ28=) | `0.00% <0.00%> (-66.40%)` | :arrow_down: |
| [pkg/operator/controller/vulnerabilityreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvdnVsbmVyYWJpbGl0eXJlcG9ydC5nbw==) | `0.00% <0.00%> (-67.08%)` | :arrow_down: |
| [pkg/operator/predicate/predicate.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL3ByZWRpY2F0ZS9wcmVkaWNhdGUuZ28=) | `70.37% <40.00%> (-22.23%)` | :arrow_down: |
| [pkg/operator/controller/limit\_checker.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvbGltaXRfY2hlY2tlci5nbw==) | `71.42% <100.00%> (ø)` | |
| [pkg/cmd/get.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2NtZC9nZXQuZ28=) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pkg/cmd/find.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2NtZC9maW5kLmdv) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pkg/cmd/root.go](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2NtZC9yb290Lmdv) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| ... and [39 more](https://codecov.io/gh/aquasecurity/starboard/pull/533/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/533?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/533?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity). Last update [3d7ad49...f7fffc0](https://codecov.io/gh/aquasecurity/starboard/pull/533?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity).


Patch:
diff --git a/pkg/configauditreport/builder.go b/pkg/configauditreport/builder.go
index e1ef872..e0fbe66 100644
--- a/pkg/configauditreport/builder.go
+++ b/pkg/configauditreport/builder.go
@@ -3,31 +3,108 @@ package configauditreport
 import (
 	"fmt"
 	"strings"
+	"time"
 
 	"github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
 	"github.com/aquasecurity/starboard/pkg/kube"
 	"github.com/aquasecurity/starboard/pkg/starboard"
+	batchv1 "k8s.io/api/batch/v1"
+	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/utils/pointer"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
 )
 
-type Builder interface {
-	Controller(controller metav1.Object) Builder
-	PodSpecHash(hash string) Builder
-	PluginConfigHash(hash string) Builder
-	Result(result v1alpha1.ConfigAuditResult) Builder
-	Get() (v1alpha1.ConfigAuditReport, error)
+type ScanJobBuilder struct {
+	plugin        Plugin
+	pluginContext starboard.PluginContext
+	timeout       time.Duration
+	object        client.Object
 }
 
-func NewBuilder(scheme *runtime.Scheme) Builder {
-	return &builder{
-		scheme: scheme,
+func NewScanJob() *ScanJobBuilder {
+	return &ScanJobBuilder{}
+}
+
+func (s *ScanJobBuilder) WithPlugin(plugin Plugin) *ScanJobBuilder {
+	s.plugin = plugin
+	return s
+}
+
+func (s *ScanJobBuilder) WithPluginContext(pluginContext starboard.PluginContext) *ScanJobBuilder {
+	s.pluginContext = pluginContext
+	return s
+}
+
+func (s *ScanJobBuilder) WithTimeout(timeout time.Duration) *ScanJobBuilder {
+	s.timeout = timeout
+	return s
+}
+
+func (s *ScanJobBuilder) WithObject(object client.Object) *ScanJobBuilder {
+	s.object = object
+	return s
+}
+
+func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
+	jobSpec, secrets, err := s.plugin.GetScanJobSpec(s.pluginContext, s.object)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	podSpec, err := kube.GetPodSpec(s.object)
+	if err != nil {
+		return nil, nil, err
 	}
+
+	podSpecHash := kube.ComputeHash(podSpec)
+
+	pluginConfigHash, err := s.plugin.GetConfigHash(s.pluginContext)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	labels := map[string]string{
+		starboard.LabelResourceKind:             s.object.GetObjectKind().GroupVersionKind().Kind,
+		starboard.LabelResourceName:             s.object.GetName(),
+		starboard.LabelResourceNamespace:        s.object.GetNamespace(),
+		starboard.LabelPodSpecHash:              podSpecHash,
+		starboard.LabelPluginConfigHash:         pluginConfigHash,
+		starboard.LabelConfigAuditReportScanner: s.pluginContext.GetName(),
+		starboard.LabelK8SAppManagedBy:          starboard.AppStarboard,
+	}
+
+	return &batchv1.Job{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      GetScanJobName(s.object),
+			Namespace: s.pluginContext.GetNamespace(),
+			Labels:    labels,
+		},
+		Spec: batchv1.JobSpec{
+			BackoffLimit:          pointer.Int32Ptr(0),
+			Completions:           pointer.Int32Ptr(1),
+			ActiveDeadlineSeconds: kube.GetActiveDeadlineSeconds(s.timeout),
+			Template: corev1.PodTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Labels: labels,
+				},
+				Spec: jobSpec,
+			},
+		},
+	}, secrets, nil
+}
+
+func GetScanJobName(obj client.Object) string {
+	return fmt.Sprintf("scan-configauditreport-%s", kube.ComputeHash(kube.Object{
+		Kind:      kube.Kind(obj.GetObjectKind().GroupVersionKind().Kind),
+		Namespace: obj.GetNamespace(),
+		Name:      obj.GetName(),
+	}))
 }
 
-type builder struct {
+type ReportBuilder struct {
 	scheme           *runtime.Scheme
 	controller       metav1.Object
 	podSpecHash      string
@@ -35,27 +112,33 @@ type builder struct {
 	result           v1alpha1.ConfigAuditResult
 }
 
-func (b *builder) Controller(controller metav1.Object) Builder {
+func NewReportBuilder(scheme *runtime.Scheme) *ReportBuilder {
+	return &ReportBuilder{
+		scheme: scheme,
+	}
+}
+
+func (b *ReportBuilder) Controller(controller metav1.Object) *ReportBuilder {
 	b.controller = controller
 	return b
 }
 
-func (b *builder) PodSpecHash(hash string) Builder {
+func (b *ReportBuilder) PodSpecHash(hash string) *ReportBuilder {
 	b.podSpecHash = hash
 	return b
 }
 
-func (b *builder) PluginConfigHash(hash string) Builder {
+func (b *ReportBuilder) PluginConfigHash(hash string) *ReportBuilder {
 	b.pluginConfigHash = hash
 	return b
 }
 
-func (b *builder) Result(result v1alpha1.ConfigAuditResult) Builder {
+func (b *ReportBuilder) Data(result v1alpha1.ConfigAuditResult) *ReportBuilder {
 	b.result = result
 	return b
 }
 
-func (b *builder) reportName() (string, error) {
+func (b *ReportBuilder) reportName() (string, error) {
 	kind, err := kube.KindForObject(b.controller, b.scheme)
 	if err != nil {
 		return "", err
@@ -64,7 +147,7 @@ func (b *builder) reportName() (string, error) {
 		b.controller.GetName()), nil
 }
 
-func (b *builder) Get() (v1alpha1.ConfigAuditReport, error) {
+func (b *ReportBuilder) Get() (v1alpha1.ConfigAuditReport, error) {
 	kind, err := kube.KindForObject(b.controller, b.scheme)
 	if err != nil {
 		return v1alpha1.ConfigAuditReport{}, fmt.Errorf("getting kind for object: %w", err)
diff --git a/pkg/configauditreport/scanner.go b/pkg/configauditreport/scanner.go
index a2563b9..1baab65 100644
--- a/pkg/configauditreport/scanner.go
+++ b/pkg/configauditreport/scanner.go
@@ -5,17 +5,13 @@ import (
 	"fmt"
 
 	"github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
-	"github.com/aquasecurity/starboard/pkg/ext"
 	"github.com/aquasecurity/starboard/pkg/kube"
 	"github.com/aquasecurity/starboard/pkg/runner"
 	"github.com/aquasecurity/starboard/pkg/starboard"
-	batchv1 "k8s.io/api/batch/v1"
-	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/klog"
-	"k8s.io/utils/pointer"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
@@ -27,7 +23,6 @@ type Scanner struct {
 	logsReader     kube.LogsReader
 	plugin         Plugin
 	pluginContext  starboard.PluginContext
-	ext.IDGenerator
 }
 
 func NewScanner(
@@ -45,7 +40,6 @@ func NewScanner(
 		pluginContext:  pluginContext,
 		objectResolver: &kube.ObjectResolver{Client: client},
 		logsReader:     kube.NewLogsReader(clientset),
-		IDGenerator:    ext.NewGoogleUUIDGenerator(),
 	}
 }
 
@@ -58,7 +52,12 @@ func (s *Scanner) Scan(ctx context.Context, workload kube.Object) (v1alpha1.Conf
 	}
 
 	klog.V(3).Infof("Scanning with options: %+v", s.opts)
-	job, secrets, err := s.getScanJob(owner)
+	job, secrets, err := NewScanJob().
+		WithPlugin(s.plugin).
+		WithPluginContext(s.pluginContext).
+		WithTimeout(s.opts.ScanJobTimeout).
+		WithObject(owner).
+		Get()
 	if err != nil {
 		return v1alpha1.ConfigAuditReport{}, err
 	}
@@ -94,41 +93,19 @@ func (s *Scanner) Scan(ctx context.Context, workload kube.Object) (v1alpha1.Conf
 		_ = logsStream.Close()
 	}()
 
-	return NewBuilder(s.scheme).
+	podSpecHash, ok := job.Labels[starboard.LabelPodSpecHash]
+	if !ok {
+		return v1alpha1.ConfigAuditReport{}, fmt.Errorf("expected label %s not set", starboard.LabelPodSpecHash)
+	}
+	pluginConfigHash, ok := job.Labels[starboard.LabelPluginConfigHash]
+	if !ok {
+		return v1alpha1.ConfigAuditReport{}, fmt.Errorf("expected label %s not set", starboard.LabelPluginConfigHash)
+	}
+
+	return NewReportBuilder(s.scheme).
 		Controller(owner).
-		Result(result).
+		PodSpecHash(podSpecHash).
+		PluginConfigHash(pluginConfigHash).
+		Data(result).
 		Get()
 }
-
-func (s *Scanner) getScanJob(obj client.Object) (*batchv1.Job, []*corev1.Secret, error) {
-	jobSpec, secrets, err := s.plugin.GetScanJobSpec(s.pluginContext, obj)
-	if err != nil {
-		return nil, nil, err
-	}
-	return &batchv1.Job{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      s.GenerateID(),
-			Namespace: starboard.NamespaceName,
-			Labels: map[string]string{
-				starboard.LabelResourceKind:      obj.GetObjectKind().GroupVersionKind().Kind,
-				starboard.LabelResourceName:      obj.GetName(),
-				starboard.LabelResourceNamespace: obj.GetNamespace(),
-			},
-		},
-		Spec: batchv1.JobSpec{
-			BackoffLimit:          pointer.Int32Ptr(0),
-			Completions:           pointer.Int32Ptr(1),
-			ActiveDeadlineSeconds: kube.GetActiveDeadlineSeconds(s.opts.ScanJobTimeout),
-			Template: corev1.PodTemplateSpec{
-				ObjectMeta: metav1.ObjectMeta{
-					Labels: map[string]string{
-						starboard.LabelResourceKind:      obj.GetObjectKind().GroupVersionKind().Kind,
-						starboard.LabelResourceName:      obj.GetName(),
-						starboard.LabelResourceNamespace: obj.GetNamespace(),
-					},
-				},
-				Spec: jobSpec,
-			},
-		},
-	}, secrets, nil
-}
diff --git a/pkg/operator/controller/ciskubebenchreport.go b/pkg/operator/controller/ciskubebenchreport.go
index b6f807f..85ab9ad 100644
--- a/pkg/operator/controller/ciskubebenchreport.go
+++ b/pkg/operator/controller/ciskubebenchreport.go
@@ -163,10 +163,10 @@ func (r *CISKubeBenchReportReconciler) newScanJob(node *corev1.Node) (*batchv1.J
 			Name:      r.getScanJobName(node),
 			Namespace: r.Config.Namespace,
 			Labels: labels.Set{
-				starboard.LabelResourceKind:        string(kube.KindNode),
-				starboard.LabelResourceName:        node.Name,
-				starboard.LabelK8SAppManagedBy:     starboard.AppStarboardOperator,
-				starboard.LabelKubeBenchReportScan: "true",
+				starboard.LabelResourceKind:           string(kube.KindNode),
+				starboard.LabelResourceName:           node.Name,
+				starboard.LabelK8SAppManagedBy:        starboard.AppStarboard,
+				starboard.LabelKubeBenchReportScanner: "true",
 			},
 		},
 		Spec: batchv1.JobSpec{
@@ -176,10 +176,10 @@ func (r *CISKubeBenchReportReconciler) newScanJob(node *corev1.Node) (*batchv1.J
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
 					Labels: labels.Set{
-						starboard.LabelResourceKind:        string(kube.KindNode),
-						starboard.LabelResourceName:        node.Name,
-						starboard.LabelK8SAppManagedBy:     starboard.AppStarboardOperator,
-						starboard.LabelKubeBenchReportScan: "true",
+						starboard.LabelResourceKind:           string(kube.KindNode),
+						starboard.LabelResourceName:           node.Name,
+						starboard.LabelK8SAppManagedBy:        starboard.AppStarboard,
+						starboard.LabelKubeBenchReportScanner: "true",
 					},
 				},
 				Spec: templateSpec,
diff --git a/pkg/operator/controller/configauditreport.go b/pkg/operator/controller/configauditreport.go
index 83eee89..9a15ec6 100644
--- a/pkg/operator/controller/configauditreport.go
+++ b/pkg/operator/controller/configauditreport.go
@@ -17,7 +17,6 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/utils/pointer"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/builder"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -139,7 +138,7 @@ func (r *ConfigAuditReportReconciler) reconcileWorkload(workloadKind kube.Kind)
 		}
 
 		log.V(1).Info("Checking whether configuration audit has been scheduled")
-		_, job, err := r.hasActiveScanJob(ctx, workloadPartial, podSpecHash)
+		_, job, err := r.hasActiveScanJob(ctx, workloadObj, podSpecHash)
 		if err != nil {
 			return ctrl.Result{}, err
 		}
@@ -160,7 +159,12 @@ func (r *ConfigAuditReportReconciler) reconcileWorkload(workloadKind kube.Kind)
 			return ctrl.Result{RequeueAfter: r.Config.ScanJobRetryAfter}, nil
 		}
 
-		job, secrets, err := r.getScanJob(workloadPartial, workloadObj, podSpecHash)
+		job, secrets, err := configauditreport.NewScanJob().
+			WithPlugin(r.Plugin).
+			WithPluginContext(r.PluginContext).
+			WithTimeout(r.Config.ScanJobTimeout).
+			WithObject(workloadObj).
+			Get()
 		if err != nil {
 			return ctrl.Result{}, err
 		}
@@ -211,8 +215,8 @@ func (r *ConfigAuditReportReconciler) hasReport(ctx context.Context, owner kube.
 	return false, nil
 }
 
-func (r *ConfigAuditReportReconciler) hasActiveScanJob(ctx context.Context, owner kube.Object, hash string) (bool, *batchv1.Job, error) {
-	jobName := r.getScanJobName(owner)
+func (r *ConfigAuditReportReconciler) hasActiveScanJob(ctx context.Context, obj client.Object, hash string) (bool, *batchv1.Job, error) {
+	jobName := configauditreport.GetScanJobName(obj)
 	job := &batchv1.Job{}
 	err := r.Client.Get(ctx, client.ObjectKey{Namespace: r.Config.Namespace, Name: jobName}, job)
 	if err != nil {
@@ -227,58 +231,6 @@ func (r *ConfigAuditReportReconciler) hasActiveScanJob(ctx context.Context, owne
 	return false, nil, nil
 }
 
-func (r *ConfigAuditReportReconciler) getScanJobName(workload kube.Object) string {
-	return fmt.Sprintf("scan-configauditreport-%s", kube.ComputeHash(workload))
-}
-
-func (r *ConfigAuditReportReconciler) getScanJob(workload kube.Object, obj client.Object, podSpecHash string) (*batchv1.Job, []*corev1.Secret, error) {
-	jobSpec, secrets, err := r.Plugin.GetScanJobSpec(r.PluginContext, obj)
-
-	if err != nil {
-		return nil, nil, err
-	}
-
-	configHash, err := r.Plugin.GetConfigHash(r.PluginContext)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	return &batchv1.Job{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      r.getScanJobName(workload),
-			Namespace: r.Config.Namespace,
-			Labels: map[string]string{
-				starboard.LabelResourceKind:          string(workload.Kind),
-				starboard.LabelResourceName:          workload.Name,
-				starboard.LabelResourceNamespace:     workload.Namespace,
-				starboard.LabelK8SAppManagedBy:       starboard.AppStarboardOperator,
-				starboard.LabelPodSpecHash:           podSpecHash,
-				starboard.LabelPluginConfigHash:      configHash,
-				starboard.LabelConfigAuditReportScan: "true",
-			},
-		},
-		Spec: batchv1.JobSpec{
-			BackoffLimit:          pointer.Int32Ptr(0),
-			Completions:           pointer.Int32Ptr(1),
-			ActiveDeadlineSeconds: kube.GetActiveDeadlineSeconds(r.Config.ScanJobTimeout),
-			Template: corev1.PodTemplateSpec{
-				ObjectMeta: metav1.ObjectMeta{
-					Labels: map[string]string{
-						starboard.LabelResourceKind:          string(workload.Kind),
-						starboard.LabelResourceName:          workload.Name,
-						starboard.LabelResourceNamespace:     workload.Namespace,
-						starboard.LabelK8SAppManagedBy:       starboard.AppStarboardOperator,
-						starboard.LabelPodSpecHash:           podSpecHash,
-						starboard.LabelPluginConfigHash:      configHash,
-						starboard.LabelConfigAuditReportScan: "true",
-					},
-				},
-				Spec: jobSpec,
-			},
-		},
-	}, secrets, nil
-}
-
 func (r *ConfigAuditReportReconciler) reconcileJobs() reconcile.Func {
 	return func(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
 		log := r.Logger.WithValues("job", req.NamespacedName)
@@ -362,11 +314,11 @@ func (r *ConfigAuditReportReconciler) processCompleteScanJob(ctx context.Context
 		_ = logsStream.Close()
 	}()
 
-	report, err := configauditreport.NewBuilder(r.Client.Scheme()).
+	report, err := configauditreport.NewReportBuilder(r.Client.Scheme()).
 		Controller(ownerObj).
 		PodSpecHash(podSpecHash).
 		PluginConfigHash(pluginConfigHash).
-		Result(result).
+		Data(result).
 		Get()
 	if err != nil {
 		return err
diff --git a/pkg/operator/controller/limit_checker.go b/pkg/operator/controller/limit_checker.go
index c224cfa..98f7aa8 100644
--- a/pkg/operator/controller/limit_checker.go
+++ b/pkg/operator/controller/limit_checker.go
@@ -37,7 +37,7 @@ func (c *checker) Check(ctx context.Context) (bool, int, error) {
 func (c *checker) countScanJobs(ctx context.Context) (int, error) {
 	var scanJobs batchv1.JobList
 	err := c.client.List(ctx, &scanJobs, client.MatchingLabels{
-		starboard.LabelK8SAppManagedBy: starboard.AppStarboardOperator,
+		starboard.LabelK8SAppManagedBy: starboard.AppStarboard,
 	}, client.InNamespace(c.config.Namespace))
 	if err != nil {
 		return 0, err
diff --git a/pkg/operator/controller/vulnerabilityreport.go b/pkg/operator/controller/vulnerabilityreport.go
index c14911c..a5a1d83 100644
--- a/pkg/operator/controller/vulnerabilityreport.go
+++ b/pkg/operator/controller/vulnerabilityreport.go
@@ -237,12 +237,12 @@ func (r *VulnerabilityReportReconciler) submitScanJob(ctx context.Context, spec
 			Name:      jobName,
 			Namespace: r.Config.Namespace,
 			Labels: map[string]string{
-				starboard.LabelResourceKind:            string(owner.Kind),
-				starboard.LabelResourceName:            owner.Name,
-				starboard.LabelResourceNamespace:       owner.Namespace,
-				starboard.LabelPodSpecHash:             hash,
-				starboard.LabelK8SAppManagedBy:         starboard.AppStarboardOperator,
-				starboard.LabelVulnerabilityReportScan: "true",
+				starboard.LabelResourceKind:               string(owner.Kind),
+				starboard.LabelResourceName:               owner.Name,
+				starboard.LabelResourceNamespace:          owner.Namespace,
+				starboard.LabelPodSpecHash:                hash,
+				starboard.LabelK8SAppManagedBy:            starboard.AppStarboard,
+				starboard.LabelVulnerabilityReportScanner: "true",
 			},
 			Annotations: map[string]string{
 				starboard.AnnotationContainerImages: containerImagesAsJSON,
@@ -255,12 +255,12 @@ func (r *VulnerabilityReportReconciler) submitScanJob(ctx context.Context, spec
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
 					Labels: map[string]string{
-						starboard.LabelResourceKind:            string(owner.Kind),
-						starboard.LabelResourceName:            owner.Name,
-						starboard.LabelResourceNamespace:       owner.Namespace,
-						starboard.LabelPodSpecHash:             hash,
-						starboard.LabelK8SAppManagedBy:         starboard.AppStarboardOperator,
-						starboard.LabelVulnerabilityReportScan: "true",
+						starboard.LabelResourceKind:               string(owner.Kind),
+						starboard.LabelResourceName:               owner.Name,
+						starboard.LabelResourceNamespace:          owner.Namespace,
+						starboard.LabelPodSpecHash:                hash,
+						starboard.LabelK8SAppManagedBy:            starboard.AppStarboard,
+						starboard.LabelVulnerabilityReportScanner: "true",
 					},
 				},
 				Spec: templateSpec,
diff --git a/pkg/operator/predicate/predicate.go b/pkg/operator/predicate/predicate.go
index 7c1c60c..de7b25f 100644
--- a/pkg/operator/predicate/predicate.go
+++ b/pkg/operator/predicate/predicate.go
@@ -59,13 +59,13 @@ var InNamespace = func(namespace string) predicate.Predicate {
 }
 
 // ManagedByStarboardOperator is a predicate.Predicate that returns true if the
-// specified client.Object is managed by the Starboard Operator.
+// specified client.Object is managed by Starboard.
 //
 // For example, pods controlled by jobs scheduled by Starboard Operator are
-// labeled with `app.kubernetes.io/managed-by=starboard-operator`.
+// labeled with `app.kubernetes.io/managed-by=starboard`.
 var ManagedByStarboardOperator = predicate.NewPredicateFuncs(func(obj client.Object) bool {
-	if managedBy, ok := obj.GetLabels()["app.kubernetes.io/managed-by"]; ok {
-		return managedBy == "starboard-operator"
+	if managedBy, ok := obj.GetLabels()[starboard.LabelK8SAppManagedBy]; ok {
+		return managedBy == starboard.AppStarboard
 	}
 	return false
 })
@@ -86,21 +86,21 @@ var JobHasAnyCondition = predicate.NewPredicateFuncs(func(obj client.Object) boo
 })
 
 var IsVulnerabilityReportScan = predicate.NewPredicateFuncs(func(obj client.Object) bool {
-	if _, ok := obj.GetLabels()[starboard.LabelVulnerabilityReportScan]; ok {
+	if _, ok := obj.GetLabels()[starboard.LabelVulnerabilityReportScanner]; ok {
 		return true
 	}
 	return false
 })
 
 var IsConfigAuditReportScan = predicate.NewPredicateFuncs(func(obj client.Object) bool {
-	if _, ok := obj.GetLabels()[starboard.LabelConfigAuditReportScan]; ok {
+	if _, ok := obj.GetLabels()[starboard.LabelConfigAuditReportScanner]; ok {
 		return true
 	}
 	return false
 })
 
 var IsKubeBenchReportScan = predicate.NewPredicateFuncs(func(obj client.Object) bool {
-	if _, ok := obj.GetLabels()[starboard.LabelKubeBenchReportScan]; ok {
+	if _, ok := obj.GetLabels()[starboard.LabelKubeBenchReportScanner]; ok {
 		return true
 	}
 	return false
diff --git a/pkg/plugin/factory.go b/pkg/plugin/factory.go
index 3bfe7ba..dec0895 100644
--- a/pkg/plugin/factory.go
+++ b/pkg/plugin/factory.go
@@ -91,7 +91,7 @@ func (r *Resolver) GetConfigAuditPlugin() (configauditreport.Plugin, starboard.P
 		WithNamespace(r.namespace).
 		WithServiceAccountName(r.serviceAccountName).
 		WithClient(r.client).
-		Build()
+		Get()
 
 	switch scanner {
 	case starboard.Polaris:
diff --git a/pkg/starboard/constants.go b/pkg/starboard/constants.go
index 370dc8b..ecf936e 100644
--- a/pkg/starboard/constants.go
+++ b/pkg/starboard/constants.go
@@ -26,12 +26,12 @@ const (
 	LabelPodSpecHash       = "pod-spec-hash"
 	LabelPluginConfigHash  = "plugin-config-hash"
 
-	LabelConfigAuditReportScan   = "configAuditReport.scanner"
-	LabelVulnerabilityReportScan = "vulnerabilityReport.scanner"
-	LabelKubeBenchReportScan     = "kubeBenchReport.scanner"
+	LabelConfigAuditReportScanner   = "configAuditReport.scanner"
+	LabelVulnerabilityReportScanner = "vulnerabilityReport.scanner"
+	LabelKubeBenchReportScanner     = "kubeBenchReport.scanner"
 
 	LabelK8SAppManagedBy = "app.kubernetes.io/managed-by"
-	AppStarboardOperator = "starboard-operator"
+	AppStarboard         = "starboard"
 )
 
 const (
diff --git a/pkg/starboard/plugin.go b/pkg/starboard/plugin.go
index 646da3e..1afcc6b 100644
--- a/pkg/starboard/plugin.go
+++ b/pkg/starboard/plugin.go
@@ -90,6 +90,6 @@ func (b *PluginContextBuilder) WithServiceAccountName(name string) *PluginContex
 	return b
 }
 
-func (b *PluginContextBuilder) Build() PluginContext {
+func (b *PluginContextBuilder) Get() PluginContext {
 	return b.ctx
 }


Test patch:
diff --git a/itest/matcher/matcher.go b/itest/matcher/matcher.go
index 5f8dfe7..11467f0 100644
--- a/itest/matcher/matcher.go
+++ b/itest/matcher/matcher.go
@@ -139,7 +139,7 @@ func (m *configAuditReportMatcher) Match(actual interface{}) (bool, error) {
 
 	matcher := MatchFields(IgnoreExtras, Fields{
 		"ObjectMeta": MatchFields(IgnoreExtras, Fields{
-			"Labels": MatchAllKeys(Keys{
+			"Labels": MatchKeys(IgnoreExtras, Keys{
 				starboard.LabelResourceKind:      Equal(gvk.Kind),
 				starboard.LabelResourceName:      Equal(m.owner.GetName()),
 				starboard.LabelResourceNamespace: Equal(m.owner.GetNamespace()),
diff --git a/pkg/configauditreport/builder_test.go b/pkg/configauditreport/builder_test.go
index 2149faf..4e83f31 100644
--- a/pkg/configauditreport/builder_test.go
+++ b/pkg/configauditreport/builder_test.go
@@ -1,22 +1,28 @@
 package configauditreport_test
 
 import (
+	. "github.com/onsi/gomega"
+
+	"io"
 	"testing"
+	"time"
 
 	"github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
 	"github.com/aquasecurity/starboard/pkg/configauditreport"
 	"github.com/aquasecurity/starboard/pkg/starboard"
-	"github.com/onsi/gomega"
 	appsv1 "k8s.io/api/apps/v1"
+	batchv1 "k8s.io/api/batch/v1"
+	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes/scheme"
 	"k8s.io/utils/pointer"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-func TestBuilder(t *testing.T) {
-	g := gomega.NewGomegaWithT(t)
+func TestReportBuilder(t *testing.T) {
+	g := NewGomegaWithT(t)
 
-	report, err := configauditreport.NewBuilder(scheme.Scheme).
+	report, err := configauditreport.NewReportBuilder(scheme.Scheme).
 		Controller(&appsv1.ReplicaSet{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      "some-owner",
@@ -25,10 +31,11 @@ func TestBuilder(t *testing.T) {
 		}).
 		PodSpecHash("xyz").
 		PluginConfigHash("nop").
-		Result(v1alpha1.ConfigAuditResult{}).Get()
+		Data(v1alpha1.ConfigAuditResult{}).
+		Get()
 
-	g.Expect(err).ToNot(gomega.HaveOccurred())
-	g.Expect(report).To(gomega.Equal(v1alpha1.ConfigAuditReport{
+	g.Expect(err).ToNot(HaveOccurred())
+	g.Expect(report).To(Equal(v1alpha1.ConfigAuditReport{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "replicaset-some-owner",
 			Namespace: "qa",
@@ -52,3 +59,84 @@ func TestBuilder(t *testing.T) {
 		Report: v1alpha1.ConfigAuditResult{},
 	}))
 }
+
+type testPlugin struct {
+	configHash string
+}
+
+func (p *testPlugin) GetScanJobSpec(ctx starboard.PluginContext, obj client.Object) (corev1.PodSpec, []*corev1.Secret, error) {
+	return corev1.PodSpec{}, nil, nil
+}
+
+func (p *testPlugin) ParseConfigAuditReportData(logsReader io.ReadCloser) (v1alpha1.ConfigAuditResult, error) {
+	return v1alpha1.ConfigAuditResult{}, nil
+}
+
+func (p *testPlugin) GetContainerName() string {
+	return ""
+}
+
+func (p *testPlugin) GetConfigHash(ctx starboard.PluginContext) (string, error) {
+	return p.configHash, nil
+}
+
+func TestScanJobBuilder(t *testing.T) {
+	g := NewGomegaWithT(t)
+	job, _, err := configauditreport.NewScanJob().
+		WithPlugin(&testPlugin{
+			configHash: "hash-test",
+		}).
+		WithPluginContext(starboard.NewPluginContext().
+			WithName("plugin-test").
+			WithNamespace("starboard-ns").
+			WithServiceAccountName("starboard-sa").
+			Get()).
+		WithTimeout(3 * time.Second).
+		WithObject(&appsv1.ReplicaSet{
+			TypeMeta: metav1.TypeMeta{
+				Kind:       "ReplicaSet",
+				APIVersion: "apps/v1",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "nginx-6799fc88d8",
+				Namespace: "prod-ns",
+			},
+		}).
+		Get()
+	g.Expect(err).ToNot(HaveOccurred())
+	g.Expect(job).NotTo(BeNil())
+	g.Expect(job).To(Equal(&batchv1.Job{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "scan-configauditreport-fcc9884cb",
+			Namespace: "starboard-ns",
+			Labels: map[string]string{
+				starboard.LabelPodSpecHash:              "58b8989656",
+				starboard.LabelPluginConfigHash:         "hash-test",
+				starboard.LabelConfigAuditReportScanner: "plugin-test",
+				starboard.LabelK8SAppManagedBy:          "starboard",
+				starboard.LabelResourceKind:             "ReplicaSet",
+				starboard.LabelResourceName:             "nginx-6799fc88d8",
+				starboard.LabelResourceNamespace:        "prod-ns",
+			},
+		},
+		Spec: batchv1.JobSpec{
+			BackoffLimit:          pointer.Int32Ptr(0),
+			Completions:           pointer.Int32Ptr(1),
+			ActiveDeadlineSeconds: pointer.Int64Ptr(3),
+			Template: corev1.PodTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Labels: map[string]string{
+						starboard.LabelPodSpecHash:              "58b8989656",
+						starboard.LabelPluginConfigHash:         "hash-test",
+						starboard.LabelConfigAuditReportScanner: "plugin-test",
+						starboard.LabelK8SAppManagedBy:          "starboard",
+						starboard.LabelResourceKind:             "ReplicaSet",
+						starboard.LabelResourceName:             "nginx-6799fc88d8",
+						starboard.LabelResourceNamespace:        "prod-ns",
+					},
+				},
+				Spec: corev1.PodSpec{},
+			},
+		},
+	}))
+}
diff --git a/pkg/operator/controller/limit_checker_test.go b/pkg/operator/controller/limit_checker_test.go
index 2ad319a..3a72bbc 100644
--- a/pkg/operator/controller/limit_checker_test.go
+++ b/pkg/operator/controller/limit_checker_test.go
@@ -34,21 +34,21 @@ var _ = Describe("LimitChecker", func() {
 					Name:      "scan-vulnerabilityreport-hash1",
 					Namespace: "starboard-operator",
 					Labels: map[string]string{
-						starboard.LabelK8SAppManagedBy: starboard.AppStarboardOperator,
+						starboard.LabelK8SAppManagedBy: starboard.AppStarboard,
 					},
 				}},
 				&batchv1.Job{ObjectMeta: metav1.ObjectMeta{
 					Name:      "scan-vulnerabilityreport-hash2",
 					Namespace: "starboard-operator",
 					Labels: map[string]string{
-						starboard.LabelK8SAppManagedBy: starboard.AppStarboardOperator,
+						starboard.LabelK8SAppManagedBy: starboard.AppStarboard,
 					},
 				}},
 				&batchv1.Job{ObjectMeta: metav1.ObjectMeta{
 					Name:      "scan-configauditreport-hash2",
 					Namespace: "starboard-operator",
 					Labels: map[string]string{
-						starboard.LabelK8SAppManagedBy: starboard.AppStarboardOperator,
+						starboard.LabelK8SAppManagedBy: starboard.AppStarboard,
 					},
 				}},
 			).Build()
@@ -74,7 +74,7 @@ var _ = Describe("LimitChecker", func() {
 					Name:      "scan-vulnerabilityreport-hash1",
 					Namespace: "starboard-operator",
 					Labels: map[string]string{
-						starboard.LabelK8SAppManagedBy: starboard.AppStarboardOperator,
+						starboard.LabelK8SAppManagedBy: starboard.AppStarboard,
 					},
 				}},
 			).Build()
diff --git a/pkg/operator/predicate/predicate_test.go b/pkg/operator/predicate/predicate_test.go
index a60ea1c..08ad314 100644
--- a/pkg/operator/predicate/predicate_test.go
+++ b/pkg/operator/predicate/predicate_test.go
@@ -1,12 +1,13 @@
 package predicate_test
 
 import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
 	"time"
 
 	"github.com/aquasecurity/starboard/pkg/operator/etc"
 	"github.com/aquasecurity/starboard/pkg/operator/predicate"
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -207,7 +208,7 @@ var _ = Describe("Predicate", func() {
 				obj := &corev1.Pod{
 					ObjectMeta: metav1.ObjectMeta{
 						Labels: map[string]string{
-							"app.kubernetes.io/managed-by": "starboard-operator",
+							"app.kubernetes.io/managed-by": "starboard",
 						},
 					},
 				}
diff --git a/pkg/plugin/conftest/plugin_test.go b/pkg/plugin/conftest/plugin_test.go
index aadd9e4..fe86938 100644
--- a/pkg/plugin/conftest/plugin_test.go
+++ b/pkg/plugin/conftest/plugin_test.go
@@ -48,7 +48,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) {
 				"foo":                                     "bar",    // This one should be skipped (no conftest.policy. prefix)
 				"conftest.policy.privileged":              "<REGO>", // This one should be skipped (no .rego suffix)
 			},
-		}).Build()).Build()
+		}).Build()).Get()
 
 	plugin := conftest.NewPlugin(sequence, fixedClock, config)
 
@@ -364,7 +364,7 @@ func TestPlugin_GetConfigHash(t *testing.T) {
 					Data: data,
 				}).
 				Build()).
-			Build()
+			Get()
 	}
 
 	t.Run("Should return different hash for different config data", func(t *testing.T) {
diff --git a/pkg/plugin/polaris/plugin_test.go b/pkg/plugin/polaris/plugin_test.go
index bddc07b..b534ffe 100644
--- a/pkg/plugin/polaris/plugin_test.go
+++ b/pkg/plugin/polaris/plugin_test.go
@@ -121,7 +121,7 @@ func TestPlugin_GetScanJobSpec(t *testing.T) {
 				WithName(string(starboard.Polaris)).
 				WithNamespace(starboard.NamespaceName).
 				WithServiceAccountName(starboard.ServiceAccountName).
-				Build()
+				Get()
 			plugin := polaris.NewPlugin(fixedClock, tc.config)
 			jobSpec, secrets, err := plugin.GetScanJobSpec(pluginContext, tc.obj)
 
@@ -208,7 +208,7 @@ func TestPlugin_GetConfigHash(t *testing.T) {
 					Data: data,
 				}).
 				Build()).
-			Build()
+			Get()
 	}
 
 	t.Run("Should return different hash for different config data", func(t *testing.T) {
diff --git a/pkg/starboard/plugin_test.go b/pkg/starboard/plugin_test.go
index 050f3e9..bf7fd3a 100644
--- a/pkg/starboard/plugin_test.go
+++ b/pkg/starboard/plugin_test.go
@@ -36,7 +36,7 @@ func TestPluginContext_GetConfig(t *testing.T) {
 		WithName("polaris").
 		WithNamespace("starboard-ns").
 		WithClient(client).
-		Build()
+		Get()
 
 	cm, err := pluginContext.GetConfig()