Skip to content

Commit 4a6e458

Browse files
authored
feat(runner): allow for optional environment variables (#443)
Signed-off-by: Mattia Buccarella <[email protected]>
1 parent 04c1ff4 commit 4a6e458

19 files changed

+324
-285
lines changed

app/cli/internal/action/attestation_init.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,11 @@ func (action *AttestationInit) Run(contractRevision int) error {
123123
}
124124

125125
// Load the env variables both the system populated and the user predefined ones
126-
if err := action.c.ResolveEnvVars(!action.dryRun); err != nil {
126+
if err := action.c.ResolveEnvVars(); err != nil {
127+
if action.dryRun {
128+
return nil
129+
}
130+
127131
_ = action.c.Reset()
128132
return err
129133
}

app/cli/internal/action/attestation_status.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,18 @@ func (action *AttestationStatus) Run() (*AttestationStatusResult, error) {
124124
}
125125

126126
res.EnvVars = envVars
127+
128+
runnerEnvVars, errors := c.Runner.ResolveEnvVars()
129+
var combinedErrs string
130+
for _, err := range errors {
131+
combinedErrs += (*err).Error() + "\n"
132+
}
133+
if len(errors) > 0 && !c.CraftingState.DryRun {
134+
return nil, fmt.Errorf("error resolving env vars: %s", combinedErrs)
135+
}
136+
127137
res.RunnerContext = &AttestationResultRunnerContext{
128-
EnvVars: c.Runner.ResolveEnvVars(),
138+
EnvVars: runnerEnvVars,
129139
RunnerType: att.RunnerType.String(),
130140
JobURL: att.RunnerUrl,
131141
}

internal/attestation/crafter/crafter.go

Lines changed: 26 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -372,49 +372,44 @@ func persistCraftingState(craftState *api.CraftingState, stateFilePath string) e
372372

373373
// ResolveEnvVars will iterate on the env vars in the allow list and resolve them from the system context
374374
// strict indicates if it should fail if any env variable can not be found
375-
func (c *Crafter) ResolveEnvVars(strict bool) error {
375+
func (c *Crafter) ResolveEnvVars() error {
376376
if err := c.requireStateLoaded(); err != nil {
377377
return err
378378
}
379379

380-
// Runner specific env variables
380+
// Runner specific environment variables
381381
c.logger.Debug().Str("runnerType", c.Runner.String()).Msg("loading runner specific env variables")
382-
var outputEnvVars = make(map[string]string)
383382
if !c.Runner.CheckEnv() {
384383
errorStr := fmt.Sprintf("couldn't detect the environment %q. Is the crafting process happening in the target env?", c.Runner.String())
385-
if strict {
386-
return fmt.Errorf("%s - %w", errorStr, ErrRunnerContextNotFound)
387-
}
388-
c.logger.Warn().Msg(errorStr)
389-
} else {
390-
c.logger.Debug().Str("runnerType", c.Runner.String()).Strs("variables", c.Runner.ListEnvVars()).Msg("list of env variables to automatically extract")
391-
outputEnvVars = c.Runner.ResolveEnvVars()
392-
if notFound := notResolvedVars(outputEnvVars, c.Runner.ListEnvVars()); len(notFound) > 0 {
393-
if strict {
394-
return fmt.Errorf("required env variables not present %q", notFound)
395-
}
396-
c.logger.Warn().Strs("key", notFound).Msg("required env variables not present")
397-
}
384+
return fmt.Errorf("%s - %w", errorStr, ErrRunnerContextNotFound)
398385
}
399386

400-
// User-defined env vars
401-
varsAllowList := c.CraftingState.InputSchema.EnvAllowList
402-
if len(varsAllowList) > 0 {
403-
c.logger.Debug().Strs("allowList", varsAllowList).Msg("loading env variables")
404-
for _, want := range varsAllowList {
405-
val := os.Getenv(want)
406-
if val == "" {
407-
continue
408-
}
387+
// Workflow run environment variables
388+
varNames := make([]string, len(c.Runner.ListEnvVars()))
389+
for index, envVarDef := range c.Runner.ListEnvVars() {
390+
varNames[index] = envVarDef.Name
391+
}
392+
c.logger.Debug().Str("runnerType", c.Runner.String()).Strs("variables", varNames).Msg("list of env variables to automatically extract")
409393

410-
outputEnvVars[want] = val
394+
outputEnvVars, errors := c.Runner.ResolveEnvVars()
395+
if len(errors) > 0 {
396+
var combinedErrs string
397+
for _, err := range errors {
398+
combinedErrs += (*err).Error() + "\n"
411399
}
400+
return fmt.Errorf("error while resolving runner environment variables: %s", combinedErrs)
401+
}
412402

413-
if notFound := notResolvedVars(outputEnvVars, varsAllowList); len(notFound) > 0 {
414-
if strict {
415-
return fmt.Errorf("required env variables not present %q", notFound)
416-
}
417-
c.logger.Warn().Strs("key", notFound).Msg("required env variables not present")
403+
// User-defined environment vars
404+
if len(c.CraftingState.InputSchema.EnvAllowList) > 0 {
405+
c.logger.Debug().Strs("allowList", c.CraftingState.InputSchema.EnvAllowList).Msg("loading env variables")
406+
}
407+
for _, want := range c.CraftingState.InputSchema.EnvAllowList {
408+
val := os.Getenv(want)
409+
if val != "" {
410+
outputEnvVars[want] = val
411+
} else {
412+
return fmt.Errorf("required env variables not present %q", want)
418413
}
419414
}
420415

@@ -427,17 +422,6 @@ func (c *Crafter) ResolveEnvVars(strict bool) error {
427422
return nil
428423
}
429424

430-
func notResolvedVars(resolved map[string]string, wantList []string) []string {
431-
var notFound []string
432-
for _, want := range wantList {
433-
if val, ok := resolved[want]; !ok || val == "" {
434-
notFound = append(notFound, want)
435-
}
436-
}
437-
438-
return notFound
439-
}
440-
441425
// Inject material to attestation state
442426
func (c *Crafter) AddMaterial(key, value string, casBackend *casclient.CASBackend, runtimeAnnotations map[string]string) error {
443427
if err := c.requireStateLoaded(); err != nil {

internal/attestation/crafter/crafter_test.go

Lines changed: 65 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -221,138 +221,108 @@ func (s *crafterSuite) TestLoadSchema() {
221221

222222
func (s *crafterSuite) TestResolveEnvVars() {
223223
testCases := []struct {
224-
name string
225-
strict bool
224+
name string
225+
226226
// Custom env variables to expose
227227
envVars map[string]string
228-
// Simulate that the crafting process is hapenning in a github action runner
229-
inGithubEnv bool
230-
wantErr bool
231-
// Total list of resolved env vars
232-
want map[string]string
228+
229+
// Simulate that the crafting process is hapenning in a specific runner
230+
inGithubEnv bool
231+
inJenkinsEnv bool
232+
233+
expectedError string
234+
expectedEnvVars map[string]string
233235
}{
234236
{
235-
name: "strict missing custom vars",
236-
strict: true,
237-
inGithubEnv: true,
238-
wantErr: true,
239-
},
240-
{
241-
name: "strict, not running in github env",
242-
strict: true,
243-
inGithubEnv: false,
244-
wantErr: true,
237+
name: "missing custom vars",
238+
inGithubEnv: true,
239+
expectedError: "required env variables not present \"CUSTOM_VAR_1\"",
240+
}, {
241+
name: "missing some github runner env",
242+
inGithubEnv: true,
243+
expectedError: "error while resolving runner environment variables: environment variable GITHUB_ACTOR cannot be resolved\n",
245244
envVars: map[string]string{
246245
"CUSTOM_VAR_1": "custom_value_1",
247246
"CUSTOM_VAR_2": "custom_value_2",
247+
"GITHUB_ACTOR": "", // This is removing one necessary variable
248248
},
249-
},
250-
{
251-
name: "strict, missing some github env",
252-
strict: true,
253-
inGithubEnv: false,
254-
wantErr: true,
255-
envVars: map[string]string{
256-
"CUSTOM_VAR_1": "custom_value_1",
257-
"CUSTOM_VAR_2": "custom_value_2",
258-
"CI": "true",
259-
"GITHUB_RUN_ID": "123",
260-
},
261-
},
262-
{
263-
name: "strict and all envs available",
264-
strict: true,
265-
inGithubEnv: true,
249+
}, {
250+
name: "missing optional jenkins variable with no error",
251+
inJenkinsEnv: true,
252+
expectedError: "",
266253
envVars: map[string]string{
254+
// Missing var: GIT_BRANCH
267255
"CUSTOM_VAR_1": "custom_value_1",
268256
"CUSTOM_VAR_2": "custom_value_2",
269257
},
270-
want: map[string]string{
271-
"CUSTOM_VAR_1": "custom_value_1",
272-
"CUSTOM_VAR_2": "custom_value_2",
273-
"GITHUB_REPOSITORY": "chainloop/chainloop",
274-
"GITHUB_RUN_ID": "123",
275-
"GITHUB_ACTOR": "chainloop",
276-
"GITHUB_REF": "refs/heads/main",
277-
"GITHUB_REPOSITORY_OWNER": "chainloop",
278-
"GITHUB_SHA": "1234567890",
279-
"RUNNER_NAME": "chainloop-runner",
280-
"RUNNER_OS": "linux",
281-
},
282-
},
283-
{
284-
name: "non strict missing custom vars",
285-
strict: false,
286-
inGithubEnv: true,
287-
wantErr: false,
288-
want: gitHubTestingEnvVars,
289-
},
290-
{
291-
name: "non strict, missing some github env",
292-
strict: false,
293-
inGithubEnv: false,
294-
wantErr: false,
295-
envVars: map[string]string{
296-
"CUSTOM_VAR_1": "custom_value_1",
297-
"CUSTOM_VAR_2": "custom_value_2",
298-
"CI": "true",
299-
"GITHUB_RUN_ID": "123",
300-
"GITHUB_REPOSITORY": "chainloop/chainloop",
301-
},
302-
want: map[string]string{
303-
"CUSTOM_VAR_1": "custom_value_1",
304-
"CUSTOM_VAR_2": "custom_value_2",
305-
"GITHUB_REPOSITORY": "chainloop/chainloop",
306-
"GITHUB_RUN_ID": "123",
307-
"GITHUB_ACTOR": "",
308-
"GITHUB_REF": "",
309-
"GITHUB_REPOSITORY_OWNER": "",
310-
"GITHUB_SHA": "",
311-
"RUNNER_NAME": "",
312-
"RUNNER_OS": "",
258+
expectedEnvVars: map[string]string{
259+
// Missing var: GIT_BRANCH
260+
"CUSTOM_VAR_1": "custom_value_1",
261+
"CUSTOM_VAR_2": "custom_value_2",
262+
"JOB_NAME": "some-job",
263+
"BUILD_URL": "http://some-url",
264+
"AGENT_WORKDIR": "/some/home/dir",
265+
"NODE_NAME": "some-node",
313266
},
314-
},
315-
{
316-
name: "non strict, wrong runner context",
317-
strict: false,
318-
inGithubEnv: false,
319-
wantErr: false,
267+
}, {
268+
name: "all optional jenkins variable with no error",
269+
inJenkinsEnv: true,
270+
expectedError: "",
320271
envVars: map[string]string{
272+
"GIT_BRANCH": "some-branch", // optional var 1
273+
"GIT_COMMIT": "some-commit", // optional var 2
321274
"CUSTOM_VAR_1": "custom_value_1",
322275
"CUSTOM_VAR_2": "custom_value_2",
323276
},
324-
want: map[string]string{
325-
"CUSTOM_VAR_1": "custom_value_1",
326-
"CUSTOM_VAR_2": "custom_value_2",
277+
expectedEnvVars: map[string]string{
278+
"GIT_BRANCH": "some-branch", // optional var 1
279+
"GIT_COMMIT": "some-commit", // optional var 2
280+
"CUSTOM_VAR_1": "custom_value_1",
281+
"CUSTOM_VAR_2": "custom_value_2",
282+
"JOB_NAME": "some-job",
283+
"BUILD_URL": "http://some-url",
284+
"AGENT_WORKDIR": "/some/home/dir",
285+
"NODE_NAME": "some-node",
327286
},
328287
},
329288
}
330289

331290
for _, tc := range testCases {
332291
s.Run(tc.name, func() {
333-
// Customs env vars
334-
for k, v := range tc.envVars {
335-
s.T().Setenv(k, v)
336-
}
337-
// Runner env variables
292+
contract := "testdata/contracts/with_env_vars.yaml"
338293
if tc.inGithubEnv {
339294
s.T().Setenv("CI", "true")
340295
for k, v := range gitHubTestingEnvVars {
341296
s.T().Setenv(k, v)
342297
}
298+
} else if tc.inJenkinsEnv {
299+
contract = "testdata/contracts/jenkins_with_env_vars.yaml"
300+
s.T().Setenv("JOB_NAME", "some-job")
301+
s.T().Setenv("BUILD_URL", "http://some-url")
302+
s.T().Setenv("AGENT_WORKDIR", "/some/home/dir")
303+
s.T().Setenv("NODE_NAME", "some-node")
304+
s.T().Setenv("JENKINS_HOME", "/some/home/dir")
343305
}
344306

345-
c, err := newInitializedCrafter(s.T(), "testdata/contracts/with_env_vars.yaml", &v1.WorkflowMetadata{}, true, "")
307+
// Customs env vars
308+
for k, v := range tc.envVars {
309+
s.T().Setenv(k, v)
310+
}
311+
312+
c, err := newInitializedCrafter(s.T(), contract, &v1.WorkflowMetadata{}, false, "")
346313
require.NoError(s.T(), err)
347314

348-
err = c.ResolveEnvVars(tc.strict)
349-
if tc.wantErr {
315+
err = c.ResolveEnvVars()
316+
317+
if tc.expectedError != "" {
350318
s.Error(err)
319+
actualError := err.Error()
320+
s.Equal(tc.expectedError, actualError)
351321
return
352322
}
353323

354324
s.NoError(err)
355-
s.Equal(tc.want, c.CraftingState.Attestation.EnvVars)
325+
s.Equal(tc.expectedEnvVars, c.CraftingState.Attestation.EnvVars)
356326
})
357327
}
358328
}

internal/attestation/crafter/runner.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,15 @@ var ErrRunnerContextNotFound = errors.New("the runner environment doesn't match
2727
type supportedRunner interface {
2828
// Whether the attestation is happening in this environment
2929
CheckEnv() bool
30+
3031
// List the env variables registered
31-
ListEnvVars() []string
32+
ListEnvVars() []*runners.EnvVarDefinition
33+
3234
// Return the list of env vars associated with this runner already resolved
33-
ResolveEnvVars() map[string]string
35+
ResolveEnvVars() (map[string]string, []*error)
36+
3437
String() string
38+
3539
// uri to the running job/workload
3640
RunURI() string
3741
}

internal/attestation/crafter/runners/azurepipeline.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,21 @@ func (r *AzurePipeline) CheckEnv() bool {
4040
return true
4141
}
4242

43-
func (r *AzurePipeline) ListEnvVars() []string {
44-
return []string{
45-
"BUILD_REQUESTEDFOREMAIL",
46-
"BUILD_REQUESTEDFOR",
47-
"BUILD_REPOSITORY_URI",
48-
"BUILD_REPOSITORY_NAME",
49-
"BUILD_BUILDID",
50-
"BUILD_BUILDNUMBER",
51-
"BUILD_BUILDURI",
52-
"BUILD_REASON",
53-
"AGENT_VERSION",
54-
"TF_BUILD",
43+
func (r *AzurePipeline) ListEnvVars() []*EnvVarDefinition {
44+
return []*EnvVarDefinition{
45+
{"BUILD_REQUESTEDFOREMAIL", false},
46+
{"BUILD_REQUESTEDFOR", false},
47+
{"BUILD_REPOSITORY_URI", false},
48+
{"BUILD_REPOSITORY_NAME", false},
49+
{"BUILD_BUILDID", false},
50+
{"BUILD_BUILDNUMBER", false},
51+
{"BUILD_BUILDURI", false},
52+
{"BUILD_REASON", false},
53+
{"AGENT_VERSION", false},
54+
{"TF_BUILD", false},
5555
}
5656
}
5757

58-
func (r *AzurePipeline) ResolveEnvVars() map[string]string {
59-
return resolveEnvVars(r.ListEnvVars())
60-
}
61-
6258
func (r *AzurePipeline) String() string {
6359
return AzurePipelineID
6460
}
@@ -84,3 +80,7 @@ func (r *AzurePipeline) RunURI() (url string) {
8480

8581
return uri.String()
8682
}
83+
84+
func (r *AzurePipeline) ResolveEnvVars() (map[string]string, []*error) {
85+
return resolveEnvVars(r.ListEnvVars())
86+
}

0 commit comments

Comments
 (0)