@@ -115,191 +115,8 @@ func (r *ScanReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
115115 case "ParseCompleted" :
116116 err = r .setHookStatus (& scan )
117117 case "ReadAndWriteHookProcessing" :
118- // First Array entry which is not Completed.
119- var nonCompletedHook * executionv1.HookStatus
118+ err = r .executeReadAndWriteHooks (& scan )
120119
121- for _ , hook := range scan .Status .ReadAndWriteHookStatus {
122- if hook .State != executionv1 .Completed {
123- nonCompletedHook = & hook
124- break
125- }
126- }
127-
128- if nonCompletedHook == nil {
129- scan .Status .State = "ReadAndWriteHookCompleted"
130- if err := r .Status ().Update (ctx , & scan ); err != nil {
131- r .Log .Error (err , "unable to update Scan status" )
132- return ctrl.Result {}, err
133- }
134- return ctrl.Result {}, nil
135- }
136-
137- if nonCompletedHook .State == executionv1 .Pending {
138- rules := []rbacv1.PolicyRule {
139- {
140- APIGroups : []string {"execution.experimental.securecodebox.io" },
141- Resources : []string {"scans" },
142- Verbs : []string {"get" },
143- },
144- }
145- serviceAccountName := "scan-completion-hook"
146- r .ensureServiceAccountExists (
147- scan .Namespace ,
148- serviceAccountName ,
149- "ScanCompletionHooks need to access the current scan to view where its results are stored" ,
150- rules ,
151- )
152-
153- rawFileURL , err := r .PresignedGetURL (scan .UID , scan .Status .RawResultFile )
154- if err != nil {
155- return ctrl.Result {}, err
156- }
157- findingsFileURL , err := r .PresignedGetURL (scan .UID , "findings.json" )
158- if err != nil {
159- return ctrl.Result {}, err
160- }
161-
162- bucketName := os .Getenv ("S3_BUCKET" )
163- rawFileUploadURL , err := r .MinioClient .PresignedPutObject (bucketName , fmt .Sprintf ("scan-%s/%s" , scan .UID , scan .Status .RawResultFile ), 12 * time .Hour )
164- if err != nil {
165- r .Log .Error (err , "Could not get presigned url from s3 or compatible storage provider" )
166- return ctrl.Result {}, err
167- }
168- findingsUploadURL , err := r .MinioClient .PresignedPutObject (bucketName , fmt .Sprintf ("scan-%s/findings.json" , scan .UID ), 12 * time .Hour )
169- if err != nil {
170- r .Log .Error (err , "Could not get presigned url from s3 or compatible storage provider" )
171- return ctrl.Result {}, err
172- }
173-
174- var hook executionv1.ScanCompletionHook
175- err = r .Get (ctx , types.NamespacedName {Name : nonCompletedHook .HookName , Namespace : scan .Namespace }, & hook )
176- if err != nil {
177- r .Log .Error (err , "Failed to get ReadAndWrite Hook for HookStatus" )
178- return ctrl.Result {}, err
179- }
180-
181- standardEnvVars := []corev1.EnvVar {
182- {
183- Name : "NAMESPACE" ,
184- ValueFrom : & corev1.EnvVarSource {
185- FieldRef : & corev1.ObjectFieldSelector {
186- FieldPath : "metadata.namespace" ,
187- },
188- },
189- },
190- {
191- Name : "SCAN_NAME" ,
192- Value : scan .Name ,
193- },
194- }
195-
196- // Starting a new job based on the current ReadAndWrite Hook
197- labels := scan .ObjectMeta .DeepCopy ().Labels
198- if labels == nil {
199- labels = make (map [string ]string )
200- }
201- labels ["experimental.securecodebox.io/job-type" ] = "read-and-write-hook"
202- var backOffLimit int32 = 3
203- job := & batch.Job {
204- ObjectMeta : metav1.ObjectMeta {
205- Annotations : make (map [string ]string ),
206- Name : fmt .Sprintf ("%s-%s" , hook .Name , scan .Name ),
207- Namespace : scan .Namespace ,
208- Labels : labels ,
209- },
210- Spec : batch.JobSpec {
211- BackoffLimit : & backOffLimit ,
212- Template : corev1.PodTemplateSpec {
213- ObjectMeta : metav1.ObjectMeta {
214- Annotations : map [string ]string {
215- "auto-discovery.experimental.securecodebox.io/ignore" : "true" ,
216- },
217- },
218- Spec : corev1.PodSpec {
219- ServiceAccountName : serviceAccountName ,
220- RestartPolicy : corev1 .RestartPolicyNever ,
221- Containers : []corev1.Container {
222- {
223- Name : "hook" ,
224- Image : hook .Spec .Image ,
225- Args : []string {
226- rawFileURL ,
227- findingsFileURL ,
228- rawFileUploadURL .String (),
229- findingsUploadURL .String (),
230- },
231- Env : append (hook .Spec .Env , standardEnvVars ... ),
232- ImagePullPolicy : "IfNotPresent" ,
233- },
234- },
235- },
236- },
237- TTLSecondsAfterFinished : nil ,
238- },
239- }
240- if err := ctrl .SetControllerReference (& scan , job , r .Scheme ); err != nil {
241- r .Log .Error (err , "Unable to set controllerReference on job" , "job" , job )
242- return ctrl.Result {}, err
243- }
244-
245- if err := r .Create (ctx , job ); err != nil {
246- r .Log .Error (err , "Unable to create Job for ReadOnlyHook" , "job" , job )
247- return ctrl.Result {}, err
248- }
249-
250- for i , hookStatus := range scan .Status .ReadAndWriteHookStatus {
251- if hookStatus .HookName == nonCompletedHook .HookName {
252- scan .Status .ReadAndWriteHookStatus [i ].JobName = job .Name
253- scan .Status .ReadAndWriteHookStatus [i ].State = executionv1 .InProgress
254- }
255- }
256-
257- if err := r .Status ().Update (ctx , & scan ); err != nil {
258- r .Log .Error (err , "unable to update Scan status" )
259- return ctrl.Result {}, err
260- }
261- return ctrl.Result {}, err
262- }
263-
264- if nonCompletedHook .State == executionv1 .InProgress {
265- jobStatus , err := r .checkIfJobIsCompleted (nonCompletedHook .JobName , scan .Namespace )
266- if err != nil {
267- r .Log .Error (err , "Failed to check job status for ReadAndWrite Hook" )
268- return ctrl.Result {}, err
269- }
270- switch jobStatus {
271- case completed :
272- for i , hookStatus := range scan .Status .ReadAndWriteHookStatus {
273- if hookStatus .HookName == nonCompletedHook .HookName {
274- scan .Status .ReadAndWriteHookStatus [i ].State = executionv1 .Completed
275- }
276- }
277-
278- if err := r .Status ().Update (ctx , & scan ); err != nil {
279- r .Log .Error (err , "unable to update Scan status" )
280- return ctrl.Result {}, err
281- }
282- return ctrl.Result {}, err
283- case incomplete :
284- // Still waiting for job to finish
285- return ctrl.Result {}, err
286-
287- case failed :
288- for i , hookStatus := range scan .Status .ReadAndWriteHookStatus {
289- if hookStatus .HookName == nonCompletedHook .HookName {
290- scan .Status .ReadAndWriteHookStatus [i ].State = executionv1 .Failed
291- } else if hookStatus .State == executionv1 .Pending {
292- scan .Status .ReadAndWriteHookStatus [i ].State = executionv1 .Cancelled
293- }
294- }
295- scan .Status .State = "Errored"
296- scan .Status .ErrorDescription = fmt .Sprintf ("Failed to execute ReadAndWrite Hook '%s' in job '%s'. Check the logs of the hook for more information." , nonCompletedHook .HookName , nonCompletedHook .JobName )
297- if err := r .Status ().Update (ctx , & scan ); err != nil {
298- r .Log .Error (err , "unable to update Scan status" )
299- return ctrl.Result {}, err
300- }
301- }
302- }
303120 case "ReadAndWriteHookCompleted" :
304121 err = r .startReadOnlyHooks (& scan )
305122 case "ReadOnlyHookProcessing" :
@@ -1197,3 +1014,193 @@ func (r *ScanReconciler) setHookStatus(scan *executionv1.Scan) error {
11971014
11981015 return nil
11991016}
1017+
1018+ func (r * ScanReconciler ) executeReadAndWriteHooks (scan * executionv1.Scan ) error {
1019+ // First Array entry which is not Completed.
1020+ ctx := context .Background ()
1021+ var nonCompletedHook * executionv1.HookStatus
1022+
1023+ for _ , hook := range scan .Status .ReadAndWriteHookStatus {
1024+ if hook .State != executionv1 .Completed {
1025+ nonCompletedHook = & hook
1026+ break
1027+ }
1028+ }
1029+
1030+ if nonCompletedHook == nil {
1031+ scan .Status .State = "ReadAndWriteHookCompleted"
1032+ if err := r .Status ().Update (ctx , scan ); err != nil {
1033+ r .Log .Error (err , "unable to update Scan status" )
1034+ return err
1035+ }
1036+ return nil
1037+ }
1038+
1039+ if nonCompletedHook .State == executionv1 .Pending {
1040+ rules := []rbacv1.PolicyRule {
1041+ {
1042+ APIGroups : []string {"execution.experimental.securecodebox.io" },
1043+ Resources : []string {"scans" },
1044+ Verbs : []string {"get" },
1045+ },
1046+ }
1047+ serviceAccountName := "scan-completion-hook"
1048+ r .ensureServiceAccountExists (
1049+ scan .Namespace ,
1050+ serviceAccountName ,
1051+ "ScanCompletionHooks need to access the current scan to view where its results are stored" ,
1052+ rules ,
1053+ )
1054+
1055+ rawFileURL , err := r .PresignedGetURL (scan .UID , scan .Status .RawResultFile )
1056+ if err != nil {
1057+ return err
1058+ }
1059+ findingsFileURL , err := r .PresignedGetURL (scan .UID , "findings.json" )
1060+ if err != nil {
1061+ return err
1062+ }
1063+
1064+ bucketName := os .Getenv ("S3_BUCKET" )
1065+ rawFileUploadURL , err := r .MinioClient .PresignedPutObject (bucketName , fmt .Sprintf ("scan-%s/%s" , scan .UID , scan .Status .RawResultFile ), 12 * time .Hour )
1066+ if err != nil {
1067+ r .Log .Error (err , "Could not get presigned url from s3 or compatible storage provider" )
1068+ return err
1069+ }
1070+ findingsUploadURL , err := r .MinioClient .PresignedPutObject (bucketName , fmt .Sprintf ("scan-%s/findings.json" , scan .UID ), 12 * time .Hour )
1071+ if err != nil {
1072+ r .Log .Error (err , "Could not get presigned url from s3 or compatible storage provider" )
1073+ return err
1074+ }
1075+
1076+ var hook executionv1.ScanCompletionHook
1077+ err = r .Get (ctx , types.NamespacedName {Name : nonCompletedHook .HookName , Namespace : scan .Namespace }, & hook )
1078+ if err != nil {
1079+ r .Log .Error (err , "Failed to get ReadAndWrite Hook for HookStatus" )
1080+ return err
1081+ }
1082+
1083+ standardEnvVars := []corev1.EnvVar {
1084+ {
1085+ Name : "NAMESPACE" ,
1086+ ValueFrom : & corev1.EnvVarSource {
1087+ FieldRef : & corev1.ObjectFieldSelector {
1088+ FieldPath : "metadata.namespace" ,
1089+ },
1090+ },
1091+ },
1092+ {
1093+ Name : "SCAN_NAME" ,
1094+ Value : scan .Name ,
1095+ },
1096+ }
1097+
1098+ // Starting a new job based on the current ReadAndWrite Hook
1099+ labels := scan .ObjectMeta .DeepCopy ().Labels
1100+ if labels == nil {
1101+ labels = make (map [string ]string )
1102+ }
1103+ labels ["experimental.securecodebox.io/job-type" ] = "read-and-write-hook"
1104+ var backOffLimit int32 = 3
1105+ job := & batch.Job {
1106+ ObjectMeta : metav1.ObjectMeta {
1107+ Annotations : make (map [string ]string ),
1108+ Name : fmt .Sprintf ("%s-%s" , hook .Name , scan .Name ),
1109+ Namespace : scan .Namespace ,
1110+ Labels : labels ,
1111+ },
1112+ Spec : batch.JobSpec {
1113+ BackoffLimit : & backOffLimit ,
1114+ Template : corev1.PodTemplateSpec {
1115+ ObjectMeta : metav1.ObjectMeta {
1116+ Annotations : map [string ]string {
1117+ "auto-discovery.experimental.securecodebox.io/ignore" : "true" ,
1118+ },
1119+ },
1120+ Spec : corev1.PodSpec {
1121+ ServiceAccountName : serviceAccountName ,
1122+ RestartPolicy : corev1 .RestartPolicyNever ,
1123+ Containers : []corev1.Container {
1124+ {
1125+ Name : "hook" ,
1126+ Image : hook .Spec .Image ,
1127+ Args : []string {
1128+ rawFileURL ,
1129+ findingsFileURL ,
1130+ rawFileUploadURL .String (),
1131+ findingsUploadURL .String (),
1132+ },
1133+ Env : append (hook .Spec .Env , standardEnvVars ... ),
1134+ ImagePullPolicy : "IfNotPresent" ,
1135+ },
1136+ },
1137+ },
1138+ },
1139+ TTLSecondsAfterFinished : nil ,
1140+ },
1141+ }
1142+ if err := ctrl .SetControllerReference (scan , job , r .Scheme ); err != nil {
1143+ r .Log .Error (err , "Unable to set controllerReference on job" , "job" , job )
1144+ return err
1145+ }
1146+
1147+ if err := r .Create (ctx , job ); err != nil {
1148+ r .Log .Error (err , "Unable to create Job for ReadOnlyHook" , "job" , job )
1149+ return err
1150+ }
1151+
1152+ for i , hookStatus := range scan .Status .ReadAndWriteHookStatus {
1153+ if hookStatus .HookName == nonCompletedHook .HookName {
1154+ scan .Status .ReadAndWriteHookStatus [i ].JobName = job .Name
1155+ scan .Status .ReadAndWriteHookStatus [i ].State = executionv1 .InProgress
1156+ }
1157+ }
1158+
1159+ if err := r .Status ().Update (ctx , scan ); err != nil {
1160+ r .Log .Error (err , "unable to update Scan status" )
1161+ return err
1162+ }
1163+ return err
1164+ }
1165+
1166+ if nonCompletedHook .State == executionv1 .InProgress {
1167+ jobStatus , err := r .checkIfJobIsCompleted (nonCompletedHook .JobName , scan .Namespace )
1168+ if err != nil {
1169+ r .Log .Error (err , "Failed to check job status for ReadAndWrite Hook" )
1170+ return err
1171+ }
1172+ switch jobStatus {
1173+ case completed :
1174+ for i , hookStatus := range scan .Status .ReadAndWriteHookStatus {
1175+ if hookStatus .HookName == nonCompletedHook .HookName {
1176+ scan .Status .ReadAndWriteHookStatus [i ].State = executionv1 .Completed
1177+ }
1178+ }
1179+
1180+ if err := r .Status ().Update (ctx , scan ); err != nil {
1181+ r .Log .Error (err , "unable to update Scan status" )
1182+ return err
1183+ }
1184+ return err
1185+ case incomplete :
1186+ // Still waiting for job to finish
1187+ return err
1188+
1189+ case failed :
1190+ for i , hookStatus := range scan .Status .ReadAndWriteHookStatus {
1191+ if hookStatus .HookName == nonCompletedHook .HookName {
1192+ scan .Status .ReadAndWriteHookStatus [i ].State = executionv1 .Failed
1193+ } else if hookStatus .State == executionv1 .Pending {
1194+ scan .Status .ReadAndWriteHookStatus [i ].State = executionv1 .Cancelled
1195+ }
1196+ }
1197+ scan .Status .State = "Errored"
1198+ scan .Status .ErrorDescription = fmt .Sprintf ("Failed to execute ReadAndWrite Hook '%s' in job '%s'. Check the logs of the hook for more information." , nonCompletedHook .HookName , nonCompletedHook .JobName )
1199+ if err := r .Status ().Update (ctx , scan ); err != nil {
1200+ r .Log .Error (err , "unable to update Scan status" )
1201+ return err
1202+ }
1203+ }
1204+ }
1205+ return nil
1206+ }
0 commit comments