diff --git a/api/compose/delegator.go b/api/compose/delegator.go new file mode 100644 index 000000000..c79ded64f --- /dev/null +++ b/api/compose/delegator.go @@ -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) +} diff --git a/api/compose/noimpl.go b/api/compose/noimpl.go new file mode 100644 index 000000000..172af3e47 --- /dev/null +++ b/api/compose/noimpl.go @@ -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 +} diff --git a/builder.Makefile b/builder.Makefile index 4e1983dc8..079da2bc1 100644 --- a/builder.Makefile +++ b/builder.Makefile @@ -57,6 +57,10 @@ protos: 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 cross: GOOS=linux GOARCH=amd64 $(GO_BUILD) $(TAGS) -o $(BINARY)-linux-amd64 ./cli diff --git a/cli/cmd/compose/compose.go b/cli/cmd/compose/compose.go index 49bf3e9e1..da105b9c4 100644 --- a/cli/cmd/compose/compose.go +++ b/cli/cmd/compose/compose.go @@ -123,6 +123,14 @@ func Command(contextType string, backend compose.Service) *cobra.Command { return fmt.Errorf("unknown docker command: %q", "compose "+args[0]) }, 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 ansi != "auto" { return errors.New(`cannot specify DEPRECATED "--no-ansi" and "--ansi". Please use only "--ansi"`) diff --git a/cli/config/flags.go b/cli/config/flags.go index 2a5265b27..f38d967e2 100644 --- a/cli/config/flags.go +++ b/cli/config/flags.go @@ -17,9 +17,11 @@ package config import ( + "fmt" "os" "path/filepath" + "github.com/pkg/errors" "github.com/spf13/pflag" "github.com/docker/compose-cli/api/config" @@ -44,3 +46,36 @@ func confDir() string { home, _ := os.UserHomeDir() 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 +} diff --git a/cli/config/flags_test.go b/cli/config/flags_test.go new file mode 100644 index 000000000..3e6890215 --- /dev/null +++ b/cli/config/flags_test.go @@ -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) +} diff --git a/cli/main.go b/cli/main.go index 2e8055a89..ae6ab7c11 100644 --- a/cli/main.go +++ b/cli/main.go @@ -29,9 +29,6 @@ import ( "time" "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/sirupsen/logrus" "github.com/spf13/cobra" @@ -48,6 +45,7 @@ import ( "github.com/docker/compose-cli/cli/cmd/logout" "github.com/docker/compose-cli/cli/cmd/run" "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/mobycli" cliopts "github.com/docker/compose-cli/cli/options" @@ -62,7 +60,6 @@ import ( var ( contextAgnosticCommands = map[string]struct{}{ - "compose": {}, "context": {}, "login": {}, "logout": {}, @@ -198,7 +195,7 @@ func main() { configDir := opts.Config config.WithDir(configDir) - currentContext := determineCurrentContext(opts.Context, configDir, opts.Hosts) + currentContext := cliconfig.GetCurrentContext(opts.Context, configDir, opts.Hosts) apicontext.WithCurrentContext(currentContext) s, err := store.New(configDir) @@ -234,27 +231,7 @@ func main() { func getBackend(ctype string, configDir string, opts cliopts.GlobalOpts) (backend.Service, error) { switch ctype { case store.DefaultContextType, store.LocalContextType: - 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 local.NewService(apiClient), nil + return local.GetLocalBackend(configDir, opts) } service, err := backend.Get(ctype) if errdefs.IsNotFoundError(err) { @@ -311,6 +288,7 @@ func exit(ctx string, err error, ctype string) { } if compose.Warning != "" { + logrus.Warn(err) fmt.Fprintln(os.Stderr, compose.Warning) } @@ -354,38 +332,6 @@ func newSigContext() (context.Context, func()) { 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)) { f(c) for _, c := range c.Commands() { diff --git a/cli/main_test.go b/cli/main_test.go index 3a0ee145c..85caf2034 100644 --- a/cli/main_test.go +++ b/cli/main_test.go @@ -17,53 +17,17 @@ package main import ( - "io/ioutil" "os" - "path/filepath" "testing" "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/context" "github.com/docker/compose-cli/cli/cmd/login" "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) { assert.Assert(t, isContextAgnosticCommand(login.Command())) assert.Assert(t, isContextAgnosticCommand(context.Command())) diff --git a/local/backend.go b/local/backend.go index 7050180c4..95ab2f573 100644 --- a/local/backend.go +++ b/local/backend.go @@ -19,7 +19,9 @@ package local import ( "os" + "github.com/docker/cli/cli/command" cliconfig "github.com/docker/cli/cli/config" + cliflags "github.com/docker/cli/cli/flags" "github.com/docker/docker/client" "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/secrets" "github.com/docker/compose-cli/api/volumes" + cliopts "github.com/docker/compose-cli/cli/options" 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 { return s.containerService } diff --git a/main.go b/main.go new file mode 100644 index 000000000..fa801fa65 --- /dev/null +++ b/main.go @@ -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"), + }) +} diff --git a/utils/e2e/framework.go b/utils/e2e/framework.go index ce9360e4a..5ce8da67c 100644 --- a/utils/e2e/framework.go +++ b/utils/e2e/framework.go @@ -85,6 +85,13 @@ func newE2eCLI(t *testing.T, binDir string) *E2eCLI { _ = 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} } @@ -117,7 +124,7 @@ func SetupExistingCLI() (string, func(), error) { return "", nil, err } - bin, err := findExecutable([]string{"../../bin", "../../../bin"}) + bin, err := findExecutable(DockerExecutableName, []string{"../../bin", "../../../bin"}) if err != nil { return "", nil, err } @@ -133,9 +140,9 @@ func SetupExistingCLI() (string, func(), error) { return d, cleanup, nil } -func findExecutable(paths []string) (string, error) { +func findExecutable(executableName string, paths []string) (string, error) { for _, p := range paths { - bin, err := filepath.Abs(path.Join(p, DockerExecutableName)) + bin, err := filepath.Abs(path.Join(p, executableName)) if err != nil { return "", err }