2017-11-20 09:30:52 -05:00
|
|
|
package swarm
|
2016-09-08 13:11:39 -04:00
|
|
|
|
|
|
|
import (
|
2018-05-03 21:02:44 -04:00
|
|
|
"context"
|
2016-09-08 13:11:39 -04:00
|
|
|
"fmt"
|
|
|
|
|
2017-04-17 18:07:56 -04:00
|
|
|
"github.com/docker/cli/cli/command"
|
2017-12-04 06:30:39 -05:00
|
|
|
"github.com/docker/cli/cli/command/stack/options"
|
2017-04-17 18:07:56 -04:00
|
|
|
"github.com/docker/cli/cli/compose/convert"
|
2018-06-26 08:07:26 -04:00
|
|
|
composetypes "github.com/docker/cli/cli/compose/types"
|
2017-02-22 15:43:13 -05:00
|
|
|
"github.com/docker/docker/api/types/swarm"
|
2017-05-22 17:06:36 -04:00
|
|
|
"github.com/docker/docker/api/types/versions"
|
2017-01-13 11:26:29 -05:00
|
|
|
"github.com/pkg/errors"
|
2023-05-05 10:00:30 -04:00
|
|
|
"github.com/spf13/pflag"
|
2016-09-08 13:11:39 -04:00
|
|
|
)
|
|
|
|
|
2017-12-04 06:30:39 -05:00
|
|
|
// Resolve image constants
|
2016-09-08 13:11:39 -04:00
|
|
|
const (
|
|
|
|
defaultNetworkDriver = "overlay"
|
2017-12-04 06:30:39 -05:00
|
|
|
ResolveImageAlways = "always"
|
|
|
|
ResolveImageChanged = "changed"
|
|
|
|
ResolveImageNever = "never"
|
2016-09-08 13:11:39 -04:00
|
|
|
)
|
|
|
|
|
2017-12-04 06:30:39 -05:00
|
|
|
// RunDeploy is the swarm implementation of docker stack deploy
|
2023-05-05 10:00:30 -04:00
|
|
|
func RunDeploy(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, opts *options.Deploy, cfg *composetypes.Config) error {
|
|
|
|
if err := validateResolveImageFlag(opts); err != nil {
|
2017-06-02 19:21:41 -04:00
|
|
|
return err
|
2017-05-22 17:06:36 -04:00
|
|
|
}
|
2020-05-20 08:01:39 -04:00
|
|
|
// client side image resolution should not be done when the supported
|
|
|
|
// server version is older than 1.30
|
|
|
|
if versions.LessThan(dockerCli.Client().ClientVersion(), "1.30") {
|
|
|
|
opts.ResolveImage = ResolveImageNever
|
|
|
|
}
|
2017-05-22 17:06:36 -04:00
|
|
|
|
2023-05-05 10:00:30 -04:00
|
|
|
if opts.Detach && !flags.Changed("detach") {
|
|
|
|
fmt.Fprintln(dockerCli.Err(), "Since --detach=false was not specified, tasks will be created in the background.\n"+
|
|
|
|
"In a future release, --detach=false will become the default.")
|
|
|
|
}
|
|
|
|
|
2018-06-26 08:07:26 -04:00
|
|
|
return deployCompose(ctx, dockerCli, opts, cfg)
|
2016-11-21 15:03:43 -05:00
|
|
|
}
|
|
|
|
|
2017-06-02 19:21:41 -04:00
|
|
|
// validateResolveImageFlag validates the opts.resolveImage command line option
|
2020-05-20 08:01:39 -04:00
|
|
|
func validateResolveImageFlag(opts *options.Deploy) error {
|
|
|
|
switch opts.ResolveImage {
|
|
|
|
case ResolveImageAlways, ResolveImageChanged, ResolveImageNever:
|
|
|
|
return nil
|
|
|
|
default:
|
2017-12-04 06:30:39 -05:00
|
|
|
return errors.Errorf("Invalid option %s for flag --resolve-image", opts.ResolveImage)
|
2017-06-02 19:21:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-21 15:03:43 -05:00
|
|
|
// checkDaemonIsSwarmManager does an Info API call to verify that the daemon is
|
|
|
|
// a swarm manager. This is necessary because we must create networks before we
|
|
|
|
// create services, but the API call for creating a network does not return a
|
|
|
|
// proper status code when it can't create a network in the "global" scope.
|
2017-05-03 17:58:52 -04:00
|
|
|
func checkDaemonIsSwarmManager(ctx context.Context, dockerCli command.Cli) error {
|
2016-11-21 15:03:43 -05:00
|
|
|
info, err := dockerCli.Client().Info(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !info.Swarm.ControlAvailable {
|
2017-05-02 15:35:25 -04:00
|
|
|
return errors.New("this node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again")
|
2016-11-08 12:05:23 -05:00
|
|
|
}
|
2016-11-21 15:03:43 -05:00
|
|
|
return nil
|
2016-11-08 12:05:23 -05:00
|
|
|
}
|
2017-02-22 15:43:13 -05:00
|
|
|
|
|
|
|
// pruneServices removes services that are no longer referenced in the source
|
2017-09-26 12:33:35 -04:00
|
|
|
func pruneServices(ctx context.Context, dockerCli command.Cli, namespace convert.Namespace, services map[string]struct{}) {
|
2017-02-22 15:43:13 -05:00
|
|
|
client := dockerCli.Client()
|
|
|
|
|
2018-05-28 06:26:14 -04:00
|
|
|
oldServices, err := getStackServices(ctx, client, namespace.Name())
|
2017-02-22 15:43:13 -05:00
|
|
|
if err != nil {
|
2018-02-26 09:59:44 -05:00
|
|
|
fmt.Fprintf(dockerCli.Err(), "Failed to list services: %s\n", err)
|
2017-02-22 15:43:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pruneServices := []swarm.Service{}
|
|
|
|
for _, service := range oldServices {
|
|
|
|
if _, exists := services[namespace.Descope(service.Spec.Name)]; !exists {
|
|
|
|
pruneServices = append(pruneServices, service)
|
|
|
|
}
|
|
|
|
}
|
2017-09-26 12:33:35 -04:00
|
|
|
removeServices(ctx, dockerCli, pruneServices)
|
2017-02-22 15:43:13 -05:00
|
|
|
}
|