remove obsolete mutli-orchestrator support

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2022-02-22 13:46:35 +01:00
parent 1d48749c1c
commit 193ede9b12
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
44 changed files with 186 additions and 581 deletions

View File

@ -60,7 +60,6 @@ type Cli interface {
ContentTrustEnabled() bool
ContextStore() store.Store
CurrentContext() string
StackOrchestrator(flagValue string) (Orchestrator, error)
DockerEndpoint() docker.Endpoint
}
@ -367,25 +366,6 @@ func (cli *DockerCli) CurrentContext() string {
return cli.currentContext
}
// StackOrchestrator resolves which stack orchestrator is in use
func (cli *DockerCli) StackOrchestrator(flagValue string) (Orchestrator, error) {
currentContext := cli.CurrentContext()
ctxRaw, err := cli.ContextStore().GetMetadata(currentContext)
if store.IsErrContextDoesNotExist(err) {
// case where the currentContext has been removed (CLI behavior is to fallback to using DOCKER_HOST based resolution)
return GetStackOrchestrator(flagValue, "", cli.ConfigFile().StackOrchestrator, cli.Err())
}
if err != nil {
return "", err
}
ctxMeta, err := GetDockerContext(ctxRaw)
if err != nil {
return "", err
}
ctxOrchestrator := string(ctxMeta.StackOrchestrator)
return GetStackOrchestrator(flagValue, ctxOrchestrator, cli.ConfigFile().StackOrchestrator, cli.Err())
}
// DockerEndpoint returns the current docker endpoint
func (cli *DockerCli) DockerEndpoint() docker.Endpoint {
return cli.dockerEndpoint

View File

@ -9,9 +9,8 @@ import (
// DockerContext is a typed representation of what we put in Context metadata
type DockerContext struct {
Description string
StackOrchestrator Orchestrator
AdditionalFields map[string]interface{}
Description string
AdditionalFields map[string]interface{}
}
// MarshalJSON implements custom JSON marshalling
@ -20,9 +19,6 @@ func (dc DockerContext) MarshalJSON() ([]byte, error) {
if dc.Description != "" {
s["Description"] = dc.Description
}
if dc.StackOrchestrator != "" {
s["StackOrchestrator"] = dc.StackOrchestrator
}
if dc.AdditionalFields != nil {
for k, v := range dc.AdditionalFields {
s[k] = v
@ -41,8 +37,6 @@ func (dc *DockerContext) UnmarshalJSON(payload []byte) error {
switch k {
case "Description":
dc.Description = v.(string)
case "StackOrchestrator":
dc.StackOrchestrator = Orchestrator(v.(string))
default:
if dc.AdditionalFields == nil {
dc.AdditionalFields = make(map[string]interface{})

View File

@ -3,7 +3,6 @@ package context
import (
"bytes"
"fmt"
"github.com/sirupsen/logrus"
"text/tabwriter"
"github.com/docker/cli/cli"
@ -59,10 +58,12 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
"default-stack-orchestrator", "",
"Default orchestrator for stack operations to use with this context (swarm|kubernetes|all)")
flags.SetAnnotation("default-stack-orchestrator", "deprecated", nil)
flags.MarkDeprecated("default-stack-orchestrator", "option will be ignored")
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint")
flags.StringToStringVar(&opts.Kubernetes, "kubernetes", nil, "set the kubernetes endpoint")
flags.SetAnnotation("kubernetes", "kubernetes", nil)
flags.SetAnnotation("kubernetes", "deprecated", nil)
flags.MarkDeprecated("kubernetes", "option will be ignored")
flags.StringVar(&opts.From, "from", "", "create context from a named context")
return cmd
}
@ -70,20 +71,17 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
// RunCreate creates a Docker context
func RunCreate(cli command.Cli, o *CreateOptions) error {
s := cli.ContextStore()
if err := checkContextNameForCreation(s, o.Name); err != nil {
return err
}
stackOrchestrator, err := command.NormalizeOrchestrator(o.DefaultStackOrchestrator)
err := checkContextNameForCreation(s, o.Name)
if err != nil {
return errors.Wrap(err, "unable to parse default-stack-orchestrator")
return err
}
switch {
case o.From == "" && o.Docker == nil && o.Kubernetes == nil:
err = createFromExistingContext(s, cli.CurrentContext(), stackOrchestrator, o)
err = createFromExistingContext(s, cli.CurrentContext(), o)
case o.From != "":
err = createFromExistingContext(s, o.From, stackOrchestrator, o)
err = createFromExistingContext(s, o.From, o)
default:
err = createNewContext(o, stackOrchestrator, cli, s)
err = createNewContext(o, cli, s)
}
if err == nil {
fmt.Fprintln(cli.Out(), o.Name)
@ -92,11 +90,11 @@ func RunCreate(cli command.Cli, o *CreateOptions) error {
return err
}
func createNewContext(o *CreateOptions, stackOrchestrator command.Orchestrator, cli command.Cli, s store.Writer) error {
func createNewContext(o *CreateOptions, cli command.Cli, s store.Writer) error {
if o.Docker == nil {
return errors.New("docker endpoint configuration is required")
}
contextMetadata := newContextMetadata(stackOrchestrator, o)
contextMetadata := newContextMetadata(o)
contextTLSData := store.ContextTLSData{
Endpoints: make(map[string]store.EndpointTLSData),
}
@ -108,10 +106,7 @@ func createNewContext(o *CreateOptions, stackOrchestrator command.Orchestrator,
if dockerTLS != nil {
contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerTLS
}
if len(o.Kubernetes) != 0 {
logrus.Warn("kubernetes orchestrator is deprecated")
}
if err := validateEndpointsAndOrchestrator(contextMetadata); err != nil {
if err := validateEndpoints(contextMetadata); err != nil {
return err
}
if err := s.CreateOrUpdate(contextMetadata); err != nil {
@ -136,26 +131,24 @@ func checkContextNameForCreation(s store.Reader, name string) error {
return nil
}
func createFromExistingContext(s store.ReaderWriter, fromContextName string, stackOrchestrator command.Orchestrator, o *CreateOptions) error {
func createFromExistingContext(s store.ReaderWriter, fromContextName string, o *CreateOptions) error {
if len(o.Docker) != 0 || len(o.Kubernetes) != 0 {
return errors.New("cannot use --docker or --kubernetes flags when --from is set")
}
reader := store.Export(fromContextName, &descriptionAndOrchestratorStoreDecorator{
Reader: s,
description: o.Description,
orchestrator: stackOrchestrator,
reader := store.Export(fromContextName, &descriptionDecorator{
Reader: s,
description: o.Description,
})
defer reader.Close()
return store.Import(o.Name, s, reader)
}
type descriptionAndOrchestratorStoreDecorator struct {
type descriptionDecorator struct {
store.Reader
description string
orchestrator command.Orchestrator
description string
}
func (d *descriptionAndOrchestratorStoreDecorator) GetMetadata(name string) (store.Metadata, error) {
func (d *descriptionDecorator) GetMetadata(name string) (store.Metadata, error) {
c, err := d.Reader.GetMetadata(name)
if err != nil {
return c, err
@ -167,19 +160,15 @@ func (d *descriptionAndOrchestratorStoreDecorator) GetMetadata(name string) (sto
if d.description != "" {
typedContext.Description = d.description
}
if d.orchestrator != command.Orchestrator("") {
typedContext.StackOrchestrator = d.orchestrator
}
c.Metadata = typedContext
return c, nil
}
func newContextMetadata(stackOrchestrator command.Orchestrator, o *CreateOptions) store.Metadata {
func newContextMetadata(o *CreateOptions) store.Metadata {
return store.Metadata{
Endpoints: make(map[string]interface{}),
Metadata: command.DockerContext{
Description: o.Description,
StackOrchestrator: stackOrchestrator,
Description: o.Description,
},
Name: o.Name,
}

View File

@ -12,7 +12,6 @@ import (
"github.com/docker/cli/cli/context/store"
"github.com/docker/cli/internal/test"
"gotest.tools/v3/assert"
"gotest.tools/v3/env"
)
func makeFakeCli(t *testing.T, opts ...func(*test.FakeCli)) (*test.FakeCli, func()) {
@ -33,8 +32,7 @@ func makeFakeCli(t *testing.T, opts ...func(*test.FakeCli)) (*test.FakeCli, func
},
},
Metadata: command.DockerContext{
Description: "",
StackOrchestrator: command.OrchestratorSwarm,
Description: "",
},
Name: command.DefaultContextName,
},
@ -59,7 +57,7 @@ func withCliConfig(configFile *configfile.ConfigFile) func(*test.FakeCli) {
}
}
func TestCreateInvalids(t *testing.T) {
func TestCreate(t *testing.T) {
cli, cleanup := makeFakeCli(t)
defer cleanup()
assert.NilError(t, cli.ContextStore().CreateOrUpdate(store.Metadata{Name: "existing-context"}))
@ -102,7 +100,7 @@ func TestCreateInvalids(t *testing.T) {
Name: "invalid-orchestrator",
DefaultStackOrchestrator: "invalid",
},
expecterErr: `specified orchestrator "invalid" is invalid, please use either kubernetes, swarm or all`,
expecterErr: "",
},
{
options: CreateOptions{
@ -158,37 +156,25 @@ func TestCreateOrchestratorEmpty(t *testing.T) {
func TestCreateFromContext(t *testing.T) {
cases := []struct {
name string
description string
orchestrator string
expectedDescription string
docker map[string]string
kubernetes map[string]string
expectedOrchestrator command.Orchestrator
name string
description string
expectedDescription string
docker map[string]string
kubernetes map[string]string
}{
{
name: "no-override",
expectedDescription: "original description",
expectedOrchestrator: command.OrchestratorSwarm,
name: "no-override",
expectedDescription: "original description",
},
{
name: "override-description",
description: "new description",
expectedDescription: "new description",
expectedOrchestrator: command.OrchestratorSwarm,
},
{
name: "override-orchestrator",
orchestrator: "kubernetes",
expectedDescription: "original description",
expectedOrchestrator: command.OrchestratorKubernetes,
name: "override-description",
description: "new description",
expectedDescription: "new description",
},
}
cli, cleanup := makeFakeCli(t)
defer cleanup()
revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
defer revert()
cli.ResetOutputBuffers()
assert.NilError(t, RunCreate(cli, &CreateOptions{
Name: "original",
@ -196,10 +182,6 @@ func TestCreateFromContext(t *testing.T) {
Docker: map[string]string{
keyHost: "tcp://42.42.42.42:2375",
},
Kubernetes: map[string]string{
keyFrom: "default",
},
DefaultStackOrchestrator: "swarm",
}))
assertContextCreateLogging(t, cli, "original")
@ -210,10 +192,6 @@ func TestCreateFromContext(t *testing.T) {
Docker: map[string]string{
keyHost: "tcp://24.24.24.24:2375",
},
Kubernetes: map[string]string{
keyFrom: "default",
},
DefaultStackOrchestrator: "swarm",
}))
assertContextCreateLogging(t, cli, "dummy")
@ -224,12 +202,10 @@ func TestCreateFromContext(t *testing.T) {
t.Run(c.name, func(t *testing.T) {
cli.ResetOutputBuffers()
err := RunCreate(cli, &CreateOptions{
From: "original",
Name: c.name,
Description: c.description,
DefaultStackOrchestrator: c.orchestrator,
Docker: c.docker,
Kubernetes: c.kubernetes,
From: "original",
Name: c.name,
Description: c.description,
Docker: c.docker,
})
assert.NilError(t, err)
assertContextCreateLogging(t, cli, c.name)
@ -240,7 +216,6 @@ func TestCreateFromContext(t *testing.T) {
dockerEndpoint, err := docker.EndpointFromContext(newContext)
assert.NilError(t, err)
assert.Equal(t, newContextTyped.Description, c.expectedDescription)
assert.Equal(t, newContextTyped.StackOrchestrator, c.expectedOrchestrator)
assert.Equal(t, dockerEndpoint.Host, "tcp://42.42.42.42:2375")
})
}
@ -248,29 +223,24 @@ func TestCreateFromContext(t *testing.T) {
func TestCreateFromCurrent(t *testing.T) {
cases := []struct {
name string
description string
orchestrator string
expectedDescription string
expectedOrchestrator command.Orchestrator
name string
description string
orchestrator string
expectedDescription string
}{
{
name: "no-override",
expectedDescription: "original description",
expectedOrchestrator: command.OrchestratorSwarm,
name: "no-override",
expectedDescription: "original description",
},
{
name: "override-description",
description: "new description",
expectedDescription: "new description",
expectedOrchestrator: command.OrchestratorSwarm,
name: "override-description",
description: "new description",
expectedDescription: "new description",
},
}
cli, cleanup := makeFakeCli(t)
defer cleanup()
revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
defer revert()
cli.ResetOutputBuffers()
assert.NilError(t, RunCreate(cli, &CreateOptions{
Name: "original",
@ -278,10 +248,6 @@ func TestCreateFromCurrent(t *testing.T) {
Docker: map[string]string{
keyHost: "tcp://42.42.42.42:2375",
},
Kubernetes: map[string]string{
keyFrom: "default",
},
DefaultStackOrchestrator: "swarm",
}))
assertContextCreateLogging(t, cli, "original")
@ -292,9 +258,8 @@ func TestCreateFromCurrent(t *testing.T) {
t.Run(c.name, func(t *testing.T) {
cli.ResetOutputBuffers()
err := RunCreate(cli, &CreateOptions{
Name: c.name,
Description: c.description,
DefaultStackOrchestrator: c.orchestrator,
Name: c.name,
Description: c.description,
})
assert.NilError(t, err)
assertContextCreateLogging(t, cli, c.name)
@ -305,7 +270,6 @@ func TestCreateFromCurrent(t *testing.T) {
dockerEndpoint, err := docker.EndpointFromContext(newContext)
assert.NilError(t, err)
assert.Equal(t, newContextTyped.Description, c.expectedDescription)
assert.Equal(t, newContextTyped.StackOrchestrator, c.expectedOrchestrator)
assert.Equal(t, dockerEndpoint.Host, "tcp://42.42.42.42:2375")
})
}

View File

@ -59,11 +59,10 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
meta.Description = "Current DOCKER_HOST based configuration"
}
desc := formatter.ClientContext{
Name: rawMeta.Name,
Current: rawMeta.Name == curContext,
Description: meta.Description,
StackOrchestrator: string(meta.StackOrchestrator),
DockerEndpoint: dockerEndpoint.Host,
Name: rawMeta.Name,
Current: rawMeta.Name == curContext,
Description: meta.Description,
DockerEndpoint: dockerEndpoint.Host,
}
contexts = append(contexts, &desc)
}

View File

@ -2,8 +2,7 @@
{
"Name": "current",
"Metadata": {
"Description": "description of current",
"StackOrchestrator": "all"
"Description": "description of current"
},
"Endpoints": {
"docker": {

View File

@ -1,5 +1,5 @@
NAME DESCRIPTION DOCKER ENDPOINT ORCHESTRATOR
current * description of current https://someswarmserver.example.com all
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
other description of other https://someswarmserver.example.com all
unset description of unset https://someswarmserver.example.com
NAME DESCRIPTION DOCKER ENDPOINT
current * description of current https://someswarmserver.example.com
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock
other description of other https://someswarmserver.example.com
unset description of unset https://someswarmserver.example.com

View File

@ -3,7 +3,6 @@ package context
import (
"bytes"
"fmt"
"github.com/sirupsen/logrus"
"text/tabwriter"
"github.com/docker/cli/cli"
@ -58,10 +57,12 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
"default-stack-orchestrator", "",
"Default orchestrator for stack operations to use with this context (swarm|kubernetes|all)")
flags.SetAnnotation("default-stack-orchestrator", "deprecated", nil)
flags.MarkDeprecated("default-stack-orchestrator", "option will be ignored")
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint")
flags.StringToStringVar(&opts.Kubernetes, "kubernetes", nil, "set the kubernetes endpoint")
flags.SetAnnotation("kubernetes", "kubernetes", nil)
flags.SetAnnotation("kubernetes", "deprecated", nil)
flags.MarkDeprecated("kubernetes", "option will be ignored")
return cmd
}
@ -79,13 +80,6 @@ func RunUpdate(cli command.Cli, o *UpdateOptions) error {
if err != nil {
return err
}
if o.DefaultStackOrchestrator != "" {
stackOrchestrator, err := command.NormalizeOrchestrator(o.DefaultStackOrchestrator)
if err != nil {
return errors.Wrap(err, "unable to parse default-stack-orchestrator")
}
dockerContext.StackOrchestrator = stackOrchestrator
}
if o.Description != "" {
dockerContext.Description = o.Description
}
@ -102,10 +96,7 @@ func RunUpdate(cli command.Cli, o *UpdateOptions) error {
c.Endpoints[docker.DockerEndpoint] = dockerEP
tlsDataToReset[docker.DockerEndpoint] = dockerTLS
}
if len(o.Kubernetes) != 0 {
logrus.Warn("kubernetes orchestrator is deprecated")
}
if err := validateEndpointsAndOrchestrator(c); err != nil {
if err := validateEndpoints(c); err != nil {
return err
}
if err := s.CreateOrUpdate(c); err != nil {
@ -122,7 +113,7 @@ func RunUpdate(cli command.Cli, o *UpdateOptions) error {
return nil
}
func validateEndpointsAndOrchestrator(c store.Metadata) error {
func validateEndpoints(c store.Metadata) error {
_, err := command.GetDockerContext(c)
return err
}

View File

@ -28,7 +28,6 @@ func TestUpdateDescriptionOnly(t *testing.T) {
assert.NilError(t, err)
dc, err := command.GetDockerContext(c)
assert.NilError(t, err)
assert.Equal(t, dc.StackOrchestrator, command.OrchestratorSwarm)
assert.Equal(t, dc.Description, "description")
assert.Equal(t, "test\n", cli.OutBuffer().String())
@ -49,7 +48,6 @@ func TestUpdateDockerOnly(t *testing.T) {
assert.NilError(t, err)
dc, err := command.GetDockerContext(c)
assert.NilError(t, err)
assert.Equal(t, dc.StackOrchestrator, command.OrchestratorSwarm)
assert.Equal(t, dc.Description, "description of test")
assert.Check(t, cmp.Contains(c.Endpoints, docker.DockerEndpoint))
assert.Equal(t, c.Endpoints[docker.DockerEndpoint].(docker.EndpointMeta).Host, "tcp://some-host")

View File

@ -9,19 +9,17 @@ import (
func TestDockerContextMetadataKeepAdditionalFields(t *testing.T) {
c := DockerContext{
Description: "test",
StackOrchestrator: OrchestratorSwarm,
Description: "test",
AdditionalFields: map[string]interface{}{
"foo": "bar",
},
}
jsonBytes, err := json.Marshal(c)
assert.NilError(t, err)
assert.Equal(t, `{"Description":"test","StackOrchestrator":"swarm","foo":"bar"}`, string(jsonBytes))
assert.Equal(t, `{"Description":"test","foo":"bar"}`, string(jsonBytes))
var c2 DockerContext
assert.NilError(t, json.Unmarshal(jsonBytes, &c2))
assert.Equal(t, c2.AdditionalFields["foo"], "bar")
assert.Equal(t, c2.StackOrchestrator, OrchestratorSwarm)
assert.Equal(t, c2.Description, "test")
}

View File

@ -41,23 +41,18 @@ type EndpointDefaultResolver interface {
// the lack of a default (e.g. because the config file which
// would contain it is missing). If there is no default then
// returns nil, nil, nil.
ResolveDefault(Orchestrator) (interface{}, *store.EndpointTLSData, error)
ResolveDefault() (interface{}, *store.EndpointTLSData, error)
}
// ResolveDefaultContext creates a Metadata for the current CLI invocation parameters
func ResolveDefaultContext(opts *cliflags.CommonOptions, config *configfile.ConfigFile, storeconfig store.Config, stderr io.Writer) (*DefaultContext, error) {
stackOrchestrator, err := GetStackOrchestrator("", "", config.StackOrchestrator, stderr)
if err != nil {
return nil, err
}
contextTLSData := store.ContextTLSData{
Endpoints: make(map[string]store.EndpointTLSData),
}
contextMetadata := store.Metadata{
Endpoints: make(map[string]interface{}),
Metadata: DockerContext{
Description: "",
StackOrchestrator: stackOrchestrator,
Description: "",
},
Name: DefaultContextName,
}
@ -77,7 +72,7 @@ func ResolveDefaultContext(opts *cliflags.CommonOptions, config *configfile.Conf
}
ep := get()
if i, ok := ep.(EndpointDefaultResolver); ok {
meta, tls, err := i.ResolveDefault(stackOrchestrator)
meta, tls, err := i.ResolveDefault()
if err != nil {
return err
}

View File

@ -71,7 +71,6 @@ func TestDefaultContextInitializer(t *testing.T) {
}, cli.ConfigFile(), DefaultContextStoreConfig(), cli.Err())
assert.NilError(t, err)
assert.Equal(t, "default", ctx.Meta.Name)
assert.Equal(t, OrchestratorSwarm, ctx.Meta.Metadata.(DockerContext).StackOrchestrator)
assert.DeepEqual(t, "ssh://someswarmserver", ctx.Meta.Endpoints[docker.DockerEndpoint].(docker.EndpointMeta).Host)
golden.Assert(t, string(ctx.TLS.Endpoints[docker.DockerEndpoint].Files["ca.pem"]), "ca.pem")
}

View File

@ -2,11 +2,10 @@ package formatter
const (
// ClientContextTableFormat is the default client context format
ClientContextTableFormat = "table {{.Name}}{{if .Current}} *{{end}}\t{{.Description}}\t{{.DockerEndpoint}}\t{{.StackOrchestrator}}"
ClientContextTableFormat = "table {{.Name}}{{if .Current}} *{{end}}\t{{.Description}}\t{{.DockerEndpoint}}"
dockerEndpointHeader = "DOCKER ENDPOINT"
stackOrchestrastorHeader = "ORCHESTRATOR"
quietContextFormat = "{{.Name}}"
dockerEndpointHeader = "DOCKER ENDPOINT"
quietContextFormat = "{{.Name}}"
)
// NewClientContextFormat returns a Format for rendering using a Context
@ -22,11 +21,10 @@ func NewClientContextFormat(source string, quiet bool) Format {
// ClientContext is a context for display
type ClientContext struct {
Name string
Description string
DockerEndpoint string
StackOrchestrator string
Current bool
Name string
Description string
DockerEndpoint string
Current bool
}
// ClientContextWrite writes formatted contexts using the Context
@ -50,10 +48,9 @@ type clientContextContext struct {
func newClientContextContext() *clientContextContext {
ctx := clientContextContext{}
ctx.Header = SubHeaderContext{
"Name": NameHeader,
"Description": DescriptionHeader,
"DockerEndpoint": dockerEndpointHeader,
"StackOrchestrator": stackOrchestrastorHeader,
"Name": NameHeader,
"Description": DescriptionHeader,
"DockerEndpoint": dockerEndpointHeader,
}
return &ctx
}
@ -81,7 +78,3 @@ func (c *clientContextContext) DockerEndpoint() string {
func (c *clientContextContext) KubernetesEndpoint() string {
return ""
}
func (c *clientContextContext) StackOrchestrator() string {
return c.c.StackOrchestrator
}

View File

@ -1,85 +0,0 @@
package command
import (
"fmt"
"io"
"os"
)
// Orchestrator type acts as an enum describing supported orchestrators.
type Orchestrator string
const (
// OrchestratorKubernetes orchestrator
OrchestratorKubernetes = Orchestrator("kubernetes")
// OrchestratorSwarm orchestrator
OrchestratorSwarm = Orchestrator("swarm")
// OrchestratorAll orchestrator
OrchestratorAll = Orchestrator("all")
orchestratorUnset = Orchestrator("")
defaultOrchestrator = OrchestratorSwarm
envVarDockerStackOrchestrator = "DOCKER_STACK_ORCHESTRATOR"
envVarDockerOrchestrator = "DOCKER_ORCHESTRATOR"
)
// HasKubernetes returns true if defined orchestrator has Kubernetes capabilities.
// Deprecated: support for kubernetes as orchestrator was removed.
func (o Orchestrator) HasKubernetes() bool {
return o == OrchestratorKubernetes || o == OrchestratorAll
}
// HasSwarm returns true if defined orchestrator has Swarm capabilities.
func (o Orchestrator) HasSwarm() bool {
return o == OrchestratorSwarm || o == OrchestratorAll
}
// HasAll returns true if defined orchestrator has both Swarm and Kubernetes capabilities.
func (o Orchestrator) HasAll() bool {
return o == OrchestratorAll
}
func normalize(value string) (Orchestrator, error) {
switch value {
case "kubernetes":
return OrchestratorKubernetes, nil
case "swarm":
return OrchestratorSwarm, nil
case "", "unset": // unset is the old value for orchestratorUnset. Keep accepting this for backward compat
return orchestratorUnset, nil
case "all":
return OrchestratorAll, nil
default:
return defaultOrchestrator, fmt.Errorf("specified orchestrator %q is invalid, please use either kubernetes, swarm or all", value)
}
}
// NormalizeOrchestrator parses an orchestrator value and checks if it is valid
func NormalizeOrchestrator(value string) (Orchestrator, error) {
return normalize(value)
}
// GetStackOrchestrator checks DOCKER_STACK_ORCHESTRATOR environment variable and configuration file
// orchestrator value and returns user defined Orchestrator.
func GetStackOrchestrator(flagValue, contextValue, globalDefault string, stderr io.Writer) (Orchestrator, error) {
// Check flag
if o, err := normalize(flagValue); o != orchestratorUnset {
return o, err
}
// Check environment variable
env := os.Getenv(envVarDockerStackOrchestrator)
if env == "" && os.Getenv(envVarDockerOrchestrator) != "" {
fmt.Fprintf(stderr, "WARNING: experimental environment variable %s is set. Please use %s instead\n", envVarDockerOrchestrator, envVarDockerStackOrchestrator)
}
if o, err := normalize(env); o != orchestratorUnset {
return o, err
}
if o, err := normalize(contextValue); o != orchestratorUnset {
return o, err
}
if o, err := normalize(globalDefault); o != orchestratorUnset {
return o, err
}
// Nothing set, use default orchestrator
return defaultOrchestrator, nil
}

View File

@ -1,67 +0,0 @@
package command
import (
"io/ioutil"
"testing"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/env"
)
func TestOrchestratorSwitch(t *testing.T) {
var testcases = []struct {
doc string
globalOrchestrator string
envOrchestrator string
flagOrchestrator string
contextOrchestrator string
expectedOrchestrator string
expectedSwarm bool
}{
{
doc: "default",
expectedOrchestrator: "swarm",
expectedSwarm: true,
},
{
doc: "allOrchestratorFlag",
flagOrchestrator: "all",
expectedOrchestrator: "all",
expectedSwarm: true,
},
{
doc: "contextOverridesConfigFile",
globalOrchestrator: "kubernetes",
contextOrchestrator: "swarm",
expectedOrchestrator: "swarm",
expectedSwarm: true,
},
{
doc: "envOverridesConfigFile",
globalOrchestrator: "kubernetes",
envOrchestrator: "swarm",
expectedOrchestrator: "swarm",
expectedSwarm: true,
},
{
doc: "flagOverridesEnv",
envOrchestrator: "kubernetes",
flagOrchestrator: "swarm",
expectedOrchestrator: "swarm",
expectedSwarm: true,
},
}
for _, testcase := range testcases {
t.Run(testcase.doc, func(t *testing.T) {
if testcase.envOrchestrator != "" {
defer env.Patch(t, "DOCKER_STACK_ORCHESTRATOR", testcase.envOrchestrator)()
}
orchestrator, err := GetStackOrchestrator(testcase.flagOrchestrator, testcase.contextOrchestrator, testcase.globalOrchestrator, ioutil.Discard)
assert.NilError(t, err)
assert.Check(t, is.Equal(testcase.expectedSwarm, orchestrator.HasSwarm()))
assert.Check(t, is.Equal(testcase.expectedOrchestrator, string(orchestrator)))
})
}
}

View File

@ -1,45 +1,20 @@
package stack
import (
"errors"
"fmt"
"strings"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
type commonOptions struct {
orchestrator command.Orchestrator
}
func (o *commonOptions) Orchestrator() command.Orchestrator {
if o == nil {
return command.OrchestratorSwarm
}
return o.orchestrator
}
// NewStackCommand returns a cobra command for `stack` subcommands
func NewStackCommand(dockerCli command.Cli) *cobra.Command {
var opts commonOptions
cmd := &cobra.Command{
Use: "stack [OPTIONS]",
Short: "Manage Docker stacks",
Args: cli.NoArgs,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
orchestrator, err := getOrchestrator(dockerCli, cmd)
if err != nil {
return err
}
opts.orchestrator = orchestrator
hideOrchestrationFlags(cmd, orchestrator)
return checkSupportedFlag(cmd, orchestrator)
},
RunE: command.ShowHelp(dockerCli.Err()),
RunE: command.ShowHelp(dockerCli.Err()),
Annotations: map[string]string{
"version": "1.25",
},
@ -50,62 +25,18 @@ func NewStackCommand(dockerCli command.Cli) *cobra.Command {
fmt.Fprintln(dockerCli.Err(), err)
return
}
if err := cmd.PersistentPreRunE(c, args); err != nil {
fmt.Fprintln(dockerCli.Err(), err)
return
}
hideOrchestrationFlags(c, opts.orchestrator)
defaultHelpFunc(c, args)
})
cmd.AddCommand(
newDeployCommand(dockerCli, &opts),
newListCommand(dockerCli, &opts),
newPsCommand(dockerCli, &opts),
newRemoveCommand(dockerCli, &opts),
newServicesCommand(dockerCli, &opts),
newDeployCommand(dockerCli),
newListCommand(dockerCli),
newPsCommand(dockerCli),
newRemoveCommand(dockerCli),
newServicesCommand(dockerCli),
)
flags := cmd.PersistentFlags()
flags.String("orchestrator", "", "Orchestrator to use (swarm|all)")
flags.SetAnnotation("orchestrator", "deprecated", nil)
flags.MarkDeprecated("orchestrator", "option will be ignored")
return cmd
}
func getOrchestrator(dockerCli command.Cli, cmd *cobra.Command) (command.Orchestrator, error) {
var orchestratorFlag string
if o, err := cmd.Flags().GetString("orchestrator"); err == nil {
orchestratorFlag = o
}
return dockerCli.StackOrchestrator(orchestratorFlag)
}
func hideOrchestrationFlags(cmd *cobra.Command, orchestrator command.Orchestrator) {
cmd.Flags().VisitAll(func(f *pflag.Flag) {
if _, ok := f.Annotations["swarm"]; ok && !orchestrator.HasSwarm() {
f.Hidden = true
}
})
for _, subcmd := range cmd.Commands() {
hideOrchestrationFlags(subcmd, orchestrator)
}
}
func checkSupportedFlag(cmd *cobra.Command, orchestrator command.Orchestrator) error {
errs := []string{}
cmd.Flags().VisitAll(func(f *pflag.Flag) {
if !f.Changed {
return
}
if _, ok := f.Annotations["swarm"]; ok && !orchestrator.HasSwarm() {
errs = append(errs, fmt.Sprintf(`"--%s" is only supported on a Docker cli with swarm features enabled`, f.Name))
}
})
for _, subcmd := range cmd.Commands() {
if err := checkSupportedFlag(subcmd, orchestrator); err != nil {
errs = append(errs, err.Error())
}
}
if len(errs) > 0 {
return errors.New(strings.Join(errs, "\n"))
}
return nil
}

View File

@ -11,7 +11,7 @@ import (
"github.com/spf13/pflag"
)
func newDeployCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command {
func newDeployCommand(dockerCli command.Cli) *cobra.Command {
var opts options.Deploy
cmd := &cobra.Command{
@ -28,7 +28,7 @@ func newDeployCommand(dockerCli command.Cli, common *commonOptions) *cobra.Comma
if err != nil {
return err
}
return RunDeploy(dockerCli, cmd.Flags(), config, common.Orchestrator(), opts)
return RunDeploy(dockerCli, cmd.Flags(), config, opts)
},
}
@ -47,7 +47,7 @@ func newDeployCommand(dockerCli command.Cli, common *commonOptions) *cobra.Comma
return cmd
}
// RunDeploy performs a stack deploy against the specified orchestrator
func RunDeploy(dockerCli command.Cli, flags *pflag.FlagSet, config *composetypes.Config, commonOrchestrator command.Orchestrator, opts options.Deploy) error {
// RunDeploy performs a stack deploy against the specified swarm cluster
func RunDeploy(dockerCli command.Cli, flags *pflag.FlagSet, config *composetypes.Config, opts options.Deploy) error {
return swarm.RunDeploy(dockerCli, opts, config)
}

View File

@ -9,7 +9,7 @@ import (
)
func TestDeployWithEmptyName(t *testing.T) {
cmd := newDeployCommand(test.NewFakeCli(&fakeClient{}), nil)
cmd := newDeployCommand(test.NewFakeCli(&fakeClient{}))
cmd.SetArgs([]string{"' '"})
cmd.SetOut(ioutil.Discard)

View File

@ -8,10 +8,9 @@ import (
const (
// SwarmStackTableFormat is the default Swarm stack format
SwarmStackTableFormat formatter.Format = "table {{.Name}}\t{{.Services}}\t{{.Orchestrator}}"
SwarmStackTableFormat formatter.Format = "table {{.Name}}\t{{.Services}}"
stackServicesHeader = "SERVICES"
stackOrchestrastorHeader = "ORCHESTRATOR"
stackServicesHeader = "SERVICES"
// TableFormatKey is an alias for formatter.TableFormatKey
TableFormatKey = formatter.TableFormatKey
@ -29,8 +28,6 @@ type Stack struct {
Name string
// Services is the number of the services
Services int
// Orchestrator is the platform where the stack is deployed
Orchestrator string
}
// StackWrite writes formatted stacks using the Context
@ -54,9 +51,8 @@ type stackContext struct {
func newStackContext() *stackContext {
stackCtx := stackContext{}
stackCtx.Header = formatter.SubHeaderContext{
"Name": formatter.NameHeader,
"Services": stackServicesHeader,
"Orchestrator": stackOrchestrastorHeader,
"Name": formatter.NameHeader,
"Services": stackServicesHeader,
}
return &stackCtx
}
@ -72,7 +68,3 @@ func (s *stackContext) Name() string {
func (s *stackContext) Services() string {
return strconv.Itoa(s.s.Services)
}
func (s *stackContext) Orchestrator() string {
return s.s.Orchestrator
}

View File

@ -27,9 +27,9 @@ func TestStackContextWrite(t *testing.T) {
// Table format
{
formatter.Context{Format: SwarmStackTableFormat},
`NAME SERVICES ORCHESTRATOR
baz 2 orchestrator1
bar 1 orchestrator2
`NAME SERVICES
baz 2
bar 1
`,
},
{
@ -49,8 +49,8 @@ bar
}
stacks := []*Stack{
{Name: "baz", Services: 2, Orchestrator: "orchestrator1"},
{Name: "bar", Services: 1, Orchestrator: "orchestrator2"},
{Name: "baz", Services: 2},
{Name: "bar", Services: 1},
}
for _, tc := range cases {
tc := tc

View File

@ -12,7 +12,7 @@ import (
"github.com/spf13/cobra"
)
func newListCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command {
func newListCommand(dockerCli command.Cli) *cobra.Command {
opts := options.List{}
cmd := &cobra.Command{
@ -21,7 +21,7 @@ func newListCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command
Short: "List stacks",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return RunList(cmd, dockerCli, opts, common.orchestrator)
return RunList(cmd, dockerCli, opts)
},
}
@ -30,16 +30,14 @@ func newListCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command
return cmd
}
// RunList performs a stack list against the specified orchestrator
func RunList(cmd *cobra.Command, dockerCli command.Cli, opts options.List, orchestrator command.Orchestrator) error {
// RunList performs a stack list against the specified swarm cluster
func RunList(cmd *cobra.Command, dockerCli command.Cli, opts options.List) error {
stacks := []*formatter.Stack{}
if orchestrator.HasSwarm() {
ss, err := swarm.GetStacks(dockerCli)
if err != nil {
return err
}
stacks = append(stacks, ss...)
ss, err := swarm.GetStacks(dockerCli)
if err != nil {
return err
}
stacks = append(stacks, ss...)
return format(dockerCli, opts, stacks)
}

View File

@ -4,7 +4,6 @@ import (
"io/ioutil"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/internal/test"
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
"github.com/docker/docker/api/types"
@ -14,10 +13,6 @@ import (
"gotest.tools/v3/golden"
)
var (
orchestrator = commonOptions{orchestrator: command.OrchestratorSwarm}
)
func TestListErrors(t *testing.T) {
testCases := []struct {
args []string
@ -52,7 +47,7 @@ func TestListErrors(t *testing.T) {
for _, tc := range testCases {
cmd := newListCommand(test.NewFakeCli(&fakeClient{
serviceListFunc: tc.serviceListFunc,
}), &orchestrator)
}))
cmd.SetArgs(tc.args)
cmd.SetOut(ioutil.Discard)
for key, value := range tc.flags {
@ -118,7 +113,7 @@ func TestStackList(t *testing.T) {
return services, nil
},
})
cmd := newListCommand(cli, &orchestrator)
cmd := newListCommand(cli)
for key, value := range tc.flags {
cmd.Flags().Set(key, value)
}

View File

@ -10,7 +10,7 @@ import (
"github.com/spf13/pflag"
)
func newPsCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command {
func newPsCommand(dockerCli command.Cli) *cobra.Command {
opts := options.PS{Filter: cliopts.NewFilterOpt()}
cmd := &cobra.Command{
@ -22,7 +22,7 @@ func newPsCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command {
if err := validateStackName(opts.Namespace); err != nil {
return err
}
return RunPs(dockerCli, cmd.Flags(), common.Orchestrator(), opts)
return RunPs(dockerCli, cmd.Flags(), opts)
},
}
flags := cmd.Flags()
@ -34,7 +34,7 @@ func newPsCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command {
return cmd
}
// RunPs performs a stack ps against the specified orchestrator
func RunPs(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.PS) error {
// RunPs performs a stack ps against the specified swarm cluster
func RunPs(dockerCli command.Cli, flags *pflag.FlagSet, opts options.PS) error {
return swarm.RunPS(dockerCli, opts)
}

View File

@ -43,7 +43,7 @@ func TestStackPsErrors(t *testing.T) {
for _, tc := range testCases {
cmd := newPsCommand(test.NewFakeCli(&fakeClient{
taskListFunc: tc.taskListFunc,
}), &orchestrator)
}))
cmd.SetArgs(tc.args)
cmd.SetOut(ioutil.Discard)
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
@ -164,7 +164,7 @@ func TestStackPs(t *testing.T) {
})
cli.SetConfigFile(&tc.config)
cmd := newPsCommand(cli, &orchestrator)
cmd := newPsCommand(cli)
cmd.SetArgs(tc.args)
for key, value := range tc.flags {
cmd.Flags().Set(key, value)

View File

@ -9,7 +9,7 @@ import (
"github.com/spf13/pflag"
)
func newRemoveCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command {
func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
var opts options.Remove
cmd := &cobra.Command{
@ -22,13 +22,13 @@ func newRemoveCommand(dockerCli command.Cli, common *commonOptions) *cobra.Comma
if err := validateStackNames(opts.Namespaces); err != nil {
return err
}
return RunRemove(dockerCli, cmd.Flags(), common.Orchestrator(), opts)
return RunRemove(dockerCli, cmd.Flags(), opts)
},
}
return cmd
}
// RunRemove performs a stack remove against the specified orchestrator
func RunRemove(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Remove) error {
// RunRemove performs a stack remove against the specified swarm cluster
func RunRemove(dockerCli command.Cli, flags *pflag.FlagSet, opts options.Remove) error {
return swarm.RunRemove(dockerCli, opts)
}

View File

@ -42,7 +42,7 @@ func fakeClientForRemoveStackTest(version string) *fakeClient {
}
func TestRemoveWithEmptyName(t *testing.T) {
cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}), &orchestrator)
cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}))
cmd.SetArgs([]string{"good", "' '", "alsogood"})
cmd.SetOut(ioutil.Discard)
@ -51,7 +51,7 @@ func TestRemoveWithEmptyName(t *testing.T) {
func TestRemoveStackVersion124DoesNotRemoveConfigsOrSecrets(t *testing.T) {
client := fakeClientForRemoveStackTest("1.24")
cmd := newRemoveCommand(test.NewFakeCli(client), &orchestrator)
cmd := newRemoveCommand(test.NewFakeCli(client))
cmd.SetArgs([]string{"foo", "bar"})
assert.NilError(t, cmd.Execute())
@ -63,7 +63,7 @@ func TestRemoveStackVersion124DoesNotRemoveConfigsOrSecrets(t *testing.T) {
func TestRemoveStackVersion125DoesNotRemoveConfigs(t *testing.T) {
client := fakeClientForRemoveStackTest("1.25")
cmd := newRemoveCommand(test.NewFakeCli(client), &orchestrator)
cmd := newRemoveCommand(test.NewFakeCli(client))
cmd.SetArgs([]string{"foo", "bar"})
assert.NilError(t, cmd.Execute())
@ -75,7 +75,7 @@ func TestRemoveStackVersion125DoesNotRemoveConfigs(t *testing.T) {
func TestRemoveStackVersion130RemovesEverything(t *testing.T) {
client := fakeClientForRemoveStackTest("1.30")
cmd := newRemoveCommand(test.NewFakeCli(client), &orchestrator)
cmd := newRemoveCommand(test.NewFakeCli(client))
cmd.SetArgs([]string{"foo", "bar"})
assert.NilError(t, cmd.Execute())
@ -106,7 +106,7 @@ func TestRemoveStackSkipEmpty(t *testing.T) {
configs: allConfigs,
}
fakeCli := test.NewFakeCli(fakeClient)
cmd := newRemoveCommand(fakeCli, &orchestrator)
cmd := newRemoveCommand(fakeCli)
cmd.SetArgs([]string{"foo", "bar"})
assert.NilError(t, cmd.Execute())
@ -154,7 +154,7 @@ func TestRemoveContinueAfterError(t *testing.T) {
return nil
},
}
cmd := newRemoveCommand(test.NewFakeCli(cli), &orchestrator)
cmd := newRemoveCommand(test.NewFakeCli(cli))
cmd.SetOut(ioutil.Discard)
cmd.SetArgs([]string{"foo", "bar"})

View File

@ -17,7 +17,7 @@ import (
"github.com/spf13/pflag"
)
func newServicesCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command {
func newServicesCommand(dockerCli command.Cli) *cobra.Command {
opts := options.Services{Filter: cliopts.NewFilterOpt()}
cmd := &cobra.Command{
@ -29,7 +29,7 @@ func newServicesCommand(dockerCli command.Cli, common *commonOptions) *cobra.Com
if err := validateStackName(opts.Namespace); err != nil {
return err
}
return RunServices(dockerCli, cmd.Flags(), common.Orchestrator(), opts)
return RunServices(dockerCli, cmd.Flags(), opts)
},
}
flags := cmd.Flags()
@ -39,17 +39,17 @@ func newServicesCommand(dockerCli command.Cli, common *commonOptions) *cobra.Com
return cmd
}
// RunServices performs a stack services against the specified orchestrator
func RunServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) error {
services, err := GetServices(dockerCli, flags, commonOrchestrator, opts)
// RunServices performs a stack services against the specified swarm cluster
func RunServices(dockerCli command.Cli, flags *pflag.FlagSet, opts options.Services) error {
services, err := GetServices(dockerCli, flags, opts)
if err != nil {
return err
}
return formatWrite(dockerCli, services, opts)
}
// GetServices returns the services for the specified orchestrator
func GetServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) ([]swarmtypes.Service, error) {
// GetServices returns the services for the specified swarm cluster
func GetServices(dockerCli command.Cli, flags *pflag.FlagSet, opts options.Services) ([]swarmtypes.Service, error) {
return swarm.GetServices(dockerCli, opts)
}

View File

@ -74,7 +74,7 @@ func TestStackServicesErrors(t *testing.T) {
nodeListFunc: tc.nodeListFunc,
taskListFunc: tc.taskListFunc,
})
cmd := newServicesCommand(cli, &orchestrator)
cmd := newServicesCommand(cli)
cmd.SetArgs(tc.args)
for key, value := range tc.flags {
cmd.Flags().Set(key, value)
@ -86,7 +86,7 @@ func TestStackServicesErrors(t *testing.T) {
}
func TestRunServicesWithEmptyName(t *testing.T) {
cmd := newServicesCommand(test.NewFakeCli(&fakeClient{}), &orchestrator)
cmd := newServicesCommand(test.NewFakeCli(&fakeClient{}))
cmd.SetArgs([]string{"' '"})
cmd.SetOut(ioutil.Discard)
@ -99,7 +99,7 @@ func TestStackServicesEmptyServiceList(t *testing.T) {
return []swarm.Service{}, nil
},
})
cmd := newServicesCommand(fakeCli, &orchestrator)
cmd := newServicesCommand(fakeCli)
cmd.SetArgs([]string{"foo"})
assert.NilError(t, cmd.Execute())
assert.Check(t, is.Equal("", fakeCli.OutBuffer().String()))
@ -112,7 +112,7 @@ func TestStackServicesWithQuietOption(t *testing.T) {
return []swarm.Service{*Service(ServiceID("id-foo"))}, nil
},
})
cmd := newServicesCommand(cli, &orchestrator)
cmd := newServicesCommand(cli)
cmd.Flags().Set("quiet", "true")
cmd.SetArgs([]string{"foo"})
assert.NilError(t, cmd.Execute())
@ -127,7 +127,7 @@ func TestStackServicesWithFormat(t *testing.T) {
}, nil
},
})
cmd := newServicesCommand(cli, &orchestrator)
cmd := newServicesCommand(cli)
cmd.SetArgs([]string{"foo"})
cmd.Flags().Set("format", "{{ .Name }}")
assert.NilError(t, cmd.Execute())
@ -145,7 +145,7 @@ func TestStackServicesWithConfigFormat(t *testing.T) {
cli.SetConfigFile(&configfile.ConfigFile{
ServicesFormat: "{{ .Name }}",
})
cmd := newServicesCommand(cli, &orchestrator)
cmd := newServicesCommand(cli)
cmd.SetArgs([]string{"foo"})
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "stack-services-with-config-format.golden")
@ -168,7 +168,7 @@ func TestStackServicesWithoutFormat(t *testing.T) {
)}, nil
},
})
cmd := newServicesCommand(cli, &orchestrator)
cmd := newServicesCommand(cli)
cmd.SetArgs([]string{"foo"})
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "stack-services-without-format.golden")

View File

@ -29,9 +29,8 @@ func GetStacks(dockerCli command.Cli) ([]*formatter.Stack, error) {
ztack, ok := m[name]
if !ok {
m[name] = &formatter.Stack{
Name: name,
Services: 1,
Orchestrator: "Swarm",
Name: name,
Services: 1,
}
} else {
ztack.Services++

View File

@ -1,4 +1,4 @@
NAME SERVICES ORCHESTRATOR
service-name-1-foo 1 Swarm
service-name-2-foo 1 Swarm
service-name-10-foo 1 Swarm
NAME SERVICES
service-name-1-foo 1
service-name-2-foo 1
service-name-10-foo 1

View File

@ -1,3 +1,3 @@
NAME SERVICES ORCHESTRATOR
service-name-bar 1 Swarm
service-name-foo 1 Swarm
NAME SERVICES
service-name-bar 1
service-name-foo 1

View File

@ -1,2 +1,2 @@
NAME SERVICES ORCHESTRATOR
service-name-foo 1 Swarm
NAME SERVICES
service-name-foo 1

View File

@ -44,7 +44,7 @@ func (s *tlsStore) getData(contextID contextdir, endpointName, filename string)
return data, nil
}
func (s *tlsStore) remove(contextID contextdir, endpointName, filename string) error {
func (s *tlsStore) remove(contextID contextdir, endpointName, filename string) error { // nolint:unused
err := os.Remove(s.filePath(contextID, endpointName, filename))
if os.IsNotExist(err) {
return nil

View File

@ -1,3 +1,3 @@
NAME DESCRIPTION DOCKER ENDPOINT ORCHESTRATOR
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
remote my remote cluster ssh://someserver kubernetes
NAME DESCRIPTION DOCKER ENDPOINT
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock
remote my remote cluster ssh://someserver

View File

@ -1,3 +1,3 @@
NAME DESCRIPTION DOCKER ENDPOINT ORCHESTRATOR
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
test unix:///var/run/docker.sock swarm
NAME DESCRIPTION DOCKER ENDPOINT
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock
test unix:///var/run/docker.sock

View File

@ -1,3 +1,3 @@
NAME DESCRIPTION DOCKER ENDPOINT ORCHESTRATOR
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
remote my remote cluster ssh://someserver kubernetes
NAME DESCRIPTION DOCKER ENDPOINT
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock
remote my remote cluster ssh://someserver

View File

@ -1,7 +1,6 @@
package stack
import (
"fmt"
"sort"
"strings"
"testing"
@ -12,23 +11,16 @@ import (
)
func TestDeployWithNamedResources(t *testing.T) {
t.Run("Swarm", func(t *testing.T) {
testDeployWithNamedResources(t, "swarm")
})
}
func testDeployWithNamedResources(t *testing.T, orchestrator string) {
stackname := fmt.Sprintf("test-stack-deploy-with-names-%s", orchestrator)
stackname := "test-stack-deploy-with-names"
composefile := golden.Path("stack-with-named-resources.yml")
result := icmd.RunCommand("docker", "stack", "deploy",
"-c", composefile, stackname, "--orchestrator", orchestrator)
defer icmd.RunCommand("docker", "stack", "rm",
"--orchestrator", orchestrator, stackname)
"-c", composefile, stackname)
defer icmd.RunCommand("docker", "stack", "rm", stackname)
result.Assert(t, icmd.Success)
stdout := strings.Split(result.Stdout(), "\n")
expected := strings.Split(string(golden.Get(t, fmt.Sprintf("stack-deploy-with-names-%s.golden", orchestrator))), "\n")
expected := strings.Split(string(golden.Get(t, "stack-deploy-with-names.golden")), "\n")
sort.Strings(stdout)
sort.Strings(expected)
assert.DeepEqual(t, stdout, expected)

View File

@ -1,7 +1,6 @@
package stack
import (
"fmt"
"testing"
"gotest.tools/v3/golden"
@ -9,13 +8,7 @@ import (
)
func TestStackDeployHelp(t *testing.T) {
t.Run("Swarm", func(t *testing.T) {
testStackDeployHelp(t, "swarm")
})
}
func testStackDeployHelp(t *testing.T, orchestrator string) {
result := icmd.RunCommand("docker", "stack", "deploy", "--orchestrator", orchestrator, "--help")
result := icmd.RunCommand("docker", "stack", "deploy", "--help")
result.Assert(t, icmd.Success)
golden.Assert(t, result.Stdout(), fmt.Sprintf("stack-deploy-help-%s.golden", orchestrator))
golden.Assert(t, result.Stdout(), "stack-deploy-help.golden")
}

View File

@ -1,7 +1,6 @@
package stack
import (
"fmt"
"strings"
"testing"
@ -14,40 +13,32 @@ import (
var pollSettings = environment.DefaultPollSettings
func TestRemove(t *testing.T) {
t.Run("Swarm", func(t *testing.T) {
testRemove(t, "swarm")
})
}
func testRemove(t *testing.T, orchestrator string) {
stackname := "test-stack-remove-" + orchestrator
deployFullStack(t, orchestrator, stackname)
defer cleanupFullStack(t, orchestrator, stackname)
result := icmd.RunCommand("docker", "stack", "rm",
stackname, "--orchestrator", orchestrator)
stackname := "test-stack-remove"
deployFullStack(t, stackname)
defer cleanupFullStack(t, stackname)
result := icmd.RunCommand("docker", "stack", "rm", stackname)
result.Assert(t, icmd.Expected{Err: icmd.None})
golden.Assert(t, result.Stdout(),
fmt.Sprintf("stack-remove-%s-success.golden", orchestrator))
golden.Assert(t, result.Stdout(), "stack-remove-success.golden")
}
func deployFullStack(t *testing.T, orchestrator, stackname string) {
func deployFullStack(t *testing.T, stackname string) {
// TODO: this stack should have full options not minimal options
result := icmd.RunCommand("docker", "stack", "deploy",
"--compose-file=./testdata/full-stack.yml", stackname, "--orchestrator", orchestrator)
"--compose-file=./testdata/full-stack.yml", stackname)
result.Assert(t, icmd.Success)
poll.WaitOn(t, taskCount(orchestrator, stackname, 2), pollSettings)
poll.WaitOn(t, taskCount(stackname, 2), pollSettings)
}
func cleanupFullStack(t *testing.T, orchestrator, stackname string) {
func cleanupFullStack(t *testing.T, stackname string) {
// FIXME(vdemeester) we shouldn't have to do that. it is hiding a race on docker stack rm
poll.WaitOn(t, stackRm(orchestrator, stackname), pollSettings)
poll.WaitOn(t, taskCount(orchestrator, stackname, 0), pollSettings)
poll.WaitOn(t, stackRm(stackname), pollSettings)
poll.WaitOn(t, taskCount(stackname, 0), pollSettings)
}
func stackRm(orchestrator, stackname string) func(t poll.LogT) poll.Result {
func stackRm(stackname string) func(t poll.LogT) poll.Result {
return func(poll.LogT) poll.Result {
result := icmd.RunCommand("docker", "stack", "rm", stackname, "--orchestrator", orchestrator)
result := icmd.RunCommand("docker", "stack", "rm", stackname)
if result.Error != nil {
if strings.Contains(result.Stderr(), "not found") {
return poll.Success()
@ -58,14 +49,9 @@ func stackRm(orchestrator, stackname string) func(t poll.LogT) poll.Result {
}
}
func taskCount(orchestrator, stackname string, expected int) func(t poll.LogT) poll.Result {
func taskCount(stackname string, expected int) func(t poll.LogT) poll.Result {
return func(poll.LogT) poll.Result {
args := []string{"stack", "ps", stackname, "--orchestrator", orchestrator}
// FIXME(chris-crone): remove when we support filtering by desired-state on kubernetes
if orchestrator == "swarm" {
args = append(args, "-f=desired-state=running")
}
result := icmd.RunCommand("docker", args...)
result := icmd.RunCommand("docker", "stack", "ps", stackname, "-f=desired-state=running")
count := lines(result.Stdout()) - 1
if count == expected {
return poll.Success()

View File

@ -9,7 +9,6 @@ Aliases:
Options:
-c, --compose-file strings Path to a Compose file, or "-" to read
from stdin
--orchestrator string Orchestrator to use (swarm|all)
--prune Prune services that are no longer referenced
--resolve-image string Query the registry to resolve image digest
and supported platforms

View File

@ -1,7 +0,0 @@
Creating network test-stack-deploy-with-names-swarm_network2
Creating network named-network
Creating secret named-secret
Creating secret test-stack-deploy-with-names-swarm_secret2
Creating config test-stack-deploy-with-names-swarm_config2
Creating config named-config
Creating service test-stack-deploy-with-names-swarm_web

View File

@ -0,0 +1,3 @@
Removing service test-stack-remove_one
Removing service test-stack-remove_two
Removing network test-stack-remove_default

View File

@ -1,3 +0,0 @@
Removing service test-stack-remove-swarm_one
Removing service test-stack-remove-swarm_two
Removing network test-stack-remove-swarm_default

View File

@ -55,7 +55,8 @@ func NewFakeCli(client client.APIClient, opts ...func(*FakeCli)) *FakeCli {
in: streams.NewIn(ioutil.NopCloser(strings.NewReader(""))),
// Use an empty string for filename so that tests don't create configfiles
// Set cli.ConfigFile().Filename to a tempfile to support Save.
configfile: configfile.New(""),
configfile: configfile.New(""),
currentContext: command.DefaultContextName,
}
for _, opt := range opts {
opt(c)
@ -214,24 +215,3 @@ func (c *FakeCli) ContentTrustEnabled() bool {
func EnableContentTrust(c *FakeCli) {
c.contentTrust = true
}
// StackOrchestrator return the selected stack orchestrator
func (c *FakeCli) StackOrchestrator(flagValue string) (command.Orchestrator, error) {
configOrchestrator := ""
if c.configfile != nil {
configOrchestrator = c.configfile.StackOrchestrator
}
ctxOrchestrator := ""
if c.currentContext != "" && c.contextStore != nil {
meta, err := c.contextStore.GetMetadata(c.currentContext)
if err != nil {
return "", err
}
context, err := command.GetDockerContext(meta)
if err != nil {
return "", err
}
ctxOrchestrator = string(context.StackOrchestrator)
}
return command.GetStackOrchestrator(flagValue, ctxOrchestrator, configOrchestrator, c.err)
}