diff --git a/operator/.vscode/launch.json b/operator/.vscode/launch.json index df17791049..a9fb1ccd86 100644 --- a/operator/.vscode/launch.json +++ b/operator/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "Start Operator (minio)", "type": "go", "request": "launch", - "mode": "auto", + "mode": "debug", "program": "main.go", "env": { "MINIO_ACCESS_KEY": "minioadmin", @@ -26,6 +26,16 @@ "request": "launch", "mode": "auto", "program": "main.go" + }, + { + "name": "Debug Unit Tests", + "type": "go", + "request": "launch", + "mode": "test", + "program": "${workspaceFolder}/controllers/execution", + "args": ["-test.v"], + //"preLaunchTask": "makefileMagic", + "env": {"KUBEBUILDER_ASSETS": "${workspaceFolder}/testbin/bin"} } ] } \ No newline at end of file diff --git a/operator/.vscode/tasks.json b/operator/.vscode/tasks.json new file mode 100644 index 0000000000..88be450e63 --- /dev/null +++ b/operator/.vscode/tasks.json @@ -0,0 +1,11 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "makefileMagic", + "command": "bash", + "args": ["-c", "source ${workspaceFolder}/testbin/setup-envtest.sh && fetch_envtest_tools ${workspaceFolder}/testbin && setup_envtest_env ${workspaceFolder}/testbin"], + "type": "shell" + }, +] +} \ No newline at end of file diff --git a/operator/Makefile b/operator/Makefile index aaf0605d2d..34f52a53cb 100644 --- a/operator/Makefile +++ b/operator/Makefile @@ -77,7 +77,11 @@ vet: ## Run go vet against code. .PHONY: test test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test -tags="fast slow" ./... -coverprofile cover.out + +.PHONY: test-fast +test-fast: manifests generate fmt vet envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test -tags="fast" ./... -coverprofile cover.out .PHONY: view-coverage view-coverage: @@ -144,7 +148,7 @@ helm-deploy: --set="image.pullPolicy=IfNotPresent" \ --set="lurker.image.repository=docker.io/$(IMG_NS)/$(LURKER_IMG)" \ --set="lurker.image.tag=$(IMG_TAG)" \ - --set="lurker.image.pullPolicy=IfNotPresent" + --set="lurker.image.pullPolicy=IfNotPresent" \ --set="minio.auth.rootUser = $(MINIO_ROOT_USER)" \ --set="minio.auth.rootPassword = $(MINIO_ROOT_PASSWORD)" diff --git a/operator/apis/execution/v1/scheduledscan_types.go b/operator/apis/execution/v1/scheduledscan_types.go index 97007f324a..09b06365a3 100644 --- a/operator/apis/execution/v1/scheduledscan_types.go +++ b/operator/apis/execution/v1/scheduledscan_types.go @@ -18,8 +18,13 @@ type ScheduledScanSpec struct { // Interval describes how often the scan should be repeated // Examples: '12h', '30m' + // +kubebuilder:validation:Optional Interval metav1.Duration `json:"interval"` + // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. + // +kubebuilder:validation:Optional + Schedule string `json:"schedule"` + // SuccessfulJobsHistoryLimit determines how many past Scans will be kept until the oldest one will be deleted, defaults to 3. When set to 0, Scans will be deleted directly after completion // +kubebuilder:validation:Optional // +kubebuilder:validation:Minimum=0 @@ -59,6 +64,7 @@ type ScheduledScanStatus struct { // +kubebuilder:printcolumn:name="UID",type=string,JSONPath=`.metadata.uid`,description="K8s Resource UID",priority=1 // +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.scanSpec.scanType`,description="Scan Type" // +kubebuilder:printcolumn:name="Interval",type=string,JSONPath=`.spec.interval`,description="Interval" +// +kubebuilder:printcolumn:name="Schedule",type=string,JSONPath=`.spec.schedule`,description="Schedule" // +kubebuilder:printcolumn:name="Findings",type=string,JSONPath=`.status.findings.count`,description="Total Finding Count" // +kubebuilder:printcolumn:name="Parameters",type=string,JSONPath=`.spec.scanSpec.parameters`,description="Arguments passed to the Scanner",priority=1 diff --git a/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml b/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml index 02adc44843..71a7430485 100644 --- a/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml +++ b/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml @@ -29,6 +29,10 @@ spec: jsonPath: .spec.interval name: Interval type: string + - description: Schedule + jsonPath: .spec.schedule + name: Schedule + type: string - description: Total Finding Count jsonPath: .status.findings.count name: Findings @@ -4240,6 +4244,9 @@ spec: type: object type: array type: object + schedule: + description: The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. + type: string successfulJobsHistoryLimit: description: SuccessfulJobsHistoryLimit determines how many past Scans will be kept until the oldest one will be deleted, defaults to 3. @@ -4248,7 +4255,6 @@ spec: minimum: 0 type: integer required: - - interval - scanSpec type: object status: diff --git a/operator/config/rbac/role.yaml b/operator/config/rbac/role.yaml index 661d011ab0..fecc10e53d 100644 --- a/operator/config/rbac/role.yaml +++ b/operator/config/rbac/role.yaml @@ -5,6 +5,13 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch - apiGroups: - batch resources: diff --git a/operator/controllers/execution/scantype_controller.go b/operator/controllers/execution/scantype_controller.go index 9cc0ea75a1..28d74600e0 100644 --- a/operator/controllers/execution/scantype_controller.go +++ b/operator/controllers/execution/scantype_controller.go @@ -31,6 +31,9 @@ type ScanTypeReconciler struct { // +kubebuilder:rbac:groups="execution.securecodebox.io",resources=scheduledscans,verbs=get;list;watch;create;update;patch // +kubebuilder:rbac:groups="execution.securecodebox.io/status",resources=scheduledscans,verbs=get;update;patch +// Allows the ScanType Controller to create and patch Events +// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch + // Reconcile compares the Service object against the state of the cluster and updates both if needed func (r *ScanTypeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log diff --git a/operator/controllers/execution/scantype_controller_test.go b/operator/controllers/execution/scantype_controller_test.go index 22b3ec4a69..27e5137ff5 100644 --- a/operator/controllers/execution/scantype_controller_test.go +++ b/operator/controllers/execution/scantype_controller_test.go @@ -2,6 +2,9 @@ // // SPDX-License-Identifier: Apache-2.0 +//go:build fast +// +build fast + package controllers import ( @@ -33,10 +36,10 @@ var _ = Describe("ScanType controller", func() { createNamespace(ctx, namespace) createScanType(ctx, namespace) - scheduledScan := createScheduledScan(ctx, namespace, true) + scheduledScan := createScheduledScanWithInterval(ctx, namespace, true) // ensure that the ScheduledScan has been triggered - waitForScheduledScanToBeTriggered(ctx, namespace) + waitForScheduledScanToBeTriggered(ctx, namespace, timeout) k8sClient.Get(ctx, types.NamespacedName{Name: "test-scan", Namespace: namespace}, &scheduledScan) initialExecutionTime := *scheduledScan.Status.LastScheduleTime @@ -74,10 +77,10 @@ var _ = Describe("ScanType controller", func() { createNamespace(ctx, namespace) createScanType(ctx, namespace) - scheduledScan := createScheduledScan(ctx, namespace, true) + scheduledScan := createScheduledScanWithInterval(ctx, namespace, true) // ensure that the ScheduledScan has been triggered - waitForScheduledScanToBeTriggered(ctx, namespace) + waitForScheduledScanToBeTriggered(ctx, namespace, timeout) k8sClient.Get(ctx, types.NamespacedName{Name: "test-scan", Namespace: namespace}, &scheduledScan) initialExecutionTime := *scheduledScan.Status.LastScheduleTime @@ -104,10 +107,10 @@ var _ = Describe("ScanType controller", func() { createNamespace(ctx, namespace) createScanType(ctx, namespace) - scheduledScan := createScheduledScan(ctx, namespace, false) + scheduledScan := createScheduledScanWithInterval(ctx, namespace, false) // ensure that the ScheduledScan has been triggered - waitForScheduledScanToBeTriggered(ctx, namespace) + waitForScheduledScanToBeTriggered(ctx, namespace, timeout) k8sClient.Get(ctx, types.NamespacedName{Name: "test-scan", Namespace: namespace}, &scheduledScan) initialExecutionTime := *scheduledScan.Status.LastScheduleTime @@ -139,7 +142,7 @@ var _ = Describe("ScanType controller", func() { }) }) -func waitForScheduledScanToBeTriggered(ctx context.Context, namespace string) { +func waitForScheduledScanToBeTriggered(ctx context.Context, namespace string, timeout time.Duration) { var scheduledScan executionv1.ScheduledScan By("Wait for ScheduledScan to trigger the initial Scan") Eventually(func() bool { diff --git a/operator/controllers/execution/scheduledscan_controller.go b/operator/controllers/execution/scheduledscan_controller.go index 83f3756f36..2b70c2897b 100644 --- a/operator/controllers/execution/scheduledscan_controller.go +++ b/operator/controllers/execution/scheduledscan_controller.go @@ -13,9 +13,11 @@ import ( "time" "github.com/go-logr/logr" + "github.com/robfig/cron" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -31,8 +33,9 @@ var ( // ScheduledScanReconciler reconciles a ScheduledScan object type ScheduledScanReconciler struct { client.Client - Log logr.Logger - Scheme *runtime.Scheme + Log logr.Logger + Scheme *runtime.Scheme + Recorder record.EventRecorder } // +kubebuilder:rbac:groups=execution.securecodebox.io,resources=scheduledscans,verbs=get;list;watch;create;update;patch;delete @@ -40,6 +43,9 @@ type ScheduledScanReconciler struct { // +kubebuilder:rbac:groups=execution.securecodebox.io,resources=scans,verbs=get;list;create // +kubebuilder:rbac:groups=execution.securecodebox.io,resources=scans/status,verbs=get +// Allows the ScheduledScan Controller to create and patch Events +// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch + // Reconcile comapares the ScheduledScan Resource with the State of the Cluster and updates both accordingly func (r *ScheduledScanReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithValues("scheduledscan", req.NamespacedName) @@ -100,11 +106,10 @@ func (r *ScheduledScanReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // Calculate the next schedule - var nextSchedule time.Time - if scheduledScan.Status.LastScheduleTime != nil { - nextSchedule = scheduledScan.Status.LastScheduleTime.Add(scheduledScan.Spec.Interval.Duration) - } else { - nextSchedule = time.Now().Add(-1 * time.Second) + nextSchedule, err := getNextSchedule(r, scheduledScan, time.Now()) + if err != nil { + log.Error(err, "Unable to calculate next schedule") + return ctrl.Result{}, err } // check if it is time to start the next Scan @@ -156,12 +161,48 @@ func (r *ScheduledScanReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // Recalculate next schedule - nextSchedule = time.Now().Add(scheduledScan.Spec.Interval.Duration) + nextSchedule, err = getNextSchedule(r, scheduledScan, time.Now()) } return ctrl.Result{RequeueAfter: nextSchedule.Sub(time.Now())}, nil } +func getNextSchedule(r *ScheduledScanReconciler, scheduledScan executionv1.ScheduledScan, now time.Time) (next time.Time, err error) { + // check if the Cron schedule is set + if scheduledScan.Spec.Schedule != "" { + sched, err := cron.ParseStandard(scheduledScan.Spec.Schedule) + if err != nil { + r.Recorder.Event(&scheduledScan, "Warning", "ScheduleParseError", fmt.Sprintf("Unparseable schedule %q: %v", scheduledScan.Spec.Schedule, err)) + return time.Time{}, fmt.Errorf("Unparseable schedule %q: %v", scheduledScan.Spec.Schedule, err) + } + + // for optimization purposes, cheat a bit and start from our last observed run time + // we could reconstitute this here, but there's not much point, since we've + // just updated it. + var earliestTime time.Time + if scheduledScan.Status.LastScheduleTime != nil { + earliestTime = scheduledScan.Status.LastScheduleTime.Time + } else { + earliestTime = scheduledScan.ObjectMeta.CreationTimestamp.Time + } + if earliestTime.After(now) { + return sched.Next(now), nil + } + return sched.Next(earliestTime), nil + } + if scheduledScan.Spec.Interval.Duration > 0 { + var nextSchedule time.Time + if scheduledScan.Status.LastScheduleTime != nil { + nextSchedule = scheduledScan.Status.LastScheduleTime.Add(scheduledScan.Spec.Interval.Duration) + } else { + nextSchedule = time.Now().Add(-1 * time.Second) + } + return nextSchedule, nil + } + r.Recorder.Event(&scheduledScan, "Warning", "NoScheduleOrInterval", "No valid schedule or interval found") + return time.Time{}, fmt.Errorf("No schedule or interval found") +} + // Copy over securecodebox.io annotations from the scheduledScan to the created scan func getAnnotationsForScan(scheduledScan executionv1.ScheduledScan) map[string]string { annotations := map[string]string{} @@ -212,6 +253,7 @@ func (r *ScheduledScanReconciler) deleteOldScans(scans []executionv1.Scan, maxCo // SetupWithManager sets up the controller and initializes every thing it needs func (r *ScheduledScanReconciler) SetupWithManager(mgr ctrl.Manager) error { + // set up a real clock, since we're not in a test ctx := context.Background() if err := mgr.GetFieldIndexer().IndexField(ctx, &executionv1.Scan{}, ownerKey, func(rawObj client.Object) []string { // grab the job object, extract the owner... diff --git a/operator/controllers/execution/scheduledscan_controller_slow_test.go b/operator/controllers/execution/scheduledscan_controller_slow_test.go new file mode 100644 index 0000000000..fd3a273875 --- /dev/null +++ b/operator/controllers/execution/scheduledscan_controller_slow_test.go @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: the secureCodeBox authors +// +// SPDX-License-Identifier: Apache-2.0 + +//go:build slow +// +build slow + +package controllers + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + executionv1 "github.com/secureCodeBox/secureCodeBox/operator/apis/execution/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + //+kubebuilder:scaffold:imports +) + +var _ = Describe("ScheduledScan controller", func() { + Context("A Scan is triggred due to a Scheduled Scan with Schedule in Spec", func() { + It("The ScheduledScan's should be triggered according to the Schedule", func() { + ctx := context.Background() + namespace := "scantype-multiple-scheduled-scan-triggerd-test-schedule" + + createNamespace(ctx, namespace) + createScanType(ctx, namespace) + scheduledScan := createScheduledScanWithSchedule(ctx, namespace, true) + + var scanlist executionv1.ScanList + + // ensure that the ScheduledScan has been triggered + waitForScheduledScanToBeTriggered(ctx, namespace, 90*time.Second) + k8sClient.List(ctx, &scanlist, client.InNamespace(namespace)) + + Expect(scheduledScan.Spec.Schedule).Should(Equal("*/1 * * * *")) + Expect(scanlist.Items).Should(HaveLen(1)) + }) + }) +}) diff --git a/operator/controllers/execution/scheduledscan_controller_test.go b/operator/controllers/execution/scheduledscan_controller_test.go index acfd2de029..0e4e4ef923 100644 --- a/operator/controllers/execution/scheduledscan_controller_test.go +++ b/operator/controllers/execution/scheduledscan_controller_test.go @@ -1,6 +1,10 @@ // SPDX-FileCopyrightText: the secureCodeBox authors // // SPDX-License-Identifier: Apache-2.0 + +//go:build fast +// +build fast + package controllers import ( @@ -64,18 +68,18 @@ var _ = Describe("ScheduledScan controller", func() { } }) }) - Context("A Scan is triggred due to a Scheduled Scan", func() { + Context("A Scan is triggred due to a Scheduled Scan with Interval in Spec", func() { It("The ScheduledScan's Finding Summary shoud be updated of with the results of the successful Scan", func() { ctx := context.Background() namespace := "scantype-multiple-scheduled-scan-triggerd-test" createNamespace(ctx, namespace) createScanType(ctx, namespace) - scheduledScan := createScheduledScan(ctx, namespace, true) + scheduledScan := createScheduledScanWithInterval(ctx, namespace, true) var scanlist executionv1.ScanList // ensure that the ScheduledScan has been triggered - waitForScheduledScanToBeTriggered(ctx, namespace) + waitForScheduledScanToBeTriggered(ctx, namespace, timeout) k8sClient.List(ctx, &scanlist, client.InNamespace(namespace)) Expect(scanlist.Items).Should(HaveLen(1)) diff --git a/operator/controllers/execution/suite_test.go b/operator/controllers/execution/suite_test.go index 425ea0079c..b87c6f125d 100644 --- a/operator/controllers/execution/suite_test.go +++ b/operator/controllers/execution/suite_test.go @@ -80,9 +80,10 @@ var _ = BeforeSuite(func() { Log: ctrl.Log.WithName("controllers").WithName("ScanTypeController"), }).SetupWithManager(k8sManager) err = (&ScheduledScanReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("ScheduledScanController"), + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + Recorder: k8sManager.GetEventRecorderFor("ScheduledScanController"), + Log: ctrl.Log.WithName("controllers").WithName("ScheduledScanController"), }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) diff --git a/operator/controllers/execution/test_utils_test.go b/operator/controllers/execution/test_utils_test.go index dadce804fa..a28d58c848 100644 --- a/operator/controllers/execution/test_utils_test.go +++ b/operator/controllers/execution/test_utils_test.go @@ -2,6 +2,9 @@ // // SPDX-License-Identifier: Apache-2.0 +//go:build fast +// +build fast + package controllers import ( @@ -63,7 +66,32 @@ func createScanType(ctx context.Context, namespace string) { Expect(k8sClient.Create(ctx, scanType)).Should(Succeed()) } -func createScheduledScan(ctx context.Context, namespace string, retriggerOnScanTypeChange bool) executionv1.ScheduledScan { +func createScheduledScanWithInterval(ctx context.Context, namespace string, retriggerOnScanTypeChange bool) executionv1.ScheduledScan { + namespaceLocalResourceMode := executionv1.NamespaceLocal + + scheduledScan := executionv1.ScheduledScan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-scan", + Namespace: namespace, + }, + Spec: executionv1.ScheduledScanSpec{ + Interval: metav1.Duration{Duration: 42 * time.Hour}, + RetriggerOnScanTypeChange: retriggerOnScanTypeChange, + ScanSpec: &executionv1.ScanSpec{ + ScanType: "nmap", + ResourceMode: &namespaceLocalResourceMode, + Parameters: []string{"scanme.nmap.org"}, + }, + }, + } + Expect(k8sClient.Create(ctx, &scheduledScan)).Should(Succeed()) + + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-scan", Namespace: namespace}, &scheduledScan)).Should(Succeed()) + + return scheduledScan +} + +func createScheduledScanWithSchedule(ctx context.Context, namespace string, retriggerOnScanTypeChange bool) executionv1.ScheduledScan { namespaceLocalResourceMode := executionv1.NamespaceLocal scheduledScan := executionv1.ScheduledScan{ @@ -72,6 +100,7 @@ func createScheduledScan(ctx context.Context, namespace string, retriggerOnScanT Namespace: namespace, }, Spec: executionv1.ScheduledScanSpec{ + Schedule: "*/1 * * * *", Interval: metav1.Duration{Duration: 42 * time.Hour}, RetriggerOnScanTypeChange: retriggerOnScanTypeChange, ScanSpec: &executionv1.ScanSpec{ diff --git a/operator/crds/execution.securecodebox.io_scheduledscans.yaml b/operator/crds/execution.securecodebox.io_scheduledscans.yaml index d71b360f9a..e1a0992408 100644 --- a/operator/crds/execution.securecodebox.io_scheduledscans.yaml +++ b/operator/crds/execution.securecodebox.io_scheduledscans.yaml @@ -4813,6 +4813,9 @@ spec: type: object type: array type: object + schedule: + description: The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. + type: string successfulJobsHistoryLimit: description: SuccessfulJobsHistoryLimit determines how many past Scans diff --git a/operator/go.mod b/operator/go.mod index 1f2fb67436..226cde4c26 100644 --- a/operator/go.mod +++ b/operator/go.mod @@ -66,6 +66,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.34.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/robfig/cron v1.2.0 github.com/rs/xid v1.4.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/operator/go.sum b/operator/go.sum index 891febcb87..4dc7ff8780 100644 --- a/operator/go.sum +++ b/operator/go.sum @@ -483,6 +483,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/operator/main.go b/operator/main.go index ad5d6bdac9..f502859a6d 100644 --- a/operator/main.go +++ b/operator/main.go @@ -79,9 +79,10 @@ func main() { os.Exit(1) } if err = (&executioncontrollers.ScheduledScanReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("execution").WithName("ScheduledScan"), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("execution").WithName("ScheduledScan"), + Recorder: mgr.GetEventRecorderFor("ScheduledScanController"), + Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ScheduledScan") os.Exit(1) diff --git a/operator/templates/rbac/role.yaml b/operator/templates/rbac/role.yaml index ac5d0c8aa8..d7306a4f3a 100644 --- a/operator/templates/rbac/role.yaml +++ b/operator/templates/rbac/role.yaml @@ -9,6 +9,13 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch - apiGroups: - batch resources: