pull OCI remote resource

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2023-08-30 10:15:35 +02:00 committed by Nicolas De loof
parent c9d54f09cf
commit e0f39ebbef
39 changed files with 447 additions and 255 deletions

View File

@ -15,12 +15,13 @@
package compose
import (
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/spf13/cobra"
)
// alphaCommand groups all experimental subcommands
func alphaCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func alphaCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
cmd := &cobra.Command{
Short: "Experimental commands",
Use: "alpha [COMMAND]",
@ -30,9 +31,9 @@ func alphaCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
},
}
cmd.AddCommand(
watchCommand(p, backend),
vizCommand(p, backend),
publishCommand(p, backend),
watchCommand(p, dockerCli, backend),
vizCommand(p, dockerCli, backend),
publishCommand(p, dockerCli, backend),
)
return cmd
}

View File

@ -26,6 +26,7 @@ import (
"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
buildx "github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
cliopts "github.com/docker/cli/opts"
ui "github.com/docker/compose/v2/pkg/progress"
"github.com/spf13/cobra"
@ -72,7 +73,7 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions,
}, nil
}
func buildCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := buildOptions{
ProjectOptions: p,
}
@ -97,9 +98,9 @@ func buildCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
if cmd.Flags().Changed("progress") && opts.ssh == "" {
fmt.Fprint(os.Stderr, "--progress is a global compose flag, better use `docker compose --progress xx build ...")
}
return runBuild(ctx, backend, opts, args)
return runBuild(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
cmd.Flags().BoolVar(&opts.push, "push", false, "Push service images.")
cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT")
@ -123,8 +124,8 @@ func buildCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return cmd
}
func runBuild(ctx context.Context, backend api.Service, opts buildOptions, services []string) error {
project, err := opts.ToProject(services, cli.WithResolvedPaths(true))
func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, opts buildOptions, services []string) error {
project, err := opts.ToProject(dockerCli, services, cli.WithResolvedPaths(true))
if err != nil {
return err
}

View File

@ -20,6 +20,7 @@ import (
"sort"
"strings"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/spf13/cobra"
)
@ -33,9 +34,10 @@ func noCompletion() validArgsFn {
}
}
func completeServiceNames(p *ProjectOptions) validArgsFn {
func completeServiceNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
project, err := p.ToProject(nil)
p.Offline = true
project, err := p.ToProject(dockerCli, nil)
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}
@ -67,9 +69,10 @@ func completeProjectNames(backend api.Service) func(cmd *cobra.Command, args []s
}
}
func completeProfileNames(p *ProjectOptions) validArgsFn {
func completeProfileNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
project, err := p.ToProject(nil)
p.Offline = true
project, err := p.ToProject(dockerCli, nil)
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}

View File

@ -26,18 +26,16 @@ import (
"strings"
"syscall"
buildx "github.com/docker/buildx/util/progress"
"github.com/compose-spec/compose-go/dotenv"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/remote"
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/dotenv"
"github.com/compose-spec/compose-go/types"
composegoutils "github.com/compose-spec/compose-go/utils"
"github.com/docker/buildx/util/logutil"
buildx "github.com/docker/buildx/util/progress"
dockercli "github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/remote"
"github.com/morikuni/aec"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -119,6 +117,7 @@ type ProjectOptions struct {
EnvFiles []string
Compatibility bool
Progress string
Offline bool
}
// ProjectFunc does stuff within a types.Project
@ -128,14 +127,14 @@ type ProjectFunc func(ctx context.Context, project *types.Project) error
type ProjectServicesFunc func(ctx context.Context, project *types.Project, services []string) error
// WithProject creates a cobra run command from a ProjectFunc based on configured project options and selected services
func (o *ProjectOptions) WithProject(fn ProjectFunc) func(cmd *cobra.Command, args []string) error {
return o.WithServices(func(ctx context.Context, project *types.Project, services []string) error {
func (o *ProjectOptions) WithProject(fn ProjectFunc, dockerCli command.Cli) func(cmd *cobra.Command, args []string) error {
return o.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []string) error {
return fn(ctx, project)
})
}
// WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services
func (o *ProjectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Command, args []string) error {
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),
@ -143,19 +142,7 @@ func (o *ProjectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Co
cli.WithContext(ctx),
}
enabled, err := remote.GitRemoteLoaderEnabled()
if err != nil {
return err
}
if enabled {
git, err := remote.NewGitRemoteLoader()
if err != nil {
return err
}
options = append(options, cli.WithResourceLoader(git))
}
project, err := o.ToProject(args, options...)
project, err := o.ToProject(dockerCli, args, options...)
if err != nil {
return err
}
@ -176,11 +163,11 @@ func (o *ProjectOptions) addProjectFlags(f *pflag.FlagSet) {
_ = f.MarkHidden("workdir")
}
func (o *ProjectOptions) projectOrName(services ...string) (*types.Project, string, error) {
func (o *ProjectOptions) projectOrName(dockerCli command.Cli, services ...string) (*types.Project, string, error) {
name := o.ProjectName
var project *types.Project
if len(o.ConfigPaths) > 0 || o.ProjectName == "" {
p, err := o.ToProject(services, cli.WithDiscardEnvFile)
p, err := o.ToProject(dockerCli, services, cli.WithDiscardEnvFile)
if err != nil {
envProjectName := os.Getenv(ComposeProjectName)
if envProjectName != "" {
@ -194,7 +181,7 @@ func (o *ProjectOptions) projectOrName(services ...string) (*types.Project, stri
return project, name, nil
}
func (o *ProjectOptions) toProjectName() (string, error) {
func (o *ProjectOptions) toProjectName(dockerCli command.Cli) (string, error) {
if o.ProjectName != "" {
return o.ProjectName, nil
}
@ -204,14 +191,22 @@ func (o *ProjectOptions) toProjectName() (string, error) {
return envProjectName, nil
}
project, err := o.ToProject(nil)
project, err := o.ToProject(dockerCli, nil)
if err != nil {
return "", err
}
return project.Name, nil
}
func (o *ProjectOptions) ToProject(services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
func (o *ProjectOptions) ToProject(dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
if !o.Offline {
var err error
po, err = o.configureRemoteLoaders(dockerCli, po)
if err != nil {
return nil, err
}
}
options, err := o.toProjectOptions(po...)
if err != nil {
return nil, compose.WrapComposeError(err)
@ -256,6 +251,33 @@ func (o *ProjectOptions) ToProject(services []string, po ...cli.ProjectOptionsFn
return project, err
}
func (o *ProjectOptions) configureRemoteLoaders(dockerCli command.Cli, po []cli.ProjectOptionsFn) ([]cli.ProjectOptionsFn, error) {
enabled, err := remote.GitRemoteLoaderEnabled()
if err != nil {
return nil, err
}
if enabled {
git, err := remote.NewGitRemoteLoader(o.Offline)
if err != nil {
return nil, err
}
po = append(po, cli.WithResourceLoader(git))
}
enabled, err = remote.OCIRemoteLoaderEnabled()
if err != nil {
return nil, err
}
if enabled {
git, err := remote.NewOCIRemoteLoader(dockerCli, o.Offline)
if err != nil {
return nil, err
}
po = append(po, cli.WithResourceLoader(git))
}
return po, nil
}
func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) {
return cli.NewProjectOptions(o.ConfigPaths,
append(po,
@ -429,32 +451,32 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
c.AddCommand(
upCommand(&opts, dockerCli, backend),
downCommand(&opts, backend),
startCommand(&opts, backend),
restartCommand(&opts, backend),
stopCommand(&opts, backend),
downCommand(&opts, dockerCli, backend),
startCommand(&opts, dockerCli, backend),
restartCommand(&opts, dockerCli, backend),
stopCommand(&opts, dockerCli, backend),
psCommand(&opts, dockerCli, backend),
listCommand(dockerCli, backend),
logsCommand(&opts, dockerCli, backend),
configCommand(&opts, dockerCli, backend),
killCommand(&opts, backend),
killCommand(&opts, dockerCli, backend),
runCommand(&opts, dockerCli, backend),
removeCommand(&opts, backend),
removeCommand(&opts, dockerCli, backend),
execCommand(&opts, dockerCli, backend),
pauseCommand(&opts, backend),
unpauseCommand(&opts, backend),
pauseCommand(&opts, dockerCli, backend),
unpauseCommand(&opts, dockerCli, backend),
topCommand(&opts, dockerCli, backend),
eventsCommand(&opts, dockerCli, backend),
portCommand(&opts, dockerCli, backend),
imagesCommand(&opts, dockerCli, backend),
versionCommand(dockerCli),
buildCommand(&opts, backend),
pushCommand(&opts, backend),
pullCommand(&opts, backend),
createCommand(&opts, backend),
copyCommand(&opts, backend),
waitCommand(&opts, backend),
alphaCommand(&opts, backend),
buildCommand(&opts, dockerCli, backend),
pushCommand(&opts, dockerCli, backend),
pullCommand(&opts, dockerCli, backend),
createCommand(&opts, dockerCli, backend),
copyCommand(&opts, dockerCli, backend),
waitCommand(&opts, dockerCli, backend),
alphaCommand(&opts, dockerCli, backend),
)
c.Flags().SetInterspersed(false)
@ -477,7 +499,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
)
c.RegisterFlagCompletionFunc( //nolint:errcheck
"profile",
completeProfileNames(&opts),
completeProfileNames(dockerCli, &opts),
)
c.Flags().StringVar(&ansi, "ansi", "auto", `Control when to print ANSI control characters ("never"|"always"|"auto")`)

View File

@ -26,7 +26,7 @@ import (
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/types"
"github.com/docker/compose/v2/pkg/remote"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -50,24 +50,18 @@ type configOptions struct {
noConsistency bool
}
func (o *configOptions) ToProject(ctx context.Context, services []string) (*types.Project, error) {
git, err := remote.NewGitRemoteLoader()
if err != nil {
return nil, err
}
return o.ProjectOptions.ToProject(services,
func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string) (*types.Project, error) {
return o.ProjectOptions.ToProject(dockerCli, services,
cli.WithInterpolation(!o.noInterpolate),
cli.WithResolvedPaths(!o.noResolvePath),
cli.WithNormalization(!o.noNormalize),
cli.WithConsistency(!o.noConsistency),
cli.WithDefaultProfiles(o.Profiles...),
cli.WithDiscardEnvFile,
cli.WithContext(ctx),
cli.WithResourceLoader(git))
cli.WithContext(ctx))
}
func configCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
func configCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := configOptions{
ProjectOptions: p,
}
@ -90,24 +84,24 @@ func configCommand(p *ProjectOptions, streams api.Streams, backend api.Service)
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
if opts.services {
return runServices(ctx, streams, opts)
return runServices(ctx, dockerCli, opts)
}
if opts.volumes {
return runVolumes(ctx, streams, opts)
return runVolumes(ctx, dockerCli, opts)
}
if opts.hash != "" {
return runHash(ctx, streams, opts)
return runHash(ctx, dockerCli, opts)
}
if opts.profiles {
return runProfiles(ctx, streams, opts, args)
return runProfiles(ctx, dockerCli, opts, args)
}
if opts.images {
return runConfigImages(ctx, streams, opts, args)
return runConfigImages(ctx, dockerCli, opts, args)
}
return runConfig(ctx, streams, backend, opts, args)
return runConfig(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := cmd.Flags()
flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]")
@ -128,9 +122,9 @@ func configCommand(p *ProjectOptions, streams api.Streams, backend api.Service)
return cmd
}
func runConfig(ctx context.Context, streams api.Streams, backend api.Service, opts configOptions, services []string) error {
func runConfig(ctx context.Context, dockerCli command.Cli, backend api.Service, opts configOptions, services []string) error {
var content []byte
project, err := opts.ToProject(ctx, services)
project, err := opts.ToProject(ctx, dockerCli, services)
if err != nil {
return err
}
@ -155,38 +149,38 @@ func runConfig(ctx context.Context, streams api.Streams, backend api.Service, op
if opts.Output != "" && len(content) > 0 {
return os.WriteFile(opts.Output, content, 0o666)
}
_, err = fmt.Fprint(streams.Out(), string(content))
_, err = fmt.Fprint(dockerCli.Out(), string(content))
return err
}
func runServices(ctx context.Context, streams api.Streams, opts configOptions) error {
project, err := opts.ToProject(ctx, nil)
func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
project, err := opts.ToProject(ctx, dockerCli, nil)
if err != nil {
return err
}
return project.WithServices(project.ServiceNames(), func(s types.ServiceConfig) error {
fmt.Fprintln(streams.Out(), s.Name)
fmt.Fprintln(dockerCli.Out(), s.Name)
return nil
})
}
func runVolumes(ctx context.Context, streams api.Streams, opts configOptions) error {
project, err := opts.ToProject(ctx, nil)
func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
project, err := opts.ToProject(ctx, dockerCli, nil)
if err != nil {
return err
}
for n := range project.Volumes {
fmt.Fprintln(streams.Out(), n)
fmt.Fprintln(dockerCli.Out(), n)
}
return nil
}
func runHash(ctx context.Context, streams api.Streams, opts configOptions) error {
func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
var services []string
if opts.hash != "*" {
services = append(services, strings.Split(opts.hash, ",")...)
}
project, err := opts.ToProject(ctx, nil)
project, err := opts.ToProject(ctx, dockerCli, nil)
if err != nil {
return err
}
@ -208,14 +202,14 @@ func runHash(ctx context.Context, streams api.Streams, opts configOptions) error
if err != nil {
return err
}
fmt.Fprintf(streams.Out(), "%s %s\n", s.Name, hash)
fmt.Fprintf(dockerCli.Out(), "%s %s\n", s.Name, hash)
}
return nil
}
func runProfiles(ctx context.Context, streams api.Streams, opts configOptions, services []string) error {
func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
set := map[string]struct{}{}
project, err := opts.ToProject(ctx, services)
project, err := opts.ToProject(ctx, dockerCli, services)
if err != nil {
return err
}
@ -230,18 +224,18 @@ func runProfiles(ctx context.Context, streams api.Streams, opts configOptions, s
}
sort.Strings(profiles)
for _, p := range profiles {
fmt.Fprintln(streams.Out(), p)
fmt.Fprintln(dockerCli.Out(), p)
}
return nil
}
func runConfigImages(ctx context.Context, streams api.Streams, opts configOptions, services []string) error {
project, err := opts.ToProject(ctx, services)
func runConfigImages(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
project, err := opts.ToProject(ctx, dockerCli, services)
if err != nil {
return err
}
for _, s := range project.Services {
fmt.Fprintln(streams.Out(), api.GetImageNameOrDefault(s, project.Name))
fmt.Fprintln(dockerCli.Out(), api.GetImageNameOrDefault(s, project.Name))
}
return nil
}

View File

@ -21,6 +21,7 @@ import (
"errors"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -37,7 +38,7 @@ type copyOptions struct {
copyUIDGID bool
}
func copyCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func copyCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := copyOptions{
ProjectOptions: p,
}
@ -58,9 +59,9 @@ func copyCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
RunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
opts.source = args[0]
opts.destination = args[1]
return runCopy(ctx, backend, opts)
return runCopy(ctx, dockerCli, backend, opts)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := copyCmd.Flags()
@ -74,8 +75,8 @@ func copyCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return copyCmd
}
func runCopy(ctx context.Context, backend api.Service, opts copyOptions) error {
name, err := opts.toProjectName()
func runCopy(ctx context.Context, dockerCli command.Cli, backend api.Service, opts copyOptions) error {
name, err := opts.toProjectName(dockerCli)
if err != nil {
return err
}

View File

@ -24,6 +24,7 @@ import (
"time"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -46,7 +47,7 @@ type createOptions struct {
scale []string
}
func createCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := createOptions{}
cmd := &cobra.Command{
Use: "create [OPTIONS] [SERVICE...]",
@ -74,8 +75,8 @@ func createCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
Timeout: opts.GetTimeout(),
QuietPull: false,
})
}),
ValidArgsFunction: completeServiceNames(p),
}, dockerCli),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := cmd.Flags()
flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.")

View File

@ -22,6 +22,7 @@ import (
"os"
"time"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/utils"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@ -39,7 +40,7 @@ type downOptions struct {
images string
}
func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func downCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := downOptions{
ProjectOptions: p,
}
@ -56,7 +57,7 @@ func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
return runDown(ctx, backend, opts, args)
return runDown(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: noCompletion(),
}
@ -76,8 +77,8 @@ func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return downCmd
}
func runDown(ctx context.Context, backend api.Service, opts downOptions, services []string) error {
project, name, err := opts.projectOrName()
func runDown(ctx context.Context, dockerCli command.Cli, backend api.Service, opts downOptions, services []string) error {
project, name, err := opts.projectOrName(dockerCli)
if err != nil {
return err
}

View File

@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/spf13/cobra"
@ -31,7 +32,7 @@ type eventsOpts struct {
json bool
}
func eventsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
func eventsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := eventsOpts{
composeOptions: &composeOptions{
ProjectOptions: p,
@ -41,17 +42,17 @@ func eventsCommand(p *ProjectOptions, streams api.Streams, backend api.Service)
Use: "events [OPTIONS] [SERVICE...]",
Short: "Receive real time events from containers.",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runEvents(ctx, streams, backend, opts, args)
return runEvents(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
cmd.Flags().BoolVar(&opts.json, "json", false, "Output events as a stream of json objects")
return cmd
}
func runEvents(ctx context.Context, streams api.Streams, backend api.Service, opts eventsOpts, services []string) error {
name, err := opts.toProjectName()
func runEvents(ctx context.Context, dockerCli command.Cli, backend api.Service, opts eventsOpts, services []string) error {
name, err := opts.toProjectName(dockerCli)
if err != nil {
return err
}
@ -71,9 +72,9 @@ func runEvents(ctx context.Context, streams api.Streams, backend api.Service, op
if err != nil {
return err
}
fmt.Fprintln(streams.Out(), string(marshal))
fmt.Fprintln(dockerCli.Out(), string(marshal))
} else {
fmt.Fprintln(streams.Out(), event)
fmt.Fprintln(dockerCli.Out(), event)
}
return nil
},

View File

@ -21,6 +21,7 @@ import (
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/compose"
"github.com/spf13/cobra"
@ -42,7 +43,7 @@ type execOpts struct {
interactive bool
}
func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := execOpts{
composeOptions: &composeOptions{
ProjectOptions: p,
@ -58,9 +59,9 @@ func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
return runExec(ctx, backend, opts)
return runExec(ctx, dockerCli, backend, opts)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
runCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: Run command in the background.")
@ -68,7 +69,7 @@ func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c
runCmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas")
runCmd.Flags().BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the process.")
runCmd.Flags().StringVarP(&opts.user, "user", "u", "", "Run the command as this user.")
runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", !streams.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.")
runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.")
runCmd.Flags().StringVarP(&opts.workingDir, "workdir", "w", "", "Path to workdir directory for this command.")
runCmd.Flags().BoolVarP(&opts.interactive, "interactive", "i", true, "Keep STDIN open even if not attached.")
@ -80,8 +81,8 @@ func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c
return runCmd
}
func runExec(ctx context.Context, backend api.Service, opts execOpts) error {
projectName, err := opts.toProjectName()
func runExec(ctx context.Context, dockerCli command.Cli, backend api.Service, opts execOpts) error {
projectName, err := opts.toProjectName(dockerCli)
if err != nil {
return err
}

View File

@ -23,6 +23,7 @@ import (
"sort"
"strings"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/go-units"
"github.com/spf13/cobra"
@ -38,7 +39,7 @@ type imageOptions struct {
Format string
}
func imagesCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
func imagesCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := imageOptions{
ProjectOptions: p,
}
@ -46,17 +47,17 @@ func imagesCommand(p *ProjectOptions, streams api.Streams, backend api.Service)
Use: "images [OPTIONS] [SERVICE...]",
Short: "List images used by the created containers",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runImages(ctx, streams, backend, opts, args)
return runImages(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
imgCmd.Flags().StringVar(&opts.Format, "format", "table", "Format the output. Values: [table | json].")
imgCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
return imgCmd
}
func runImages(ctx context.Context, streams api.Streams, backend api.Service, opts imageOptions, services []string) error {
projectName, err := opts.toProjectName()
func runImages(ctx context.Context, dockerCli command.Cli, backend api.Service, opts imageOptions, services []string) error {
projectName, err := opts.toProjectName(dockerCli)
if err != nil {
return err
}
@ -80,7 +81,7 @@ func runImages(ctx context.Context, streams api.Streams, backend api.Service, op
}
}
for _, img := range ids {
fmt.Fprintln(streams.Out(), img)
fmt.Fprintln(dockerCli.Out(), img)
}
return nil
}
@ -89,7 +90,7 @@ func runImages(ctx context.Context, streams api.Streams, backend api.Service, op
return images[i].ContainerName < images[j].ContainerName
})
return formatter.Print(images, opts.Format, streams.Out(),
return formatter.Print(images, opts.Format, dockerCli.Out(),
func(w io.Writer) {
for _, img := range images {
id := stringid.TruncateID(img.ID)

View File

@ -20,6 +20,7 @@ import (
"context"
"os"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -32,7 +33,7 @@ type killOptions struct {
signal string
}
func killCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func killCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := killOptions{
ProjectOptions: p,
}
@ -40,9 +41,9 @@ func killCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
Use: "kill [OPTIONS] [SERVICE...]",
Short: "Force stop service containers.",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runKill(ctx, backend, opts, args)
return runKill(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := cmd.Flags()
@ -53,8 +54,8 @@ func killCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return cmd
}
func runKill(ctx context.Context, backend api.Service, opts killOptions, services []string) error {
project, name, err := opts.projectOrName(services...)
func runKill(ctx context.Context, dockerCli command.Cli, backend api.Service, opts killOptions, services []string) error {
project, name, err := opts.projectOrName(dockerCli, services...)
if err != nil {
return err
}

View File

@ -22,6 +22,7 @@ import (
"io"
"strings"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/cmd/formatter"
"github.com/docker/cli/opts"
@ -37,13 +38,13 @@ type lsOptions struct {
Filter opts.FilterOpt
}
func listCommand(streams api.Streams, backend api.Service) *cobra.Command {
func listCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
lsOpts := lsOptions{Filter: opts.NewFilterOpt()}
lsCmd := &cobra.Command{
Use: "ls [OPTIONS]",
Short: "List running compose projects",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runList(ctx, streams, backend, lsOpts)
return runList(ctx, dockerCli, backend, lsOpts)
}),
Args: cobra.NoArgs,
ValidArgsFunction: noCompletion(),
@ -60,7 +61,7 @@ var acceptedListFilters = map[string]bool{
"name": true,
}
func runList(ctx context.Context, streams api.Streams, backend api.Service, lsOpts lsOptions) error {
func runList(ctx context.Context, dockerCli command.Cli, backend api.Service, lsOpts lsOptions) error {
filters := lsOpts.Filter.Value()
err := filters.Validate(acceptedListFilters)
if err != nil {
@ -73,7 +74,7 @@ func runList(ctx context.Context, streams api.Streams, backend api.Service, lsOp
}
if lsOpts.Quiet {
for _, s := range stackList {
fmt.Fprintln(streams.Out(), s.Name)
fmt.Fprintln(dockerCli.Out(), s.Name)
}
return nil
}
@ -90,7 +91,7 @@ func runList(ctx context.Context, streams api.Streams, backend api.Service, lsOp
}
view := viewFromStackList(stackList)
return formatter.Print(view, lsOpts.Format, streams.Out(), func(w io.Writer) {
return formatter.Print(view, lsOpts.Format, dockerCli.Out(), func(w io.Writer) {
for _, stack := range view {
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", stack.Name, stack.Status, stack.ConfigFiles)
}

View File

@ -19,6 +19,7 @@ package compose
import (
"context"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/cmd/formatter"
@ -37,7 +38,7 @@ type logsOptions struct {
timestamps bool
}
func logsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
func logsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := logsOptions{
ProjectOptions: p,
}
@ -45,9 +46,9 @@ func logsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c
Use: "logs [OPTIONS] [SERVICE...]",
Short: "View output from containers",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runLogs(ctx, streams, backend, opts, args)
return runLogs(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := logsCmd.Flags()
flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output.")
@ -60,12 +61,12 @@ func logsCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c
return logsCmd
}
func runLogs(ctx context.Context, streams api.Streams, backend api.Service, opts logsOptions, services []string) error {
project, name, err := opts.projectOrName(services...)
func runLogs(ctx context.Context, dockerCli command.Cli, backend api.Service, opts logsOptions, services []string) error {
project, name, err := opts.projectOrName(dockerCli, services...)
if err != nil {
return err
}
consumer := formatter.NewLogConsumer(ctx, streams.Out(), streams.Err(), !opts.noColor, !opts.noPrefix, false)
consumer := formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), !opts.noColor, !opts.noPrefix, false)
return backend.Logs(ctx, name, consumer, api.LogOptions{
Project: project,
Services: services,

View File

@ -19,6 +19,7 @@ package compose
import (
"context"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -28,7 +29,7 @@ type pauseOptions struct {
*ProjectOptions
}
func pauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func pauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := pauseOptions{
ProjectOptions: p,
}
@ -36,15 +37,15 @@ func pauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
Use: "pause [SERVICE...]",
Short: "Pause services",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runPause(ctx, backend, opts, args)
return runPause(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
return cmd
}
func runPause(ctx context.Context, backend api.Service, opts pauseOptions, services []string) error {
project, name, err := opts.projectOrName(services...)
func runPause(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pauseOptions, services []string) error {
project, name, err := opts.projectOrName(dockerCli, services...)
if err != nil {
return err
}
@ -59,7 +60,7 @@ type unpauseOptions struct {
*ProjectOptions
}
func unpauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func unpauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := unpauseOptions{
ProjectOptions: p,
}
@ -67,15 +68,15 @@ func unpauseCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
Use: "unpause [SERVICE...]",
Short: "Unpause services",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runUnPause(ctx, backend, opts, args)
return runUnPause(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
return cmd
}
func runUnPause(ctx context.Context, backend api.Service, opts unpauseOptions, services []string) error {
project, name, err := opts.projectOrName(services...)
func runUnPause(ctx context.Context, dockerCli command.Cli, backend api.Service, opts unpauseOptions, services []string) error {
project, name, err := opts.projectOrName(dockerCli, services...)
if err != nil {
return err
}

View File

@ -22,6 +22,7 @@ import (
"strconv"
"strings"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -34,7 +35,7 @@ type portOptions struct {
index int
}
func portCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
func portCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := portOptions{
ProjectOptions: p,
}
@ -52,17 +53,17 @@ func portCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
return runPort(ctx, streams, backend, opts, args[0])
return runPort(ctx, dockerCli, backend, opts, args[0])
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
cmd.Flags().StringVar(&opts.protocol, "protocol", "tcp", "tcp or udp")
cmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas")
return cmd
}
func runPort(ctx context.Context, streams api.Streams, backend api.Service, opts portOptions, service string) error {
projectName, err := opts.toProjectName()
func runPort(ctx context.Context, dockerCli command.Cli, backend api.Service, opts portOptions, service string) error {
projectName, err := opts.toProjectName(dockerCli)
if err != nil {
return err
}
@ -74,6 +75,6 @@ func runPort(ctx context.Context, streams api.Streams, backend api.Service, opts
return err
}
fmt.Fprintf(streams.Out(), "%s:%d\n", ip, port)
fmt.Fprintf(dockerCli.Out(), "%s:%d\n", ip, port)
return nil
}

View File

@ -75,7 +75,7 @@ func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
RunE: Adapt(func(ctx context.Context, args []string) error {
return runPs(ctx, dockerCli, backend, args, opts)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := psCmd.Flags()
flags.StringVar(&opts.Format, "format", "table", cliflags.FormatHelp)
@ -88,7 +88,7 @@ func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
}
func runPs(ctx context.Context, dockerCli command.Cli, backend api.Service, services []string, opts psOptions) error {
project, name, err := opts.projectOrName(services...)
project, name, err := opts.projectOrName(dockerCli, services...)
if err != nil {
return err
}

View File

@ -19,12 +19,13 @@ package compose
import (
"context"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
)
func publishCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := pushOptions{
ProjectOptions: p,
}
@ -32,18 +33,18 @@ func publishCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
Use: "publish [OPTIONS] [REPOSITORY]",
Short: "Publish compose application",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runPublish(ctx, backend, opts, args[0])
return runPublish(ctx, dockerCli, backend, opts, args[0])
}),
Args: cobra.ExactArgs(1),
}
return publishCmd
}
func runPublish(ctx context.Context, backend api.Service, opts pushOptions, repository string) error {
project, err := opts.ToProject(nil)
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pushOptions, repository string) error {
project, err := opts.ToProject(dockerCli, nil)
if err != nil {
return err
}
return backend.Publish(ctx, project, repository)
return backend.Publish(ctx, project, repository, api.PublishOptions{})
}

View File

@ -22,6 +22,7 @@ import (
"os"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/command"
"github.com/morikuni/aec"
"github.com/spf13/cobra"
@ -39,7 +40,7 @@ type pullOptions struct {
noBuildable bool
}
func pullCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := pullOptions{
ProjectOptions: p,
}
@ -53,9 +54,9 @@ func pullCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
return runPull(ctx, backend, opts, args)
return runPull(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := cmd.Flags()
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information.")
@ -69,8 +70,8 @@ func pullCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return cmd
}
func runPull(ctx context.Context, backend api.Service, opts pullOptions, services []string) error {
project, err := opts.ToProject(services)
func runPull(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pullOptions, services []string) error {
project, err := opts.ToProject(dockerCli, services)
if err != nil {
return err
}

View File

@ -20,6 +20,7 @@ import (
"context"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -33,7 +34,7 @@ type pushOptions struct {
Quiet bool
}
func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func pushCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := pushOptions{
ProjectOptions: p,
}
@ -41,9 +42,9 @@ func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
Use: "push [OPTIONS] [SERVICE...]",
Short: "Push service images",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runPush(ctx, backend, opts, args)
return runPush(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures")
pushCmd.Flags().BoolVar(&opts.IncludeDeps, "include-deps", false, "Also push images of services declared as dependencies")
@ -52,8 +53,8 @@ func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return pushCmd
}
func runPush(ctx context.Context, backend api.Service, opts pushOptions, services []string) error {
project, err := opts.ToProject(services)
func runPush(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pushOptions, services []string) error {
project, err := opts.ToProject(dockerCli, services)
if err != nil {
return err
}

View File

@ -19,6 +19,7 @@ package compose
import (
"context"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/spf13/cobra"
)
@ -30,7 +31,7 @@ type removeOptions struct {
volumes bool
}
func removeCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func removeCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := removeOptions{
ProjectOptions: p,
}
@ -44,9 +45,9 @@ can override this with -v. To list all volumes, use "docker volume ls".
Any data which is not in a volume will be lost.`,
RunE: Adapt(func(ctx context.Context, args []string) error {
return runRemove(ctx, backend, opts, args)
return runRemove(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
f := cmd.Flags()
f.BoolVarP(&opts.force, "force", "f", false, "Don't ask to confirm removal")
@ -58,8 +59,8 @@ Any data which is not in a volume will be lost.`,
return cmd
}
func runRemove(ctx context.Context, backend api.Service, opts removeOptions, services []string) error {
project, name, err := opts.projectOrName(services...)
func runRemove(ctx context.Context, dockerCli command.Cli, backend api.Service, opts removeOptions, services []string) error {
project, name, err := opts.projectOrName(dockerCli, services...)
if err != nil {
return err
}

View File

@ -20,6 +20,7 @@ import (
"context"
"time"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -32,7 +33,7 @@ type restartOptions struct {
noDeps bool
}
func restartCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func restartCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := restartOptions{
ProjectOptions: p,
}
@ -43,9 +44,9 @@ func restartCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts.timeChanged = cmd.Flags().Changed("timeout")
},
RunE: Adapt(func(ctx context.Context, args []string) error {
return runRestart(ctx, backend, opts, args)
return runRestart(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := restartCmd.Flags()
flags.IntVarP(&opts.timeout, "timeout", "t", 0, "Specify a shutdown timeout in seconds")
@ -54,8 +55,8 @@ func restartCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return restartCmd
}
func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error {
project, name, err := opts.projectOrName()
func runRestart(ctx context.Context, dockerCli command.Cli, backend api.Service, opts restartOptions, services []string) error {
project, name, err := opts.projectOrName(dockerCli)
if err != nil {
return err
}

View File

@ -26,6 +26,7 @@ import (
cgo "github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/opts"
"github.com/mattn/go-shellwords"
"github.com/spf13/cobra"
@ -111,7 +112,7 @@ func (options runOptions) apply(project *types.Project) error {
return nil
}
func runCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
options := runOptions{
composeOptions: &composeOptions{
ProjectOptions: p,
@ -152,7 +153,7 @@ func runCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *co
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
project, err := p.ToProject([]string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile)
project, err := p.ToProject(dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile)
if err != nil {
return err
}
@ -162,16 +163,16 @@ func runCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *co
}
options.ignoreOrphans = utils.StringToBool(project.Environment[ComposeIgnoreOrphans])
return runRun(ctx, backend, project, options, createOpts, buildOpts, streams)
return runRun(ctx, backend, project, options, createOpts, buildOpts, dockerCli)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := cmd.Flags()
flags.BoolVarP(&options.Detach, "detach", "d", false, "Run container in background and print container ID")
flags.StringArrayVarP(&options.environment, "env", "e", []string{}, "Set environment variables")
flags.StringArrayVarP(&options.labels, "label", "l", []string{}, "Add or override a label")
flags.BoolVar(&options.Remove, "rm", false, "Automatically remove the container when it exits")
flags.BoolVarP(&options.noTty, "no-TTY", "T", !streams.Out().IsTerminal(), "Disable pseudo-TTY allocation (default: auto-detected).")
flags.BoolVarP(&options.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation (default: auto-detected).")
flags.StringVar(&options.name, "name", "", "Assign a name to the container")
flags.StringVarP(&options.user, "user", "u", "", "Run as specified username or uid")
flags.StringVarP(&options.workdir, "workdir", "w", "", "Working directory inside the container")
@ -206,7 +207,7 @@ func normalizeRunFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(name)
}
func runRun(ctx context.Context, backend api.Service, project *types.Project, options runOptions, createOpts createOptions, buildOpts buildOptions, streams api.Streams) error {
func runRun(ctx context.Context, backend api.Service, project *types.Project, options runOptions, createOpts createOptions, buildOpts buildOptions, dockerCli command.Cli) error {
err := options.apply(project)
if err != nil {
return err
@ -228,7 +229,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
buildForDeps = &bo
}
return startDependencies(ctx, backend, *project, buildForDeps, options.Service, options.ignoreOrphans)
}, streams.Err())
}, dockerCli.Err())
if err != nil {
return err
}

View File

@ -19,6 +19,7 @@ package compose
import (
"context"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/spf13/cobra"
)
@ -27,7 +28,7 @@ type startOptions struct {
*ProjectOptions
}
func startCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func startCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := startOptions{
ProjectOptions: p,
}
@ -35,15 +36,15 @@ func startCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
Use: "start [SERVICE...]",
Short: "Start services",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runStart(ctx, backend, opts, args)
return runStart(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
return startCmd
}
func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error {
project, name, err := opts.projectOrName(services...)
func runStart(ctx context.Context, dockerCli command.Cli, backend api.Service, opts startOptions, services []string) error {
project, name, err := opts.projectOrName(dockerCli, services...)
if err != nil {
return err
}

View File

@ -20,6 +20,7 @@ import (
"context"
"time"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -31,7 +32,7 @@ type stopOptions struct {
timeout int
}
func stopCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func stopCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := stopOptions{
ProjectOptions: p,
}
@ -42,9 +43,9 @@ func stopCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
opts.timeChanged = cmd.Flags().Changed("timeout")
},
RunE: Adapt(func(ctx context.Context, args []string) error {
return runStop(ctx, backend, opts, args)
return runStop(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := cmd.Flags()
flags.IntVarP(&opts.timeout, "timeout", "t", 0, "Specify a shutdown timeout in seconds")
@ -52,8 +53,8 @@ func stopCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return cmd
}
func runStop(ctx context.Context, backend api.Service, opts stopOptions, services []string) error {
project, name, err := opts.projectOrName(services...)
func runStop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts stopOptions, services []string) error {
project, name, err := opts.projectOrName(dockerCli, services...)
if err != nil {
return err
}

View File

@ -24,6 +24,7 @@ import (
"strings"
"text/tabwriter"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
@ -33,7 +34,7 @@ type topOptions struct {
*ProjectOptions
}
func topCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
func topCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := topOptions{
ProjectOptions: p,
}
@ -41,15 +42,15 @@ func topCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *co
Use: "top [SERVICES...]",
Short: "Display the running processes",
RunE: Adapt(func(ctx context.Context, args []string) error {
return runTop(ctx, streams, backend, opts, args)
return runTop(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
return topCmd
}
func runTop(ctx context.Context, streams api.Streams, backend api.Service, opts topOptions, services []string) error {
projectName, err := opts.toProjectName()
func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts topOptions, services []string) error {
projectName, err := opts.toProjectName(dockerCli)
if err != nil {
return err
}
@ -63,8 +64,8 @@ func runTop(ctx context.Context, streams api.Streams, backend api.Service, opts
})
for _, container := range containers {
fmt.Fprintf(streams.Out(), "%s\n", container.Name)
err := psPrinter(streams.Out(), func(w io.Writer) {
fmt.Fprintf(dockerCli.Out(), "%s\n", container.Name)
err := psPrinter(dockerCli.Out(), func(w io.Writer) {
for _, proc := range container.Processes {
info := []interface{}{}
for _, p := range proc {

View File

@ -26,6 +26,7 @@ import (
xprogress "github.com/docker/buildx/util/progress"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/cmd/formatter"
"github.com/spf13/cobra"
@ -73,7 +74,7 @@ func (opts upOptions) apply(project *types.Project, services []string) error {
return nil
}
func upCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cobra.Command {
func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
up := upOptions{}
create := createOptions{}
build := buildOptions{ProjectOptions: p}
@ -85,7 +86,7 @@ func upCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cob
create.timeChanged = cmd.Flags().Changed("timeout")
return validateFlags(&up, &create)
}),
RunE: p.WithServices(func(ctx context.Context, project *types.Project, services []string) error {
RunE: p.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []string) error {
create.ignoreOrphans = utils.StringToBool(project.Environment[ComposeIgnoreOrphans])
if create.ignoreOrphans && create.removeOrphans {
return fmt.Errorf("cannot combine %s and --remove-orphans", ComposeIgnoreOrphans)
@ -93,9 +94,9 @@ func upCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *cob
if len(up.attach) != 0 && up.attachDependencies {
return errors.New("cannot combine --attach and --attach-dependencies")
}
return runUp(ctx, streams, backend, create, up, build, project, services)
return runUp(ctx, dockerCli, backend, create, up, build, project, services)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := upCmd.Flags()
flags.BoolVarP(&up.Detach, "detach", "d", false, "Detached mode: Run containers in the background")
@ -153,7 +154,7 @@ func validateFlags(up *upOptions, create *createOptions) error {
func runUp(
ctx context.Context,
streams api.Streams,
dockerCli command.Cli,
backend api.Service,
createOptions createOptions,
upOptions upOptions,
@ -212,7 +213,7 @@ func runUp(
var consumer api.LogConsumer
var attach []string
if !upOptions.Detach {
consumer = formatter.NewLogConsumer(ctx, streams.Out(), streams.Err(), !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp)
consumer = formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp)
var attachSet utils.Set[string]
if len(upOptions.attach) != 0 {

View File

@ -33,14 +33,14 @@ type versionOptions struct {
short bool
}
func versionCommand(streams command.Cli) *cobra.Command {
func versionCommand(dockerCli command.Cli) *cobra.Command {
opts := versionOptions{}
cmd := &cobra.Command{
Use: "version [OPTIONS]",
Short: "Show the Docker Compose version information",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
runVersion(opts, streams)
runVersion(opts, dockerCli)
return nil
},
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
@ -57,14 +57,14 @@ func versionCommand(streams command.Cli) *cobra.Command {
return cmd
}
func runVersion(opts versionOptions, streams command.Cli) {
func runVersion(opts versionOptions, dockerCli command.Cli) {
if opts.short {
fmt.Fprintln(streams.Out(), strings.TrimPrefix(internal.Version, "v"))
fmt.Fprintln(dockerCli.Out(), strings.TrimPrefix(internal.Version, "v"))
return
}
if opts.format == formatter.JSON {
fmt.Fprintf(streams.Out(), "{\"version\":%q}\n", internal.Version)
fmt.Fprintf(dockerCli.Out(), "{\"version\":%q}\n", internal.Version)
return
}
fmt.Fprintln(streams.Out(), "Docker Compose version", internal.Version)
fmt.Fprintln(dockerCli.Out(), "Docker Compose version", internal.Version)
}

View File

@ -22,6 +22,7 @@ import (
"os"
"strings"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/spf13/cobra"
)
@ -34,7 +35,7 @@ type vizOptions struct {
indentationStr string
}
func vizCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func vizCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := vizOptions{
ProjectOptions: p,
}
@ -50,7 +51,7 @@ func vizCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return err
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
return runViz(ctx, backend, &opts)
return runViz(ctx, dockerCli, backend, &opts)
}),
}
@ -62,9 +63,9 @@ func vizCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return cmd
}
func runViz(ctx context.Context, backend api.Service, opts *vizOptions) error {
func runViz(ctx context.Context, dockerCli command.Cli, backend api.Service, opts *vizOptions) error {
_, _ = fmt.Fprintln(os.Stderr, "viz command is EXPERIMENTAL")
project, err := opts.ToProject(nil)
project, err := opts.ToProject(dockerCli, nil)
if err != nil {
return err
}

View File

@ -21,6 +21,7 @@ import (
"os"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/spf13/cobra"
)
@ -33,7 +34,7 @@ type waitOptions struct {
downProject bool
}
func waitCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func waitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := waitOptions{
ProjectOptions: p,
}
@ -46,7 +47,7 @@ func waitCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
Args: cli.RequiresMinArgs(1),
RunE: Adapt(func(ctx context.Context, services []string) error {
opts.services = services
statusCode, err = runWait(ctx, backend, &opts)
statusCode, err = runWait(ctx, dockerCli, backend, &opts)
return err
}),
PostRun: func(cmd *cobra.Command, args []string) {
@ -59,8 +60,8 @@ func waitCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return cmd
}
func runWait(ctx context.Context, backend api.Service, opts *waitOptions) (int64, error) {
_, name, err := opts.projectOrName()
func runWait(ctx context.Context, dockerCli command.Cli, backend api.Service, opts *waitOptions) (int64, error) {
_, name, err := opts.projectOrName(dockerCli)
if err != nil {
return 0, err
}

View File

@ -21,6 +21,7 @@ import (
"fmt"
"os"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/internal/locker"
"github.com/docker/compose/v2/pkg/api"
@ -32,7 +33,7 @@ type watchOptions struct {
quiet bool
}
func watchCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
opts := watchOptions{
ProjectOptions: p,
}
@ -43,18 +44,18 @@ func watchCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
return runWatch(ctx, backend, opts, args)
return runWatch(ctx, dockerCli, backend, opts, args)
}),
ValidArgsFunction: completeServiceNames(p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
cmd.Flags().BoolVar(&opts.quiet, "quiet", false, "hide build output")
return cmd
}
func runWatch(ctx context.Context, backend api.Service, opts watchOptions, services []string) error {
func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, opts watchOptions, services []string) error {
fmt.Fprintln(os.Stderr, "watch command is EXPERIMENTAL")
project, err := opts.ToProject(nil)
project, err := opts.ToProject(dockerCli, nil)
if err != nil {
return err
}

1
go.mod
View File

@ -54,6 +54,7 @@ require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
github.com/aws/aws-sdk-go-v2 v1.17.6 // indirect
github.com/aws/aws-sdk-go-v2/config v1.18.16 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.16 // indirect

3
go.sum
View File

@ -60,8 +60,9 @@ github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCzt
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ=
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=

View File

@ -75,7 +75,7 @@ type Service interface {
// Port executes the equivalent to a `compose port`
Port(ctx context.Context, projectName string, service string, port uint16, options PortOptions) (string, int, error)
// Publish executes the equivalent to a `compose publish`
Publish(ctx context.Context, project *types.Project, repository string) error
Publish(ctx context.Context, project *types.Project, repository string, options PublishOptions) error
// Images executes the equivalent of a `compose images`
Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
// MaxConcurrency defines upper limit for concurrent operations against engine API
@ -354,6 +354,10 @@ type PortOptions struct {
Index int
}
// PublishOptions group options of the Publish API
type PublishOptions struct {
}
func (e Event) String() string {
t := e.Timestamp.Format("2006-01-02 15:04:05.000000")
var attr []string

View File

@ -55,7 +55,7 @@ type ServiceProxy struct {
DryRunModeFn func(ctx context.Context, dryRun bool) (context.Context, error)
VizFn func(ctx context.Context, project *types.Project, options VizOptions) (string, error)
WaitFn func(ctx context.Context, projectName string, options WaitOptions) (int64, error)
PublishFn func(ctx context.Context, project *types.Project, repository string) error
PublishFn func(ctx context.Context, project *types.Project, repository string, options PublishOptions) error
interceptors []Interceptor
}
@ -313,8 +313,8 @@ func (s *ServiceProxy) Port(ctx context.Context, projectName string, service str
return s.PortFn(ctx, projectName, service, port, options)
}
func (s *ServiceProxy) Publish(ctx context.Context, project *types.Project, repository string) error {
return s.PublishFn(ctx, project, repository)
func (s *ServiceProxy) Publish(ctx context.Context, project *types.Project, repository string, options PublishOptions) error {
return s.PublishFn(ctx, project, repository, options)
}
// Images implements Service interface

View File

@ -20,11 +20,11 @@ import (
"context"
"github.com/compose-spec/compose-go/types"
"github.com/distribution/distribution/v3/reference"
"github.com/distribution/reference"
"github.com/docker/compose/v2/pkg/api"
)
func (s *composeService) Publish(ctx context.Context, project *types.Project, repository string) error {
func (s *composeService) Publish(ctx context.Context, project *types.Project, repository string, options api.PublishOptions) error {
err := s.Push(ctx, project, api.PushOptions{})
if err != nil {
return err

View File

@ -267,17 +267,17 @@ func (mr *MockServiceMockRecorder) Ps(ctx, projectName, options interface{}) *go
}
// Publish mocks base method.
func (m *MockService) Publish(ctx context.Context, project *types.Project, repository string) error {
func (m *MockService) Publish(ctx context.Context, project *types.Project, repository string, options api.PublishOptions) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Publish", ctx, project, repository)
ret := m.ctrl.Call(m, "Publish", ctx, project, repository, options)
ret0, _ := ret[0].(error)
return ret0
}
// Publish indicates an expected call of Publish.
func (mr *MockServiceMockRecorder) Publish(ctx, project, repository interface{}) *gomock.Call {
func (mr *MockServiceMockRecorder) Publish(ctx, project, repository, options interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockService)(nil).Publish), ctx, project, repository)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockService)(nil).Publish), ctx, project, repository, options)
}
// Pull mocks base method.

View File

@ -46,7 +46,7 @@ func GitRemoteLoaderEnabled() (bool, error) {
return false, nil
}
func NewGitRemoteLoader() (loader.ResourceLoader, error) {
func NewGitRemoteLoader(offline bool) (loader.ResourceLoader, error) {
// xdg.CacheFile creates the parent directories for the target file path
// and returns the fully qualified path, so use "git" as a filename and
// then chop it off after, i.e. no ~/.cache/docker-compose/git file will
@ -57,12 +57,14 @@ func NewGitRemoteLoader() (loader.ResourceLoader, error) {
}
cache = filepath.Dir(cache)
return gitRemoteLoader{
cache: cache,
cache: cache,
offline: offline,
}, err
}
type gitRemoteLoader struct {
cache string
cache string
offline bool
}
func (g gitRemoteLoader) Accept(path string) bool {
@ -104,6 +106,9 @@ func (g gitRemoteLoader) Load(ctx context.Context, path string) (string, error)
local := filepath.Join(g.cache, ref.Commit)
if _, err := os.Stat(local); os.IsNotExist(err) {
if g.offline {
return "", nil
}
err = g.checkout(ctx, local, ref)
if err != nil {
return "", err
@ -167,7 +172,7 @@ func (g gitRemoteLoader) gitCommandEnv() []string {
// Disable any ssh connection pooling by Git and do not attempt to prompt the user.
env["GIT_SSH_COMMAND"] = "ssh -o ControlMaster=no -o BatchMode=yes"
}
v := values(env)
v := env.Values()
return v
}
@ -182,11 +187,3 @@ func findFile(names []string, pwd string) (string, error) {
}
var _ loader.ResourceLoader = gitRemoteLoader{}
func values(m types.Mapping) []string {
values := make([]string, 0, len(m))
for k, v := range m {
values = append(values, fmt.Sprintf("%s=%s", k, v))
}
return values
}

144
pkg/remote/oci.go Normal file
View File

@ -0,0 +1,144 @@
/*
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 remote
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/adrg/xdg"
"github.com/distribution/reference"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/cli/cli/command"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/compose-spec/compose-go/loader"
"github.com/pkg/errors"
)
func OCIRemoteLoaderEnabled() (bool, error) {
if v := os.Getenv("COMPOSE_EXPERIMENTAL_OCI_REMOTE"); v != "" {
enabled, err := strconv.ParseBool(v)
if err != nil {
return false, errors.Wrap(err, "COMPOSE_EXPERIMENTAL_OCI_REMOTE environment variable expects boolean value")
}
return enabled, err
}
return false, nil
}
func NewOCIRemoteLoader(dockerCli command.Cli, offline bool) (loader.ResourceLoader, error) {
// xdg.CacheFile creates the parent directories for the target file path
// and returns the fully qualified path, so use "git" as a filename and
// then chop it off after, i.e. no ~/.cache/docker-compose/git file will
// ever be created
cache, err := xdg.CacheFile(filepath.Join("docker-compose", "oci"))
if err != nil {
return nil, fmt.Errorf("initializing git cache: %w", err)
}
cache = filepath.Dir(cache)
return ociRemoteLoader{
cache: cache,
dockerCli: dockerCli,
offline: offline,
}, err
}
type ociRemoteLoader struct {
cache string
dockerCli command.Cli
offline bool
}
const prefix = "oci:"
func (g ociRemoteLoader) Accept(path string) bool {
return strings.HasPrefix(path, prefix)
}
func (g ociRemoteLoader) Load(ctx context.Context, path string) (string, error) {
if g.offline {
return "", nil
}
ref, err := reference.ParseDockerRef(path[len(prefix):])
if err != nil {
return "", err
}
opt, err := storeutil.GetImageConfig(g.dockerCli, nil)
if err != nil {
return "", err
}
resolver := imagetools.New(opt)
content, descriptor, err := resolver.Get(ctx, ref.String())
if err != nil {
return "", err
}
local := filepath.Join(g.cache, descriptor.Digest.Hex())
composeFile := filepath.Join(local, "compose.yaml")
if _, err = os.Stat(local); os.IsNotExist(err) {
err = os.MkdirAll(local, 0o700)
if err != nil {
return "", err
}
f, err := os.Create(composeFile)
if err != nil {
return "", err
}
defer f.Close() //nolint:errcheck
var descriptor v1.Manifest
err = json.Unmarshal(content, &descriptor)
if err != nil {
return "", err
}
for i, layer := range descriptor.Layers {
digested, err := reference.WithDigest(ref, layer.Digest)
if err != nil {
return "", err
}
content, _, err := resolver.Get(ctx, digested.String())
if err != nil {
return "", err
}
if i > 0 {
_, err = f.Write([]byte("\n---\n"))
if err != nil {
return "", err
}
}
_, err = f.Write(content)
if err != nil {
return "", err
}
}
}
return composeFile, nil
}
var _ loader.ResourceLoader = ociRemoteLoader{}