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

Commit ac9af9a

Browse files
authored
Merge pull request #26 from secureCodeBox/bug/object-name-truncation
Fix Crashes of Scans with long Names
2 parents 847dd90 + b494d58 commit ac9af9a

File tree

5 files changed

+141
-105
lines changed

5 files changed

+141
-105
lines changed

operator/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ RUN go mod download
1313
COPY main.go main.go
1414
COPY apis/ apis/
1515
COPY controllers/ controllers/
16+
COPY utils/ utils/
1617

1718
# Build
1819
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go

operator/controllers/execution/scan_controller.go

Lines changed: 68 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939

4040
"github.com/minio/minio-go/v6"
4141
executionv1 "github.com/secureCodeBox/secureCodeBox-v2-alpha/operator/apis/execution/v1"
42+
util "github.com/secureCodeBox/secureCodeBox-v2-alpha/operator/utils"
4243
)
4344

4445
// ScanReconciler reconciles a Scan object
@@ -116,7 +117,6 @@ func (r *ScanReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
116117
err = r.setHookStatus(&scan)
117118
case "ReadAndWriteHookProcessing":
118119
err = r.executeReadAndWriteHooks(&scan)
119-
120120
case "ReadAndWriteHookCompleted":
121121
err = r.startReadOnlyHooks(&scan)
122122
case "ReadOnlyHookProcessing":
@@ -129,21 +129,6 @@ func (r *ScanReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
129129
return ctrl.Result{}, nil
130130
}
131131

132-
func (r *ScanReconciler) getJob(name, namespace string) (*batch.Job, error) {
133-
ctx := context.Background()
134-
135-
var job batch.Job
136-
err := r.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, &job)
137-
if apierrors.IsNotFound(err) {
138-
return nil, nil
139-
} else if err != nil {
140-
r.Log.Error(err, "unable to get job")
141-
return nil, err
142-
}
143-
144-
return &job, nil
145-
}
146-
147132
type jobCompletionType string
148133

149134
const (
@@ -153,22 +138,51 @@ const (
153138
unknown jobCompletionType = "Unknown"
154139
)
155140

156-
func (r *ScanReconciler) checkIfJobIsCompleted(name, namespace string) (jobCompletionType, error) {
157-
job, err := r.getJob(name, namespace)
158-
if err != nil {
159-
return unknown, err
141+
func allJobsCompleted(jobs *batch.JobList) jobCompletionType {
142+
hasCompleted := true
143+
144+
for _, job := range jobs.Items {
145+
if job.Status.Failed > 0 {
146+
return failed
147+
} else if job.Status.Succeeded == 0 {
148+
hasCompleted = false
149+
}
160150
}
161-
if job == nil {
162-
return unknown, errors.New("Both Job and error were nil. This isn't really expected")
151+
152+
if hasCompleted {
153+
return completed
163154
}
155+
return incomplete
156+
}
157+
158+
func (r *ScanReconciler) getJobsForScan(scan *executionv1.Scan, labels client.MatchingLabels) (*batch.JobList, error) {
159+
ctx := context.Background()
164160

165-
if job.Status.Succeeded != 0 {
166-
return completed, nil
161+
// check if k8s job for scan was already created
162+
var jobs batch.JobList
163+
if err := r.List(
164+
ctx,
165+
&jobs,
166+
client.InNamespace(scan.Namespace),
167+
client.MatchingField(ownerKey, scan.Name),
168+
labels,
169+
); err != nil {
170+
r.Log.Error(err, "Unable to list child jobs")
171+
return nil, err
167172
}
168-
if job.Status.Failed != 0 {
169-
return failed, nil
173+
174+
return &jobs, nil
175+
}
176+
177+
func (r *ScanReconciler) checkIfJobIsCompleted(scan *executionv1.Scan, labels client.MatchingLabels) (jobCompletionType, error) {
178+
jobs, err := r.getJobsForScan(scan, labels)
179+
if err != nil {
180+
return unknown, err
170181
}
171-
return unknown, nil
182+
183+
r.Log.V(9).Info("Got related jobs", "count", len(jobs.Items))
184+
185+
return allJobsCompleted(jobs), nil
172186
}
173187

174188
// Helper functions to check and remove string from a slice of strings.
@@ -220,11 +234,11 @@ func (r *ScanReconciler) startScan(scan *executionv1.Scan) error {
220234
namespacedName := fmt.Sprintf("%s/%s", scan.Namespace, scan.Name)
221235
log := r.Log.WithValues("scan_init", namespacedName)
222236

223-
job, err := r.getJob(fmt.Sprintf("scan-%s", scan.Name), scan.Namespace)
237+
jobs, err := r.getJobsForScan(scan, client.MatchingLabels{"experimental.securecodebox.io/job-type": "scanner"})
224238
if err != nil {
225239
return err
226240
}
227-
if job != nil {
241+
if len(jobs.Items) > 0 {
228242
log.V(8).Info("Job already exists. Doesn't need to be created.")
229243
return nil
230244
}
@@ -267,7 +281,7 @@ func (r *ScanReconciler) startScan(scan *executionv1.Scan) error {
267281
rules,
268282
)
269283

270-
job, err = r.constructJobForScan(scan, &scanType)
284+
job, err := r.constructJobForScan(scan, &scanType)
271285
if err != nil {
272286
log.Error(err, "unable to create job object ScanType")
273287
return err
@@ -296,7 +310,7 @@ func (r *ScanReconciler) startScan(scan *executionv1.Scan) error {
296310
func (r *ScanReconciler) checkIfScanIsCompleted(scan *executionv1.Scan) error {
297311
ctx := context.Background()
298312

299-
status, err := r.checkIfJobIsCompleted(fmt.Sprintf("scan-%s", scan.Name), scan.Namespace)
313+
status, err := r.checkIfJobIsCompleted(scan, client.MatchingLabels{"experimental.securecodebox.io/job-type": "scanner"})
300314
if err != nil {
301315
return err
302316
}
@@ -326,11 +340,11 @@ func (r *ScanReconciler) startParser(scan *executionv1.Scan) error {
326340
namespacedName := fmt.Sprintf("%s/%s", scan.Namespace, scan.Name)
327341
log := r.Log.WithValues("scan_parse", namespacedName)
328342

329-
job, err := r.getJob(fmt.Sprintf("parse-%s", scan.Name), scan.Namespace)
343+
jobs, err := r.getJobsForScan(scan, client.MatchingLabels{"experimental.securecodebox.io/job-type": "parser"})
330344
if err != nil {
331345
return err
332346
}
333-
if job != nil {
347+
if len(jobs.Items) > 0 {
334348
log.V(8).Info("Job already exists. Doesn't need to be created.")
335349
return nil
336350
}
@@ -384,12 +398,12 @@ func (r *ScanReconciler) startParser(scan *executionv1.Scan) error {
384398
labels["experimental.securecodebox.io/job-type"] = "parser"
385399
automountServiceAccountToken := true
386400
var backOffLimit int32 = 3
387-
job = &batch.Job{
401+
job := &batch.Job{
388402
ObjectMeta: metav1.ObjectMeta{
389-
Annotations: make(map[string]string),
390-
Name: fmt.Sprintf("parse-%s", scan.Name),
391-
Namespace: scan.Namespace,
392-
Labels: labels,
403+
Annotations: make(map[string]string),
404+
GenerateName: util.TruncateName(fmt.Sprintf("parse-%s", scan.Name)),
405+
Namespace: scan.Namespace,
406+
Labels: labels,
393407
},
394408
Spec: batch.JobSpec{
395409
BackoffLimit: &backOffLimit,
@@ -459,7 +473,7 @@ func (r *ScanReconciler) startParser(scan *executionv1.Scan) error {
459473
func (r *ScanReconciler) checkIfParsingIsCompleted(scan *executionv1.Scan) error {
460474
ctx := context.Background()
461475

462-
status, err := r.checkIfJobIsCompleted(fmt.Sprintf("parse-%s", scan.Name), scan.Namespace)
476+
status, err := r.checkIfJobIsCompleted(scan, client.MatchingLabels{"experimental.securecodebox.io/job-type": "parser"})
463477
if err != nil {
464478
return err
465479
}
@@ -503,9 +517,9 @@ func (r *ScanReconciler) constructJobForScan(scan *executionv1.Scan, scanType *e
503517
labels["experimental.securecodebox.io/job-type"] = "scanner"
504518
job := &batch.Job{
505519
ObjectMeta: metav1.ObjectMeta{
506-
Labels: labels,
507-
Name: fmt.Sprintf("scan-%s", scan.Name),
508-
Namespace: scan.Namespace,
520+
Labels: labels,
521+
GenerateName: util.TruncateName(fmt.Sprintf("scan-%s", scan.Name)),
522+
Namespace: scan.Namespace,
509523
},
510524
Spec: *scanType.Spec.JobTemplate.Spec.DeepCopy(),
511525
}
@@ -729,44 +743,13 @@ func (r *ScanReconciler) startReadOnlyHooks(scan *executionv1.Scan) error {
729743
return nil
730744
}
731745

732-
func allJobsCompleted(jobs *batch.JobList) jobCompletionType {
733-
hasCompleted := true
734-
735-
for _, job := range jobs.Items {
736-
if job.Status.Failed > 0 {
737-
return failed
738-
} else if job.Status.Succeeded == 0 {
739-
hasCompleted = false
740-
}
741-
}
742-
743-
if hasCompleted {
744-
return completed
745-
}
746-
return incomplete
747-
}
748-
749746
func (r *ScanReconciler) checkIfReadOnlyHookIsCompleted(scan *executionv1.Scan) error {
750747
ctx := context.Background()
751-
752-
// check if k8s job for scan was already created
753-
var readOnlyHookJobs batch.JobList
754-
if err := r.List(
755-
ctx,
756-
&readOnlyHookJobs,
757-
client.InNamespace(scan.Namespace),
758-
client.MatchingField(ownerKey, scan.Name),
759-
client.MatchingLabels{
760-
"experimental.securecodebox.io/job-type": "read-only-hook",
761-
},
762-
); err != nil {
763-
r.Log.Error(err, "Unable to list child jobs")
748+
readOnlyHookCompletion, err := r.checkIfJobIsCompleted(scan, client.MatchingLabels{"experimental.securecodebox.io/job-type": "read-only-hook"})
749+
if err != nil {
764750
return err
765751
}
766752

767-
r.Log.V(9).Info("Got related jobs", "count", len(readOnlyHookJobs.Items))
768-
769-
readOnlyHookCompletion := allJobsCompleted(&readOnlyHookJobs)
770753
if readOnlyHookCompletion == completed {
771754
r.Log.V(7).Info("All ReadOnlyHooks have completed")
772755
scan.Status.State = "Done"
@@ -1018,13 +1001,15 @@ func (r *ScanReconciler) createJobForHook(hook *executionv1.ScanCompletionHook,
10181001
} else if hook.Spec.Type == executionv1.ReadOnly {
10191002
labels["experimental.securecodebox.io/job-type"] = "read-only-hook"
10201003
}
1004+
labels["experimental.securecodebox.io/hook-name"] = hook.Name
1005+
10211006
var backOffLimit int32 = 3
10221007
job := &batch.Job{
10231008
ObjectMeta: metav1.ObjectMeta{
1024-
Annotations: make(map[string]string),
1025-
Name: fmt.Sprintf("%s-%s", hook.Name, scan.Name),
1026-
Namespace: scan.Namespace,
1027-
Labels: labels,
1009+
Annotations: make(map[string]string),
1010+
GenerateName: util.TruncateName(fmt.Sprintf("%s-%s", hook.Name, scan.Name)),
1011+
Namespace: scan.Namespace,
1012+
Labels: labels,
10281013
},
10291014
Spec: batch.JobSpec{
10301015
BackoffLimit: &backOffLimit,
@@ -1143,7 +1128,10 @@ func (r *ScanReconciler) executeReadAndWriteHooks(scan *executionv1.Scan) error
11431128
})
11441129
return err
11451130
case executionv1.InProgress:
1146-
jobStatus, err := r.checkIfJobIsCompleted(nonCompletedHook.JobName, scan.Namespace)
1131+
jobStatus, err := r.checkIfJobIsCompleted(scan, client.MatchingLabels{
1132+
"experimental.securecodebox.io/job-type": "read-and-write-hook",
1133+
"experimental.securecodebox.io/hook-name": nonCompletedHook.HookName,
1134+
})
11471135
if err != nil {
11481136
r.Log.Error(err, "Failed to check job status for ReadAndWrite Hook")
11491137
return err

operator/utils/truncatedname.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package utils
2+
3+
import "fmt"
4+
5+
// TruncateName Ensures that the name used for a kubernetes object doesn't exceed the 63 char length limit. This actually cuts of anything after char 57, so that we can use the randomly generated suffix from k8s `generateName`.
6+
func TruncateName(name string) string {
7+
if len(name) >= 57 {
8+
return fmt.Sprintf("%s-", name[0:57])
9+
}
10+
return fmt.Sprintf("%s-", name)
11+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package utils
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
type testData struct {
9+
in string
10+
out string
11+
}
12+
13+
func TestAbc(t *testing.T) {
14+
var tests = []testData{
15+
{
16+
in: "abc",
17+
out: "abc-",
18+
},
19+
{
20+
in: "scan-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
21+
out: "scan-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-",
22+
},
23+
{
24+
in: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
25+
out: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-",
26+
},
27+
}
28+
29+
for _, test := range tests {
30+
actual := TruncateName(test.in)
31+
if actual != test.out {
32+
t.Error(fmt.Errorf("TruncateName(\"%s\") returned \"%s\", expected \"%s\"", test.in, actual, test.out))
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)