Repository: aquasecurity/starboard
Pull request #645 (2021-07-26 08:31:59)

feat(polaris): Allow configuration of resource request and limits
Signed-off-by: Daniel Pacak <pacak.daniel@gmail.com>

codecov[bot]: # [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/645?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) Report
> Merging [#645](https://codecov.io/gh/aquasecurity/starboard/pull/645?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (f415cd9) into [main](https://codecov.io/gh/aquasecurity/starboard/commit/134b2f5e860d8f813e070775414905008d2afaae?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (134b2f5) will **decrease** coverage by `22.38%`.
> The diff coverage is `72.72%`.

[![Impacted file tree graph](https://codecov.io/gh/aquasecurity/starboard/pull/645/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/645?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)

```diff
@@             Coverage Diff             @@
##             main     #645       +/-   ##
===========================================
- Coverage   68.34%   45.95%   -22.39%     
===========================================
  Files          58       41       -17     
  Lines        4511     3688      -823     
===========================================
- Hits         3083     1695     -1388     
- Misses       1062     1880      +818     
+ Partials      366      113      -253     
```


| [Impacted Files](https://codecov.io/gh/aquasecurity/starboard/pull/645?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) | Coverage Δ | |
|---|---|---|
| [pkg/plugin/trivy/plugin.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL3BsdWdpbi90cml2eS9wbHVnaW4uZ28=) | `83.09% <ø> (-0.95%)` | :arrow_down: |
| [pkg/plugin/polaris/plugin.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL3BsdWdpbi9wb2xhcmlzL3BsdWdpbi5nbw==) | `83.42% <72.72%> (-7.18%)` | :arrow_down: |
| [pkg/kubehunter/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2t1YmVodW50ZXIvc2Nhbm5lci5nbw==) | `0.00% <0.00%> (-80.51%)` | :arrow_down: |
| [pkg/plugin/factory.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL3BsdWdpbi9mYWN0b3J5Lmdv) | `0.00% <0.00%> (-80.49%)` | :arrow_down: |
| [pkg/configauditreport/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/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%> (-70.77%)` | :arrow_down: |
| [pkg/vulnerabilityreport/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL3Z1bG5lcmFiaWxpdHlyZXBvcnQvc2Nhbm5lci5nbw==) | `0.00% <0.00%> (-70.54%)` | :arrow_down: |
| [pkg/operator/controller/configauditreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/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%> (-65.75%)` | :arrow_down: |
| [pkg/operator/controller/vulnerabilityreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/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%> (-65.17%)` | :arrow_down: |
| [pkg/kube/runnable\_job.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2t1YmUvcnVubmFibGVfam9iLmdv) | `0.00% <0.00%> (-64.39%)` | :arrow_down: |
| [pkg/operator/controller/ciskubebenchreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/645/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%> (-62.77%)` | :arrow_down: |
| ... and [31 more](https://codecov.io/gh/aquasecurity/starboard/pull/645/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/645?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/645?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity). Last update [134b2f5...f415cd9](https://codecov.io/gh/aquasecurity/starboard/pull/645?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/deploy/static/05-starboard-operator.config.yaml b/deploy/static/05-starboard-operator.config.yaml
index 02f04f9..a33e811 100644
--- a/deploy/static/05-starboard-operator.config.yaml
+++ b/deploy/static/05-starboard-operator.config.yaml
@@ -4,19 +4,31 @@ kind: ConfigMap
 metadata:
   name: starboard
   namespace: starboard-operator
+  labels:
+    "app.kubernetes.io/managed-by": "starboard"
 data:
   vulnerabilityReports.scanner: Trivy
   configAuditReports.scanner: Polaris
   kube-bench.imageRef: docker.io/aquasec/kube-bench:0.6.3
 ---
 apiVersion: v1
+kind: Secret
+metadata:
+  name: starboard
+  namespace: starboard-operator
+  labels:
+    "app.kubernetes.io/managed-by": "starboard"
+---
+apiVersion: v1
 kind: ConfigMap
 metadata:
   name: starboard-trivy-config
   namespace: starboard-operator
+  labels:
+    "app.kubernetes.io/managed-by": "starboard"
 data:
-  trivy.severity: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
   trivy.imageRef: docker.io/aquasec/trivy:0.16.0
+  trivy.severity: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
   trivy.mode: Standalone
   trivy.resources.request.cpu: 100m
   trivy.resources.request.memory: 100M
@@ -28,8 +40,14 @@ kind: ConfigMap
 metadata:
   name: starboard-polaris-config
   namespace: starboard-operator
+  labels:
+    "app.kubernetes.io/managed-by": "starboard"
 data:
   polaris.imageRef: quay.io/fairwinds/polaris:3.2
+  polaris.resources.request.cpu: "50m"
+  polaris.resources.request.memory: "50M"
+  polaris.resources.limit.cpu: "300m"
+  polaris.resources.limit.memory: "300M"
   polaris.config.yaml: |
     checks:
       # reliability
@@ -214,9 +232,3 @@ data:
         - kube-hunter
         rules:
         - runAsRootAllowed
----
-apiVersion: v1
-kind: Secret
-metadata:
-  name: starboard
-  namespace: starboard-operator
diff --git a/docs/settings.md b/docs/settings.md
index 5424901..e5d4d06 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -56,33 +56,41 @@ The following tables list available configuration settings with their default va
     used if `vulnerabilityReports.scanner` is set to `Trivy`). Check
     [integrations](./integrations/vulnerability-scanners/index.md) page to see example configuration settings for common use cases.
 
-| CONFIGMAP KEY                   | DEFAULT                                                | DESCRIPTION |
-| ------------------------------- | ------------------------------------------------------ | ----------- |
-| `vulnerabilityReports.scanner`  | `Trivy`                                                | The name of the plugin that generates vulnerability reports. Either `Trivy` or `Aqua`. |
-| `configAuditReports.scanner`    | `Polaris`                                              | The name of the plugin that generates config audit reports. Either `Polaris` or `Conftest`. |
-| `trivy.httpProxy`               | N/A                                                    | The HTTP proxy used by Trivy to download the vulnerabilities database from GitHub. |
-| `trivy.httpsProxy`              | N/A                                                    | The HTTPS proxy used by Trivy to download the vulnerabilities database from GitHub. |
-| `trivy.noProxy`                 | N/A                                                    | A comma separated list of IPs and domain names that are not subject to proxy settings. |
-| `trivy.severity`                | `UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL`                     | A comma separated list of severity levels reported by Trivy |
-| `trivy.imageRef`                | `docker.io/aquasec/trivy:0.16.0`                       | Trivy image reference |
-| `trivy.mode`                    | `Standalone`                                           | Trivy client mode. Either `Standalone` or `ClientServer`. Depending on the active mode other settings might be applicable or required. |
-| `trivy.ignoreUnfixed`           | N/A                                                    | Whether to show only fixed vulnerabilities in vulnerabilities reported by Trivy. Set to `"true"` to enable it. |
-| `trivy.skipFiles`               | N/A                                                    | A comma separated list of file paths for Trivy to skip traversal. |
-| `trivy.skipDirs`                | N/A                                                    | A comma separated list of directories for Trivy to skip traversal. |
-| `trivy.ignoreFile`              | N/A                                                    | It specifies the `.trivyignore` file which contains a list of vulnerability IDs to be ignored from vulnerabilities reported by Trivy. |
-| `trivy.serverURL`               | N/A                                                    | The endpoint URL of the Trivy server. Required in `ClientServer` mode. |
-| `trivy.serverTokenHeader`       | `Trivy-Token`                                          | The name of the HTTP header to send the authentication token to Trivy server. Only application in `ClientServer` mode when `trivy.serverToken` is specified. |
-| `trivy.insecureRegistry.<id>`   | N/A                                                    | The registry to which insecure connections are allowed. There can be multiple registries with different registry `<id>`. |
-| `aqua.imageRef`                 | `docker.io/aquasec/scanner:5.3`                        | Aqua scanner image reference. The tag determines the version of the `scanner` binary executable and it must be compatible with version of Aqua console. |
-| `aqua.serverURL`                | N/A                                                    | The endpoint URL of Aqua management console |
-| `kube-bench.imageRef`           | `docker.io/aquasec/kube-bench:0.6.3`                   | kube-bench image reference |
-| `kube-hunter.imageRef`          | `docker.io/aquasec/kube-hunter:0.4.1`                  | kube-hunter image reference |
-| `kube-hunter.quick`             | `"false"`                                              | Whether to use kube-hunter's "quick" scanning mode (subnet 24). Set to `"true"` to enable. |
-| `polaris.imageRef`              | `quay.io/fairwinds/polaris:3.2`                        | Polaris image reference |
-| `polaris.config.yaml`           | [Check the default value here][default-polaris-config] | Polaris configuration file |
-| `conftest.imageRef`             | `docker.io/openpolicyagent/conftest:v0.25.0`           | Conftest image reference |
-| `scanJob.tolerations`           | N/A                                                    | JSON representation of the [tolerations] to be applied to the scanner pods so that they can run on nodes with matching taints. Example: `'[{"key":"key1", "operator":"Equal", "value":"value1", "effect":"NoSchedule"}]'` |
-| `scanJob.annotations`           | N/A                                                    | One-line comma-separated representation of the annotations which the user wants the scanner pods to be annotated with. Example: `foo=bar,env=stage` will annotate the scanner pods with the annotations `foo: bar` and `env: stage` |
+| CONFIGMAP KEY                      | DEFAULT                                                | DESCRIPTION |
+| ---------------------------------- | ------------------------------------------------------ | ----------- |
+| `vulnerabilityReports.scanner`     | `Trivy`                                                | The name of the plugin that generates vulnerability reports. Either `Trivy` or `Aqua`. |
+| `configAuditReports.scanner`       | `Polaris`                                              | The name of the plugin that generates config audit reports. Either `Polaris` or `Conftest`. |
+| `trivy.httpProxy`                  | N/A                                                    | The HTTP proxy used by Trivy to download the vulnerabilities database from GitHub. |
+| `trivy.httpsProxy`                 | N/A                                                    | The HTTPS proxy used by Trivy to download the vulnerabilities database from GitHub. |
+| `trivy.noProxy`                    | N/A                                                    | A comma separated list of IPs and domain names that are not subject to proxy settings. |
+| `trivy.severity`                   | `UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL`                     | A comma separated list of severity levels reported by Trivy |
+| `trivy.imageRef`                   | `docker.io/aquasec/trivy:0.16.0`                       | Trivy image reference |
+| `trivy.mode`                       | `Standalone`                                           | Trivy client mode. Either `Standalone` or `ClientServer`. Depending on the active mode other settings might be applicable or required. |
+| `trivy.ignoreUnfixed`              | N/A                                                    | Whether to show only fixed vulnerabilities in vulnerabilities reported by Trivy. Set to `"true"` to enable it. |
+| `trivy.skipFiles`                  | N/A                                                    | A comma separated list of file paths for Trivy to skip traversal. |
+| `trivy.skipDirs`                   | N/A                                                    | A comma separated list of directories for Trivy to skip traversal. |
+| `trivy.ignoreFile`                 | N/A                                                    | It specifies the `.trivyignore` file which contains a list of vulnerability IDs to be ignored from vulnerabilities reported by Trivy. |
+| `trivy.serverURL`                  | N/A                                                    | The endpoint URL of the Trivy server. Required in `ClientServer` mode. |
+| `trivy.serverTokenHeader`          | `Trivy-Token`                                          | The name of the HTTP header to send the authentication token to Trivy server. Only application in `ClientServer` mode when `trivy.serverToken` is specified. |
+| `trivy.insecureRegistry.<id>`      | N/A                                                    | The registry to which insecure connections are allowed. There can be multiple registries with different registry `<id>`. |
+| `trivy.resources.request.cpu`      | `100m`                                                 | The minimum amount of CPU required to run Trivy scanner pod. |
+| `trivy.resources.request.memory`   | `100M`                                                 | The minimum amount of memory required to run Trivy scanner pod. |
+| `trivy.resources.limit.cpu`        | `500m`                                                 | The maximum amount of CPU allowed to run Trivy scanner pod. |
+| `trivy.resources.limit.memory`     | `500M`                                                 | The maximum amount of memory allowed to run Trivy scanner pod. |
+| `aqua.imageRef`                    | `docker.io/aquasec/scanner:5.3`                        | Aqua scanner image reference. The tag determines the version of the `scanner` binary executable and it must be compatible with version of Aqua console. |
+| `aqua.serverURL`                   | N/A                                                    | The endpoint URL of Aqua management console |
+| `kube-bench.imageRef`              | `docker.io/aquasec/kube-bench:0.6.3`                   | kube-bench image reference |
+| `kube-hunter.imageRef`             | `docker.io/aquasec/kube-hunter:0.4.1`                  | kube-hunter image reference |
+| `kube-hunter.quick`                | `"false"`                                              | Whether to use kube-hunter's "quick" scanning mode (subnet 24). Set to `"true"` to enable. |
+| `polaris.imageRef`                 | `quay.io/fairwinds/polaris:3.2`                        | Polaris image reference |
+| `polaris.config.yaml`              | [Check the default value here][default-polaris-config] | Polaris configuration file |
+| `polaris.resources.request.cpu`    | `50m`                                                  | The minimum amount of CPU required to run Polaris scanner pod. |
+| `polaris.resources.request.memory` | `50M`                                                  | The minimum amount of memory required to run Polaris scanner pod. |
+| `polaris.resources.limit.cpu`      | `300m`                                                 | The maximum amount of CPU allowed to run Polaris scanner pod. |
+| `polaris.resources.limit.memory`   | `300M`                                                 | The maximum amount of memory allowed to run polaris scanner pod. |
+| `conftest.imageRef`                | `docker.io/openpolicyagent/conftest:v0.25.0`           | Conftest image reference |
+| `scanJob.tolerations`              | N/A                                                    | JSON representation of the [tolerations] to be applied to the scanner pods so that they can run on nodes with matching taints. Example: `'[{"key":"key1", "operator":"Equal", "value":"value1", "effect":"NoSchedule"}]'` |
+| `scanJob.annotations`              | N/A                                                    | One-line comma-separated representation of the annotations which the user wants the scanner pods to be annotated with. Example: `foo=bar,env=stage` will annotate the scanner pods with the annotations `foo: bar` and `env: stage` |
 
 | SECRET KEY                  | DESCRIPTION |
 | --------------------------- | ----------- |
diff --git a/pkg/plugin/polaris/plugin.go b/pkg/plugin/polaris/plugin.go
index a293c60..553b84d 100644
--- a/pkg/plugin/polaris/plugin.go
+++ b/pkg/plugin/polaris/plugin.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
+	"strings"
 
 	"github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
 	"github.com/aquasecurity/starboard/pkg/configauditreport"
@@ -23,7 +24,17 @@ const (
 )
 
 const (
-	polarisConfigYAML = `checks:
+	keyImageRef                = "polaris.imageRef"
+	keyConfigYaml              = "polaris.config.yaml"
+	keyResourcesRequestsCPU    = "polaris.resources.request.cpu"
+	keyResourcesRequestsMemory = "polaris.resources.request.memory"
+	keyResourcesLimitCPU       = "polaris.resources.limit.cpu"
+	keyResourcesLimitMemory    = "polaris.resources.limit.memory"
+)
+
+const (
+	// DefaultConfigYAML the default Polaris config YAML as string literal.
+	DefaultConfigYAML = `checks:
   # reliability
   multipleReplicasForDeployment: ignore
   priorityClassNotSet: ignore
@@ -209,23 +220,80 @@ exemptions:
 `
 )
 
+// Config defines configuration params for this plugin.
+type Config struct {
+	starboard.PluginConfig
+}
+
+// GetImageRef returns upstream Polaris container image reference.
+func (c Config) GetImageRef() (string, error) {
+	return c.GetRequiredData(keyImageRef)
+}
+
+// GetResourceRequirements constructs ResourceRequirements from the Config.
+func (c Config) GetResourceRequirements() (corev1.ResourceRequirements, error) {
+	requirements := corev1.ResourceRequirements{
+		Requests: corev1.ResourceList{},
+		Limits:   corev1.ResourceList{},
+	}
+
+	err := c.setResourceLimit(keyResourcesRequestsCPU, &requirements.Requests, corev1.ResourceCPU)
+	if err != nil {
+		return requirements, err
+	}
+
+	err = c.setResourceLimit(keyResourcesRequestsMemory, &requirements.Requests, corev1.ResourceMemory)
+	if err != nil {
+		return requirements, err
+	}
+
+	err = c.setResourceLimit(keyResourcesLimitCPU, &requirements.Limits, corev1.ResourceCPU)
+	if err != nil {
+		return requirements, err
+	}
+
+	err = c.setResourceLimit(keyResourcesLimitMemory, &requirements.Limits, corev1.ResourceMemory)
+	if err != nil {
+		return requirements, err
+	}
+
+	return requirements, nil
+}
+
+func (c Config) setResourceLimit(configKey string, k8sResourceList *corev1.ResourceList, k8sResourceName corev1.ResourceName) error {
+	if value, found := c.Data[configKey]; found {
+		quantity, err := resource.ParseQuantity(value)
+		if err != nil {
+			return fmt.Errorf("parsing resource definition %s: %s %v", configKey, value, err)
+		}
+
+		(*k8sResourceList)[k8sResourceName] = quantity
+	}
+	return nil
+}
+
 type plugin struct {
 	clock ext.Clock
 }
 
 // NewPlugin constructs a new configauditreport.Plugin, which is using an
-// official Polaris container image to audit Kubernetes workloads.
+// upstream Polaris container image to audit configuration of Kubernetes workloads.
 func NewPlugin(clock ext.Clock) configauditreport.Plugin {
 	return &plugin{
 		clock: clock,
 	}
 }
 
+// Init ensures the default Config required by this plugin.
 func (p *plugin) Init(ctx starboard.PluginContext) error {
 	return ctx.EnsureConfig(starboard.PluginConfig{
 		Data: map[string]string{
-			"polaris.imageRef":    "quay.io/fairwinds/polaris:3.2",
-			"polaris.config.yaml": polarisConfigYAML,
+			keyImageRef:                "quay.io/fairwinds/polaris:3.2",
+			keyConfigYaml:              DefaultConfigYAML,
+			keyResourcesRequestsCPU:    "50m",
+			keyResourcesRequestsMemory: "50M",
+			keyResourcesLimitCPU:       "300m",
+			keyResourcesLimitMemory:    "300M",
 		},
 	})
 }
@@ -235,21 +303,36 @@ func (p *plugin) GetConfigHash(ctx starboard.PluginContext) (string, error) {
 	if err != nil {
 		return "", err
 	}
-	return kube.ComputeHash(cm.Data), nil
+	data := make(map[string]string)
+	for key, value := range cm.Data {
+		if strings.HasPrefix(key, "polaris.resources.") {
+			continue
+		}
+		data[key] = value
+	}
+	return kube.ComputeHash(data), nil
 }
 
-func (p *plugin) getImageRef(ctx starboard.PluginContext) (string, error) {
-	config, err := ctx.GetConfig()
+func (p *plugin) newConfigFrom(ctx starboard.PluginContext) (Config, error) {
+	pluginConfig, err := ctx.GetConfig()
 	if err != nil {
-		return "", err
+		return Config{}, fmt.Errorf("getting config: %w", err)
 	}
-	return config.GetRequiredData("polaris.imageRef")
+	return Config{PluginConfig: pluginConfig}, nil
 }
 
 func (p *plugin) GetScanJobSpec(ctx starboard.PluginContext, obj client.Object) (corev1.PodSpec, []*corev1.Secret, error) {
-	imageRef, err := p.getImageRef(ctx)
+	config, err := p.newConfigFrom(ctx)
+	if err != nil {
+		return corev1.PodSpec{}, nil, fmt.Errorf("constructing config from plugin context: %w", err)
+	}
+	imageRef, err := config.GetImageRef()
 	if err != nil {
-		return corev1.PodSpec{}, nil, err
+		return corev1.PodSpec{}, nil, fmt.Errorf("getting image ref: %w", err)
+	}
+	requirements, err := config.GetResourceRequirements()
+	if err != nil {
+		return corev1.PodSpec{}, nil, fmt.Errorf("getting resource requirements: %w", err)
 	}
 	sourceName := p.sourceNameFrom(obj)
 
@@ -276,16 +359,7 @@ func (p *plugin) GetScanJobSpec(ctx starboard.PluginContext, obj client.Object)
 				Image:                    imageRef,
 				ImagePullPolicy:          corev1.PullIfNotPresent,
 				TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
-				Resources: corev1.ResourceRequirements{
-					Limits: corev1.ResourceList{
-						corev1.ResourceCPU:    resource.MustParse("300m"),
-						corev1.ResourceMemory: resource.MustParse("300M"),
-					},
-					Requests: corev1.ResourceList{
-						corev1.ResourceCPU:    resource.MustParse("50m"),
-						corev1.ResourceMemory: resource.MustParse("50M"),
-					},
-				},
+				Resources:                requirements,
 				VolumeMounts: []corev1.VolumeMount{
 					{
 						Name:      configVolume,
@@ -316,35 +390,24 @@ func (p *plugin) GetContainerName() string {
 }
 
 func (p *plugin) ParseConfigAuditReportData(ctx starboard.PluginContext, logsReader io.ReadCloser) (v1alpha1.ConfigAuditReportData, error) {
+	config, err := p.newConfigFrom(ctx)
+	if err != nil {
+		return v1alpha1.ConfigAuditReportData{}, fmt.Errorf("constructing config from plugin context: %w", err)
+	}
 	var report Report
-	err := json.NewDecoder(logsReader).Decode(&report)
+	err = json.NewDecoder(logsReader).Decode(&report)
 	if err != nil {
 		return v1alpha1.ConfigAuditReportData{}, err
 	}
-	return p.configAuditReportDataFrom(ctx, report.Results[0])
-}
-
-func (p *plugin) sourceNameFrom(obj client.Object) string {
-	gvk := obj.GetObjectKind().GroupVersionKind()
-	group := gvk.Group
-	if len(group) > 0 {
-		group = "." + group
-	}
-	return fmt.Sprintf("%s/%s%s/%s/%s",
-		obj.GetNamespace(),
-		gvk.Kind,
-		group,
-		gvk.Version,
-		obj.GetName(),
-	)
-}
 
-func (p *plugin) configAuditReportDataFrom(ctx starboard.PluginContext, result Result) (v1alpha1.ConfigAuditReportData, error) {
 	var checks []v1alpha1.Check
 	var podChecks []v1alpha1.Check
 	containerNameToChecks := make(map[string][]v1alpha1.Check)
 
-	for _, pr := range result.PodResult.Results {
+	if len(report.Results) != 1 {
+		return v1alpha1.ConfigAuditReportData{}, fmt.Errorf("unexpected report results count, got: %d, want: %d", len(report.Results), 1)
+	}
+	for _, pr := range report.Results[0].PodResult.Results {
 		check := v1alpha1.Check{
 			ID:       pr.ID,
 			Message:  pr.Message,
@@ -356,7 +419,7 @@ func (p *plugin) configAuditReportDataFrom(ctx starboard.PluginContext, result R
 		podChecks = append(podChecks, check)
 	}
 
-	for _, cr := range result.PodResult.ContainerResults {
+	for _, cr := range report.Results[0].PodResult.ContainerResults {
 		var containerChecks []v1alpha1.Check
 		for _, crr := range cr.Results {
 			containerChecks = append(containerChecks, v1alpha1.Check{
@@ -376,14 +439,14 @@ func (p *plugin) configAuditReportDataFrom(ctx starboard.PluginContext, result R
 		containerNameToChecks[cr.Name] = containerChecks
 	}
 
-	imageRef, err := p.getImageRef(ctx)
+	imageRef, err := config.GetImageRef()
 	if err != nil {
-		return v1alpha1.ConfigAuditReportData{}, err
+		return v1alpha1.ConfigAuditReportData{}, fmt.Errorf("getting image ref: %w", err)
 	}
 
 	version, err := starboard.GetVersionFromImageRef(imageRef)
 	if err != nil {
-		return v1alpha1.ConfigAuditReportData{}, err
+		return v1alpha1.ConfigAuditReportData{}, fmt.Errorf("getting version from image ref: %w", err)
 	}
 
 	return v1alpha1.ConfigAuditReportData{
@@ -401,6 +464,21 @@ func (p *plugin) configAuditReportDataFrom(ctx starboard.PluginContext, result R
 	}, nil
 }
 
+func (p *plugin) sourceNameFrom(obj client.Object) string {
+	gvk := obj.GetObjectKind().GroupVersionKind()
+	group := gvk.Group
+	if len(group) > 0 {
+		group = "." + group
+	}
+	return fmt.Sprintf("%s/%s%s/%s/%s",
+		obj.GetNamespace(),
+		gvk.Kind,
+		group,
+		gvk.Version,
+		obj.GetName(),
+	)
+}
+
 func (p *plugin) configAuditSummaryFrom(podChecks []v1alpha1.Check, containerChecks map[string][]v1alpha1.Check) v1alpha1.ConfigAuditSummary {
 	var summary v1alpha1.ConfigAuditSummary
 	for _, c := range podChecks {
diff --git a/pkg/plugin/trivy/plugin.go b/pkg/plugin/trivy/plugin.go
index d7c88b1..123ca7e 100644
--- a/pkg/plugin/trivy/plugin.go
+++ b/pkg/plugin/trivy/plugin.go
@@ -24,11 +24,6 @@ const (
 	Plugin = "Trivy"
 )
 
-type plugin struct {
-	clock       ext.Clock
-	idGenerator ext.IDGenerator
-}
-
 const (
 	keyTrivyImageRef               = "trivy.imageRef"
 	keyTrivyMode                   = "trivy.mode"
@@ -62,11 +57,12 @@ const (
 	ClientServer Mode = "ClientServer"
 )
 
-// Config defines configuration params for the Trivy vulnerabilityreport.Plugin.
+// Config defines configuration params for this plugin.
 type Config struct {
 	starboard.PluginConfig
 }
 
+// GetImageRef returns upstream Trivy container image reference.
 func (c Config) GetImageRef() (string, error) {
 	return c.GetRequiredData(keyTrivyImageRef)
 }
@@ -109,7 +105,7 @@ func (c Config) GetInsecureRegistries() map[string]bool {
 	return insecureRegistries
 }
 
-// GetResourceRequirements creates v1.ResourceRequirements from the Config.
+// GetResourceRequirements creates ResourceRequirements from the Config.
 func (c Config) GetResourceRequirements() (corev1.ResourceRequirements, error) {
 	requirements := corev1.ResourceRequirements{
 		Requests: corev1.ResourceList{},
@@ -151,6 +147,11 @@ func (c Config) setResourceLimit(configKey string, k8sResourceList *corev1.Resou
 	return nil
 }
 
+type plugin struct {
+	clock       ext.Clock
+	idGenerator ext.IDGenerator
+}
+
 // NewPlugin constructs a new vulnerabilityreport.Plugin, which is using an
 // upstream Trivy container image to scan Kubernetes workloads.
 //


Test patch:
diff --git a/pkg/plugin/polaris/plugin_test.go b/pkg/plugin/polaris/plugin_test.go
index 45020b2..ca11a9f 100644
--- a/pkg/plugin/polaris/plugin_test.go
+++ b/pkg/plugin/polaris/plugin_test.go
@@ -12,6 +12,7 @@ import (
 	"github.com/aquasecurity/starboard/pkg/ext"
 	"github.com/aquasecurity/starboard/pkg/plugin/polaris"
 	"github.com/aquasecurity/starboard/pkg/starboard"
+	"github.com/stretchr/testify/require"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
@@ -27,55 +28,180 @@ var (
 	fixedClock = ext.NewFixedClock(fixedTime)
 )
 
+func TestConfig_GetResourceRequirements(t *testing.T) {
+	testCases := []struct {
+		name                 string
+		configData           polaris.Config
+		expectedError        string
+		expectedRequirements corev1.ResourceRequirements
+	}{
+		{
+			name:          "Should return empty requirements by default",
+			configData:    polaris.Config{PluginConfig: starboard.PluginConfig{}},
+			expectedError: "",
+			expectedRequirements: corev1.ResourceRequirements{
+				Requests: corev1.ResourceList{},
+				Limits:   corev1.ResourceList{},
+			},
+		},
+		{
+			name: "Should return configured resource requirement",
+			configData: polaris.Config{PluginConfig: starboard.PluginConfig{
+				Data: map[string]string{
+					"polaris.resources.request.cpu":    "800m",
+					"polaris.resources.request.memory": "200M",
+					"polaris.resources.limit.cpu":      "600m",
+					"polaris.resources.limit.memory":   "700M",
+				},
+			}},
+			expectedError: "",
+			expectedRequirements: corev1.ResourceRequirements{
+				Requests: corev1.ResourceList{
+					corev1.ResourceCPU:    resource.MustParse("800m"),
+					corev1.ResourceMemory: resource.MustParse("200M"),
+				},
+				Limits: corev1.ResourceList{
+					corev1.ResourceCPU:    resource.MustParse("600m"),
+					corev1.ResourceMemory: resource.MustParse("700M"),
+				},
+			},
+		},
+		{
+			name: "Should return error if resource is not parseable",
+			configData: polaris.Config{PluginConfig: starboard.PluginConfig{
+				Data: map[string]string{
+					"polaris.resources.request.cpu":    "roughly 100",
+					"polaris.resources.request.memory": "200M",
+					"polaris.resources.limit.cpu":      "600m",
+					"polaris.resources.limit.memory":   "700M",
+				},
+			}},
+			expectedError: "parsing resource definition polaris.resources.request.cpu: roughly 100 quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'",
+			expectedRequirements: corev1.ResourceRequirements{
+				Requests: corev1.ResourceList{
+					corev1.ResourceCPU:    resource.MustParse("800m"),
+					corev1.ResourceMemory: resource.MustParse("200M"),
+				},
+				Limits: corev1.ResourceList{
+					corev1.ResourceCPU:    resource.MustParse("600m"),
+					corev1.ResourceMemory: resource.MustParse("700M"),
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			resourceRequirement, err := tc.configData.GetResourceRequirements()
+			if tc.expectedError != "" {
+				require.EqualError(t, err, tc.expectedError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tc.expectedRequirements, resourceRequirement, tc.name)
+			}
+
+		})
+	}
+}
+
 func TestPlugin_Init(t *testing.T) {
-	g := NewGomegaWithT(t)
 
-	client := fake.NewClientBuilder().WithObjects(&corev1.ConfigMap{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:            "starboard-polaris-config",
-			Namespace:       "starboard-ns",
-			ResourceVersion: "0",
-		},
-		Data: map[string]string{
-			"polaris.imageRef": "quay.io/fairwinds/polaris:3.2",
-			"polaris.config.yaml": `checks:
+	t.Run("Should create the default config", func(t *testing.T) {
+		g := NewGomegaWithT(t)
+
+		client := fake.NewClientBuilder().Build()
+
+		pluginContext := starboard.NewPluginContext().
+			WithName(string(starboard.Polaris)).
+			WithNamespace("starboard-ns").
+			WithServiceAccountName("starboard-sa").
+			WithClient(client).
+			Get()
+
+		instance := polaris.NewPlugin(fixedClock)
+		err := instance.Init(pluginContext)
+		g.Expect(err).ToNot(HaveOccurred())
+
+		var cm corev1.ConfigMap
+		err = client.Get(context.Background(), types.NamespacedName{
+			Namespace: "starboard-ns",
+			Name:      "starboard-polaris-config",
+		}, &cm)
+		g.Expect(err).ToNot(HaveOccurred())
+		g.Expect(cm).To(Equal(corev1.ConfigMap{
+			TypeMeta: metav1.TypeMeta{
+				APIVersion: "v1",
+				Kind:       "ConfigMap",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "starboard-polaris-config",
+				Namespace: "starboard-ns",
+				Labels: map[string]string{
+					"app.kubernetes.io/managed-by": "starboard",
+				},
+				ResourceVersion: "1",
+			},
+			Data: map[string]string{
+				"polaris.imageRef":                 "quay.io/fairwinds/polaris:3.2",
+				"polaris.config.yaml":              polaris.DefaultConfigYAML,
+				"polaris.resources.request.cpu":    "50m",
+				"polaris.resources.request.memory": "50M",
+				"polaris.resources.limit.cpu":      "300m",
+				"polaris.resources.limit.memory":   "300M",
+			},
+		}))
+	})
+
+	t.Run("Should not overwrite existing config", func(t *testing.T) {
+		g := NewGomegaWithT(t)
+
+		client := fake.NewClientBuilder().WithObjects(&corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:            "starboard-polaris-config",
+				Namespace:       "starboard-ns",
+				ResourceVersion: "0",
+			},
+			Data: map[string]string{
+				"polaris.imageRef": "quay.io/fairwinds/polaris:3.2",
+				"polaris.config.yaml": `checks:
   cpuRequestsMissing: warning`,
-		},
-	}).Build()
+			},
+		}).Build()
 
-	pluginContext := starboard.NewPluginContext().
-		WithName(string(starboard.Polaris)).
-		WithNamespace("starboard-ns").
-		WithServiceAccountName("starboard-sa").
-		WithClient(client).
-		Get()
+		pluginContext := starboard.NewPluginContext().
+			WithName(string(starboard.Polaris)).
+			WithNamespace("starboard-ns").
+			WithServiceAccountName("starboard-sa").
+			WithClient(client).
+			Get()
 
-	instance := polaris.NewPlugin(fixedClock)
-	err := instance.Init(pluginContext)
-	g.Expect(err).ToNot(HaveOccurred())
+		instance := polaris.NewPlugin(fixedClock)
+		err := instance.Init(pluginContext)
+		g.Expect(err).ToNot(HaveOccurred())
 
-	var cm corev1.ConfigMap
-	err = client.Get(context.Background(), types.NamespacedName{
-		Namespace: "starboard-ns",
-		Name:      "starboard-polaris-config",
-	}, &cm)
-	g.Expect(err).ToNot(HaveOccurred())
-	g.Expect(cm).To(Equal(corev1.ConfigMap{
-		TypeMeta: metav1.TypeMeta{
-			APIVersion: "v1",
-			Kind:       "ConfigMap",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:            "starboard-polaris-config",
-			Namespace:       "starboard-ns",
-			ResourceVersion: "0",
-		},
-		Data: map[string]string{
-			"polaris.imageRef": "quay.io/fairwinds/polaris:3.2",
-			"polaris.config.yaml": `checks:
+		var cm corev1.ConfigMap
+		err = client.Get(context.Background(), types.NamespacedName{
+			Namespace: "starboard-ns",
+			Name:      "starboard-polaris-config",
+		}, &cm)
+		g.Expect(err).ToNot(HaveOccurred())
+		g.Expect(cm).To(Equal(corev1.ConfigMap{
+			TypeMeta: metav1.TypeMeta{
+				APIVersion: "v1",
+				Kind:       "ConfigMap",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:            "starboard-polaris-config",
+				Namespace:       "starboard-ns",
+				ResourceVersion: "0",
+			},
+			Data: map[string]string{
+				"polaris.imageRef": "quay.io/fairwinds/polaris:3.2",
+				"polaris.config.yaml": `checks:
   cpuRequestsMissing: warning`,
-		},
-	}))
+			},
+		}))
+	})
+
 }
 
 func TestPlugin_GetScanJobSpec(t *testing.T) {
@@ -90,7 +216,11 @@ func TestPlugin_GetScanJobSpec(t *testing.T) {
 		{
 			name: "Should return job spec for Deployment",
 			config: map[string]string{
-				"polaris.imageRef": "quay.io/fairwinds/polaris:3.2",
+				"polaris.imageRef":                 "quay.io/fairwinds/polaris:3.2",
+				"polaris.resources.request.cpu":    "50m",
+				"polaris.resources.request.memory": "50M",
+				"polaris.resources.limit.cpu":      "300m",
+				"polaris.resources.limit.memory":   "300M",
 			},
 			obj: &appsv1.Deployment{
 				TypeMeta: metav1.TypeMeta{
@@ -327,4 +457,25 @@ func TestPlugin_GetConfigHash(t *testing.T) {
 		g.Expect(err).ToNot(HaveOccurred())
 		g.Expect(hash1).To(Equal(hash2))
 	})
+
+	t.Run("Should exclude resource requirements from calculating hash", func(t *testing.T) {
+		g := NewGomegaWithT(t)
+
+		pluginContext1 := newPluginContextWithConfigData(map[string]string{
+			"foo":                           "bar",
+			"polaris.resources.request.cpu": "50m",
+		})
+		pluginContext2 := newPluginContextWithConfigData(map[string]string{
+			"foo":                           "bar",
+			"polaris.resources.request.cpu": "60m",
+		})
+
+		plugin := polaris.NewPlugin(fixedClock)
+		hash1, err := plugin.GetConfigHash(pluginContext1)
+		g.Expect(err).ToNot(HaveOccurred())
+
+		hash2, err := plugin.GetConfigHash(pluginContext2)
+		g.Expect(err).ToNot(HaveOccurred())
+		g.Expect(hash1).To(Equal(hash2))
+	})
 }
diff --git a/pkg/plugin/trivy/plugin_test.go b/pkg/plugin/trivy/plugin_test.go
index 0b35b5b..ccdef35 100644
--- a/pkg/plugin/trivy/plugin_test.go
+++ b/pkg/plugin/trivy/plugin_test.go
@@ -316,6 +316,60 @@ func TestPlugin_Init(t *testing.T) {
 			},
 		}, cm)
 	})
+
+	t.Run("Should not overwrite existing config", func(t *testing.T) {
+		client := fake.NewClientBuilder().WithObjects(
+			&corev1.ConfigMap{
+				TypeMeta: metav1.TypeMeta{
+					APIVersion: "v1",
+					Kind:       "ConfigMap",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:            "starboard-trivy-config",
+					Namespace:       "starboard-ns",
+					ResourceVersion: "1",
+				},
+				Data: map[string]string{
+					"trivy.imageRef": "docker.io/aquasec/trivy:0.16.0",
+					"trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL",
+					"trivy.mode":     "Standalone",
+				},
+			}).Build()
+
+		instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator())
+
+		pluginContext := starboard.NewPluginContext().
+			WithName(trivy.Plugin).
+			WithNamespace("starboard-ns").
+			WithServiceAccountName("starboard-sa").
+			WithClient(client).
+			Get()
+		err := instance.Init(pluginContext)
+		require.NoError(t, err)
+
+		var cm corev1.ConfigMap
+		err = client.Get(context.Background(), types.NamespacedName{
+			Namespace: "starboard-ns",
+			Name:      "starboard-trivy-config",
+		}, &cm)
+		require.NoError(t, err)
+		assert.Equal(t, corev1.ConfigMap{
+			TypeMeta: metav1.TypeMeta{
+				APIVersion: "v1",
+				Kind:       "ConfigMap",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:            "starboard-trivy-config",
+				Namespace:       "starboard-ns",
+				ResourceVersion: "1",
+			},
+			Data: map[string]string{
+				"trivy.imageRef": "docker.io/aquasec/trivy:0.16.0",
+				"trivy.severity": "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL",
+				"trivy.mode":     "Standalone",
+			},
+		}, cm)
+	})
 }
 
 func TestPlugin_GetScanJobSpec(t *testing.T) {