Forráskód Böngészése

Merge pull request #24237 from aaronlehmann/listen-addr

Split advertised address from listen address; change address detection strategy
Aaron Lehmann 9 éve
szülő
commit
9c1be541ff
76 módosított fájl, 2284 hozzáadás és 637 törlés
  1. 11 2
      api/client/swarm/init.go
  2. 9 5
      api/client/swarm/join.go
  3. 1 0
      api/client/swarm/opts.go
  4. 1 0
      api/client/system/info.go
  5. 5 3
      cmd/dockerd/daemon.go
  6. 14 2
      contrib/completion/bash/docker
  7. 2 0
      contrib/completion/zsh/_docker
  8. 166 33
      daemon/cluster/cluster.go
  9. 268 0
      daemon/cluster/listen_addr.go
  10. 9 0
      daemon/config.go
  11. 4 0
      daemon/config_solaris.go
  12. 5 0
      daemon/config_unix.go
  13. 5 0
      daemon/config_windows.go
  14. 37 0
      daemon/daemon.go
  15. 17 2
      docs/reference/api/docker_remote_api_v1.24.md
  16. 17 2
      docs/reference/api/docker_remote_api_v1.25.md
  17. 3 0
      docs/reference/commandline/dockerd.md
  18. 25 4
      docs/reference/commandline/swarm_init.md
  19. 36 6
      docs/reference/commandline/swarm_join.md
  20. 5 5
      docs/swarm/swarm-tutorial/create-swarm.md
  21. 3 3
      hack/vendor.sh
  22. 1 1
      integration-cli/check_test.go
  23. 4 6
      integration-cli/docker_api_swarm_test.go
  24. 6 0
      man/dockerd.8.md
  25. 14 22
      vendor/src/github.com/docker/libnetwork/agent.go
  26. 2 1
      vendor/src/github.com/docker/libnetwork/cluster/provider.go
  27. 8 0
      vendor/src/github.com/docker/libnetwork/config/config.go
  28. 4 0
      vendor/src/github.com/docker/libnetwork/controller.go
  29. 3 2
      vendor/src/github.com/docker/libnetwork/discoverapi/discoverapi.go
  30. 2 2
      vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_store.go
  31. 1 1
      vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go
  32. 2 2
      vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go
  33. 2 2
      vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go
  34. 40 16
      vendor/src/github.com/docker/libnetwork/drivers/overlay/encryption.go
  35. 7 5
      vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go
  36. 1 1
      vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_serf.go
  37. 36 26
      vendor/src/github.com/docker/libnetwork/drivers/overlay/overlay.go
  38. 10 1
      vendor/src/github.com/docker/libnetwork/endpoint_cnt.go
  39. 5 1
      vendor/src/github.com/docker/libnetwork/network.go
  40. 1 1
      vendor/src/github.com/docker/libnetwork/networkdb/cluster.go
  41. 2 2
      vendor/src/github.com/docker/libnetwork/networkdb/networkdb.go
  42. 1 0
      vendor/src/github.com/docker/libnetwork/osl/interface_linux.go
  43. 15 4
      vendor/src/github.com/docker/libnetwork/osl/namespace_linux.go
  44. 4 0
      vendor/src/github.com/docker/libnetwork/osl/namespace_unsupported.go
  45. 4 0
      vendor/src/github.com/docker/libnetwork/osl/namespace_windows.go
  46. 4 0
      vendor/src/github.com/docker/libnetwork/osl/sandbox_freebsd.go
  47. 17 3
      vendor/src/github.com/docker/libnetwork/sandbox.go
  48. 1 1
      vendor/src/github.com/docker/libnetwork/sandbox_externalkey_unix.go
  49. 7 0
      vendor/src/github.com/docker/swarmkit/agent/exec/controller.go
  50. 26 20
      vendor/src/github.com/docker/swarmkit/agent/node.go
  51. 3 3
      vendor/src/github.com/docker/swarmkit/agent/storage.go
  52. 5 4
      vendor/src/github.com/docker/swarmkit/agent/task.go
  53. 156 100
      vendor/src/github.com/docker/swarmkit/api/objects.pb.go
  54. 4 0
      vendor/src/github.com/docker/swarmkit/api/objects.proto
  55. 655 256
      vendor/src/github.com/docker/swarmkit/api/types.pb.go
  56. 48 0
      vendor/src/github.com/docker/swarmkit/api/types.proto
  57. 19 3
      vendor/src/github.com/docker/swarmkit/ca/auth.go
  58. 13 3
      vendor/src/github.com/docker/swarmkit/ca/forward.go
  59. 1 1
      vendor/src/github.com/docker/swarmkit/ca/transport.go
  60. 79 0
      vendor/src/github.com/docker/swarmkit/manager/controlapi/service.go
  61. 1 1
      vendor/src/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go
  62. 10 31
      vendor/src/github.com/docker/swarmkit/manager/manager.go
  63. 8 2
      vendor/src/github.com/docker/swarmkit/manager/orchestrator/replicated.go
  64. 51 5
      vendor/src/github.com/docker/swarmkit/manager/orchestrator/restart.go
  65. 2 2
      vendor/src/github.com/docker/swarmkit/manager/orchestrator/tasks.go
  66. 164 21
      vendor/src/github.com/docker/swarmkit/manager/orchestrator/updater.go
  67. 24 2
      vendor/src/github.com/docker/swarmkit/manager/state/raft/raft.go
  68. 5 0
      vendor/src/github.com/docker/swarmkit/manager/state/watch.go
  69. 12 1
      vendor/src/github.com/vishvananda/netlink/addr_linux.go
  70. 2 2
      vendor/src/github.com/vishvananda/netlink/filter_linux.go
  71. 14 3
      vendor/src/github.com/vishvananda/netlink/link_linux.go
  72. 57 6
      vendor/src/github.com/vishvananda/netlink/nl/nl_linux.go
  73. 30 0
      vendor/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go
  74. 12 1
      vendor/src/github.com/vishvananda/netlink/route_linux.go
  75. 12 3
      vendor/src/github.com/vishvananda/netlink/xfrm_state.go
  76. 24 0
      vendor/src/github.com/vishvananda/netlink/xfrm_state_linux.go

+ 11 - 2
api/client/swarm/init.go

@@ -1,7 +1,9 @@
 package swarm
 
 import (
+	"errors"
 	"fmt"
+	"strings"
 
 	"golang.org/x/net/context"
 
@@ -21,7 +23,9 @@ const (
 
 type initOptions struct {
 	swarmOptions
-	listenAddr      NodeAddrOption
+	listenAddr NodeAddrOption
+	// Not a NodeAddrOption because it has no default port.
+	advertiseAddr   string
 	forceNewCluster bool
 }
 
@@ -40,7 +44,8 @@ func newInitCommand(dockerCli *client.DockerCli) *cobra.Command {
 	}
 
 	flags := cmd.Flags()
-	flags.Var(&opts.listenAddr, "listen-addr", "Listen address")
+	flags.Var(&opts.listenAddr, flagListenAddr, "Listen address (format: <ip|interface>[:port])")
+	flags.StringVar(&opts.advertiseAddr, flagAdvertiseAddr, "", "Advertised address (format: <ip|interface>[:port])")
 	flags.BoolVar(&opts.forceNewCluster, "force-new-cluster", false, "Force create a new cluster from current state.")
 	addSwarmFlags(flags, &opts.swarmOptions)
 	return cmd
@@ -52,12 +57,16 @@ func runInit(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts initOptions
 
 	req := swarm.InitRequest{
 		ListenAddr:      opts.listenAddr.String(),
+		AdvertiseAddr:   opts.advertiseAddr,
 		ForceNewCluster: opts.forceNewCluster,
 		Spec:            opts.swarmOptions.ToSpec(),
 	}
 
 	nodeID, err := client.SwarmInit(ctx, req)
 	if err != nil {
+		if strings.Contains(err.Error(), "could not choose an IP address to advertise") || strings.Contains(err.Error(), "could not find the system's IP address") {
+			return errors.New(err.Error() + " - specify one with --advertise-addr")
+		}
 		return err
 	}
 

+ 9 - 5
api/client/swarm/join.go

@@ -14,7 +14,9 @@ import (
 type joinOptions struct {
 	remote     string
 	listenAddr NodeAddrOption
-	token      string
+	// Not a NodeAddrOption because it has no default port.
+	advertiseAddr string
+	token         string
 }
 
 func newJoinCommand(dockerCli *client.DockerCli) *cobra.Command {
@@ -33,7 +35,8 @@ func newJoinCommand(dockerCli *client.DockerCli) *cobra.Command {
 	}
 
 	flags := cmd.Flags()
-	flags.Var(&opts.listenAddr, flagListenAddr, "Listen address")
+	flags.Var(&opts.listenAddr, flagListenAddr, "Listen address (format: <ip|interface>[:port])")
+	flags.StringVar(&opts.advertiseAddr, flagAdvertiseAddr, "", "Advertised address (format: <ip|interface>[:port])")
 	flags.StringVar(&opts.token, flagToken, "", "Token for entry into the swarm")
 	return cmd
 }
@@ -43,9 +46,10 @@ func runJoin(dockerCli *client.DockerCli, opts joinOptions) error {
 	ctx := context.Background()
 
 	req := swarm.JoinRequest{
-		JoinToken:   opts.token,
-		ListenAddr:  opts.listenAddr.String(),
-		RemoteAddrs: []string{opts.remote},
+		JoinToken:     opts.token,
+		ListenAddr:    opts.listenAddr.String(),
+		AdvertiseAddr: opts.advertiseAddr,
+		RemoteAddrs:   []string{opts.remote},
 	}
 	err := client.SwarmJoin(ctx, req)
 	if err != nil {

+ 1 - 0
api/client/swarm/opts.go

@@ -18,6 +18,7 @@ const (
 	flagCertExpiry          = "cert-expiry"
 	flagDispatcherHeartbeat = "dispatcher-heartbeat"
 	flagListenAddr          = "listen-addr"
+	flagAdvertiseAddr       = "advertise-addr"
 	flagToken               = "token"
 	flagTaskHistoryLimit    = "task-history-limit"
 	flagExternalCA          = "external-ca"

+ 1 - 0
api/client/system/info.go

@@ -86,6 +86,7 @@ func runInfo(dockerCli *client.DockerCli) error {
 			fmt.Fprintf(dockerCli.Out(), " Managers: %d\n", info.Swarm.Managers)
 			fmt.Fprintf(dockerCli.Out(), " Nodes: %d\n", info.Swarm.Nodes)
 		}
+		fmt.Fprintf(dockerCli.Out(), " Node Address: %s\n", info.Swarm.NodeAddr)
 	}
 
 	if len(info.Runtimes) > 0 {

+ 5 - 3
cmd/dockerd/daemon.go

@@ -274,9 +274,11 @@ func (cli *DaemonCli) start() (err error) {
 	name, _ := os.Hostname()
 
 	c, err := cluster.New(cluster.Config{
-		Root:    cli.Config.Root,
-		Name:    name,
-		Backend: d,
+		Root:                   cli.Config.Root,
+		Name:                   name,
+		Backend:                d,
+		NetworkSubnetsProvider: d,
+		DefaultAdvertiseAddr:   cli.Config.SwarmDefaultAdvertiseAddr,
 	})
 	if err != nil {
 		logrus.Fatalf("Error creating cluster component: %v", err)

+ 14 - 2
contrib/completion/bash/docker

@@ -1839,11 +1839,17 @@ _docker_swarm_init() {
 			fi
 			return
 			;;
+		--advertise-addr)
+			if [[ $cur == *: ]] ; then
+				COMPREPLY=( $( compgen -W "2377" -- "${cur##*:}" ) )
+			fi
+			return
+			;;
 	esac
 
 	case "$cur" in
 		-*)
-			COMPREPLY=( $( compgen -W "--force-new-cluster --help --listen-addr" -- "$cur" ) )
+			COMPREPLY=( $( compgen -W "--advertise-addr --force-new-cluster --help --listen-addr" -- "$cur" ) )
 			;;
 	esac
 }
@@ -1873,11 +1879,17 @@ _docker_swarm_join() {
 			fi
 			return
 			;;
+		--advertise-addr)
+			if [[ $cur == *: ]] ; then
+				COMPREPLY=( $( compgen -W "2377" -- "${cur##*:}" ) )
+			fi
+			return
+			;;
 	esac
 
 	case "$cur" in
 		-*)
-			COMPREPLY=( $( compgen -W "--help --listen-addr --token" -- "$cur" ) )
+			COMPREPLY=( $( compgen -W "--adveritse-addr --help --listen-addr --token" -- "$cur" ) )
 			;;
 		*:)
 			COMPREPLY=( $( compgen -W "2377" -- "${cur##*:}" ) )

+ 2 - 0
contrib/completion/zsh/_docker

@@ -1203,6 +1203,7 @@ __docker_swarm_subcommand() {
         (init)
             _arguments $(__docker_arguments) \
                 $opts_help \
+                "($help)--advertise-addr[Advertised address]:ip\:port: " \
                 "($help)*--external-ca=[Specifications of one or more certificate signing endpoints]:endpoint: " \
                 "($help)--force-new-cluster[Force create a new cluster from current state]" \
                 "($help)--listen-addr=[Listen address]:ip\:port: " && ret=0
@@ -1215,6 +1216,7 @@ __docker_swarm_subcommand() {
         (join)
             _arguments $(__docker_arguments) \
                 $opts_help \
+                "($help)--advertise-addr[Advertised address]:ip\:port: " \
                 "($help)--listen-addr=[Listen address]:ip\:port: " \
                 "($help)--token=[Token for entry into the swarm]:secret: " \
                 "($help -):host\:port: " && ret=0

+ 166 - 33
daemon/cluster/cluster.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
+	"net"
 	"os"
 	"path/filepath"
 	"strings"
@@ -73,14 +74,35 @@ var defaultSpec = types.Spec{
 }
 
 type state struct {
+	// LocalAddr is this machine's local IP or hostname, if specified.
+	LocalAddr string
+	// RemoteAddr is the address that was given to "swarm join. It is used
+	// to find LocalAddr if necessary.
+	RemoteAddr string
+	// ListenAddr is the address we bind to, including a port.
 	ListenAddr string
+	// AdvertiseAddr is the address other nodes should connect to,
+	// including a port.
+	AdvertiseAddr string
+}
+
+// NetworkSubnetsProvider exposes functions for retrieving the subnets
+// of networks managed by Docker, so they can be filtered.
+type NetworkSubnetsProvider interface {
+	V4Subnets() []net.IPNet
+	V6Subnets() []net.IPNet
 }
 
 // Config provides values for Cluster.
 type Config struct {
-	Root    string
-	Name    string
-	Backend executorpkg.Backend
+	Root                   string
+	Name                   string
+	Backend                executorpkg.Backend
+	NetworkSubnetsProvider NetworkSubnetsProvider
+
+	// DefaultAdvertiseAddr is the default host/IP or network interface to use
+	// if no AdvertiseAddr value is specified.
+	DefaultAdvertiseAddr string
 }
 
 // Cluster provides capabilities to participate in a cluster as a worker or a
@@ -88,13 +110,17 @@ type Config struct {
 type Cluster struct {
 	sync.RWMutex
 	*node
-	root        string
-	config      Config
-	configEvent chan struct{} // todo: make this array and goroutine safe
-	listenAddr  string
-	stop        bool
-	err         error
-	cancelDelay func()
+	root            string
+	config          Config
+	configEvent     chan struct{} // todo: make this array and goroutine safe
+	localAddr       string
+	actualLocalAddr string // after resolution, not persisted
+	remoteAddr      string
+	listenAddr      string
+	advertiseAddr   string
+	stop            bool
+	err             error
+	cancelDelay     func()
 }
 
 type node struct {
@@ -126,7 +152,7 @@ func New(config Config) (*Cluster, error) {
 		return nil, err
 	}
 
-	n, err := c.startNewNode(false, st.ListenAddr, "", "")
+	n, err := c.startNewNode(false, st.LocalAddr, st.RemoteAddr, st.ListenAddr, st.AdvertiseAddr, "", "")
 	if err != nil {
 		return nil, err
 	}
@@ -162,7 +188,12 @@ func (c *Cluster) loadState() (*state, error) {
 }
 
 func (c *Cluster) saveState() error {
-	dt, err := json.Marshal(state{ListenAddr: c.listenAddr})
+	dt, err := json.Marshal(state{
+		LocalAddr:     c.localAddr,
+		RemoteAddr:    c.remoteAddr,
+		ListenAddr:    c.listenAddr,
+		AdvertiseAddr: c.advertiseAddr,
+	})
 	if err != nil {
 		return err
 	}
@@ -195,7 +226,7 @@ func (c *Cluster) reconnectOnFailure(n *node) {
 			return
 		}
 		var err error
-		n, err = c.startNewNode(false, c.listenAddr, c.getRemoteAddress(), "")
+		n, err = c.startNewNode(false, c.localAddr, c.getRemoteAddress(), c.listenAddr, c.advertiseAddr, c.getRemoteAddress(), "")
 		if err != nil {
 			c.err = err
 			close(n.done)
@@ -204,24 +235,55 @@ func (c *Cluster) reconnectOnFailure(n *node) {
 	}
 }
 
-func (c *Cluster) startNewNode(forceNewCluster bool, listenAddr, joinAddr, joinToken string) (*node, error) {
+func (c *Cluster) startNewNode(forceNewCluster bool, localAddr, remoteAddr, listenAddr, advertiseAddr, joinAddr, joinToken string) (*node, error) {
 	if err := c.config.Backend.IsSwarmCompatible(); err != nil {
 		return nil, err
 	}
+
+	actualLocalAddr := localAddr
+	if actualLocalAddr == "" {
+		// If localAddr was not specified, resolve it automatically
+		// based on the route to joinAddr. localAddr can only be left
+		// empty on "join".
+		listenHost, _, err := net.SplitHostPort(listenAddr)
+		if err != nil {
+			return nil, fmt.Errorf("could not parse listen address: %v", err)
+		}
+
+		listenAddrIP := net.ParseIP(listenHost)
+		if listenAddrIP == nil || !listenAddrIP.IsUnspecified() {
+			actualLocalAddr = listenHost
+		} else {
+			if remoteAddr == "" {
+				// Should never happen except using swarms created by
+				// old versions that didn't save remoteAddr.
+				remoteAddr = "8.8.8.8:53"
+			}
+			conn, err := net.Dial("udp", remoteAddr)
+			if err != nil {
+				return nil, fmt.Errorf("could not find local IP address: %v", err)
+			}
+			localHostPort := conn.LocalAddr().String()
+			actualLocalAddr, _, _ = net.SplitHostPort(localHostPort)
+			conn.Close()
+		}
+	}
+
 	c.node = nil
 	c.cancelDelay = nil
 	c.stop = false
 	n, err := swarmagent.NewNode(&swarmagent.NodeConfig{
-		Hostname:         c.config.Name,
-		ForceNewCluster:  forceNewCluster,
-		ListenControlAPI: filepath.Join(c.root, controlSocket),
-		ListenRemoteAPI:  listenAddr,
-		JoinAddr:         joinAddr,
-		StateDir:         c.root,
-		JoinToken:        joinToken,
-		Executor:         container.NewExecutor(c.config.Backend),
-		HeartbeatTick:    1,
-		ElectionTick:     3,
+		Hostname:           c.config.Name,
+		ForceNewCluster:    forceNewCluster,
+		ListenControlAPI:   filepath.Join(c.root, controlSocket),
+		ListenRemoteAPI:    listenAddr,
+		AdvertiseRemoteAPI: advertiseAddr,
+		JoinAddr:           joinAddr,
+		StateDir:           c.root,
+		JoinToken:          joinToken,
+		Executor:           container.NewExecutor(c.config.Backend),
+		HeartbeatTick:      1,
+		ElectionTick:       3,
 	})
 	if err != nil {
 		return nil, err
@@ -236,8 +298,13 @@ func (c *Cluster) startNewNode(forceNewCluster bool, listenAddr, joinAddr, joinT
 		reconnectDelay: initialReconnectDelay,
 	}
 	c.node = node
+	c.localAddr = localAddr
+	c.actualLocalAddr = actualLocalAddr // not saved
+	c.remoteAddr = remoteAddr
 	c.listenAddr = listenAddr
+	c.advertiseAddr = advertiseAddr
 	c.saveState()
+
 	c.config.Backend.SetClusterProvider(c)
 	go func() {
 		err := n.Err(ctx)
@@ -301,8 +368,49 @@ func (c *Cluster) Init(req types.InitRequest) (string, error) {
 		return "", err
 	}
 
+	listenHost, listenPort, err := resolveListenAddr(req.ListenAddr)
+	if err != nil {
+		c.Unlock()
+		return "", err
+	}
+
+	advertiseHost, advertisePort, err := c.resolveAdvertiseAddr(req.AdvertiseAddr, listenPort)
+	if err != nil {
+		c.Unlock()
+		return "", err
+	}
+
+	localAddr := listenHost
+
+	// If the advertise address is not one of the system's
+	// addresses, we also require a listen address.
+	listenAddrIP := net.ParseIP(listenHost)
+	if listenAddrIP != nil && listenAddrIP.IsUnspecified() {
+		advertiseIP := net.ParseIP(advertiseHost)
+		if advertiseIP == nil {
+			// not an IP
+			c.Unlock()
+			return "", errMustSpecifyListenAddr
+		}
+
+		systemIPs := listSystemIPs()
+
+		found := false
+		for _, systemIP := range systemIPs {
+			if systemIP.Equal(advertiseIP) {
+				found = true
+				break
+			}
+		}
+		if !found {
+			c.Unlock()
+			return "", errMustSpecifyListenAddr
+		}
+		localAddr = advertiseIP.String()
+	}
+
 	// todo: check current state existing
-	n, err := c.startNewNode(req.ForceNewCluster, req.ListenAddr, "", "")
+	n, err := c.startNewNode(req.ForceNewCluster, localAddr, "", net.JoinHostPort(listenHost, listenPort), net.JoinHostPort(advertiseHost, advertisePort), "", "")
 	if err != nil {
 		c.Unlock()
 		return "", err
@@ -339,8 +447,23 @@ func (c *Cluster) Join(req types.JoinRequest) error {
 		c.Unlock()
 		return err
 	}
+
+	listenHost, listenPort, err := resolveListenAddr(req.ListenAddr)
+	if err != nil {
+		c.Unlock()
+		return err
+	}
+
+	var advertiseAddr string
+	advertiseHost, advertisePort, err := c.resolveAdvertiseAddr(req.AdvertiseAddr, listenPort)
+	// For joining, we don't need to provide an advertise address,
+	// since the remote side can detect it.
+	if err == nil {
+		advertiseAddr = net.JoinHostPort(advertiseHost, advertisePort)
+	}
+
 	// todo: check current state existing
-	n, err := c.startNewNode(false, req.ListenAddr, req.RemoteAddrs[0], req.JoinToken)
+	n, err := c.startNewNode(false, "", req.RemoteAddrs[0], net.JoinHostPort(listenHost, listenPort), advertiseAddr, req.RemoteAddrs[0], req.JoinToken)
 	if err != nil {
 		c.Unlock()
 		return err
@@ -530,15 +653,22 @@ func (c *Cluster) IsAgent() bool {
 	return c.node != nil && c.ready
 }
 
-// GetListenAddress returns the listening address for current manager's
-// consensus and dispatcher APIs.
-func (c *Cluster) GetListenAddress() string {
+// GetLocalAddress returns the local address.
+func (c *Cluster) GetLocalAddress() string {
 	c.RLock()
 	defer c.RUnlock()
-	if c.isActiveManager() {
-		return c.listenAddr
+	return c.actualLocalAddr
+}
+
+// GetAdvertiseAddress returns the remotely reachable address of this node.
+func (c *Cluster) GetAdvertiseAddress() string {
+	c.RLock()
+	defer c.RUnlock()
+	if c.advertiseAddr != "" {
+		advertiseHost, _, _ := net.SplitHostPort(c.advertiseAddr)
+		return advertiseHost
 	}
-	return ""
+	return c.actualLocalAddr
 }
 
 // GetRemoteAddress returns a known advertise address of a remote manager if
@@ -572,7 +702,10 @@ func (c *Cluster) ListenClusterEvents() <-chan struct{} {
 
 // Info returns information about the current cluster state.
 func (c *Cluster) Info() types.Info {
-	var info types.Info
+	info := types.Info{
+		NodeAddr: c.GetAdvertiseAddress(),
+	}
+
 	c.RLock()
 	defer c.RUnlock()
 

+ 268 - 0
daemon/cluster/listen_addr.go

@@ -0,0 +1,268 @@
+package cluster
+
+import (
+	"errors"
+	"fmt"
+	"net"
+)
+
+var (
+	errNoSuchInterface         = errors.New("no such interface")
+	errMultipleIPs             = errors.New("could not choose an IP address to advertise since this system has multiple addresses")
+	errNoIP                    = errors.New("could not find the system's IP address")
+	errMustSpecifyListenAddr   = errors.New("must specify a listening address because the address to advertise is not recognized as a system address")
+	errBadListenAddr           = errors.New("listen address must be an IP address or network interface (with optional port number)")
+	errBadAdvertiseAddr        = errors.New("advertise address must be an IP address or network interface (with optional port number)")
+	errBadDefaultAdvertiseAddr = errors.New("default advertise address must be an IP address or network interface (without a port number)")
+)
+
+func resolveListenAddr(specifiedAddr string) (string, string, error) {
+	specifiedHost, specifiedPort, err := net.SplitHostPort(specifiedAddr)
+	if err != nil {
+		return "", "", fmt.Errorf("could not parse listen address %s", specifiedAddr)
+	}
+
+	// Does the host component match any of the interface names on the
+	// system? If so, use the address from that interface.
+	interfaceAddr, err := resolveInterfaceAddr(specifiedHost)
+	if err == nil {
+		return interfaceAddr.String(), specifiedPort, nil
+	}
+	if err != errNoSuchInterface {
+		return "", "", err
+	}
+
+	// If it's not an interface, it must be an IP (for now)
+	if net.ParseIP(specifiedHost) == nil {
+		return "", "", errBadListenAddr
+	}
+
+	return specifiedHost, specifiedPort, nil
+}
+
+func (c *Cluster) resolveAdvertiseAddr(advertiseAddr, listenAddrPort string) (string, string, error) {
+	// Approach:
+	// - If an advertise address is specified, use that. Resolve the
+	//   interface's address if an interface was specified in
+	//   advertiseAddr. Fill in the port from listenAddrPort if necessary.
+	// - If DefaultAdvertiseAddr is not empty, use that with the port from
+	//   listenAddrPort. Resolve the interface's address from
+	//   if an interface name was specified in DefaultAdvertiseAddr.
+	// - Otherwise, try to autodetect the system's address. Use the port in
+	//   listenAddrPort with this address if autodetection succeeds.
+
+	if advertiseAddr != "" {
+		advertiseHost, advertisePort, err := net.SplitHostPort(advertiseAddr)
+		if err != nil {
+			// Not a host:port specification
+			advertiseHost = advertiseAddr
+			advertisePort = listenAddrPort
+		}
+
+		// Does the host component match any of the interface names on the
+		// system? If so, use the address from that interface.
+		interfaceAddr, err := resolveInterfaceAddr(advertiseHost)
+		if err == nil {
+			return interfaceAddr.String(), advertisePort, nil
+		}
+		if err != errNoSuchInterface {
+			return "", "", err
+		}
+
+		// If it's not an interface, it must be an IP (for now)
+		if net.ParseIP(advertiseHost) == nil {
+			return "", "", errBadAdvertiseAddr
+		}
+
+		return advertiseHost, advertisePort, nil
+	}
+
+	if c.config.DefaultAdvertiseAddr != "" {
+		// Does the default advertise address component match any of the
+		// interface names on the system? If so, use the address from
+		// that interface.
+		interfaceAddr, err := resolveInterfaceAddr(c.config.DefaultAdvertiseAddr)
+		if err == nil {
+			return interfaceAddr.String(), listenAddrPort, nil
+		}
+		if err != errNoSuchInterface {
+			return "", "", err
+		}
+
+		// If it's not an interface, it must be an IP (for now)
+		if net.ParseIP(c.config.DefaultAdvertiseAddr) == nil {
+			return "", "", errBadDefaultAdvertiseAddr
+		}
+
+		return c.config.DefaultAdvertiseAddr, listenAddrPort, nil
+	}
+
+	systemAddr, err := c.resolveSystemAddr()
+	if err != nil {
+		return "", "", err
+	}
+	return systemAddr.String(), listenAddrPort, nil
+}
+
+func resolveInterfaceAddr(specifiedInterface string) (net.IP, error) {
+	// Use a specific interface's IP address.
+	intf, err := net.InterfaceByName(specifiedInterface)
+	if err != nil {
+		return nil, errNoSuchInterface
+	}
+
+	addrs, err := intf.Addrs()
+	if err != nil {
+		return nil, err
+	}
+
+	var interfaceAddr4, interfaceAddr6 net.IP
+
+	for _, addr := range addrs {
+		ipAddr, ok := addr.(*net.IPNet)
+
+		if ok {
+			if ipAddr.IP.To4() != nil {
+				// IPv4
+				if interfaceAddr4 != nil {
+					return nil, fmt.Errorf("interface %s has more than one IPv4 address", specifiedInterface)
+				}
+				interfaceAddr4 = ipAddr.IP
+			} else {
+				// IPv6
+				if interfaceAddr6 != nil {
+					return nil, fmt.Errorf("interface %s has more than one IPv6 address", specifiedInterface)
+				}
+				interfaceAddr6 = ipAddr.IP
+			}
+		}
+	}
+
+	if interfaceAddr4 == nil && interfaceAddr6 == nil {
+		return nil, fmt.Errorf("interface %s has no usable IPv4 or IPv6 address", specifiedInterface)
+	}
+
+	// In the case that there's exactly one IPv4 address
+	// and exactly one IPv6 address, favor IPv4 over IPv6.
+	if interfaceAddr4 != nil {
+		return interfaceAddr4, nil
+	}
+	return interfaceAddr6, nil
+}
+
+func (c *Cluster) resolveSystemAddr() (net.IP, error) {
+	// Use the system's only IP address, or fail if there are
+	// multiple addresses to choose from.
+	interfaces, err := net.Interfaces()
+	if err != nil {
+		return nil, err
+	}
+
+	var systemAddr net.IP
+
+	// List Docker-managed subnets
+	v4Subnets := c.config.NetworkSubnetsProvider.V4Subnets()
+	v6Subnets := c.config.NetworkSubnetsProvider.V6Subnets()
+
+ifaceLoop:
+	for _, intf := range interfaces {
+		// Skip inactive interfaces and loopback interfaces
+		if (intf.Flags&net.FlagUp == 0) || (intf.Flags&net.FlagLoopback) != 0 {
+			continue
+		}
+
+		addrs, err := intf.Addrs()
+		if err != nil {
+			continue
+		}
+
+		var interfaceAddr4, interfaceAddr6 net.IP
+
+		for _, addr := range addrs {
+			ipAddr, ok := addr.(*net.IPNet)
+
+			// Skip loopback and link-local addresses
+			if !ok || !ipAddr.IP.IsGlobalUnicast() {
+				continue
+			}
+
+			if ipAddr.IP.To4() != nil {
+				// IPv4
+
+				// Ignore addresses in subnets that are managed by Docker.
+				for _, subnet := range v4Subnets {
+					if subnet.Contains(ipAddr.IP) {
+						continue ifaceLoop
+					}
+				}
+
+				if interfaceAddr4 != nil {
+					return nil, errMultipleIPs
+				}
+
+				interfaceAddr4 = ipAddr.IP
+			} else {
+				// IPv6
+
+				// Ignore addresses in subnets that are managed by Docker.
+				for _, subnet := range v6Subnets {
+					if subnet.Contains(ipAddr.IP) {
+						continue ifaceLoop
+					}
+				}
+
+				if interfaceAddr6 != nil {
+					return nil, errMultipleIPs
+				}
+
+				interfaceAddr6 = ipAddr.IP
+			}
+		}
+
+		// In the case that this interface has exactly one IPv4 address
+		// and exactly one IPv6 address, favor IPv4 over IPv6.
+		if interfaceAddr4 != nil {
+			if systemAddr != nil {
+				return nil, errMultipleIPs
+			}
+			systemAddr = interfaceAddr4
+		} else if interfaceAddr6 != nil {
+			if systemAddr != nil {
+				return nil, errMultipleIPs
+			}
+			systemAddr = interfaceAddr6
+		}
+	}
+
+	if systemAddr == nil {
+		return nil, errNoIP
+	}
+
+	return systemAddr, nil
+}
+
+func listSystemIPs() []net.IP {
+	interfaces, err := net.Interfaces()
+	if err != nil {
+		return nil
+	}
+
+	var systemAddrs []net.IP
+
+	for _, intf := range interfaces {
+		addrs, err := intf.Addrs()
+		if err != nil {
+			continue
+		}
+
+		for _, addr := range addrs {
+			ipAddr, ok := addr.(*net.IPNet)
+
+			if ok {
+				systemAddrs = append(systemAddrs, ipAddr.IP)
+			}
+		}
+	}
+
+	return systemAddrs
+}

+ 9 - 0
daemon/config.go

@@ -127,6 +127,13 @@ type CommonConfig struct {
 	// Embedded structs that allow config
 	// deserialization without the full struct.
 	CommonTLSOptions
+
+	// SwarmDefaultAdvertiseAddr is the default host/IP or network interface
+	// to use if a wildcard address is specified in the ListenAddr value
+	// given to the /swarm/init endpoint and no advertise address is
+	// specified.
+	SwarmDefaultAdvertiseAddr string `json:"swarm-default-advertise-addr"`
+
 	LogConfig
 	bridgeConfig // bridgeConfig holds bridge network specific configuration.
 	registry.ServiceOptions
@@ -167,6 +174,8 @@ func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string)
 	cmd.IntVar(&maxConcurrentDownloads, []string{"-max-concurrent-downloads"}, defaultMaxConcurrentDownloads, usageFn("Set the max concurrent downloads for each pull"))
 	cmd.IntVar(&maxConcurrentUploads, []string{"-max-concurrent-uploads"}, defaultMaxConcurrentUploads, usageFn("Set the max concurrent uploads for each push"))
 
+	cmd.StringVar(&config.SwarmDefaultAdvertiseAddr, []string{"-swarm-default-advertise-addr"}, "", usageFn("Set default address or interface for swarm advertised address"))
+
 	config.MaxConcurrentDownloads = &maxConcurrentDownloads
 	config.MaxConcurrentUploads = &maxConcurrentUploads
 }

+ 4 - 0
daemon/config_solaris.go

@@ -38,6 +38,10 @@ func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) strin
 	config.attachExperimentalFlags(cmd, usageFn)
 }
 
+// GetExecRoot returns the user configured Exec-root
+func (config *Config) GetExecRoot() string {
+	return config.ExecRoot
+}
 func (config *Config) isSwarmCompatible() error {
 	return nil
 }

+ 5 - 0
daemon/config_unix.go

@@ -124,6 +124,11 @@ func (config *Config) GetAllRuntimes() map[string]types.Runtime {
 	return rts
 }
 
+// GetExecRoot returns the user configured Exec-root
+func (config *Config) GetExecRoot() string {
+	return config.ExecRoot
+}
+
 func (config *Config) isSwarmCompatible() error {
 	if config.ClusterStore != "" || config.ClusterAdvertise != "" {
 		return fmt.Errorf("--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode")

+ 5 - 0
daemon/config_windows.go

@@ -58,6 +58,11 @@ func (config *Config) GetAllRuntimes() map[string]types.Runtime {
 	return map[string]types.Runtime{}
 }
 
+// GetExecRoot returns the user configured Exec-root
+func (config *Config) GetExecRoot() string {
+	return ""
+}
+
 func (config *Config) isSwarmCompatible() error {
 	return nil
 }

+ 37 - 0
daemon/daemon.go

@@ -728,6 +728,42 @@ func (daemon *Daemon) Unmount(container *container.Container) error {
 	return nil
 }
 
+// V4Subnets returns the IPv4 subnets of networks that are managed by Docker.
+func (daemon *Daemon) V4Subnets() []net.IPNet {
+	var subnets []net.IPNet
+
+	managedNetworks := daemon.netController.Networks()
+
+	for _, managedNetwork := range managedNetworks {
+		v4Infos, _ := managedNetwork.Info().IpamInfo()
+		for _, v4Info := range v4Infos {
+			if v4Info.IPAMData.Pool != nil {
+				subnets = append(subnets, *v4Info.IPAMData.Pool)
+			}
+		}
+	}
+
+	return subnets
+}
+
+// V6Subnets returns the IPv6 subnets of networks that are managed by Docker.
+func (daemon *Daemon) V6Subnets() []net.IPNet {
+	var subnets []net.IPNet
+
+	managedNetworks := daemon.netController.Networks()
+
+	for _, managedNetwork := range managedNetworks {
+		_, v6Infos := managedNetwork.Info().IpamInfo()
+		for _, v6Info := range v6Infos {
+			if v6Info.IPAMData.Pool != nil {
+				subnets = append(subnets, *v6Info.IPAMData.Pool)
+			}
+		}
+	}
+
+	return subnets
+}
+
 func writeDistributionProgress(cancelFunc func(), outStream io.Writer, progressChan <-chan progress.Progress) {
 	progressOutput := streamformatter.NewJSONStreamFormatter().NewProgressOutput(outStream, false)
 	operationCancelled := false
@@ -1005,6 +1041,7 @@ func (daemon *Daemon) networkOptions(dconfig *Config, activeSandboxes map[string
 	}
 
 	options = append(options, nwconfig.OptionDataDir(dconfig.Root))
+	options = append(options, nwconfig.OptionExecRoot(dconfig.GetExecRoot()))
 
 	dd := runconfig.DefaultDaemonNetworkMode()
 	dn := runconfig.DefaultDaemonNetworkMode().NetworkName()

+ 17 - 2
docs/reference/api/docker_remote_api_v1.24.md

@@ -3591,6 +3591,7 @@ Initialize a new Swarm
 
     {
       "ListenAddr": "0.0.0.0:4500",
+      "AdvertiseAddr": "192.168.1.1:4500",
       "ForceNewCluster": false,
       "Spec": {
         "Orchestration": {},
@@ -3614,8 +3615,16 @@ Initialize a new Swarm
 
 JSON Parameters:
 
-- **ListenAddr** – Listen address used for inter-manager communication, as well as determining.
-  the networking interface used for the VXLAN Tunnel Endpoint (VTEP).
+- **ListenAddr** – Listen address used for inter-manager communication, as well as determining
+  the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an
+  address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port
+  number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is
+  used.
+- **AdvertiseAddr** – Externally reachable address advertised to other nodes. This can either be
+  an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port
+  number, like `eth0:4567`. If the port number is omitted, the port number from the listen
+  address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when
+  possible.
 - **ForceNewCluster** – Force creating a new Swarm even if already part of one.
 - **Spec** – Configuration settings of the new Swarm.
     - **Orchestration** – Configuration settings for the orchestration aspects of the Swarm.
@@ -3656,6 +3665,7 @@ Join an existing new Swarm
 
     {
       "ListenAddr": "0.0.0.0:4500",
+      "AdvertiseAddr: "192.168.1.1:4500",
       "RemoteAddrs": ["node1:4500"],
       "JoinToken": "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2"
     }
@@ -3676,6 +3686,11 @@ JSON Parameters:
 
 - **ListenAddr** – Listen address used for inter-manager communication if the node gets promoted to
   manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP).
+- **AdvertiseAddr** – Externally reachable address advertised to other nodes. This can either be
+  an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port
+  number, like `eth0:4567`. If the port number is omitted, the port number from the listen
+  address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when
+  possible.
 - **RemoteAddr** – Address of any manager node already participating in the Swarm to join.
 - **JoinToken** – Secret token for joining this Swarm.
 

+ 17 - 2
docs/reference/api/docker_remote_api_v1.25.md

@@ -3592,6 +3592,7 @@ Initialize a new Swarm
 
     {
       "ListenAddr": "0.0.0.0:4500",
+      "AdvertiseAddr": "192.168.1.1:4500",
       "ForceNewCluster": false,
       "Spec": {
         "Orchestration": {},
@@ -3615,8 +3616,16 @@ Initialize a new Swarm
 
 JSON Parameters:
 
-- **ListenAddr** – Listen address used for inter-manager communication, as well as determining.
-  the networking interface used for the VXLAN Tunnel Endpoint (VTEP).
+- **ListenAddr** – Listen address used for inter-manager communication, as well as determining
+  the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an
+  address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port
+  number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is
+  used.
+- **AdvertiseAddr** – Externally reachable address advertised to other nodes. This can either be
+  an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port
+  number, like `eth0:4567`. If the port number is omitted, the port number from the listen
+  address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when
+  possible.
 - **ForceNewCluster** – Force creating a new Swarm even if already part of one.
 - **Spec** – Configuration settings of the new Swarm.
     - **Orchestration** – Configuration settings for the orchestration aspects of the Swarm.
@@ -3657,6 +3666,7 @@ Join an existing new Swarm
 
     {
       "ListenAddr": "0.0.0.0:4500",
+      "AdvertiseAddr": "192.168.1.1:4500",
       "RemoteAddrs": ["node1:4500"],
       "JoinToken": "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2"
     }
@@ -3677,6 +3687,11 @@ JSON Parameters:
 
 - **ListenAddr** – Listen address used for inter-manager communication if the node gets promoted to
   manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP).
+- **AdvertiseAddr** – Externally reachable address advertised to other nodes. This can either be
+  an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port
+  number, like `eth0:4567`. If the port number is omitted, the port number from the listen
+  address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when
+  possible.
 - **RemoteAddr** – Address of any manager node already participating in the Swarm to join.
 - **JoinToken** – Secret token for joining this Swarm.
 

+ 3 - 0
docs/reference/commandline/dockerd.md

@@ -69,6 +69,7 @@ Options:
       -s, --storage-driver                   Storage driver to use
       --selinux-enabled                      Enable selinux support
       --storage-opt=[]                       Storage driver options
+      --swarm-default-advertise-addr         Set default address or interface for swarm advertised address
       --tls                                  Use TLS; implied by --tlsverify
       --tlscacert=~/.docker/ca.pem           Trust certs signed only by this CA
       --tlscert=~/.docker/cert.pem           Path to TLS certificate file
@@ -1042,6 +1043,7 @@ This is a full example of the allowed configuration options on Linux:
 	"tlscacert": "",
 	"tlscert": "",
 	"tlskey": "",
+	"swarm-default-advertise-addr": "",
 	"api-cors-header": "",
 	"selinux-enabled": false,
 	"userns-remap": "",
@@ -1112,6 +1114,7 @@ This is a full example of the allowed configuration options on Windows:
     "tlscacert": "",
     "tlscert": "",
     "tlskey": "",
+    "swarm-default-advertise-addr": "",
     "group": "",
     "default-ulimits": {},
     "bridge": "",

+ 25 - 4
docs/reference/commandline/swarm_init.md

@@ -17,12 +17,13 @@ Usage:  docker swarm init [OPTIONS]
 Initialize a swarm
 
 Options:
+      --advertise-addr value            Advertised address (format: <ip|interface>[:port])
       --cert-expiry duration            Validity period for node certificates (default 2160h0m0s)
       --dispatcher-heartbeat duration   Dispatcher heartbeat period (default 5s)
       --external-ca value               Specifications of one or more certificate signing endpoints
       --force-new-cluster               Force create a new cluster from current state.
       --help                            Print usage
-      --listen-addr value               Listen address (default 0.0.0.0:2377)
+      --listen-addr value               Listen address (format: <ip|interface>[:port])
       --task-history-limit int          Task history retention limit (default 5)
 ```
 
@@ -31,7 +32,7 @@ in the newly created one node swarm cluster.
 
 
 ```bash
-$ docker swarm init --listen-addr 192.168.99.121:2377
+$ docker swarm init --advertise-addr 192.168.99.121
 Swarm initialized: current node (bvz81updecsj6wjz393c09vti) is now a manager.
 
 To add a worker to this swarm, run the following command:
@@ -70,11 +71,31 @@ The URL specifies the endpoint where signing requests should be submitted.
 
 ### `--force-new-cluster`
 
-This flag forces an existing node that was part of a quorum that was lost to restart as a single node Manager without losing its data
+This flag forces an existing node that was part of a quorum that was lost to restart as a single node Manager without losing its data.
 
 ### `--listen-addr value`
 
-The node listens for inbound swarm manager traffic on this IP:PORT
+The node listens for inbound Swarm manager traffic on this address. The default is to listen on
+0.0.0.0:2377. It is also possible to specify a network interface to listen on that interface's
+address; for example `--listen-addr eth0:2377`.
+
+Specifying a port is optional. If the value is a bare IP address or interface
+name, the default port 2377 will be used.
+
+### `--advertise-addr value`
+
+This flag specifies the address that will be advertised to other members of the
+swarm for API access and overlay networking. If unspecified, Docker will check
+if the system has a single IP address, and use that IP address with with the
+listening port (see `--listen-addr`). If the system has multiple IP addresses,
+`--advertise-addr` must be specified so that the correct address is chosen for
+inter-manager communication and overlay networking.
+
+It is also possible to specify a network interface to advertise that interface's address;
+for example `--advertise-addr eth0:2377`.
+
+Specifying a port is optional. If the value is a bare IP address or interface
+name, the default port 2377 will be used.
 
 ### `--task-history-limit`
 

+ 36 - 6
docs/reference/commandline/swarm_join.md

@@ -17,9 +17,10 @@ Usage:  docker swarm join [OPTIONS] HOST:PORT
 Join a swarm as a node and/or manager
 
 Options:
-      --help                Print usage
-      --listen-addr value   Listen address (default 0.0.0.0:2377)
-      --token string        Token for entry into the swarm
+      --advertise-addr value   Advertised address (format: <ip|interface>[:port])
+      --help                   Print usage
+      --listen-addr value      Listen address (format: <ip|interface>[:port)
+      --token string           Token for entry into the swarm
 ```
 
 Join a node to a swarm. The node joins as a manager node or worker node based upon the token you
@@ -31,7 +32,7 @@ pass a worker token, the node joins as a worker.
 The example below demonstrates joining a manager node using a manager token.
 
 ```bash
-$ docker swarm join --token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2 --listen-addr 192.168.99.122:2377 192.168.99.121:2377
+$ docker swarm join --token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2 192.168.99.121:2377
 This node joined a swarm as a manager.
 $ docker node ls
 ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
@@ -48,7 +49,7 @@ should join as workers instead. Managers should be stable hosts that have static
 The example below demonstrates joining a worker node using a worker token.
 
 ```bash
-$ docker swarm join --token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx --listen-addr 192.168.99.123:2377 192.168.99.121:2377
+$ docker swarm join --token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx 192.168.99.121:2377
 This node joined a swarm as a worker.
 $ docker node ls
 ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
@@ -59,7 +60,36 @@ dvfxp4zseq4s0rih1selh0d20 *  manager1  Ready   Active        Leader
 
 ### `--listen-addr value`
 
-The node listens for inbound swarm manager traffic on this IP:PORT
+If the node is a manager, it will listen for inbound Swarm manager traffic on this
+address. The default is to listen on 0.0.0.0:2377. It is also possible to specify a
+network interface to listen on that interface's address; for example `--listen-addr eth0:2377`.
+
+Specifying a port is optional. If the value is a bare IP address, or interface
+name, the default port 2377 will be used.
+
+This flag is generally not necessary when joining an existing swarm.
+
+### `--advertise-addr value`
+
+This flag specifies the address that will be advertised to other members of the
+swarm for API access. If unspecified, Docker will check if the system has a
+single IP address, and use that IP address with with the listening port (see
+`--listen-addr`). If the system has multiple IP addresses, `--advertise-addr`
+must be specified so that the correct address is chosen for inter-manager
+communication and overlay networking.
+
+It is also possible to specify a network interface to advertise that interface's address;
+for example `--advertise-addr eth0:2377`.
+
+Specifying a port is optional. If the value is a bare IP address, or interface
+name, the default port 2377 will be used.
+
+This flag is generally not necessary when joining an existing swarm.
+
+### `--manager`
+
+Joins the node as a manager
+>>>>>>> 22565e1... Split advertised address from listen address
 
 ### `--token string`
 

+ 5 - 5
docs/swarm/swarm-tutorial/create-swarm.md

@@ -23,14 +23,14 @@ node. For example, the tutorial uses a machine named `manager1`.
 2. Run the following command to create a new swarm:
 
     ```bash
-    docker swarm init --listen-addr <MANAGER-IP>:<PORT>
+    docker swarm init --advertise-addr <MANAGER-IP>
     ```
 
     In the tutorial, the following command creates a swarm on the `manager1`
     machine:
 
     ```bash
-    $ docker swarm init --listen-addr 192.168.99.100:2377
+    $ docker swarm init --advertise-addr 192.168.99.100
     Swarm initialized: current node (dxn1zf6l61qsb1josjja83ngz) is now a manager.
 
     To add a worker to this swarm, run the following command:
@@ -44,9 +44,9 @@ node. For example, the tutorial uses a machine named `manager1`.
         192.168.99.100:2377
     ```
 
-    The `--listen-addr` flag configures the manager node to listen on port
-    `2377`. The other nodes in the swarm must be able to access the manager at
-    the IP address.
+    The `--advertise-addr` flag configures the manager node to publish its
+    address as `192.168.99.100`. The other nodes in the swarm must be able
+    to access the manager at the IP address.
 
     The output incudes the commands to join new nodes to the swarm. Nodes will
     join as managers or workers depending on the value for the `--swarm-token`

+ 3 - 3
hack/vendor.sh

@@ -65,7 +65,7 @@ clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
 clone git github.com/imdario/mergo 0.2.1
 
 #get libnetwork packages
-clone git github.com/docker/libnetwork 905d374c096ca1f3a9b75529e52518b7540179f3
+clone git github.com/docker/libnetwork 83ab4deaa2da3deb32cb5e64ceec43801dc17370
 clone git github.com/docker/go-events afb2b9f2c23f33ada1a22b03651775fdc65a5089
 clone git github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
@@ -75,7 +75,7 @@ clone git github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa07
 clone git github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
 clone git github.com/docker/libkv v0.2.1
 clone git github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
-clone git github.com/vishvananda/netlink 734d02c3e202f682c74b71314b2c61eec0170fd4
+clone git github.com/vishvananda/netlink e73bad418fd727ed3a02830b1af1ad0283a1de6c
 clone git github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
 clone git github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
 clone git github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
@@ -139,7 +139,7 @@ clone git github.com/docker/docker-credential-helpers v0.3.0
 clone git github.com/docker/containerd 0ac3cd1be170d180b2baed755e8f0da547ceb267
 
 # cluster
-clone git github.com/docker/swarmkit 38857c06dafcf939a56d2650d8e0011b5aace384
+clone git github.com/docker/swarmkit 4d7e44321726f011d010cdb72d2230f5db2b604e
 clone git github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
 clone git github.com/gogo/protobuf 43a2e0b1c32252bfbbdf81f7faa7a88fb3fa4028
 clone git github.com/cloudflare/cfssl b895b0549c0ff676f92cf09ba971ae02bb41367b

+ 1 - 1
integration-cli/check_test.go

@@ -211,7 +211,7 @@ func (s *DockerSwarmSuite) AddDaemon(c *check.C, joinSwarm, manager bool) *Swarm
 		port:   defaultSwarmPort + s.portIndex,
 	}
 	d.listenAddr = fmt.Sprintf("0.0.0.0:%d", d.port)
-	err := d.StartWithBusybox("--iptables=false") // avoid networking conflicts
+	err := d.StartWithBusybox("--iptables=false", "--swarm-default-advertise-addr=lo") // avoid networking conflicts
 	c.Assert(err, check.IsNil)
 
 	if joinSwarm == true {

+ 4 - 6
integration-cli/docker_api_swarm_test.go

@@ -624,11 +624,9 @@ func (s *DockerSwarmSuite) TestApiSwarmLeaveOnPendingJoin(c *check.C) {
 
 	go d2.Join(swarm.JoinRequest{
 		RemoteAddrs: []string{"nosuchhost:1234"},
-	}) // will block on pending state
-
-	waitAndAssert(c, defaultReconciliationTimeout, d2.checkLocalNodeState, checker.Equals, swarm.LocalNodeStatePending)
+	})
 
-	c.Assert(d2.Leave(true), checker.IsNil)
+	waitAndAssert(c, defaultReconciliationTimeout, d2.checkLocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
 
 	waitAndAssert(c, defaultReconciliationTimeout, d2.checkActiveContainerCount, checker.Equals, 1)
 
@@ -642,9 +640,9 @@ func (s *DockerSwarmSuite) TestApiSwarmRestoreOnPendingJoin(c *check.C) {
 	d := s.AddDaemon(c, false, false)
 	go d.Join(swarm.JoinRequest{
 		RemoteAddrs: []string{"nosuchhost:1234"},
-	}) // will block on pending state
+	})
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.checkLocalNodeState, checker.Equals, swarm.LocalNodeStatePending)
+	waitAndAssert(c, defaultReconciliationTimeout, d.checkLocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
 
 	c.Assert(d.Stop(), checker.IsNil)
 	c.Assert(d.Start(), checker.IsNil)

+ 6 - 0
man/dockerd.8.md

@@ -55,6 +55,7 @@ dockerd - Enable daemon mode
 [**-s**|**--storage-driver**[=*STORAGE-DRIVER*]]
 [**--selinux-enabled**]
 [**--storage-opt**[=*[]*]]
+[**--swarm-default-advertise-addr**[=*IP|INTERFACE*]]
 [**--tls**]
 [**--tlscacert**[=*~/.docker/ca.pem*]]
 [**--tlscert**[=*~/.docker/cert.pem*]]
@@ -239,6 +240,11 @@ output otherwise.
 **--storage-opt**=[]
   Set storage driver options. See STORAGE DRIVER OPTIONS.
 
+**--swarm-default-advertise-addr**=*IP|INTERFACE*
+  Set default address or interface for swarm to advertise as its externally-reachable address to other cluster
+  members. This can be a hostname, an IP address, or an interface such as `eth0`. A port cannot be specified with
+  this option.
+
 **--tls**=*true*|*false*
   Use TLS; implied by --tlsverify. Default is false.
 

+ 14 - 22
vendor/src/github.com/docker/libnetwork/agent.go

@@ -35,6 +35,7 @@ func (b ByTime) Less(i, j int) bool { return b[i].LamportTime < b[j].LamportTime
 type agent struct {
 	networkDB         *networkdb.NetworkDB
 	bindAddr          string
+	advertiseAddr     string
 	epTblCancel       func()
 	driverCancelFuncs map[string][]func()
 }
@@ -236,25 +237,14 @@ func (c *controller) handleKeyChangeV1(keys []*types.EncryptionKey) error {
 func (c *controller) agentSetup() error {
 	clusterProvider := c.cfg.Daemon.ClusterProvider
 
-	bindAddr, _, _ := net.SplitHostPort(clusterProvider.GetListenAddress())
+	bindAddr := clusterProvider.GetLocalAddress()
+	advAddr := clusterProvider.GetAdvertiseAddress()
 	remote := clusterProvider.GetRemoteAddress()
 	remoteAddr, _, _ := net.SplitHostPort(remote)
 
-	// Determine the BindAddress from RemoteAddress or through best-effort routing
-	if !isValidClusteringIP(bindAddr) {
-		if !isValidClusteringIP(remoteAddr) {
-			remote = "8.8.8.8:53"
-		}
-		conn, err := net.Dial("udp", remote)
-		if err == nil {
-			bindHostPort := conn.LocalAddr().String()
-			bindAddr, _, _ = net.SplitHostPort(bindHostPort)
-			conn.Close()
-		}
-	}
-
-	if bindAddr != "" && c.agent == nil {
-		if err := c.agentInit(bindAddr); err != nil {
+	logrus.Infof("Initializing Libnetwork Agent Local-addr=%s Adv-addr=%s Remote-addr =%s", bindAddr, advAddr, remoteAddr)
+	if advAddr != "" && c.agent == nil {
+		if err := c.agentInit(bindAddr, advAddr); err != nil {
 			logrus.Errorf("Error in agentInit : %v", err)
 		} else {
 			c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
@@ -312,7 +302,7 @@ func (c *controller) getPrimaryKeyTag(subsys string) ([]byte, uint64) {
 	return keys[1].Key, keys[1].LamportTime
 }
 
-func (c *controller) agentInit(bindAddrOrInterface string) error {
+func (c *controller) agentInit(bindAddrOrInterface, advertiseAddr string) error {
 	if !c.isAgent() {
 		return nil
 	}
@@ -325,9 +315,9 @@ func (c *controller) agentInit(bindAddrOrInterface string) error {
 	keys, tags := c.getKeys(subsysGossip)
 	hostname, _ := os.Hostname()
 	nDB, err := networkdb.New(&networkdb.Config{
-		BindAddr: bindAddr,
-		NodeName: hostname,
-		Keys:     keys,
+		AdvertiseAddr: advertiseAddr,
+		NodeName:      hostname,
+		Keys:          keys,
 	})
 
 	if err != nil {
@@ -339,6 +329,7 @@ func (c *controller) agentInit(bindAddrOrInterface string) error {
 	c.agent = &agent{
 		networkDB:         nDB,
 		bindAddr:          bindAddr,
+		advertiseAddr:     advertiseAddr,
 		epTblCancel:       cancel,
 		driverCancelFuncs: make(map[string][]func()),
 	}
@@ -377,8 +368,9 @@ func (c *controller) agentDriverNotify(d driverapi.Driver) {
 	}
 
 	d.DiscoverNew(discoverapi.NodeDiscovery, discoverapi.NodeDiscoveryData{
-		Address: c.agent.bindAddr,
-		Self:    true,
+		Address:     c.agent.advertiseAddr,
+		BindAddress: c.agent.bindAddr,
+		Self:        true,
 	})
 
 	drvEnc := discoverapi.DriverEncryptionConfig{}

+ 2 - 1
vendor/src/github.com/docker/libnetwork/cluster/provider.go

@@ -4,7 +4,8 @@ package cluster
 type Provider interface {
 	IsManager() bool
 	IsAgent() bool
-	GetListenAddress() string
+	GetLocalAddress() string
+	GetAdvertiseAddress() string
 	GetRemoteAddress() string
 	ListenClusterEvents() <-chan struct{}
 }

+ 8 - 0
vendor/src/github.com/docker/libnetwork/config/config.go

@@ -11,6 +11,7 @@ import (
 	"github.com/docker/libnetwork/cluster"
 	"github.com/docker/libnetwork/datastore"
 	"github.com/docker/libnetwork/netlabel"
+	"github.com/docker/libnetwork/osl"
 )
 
 // Config encapsulates configurations of various Libnetwork components
@@ -197,6 +198,13 @@ func OptionDataDir(dataDir string) Option {
 	}
 }
 
+// OptionExecRoot function returns an option setter for exec root folder
+func OptionExecRoot(execRoot string) Option {
+	return func(c *Config) {
+		osl.SetBasePath(execRoot)
+	}
+}
+
 // ProcessOptions processes options and stores it in config
 func (c *Config) ProcessOptions(options ...Option) {
 	for _, opt := range options {

+ 4 - 0
vendor/src/github.com/docker/libnetwork/controller.go

@@ -378,6 +378,10 @@ func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error {
 		return nil
 	}
 
+	c.Lock()
+	c.cfg = cfg
+	c.Unlock()
+
 	var dsConfig *discoverapi.DatastoreConfigData
 	for scope, sCfg := range cfg.Scopes {
 		if scope == datastore.LocalScope || !sCfg.IsValid() {

+ 3 - 2
vendor/src/github.com/docker/libnetwork/discoverapi/discoverapi.go

@@ -26,8 +26,9 @@ const (
 
 // NodeDiscoveryData represents the structure backing the node discovery data json string
 type NodeDiscoveryData struct {
-	Address string
-	Self    bool
+	Address     string
+	BindAddress string
+	Self        bool
 }
 
 // DatastoreConfigData is the data for the datastore update event message

+ 2 - 2
vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_store.go

@@ -83,9 +83,9 @@ func (d *driver) populateEndpoints() error {
 		n, ok := d.networks[ep.nid]
 		if !ok {
 			logrus.Debugf("Network (%s) not found for restored bridge endpoint (%s)", ep.nid[0:7], ep.id[0:7])
-			logrus.Debugf("Deleting stale bridge endpoint (%s) from store", ep.nid[0:7])
+			logrus.Debugf("Deleting stale bridge endpoint (%s) from store", ep.id[0:7])
 			if err := d.storeDelete(ep); err != nil {
-				logrus.Debugf("Failed to delete stale bridge endpoint (%s) from store", ep.nid[0:7])
+				logrus.Debugf("Failed to delete stale bridge endpoint (%s) from store", ep.id[0:7])
 			}
 			continue
 		}

+ 1 - 1
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go

@@ -82,6 +82,6 @@ func (d *driver) DeleteEndpoint(nid, eid string) error {
 	if err := d.storeDelete(ep); err != nil {
 		logrus.Warnf("Failed to remove ipvlan endpoint %s from store: %v", ep.id[0:7], err)
 	}
-
+	n.deleteEndpoint(ep.id)
 	return nil
 }

+ 2 - 2
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go

@@ -96,9 +96,9 @@ func (d *driver) populateEndpoints() error {
 		n, ok := d.networks[ep.nid]
 		if !ok {
 			logrus.Debugf("Network (%s) not found for restored ipvlan endpoint (%s)", ep.nid[0:7], ep.id[0:7])
-			logrus.Debugf("Deleting stale ipvlan endpoint (%s) from store", ep.nid[0:7])
+			logrus.Debugf("Deleting stale ipvlan endpoint (%s) from store", ep.id[0:7])
 			if err := d.storeDelete(ep); err != nil {
-				logrus.Debugf("Failed to delete stale ipvlan endpoint (%s) from store", ep.nid[0:7])
+				logrus.Debugf("Failed to delete stale ipvlan endpoint (%s) from store", ep.id[0:7])
 			}
 			continue
 		}

+ 2 - 2
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go

@@ -96,9 +96,9 @@ func (d *driver) populateEndpoints() error {
 		n, ok := d.networks[ep.nid]
 		if !ok {
 			logrus.Debugf("Network (%s) not found for restored macvlan endpoint (%s)", ep.nid[0:7], ep.id[0:7])
-			logrus.Debugf("Deleting stale macvlan endpoint (%s) from store", ep.nid[0:7])
+			logrus.Debugf("Deleting stale macvlan endpoint (%s) from store", ep.id[0:7])
 			if err := d.storeDelete(ep); err != nil {
-				logrus.Debugf("Failed to delete stale macvlan endpoint (%s) from store", ep.nid[0:7])
+				logrus.Debugf("Failed to delete stale macvlan endpoint (%s) from store", ep.id[0:7])
 			}
 			continue
 		}

+ 40 - 16
vendor/src/github.com/docker/libnetwork/drivers/overlay/encryption.go

@@ -2,23 +2,27 @@ package overlay
 
 import (
 	"bytes"
+	"encoding/binary"
 	"encoding/hex"
 	"fmt"
+	"hash/fnv"
 	"net"
 	"sync"
 	"syscall"
 
+	"strconv"
+
 	log "github.com/Sirupsen/logrus"
 	"github.com/docker/libnetwork/iptables"
 	"github.com/docker/libnetwork/ns"
 	"github.com/docker/libnetwork/types"
 	"github.com/vishvananda/netlink"
-	"strconv"
 )
 
 const (
-	mark    = uint32(0xD0C4E3)
-	timeout = 30
+	mark         = uint32(0xD0C4E3)
+	timeout      = 30
+	pktExpansion = 26 // SPI(4) + SeqN(4) + IV(8) + PadLength(1) + NextHeader(1) + ICV(8)
 )
 
 const (
@@ -85,6 +89,7 @@ func (d *driver) checkEncryption(nid string, rIP net.IP, vxlanID uint32, isLocal
 	}
 
 	lIP := types.GetMinimalIP(net.ParseIP(d.bindAddress))
+	aIP := types.GetMinimalIP(net.ParseIP(d.advertiseAddress))
 	nodes := map[string]net.IP{}
 
 	switch {
@@ -107,7 +112,7 @@ func (d *driver) checkEncryption(nid string, rIP net.IP, vxlanID uint32, isLocal
 
 	if add {
 		for _, rIP := range nodes {
-			if err := setupEncryption(lIP, rIP, vxlanID, d.secMap, d.keys); err != nil {
+			if err := setupEncryption(lIP, aIP, rIP, vxlanID, d.secMap, d.keys); err != nil {
 				log.Warnf("Failed to program network encryption between %s and %s: %v", lIP, rIP, err)
 			}
 		}
@@ -122,7 +127,7 @@ func (d *driver) checkEncryption(nid string, rIP net.IP, vxlanID uint32, isLocal
 	return nil
 }
 
-func setupEncryption(localIP, remoteIP net.IP, vni uint32, em *encrMap, keys []*key) error {
+func setupEncryption(localIP, advIP, remoteIP net.IP, vni uint32, em *encrMap, keys []*key) error {
 	log.Debugf("Programming encryption for vxlan %d between %s and %s", vni, localIP, remoteIP)
 	rIPs := remoteIP.String()
 
@@ -134,7 +139,7 @@ func setupEncryption(localIP, remoteIP net.IP, vni uint32, em *encrMap, keys []*
 	}
 
 	for i, k := range keys {
-		spis := &spi{buildSPI(localIP, remoteIP, k.tag), buildSPI(remoteIP, localIP, k.tag)}
+		spis := &spi{buildSPI(advIP, remoteIP, k.tag), buildSPI(remoteIP, advIP, k.tag)}
 		dir := reverse
 		if i == 0 {
 			dir = bidir
@@ -216,7 +221,6 @@ func programMangle(vni uint32, add bool) (err error) {
 
 func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (fSA *netlink.XfrmState, rSA *netlink.XfrmState, err error) {
 	var (
-		crypt       *netlink.XfrmStateAlgo
 		action      = "Removing"
 		xfrmProgram = ns.NlHandle().XfrmStateDel
 	)
@@ -224,7 +228,6 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f
 	if add {
 		action = "Adding"
 		xfrmProgram = ns.NlHandle().XfrmStateAdd
-		crypt = &netlink.XfrmStateAlgo{Name: "cbc(aes)", Key: k.value}
 	}
 
 	if dir&reverse > 0 {
@@ -236,7 +239,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f
 			Mode:  netlink.XFRM_MODE_TRANSPORT,
 		}
 		if add {
-			rSA.Crypt = crypt
+			rSA.Aead = buildAeadAlgo(k, spi.reverse)
 		}
 
 		exists, err := saExists(rSA)
@@ -261,7 +264,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f
 			Mode:  netlink.XFRM_MODE_TRANSPORT,
 		}
 		if add {
-			fSA.Crypt = crypt
+			fSA.Aead = buildAeadAlgo(k, spi.forward)
 		}
 
 		exists, err := saExists(fSA)
@@ -354,13 +357,23 @@ func spExists(sp *netlink.XfrmPolicy) (bool, error) {
 }
 
 func buildSPI(src, dst net.IP, st uint32) int {
-	spi := int(st)
-	f := src[len(src)-4:]
-	t := dst[len(dst)-4:]
-	for i := 0; i < 4; i++ {
-		spi = spi ^ (int(f[i])^int(t[3-i]))<<uint32(8*i)
+	b := make([]byte, 4)
+	binary.BigEndian.PutUint32(b, st)
+	h := fnv.New32a()
+	h.Write(src)
+	h.Write(b)
+	h.Write(dst)
+	return int(binary.BigEndian.Uint32(h.Sum(nil)))
+}
+
+func buildAeadAlgo(k *key, s int) *netlink.XfrmStateAlgo {
+	salt := make([]byte, 4)
+	binary.BigEndian.PutUint32(salt, uint32(s))
+	return &netlink.XfrmStateAlgo{
+		Name:   "rfc4106(gcm(aes))",
+		Key:    append(k.value, salt...),
+		ICVLen: 64,
 	}
-	return spi
 }
 
 func (d *driver) secMapWalk(f func(string, []*spi) ([]*spi, bool)) error {
@@ -560,3 +573,14 @@ func updateNodeKey(lIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, priIdx,
 
 	return spis
 }
+
+func (n *network) maxMTU() int {
+	mtu := vxlanVethMTU
+	if n.secure {
+		// In case of encryption account for the
+		// esp packet espansion and padding
+		mtu -= pktExpansion
+		mtu -= (mtu % 4)
+	}
+	return mtu
+}

+ 7 - 5
vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go

@@ -75,11 +75,13 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 	// Set the container interface and its peer MTU to 1450 to allow
 	// for 50 bytes vxlan encap (inner eth header(14) + outer IP(20) +
 	// outer UDP(8) + vxlan header(8))
+	mtu := n.maxMTU()
+
 	veth, err := nlh.LinkByName(overlayIfName)
 	if err != nil {
 		return fmt.Errorf("cound not find link by name %s: %v", overlayIfName, err)
 	}
-	err = nlh.LinkSetMTU(veth, vxlanVethMTU)
+	err = nlh.LinkSetMTU(veth, mtu)
 	if err != nil {
 		return err
 	}
@@ -93,7 +95,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 	if err != nil {
 		return fmt.Errorf("could not find link by name %s: %v", containerIfName, err)
 	}
-	err = nlh.LinkSetMTU(veth, vxlanVethMTU)
+	err = nlh.LinkSetMTU(veth, mtu)
 	if err != nil {
 		return err
 	}
@@ -119,7 +121,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 	}
 
 	d.peerDbAdd(nid, eid, ep.addr.IP, ep.addr.Mask, ep.mac,
-		net.ParseIP(d.bindAddress), true)
+		net.ParseIP(d.advertiseAddress), true)
 
 	if err := d.checkEncryption(nid, nil, n.vxlanID(s), true, true); err != nil {
 		log.Warn(err)
@@ -128,7 +130,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 	buf, err := proto.Marshal(&PeerRecord{
 		EndpointIP:       ep.addr.String(),
 		EndpointMAC:      ep.mac.String(),
-		TunnelEndpointIP: d.bindAddress,
+		TunnelEndpointIP: d.advertiseAddress,
 	})
 	if err != nil {
 		return err
@@ -159,7 +161,7 @@ func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key stri
 
 	// Ignore local peers. We already know about them and they
 	// should not be added to vxlan fdb.
-	if peer.TunnelEndpointIP == d.bindAddress {
+	if peer.TunnelEndpointIP == d.advertiseAddress {
 		return
 	}
 

+ 1 - 1
vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_serf.go

@@ -40,7 +40,7 @@ func (d *driver) serfInit() error {
 
 	config := serf.DefaultConfig()
 	config.Init()
-	config.MemberlistConfig.BindAddr = d.bindAddress
+	config.MemberlistConfig.BindAddr = d.advertiseAddress
 
 	d.eventCh = make(chan serf.Event, 4)
 	config.EventCh = d.eventCh

+ 36 - 26
vendor/src/github.com/docker/libnetwork/drivers/overlay/overlay.go

@@ -31,22 +31,23 @@ const (
 var initVxlanIdm = make(chan (bool), 1)
 
 type driver struct {
-	eventCh      chan serf.Event
-	notifyCh     chan ovNotify
-	exitCh       chan chan struct{}
-	bindAddress  string
-	neighIP      string
-	config       map[string]interface{}
-	peerDb       peerNetworkMap
-	secMap       *encrMap
-	serfInstance *serf.Serf
-	networks     networkTable
-	store        datastore.DataStore
-	localStore   datastore.DataStore
-	vxlanIdm     *idm.Idm
-	once         sync.Once
-	joinOnce     sync.Once
-	keys         []*key
+	eventCh          chan serf.Event
+	notifyCh         chan ovNotify
+	exitCh           chan chan struct{}
+	bindAddress      string
+	advertiseAddress string
+	neighIP          string
+	config           map[string]interface{}
+	peerDb           peerNetworkMap
+	secMap           *encrMap
+	serfInstance     *serf.Serf
+	networks         networkTable
+	store            datastore.DataStore
+	localStore       datastore.DataStore
+	vxlanIdm         *idm.Idm
+	once             sync.Once
+	joinOnce         sync.Once
+	keys             []*key
 	sync.Mutex
 }
 
@@ -111,7 +112,11 @@ func (d *driver) restoreEndpoints() error {
 		ep := kvo.(*endpoint)
 		n := d.network(ep.nid)
 		if n == nil {
-			logrus.Debugf("Network (%s) not found for restored endpoint (%s)", ep.nid, ep.id)
+			logrus.Debugf("Network (%s) not found for restored endpoint (%s)", ep.nid[0:7], ep.id[0:7])
+			logrus.Debugf("Deleting stale overlay endpoint (%s) from store", ep.id[0:7])
+			if err := d.deleteEndpointFromStore(ep); err != nil {
+				logrus.Debugf("Failed to delete stale overlay endpoint (%s) from store", ep.id[0:7])
+			}
 			continue
 		}
 		n.addEndpoint(ep)
@@ -140,7 +145,7 @@ func (d *driver) restoreEndpoints() error {
 		}
 
 		n.incEndpointCount()
-		d.peerDbAdd(ep.nid, ep.id, ep.addr.IP, ep.addr.Mask, ep.mac, net.ParseIP(d.bindAddress), true)
+		d.peerDbAdd(ep.nid, ep.id, ep.addr.IP, ep.addr.Mask, ep.mac, net.ParseIP(d.advertiseAddress), true)
 	}
 	return nil
 }
@@ -211,20 +216,25 @@ func validateSelf(node string) error {
 	return fmt.Errorf("Multi-Host overlay networking requires cluster-advertise(%s) to be configured with a local ip-address that is reachable within the cluster", advIP.String())
 }
 
-func (d *driver) nodeJoin(node string, self bool) {
+func (d *driver) nodeJoin(advertiseAddress, bindAddress string, self bool) {
 	if self && !d.isSerfAlive() {
-		if err := validateSelf(node); err != nil {
-			logrus.Errorf("%s", err.Error())
-		}
 		d.Lock()
-		d.bindAddress = node
+		d.advertiseAddress = advertiseAddress
+		d.bindAddress = bindAddress
 		d.Unlock()
 
 		// If there is no cluster store there is no need to start serf.
 		if d.store != nil {
+			if err := validateSelf(advertiseAddress); err != nil {
+				logrus.Warnf("%s", err.Error())
+			}
 			err := d.serfInit()
 			if err != nil {
 				logrus.Errorf("initializing serf instance failed: %v", err)
+				d.Lock()
+				d.advertiseAddress = ""
+				d.bindAddress = ""
+				d.Unlock()
 				return
 			}
 		}
@@ -232,7 +242,7 @@ func (d *driver) nodeJoin(node string, self bool) {
 
 	d.Lock()
 	if !self {
-		d.neighIP = node
+		d.neighIP = advertiseAddress
 	}
 	neighIP := d.neighIP
 	d.Unlock()
@@ -246,7 +256,7 @@ func (d *driver) nodeJoin(node string, self bool) {
 			}
 		})
 		if err != nil {
-			logrus.Errorf("joining serf neighbor %s failed: %v", node, err)
+			logrus.Errorf("joining serf neighbor %s failed: %v", advertiseAddress, err)
 			d.Lock()
 			d.joinOnce = sync.Once{}
 			d.Unlock()
@@ -286,7 +296,7 @@ func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{})
 		if !ok || nodeData.Address == "" {
 			return fmt.Errorf("invalid discovery data")
 		}
-		d.nodeJoin(nodeData.Address, nodeData.Self)
+		d.nodeJoin(nodeData.Address, nodeData.BindAddress, nodeData.Self)
 	case discoverapi.DatastoreConfig:
 		if d.store != nil {
 			return types.ForbiddenErrorf("cannot accept datastore configuration: Overlay driver has a datastore configured already")

+ 10 - 1
vendor/src/github.com/docker/libnetwork/endpoint_cnt.go

@@ -113,6 +113,9 @@ func (ec *endpointCnt) updateStore() error {
 	if store == nil {
 		return fmt.Errorf("store not found for scope %s on endpoint count update", ec.DataScope())
 	}
+	// make a copy of count and n to avoid being overwritten by store.GetObject
+	count := ec.EndpointCnt()
+	n := ec.n
 	for {
 		if err := ec.n.getController().updateToStore(ec); err == nil || err != datastore.ErrKeyModified {
 			return err
@@ -120,6 +123,10 @@ func (ec *endpointCnt) updateStore() error {
 		if err := store.GetObject(datastore.Key(ec.Key()...), ec); err != nil {
 			return fmt.Errorf("could not update the kvobject to latest on endpoint count update: %v", err)
 		}
+		ec.Lock()
+		ec.Count = count
+		ec.n = n
+		ec.Unlock()
 	}
 }
 
@@ -136,7 +143,9 @@ retry:
 	if inc {
 		ec.Count++
 	} else {
-		ec.Count--
+		if ec.Count > 0 {
+			ec.Count--
+		}
 	}
 	ec.Unlock()
 

+ 5 - 1
vendor/src/github.com/docker/libnetwork/network.go

@@ -1105,9 +1105,13 @@ func (n *network) getSvcRecords(ep *endpoint) []etchosts.Record {
 	}
 
 	var recs []etchosts.Record
-	sr, _ := n.ctrlr.svcRecords[n.id]
+
 	epName := ep.Name()
 
+	n.ctrlr.Lock()
+	sr, _ := n.ctrlr.svcRecords[n.id]
+	n.ctrlr.Unlock()
+
 	for h, ip := range sr.svcMap {
 		if strings.Split(h, ".")[0] == epName {
 			continue

+ 1 - 1
vendor/src/github.com/docker/libnetwork/networkdb/cluster.go

@@ -81,7 +81,7 @@ func (nDB *NetworkDB) RemoveKey(key []byte) {
 func (nDB *NetworkDB) clusterInit() error {
 	config := memberlist.DefaultLANConfig()
 	config.Name = nDB.config.NodeName
-	config.BindAddr = nDB.config.BindAddr
+	config.AdvertiseAddr = nDB.config.AdvertiseAddr
 
 	if nDB.config.BindPort != 0 {
 		config.BindPort = nDB.config.BindPort

+ 2 - 2
vendor/src/github.com/docker/libnetwork/networkdb/networkdb.go

@@ -107,9 +107,9 @@ type Config struct {
 	// NodeName is the cluster wide unique name for this node.
 	NodeName string
 
-	// BindAddr is the local node's IP address that we bind to for
+	// AdvertiseAddr is the node's IP address that we advertise for
 	// cluster communication.
-	BindAddr string
+	AdvertiseAddr string
 
 	// BindPort is the local node's port to which we bind to for
 	// cluster communication.

+ 1 - 0
vendor/src/github.com/docker/libnetwork/osl/interface_linux.go

@@ -303,6 +303,7 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If
 	for err = nlh.LinkSetUp(iface); err != nil && cnt < 3; cnt++ {
 		log.Debugf("retrying link setup because of: %v", err)
 		time.Sleep(10 * time.Millisecond)
+		err = nlh.LinkSetUp(iface)
 	}
 	if err != nil {
 		return fmt.Errorf("failed to set link up: %v", err)

+ 15 - 4
vendor/src/github.com/docker/libnetwork/osl/namespace_linux.go

@@ -6,6 +6,7 @@ import (
 	"net"
 	"os"
 	"os/exec"
+	"path/filepath"
 	"runtime"
 	"strconv"
 	"strings"
@@ -21,7 +22,7 @@ import (
 	"github.com/vishvananda/netns"
 )
 
-const prefix = "/var/run/docker/netns"
+const defaultPrefix = "/var/run/docker"
 
 var (
 	once             sync.Once
@@ -30,6 +31,7 @@ var (
 	gpmWg            sync.WaitGroup
 	gpmCleanupPeriod = 60 * time.Second
 	gpmChan          = make(chan chan struct{})
+	prefix           = defaultPrefix
 )
 
 // The networkNamespace type is the linux implementation of the Sandbox
@@ -48,12 +50,21 @@ type networkNamespace struct {
 	sync.Mutex
 }
 
+// SetBasePath sets the base url prefix for the ns path
+func SetBasePath(path string) {
+	prefix = path
+}
+
 func init() {
 	reexec.Register("netns-create", reexecCreateNamespace)
 }
 
+func basePath() string {
+	return filepath.Join(prefix, "netns")
+}
+
 func createBasePath() {
-	err := os.MkdirAll(prefix, 0755)
+	err := os.MkdirAll(basePath(), 0755)
 	if err != nil {
 		panic("Could not create net namespace path directory")
 	}
@@ -142,7 +153,7 @@ func GenerateKey(containerID string) string {
 			indexStr string
 			tmpkey   string
 		)
-		dir, err := ioutil.ReadDir(prefix)
+		dir, err := ioutil.ReadDir(basePath())
 		if err != nil {
 			return ""
 		}
@@ -172,7 +183,7 @@ func GenerateKey(containerID string) string {
 		maxLen = len(containerID)
 	}
 
-	return prefix + "/" + containerID[:maxLen]
+	return basePath() + "/" + containerID[:maxLen]
 }
 
 // NewSandbox provides a new sandbox instance created in an os specific way

+ 4 - 0
vendor/src/github.com/docker/libnetwork/osl/namespace_unsupported.go

@@ -10,3 +10,7 @@ func GC() {
 func GetSandboxForExternalKey(path string, key string) (Sandbox, error) {
 	return nil, nil
 }
+
+// SetBasePath sets the base url prefix for the ns path
+func SetBasePath(path string) {
+}

+ 4 - 0
vendor/src/github.com/docker/libnetwork/osl/namespace_windows.go

@@ -37,3 +37,7 @@ func InitOSContext() func() {
 func SetupTestOSContext(t *testing.T) func() {
 	return func() {}
 }
+
+// SetBasePath sets the base url prefix for the ns path
+func SetBasePath(path string) {
+}

+ 4 - 0
vendor/src/github.com/docker/libnetwork/osl/sandbox_freebsd.go

@@ -38,3 +38,7 @@ func InitOSContext() func() {
 func SetupTestOSContext(t *testing.T) func() {
 	return func() {}
 }
+
+// SetBasePath sets the base url prefix for the ns path
+func SetBasePath(path string) {
+}

+ 17 - 3
vendor/src/github.com/docker/libnetwork/sandbox.go

@@ -413,7 +413,12 @@ func (sb *sandbox) ResolveIP(ip string) string {
 	for _, ep := range sb.getConnectedEndpoints() {
 		n := ep.getNetwork()
 
-		sr, ok := n.getController().svcRecords[n.ID()]
+		c := n.getController()
+
+		c.Lock()
+		sr, ok := c.svcRecords[n.ID()]
+		c.Unlock()
+
 		if !ok {
 			continue
 		}
@@ -454,7 +459,12 @@ func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP, error) {
 	for _, ep := range sb.getConnectedEndpoints() {
 		n := ep.getNetwork()
 
-		sr, ok := n.getController().svcRecords[n.ID()]
+		c := n.getController()
+
+		c.Lock()
+		sr, ok := c.svcRecords[n.ID()]
+		c.Unlock()
+
 		if !ok {
 			continue
 		}
@@ -575,7 +585,11 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin
 			ep.Unlock()
 		}
 
-		sr, ok := n.getController().svcRecords[n.ID()]
+		c := n.getController()
+		c.Lock()
+		sr, ok := c.svcRecords[n.ID()]
+		c.Unlock()
+
 		if !ok {
 			continue
 		}

+ 1 - 1
vendor/src/github.com/docker/libnetwork/sandbox_externalkey_unix.go

@@ -15,7 +15,7 @@ import (
 	"github.com/opencontainers/runc/libcontainer/configs"
 )
 
-const udsBase = "/var/lib/docker/network/files/"
+const udsBase = "/run/docker/libnetwork/"
 const success = "success"
 
 // processSetKeyReexec is a private function that must be called only on an reexec path

+ 7 - 0
vendor/src/github.com/docker/swarmkit/agent/exec/controller.go

@@ -2,10 +2,13 @@ package exec
 
 import (
 	"fmt"
+	"reflect"
+	"time"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/log"
+	"github.com/docker/swarmkit/protobuf/ptypes"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 )
@@ -182,6 +185,10 @@ func Do(ctx context.Context, task *api.Task, ctlr Controller) (*api.TaskStatus,
 	// is completed.
 	defer func() {
 		logStateChange(ctx, task.DesiredState, task.Status.State, status.State)
+
+		if !reflect.DeepEqual(status, task.Status) {
+			status.Timestamp = ptypes.MustTimestampProto(time.Now())
+		}
 	}()
 
 	// extract the container status from the container, if supported.

+ 26 - 20
vendor/src/github.com/docker/swarmkit/agent/node.go

@@ -31,10 +31,10 @@ const stateFilename = "state.json"
 
 // NodeConfig provides values for a Node.
 type NodeConfig struct {
-	// Hostname the name of host for agent instance.
+	// Hostname is the name of host for agent instance.
 	Hostname string
 
-	// JoinAddrs specifies node that should be used for the initial connection to
+	// JoinAddr specifies node that should be used for the initial connection to
 	// other manager in cluster. This should be only one address and optional,
 	// the actual remotes come from the stored state.
 	JoinAddr string
@@ -60,6 +60,10 @@ type NodeConfig struct {
 	// and raft members connect to.
 	ListenRemoteAPI string
 
+	// AdvertiseRemoteAPI specifies the address that should be advertised
+	// for connections to the remote API (including the raft service).
+	AdvertiseRemoteAPI string
+
 	// Executor specifies the executor to use for the agent.
 	Executor exec.Executor
 
@@ -425,6 +429,9 @@ func (n *Node) CertificateRequested() <-chan struct{} {
 
 func (n *Node) setControlSocket(conn *grpc.ClientConn) {
 	n.Lock()
+	if n.conn != nil {
+		n.conn.Close()
+	}
 	n.conn = conn
 	n.connCond.Broadcast()
 	n.Unlock()
@@ -478,7 +485,7 @@ func (n *Node) NodeMembership() api.NodeSpec_Membership {
 	return n.nodeMembership
 }
 
-// Manager return manager instance started by node. May be nil.
+// Manager returns manager instance started by node. May be nil.
 func (n *Node) Manager() *manager.Manager {
 	n.RLock()
 	defer n.RUnlock()
@@ -542,6 +549,8 @@ func (n *Node) initManagerConnection(ctx context.Context, ready chan<- struct{})
 	opts := []grpc.DialOption{}
 	insecureCreds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})
 	opts = append(opts, grpc.WithTransportCredentials(insecureCreds))
+	// Using listen address instead of advertised address because this is a
+	// local connection.
 	addr := n.config.ListenControlAPI
 	opts = append(opts, grpc.WithDialer(
 		func(addr string, timeout time.Duration) (net.Conn, error) {
@@ -571,11 +580,11 @@ func (n *Node) initManagerConnection(ctx context.Context, ready chan<- struct{})
 	}
 }
 
-func (n *Node) waitRole(ctx context.Context, role string) error {
+func (n *Node) waitRole(ctx context.Context, role string) {
 	n.roleCond.L.Lock()
 	if role == n.role {
 		n.roleCond.L.Unlock()
-		return nil
+		return
 	}
 	finishCh := make(chan struct{})
 	defer close(finishCh)
@@ -591,17 +600,14 @@ func (n *Node) waitRole(ctx context.Context, role string) error {
 	for role != n.role {
 		n.roleCond.Wait()
 		if ctx.Err() != nil {
-			return ctx.Err()
+			return
 		}
 	}
-	return nil
 }
 
 func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig, ready chan struct{}) error {
 	for {
-		if err := n.waitRole(ctx, ca.ManagerRole); err != nil {
-			return err
-		}
+		n.waitRole(ctx, ca.ManagerRole)
 		if ctx.Err() != nil {
 			return ctx.Err()
 		}
@@ -612,6 +618,7 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig
 				"tcp":  n.config.ListenRemoteAPI,
 				"unix": n.config.ListenControlAPI,
 			},
+			AdvertiseAddr:  n.config.AdvertiseRemoteAPI,
 			SecurityConfig: securityConfig,
 			ExternalCAs:    n.config.ExternalCAs,
 			JoinRaft:       remoteAddr.Addr,
@@ -647,25 +654,24 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig
 			ready = nil
 		}
 
-		if err := n.waitRole(ctx, ca.AgentRole); err != nil {
-			m.Stop(context.Background())
-		}
+		n.waitRole(ctx, ca.AgentRole)
+
+		n.Lock()
+		n.manager = nil
+		n.Unlock()
 
 		select {
 		case <-done:
 		case <-ctx.Done():
+			err = ctx.Err()
 			m.Stop(context.Background())
-			return ctx.Err()
+			<-done
 		}
-
 		connCancel()
 
-		n.Lock()
-		n.manager = nil
-		if n.conn != nil {
-			n.conn.Close()
+		if err != nil {
+			return err
 		}
-		n.Unlock()
 	}
 }
 

+ 3 - 3
vendor/src/github.com/docker/swarmkit/agent/storage.go

@@ -118,10 +118,10 @@ func WalkTaskStatus(tx *bolt.Tx, fn func(id string, status *api.TaskStatus) erro
 // PutTask places the task into the database.
 func PutTask(tx *bolt.Tx, task *api.Task) error {
 	return withCreateTaskBucketIfNotExists(tx, task.ID, func(bkt *bolt.Bucket) error {
-		task = task.Copy()
-		task.Status = api.TaskStatus{} // blank out the status.
+		taskCopy := *task
+		taskCopy.Status = api.TaskStatus{} // blank out the status.
 
-		p, err := proto.Marshal(task)
+		p, err := proto.Marshal(&taskCopy)
 		if err != nil {
 			return err
 		}

+ 5 - 4
vendor/src/github.com/docker/swarmkit/agent/task.go

@@ -247,10 +247,11 @@ func (tm *taskManager) run(ctx context.Context) {
 //
 // This used to decide whether or not to propagate a task update to a controller.
 func tasksEqual(a, b *api.Task) bool {
-	a, b = a.Copy(), b.Copy()
+	// shallow copy
+	copyA, copyB := *a, *b
 
-	a.Status, b.Status = api.TaskStatus{}, api.TaskStatus{}
-	a.Meta, b.Meta = api.Meta{}, api.Meta{}
+	copyA.Status, copyB.Status = api.TaskStatus{}, api.TaskStatus{}
+	copyA.Meta, copyB.Meta = api.Meta{}, api.Meta{}
 
-	return reflect.DeepEqual(a, b)
+	return reflect.DeepEqual(&copyA, &copyB)
 }

+ 156 - 100
vendor/src/github.com/docker/swarmkit/api/objects.pb.go

@@ -71,6 +71,9 @@ type Service struct {
 	// the optional fields like node_port or virtual_ip and it
 	// could be auto allocated by the system.
 	Endpoint *Endpoint `protobuf:"bytes,4,opt,name=endpoint" json:"endpoint,omitempty"`
+	// UpdateStatus contains the status of an update, if one is in
+	// progress.
+	UpdateStatus *UpdateStatus `protobuf:"bytes,5,opt,name=update_status,json=updateStatus" json:"update_status,omitempty"`
 }
 
 func (m *Service) Reset()                    { *m = Service{} }
@@ -278,10 +281,11 @@ func (m *Service) Copy() *Service {
 	}
 
 	o := &Service{
-		ID:       m.ID,
-		Meta:     *m.Meta.Copy(),
-		Spec:     *m.Spec.Copy(),
-		Endpoint: m.Endpoint.Copy(),
+		ID:           m.ID,
+		Meta:         *m.Meta.Copy(),
+		Spec:         *m.Spec.Copy(),
+		Endpoint:     m.Endpoint.Copy(),
+		UpdateStatus: m.UpdateStatus.Copy(),
 	}
 
 	return o
@@ -464,7 +468,7 @@ func (this *Service) GoString() string {
 	if this == nil {
 		return "nil"
 	}
-	s := make([]string, 0, 8)
+	s := make([]string, 0, 9)
 	s = append(s, "&api.Service{")
 	s = append(s, "ID: "+fmt.Sprintf("%#v", this.ID)+",\n")
 	s = append(s, "Meta: "+strings.Replace(this.Meta.GoString(), `&`, ``, 1)+",\n")
@@ -472,6 +476,9 @@ func (this *Service) GoString() string {
 	if this.Endpoint != nil {
 		s = append(s, "Endpoint: "+fmt.Sprintf("%#v", this.Endpoint)+",\n")
 	}
+	if this.UpdateStatus != nil {
+		s = append(s, "UpdateStatus: "+fmt.Sprintf("%#v", this.UpdateStatus)+",\n")
+	}
 	s = append(s, "}")
 	return strings.Join(s, "")
 }
@@ -785,6 +792,16 @@ func (m *Service) MarshalTo(data []byte) (int, error) {
 		}
 		i += n13
 	}
+	if m.UpdateStatus != nil {
+		data[i] = 0x2a
+		i++
+		i = encodeVarintObjects(data, i, uint64(m.UpdateStatus.Size()))
+		n14, err := m.UpdateStatus.MarshalTo(data[i:])
+		if err != nil {
+			return 0, err
+		}
+		i += n14
+	}
 	return i, nil
 }
 
@@ -807,11 +824,11 @@ func (m *Endpoint) MarshalTo(data []byte) (int, error) {
 		data[i] = 0xa
 		i++
 		i = encodeVarintObjects(data, i, uint64(m.Spec.Size()))
-		n14, err := m.Spec.MarshalTo(data[i:])
+		n15, err := m.Spec.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n14
+		i += n15
 	}
 	if len(m.Ports) > 0 {
 		for _, msg := range m.Ports {
@@ -894,19 +911,19 @@ func (m *Task) MarshalTo(data []byte) (int, error) {
 	data[i] = 0x12
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.Meta.Size()))
-	n15, err := m.Meta.MarshalTo(data[i:])
+	n16, err := m.Meta.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n15
+	i += n16
 	data[i] = 0x1a
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.Spec.Size()))
-	n16, err := m.Spec.MarshalTo(data[i:])
+	n17, err := m.Spec.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n16
+	i += n17
 	if len(m.ServiceID) > 0 {
 		data[i] = 0x22
 		i++
@@ -927,27 +944,27 @@ func (m *Task) MarshalTo(data []byte) (int, error) {
 	data[i] = 0x3a
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.Annotations.Size()))
-	n17, err := m.Annotations.MarshalTo(data[i:])
+	n18, err := m.Annotations.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n17
+	i += n18
 	data[i] = 0x42
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.ServiceAnnotations.Size()))
-	n18, err := m.ServiceAnnotations.MarshalTo(data[i:])
+	n19, err := m.ServiceAnnotations.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n18
+	i += n19
 	data[i] = 0x4a
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.Status.Size()))
-	n19, err := m.Status.MarshalTo(data[i:])
+	n20, err := m.Status.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n19
+	i += n20
 	if m.DesiredState != 0 {
 		data[i] = 0x50
 		i++
@@ -969,21 +986,21 @@ func (m *Task) MarshalTo(data []byte) (int, error) {
 		data[i] = 0x62
 		i++
 		i = encodeVarintObjects(data, i, uint64(m.Endpoint.Size()))
-		n20, err := m.Endpoint.MarshalTo(data[i:])
+		n21, err := m.Endpoint.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n20
+		i += n21
 	}
 	if m.LogDriver != nil {
 		data[i] = 0x6a
 		i++
 		i = encodeVarintObjects(data, i, uint64(m.LogDriver.Size()))
-		n21, err := m.LogDriver.MarshalTo(data[i:])
+		n22, err := m.LogDriver.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n21
+		i += n22
 	}
 	return i, nil
 }
@@ -1007,11 +1024,11 @@ func (m *NetworkAttachment) MarshalTo(data []byte) (int, error) {
 		data[i] = 0xa
 		i++
 		i = encodeVarintObjects(data, i, uint64(m.Network.Size()))
-		n22, err := m.Network.MarshalTo(data[i:])
+		n23, err := m.Network.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n22
+		i += n23
 	}
 	if len(m.Addresses) > 0 {
 		for _, s := range m.Addresses {
@@ -1070,38 +1087,38 @@ func (m *Network) MarshalTo(data []byte) (int, error) {
 	data[i] = 0x12
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.Meta.Size()))
-	n23, err := m.Meta.MarshalTo(data[i:])
+	n24, err := m.Meta.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n23
+	i += n24
 	data[i] = 0x1a
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.Spec.Size()))
-	n24, err := m.Spec.MarshalTo(data[i:])
+	n25, err := m.Spec.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n24
+	i += n25
 	if m.DriverState != nil {
 		data[i] = 0x22
 		i++
 		i = encodeVarintObjects(data, i, uint64(m.DriverState.Size()))
-		n25, err := m.DriverState.MarshalTo(data[i:])
+		n26, err := m.DriverState.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n25
+		i += n26
 	}
 	if m.IPAM != nil {
 		data[i] = 0x2a
 		i++
 		i = encodeVarintObjects(data, i, uint64(m.IPAM.Size()))
-		n26, err := m.IPAM.MarshalTo(data[i:])
+		n27, err := m.IPAM.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n26
+		i += n27
 	}
 	return i, nil
 }
@@ -1130,27 +1147,27 @@ func (m *Cluster) MarshalTo(data []byte) (int, error) {
 	data[i] = 0x12
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.Meta.Size()))
-	n27, err := m.Meta.MarshalTo(data[i:])
+	n28, err := m.Meta.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n27
+	i += n28
 	data[i] = 0x1a
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.Spec.Size()))
-	n28, err := m.Spec.MarshalTo(data[i:])
+	n29, err := m.Spec.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n28
+	i += n29
 	data[i] = 0x22
 	i++
 	i = encodeVarintObjects(data, i, uint64(m.RootCA.Size()))
-	n29, err := m.RootCA.MarshalTo(data[i:])
+	n30, err := m.RootCA.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n29
+	i += n30
 	if len(m.NetworkBootstrapKeys) > 0 {
 		for _, msg := range m.NetworkBootstrapKeys {
 			data[i] = 0x2a
@@ -1260,6 +1277,10 @@ func (m *Service) Size() (n int) {
 		l = m.Endpoint.Size()
 		n += 1 + l + sovObjects(uint64(l))
 	}
+	if m.UpdateStatus != nil {
+		l = m.UpdateStatus.Size()
+		n += 1 + l + sovObjects(uint64(l))
+	}
 	return n
 }
 
@@ -1467,6 +1488,7 @@ func (this *Service) String() string {
 		`Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`,
 		`Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "ServiceSpec", "ServiceSpec", 1), `&`, ``, 1) + `,`,
 		`Endpoint:` + strings.Replace(fmt.Sprintf("%v", this.Endpoint), "Endpoint", "Endpoint", 1) + `,`,
+		`UpdateStatus:` + strings.Replace(fmt.Sprintf("%v", this.UpdateStatus), "UpdateStatus", "UpdateStatus", 1) + `,`,
 		`}`,
 	}, "")
 	return s
@@ -2160,6 +2182,39 @@ func (m *Service) Unmarshal(data []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 5:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field UpdateStatus", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowObjects
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthObjects
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.UpdateStatus == nil {
+				m.UpdateStatus = &UpdateStatus{}
+			}
+			if err := m.UpdateStatus.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipObjects(data[iNdEx:])
@@ -3527,67 +3582,68 @@ var (
 )
 
 var fileDescriptorObjects = []byte{
-	// 981 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0x1b, 0x45,
-	0x14, 0xaf, 0xed, 0x8d, 0xed, 0x7d, 0x8e, 0x23, 0x31, 0x54, 0xd5, 0x36, 0x84, 0xa4, 0xb8, 0x02,
-	0x71, 0x40, 0xae, 0x28, 0x05, 0x81, 0xa0, 0x42, 0xb6, 0x13, 0x81, 0x05, 0x81, 0x68, 0x5a, 0x85,
-	0xe3, 0x6a, 0xb2, 0x3b, 0x35, 0x8b, 0xed, 0xdd, 0xd5, 0xcc, 0x24, 0x55, 0x6e, 0x88, 0x0f, 0xc0,
-	0x47, 0xe0, 0xab, 0x70, 0x8d, 0x10, 0x07, 0x8e, 0x9c, 0x2a, 0xda, 0x1b, 0x27, 0xf8, 0x08, 0xbc,
-	0xf9, 0xb3, 0xf6, 0x56, 0x5e, 0x87, 0x56, 0xaa, 0x72, 0x58, 0x69, 0xfe, 0xfc, 0x7e, 0xbf, 0x79,
-	0xef, 0xcd, 0x7b, 0x6f, 0x07, 0xba, 0xd9, 0xc9, 0x0f, 0x3c, 0x52, 0xb2, 0x9f, 0x8b, 0x4c, 0x65,
-	0x84, 0xc4, 0x59, 0x34, 0xe5, 0xa2, 0x2f, 0x1f, 0x33, 0x31, 0x9f, 0x26, 0xaa, 0x7f, 0xf6, 0xfe,
-	0x76, 0x47, 0x9d, 0xe7, 0xdc, 0x01, 0xb6, 0x3b, 0x32, 0xe7, 0x51, 0x31, 0xb9, 0xa9, 0x92, 0x39,
-	0x97, 0x8a, 0xcd, 0xf3, 0x3b, 0x8b, 0x91, 0xdb, 0xba, 0x3e, 0xc9, 0x26, 0x99, 0x19, 0xde, 0xd1,
-	0x23, 0xbb, 0xda, 0xfb, 0xb5, 0x06, 0xde, 0x21, 0x57, 0x8c, 0x7c, 0x0a, 0xad, 0x33, 0x2e, 0x64,
-	0x92, 0xa5, 0x41, 0xed, 0x56, 0xed, 0xdd, 0xce, 0xdd, 0x37, 0xfa, 0xab, 0x27, 0xf7, 0x8f, 0x2d,
-	0x64, 0xe8, 0x5d, 0x3c, 0xd9, 0xbb, 0x46, 0x0b, 0x06, 0xf9, 0x0c, 0x20, 0x12, 0x9c, 0x29, 0x1e,
-	0x87, 0x4c, 0x05, 0x75, 0xc3, 0x7f, 0xb3, 0x8a, 0xff, 0xb0, 0x30, 0x8a, 0xfa, 0x8e, 0x30, 0x50,
-	0x9a, 0x7d, 0x9a, 0xc7, 0x05, 0xbb, 0xf1, 0x42, 0x6c, 0x47, 0x18, 0xa8, 0xde, 0xdf, 0x0d, 0xf0,
-	0xbe, 0xc9, 0x62, 0x4e, 0x6e, 0x40, 0x3d, 0x89, 0x8d, 0xf1, 0xfe, 0xb0, 0xf9, 0xec, 0xc9, 0x5e,
-	0x7d, 0xbc, 0x4f, 0x71, 0x85, 0xdc, 0x05, 0x6f, 0x8e, 0x1e, 0x3a, 0xb3, 0x82, 0x2a, 0x61, 0x1d,
-	0x01, 0xe7, 0x93, 0xc1, 0x92, 0x8f, 0xc0, 0xd3, 0x61, 0x75, 0xc6, 0xec, 0x54, 0x71, 0xf4, 0x99,
-	0x0f, 0x10, 0x53, 0xf0, 0x34, 0x9e, 0x1c, 0x40, 0x27, 0xe6, 0x32, 0x12, 0x49, 0xae, 0x74, 0x24,
-	0x3d, 0x43, 0xbf, 0xbd, 0x8e, 0xbe, 0xbf, 0x84, 0xd2, 0x32, 0x0f, 0x23, 0xd2, 0x44, 0x3f, 0xd5,
-	0xa9, 0x0c, 0x36, 0x8c, 0xc2, 0xee, 0x5a, 0x03, 0x0c, 0xca, 0x99, 0xe0, 0x38, 0xe4, 0x4b, 0xd8,
-	0x9a, 0xb3, 0x94, 0x4d, 0xb8, 0x08, 0x9d, 0x4a, 0xd3, 0xa8, 0xbc, 0x55, 0xe9, 0xba, 0x45, 0x5a,
-	0x21, 0xda, 0x9d, 0x97, 0xa7, 0xe8, 0x0e, 0x30, 0xa5, 0x58, 0xf4, 0xfd, 0x9c, 0xa7, 0x2a, 0x68,
-	0x19, 0x95, 0xb7, 0x2b, 0x6d, 0xe1, 0xea, 0x71, 0x26, 0xa6, 0x83, 0x05, 0x98, 0x96, 0x88, 0xe4,
-	0x0b, 0xe8, 0x44, 0x5c, 0xa8, 0xe4, 0x51, 0x12, 0xe1, 0xa5, 0x05, 0x6d, 0xa3, 0xb3, 0x57, 0xa5,
-	0x33, 0x5a, 0xc2, 0x9c, 0x53, 0x65, 0x66, 0xef, 0xb7, 0x1a, 0xb4, 0x1e, 0x70, 0x71, 0x96, 0x44,
-	0xaf, 0xf6, 0xba, 0x3f, 0x79, 0xee, 0xba, 0x2b, 0x2d, 0x73, 0xc7, 0xae, 0xdc, 0xf8, 0xc7, 0xd0,
-	0xe6, 0x69, 0x9c, 0x67, 0x09, 0x06, 0xc8, 0x5b, 0x9f, 0x2d, 0x07, 0x0e, 0x43, 0x17, 0xe8, 0xde,
-	0x2f, 0x75, 0x68, 0x17, 0xcb, 0xe4, 0x9e, 0xb3, 0xc0, 0xd6, 0xde, 0xad, 0xcb, 0x24, 0xb4, 0x09,
-	0xee, 0xf0, 0x7b, 0xb0, 0x91, 0x67, 0x42, 0x49, 0x74, 0xb6, 0xb1, 0x2e, 0x4d, 0x8e, 0x10, 0x30,
-	0xca, 0xd2, 0x47, 0xc9, 0x84, 0x5a, 0x30, 0xf9, 0x0e, 0x3a, 0x67, 0x89, 0x50, 0xa7, 0x6c, 0x16,
-	0x26, 0xb9, 0x44, 0xa7, 0x35, 0xf7, 0x9d, 0xcb, 0x8e, 0xec, 0x1f, 0x5b, 0xfc, 0xf8, 0x68, 0xb8,
-	0x85, 0xa1, 0x86, 0xc5, 0x54, 0x52, 0x70, 0x52, 0xe3, 0x5c, 0x6e, 0x1f, 0x82, 0xbf, 0xd8, 0x21,
-	0xef, 0x01, 0xa4, 0x36, 0x2b, 0xc2, 0xc5, 0x3d, 0x75, 0x91, 0xec, 0xbb, 0x5c, 0xc1, 0xeb, 0xf2,
-	0x1d, 0x60, 0x1c, 0x13, 0x02, 0x1e, 0x8b, 0x63, 0x61, 0x6e, 0xcd, 0xa7, 0x66, 0xdc, 0xfb, 0x7d,
-	0x03, 0xbc, 0x87, 0x4c, 0x4e, 0xaf, 0xba, 0xb2, 0xf5, 0x99, 0x2b, 0xf7, 0x8c, 0xee, 0x48, 0x9b,
-	0x02, 0xda, 0x1d, 0x6f, 0xe9, 0x8e, 0x4b, 0x0c, 0xed, 0x8e, 0x03, 0x58, 0x77, 0xe4, 0x2c, 0x53,
-	0xa6, 0x7c, 0x3d, 0x6a, 0xc6, 0xe4, 0x36, 0xb4, 0x52, 0x2c, 0x59, 0x4d, 0x6f, 0x1a, 0x3a, 0x20,
-	0xbd, 0xa9, 0xab, 0x18, 0xb9, 0x4d, 0xbd, 0x85, 0x44, 0x2c, 0x15, 0x96, 0xa6, 0x19, 0x96, 0x1f,
-	0xf6, 0x01, 0xe9, 0x4a, 0xae, 0x32, 0x21, 0x07, 0x4b, 0x58, 0x51, 0x2a, 0x25, 0x26, 0x39, 0x86,
-	0xd7, 0x0b, 0x7b, 0xcb, 0x82, 0xed, 0x97, 0x11, 0x24, 0x4e, 0xa1, 0xb4, 0x53, 0x6a, 0x4d, 0xfe,
-	0xfa, 0xd6, 0x64, 0x22, 0x58, 0xd5, 0x9a, 0x86, 0xd0, 0xc5, 0x3e, 0x97, 0x08, 0x6c, 0xf5, 0x7a,
-	0x85, 0x07, 0x80, 0x22, 0x5b, 0x6b, 0xba, 0xbd, 0x13, 0xe1, 0x74, 0xd3, 0x71, 0xcc, 0x8c, 0x0c,
-	0xa0, 0xed, 0xf2, 0x46, 0x06, 0x1d, 0x93, 0xbb, 0x2f, 0xd8, 0x92, 0x16, 0xb4, 0xe7, 0x8a, 0x76,
-	0xf3, 0x65, 0x8a, 0x16, 0x3b, 0x05, 0xcc, 0xb2, 0x49, 0x18, 0x8b, 0x04, 0xff, 0x7d, 0x41, 0xd7,
-	0x70, 0xb7, 0xab, 0xb8, 0xfb, 0x06, 0x41, 0x7d, 0x44, 0xdb, 0x61, 0xef, 0xa7, 0x1a, 0xbc, 0xb6,
-	0x62, 0x14, 0xf9, 0x10, 0xb3, 0xc2, 0x2e, 0x5e, 0xf6, 0xdf, 0x75, 0x3c, 0x5a, 0x60, 0xc9, 0x0e,
-	0xf8, 0xba, 0x46, 0xb8, 0x94, 0xdc, 0x56, 0xbf, 0x4f, 0x97, 0x0b, 0x24, 0x80, 0x16, 0x9b, 0x25,
-	0x4c, 0xef, 0x35, 0xcc, 0x5e, 0x31, 0xed, 0xfd, 0x5c, 0x87, 0x96, 0x13, 0xbb, 0xea, 0x0e, 0xea,
-	0x8e, 0x5d, 0xa9, 0xac, 0xfb, 0xb0, 0x69, 0xc3, 0xe9, 0x52, 0xc2, 0xfb, 0xdf, 0xa0, 0x76, 0x2c,
-	0xde, 0xa6, 0xc3, 0x7d, 0xf0, 0x92, 0x9c, 0xcd, 0xdd, 0x9f, 0xb2, 0xf2, 0xe4, 0xf1, 0xd1, 0xe0,
-	0xf0, 0xdb, 0xdc, 0x66, 0x76, 0x1b, 0x1d, 0xf5, 0xf4, 0x02, 0x35, 0xb4, 0xde, 0x3f, 0x18, 0x90,
-	0xd1, 0xec, 0x54, 0x2a, 0x2e, 0xae, 0x3a, 0x20, 0xee, 0xd8, 0x95, 0x80, 0x8c, 0xa0, 0x25, 0xb2,
-	0x4c, 0x85, 0x11, 0xbb, 0x2c, 0x16, 0x14, 0x21, 0xa3, 0xc1, 0x70, 0x4b, 0x13, 0x75, 0x23, 0xb1,
-	0x73, 0xda, 0xd4, 0xd4, 0x11, 0xc3, 0x26, 0x7f, 0xa3, 0x68, 0xbf, 0x27, 0xb8, 0x22, 0x95, 0x60,
-	0x79, 0x38, 0xe5, 0xe7, 0xfa, 0x49, 0xd1, 0x58, 0xf7, 0x18, 0x38, 0x48, 0x23, 0x71, 0x6e, 0x02,
-	0xf5, 0x15, 0x3f, 0xa7, 0xd7, 0x9d, 0xc0, 0xb0, 0xe0, 0xe3, 0xa2, 0x24, 0x9f, 0xc3, 0x0e, 0x5f,
-	0xc0, 0xb4, 0x62, 0x38, 0xc3, 0x17, 0x19, 0xfe, 0x58, 0xc2, 0x68, 0x86, 0x8a, 0xa6, 0xb7, 0x79,
-	0xf4, 0x26, 0x2f, 0x4b, 0x7d, 0x6d, 0x11, 0x23, 0x0d, 0x18, 0xee, 0x5c, 0x3c, 0xdd, 0xbd, 0xf6,
-	0x27, 0x7e, 0xff, 0x3e, 0xdd, 0xad, 0xfd, 0xf8, 0x6c, 0xb7, 0x76, 0x81, 0xdf, 0x1f, 0xf8, 0xfd,
-	0x85, 0xdf, 0x49, 0xd3, 0xbc, 0x4b, 0x3f, 0xf8, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x27, 0xf6,
-	0x9e, 0x07, 0x0b, 0x00, 0x00,
+	// 1000 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0x4d, 0x6f, 0x1b, 0x45,
+	0x18, 0xae, 0x93, 0x8d, 0xed, 0x7d, 0x1d, 0x47, 0x62, 0xa8, 0xaa, 0x6d, 0x08, 0x49, 0x71, 0x05,
+	0xe2, 0x80, 0x5c, 0x51, 0x0a, 0xa2, 0x82, 0x0a, 0xd9, 0x4e, 0x04, 0x16, 0x04, 0xa2, 0x69, 0x09,
+	0xc7, 0xd5, 0x64, 0x77, 0x6a, 0x16, 0xdb, 0xbb, 0xab, 0x99, 0x71, 0xaa, 0xdc, 0x10, 0x3f, 0x00,
+	0x89, 0x3f, 0xc0, 0x5f, 0xe1, 0x9a, 0x03, 0x07, 0x8e, 0x9c, 0x2a, 0xda, 0x1b, 0x27, 0xf8, 0x09,
+	0xbc, 0xf3, 0xb1, 0xf6, 0x46, 0x5e, 0x87, 0x56, 0xaa, 0x72, 0x58, 0x69, 0x3e, 0x9e, 0xe7, 0x99,
+	0xf7, 0x6b, 0xde, 0x59, 0x68, 0x67, 0x27, 0x3f, 0xf0, 0x48, 0xc9, 0x6e, 0x2e, 0x32, 0x95, 0x11,
+	0x12, 0x67, 0xd1, 0x98, 0x8b, 0xae, 0x7c, 0xc2, 0xc4, 0x74, 0x9c, 0xa8, 0xee, 0xe9, 0xfb, 0xdb,
+	0x2d, 0x75, 0x96, 0x73, 0x07, 0xd8, 0x6e, 0xc9, 0x9c, 0x47, 0xc5, 0xe4, 0xa6, 0x4a, 0xa6, 0x5c,
+	0x2a, 0x36, 0xcd, 0xef, 0xcc, 0x47, 0x6e, 0xeb, 0xfa, 0x28, 0x1b, 0x65, 0x66, 0x78, 0x47, 0x8f,
+	0xec, 0x6a, 0xe7, 0xb7, 0x1a, 0x78, 0x87, 0x5c, 0x31, 0xf2, 0x09, 0x34, 0x4e, 0xb9, 0x90, 0x49,
+	0x96, 0x06, 0xb5, 0x5b, 0xb5, 0x77, 0x5b, 0x77, 0xdf, 0xe8, 0x2e, 0x9f, 0xdc, 0x3d, 0xb6, 0x90,
+	0xbe, 0x77, 0xfe, 0x74, 0xef, 0x1a, 0x2d, 0x18, 0xe4, 0x53, 0x80, 0x48, 0x70, 0xa6, 0x78, 0x1c,
+	0x32, 0x15, 0xac, 0x19, 0xfe, 0x9b, 0x55, 0xfc, 0x47, 0x85, 0x51, 0xd4, 0x77, 0x84, 0x9e, 0xd2,
+	0xec, 0x59, 0x1e, 0x17, 0xec, 0xf5, 0x17, 0x62, 0x3b, 0x42, 0x4f, 0x75, 0xfe, 0x5e, 0x07, 0xef,
+	0xeb, 0x2c, 0xe6, 0xe4, 0x06, 0xac, 0x25, 0xb1, 0x31, 0xde, 0xef, 0xd7, 0x9f, 0x3f, 0xdd, 0x5b,
+	0x1b, 0xee, 0x53, 0x5c, 0x21, 0x77, 0xc1, 0x9b, 0xa2, 0x87, 0xce, 0xac, 0xa0, 0x4a, 0x58, 0x47,
+	0xc0, 0xf9, 0x64, 0xb0, 0xe4, 0x23, 0xf0, 0x74, 0x58, 0x9d, 0x31, 0x3b, 0x55, 0x1c, 0x7d, 0xe6,
+	0x43, 0xc4, 0x14, 0x3c, 0x8d, 0x27, 0x07, 0xd0, 0x8a, 0xb9, 0x8c, 0x44, 0x92, 0x2b, 0x1d, 0x49,
+	0xcf, 0xd0, 0x6f, 0xaf, 0xa2, 0xef, 0x2f, 0xa0, 0xb4, 0xcc, 0xc3, 0x88, 0xd4, 0xd1, 0x4f, 0x35,
+	0x93, 0xc1, 0x86, 0x51, 0xd8, 0x5d, 0x69, 0x80, 0x41, 0x39, 0x13, 0x1c, 0x87, 0x7c, 0x01, 0x5b,
+	0x53, 0x96, 0xb2, 0x11, 0x17, 0xa1, 0x53, 0xa9, 0x1b, 0x95, 0xb7, 0x2a, 0x5d, 0xb7, 0x48, 0x2b,
+	0x44, 0xdb, 0xd3, 0xf2, 0x14, 0xdd, 0x01, 0xa6, 0x14, 0x8b, 0xbe, 0x9f, 0xf2, 0x54, 0x05, 0x0d,
+	0xa3, 0xf2, 0x76, 0xa5, 0x2d, 0x5c, 0x3d, 0xc9, 0xc4, 0xb8, 0x37, 0x07, 0xd3, 0x12, 0x91, 0x7c,
+	0x0e, 0xad, 0x88, 0x0b, 0x95, 0x3c, 0x4e, 0x22, 0x4c, 0x5a, 0xd0, 0x34, 0x3a, 0x7b, 0x55, 0x3a,
+	0x83, 0x05, 0xcc, 0x39, 0x55, 0x66, 0x76, 0x7e, 0x59, 0x83, 0xc6, 0x43, 0x2e, 0x4e, 0x93, 0xe8,
+	0xd5, 0xa6, 0xfb, 0xfe, 0x85, 0x74, 0x57, 0x5a, 0xe6, 0x8e, 0x5d, 0xca, 0xf8, 0xc7, 0xd0, 0xe4,
+	0x69, 0x9c, 0x67, 0x09, 0x06, 0xc8, 0x5b, 0x5d, 0x2d, 0x07, 0x0e, 0x43, 0xe7, 0x68, 0x0c, 0x6e,
+	0xdb, 0x56, 0x71, 0x78, 0x21, 0xd7, 0xb7, 0xaa, 0xe8, 0xdf, 0x1a, 0xa0, 0x4b, 0xd2, 0xe6, 0xac,
+	0x34, 0xeb, 0xfc, 0xba, 0x06, 0xcd, 0x42, 0x9d, 0xdc, 0x73, 0x8e, 0xd4, 0x56, 0x4b, 0x15, 0x58,
+	0xed, 0x89, 0xf3, 0xe1, 0x1e, 0x6c, 0xe4, 0x99, 0x50, 0x12, 0x63, 0xb6, 0xbe, 0xaa, 0xda, 0x8e,
+	0x10, 0x30, 0xc8, 0xd2, 0xc7, 0xc9, 0x88, 0x5a, 0x30, 0xf9, 0x0e, 0x5a, 0xa7, 0x89, 0x50, 0x33,
+	0x36, 0x09, 0x93, 0x5c, 0x62, 0xec, 0x34, 0xf7, 0x9d, 0xcb, 0x8e, 0xec, 0x1e, 0x5b, 0xfc, 0xf0,
+	0xa8, 0xbf, 0x85, 0x19, 0x83, 0xf9, 0x54, 0x52, 0x70, 0x52, 0xc3, 0x5c, 0x6e, 0x1f, 0x82, 0x3f,
+	0xdf, 0x21, 0xef, 0x01, 0xa4, 0xb6, 0xb8, 0xc2, 0x79, 0xba, 0xdb, 0x48, 0xf6, 0x5d, 0xc9, 0x61,
+	0xd6, 0x7d, 0x07, 0x18, 0xc6, 0x84, 0x80, 0xc7, 0xe2, 0x58, 0x98, 0xe4, 0xfb, 0xd4, 0x8c, 0x3b,
+	0xbf, 0x6f, 0x80, 0xf7, 0x88, 0xc9, 0xf1, 0x55, 0x37, 0x08, 0x7d, 0xe6, 0x52, 0xb9, 0xa0, 0x3b,
+	0xd2, 0x56, 0x92, 0x76, 0xc7, 0x5b, 0xb8, 0xe3, 0xea, 0x4b, 0xbb, 0xe3, 0x00, 0xd6, 0x1d, 0x39,
+	0xc9, 0x94, 0xa9, 0x0c, 0x8f, 0x9a, 0x31, 0xb9, 0x0d, 0x8d, 0x14, 0x6f, 0xbe, 0xa6, 0xd7, 0x0d,
+	0x1d, 0x90, 0x5e, 0xd7, 0xcd, 0x00, 0xb9, 0x75, 0xbd, 0x85, 0x44, 0xbc, 0x71, 0x2c, 0x4d, 0x33,
+	0xac, 0x10, 0x6c, 0x27, 0xd2, 0xdd, 0xdc, 0xca, 0xba, 0xee, 0x2d, 0x60, 0xc5, 0x8d, 0x2b, 0x31,
+	0xc9, 0x31, 0xbc, 0x5e, 0xd8, 0x5b, 0x16, 0x6c, 0xbe, 0x8c, 0x20, 0x71, 0x0a, 0xa5, 0x9d, 0x52,
+	0x87, 0xf3, 0x57, 0x77, 0x38, 0x13, 0xc1, 0xaa, 0x0e, 0xd7, 0x87, 0x36, 0xb6, 0xcb, 0x44, 0xe0,
+	0x8b, 0xa1, 0x57, 0x78, 0x00, 0x28, 0xb2, 0xb5, 0xe2, 0xd1, 0x70, 0x22, 0x9c, 0x6e, 0x3a, 0x8e,
+	0x99, 0x91, 0x1e, 0x34, 0x5d, 0xdd, 0xc8, 0xa0, 0x65, 0x6a, 0xf7, 0x05, 0x3b, 0xdb, 0x9c, 0x76,
+	0xe1, 0xee, 0x6f, 0xbe, 0xd4, 0xdd, 0xbf, 0x0f, 0x30, 0xc9, 0x46, 0x61, 0x2c, 0x12, 0x7c, 0x42,
+	0x83, 0xb6, 0xe1, 0x6e, 0x57, 0x71, 0xf7, 0x0d, 0x82, 0xfa, 0x88, 0xb6, 0xc3, 0xce, 0x4f, 0x35,
+	0x78, 0x6d, 0xc9, 0x28, 0xf2, 0x21, 0x56, 0x85, 0x5d, 0xbc, 0xec, 0xf9, 0x76, 0x3c, 0x5a, 0x60,
+	0xc9, 0x0e, 0xf8, 0xfa, 0x8e, 0x70, 0x29, 0xb9, 0xbd, 0xfd, 0x3e, 0x5d, 0x2c, 0x90, 0x00, 0x1a,
+	0x6c, 0x92, 0x30, 0xbd, 0xb7, 0x6e, 0xf6, 0x8a, 0x69, 0xe7, 0x67, 0x6c, 0xc4, 0x4e, 0xec, 0xaa,
+	0x1b, 0xb1, 0x3b, 0x76, 0xe9, 0x66, 0x3d, 0x80, 0x4d, 0x1b, 0x4e, 0x57, 0x12, 0xde, 0xff, 0x06,
+	0xb5, 0x65, 0xf1, 0xb6, 0x1c, 0x1e, 0x80, 0x97, 0xe4, 0x6c, 0xea, 0x9a, 0x70, 0xe5, 0xc9, 0xc3,
+	0xa3, 0xde, 0xe1, 0x37, 0xb9, 0xad, 0xec, 0x26, 0x3a, 0xea, 0xe9, 0x05, 0x6a, 0x68, 0x9d, 0x7f,
+	0x30, 0x20, 0x83, 0xc9, 0x4c, 0x2a, 0x2e, 0xae, 0x3a, 0x20, 0xee, 0xd8, 0xa5, 0x80, 0x0c, 0xa0,
+	0x21, 0xb2, 0x4c, 0x85, 0x11, 0xbb, 0x2c, 0x16, 0x14, 0x21, 0x83, 0x5e, 0x7f, 0x4b, 0x13, 0x75,
+	0x23, 0xb1, 0x73, 0x5a, 0xd7, 0xd4, 0x01, 0xc3, 0x26, 0x7f, 0xa3, 0x68, 0xbf, 0x27, 0xb8, 0x22,
+	0x95, 0x60, 0x79, 0x38, 0xe6, 0x67, 0xfa, 0xb5, 0x5a, 0x5f, 0xf5, 0x4f, 0x71, 0x90, 0x46, 0xe2,
+	0xcc, 0x04, 0xea, 0x4b, 0x7e, 0x46, 0xaf, 0x3b, 0x81, 0x7e, 0xc1, 0xc7, 0x45, 0x49, 0x3e, 0x83,
+	0x1d, 0x3e, 0x87, 0x69, 0xc5, 0x70, 0x82, 0x3f, 0x76, 0xf8, 0xb0, 0x84, 0xd1, 0x04, 0x15, 0x4d,
+	0x6f, 0xf3, 0xe8, 0x4d, 0x5e, 0x96, 0xfa, 0xca, 0x22, 0x06, 0x1a, 0xd0, 0xdf, 0x39, 0x7f, 0xb6,
+	0x7b, 0xed, 0x4f, 0xfc, 0xfe, 0x7d, 0xb6, 0x5b, 0xfb, 0xf1, 0xf9, 0x6e, 0xed, 0x1c, 0xbf, 0x3f,
+	0xf0, 0xfb, 0x0b, 0xbf, 0x93, 0xba, 0xf9, 0xbd, 0xfd, 0xe0, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff,
+	0x56, 0x49, 0xe6, 0x55, 0x4e, 0x0b, 0x00, 0x00,
 }

+ 4 - 0
vendor/src/github.com/docker/swarmkit/api/objects.proto

@@ -62,6 +62,10 @@ message Service {
 	// the optional fields like node_port or virtual_ip and it
 	// could be auto allocated by the system.
 	Endpoint endpoint = 4;
+
+	// UpdateStatus contains the status of an update, if one is in
+	// progress.
+	UpdateStatus update_status = 5;
 }
 
 // Endpoint specified all the network parameters required to

+ 655 - 256
vendor/src/github.com/docker/swarmkit/api/types.pb.go

@@ -31,6 +31,7 @@
 		Mount
 		RestartPolicy
 		UpdateConfig
+		UpdateStatus
 		ContainerStatus
 		TaskStatus
 		IPAMConfig
@@ -395,6 +396,58 @@ func (RestartPolicy_RestartCondition) EnumDescriptor() ([]byte, []int) {
 	return fileDescriptorTypes, []int{12, 0}
 }
 
+type UpdateConfig_FailureAction int32
+
+const (
+	UpdateConfig_PAUSE    UpdateConfig_FailureAction = 0
+	UpdateConfig_CONTINUE UpdateConfig_FailureAction = 1
+)
+
+var UpdateConfig_FailureAction_name = map[int32]string{
+	0: "PAUSE",
+	1: "CONTINUE",
+}
+var UpdateConfig_FailureAction_value = map[string]int32{
+	"PAUSE":    0,
+	"CONTINUE": 1,
+}
+
+func (x UpdateConfig_FailureAction) String() string {
+	return proto.EnumName(UpdateConfig_FailureAction_name, int32(x))
+}
+func (UpdateConfig_FailureAction) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptorTypes, []int{13, 0}
+}
+
+type UpdateStatus_UpdateState int32
+
+const (
+	UpdateStatus_UNKNOWN   UpdateStatus_UpdateState = 0
+	UpdateStatus_UPDATING  UpdateStatus_UpdateState = 1
+	UpdateStatus_PAUSED    UpdateStatus_UpdateState = 2
+	UpdateStatus_COMPLETED UpdateStatus_UpdateState = 3
+)
+
+var UpdateStatus_UpdateState_name = map[int32]string{
+	0: "UNKNOWN",
+	1: "UPDATING",
+	2: "PAUSED",
+	3: "COMPLETED",
+}
+var UpdateStatus_UpdateState_value = map[string]int32{
+	"UNKNOWN":   0,
+	"UPDATING":  1,
+	"PAUSED":    2,
+	"COMPLETED": 3,
+}
+
+func (x UpdateStatus_UpdateState) String() string {
+	return proto.EnumName(UpdateStatus_UpdateState_name, int32(x))
+}
+func (UpdateStatus_UpdateState) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptorTypes, []int{14, 0}
+}
+
 // AddressFamily specifies the network address family that
 // this IPAMConfig belongs to.
 type IPAMConfig_AddressFamily int32
@@ -420,7 +473,7 @@ func (x IPAMConfig_AddressFamily) String() string {
 	return proto.EnumName(IPAMConfig_AddressFamily_name, int32(x))
 }
 func (IPAMConfig_AddressFamily) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptorTypes, []int{16, 0}
+	return fileDescriptorTypes, []int{17, 0}
 }
 
 type PortConfig_Protocol int32
@@ -442,7 +495,7 @@ var PortConfig_Protocol_value = map[string]int32{
 func (x PortConfig_Protocol) String() string {
 	return proto.EnumName(PortConfig_Protocol_name, int32(x))
 }
-func (PortConfig_Protocol) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{17, 0} }
+func (PortConfig_Protocol) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{18, 0} }
 
 type IssuanceStatus_State int32
 
@@ -472,7 +525,7 @@ var IssuanceStatus_State_value = map[string]int32{
 func (x IssuanceStatus_State) String() string {
 	return proto.EnumName(IssuanceStatus_State_name, int32(x))
 }
-func (IssuanceStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22, 0} }
+func (IssuanceStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{23, 0} }
 
 type ExternalCA_CAProtocol int32
 
@@ -491,7 +544,7 @@ func (x ExternalCA_CAProtocol) String() string {
 	return proto.EnumName(ExternalCA_CAProtocol_name, int32(x))
 }
 func (ExternalCA_CAProtocol) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptorTypes, []int{24, 0}
+	return fileDescriptorTypes, []int{25, 0}
 }
 
 // Encryption algorithm that can implemented using this key
@@ -512,7 +565,7 @@ func (x EncryptionKey_Algorithm) String() string {
 	return proto.EnumName(EncryptionKey_Algorithm_name, int32(x))
 }
 func (EncryptionKey_Algorithm) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptorTypes, []int{34, 0}
+	return fileDescriptorTypes, []int{35, 0}
 }
 
 // Version tracks the last time an object in the store was updated.
@@ -743,12 +796,37 @@ type UpdateConfig struct {
 	Parallelism uint64 `protobuf:"varint,1,opt,name=parallelism,proto3" json:"parallelism,omitempty"`
 	// Amount of time between updates.
 	Delay docker_swarmkit_v11.Duration `protobuf:"bytes,2,opt,name=delay" json:"delay"`
+	// FailureAction is the action to take when an update failures.
+	// Currently, a failure is defined as a single updated task failing to
+	// reach the RUNNING state. In the future, there will be configuration
+	// to define what is treated as a failure (see #486 for a proposal).
+	FailureAction UpdateConfig_FailureAction `protobuf:"varint,3,opt,name=failure_action,json=failureAction,proto3,enum=docker.swarmkit.v1.UpdateConfig_FailureAction" json:"failure_action,omitempty"`
 }
 
 func (m *UpdateConfig) Reset()                    { *m = UpdateConfig{} }
 func (*UpdateConfig) ProtoMessage()               {}
 func (*UpdateConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13} }
 
+// UpdateStatus is the status of an update in progress.
+type UpdateStatus struct {
+	// State is the state of this update. It indicates whether the
+	// update is in progress, completed, or is paused.
+	State UpdateStatus_UpdateState `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.UpdateStatus_UpdateState" json:"state,omitempty"`
+	// StartedAt is the time at which the update was started.
+	StartedAt *docker_swarmkit_v1.Timestamp `protobuf:"bytes,2,opt,name=started_at,json=startedAt" json:"started_at,omitempty"`
+	// CompletedAt is the time at which the update completed.
+	CompletedAt *docker_swarmkit_v1.Timestamp `protobuf:"bytes,3,opt,name=completed_at,json=completedAt" json:"completed_at,omitempty"`
+	// Message explains how the update got into its current state. For
+	// example, if the update is paused, it will explain what is preventing
+	// the update from proceeding (typically the failure of a task to start up
+	// when OnFailure is PAUSE).
+	Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"`
+}
+
+func (m *UpdateStatus) Reset()                    { *m = UpdateStatus{} }
+func (*UpdateStatus) ProtoMessage()               {}
+func (*UpdateStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{14} }
+
 // Container specific status.
 type ContainerStatus struct {
 	ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"`
@@ -758,7 +836,7 @@ type ContainerStatus struct {
 
 func (m *ContainerStatus) Reset()                    { *m = ContainerStatus{} }
 func (*ContainerStatus) ProtoMessage()               {}
-func (*ContainerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{14} }
+func (*ContainerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{15} }
 
 type TaskStatus struct {
 	Timestamp *docker_swarmkit_v1.Timestamp `protobuf:"bytes,1,opt,name=timestamp" json:"timestamp,omitempty"`
@@ -791,7 +869,7 @@ type TaskStatus struct {
 
 func (m *TaskStatus) Reset()                    { *m = TaskStatus{} }
 func (*TaskStatus) ProtoMessage()               {}
-func (*TaskStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{15} }
+func (*TaskStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16} }
 
 type isTaskStatus_RuntimeStatus interface {
 	isTaskStatus_RuntimeStatus()
@@ -893,7 +971,7 @@ type IPAMConfig struct {
 
 func (m *IPAMConfig) Reset()                    { *m = IPAMConfig{} }
 func (*IPAMConfig) ProtoMessage()               {}
-func (*IPAMConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16} }
+func (*IPAMConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{17} }
 
 // PortConfig specifies an exposed port which can be
 // addressed using the given name. This can be later queried
@@ -918,7 +996,7 @@ type PortConfig struct {
 
 func (m *PortConfig) Reset()                    { *m = PortConfig{} }
 func (*PortConfig) ProtoMessage()               {}
-func (*PortConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{17} }
+func (*PortConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{18} }
 
 // Driver is a generic driver type to be used throughout the API. For now, a
 // driver is simply a name and set of options. The field contents depend on the
@@ -931,7 +1009,7 @@ type Driver struct {
 
 func (m *Driver) Reset()                    { *m = Driver{} }
 func (*Driver) ProtoMessage()               {}
-func (*Driver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{18} }
+func (*Driver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{19} }
 
 type IPAMOptions struct {
 	Driver  *Driver       `protobuf:"bytes,1,opt,name=driver" json:"driver,omitempty"`
@@ -940,7 +1018,7 @@ type IPAMOptions struct {
 
 func (m *IPAMOptions) Reset()                    { *m = IPAMOptions{} }
 func (*IPAMOptions) ProtoMessage()               {}
-func (*IPAMOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{19} }
+func (*IPAMOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{20} }
 
 // Peer should be used anywhere where we are describing a remote peer.
 type Peer struct {
@@ -950,7 +1028,7 @@ type Peer struct {
 
 func (m *Peer) Reset()                    { *m = Peer{} }
 func (*Peer) ProtoMessage()               {}
-func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{20} }
+func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21} }
 
 // WeightedPeer should be used anywhere where we are describing a remote peer
 // with a weight.
@@ -961,7 +1039,7 @@ type WeightedPeer struct {
 
 func (m *WeightedPeer) Reset()                    { *m = WeightedPeer{} }
 func (*WeightedPeer) ProtoMessage()               {}
-func (*WeightedPeer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21} }
+func (*WeightedPeer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22} }
 
 type IssuanceStatus struct {
 	State IssuanceStatus_State `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.IssuanceStatus_State" json:"state,omitempty"`
@@ -973,7 +1051,7 @@ type IssuanceStatus struct {
 
 func (m *IssuanceStatus) Reset()                    { *m = IssuanceStatus{} }
 func (*IssuanceStatus) ProtoMessage()               {}
-func (*IssuanceStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22} }
+func (*IssuanceStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{23} }
 
 type AcceptancePolicy struct {
 	Policies []*AcceptancePolicy_RoleAdmissionPolicy `protobuf:"bytes,1,rep,name=policies" json:"policies,omitempty"`
@@ -981,7 +1059,7 @@ type AcceptancePolicy struct {
 
 func (m *AcceptancePolicy) Reset()                    { *m = AcceptancePolicy{} }
 func (*AcceptancePolicy) ProtoMessage()               {}
-func (*AcceptancePolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{23} }
+func (*AcceptancePolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} }
 
 type AcceptancePolicy_RoleAdmissionPolicy struct {
 	Role NodeRole `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"`
@@ -996,7 +1074,7 @@ type AcceptancePolicy_RoleAdmissionPolicy struct {
 func (m *AcceptancePolicy_RoleAdmissionPolicy) Reset()      { *m = AcceptancePolicy_RoleAdmissionPolicy{} }
 func (*AcceptancePolicy_RoleAdmissionPolicy) ProtoMessage() {}
 func (*AcceptancePolicy_RoleAdmissionPolicy) Descriptor() ([]byte, []int) {
-	return fileDescriptorTypes, []int{23, 0}
+	return fileDescriptorTypes, []int{24, 0}
 }
 
 type AcceptancePolicy_RoleAdmissionPolicy_Secret struct {
@@ -1011,7 +1089,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Reset() {
 }
 func (*AcceptancePolicy_RoleAdmissionPolicy_Secret) ProtoMessage() {}
 func (*AcceptancePolicy_RoleAdmissionPolicy_Secret) Descriptor() ([]byte, []int) {
-	return fileDescriptorTypes, []int{23, 0, 0}
+	return fileDescriptorTypes, []int{24, 0, 0}
 }
 
 type ExternalCA struct {
@@ -1026,7 +1104,7 @@ type ExternalCA struct {
 
 func (m *ExternalCA) Reset()                    { *m = ExternalCA{} }
 func (*ExternalCA) ProtoMessage()               {}
-func (*ExternalCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} }
+func (*ExternalCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} }
 
 type CAConfig struct {
 	// NodeCertExpiry is the duration certificates should be issued for
@@ -1038,7 +1116,7 @@ type CAConfig struct {
 
 func (m *CAConfig) Reset()                    { *m = CAConfig{} }
 func (*CAConfig) ProtoMessage()               {}
-func (*CAConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} }
+func (*CAConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} }
 
 // OrchestrationConfig defines cluster-level orchestration settings.
 type OrchestrationConfig struct {
@@ -1049,7 +1127,7 @@ type OrchestrationConfig struct {
 
 func (m *OrchestrationConfig) Reset()                    { *m = OrchestrationConfig{} }
 func (*OrchestrationConfig) ProtoMessage()               {}
-func (*OrchestrationConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} }
+func (*OrchestrationConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} }
 
 // TaskDefaults specifies default values for task creation.
 type TaskDefaults struct {
@@ -1063,7 +1141,7 @@ type TaskDefaults struct {
 
 func (m *TaskDefaults) Reset()                    { *m = TaskDefaults{} }
 func (*TaskDefaults) ProtoMessage()               {}
-func (*TaskDefaults) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} }
+func (*TaskDefaults) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} }
 
 // DispatcherConfig defines cluster-level dispatcher settings.
 type DispatcherConfig struct {
@@ -1074,7 +1152,7 @@ type DispatcherConfig struct {
 
 func (m *DispatcherConfig) Reset()                    { *m = DispatcherConfig{} }
 func (*DispatcherConfig) ProtoMessage()               {}
-func (*DispatcherConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} }
+func (*DispatcherConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} }
 
 // RaftConfig defines raft settings for the cluster.
 type RaftConfig struct {
@@ -1096,7 +1174,7 @@ type RaftConfig struct {
 
 func (m *RaftConfig) Reset()                    { *m = RaftConfig{} }
 func (*RaftConfig) ProtoMessage()               {}
-func (*RaftConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} }
+func (*RaftConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} }
 
 // Placement specifies task distribution constraints.
 type Placement struct {
@@ -1106,7 +1184,7 @@ type Placement struct {
 
 func (m *Placement) Reset()                    { *m = Placement{} }
 func (*Placement) ProtoMessage()               {}
-func (*Placement) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} }
+func (*Placement) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} }
 
 // JoinToken contains the join tokens for workers and managers.
 type JoinTokens struct {
@@ -1118,7 +1196,7 @@ type JoinTokens struct {
 
 func (m *JoinTokens) Reset()                    { *m = JoinTokens{} }
 func (*JoinTokens) ProtoMessage()               {}
-func (*JoinTokens) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} }
+func (*JoinTokens) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} }
 
 type RootCA struct {
 	// CAKey is the root CA private key.
@@ -1133,7 +1211,7 @@ type RootCA struct {
 
 func (m *RootCA) Reset()                    { *m = RootCA{} }
 func (*RootCA) ProtoMessage()               {}
-func (*RootCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} }
+func (*RootCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{33} }
 
 type Certificate struct {
 	Role        NodeRole       `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"`
@@ -1146,7 +1224,7 @@ type Certificate struct {
 
 func (m *Certificate) Reset()                    { *m = Certificate{} }
 func (*Certificate) ProtoMessage()               {}
-func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{33} }
+func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{34} }
 
 // Symmetric keys to encrypt inter-agent communication.
 type EncryptionKey struct {
@@ -1162,7 +1240,7 @@ type EncryptionKey struct {
 
 func (m *EncryptionKey) Reset()                    { *m = EncryptionKey{} }
 func (*EncryptionKey) ProtoMessage()               {}
-func (*EncryptionKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{34} }
+func (*EncryptionKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{35} }
 
 // ManagerStatus provides informations about the state of a manager in the cluster.
 type ManagerStatus struct {
@@ -1179,7 +1257,7 @@ type ManagerStatus struct {
 
 func (m *ManagerStatus) Reset()                    { *m = ManagerStatus{} }
 func (*ManagerStatus) ProtoMessage()               {}
-func (*ManagerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{35} }
+func (*ManagerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{36} }
 
 func init() {
 	proto.RegisterType((*Version)(nil), "docker.swarmkit.v1.Version")
@@ -1199,6 +1277,7 @@ func init() {
 	proto.RegisterType((*Mount_TmpfsOptions)(nil), "docker.swarmkit.v1.Mount.TmpfsOptions")
 	proto.RegisterType((*RestartPolicy)(nil), "docker.swarmkit.v1.RestartPolicy")
 	proto.RegisterType((*UpdateConfig)(nil), "docker.swarmkit.v1.UpdateConfig")
+	proto.RegisterType((*UpdateStatus)(nil), "docker.swarmkit.v1.UpdateStatus")
 	proto.RegisterType((*ContainerStatus)(nil), "docker.swarmkit.v1.ContainerStatus")
 	proto.RegisterType((*TaskStatus)(nil), "docker.swarmkit.v1.TaskStatus")
 	proto.RegisterType((*IPAMConfig)(nil), "docker.swarmkit.v1.IPAMConfig")
@@ -1230,6 +1309,8 @@ func init() {
 	proto.RegisterEnum("docker.swarmkit.v1.Mount_MountType", Mount_MountType_name, Mount_MountType_value)
 	proto.RegisterEnum("docker.swarmkit.v1.Mount_BindOptions_MountPropagation", Mount_BindOptions_MountPropagation_name, Mount_BindOptions_MountPropagation_value)
 	proto.RegisterEnum("docker.swarmkit.v1.RestartPolicy_RestartCondition", RestartPolicy_RestartCondition_name, RestartPolicy_RestartCondition_value)
+	proto.RegisterEnum("docker.swarmkit.v1.UpdateConfig_FailureAction", UpdateConfig_FailureAction_name, UpdateConfig_FailureAction_value)
+	proto.RegisterEnum("docker.swarmkit.v1.UpdateStatus_UpdateState", UpdateStatus_UpdateState_name, UpdateStatus_UpdateState_value)
 	proto.RegisterEnum("docker.swarmkit.v1.IPAMConfig_AddressFamily", IPAMConfig_AddressFamily_name, IPAMConfig_AddressFamily_value)
 	proto.RegisterEnum("docker.swarmkit.v1.PortConfig_Protocol", PortConfig_Protocol_name, PortConfig_Protocol_value)
 	proto.RegisterEnum("docker.swarmkit.v1.IssuanceStatus_State", IssuanceStatus_State_name, IssuanceStatus_State_value)
@@ -1484,8 +1565,24 @@ func (m *UpdateConfig) Copy() *UpdateConfig {
 	}
 
 	o := &UpdateConfig{
-		Parallelism: m.Parallelism,
-		Delay:       *m.Delay.Copy(),
+		Parallelism:   m.Parallelism,
+		Delay:         *m.Delay.Copy(),
+		FailureAction: m.FailureAction,
+	}
+
+	return o
+}
+
+func (m *UpdateStatus) Copy() *UpdateStatus {
+	if m == nil {
+		return nil
+	}
+
+	o := &UpdateStatus{
+		State:       m.State,
+		StartedAt:   m.StartedAt.Copy(),
+		CompletedAt: m.CompletedAt.Copy(),
+		Message:     m.Message,
 	}
 
 	return o
@@ -2118,10 +2215,28 @@ func (this *UpdateConfig) GoString() string {
 	if this == nil {
 		return "nil"
 	}
-	s := make([]string, 0, 6)
+	s := make([]string, 0, 7)
 	s = append(s, "&api.UpdateConfig{")
 	s = append(s, "Parallelism: "+fmt.Sprintf("%#v", this.Parallelism)+",\n")
 	s = append(s, "Delay: "+strings.Replace(this.Delay.GoString(), `&`, ``, 1)+",\n")
+	s = append(s, "FailureAction: "+fmt.Sprintf("%#v", this.FailureAction)+",\n")
+	s = append(s, "}")
+	return strings.Join(s, "")
+}
+func (this *UpdateStatus) GoString() string {
+	if this == nil {
+		return "nil"
+	}
+	s := make([]string, 0, 8)
+	s = append(s, "&api.UpdateStatus{")
+	s = append(s, "State: "+fmt.Sprintf("%#v", this.State)+",\n")
+	if this.StartedAt != nil {
+		s = append(s, "StartedAt: "+fmt.Sprintf("%#v", this.StartedAt)+",\n")
+	}
+	if this.CompletedAt != nil {
+		s = append(s, "CompletedAt: "+fmt.Sprintf("%#v", this.CompletedAt)+",\n")
+	}
+	s = append(s, "Message: "+fmt.Sprintf("%#v", this.Message)+",\n")
 	s = append(s, "}")
 	return strings.Join(s, "")
 }
@@ -3144,6 +3259,60 @@ func (m *UpdateConfig) MarshalTo(data []byte) (int, error) {
 		return 0, err
 	}
 	i += n12
+	if m.FailureAction != 0 {
+		data[i] = 0x18
+		i++
+		i = encodeVarintTypes(data, i, uint64(m.FailureAction))
+	}
+	return i, nil
+}
+
+func (m *UpdateStatus) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *UpdateStatus) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.State != 0 {
+		data[i] = 0x8
+		i++
+		i = encodeVarintTypes(data, i, uint64(m.State))
+	}
+	if m.StartedAt != nil {
+		data[i] = 0x12
+		i++
+		i = encodeVarintTypes(data, i, uint64(m.StartedAt.Size()))
+		n13, err := m.StartedAt.MarshalTo(data[i:])
+		if err != nil {
+			return 0, err
+		}
+		i += n13
+	}
+	if m.CompletedAt != nil {
+		data[i] = 0x1a
+		i++
+		i = encodeVarintTypes(data, i, uint64(m.CompletedAt.Size()))
+		n14, err := m.CompletedAt.MarshalTo(data[i:])
+		if err != nil {
+			return 0, err
+		}
+		i += n14
+	}
+	if len(m.Message) > 0 {
+		data[i] = 0x22
+		i++
+		i = encodeVarintTypes(data, i, uint64(len(m.Message)))
+		i += copy(data[i:], m.Message)
+	}
 	return i, nil
 }
 
@@ -3200,11 +3369,11 @@ func (m *TaskStatus) MarshalTo(data []byte) (int, error) {
 		data[i] = 0xa
 		i++
 		i = encodeVarintTypes(data, i, uint64(m.Timestamp.Size()))
-		n13, err := m.Timestamp.MarshalTo(data[i:])
+		n15, err := m.Timestamp.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n13
+		i += n15
 	}
 	if m.State != 0 {
 		data[i] = 0x10
@@ -3224,11 +3393,11 @@ func (m *TaskStatus) MarshalTo(data []byte) (int, error) {
 		i += copy(data[i:], m.Err)
 	}
 	if m.RuntimeStatus != nil {
-		nn14, err := m.RuntimeStatus.MarshalTo(data[i:])
+		nn16, err := m.RuntimeStatus.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += nn14
+		i += nn16
 	}
 	return i, nil
 }
@@ -3239,11 +3408,11 @@ func (m *TaskStatus_Container) MarshalTo(data []byte) (int, error) {
 		data[i] = 0x2a
 		i++
 		i = encodeVarintTypes(data, i, uint64(m.Container.Size()))
-		n15, err := m.Container.MarshalTo(data[i:])
+		n17, err := m.Container.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n15
+		i += n17
 	}
 	return i, nil
 }
@@ -3404,11 +3573,11 @@ func (m *IPAMOptions) MarshalTo(data []byte) (int, error) {
 		data[i] = 0xa
 		i++
 		i = encodeVarintTypes(data, i, uint64(m.Driver.Size()))
-		n16, err := m.Driver.MarshalTo(data[i:])
+		n18, err := m.Driver.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n16
+		i += n18
 	}
 	if len(m.Configs) > 0 {
 		for _, msg := range m.Configs {
@@ -3474,11 +3643,11 @@ func (m *WeightedPeer) MarshalTo(data []byte) (int, error) {
 		data[i] = 0xa
 		i++
 		i = encodeVarintTypes(data, i, uint64(m.Peer.Size()))
-		n17, err := m.Peer.MarshalTo(data[i:])
+		n19, err := m.Peer.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n17
+		i += n19
 	}
 	if m.Weight != 0 {
 		data[i] = 0x10
@@ -3581,11 +3750,11 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) MarshalTo(data []byte) (int, erro
 		data[i] = 0x1a
 		i++
 		i = encodeVarintTypes(data, i, uint64(m.Secret.Size()))
-		n18, err := m.Secret.MarshalTo(data[i:])
+		n20, err := m.Secret.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n18
+		i += n20
 	}
 	return i, nil
 }
@@ -3685,11 +3854,11 @@ func (m *CAConfig) MarshalTo(data []byte) (int, error) {
 		data[i] = 0xa
 		i++
 		i = encodeVarintTypes(data, i, uint64(m.NodeCertExpiry.Size()))
-		n19, err := m.NodeCertExpiry.MarshalTo(data[i:])
+		n21, err := m.NodeCertExpiry.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n19
+		i += n21
 	}
 	if len(m.ExternalCAs) > 0 {
 		for _, msg := range m.ExternalCAs {
@@ -3748,11 +3917,11 @@ func (m *TaskDefaults) MarshalTo(data []byte) (int, error) {
 		data[i] = 0xa
 		i++
 		i = encodeVarintTypes(data, i, uint64(m.LogDriver.Size()))
-		n20, err := m.LogDriver.MarshalTo(data[i:])
+		n22, err := m.LogDriver.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n20
+		i += n22
 	}
 	return i, nil
 }
@@ -3776,11 +3945,11 @@ func (m *DispatcherConfig) MarshalTo(data []byte) (int, error) {
 		data[i] = 0xa
 		i++
 		i = encodeVarintTypes(data, i, uint64(m.HeartbeatPeriod.Size()))
-		n21, err := m.HeartbeatPeriod.MarshalTo(data[i:])
+		n23, err := m.HeartbeatPeriod.MarshalTo(data[i:])
 		if err != nil {
 			return 0, err
 		}
-		i += n21
+		i += n23
 	}
 	return i, nil
 }
@@ -3927,11 +4096,11 @@ func (m *RootCA) MarshalTo(data []byte) (int, error) {
 	data[i] = 0x22
 	i++
 	i = encodeVarintTypes(data, i, uint64(m.JoinTokens.Size()))
-	n22, err := m.JoinTokens.MarshalTo(data[i:])
+	n24, err := m.JoinTokens.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n22
+	i += n24
 	return i, nil
 }
 
@@ -3964,11 +4133,11 @@ func (m *Certificate) MarshalTo(data []byte) (int, error) {
 	data[i] = 0x1a
 	i++
 	i = encodeVarintTypes(data, i, uint64(m.Status.Size()))
-	n23, err := m.Status.MarshalTo(data[i:])
+	n25, err := m.Status.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n23
+	i += n25
 	if len(m.Certificate) > 0 {
 		data[i] = 0x22
 		i++
@@ -4364,6 +4533,30 @@ func (m *UpdateConfig) Size() (n int) {
 	}
 	l = m.Delay.Size()
 	n += 1 + l + sovTypes(uint64(l))
+	if m.FailureAction != 0 {
+		n += 1 + sovTypes(uint64(m.FailureAction))
+	}
+	return n
+}
+
+func (m *UpdateStatus) Size() (n int) {
+	var l int
+	_ = l
+	if m.State != 0 {
+		n += 1 + sovTypes(uint64(m.State))
+	}
+	if m.StartedAt != nil {
+		l = m.StartedAt.Size()
+		n += 1 + l + sovTypes(uint64(l))
+	}
+	if m.CompletedAt != nil {
+		l = m.CompletedAt.Size()
+		n += 1 + l + sovTypes(uint64(l))
+	}
+	l = len(m.Message)
+	if l > 0 {
+		n += 1 + l + sovTypes(uint64(l))
+	}
 	return n
 }
 
@@ -5010,6 +5203,20 @@ func (this *UpdateConfig) String() string {
 	s := strings.Join([]string{`&UpdateConfig{`,
 		`Parallelism:` + fmt.Sprintf("%v", this.Parallelism) + `,`,
 		`Delay:` + strings.Replace(strings.Replace(this.Delay.String(), "Duration", "docker_swarmkit_v11.Duration", 1), `&`, ``, 1) + `,`,
+		`FailureAction:` + fmt.Sprintf("%v", this.FailureAction) + `,`,
+		`}`,
+	}, "")
+	return s
+}
+func (this *UpdateStatus) String() string {
+	if this == nil {
+		return "nil"
+	}
+	s := strings.Join([]string{`&UpdateStatus{`,
+		`State:` + fmt.Sprintf("%v", this.State) + `,`,
+		`StartedAt:` + strings.Replace(fmt.Sprintf("%v", this.StartedAt), "Timestamp", "docker_swarmkit_v1.Timestamp", 1) + `,`,
+		`CompletedAt:` + strings.Replace(fmt.Sprintf("%v", this.CompletedAt), "Timestamp", "docker_swarmkit_v1.Timestamp", 1) + `,`,
+		`Message:` + fmt.Sprintf("%v", this.Message) + `,`,
 		`}`,
 	}, "")
 	return s
@@ -7566,6 +7773,189 @@ func (m *UpdateConfig) Unmarshal(data []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field FailureAction", wireType)
+			}
+			m.FailureAction = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.FailureAction |= (UpdateConfig_FailureAction(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTypes(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthTypes
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *UpdateStatus) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTypes
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: UpdateStatus: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: UpdateStatus: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field State", wireType)
+			}
+			m.State = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.State |= (UpdateStatus_UpdateState(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field StartedAt", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTypes
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.StartedAt == nil {
+				m.StartedAt = &docker_swarmkit_v1.Timestamp{}
+			}
+			if err := m.StartedAt.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field CompletedAt", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTypes
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.CompletedAt == nil {
+				m.CompletedAt = &docker_swarmkit_v1.Timestamp{}
+			}
+			if err := m.CompletedAt.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTypes
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTypes
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Message = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipTypes(data[iNdEx:])
@@ -10866,206 +11256,215 @@ var (
 )
 
 var fileDescriptorTypes = []byte{
-	// 3210 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x59, 0xcb, 0x6f, 0x1b, 0xd7,
-	0xd5, 0x17, 0x9f, 0x22, 0x0f, 0x29, 0x89, 0x1e, 0x3b, 0x8e, 0xcc, 0x28, 0xb2, 0xbe, 0x49, 0x9c,
-	0x38, 0x8f, 0x8f, 0x89, 0x95, 0x7c, 0x1f, 0x9c, 0x04, 0x8d, 0x33, 0x7c, 0xc8, 0x62, 0x2c, 0x91,
-	0xc4, 0x25, 0x25, 0x23, 0x28, 0x50, 0x62, 0x34, 0xbc, 0x12, 0xc7, 0x1a, 0xce, 0xb0, 0x33, 0x43,
-	0xc9, 0x6c, 0x51, 0xc0, 0xed, 0xa6, 0x45, 0x56, 0xdd, 0x17, 0x41, 0x50, 0xb4, 0xe8, 0xae, 0xeb,
-	0x02, 0x5d, 0x79, 0xe9, 0x65, 0x8a, 0x02, 0x45, 0xd0, 0x02, 0x41, 0x93, 0xfe, 0x03, 0x01, 0xba,
-	0xc8, 0xa2, 0x5d, 0xf4, 0xdc, 0xc7, 0x0c, 0x1f, 0x1e, 0x2b, 0x4a, 0x93, 0x85, 0xc0, 0xb9, 0xe7,
-	0xfe, 0xce, 0xb9, 0xaf, 0x73, 0xcf, 0xf9, 0x9d, 0x2b, 0xc8, 0xf9, 0xe3, 0x21, 0xf5, 0x4a, 0x43,
-	0xd7, 0xf1, 0x1d, 0x45, 0xe9, 0x39, 0xc6, 0x31, 0x75, 0x4b, 0xde, 0xa9, 0xee, 0x0e, 0x8e, 0x4d,
-	0xbf, 0x74, 0x72, 0xa3, 0x78, 0xc5, 0x37, 0x07, 0xd4, 0xf3, 0xf5, 0xc1, 0xf0, 0xb5, 0xf0, 0x4b,
-	0xc0, 0x8b, 0x4f, 0xf7, 0x46, 0xae, 0xee, 0x9b, 0x8e, 0xfd, 0x5a, 0xf0, 0x21, 0x3b, 0x2e, 0x1d,
-	0x39, 0x47, 0x0e, 0xff, 0x7c, 0x8d, 0x7d, 0x09, 0xa9, 0x7a, 0x15, 0x16, 0xf7, 0xa9, 0xeb, 0x21,
-	0x4c, 0xb9, 0x04, 0x29, 0xd3, 0xee, 0xd1, 0xfb, 0xab, 0xb1, 0x8d, 0xd8, 0xf5, 0x24, 0x11, 0x0d,
-	0xf5, 0xd7, 0x31, 0xc8, 0x69, 0xb6, 0xed, 0xf8, 0xdc, 0x96, 0xa7, 0x28, 0x90, 0xb4, 0xf5, 0x01,
-	0xe5, 0xa0, 0x2c, 0xe1, 0xdf, 0x4a, 0x05, 0xd2, 0x96, 0x7e, 0x40, 0x2d, 0x6f, 0x35, 0xbe, 0x91,
-	0xb8, 0x9e, 0xdb, 0x7c, 0xa5, 0xf4, 0xf8, 0x9c, 0x4b, 0x53, 0x46, 0x4a, 0x3b, 0x1c, 0x5d, 0xb3,
-	0x7d, 0x77, 0x4c, 0xa4, 0x6a, 0xf1, 0x2d, 0xc8, 0x4d, 0x89, 0x95, 0x02, 0x24, 0x8e, 0xe9, 0x58,
-	0x0e, 0xc3, 0x3e, 0xd9, 0xfc, 0x4e, 0x74, 0x6b, 0x44, 0x71, 0x10, 0x26, 0x13, 0x8d, 0xb7, 0xe3,
-	0x37, 0x63, 0xea, 0x07, 0x90, 0x25, 0xd4, 0x73, 0x46, 0xae, 0x41, 0x3d, 0xe5, 0x25, 0xc8, 0xda,
-	0xba, 0xed, 0x74, 0x8d, 0xe1, 0xc8, 0xe3, 0xea, 0x89, 0x72, 0xfe, 0x8b, 0xcf, 0xae, 0x66, 0x1a,
-	0x28, 0xac, 0xb4, 0xf6, 0x3c, 0x92, 0x61, 0xdd, 0x15, 0xec, 0x55, 0xfe, 0x07, 0xf2, 0x03, 0x3a,
-	0x70, 0xdc, 0x71, 0xf7, 0x60, 0xec, 0x53, 0x8f, 0x1b, 0x4e, 0x90, 0x9c, 0x90, 0x95, 0x99, 0x48,
-	0xfd, 0x65, 0x0c, 0x2e, 0x05, 0xb6, 0x09, 0xfd, 0xe1, 0xc8, 0x74, 0xe9, 0x80, 0xda, 0xbe, 0xa7,
-	0xfc, 0x1f, 0xae, 0xd9, 0x1c, 0x98, 0xbe, 0x18, 0x23, 0xb7, 0xf9, 0x6c, 0xd4, 0x9a, 0xc3, 0x59,
-	0x11, 0x09, 0x56, 0x34, 0xc8, 0xbb, 0xd4, 0xa3, 0xee, 0x89, 0xd8, 0x09, 0x3e, 0xe4, 0xd7, 0x2a,
-	0xcf, 0xa8, 0xa8, 0x5b, 0x90, 0x69, 0x59, 0xba, 0x7f, 0xe8, 0xb8, 0x03, 0x45, 0x85, 0xbc, 0xee,
-	0x1a, 0x7d, 0xd3, 0xa7, 0x86, 0x3f, 0x72, 0x83, 0x53, 0x99, 0x91, 0x29, 0x97, 0x21, 0xee, 0x88,
-	0x81, 0xb2, 0xe5, 0x34, 0xee, 0x44, 0xbc, 0xd9, 0x26, 0x28, 0x51, 0xdf, 0x81, 0x0b, 0x2d, 0x6b,
-	0x74, 0x64, 0xda, 0x55, 0xea, 0x19, 0xae, 0x39, 0x64, 0xd6, 0xd9, 0xf1, 0x32, 0xe7, 0x0b, 0x8e,
-	0x97, 0x7d, 0x87, 0x47, 0x1e, 0x9f, 0x1c, 0xb9, 0xfa, 0xf3, 0x38, 0x5c, 0xa8, 0xd9, 0xa8, 0x4c,
-	0xa7, 0xb5, 0xaf, 0xc1, 0x32, 0xe5, 0xc2, 0xee, 0x89, 0x70, 0x2a, 0x69, 0x67, 0x49, 0x48, 0x03,
-	0x4f, 0xab, 0xcf, 0xf9, 0xcb, 0x8d, 0xa8, 0xe5, 0x3f, 0x66, 0x3d, 0xca, 0x6b, 0x94, 0x1a, 0x2c,
-	0x0e, 0xf9, 0x22, 0xbc, 0xd5, 0x04, 0xb7, 0x75, 0x2d, 0xca, 0xd6, 0x63, 0xeb, 0x2c, 0x27, 0x1f,
-	0x7d, 0x76, 0x75, 0x81, 0x04, 0xba, 0xdf, 0xc6, 0xf9, 0xfe, 0x11, 0x83, 0x95, 0x86, 0xd3, 0x9b,
-	0xd9, 0x87, 0x22, 0x64, 0xfa, 0x8e, 0xe7, 0x4f, 0x5d, 0x94, 0xb0, 0xad, 0xdc, 0x84, 0xcc, 0x50,
-	0x1e, 0x9f, 0x3c, 0xfd, 0xb5, 0xe8, 0x29, 0x0b, 0x0c, 0x09, 0xd1, 0xca, 0x3b, 0x90, 0x75, 0x03,
-	0x9f, 0xc0, 0xd5, 0x9e, 0xc3, 0x71, 0x26, 0x78, 0xe5, 0x7b, 0x90, 0x16, 0x87, 0xb0, 0x9a, 0xe4,
-	0x9a, 0xd7, 0xce, 0xb5, 0xe7, 0x44, 0x2a, 0xa9, 0x9f, 0xc6, 0xa0, 0x40, 0xf4, 0x43, 0x7f, 0x97,
-	0x0e, 0x0e, 0xa8, 0xdb, 0xc6, 0x8b, 0x8c, 0xf7, 0xe7, 0x32, 0x9e, 0x23, 0xd5, 0x7b, 0xd4, 0xe5,
-	0x8b, 0xcc, 0x10, 0xd9, 0x52, 0xf6, 0x98, 0x93, 0xeb, 0x46, 0x5f, 0x3f, 0x30, 0x2d, 0xd3, 0x1f,
-	0xf3, 0x65, 0x2e, 0x47, 0x9f, 0xf2, 0xbc, 0x4d, 0x9c, 0xfc, 0x44, 0x91, 0xcc, 0x98, 0x51, 0x56,
-	0x61, 0x11, 0x63, 0x9d, 0xa7, 0x1f, 0x51, 0xbe, 0xfa, 0x2c, 0x09, 0x9a, 0xe8, 0xca, 0xf9, 0x69,
-	0x3d, 0x25, 0x07, 0x8b, 0x7b, 0x8d, 0x3b, 0x8d, 0xe6, 0xdd, 0x46, 0x61, 0x41, 0x59, 0x81, 0xdc,
-	0x5e, 0x83, 0xd4, 0xb4, 0xca, 0xb6, 0x56, 0xde, 0xa9, 0x15, 0x62, 0xca, 0x12, 0x86, 0x8b, 0xb0,
-	0x19, 0x57, 0x3f, 0x8e, 0x01, 0xb0, 0x03, 0x94, 0x8b, 0x7a, 0x1b, 0x52, 0x18, 0x4f, 0x7d, 0x71,
-	0x70, 0xcb, 0x9b, 0xcf, 0x47, 0xcd, 0x7a, 0x02, 0x2f, 0xb1, 0x1f, 0x4a, 0x84, 0xca, 0xf4, 0x0c,
-	0xe3, 0xf3, 0x33, 0x4c, 0x71, 0xe4, 0xec, 0xd4, 0x32, 0x90, 0xac, 0xb2, 0xaf, 0x98, 0x92, 0x85,
-	0x14, 0xce, 0xa9, 0xfa, 0x41, 0x21, 0x8e, 0xce, 0x97, 0xaf, 0xd6, 0xdb, 0x95, 0x66, 0xa3, 0x51,
-	0xab, 0x74, 0x6a, 0xd5, 0x42, 0x42, 0xbd, 0x06, 0xa9, 0xfa, 0x00, 0xad, 0x28, 0x6b, 0xcc, 0x03,
-	0x0e, 0xa9, 0x4b, 0x6d, 0x23, 0x70, 0xac, 0x89, 0x40, 0xfd, 0x04, 0x8d, 0xec, 0x3a, 0x23, 0xdb,
-	0x57, 0x36, 0xa7, 0x6e, 0xf1, 0xf2, 0xe6, 0x7a, 0xd4, 0x12, 0x38, 0xb0, 0xd4, 0x41, 0x94, 0xbc,
-	0xe5, 0x78, 0x98, 0xc2, 0x57, 0xe4, 0xd4, 0x65, 0x8b, 0xc9, 0x7d, 0xdd, 0x3d, 0xa2, 0xbe, 0xdc,
-	0x74, 0xd9, 0x52, 0xae, 0x43, 0x06, 0x4f, 0xa7, 0xe7, 0xd8, 0xd6, 0x98, 0xbb, 0x54, 0x46, 0x84,
-	0x59, 0x3c, 0x87, 0x5e, 0x13, 0x65, 0x24, 0xec, 0x55, 0xb6, 0x21, 0x7f, 0x80, 0xc9, 0xa4, 0xeb,
-	0x0c, 0x45, 0xcc, 0x4b, 0x3d, 0xd9, 0x01, 0xc5, 0xac, 0xca, 0x88, 0x6e, 0x0a, 0x30, 0xc9, 0x1d,
-	0x4c, 0x1a, 0x4a, 0x03, 0x96, 0x4f, 0x1c, 0x6b, 0x34, 0xa0, 0xa1, 0xad, 0x34, 0xb7, 0xf5, 0xe2,
-	0x93, 0x6d, 0xed, 0x73, 0x7c, 0x60, 0x6d, 0xe9, 0x64, 0xba, 0xa9, 0xdc, 0x81, 0x25, 0x7f, 0x30,
-	0x3c, 0xf4, 0x42, 0x73, 0x8b, 0xdc, 0xdc, 0x0b, 0x67, 0x6c, 0x18, 0x83, 0x07, 0xd6, 0xf2, 0xfe,
-	0x54, 0xab, 0xf8, 0xb3, 0x04, 0xe4, 0xa6, 0x66, 0xae, 0xb4, 0x21, 0x87, 0x39, 0x76, 0xa8, 0x1f,
-	0xf1, 0xb8, 0x2d, 0xcf, 0xe2, 0xc6, 0xb9, 0x56, 0x5d, 0x6a, 0x4d, 0x14, 0xc9, 0xb4, 0x15, 0xf5,
-	0xa3, 0x38, 0xe4, 0xa6, 0x3a, 0x95, 0x97, 0x21, 0x43, 0x5a, 0xa4, 0xbe, 0xaf, 0x75, 0x6a, 0x85,
-	0x85, 0xe2, 0xda, 0x87, 0x1f, 0x6d, 0xac, 0x72, 0x6b, 0xd3, 0x06, 0x5a, 0xae, 0x79, 0xc2, 0x5c,
-	0xef, 0x3a, 0x2c, 0x06, 0xd0, 0x58, 0xf1, 0x19, 0x84, 0x3e, 0x3d, 0x0f, 0x9d, 0x42, 0x92, 0xf6,
-	0xb6, 0x46, 0xd0, 0xfb, 0xe2, 0xd1, 0x48, 0xd2, 0xee, 0xeb, 0x2e, 0xed, 0x29, 0x2f, 0x40, 0x5a,
-	0x02, 0x13, 0xc5, 0x22, 0x02, 0x2f, 0xcf, 0x03, 0x27, 0x38, 0xd2, 0xde, 0xd1, 0xf6, 0x6b, 0x85,
-	0x64, 0x34, 0x8e, 0xb4, 0x2d, 0xfd, 0x84, 0x2a, 0xcf, 0xe3, 0x3d, 0xe1, 0xb0, 0x54, 0xf1, 0x0a,
-	0xc2, 0x9e, 0x7a, 0xcc, 0x1c, 0x43, 0x15, 0x57, 0x7f, 0xf1, 0x9b, 0xf5, 0x85, 0x3f, 0xfe, 0x76,
-	0xbd, 0x30, 0xdf, 0x5d, 0xfc, 0x77, 0x0c, 0x96, 0x66, 0x8e, 0x1c, 0x53, 0x64, 0xda, 0x76, 0x0c,
-	0x67, 0x28, 0xc2, 0x79, 0xa6, 0x0c, 0xe8, 0xa5, 0xe9, 0x86, 0x53, 0x41, 0x09, 0x91, 0x3d, 0xe8,
-	0x07, 0xb3, 0x09, 0xe9, 0x8d, 0x73, 0xfa, 0x53, 0x64, 0x4a, 0xba, 0x05, 0x4b, 0x3d, 0xdc, 0x47,
-	0xea, 0x76, 0x0d, 0xc7, 0x3e, 0x34, 0x8f, 0x64, 0xa8, 0x2e, 0x46, 0xd9, 0xac, 0x72, 0x20, 0xc9,
-	0x0b, 0x85, 0x0a, 0xc7, 0x7f, 0x8b, 0x64, 0x54, 0xdc, 0x87, 0xfc, 0xb4, 0x87, 0x2a, 0xcf, 0x02,
-	0x78, 0xe6, 0x8f, 0xa8, 0xe4, 0x37, 0x9c, 0x0d, 0x91, 0x2c, 0x93, 0x70, 0x76, 0xa3, 0xbc, 0x08,
-	0xc9, 0x01, 0x86, 0x32, 0x6e, 0x27, 0x55, 0xbe, 0xc8, 0x72, 0xe2, 0x5f, 0x3f, 0xbb, 0x9a, 0x73,
-	0xbc, 0xd2, 0x96, 0x69, 0xd1, 0x5d, 0xec, 0x22, 0x1c, 0xa0, 0x9e, 0x40, 0x92, 0x85, 0x0a, 0xe5,
-	0x19, 0x48, 0x96, 0xeb, 0x8d, 0x2a, 0xba, 0xda, 0x05, 0x3c, 0x9d, 0x25, 0xbe, 0x25, 0xac, 0x83,
-	0xf9, 0xae, 0x72, 0x15, 0xd2, 0xfb, 0xcd, 0x9d, 0xbd, 0x5d, 0xe6, 0x5e, 0x17, 0xb1, 0x7b, 0x25,
-	0xec, 0x16, 0x9b, 0x86, 0xb3, 0x49, 0x75, 0x76, 0x5b, 0x5b, 0x6d, 0x74, 0x2a, 0x05, 0xfb, 0x97,
-	0xc3, 0x7e, 0x3e, 0xe7, 0xe2, 0x05, 0x79, 0xaa, 0xd9, 0x50, 0xae, 0xfe, 0x2b, 0x0e, 0x4b, 0x84,
-	0xf1, 0x5b, 0xd7, 0x6f, 0x39, 0x96, 0x69, 0x8c, 0x95, 0x16, 0x64, 0x71, 0x5b, 0x7b, 0xe6, 0xd4,
-	0x9d, 0xda, 0x7c, 0x42, 0x12, 0x9c, 0x68, 0x05, 0xad, 0x4a, 0xa0, 0x49, 0x26, 0x46, 0x30, 0x58,
-	0xa6, 0x7a, 0xd4, 0xd2, 0xc7, 0x67, 0x65, 0xe3, 0xaa, 0xe4, 0xd2, 0x44, 0x40, 0x39, 0x73, 0xd4,
-	0xef, 0x77, 0x75, 0xdf, 0xa7, 0x83, 0xa1, 0x2f, 0xb2, 0x71, 0x12, 0x99, 0xa3, 0x7e, 0x5f, 0x93,
-	0x22, 0xe5, 0x4d, 0x48, 0x9f, 0xe2, 0xae, 0x38, 0xa7, 0x32, 0xe1, 0x9e, 0x6d, 0x57, 0x62, 0xd5,
-	0x0f, 0x59, 0x9e, 0x9d, 0x9b, 0x2c, 0xdb, 0xf5, 0x46, 0xb3, 0x51, 0x0b, 0x76, 0x5d, 0xf6, 0x37,
-	0xed, 0x86, 0x63, 0xb3, 0x1b, 0x03, 0xcd, 0x46, 0x77, 0x4b, 0xab, 0xef, 0xec, 0x11, 0xb6, 0xf3,
-	0x97, 0x10, 0x52, 0x08, 0x21, 0x5b, 0xba, 0x69, 0x31, 0x12, 0x78, 0x05, 0x12, 0x5a, 0x03, 0xb3,
-	0x4b, 0xb1, 0x80, 0xdd, 0xf9, 0xb0, 0x5b, 0xb3, 0xc7, 0x93, 0xcb, 0x34, 0x3f, 0xae, 0x7a, 0x0f,
-	0xf2, 0x7b, 0xc3, 0x1e, 0x06, 0x04, 0xe1, 0x98, 0xca, 0x06, 0x46, 0x34, 0xdd, 0xd5, 0x2d, 0x8b,
-	0x5a, 0xa6, 0x37, 0x90, 0x75, 0xc2, 0xb4, 0x08, 0xc9, 0xcd, 0xf9, 0xf7, 0x52, 0x72, 0x30, 0xa1,
-	0xa0, 0xfe, 0x04, 0x56, 0x70, 0x14, 0x5f, 0x47, 0xb2, 0x11, 0xd0, 0x8b, 0x4d, 0xc8, 0x1b, 0x81,
-	0xa8, 0x6b, 0xf6, 0xc4, 0x0d, 0x28, 0xaf, 0xe0, 0xfd, 0xcd, 0x85, 0xd0, 0x7a, 0x95, 0xe4, 0x42,
-	0x50, 0xbd, 0xc7, 0xd6, 0x39, 0x44, 0xa8, 0x70, 0xe8, 0x45, 0x84, 0x26, 0x5a, 0x08, 0x61, 0x32,
-	0xdc, 0xc5, 0x2c, 0xbd, 0x6f, 0xfa, 0x78, 0x2b, 0x7b, 0x82, 0x40, 0xa4, 0x48, 0x86, 0x09, 0x2a,
-	0xcc, 0xc1, 0x7f, 0x1a, 0x07, 0xe8, 0xe8, 0xde, 0xb1, 0x1c, 0x1a, 0xa9, 0x56, 0x58, 0x58, 0x9d,
-	0x45, 0xf0, 0x3b, 0x01, 0x88, 0x4c, 0xf0, 0xca, 0x1b, 0x01, 0x83, 0x10, 0xbc, 0x27, 0x5a, 0x51,
-	0x8e, 0x15, 0x45, 0x1d, 0x66, 0xc9, 0x0d, 0xbb, 0xff, 0xd4, 0x75, 0xb9, 0x17, 0xe1, 0xfd, 0xc7,
-	0x4f, 0xac, 0xb7, 0xb2, 0xe1, 0x9a, 0x65, 0x36, 0x7d, 0x2e, 0x6a, 0x90, 0xb9, 0x0d, 0xdd, 0x5e,
-	0x20, 0x13, 0xbd, 0x72, 0x01, 0x96, 0x5d, 0xbc, 0x66, 0x38, 0xeb, 0xae, 0xc7, 0xbb, 0xd5, 0x3f,
-	0xe3, 0x1e, 0xd4, 0x5b, 0xda, 0xae, 0x3c, 0xed, 0x2a, 0xa4, 0x0f, 0xf5, 0x81, 0x69, 0x8d, 0xe5,
-	0x35, 0x7b, 0x35, 0x6a, 0x88, 0x09, 0xbe, 0xa4, 0xf5, 0x7a, 0x48, 0x37, 0xbd, 0x2d, 0xae, 0x43,
-	0xa4, 0x2e, 0xa7, 0x15, 0xa3, 0x03, 0x1b, 0xe9, 0x43, 0x40, 0x2b, 0x78, 0x8b, 0xc5, 0x30, 0x57,
-	0xb7, 0xc3, 0xd5, 0x8a, 0x06, 0xdb, 0x05, 0x0c, 0xe4, 0xf4, 0x54, 0x1f, 0xcb, 0xf5, 0x06, 0x4d,
-	0x24, 0x11, 0x19, 0x51, 0x05, 0xd1, 0x1e, 0x2e, 0x99, 0x05, 0xe9, 0xaf, 0x9b, 0x0f, 0x91, 0x70,
-	0x11, 0x9d, 0x43, 0xed, 0xe2, 0x3b, 0x3c, 0xa4, 0x4c, 0xba, 0xbe, 0x11, 0xdb, 0x7f, 0x1d, 0x96,
-	0x66, 0xd6, 0xf9, 0x18, 0x9f, 0xab, 0xb7, 0xf6, 0xdf, 0x2c, 0x24, 0xe5, 0xd7, 0xff, 0x17, 0xd2,
-	0xea, 0x3f, 0x91, 0x5e, 0xb6, 0x1c, 0x7e, 0xad, 0xd8, 0xae, 0x46, 0xd7, 0xcf, 0x19, 0x5e, 0x8d,
-	0x1b, 0x8e, 0x25, 0x7d, 0x26, 0x92, 0xd0, 0x4c, 0xac, 0x30, 0x7e, 0xc0, 0xe1, 0x24, 0x54, 0xc4,
-	0xe8, 0x9b, 0x13, 0xcc, 0xac, 0x3b, 0x44, 0x1c, 0xdf, 0xd6, 0x25, 0x02, 0x42, 0xc4, 0x34, 0x59,
-	0x71, 0x36, 0x1c, 0x1d, 0xe0, 0x35, 0xed, 0xd3, 0x9e, 0xc0, 0x24, 0x39, 0x66, 0x29, 0x94, 0x32,
-	0x98, 0x5a, 0xc5, 0xf2, 0x32, 0xb0, 0xb9, 0x0a, 0x89, 0x4e, 0xa5, 0x85, 0x71, 0x67, 0x05, 0xa3,
-	0x46, 0x2e, 0x10, 0xa3, 0x88, 0xf5, 0xec, 0x55, 0x5b, 0x18, 0x6e, 0x66, 0x7a, 0x50, 0x54, 0x4c,
-	0xb2, 0x70, 0xa2, 0xfe, 0x2a, 0x06, 0x69, 0x91, 0xdc, 0x22, 0x57, 0xac, 0xc1, 0x62, 0x40, 0xb9,
-	0x44, 0xc6, 0x7d, 0xf1, 0xc9, 0xd9, 0xb1, 0x24, 0x93, 0x99, 0x38, 0xc7, 0x40, 0xaf, 0xf8, 0x36,
-	0xe4, 0xa7, 0x3b, 0xbe, 0xd1, 0x29, 0xfe, 0x18, 0x72, 0xcc, 0x51, 0x82, 0x2c, 0xb9, 0x09, 0x69,
-	0x91, 0x80, 0xe5, 0x55, 0x3f, 0x2b, 0x55, 0x4b, 0x24, 0x46, 0xba, 0x45, 0x91, 0xde, 0x83, 0xc2,
-	0x73, 0xfd, 0x6c, 0x77, 0x24, 0x01, 0x5c, 0xbd, 0x05, 0xc9, 0x16, 0x45, 0x0b, 0xcf, 0xc1, 0xa2,
-	0x8d, 0xa1, 0x67, 0x12, 0xd9, 0x24, 0x33, 0xe9, 0x51, 0x8c, 0x58, 0x69, 0xd6, 0x85, 0xf1, 0x0c,
-	0x37, 0x4f, 0x47, 0x7f, 0x0b, 0x6a, 0x6f, 0xf6, 0xad, 0x76, 0x20, 0x7f, 0x97, 0x9a, 0x47, 0x7d,
-	0x1f, 0x4f, 0x8c, 0x19, 0x7a, 0x15, 0x92, 0x43, 0x1a, 0x4e, 0x7e, 0x35, 0xd2, 0x75, 0xb0, 0x9f,
-	0x70, 0x14, 0xbb, 0x90, 0xa7, 0x5c, 0x5b, 0x3e, 0x77, 0xc8, 0x96, 0xfa, 0xfb, 0x38, 0x2c, 0xd7,
-	0x3d, 0x6f, 0xa4, 0x63, 0x29, 0x21, 0xa3, 0xe0, 0xbb, 0xb3, 0xa5, 0xd0, 0xf5, 0xc8, 0x15, 0xce,
-	0xa8, 0xcc, 0x96, 0x43, 0x32, 0x72, 0xc5, 0xc3, 0xc8, 0xa5, 0x3e, 0x8a, 0x05, 0x75, 0xd0, 0xb5,
-	0xa9, 0x7b, 0x53, 0x5c, 0x45, 0x27, 0xba, 0x34, 0x6d, 0x89, 0xee, 0xd9, 0xc7, 0xb6, 0x73, 0x6a,
-	0x63, 0xa2, 0xc5, 0xba, 0xa8, 0x51, 0xbb, 0x8b, 0x9e, 0x76, 0x19, 0x41, 0xca, 0x0c, 0x88, 0x50,
-	0x9b, 0x9e, 0x32, 0x4b, 0xad, 0x5a, 0xa3, 0x5a, 0x6f, 0xdc, 0xc6, 0xf4, 0xf6, 0xb8, 0xa5, 0x16,
-	0xc5, 0x74, 0x66, 0x1f, 0xe1, 0x76, 0xa7, 0xeb, 0xed, 0xf6, 0x1e, 0x67, 0xaa, 0x4f, 0x23, 0xea,
-	0xe2, 0x0c, 0x8a, 0x35, 0x90, 0xa6, 0x22, 0x88, 0x65, 0x52, 0x04, 0x25, 0x23, 0x40, 0x2c, 0x99,
-	0x62, 0x00, 0x11, 0x1e, 0xfe, 0xb7, 0x38, 0x14, 0x34, 0xc3, 0xa0, 0x43, 0x9f, 0xf5, 0x4b, 0x76,
-	0xd2, 0xc1, 0x9b, 0xcc, 0xbe, 0x4c, 0xce, 0xb6, 0x98, 0x5b, 0xdc, 0x8c, 0x7c, 0x0b, 0x9b, 0xd3,
-	0x2b, 0x11, 0xc7, 0xa2, 0x5a, 0x6f, 0x60, 0x7a, 0xec, 0x7d, 0x44, 0xc8, 0x48, 0x68, 0xa9, 0xf8,
-	0x65, 0x0c, 0x2e, 0x46, 0x20, 0x94, 0xd7, 0x21, 0xe9, 0xa2, 0x58, 0x1e, 0xcf, 0xda, 0x93, 0x2a,
-	0x55, 0xa6, 0x4a, 0x38, 0x52, 0x59, 0x07, 0xd0, 0x47, 0xbe, 0xa3, 0xf3, 0xf1, 0xf9, 0xc1, 0x64,
-	0xc8, 0x94, 0x44, 0xb9, 0x8b, 0xd1, 0x9a, 0x1a, 0xae, 0x2c, 0xf6, 0x72, 0x9b, 0xb7, 0xfe, 0xdb,
-	0xd9, 0x97, 0xda, 0xdc, 0x0c, 0x91, 0xe6, 0x8a, 0x25, 0xac, 0x13, 0xf8, 0x17, 0xf3, 0x68, 0xa4,
-	0x14, 0x3a, 0x9f, 0x74, 0x9e, 0xf0, 0x6f, 0xe6, 0x28, 0xba, 0x75, 0x14, 0x38, 0x0a, 0x7e, 0xaa,
-	0x1f, 0x63, 0x2e, 0xaa, 0xdd, 0xf7, 0xa9, 0x6b, 0xeb, 0x56, 0x45, 0x53, 0x6a, 0x53, 0x11, 0x52,
-	0xac, 0xf6, 0xa5, 0xc8, 0xf7, 0x8b, 0x50, 0xa3, 0x54, 0xd1, 0x22, 0x62, 0x24, 0xb2, 0x83, 0x91,
-	0x6b, 0xc9, 0xb7, 0x30, 0xce, 0x0e, 0xf6, 0xc8, 0x0e, 0x61, 0x32, 0xf6, 0x90, 0x14, 0x44, 0xa4,
-	0xc4, 0x93, 0x1f, 0x31, 0xa7, 0x06, 0xf8, 0xee, 0xa3, 0xd2, 0xab, 0x00, 0x93, 0x59, 0xe3, 0x51,
-	0xa5, 0x2a, 0x5b, 0xed, 0xf6, 0x0e, 0x5e, 0x0f, 0x4e, 0xa6, 0x27, 0x5d, 0x5c, 0xac, 0xfe, 0x2e,
-	0x06, 0x99, 0x8a, 0x26, 0xb3, 0xca, 0x16, 0x14, 0x78, 0x2c, 0x31, 0xa8, 0xeb, 0x77, 0xe9, 0xfd,
-	0xa1, 0xe9, 0x8e, 0x65, 0x38, 0x38, 0x9b, 0x76, 0x2e, 0x33, 0xad, 0x0a, 0x2a, 0xd5, 0xb8, 0x8e,
-	0x42, 0x20, 0x4f, 0xe5, 0x12, 0xbb, 0x86, 0x1e, 0x04, 0xe7, 0xf5, 0xb3, 0xb7, 0x42, 0x50, 0xb2,
-	0x49, 0x1b, 0x8b, 0xf6, 0xc0, 0x48, 0x45, 0xf7, 0xd4, 0x7d, 0xb8, 0xd8, 0x74, 0x8d, 0x3e, 0x92,
-	0x23, 0x31, 0xa8, 0x9c, 0xf2, 0x2d, 0x58, 0xf3, 0x91, 0x04, 0x75, 0xfb, 0xa6, 0xe7, 0xb3, 0x27,
-	0x58, 0xf4, 0x0d, 0x6a, 0xb3, 0xfe, 0x2e, 0x7f, 0x2a, 0x95, 0xc5, 0xca, 0x15, 0x86, 0xd9, 0x16,
-	0x10, 0x12, 0x20, 0x76, 0x18, 0x40, 0xad, 0x63, 0xad, 0x83, 0x9d, 0x55, 0x7a, 0xa8, 0x8f, 0x2c,
-	0x24, 0xdc, 0x6f, 0x01, 0x58, 0xce, 0x51, 0xf7, 0xdc, 0x91, 0x3c, 0x8b, 0x68, 0xf1, 0xa9, 0x7e,
-	0x1f, 0x0a, 0x55, 0xd3, 0x1b, 0xea, 0x3e, 0x4e, 0x53, 0x56, 0x61, 0xca, 0x6d, 0x28, 0xf4, 0x29,
-	0xd2, 0xe1, 0x03, 0xaa, 0x63, 0x4a, 0xa5, 0xae, 0xe9, 0xf4, 0xce, 0xb5, 0xa5, 0x2b, 0xa1, 0x56,
-	0x8b, 0x2b, 0xa9, 0x5f, 0x21, 0x01, 0x60, 0xcf, 0x5c, 0xd2, 0xee, 0x2b, 0x70, 0xc1, 0xb3, 0xf5,
-	0xa1, 0xd7, 0x77, 0xfc, 0xae, 0x69, 0xfb, 0xec, 0x5d, 0xd7, 0x92, 0x54, 0xba, 0x10, 0x74, 0xd4,
-	0xa5, 0x1c, 0x43, 0xbb, 0x72, 0x4c, 0xe9, 0xb0, 0xeb, 0x58, 0xbd, 0x6e, 0xd0, 0x29, 0xde, 0x72,
-	0x11, 0xcd, 0x7a, 0x9a, 0x56, 0xaf, 0x1d, 0xc8, 0x95, 0x32, 0xac, 0xb3, 0x1d, 0xc0, 0x4d, 0x72,
-	0x31, 0x6c, 0x74, 0x0f, 0x1d, 0xb7, 0xeb, 0x59, 0xce, 0x29, 0x7e, 0x58, 0xf8, 0x43, 0xdd, 0xa0,
-	0x4e, 0x29, 0x22, 0xaa, 0x26, 0x40, 0x5b, 0x8e, 0xdb, 0xc6, 0xbe, 0xad, 0x00, 0xc1, 0x58, 0xc2,
-	0x64, 0xd9, 0xbe, 0x69, 0x1c, 0x07, 0x2c, 0x21, 0x94, 0x76, 0x50, 0x88, 0x81, 0x72, 0x89, 0x5a,
-	0xd4, 0xe0, 0xe7, 0xc5, 0x51, 0x29, 0x8e, 0xca, 0x07, 0x42, 0x06, 0x52, 0xff, 0x17, 0xb2, 0x2d,
-	0x4b, 0x37, 0xf8, 0x8b, 0x39, 0x2b, 0x1e, 0x30, 0x03, 0x32, 0x27, 0xc0, 0x55, 0x8b, 0xe8, 0x98,
-	0x25, 0xd3, 0x22, 0xf5, 0x5d, 0x80, 0xf7, 0x1d, 0xd3, 0xee, 0x38, 0xc7, 0xd4, 0xe6, 0x8f, 0x8b,
-	0xa7, 0x8e, 0x7b, 0x2c, 0x8f, 0x12, 0x89, 0xa3, 0x68, 0x71, 0xa2, 0xac, 0xdb, 0x48, 0x8c, 0xdd,
-	0xf0, 0x8d, 0x4d, 0x34, 0x59, 0x72, 0x49, 0x13, 0xc7, 0xf1, 0x31, 0x5e, 0x6c, 0x40, 0xda, 0xd0,
-	0xbb, 0xc1, 0xcd, 0xcb, 0x97, 0xb3, 0xe8, 0xa1, 0xa9, 0x8a, 0x76, 0x87, 0x8e, 0x49, 0xca, 0xd0,
-	0xf1, 0x87, 0x65, 0x5f, 0x44, 0xb0, 0xfb, 0xc2, 0xcd, 0xe4, 0x45, 0xf6, 0xc5, 0x0b, 0x85, 0x12,
-	0x82, 0xca, 0xec, 0x17, 0x03, 0x6c, 0x5e, 0x82, 0xba, 0x7d, 0xdd, 0xeb, 0x0b, 0xae, 0x5a, 0x5e,
-	0x46, 0x24, 0x08, 0xe4, 0x36, 0x4a, 0x09, 0x08, 0x34, 0xfb, 0xc6, 0x30, 0x92, 0xbb, 0x87, 0x6b,
-	0xe8, 0xfa, 0x7c, 0x11, 0xb2, 0xf4, 0x8b, 0xbc, 0x3f, 0x93, 0xa5, 0xca, 0x42, 0x08, 0xee, 0x85,
-	0x12, 0xf5, 0x2f, 0x31, 0xc8, 0x31, 0x9b, 0xe6, 0xa1, 0x69, 0xb0, 0x6c, 0xf9, 0xcd, 0x23, 0x3d,
-	0x86, 0x3a, 0xc3, 0x73, 0xe5, 0xda, 0x78, 0xa8, 0xab, 0xb4, 0x09, 0x61, 0x32, 0xe5, 0x3d, 0x0c,
-	0xf2, 0x3c, 0x5b, 0xcb, 0x20, 0xaf, 0x7e, 0x7d, 0x5e, 0x97, 0x53, 0x94, 0x7a, 0xfc, 0x2c, 0x27,
-	0xb3, 0xe3, 0xab, 0xcc, 0x93, 0x69, 0x11, 0xfb, 0xa7, 0x83, 0x61, 0x73, 0xa7, 0x90, 0xff, 0x74,
-	0xa8, 0x34, 0x08, 0x4a, 0xd4, 0x3f, 0xc5, 0x60, 0xa9, 0x66, 0x1b, 0xee, 0x98, 0x07, 0x49, 0x76,
-	0x10, 0x6b, 0x90, 0xc5, 0x92, 0xc0, 0x1b, 0x7b, 0x58, 0x37, 0x07, 0x6f, 0x9a, 0xa1, 0x40, 0xa9,
-	0x43, 0x16, 0xd3, 0x81, 0xe3, 0x9a, 0x7e, 0x7f, 0x20, 0xb9, 0x71, 0x74, 0x60, 0x9e, 0xb6, 0x59,
-	0xd2, 0x02, 0x15, 0x32, 0xd1, 0x0e, 0x42, 0x71, 0x82, 0x4f, 0x96, 0x87, 0x62, 0xac, 0xe2, 0x2d,
-	0x2c, 0xd8, 0x90, 0xf5, 0x76, 0x59, 0x1d, 0xc4, 0xd7, 0x81, 0x05, 0xad, 0x94, 0xb1, 0xda, 0x4e,
-	0x55, 0x21, 0x1b, 0x1a, 0x63, 0x2f, 0xc9, 0x5a, 0xad, 0xdd, 0xbd, 0xb1, 0x79, 0xb3, 0x7b, 0xbb,
-	0xb2, 0x8b, 0x81, 0x59, 0x30, 0x81, 0x3f, 0xe0, 0x9a, 0x76, 0x85, 0x0f, 0x4a, 0xe2, 0x84, 0xce,
-	0xe5, 0xe2, 0x8d, 0x0f, 0xa8, 0x5d, 0x52, 0x38, 0x17, 0x0b, 0x02, 0x8c, 0xda, 0xb1, 0xae, 0x68,
-	0x6a, 0x37, 0xf5, 0xa2, 0x9e, 0x38, 0xf3, 0x45, 0x3d, 0xf9, 0x9d, 0xbc, 0xa8, 0xbf, 0xfc, 0x55,
-	0x02, 0xb2, 0x61, 0x25, 0xca, 0x5c, 0x86, 0x31, 0xad, 0x05, 0xf1, 0x46, 0x10, 0xca, 0x1b, 0x9c,
-	0x63, 0x65, 0xb5, 0x9d, 0x9d, 0x66, 0x45, 0x63, 0x0f, 0xd2, 0xef, 0x09, 0x2a, 0x16, 0x02, 0x34,
-	0x8c, 0x1d, 0xec, 0xd0, 0x7b, 0x8a, 0x3a, 0xa1, 0x62, 0x0f, 0xe4, 0x4b, 0x44, 0x88, 0x0a, 0x78,
-	0xd8, 0xf3, 0x90, 0xd1, 0xda, 0xed, 0xfa, 0xed, 0x06, 0x5a, 0x7a, 0x18, 0x2b, 0x3e, 0x85, 0xa0,
-	0x0b, 0x13, 0x53, 0x48, 0x21, 0x8e, 0x6c, 0xb4, 0xc4, 0x50, 0x95, 0x4a, 0xad, 0xc5, 0xc6, 0x7b,
-	0x10, 0x9f, 0x47, 0x71, 0x02, 0xc2, 0x5f, 0x15, 0xb3, 0x2d, 0x52, 0x6b, 0x69, 0x84, 0x8d, 0xf8,
-	0x30, 0x3e, 0x37, 0xaf, 0x96, 0x4b, 0x87, 0xba, 0xcb, 0xc6, 0x5c, 0x0f, 0x5e, 0xd7, 0x1f, 0x24,
-	0xc4, 0xcb, 0xd3, 0xa4, 0xfc, 0xc6, 0xfd, 0x1d, 0xb3, 0xd1, 0xda, 0x1d, 0x8d, 0x74, 0xb8, 0x99,
-	0xc4, 0xdc, 0x68, 0x6d, 0xf6, 0x28, 0xc2, 0xac, 0xe0, 0xea, 0xc8, 0x5e, 0xa3, 0xc1, 0x57, 0x97,
-	0x9c, 0x5b, 0x1d, 0x19, 0xd9, 0x36, 0xc3, 0x5c, 0xc3, 0xa4, 0xdc, 0xdc, 0x6d, 0xed, 0xd4, 0x3a,
-	0xb5, 0xc2, 0xc3, 0xe4, 0xdc, 0x84, 0x2a, 0xce, 0x60, 0x68, 0x51, 0x5f, 0x2c, 0xaf, 0xbd, 0xbd,
-	0xd7, 0xe1, 0x8f, 0xff, 0x0f, 0x52, 0xf3, 0x03, 0xf6, 0x47, 0x7e, 0x8f, 0x91, 0xdf, 0x8d, 0x90,
-	0x8d, 0x3e, 0x4c, 0x09, 0x12, 0x10, 0x62, 0x04, 0x15, 0x65, 0x76, 0x48, 0xed, 0x7d, 0xf1, 0x7f,
-	0x82, 0x07, 0xe9, 0x39, 0x3b, 0x84, 0xde, 0xc3, 0x60, 0x8c, 0x84, 0x35, 0x7c, 0x58, 0x0b, 0xbb,
-	0x5e, 0xfe, 0x01, 0x64, 0x82, 0x80, 0x81, 0xbb, 0x93, 0xbe, 0xdb, 0x24, 0x77, 0x6a, 0x04, 0x8f,
-	0x9e, 0xef, 0x4e, 0xd0, 0x73, 0x57, 0x44, 0xdc, 0x0d, 0x58, 0xdc, 0xd5, 0x1a, 0xda, 0x6d, 0x04,
-	0xc8, 0x87, 0xbd, 0x00, 0x20, 0xbd, 0xbe, 0x58, 0x90, 0x03, 0x84, 0x36, 0xcb, 0x6b, 0x8f, 0x3e,
-	0x5f, 0x5f, 0xf8, 0x14, 0xff, 0xbe, 0xfc, 0x7c, 0x3d, 0xf6, 0xe0, 0x8b, 0xf5, 0xd8, 0x23, 0xfc,
-	0xfb, 0x04, 0xff, 0xfe, 0x8e, 0x7f, 0x07, 0x69, 0xce, 0xc8, 0xde, 0xf8, 0x4f, 0x00, 0x00, 0x00,
-	0xff, 0xff, 0x0e, 0xef, 0x48, 0x56, 0xe9, 0x1e, 0x00, 0x00,
+	// 3349 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x59, 0x4d, 0x6c, 0x1b, 0xd7,
+	0x11, 0x36, 0x7f, 0x45, 0x0e, 0x49, 0x99, 0x5e, 0x3b, 0x8e, 0xcc, 0x38, 0xb2, 0xb3, 0x89, 0x13,
+	0xe7, 0xa7, 0x4c, 0xac, 0xa4, 0x85, 0x93, 0xb4, 0x71, 0x96, 0x3f, 0xb2, 0x19, 0x4b, 0x14, 0xf1,
+	0x28, 0xca, 0x08, 0x0a, 0x94, 0x58, 0x91, 0x4f, 0xe2, 0x46, 0xcb, 0x5d, 0x76, 0x77, 0x29, 0x99,
+	0x2d, 0x0a, 0xb8, 0xbd, 0xb4, 0xc8, 0xa9, 0xf7, 0x22, 0x08, 0x8a, 0x16, 0xbd, 0xf5, 0xd0, 0x53,
+	0x81, 0x9e, 0x7c, 0xf4, 0x31, 0x45, 0x81, 0x22, 0x68, 0x81, 0xa0, 0x49, 0x8f, 0xbd, 0x04, 0xe8,
+	0x21, 0x87, 0xf6, 0xd0, 0x79, 0x3f, 0xfb, 0x43, 0x7a, 0xad, 0x28, 0x4d, 0x0e, 0x82, 0xf6, 0xcd,
+	0xfb, 0x66, 0xde, 0xdf, 0xbc, 0x99, 0x6f, 0x1e, 0xa1, 0xe0, 0xcd, 0x26, 0xd4, 0xad, 0x4e, 0x1c,
+	0xdb, 0xb3, 0x15, 0x65, 0x68, 0x0f, 0x0e, 0xa8, 0x53, 0x75, 0x8f, 0x74, 0x67, 0x7c, 0x60, 0x78,
+	0xd5, 0xc3, 0x6b, 0x95, 0x0b, 0x9e, 0x31, 0xa6, 0xae, 0xa7, 0x8f, 0x27, 0x2f, 0x07, 0x5f, 0x02,
+	0x5e, 0x79, 0x7c, 0x38, 0x75, 0x74, 0xcf, 0xb0, 0xad, 0x97, 0xfd, 0x0f, 0xd9, 0x71, 0x6e, 0xdf,
+	0xde, 0xb7, 0xf9, 0xe7, 0xcb, 0xec, 0x4b, 0x48, 0xd5, 0x4b, 0xb0, 0xb4, 0x43, 0x1d, 0x17, 0x61,
+	0xca, 0x39, 0xc8, 0x18, 0xd6, 0x90, 0xde, 0x5d, 0x49, 0x5c, 0x4e, 0x5c, 0x4d, 0x13, 0xd1, 0x50,
+	0x7f, 0x9d, 0x80, 0x82, 0x66, 0x59, 0xb6, 0xc7, 0x6d, 0xb9, 0x8a, 0x02, 0x69, 0x4b, 0x1f, 0x53,
+	0x0e, 0xca, 0x13, 0xfe, 0xad, 0xd4, 0x21, 0x6b, 0xea, 0xbb, 0xd4, 0x74, 0x57, 0x92, 0x97, 0x53,
+	0x57, 0x0b, 0x6b, 0x2f, 0x56, 0x1f, 0x9e, 0x73, 0x35, 0x62, 0xa4, 0xba, 0xc1, 0xd1, 0x4d, 0xcb,
+	0x73, 0x66, 0x44, 0xaa, 0x56, 0x5e, 0x87, 0x42, 0x44, 0xac, 0x94, 0x21, 0x75, 0x40, 0x67, 0x72,
+	0x18, 0xf6, 0xc9, 0xe6, 0x77, 0xa8, 0x9b, 0x53, 0x8a, 0x83, 0x30, 0x99, 0x68, 0xbc, 0x91, 0xbc,
+	0x9e, 0x50, 0xdf, 0x85, 0x3c, 0xa1, 0xae, 0x3d, 0x75, 0x06, 0xd4, 0x55, 0x9e, 0x87, 0xbc, 0xa5,
+	0x5b, 0x76, 0x7f, 0x30, 0x99, 0xba, 0x5c, 0x3d, 0x55, 0x2b, 0x7e, 0xf6, 0xc9, 0xa5, 0x5c, 0x1b,
+	0x85, 0xf5, 0x4e, 0xcf, 0x25, 0x39, 0xd6, 0x5d, 0xc7, 0x5e, 0xe5, 0x29, 0x28, 0x8e, 0xe9, 0xd8,
+	0x76, 0x66, 0xfd, 0xdd, 0x99, 0x47, 0x5d, 0x6e, 0x38, 0x45, 0x0a, 0x42, 0x56, 0x63, 0x22, 0xf5,
+	0x97, 0x09, 0x38, 0xe7, 0xdb, 0x26, 0xf4, 0x87, 0x53, 0xc3, 0xa1, 0x63, 0x6a, 0x79, 0xae, 0xf2,
+	0x6d, 0x5c, 0xb3, 0x31, 0x36, 0x3c, 0x31, 0x46, 0x61, 0xed, 0xc9, 0xb8, 0x35, 0x07, 0xb3, 0x22,
+	0x12, 0xac, 0x68, 0x50, 0x74, 0xa8, 0x4b, 0x9d, 0x43, 0xb1, 0x13, 0x7c, 0xc8, 0x2f, 0x55, 0x9e,
+	0x53, 0x51, 0xd7, 0x21, 0xd7, 0x31, 0x75, 0x6f, 0xcf, 0x76, 0xc6, 0x8a, 0x0a, 0x45, 0xdd, 0x19,
+	0x8c, 0x0c, 0x8f, 0x0e, 0xbc, 0xa9, 0xe3, 0x9f, 0xca, 0x9c, 0x4c, 0x39, 0x0f, 0x49, 0x5b, 0x0c,
+	0x94, 0xaf, 0x65, 0x71, 0x27, 0x92, 0x5b, 0x5d, 0x82, 0x12, 0xf5, 0x4d, 0x38, 0xd3, 0x31, 0xa7,
+	0xfb, 0x86, 0xd5, 0xa0, 0xee, 0xc0, 0x31, 0x26, 0xcc, 0x3a, 0x3b, 0x5e, 0xe6, 0x7c, 0xfe, 0xf1,
+	0xb2, 0xef, 0xe0, 0xc8, 0x93, 0xe1, 0x91, 0xab, 0x3f, 0x4f, 0xc2, 0x99, 0xa6, 0x85, 0xca, 0x34,
+	0xaa, 0x7d, 0x05, 0x96, 0x29, 0x17, 0xf6, 0x0f, 0x85, 0x53, 0x49, 0x3b, 0x25, 0x21, 0xf5, 0x3d,
+	0xad, 0xb5, 0xe0, 0x2f, 0xd7, 0xe2, 0x96, 0xff, 0x90, 0xf5, 0x38, 0xaf, 0x51, 0x9a, 0xb0, 0x34,
+	0xe1, 0x8b, 0x70, 0x57, 0x52, 0xdc, 0xd6, 0x95, 0x38, 0x5b, 0x0f, 0xad, 0xb3, 0x96, 0x7e, 0xf0,
+	0xc9, 0xa5, 0x53, 0xc4, 0xd7, 0xfd, 0x3a, 0xce, 0xf7, 0xcf, 0x04, 0x9c, 0x6e, 0xdb, 0xc3, 0xb9,
+	0x7d, 0xa8, 0x40, 0x6e, 0x64, 0xbb, 0x5e, 0xe4, 0xa2, 0x04, 0x6d, 0xe5, 0x3a, 0xe4, 0x26, 0xf2,
+	0xf8, 0xe4, 0xe9, 0x5f, 0x8c, 0x9f, 0xb2, 0xc0, 0x90, 0x00, 0xad, 0xbc, 0x09, 0x79, 0xc7, 0xf7,
+	0x09, 0x5c, 0xed, 0x09, 0x1c, 0x27, 0xc4, 0x2b, 0xdf, 0x83, 0xac, 0x38, 0x84, 0x95, 0x34, 0xd7,
+	0xbc, 0x72, 0xa2, 0x3d, 0x27, 0x52, 0x49, 0xfd, 0x38, 0x01, 0x65, 0xa2, 0xef, 0x79, 0x9b, 0x74,
+	0xbc, 0x4b, 0x9d, 0x2e, 0x5e, 0x64, 0xbc, 0x3f, 0xe7, 0xf1, 0x1c, 0xa9, 0x3e, 0xa4, 0x0e, 0x5f,
+	0x64, 0x8e, 0xc8, 0x96, 0xd2, 0x63, 0x4e, 0xae, 0x0f, 0x46, 0xfa, 0xae, 0x61, 0x1a, 0xde, 0x8c,
+	0x2f, 0x73, 0x39, 0xfe, 0x94, 0x17, 0x6d, 0xe2, 0xe4, 0x43, 0x45, 0x32, 0x67, 0x46, 0x59, 0x81,
+	0x25, 0x8c, 0x75, 0xae, 0xbe, 0x4f, 0xf9, 0xea, 0xf3, 0xc4, 0x6f, 0xa2, 0x2b, 0x17, 0xa3, 0x7a,
+	0x4a, 0x01, 0x96, 0x7a, 0xed, 0xdb, 0xed, 0xad, 0x3b, 0xed, 0xf2, 0x29, 0xe5, 0x34, 0x14, 0x7a,
+	0x6d, 0xd2, 0xd4, 0xea, 0xb7, 0xb4, 0xda, 0x46, 0xb3, 0x9c, 0x50, 0x4a, 0x18, 0x2e, 0x82, 0x66,
+	0x52, 0xfd, 0x30, 0x01, 0xc0, 0x0e, 0x50, 0x2e, 0xea, 0x0d, 0xc8, 0x60, 0x3c, 0xf5, 0xc4, 0xc1,
+	0x2d, 0xaf, 0x3d, 0x13, 0x37, 0xeb, 0x10, 0x5e, 0x65, 0xff, 0x28, 0x11, 0x2a, 0xd1, 0x19, 0x26,
+	0x17, 0x67, 0x98, 0xe1, 0xc8, 0xf9, 0xa9, 0xe5, 0x20, 0xdd, 0x60, 0x5f, 0x09, 0x25, 0x0f, 0x19,
+	0x9c, 0x53, 0xe3, 0xdd, 0x72, 0x12, 0x9d, 0xaf, 0xd8, 0x68, 0x75, 0xeb, 0x5b, 0xed, 0x76, 0xb3,
+	0xbe, 0xdd, 0x6c, 0x94, 0x53, 0xea, 0x15, 0xc8, 0xb4, 0xc6, 0x68, 0x45, 0xb9, 0xc8, 0x3c, 0x60,
+	0x8f, 0x3a, 0xd4, 0x1a, 0xf8, 0x8e, 0x15, 0x0a, 0xd4, 0x8f, 0xd0, 0xc8, 0xa6, 0x3d, 0xb5, 0x3c,
+	0x65, 0x2d, 0x72, 0x8b, 0x97, 0xd7, 0x56, 0xe3, 0x96, 0xc0, 0x81, 0xd5, 0x6d, 0x44, 0xc9, 0x5b,
+	0x8e, 0x87, 0x29, 0x7c, 0x45, 0x4e, 0x5d, 0xb6, 0x98, 0xdc, 0xd3, 0x9d, 0x7d, 0xea, 0xc9, 0x4d,
+	0x97, 0x2d, 0xe5, 0x2a, 0xe4, 0xf0, 0x74, 0x86, 0xb6, 0x65, 0xce, 0xb8, 0x4b, 0xe5, 0x44, 0x98,
+	0xc5, 0x73, 0x18, 0x6e, 0xa1, 0x8c, 0x04, 0xbd, 0xca, 0x2d, 0x28, 0xee, 0x62, 0x32, 0xe9, 0xdb,
+	0x13, 0x11, 0xf3, 0x32, 0x8f, 0x76, 0x40, 0x31, 0xab, 0x1a, 0xa2, 0xb7, 0x04, 0x98, 0x14, 0x76,
+	0xc3, 0x86, 0xd2, 0x86, 0xe5, 0x43, 0xdb, 0x9c, 0x8e, 0x69, 0x60, 0x2b, 0xcb, 0x6d, 0x3d, 0xf7,
+	0x68, 0x5b, 0x3b, 0x1c, 0xef, 0x5b, 0x2b, 0x1d, 0x46, 0x9b, 0xca, 0x6d, 0x28, 0x79, 0xe3, 0xc9,
+	0x9e, 0x1b, 0x98, 0x5b, 0xe2, 0xe6, 0x9e, 0x3d, 0x66, 0xc3, 0x18, 0xdc, 0xb7, 0x56, 0xf4, 0x22,
+	0xad, 0xca, 0xcf, 0x52, 0x50, 0x88, 0xcc, 0x5c, 0xe9, 0x42, 0x01, 0x73, 0xec, 0x44, 0xdf, 0xe7,
+	0x71, 0x5b, 0x9e, 0xc5, 0xb5, 0x13, 0xad, 0xba, 0xda, 0x09, 0x15, 0x49, 0xd4, 0x8a, 0xfa, 0x41,
+	0x12, 0x0a, 0x91, 0x4e, 0xe5, 0x05, 0xc8, 0x91, 0x0e, 0x69, 0xed, 0x68, 0xdb, 0xcd, 0xf2, 0xa9,
+	0xca, 0xc5, 0xf7, 0x3f, 0xb8, 0xbc, 0xc2, 0xad, 0x45, 0x0d, 0x74, 0x1c, 0xe3, 0x90, 0xb9, 0xde,
+	0x55, 0x58, 0xf2, 0xa1, 0x89, 0xca, 0x13, 0x08, 0x7d, 0x7c, 0x11, 0x1a, 0x41, 0x92, 0xee, 0x2d,
+	0x8d, 0xa0, 0xf7, 0x25, 0xe3, 0x91, 0xa4, 0x3b, 0xd2, 0x1d, 0x3a, 0x54, 0x9e, 0x85, 0xac, 0x04,
+	0xa6, 0x2a, 0x15, 0x04, 0x9e, 0x5f, 0x04, 0x86, 0x38, 0xd2, 0xdd, 0xd0, 0x76, 0x9a, 0xe5, 0x74,
+	0x3c, 0x8e, 0x74, 0x4d, 0xfd, 0x90, 0x2a, 0xcf, 0xe0, 0x3d, 0xe1, 0xb0, 0x4c, 0xe5, 0x02, 0xc2,
+	0x1e, 0x7b, 0xc8, 0x1c, 0x43, 0x55, 0x56, 0x7e, 0xf1, 0x9b, 0xd5, 0x53, 0x7f, 0xfa, 0xed, 0x6a,
+	0x79, 0xb1, 0xbb, 0xf2, 0xdf, 0x04, 0x94, 0xe6, 0x8e, 0x1c, 0x53, 0x64, 0xd6, 0xb2, 0x07, 0xf6,
+	0x44, 0x84, 0xf3, 0x5c, 0x0d, 0xd0, 0x4b, 0xb3, 0x6d, 0xbb, 0x8e, 0x12, 0x22, 0x7b, 0xd0, 0x0f,
+	0xe6, 0x13, 0xd2, 0xab, 0x27, 0xf4, 0xa7, 0xd8, 0x94, 0x74, 0x03, 0x4a, 0x43, 0xdc, 0x47, 0xea,
+	0xf4, 0x07, 0xb6, 0xb5, 0x67, 0xec, 0xcb, 0x50, 0x5d, 0x89, 0xb3, 0xd9, 0xe0, 0x40, 0x52, 0x14,
+	0x0a, 0x75, 0x8e, 0xff, 0x1a, 0xc9, 0xa8, 0xb2, 0x03, 0xc5, 0xa8, 0x87, 0x2a, 0x4f, 0x02, 0xb8,
+	0xc6, 0x8f, 0xa8, 0xe4, 0x37, 0x9c, 0x0d, 0x91, 0x3c, 0x93, 0x70, 0x76, 0xa3, 0x3c, 0x07, 0xe9,
+	0x31, 0x86, 0x32, 0x6e, 0x27, 0x53, 0x3b, 0xcb, 0x72, 0xe2, 0xdf, 0x3e, 0xb9, 0x54, 0xb0, 0xdd,
+	0xea, 0xba, 0x61, 0xd2, 0x4d, 0xec, 0x22, 0x1c, 0xa0, 0x1e, 0x42, 0x9a, 0x85, 0x0a, 0xe5, 0x09,
+	0x48, 0xd7, 0x5a, 0xed, 0x06, 0xba, 0xda, 0x19, 0x3c, 0x9d, 0x12, 0xdf, 0x12, 0xd6, 0xc1, 0x7c,
+	0x57, 0xb9, 0x04, 0xd9, 0x9d, 0xad, 0x8d, 0xde, 0x26, 0x73, 0xaf, 0xb3, 0xd8, 0x7d, 0x3a, 0xe8,
+	0x16, 0x9b, 0x86, 0xb3, 0xc9, 0x6c, 0x6f, 0x76, 0xd6, 0xbb, 0xe8, 0x54, 0x0a, 0xf6, 0x2f, 0x07,
+	0xfd, 0x7c, 0xce, 0x95, 0x33, 0xf2, 0x54, 0xf3, 0x81, 0x5c, 0xfd, 0x4f, 0x12, 0x4a, 0x84, 0xf1,
+	0x5b, 0xc7, 0xeb, 0xd8, 0xa6, 0x31, 0x98, 0x29, 0x1d, 0xc8, 0xe3, 0xb6, 0x0e, 0x8d, 0xc8, 0x9d,
+	0x5a, 0x7b, 0x44, 0x12, 0x0c, 0xb5, 0xfc, 0x56, 0xdd, 0xd7, 0x24, 0xa1, 0x11, 0x0c, 0x96, 0x99,
+	0x21, 0x35, 0xf5, 0xd9, 0x71, 0xd9, 0xb8, 0x21, 0xb9, 0x34, 0x11, 0x50, 0xce, 0x1c, 0xf5, 0xbb,
+	0x7d, 0xdd, 0xf3, 0xe8, 0x78, 0xe2, 0x89, 0x6c, 0x9c, 0x46, 0xe6, 0xa8, 0xdf, 0xd5, 0xa4, 0x48,
+	0x79, 0x0d, 0xb2, 0x47, 0xb8, 0x2b, 0xf6, 0x91, 0x4c, 0xb8, 0xc7, 0xdb, 0x95, 0x58, 0xf5, 0x7d,
+	0x96, 0x67, 0x17, 0x26, 0xcb, 0x76, 0xbd, 0xbd, 0xd5, 0x6e, 0xfa, 0xbb, 0x2e, 0xfb, 0xb7, 0xac,
+	0xb6, 0x6d, 0xb1, 0x1b, 0x03, 0x5b, 0xed, 0xfe, 0xba, 0xd6, 0xda, 0xe8, 0x11, 0xb6, 0xf3, 0xe7,
+	0x10, 0x52, 0x0e, 0x20, 0xeb, 0xba, 0x61, 0x32, 0x12, 0x78, 0x01, 0x52, 0x5a, 0x1b, 0xb3, 0x4b,
+	0xa5, 0x8c, 0xdd, 0xc5, 0xa0, 0x5b, 0xb3, 0x66, 0xe1, 0x65, 0x5a, 0x1c, 0x57, 0xfd, 0x57, 0x02,
+	0x8a, 0xbd, 0xc9, 0x10, 0x23, 0x82, 0xf0, 0x4c, 0xe5, 0x32, 0x86, 0x34, 0xdd, 0xd1, 0x4d, 0x93,
+	0x9a, 0x86, 0x3b, 0x96, 0x85, 0x42, 0x54, 0x84, 0xec, 0xe6, 0xe4, 0x9b, 0x29, 0x49, 0x98, 0xdc,
+	0xd2, 0x1e, 0x2c, 0xef, 0x89, 0xc9, 0xf6, 0xf5, 0x01, 0x3f, 0xdd, 0x14, 0x3f, 0xdd, 0x6a, 0x9c,
+	0x89, 0xe8, 0xac, 0xaa, 0x72, 0x8d, 0x1a, 0xd7, 0x22, 0xa5, 0xbd, 0x68, 0x53, 0xbd, 0x0a, 0xa5,
+	0xb9, 0x7e, 0x96, 0x69, 0x3b, 0x5a, 0xaf, 0x8b, 0xbb, 0xa9, 0x14, 0x21, 0x87, 0x69, 0x76, 0xbb,
+	0xd5, 0xee, 0xe1, 0xc6, 0xa9, 0x7f, 0x48, 0xfa, 0xab, 0x95, 0x4c, 0xa0, 0x36, 0xcf, 0x04, 0x5e,
+	0x7a, 0xf4, 0x44, 0x24, 0x17, 0x08, 0x1b, 0x01, 0x23, 0xf8, 0x2e, 0x5e, 0x40, 0xb6, 0xa9, 0x74,
+	0x88, 0xce, 0x72, 0x1c, 0xdb, 0xdf, 0xf6, 0xeb, 0x38, 0xbc, 0x9f, 0x42, 0x41, 0xf3, 0x94, 0xb7,
+	0xa1, 0x38, 0xb0, 0xc7, 0x13, 0x93, 0x4a, 0xfd, 0xd4, 0x49, 0xf4, 0x0b, 0x81, 0x0a, 0x5a, 0x88,
+	0x30, 0x92, 0xf4, 0x3c, 0x23, 0xa9, 0x23, 0x2d, 0x0a, 0xe7, 0x3b, 0xcf, 0x4b, 0x70, 0x63, 0x7a,
+	0x9d, 0x86, 0x86, 0x3b, 0x73, 0x13, 0xb9, 0x09, 0x40, 0x96, 0xef, 0x18, 0x26, 0x03, 0xc6, 0x9d,
+	0xea, 0x5b, 0x9b, 0x9d, 0x8d, 0xa6, 0x60, 0x26, 0x3f, 0x81, 0xd3, 0x78, 0x08, 0x9e, 0x8e, 0x14,
+	0xd1, 0x27, 0x85, 0x6b, 0x6c, 0xce, 0x52, 0xd4, 0x37, 0x86, 0x22, 0x6e, 0xd5, 0x4e, 0x63, 0xd4,
+	0x2d, 0x04, 0xd0, 0x56, 0x83, 0xcd, 0xd2, 0x6f, 0x0c, 0x99, 0x77, 0x4e, 0x10, 0x2a, 0xc2, 0xd0,
+	0x12, 0x42, 0x53, 0x1d, 0x84, 0x30, 0x19, 0xfa, 0x7e, 0x9e, 0xde, 0x35, 0x3c, 0x8c, 0xa5, 0x43,
+	0x41, 0xfb, 0x32, 0x24, 0xc7, 0x04, 0x75, 0x16, 0x96, 0x7e, 0x9a, 0x04, 0xd8, 0xd6, 0xdd, 0x03,
+	0x39, 0x34, 0x12, 0xe4, 0xa0, 0x1c, 0x3e, 0xae, 0x2c, 0x8b, 0xec, 0x75, 0x80, 0x57, 0x5e, 0xf5,
+	0x4f, 0x5b, 0xb0, 0xd5, 0x78, 0x45, 0x39, 0x56, 0x1c, 0xe1, 0x9b, 0xa7, 0xa4, 0x2c, 0x6a, 0x53,
+	0xc7, 0x91, 0x9b, 0xce, 0x3e, 0xb1, 0x4a, 0xce, 0x07, 0x6b, 0x96, 0x1c, 0xe8, 0xe9, 0xb8, 0x41,
+	0x16, 0x36, 0xf4, 0xd6, 0x29, 0x12, 0xea, 0xd5, 0xca, 0xb0, 0xec, 0x60, 0x70, 0xc4, 0x59, 0xf7,
+	0x5d, 0xde, 0xad, 0xfe, 0x05, 0xf7, 0xa0, 0xd5, 0xd1, 0x36, 0xe5, 0x15, 0x6d, 0x40, 0x76, 0x4f,
+	0x1f, 0x1b, 0xe6, 0xec, 0x38, 0xaf, 0x0d, 0xf1, 0x55, 0x6d, 0x38, 0xc4, 0x22, 0xc1, 0x5d, 0xe7,
+	0x3a, 0x44, 0xea, 0x72, 0x32, 0x38, 0xdd, 0xb5, 0xa8, 0x17, 0x90, 0x41, 0xde, 0x62, 0x99, 0xc7,
+	0xd1, 0xad, 0x60, 0xb5, 0xa2, 0xc1, 0x76, 0x01, 0xd3, 0x2f, 0x3d, 0xd2, 0x67, 0xbe, 0x93, 0xc9,
+	0x26, 0x52, 0xbf, 0x9c, 0xa8, 0x5d, 0xe9, 0x10, 0x97, 0xcc, 0x52, 0xeb, 0x97, 0xcd, 0x87, 0x48,
+	0xb8, 0xc8, 0xa9, 0x81, 0x76, 0xe5, 0x4d, 0x9e, 0x08, 0xc2, 0xae, 0xaf, 0x54, 0xa3, 0xbd, 0x02,
+	0xa5, 0xb9, 0x75, 0x3e, 0xc4, 0xc2, 0x5b, 0x9d, 0x9d, 0xd7, 0xca, 0x69, 0xf9, 0xf5, 0x9d, 0x72,
+	0x56, 0xfd, 0x37, 0x16, 0x05, 0x1d, 0x9b, 0x07, 0x43, 0xb6, 0xab, 0xf1, 0xaf, 0x1e, 0x39, 0xfe,
+	0x86, 0x32, 0xb0, 0x4d, 0xe9, 0x33, 0xb1, 0x34, 0x34, 0xb4, 0xc2, 0x58, 0x1d, 0x87, 0x93, 0x40,
+	0x11, 0x73, 0x66, 0x41, 0xf0, 0xe9, 0xfe, 0x04, 0x71, 0x7c, 0x5b, 0x4b, 0x04, 0x84, 0x88, 0x69,
+	0xb2, 0x92, 0x7a, 0x32, 0xdd, 0xc5, 0xd8, 0x3a, 0xc2, 0x10, 0xc0, 0x31, 0x69, 0x8e, 0x29, 0x05,
+	0x52, 0x06, 0x53, 0x1b, 0x90, 0xf3, 0xad, 0xe3, 0x71, 0xa4, 0xb6, 0xeb, 0x1d, 0xcc, 0x16, 0xa7,
+	0x31, 0xd6, 0x17, 0x7c, 0x31, 0x8a, 0x58, 0x4f, 0xaf, 0xd1, 0xc1, 0x24, 0x31, 0xd7, 0x83, 0xa2,
+	0x4a, 0x9a, 0x25, 0x01, 0xf5, 0x57, 0x09, 0xc8, 0x0a, 0x4a, 0x12, 0xbb, 0x62, 0x0d, 0x96, 0x7c,
+	0xa2, 0x2c, 0x78, 0xd2, 0x73, 0x8f, 0xe6, 0x34, 0x55, 0x49, 0x41, 0xc4, 0x39, 0xfa, 0x7a, 0x95,
+	0x37, 0xa0, 0x18, 0xed, 0xf8, 0x4a, 0xa7, 0xf8, 0x63, 0x28, 0x30, 0x47, 0xf1, 0xb9, 0xcd, 0x1a,
+	0x64, 0x05, 0x6d, 0x92, 0x57, 0xfd, 0x38, 0x82, 0x25, 0x91, 0x98, 0x9e, 0x96, 0x04, 0x29, 0xf3,
+	0x9f, 0x0b, 0x56, 0x8f, 0x77, 0x47, 0xe2, 0xc3, 0xd5, 0x1b, 0x90, 0xee, 0x50, 0xb4, 0xf0, 0x34,
+	0x2c, 0x59, 0x18, 0x7a, 0xc2, 0xc8, 0x26, 0xf9, 0xe4, 0x90, 0x62, 0xc4, 0xca, 0xb2, 0x2e, 0x8c,
+	0x67, 0xb8, 0x79, 0x3a, 0xfa, 0x9b, 0xff, 0x62, 0xc2, 0xbe, 0xd5, 0x6d, 0x28, 0xde, 0xa1, 0xc6,
+	0xfe, 0x08, 0xe3, 0x32, 0x37, 0xf4, 0x12, 0xa4, 0x27, 0x34, 0x98, 0xfc, 0x4a, 0xac, 0xeb, 0x60,
+	0x3f, 0xe1, 0x28, 0x76, 0x21, 0x8f, 0xb8, 0xb6, 0x7c, 0xa4, 0x92, 0x2d, 0xf5, 0xf7, 0x49, 0x58,
+	0x6e, 0xb9, 0xee, 0x54, 0xc7, 0x02, 0x50, 0x46, 0xc1, 0xb7, 0xe6, 0xd3, 0xd6, 0xd5, 0xd8, 0x15,
+	0xce, 0xa9, 0xcc, 0x17, 0xb1, 0x32, 0x72, 0x25, 0x83, 0xc8, 0xa5, 0x3e, 0x48, 0xf8, 0xd5, 0xeb,
+	0x95, 0xc8, 0xbd, 0xa9, 0xac, 0xa0, 0x13, 0x9d, 0x8b, 0x5a, 0xa2, 0x3d, 0xeb, 0xc0, 0xb2, 0x8f,
+	0x2c, 0xa4, 0x47, 0x58, 0xcd, 0xb6, 0x9b, 0x77, 0xd0, 0xd3, 0xce, 0x23, 0x48, 0x99, 0x03, 0x11,
+	0x6a, 0xd1, 0x23, 0x66, 0xa9, 0xd3, 0x6c, 0x37, 0x58, 0x86, 0x49, 0xc6, 0x58, 0xea, 0x50, 0x24,
+	0x21, 0xd6, 0x3e, 0x6e, 0x77, 0xb6, 0xd5, 0xed, 0xf6, 0x78, 0x7d, 0xf1, 0x38, 0xa2, 0xce, 0xce,
+	0xa1, 0x58, 0x03, 0x8b, 0x0b, 0x04, 0x31, 0xfe, 0x83, 0xa0, 0x74, 0x0c, 0x88, 0xa5, 0x7f, 0x0c,
+	0x20, 0xc2, 0xc3, 0xff, 0x9e, 0x84, 0xb2, 0x36, 0x18, 0xd0, 0x89, 0xc7, 0xfa, 0x25, 0xa7, 0xdc,
+	0xc6, 0x9b, 0xcc, 0xbe, 0x0c, 0xce, 0x91, 0x99, 0x5b, 0x5c, 0x8f, 0x7d, 0xc1, 0x5c, 0xd0, 0xab,
+	0x12, 0xdb, 0xa4, 0xda, 0x70, 0x6c, 0xb8, 0xec, 0x55, 0x4b, 0xc8, 0x48, 0x60, 0xa9, 0xf2, 0x79,
+	0x02, 0xce, 0xc6, 0x20, 0x94, 0x57, 0x20, 0xed, 0xa0, 0x58, 0x1e, 0xcf, 0xc5, 0x47, 0xbd, 0x2f,
+	0x30, 0x55, 0xc2, 0x91, 0xca, 0x2a, 0x80, 0x3e, 0xf5, 0x6c, 0x9d, 0x8f, 0xcf, 0x0f, 0x26, 0x47,
+	0x22, 0x12, 0xe5, 0x0e, 0x46, 0x6b, 0x3a, 0x70, 0xa8, 0x4f, 0x10, 0x6e, 0xfc, 0xbf, 0xb3, 0xaf,
+	0x76, 0xb9, 0x19, 0x22, 0xcd, 0x55, 0xaa, 0x58, 0xdd, 0xf1, 0x2f, 0xe6, 0xd1, 0xc8, 0x15, 0x74,
+	0x3e, 0xe9, 0x22, 0xe1, 0xdf, 0xcc, 0x51, 0x74, 0x73, 0xdf, 0x77, 0x14, 0xfc, 0x54, 0x3f, 0xc4,
+	0x5c, 0xd4, 0xbc, 0xeb, 0x51, 0xc7, 0xd2, 0xcd, 0xba, 0xa6, 0x34, 0x23, 0x11, 0x52, 0xac, 0xf6,
+	0xf9, 0xd8, 0x57, 0xa7, 0x40, 0xa3, 0x5a, 0xd7, 0x62, 0x62, 0x24, 0xb2, 0x83, 0xa9, 0x63, 0xca,
+	0x17, 0x4c, 0xce, 0x0e, 0x7a, 0x64, 0x83, 0x30, 0x19, 0x7b, 0xfe, 0xf3, 0x23, 0x52, 0xea, 0xd1,
+	0x4f, 0xcf, 0x91, 0x01, 0xbe, 0xf9, 0xa8, 0xf4, 0x12, 0x40, 0x38, 0x6b, 0x3c, 0xaa, 0x4c, 0x7d,
+	0xbd, 0xdb, 0xdd, 0xc0, 0xeb, 0xc1, 0x4b, 0xa0, 0xb0, 0x8b, 0x8b, 0xd5, 0xdf, 0x25, 0x90, 0x73,
+	0x6a, 0x32, 0xab, 0xac, 0x43, 0x99, 0xc7, 0x92, 0x01, 0x75, 0xbc, 0x3e, 0xbd, 0x3b, 0x31, 0x9c,
+	0x99, 0x0c, 0x07, 0xc7, 0x17, 0x0b, 0xcb, 0x4c, 0xab, 0x8e, 0x4a, 0x4d, 0xae, 0xa3, 0x10, 0x28,
+	0x52, 0xb9, 0xc4, 0xfe, 0x40, 0xf7, 0x83, 0xf3, 0xea, 0xf1, 0x5b, 0x21, 0x28, 0x59, 0xd8, 0x76,
+	0x49, 0xc1, 0x37, 0x52, 0xd7, 0x5d, 0x75, 0x07, 0xce, 0x6e, 0x39, 0x83, 0x11, 0x92, 0x23, 0x31,
+	0xa8, 0x9c, 0xf2, 0x0d, 0xb8, 0xe8, 0x21, 0x09, 0xea, 0x8f, 0x0c, 0xd7, 0x63, 0x0f, 0xe7, 0xe8,
+	0x1b, 0xd4, 0x62, 0xfd, 0x7d, 0xfe, 0xc0, 0x2d, 0x4b, 0xcc, 0x0b, 0x0c, 0x73, 0x4b, 0x40, 0x88,
+	0x8f, 0xd8, 0x60, 0x00, 0xb5, 0x85, 0x15, 0x2a, 0x76, 0x36, 0xe8, 0x9e, 0x3e, 0x35, 0xb1, 0x4c,
+	0x7a, 0x1d, 0xc0, 0xb4, 0xf7, 0xfb, 0x27, 0x8e, 0xe4, 0x79, 0x44, 0x8b, 0x4f, 0xf5, 0xfb, 0x50,
+	0x6e, 0x18, 0xee, 0x44, 0xf7, 0x70, 0x9a, 0xb2, 0x76, 0x56, 0x6e, 0x42, 0x79, 0x44, 0x91, 0x3e,
+	0xef, 0x52, 0x1d, 0x53, 0x2a, 0x75, 0x0c, 0x7b, 0x78, 0xa2, 0x2d, 0x3d, 0x1d, 0x68, 0x75, 0xb8,
+	0x92, 0xfa, 0x05, 0x12, 0x00, 0xf6, 0x38, 0x29, 0xed, 0xbe, 0x08, 0x67, 0x5c, 0x4b, 0x9f, 0xb8,
+	0x23, 0xdb, 0xeb, 0x1b, 0x96, 0xc7, 0x5e, 0xe3, 0x4d, 0x59, 0xff, 0x94, 0xfd, 0x8e, 0x96, 0x94,
+	0x63, 0x68, 0x57, 0x0e, 0x28, 0x9d, 0xf4, 0x6d, 0x73, 0xd8, 0xf7, 0x3b, 0xc5, 0x0b, 0x3c, 0xa2,
+	0x59, 0xcf, 0x96, 0x39, 0xec, 0xfa, 0x72, 0x2c, 0x33, 0x56, 0xd9, 0x0e, 0xe0, 0x26, 0x39, 0x18,
+	0x36, 0xfa, 0x7b, 0xb6, 0xd3, 0x77, 0x4d, 0xfb, 0x08, 0x3f, 0x4c, 0xfc, 0x47, 0x1d, 0xbf, 0xba,
+	0xac, 0x20, 0xaa, 0x29, 0x40, 0xeb, 0xb6, 0xd3, 0xc5, 0xbe, 0x75, 0x1f, 0xc1, 0x58, 0x42, 0xb8,
+	0x6c, 0xcf, 0x18, 0x1c, 0xf8, 0x2c, 0x21, 0x90, 0x6e, 0xa3, 0x10, 0x03, 0x65, 0x89, 0x9a, 0x94,
+	0xd7, 0x41, 0x02, 0x95, 0xe1, 0xa8, 0xa2, 0x2f, 0x64, 0x20, 0xf5, 0x5b, 0x90, 0xef, 0x98, 0xfa,
+	0x80, 0xff, 0xce, 0xc1, 0x2a, 0x3e, 0xcc, 0x80, 0xcc, 0x09, 0x70, 0xd5, 0x22, 0x3a, 0xe6, 0x49,
+	0x54, 0xa4, 0xbe, 0x05, 0xf0, 0x8e, 0x6d, 0x58, 0xdb, 0xf6, 0x01, 0xb5, 0xf8, 0x93, 0xf0, 0x91,
+	0xed, 0x1c, 0xc8, 0xa3, 0x44, 0xe2, 0x28, 0x5a, 0x9c, 0x28, 0xeb, 0x16, 0x12, 0x63, 0x27, 0x78,
+	0x19, 0x15, 0x4d, 0x96, 0x5c, 0xb2, 0xc4, 0xb6, 0x3d, 0x8c, 0x17, 0x97, 0x21, 0x3b, 0xd0, 0xfb,
+	0xfe, 0xcd, 0x2b, 0xd6, 0xf2, 0xe8, 0xa1, 0x99, 0xba, 0x76, 0x9b, 0xce, 0x48, 0x66, 0xa0, 0xe3,
+	0x3f, 0x96, 0x7d, 0x11, 0xc1, 0xee, 0x0b, 0x37, 0x53, 0x14, 0xd9, 0x17, 0x2f, 0x14, 0x4a, 0x08,
+	0x2a, 0xb3, 0xff, 0x18, 0x60, 0x8b, 0x12, 0xd4, 0x1f, 0xe9, 0xee, 0x48, 0x70, 0xd5, 0xda, 0x32,
+	0x22, 0x41, 0x20, 0x6f, 0xa1, 0x94, 0x80, 0x40, 0xb3, 0x6f, 0x0c, 0x23, 0x85, 0xf7, 0x70, 0x0d,
+	0x7d, 0x8f, 0x2f, 0x42, 0x16, 0xec, 0xb1, 0xf7, 0x27, 0x5c, 0xaa, 0xac, 0x5e, 0xe1, 0xbd, 0x40,
+	0xa2, 0xfe, 0x35, 0x01, 0x05, 0x66, 0xd3, 0xd8, 0x33, 0x06, 0x2c, 0x5b, 0x7e, 0xf5, 0x48, 0x8f,
+	0xa1, 0x6e, 0xe0, 0x3a, 0x72, 0x6d, 0x3c, 0xd4, 0xd5, 0xbb, 0x84, 0x30, 0x19, 0xd6, 0x82, 0x59,
+	0xc1, 0xf8, 0x65, 0x90, 0x57, 0xbf, 0x3c, 0xaf, 0xcb, 0x29, 0x4a, 0x3d, 0x7e, 0x96, 0xe1, 0xec,
+	0xf8, 0x2a, 0x8b, 0x24, 0x2a, 0x62, 0x3f, 0x15, 0x0d, 0x2c, 0xee, 0x14, 0xf2, 0xa7, 0xa2, 0x7a,
+	0x9b, 0xa0, 0x44, 0xfd, 0x73, 0x02, 0x4a, 0x4d, 0x6b, 0xe0, 0xcc, 0x78, 0x90, 0x64, 0x07, 0x71,
+	0x11, 0xf2, 0x58, 0x12, 0xb8, 0x33, 0xd7, 0xa3, 0x63, 0xff, 0x25, 0x3a, 0x10, 0x28, 0x2d, 0xc8,
+	0x63, 0x3a, 0xb0, 0x1d, 0xc3, 0x1b, 0x8d, 0x25, 0x37, 0x8e, 0x0f, 0xcc, 0x51, 0x9b, 0x55, 0xcd,
+	0x57, 0x21, 0xa1, 0xb6, 0x1f, 0x8a, 0x53, 0x7c, 0xb2, 0x3c, 0x14, 0x3f, 0x05, 0x45, 0x13, 0x0b,
+	0x36, 0x64, 0xbd, 0x7d, 0x56, 0x07, 0xf1, 0x75, 0xa4, 0x49, 0x41, 0xca, 0x58, 0x6d, 0xa7, 0xaa,
+	0x90, 0x0f, 0x8c, 0xb1, 0xf7, 0x7f, 0xad, 0xd9, 0xed, 0x5f, 0x5b, 0xbb, 0xde, 0xbf, 0x59, 0xdf,
+	0xc4, 0xc0, 0x2c, 0x98, 0xc0, 0x1f, 0x71, 0x4d, 0x9b, 0xc2, 0x07, 0x25, 0x71, 0x42, 0xe7, 0x72,
+	0xf0, 0xc6, 0xfb, 0xd4, 0x2e, 0x2d, 0x9c, 0x8b, 0x05, 0x01, 0x46, 0xed, 0x58, 0x57, 0x3c, 0xb5,
+	0x8b, 0xfc, 0x0e, 0x92, 0x3a, 0xf6, 0x77, 0x90, 0xf4, 0x37, 0xf2, 0x3b, 0xc8, 0x0b, 0x5f, 0xa4,
+	0x20, 0x1f, 0x54, 0xa2, 0xcc, 0x65, 0x18, 0xd3, 0x3a, 0x25, 0x5e, 0x76, 0x02, 0x79, 0x9b, 0x73,
+	0xac, 0xbc, 0xb6, 0xb1, 0xb1, 0x55, 0xd7, 0x58, 0xb1, 0xfe, 0xb6, 0xa0, 0x62, 0x01, 0x40, 0xc3,
+	0xd8, 0xc1, 0x0e, 0x7d, 0xa8, 0xa8, 0x21, 0x15, 0xbb, 0x27, 0xdf, 0x8f, 0x02, 0x94, 0xcf, 0xc3,
+	0x9e, 0x81, 0x9c, 0xd6, 0xed, 0xb6, 0x6e, 0xb6, 0xd1, 0xd2, 0xfd, 0x44, 0xe5, 0x31, 0x04, 0x9d,
+	0x09, 0x4d, 0x21, 0x85, 0xd8, 0xb7, 0xd0, 0x12, 0x43, 0xd5, 0xeb, 0xcd, 0x0e, 0x1b, 0xef, 0x5e,
+	0x72, 0x11, 0xc5, 0x09, 0x08, 0x7f, 0x0b, 0xce, 0x77, 0x48, 0xb3, 0xa3, 0x11, 0x36, 0xe2, 0xfd,
+	0xe4, 0xc2, 0xbc, 0x3a, 0x0e, 0x9d, 0xe8, 0x0e, 0x1b, 0x73, 0xd5, 0xff, 0x4d, 0xe4, 0x5e, 0x4a,
+	0xbc, 0x17, 0x86, 0xe5, 0x37, 0xee, 0xef, 0x8c, 0x8d, 0xd6, 0xdd, 0xd6, 0x08, 0x7f, 0xa5, 0xb8,
+	0x9f, 0x5a, 0x18, 0xad, 0xcb, 0x1e, 0x51, 0x98, 0x15, 0x5c, 0x1d, 0xe9, 0xb5, 0xdb, 0x7c, 0x75,
+	0xe9, 0x85, 0xd5, 0x91, 0xa9, 0x65, 0x31, 0xcc, 0x15, 0xf6, 0x10, 0x24, 0x5e, 0x35, 0xca, 0xf7,
+	0xd3, 0x0b, 0x13, 0xaa, 0xfb, 0xcf, 0x29, 0x7c, 0xc0, 0x5b, 0xbd, 0x6d, 0xfe, 0x93, 0xcd, 0xbd,
+	0xcc, 0xe2, 0x80, 0xa3, 0xa9, 0x37, 0x64, 0xe4, 0xf7, 0x72, 0xc0, 0x46, 0xef, 0x67, 0x04, 0x09,
+	0x08, 0x30, 0x82, 0x8a, 0x32, 0x3b, 0xa4, 0xf9, 0x8e, 0xf8, 0x75, 0xe7, 0x5e, 0x76, 0xc1, 0x0e,
+	0xa1, 0xef, 0x61, 0x30, 0x46, 0xc2, 0x1a, 0x3c, 0x87, 0x06, 0x5d, 0x2f, 0xfc, 0x00, 0x72, 0x7e,
+	0xc0, 0xc0, 0xdd, 0xc9, 0xde, 0xd9, 0x22, 0xb7, 0x9b, 0x04, 0x8f, 0x9e, 0xef, 0x8e, 0xdf, 0x73,
+	0x47, 0x44, 0xdc, 0xcb, 0xb0, 0xb4, 0xa9, 0xb5, 0xb5, 0x9b, 0x08, 0x90, 0xcf, 0xb1, 0x3e, 0x40,
+	0x7a, 0x7d, 0xa5, 0x2c, 0x07, 0x08, 0x6c, 0xd6, 0x2e, 0x3e, 0xf8, 0x74, 0xf5, 0xd4, 0xc7, 0xf8,
+	0xf7, 0xf9, 0xa7, 0xab, 0x89, 0x7b, 0x9f, 0xad, 0x26, 0x1e, 0xe0, 0xdf, 0x47, 0xf8, 0xf7, 0x0f,
+	0xfc, 0xdb, 0xcd, 0x72, 0x46, 0xf6, 0xea, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x84, 0x8b, 0x50,
+	0xe8, 0x9f, 0x20, 0x00, 0x00,
 }

+ 48 - 0
vendor/src/github.com/docker/swarmkit/api/types.proto

@@ -277,6 +277,54 @@ message UpdateConfig {
 
 	// Amount of time between updates.
 	Duration delay = 2 [(gogoproto.nullable) = false];
+
+	enum FailureAction {
+		PAUSE = 0;
+		CONTINUE = 1;
+		// TODO(aaronl): Add ROLLBACK as a supported failure mode.
+		// (#486)
+	}
+
+	// FailureAction is the action to take when an update failures.
+	// Currently, a failure is defined as a single updated task failing to
+	// reach the RUNNING state. In the future, there will be configuration
+	// to define what is treated as a failure (see #486 for a proposal).
+	FailureAction failure_action = 3;
+}
+
+// UpdateStatus is the status of an update in progress.
+message UpdateStatus {
+	enum UpdateState {
+		UNKNOWN = 0;
+		UPDATING = 1;
+		PAUSED = 2;
+		COMPLETED = 3;
+		// TODO(aaronl): add ROLLING_BACK, ROLLED_BACK as part of
+		// rollback support.
+	}
+
+	// State is the state of this update. It indicates whether the
+	// update is in progress, completed, or is paused.
+	UpdateState state = 1;
+
+	// StartedAt is the time at which the update was started.
+	Timestamp started_at = 2;
+
+	// CompletedAt is the time at which the update completed.
+	Timestamp completed_at = 3;
+
+	// TODO(aaronl): Consider adding a timestamp showing when the most
+	// recent task update took place. Currently, this is nontrivial
+	// because each service update kicks off a replacement update, so
+	// updating the service object with a timestamp at every step along
+	// the rolling update would cause the rolling update to be constantly
+	// restarted.
+
+	// Message explains how the update got into its current state. For
+	// example, if the update is paused, it will explain what is preventing
+	// the update from proceeding (typically the failure of a task to start up
+	// when OnFailure is PAUSE).
+	string message = 4;
 }
 
 // TaskState enumerates the states that a task progresses through within an

+ 19 - 3
vendor/src/github.com/docker/swarmkit/ca/auth.go

@@ -122,7 +122,7 @@ func AuthorizeForwardedRoleAndOrg(ctx context.Context, authorizedRoles, forwarde
 		// This was a forwarded request. Authorize the forwarder, and
 		// check if the forwarded role matches one of the authorized
 		// roles.
-		forwardedID, forwardedOrg, forwardedOUs := forwardedTLSInfoFromContext(ctx)
+		_, forwardedID, forwardedOrg, forwardedOUs := forwardedTLSInfoFromContext(ctx)
 
 		if len(forwardedOUs) == 0 || forwardedID == "" || forwardedOrg == "" {
 			return "", grpc.Errorf(codes.PermissionDenied, "Permission denied: missing information in forwarded request")
@@ -178,6 +178,10 @@ type RemoteNodeInfo struct {
 	// ForwardedBy contains information for the node that forwarded this
 	// request. It is set to nil if the request was received directly.
 	ForwardedBy *RemoteNodeInfo
+
+	// RemoteAddr is the address that this node is connecting to the cluster
+	// from.
+	RemoteAddr string
 }
 
 // RemoteNode returns the node ID and role from the client's TLS certificate.
@@ -195,18 +199,30 @@ func RemoteNode(ctx context.Context) (RemoteNodeInfo, error) {
 		org = certSubj.Organization[0]
 	}
 
+	peer, ok := peer.FromContext(ctx)
+	if !ok {
+		return RemoteNodeInfo{}, grpc.Errorf(codes.PermissionDenied, "Permission denied: no peer info")
+	}
+
 	directInfo := RemoteNodeInfo{
 		Roles:        certSubj.OrganizationalUnit,
 		NodeID:       certSubj.CommonName,
 		Organization: org,
+		RemoteAddr:   peer.Addr.String(),
 	}
 
 	if isForwardedRequest(ctx) {
-		cn, org, ous := forwardedTLSInfoFromContext(ctx)
+		remoteAddr, cn, org, ous := forwardedTLSInfoFromContext(ctx)
 		if len(ous) == 0 || cn == "" || org == "" {
 			return RemoteNodeInfo{}, grpc.Errorf(codes.PermissionDenied, "Permission denied: missing information in forwarded request")
 		}
-		return RemoteNodeInfo{Roles: ous, NodeID: cn, Organization: org, ForwardedBy: &directInfo}, nil
+		return RemoteNodeInfo{
+			Roles:        ous,
+			NodeID:       cn,
+			Organization: org,
+			ForwardedBy:  &directInfo,
+			RemoteAddr:   remoteAddr,
+		}, nil
 	}
 
 	return directInfo, nil

+ 13 - 3
vendor/src/github.com/docker/swarmkit/ca/forward.go

@@ -3,6 +3,7 @@ package ca
 import (
 	"golang.org/x/net/context"
 	"google.golang.org/grpc/metadata"
+	"google.golang.org/grpc/peer"
 )
 
 const (
@@ -10,20 +11,24 @@ const (
 	certCNKey        = "forwarded_cert_cn"
 	certOUKey        = "forwarded_cert_ou"
 	certOrgKey       = "forwarded_cert_org"
+	remoteAddrKey    = "remote_addr"
 )
 
 // forwardedTLSInfoFromContext obtains forwarded TLS CN/OU from the grpc.MD
 // object in ctx.
-func forwardedTLSInfoFromContext(ctx context.Context) (string, string, []string) {
-	var cn, org string
+func forwardedTLSInfoFromContext(ctx context.Context) (remoteAddr string, cn string, org string, ous []string) {
 	md, _ := metadata.FromContext(ctx)
+	if len(md[remoteAddrKey]) != 0 {
+		remoteAddr = md[remoteAddrKey][0]
+	}
 	if len(md[certCNKey]) != 0 {
 		cn = md[certCNKey][0]
 	}
 	if len(md[certOrgKey]) != 0 {
 		org = md[certOrgKey][0]
 	}
-	return cn, org, md[certOUKey]
+	ous = md[certOUKey]
+	return
 }
 
 func isForwardedRequest(ctx context.Context) bool {
@@ -54,6 +59,7 @@ func WithMetadataForwardTLSInfo(ctx context.Context) (context.Context, error) {
 			org = certSubj.Organization[0]
 		}
 	}
+
 	// If there's no TLS cert, forward with blank TLS metadata.
 	// Note that the presence of this blank metadata is extremely
 	// important. Without it, it would look like manager is making
@@ -62,6 +68,10 @@ func WithMetadataForwardTLSInfo(ctx context.Context) (context.Context, error) {
 	md[certCNKey] = []string{cn}
 	md[certOrgKey] = []string{org}
 	md[certOUKey] = ous
+	peer, ok := peer.FromContext(ctx)
+	if ok {
+		md[remoteAddrKey] = []string{peer.Addr.String()}
+	}
 
 	return metadata.NewContext(ctx, md), nil
 }

+ 1 - 1
vendor/src/github.com/docker/swarmkit/ca/transport.go

@@ -16,7 +16,7 @@ import (
 )
 
 var (
-	// alpnProtoStr are the specified application level protocols for gRPC.
+	// alpnProtoStr is the specified application level protocols for gRPC.
 	alpnProtoStr = []string{"h2"}
 )
 

+ 79 - 0
vendor/src/github.com/docker/swarmkit/manager/controlapi/service.go

@@ -3,6 +3,7 @@ package controlapi
 import (
 	"errors"
 	"reflect"
+	"strconv"
 
 	"github.com/docker/engine-api/types/reference"
 	"github.com/docker/swarmkit/api"
@@ -144,6 +145,10 @@ func validateEndpointSpec(epSpec *api.EndpointSpec) error {
 		return nil
 	}
 
+	if len(epSpec.Ports) > 0 && epSpec.Mode == api.ResolutionModeDNSRoundRobin {
+		return grpc.Errorf(codes.InvalidArgument, "EndpointSpec: ports can't be used with dnsrr mode")
+	}
+
 	portSet := make(map[api.PortConfig]struct{})
 	for _, port := range epSpec.Ports {
 		if _, ok := portSet[*port]; ok {
@@ -175,6 +180,59 @@ func validateServiceSpec(spec *api.ServiceSpec) error {
 	return nil
 }
 
+// checkPortConflicts does a best effort to find if the passed in spec has port
+// conflicts with existing services.
+func (s *Server) checkPortConflicts(spec *api.ServiceSpec) error {
+	if spec.Endpoint == nil {
+		return nil
+	}
+
+	pcToString := func(pc *api.PortConfig) string {
+		port := strconv.FormatUint(uint64(pc.PublishedPort), 10)
+		return port + "/" + pc.Protocol.String()
+	}
+
+	reqPorts := make(map[string]bool)
+	for _, pc := range spec.Endpoint.Ports {
+		if pc.PublishedPort > 0 {
+			reqPorts[pcToString(pc)] = true
+		}
+	}
+	if len(reqPorts) == 0 {
+		return nil
+	}
+
+	var (
+		services []*api.Service
+		err      error
+	)
+
+	s.store.View(func(tx store.ReadTx) {
+		services, err = store.FindServices(tx, store.All)
+	})
+	if err != nil {
+		return err
+	}
+
+	for _, service := range services {
+		if service.Spec.Endpoint != nil {
+			for _, pc := range service.Spec.Endpoint.Ports {
+				if reqPorts[pcToString(pc)] {
+					return grpc.Errorf(codes.InvalidArgument, "port '%d' is already in use by service %s", pc.PublishedPort, service.ID)
+				}
+			}
+		}
+		if service.Endpoint != nil {
+			for _, pc := range service.Endpoint.Ports {
+				if reqPorts[pcToString(pc)] {
+					return grpc.Errorf(codes.InvalidArgument, "port '%d' is already in use by service %s", pc.PublishedPort, service.ID)
+				}
+			}
+		}
+	}
+	return nil
+}
+
 // CreateService creates and return a Service based on the provided ServiceSpec.
 // - Returns `InvalidArgument` if the ServiceSpec is malformed.
 // - Returns `Unimplemented` if the ServiceSpec references unimplemented features.
@@ -185,6 +243,10 @@ func (s *Server) CreateService(ctx context.Context, request *api.CreateServiceRe
 		return nil, err
 	}
 
+	if err := s.checkPortConflicts(request.Spec); err != nil {
+		return nil, err
+	}
+
 	// TODO(aluzzardi): Consider using `Name` as a primary key to handle
 	// duplicate creations. See #65
 	service := &api.Service{
@@ -239,6 +301,19 @@ func (s *Server) UpdateService(ctx context.Context, request *api.UpdateServiceRe
 	}
 
 	var service *api.Service
+	s.store.View(func(tx store.ReadTx) {
+		service = store.GetService(tx, request.ServiceID)
+	})
+	if service == nil {
+		return nil, grpc.Errorf(codes.NotFound, "service %s not found", request.ServiceID)
+	}
+
+	if request.Spec.Endpoint != nil && !reflect.DeepEqual(request.Spec.Endpoint, service.Spec.Endpoint) {
+		if err := s.checkPortConflicts(request.Spec); err != nil {
+			return nil, err
+		}
+	}
+
 	err := s.store.Update(func(tx store.Tx) error {
 		service = store.GetService(tx, request.ServiceID)
 		if service == nil {
@@ -257,6 +332,10 @@ func (s *Server) UpdateService(ctx context.Context, request *api.UpdateServiceRe
 		}
 		service.Meta.Version = *request.ServiceVersion
 		service.Spec = *request.Spec.Copy()
+
+		// Reset update status
+		service.UpdateStatus = nil
+
 		return store.UpdateService(tx, service)
 	})
 	if err != nil {

+ 1 - 1
vendor/src/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go

@@ -52,7 +52,7 @@ var (
 	// ErrSessionInvalid returned when the session in use is no longer valid.
 	// The node should re-register and start a new session.
 	ErrSessionInvalid = errors.New("session invalid")
-	// ErrNodeNotFound returned when the Node doesn't exists in raft.
+	// ErrNodeNotFound returned when the Node doesn't exist in raft.
 	ErrNodeNotFound = errors.New("node not found")
 )
 

+ 10 - 31
vendor/src/github.com/docker/swarmkit/manager/manager.go

@@ -33,7 +33,7 @@ import (
 
 const (
 	// defaultTaskHistoryRetentionLimit is the number of tasks to keep.
-	defaultTaskHistoryRetentionLimit = 10
+	defaultTaskHistoryRetentionLimit = 5
 )
 
 // Config is used to tune the Manager.
@@ -49,6 +49,9 @@ type Config struct {
 	// ProtoAddr fields will be used to create listeners otherwise.
 	ProtoListener map[string]net.Listener
 
+	// AdvertiseAddr is a map of addresses to advertise, by protocol.
+	AdvertiseAddr string
+
 	// JoinRaft is an optional address of a node in an existing raft
 	// cluster to join.
 	JoinRaft string
@@ -120,41 +123,17 @@ func New(config *Config) (*Manager, error) {
 
 	tcpAddr := config.ProtoAddr["tcp"]
 
-	if tcpAddr == "" {
-		return nil, errors.New("no tcp listen address or listener provided")
+	if config.AdvertiseAddr != "" {
+		tcpAddr = config.AdvertiseAddr
 	}
 
-	listenHost, listenPort, err := net.SplitHostPort(tcpAddr)
-	if err == nil {
-		ip := net.ParseIP(listenHost)
-		if ip != nil && ip.IsUnspecified() {
-			// Find our local IP address associated with the default route.
-			// This may not be the appropriate address to use for internal
-			// cluster communications, but it seems like the best default.
-			// The admin can override this address if necessary.
-			conn, err := net.Dial("udp", "8.8.8.8:53")
-			if err != nil {
-				return nil, fmt.Errorf("could not determine local IP address: %v", err)
-			}
-			localAddr := conn.LocalAddr().String()
-			conn.Close()
-
-			listenHost, _, err = net.SplitHostPort(localAddr)
-			if err != nil {
-				return nil, fmt.Errorf("could not split local IP address: %v", err)
-			}
-
-			tcpAddr = net.JoinHostPort(listenHost, listenPort)
-		}
+	if tcpAddr == "" {
+		return nil, errors.New("no tcp listen address or listener provided")
 	}
 
-	// TODO(stevvooe): Reported address of manager is plumbed to listen addr
-	// for now, may want to make this separate. This can be tricky to get right
-	// so we need to make it easy to override. This needs to be the address
-	// through which agent nodes access the manager.
 	dispatcherConfig.Addr = tcpAddr
 
-	err = os.MkdirAll(filepath.Dir(config.ProtoAddr["unix"]), 0700)
+	err := os.MkdirAll(filepath.Dir(config.ProtoAddr["unix"]), 0700)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create socket directory: %v", err)
 	}
@@ -359,7 +338,7 @@ func (m *Manager) Run(parent context.Context) error {
 				if err != nil {
 					log.G(ctx).WithError(err).Error("failed to create allocator")
 					// TODO(stevvooe): It doesn't seem correct here to fail
-					// creating the allocator but then use it anyways.
+					// creating the allocator but then use it anyway.
 				}
 
 				go func(keyManager *keymanager.KeyManager) {

+ 8 - 2
vendor/src/github.com/docker/swarmkit/manager/orchestrator/replicated.go

@@ -62,8 +62,14 @@ func (r *ReplicatedOrchestrator) Run(ctx context.Context) error {
 		if err = r.initTasks(ctx, readTx); err != nil {
 			return
 		}
-		err = r.initServices(readTx)
-		err = r.initCluster(readTx)
+
+		if err = r.initServices(readTx); err != nil {
+			return
+		}
+
+		if err = r.initCluster(readTx); err != nil {
+			return
+		}
 	})
 	if err != nil {
 		return err

+ 51 - 5
vendor/src/github.com/docker/swarmkit/manager/orchestrator/restart.go

@@ -31,8 +31,13 @@ type instanceRestartInfo struct {
 }
 
 type delayedStart struct {
+	// cancel is called to cancel the delayed start.
 	cancel func()
 	doneCh chan struct{}
+
+	// waiter is set to true if the next restart is waiting for this delay
+	// to complete.
+	waiter bool
 }
 
 // RestartSupervisor initiates and manages restarts. It's responsible for
@@ -40,7 +45,7 @@ type delayedStart struct {
 type RestartSupervisor struct {
 	mu               sync.Mutex
 	store            *store.MemoryStore
-	delays           map[string]delayedStart
+	delays           map[string]*delayedStart
 	history          map[instanceTuple]*instanceRestartInfo
 	historyByService map[string]map[instanceTuple]struct{}
 	taskTimeout      time.Duration
@@ -50,18 +55,59 @@ type RestartSupervisor struct {
 func NewRestartSupervisor(store *store.MemoryStore) *RestartSupervisor {
 	return &RestartSupervisor{
 		store:            store,
-		delays:           make(map[string]delayedStart),
+		delays:           make(map[string]*delayedStart),
 		history:          make(map[instanceTuple]*instanceRestartInfo),
 		historyByService: make(map[string]map[instanceTuple]struct{}),
 		taskTimeout:      defaultOldTaskTimeout,
 	}
 }
 
+func (r *RestartSupervisor) waitRestart(ctx context.Context, oldDelay *delayedStart, cluster *api.Cluster, taskID string) {
+	// Wait for the last restart delay to elapse.
+	select {
+	case <-oldDelay.doneCh:
+	case <-ctx.Done():
+		return
+	}
+
+	// Start the next restart
+	err := r.store.Update(func(tx store.Tx) error {
+		t := store.GetTask(tx, taskID)
+		if t == nil {
+			return nil
+		}
+		service := store.GetService(tx, t.ServiceID)
+		if service == nil {
+			return nil
+		}
+		return r.Restart(ctx, tx, cluster, service, *t)
+	})
+
+	if err != nil {
+		log.G(ctx).WithError(err).Errorf("failed to restart task after waiting for previous restart")
+	}
+}
+
 // Restart initiates a new task to replace t if appropriate under the service's
 // restart policy.
 func (r *RestartSupervisor) Restart(ctx context.Context, tx store.Tx, cluster *api.Cluster, service *api.Service, t api.Task) error {
 	// TODO(aluzzardi): This function should not depend on `service`.
 
+	// Is the old task still in the process of restarting? If so, wait for
+	// its restart delay to elapse, to avoid tight restart loops (for
+	// example, when the image doesn't exist).
+	r.mu.Lock()
+	oldDelay, ok := r.delays[t.ID]
+	if ok {
+		if !oldDelay.waiter {
+			oldDelay.waiter = true
+			go r.waitRestart(ctx, oldDelay, cluster, t.ID)
+		}
+		r.mu.Unlock()
+		return nil
+	}
+	r.mu.Unlock()
+
 	t.DesiredState = api.TaskStateShutdown
 	err := store.UpdateTask(tx, &t)
 	if err != nil {
@@ -87,10 +133,10 @@ func (r *RestartSupervisor) Restart(ctx context.Context, tx store.Tx, cluster *a
 
 	n := store.GetNode(tx, t.NodeID)
 
-	restartTask.DesiredState = api.TaskStateAccepted
+	restartTask.DesiredState = api.TaskStateReady
 
 	var restartDelay time.Duration
-	// Restart delay does not applied to drained nodes
+	// Restart delay is not applied to drained nodes
 	if n == nil || n.Spec.Availability != api.NodeAvailabilityDrain {
 		if t.Spec.Restart != nil && t.Spec.Restart.Delay != nil {
 			var err error
@@ -254,7 +300,7 @@ func (r *RestartSupervisor) DelayStart(ctx context.Context, _ store.Tx, oldTask
 		<-oldDelay.doneCh
 		r.mu.Lock()
 	}
-	r.delays[newTaskID] = delayedStart{cancel: cancel, doneCh: doneCh}
+	r.delays[newTaskID] = &delayedStart{cancel: cancel, doneCh: doneCh}
 	r.mu.Unlock()
 
 	var watch chan events.Event

+ 2 - 2
vendor/src/github.com/docker/swarmkit/manager/orchestrator/tasks.go

@@ -56,7 +56,7 @@ func (r *ReplicatedOrchestrator) initTasks(ctx context.Context, readTx store.Rea
 				continue
 			}
 			// TODO(aluzzardi): This is shady. We should have a more generic condition.
-			if t.DesiredState != api.TaskStateAccepted || !isReplicatedService(service) {
+			if t.DesiredState != api.TaskStateReady || !isReplicatedService(service) {
 				continue
 			}
 			restartDelay := defaultRestartDelay
@@ -80,7 +80,7 @@ func (r *ReplicatedOrchestrator) initTasks(ctx context.Context, readTx store.Rea
 						_ = batch.Update(func(tx store.Tx) error {
 							t := store.GetTask(tx, t.ID)
 							// TODO(aluzzardi): This is shady as well. We should have a more generic condition.
-							if t == nil || t.DesiredState != api.TaskStateAccepted {
+							if t == nil || t.DesiredState != api.TaskStateReady {
 								return nil
 							}
 							r.restarts.DelayStart(ctx, tx, nil, t.ID, restartDelay, true)

+ 164 - 21
vendor/src/github.com/docker/swarmkit/manager/orchestrator/updater.go

@@ -8,6 +8,7 @@ import (
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/go-events"
 	"github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/log"
 	"github.com/docker/swarmkit/manager/state"
@@ -43,13 +44,17 @@ func (u *UpdateSupervisor) Update(ctx context.Context, cluster *api.Cluster, ser
 	id := service.ID
 
 	if update, ok := u.updates[id]; ok {
+		if !update.isServiceDirty(service) {
+			// There's already an update working towards this goal.
+			return
+		}
 		update.Cancel()
 	}
 
-	update := NewUpdater(u.store, u.restarts)
+	update := NewUpdater(u.store, u.restarts, cluster, service)
 	u.updates[id] = update
 	go func() {
-		update.Run(ctx, cluster, service, tasks)
+		update.Run(ctx, tasks)
 		u.l.Lock()
 		if u.updates[id] == update {
 			delete(u.updates, id)
@@ -74,6 +79,9 @@ type Updater struct {
 	watchQueue *watch.Queue
 	restarts   *RestartSupervisor
 
+	cluster    *api.Cluster
+	newService *api.Service
+
 	// stopChan signals to the state machine to stop running.
 	stopChan chan struct{}
 	// doneChan is closed when the state machine terminates.
@@ -81,11 +89,13 @@ type Updater struct {
 }
 
 // NewUpdater creates a new Updater.
-func NewUpdater(store *store.MemoryStore, restartSupervisor *RestartSupervisor) *Updater {
+func NewUpdater(store *store.MemoryStore, restartSupervisor *RestartSupervisor, cluster *api.Cluster, newService *api.Service) *Updater {
 	return &Updater{
 		store:      store,
 		watchQueue: store.WatchQueue(),
 		restarts:   restartSupervisor,
+		cluster:    cluster.Copy(),
+		newService: newService.Copy(),
 		stopChan:   make(chan struct{}),
 		doneChan:   make(chan struct{}),
 	}
@@ -98,22 +108,35 @@ func (u *Updater) Cancel() {
 }
 
 // Run starts the update and returns only once its complete or cancelled.
-func (u *Updater) Run(ctx context.Context, cluster *api.Cluster, service *api.Service, tasks []*api.Task) {
+func (u *Updater) Run(ctx context.Context, tasks []*api.Task) {
 	defer close(u.doneChan)
 
+	service := u.newService
+
+	// If the update is in a PAUSED state, we should not do anything.
+	if service.UpdateStatus != nil && service.UpdateStatus.State == api.UpdateStatus_PAUSED {
+		return
+	}
+
 	dirtyTasks := []*api.Task{}
 	for _, t := range tasks {
-		if !reflect.DeepEqual(service.Spec.Task, t.Spec) ||
-			(t.Endpoint != nil &&
-				!reflect.DeepEqual(service.Spec.Endpoint, t.Endpoint.Spec)) {
+		if u.isTaskDirty(t) {
 			dirtyTasks = append(dirtyTasks, t)
 		}
 	}
 	// Abort immediately if all tasks are clean.
 	if len(dirtyTasks) == 0 {
+		if service.UpdateStatus != nil && service.UpdateStatus.State == api.UpdateStatus_UPDATING {
+			u.completeUpdate(ctx, service.ID)
+		}
 		return
 	}
 
+	// If there's no update in progress, we are starting one.
+	if service.UpdateStatus == nil {
+		u.startUpdate(ctx, service.ID)
+	}
+
 	parallelism := 0
 	if service.Spec.Update != nil {
 		parallelism = int(service.Spec.Update.Parallelism)
@@ -130,39 +153,76 @@ func (u *Updater) Run(ctx context.Context, cluster *api.Cluster, service *api.Se
 	wg.Add(parallelism)
 	for i := 0; i < parallelism; i++ {
 		go func() {
-			u.worker(ctx, cluster, service, taskQueue)
+			u.worker(ctx, taskQueue)
 			wg.Done()
 		}()
 	}
 
-	for _, t := range dirtyTasks {
-		// Wait for a worker to pick up the task or abort the update, whichever comes first.
-		select {
-		case <-u.stopChan:
-			break
+	var failedTaskWatch chan events.Event
+
+	if service.Spec.Update == nil || service.Spec.Update.FailureAction == api.UpdateConfig_PAUSE {
+		var cancelWatch func()
+		failedTaskWatch, cancelWatch = state.Watch(
+			u.store.WatchQueue(),
+			state.EventUpdateTask{
+				Task:   &api.Task{ServiceID: service.ID, Status: api.TaskStatus{State: api.TaskStateRunning}},
+				Checks: []state.TaskCheckFunc{state.TaskCheckServiceID, state.TaskCheckStateGreaterThan},
+			},
+		)
+		defer cancelWatch()
+	}
 
-		case taskQueue <- t:
+	stopped := false
+
+taskLoop:
+	for _, t := range dirtyTasks {
+	retryLoop:
+		for {
+			// Wait for a worker to pick up the task or abort the update, whichever comes first.
+			select {
+			case <-u.stopChan:
+				stopped = true
+				break taskLoop
+			case ev := <-failedTaskWatch:
+				failedTask := ev.(state.EventUpdateTask).Task
+
+				// If this failed/completed task has a spec matching
+				// the one we're updating to, we should pause the
+				// update.
+				if !u.isTaskDirty(failedTask) {
+					stopped = true
+					message := fmt.Sprintf("update paused due to failure or early termination of task %s", failedTask.ID)
+					u.pauseUpdate(ctx, service.ID, message)
+					break taskLoop
+				}
+			case taskQueue <- t:
+				break retryLoop
+			}
 		}
 	}
 
 	close(taskQueue)
 	wg.Wait()
+
+	if !stopped {
+		u.completeUpdate(ctx, service.ID)
+	}
 }
 
-func (u *Updater) worker(ctx context.Context, cluster *api.Cluster, service *api.Service, queue <-chan *api.Task) {
+func (u *Updater) worker(ctx context.Context, queue <-chan *api.Task) {
 	for t := range queue {
-		updated := newTask(cluster, service, t.Slot)
+		updated := newTask(u.cluster, u.newService, t.Slot)
 		updated.DesiredState = api.TaskStateReady
-		if isGlobalService(service) {
+		if isGlobalService(u.newService) {
 			updated.NodeID = t.NodeID
 		}
 
-		if err := u.updateTask(ctx, service, t, updated); err != nil {
+		if err := u.updateTask(ctx, t, updated); err != nil {
 			log.G(ctx).WithError(err).WithField("task.id", t.ID).Error("update failed")
 		}
 
-		if service.Spec.Update != nil && (service.Spec.Update.Delay.Seconds != 0 || service.Spec.Update.Delay.Nanos != 0) {
-			delay, err := ptypes.Duration(&service.Spec.Update.Delay)
+		if u.newService.Spec.Update != nil && (u.newService.Spec.Update.Delay.Seconds != 0 || u.newService.Spec.Update.Delay.Nanos != 0) {
+			delay, err := ptypes.Duration(&u.newService.Spec.Update.Delay)
 			if err != nil {
 				log.G(ctx).WithError(err).Error("invalid update delay")
 				continue
@@ -176,7 +236,7 @@ func (u *Updater) worker(ctx context.Context, cluster *api.Cluster, service *api
 	}
 }
 
-func (u *Updater) updateTask(ctx context.Context, service *api.Service, original, updated *api.Task) error {
+func (u *Updater) updateTask(ctx context.Context, original, updated *api.Task) error {
 	log.G(ctx).Debugf("replacing %s with %s", original.ID, updated.ID)
 	// Kick off the watch before even creating the updated task. This is in order to avoid missing any event.
 	taskUpdates, cancel := state.Watch(u.watchQueue, state.EventUpdateTask{
@@ -231,3 +291,86 @@ func (u *Updater) updateTask(ctx context.Context, service *api.Service, original
 		}
 	}
 }
+
+func (u *Updater) isTaskDirty(t *api.Task) bool {
+	return !reflect.DeepEqual(u.newService.Spec.Task, t.Spec) ||
+		(t.Endpoint != nil && !reflect.DeepEqual(u.newService.Spec.Endpoint, t.Endpoint.Spec))
+}
+
+func (u *Updater) isServiceDirty(service *api.Service) bool {
+	return !reflect.DeepEqual(u.newService.Spec.Task, service.Spec.Task) ||
+		!reflect.DeepEqual(u.newService.Spec.Endpoint, service.Spec.Endpoint)
+}
+
+func (u *Updater) startUpdate(ctx context.Context, serviceID string) {
+	err := u.store.Update(func(tx store.Tx) error {
+		service := store.GetService(tx, serviceID)
+		if service == nil {
+			return nil
+		}
+		if service.UpdateStatus != nil {
+			return nil
+		}
+
+		service.UpdateStatus = &api.UpdateStatus{
+			State:     api.UpdateStatus_UPDATING,
+			Message:   "update in progress",
+			StartedAt: ptypes.MustTimestampProto(time.Now()),
+		}
+
+		return store.UpdateService(tx, service)
+	})
+
+	if err != nil {
+		log.G(ctx).WithError(err).Errorf("failed to mark update of service %s in progress", serviceID)
+	}
+}
+
+func (u *Updater) pauseUpdate(ctx context.Context, serviceID, message string) {
+	log.G(ctx).Debugf("pausing update of service %s", serviceID)
+
+	err := u.store.Update(func(tx store.Tx) error {
+		service := store.GetService(tx, serviceID)
+		if service == nil {
+			return nil
+		}
+		if service.UpdateStatus == nil {
+			// The service was updated since we started this update
+			return nil
+		}
+
+		service.UpdateStatus.State = api.UpdateStatus_PAUSED
+		service.UpdateStatus.Message = message
+
+		return store.UpdateService(tx, service)
+	})
+
+	if err != nil {
+		log.G(ctx).WithError(err).Errorf("failed to pause update of service %s", serviceID)
+	}
+}
+
+func (u *Updater) completeUpdate(ctx context.Context, serviceID string) {
+	log.G(ctx).Debugf("update of service %s complete", serviceID)
+
+	err := u.store.Update(func(tx store.Tx) error {
+		service := store.GetService(tx, serviceID)
+		if service == nil {
+			return nil
+		}
+		if service.UpdateStatus == nil {
+			// The service was changed since we started this update
+			return nil
+		}
+
+		service.UpdateStatus.State = api.UpdateStatus_COMPLETED
+		service.UpdateStatus.Message = "update completed"
+		service.UpdateStatus.CompletedAt = ptypes.MustTimestampProto(time.Now())
+
+		return store.UpdateService(tx, service)
+	})
+
+	if err != nil {
+		log.G(ctx).WithError(err).Errorf("failed to mark update of service %s complete", serviceID)
+	}
+}

+ 24 - 2
vendor/src/github.com/docker/swarmkit/manager/state/raft/raft.go

@@ -2,8 +2,10 @@ package raft
 
 import (
 	"errors"
+	"fmt"
 	"math"
 	"math/rand"
+	"net"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -537,13 +539,33 @@ func (n *Node) Join(ctx context.Context, req *api.JoinRequest) (*api.JoinRespons
 		}
 	}
 
+	remoteAddr := req.Addr
+
+	// If the joining node sent an address like 0.0.0.0:4242, automatically
+	// determine its actual address based on the GRPC connection. This
+	// avoids the need for a prospective member to know its own address.
+
+	requestHost, requestPort, err := net.SplitHostPort(remoteAddr)
+	if err != nil {
+		return nil, fmt.Errorf("invalid address %s in raft join request", remoteAddr)
+	}
+
+	requestIP := net.ParseIP(requestHost)
+	if requestIP != nil && requestIP.IsUnspecified() {
+		remoteHost, _, err := net.SplitHostPort(nodeInfo.RemoteAddr)
+		if err != nil {
+			return nil, err
+		}
+		remoteAddr = net.JoinHostPort(remoteHost, requestPort)
+	}
+
 	// We do not bother submitting a configuration change for the
 	// new member if we can't contact it back using its address
-	if err := n.checkHealth(ctx, req.Addr, 5*time.Second); err != nil {
+	if err := n.checkHealth(ctx, remoteAddr, 5*time.Second); err != nil {
 		return nil, err
 	}
 
-	err = n.addMember(ctx, req.Addr, raftID, nodeInfo.NodeID)
+	err = n.addMember(ctx, remoteAddr, raftID, nodeInfo.NodeID)
 	if err != nil {
 		log.WithError(err).Errorf("failed to add member")
 		return nil, err

+ 5 - 0
vendor/src/github.com/docker/swarmkit/manager/state/watch.go

@@ -40,6 +40,11 @@ func TaskCheckNodeID(t1, t2 *api.Task) bool {
 	return t1.NodeID == t2.NodeID
 }
 
+// TaskCheckServiceID is a TaskCheckFunc for matching service IDs.
+func TaskCheckServiceID(t1, t2 *api.Task) bool {
+	return t1.ServiceID == t2.ServiceID
+}
+
 // TaskCheckStateGreaterThan is a TaskCheckFunc for checking task state.
 func TaskCheckStateGreaterThan(t1, t2 *api.Task) bool {
 	return t2.Status.State > t1.Status.State

+ 12 - 1
vendor/src/github.com/vishvananda/netlink/addr_linux.go

@@ -8,6 +8,7 @@ import (
 	"syscall"
 
 	"github.com/vishvananda/netlink/nl"
+	"github.com/vishvananda/netns"
 )
 
 // IFA_FLAGS is a u32 attribute.
@@ -192,7 +193,17 @@ type AddrUpdate struct {
 // AddrSubscribe takes a chan down which notifications will be sent
 // when addresses change.  Close the 'done' chan to stop subscription.
 func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error {
-	s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR)
+	return addrSubscribe(netns.None(), netns.None(), ch, done)
+}
+
+// AddrSubscribeAt works like AddrSubscribe plus it allows the caller
+// to choose the network namespace in which to subscribe (ns).
+func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
+	return addrSubscribe(ns, netns.None(), ch, done)
+}
+
+func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
+	s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR)
 	if err != nil {
 		return err
 	}

+ 2 - 2
vendor/src/github.com/vishvananda/netlink/filter_linux.go

@@ -143,7 +143,7 @@ func (h *Handle) FilterAdd(filter Filter) error {
 		if u32.RedirIndex != 0 {
 			u32.Actions = append([]Action{NewMirredAction(u32.RedirIndex)}, u32.Actions...)
 		}
-		if err := encodeActions(actionsAttr, u32.Actions); err != nil {
+		if err := EncodeActions(actionsAttr, u32.Actions); err != nil {
 			return err
 		}
 	} else if fw, ok := filter.(*Fw); ok {
@@ -309,7 +309,7 @@ func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) {
 	attrs.Bindcnt = int(tcgen.Bindcnt)
 }
 
-func encodeActions(attr *nl.RtAttr, actions []Action) error {
+func EncodeActions(attr *nl.RtAttr, actions []Action) error {
 	tabIndex := int(nl.TCA_ACT_TAB)
 
 	for _, action := range actions {

+ 14 - 3
vendor/src/github.com/vishvananda/netlink/link_linux.go

@@ -10,6 +10,7 @@ import (
 	"unsafe"
 
 	"github.com/vishvananda/netlink/nl"
+	"github.com/vishvananda/netns"
 )
 
 const SizeofLinkStats = 0x5c
@@ -425,7 +426,7 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
 		nl.NewRtAttrChild(data, nl.IFLA_VXLAN_UDP_CSUM, boolAttr(vxlan.UDPCSum))
 	}
 	if vxlan.GBP {
-		nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, boolAttr(vxlan.GBP))
+		nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, []byte{})
 	}
 	if vxlan.NoAge {
 		nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0))
@@ -1011,7 +1012,17 @@ type LinkUpdate struct {
 // LinkSubscribe takes a chan down which notifications will be sent
 // when links change.  Close the 'done' chan to stop subscription.
 func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error {
-	s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK)
+	return linkSubscribe(netns.None(), netns.None(), ch, done)
+}
+
+// LinkSubscribeAt works like LinkSubscribe plus it allows the caller
+// to choose the network namespace in which to subscribe (ns).
+func LinkSubscribeAt(ns netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error {
+	return linkSubscribe(ns, netns.None(), ch, done)
+}
+
+func linkSubscribe(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error {
+	s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK)
 	if err != nil {
 		return err
 	}
@@ -1152,7 +1163,7 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
 		case nl.IFLA_VXLAN_UDP_CSUM:
 			vxlan.UDPCSum = int8(datum.Value[0]) != 0
 		case nl.IFLA_VXLAN_GBP:
-			vxlan.GBP = int8(datum.Value[0]) != 0
+			vxlan.GBP = true
 		case nl.IFLA_VXLAN_AGEING:
 			vxlan.Age = int(native.Uint32(datum.Value[0:4]))
 			vxlan.NoAge = vxlan.Age == 0

+ 57 - 6
vendor/src/github.com/vishvananda/netlink/nl/nl_linux.go

@@ -331,24 +331,63 @@ func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
 // moves back into it when done. If newNs is close, the socket will be opened
 // in the current network namespace.
 func GetNetlinkSocketAt(newNs, curNs netns.NsHandle, protocol int) (*NetlinkSocket, error) {
-	var err error
+	c, err := executeInNetns(newNs, curNs)
+	if err != nil {
+		return nil, err
+	}
+	defer c()
+	return getNetlinkSocket(protocol)
+}
 
+// executeInNetns sets execution of the code following this call to the
+// network namespace newNs, then moves the thread back to curNs if open,
+// otherwise to the current netns at the time the function was invoked
+// In case of success, the caller is expected to execute the returned function
+// at the end of the code that needs to be executed in the network namespace.
+// Example:
+// func jobAt(...) error {
+//      d, err := executeInNetns(...)
+//      if err != nil { return err}
+//      defer d()
+//      < code which needs to be executed in specific netns>
+//  }
+// TODO: his function probably belongs to netns pkg.
+func executeInNetns(newNs, curNs netns.NsHandle) (func(), error) {
+	var (
+		err       error
+		moveBack  func(netns.NsHandle) error
+		closeNs   func() error
+		unlockThd func()
+	)
+	restore := func() {
+		// order matters
+		if moveBack != nil {
+			moveBack(curNs)
+		}
+		if closeNs != nil {
+			closeNs()
+		}
+		if unlockThd != nil {
+			unlockThd()
+		}
+	}
 	if newNs.IsOpen() {
 		runtime.LockOSThread()
-		defer runtime.UnlockOSThread()
+		unlockThd = runtime.UnlockOSThread
 		if !curNs.IsOpen() {
 			if curNs, err = netns.Get(); err != nil {
+				restore()
 				return nil, fmt.Errorf("could not get current namespace while creating netlink socket: %v", err)
 			}
-			defer curNs.Close()
+			closeNs = curNs.Close
 		}
 		if err := netns.Set(newNs); err != nil {
+			restore()
 			return nil, fmt.Errorf("failed to set into network namespace %d while creating netlink socket: %v", newNs, err)
 		}
-		defer netns.Set(curNs)
+		moveBack = netns.Set
 	}
-
-	return getNetlinkSocket(protocol)
+	return restore, nil
 }
 
 // Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
@@ -377,6 +416,18 @@ func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
 	return s, nil
 }
 
+// SubscribeAt works like Subscribe plus let's the caller choose the network
+// namespace in which the socket would be opened (newNs). Then control goes back
+// to curNs if open, otherwise to the netns at the time this function was called.
+func SubscribeAt(newNs, curNs netns.NsHandle, protocol int, groups ...uint) (*NetlinkSocket, error) {
+	c, err := executeInNetns(newNs, curNs)
+	if err != nil {
+		return nil, err
+	}
+	defer c()
+	return Subscribe(protocol, groups...)
+}
+
 func (s *NetlinkSocket) Close() {
 	syscall.Close(s.fd)
 	s.fd = -1

+ 30 - 0
vendor/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go

@@ -10,6 +10,7 @@ const (
 	SizeofXfrmUsersaInfo  = 0xe0
 	SizeofXfrmAlgo        = 0x44
 	SizeofXfrmAlgoAuth    = 0x48
+	SizeofXfrmAlgoAEAD    = 0x48
 	SizeofXfrmEncapTmpl   = 0x18
 	SizeofXfrmUsersaFlush = 0x8
 )
@@ -194,6 +195,35 @@ func (msg *XfrmAlgoAuth) Serialize() []byte {
 //   char    alg_key[0];
 // }
 
+type XfrmAlgoAEAD struct {
+	AlgName   [64]byte
+	AlgKeyLen uint32
+	AlgICVLen uint32
+	AlgKey    []byte
+}
+
+func (msg *XfrmAlgoAEAD) Len() int {
+	return SizeofXfrmAlgoAEAD + int(msg.AlgKeyLen/8)
+}
+
+func DeserializeXfrmAlgoAEAD(b []byte) *XfrmAlgoAEAD {
+	ret := XfrmAlgoAEAD{}
+	copy(ret.AlgName[:], b[0:64])
+	ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64]))
+	ret.AlgICVLen = *(*uint32)(unsafe.Pointer(&b[68]))
+	ret.AlgKey = b[72:ret.Len()]
+	return &ret
+}
+
+func (msg *XfrmAlgoAEAD) Serialize() []byte {
+	b := make([]byte, msg.Len())
+	copy(b[0:64], msg.AlgName[:])
+	copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:])
+	copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgICVLen)))[:])
+	copy(b[72:msg.Len()], msg.AlgKey[:])
+	return b
+}
+
 // struct xfrm_encap_tmpl {
 //   __u16   encap_type;
 //   __be16    encap_sport;

+ 12 - 1
vendor/src/github.com/vishvananda/netlink/route_linux.go

@@ -6,6 +6,7 @@ import (
 	"syscall"
 
 	"github.com/vishvananda/netlink/nl"
+	"github.com/vishvananda/netns"
 )
 
 // RtAttr is shared so it is in netlink_linux.go
@@ -421,7 +422,17 @@ func (h *Handle) RouteGet(destination net.IP) ([]Route, error) {
 // RouteSubscribe takes a chan down which notifications will be sent
 // when routes are added or deleted. Close the 'done' chan to stop subscription.
 func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error {
-	s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE)
+	return routeSubscribeAt(netns.None(), netns.None(), ch, done)
+}
+
+// RouteSubscribeAt works like RouteSubscribe plus it allows the caller
+// to choose the network namespace in which to subscribe (ns).
+func RouteSubscribeAt(ns netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error {
+	return routeSubscribeAt(ns, netns.None(), ch, done)
+}
+
+func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error {
+	s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE)
 	if err != nil {
 		return err
 	}

+ 12 - 3
vendor/src/github.com/vishvananda/netlink/xfrm_state.go

@@ -10,10 +10,18 @@ type XfrmStateAlgo struct {
 	Name        string
 	Key         []byte
 	TruncateLen int // Auth only
+	ICVLen      int // AEAD only
 }
 
 func (a XfrmStateAlgo) String() string {
-	return fmt.Sprintf("{Name: %s, Key: 0x%x, TruncateLen: %d}", a.Name, a.Key, a.TruncateLen)
+	base := fmt.Sprintf("{Name: %s, Key: 0x%x", a.Name, a.Key)
+	if a.TruncateLen != 0 {
+		base = fmt.Sprintf("%s, Truncate length: %d", base, a.TruncateLen)
+	}
+	if a.ICVLen != 0 {
+		base = fmt.Sprintf("%s, ICV length: %d", base, a.ICVLen)
+	}
+	return fmt.Sprintf("%s}", base)
 }
 
 // EncapType is an enum representing the optional packet encapsulation.
@@ -73,12 +81,13 @@ type XfrmState struct {
 	Mark         *XfrmMark
 	Auth         *XfrmStateAlgo
 	Crypt        *XfrmStateAlgo
+	Aead         *XfrmStateAlgo
 	Encap        *XfrmStateEncap
 }
 
 func (sa XfrmState) String() string {
-	return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Encap: %v",
-		sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Encap)
+	return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Aead: %v,Encap: %v",
+		sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Aead, sa.Encap)
 }
 func (sa XfrmState) Print(stats bool) string {
 	if !stats {

+ 24 - 0
vendor/src/github.com/vishvananda/netlink/xfrm_state_linux.go

@@ -35,6 +35,20 @@ func writeStateAlgoAuth(a *XfrmStateAlgo) []byte {
 	return algo.Serialize()
 }
 
+func writeStateAlgoAead(a *XfrmStateAlgo) []byte {
+	algo := nl.XfrmAlgoAEAD{
+		AlgKeyLen: uint32(len(a.Key) * 8),
+		AlgICVLen: uint32(a.ICVLen),
+		AlgKey:    a.Key,
+	}
+	end := len(a.Name)
+	if end > 64 {
+		end = 64
+	}
+	copy(algo.AlgName[:end], a.Name)
+	return algo.Serialize()
+}
+
 func writeMark(m *XfrmMark) []byte {
 	mark := &nl.XfrmMark{
 		Value: m.Value,
@@ -97,6 +111,10 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
 		out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt))
 		req.AddData(out)
 	}
+	if state.Aead != nil {
+		out := nl.NewRtAttr(nl.XFRMA_ALG_AEAD, writeStateAlgoAead(state.Aead))
+		req.AddData(out)
+	}
 	if state.Encap != nil {
 		encapData := make([]byte, nl.SizeofXfrmEncapTmpl)
 		encap := nl.DeserializeXfrmEncapTmpl(encapData)
@@ -271,6 +289,12 @@ func parseXfrmState(m []byte, family int) (*XfrmState, error) {
 			state.Auth.Name = nl.BytesToString(algo.AlgName[:])
 			state.Auth.Key = algo.AlgKey
 			state.Auth.TruncateLen = int(algo.AlgTruncLen)
+		case nl.XFRMA_ALG_AEAD:
+			state.Aead = new(XfrmStateAlgo)
+			algo := nl.DeserializeXfrmAlgoAEAD(attr.Value[:])
+			state.Aead.Name = nl.BytesToString(algo.AlgName[:])
+			state.Aead.Key = algo.AlgKey
+			state.Aead.ICVLen = int(algo.AlgICVLen)
 		case nl.XFRMA_ENCAP:
 			encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:])
 			state.Encap = new(XfrmStateEncap)