Skip to content

Commit 94f2f97

Browse files
authored
Add support for multiple Buildkit secrets with env vars or files as source (#359)
1 parent d0b9da3 commit 94f2f97

File tree

3 files changed

+182
-1
lines changed

3 files changed

+182
-1
lines changed

cmd/drone-docker/main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,16 @@ func main() {
254254
Usage: "secret key value pair eg id=MYSECRET",
255255
EnvVar: "PLUGIN_SECRET",
256256
},
257+
cli.StringSliceFlag{
258+
Name: "secrets-from-env",
259+
Usage: "secret key value pair eg secret_name=secret",
260+
EnvVar: "PLUGIN_SECRETS_FROM_ENV",
261+
},
262+
cli.StringSliceFlag{
263+
Name: "secrets-from-file",
264+
Usage: "secret key value pairs eg secret_name=/path/to/secret",
265+
EnvVar: "PLUGIN_SECRETS_FROM_FILE",
266+
},
257267
cli.StringFlag{
258268
Name: "drone-card-path",
259269
Usage: "card path location to write to",
@@ -298,6 +308,8 @@ func run(c *cli.Context) error {
298308
Link: c.String("link"),
299309
NoCache: c.Bool("no-cache"),
300310
Secret: c.String("secret"),
311+
SecretEnvs: c.StringSlice("secrets-from-env"),
312+
SecretFiles: c.StringSlice("secrets-from-file"),
301313
AddHost: c.StringSlice("add-host"),
302314
Quiet: c.Bool("quiet"),
303315
},

docker.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type (
5959
Link string // Git repo link
6060
NoCache bool // Docker build no-cache
6161
Secret string // secret keypair
62+
SecretEnvs []string // Docker build secrets with env var as source
63+
SecretFiles []string // Docker build secrets with file as source
6264
AddHost []string // Docker build add-host
6365
Quiet bool // Docker build quiet
6466
}
@@ -306,6 +308,16 @@ func commandBuild(build Build) *exec.Cmd {
306308
if build.Secret != "" {
307309
args = append(args, "--secret", build.Secret)
308310
}
311+
for _, secret := range build.SecretEnvs {
312+
if arg, err := getSecretStringCmdArg(secret); err == nil {
313+
args = append(args, "--secret", arg)
314+
}
315+
}
316+
for _, secret := range build.SecretFiles {
317+
if arg, err := getSecretFileCmdArg(secret); err == nil {
318+
args = append(args, "--secret", arg)
319+
}
320+
}
309321
if build.Target != "" {
310322
args = append(args, "--target", build.Target)
311323
}
@@ -338,12 +350,40 @@ func commandBuild(build Build) *exec.Cmd {
338350
}
339351

340352
// we need to enable buildkit, for secret support
341-
if build.Secret != "" {
353+
if build.Secret != "" || len(build.SecretEnvs) > 0 || len(build.SecretFiles) > 0 {
342354
os.Setenv("DOCKER_BUILDKIT", "1")
343355
}
344356
return exec.Command(dockerExe, args...)
345357
}
346358

359+
func getSecretStringCmdArg(kvp string) (string, error) {
360+
return getSecretCmdArg(kvp, false)
361+
}
362+
363+
func getSecretFileCmdArg(kvp string) (string, error) {
364+
return getSecretCmdArg(kvp, true)
365+
}
366+
367+
func getSecretCmdArg(kvp string, file bool) (string, error) {
368+
delimIndex := strings.IndexByte(kvp, '=')
369+
if delimIndex == -1 {
370+
return "", fmt.Errorf("%s is not a valid secret", kvp)
371+
}
372+
373+
key := kvp[:delimIndex]
374+
value := kvp[delimIndex+1:]
375+
376+
if key == "" || value == "" {
377+
return "", fmt.Errorf("%s is not a valid secret", kvp)
378+
}
379+
380+
if file {
381+
return fmt.Sprintf("id=%s,src=%s", key, value), nil
382+
}
383+
384+
return fmt.Sprintf("id=%s,env=%s", key, value), nil
385+
}
386+
347387
// helper function to add proxy values from the environment
348388
func addProxyBuildArgs(build *Build) {
349389
addProxyValue(build, "http_proxy")

docker_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,130 @@
11
package docker
2+
3+
import (
4+
"os/exec"
5+
"reflect"
6+
"testing"
7+
)
8+
9+
func TestCommandBuild(t *testing.T) {
10+
tcs := []struct {
11+
name string
12+
build Build
13+
want *exec.Cmd
14+
}{
15+
{
16+
name: "secret from env var",
17+
build: Build{
18+
Name: "plugins/drone-docker:latest",
19+
Dockerfile: "Dockerfile",
20+
Context: ".",
21+
SecretEnvs: []string{
22+
"foo_secret=FOO_SECRET_ENV_VAR",
23+
},
24+
},
25+
want: exec.Command(
26+
dockerExe,
27+
"build",
28+
"--rm=true",
29+
"-f",
30+
"Dockerfile",
31+
"-t",
32+
"plugins/drone-docker:latest",
33+
".",
34+
"--secret id=foo_secret,env=FOO_SECRET_ENV_VAR",
35+
),
36+
},
37+
{
38+
name: "secret from file",
39+
build: Build{
40+
Name: "plugins/drone-docker:latest",
41+
Dockerfile: "Dockerfile",
42+
Context: ".",
43+
SecretFiles: []string{
44+
"foo_secret=/path/to/foo_secret",
45+
},
46+
},
47+
want: exec.Command(
48+
dockerExe,
49+
"build",
50+
"--rm=true",
51+
"-f",
52+
"Dockerfile",
53+
"-t",
54+
"plugins/drone-docker:latest",
55+
".",
56+
"--secret id=foo_secret,src=/path/to/foo_secret",
57+
),
58+
},
59+
{
60+
name: "multiple mixed secrets",
61+
build: Build{
62+
Name: "plugins/drone-docker:latest",
63+
Dockerfile: "Dockerfile",
64+
Context: ".",
65+
SecretEnvs: []string{
66+
"foo_secret=FOO_SECRET_ENV_VAR",
67+
"bar_secret=BAR_SECRET_ENV_VAR",
68+
},
69+
SecretFiles: []string{
70+
"foo_secret=/path/to/foo_secret",
71+
"bar_secret=/path/to/bar_secret",
72+
},
73+
},
74+
want: exec.Command(
75+
dockerExe,
76+
"build",
77+
"--rm=true",
78+
"-f",
79+
"Dockerfile",
80+
"-t",
81+
"plugins/drone-docker:latest",
82+
".",
83+
"--secret id=foo_secret,env=FOO_SECRET_ENV_VAR",
84+
"--secret id=bar_secret,env=BAR_SECRET_ENV_VAR",
85+
"--secret id=foo_secret,src=/path/to/foo_secret",
86+
"--secret id=bar_secret,src=/path/to/bar_secret",
87+
),
88+
},
89+
{
90+
name: "invalid mixed secrets",
91+
build: Build{
92+
Name: "plugins/drone-docker:latest",
93+
Dockerfile: "Dockerfile",
94+
Context: ".",
95+
SecretEnvs: []string{
96+
"foo_secret=",
97+
"=FOO_SECRET_ENV_VAR",
98+
"",
99+
},
100+
SecretFiles: []string{
101+
"foo_secret=",
102+
"=/path/to/bar_secret",
103+
"",
104+
},
105+
},
106+
want: exec.Command(
107+
dockerExe,
108+
"build",
109+
"--rm=true",
110+
"-f",
111+
"Dockerfile",
112+
"-t",
113+
"plugins/drone-docker:latest",
114+
".",
115+
),
116+
},
117+
}
118+
119+
for _, tc := range tcs {
120+
tc := tc
121+
122+
t.Run(tc.name, func(t *testing.T) {
123+
cmd := commandBuild(tc.build)
124+
125+
if !reflect.DeepEqual(cmd.String(), tc.want.String()) {
126+
t.Errorf("Got cmd %v, want %v", cmd, tc.want)
127+
}
128+
})
129+
}
130+
}

0 commit comments

Comments
 (0)