From 4b70ff0ccdbd44f19412fd75ceb0aab3a8020dfd Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Mon, 27 Jan 2025 10:03:26 +0100 Subject: [PATCH] fix support for ssh key from CLI flags Signed-off-by: Nicolas De Loof --- cmd/compose/build.go | 21 ++++--- cmd/compose/create_test.go | 8 ++- pkg/api/api.go | 2 + pkg/compose/build_bake.go | 125 ++++++++++++++++++++++++++++--------- 4 files changed, 114 insertions(+), 42 deletions(-) diff --git a/cmd/compose/build.go b/cmd/compose/build.go index ca9e0f25932..4b0e59bfd81 100644 --- a/cmd/compose/build.go +++ b/cmd/compose/build.go @@ -68,16 +68,17 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, uiMode = "rawjson" } return api.BuildOptions{ - Pull: opts.pull, - Push: opts.push, - Progress: uiMode, - Args: types.NewMappingWithEquals(opts.args), - NoCache: opts.noCache, - Quiet: opts.quiet, - Services: services, - Deps: opts.deps, - SSHs: SSHKeys, - Builder: builderName, + Pull: opts.pull, + Push: opts.push, + Progress: uiMode, + Args: types.NewMappingWithEquals(opts.args), + NoCache: opts.noCache, + Quiet: opts.quiet, + Services: services, + Deps: opts.deps, + SSHs: SSHKeys, + Builder: builderName, + Compatibility: opts.Compatibility, }, nil } diff --git a/cmd/compose/create_test.go b/cmd/compose/create_test.go index 445f81ecb0b..e4677b973b1 100644 --- a/cmd/compose/create_test.go +++ b/cmd/compose/create_test.go @@ -40,7 +40,9 @@ func TestRunCreate(t *testing.T) { ) createOpts := createOptions{} - buildOpts := buildOptions{} + buildOpts := buildOptions{ + ProjectOptions: &ProjectOptions{}, + } project := sampleProject() err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil) require.NoError(t, err) @@ -58,7 +60,9 @@ func TestRunCreate_Build(t *testing.T) { createOpts := createOptions{ Build: true, } - buildOpts := buildOptions{} + buildOpts := buildOptions{ + ProjectOptions: &ProjectOptions{}, + } project := sampleProject() err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil) require.NoError(t, err) diff --git a/pkg/api/api.go b/pkg/api/api.go index 8d2d1280328..948de675049 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -155,6 +155,8 @@ type BuildOptions struct { Memory int64 // Builder name passed in the command line Builder string + // Compatibility let compose run with best backward compatibility + Compatibility bool } // Apply mutates project according to build options diff --git a/pkg/compose/build_bake.go b/pkg/compose/build_bake.go index 2ff8c6b29cb..a8468f8be05 100644 --- a/pkg/compose/build_bake.go +++ b/pkg/compose/build_bake.go @@ -26,6 +26,7 @@ import ( "os" "os/exec" "path/filepath" + "slices" "strconv" "strings" @@ -35,6 +36,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/progress" + "github.com/docker/docker/api/types/versions" "github.com/docker/docker/builder/remotecontext/urlutil" "github.com/moby/buildkit/client" "github.com/moby/buildkit/util/progress/progressui" @@ -93,19 +95,25 @@ type bakeGroup struct { } type bakeTarget struct { - Context string `json:"context,omitempty"` - Dockerfile string `json:"dockerfile,omitempty"` - Args map[string]string `json:"args,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Tags []string `json:"tags,omitempty"` - CacheFrom []string `json:"cache-from,omitempty"` - CacheTo []string `json:"cache-to,omitempty"` - Secrets []string `json:"secret,omitempty"` - SSH []string `json:"ssh,omitempty"` - Platforms []string `json:"platforms,omitempty"` - Target string `json:"target,omitempty"` - Pull bool `json:"pull,omitempty"` - NoCache bool `json:"no-cache,omitempty"` + Context string `json:"context,omitempty"` + Contexts map[string]string `json:"contexts,omitempty"` + Dockerfile string `json:"dockerfile,omitempty"` + DockerfileInline string `json:"dockerfile-inline,omitempty"` + Args map[string]string `json:"args,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Tags []string `json:"tags,omitempty"` + CacheFrom []string `json:"cache-from,omitempty"` + CacheTo []string `json:"cache-to,omitempty"` + Secrets []string `json:"secret,omitempty"` + SSH []string `json:"ssh,omitempty"` + Platforms []string `json:"platforms,omitempty"` + Target string `json:"target,omitempty"` + Pull bool `json:"pull,omitempty"` + NoCache bool `json:"no-cache,omitempty"` + ShmSize types.UnitBytes `json:"shm-size,omitempty"` + Ulimits []string `json:"ulimits,omitempty"` + Entitlements []string `json:"entitlements,omitempty"` + Outputs []string `json:"output,omitempty"` } type bakeMetadata map[string]buildStatus @@ -136,8 +144,9 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project Targets: map[string]bakeTarget{}, } var group bakeGroup + var privileged bool - for _, service := range serviceToBeBuild { + for serviceName, service := range serviceToBeBuild { if service.Build == nil { continue } @@ -153,23 +162,43 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project image := api.GetImageNameOrDefault(service, project.Name) - cfg.Targets[image] = bakeTarget{ - Context: build.Context, - Dockerfile: dockerFilePath(build.Context, build.Dockerfile), - Args: args, - Labels: build.Labels, - Tags: append(build.Tags, image), + entitlements := build.Entitlements + if slices.Contains(build.Entitlements, "security.insecure") { + privileged = true + } + if build.Privileged { + entitlements = append(entitlements, "security.insecure") + privileged = true + } + + outputs := []string{"type=docker"} + if options.Push && service.Image != "" { + outputs = append(outputs, "type=image,push=true") + } + + cfg.Targets[serviceName] = bakeTarget{ + Context: build.Context, + Contexts: additionalContexts(build.AdditionalContexts, service.DependsOn, options.Compatibility), + Dockerfile: dockerFilePath(build.Context, build.Dockerfile), + DockerfileInline: build.DockerfileInline, + Args: args, + Labels: build.Labels, + Tags: append(build.Tags, image), CacheFrom: build.CacheFrom, // CacheTo: TODO - Platforms: build.Platforms, - Target: build.Target, - Secrets: toBakeSecrets(project, build.Secrets), - SSH: toBakeSSH(build.SSH), - Pull: options.Pull, - NoCache: options.NoCache, + Platforms: build.Platforms, + Target: build.Target, + Secrets: toBakeSecrets(project, build.Secrets), + SSH: toBakeSSH(append(build.SSH, options.SSHs...)), + Pull: options.Pull, + NoCache: options.NoCache, + ShmSize: build.ShmSize, + Ulimits: toBakeUlimits(build.Ulimits), + Entitlements: entitlements, + Outputs: outputs, } - group.Targets = append(group.Targets, image) + group.Targets = append(group.Targets, serviceName) } cfg.Groups["default"] = group @@ -188,7 +217,14 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project if err != nil { return nil, err } - cmd := exec.CommandContext(ctx, buildx.Path, "bake", "--file", "-", "--progress", "rawjson", "--metadata-file", metadata.Name()) + + args := []string{"bake", "--file", "-", "--progress", "rawjson", "--metadata-file", metadata.Name()} + mustAllow := buildx.Version != "" && versions.GreaterThanOrEqualTo(buildx.Version[1:], "0.17.0") + if privileged && mustAllow { + args = append(args, "--allow", "security.insecure") + } + + cmd := exec.CommandContext(ctx, buildx.Path, args...) // Remove DOCKER_CLI_PLUGIN... variable so buildx can detect it run standalone cmd.Env = filter(os.Environ(), manager.ReexecEnvvar) @@ -258,6 +294,31 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project return results, nil } +func additionalContexts(contexts types.Mapping, dependencies types.DependsOnConfig, compatibility bool) map[string]string { + ac := map[string]string{} + if compatibility { + for name := range dependencies { + ac[name] = "target:" + name + } + } + for k, v := range contexts { + ac[k] = v + } + return ac +} + +func toBakeUlimits(ulimits map[string]*types.UlimitsConfig) []string { + s := []string{} + for u, l := range ulimits { + if l.Single > 0 { + s = append(s, fmt.Sprintf("%s=%d", u, l.Single)) + } else { + s = append(s, fmt.Sprintf("%s=%d:%d", u, l.Soft, l.Hard)) + } + } + return s +} + func toBakeSSH(ssh types.SSHConfig) []string { var s []string for _, key := range ssh { @@ -270,11 +331,15 @@ func toBakeSecrets(project *types.Project, secrets []types.ServiceSecretConfig) var s []string for _, ref := range secrets { def := project.Secrets[ref.Source] + target := ref.Target + if target == "" { + target = ref.Source + } switch { case def.Environment != "": - s = append(s, fmt.Sprintf("id=%s,type=env,env=%s", ref.Source, def.Environment)) + s = append(s, fmt.Sprintf("id=%s,type=env,env=%s", target, def.Environment)) case def.File != "": - s = append(s, fmt.Sprintf("id=%s,type=file,src=%s", ref.Source, def.File)) + s = append(s, fmt.Sprintf("id=%s,type=file,src=%s", target, def.File)) } } return s