1package swarm 2 3import ( 4 "context" 5 "fmt" 6 "sort" 7 "strings" 8 9 "github.com/docker/cli/cli/command" 10 "github.com/docker/cli/cli/command/stack/options" 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/swarm" 13 "github.com/docker/docker/api/types/versions" 14 "github.com/pkg/errors" 15) 16 17// RunRemove is the swarm implementation of docker stack remove 18func RunRemove(dockerCli command.Cli, opts options.Remove) error { 19 client := dockerCli.Client() 20 ctx := context.Background() 21 22 var errs []string 23 for _, namespace := range opts.Namespaces { 24 services, err := getStackServices(ctx, client, namespace) 25 if err != nil { 26 return err 27 } 28 29 networks, err := getStackNetworks(ctx, client, namespace) 30 if err != nil { 31 return err 32 } 33 34 var secrets []swarm.Secret 35 if versions.GreaterThanOrEqualTo(client.ClientVersion(), "1.25") { 36 secrets, err = getStackSecrets(ctx, client, namespace) 37 if err != nil { 38 return err 39 } 40 } 41 42 var configs []swarm.Config 43 if versions.GreaterThanOrEqualTo(client.ClientVersion(), "1.30") { 44 configs, err = getStackConfigs(ctx, client, namespace) 45 if err != nil { 46 return err 47 } 48 } 49 50 if len(services)+len(networks)+len(secrets)+len(configs) == 0 { 51 fmt.Fprintf(dockerCli.Err(), "Nothing found in stack: %s\n", namespace) 52 continue 53 } 54 55 hasError := removeServices(ctx, dockerCli, services) 56 hasError = removeSecrets(ctx, dockerCli, secrets) || hasError 57 hasError = removeConfigs(ctx, dockerCli, configs) || hasError 58 hasError = removeNetworks(ctx, dockerCli, networks) || hasError 59 60 if hasError { 61 errs = append(errs, fmt.Sprintf("Failed to remove some resources from stack: %s", namespace)) 62 } 63 } 64 65 if len(errs) > 0 { 66 return errors.Errorf(strings.Join(errs, "\n")) 67 } 68 return nil 69} 70 71func sortServiceByName(services []swarm.Service) func(i, j int) bool { 72 return func(i, j int) bool { 73 return services[i].Spec.Name < services[j].Spec.Name 74 } 75} 76 77func removeServices( 78 ctx context.Context, 79 dockerCli command.Cli, 80 services []swarm.Service, 81) bool { 82 var hasError bool 83 sort.Slice(services, sortServiceByName(services)) 84 for _, service := range services { 85 fmt.Fprintf(dockerCli.Out(), "Removing service %s\n", service.Spec.Name) 86 if err := dockerCli.Client().ServiceRemove(ctx, service.ID); err != nil { 87 hasError = true 88 fmt.Fprintf(dockerCli.Err(), "Failed to remove service %s: %s", service.ID, err) 89 } 90 } 91 return hasError 92} 93 94func removeNetworks( 95 ctx context.Context, 96 dockerCli command.Cli, 97 networks []types.NetworkResource, 98) bool { 99 var hasError bool 100 for _, network := range networks { 101 fmt.Fprintf(dockerCli.Out(), "Removing network %s\n", network.Name) 102 if err := dockerCli.Client().NetworkRemove(ctx, network.ID); err != nil { 103 hasError = true 104 fmt.Fprintf(dockerCli.Err(), "Failed to remove network %s: %s", network.ID, err) 105 } 106 } 107 return hasError 108} 109 110func removeSecrets( 111 ctx context.Context, 112 dockerCli command.Cli, 113 secrets []swarm.Secret, 114) bool { 115 var hasError bool 116 for _, secret := range secrets { 117 fmt.Fprintf(dockerCli.Out(), "Removing secret %s\n", secret.Spec.Name) 118 if err := dockerCli.Client().SecretRemove(ctx, secret.ID); err != nil { 119 hasError = true 120 fmt.Fprintf(dockerCli.Err(), "Failed to remove secret %s: %s", secret.ID, err) 121 } 122 } 123 return hasError 124} 125 126func removeConfigs( 127 ctx context.Context, 128 dockerCli command.Cli, 129 configs []swarm.Config, 130) bool { 131 var hasError bool 132 for _, config := range configs { 133 fmt.Fprintf(dockerCli.Out(), "Removing config %s\n", config.Spec.Name) 134 if err := dockerCli.Client().ConfigRemove(ctx, config.ID); err != nil { 135 hasError = true 136 fmt.Fprintf(dockerCli.Err(), "Failed to remove config %s: %s", config.ID, err) 137 } 138 } 139 return hasError 140} 141