Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/compose/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func attachCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Bac
}

func runAttach(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts attachOpts) error {
projectName, err := opts.toProjectName(ctx, dockerCli)
dockerCli, projectName, err := opts.toProjectName(ctx, dockerCli)
if err != nil {
return err
}
Expand Down
15 changes: 13 additions & 2 deletions cmd/compose/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,24 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backendOptions *Backen
if opts.print {
backendOptions.Add(compose.WithEventProcessor(display.Quiet()))
}
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
// Load project first to detect x-context extension
tempBackend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

opts.All = true // do not drop resources as build may involve some dependencies by additional_contexts
project, _, err := opts.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
project, _, err := opts.ToProject(ctx, dockerCli, tempBackend, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compose/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func commitCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Bac
}

func runCommit(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, options commitOptions) error {
projectName, err := options.toProjectName(ctx, dockerCli)
dockerCli, projectName, err := options.toProjectName(ctx, dockerCli)
if err != nil {
return err
}
Expand Down
69 changes: 63 additions & 6 deletions cmd/compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
dockercli "github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/metadata"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/flags"
"github.com/docker/cli/pkg/kvfile"
"github.com/morikuni/aec"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -164,6 +165,49 @@ func (o *ProjectOptions) WithProject(fn ProjectFunc, dockerCli command.Cli) func
})
}

// dockerCliKey is a context key used to propagate a context-switched Docker CLI through the call chain.
type dockerCliKey struct{}

// switchDockerContextFromProject checks for an "x-context" extension in the compose project and returns
// a new DockerCli initialized for that context if it differs from the current one.
// If no switch is needed, it returns the original dockerCli unchanged.
func switchDockerContextFromProject(dockerCli command.Cli, project *types.Project) (command.Cli, error) {
if project == nil {
return dockerCli, nil
}
xctxVal, ok := project.Extensions["x-context"]
if !ok {
return dockerCli, nil
}
contextName, ok := xctxVal.(string)
if !ok || contextName == "" || contextName == dockerCli.CurrentContext() {
return dockerCli, nil
}
newCli, err := command.NewDockerCli(
command.WithInputStream(dockerCli.In()),
command.WithOutputStream(dockerCli.Out()),
command.WithErrorStream(dockerCli.Err()),
)
if err != nil {
return nil, fmt.Errorf("failed to create docker client for context %q: %w", contextName, err)
}
clientOpts := flags.NewClientOptions()
clientOpts.Context = contextName
if err := newCli.Initialize(clientOpts); err != nil {
return nil, fmt.Errorf("failed to initialize docker client for context %q: %w", contextName, err)
}
return newCli, nil
}

// getDockerCli returns the Docker CLI stored in the context (set by WithServices when x-context is
// specified), falling back to defaultCli when none is stored.
func getDockerCli(ctx context.Context, defaultCli command.Cli) command.Cli {
if cli, ok := ctx.Value(dockerCliKey{}).(command.Cli); ok {
return cli
}
return defaultCli
}

// WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services
func (o *ProjectOptions) WithServices(dockerCli command.Cli, fn ProjectServicesFunc) func(cmd *cobra.Command, args []string) error {
return Adapt(func(ctx context.Context, services []string) error {
Expand All @@ -177,7 +221,14 @@ func (o *ProjectOptions) WithServices(dockerCli command.Cli, fn ProjectServicesF
return err
}

// Apply x-context if specified in the compose file
dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

ctx = context.WithValue(ctx, tracing.MetricsKey{}, metrics)
ctx = context.WithValue(ctx, dockerCliKey{}, dockerCli)

project, err = project.WithServicesEnvironmentResolved(true)
if err != nil {
Expand Down Expand Up @@ -265,26 +316,32 @@ func (o *ProjectOptions) projectOrName(ctx context.Context, dockerCli command.Cl
return project, name, nil
}

func (o *ProjectOptions) toProjectName(ctx context.Context, dockerCli command.Cli) (string, error) {
func (o *ProjectOptions) toProjectName(ctx context.Context, dockerCli command.Cli) (command.Cli, string, error) {
if o.ProjectName != "" {
return o.ProjectName, nil
return dockerCli, o.ProjectName, nil
}

envProjectName := os.Getenv(ComposeProjectName)
if envProjectName != "" {
return envProjectName, nil
return dockerCli, envProjectName, nil
}

backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return "", err
return nil, "", err
}

project, _, err := o.ToProject(ctx, dockerCli, backend, nil)
if err != nil {
return "", err
return nil, "", err
}
return project.Name, nil

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return nil, "", err
}

return dockerCli, project.Name, nil
}

func (o *ProjectOptions) ToModel(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (map[string]any, error) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/compose/cp.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func copyCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backe
}

func runCopy(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts copyOptions) error {
name, err := opts.toProjectName(ctx, dockerCli)
dockerCli, name, err := opts.toProjectName(ctx, dockerCli)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compose/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func createCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Bac
return nil
}),
RunE: p.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []string) error {
return runCreate(ctx, dockerCli, backendOptions, opts, buildOpts, project, services)
return runCreate(ctx, getDockerCli(ctx, dockerCli), backendOptions, opts, buildOpts, project, services)
}),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
Expand Down
5 changes: 5 additions & 0 deletions cmd/compose/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ func runDown(ctx context.Context, dockerCli command.Cli, backendOptions *Backend
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

var timeout *time.Duration
if opts.timeChanged {
timeoutValue := time.Duration(opts.timeout) * time.Second
Expand Down
2 changes: 1 addition & 1 deletion cmd/compose/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func eventsCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Bac
}

func runEvents(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts eventsOpts, services []string) error {
name, err := opts.toProjectName(ctx, dockerCli)
dockerCli, name, err := opts.toProjectName(ctx, dockerCli)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compose/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func execCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backe
}

func runExec(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts execOpts) error {
projectName, err := opts.toProjectName(ctx, dockerCli)
dockerCli, projectName, err := opts.toProjectName(ctx, dockerCli)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compose/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func exportCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Bac
}

func runExport(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, options exportOptions) error {
projectName, err := options.toProjectName(ctx, dockerCli)
dockerCli, projectName, err := options.toProjectName(ctx, dockerCli)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compose/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func imagesCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Bac
}

func runImages(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts imageOptions, services []string) error {
projectName, err := opts.toProjectName(ctx, dockerCli)
dockerCli, projectName, err := opts.toProjectName(ctx, dockerCli)
if err != nil {
return err
}
Expand Down
10 changes: 10 additions & 0 deletions cmd/compose/kill.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ func runKill(ctx context.Context, dockerCli command.Cli, backendOptions *Backend
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions cmd/compose/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ func runLogs(ctx context.Context, dockerCli command.Cli, backendOptions *Backend
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

// exclude services configured to ignore output (attach: false), until explicitly selected
if project != nil && len(services) == 0 {
for n, service := range project.Services {
Expand Down
10 changes: 10 additions & 0 deletions cmd/compose/pause.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ func runPause(ctx context.Context, dockerCli command.Cli, backendOptions *Backen
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
if err != nil {
return err
Expand Down Expand Up @@ -86,6 +91,11 @@ func runUnPause(ctx context.Context, dockerCli command.Cli, backendOptions *Back
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion cmd/compose/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func portCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backe
}

func runPort(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts portOptions, service string) error {
projectName, err := opts.toProjectName(ctx, dockerCli)
dockerCli, projectName, err := opts.toProjectName(ctx, dockerCli)
if err != nil {
return err
}
Expand Down
15 changes: 15 additions & 0 deletions cmd/compose/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ func runPs(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOp
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

if project != nil {
names := project.ServiceNames()
if len(services) > 0 {
Expand Down
15 changes: 13 additions & 2 deletions cmd/compose/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,23 @@ func (opts pullOptions) apply(project *types.Project, services []string) (*types
}

func runPull(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts pullOptions, services []string) error {
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
// Load project first to detect x-context extension
tempBackend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

project, _, err := opts.ToProject(ctx, dockerCli, tempBackend, services, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}

project, _, err := opts.ToProject(ctx, dockerCli, backend, services, cli.WithoutEnvironmentResolution)
dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
if err != nil {
return err
}
Expand Down
15 changes: 13 additions & 2 deletions cmd/compose/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,23 @@ func pushCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backe
}

func runPush(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts pushOptions, services []string) error {
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
// Load project first to detect x-context extension
tempBackend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

project, _, err := opts.ToProject(ctx, dockerCli, tempBackend, services)
if err != nil {
return err
}

project, _, err := opts.ToProject(ctx, dockerCli, backend, services)
dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
if err != nil {
return err
}
Expand Down
10 changes: 10 additions & 0 deletions cmd/compose/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ func runRemove(ctx context.Context, dockerCli command.Cli, backendOptions *Backe
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions cmd/compose/restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ func runRestart(ctx context.Context, dockerCli command.Cli, backendOptions *Back
return err
}

dockerCli, err = switchDockerContextFromProject(dockerCli, project)
if err != nil {
return err
}

if project != nil && len(services) > 0 {
project, err = project.WithServicesEnabled(services...)
if err != nil {
Expand Down
Loading