github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/cli/command/stack/swarm/deploy.go (about)

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