@@ -5,7 +5,14 @@ import (
5
5
"fmt"
6
6
7
7
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"
8
14
"k8s.io/apimachinery/pkg/types"
15
+ ctrl "sigs.k8s.io/controller-runtime"
9
16
"sigs.k8s.io/controller-runtime/pkg/client"
10
17
)
11
18
@@ -170,6 +177,20 @@ func (r *ScanReconciler) executeReadAndWriteHooks(scan *executionv1.Scan) error
170
177
return nil
171
178
}
172
179
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
+
173
194
func (r * ScanReconciler ) startReadOnlyHooks (scan * executionv1.Scan ) error {
174
195
ctx := context .Background ()
175
196
@@ -278,3 +299,129 @@ func (r *ScanReconciler) checkIfReadOnlyHookIsCompleted(scan *executionv1.Scan)
278
299
// Waiting until all are done.
279
300
return nil
280
301
}
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