Skip to content
Merged
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
8 changes: 7 additions & 1 deletion cmd/compose/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

"github.com/docker/compose/v2/cmd/formatter"
"github.com/docker/compose/v2/pkg/bridge"
"github.com/docker/compose/v2/pkg/compose"
)

func bridgeCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
Expand Down Expand Up @@ -62,7 +63,12 @@ func convertCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
}

func runConvert(ctx context.Context, dockerCli command.Cli, p *ProjectOptions, opts bridge.ConvertOptions) error {
project, _, err := p.ToProject(ctx, dockerCli, nil)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

project, _, err := p.ToProject(ctx, dockerCli, backend, nil)
if err != nil {
return err
}
Expand Down
11 changes: 6 additions & 5 deletions cmd/compose/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,13 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Back
}

func runBuild(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts buildOptions, services []string) error {
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
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, nil, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution)
project, _, err := opts.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}
Expand All @@ -166,9 +171,5 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backendOptions *Backen
}
apiBuildOptions.Attestations = true

backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
if err != nil {
return err
}
return backend.Build(ctx, project, apiBuildOptions)
}
14 changes: 12 additions & 2 deletions cmd/compose/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ func noCompletion() validArgsFn {
func completeServiceNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
p.Offline = true
project, _, err := p.ToProject(cmd.Context(), dockerCli, nil)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}

project, _, err := p.ToProject(cmd.Context(), dockerCli, backend, nil)
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}
Expand Down Expand Up @@ -79,7 +84,12 @@ func completeProjectNames(dockerCli command.Cli, backendOptions *BackendOptions)
func completeProfileNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
p.Offline = true
project, _, err := p.ToProject(cmd.Context(), dockerCli, nil)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}

project, _, err := p.ToProject(cmd.Context(), dockerCli, backend, nil)
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}
Expand Down
87 changes: 35 additions & 52 deletions cmd/compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,12 @@ func (o *ProjectOptions) WithProject(fn ProjectFunc, dockerCli command.Cli) func
// 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, args []string) error {
options := []cli.ProjectOptionsFn{
cli.WithResolvedPaths(true),
cli.WithoutEnvironmentResolution,
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

project, metrics, err := o.ToProject(ctx, dockerCli, args, options...)
project, metrics, err := o.ToProject(ctx, dockerCli, backend, args, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}
Expand Down Expand Up @@ -236,7 +236,12 @@ func (o *ProjectOptions) projectOrName(ctx context.Context, dockerCli command.Cl
name := o.ProjectName
var project *types.Project
if len(o.ConfigPaths) > 0 || o.ProjectName == "" {
p, _, err := o.ToProject(ctx, dockerCli, services, cli.WithDiscardEnvFile, cli.WithoutEnvironmentResolution)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return nil, "", err
}

p, _, err := o.ToProject(ctx, dockerCli, backend, services, cli.WithDiscardEnvFile, cli.WithoutEnvironmentResolution)
if err != nil {
envProjectName := os.Getenv(ComposeProjectName)
if envProjectName != "" {
Expand All @@ -260,7 +265,12 @@ func (o *ProjectOptions) toProjectName(ctx context.Context, dockerCli command.Cl
return envProjectName, nil
}

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

project, _, err := o.ToProject(ctx, dockerCli, backend, nil)
if err != nil {
return "", err
}
Expand All @@ -285,19 +295,14 @@ func (o *ProjectOptions) ToModel(ctx context.Context, dockerCli command.Cli, ser
return options.LoadModel(ctx)
}

func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, tracing.Metrics, error) { //nolint:gocyclo
// ToProject loads a Compose project using the LoadProject API.
// Accepts optional cli.ProjectOptionsFn to control loader behavior.
func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, backend api.Compose, services []string, po ...cli.ProjectOptionsFn) (*types.Project, tracing.Metrics, error) {
var metrics tracing.Metrics
remotes := o.remoteLoaders(dockerCli)
for _, r := range remotes {
po = append(po, cli.WithResourceLoader(r))
}

options, err := o.toProjectOptions(po...)
if err != nil {
return nil, metrics, err
}

options.WithListeners(func(event string, metadata map[string]any) {
// Setup metrics listener to collect project data
metricsListener := func(event string, metadata map[string]any) {
switch event {
case "extends":
metrics.CountExtends++
Expand All @@ -318,50 +323,28 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
}
}
}
})

if o.Compatibility || utils.StringToBool(options.Environment[ComposeCompatibility]) {
api.Separator = "_"
}

project, err := options.LoadProject(ctx)
if err != nil {
return nil, metrics, err
}

if project.Name == "" {
return nil, metrics, errors.New("project name can't be empty. Use `--project-name` to set a valid name")
loadOpts := api.ProjectLoadOptions{
ProjectName: o.ProjectName,
ConfigPaths: o.ConfigPaths,
WorkingDir: o.ProjectDir,
EnvFiles: o.EnvFiles,
Profiles: o.Profiles,
Services: services,
Offline: o.Offline,
All: o.All,
Compatibility: o.Compatibility,
ProjectOptionsFns: po,
LoadListeners: []api.LoadListener{metricsListener},
}

project, err = project.WithServicesEnabled(services...)
project, err := backend.LoadProject(ctx, loadOpts)
if err != nil {
return nil, metrics, err
}

for name, s := range project.Services {
s.CustomLabels = map[string]string{
api.ProjectLabel: project.Name,
api.ServiceLabel: name,
api.VersionLabel: api.ComposeVersion,
api.WorkingDirLabel: project.WorkingDir,
api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","),
api.OneoffLabel: "False", // default, will be overridden by `run` command
}
if len(o.EnvFiles) != 0 {
s.CustomLabels[api.EnvironmentFileLabel] = strings.Join(o.EnvFiles, ",")
}
project.Services[name] = s
}

project, err = project.WithSelectedServices(services)
if err != nil {
return nil, tracing.Metrics{}, err
}

if !o.All {
project = project.WithoutUnnecessaryResources()
}
return project, metrics, err
return project, metrics, nil
}

func (o *ProjectOptions) remoteLoaders(dockerCli command.Cli) []loader.ResourceLoader {
Expand Down
81 changes: 66 additions & 15 deletions cmd/compose/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,26 +61,30 @@ type configOptions struct {
lockImageDigests bool
}

func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
po = append(po, o.ToProjectOptions()...)
project, _, err := o.ProjectOptions.ToProject(ctx, dockerCli, services, po...)
func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, backend api.Compose, services []string) (*types.Project, error) {
project, _, err := o.ProjectOptions.ToProject(ctx, dockerCli, backend, services, o.toProjectOptionsFns()...)
return project, err
}

func (o *configOptions) ToModel(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (map[string]any, error) {
po = append(po, o.ToProjectOptions()...)
po = append(po, o.toProjectOptionsFns()...)
return o.ProjectOptions.ToModel(ctx, dockerCli, services, po...)
}

func (o *configOptions) ToProjectOptions() []cli.ProjectOptionsFn {
return []cli.ProjectOptionsFn{
// toProjectOptionsFns converts config options to cli.ProjectOptionsFn
func (o *configOptions) toProjectOptionsFns() []cli.ProjectOptionsFn {
fns := []cli.ProjectOptionsFn{
cli.WithInterpolation(!o.noInterpolate),
cli.WithResolvedPaths(!o.noResolvePath),
cli.WithNormalization(!o.noNormalize),
cli.WithConsistency(!o.noConsistency),
cli.WithDefaultProfiles(o.Profiles...),
cli.WithDiscardEnvFile,
}
if o.noResolveEnv {
fns = append(fns, cli.WithoutEnvironmentResolution)
}
return fns
}

func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
Expand Down Expand Up @@ -197,7 +201,12 @@ func runConfig(ctx context.Context, dockerCli command.Cli, opts configOptions, s
}

func runConfigInterpolate(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) ([]byte, error) {
project, err := opts.ToProject(ctx, dockerCli, services)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return nil, err
}

project, err := opts.ToProject(ctx, dockerCli, backend, services)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -353,7 +362,12 @@ func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions)
return nil
}

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

project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}
Expand All @@ -366,7 +380,12 @@ func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions)
}

func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}
Expand All @@ -377,7 +396,12 @@ func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions)
}

func runNetworks(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}
Expand All @@ -388,7 +412,12 @@ func runNetworks(ctx context.Context, dockerCli command.Cli, opts configOptions)
}

func runModels(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}
Expand All @@ -405,7 +434,13 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
if opts.hash != "*" {
services = append(services, strings.Split(opts.hash, ",")...)
}
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)

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

project, _, err := opts.ProjectOptions.ToProject(ctx, dockerCli, backend, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}
Expand Down Expand Up @@ -440,7 +475,13 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err

func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
set := map[string]struct{}{}
project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)

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

project, err := opts.ToProject(ctx, dockerCli, backend, services)
if err != nil {
return err
}
Expand All @@ -461,7 +502,12 @@ func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions,
}

func runConfigImages(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

project, err := opts.ToProject(ctx, dockerCli, backend, services)
if err != nil {
return err
}
Expand Down Expand Up @@ -498,7 +544,12 @@ func runVariables(ctx context.Context, dockerCli command.Cli, opts configOptions
}

func runEnvironment(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
project, err := opts.ToProject(ctx, dockerCli, services)
backend, err := compose.NewComposeService(dockerCli)
if err != nil {
return err
}

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