mirror of https://github.com/docker/compose.git
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:
parent
85af8cdaaa
commit
1dc97e8c4b
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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"`)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
62
cli/main.go
62
cli/main.go
|
@ -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() {
|
||||||
|
|
|
@ -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()))
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
|
})
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue