Skip to content

Commit e19dc5d

Browse files
committed
prow: sidecar: allow configuring ignore interrupts
Add a field `ignore_interrupts` to decoration config spec to configure ignore interrupts option for the sidecar. Signed-off-by: Vivek Singh <[email protected]>
1 parent 0f580cf commit e19dc5d

File tree

6 files changed

+203
-7
lines changed

6 files changed

+203
-7
lines changed

prow/apis/prowjobs/v1/types.go

+8
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,10 @@ type DecorationConfig struct {
371371
// CensorSecrets determines if the sidecar should be instructed to censor
372372
// secret data that is otherwise mounted to the ProwJob Pod
373373
CensorSecrets *bool `json:"censor_secrets,omitempty"`
374+
375+
// IgnoreInterrupts causes sidecar to ignore interrupts and
376+
// hope that the test process exits cleanly before starting an upload.
377+
IgnoreInterrupts *bool `json:"ignore_interrupts,omitempty"`
374378
}
375379

376380
// Resources holds resource requests and limits for
@@ -469,6 +473,10 @@ func (d *DecorationConfig) ApplyDefault(def *DecorationConfig) *DecorationConfig
469473
merged.CensorSecrets = def.CensorSecrets
470474
}
471475

476+
if merged.IgnoreInterrupts == nil {
477+
merged.IgnoreInterrupts = def.IgnoreInterrupts
478+
}
479+
472480
return &merged
473481
}
474482

prow/config/prow-config-documented.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,10 @@ plank:
515515
# secret data that is otherwise mounted to the ProwJob Pod
516516
censor_secrets: false
517517

518+
# IgnoreInterrupts causes sidecar to ignore interrupts and
519+
# hope that the test process exits cleanly before starting an upload.
520+
ignore_interrupts: false
521+
518522
# CookieFileSecret is the name of a kubernetes secret that contains
519523
# a git http.cookiefile, which should be used during the cloning process.
520524
cookiefile_secret: ' '
@@ -659,6 +663,15 @@ plank:
659663
# secret data that is otherwise mounted to the ProwJob Pod
660664
censor_secrets: false
661665

666+
# If `ignore_interrupts` is set, `sidecar` will do nothing upon receipt of
667+
# the interrupt signal; this implicitly means that upload of logs and artifacts
668+
# will begin when the test process exits, which may be as long as the grace
669+
# period if the test process does not gracefully handle interrupts. This will
670+
# require that the user configures the Pod's termination grace period to be
671+
# longer than the `entrypoint` grace period for the test process and the time
672+
# taken by `sidecar` to upload all relevant artifacts.
673+
ignore_interrupts: false
674+
662675
# CookieFileSecret is the name of a kubernetes secret that contains
663676
# a git http.cookiefile, which should be used during the cloning process.
664677
cookiefile_secret: ' '

prow/pod-utils/decorate/podspec.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,14 @@ func decorate(spec *coreapi.PodSpec, pj *prowapi.ProwJob, rawEnv map[string]stri
742742
wrappers = append(wrappers, *wrapperOptions)
743743
}
744744

745-
sidecar, err := Sidecar(pj.Spec.DecorationConfig, blobStorageOptions, blobStorageMounts, logMount, outputMount, encodedJobSpec, !RequirePassingEntries, !IgnoreInterrupts, secretVolumeMounts, wrappers...)
745+
var ignoreInterrupts bool
746+
if pj.Spec.DecorationConfig.IgnoreInterrupts == nil { // if nil, set it to false
747+
ignoreInterrupts = false
748+
} else {
749+
ignoreInterrupts = *pj.Spec.DecorationConfig.IgnoreInterrupts
750+
}
751+
752+
sidecar, err := Sidecar(pj.Spec.DecorationConfig, blobStorageOptions, blobStorageMounts, logMount, outputMount, encodedJobSpec, !RequirePassingEntries, ignoreInterrupts, secretVolumeMounts, wrappers...)
746753
if err != nil {
747754
return fmt.Errorf("create sidecar: %v", err)
748755
}
@@ -793,8 +800,6 @@ func DetermineWorkDir(baseDir string, refs []prowapi.Refs) string {
793800
const (
794801
// RequirePassingEntries causes sidecar to return an error if any entry fails. Otherwise it exits cleanly so long as it can complete.
795802
RequirePassingEntries = true
796-
// IgnoreInterrupts causes sidecar to ignore interrupts and hope that the test process exits cleanly before starting an upload.
797-
IgnoreInterrupts = true
798803
)
799804

800805
func Sidecar(config *prowapi.DecorationConfig, gcsOptions gcsupload.Options, blobStorageMounts []coreapi.VolumeMount, logMount coreapi.VolumeMount, outputMount *coreapi.VolumeMount, encodedJobSpec string, requirePassingEntries, ignoreInterrupts bool, secretVolumeMounts []coreapi.VolumeMount, wrappers ...wrapper.Options) (*coreapi.Container, error) {

prow/pod-utils/decorate/podspec_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,7 @@ func TestDecorate(t *testing.T) {
11301130
gCSCredentialsSecret := "gcs-secret"
11311131
defaultServiceAccountName := "default-sa"
11321132
censor := true
1133+
ignoreInterrupts := false
11331134
var testCases = []struct {
11341135
name string
11351136
spec *coreapi.PodSpec
@@ -1230,6 +1231,53 @@ func TestDecorate(t *testing.T) {
12301231
},
12311232
rawEnv: map[string]string{"custom": "env"},
12321233
},
1234+
{
1235+
name: "ignore interrupts in sidecar",
1236+
spec: &coreapi.PodSpec{
1237+
Volumes: []coreapi.Volume{
1238+
{Name: "secret", VolumeSource: coreapi.VolumeSource{Secret: &coreapi.SecretVolumeSource{SecretName: "secretname"}}},
1239+
},
1240+
Containers: []coreapi.Container{
1241+
{Name: "test", Command: []string{"/bin/ls"}, Args: []string{"-l", "-a"}, VolumeMounts: []coreapi.VolumeMount{{Name: "secret", MountPath: "/secret"}}},
1242+
},
1243+
ServiceAccountName: "tester",
1244+
},
1245+
pj: &prowapi.ProwJob{
1246+
Spec: prowapi.ProwJobSpec{
1247+
DecorationConfig: &prowapi.DecorationConfig{
1248+
Timeout: &prowapi.Duration{Duration: time.Minute},
1249+
GracePeriod: &prowapi.Duration{Duration: time.Hour},
1250+
UtilityImages: &prowapi.UtilityImages{
1251+
CloneRefs: "cloneimage",
1252+
InitUpload: "initimage",
1253+
Entrypoint: "entrypointimage",
1254+
Sidecar: "sidecarimage",
1255+
},
1256+
Resources: &prowapi.Resources{
1257+
CloneRefs: &coreapi.ResourceRequirements{Limits: coreapi.ResourceList{"cpu": resource.Quantity{}}, Requests: coreapi.ResourceList{"memory": resource.Quantity{}}},
1258+
InitUpload: &coreapi.ResourceRequirements{Limits: coreapi.ResourceList{"cpu": resource.Quantity{}}, Requests: coreapi.ResourceList{"memory": resource.Quantity{}}},
1259+
PlaceEntrypoint: &coreapi.ResourceRequirements{Limits: coreapi.ResourceList{"cpu": resource.Quantity{}}, Requests: coreapi.ResourceList{"memory": resource.Quantity{}}},
1260+
Sidecar: &coreapi.ResourceRequirements{Limits: coreapi.ResourceList{"cpu": resource.Quantity{}}, Requests: coreapi.ResourceList{"memory": resource.Quantity{}}},
1261+
},
1262+
GCSConfiguration: &prowapi.GCSConfiguration{
1263+
Bucket: "bucket",
1264+
PathStrategy: "single",
1265+
DefaultOrg: "org",
1266+
DefaultRepo: "repo",
1267+
},
1268+
GCSCredentialsSecret: &gCSCredentialsSecret,
1269+
DefaultServiceAccountName: &defaultServiceAccountName,
1270+
IgnoreInterrupts: &ignoreInterrupts,
1271+
},
1272+
Refs: &prowapi.Refs{
1273+
Org: "org", Repo: "repo", BaseRef: "main", BaseSHA: "abcd1234",
1274+
Pulls: []prowapi.Pull{{Number: 1, SHA: "aksdjhfkds"}},
1275+
},
1276+
ExtraRefs: []prowapi.Refs{{Org: "other", Repo: "something", BaseRef: "release", BaseSHA: "sldijfsd"}},
1277+
},
1278+
},
1279+
rawEnv: map[string]string{"custom": "env"},
1280+
},
12331281
}
12341282

12351283
for _, testCase := range testCases {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
containers:
2+
- command:
3+
- /tools/entrypoint
4+
env:
5+
- name: ARTIFACTS
6+
value: /logs/artifacts
7+
- name: GOPATH
8+
value: /home/prow/go
9+
- name: custom
10+
value: env
11+
- name: ENTRYPOINT_OPTIONS
12+
value: '{"timeout":60000000000,"grace_period":3600000000000,"artifact_dir":"/logs/artifacts","args":["/bin/ls","-l","-a"],"container_name":"test","process_log":"/logs/process-log.txt","marker_file":"/logs/marker-file.txt","metadata_file":"/logs/artifacts/metadata.json"}'
13+
name: test
14+
resources: {}
15+
volumeMounts:
16+
- mountPath: /secret
17+
name: secret
18+
- mountPath: /logs
19+
name: logs
20+
- mountPath: /tools
21+
name: tools
22+
- mountPath: /home/prow/go
23+
name: code
24+
workingDir: /home/prow/go/src/github.com/org/repo
25+
- command:
26+
- /sidecar
27+
env:
28+
- name: JOB_SPEC
29+
- name: SIDECAR_OPTIONS
30+
value: '{"gcs_options":{"items":["/logs/artifacts"],"bucket":"bucket","path_strategy":"single","default_org":"org","default_repo":"repo","gcs_credentials_file":"/secrets/gcs/service-account.json","dry_run":false},"entries":[{"args":["/bin/ls","-l","-a"],"container_name":"test","process_log":"/logs/process-log.txt","marker_file":"/logs/marker-file.txt","metadata_file":"/logs/artifacts/metadata.json"}],"censoring_options":{}, "ignore_interrupts":true}'
31+
image: sidecarimage
32+
name: sidecar
33+
resources:
34+
limits:
35+
cpu: "0"
36+
requests:
37+
memory: "0"
38+
volumeMounts:
39+
- mountPath: /logs
40+
name: logs
41+
- mountPath: /secrets/gcs
42+
name: gcs-credentials
43+
- mountPath: /secret
44+
name: secret
45+
initContainers:
46+
- command:
47+
- /clonerefs
48+
env:
49+
- name: CLONEREFS_OPTIONS
50+
value: '{"src_root":"/home/prow/go","log":"/logs/clone.json","git_user_name":"ci-robot","git_user_email":"[email protected]","refs":[{"org":"org","repo":"repo","base_ref":"main","base_sha":"abcd1234","pulls":[{"number":1,"author":"","sha":"aksdjhfkds"}]},{"org":"other","repo":"something","base_ref":"release","base_sha":"sldijfsd"}]}'
51+
image: cloneimage
52+
name: clonerefs
53+
resources:
54+
limits:
55+
cpu: "0"
56+
requests:
57+
memory: "0"
58+
volumeMounts:
59+
- mountPath: /logs
60+
name: logs
61+
- mountPath: /home/prow/go
62+
name: code
63+
- mountPath: /tmp
64+
name: clonerefs-tmp
65+
- command:
66+
- /initupload
67+
env:
68+
- name: INITUPLOAD_OPTIONS
69+
value: '{"bucket":"bucket","path_strategy":"single","default_org":"org","default_repo":"repo","gcs_credentials_file":"/secrets/gcs/service-account.json","dry_run":false,"log":"/logs/clone.json"}'
70+
- name: JOB_SPEC
71+
image: initimage
72+
name: initupload
73+
resources:
74+
limits:
75+
cpu: "0"
76+
requests:
77+
memory: "0"
78+
volumeMounts:
79+
- mountPath: /logs
80+
name: logs
81+
- mountPath: /secrets/gcs
82+
name: gcs-credentials
83+
- args:
84+
- /entrypoint
85+
- /tools/entrypoint
86+
command:
87+
- /bin/cp
88+
image: entrypointimage
89+
name: place-entrypoint
90+
resources:
91+
limits:
92+
cpu: "0"
93+
requests:
94+
memory: "0"
95+
volumeMounts:
96+
- mountPath: /tools
97+
name: tools
98+
serviceAccountName: tester
99+
terminationGracePeriodSeconds: 4500
100+
volumes:
101+
- name: secret
102+
secret:
103+
secretName: secretname
104+
- emptyDir: {}
105+
name: logs
106+
- emptyDir: {}
107+
name: tools
108+
- name: gcs-credentials
109+
secret:
110+
secretName: gcs-secret
111+
- emptyDir: {}
112+
name: clonerefs-tmp
113+
- emptyDir: {}
114+
name: code

prow/sidecar/run.go

+12-4
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ func (o Options) Run(ctx context.Context) (int, error) {
108108
return 0, fmt.Errorf("could not resolve job spec: %v", err)
109109
}
110110

111+
entries := o.entries()
112+
buildLogs := logReaders(entries)
113+
metadata := combineMetadata(entries)
114+
111115
ctx, cancel := context.WithCancel(ctx)
112116

113117
interrupt := make(chan os.Signal)
@@ -125,7 +129,12 @@ func (o Options) Run(ctx context.Context) (int, error) {
125129
// second upload but we can tolerate this as we'd rather get SOME
126130
// data into GCS than attempt to cancel these uploads and get none.
127131
logrus.Errorf("Received an interrupt: %s, cancelling...", s)
128-
cancel()
132+
133+
err = o.doUpload(spec, false, true, metadata, buildLogs)
134+
if err != nil {
135+
logrus.Errorf("Failed to perform best-effort upload: %v", err)
136+
}
137+
129138
}
130139
case <-ctx.Done():
131140
}
@@ -135,7 +144,7 @@ func (o Options) Run(ctx context.Context) (int, error) {
135144
// This only fires if the prowjob controller and sidecar are at different commits
136145
logrus.Warnf("Using deprecated wrapper_options instead of entries. Please update prow/pod-utils/decorate before June 2019")
137146
}
138-
entries := o.entries()
147+
139148
passed, aborted, failures := wait(ctx, entries)
140149

141150
cancel()
@@ -151,8 +160,7 @@ func (o Options) Run(ctx context.Context) (int, error) {
151160
logrus.Warnf("Failed to censor data: %v", err)
152161
}
153162
}
154-
buildLogs := logReaders(entries)
155-
metadata := combineMetadata(entries)
163+
156164
return failures, o.doUpload(spec, passed, aborted, metadata, buildLogs)
157165
}
158166

0 commit comments

Comments
 (0)