github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/command/stack/swarm/deploy.go (about)

     1  package swarm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/docker/cli/cli/command"
     8  	"github.com/docker/cli/cli/command/stack/options"
     9  	"github.com/docker/cli/cli/compose/convert"
    10  	composetypes "github.com/docker/cli/cli/compose/types"
    11  	"github.com/docker/docker/api/types/swarm"
    12  	"github.com/docker/docker/api/types/versions"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  // Resolve image constants
    17  const (
    18  	defaultNetworkDriver = "overlay"
    19  	ResolveImageAlways   = "always"
    20  	ResolveImageChanged  = "changed"
    21  	ResolveImageNever    = "never"
    22  )
    23  
    24  // RunDeploy is the swarm implementation of docker stack deploy
    25  func RunDeploy(dockerCli command.Cli, opts options.Deploy, cfg *composetypes.Config) error {
    26  	ctx := context.Background()
    27  
    28  	if err := validateResolveImageFlag(dockerCli, &opts); err != nil {
    29  		return err
    30  	}
    31  
    32  	return deployCompose(ctx, dockerCli, opts, cfg)
    33  }
    34  
    35  // validateResolveImageFlag validates the opts.resolveImage command line option
    36  // and also turns image resolution off if the version is older than 1.30
    37  func validateResolveImageFlag(dockerCli command.Cli, opts *options.Deploy) error {
    38  	if opts.ResolveImage != ResolveImageAlways && opts.ResolveImage != ResolveImageChanged && opts.ResolveImage != ResolveImageNever {
    39  		return errors.Errorf("Invalid option %s for flag --resolve-image", opts.ResolveImage)
    40  	}
    41  	// client side image resolution should not be done when the supported
    42  	// server version is older than 1.30
    43  	if versions.LessThan(dockerCli.Client().ClientVersion(), "1.30") {
    44  		opts.ResolveImage = ResolveImageNever
    45  	}
    46  	return nil
    47  }
    48  
    49  // checkDaemonIsSwarmManager does an Info API call to verify that the daemon is
    50  // a swarm manager. This is necessary because we must create networks before we
    51  // create services, but the API call for creating a network does not return a
    52  // proper status code when it can't create a network in the "global" scope.
    53  func checkDaemonIsSwarmManager(ctx context.Context, dockerCli command.Cli) error {
    54  	info, err := dockerCli.Client().Info(ctx)
    55  	if err != nil {
    56  		return err
    57  	}
    58  	if !info.Swarm.ControlAvailable {
    59  		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")
    60  	}
    61  	return nil
    62  }
    63  
    64  // pruneServices removes services that are no longer referenced in the source
    65  func pruneServices(ctx context.Context, dockerCli command.Cli, namespace convert.Namespace, services map[string]struct{}) {
    66  	client := dockerCli.Client()
    67  
    68  	oldServices, err := getStackServices(ctx, client, namespace.Name())
    69  	if err != nil {
    70  		fmt.Fprintf(dockerCli.Err(), "Failed to list services: %s\n", err)
    71  	}
    72  
    73  	pruneServices := []swarm.Service{}
    74  	for _, service := range oldServices {
    75  		if _, exists := services[namespace.Descope(service.Spec.Name)]; !exists {
    76  			pruneServices = append(pruneServices, service)
    77  		}
    78  	}
    79  	removeServices(ctx, dockerCli, pruneServices)
    80  }