mirror of https://github.com/docker/docs.git
Add experimental docker stack commands
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
87f31e379d
commit
71104bb592
|
@ -0,0 +1,62 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package bundlefile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bundlefile stores the contents of a bundlefile
|
||||||
|
type Bundlefile struct {
|
||||||
|
Version string
|
||||||
|
Services map[string]Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service is a service from a bundlefile
|
||||||
|
type Service struct {
|
||||||
|
Image string
|
||||||
|
Command []string `json:",omitempty"`
|
||||||
|
Args []string `json:",omitempty"`
|
||||||
|
Env []string `json:",omitempty"`
|
||||||
|
Labels map[string]string `json:",omitempty"`
|
||||||
|
Ports []Port `json:",omitempty"`
|
||||||
|
WorkingDir *string `json:",omitempty"`
|
||||||
|
User *string `json:",omitempty"`
|
||||||
|
Networks []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Port is a port as defined in a bundlefile
|
||||||
|
type Port struct {
|
||||||
|
Protocol string
|
||||||
|
Port uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFile loads a bundlefile from a path to the file
|
||||||
|
func LoadFile(path string) (*Bundlefile, error) {
|
||||||
|
reader, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bundlefile := &Bundlefile{}
|
||||||
|
|
||||||
|
if err := json.NewDecoder(reader).Decode(bundlefile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bundlefile, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print writes the contents of the bundlefile to the output writer
|
||||||
|
// as human readable json
|
||||||
|
func Print(out io.Writer, bundle *Bundlefile) error {
|
||||||
|
bytes, err := json.MarshalIndent(*bundle, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = out.Write(bytes)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ import (
|
||||||
func NewNodeCommand(dockerCli *client.DockerCli) *cobra.Command {
|
func NewNodeCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "node",
|
Use: "node",
|
||||||
Short: "Manage docker swarm nodes",
|
Short: "Manage Docker Swarm nodes",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func NewServiceCommand(dockerCli *client.DockerCli) *cobra.Command {
|
func NewServiceCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "service",
|
Use: "service",
|
||||||
Short: "Manage docker services",
|
Short: "Manage Docker services",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewStackCommand returns a cobra command for `stack` subcommands
|
||||||
|
func NewStackCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "stack",
|
||||||
|
Short: "Manage Docker stacks",
|
||||||
|
Args: cli.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.AddCommand(
|
||||||
|
newConfigCommand(dockerCli),
|
||||||
|
newDeployCommand(dockerCli),
|
||||||
|
newRemoveCommand(dockerCli),
|
||||||
|
newTasksCommand(dockerCli),
|
||||||
|
)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTopLevelDeployCommand return a command for `docker deploy`
|
||||||
|
func NewTopLevelDeployCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
cmd := newDeployCommand(dockerCli)
|
||||||
|
// Remove the aliases at the top level
|
||||||
|
cmd.Aliases = []string{}
|
||||||
|
return cmd
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
// +build !experimental
|
||||||
|
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewStackCommand returns nocommand
|
||||||
|
func NewStackCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
return &cobra.Command{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTopLevelDeployCommand return no command
|
||||||
|
func NewTopLevelDeployCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
return &cobra.Command{}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/engine-api/client"
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/docker/engine-api/types/filters"
|
||||||
|
"github.com/docker/engine-api/types/swarm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
labelNamespace = "com.docker.stack.namespace"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getStackLabels(namespace string, labels map[string]string) map[string]string {
|
||||||
|
if labels == nil {
|
||||||
|
labels = make(map[string]string)
|
||||||
|
}
|
||||||
|
labels[labelNamespace] = namespace
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStackFilter(namespace string) filters.Args {
|
||||||
|
filter := filters.NewArgs()
|
||||||
|
filter.Add("label", labelNamespace+"="+namespace)
|
||||||
|
return filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServices(
|
||||||
|
ctx context.Context,
|
||||||
|
apiclient client.APIClient,
|
||||||
|
namespace string,
|
||||||
|
) ([]swarm.Service, error) {
|
||||||
|
return apiclient.ServiceList(
|
||||||
|
ctx,
|
||||||
|
types.ServiceListOptions{Filter: getStackFilter(namespace)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNetworks(
|
||||||
|
ctx context.Context,
|
||||||
|
apiclient client.APIClient,
|
||||||
|
namespace string,
|
||||||
|
) ([]types.NetworkResource, error) {
|
||||||
|
return apiclient.NetworkList(
|
||||||
|
ctx,
|
||||||
|
types.NetworkListOptions{Filters: getStackFilter(namespace)})
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/api/client/bundlefile"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type configOptions struct {
|
||||||
|
bundlefile string
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConfigCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
var opts configOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "config [OPTIONS] STACK",
|
||||||
|
Short: "Print the stack configuration",
|
||||||
|
Args: cli.ExactArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
opts.namespace = args[0]
|
||||||
|
return runConfig(dockerCli, opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
addBundlefileFlag(&opts.bundlefile, flags)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runConfig(dockerCli *client.DockerCli, opts configOptions) error {
|
||||||
|
bundle, err := loadBundlefile(dockerCli.Err(), opts.namespace, opts.bundlefile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return bundlefile.Print(dockerCli.Out(), bundle)
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/api/client/bundlefile"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/docker/engine-api/types/network"
|
||||||
|
"github.com/docker/engine-api/types/swarm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultNetworkDriver = "overlay"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deployOptions struct {
|
||||||
|
bundlefile string
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDeployCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
var opts deployOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "deploy [OPTIONS] STACK",
|
||||||
|
Aliases: []string{"up"},
|
||||||
|
Short: "Create and update a stack",
|
||||||
|
Args: cli.ExactArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
opts.namespace = args[0]
|
||||||
|
return runDeploy(dockerCli, opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
addBundlefileFlag(&opts.bundlefile, flags)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDeploy(dockerCli *client.DockerCli, opts deployOptions) error {
|
||||||
|
bundle, err := loadBundlefile(dockerCli.Err(), opts.namespace, opts.bundlefile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
networks := getUniqueNetworkNames(bundle.Services)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if err := updateNetworks(ctx, dockerCli, networks, opts.namespace); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return deployServices(ctx, dockerCli, bundle.Services, opts.namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUniqueNetworkNames(services map[string]bundlefile.Service) []string {
|
||||||
|
networkSet := make(map[string]bool)
|
||||||
|
for _, service := range services {
|
||||||
|
for _, network := range service.Networks {
|
||||||
|
networkSet[network] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
networks := []string{}
|
||||||
|
for network := range networkSet {
|
||||||
|
networks = append(networks, network)
|
||||||
|
}
|
||||||
|
return networks
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNetworks(
|
||||||
|
ctx context.Context,
|
||||||
|
dockerCli *client.DockerCli,
|
||||||
|
networks []string,
|
||||||
|
namespace string,
|
||||||
|
) error {
|
||||||
|
client := dockerCli.Client()
|
||||||
|
|
||||||
|
existingNetworks, err := getNetworks(ctx, client, namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
existingNetworkMap := make(map[string]types.NetworkResource)
|
||||||
|
for _, network := range existingNetworks {
|
||||||
|
existingNetworkMap[network.Name] = network
|
||||||
|
}
|
||||||
|
|
||||||
|
createOpts := types.NetworkCreate{
|
||||||
|
Labels: getStackLabels(namespace, nil),
|
||||||
|
Driver: defaultNetworkDriver,
|
||||||
|
// TODO: remove when engine-api uses omitempty for IPAM
|
||||||
|
IPAM: network.IPAM{Driver: "default"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, internalName := range networks {
|
||||||
|
name := fmt.Sprintf("%s_%s", namespace, internalName)
|
||||||
|
|
||||||
|
if _, exists := existingNetworkMap[name]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(dockerCli.Out(), "Creating network %s\n", name)
|
||||||
|
if _, err := client.NetworkCreate(ctx, name, createOpts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertNetworks(networks []string, namespace string, name string) []swarm.NetworkAttachmentConfig {
|
||||||
|
nets := []swarm.NetworkAttachmentConfig{}
|
||||||
|
for _, network := range networks {
|
||||||
|
nets = append(nets, swarm.NetworkAttachmentConfig{
|
||||||
|
Target: namespace + "_" + network,
|
||||||
|
Aliases: []string{name},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nets
|
||||||
|
}
|
||||||
|
|
||||||
|
func deployServices(
|
||||||
|
ctx context.Context,
|
||||||
|
dockerCli *client.DockerCli,
|
||||||
|
services map[string]bundlefile.Service,
|
||||||
|
namespace string,
|
||||||
|
) error {
|
||||||
|
apiClient := dockerCli.Client()
|
||||||
|
out := dockerCli.Out()
|
||||||
|
|
||||||
|
existingServices, err := getServices(ctx, apiClient, namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
existingServiceMap := make(map[string]swarm.Service)
|
||||||
|
for _, service := range existingServices {
|
||||||
|
existingServiceMap[service.Spec.Name] = service
|
||||||
|
}
|
||||||
|
|
||||||
|
for internalName, service := range services {
|
||||||
|
name := fmt.Sprintf("%s_%s", namespace, internalName)
|
||||||
|
|
||||||
|
var ports []swarm.PortConfig
|
||||||
|
for _, portSpec := range service.Ports {
|
||||||
|
ports = append(ports, swarm.PortConfig{
|
||||||
|
Protocol: swarm.PortConfigProtocol(portSpec.Protocol),
|
||||||
|
TargetPort: portSpec.Port,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceSpec := swarm.ServiceSpec{
|
||||||
|
Annotations: swarm.Annotations{
|
||||||
|
Name: name,
|
||||||
|
Labels: getStackLabels(namespace, service.Labels),
|
||||||
|
},
|
||||||
|
TaskTemplate: swarm.TaskSpec{
|
||||||
|
ContainerSpec: swarm.ContainerSpec{
|
||||||
|
Image: service.Image,
|
||||||
|
Command: service.Command,
|
||||||
|
Args: service.Args,
|
||||||
|
Env: service.Env,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EndpointSpec: &swarm.EndpointSpec{
|
||||||
|
Ports: ports,
|
||||||
|
},
|
||||||
|
Networks: convertNetworks(service.Networks, namespace, internalName),
|
||||||
|
}
|
||||||
|
|
||||||
|
cspec := &serviceSpec.TaskTemplate.ContainerSpec
|
||||||
|
if service.WorkingDir != nil {
|
||||||
|
cspec.Dir = *service.WorkingDir
|
||||||
|
}
|
||||||
|
if service.User != nil {
|
||||||
|
cspec.User = *service.User
|
||||||
|
}
|
||||||
|
|
||||||
|
if service, exists := existingServiceMap[name]; exists {
|
||||||
|
fmt.Fprintf(out, "Updating service %s (id: %s)\n", name, service.ID)
|
||||||
|
|
||||||
|
if err := apiClient.ServiceUpdate(
|
||||||
|
ctx,
|
||||||
|
service.ID,
|
||||||
|
service.Version,
|
||||||
|
serviceSpec,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(out, "Creating service %s\n", name)
|
||||||
|
|
||||||
|
if _, err := apiClient.ServiceCreate(ctx, serviceSpec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client/bundlefile"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addBundlefileFlag(opt *string, flags *pflag.FlagSet) {
|
||||||
|
flags.StringVarP(
|
||||||
|
opt,
|
||||||
|
"bundle", "f", "",
|
||||||
|
"Path to a bundle (Default: STACK.dsb)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadBundlefile(stderr io.Writer, namespace string, path string) (*bundlefile.Bundlefile, error) {
|
||||||
|
defaultPath := fmt.Sprintf("%s.dsb", namespace)
|
||||||
|
|
||||||
|
if path == "" {
|
||||||
|
path = defaultPath
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Bundle %s not found. Specify the path with -f or --bundle",
|
||||||
|
path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(stderr, "Loading bundle from %s\n", path)
|
||||||
|
bundle, err := bundlefile.LoadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error reading %s: %v\n", path, err)
|
||||||
|
}
|
||||||
|
return bundle, err
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type removeOptions struct {
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
var opts removeOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "rm STACK",
|
||||||
|
Aliases: []string{"remove", "down"},
|
||||||
|
Short: "Remove the stack",
|
||||||
|
Args: cli.ExactArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
opts.namespace = args[0]
|
||||||
|
return runRemove(dockerCli, opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRemove(dockerCli *client.DockerCli, opts removeOptions) error {
|
||||||
|
namespace := opts.namespace
|
||||||
|
client := dockerCli.Client()
|
||||||
|
stderr := dockerCli.Err()
|
||||||
|
ctx := context.Background()
|
||||||
|
hasError := false
|
||||||
|
|
||||||
|
services, err := getServices(ctx, client, namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, service := range services {
|
||||||
|
fmt.Fprintf(stderr, "Removing service %s\n", service.Spec.Name)
|
||||||
|
if err := client.ServiceRemove(ctx, service.ID); err != nil {
|
||||||
|
hasError = true
|
||||||
|
fmt.Fprintf(stderr, "Failed to remove service %s: %s", service.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
networks, err := getNetworks(ctx, client, namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, network := range networks {
|
||||||
|
fmt.Fprintf(stderr, "Removing network %s\n", network.Name)
|
||||||
|
if err := client.NetworkRemove(ctx, network.ID); err != nil {
|
||||||
|
hasError = true
|
||||||
|
fmt.Fprintf(stderr, "Failed to remove network %s: %s", network.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasError {
|
||||||
|
return fmt.Errorf("Failed to remove some resources")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/api/client/idresolver"
|
||||||
|
"github.com/docker/docker/api/client/task"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/docker/docker/opts"
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/docker/engine-api/types/swarm"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tasksOptions struct {
|
||||||
|
all bool
|
||||||
|
filter opts.FilterOpt
|
||||||
|
namespace string
|
||||||
|
noResolve bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTasksCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
opts := tasksOptions{filter: opts.NewFilterOpt()}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "tasks [OPTIONS] STACK",
|
||||||
|
Short: "List the tasks in the stack",
|
||||||
|
Args: cli.ExactArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
opts.namespace = args[0]
|
||||||
|
return runTasks(dockerCli, opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.BoolVarP(&opts.all, "all", "a", false, "Display all tasks")
|
||||||
|
flags.BoolVarP(&opts.noResolve, "no-resolve", "n", false, "Do not map IDs to Names")
|
||||||
|
flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTasks(dockerCli *client.DockerCli, opts tasksOptions) error {
|
||||||
|
client := dockerCli.Client()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
filter := opts.filter.Value()
|
||||||
|
filter.Add("label", labelNamespace+"="+opts.namespace)
|
||||||
|
if !opts.all && !filter.Include("desired_state") {
|
||||||
|
filter.Add("desired_state", string(swarm.TaskStateRunning))
|
||||||
|
filter.Add("desired_state", string(swarm.TaskStateAccepted))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks, err := client.TaskList(ctx, types.TaskListOptions{Filter: filter})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.Print(dockerCli, ctx, tasks, idresolver.New(client, opts.noResolve))
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func NewSwarmCommand(dockerCli *client.DockerCli) *cobra.Command {
|
func NewSwarmCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "swarm",
|
Use: "swarm",
|
||||||
Short: "Manage docker swarm",
|
Short: "Manage Docker Swarm",
|
||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/docker/docker/api/client/node"
|
"github.com/docker/docker/api/client/node"
|
||||||
"github.com/docker/docker/api/client/registry"
|
"github.com/docker/docker/api/client/registry"
|
||||||
"github.com/docker/docker/api/client/service"
|
"github.com/docker/docker/api/client/service"
|
||||||
|
"github.com/docker/docker/api/client/stack"
|
||||||
"github.com/docker/docker/api/client/swarm"
|
"github.com/docker/docker/api/client/swarm"
|
||||||
"github.com/docker/docker/api/client/system"
|
"github.com/docker/docker/api/client/system"
|
||||||
"github.com/docker/docker/api/client/volume"
|
"github.com/docker/docker/api/client/volume"
|
||||||
|
@ -41,6 +42,8 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
||||||
rootCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
node.NewNodeCommand(dockerCli),
|
node.NewNodeCommand(dockerCli),
|
||||||
service.NewServiceCommand(dockerCli),
|
service.NewServiceCommand(dockerCli),
|
||||||
|
stack.NewStackCommand(dockerCli),
|
||||||
|
stack.NewTopLevelDeployCommand(dockerCli),
|
||||||
swarm.NewSwarmCommand(dockerCli),
|
swarm.NewSwarmCommand(dockerCli),
|
||||||
container.NewAttachCommand(dockerCli),
|
container.NewAttachCommand(dockerCli),
|
||||||
container.NewCommitCommand(dockerCli),
|
container.NewCommitCommand(dockerCli),
|
||||||
|
@ -96,8 +99,10 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
||||||
func (c CobraAdaptor) Usage() []cli.Command {
|
func (c CobraAdaptor) Usage() []cli.Command {
|
||||||
cmds := []cli.Command{}
|
cmds := []cli.Command{}
|
||||||
for _, cmd := range c.rootCmd.Commands() {
|
for _, cmd := range c.rootCmd.Commands() {
|
||||||
|
if cmd.Name() != "" {
|
||||||
cmds = append(cmds, cli.Command{Name: cmd.Name(), Description: cmd.Short})
|
cmds = append(cmds, cli.Command{Name: cmd.Name(), Description: cmd.Short})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return cmds
|
return cmds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue