Skip to content
This repository was archived by the owner on Feb 16, 2023. It is now read-only.

Commit d20ccf8

Browse files
authored
Merge pull request #243 from secrethub/feature/env-var-references
Substitute os env vars with the 'secrethub://' prefix
2 parents a2da5d4 + 9e957d0 commit d20ccf8

3 files changed

Lines changed: 87 additions & 16 deletions

File tree

internals/secrethub/inject.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type InjectCommand struct {
3434
useClipboard bool
3535
clearClipboardAfter time.Duration
3636
clipper clip.Clipper
37-
osEnv func() []string
37+
osEnv []string
3838
newClient newClientFunc
3939
templateVars map[string]string
4040
templateVersion string
@@ -45,7 +45,7 @@ type InjectCommand struct {
4545
func NewInjectCommand(io ui.IO, newClient newClientFunc) *InjectCommand {
4646
return &InjectCommand{
4747
clipper: clip.NewClipboard(),
48-
osEnv: os.Environ,
48+
osEnv: os.Environ(),
4949
clearClipboardAfter: defaultClearClipboardAfter,
5050
io: io,
5151
newClient: newClient,
@@ -101,7 +101,7 @@ func (cmd *InjectCommand) Run() error {
101101
}
102102
}
103103

104-
osEnv, _ := parseKeyValueStringsToMap(cmd.osEnv())
104+
osEnv, _ := parseKeyValueStringsToMap(cmd.osEnv)
105105

106106
var templateVariableReader tpl.VariableReader
107107
templateVariableReader, err = newVariableReader(osEnv, cmd.templateVars)

internals/secrethub/run.go

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ const (
4949
// templateVarEnvVarPrefix is used to prefix environment variables
5050
// that should be used as template variables.
5151
templateVarEnvVarPrefix = "SECRETHUB_VAR_"
52+
// prefix of the values of environment variables that will be
53+
// substituted with secrets
54+
secretReferencePrefix = "secrethub://"
5255
)
5356

5457
// RunCommand runs a program and passes environment variables to it that are
@@ -57,7 +60,7 @@ const (
5760
type RunCommand struct {
5861
command []string
5962
io ui.IO
60-
osEnv func() []string
63+
osEnv []string
6164
envar map[string]string
6265
envFile string
6366
templateVars map[string]string
@@ -74,7 +77,7 @@ type RunCommand struct {
7477
func NewRunCommand(io ui.IO, newClient newClientFunc) *RunCommand {
7578
return &RunCommand{
7679
io: io,
77-
osEnv: os.Environ,
80+
osEnv: os.Environ(),
7881
envar: make(map[string]string),
7982
templateVars: make(map[string]string),
8083
newClient: newClient,
@@ -112,6 +115,11 @@ func (cmd *RunCommand) Run() error {
112115
// Parse
113116
envSources := []EnvSource{}
114117

118+
osEnv, passthroughEnv := parseKeyValueStringsToMap(cmd.osEnv)
119+
120+
referenceEnv := newReferenceEnv(osEnv)
121+
envSources = append(envSources, referenceEnv)
122+
115123
// TODO: Validate the flags when parsing by implementing the Flag interface for EnvFlags.
116124
flagSource, err := NewEnvFlags(cmd.envar)
117125
if err != nil {
@@ -131,11 +139,6 @@ func (cmd *RunCommand) Run() error {
131139
}
132140
}
133141

134-
osEnv, passthroughEnv := parseKeyValueStringsToMap(cmd.osEnv())
135-
if err != nil {
136-
return err
137-
}
138-
139142
if cmd.envFile != "" {
140143
templateVariableReader, err := newVariableReader(osEnv, cmd.templateVars)
141144
if err != nil {
@@ -424,6 +427,44 @@ func ReadEnvFile(filepath string, varReader tpl.VariableReader, parser tpl.Parse
424427
}, nil
425428
}
426429

430+
// referenceEnv is an environment with secrets configured with the
431+
// secrethub:// syntax in the os environment variables.
432+
type referenceEnv struct {
433+
envVars map[string]string
434+
}
435+
436+
// newReferenceEnv returns an environment with secrets configured in the
437+
// os environment with the secrethub:// syntax.
438+
func newReferenceEnv(osEnv map[string]string) *referenceEnv {
439+
envVars := make(map[string]string)
440+
for key, value := range osEnv {
441+
if strings.HasPrefix(value, secretReferencePrefix) {
442+
envVars[key] = strings.TrimPrefix(value, secretReferencePrefix)
443+
}
444+
}
445+
return &referenceEnv{
446+
envVars: envVars,
447+
}
448+
}
449+
450+
// Env returns a map of key value pairs with the secrets configured with the
451+
// secrethub:// syntax.
452+
func (env *referenceEnv) Env(_ map[string]string, secretReader tpl.SecretReader) (map[string]string, error) {
453+
envVarsWithSecrets := make(map[string]string)
454+
for key, path := range env.envVars {
455+
secret, err := secretReader.ReadSecret(path)
456+
if err != nil {
457+
return nil, err
458+
}
459+
envVarsWithSecrets[key] = secret
460+
}
461+
return envVarsWithSecrets, nil
462+
}
463+
464+
func (env *referenceEnv) Secrets() []string {
465+
return nil
466+
}
467+
427468
// EnvFile contains an environment that is read from a file.
428469
type EnvFile struct {
429470
path string

internals/secrethub/run_test.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -458,13 +458,11 @@ func TestRunCommand_Run(t *testing.T) {
458458
}{
459459
"success, no secrets": {
460460
command: RunCommand{
461-
osEnv: func() []string { return []string{} },
462461
command: []string{"echo", "test"},
463462
},
464463
},
465464
"missing secret": {
466465
command: RunCommand{
467-
osEnv: func() []string { return []string{} },
468466
command: []string{"echo", "test"},
469467
envar: map[string]string{
470468
"missing": "path/to/unexisting/secret",
@@ -486,7 +484,6 @@ func TestRunCommand_Run(t *testing.T) {
486484
},
487485
"missing secret ignored": {
488486
command: RunCommand{
489-
osEnv: func() []string { return []string{} },
490487
command: []string{"echo", "test"},
491488
envar: map[string]string{
492489
"missing": "path/to/unexisting/secret",
@@ -508,7 +505,6 @@ func TestRunCommand_Run(t *testing.T) {
508505
},
509506
"repo does not exist ignored": {
510507
command: RunCommand{
511-
osEnv: func() []string { return []string{} },
512508
command: []string{"echo", "test"},
513509
envar: map[string]string{
514510
"missing": "unexisting/repo/secret",
@@ -530,7 +526,6 @@ func TestRunCommand_Run(t *testing.T) {
530526
},
531527
"invalid template var: start with a number": {
532528
command: RunCommand{
533-
osEnv: func() []string { return []string{} },
534529
envFile: "secrethub.env",
535530
templateVars: map[string]string{
536531
"0foo": "value",
@@ -541,7 +536,6 @@ func TestRunCommand_Run(t *testing.T) {
541536
},
542537
"invalid template var: illegal character": {
543538
command: RunCommand{
544-
osEnv: func() []string { return []string{} },
545539
envFile: "secrethub.env",
546540
templateVars: map[string]string{
547541
"foo@bar": "value",
@@ -550,6 +544,42 @@ func TestRunCommand_Run(t *testing.T) {
550544
},
551545
err: ErrInvalidTemplateVar("foo@bar"),
552546
},
547+
"os env secret not found": {
548+
command: RunCommand{
549+
osEnv: []string{"TEST=secrethub://nonexistent/secret/path"},
550+
command: []string{"echo", "test"},
551+
newClient: func() (secrethub.ClientInterface, error) {
552+
return fakeclient.Client{
553+
SecretService: &fakeclient.SecretService{
554+
VersionService: &fakeclient.SecretVersionService{
555+
WithDataGetter: fakeclient.WithDataGetter{
556+
Err: api.ErrSecretNotFound,
557+
},
558+
},
559+
},
560+
}, nil
561+
},
562+
},
563+
err: api.ErrSecretNotFound,
564+
},
565+
"os env secret not found ignored": {
566+
command: RunCommand{
567+
osEnv: []string{"TEST=secrethub://nonexistent/secret/path"},
568+
ignoreMissingSecrets: true,
569+
command: []string{"echo", "test"},
570+
newClient: func() (secrethub.ClientInterface, error) {
571+
return fakeclient.Client{
572+
SecretService: &fakeclient.SecretService{
573+
VersionService: &fakeclient.SecretVersionService{
574+
WithDataGetter: fakeclient.WithDataGetter{
575+
Err: api.ErrSecretNotFound,
576+
},
577+
},
578+
},
579+
}, nil
580+
},
581+
},
582+
},
553583
}
554584

555585
for name, tc := range cases {

0 commit comments

Comments
 (0)