Skip to content

Commit ff73827

Browse files
gloursndeloof
authored andcommitted
Add support of ssh authentications defined in compose file or via cli flags
Signed-off-by: Guillaume Lours <[email protected]>
1 parent 3c12b94 commit ff73827

File tree

9 files changed

+93
-18
lines changed

9 files changed

+93
-18
lines changed

cmd/compose/build.go

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"strings"
2424

2525
"github.com/compose-spec/compose-go/cli"
26+
"github.com/compose-spec/compose-go/loader"
2627
"github.com/compose-spec/compose-go/types"
2728
buildx "github.com/docker/buildx/util/progress"
2829
"github.com/docker/compose/v2/pkg/utils"
@@ -40,6 +41,28 @@ type buildOptions struct {
4041
args []string
4142
noCache bool
4243
memory string
44+
ssh string
45+
}
46+
47+
func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) {
48+
var SSHKeys []types.SSHKey
49+
var err error
50+
if opts.ssh != "" {
51+
SSHKeys, err = loader.ParseShortSSHSyntax(opts.ssh)
52+
if err != nil {
53+
return api.BuildOptions{}, err
54+
}
55+
}
56+
57+
return api.BuildOptions{
58+
Pull: opts.pull,
59+
Progress: opts.progress,
60+
Args: types.NewMappingWithEquals(opts.args),
61+
NoCache: opts.noCache,
62+
Quiet: opts.quiet,
63+
Services: services,
64+
SSHs: SSHKeys,
65+
}, nil
4366
}
4467

4568
var printerModes = []string{
@@ -73,7 +96,10 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
7396
}
7497
return nil
7598
}),
76-
RunE: Adapt(func(ctx context.Context, args []string) error {
99+
RunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
100+
if cmd.Flags().Changed("ssh") && opts.ssh == "" {
101+
opts.ssh = "default"
102+
}
77103
return runBuild(ctx, backend, opts, args)
78104
}),
79105
ValidArgsFunction: serviceCompletion(p),
@@ -82,6 +108,7 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
82108
cmd.Flags().BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image.")
83109
cmd.Flags().StringVar(&opts.progress, "progress", buildx.PrinterModeAuto, fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", ")))
84110
cmd.Flags().StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables for services.")
111+
cmd.Flags().StringVar(&opts.ssh, "ssh", "", "Set SSH authentications used when building service images. (use 'default' for using you default SSH Agent)")
85112
cmd.Flags().Bool("parallel", true, "Build images in parallel. DEPRECATED")
86113
cmd.Flags().MarkHidden("parallel") //nolint:errcheck
87114
cmd.Flags().Bool("compress", true, "Compress the build context using gzip. DEPRECATED")
@@ -103,12 +130,9 @@ func runBuild(ctx context.Context, backend api.Service, opts buildOptions, servi
103130
return err
104131
}
105132

106-
return backend.Build(ctx, project, api.BuildOptions{
107-
Pull: opts.pull,
108-
Progress: opts.progress,
109-
Args: types.NewMappingWithEquals(opts.args),
110-
NoCache: opts.noCache,
111-
Quiet: opts.quiet,
112-
Services: services,
113-
})
133+
apiBuildOptions, err := opts.toAPIBuildOptions(services)
134+
if err != nil {
135+
return err
136+
}
137+
return backend.Build(ctx, project, apiBuildOptions)
114138
}

docs/reference/compose_build.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Build or rebuild services
1212
| `--progress` | `string` | `auto` | Set type of progress output (auto, tty, plain, quiet) |
1313
| `--pull` | | | Always attempt to pull a newer version of the image. |
1414
| `-q`, `--quiet` | | | Don't print anything to STDOUT |
15+
| `--ssh` | `string` | | Set SSH authentications used when building service images. (use 'default' for using you default SSH Agent) |
1516

1617

1718
<!---MARKER_GEN_END-->

docs/reference/docker_compose_build.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ options:
117117
experimentalcli: false
118118
kubernetes: false
119119
swarm: false
120+
- option: ssh
121+
value_type: string
122+
description: |
123+
Set SSH authentications used when building service images. (use 'default' for using you default SSH Agent)
124+
deprecated: false
125+
hidden: false
126+
experimental: false
127+
experimentalcli: false
128+
kubernetes: false
129+
swarm: false
120130
deprecated: false
121131
experimental: false
122132
experimentalcli: false

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/AlecAivazis/survey/v2 v2.3.2
77
github.com/buger/goterm v1.0.4
88
github.com/cnabio/cnab-to-oci v0.3.1-beta1
9-
github.com/compose-spec/compose-go v1.2.1
9+
github.com/compose-spec/compose-go v1.2.2
1010
github.com/containerd/console v1.0.3
1111
github.com/containerd/containerd v1.6.1
1212
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,8 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC
302302
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
303303
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
304304
github.com/compose-spec/compose-go v1.0.8/go.mod h1:REnCbBugoIdHB7S1sfkN/aJ7AJpNApGNjNiVjA9L8x4=
305-
github.com/compose-spec/compose-go v1.2.1 h1:8+DAP7Mt/Ohl5y6YbZdilLMvIhMxvuSZcNZyywjQmJE=
306-
github.com/compose-spec/compose-go v1.2.1/go.mod h1:pAy7Mikpeft4pxkFU565/DRHEbDfR84G6AQuiL+Hdg8=
305+
github.com/compose-spec/compose-go v1.2.2 h1:y1dwl3KUTBnWPVur6EZno9zUIum6Q87/F5keljnGQB4=
306+
github.com/compose-spec/compose-go v1.2.2/go.mod h1:pAy7Mikpeft4pxkFU565/DRHEbDfR84G6AQuiL+Hdg8=
307307
github.com/compose-spec/godotenv v1.1.1/go.mod h1:zF/3BOa18Z24tts5qnO/E9YURQanJTBUf7nlcCTNsyc=
308308
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
309309
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=

pkg/api/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ type BuildOptions struct {
9191
Quiet bool
9292
// Services passed in the command line to be built
9393
Services []string
94+
// Ssh authentications passed in the command line
95+
SSHs []types.SSHKey
9496
}
9597

9698
// CreateOptions group options of the Create API

pkg/compose/build.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
bclient "github.com/moby/buildkit/client"
3232
"github.com/moby/buildkit/session"
3333
"github.com/moby/buildkit/session/auth/authprovider"
34+
"github.com/moby/buildkit/session/sshforward/sshprovider"
3435
specs "github.com/opencontainers/image-spec/specs-go/v1"
3536

3637
"github.com/docker/compose/v2/pkg/api"
@@ -81,6 +82,14 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
8182
})
8283
}
8384

85+
if len(options.SSHs) > 0 || len(service.Build.SSH) > 0 {
86+
sshAgentProvider, err := sshAgentProvider(append(service.Build.SSH, options.SSHs...))
87+
if err != nil {
88+
return err
89+
}
90+
buildOptions.Session = append(buildOptions.Session, sshAgentProvider)
91+
}
92+
8493
opts[imageName] = buildOptions
8594
}
8695
}
@@ -296,3 +305,14 @@ func dockerFilePath(context string, dockerfile string) string {
296305
}
297306
return filepath.Join(context, dockerfile)
298307
}
308+
309+
func sshAgentProvider(sshKeys types.SSHConfig) (session.Attachable, error) {
310+
sshConfig := make([]sshprovider.AgentConfig, 0, len(sshKeys))
311+
for _, sshKey := range sshKeys {
312+
sshConfig = append(sshConfig, sshprovider.AgentConfig{
313+
ID: sshKey.ID,
314+
Paths: []string{sshKey.Path},
315+
})
316+
}
317+
return sshprovider.NewSSHAgentProvider(sshConfig)
318+
}

pkg/e2e/compose_build_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package e2e
1818

1919
import (
2020
"net/http"
21+
"os"
2122
"strings"
2223
"testing"
2324
"time"
@@ -79,6 +80,20 @@ func TestLocalComposeBuild(t *testing.T) {
7980
res.Assert(t, icmd.Expected{Out: `"RESULT": "SUCCESS"`})
8081
})
8182

83+
t.Run("build failed with ssh default value", func(t *testing.T) {
84+
//unset SSH_AUTH_SOCK to be sure we don't have a default value for the SSH Agent
85+
defaultSSHAUTHSOCK := os.Getenv("SSH_AUTH_SOCK")
86+
os.Unsetenv("SSH_AUTH_SOCK") //nolint:errcheck
87+
defer os.Setenv("SSH_AUTH_SOCK", defaultSSHAUTHSOCK) //nolint:errcheck
88+
89+
res := c.RunDockerComposeCmdNoCheck("--project-directory", "fixtures/build-test", "build", "--ssh", "")
90+
res.Assert(t, icmd.Expected{
91+
ExitCode: 1,
92+
Err: "invalid empty ssh agent socket: make sure SSH_AUTH_SOCK is set",
93+
})
94+
95+
})
96+
8297
t.Run("build as part of up", func(t *testing.T) {
8398
c.RunDockerOrExitError("rmi", "build-test_nginx")
8499
c.RunDockerOrExitError("rmi", "custom-nginx")

pkg/e2e/framework.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,17 +204,20 @@ func (c *E2eCLI) RunDockerCmd(args ...string) *icmd.Result {
204204

205205
// RunDockerComposeCmd runs a docker compose command, expects no error and returns a result
206206
func (c *E2eCLI) RunDockerComposeCmd(args ...string) *icmd.Result {
207+
res := c.RunDockerComposeCmdNoCheck(args...)
208+
res.Assert(c.test, icmd.Success)
209+
return res
210+
}
211+
212+
// RunDockerComposeCmdNoCheck runs a docker compose command, don't presume of any expectation and returns a result
213+
func (c *E2eCLI) RunDockerComposeCmdNoCheck(args ...string) *icmd.Result {
207214
if composeStandaloneMode {
208215
composeBinary, err := findExecutable(DockerComposeExecutableName, []string{"../../bin", "../../../bin"})
209216
assert.NilError(c.test, err)
210-
res := icmd.RunCmd(c.NewCmd(composeBinary, args...))
211-
res.Assert(c.test, icmd.Success)
212-
return res
217+
return icmd.RunCmd(c.NewCmd(composeBinary, args...))
213218
}
214219
args = append([]string{"compose"}, args...)
215-
res := icmd.RunCmd(c.NewCmd(DockerExecutableName, args...))
216-
res.Assert(c.test, icmd.Success)
217-
return res
220+
return icmd.RunCmd(c.NewCmd(DockerExecutableName, args...))
218221
}
219222

220223
// StdoutContains returns a predicate on command result expecting a string in stdout

0 commit comments

Comments
 (0)