2017-03-26 02:23:24 -04:00
|
|
|
package stack
|
|
|
|
|
|
|
|
import (
|
2018-05-03 21:02:44 -04:00
|
|
|
"context"
|
2017-03-26 02:23:24 -04:00
|
|
|
"strings"
|
|
|
|
|
2017-04-17 18:07:56 -04:00
|
|
|
"github.com/docker/cli/cli/compose/convert"
|
2017-06-12 16:14:05 -04:00
|
|
|
"github.com/docker/docker/api"
|
2017-03-26 02:23:24 -04:00
|
|
|
"github.com/docker/docker/api/types"
|
|
|
|
"github.com/docker/docker/api/types/filters"
|
|
|
|
"github.com/docker/docker/api/types/swarm"
|
2017-05-08 13:51:30 -04:00
|
|
|
"github.com/docker/docker/client"
|
2017-03-26 02:23:24 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
type fakeClient struct {
|
|
|
|
client.Client
|
|
|
|
|
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>
2017-06-30 20:00:16 -04:00
|
|
|
version string
|
|
|
|
|
2017-03-26 02:23:24 -04:00
|
|
|
services []string
|
|
|
|
networks []string
|
|
|
|
secrets []string
|
2017-05-26 20:30:33 -04:00
|
|
|
configs []string
|
2017-03-26 02:23:24 -04:00
|
|
|
|
|
|
|
removedServices []string
|
|
|
|
removedNetworks []string
|
|
|
|
removedSecrets []string
|
2017-05-26 20:30:33 -04:00
|
|
|
removedConfigs []string
|
2017-03-26 02:23:24 -04:00
|
|
|
|
2017-06-20 14:00:01 -04:00
|
|
|
serviceListFunc func(options types.ServiceListOptions) ([]swarm.Service, error)
|
|
|
|
networkListFunc func(options types.NetworkListOptions) ([]types.NetworkResource, error)
|
|
|
|
secretListFunc func(options types.SecretListOptions) ([]swarm.Secret, error)
|
|
|
|
configListFunc func(options types.ConfigListOptions) ([]swarm.Config, error)
|
|
|
|
nodeListFunc func(options types.NodeListOptions) ([]swarm.Node, error)
|
|
|
|
taskListFunc func(options types.TaskListOptions) ([]swarm.Task, error)
|
|
|
|
nodeInspectWithRaw func(ref string) (swarm.Node, []byte, error)
|
2017-07-26 09:33:57 -04:00
|
|
|
|
|
|
|
serviceUpdateFunc func(serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
|
|
|
|
|
|
|
|
serviceRemoveFunc func(serviceID string) error
|
|
|
|
networkRemoveFunc func(networkID string) error
|
|
|
|
secretRemoveFunc func(secretID string) error
|
|
|
|
configRemoveFunc func(configID string) error
|
2017-03-26 02:23:24 -04:00
|
|
|
}
|
|
|
|
|
2017-06-12 16:14:05 -04:00
|
|
|
func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error) {
|
|
|
|
return types.Version{
|
|
|
|
Version: "docker-dev",
|
|
|
|
APIVersion: api.DefaultVersion,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
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>
2017-06-30 20:00:16 -04:00
|
|
|
func (cli *fakeClient) ClientVersion() string {
|
|
|
|
return cli.version
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:23:24 -04:00
|
|
|
func (cli *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
|
|
|
if cli.serviceListFunc != nil {
|
|
|
|
return cli.serviceListFunc(options)
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace := namespaceFromFilters(options.Filters)
|
|
|
|
servicesList := []swarm.Service{}
|
|
|
|
for _, name := range cli.services {
|
|
|
|
if belongToNamespace(name, namespace) {
|
|
|
|
servicesList = append(servicesList, serviceFromName(name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return servicesList, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cli *fakeClient) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) {
|
|
|
|
if cli.networkListFunc != nil {
|
|
|
|
return cli.networkListFunc(options)
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace := namespaceFromFilters(options.Filters)
|
|
|
|
networksList := []types.NetworkResource{}
|
|
|
|
for _, name := range cli.networks {
|
|
|
|
if belongToNamespace(name, namespace) {
|
|
|
|
networksList = append(networksList, networkFromName(name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return networksList, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cli *fakeClient) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
|
|
|
|
if cli.secretListFunc != nil {
|
|
|
|
return cli.secretListFunc(options)
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace := namespaceFromFilters(options.Filters)
|
|
|
|
secretsList := []swarm.Secret{}
|
|
|
|
for _, name := range cli.secrets {
|
|
|
|
if belongToNamespace(name, namespace) {
|
|
|
|
secretsList = append(secretsList, secretFromName(name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return secretsList, nil
|
|
|
|
}
|
|
|
|
|
2017-05-26 20:30:33 -04:00
|
|
|
func (cli *fakeClient) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) {
|
|
|
|
if cli.configListFunc != nil {
|
|
|
|
return cli.configListFunc(options)
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace := namespaceFromFilters(options.Filters)
|
|
|
|
configsList := []swarm.Config{}
|
|
|
|
for _, name := range cli.configs {
|
|
|
|
if belongToNamespace(name, namespace) {
|
|
|
|
configsList = append(configsList, configFromName(name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return configsList, nil
|
|
|
|
}
|
|
|
|
|
2017-06-20 14:00:01 -04:00
|
|
|
func (cli *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
|
|
|
if cli.taskListFunc != nil {
|
|
|
|
return cli.taskListFunc(options)
|
|
|
|
}
|
|
|
|
return []swarm.Task{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cli *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
|
|
|
if cli.nodeListFunc != nil {
|
|
|
|
return cli.nodeListFunc(options)
|
|
|
|
}
|
|
|
|
return []swarm.Node{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, ref string) (swarm.Node, []byte, error) {
|
|
|
|
if cli.nodeInspectWithRaw != nil {
|
|
|
|
return cli.nodeInspectWithRaw(ref)
|
|
|
|
}
|
|
|
|
return swarm.Node{}, nil, nil
|
|
|
|
}
|
|
|
|
|
2017-07-26 09:33:57 -04:00
|
|
|
func (cli *fakeClient) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
|
|
|
|
if cli.serviceUpdateFunc != nil {
|
|
|
|
return cli.serviceUpdateFunc(serviceID, version, service, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
return types.ServiceUpdateResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:23:24 -04:00
|
|
|
func (cli *fakeClient) ServiceRemove(ctx context.Context, serviceID string) error {
|
|
|
|
if cli.serviceRemoveFunc != nil {
|
|
|
|
return cli.serviceRemoveFunc(serviceID)
|
|
|
|
}
|
|
|
|
|
|
|
|
cli.removedServices = append(cli.removedServices, serviceID)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cli *fakeClient) NetworkRemove(ctx context.Context, networkID string) error {
|
|
|
|
if cli.networkRemoveFunc != nil {
|
|
|
|
return cli.networkRemoveFunc(networkID)
|
|
|
|
}
|
|
|
|
|
|
|
|
cli.removedNetworks = append(cli.removedNetworks, networkID)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cli *fakeClient) SecretRemove(ctx context.Context, secretID string) error {
|
|
|
|
if cli.secretRemoveFunc != nil {
|
|
|
|
return cli.secretRemoveFunc(secretID)
|
|
|
|
}
|
|
|
|
|
|
|
|
cli.removedSecrets = append(cli.removedSecrets, secretID)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-05-26 20:30:33 -04:00
|
|
|
func (cli *fakeClient) ConfigRemove(ctx context.Context, configID string) error {
|
|
|
|
if cli.configRemoveFunc != nil {
|
|
|
|
return cli.configRemoveFunc(configID)
|
|
|
|
}
|
|
|
|
|
|
|
|
cli.removedConfigs = append(cli.removedConfigs, configID)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:23:24 -04:00
|
|
|
func serviceFromName(name string) swarm.Service {
|
|
|
|
return swarm.Service{
|
|
|
|
ID: "ID-" + name,
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: name},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func networkFromName(name string) types.NetworkResource {
|
|
|
|
return types.NetworkResource{
|
|
|
|
ID: "ID-" + name,
|
|
|
|
Name: name,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func secretFromName(name string) swarm.Secret {
|
|
|
|
return swarm.Secret{
|
|
|
|
ID: "ID-" + name,
|
|
|
|
Spec: swarm.SecretSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: name},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-26 20:30:33 -04:00
|
|
|
func configFromName(name string) swarm.Config {
|
|
|
|
return swarm.Config{
|
|
|
|
ID: "ID-" + name,
|
|
|
|
Spec: swarm.ConfigSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: name},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:23:24 -04:00
|
|
|
func namespaceFromFilters(filters filters.Args) string {
|
|
|
|
label := filters.Get("label")[0]
|
|
|
|
return strings.TrimPrefix(label, convert.LabelNamespace+"=")
|
|
|
|
}
|
|
|
|
|
|
|
|
func belongToNamespace(id, namespace string) bool {
|
|
|
|
return strings.HasPrefix(id, namespace+"_")
|
|
|
|
}
|
|
|
|
|
|
|
|
func objectName(namespace, name string) string {
|
|
|
|
return namespace + "_" + name
|
|
|
|
}
|
|
|
|
|
|
|
|
func objectID(name string) string {
|
|
|
|
return "ID-" + name
|
|
|
|
}
|
|
|
|
|
|
|
|
func buildObjectIDs(objectNames []string) []string {
|
|
|
|
IDs := make([]string, len(objectNames))
|
|
|
|
for i, name := range objectNames {
|
|
|
|
IDs[i] = objectID(name)
|
|
|
|
}
|
|
|
|
return IDs
|
|
|
|
}
|