Compose as a cli plugin

Signed-off-by: Guillaume Tardif <guillaume.tardif@gmail.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Guillaume Tardif 2021-03-04 19:21:42 +01:00 committed by Nicolas De Loof
parent 85af8cdaaa
commit 1dc97e8c4b
11 changed files with 494 additions and 97 deletions

143
api/compose/delegator.go Normal file
View File

@ -0,0 +1,143 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"github.com/compose-spec/compose-go/types"
)
// ServiceDelegator implements Service by delegating to another implementation. This allows lazy init
type ServiceDelegator struct {
Delegate Service
}
//Build implements Service interface
func (s *ServiceDelegator) Build(ctx context.Context, project *types.Project, options BuildOptions) error {
return s.Delegate.Build(ctx, project, options)
}
//Push implements Service interface
func (s *ServiceDelegator) Push(ctx context.Context, project *types.Project, options PushOptions) error {
return s.Delegate.Push(ctx, project, options)
}
//Pull implements Service interface
func (s *ServiceDelegator) Pull(ctx context.Context, project *types.Project, options PullOptions) error {
return s.Delegate.Pull(ctx, project, options)
}
//Create implements Service interface
func (s *ServiceDelegator) Create(ctx context.Context, project *types.Project, options CreateOptions) error {
return s.Delegate.Create(ctx, project, options)
}
//Start implements Service interface
func (s *ServiceDelegator) Start(ctx context.Context, project *types.Project, options StartOptions) error {
return s.Delegate.Start(ctx, project, options)
}
//Restart implements Service interface
func (s *ServiceDelegator) Restart(ctx context.Context, project *types.Project, options RestartOptions) error {
return s.Delegate.Restart(ctx, project, options)
}
//Stop implements Service interface
func (s *ServiceDelegator) Stop(ctx context.Context, project *types.Project, options StopOptions) error {
return s.Delegate.Stop(ctx, project, options)
}
//Up implements Service interface
func (s *ServiceDelegator) Up(ctx context.Context, project *types.Project, options UpOptions) error {
return s.Delegate.Up(ctx, project, options)
}
//Down implements Service interface
func (s *ServiceDelegator) Down(ctx context.Context, project string, options DownOptions) error {
return s.Delegate.Down(ctx, project, options)
}
//Logs implements Service interface
func (s *ServiceDelegator) Logs(ctx context.Context, project string, consumer LogConsumer, options LogOptions) error {
return s.Delegate.Logs(ctx, project, consumer, options)
}
//Ps implements Service interface
func (s *ServiceDelegator) Ps(ctx context.Context, project string, options PsOptions) ([]ContainerSummary, error) {
return s.Delegate.Ps(ctx, project, options)
}
//List implements Service interface
func (s *ServiceDelegator) List(ctx context.Context, options ListOptions) ([]Stack, error) {
return s.Delegate.List(ctx, options)
}
//Convert implements Service interface
func (s *ServiceDelegator) Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error) {
return s.Delegate.Convert(ctx, project, options)
}
//Kill implements Service interface
func (s *ServiceDelegator) Kill(ctx context.Context, project *types.Project, options KillOptions) error {
return s.Delegate.Kill(ctx, project, options)
}
//RunOneOffContainer implements Service interface
func (s *ServiceDelegator) RunOneOffContainer(ctx context.Context, project *types.Project, options RunOptions) (int, error) {
return s.Delegate.RunOneOffContainer(ctx, project, options)
}
//Remove implements Service interface
func (s *ServiceDelegator) Remove(ctx context.Context, project *types.Project, options RemoveOptions) ([]string, error) {
return s.Delegate.Remove(ctx, project, options)
}
//Exec implements Service interface
func (s *ServiceDelegator) Exec(ctx context.Context, project *types.Project, options RunOptions) error {
return s.Delegate.Exec(ctx, project, options)
}
//Pause implements Service interface
func (s *ServiceDelegator) Pause(ctx context.Context, project string, options PauseOptions) error {
return s.Delegate.Pause(ctx, project, options)
}
//UnPause implements Service interface
func (s *ServiceDelegator) UnPause(ctx context.Context, project string, options PauseOptions) error {
return s.Delegate.UnPause(ctx, project, options)
}
//Top implements Service interface
func (s *ServiceDelegator) Top(ctx context.Context, project string, services []string) ([]ContainerProcSummary, error) {
return s.Delegate.Top(ctx, project, services)
}
//Events implements Service interface
func (s *ServiceDelegator) Events(ctx context.Context, project string, options EventsOptions) error {
return s.Delegate.Events(ctx, project, options)
}
//Port implements Service interface
func (s *ServiceDelegator) Port(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error) {
return s.Delegate.Port(ctx, project, service, port, options)
}
//Images implements Service interface
func (s *ServiceDelegator) Images(ctx context.Context, project string, options ImagesOptions) ([]ImageSummary, error) {
return s.Delegate.Images(ctx, project, options)
}

143
api/compose/noimpl.go Normal file
View File

@ -0,0 +1,143 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"github.com/compose-spec/compose-go/types"
"github.com/docker/compose-cli/api/errdefs"
)
// NoImpl implements Service to return ErrNotImplemented
type NoImpl struct{}
//Build implements Service interface
func (s NoImpl) Build(ctx context.Context, project *types.Project, options BuildOptions) error {
return errdefs.ErrNotImplemented
}
//Push implements Service interface
func (s NoImpl) Push(ctx context.Context, project *types.Project, options PushOptions) error {
return errdefs.ErrNotImplemented
}
//Pull implements Service interface
func (s NoImpl) Pull(ctx context.Context, project *types.Project, options PullOptions) error {
return errdefs.ErrNotImplemented
}
//Create implements Service interface
func (s NoImpl) Create(ctx context.Context, project *types.Project, options CreateOptions) error {
return errdefs.ErrNotImplemented
}
//Start implements Service interface
func (s NoImpl) Start(ctx context.Context, project *types.Project, options StartOptions) error {
return errdefs.ErrNotImplemented
}
//Restart implements Service interface
func (s NoImpl) Restart(ctx context.Context, project *types.Project, options RestartOptions) error {
return errdefs.ErrNotImplemented
}
//Stop implements Service interface
func (s NoImpl) Stop(ctx context.Context, project *types.Project, options StopOptions) error {
return errdefs.ErrNotImplemented
}
//Up implements Service interface
func (s NoImpl) Up(ctx context.Context, project *types.Project, options UpOptions) error {
return errdefs.ErrNotImplemented
}
//Down implements Service interface
func (s NoImpl) Down(ctx context.Context, project string, options DownOptions) error {
return errdefs.ErrNotImplemented
}
//Logs implements Service interface
func (s NoImpl) Logs(ctx context.Context, project string, consumer LogConsumer, options LogOptions) error {
return errdefs.ErrNotImplemented
}
//Ps implements Service interface
func (s NoImpl) Ps(ctx context.Context, project string, options PsOptions) ([]ContainerSummary, error) {
return nil, errdefs.ErrNotImplemented
}
//List implements Service interface
func (s NoImpl) List(ctx context.Context, options ListOptions) ([]Stack, error) {
return nil, errdefs.ErrNotImplemented
}
//Convert implements Service interface
func (s NoImpl) Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error) {
return nil, errdefs.ErrNotImplemented
}
//Kill implements Service interface
func (s NoImpl) Kill(ctx context.Context, project *types.Project, options KillOptions) error {
return errdefs.ErrNotImplemented
}
//RunOneOffContainer implements Service interface
func (s NoImpl) RunOneOffContainer(ctx context.Context, project *types.Project, options RunOptions) (int, error) {
return 0, errdefs.ErrNotImplemented
}
//Remove implements Service interface
func (s NoImpl) Remove(ctx context.Context, project *types.Project, options RemoveOptions) ([]string, error) {
return nil, errdefs.ErrNotImplemented
}
//Exec implements Service interface
func (s NoImpl) Exec(ctx context.Context, project *types.Project, options RunOptions) error {
return errdefs.ErrNotImplemented
}
//Pause implements Service interface
func (s NoImpl) Pause(ctx context.Context, project string, options PauseOptions) error {
return errdefs.ErrNotImplemented
}
//UnPause implements Service interface
func (s NoImpl) UnPause(ctx context.Context, project string, options PauseOptions) error {
return errdefs.ErrNotImplemented
}
//Top implements Service interface
func (s NoImpl) Top(ctx context.Context, project string, services []string) ([]ContainerProcSummary, error) {
return nil, errdefs.ErrNotImplemented
}
//Events implements Service interface
func (s NoImpl) Events(ctx context.Context, project string, options EventsOptions) error {
return errdefs.ErrNotImplemented
}
//Port implements Service interface
func (s NoImpl) Port(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error) {
return "", 0, errdefs.ErrNotImplemented
}
//Images implements Service interface
func (s NoImpl) Images(ctx context.Context, project string, options ImagesOptions) ([]ImageSummary, error) {
return nil, errdefs.ErrNotImplemented
}

View File

@ -57,6 +57,10 @@ protos:
cli: cli:
GOOS=${GOOS} GOARCH=${GOARCH} $(GO_BUILD) $(TAGS) -o $(BINARY_WITH_EXTENSION) ./cli GOOS=${GOOS} GOARCH=${GOARCH} $(GO_BUILD) $(TAGS) -o $(BINARY_WITH_EXTENSION) ./cli
.PHONY: compose-plugin
compose-plugin:
GOOS=${GOOS} GOARCH=${GOARCH} $(GO_BUILD) $(TAGS) -o ./bin/docker-compose .
.PHONY: cross .PHONY: cross
cross: cross:
GOOS=linux GOARCH=amd64 $(GO_BUILD) $(TAGS) -o $(BINARY)-linux-amd64 ./cli GOOS=linux GOARCH=amd64 $(GO_BUILD) $(TAGS) -o $(BINARY)-linux-amd64 ./cli

View File

@ -123,6 +123,14 @@ func Command(contextType string, backend compose.Service) *cobra.Command {
return fmt.Errorf("unknown docker command: %q", "compose "+args[0]) return fmt.Errorf("unknown docker command: %q", "compose "+args[0])
}, },
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
parent := cmd.Root()
parentPrerun := parent.PersistentPreRunE
if parentPrerun != nil {
err := parentPrerun(cmd, args)
if err != nil {
return err
}
}
if noAnsi { if noAnsi {
if ansi != "auto" { if ansi != "auto" {
return errors.New(`cannot specify DEPRECATED "--no-ansi" and "--ansi". Please use only "--ansi"`) return errors.New(`cannot specify DEPRECATED "--no-ansi" and "--ansi". Please use only "--ansi"`)

View File

@ -17,9 +17,11 @@
package config package config
import ( import (
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"github.com/pkg/errors"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/docker/compose-cli/api/config" "github.com/docker/compose-cli/api/config"
@ -44,3 +46,36 @@ func confDir() string {
home, _ := os.UserHomeDir() home, _ := os.UserHomeDir()
return filepath.Join(home, config.ConfigFileDir) return filepath.Join(home, config.ConfigFileDir)
} }
// GetCurrentContext get current context based on opts, env vars
func GetCurrentContext(contextOpt string, configDir string, hosts []string) string {
// host and context flags cannot be both set at the same time -- the local backend enforces this when resolving hostname
// -H flag disables context --> set default as current
if len(hosts) > 0 {
return "default"
}
// DOCKER_HOST disables context --> set default as current
if _, present := os.LookupEnv("DOCKER_HOST"); present {
return "default"
}
res := contextOpt
if res == "" {
// check if DOCKER_CONTEXT env variable was set
if _, present := os.LookupEnv("DOCKER_CONTEXT"); present {
res = os.Getenv("DOCKER_CONTEXT")
}
if res == "" {
config, err := config.LoadFile(configDir)
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrap(err, "WARNING"))
return "default"
}
res = config.CurrentContext
}
}
if res == "" {
res = "default"
}
return res
}

61
cli/config/flags_test.go Normal file
View File

@ -0,0 +1,61 @@
/*
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 config
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"gotest.tools/v3/assert"
"github.com/docker/compose-cli/api/config"
)
var contextSetConfig = []byte(`{
"currentContext": "some-context"
}`)
func TestDetermineCurrentContext(t *testing.T) {
d, err := ioutil.TempDir("", "")
// nolint errcheck
defer os.RemoveAll(d)
assert.NilError(t, err)
err = ioutil.WriteFile(filepath.Join(d, config.ConfigFileName), contextSetConfig, 0644)
assert.NilError(t, err)
// If nothing set, fallback to default
c := GetCurrentContext("", "", []string{})
assert.Equal(t, c, "default")
// If context flag set, use that
c = GetCurrentContext("other-context", "", []string{})
assert.Equal(t, c, "other-context")
// If no context flag, use config
c = GetCurrentContext("", d, []string{})
assert.Equal(t, c, "some-context")
// Ensure context flag overrides config
c = GetCurrentContext("other-context", d, []string{})
assert.Equal(t, "other-context", c)
// Ensure host flag overrides context
c = GetCurrentContext("other-context", d, []string{"hostname"})
assert.Equal(t, "default", c)
}

View File

@ -29,9 +29,6 @@ import (
"time" "time"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
cliconfig "github.com/docker/cli/cli/config"
cliflags "github.com/docker/cli/cli/flags"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -48,6 +45,7 @@ import (
"github.com/docker/compose-cli/cli/cmd/logout" "github.com/docker/compose-cli/cli/cmd/logout"
"github.com/docker/compose-cli/cli/cmd/run" "github.com/docker/compose-cli/cli/cmd/run"
"github.com/docker/compose-cli/cli/cmd/volume" "github.com/docker/compose-cli/cli/cmd/volume"
cliconfig "github.com/docker/compose-cli/cli/config"
"github.com/docker/compose-cli/cli/metrics" "github.com/docker/compose-cli/cli/metrics"
"github.com/docker/compose-cli/cli/mobycli" "github.com/docker/compose-cli/cli/mobycli"
cliopts "github.com/docker/compose-cli/cli/options" cliopts "github.com/docker/compose-cli/cli/options"
@ -62,7 +60,6 @@ import (
var ( var (
contextAgnosticCommands = map[string]struct{}{ contextAgnosticCommands = map[string]struct{}{
"compose": {},
"context": {}, "context": {},
"login": {}, "login": {},
"logout": {}, "logout": {},
@ -198,7 +195,7 @@ func main() {
configDir := opts.Config configDir := opts.Config
config.WithDir(configDir) config.WithDir(configDir)
currentContext := determineCurrentContext(opts.Context, configDir, opts.Hosts) currentContext := cliconfig.GetCurrentContext(opts.Context, configDir, opts.Hosts)
apicontext.WithCurrentContext(currentContext) apicontext.WithCurrentContext(currentContext)
s, err := store.New(configDir) s, err := store.New(configDir)
@ -234,27 +231,7 @@ func main() {
func getBackend(ctype string, configDir string, opts cliopts.GlobalOpts) (backend.Service, error) { func getBackend(ctype string, configDir string, opts cliopts.GlobalOpts) (backend.Service, error) {
switch ctype { switch ctype {
case store.DefaultContextType, store.LocalContextType: case store.DefaultContextType, store.LocalContextType:
configFile, err := cliconfig.Load(configDir) return local.GetLocalBackend(configDir, opts)
if err != nil {
return nil, err
}
options := cliflags.CommonOptions{
Context: opts.Context,
Debug: opts.Debug,
Hosts: opts.Hosts,
LogLevel: opts.LogLevel,
}
if opts.TLSVerify {
options.TLS = opts.TLS
options.TLSVerify = opts.TLSVerify
options.TLSOptions = opts.TLSOptions
}
apiClient, err := command.NewAPIClientFromFlags(&options, configFile)
if err != nil {
return nil, err
}
return local.NewService(apiClient), nil
} }
service, err := backend.Get(ctype) service, err := backend.Get(ctype)
if errdefs.IsNotFoundError(err) { if errdefs.IsNotFoundError(err) {
@ -311,6 +288,7 @@ func exit(ctx string, err error, ctype string) {
} }
if compose.Warning != "" { if compose.Warning != "" {
logrus.Warn(err)
fmt.Fprintln(os.Stderr, compose.Warning) fmt.Fprintln(os.Stderr, compose.Warning)
} }
@ -354,38 +332,6 @@ func newSigContext() (context.Context, func()) {
return ctx, cancel return ctx, cancel
} }
func determineCurrentContext(flag string, configDir string, hosts []string) string {
// host and context flags cannot be both set at the same time -- the local backend enforces this when resolving hostname
// -H flag disables context --> set default as current
if len(hosts) > 0 {
return "default"
}
// DOCKER_HOST disables context --> set default as current
if _, present := os.LookupEnv("DOCKER_HOST"); present {
return "default"
}
res := flag
if res == "" {
// check if DOCKER_CONTEXT env variable was set
if _, present := os.LookupEnv("DOCKER_CONTEXT"); present {
res = os.Getenv("DOCKER_CONTEXT")
}
if res == "" {
config, err := config.LoadFile(configDir)
if err != nil {
fmt.Fprintln(os.Stderr, errors.Wrap(err, "WARNING"))
return "default"
}
res = config.CurrentContext
}
}
if res == "" {
res = "default"
}
return res
}
func walk(c *cobra.Command, f func(*cobra.Command)) { func walk(c *cobra.Command, f func(*cobra.Command)) {
f(c) f(c)
for _, c := range c.Commands() { for _, c := range c.Commands() {

View File

@ -17,53 +17,17 @@
package main package main
import ( import (
"io/ioutil"
"os" "os"
"path/filepath"
"testing" "testing"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
"github.com/docker/compose-cli/api/config"
"github.com/docker/compose-cli/cli/cmd" "github.com/docker/compose-cli/cli/cmd"
"github.com/docker/compose-cli/cli/cmd/context" "github.com/docker/compose-cli/cli/cmd/context"
"github.com/docker/compose-cli/cli/cmd/login" "github.com/docker/compose-cli/cli/cmd/login"
"github.com/docker/compose-cli/cli/cmd/run" "github.com/docker/compose-cli/cli/cmd/run"
) )
var contextSetConfig = []byte(`{
"currentContext": "some-context"
}`)
func TestDetermineCurrentContext(t *testing.T) {
d, err := ioutil.TempDir("", "")
// nolint errcheck
defer os.RemoveAll(d)
assert.NilError(t, err)
err = ioutil.WriteFile(filepath.Join(d, config.ConfigFileName), contextSetConfig, 0644)
assert.NilError(t, err)
// If nothing set, fallback to default
c := determineCurrentContext("", "", []string{})
assert.Equal(t, c, "default")
// If context flag set, use that
c = determineCurrentContext("other-context", "", []string{})
assert.Equal(t, c, "other-context")
// If no context flag, use config
c = determineCurrentContext("", d, []string{})
assert.Equal(t, c, "some-context")
// Ensure context flag overrides config
c = determineCurrentContext("other-context", d, []string{})
assert.Equal(t, "other-context", c)
// Ensure host flag overrides context
c = determineCurrentContext("other-context", d, []string{"hostname"})
assert.Equal(t, "default", c)
}
func TestCheckOwnCommand(t *testing.T) { func TestCheckOwnCommand(t *testing.T) {
assert.Assert(t, isContextAgnosticCommand(login.Command())) assert.Assert(t, isContextAgnosticCommand(login.Command()))
assert.Assert(t, isContextAgnosticCommand(context.Command())) assert.Assert(t, isContextAgnosticCommand(context.Command()))

View File

@ -19,7 +19,9 @@ package local
import ( import (
"os" "os"
"github.com/docker/cli/cli/command"
cliconfig "github.com/docker/cli/cli/config" cliconfig "github.com/docker/cli/cli/config"
cliflags "github.com/docker/cli/cli/flags"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/compose-cli/api/backend" "github.com/docker/compose-cli/api/backend"
@ -28,6 +30,7 @@ import (
"github.com/docker/compose-cli/api/resources" "github.com/docker/compose-cli/api/resources"
"github.com/docker/compose-cli/api/secrets" "github.com/docker/compose-cli/api/secrets"
"github.com/docker/compose-cli/api/volumes" "github.com/docker/compose-cli/api/volumes"
cliopts "github.com/docker/compose-cli/cli/options"
local_compose "github.com/docker/compose-cli/local/compose" local_compose "github.com/docker/compose-cli/local/compose"
) )
@ -47,6 +50,31 @@ func NewService(apiClient client.APIClient) backend.Service {
} }
} }
// GetLocalBackend initialize local backend
func GetLocalBackend(configDir string, opts cliopts.GlobalOpts) (backend.Service, error) {
configFile, err := cliconfig.Load(configDir)
if err != nil {
return nil, err
}
options := cliflags.CommonOptions{
Context: opts.Context,
Debug: opts.Debug,
Hosts: opts.Hosts,
LogLevel: opts.LogLevel,
}
if opts.TLSVerify {
options.TLS = opts.TLS
options.TLSVerify = opts.TLSVerify
options.TLSOptions = opts.TLSOptions
}
apiClient, err := command.NewAPIClientFromFlags(&options, configFile)
if err != nil {
return nil, err
}
return NewService(apiClient), nil
}
func (s *local) ContainerService() containers.Service { func (s *local) ContainerService() containers.Service {
return s.containerService return s.containerService
} }

58
main.go Normal file
View File

@ -0,0 +1,58 @@
/*
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 main
import (
"strings"
"github.com/spf13/cobra"
"github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli-plugins/plugin"
"github.com/docker/cli/cli/command"
api "github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/api/context/store"
"github.com/docker/compose-cli/cli/cmd/compose"
"github.com/docker/compose-cli/internal"
impl "github.com/docker/compose-cli/local/compose"
)
func main() {
plugin.Run(func(dockerCli command.Cli) *cobra.Command {
lazyInit := api.ServiceDelegator{
Delegate: api.NoImpl{},
}
cmd := compose.Command(store.DefaultContextType, &lazyInit)
originalPreRun := cmd.PersistentPreRunE
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if err := plugin.PersistentPreRunE(cmd, args); err != nil {
return err
}
lazyInit.Delegate = impl.NewComposeService(dockerCli.Client(), dockerCli.ConfigFile())
if originalPreRun != nil {
return originalPreRun(cmd, args)
}
return nil
}
return cmd
},
manager.Metadata{
SchemaVersion: "0.1.0",
Vendor: "Docker Inc.",
Version: strings.TrimPrefix(internal.Version, "v"),
})
}

View File

@ -85,6 +85,13 @@ func newE2eCLI(t *testing.T, binDir string) *E2eCLI {
_ = os.RemoveAll(d) _ = os.RemoveAll(d)
}) })
_ = os.MkdirAll(filepath.Join(d, "cli-plugins"), 0755)
composePlugin, _ := findExecutable("docker-compose", []string{"../../bin", "../../../bin"})
err = CopyFile(composePlugin, filepath.Join(d, "cli-plugins", "docker-compose"))
if err != nil {
panic(err)
}
return &E2eCLI{binDir, d, t} return &E2eCLI{binDir, d, t}
} }
@ -117,7 +124,7 @@ func SetupExistingCLI() (string, func(), error) {
return "", nil, err return "", nil, err
} }
bin, err := findExecutable([]string{"../../bin", "../../../bin"}) bin, err := findExecutable(DockerExecutableName, []string{"../../bin", "../../../bin"})
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -133,9 +140,9 @@ func SetupExistingCLI() (string, func(), error) {
return d, cleanup, nil return d, cleanup, nil
} }
func findExecutable(paths []string) (string, error) { func findExecutable(executableName string, paths []string) (string, error) {
for _, p := range paths { for _, p := range paths {
bin, err := filepath.Abs(path.Join(p, DockerExecutableName)) bin, err := filepath.Abs(path.Join(p, executableName))
if err != nil { if err != nil {
return "", err return "", err
} }