From a48945b208f84f3aa266fc59021d2eaeb0a0a5fd Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 6 Jun 2025 09:44:31 +0200 Subject: [PATCH 1/2] move `run` logic inside backend Signed-off-by: Nicolas De Loof --- cmd/compose/run.go | 59 ++++++---------------------------------------- go.mod | 3 ++- go.sum | 8 +++++-- pkg/api/api.go | 4 +--- pkg/compose/run.go | 38 +++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 58 deletions(-) diff --git a/cmd/compose/run.go b/cmd/compose/run.go index f6fb05715fe..3ae8b9d9578 100644 --- a/cmd/compose/run.go +++ b/cmd/compose/run.go @@ -271,22 +271,6 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op return err } - err = progress.Run(ctx, func(ctx context.Context) error { - var buildForDeps *api.BuildOptions - if !createOpts.noBuild { - // allow dependencies needing build to be implicitly selected - bo, err := buildOpts.toAPIBuildOptions(nil) - if err != nil { - return err - } - buildForDeps = &bo - } - return startDependencies(ctx, backend, *project, buildForDeps, options) - }, dockerCli.Err()) - if err != nil { - return err - } - labels := types.Labels{} for _, s := range options.labels { parts := strings.SplitN(s, "=", 2) @@ -298,9 +282,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op var buildForRun *api.BuildOptions if !createOpts.noBuild { - // dependencies have already been started above, so only the service - // being run might need to be built at this point - bo, err := buildOpts.toAPIBuildOptions([]string{options.Service}) + bo, err := buildOpts.toAPIBuildOptions(project.ServiceNames()) if err != nil { return err } @@ -314,7 +296,12 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op // start container and attach to container streams runOpts := api.RunOptions{ - Build: buildForRun, + CreateOptions: api.CreateOptions{ + Build: buildForRun, + RemoveOrphans: options.removeOrphans, + IgnoreOrphans: options.ignoreOrphans, + QuietPull: options.quietPull, + }, Name: options.name, Service: options.Service, Command: options.Command, @@ -332,7 +319,6 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op UseNetworkAliases: options.useAliases, NoDeps: options.noDeps, Index: 0, - QuietPull: options.quietPull, } for name, service := range project.Services { @@ -352,34 +338,3 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op } return err } - -func startDependencies(ctx context.Context, backend api.Service, project types.Project, buildOpts *api.BuildOptions, options runOptions) error { - dependencies := types.Services{} - var requestedService types.ServiceConfig - for name, service := range project.Services { - if name != options.Service { - dependencies[name] = service - } else { - requestedService = service - } - } - - project.Services = dependencies - project.DisabledServices[options.Service] = requestedService - err := backend.Create(ctx, &project, api.CreateOptions{ - Build: buildOpts, - IgnoreOrphans: options.ignoreOrphans, - RemoveOrphans: options.removeOrphans, - QuietPull: options.quietPull, - }) - if err != nil { - return err - } - - if len(dependencies) > 0 { - return backend.Start(ctx, project.Name, api.StartOptions{ - Project: &project, - }) - } - return nil -} diff --git a/go.mod b/go.mod index 0ed0288e353..536b528c81f 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/buger/goterm v1.0.4 - github.com/compose-spec/compose-go/v2 v2.6.4 + github.com/compose-spec/compose-go/v2 v2.6.5-0.20250605125952-a0d3b94b8dc9 github.com/containerd/containerd/v2 v2.1.2 github.com/containerd/errdefs v1.0.0 github.com/containerd/platforms v1.0.0-rc.1 @@ -157,6 +157,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect diff --git a/go.sum b/go.sum index c9cdc476efb..9b2461f9dca 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= -github.com/compose-spec/compose-go/v2 v2.6.4 h1:Gjv6x8eAhqwwWvoXIo0oZ4bDQBh0OMwdU7LUL9PDLiM= -github.com/compose-spec/compose-go/v2 v2.6.4/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= +github.com/compose-spec/compose-go/v2 v2.6.5-0.20250605125952-a0d3b94b8dc9 h1:VULSSHxkc7u/U349sHp1RbnWAcnf7JD0HY3rGeZrMaM= +github.com/compose-spec/compose-go/v2 v2.6.5-0.20250605125952-a0d3b94b8dc9/go.mod h1:TmjkIB9W73fwVxkYY+u2uhMbMUakjiif79DlYgXsyvU= github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo= github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= @@ -125,6 +125,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/buildx v0.24.0 h1:qiD+xktY+Fs3R79oz8M+7pbhip78qGLx6LBuVmyb+64= github.com/docker/buildx v0.24.0/go.mod h1:vYkdBUBjFo/i5vUE0mkajGlk03gE0T/HaGXXhgIxo8E= github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A= @@ -423,6 +425,8 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs= github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU= diff --git a/pkg/api/api.go b/pkg/api/api.go index 21fbbda3599..1be883899c6 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -351,7 +351,7 @@ type RemoveOptions struct { // RunOptions group options of the Run API type RunOptions struct { - Build *BuildOptions + CreateOptions // Project is the compose project used to define this app. Might be nil if user ran command just with project name Project *types.Project Name string @@ -371,8 +371,6 @@ type RunOptions struct { Privileged bool UseNetworkAliases bool NoDeps bool - // QuietPull makes the pulling process quiet - QuietPull bool // used by exec Index int } diff --git a/pkg/compose/run.go b/pkg/compose/run.go index b88d9767daa..7b4f5678fef 100644 --- a/pkg/compose/run.go +++ b/pkg/compose/run.go @@ -28,6 +28,7 @@ import ( "github.com/docker/cli/cli" cmd "github.com/docker/cli/cli/command/container" "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/progress" "github.com/docker/docker/pkg/stringid" ) @@ -58,6 +59,13 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types. } func (s *composeService) prepareRun(ctx context.Context, project *types.Project, opts api.RunOptions) (string, error) { + err = progress.Run(ctx, func(ctx context.Context) error { + return s.startDependencies(ctx, project, opts) + }, s.stdinfo()) + if err != nil { + return "", err + } + service, err := project.GetService(opts.Service) if err != nil { return "", err @@ -160,3 +168,33 @@ func applyRunOptions(project *types.Project, service *types.ServiceConfig, opts service.Labels = service.Labels.Add(k, v) } } + +func (s *composeService) startDependencies(ctx context.Context, project *types.Project, options api.RunOptions) error { + var dependencies []string + for name, _ := range project.Services { + if name != options.Service { + dependencies = append(dependencies, name) + } + } + + project, err := project.WithSelectedServices(dependencies) + if err != nil { + return err + } + err = s.Create(ctx, project, api.CreateOptions{ + Build: options.Build, + IgnoreOrphans: options.IgnoreOrphans, + RemoveOrphans: options.RemoveOrphans, + QuietPull: options.QuietPull, + }) + if err != nil { + return err + } + + if len(dependencies) > 0 { + return s.Start(ctx, project.Name, api.StartOptions{ + Project: project, + }) + } + return nil +} From 2138b5e2c29b3b592c268e24c16354884ae9a057 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 6 Jun 2025 09:45:09 +0200 Subject: [PATCH 2/2] add (temporary) support for use_api_socket Signed-off-by: Nicolas De Loof --- pkg/compose/apiSocket.go | 90 ++++++++++++++++++++++++++++++++++++++++ pkg/compose/create.go | 7 ++++ pkg/compose/run.go | 8 +++- 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 pkg/compose/apiSocket.go diff --git a/pkg/compose/apiSocket.go b/pkg/compose/apiSocket.go new file mode 100644 index 00000000000..52e702a5013 --- /dev/null +++ b/pkg/compose/apiSocket.go @@ -0,0 +1,90 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package compose + +import ( + "bytes" + "fmt" + "strings" + + "github.com/compose-spec/compose-go/v2/types" + "github.com/docker/cli/cli/config/configfile" +) + +// --use-api-socket is not actually supported by the Docker Engine +// but is a client-side hack (see https://github.com/docker/cli/blob/master/cli/command/container/create.go#L246) +// we replicate here by transforming the project model + +func (s *composeService) useAPISocket(project *types.Project) (*types.Project, error) { + useAPISocket := false + for _, service := range project.Services { + if service.UseAPISocket { + useAPISocket = true + break + } + } + if !useAPISocket { + return project, nil + } + + socket := s.dockerCli.DockerEndpoint().Host + if !strings.HasPrefix(socket, "unix://") { + return nil, fmt.Errorf("use_api_socket can only be used with unix sockets: docker endpoint %s is incompatible", socket) + } + socket = strings.TrimPrefix(socket, "unix://") // should we confirm absolute path? + + creds, err := s.dockerCli.ConfigFile().GetAllCredentials() + if err != nil { + return nil, fmt.Errorf("resolving credentials failed: %w", err) + } + newConfig := &configfile.ConfigFile{ + AuthConfigs: creds, + } + var configBuf bytes.Buffer + if err := newConfig.SaveToWriter(&configBuf); err != nil { + return nil, fmt.Errorf("saving creds for API socket: %w", err) + } + + project.Configs["#apisocket"] = types.ConfigObjConfig{ + Content: configBuf.String(), + } + + for name, service := range project.Services { + service.Volumes = append(service.Volumes, types.ServiceVolumeConfig{ + Type: types.VolumeTypeBind, + Source: socket, + Target: "/var/run/docker.sock", + }) + + _, envvarPresent := service.Environment["DOCKER_CONFIG"] + + // If the DOCKER_CONFIG env var is already present, we assume the client knows + // what they're doing and don't inject the creds. + if !envvarPresent { + // Set our special little location for the config file. + path := "/run/secrets/docker" + service.Environment["DOCKER_CONFIG"] = &path + } + + service.Configs = append(service.Configs, types.ServiceConfigObjConfig{ + Source: "#apisocket", + Target: "/run/secrets/docker/config.json", + }) + project.Services[name] = service + } + return project, nil +} diff --git a/pkg/compose/create.go b/pkg/compose/create.go index 01e8ca6ea50..bee76f7c58e 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -114,6 +114,13 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt "--remove-orphans flag to clean it up.", orphans.names()) } } + + // Temporary implementation of use_api_socket until we get actual support inside docker engine + project, err = s.useAPISocket(project) + if err != nil { + return err + } + return newConvergence(options.Services, observedState, networks, volumes, s).apply(ctx, project, options) } diff --git a/pkg/compose/run.go b/pkg/compose/run.go index 7b4f5678fef..db8ed34dedb 100644 --- a/pkg/compose/run.go +++ b/pkg/compose/run.go @@ -59,6 +59,12 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types. } func (s *composeService) prepareRun(ctx context.Context, project *types.Project, opts api.RunOptions) (string, error) { + // Temporary implementation of use_api_socket until we get actual support inside docker engine + project, err := s.useAPISocket(project) + if err != nil { + return "", err + } + err = progress.Run(ctx, func(ctx context.Context) error { return s.startDependencies(ctx, project, opts) }, s.stdinfo()) @@ -171,7 +177,7 @@ func applyRunOptions(project *types.Project, service *types.ServiceConfig, opts func (s *composeService) startDependencies(ctx context.Context, project *types.Project, options api.RunOptions) error { var dependencies []string - for name, _ := range project.Services { + for name := range project.Services { if name != options.Service { dependencies = append(dependencies, name) }