@@ -5,7 +5,14 @@ import (
55 "fmt"
66
77 executionv1 "github.com/secureCodeBox/secureCodeBox-v2-alpha/operator/apis/execution/v1"
8+ util "github.com/secureCodeBox/secureCodeBox-v2-alpha/operator/utils"
9+ batch "k8s.io/api/batch/v1"
10+ corev1 "k8s.io/api/core/v1"
11+ rbacv1 "k8s.io/api/rbac/v1"
12+ resource "k8s.io/apimachinery/pkg/api/resource"
13+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
814 "k8s.io/apimachinery/pkg/types"
15+ ctrl "sigs.k8s.io/controller-runtime"
916 "sigs.k8s.io/controller-runtime/pkg/client"
1017)
1118
@@ -170,6 +177,20 @@ func (r *ScanReconciler) executeReadAndWriteHooks(scan *executionv1.Scan) error
170177 return nil
171178}
172179
180+ func containsJobForHook (jobs * batch.JobList , hook executionv1.ScanCompletionHook ) bool {
181+ if len (jobs .Items ) == 0 {
182+ return false
183+ }
184+
185+ for _ , job := range jobs .Items {
186+ if job .ObjectMeta .Labels ["experimental.securecodebox.io/hook-name" ] == hook .Name {
187+ return true
188+ }
189+ }
190+
191+ return false
192+ }
193+
173194func (r * ScanReconciler ) startReadOnlyHooks (scan * executionv1.Scan ) error {
174195 ctx := context .Background ()
175196
@@ -278,3 +299,129 @@ func (r *ScanReconciler) checkIfReadOnlyHookIsCompleted(scan *executionv1.Scan)
278299 // Waiting until all are done.
279300 return nil
280301}
302+
303+ func (r * ScanReconciler ) createJobForHook (hook * executionv1.ScanCompletionHook , scan * executionv1.Scan , cliArgs []string ) (string , error ) {
304+ ctx := context .Background ()
305+
306+ serviceAccountName := "scan-completion-hook"
307+ if hook .Spec .ServiceAccountName != nil {
308+ // Hook uses a custom ServiceAccount
309+ serviceAccountName = * hook .Spec .ServiceAccountName
310+ } else {
311+ // Check and create a serviceAccount for the hook in its namespace, if it doesn't already exist.
312+ rules := []rbacv1.PolicyRule {
313+ {
314+ APIGroups : []string {"execution.experimental.securecodebox.io" },
315+ Resources : []string {"scans" },
316+ Verbs : []string {"get" },
317+ },
318+ {
319+ APIGroups : []string {"execution.experimental.securecodebox.io" },
320+ Resources : []string {"scans/status" },
321+ Verbs : []string {"get" , "patch" },
322+ },
323+ }
324+
325+ r .ensureServiceAccountExists (
326+ hook .Namespace ,
327+ serviceAccountName ,
328+ "ScanCompletionHooks need to access the current scan to view where its results are stored" ,
329+ rules ,
330+ )
331+ }
332+
333+ standardEnvVars := []corev1.EnvVar {
334+ {
335+ Name : "NAMESPACE" ,
336+ ValueFrom : & corev1.EnvVarSource {
337+ FieldRef : & corev1.ObjectFieldSelector {
338+ FieldPath : "metadata.namespace" ,
339+ },
340+ },
341+ },
342+ {
343+ Name : "SCAN_NAME" ,
344+ Value : scan .Name ,
345+ },
346+ }
347+
348+ // Starting a new job based on the current ReadAndWrite Hook
349+ labels := scan .ObjectMeta .DeepCopy ().Labels
350+ if labels == nil {
351+ labels = make (map [string ]string )
352+ }
353+ if hook .Spec .Type == executionv1 .ReadAndWrite {
354+ labels ["experimental.securecodebox.io/job-type" ] = "read-and-write-hook"
355+ } else if hook .Spec .Type == executionv1 .ReadOnly {
356+ labels ["experimental.securecodebox.io/job-type" ] = "read-only-hook"
357+ }
358+ labels ["experimental.securecodebox.io/hook-name" ] = hook .Name
359+
360+ var backOffLimit int32 = 3
361+ job := & batch.Job {
362+ ObjectMeta : metav1.ObjectMeta {
363+ Annotations : make (map [string ]string ),
364+ GenerateName : util .TruncateName (fmt .Sprintf ("%s-%s" , hook .Name , scan .Name )),
365+ Namespace : scan .Namespace ,
366+ Labels : labels ,
367+ },
368+ Spec : batch.JobSpec {
369+ BackoffLimit : & backOffLimit ,
370+ Template : corev1.PodTemplateSpec {
371+ ObjectMeta : metav1.ObjectMeta {
372+ Annotations : map [string ]string {
373+ "auto-discovery.experimental.securecodebox.io/ignore" : "true" ,
374+ },
375+ },
376+ Spec : corev1.PodSpec {
377+ ServiceAccountName : serviceAccountName ,
378+ RestartPolicy : corev1 .RestartPolicyNever ,
379+ ImagePullSecrets : hook .Spec .ImagePullSecrets ,
380+ Containers : []corev1.Container {
381+ {
382+ Name : "hook" ,
383+ Image : hook .Spec .Image ,
384+ Args : cliArgs ,
385+ Env : append (hook .Spec .Env , standardEnvVars ... ),
386+ ImagePullPolicy : "IfNotPresent" ,
387+ Resources : corev1.ResourceRequirements {
388+ Requests : corev1.ResourceList {
389+ corev1 .ResourceCPU : resource .MustParse ("200m" ),
390+ corev1 .ResourceMemory : resource .MustParse ("100Mi" ),
391+ },
392+ Limits : corev1.ResourceList {
393+ corev1 .ResourceCPU : resource .MustParse ("400m" ),
394+ corev1 .ResourceMemory : resource .MustParse ("200Mi" ),
395+ },
396+ },
397+ },
398+ },
399+ },
400+ },
401+ TTLSecondsAfterFinished : nil ,
402+ },
403+ }
404+ if err := ctrl .SetControllerReference (scan , job , r .Scheme ); err != nil {
405+ r .Log .Error (err , "Unable to set controllerReference on job" , "job" , job )
406+ return "" , err
407+ }
408+
409+ if err := r .Create (ctx , job ); err != nil {
410+ return "" , err
411+ }
412+ return job .Name , nil
413+ }
414+
415+ func (r * ScanReconciler ) updateHookStatus (scan * executionv1.Scan , hookStatus executionv1.HookStatus ) error {
416+ for i , hook := range scan .Status .ReadAndWriteHookStatus {
417+ if hook .HookName == hookStatus .HookName {
418+ scan .Status .ReadAndWriteHookStatus [i ] = hookStatus
419+ break
420+ }
421+ }
422+ if err := r .Status ().Update (context .Background (), scan ); err != nil {
423+ r .Log .Error (err , "unable to update Scan status" )
424+ return err
425+ }
426+ return nil
427+ }
0 commit comments