Don't attempt to remove unsupported resources on older daemon

When running `docker stack rm <some stack>` against an older daemon,
a warning was printed for "configs" being ignored;

    WARNING: ignoring "configs" (requires API version 1.30, but the Docker daemon API version is 1.26)

Given that an old daemon cannot _have_ configs, there should not be
a need to warn, or _attempt_ to remove these resources.

This patch removes the warning, and skips fetching (and removing)
configs.

A check if _secrets_ are supported by the daemon is also added,
given that this would result in an error when attempted against
an older (pre 1.13) daemon.

There is one situation where this could lead to secrets or
configs being left behind; if the client is connecting to a
daemon that _does_ support secrets, configs, but the API version
is overridden using `DOCKER_API_VERSION`, no warning is printed,
and secrets and configs are not attempted to be removed.

Given that `DOCKER_API_VERSION` is regarded a feature for
debugging / "power users", it should be ok to ignore this.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2017-06-30 17:00:16 -07:00
parent 6908e58f0f
commit 2429f15672
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
3 changed files with 51 additions and 11 deletions

View File

@ -15,6 +15,8 @@ import (
type fakeClient struct { type fakeClient struct {
client.Client client.Client
version string
services []string services []string
networks []string networks []string
secrets []string secrets []string
@ -45,6 +47,10 @@ func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error)
}, nil }, nil
} }
func (cli *fakeClient) ClientVersion() string {
return cli.version
}
func (cli *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) { func (cli *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
if cli.serviceListFunc != nil { if cli.serviceListFunc != nil {
return cli.serviceListFunc(options) return cli.serviceListFunc(options)

View File

@ -51,20 +51,16 @@ func runRemove(dockerCli command.Cli, opts removeOptions) error {
return err return err
} }
secrets, err := getStackSecrets(ctx, client, namespace) var secrets []swarm.Secret
if err != nil { if versions.GreaterThanOrEqualTo(client.ClientVersion(), "1.25") {
return err secrets, err = getStackSecrets(ctx, client, namespace)
if err != nil {
return err
}
} }
var configs []swarm.Config var configs []swarm.Config
if versions.GreaterThanOrEqualTo(client.ClientVersion(), "1.30") {
version, err := client.ServerVersion(ctx)
if err != nil {
return err
}
if versions.LessThan(version.APIVersion, "1.30") {
fmt.Fprintf(dockerCli.Err(), "WARNING: ignoring \"configs\" (requires API version 1.30, but the Docker daemon API version is %s)\n", version.APIVersion)
} else {
configs, err = getStackConfigs(ctx, client, namespace) configs, err = getStackConfigs(ctx, client, namespace)
if err != nil { if err != nil {
return err return err

View File

@ -40,7 +40,9 @@ func TestRemoveStack(t *testing.T) {
} }
allConfigIDs := buildObjectIDs(allConfigs) allConfigIDs := buildObjectIDs(allConfigs)
// Using API 1.24; removes services, networks, but doesn't remove configs and secrets
cli := &fakeClient{ cli := &fakeClient{
version: "1.24",
services: allServices, services: allServices,
networks: allNetworks, networks: allNetworks,
secrets: allSecrets, secrets: allSecrets,
@ -49,6 +51,40 @@ func TestRemoveStack(t *testing.T) {
cmd := newRemoveCommand(test.NewFakeCli(cli, &bytes.Buffer{})) cmd := newRemoveCommand(test.NewFakeCli(cli, &bytes.Buffer{}))
cmd.SetArgs([]string{"foo", "bar"}) cmd.SetArgs([]string{"foo", "bar"})
assert.NoError(t, cmd.Execute())
assert.Equal(t, allServiceIDs, cli.removedServices)
assert.Equal(t, allNetworkIDs, cli.removedNetworks)
assert.Nil(t, cli.removedSecrets)
assert.Nil(t, cli.removedConfigs)
// Using API 1.25; removes services, networks, but doesn't remove configs
cli = &fakeClient{
version: "1.25",
services: allServices,
networks: allNetworks,
secrets: allSecrets,
configs: allConfigs,
}
cmd = newRemoveCommand(test.NewFakeCli(cli, &bytes.Buffer{}))
cmd.SetArgs([]string{"foo", "bar"})
assert.NoError(t, cmd.Execute())
assert.Equal(t, allServiceIDs, cli.removedServices)
assert.Equal(t, allNetworkIDs, cli.removedNetworks)
assert.Equal(t, allSecretIDs, cli.removedSecrets)
assert.Nil(t, cli.removedConfigs)
// Using API 1.30; removes services, networks, configs, and secrets
cli = &fakeClient{
version: "1.30",
services: allServices,
networks: allNetworks,
secrets: allSecrets,
configs: allConfigs,
}
cmd = newRemoveCommand(test.NewFakeCli(cli, &bytes.Buffer{}))
cmd.SetArgs([]string{"foo", "bar"})
assert.NoError(t, cmd.Execute()) assert.NoError(t, cmd.Execute())
assert.Equal(t, allServiceIDs, cli.removedServices) assert.Equal(t, allServiceIDs, cli.removedServices)
assert.Equal(t, allNetworkIDs, cli.removedNetworks) assert.Equal(t, allNetworkIDs, cli.removedNetworks)
@ -72,6 +108,7 @@ func TestRemoveStackSkipEmpty(t *testing.T) {
allConfigIDs := buildObjectIDs(allConfigs) allConfigIDs := buildObjectIDs(allConfigs)
fakeClient := &fakeClient{ fakeClient := &fakeClient{
version: "1.30",
services: allServices, services: allServices,
networks: allNetworks, networks: allNetworks,
secrets: allSecrets, secrets: allSecrets,
@ -106,6 +143,7 @@ func TestRemoveContinueAfterError(t *testing.T) {
removedServices := []string{} removedServices := []string{}
cli := &fakeClient{ cli := &fakeClient{
version: "1.30",
services: allServices, services: allServices,
networks: allNetworks, networks: allNetworks,
secrets: allSecrets, secrets: allSecrets,