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 }