Skip to content
This repository was archived by the owner on Oct 14, 2020. It is now read-only.

Commit e832d0e

Browse files
authored
Merge pull request #68 from secureCodeBox/refactoring-scan-controller
Refactoring scan controller
2 parents 54fcff1 + 2666a02 commit e832d0e

File tree

9 files changed

+1304
-1230
lines changed

9 files changed

+1304
-1230
lines changed

operator/controllers/execution/scan_controller.go

Lines changed: 0 additions & 1229 deletions
This file was deleted.

operator/controllers/execution/scans/hook_reconciler.go

Lines changed: 427 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package scancontrollers
2+
3+
import (
4+
"context"
5+
6+
executionv1 "github.com/secureCodeBox/secureCodeBox-v2-alpha/operator/apis/execution/v1"
7+
batch "k8s.io/api/batch/v1"
8+
"sigs.k8s.io/controller-runtime/pkg/client"
9+
)
10+
11+
type jobCompletionType string
12+
13+
const (
14+
completed jobCompletionType = "Completed"
15+
failed jobCompletionType = "Failed"
16+
incomplete jobCompletionType = "Incomplete"
17+
unknown jobCompletionType = "Unknown"
18+
)
19+
20+
func allJobsCompleted(jobs *batch.JobList) jobCompletionType {
21+
hasCompleted := true
22+
23+
for _, job := range jobs.Items {
24+
if job.Status.Failed > 0 {
25+
return failed
26+
} else if job.Status.Succeeded == 0 {
27+
hasCompleted = false
28+
}
29+
}
30+
31+
if hasCompleted {
32+
return completed
33+
}
34+
return incomplete
35+
}
36+
37+
func (r *ScanReconciler) getJobsForScan(scan *executionv1.Scan, labels client.MatchingLabels) (*batch.JobList, error) {
38+
ctx := context.Background()
39+
40+
// check if k8s job for scan was already created
41+
var jobs batch.JobList
42+
if err := r.List(
43+
ctx,
44+
&jobs,
45+
client.InNamespace(scan.Namespace),
46+
client.MatchingField(ownerKey, scan.Name),
47+
labels,
48+
); err != nil {
49+
r.Log.Error(err, "Unable to list child jobs")
50+
return nil, err
51+
}
52+
53+
return &jobs, nil
54+
}
55+
56+
func (r *ScanReconciler) checkIfJobIsCompleted(scan *executionv1.Scan, labels client.MatchingLabels) (jobCompletionType, error) {
57+
jobs, err := r.getJobsForScan(scan, labels)
58+
if err != nil {
59+
return unknown, err
60+
}
61+
62+
r.Log.V(9).Info("Got related jobs", "count", len(jobs.Items))
63+
64+
return allJobsCompleted(jobs), nil
65+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package scancontrollers
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
executionv1 "github.com/secureCodeBox/secureCodeBox-v2-alpha/operator/apis/execution/v1"
9+
util "github.com/secureCodeBox/secureCodeBox-v2-alpha/operator/utils"
10+
batch "k8s.io/api/batch/v1"
11+
corev1 "k8s.io/api/core/v1"
12+
rbacv1 "k8s.io/api/rbac/v1"
13+
resource "k8s.io/apimachinery/pkg/api/resource"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/apimachinery/pkg/types"
16+
ctrl "sigs.k8s.io/controller-runtime"
17+
"sigs.k8s.io/controller-runtime/pkg/client"
18+
)
19+
20+
func (r *ScanReconciler) startParser(scan *executionv1.Scan) error {
21+
ctx := context.Background()
22+
namespacedName := fmt.Sprintf("%s/%s", scan.Namespace, scan.Name)
23+
log := r.Log.WithValues("scan_parse", namespacedName)
24+
25+
jobs, err := r.getJobsForScan(scan, client.MatchingLabels{"experimental.securecodebox.io/job-type": "parser"})
26+
if err != nil {
27+
return err
28+
}
29+
if len(jobs.Items) > 0 {
30+
log.V(8).Info("Job already exists. Doesn't need to be created.")
31+
return nil
32+
}
33+
34+
parseType := scan.Status.RawResultType
35+
36+
// get the scan template for the scan
37+
var parseDefinition executionv1.ParseDefinition
38+
if err := r.Get(ctx, types.NamespacedName{Name: parseType, Namespace: scan.Namespace}, &parseDefinition); err != nil {
39+
log.V(7).Info("Unable to fetch ParseDefinition")
40+
41+
scan.Status.State = "Errored"
42+
scan.Status.ErrorDescription = fmt.Sprintf("No ParseDefinition for ResultType '%s' found in Scans Namespace.", parseType)
43+
if err := r.Status().Update(ctx, scan); err != nil {
44+
r.Log.Error(err, "unable to update Scan status")
45+
return err
46+
}
47+
48+
return fmt.Errorf("No ParseDefinition of type '%s' found", parseType)
49+
}
50+
log.Info("Matching ParseDefinition Found", "ParseDefinition", parseType)
51+
52+
findingsUploadURL, err := r.PresignedPutURL(scan.UID, "findings.json")
53+
if err != nil {
54+
r.Log.Error(err, "Could not get presigned url from s3 or compatible storage provider")
55+
return err
56+
}
57+
rawResultDownloadURL, err := r.PresignedGetURL(scan.UID, scan.Status.RawResultFile)
58+
if err != nil {
59+
return err
60+
}
61+
62+
rules := []rbacv1.PolicyRule{
63+
{
64+
APIGroups: []string{"execution.experimental.securecodebox.io"},
65+
Resources: []string{"scans/status"},
66+
Verbs: []string{"get", "patch"},
67+
},
68+
}
69+
r.ensureServiceAccountExists(
70+
scan.Namespace,
71+
"parser",
72+
"Parser need to access the status of Scans to update how many findings have been identified",
73+
rules,
74+
)
75+
76+
labels := scan.ObjectMeta.DeepCopy().Labels
77+
if labels == nil {
78+
labels = make(map[string]string)
79+
}
80+
labels["experimental.securecodebox.io/job-type"] = "parser"
81+
automountServiceAccountToken := true
82+
var backOffLimit int32 = 3
83+
job := &batch.Job{
84+
ObjectMeta: metav1.ObjectMeta{
85+
Annotations: make(map[string]string),
86+
GenerateName: util.TruncateName(fmt.Sprintf("parse-%s", scan.Name)),
87+
Namespace: scan.Namespace,
88+
Labels: labels,
89+
},
90+
Spec: batch.JobSpec{
91+
BackoffLimit: &backOffLimit,
92+
Template: corev1.PodTemplateSpec{
93+
ObjectMeta: metav1.ObjectMeta{
94+
Annotations: map[string]string{
95+
"auto-discovery.experimental.securecodebox.io/ignore": "true",
96+
},
97+
},
98+
Spec: corev1.PodSpec{
99+
RestartPolicy: corev1.RestartPolicyNever,
100+
ServiceAccountName: "parser",
101+
ImagePullSecrets: parseDefinition.Spec.ImagePullSecrets,
102+
Containers: []corev1.Container{
103+
{
104+
Name: "parser",
105+
Image: parseDefinition.Spec.Image,
106+
Env: []corev1.EnvVar{
107+
{
108+
Name: "NAMESPACE",
109+
ValueFrom: &corev1.EnvVarSource{
110+
FieldRef: &corev1.ObjectFieldSelector{
111+
FieldPath: "metadata.namespace",
112+
},
113+
},
114+
},
115+
{
116+
Name: "SCAN_NAME",
117+
Value: scan.Name,
118+
},
119+
},
120+
Args: []string{
121+
rawResultDownloadURL,
122+
findingsUploadURL,
123+
},
124+
ImagePullPolicy: "Always",
125+
Resources: corev1.ResourceRequirements{
126+
Requests: corev1.ResourceList{
127+
corev1.ResourceCPU: resource.MustParse("200m"),
128+
corev1.ResourceMemory: resource.MustParse("100Mi"),
129+
},
130+
Limits: corev1.ResourceList{
131+
corev1.ResourceCPU: resource.MustParse("400m"),
132+
corev1.ResourceMemory: resource.MustParse("200Mi"),
133+
},
134+
},
135+
},
136+
},
137+
AutomountServiceAccountToken: &automountServiceAccountToken,
138+
},
139+
},
140+
TTLSecondsAfterFinished: nil,
141+
},
142+
}
143+
144+
if err := ctrl.SetControllerReference(scan, job, r.Scheme); err != nil {
145+
return err
146+
}
147+
148+
log.V(7).Info("Constructed Job object", "job args", strings.Join(job.Spec.Template.Spec.Containers[0].Args, ", "))
149+
150+
if err := r.Create(ctx, job); err != nil {
151+
log.Error(err, "unable to create Job for Parser", "job", job)
152+
return err
153+
}
154+
155+
scan.Status.State = "Parsing"
156+
if err := r.Status().Update(ctx, scan); err != nil {
157+
log.Error(err, "unable to update Scan status")
158+
return err
159+
}
160+
161+
log.V(1).Info("created Parse Job for Scan", "job", job)
162+
return nil
163+
}
164+
165+
func (r *ScanReconciler) checkIfParsingIsCompleted(scan *executionv1.Scan) error {
166+
ctx := context.Background()
167+
168+
status, err := r.checkIfJobIsCompleted(scan, client.MatchingLabels{"experimental.securecodebox.io/job-type": "parser"})
169+
if err != nil {
170+
return err
171+
}
172+
173+
switch status {
174+
case completed:
175+
r.Log.V(7).Info("Parsing is completed")
176+
scan.Status.State = "ParseCompleted"
177+
if err := r.Status().Update(ctx, scan); err != nil {
178+
r.Log.Error(err, "unable to update Scan status")
179+
return err
180+
}
181+
case failed:
182+
scan.Status.State = "Errored"
183+
scan.Status.ErrorDescription = "Failed to run the Parser. This is likely a Bug, we would like to know about. Please open up a Issue on GitHub."
184+
if err := r.Status().Update(ctx, scan); err != nil {
185+
r.Log.Error(err, "unable to update Scan status")
186+
return err
187+
}
188+
}
189+
190+
return nil
191+
}

0 commit comments

Comments
 (0)