Browse Source

Merge pull request #29963 from stevvooe/use-digest-package

*: use opencontainers/go-digest package
Aaron Lehmann 8 years ago
parent
commit
7c62f58b27
90 changed files with 767 additions and 540 deletions
  1. 1 0
      api/types/reference/image_reference_test.go
  2. 2 2
      cli/command/image/trust.go
  3. 2 2
      cli/command/service/trust.go
  4. 1 1
      cli/compose/schema/bindata.go
  5. 2 2
      daemon/cluster/cluster.go
  6. 2 2
      daemon/cluster/convert/node.go
  7. 2 2
      daemon/cluster/executor/container/adapter.go
  8. 1 1
      daemon/disk_usage.go
  9. 2 2
      daemon/image_pull.go
  10. 1 1
      daemon/prune.go
  11. 1 1
      distribution/config.go
  12. 1 1
      distribution/metadata/v2_metadata_service.go
  13. 1 1
      distribution/metadata/v2_metadata_service_test.go
  14. 1 1
      distribution/pull.go
  15. 5 17
      distribution/pull_v2.go
  16. 1 1
      distribution/pull_v2_test.go
  17. 1 1
      distribution/push_v1.go
  18. 2 2
      distribution/push_v2.go
  19. 1 1
      distribution/push_v2_test.go
  20. 1 1
      distribution/xfer/download_test.go
  21. 1 1
      image/fs.go
  22. 1 1
      image/fs_test.go
  23. 1 1
      image/image.go
  24. 5 4
      image/store.go
  25. 1 1
      image/store_test.go
  26. 1 1
      image/tarexport/load.go
  27. 1 1
      image/tarexport/save.go
  28. 1 1
      image/v1/imagev1.go
  29. 1 1
      integration-cli/docker_cli_by_digest_test.go
  30. 1 1
      integration-cli/docker_cli_pull_local_test.go
  31. 2 2
      integration-cli/docker_cli_pull_test.go
  32. 1 1
      integration-cli/docker_cli_save_load_test.go
  33. 1 1
      integration-cli/registry/registry.go
  34. 2 2
      layer/empty_test.go
  35. 4 4
      layer/filestore.go
  36. 1 1
      layer/filestore_test.go
  37. 1 1
      layer/layer.go
  38. 2 2
      layer/layer_store.go
  39. 1 1
      layer/layer_test.go
  40. 1 1
      layer/layer_windows.go
  41. 2 2
      layer/migration.go
  42. 2 6
      layer/ro_layer.go
  43. 4 4
      migrate/v1/migratev1.go
  44. 1 1
      migrate/v1/migratev1_test.go
  45. 3 3
      plugin/backend_linux.go
  46. 5 4
      plugin/blobstore.go
  47. 1 1
      plugin/manager.go
  48. 1 1
      plugin/manager_linux.go
  49. 1 1
      plugin/v2/plugin.go
  50. 2 2
      reference/reference.go
  51. 1 1
      reference/reference_test.go
  52. 1 1
      reference/store.go
  53. 1 1
      reference/store_test.go
  54. 3 2
      vendor.conf
  55. 1 1
      vendor/github.com/docker/distribution/blobs.go
  56. 16 14
      vendor/github.com/docker/distribution/digestset/set.go
  57. 1 1
      vendor/github.com/docker/distribution/errors.go
  58. 1 1
      vendor/github.com/docker/distribution/manifest/manifestlist/manifestlist.go
  59. 1 1
      vendor/github.com/docker/distribution/manifest/schema1/config_builder.go
  60. 1 1
      vendor/github.com/docker/distribution/manifest/schema1/manifest.go
  61. 1 1
      vendor/github.com/docker/distribution/manifest/schema1/reference_builder.go
  62. 1 1
      vendor/github.com/docker/distribution/manifest/schema2/builder.go
  63. 1 1
      vendor/github.com/docker/distribution/manifest/schema2/manifest.go
  64. 1 1
      vendor/github.com/docker/distribution/manifests.go
  65. 2 2
      vendor/github.com/docker/distribution/reference/reference.go
  66. 1 1
      vendor/github.com/docker/distribution/registry/api/v2/descriptors.go
  67. 5 5
      vendor/github.com/docker/distribution/registry/client/repository.go
  68. 1 1
      vendor/github.com/docker/distribution/registry/storage/cache/cachedblobdescriptorstore.go
  69. 1 1
      vendor/github.com/docker/distribution/registry/storage/cache/memory/memory.go
  70. 10 9
      vendor/github.com/docker/swarmkit/agent/worker.go
  71. 116 75
      vendor/github.com/docker/swarmkit/api/objects.pb.go
  72. 11 0
      vendor/github.com/docker/swarmkit/api/objects.proto
  73. 116 116
      vendor/github.com/docker/swarmkit/api/specs.pb.go
  74. 2 2
      vendor/github.com/docker/swarmkit/api/specs.proto
  75. 2 2
      vendor/github.com/docker/swarmkit/ca/certificates.go
  76. 2 2
      vendor/github.com/docker/swarmkit/ca/config.go
  77. 4 3
      vendor/github.com/docker/swarmkit/ca/server.go
  78. 1 1
      vendor/github.com/docker/swarmkit/manager/constraint/constraint.go
  79. 3 16
      vendor/github.com/docker/swarmkit/manager/controlapi/node.go
  80. 32 22
      vendor/github.com/docker/swarmkit/manager/manager.go
  81. 167 0
      vendor/github.com/docker/swarmkit/manager/role_manager.go
  82. 55 44
      vendor/github.com/docker/swarmkit/manager/state/raft/raft.go
  83. 6 2
      vendor/github.com/docker/swarmkit/manager/state/raft/wait.go
  84. 1 1
      vendor/github.com/docker/swarmkit/manager/state/store/nodes.go
  85. 52 40
      vendor/github.com/docker/swarmkit/node/node.go
  86. 15 31
      vendor/github.com/opencontainers/go-digest/algorithm.go
  87. 23 27
      vendor/github.com/opencontainers/go-digest/digest.go
  88. 25 0
      vendor/github.com/opencontainers/go-digest/digester.go
  89. 0 0
      vendor/github.com/opencontainers/go-digest/doc.go
  90. 0 13
      vendor/github.com/opencontainers/go-digest/verifiers.go

+ 1 - 0
api/types/reference/image_reference_test.go

@@ -1,6 +1,7 @@
 package reference
 
 import (
+	_ "crypto/sha256"
 	"testing"
 )
 

+ 2 - 2
cli/command/image/trust.go

@@ -10,7 +10,6 @@ import (
 	"sort"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/trust"
@@ -19,6 +18,7 @@ import (
 	"github.com/docker/docker/registry"
 	"github.com/docker/notary/client"
 	"github.com/docker/notary/tuf/data"
+	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 )
 
@@ -58,7 +58,7 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI
 		var pushResult types.PushResult
 		err := json.Unmarshal(*aux, &pushResult)
 		if err == nil && pushResult.Tag != "" {
-			if dgst, err := digest.ParseDigest(pushResult.Digest); err == nil {
+			if dgst, err := digest.Parse(pushResult.Digest); err == nil {
 				h, err := hex.DecodeString(dgst.Hex())
 				if err != nil {
 					target = nil

+ 2 - 2
cli/command/service/trust.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	distreference "github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/cli/command"
@@ -13,6 +12,7 @@ import (
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/notary/tuf/data"
+	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 )
@@ -30,7 +30,7 @@ func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.Serv
 	// could be parsed as a digest reference. Specifying an image ID
 	// is valid but not resolvable. There is no warning message for
 	// an image ID because it's valid to use one.
-	if _, err := digest.ParseDigest(image); err == nil {
+	if _, err := digest.Parse(image); err == nil {
 		return nil
 	}
 

+ 1 - 1
cli/compose/schema/bindata.go

@@ -182,6 +182,7 @@ type bintree struct {
 	Func     func() (*asset, error)
 	Children map[string]*bintree
 }
+
 var _bintree = &bintree{nil, map[string]*bintree{
 	"data": &bintree{nil, map[string]*bintree{
 		"config_schema_v3.0.json": &bintree{dataConfig_schema_v30Json, map[string]*bintree{}},
@@ -234,4 +235,3 @@ func _filePath(dir, name string) string {
 	cannonicalName := strings.Replace(name, "\\", "/", -1)
 	return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
 }
-

+ 2 - 2
daemon/cluster/cluster.go

@@ -52,7 +52,6 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	distreference "github.com/docker/distribution/reference"
 	apierrors "github.com/docker/docker/api/errors"
 	apitypes "github.com/docker/docker/api/types"
@@ -73,6 +72,7 @@ import (
 	"github.com/docker/swarmkit/manager/encryption"
 	swarmnode "github.com/docker/swarmkit/node"
 	"github.com/docker/swarmkit/protobuf/ptypes"
+	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 )
@@ -832,7 +832,7 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
 // TODO(nishanttotla): After the packages converge, the function must
 // convert distreference.Named -> distreference.Canonical, and the logic simplified.
 func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
-	if _, err := digest.ParseDigest(image); err == nil {
+	if _, err := digest.Parse(image); err == nil {
 		return "", errors.New("image reference is an image ID")
 	}
 	ref, err := distreference.ParseNamed(image)

+ 2 - 2
daemon/cluster/convert/node.go

@@ -14,7 +14,7 @@ func NodeFromGRPC(n swarmapi.Node) types.Node {
 	node := types.Node{
 		ID: n.ID,
 		Spec: types.NodeSpec{
-			Role:         types.NodeRole(strings.ToLower(n.Spec.Role.String())),
+			Role:         types.NodeRole(strings.ToLower(n.Spec.DesiredRole.String())),
 			Availability: types.NodeAvailability(strings.ToLower(n.Spec.Availability.String())),
 		},
 		Status: types.NodeStatus{
@@ -74,7 +74,7 @@ func NodeSpecToGRPC(s types.NodeSpec) (swarmapi.NodeSpec, error) {
 		},
 	}
 	if role, ok := swarmapi.NodeRole_value[strings.ToUpper(string(s.Role))]; ok {
-		spec.Role = swarmapi.NodeRole(role)
+		spec.DesiredRole = swarmapi.NodeRole(role)
 	} else {
 		return swarmapi.NodeSpec{}, fmt.Errorf("invalid Role: %q", s.Role)
 	}

+ 2 - 2
daemon/cluster/executor/container/adapter.go

@@ -11,7 +11,6 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	containertypes "github.com/docker/docker/api/types/container"
@@ -24,6 +23,7 @@ import (
 	"github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/log"
 	"github.com/docker/swarmkit/protobuf/ptypes"
+	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 	"golang.org/x/time/rate"
 )
@@ -54,7 +54,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
 	spec := c.container.spec()
 
 	// Skip pulling if the image is referenced by image ID.
-	if _, err := digest.ParseDigest(spec.Image); err == nil {
+	if _, err := digest.Parse(spec.Image); err == nil {
 		return nil
 	}
 

+ 1 - 1
daemon/disk_usage.go

@@ -4,12 +4,12 @@ import (
 	"fmt"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/directory"
 	"github.com/docker/docker/volume"
+	"github.com/opencontainers/go-digest"
 )
 
 func (daemon *Daemon) getLayerRefs() map[layer.ChainID]int {

+ 2 - 2
daemon/image_pull.go

@@ -5,7 +5,6 @@ import (
 	"strings"
 
 	dist "github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/distribution"
@@ -13,6 +12,7 @@ import (
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
+	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 )
 
@@ -32,7 +32,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead
 	if tag != "" {
 		// The "tag" could actually be a digest.
 		var dgst digest.Digest
-		dgst, err = digest.ParseDigest(tag)
+		dgst, err = digest.Parse(tag)
 		if err == nil {
 			ref, err = reference.WithDigest(reference.TrimNamed(ref), dgst)
 		} else {

+ 1 - 1
daemon/prune.go

@@ -5,7 +5,6 @@ import (
 	"regexp"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/image"
@@ -15,6 +14,7 @@ import (
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/volume"
 	"github.com/docker/libnetwork"
+	"github.com/opencontainers/go-digest"
 )
 
 // ContainersPrune removes unused containers

+ 1 - 1
distribution/config.go

@@ -7,7 +7,6 @@ import (
 	"runtime"
 
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/distribution/metadata"
@@ -18,6 +17,7 @@ import (
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/libtrust"
+	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 )
 

+ 1 - 1
distribution/metadata/v2_metadata_service.go

@@ -7,9 +7,9 @@ import (
 	"encoding/json"
 	"errors"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/layer"
+	"github.com/opencontainers/go-digest"
 )
 
 // V2MetadataService maps layer IDs to a set of known metadata for

+ 1 - 1
distribution/metadata/v2_metadata_service_test.go

@@ -8,8 +8,8 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/layer"
+	"github.com/opencontainers/go-digest"
 )
 
 func TestV2MetadataService(t *testing.T) {

+ 1 - 1
distribution/pull.go

@@ -5,12 +5,12 @@ import (
 	"fmt"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
+	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 )
 

+ 5 - 17
distribution/pull_v2.go

@@ -12,7 +12,6 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest/manifestlist"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema2"
@@ -29,6 +28,7 @@ import (
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
+	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 )
 
@@ -228,10 +228,7 @@ func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progre
 	defer reader.Close()
 
 	if ld.verifier == nil {
-		ld.verifier, err = digest.NewDigestVerifier(ld.digest)
-		if err != nil {
-			return nil, 0, xfer.DoNotRetry{Err: err}
-		}
+		ld.verifier = ld.digest.Verifier()
 	}
 
 	_, err = io.Copy(tmpFile, io.TeeReader(reader, ld.verifier))
@@ -716,10 +713,7 @@ func (p *v2Puller) pullSchema2Config(ctx context.Context, dgst digest.Digest) (c
 	}
 
 	// Verify image config digest
-	verifier, err := digest.NewDigestVerifier(dgst)
-	if err != nil {
-		return nil, err
-	}
+	verifier := dgst.Verifier()
 	if _, err := verifier.Write(configJSON); err != nil {
 		return nil, err
 	}
@@ -742,10 +736,7 @@ func schema2ManifestDigest(ref reference.Named, mfst distribution.Manifest) (dig
 
 	// If pull by digest, then verify the manifest digest.
 	if digested, isDigested := ref.(reference.Canonical); isDigested {
-		verifier, err := digest.NewDigestVerifier(digested.Digest())
-		if err != nil {
-			return "", err
-		}
+		verifier := digested.Digest().Verifier()
 		if _, err := verifier.Write(canonical); err != nil {
 			return "", err
 		}
@@ -798,10 +789,7 @@ func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference
 	// important to do this first, before any other content validation. If the
 	// digest cannot be verified, don't even bother with those other things.
 	if digested, isCanonical := ref.(reference.Canonical); isCanonical {
-		verifier, err := digest.NewDigestVerifier(digested.Digest())
-		if err != nil {
-			return nil, err
-		}
+		verifier := digested.Digest().Verifier()
 		if _, err := verifier.Write(signedManifest.Canonical); err != nil {
 			return nil, err
 		}

+ 1 - 1
distribution/pull_v2_test.go

@@ -8,9 +8,9 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/docker/reference"
+	"github.com/opencontainers/go-digest"
 )
 
 // TestFixManifestLayers checks that fixManifestLayers removes a duplicate

+ 1 - 1
distribution/push_v1.go

@@ -5,7 +5,6 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/dockerversion"
@@ -17,6 +16,7 @@ import (
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
+	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 )
 

+ 2 - 2
distribution/push_v2.go

@@ -13,7 +13,6 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema2"
 	distreference "github.com/docker/distribution/reference"
@@ -27,6 +26,7 @@ import (
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
+	"github.com/opencontainers/go-digest"
 )
 
 const (
@@ -435,7 +435,7 @@ func (pd *v2PushDescriptor) uploadUsingSession(
 		return distribution.Descriptor{}, fmt.Errorf("unsupported layer media type %s", m)
 	}
 
-	digester := digest.Canonical.New()
+	digester := digest.Canonical.Digester()
 	tee := io.TeeReader(reader, digester.Hash())
 
 	nn, err := layerUpload.ReadFrom(tee)

+ 1 - 1
distribution/push_v2_test.go

@@ -7,13 +7,13 @@ import (
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest/schema2"
 	distreference "github.com/docker/distribution/reference"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/reference"
+	"github.com/opencontainers/go-digest"
 )
 
 func TestGetRepositoryMountCandidates(t *testing.T) {

+ 1 - 1
distribution/xfer/download_test.go

@@ -12,10 +12,10 @@ import (
 	"time"
 
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/progress"
+	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 )
 

+ 1 - 1
image/fs.go

@@ -8,8 +8,8 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/pkg/ioutils"
+	"github.com/opencontainers/go-digest"
 )
 
 // DigestWalkFunc is function called by StoreBackend.Walk

+ 1 - 1
image/fs_test.go

@@ -11,7 +11,7 @@ import (
 	"path/filepath"
 	"testing"
 
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 func TestFSGetSet(t *testing.T) {

+ 1 - 1
image/image.go

@@ -6,8 +6,8 @@ import (
 	"io"
 	"time"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types/container"
+	"github.com/opencontainers/go-digest"
 )
 
 // ID is the content-addressable ID of an image.

+ 5 - 4
image/store.go

@@ -7,8 +7,9 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
+	"github.com/docker/distribution/digestset"
 	"github.com/docker/docker/layer"
+	"github.com/opencontainers/go-digest"
 )
 
 // Store is an interface for creating and accessing images
@@ -40,7 +41,7 @@ type store struct {
 	ls        LayerGetReleaser
 	images    map[ID]*imageMeta
 	fs        StoreBackend
-	digestSet *digest.Set
+	digestSet *digestset.Set
 }
 
 // NewImageStore returns new store object for given layer store
@@ -49,7 +50,7 @@ func NewImageStore(fs StoreBackend, ls LayerGetReleaser) (Store, error) {
 		ls:        ls,
 		images:    make(map[ID]*imageMeta),
 		fs:        fs,
-		digestSet: digest.NewSet(),
+		digestSet: digestset.NewSet(),
 	}
 
 	// load all current images and retain layers
@@ -170,7 +171,7 @@ func (is *store) Search(term string) (ID, error) {
 
 	dgst, err := is.digestSet.Lookup(term)
 	if err != nil {
-		if err == digest.ErrDigestNotFound {
+		if err == digestset.ErrDigestNotFound {
 			err = fmt.Errorf("No such image: %s", term)
 		}
 		return "", err

+ 1 - 1
image/store_test.go

@@ -5,8 +5,8 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/layer"
+	"github.com/opencontainers/go-digest"
 )
 
 func TestRestore(t *testing.T) {

+ 1 - 1
image/tarexport/load.go

@@ -11,7 +11,6 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
@@ -23,6 +22,7 @@ import (
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/reference"
+	"github.com/opencontainers/go-digest"
 )
 
 func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {

+ 1 - 1
image/tarexport/save.go

@@ -10,13 +10,13 @@ import (
 	"time"
 
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/reference"
+	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 )
 

+ 1 - 1
image/v1/imagev1.go

@@ -6,11 +6,11 @@ import (
 	"strings"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/opencontainers/go-digest"
 )
 
 // noFallbackMinVersion is the minimum version for which v1compatibility

+ 1 - 1
integration-cli/docker_cli_by_digest_test.go

@@ -8,13 +8,13 @@ import (
 	"regexp"
 	"strings"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/go-check/check"
+	"github.com/opencontainers/go-digest"
 )
 
 var (

+ 1 - 1
integration-cli/docker_cli_pull_local_test.go

@@ -11,12 +11,12 @@ import (
 	"strings"
 
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest"
 	"github.com/docker/distribution/manifest/manifestlist"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/go-check/check"
+	"github.com/opencontainers/go-digest"
 )
 
 // testPullImageWithAliases pulls a specific image tag and verifies that any aliases (i.e., other

+ 2 - 2
integration-cli/docker_cli_pull_test.go

@@ -7,9 +7,9 @@ import (
 	"sync"
 	"time"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/go-check/check"
+	"github.com/opencontainers/go-digest"
 )
 
 // TestPullFromCentralRegistry pulls an image from the central registry and verifies that the client
@@ -26,7 +26,7 @@ func (s *DockerHubPullSuite) TestPullFromCentralRegistry(c *check.C) {
 	matches := regexp.MustCompile(`Digest: (.+)\n`).FindAllStringSubmatch(out, -1)
 	c.Assert(len(matches), checker.Equals, 1, check.Commentf("expected exactly one image digest in the output"))
 	c.Assert(len(matches[0]), checker.Equals, 2, check.Commentf("unexpected number of submatches for the digest"))
-	_, err := digest.ParseDigest(matches[0][1])
+	_, err := digest.Parse(matches[0][1])
 	c.Check(err, checker.IsNil, check.Commentf("invalid digest %q in output", matches[0][1]))
 
 	// We should have a single entry in images.

+ 1 - 1
integration-cli/docker_cli_save_load_test.go

@@ -13,10 +13,10 @@ import (
 	"strings"
 	"time"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/pkg/testutil"
 	"github.com/go-check/check"
+	"github.com/opencontainers/go-digest"
 )
 
 // save a repo using gz compression and try to load it using stdout

+ 1 - 1
integration-cli/registry/registry.go

@@ -8,7 +8,7 @@ import (
 	"os/exec"
 	"path/filepath"
 
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 const (

+ 2 - 2
layer/empty_test.go

@@ -4,7 +4,7 @@ import (
 	"io"
 	"testing"
 
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 func TestEmptyLayer(t *testing.T) {
@@ -33,7 +33,7 @@ func TestEmptyLayer(t *testing.T) {
 		t.Fatalf("error streaming tar for empty layer: %v", err)
 	}
 
-	digester := digest.Canonical.New()
+	digester := digest.Canonical.Digester()
 	_, err = io.Copy(digester.Hash(), tarStream)
 
 	if err != nil {

+ 4 - 4
layer/filestore.go

@@ -15,8 +15,8 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/pkg/ioutils"
+	"github.com/opencontainers/go-digest"
 )
 
 var (
@@ -165,7 +165,7 @@ func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) {
 		return "", err
 	}
 
-	dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
+	dgst, err := digest.Parse(strings.TrimSpace(string(content)))
 	if err != nil {
 		return "", err
 	}
@@ -179,7 +179,7 @@ func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) {
 		return "", err
 	}
 
-	dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
+	dgst, err := digest.Parse(strings.TrimSpace(string(content)))
 	if err != nil {
 		return "", err
 	}
@@ -296,7 +296,7 @@ func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) {
 		return "", err
 	}
 
-	dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
+	dgst, err := digest.Parse(strings.TrimSpace(string(content)))
 	if err != nil {
 		return "", err
 	}

+ 1 - 1
layer/filestore_test.go

@@ -10,7 +10,7 @@ import (
 	"syscall"
 	"testing"
 
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 func randomLayerID(seed int64) ChainID {

+ 1 - 1
layer/layer.go

@@ -15,8 +15,8 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/pkg/archive"
+	"github.com/opencontainers/go-digest"
 )
 
 var (

+ 2 - 2
layer/layer_store.go

@@ -9,11 +9,11 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/daemon/graphdriver"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/opencontainers/go-digest"
 	"github.com/vbatts/tar-split/tar/asm"
 	"github.com/vbatts/tar-split/tar/storage"
 )
@@ -204,7 +204,7 @@ func (ls *layerStore) loadMount(mount string) error {
 }
 
 func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent string, layer *roLayer) error {
-	digester := digest.Canonical.New()
+	digester := digest.Canonical.Digester()
 	tr := io.TeeReader(ts, digester.Hash())
 
 	tsw, err := tx.TarSplitWriter(true)

+ 1 - 1
layer/layer_test.go

@@ -10,12 +10,12 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/daemon/graphdriver"
 	"github.com/docker/docker/daemon/graphdriver/vfs"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/opencontainers/go-digest"
 )
 
 func init() {

+ 1 - 1
layer/layer_windows.go

@@ -5,8 +5,8 @@ import (
 	"fmt"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/daemon/graphdriver"
+	"github.com/opencontainers/go-digest"
 )
 
 // GetLayerPath returns the path to a layer

+ 2 - 2
layer/migration.go

@@ -8,7 +8,7 @@ import (
 	"os"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 	"github.com/vbatts/tar-split/tar/asm"
 	"github.com/vbatts/tar-split/tar/storage"
 )
@@ -98,7 +98,7 @@ func (ls *layerStore) ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataP
 		return
 	}
 
-	dgst := digest.Canonical.New()
+	dgst := digest.Canonical.Digester()
 	err = ls.assembleTarTo(id, uncompressed, &size, dgst.Hash())
 	if err != nil {
 		return

+ 2 - 6
layer/ro_layer.go

@@ -5,7 +5,7 @@ import (
 	"io"
 
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 type roLayer struct {
@@ -156,14 +156,10 @@ func storeLayer(tx MetadataTransaction, layer *roLayer) error {
 }
 
 func newVerifiedReadCloser(rc io.ReadCloser, dgst digest.Digest) (io.ReadCloser, error) {
-	verifier, err := digest.NewDigestVerifier(dgst)
-	if err != nil {
-		return nil, err
-	}
 	return &verifiedReadCloser{
 		rc:       rc,
 		dgst:     dgst,
-		verifier: verifier,
+		verifier: dgst.Verifier(),
 	}, nil
 }
 

+ 4 - 4
migrate/v1/migratev1.go

@@ -14,13 +14,13 @@ import (
 	"encoding/json"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/image"
 	imagev1 "github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/reference"
+	"github.com/opencontainers/go-digest"
 )
 
 type graphIDRegistrar interface {
@@ -327,7 +327,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
 					logrus.Errorf("migrate tags: invalid name %q, %q", name, err)
 					continue
 				}
-				if dgst, err := digest.ParseDigest(tag); err == nil {
+				if dgst, err := digest.Parse(tag); err == nil {
 					canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
 					if err != nil {
 						logrus.Errorf("migrate tags: invalid digest %q, %q", dgst, err)
@@ -425,7 +425,7 @@ func migrateImage(id, root string, ls graphIDRegistrar, is image.Store, ms metad
 	if err != nil {
 		return err
 	}
-	diffID, err := digest.ParseDigest(string(diffIDData))
+	diffID, err := digest.Parse(string(diffIDData))
 	if err != nil {
 		return err
 	}
@@ -477,7 +477,7 @@ func migrateImage(id, root string, ls graphIDRegistrar, is image.Store, ms metad
 
 	checksum, err := ioutil.ReadFile(filepath.Join(root, graphDirName, id, "checksum"))
 	if err == nil { // best effort
-		dgst, err := digest.ParseDigest(string(checksum))
+		dgst, err := digest.Parse(string(checksum))
 		if err == nil {
 			V2MetadataService := metadata.NewV2MetadataService(ms)
 			V2MetadataService.Add(layer.DiffID(), metadata.V2Metadata{Digest: dgst})

+ 1 - 1
migrate/v1/migratev1_test.go

@@ -13,11 +13,11 @@ import (
 	"runtime"
 	"testing"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/reference"
+	"github.com/opencontainers/go-digest"
 )
 
 func TestMigrateRefs(t *testing.T) {

+ 3 - 3
plugin/backend_linux.go

@@ -16,7 +16,6 @@ import (
 	"strings"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/distribution"
@@ -29,6 +28,7 @@ import (
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/plugin/v2"
 	"github.com/docker/docker/reference"
+	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 )
@@ -123,7 +123,7 @@ func (s *tempConfigStore) Put(c []byte) (digest.Digest, error) {
 
 func (s *tempConfigStore) Get(d digest.Digest) ([]byte, error) {
 	if d != s.configDigest {
-		return nil, digest.ErrDigestNotFound
+		return nil, fmt.Errorf("digest not found")
 	}
 	return s.config, nil
 }
@@ -556,7 +556,7 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
 	}
 	defer rootFSBlob.Close()
 	gzw := gzip.NewWriter(rootFSBlob)
-	layerDigester := digest.Canonical.New()
+	layerDigester := digest.Canonical.Digester()
 	rootFSReader := io.TeeReader(rootFS, io.MultiWriter(gzw, layerDigester.Hash()))
 
 	if err := chrootarchive.Untar(rootFSReader, tmpRootFSDir, nil); err != nil {

+ 5 - 4
plugin/blobstore.go

@@ -1,18 +1,19 @@
 package plugin
 
 import (
+	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
 	"path/filepath"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/distribution/xfer"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/progress"
+	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 )
@@ -86,7 +87,7 @@ type insertion struct {
 }
 
 func newInsertion(tempFile *os.File) *insertion {
-	digester := digest.Canonical.New()
+	digester := digest.Canonical.Digester()
 	return &insertion{f: tempFile, digester: digester, Writer: io.MultiWriter(tempFile, digester.Hash())}
 }
 
@@ -141,7 +142,7 @@ func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.Roo
 		if err != nil {
 			return initialRootFS, nil, err
 		}
-		digester := digest.Canonical.New()
+		digester := digest.Canonical.Digester()
 		if _, err := archive.ApplyLayer(dm.tmpDir, io.TeeReader(inflatedLayerData, digester.Hash())); err != nil {
 			return initialRootFS, nil, err
 		}
@@ -174,7 +175,7 @@ func (dm *downloadManager) Put(dt []byte) (digest.Digest, error) {
 }
 
 func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) {
-	return nil, digest.ErrDigestNotFound
+	return nil, fmt.Errorf("digest not found")
 }
 func (dm *downloadManager) RootFSFromConfig(c []byte) (*image.RootFS, error) {
 	return configToRootFS(c)

+ 1 - 1
plugin/manager.go

@@ -12,7 +12,6 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
@@ -22,6 +21,7 @@ import (
 	"github.com/docker/docker/plugin/v2"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
+	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 )
 

+ 1 - 1
plugin/manager_linux.go

@@ -11,7 +11,6 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/daemon/initlayer"
 	"github.com/docker/docker/libcontainerd"
@@ -19,6 +18,7 @@ import (
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/plugin/v2"
+	"github.com/opencontainers/go-digest"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 )

+ 1 - 1
plugin/v2/plugin.go

@@ -5,10 +5,10 @@ import (
 	"strings"
 	"sync"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugins"
+	"github.com/opencontainers/go-digest"
 )
 
 // Plugin represents an individual plugin.

+ 2 - 2
reference/reference.go

@@ -5,9 +5,9 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/docker/distribution/digest"
 	distreference "github.com/docker/distribution/reference"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/opencontainers/go-digest"
 )
 
 const (
@@ -166,7 +166,7 @@ func ParseIDOrReference(idOrRef string) (digest.Digest, Named, error) {
 	if err := stringid.ValidateID(idOrRef); err == nil {
 		idOrRef = "sha256:" + idOrRef
 	}
-	if dgst, err := digest.ParseDigest(idOrRef); err == nil {
+	if dgst, err := digest.Parse(idOrRef); err == nil {
 		return dgst, nil, nil
 	}
 	ref, err := ParseNamed(idOrRef)

+ 1 - 1
reference/reference_test.go

@@ -3,7 +3,7 @@ package reference
 import (
 	"testing"
 
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 func TestValidateReferenceName(t *testing.T) {

+ 1 - 1
reference/store.go

@@ -9,8 +9,8 @@ import (
 	"sort"
 	"sync"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/pkg/ioutils"
+	"github.com/opencontainers/go-digest"
 )
 
 var (

+ 1 - 1
reference/store_test.go

@@ -8,7 +8,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 var (

+ 3 - 2
vendor.conf

@@ -44,8 +44,9 @@ github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
 github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
 
 # get graph and distribution packages
-github.com/docker/distribution 28602af35aceda2f8d571bad7ca37a54cf0250bc
+github.com/docker/distribution 7dba427612198a11b161a27f9d40bb2dca1ccd20
 github.com/vbatts/tar-split v0.10.1
+github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
 
 # get go-zfs packages
 github.com/mistifyio/go-zfs 22c9b32c84eb0d0c6f4043b6e90fc94073de92fa
@@ -102,7 +103,7 @@ github.com/docker/containerd 03e5862ec0d8d3b3f750e19fca3ee367e13c090e
 github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
 
 # cluster
-github.com/docker/swarmkit 2e956c40c02ad527c90ec85bdae25a0acac1bd87
+github.com/docker/swarmkit 4762d92234d286ae7c9e061470485e4d34ef8ebd
 github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
 github.com/gogo/protobuf v0.3
 github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a

+ 1 - 1
vendor/github.com/docker/distribution/blobs.go

@@ -8,8 +8,8 @@ import (
 	"time"
 
 	"github.com/docker/distribution/context"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/reference"
+	"github.com/opencontainers/go-digest"
 )
 
 var (

+ 16 - 14
vendor/github.com/docker/distribution/digest/set.go → vendor/github.com/docker/distribution/digestset/set.go

@@ -1,10 +1,12 @@
-package digest
+package digestset
 
 import (
 	"errors"
 	"sort"
 	"strings"
 	"sync"
+
+	digest "github.com/opencontainers/go-digest"
 )
 
 var (
@@ -44,7 +46,7 @@ func NewSet() *Set {
 // values or short values. This function does not test equality,
 // rather whether the second value could match against the first
 // value.
-func checkShortMatch(alg Algorithm, hex, shortAlg, shortHex string) bool {
+func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool {
 	if len(hex) == len(shortHex) {
 		if hex != shortHex {
 			return false
@@ -64,7 +66,7 @@ func checkShortMatch(alg Algorithm, hex, shortAlg, shortHex string) bool {
 // If no digests could be found ErrDigestNotFound will be returned
 // with an empty digest value. If multiple matches are found
 // ErrDigestAmbiguous will be returned with an empty digest value.
-func (dst *Set) Lookup(d string) (Digest, error) {
+func (dst *Set) Lookup(d string) (digest.Digest, error) {
 	dst.mutex.RLock()
 	defer dst.mutex.RUnlock()
 	if len(dst.entries) == 0 {
@@ -72,11 +74,11 @@ func (dst *Set) Lookup(d string) (Digest, error) {
 	}
 	var (
 		searchFunc func(int) bool
-		alg        Algorithm
+		alg        digest.Algorithm
 		hex        string
 	)
-	dgst, err := ParseDigest(d)
-	if err == ErrDigestInvalidFormat {
+	dgst, err := digest.Parse(d)
+	if err == digest.ErrDigestInvalidFormat {
 		hex = d
 		searchFunc = func(i int) bool {
 			return dst.entries[i].val >= d
@@ -108,7 +110,7 @@ func (dst *Set) Lookup(d string) (Digest, error) {
 // Add adds the given digest to the set. An error will be returned
 // if the given digest is invalid. If the digest already exists in the
 // set, this operation will be a no-op.
-func (dst *Set) Add(d Digest) error {
+func (dst *Set) Add(d digest.Digest) error {
 	if err := d.Validate(); err != nil {
 		return err
 	}
@@ -139,7 +141,7 @@ func (dst *Set) Add(d Digest) error {
 // Remove removes the given digest from the set. An err will be
 // returned if the given digest is invalid. If the digest does
 // not exist in the set, this operation will be a no-op.
-func (dst *Set) Remove(d Digest) error {
+func (dst *Set) Remove(d digest.Digest) error {
 	if err := d.Validate(); err != nil {
 		return err
 	}
@@ -167,10 +169,10 @@ func (dst *Set) Remove(d Digest) error {
 }
 
 // All returns all the digests in the set
-func (dst *Set) All() []Digest {
+func (dst *Set) All() []digest.Digest {
 	dst.mutex.RLock()
 	defer dst.mutex.RUnlock()
-	retValues := make([]Digest, len(dst.entries))
+	retValues := make([]digest.Digest, len(dst.entries))
 	for i := range dst.entries {
 		retValues[i] = dst.entries[i].digest
 	}
@@ -183,10 +185,10 @@ func (dst *Set) All() []Digest {
 // entire value of digest if uniqueness cannot be achieved without the
 // full value. This function will attempt to make short codes as short
 // as possible to be unique.
-func ShortCodeTable(dst *Set, length int) map[Digest]string {
+func ShortCodeTable(dst *Set, length int) map[digest.Digest]string {
 	dst.mutex.RLock()
 	defer dst.mutex.RUnlock()
-	m := make(map[Digest]string, len(dst.entries))
+	m := make(map[digest.Digest]string, len(dst.entries))
 	l := length
 	resetIdx := 0
 	for i := 0; i < len(dst.entries); i++ {
@@ -222,9 +224,9 @@ func ShortCodeTable(dst *Set, length int) map[Digest]string {
 }
 
 type digestEntry struct {
-	alg    Algorithm
+	alg    digest.Algorithm
 	val    string
-	digest Digest
+	digest digest.Digest
 }
 
 type digestEntries []*digestEntry

+ 1 - 1
vendor/github.com/docker/distribution/errors.go

@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 // ErrAccessDenied is returned when an access to a requested resource is

+ 1 - 1
vendor/github.com/docker/distribution/manifest/manifestlist/manifestlist.go

@@ -6,8 +6,8 @@ import (
 	"fmt"
 
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest"
+	"github.com/opencontainers/go-digest"
 )
 
 // MediaTypeManifestList specifies the mediaType for manifest lists.

+ 1 - 1
vendor/github.com/docker/distribution/manifest/schema1/config_builder.go

@@ -9,10 +9,10 @@ import (
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/libtrust"
+	"github.com/opencontainers/go-digest"
 )
 
 type diffID digest.Digest

+ 1 - 1
vendor/github.com/docker/distribution/manifest/schema1/manifest.go

@@ -5,9 +5,9 @@ import (
 	"fmt"
 
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest"
 	"github.com/docker/libtrust"
+	"github.com/opencontainers/go-digest"
 )
 
 const (

+ 1 - 1
vendor/github.com/docker/distribution/manifest/schema1/reference_builder.go

@@ -6,10 +6,10 @@ import (
 	"errors"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/libtrust"
+	"github.com/opencontainers/go-digest"
 )
 
 // referenceManifestBuilder is a type for constructing manifests from schema1

+ 1 - 1
vendor/github.com/docker/distribution/manifest/schema2/builder.go

@@ -3,7 +3,7 @@ package schema2
 import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 // builder is a type for constructing manifests.

+ 1 - 1
vendor/github.com/docker/distribution/manifest/schema2/manifest.go

@@ -6,8 +6,8 @@ import (
 	"fmt"
 
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest"
+	"github.com/opencontainers/go-digest"
 )
 
 const (

+ 1 - 1
vendor/github.com/docker/distribution/manifests.go

@@ -5,7 +5,7 @@ import (
 	"mime"
 
 	"github.com/docker/distribution/context"
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 // Manifest represents a registry object specifying a set of

+ 2 - 2
vendor/github.com/docker/distribution/reference/reference.go

@@ -27,7 +27,7 @@ import (
 	"path"
 	"strings"
 
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 )
 
 const (
@@ -170,7 +170,7 @@ func Parse(s string) (Reference, error) {
 	}
 	if matches[3] != "" {
 		var err error
-		ref.digest, err = digest.ParseDigest(matches[3])
+		ref.digest, err = digest.Parse(matches[3])
 		if err != nil {
 			return nil, err
 		}

+ 1 - 1
vendor/github.com/docker/distribution/registry/api/v2/descriptors.go

@@ -4,9 +4,9 @@ import (
 	"net/http"
 	"regexp"
 
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/api/errcode"
+	"github.com/opencontainers/go-digest"
 )
 
 var (

+ 5 - 5
vendor/github.com/docker/distribution/registry/client/repository.go

@@ -15,12 +15,12 @@ import (
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/api/v2"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/storage/cache"
 	"github.com/docker/distribution/registry/storage/cache/memory"
+	"github.com/opencontainers/go-digest"
 )
 
 // Registry provides an interface for calling Repositories, which returns a catalog of repositories.
@@ -268,7 +268,7 @@ func descriptorFromResponse(response *http.Response) (distribution.Descriptor, e
 		return desc, nil
 	}
 
-	dgst, err := digest.ParseDigest(digestHeader)
+	dgst, err := digest.Parse(digestHeader)
 	if err != nil {
 		return distribution.Descriptor{}, err
 	}
@@ -475,7 +475,7 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis
 		return nil, distribution.ErrManifestNotModified
 	} else if SuccessStatus(resp.StatusCode) {
 		if contentDgst != nil {
-			dgst, err := digest.ParseDigest(resp.Header.Get("Docker-Content-Digest"))
+			dgst, err := digest.Parse(resp.Header.Get("Docker-Content-Digest"))
 			if err == nil {
 				*contentDgst = dgst
 			}
@@ -553,7 +553,7 @@ func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options .
 
 	if SuccessStatus(resp.StatusCode) {
 		dgstHeader := resp.Header.Get("Docker-Content-Digest")
-		dgst, err := digest.ParseDigest(dgstHeader)
+		dgst, err := digest.Parse(dgstHeader)
 		if err != nil {
 			return "", err
 		}
@@ -661,7 +661,7 @@ func (bs *blobs) Put(ctx context.Context, mediaType string, p []byte) (distribut
 	if err != nil {
 		return distribution.Descriptor{}, err
 	}
-	dgstr := digest.Canonical.New()
+	dgstr := digest.Canonical.Digester()
 	n, err := io.Copy(writer, io.TeeReader(bytes.NewReader(p), dgstr.Hash()))
 	if err != nil {
 		return distribution.Descriptor{}, err

+ 1 - 1
vendor/github.com/docker/distribution/registry/storage/cache/cachedblobdescriptorstore.go

@@ -2,7 +2,7 @@ package cache
 
 import (
 	"github.com/docker/distribution/context"
-	"github.com/docker/distribution/digest"
+	"github.com/opencontainers/go-digest"
 
 	"github.com/docker/distribution"
 )

+ 1 - 1
vendor/github.com/docker/distribution/registry/storage/cache/memory/memory.go

@@ -5,9 +5,9 @@ import (
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/storage/cache"
+	"github.com/opencontainers/go-digest"
 )
 
 type inMemoryBlobDescriptorCacheProvider struct {

+ 10 - 9
vendor/github.com/docker/swarmkit/agent/worker.go

@@ -302,15 +302,6 @@ func reconcileTaskState(ctx context.Context, w *worker, assignments []*api.Assig
 }
 
 func reconcileSecrets(ctx context.Context, w *worker, assignments []*api.AssignmentChange, fullSnapshot bool) error {
-	var secrets exec.SecretsManager
-	provider, ok := w.executor.(exec.SecretsProvider)
-	if !ok {
-		log.G(ctx).Warn("secrets update ignored; executor does not support secrets")
-		return nil
-	}
-
-	secrets = provider.Secrets()
-
 	var (
 		updatedSecrets []api.Secret
 		removedSecrets []string
@@ -327,6 +318,16 @@ func reconcileSecrets(ctx context.Context, w *worker, assignments []*api.Assignm
 		}
 	}
 
+	provider, ok := w.executor.(exec.SecretsProvider)
+	if !ok {
+		if len(updatedSecrets) != 0 || len(removedSecrets) != 0 {
+			log.G(ctx).Warn("secrets update ignored; executor does not support secrets")
+		}
+		return nil
+	}
+
+	secrets := provider.Secrets()
+
 	log.G(ctx).WithFields(logrus.Fields{
 		"len(updatedSecrets)": len(updatedSecrets),
 		"len(removedSecrets)": len(removedSecrets),

+ 116 - 75
vendor/github.com/docker/swarmkit/api/objects.pb.go

@@ -57,6 +57,16 @@ type Node struct {
 	Attachment *NetworkAttachment `protobuf:"bytes,7,opt,name=attachment" json:"attachment,omitempty"`
 	// Certificate is the TLS certificate issued for the node, if any.
 	Certificate Certificate `protobuf:"bytes,8,opt,name=certificate" json:"certificate"`
+	// Role is the *observed* role for this node. It differs from the
+	// desired role set in Node.Spec.Role because the role here is only
+	// updated after the Raft member list has been reconciled with the
+	// desired role from the spec.
+	//
+	// This field represents the current reconciled state. If an action is
+	// to be performed, first verify the role in the cert. This field only
+	// shows the privilege level that the CA would currently grant when
+	// issuing or renewing the node's certificate.
+	Role NodeRole `protobuf:"varint,9,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"`
 }
 
 func (m *Node) Reset()                    { *m = Node{} }
@@ -303,6 +313,7 @@ func (m *Node) Copy() *Node {
 		ManagerStatus: m.ManagerStatus.Copy(),
 		Attachment:    m.Attachment.Copy(),
 		Certificate:   *m.Certificate.Copy(),
+		Role:          m.Role,
 	}
 
 	return o
@@ -504,7 +515,7 @@ func (this *Node) GoString() string {
 	if this == nil {
 		return "nil"
 	}
-	s := make([]string, 0, 12)
+	s := make([]string, 0, 13)
 	s = append(s, "&api.Node{")
 	s = append(s, "ID: "+fmt.Sprintf("%#v", this.ID)+",\n")
 	s = append(s, "Meta: "+strings.Replace(this.Meta.GoString(), `&`, ``, 1)+",\n")
@@ -520,6 +531,7 @@ func (this *Node) GoString() string {
 		s = append(s, "Attachment: "+fmt.Sprintf("%#v", this.Attachment)+",\n")
 	}
 	s = append(s, "Certificate: "+strings.Replace(this.Certificate.GoString(), `&`, ``, 1)+",\n")
+	s = append(s, "Role: "+fmt.Sprintf("%#v", this.Role)+",\n")
 	s = append(s, "}")
 	return strings.Join(s, "")
 }
@@ -834,6 +846,11 @@ func (m *Node) MarshalTo(data []byte) (int, error) {
 		return 0, err
 	}
 	i += n10
+	if m.Role != 0 {
+		data[i] = 0x48
+		i++
+		i = encodeVarintObjects(data, i, uint64(m.Role))
+	}
 	return i, nil
 }
 
@@ -1451,6 +1468,9 @@ func (m *Node) Size() (n int) {
 	}
 	l = m.Certificate.Size()
 	n += 1 + l + sovObjects(uint64(l))
+	if m.Role != 0 {
+		n += 1 + sovObjects(uint64(m.Role))
+	}
 	return n
 }
 
@@ -1707,6 +1727,7 @@ func (this *Node) String() string {
 		`ManagerStatus:` + strings.Replace(fmt.Sprintf("%v", this.ManagerStatus), "ManagerStatus", "ManagerStatus", 1) + `,`,
 		`Attachment:` + strings.Replace(fmt.Sprintf("%v", this.Attachment), "NetworkAttachment", "NetworkAttachment", 1) + `,`,
 		`Certificate:` + strings.Replace(strings.Replace(this.Certificate.String(), "Certificate", "Certificate", 1), `&`, ``, 1) + `,`,
+		`Role:` + fmt.Sprintf("%v", this.Role) + `,`,
 		`}`,
 	}, "")
 	return s
@@ -2268,6 +2289,25 @@ func (m *Node) Unmarshal(data []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 9:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
+			}
+			m.Role = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowObjects
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Role |= (NodeRole(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
 		default:
 			iNdEx = preIndex
 			skippy, err := skipObjects(data[iNdEx:])
@@ -4186,78 +4226,79 @@ var (
 func init() { proto.RegisterFile("objects.proto", fileDescriptorObjects) }
 
 var fileDescriptorObjects = []byte{
-	// 1161 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x57, 0x4d, 0x8f, 0x1b, 0x35,
-	0x18, 0xee, 0x24, 0xb3, 0xf9, 0x78, 0xb3, 0x59, 0x81, 0xa9, 0xca, 0x34, 0x2c, 0xc9, 0x92, 0x0a,
-	0x54, 0xa1, 0x2a, 0x15, 0xa5, 0xa0, 0x2d, 0xb4, 0x82, 0x7c, 0x09, 0xa2, 0x52, 0xa8, 0xdc, 0xb2,
-	0x3d, 0x46, 0xde, 0x19, 0x37, 0x0c, 0x99, 0x8c, 0x47, 0xb6, 0x93, 0x2a, 0x37, 0xc4, 0x0f, 0xe0,
-	0x27, 0x20, 0xce, 0xfc, 0x09, 0xae, 0x7b, 0xe0, 0xc0, 0x0d, 0x4e, 0x11, 0x9b, 0x1b, 0x37, 0x7e,
-	0x02, 0xb2, 0xc7, 0x93, 0xcc, 0x2a, 0x93, 0x65, 0x2b, 0x55, 0x7b, 0xb3, 0xe3, 0xe7, 0x79, 0xde,
-	0xd7, 0xaf, 0x1f, 0xbf, 0xe3, 0x40, 0x95, 0x1d, 0x7f, 0x4f, 0x5d, 0x29, 0x5a, 0x11, 0x67, 0x92,
-	0x21, 0xe4, 0x31, 0x77, 0x4c, 0x79, 0x4b, 0xbc, 0x20, 0x7c, 0x32, 0xf6, 0x65, 0x6b, 0xf6, 0x41,
-	0xad, 0x22, 0xe7, 0x11, 0x35, 0x80, 0x5a, 0x45, 0x44, 0xd4, 0x4d, 0x26, 0xd7, 0xa5, 0x3f, 0xa1,
-	0x42, 0x92, 0x49, 0x74, 0x7b, 0x35, 0x32, 0x4b, 0x57, 0x47, 0x6c, 0xc4, 0xf4, 0xf0, 0xb6, 0x1a,
-	0xc5, 0xbf, 0x36, 0x7f, 0xb3, 0xc0, 0x7e, 0x44, 0x25, 0x41, 0x9f, 0x42, 0x71, 0x46, 0xb9, 0xf0,
-	0x59, 0xe8, 0x58, 0x07, 0xd6, 0xcd, 0xca, 0x9d, 0xb7, 0x5a, 0x9b, 0x91, 0x5b, 0x47, 0x31, 0xa4,
-	0x63, 0x9f, 0x2c, 0x1a, 0x57, 0x70, 0xc2, 0x40, 0xf7, 0x01, 0x5c, 0x4e, 0x89, 0xa4, 0xde, 0x90,
-	0x48, 0x27, 0xa7, 0xf9, 0x6f, 0x67, 0xf1, 0x9f, 0x26, 0x49, 0xe1, 0xb2, 0x21, 0xb4, 0xa5, 0x62,
-	0x4f, 0x23, 0x2f, 0x61, 0xe7, 0x2f, 0xc4, 0x36, 0x84, 0xb6, 0x6c, 0xfe, 0x93, 0x07, 0xfb, 0x6b,
-	0xe6, 0x51, 0x74, 0x0d, 0x72, 0xbe, 0xa7, 0x93, 0x2f, 0x77, 0x0a, 0xcb, 0x45, 0x23, 0x37, 0xe8,
-	0xe1, 0x9c, 0xef, 0xa1, 0x3b, 0x60, 0x4f, 0xa8, 0x24, 0x26, 0x2d, 0x27, 0x4b, 0x58, 0x55, 0xc0,
-	0xec, 0x49, 0x63, 0xd1, 0xc7, 0x60, 0xab, 0xb2, 0x9a, 0x64, 0xf6, 0xb3, 0x38, 0x2a, 0xe6, 0x93,
-	0x88, 0xba, 0x09, 0x4f, 0xe1, 0x51, 0x1f, 0x2a, 0x1e, 0x15, 0x2e, 0xf7, 0x23, 0xa9, 0x2a, 0x69,
-	0x6b, 0xfa, 0x8d, 0x6d, 0xf4, 0xde, 0x1a, 0x8a, 0xd3, 0x3c, 0x74, 0x1f, 0x0a, 0x42, 0x12, 0x39,
-	0x15, 0xce, 0x8e, 0x56, 0xa8, 0x6f, 0x4d, 0x40, 0xa3, 0x4c, 0x0a, 0x86, 0x83, 0xbe, 0x84, 0xbd,
-	0x09, 0x09, 0xc9, 0x88, 0xf2, 0xa1, 0x51, 0x29, 0x68, 0x95, 0x77, 0x32, 0xb7, 0x1e, 0x23, 0x63,
-	0x21, 0x5c, 0x9d, 0xa4, 0xa7, 0xa8, 0x0f, 0x40, 0xa4, 0x24, 0xee, 0x77, 0x13, 0x1a, 0x4a, 0xa7,
-	0xa8, 0x55, 0xde, 0xcd, 0xcc, 0x85, 0xca, 0x17, 0x8c, 0x8f, 0xdb, 0x2b, 0x30, 0x4e, 0x11, 0xd1,
-	0x17, 0x50, 0x71, 0x29, 0x97, 0xfe, 0x73, 0xdf, 0x25, 0x92, 0x3a, 0x25, 0xad, 0xd3, 0xc8, 0xd2,
-	0xe9, 0xae, 0x61, 0x66, 0x53, 0x69, 0x66, 0xf3, 0xcf, 0x1c, 0x14, 0x9f, 0x50, 0x3e, 0xf3, 0xdd,
-	0x57, 0x7b, 0xdc, 0xf7, 0xce, 0x1c, 0x77, 0x66, 0x66, 0x26, 0xec, 0xc6, 0x89, 0x1f, 0x42, 0x89,
-	0x86, 0x5e, 0xc4, 0xfc, 0x50, 0x9a, 0xe3, 0xce, 0x74, 0x4b, 0xdf, 0x60, 0xf0, 0x0a, 0x8d, 0xfa,
-	0x50, 0x8d, 0x5d, 0x3c, 0x3c, 0x73, 0xd6, 0x07, 0x59, 0xf4, 0x6f, 0x35, 0xd0, 0x1c, 0xd2, 0xee,
-	0x34, 0x35, 0x43, 0x3d, 0xa8, 0x46, 0x9c, 0xce, 0x7c, 0x36, 0x15, 0x43, 0xbd, 0x89, 0xc2, 0x85,
-	0x36, 0x81, 0x77, 0x13, 0x96, 0x9a, 0x35, 0x7f, 0xce, 0x41, 0x29, 0xc9, 0x11, 0xdd, 0x35, 0xe5,
-	0xb0, 0xb6, 0x27, 0x94, 0x60, 0xb5, 0x54, 0x5c, 0x89, 0xbb, 0xb0, 0x13, 0x31, 0x2e, 0x85, 0x93,
-	0x3b, 0xc8, 0x6f, 0xf3, 0xec, 0x63, 0xc6, 0x65, 0x97, 0x85, 0xcf, 0xfd, 0x11, 0x8e, 0xc1, 0xe8,
-	0x19, 0x54, 0x66, 0x3e, 0x97, 0x53, 0x12, 0x0c, 0xfd, 0x48, 0x38, 0x79, 0xcd, 0x7d, 0xef, 0xbc,
-	0x90, 0xad, 0xa3, 0x18, 0x3f, 0x78, 0xdc, 0xd9, 0x5b, 0x2e, 0x1a, 0xb0, 0x9a, 0x0a, 0x0c, 0x46,
-	0x6a, 0x10, 0x89, 0xda, 0x23, 0x28, 0xaf, 0x56, 0xd0, 0x2d, 0x80, 0x30, 0xb6, 0xe8, 0x70, 0x65,
-	0x9a, 0xea, 0x72, 0xd1, 0x28, 0x1b, 0xe3, 0x0e, 0x7a, 0xb8, 0x6c, 0x00, 0x03, 0x0f, 0x21, 0xb0,
-	0x89, 0xe7, 0x71, 0x6d, 0xa1, 0x32, 0xd6, 0xe3, 0xe6, 0xef, 0x3b, 0x60, 0x3f, 0x25, 0x62, 0x7c,
-	0xd9, 0x6d, 0x46, 0xc5, 0xdc, 0x30, 0xdd, 0x2d, 0x00, 0x11, 0x1f, 0xa5, 0xda, 0x8e, 0xbd, 0xde,
-	0x8e, 0x39, 0x60, 0xb5, 0x1d, 0x03, 0x88, 0xb7, 0x23, 0x02, 0x26, 0xb5, 0xbf, 0x6c, 0xac, 0xc7,
-	0xe8, 0x06, 0x14, 0x43, 0xe6, 0x69, 0x7a, 0x41, 0xd3, 0x61, 0xb9, 0x68, 0x14, 0x54, 0x4b, 0x19,
-	0xf4, 0x70, 0x41, 0x2d, 0x0d, 0x3c, 0x75, 0x6f, 0x49, 0x18, 0x32, 0x49, 0x54, 0x53, 0x12, 0xe6,
-	0xfe, 0x67, 0x1a, 0xab, 0xbd, 0x86, 0x25, 0xf7, 0x36, 0xc5, 0x44, 0x47, 0xf0, 0x46, 0x92, 0x6f,
-	0x5a, 0xb0, 0xf4, 0x32, 0x82, 0xc8, 0x28, 0xa4, 0x56, 0x52, 0x7d, 0xb2, 0xbc, 0xbd, 0x4f, 0xea,
-	0x0a, 0x66, 0xf5, 0xc9, 0x0e, 0x54, 0x3d, 0x2a, 0x7c, 0x4e, 0x3d, 0x7d, 0x03, 0xa9, 0x03, 0x07,
-	0xd6, 0xcd, 0xbd, 0x2d, 0x9f, 0x1e, 0x23, 0x42, 0xf1, 0xae, 0xe1, 0xe8, 0x19, 0x6a, 0x43, 0xc9,
-	0xf8, 0x46, 0x38, 0x15, 0xed, 0xdd, 0x0b, 0xf6, 0xc7, 0x15, 0xed, 0x4c, 0x07, 0xd9, 0x7d, 0xa9,
-	0x0e, 0x72, 0x0f, 0x20, 0x60, 0xa3, 0xa1, 0xc7, 0xfd, 0x19, 0xe5, 0x4e, 0x55, 0x73, 0x6b, 0x59,
-	0xdc, 0x9e, 0x46, 0xe0, 0x72, 0xc0, 0x46, 0xf1, 0xb0, 0xf9, 0xa3, 0x05, 0xaf, 0x6f, 0x24, 0x85,
-	0x3e, 0x82, 0xa2, 0x49, 0xeb, 0xbc, 0x47, 0x80, 0xe1, 0xe1, 0x04, 0x8b, 0xf6, 0xa1, 0xac, 0xee,
-	0x08, 0x15, 0x82, 0xc6, 0xb7, 0xbf, 0x8c, 0xd7, 0x3f, 0x20, 0x07, 0x8a, 0x24, 0xf0, 0x89, 0x5a,
-	0xcb, 0xeb, 0xb5, 0x64, 0xda, 0xfc, 0x29, 0x07, 0x45, 0x23, 0x76, 0xd9, 0xed, 0xdc, 0x84, 0xdd,
-	0xb8, 0x59, 0x0f, 0x60, 0x37, 0x2e, 0xa7, 0xb1, 0x84, 0xfd, 0xbf, 0x45, 0xad, 0xc4, 0xf8, 0xd8,
-	0x0e, 0x0f, 0xc0, 0xf6, 0x23, 0x32, 0x31, 0xad, 0x3c, 0x33, 0xf2, 0xe0, 0x71, 0xfb, 0xd1, 0x37,
-	0x51, 0xec, 0xec, 0xd2, 0x72, 0xd1, 0xb0, 0xd5, 0x0f, 0x58, 0xd3, 0x9a, 0xbf, 0xec, 0x40, 0xb1,
-	0x1b, 0x4c, 0x85, 0xa4, 0xfc, 0xb2, 0x0b, 0x62, 0xc2, 0x6e, 0x14, 0xa4, 0x0b, 0x45, 0xce, 0x98,
-	0x1c, 0xba, 0xe4, 0xbc, 0x5a, 0x60, 0xc6, 0x64, 0xb7, 0xdd, 0xd9, 0x53, 0x44, 0xd5, 0x48, 0xe2,
-	0x39, 0x2e, 0x28, 0x6a, 0x97, 0xa0, 0x67, 0x70, 0x2d, 0x69, 0xbf, 0xc7, 0x8c, 0x49, 0x21, 0x39,
-	0x89, 0x86, 0x63, 0x3a, 0x57, 0xdf, 0xbc, 0xfc, 0xb6, 0x97, 0x49, 0x3f, 0x74, 0xf9, 0x5c, 0x17,
-	0xea, 0x21, 0x9d, 0xe3, 0xab, 0x46, 0xa0, 0x93, 0xf0, 0x1f, 0xd2, 0xb9, 0x40, 0x9f, 0xc1, 0x3e,
-	0x5d, 0xc1, 0x94, 0xe2, 0x30, 0x20, 0x13, 0xf5, 0x61, 0x19, 0xba, 0x01, 0x73, 0xc7, 0xba, 0xb7,
-	0xd9, 0xf8, 0x3a, 0x4d, 0x4b, 0x7d, 0x15, 0x23, 0xba, 0x0a, 0x80, 0x04, 0x38, 0xc7, 0x01, 0x71,
-	0xc7, 0x81, 0x2f, 0xd4, 0xfb, 0x33, 0xf5, 0xd8, 0x50, 0xed, 0x49, 0xe5, 0x76, 0x78, 0x4e, 0xb5,
-	0x5a, 0x9d, 0x35, 0x37, 0xf5, 0x74, 0x11, 0xfd, 0x50, 0xf2, 0x39, 0x7e, 0xf3, 0x38, 0x7b, 0x15,
-	0x75, 0xa0, 0x32, 0x0d, 0x55, 0xf8, 0xb8, 0x06, 0xe5, 0x8b, 0xd6, 0x00, 0x62, 0x96, 0xda, 0x79,
-	0x6d, 0x06, 0xfb, 0xe7, 0x05, 0x47, 0xaf, 0x41, 0x7e, 0x4c, 0xe7, 0xb1, 0x7f, 0xb0, 0x1a, 0xa2,
-	0xcf, 0x61, 0x67, 0x46, 0x82, 0x29, 0x35, 0xce, 0x79, 0x3f, 0x2b, 0x5e, 0xb6, 0x24, 0x8e, 0x89,
-	0x9f, 0xe4, 0x0e, 0xad, 0xe6, 0xaf, 0x16, 0x14, 0x9e, 0x50, 0x97, 0x53, 0xf9, 0x4a, 0x1d, 0x7a,
-	0x78, 0xc6, 0xa1, 0xf5, 0xec, 0xc7, 0x8b, 0x8a, 0xba, 0x61, 0xd0, 0x1a, 0x94, 0xfc, 0x50, 0x52,
-	0x1e, 0x92, 0x40, 0x3b, 0xb4, 0x84, 0x57, 0xf3, 0xce, 0xfe, 0xc9, 0x69, 0xfd, 0xca, 0x5f, 0xa7,
-	0xf5, 0x2b, 0xff, 0x9e, 0xd6, 0xad, 0x1f, 0x96, 0x75, 0xeb, 0x64, 0x59, 0xb7, 0xfe, 0x58, 0xd6,
-	0xad, 0xbf, 0x97, 0x75, 0xeb, 0xb8, 0xa0, 0xff, 0x02, 0x7d, 0xf8, 0x5f, 0x00, 0x00, 0x00, 0xff,
-	0xff, 0x38, 0xf8, 0x23, 0xac, 0x72, 0x0d, 0x00, 0x00,
+	// 1176 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x57, 0xcf, 0x8e, 0x1b, 0x35,
+	0x1c, 0xee, 0x64, 0x67, 0x93, 0xcc, 0x2f, 0x9b, 0x15, 0x98, 0xaa, 0x4c, 0x97, 0x6d, 0xb2, 0xa4,
+	0x02, 0x55, 0xa8, 0x4a, 0xa1, 0x14, 0xb4, 0x85, 0x56, 0x90, 0x7f, 0x82, 0xa8, 0x14, 0x2a, 0xb7,
+	0xb4, 0xc7, 0xc8, 0x3b, 0xe3, 0x86, 0x21, 0x93, 0xf1, 0xc8, 0x76, 0x52, 0xe5, 0x86, 0x78, 0x00,
+	0x5e, 0x00, 0x09, 0x71, 0xe6, 0x25, 0xb8, 0xf6, 0xc0, 0x81, 0x1b, 0x9c, 0x22, 0x9a, 0x27, 0xe0,
+	0x11, 0x90, 0x3d, 0x9e, 0x64, 0x56, 0x99, 0x84, 0xad, 0x54, 0xed, 0xcd, 0x8e, 0xbf, 0xef, 0xfb,
+	0xfd, 0x99, 0xcf, 0xbf, 0xf5, 0x42, 0x95, 0x9d, 0x7c, 0x4f, 0x3d, 0x29, 0x9a, 0x31, 0x67, 0x92,
+	0x21, 0xe4, 0x33, 0x6f, 0x44, 0x79, 0x53, 0x3c, 0x23, 0x7c, 0x3c, 0x0a, 0x64, 0x73, 0xfa, 0xc1,
+	0x41, 0x45, 0xce, 0x62, 0x6a, 0x00, 0x07, 0x15, 0x11, 0x53, 0x2f, 0xdd, 0x5c, 0x96, 0xc1, 0x98,
+	0x0a, 0x49, 0xc6, 0xf1, 0x8d, 0xe5, 0xca, 0x1c, 0x5d, 0x1c, 0xb2, 0x21, 0xd3, 0xcb, 0x1b, 0x6a,
+	0x95, 0xfc, 0xda, 0xf8, 0xdd, 0x02, 0xfb, 0x3e, 0x95, 0x04, 0x7d, 0x0a, 0xa5, 0x29, 0xe5, 0x22,
+	0x60, 0x91, 0x6b, 0x1d, 0x59, 0xd7, 0x2a, 0x37, 0xdf, 0x6a, 0xae, 0x47, 0x6e, 0x3e, 0x4e, 0x20,
+	0x6d, 0xfb, 0xf9, 0xbc, 0x7e, 0x01, 0xa7, 0x0c, 0x74, 0x07, 0xc0, 0xe3, 0x94, 0x48, 0xea, 0x0f,
+	0x88, 0x74, 0x0b, 0x9a, 0x7f, 0x25, 0x8f, 0xff, 0x28, 0x4d, 0x0a, 0x3b, 0x86, 0xd0, 0x92, 0x8a,
+	0x3d, 0x89, 0xfd, 0x94, 0xbd, 0x73, 0x26, 0xb6, 0x21, 0xb4, 0x64, 0xe3, 0x67, 0x1b, 0xec, 0xaf,
+	0x99, 0x4f, 0xd1, 0x25, 0x28, 0x04, 0xbe, 0x4e, 0xde, 0x69, 0x17, 0x17, 0xf3, 0x7a, 0xa1, 0xdf,
+	0xc5, 0x85, 0xc0, 0x47, 0x37, 0xc1, 0x1e, 0x53, 0x49, 0x4c, 0x5a, 0x6e, 0x9e, 0xb0, 0xea, 0x80,
+	0xa9, 0x49, 0x63, 0xd1, 0xc7, 0x60, 0xab, 0xb6, 0x9a, 0x64, 0x0e, 0xf3, 0x38, 0x2a, 0xe6, 0xc3,
+	0x98, 0x7a, 0x29, 0x4f, 0xe1, 0x51, 0x0f, 0x2a, 0x3e, 0x15, 0x1e, 0x0f, 0x62, 0xa9, 0x3a, 0x69,
+	0x6b, 0xfa, 0xd5, 0x4d, 0xf4, 0xee, 0x0a, 0x8a, 0xb3, 0x3c, 0x74, 0x07, 0x8a, 0x42, 0x12, 0x39,
+	0x11, 0xee, 0xae, 0x56, 0xa8, 0x6d, 0x4c, 0x40, 0xa3, 0x4c, 0x0a, 0x86, 0x83, 0xbe, 0x84, 0xfd,
+	0x31, 0x89, 0xc8, 0x90, 0xf2, 0x81, 0x51, 0x29, 0x6a, 0x95, 0xb7, 0x73, 0x4b, 0x4f, 0x90, 0x89,
+	0x10, 0xae, 0x8e, 0xb3, 0x5b, 0xd4, 0x03, 0x20, 0x52, 0x12, 0xef, 0xbb, 0x31, 0x8d, 0xa4, 0x5b,
+	0xd2, 0x2a, 0xef, 0xe4, 0xe6, 0x42, 0xe5, 0x33, 0xc6, 0x47, 0xad, 0x25, 0x18, 0x67, 0x88, 0xe8,
+	0x0b, 0xa8, 0x78, 0x94, 0xcb, 0xe0, 0x69, 0xe0, 0x11, 0x49, 0xdd, 0xb2, 0xd6, 0xa9, 0xe7, 0xe9,
+	0x74, 0x56, 0x30, 0x53, 0x54, 0x96, 0x89, 0xde, 0x07, 0x9b, 0xb3, 0x90, 0xba, 0xce, 0x91, 0x75,
+	0x6d, 0x7f, 0xf3, 0x67, 0xc1, 0x2c, 0xa4, 0x58, 0x23, 0x1b, 0x7f, 0x15, 0xa0, 0xf4, 0x90, 0xf2,
+	0x69, 0xe0, 0xbd, 0x5a, 0x83, 0xdc, 0x3e, 0x65, 0x90, 0xdc, 0x5a, 0x4c, 0xd8, 0x35, 0x8f, 0x1c,
+	0x43, 0x99, 0x46, 0x7e, 0xcc, 0x82, 0x48, 0x1a, 0x83, 0xe4, 0x16, 0xd2, 0x33, 0x18, 0xbc, 0x44,
+	0xa3, 0x1e, 0x54, 0x13, 0xdf, 0x0f, 0x4e, 0xb9, 0xe3, 0x28, 0x8f, 0xfe, 0xad, 0x06, 0x9a, 0xcf,
+	0xba, 0x37, 0xc9, 0xec, 0x50, 0x17, 0xaa, 0x31, 0xa7, 0xd3, 0x80, 0x4d, 0xc4, 0x40, 0x17, 0x51,
+	0x3c, 0x53, 0x11, 0x78, 0x2f, 0x65, 0xa9, 0x5d, 0xe3, 0x97, 0x02, 0x94, 0xd3, 0x1c, 0xd1, 0x2d,
+	0xd3, 0x0e, 0x6b, 0x73, 0x42, 0x29, 0x56, 0x4b, 0x25, 0x9d, 0xb8, 0x05, 0xbb, 0x31, 0xe3, 0x52,
+	0xb8, 0x85, 0xa3, 0x9d, 0x4d, 0x2e, 0x7f, 0xc0, 0xb8, 0xec, 0xb0, 0xe8, 0x69, 0x30, 0xc4, 0x09,
+	0x18, 0x3d, 0x81, 0xca, 0x34, 0xe0, 0x72, 0x42, 0xc2, 0x41, 0x10, 0x0b, 0x77, 0x47, 0x73, 0xdf,
+	0xdd, 0x16, 0xb2, 0xf9, 0x38, 0xc1, 0xf7, 0x1f, 0xb4, 0xf7, 0x17, 0xf3, 0x3a, 0x2c, 0xb7, 0x02,
+	0x83, 0x91, 0xea, 0xc7, 0xe2, 0xe0, 0x3e, 0x38, 0xcb, 0x13, 0x74, 0x1d, 0x20, 0x4a, 0x4c, 0x3d,
+	0x58, 0x9a, 0xa6, 0xba, 0x98, 0xd7, 0x1d, 0x63, 0xf5, 0x7e, 0x17, 0x3b, 0x06, 0xd0, 0xf7, 0x11,
+	0x02, 0x9b, 0xf8, 0x3e, 0xd7, 0x16, 0x72, 0xb0, 0x5e, 0x37, 0xfe, 0xd8, 0x05, 0xfb, 0x11, 0x11,
+	0xa3, 0xf3, 0x1e, 0x4c, 0x2a, 0xe6, 0x9a, 0xe9, 0xae, 0x03, 0x88, 0xe4, 0x53, 0xaa, 0x72, 0xec,
+	0x55, 0x39, 0xe6, 0x03, 0xab, 0x72, 0x0c, 0x20, 0x29, 0x47, 0x84, 0x4c, 0x6a, 0x7f, 0xd9, 0x58,
+	0xaf, 0xd1, 0x55, 0x28, 0x45, 0xcc, 0xd7, 0xf4, 0xa2, 0xa6, 0xc3, 0x62, 0x5e, 0x2f, 0xaa, 0xeb,
+	0xd6, 0xef, 0xe2, 0xa2, 0x3a, 0xea, 0xfb, 0xea, 0xa6, 0x93, 0x28, 0x62, 0x92, 0xa8, 0x31, 0x26,
+	0xcc, 0xc4, 0xc8, 0x35, 0x56, 0x6b, 0x05, 0x4b, 0x6f, 0x7a, 0x86, 0x89, 0x1e, 0xc3, 0x1b, 0x69,
+	0xbe, 0x59, 0xc1, 0xf2, 0xcb, 0x08, 0x22, 0xa3, 0x90, 0x39, 0xc9, 0x4c, 0x56, 0x67, 0xf3, 0x64,
+	0xd5, 0x1d, 0xcc, 0x9b, 0xac, 0x6d, 0xa8, 0xfa, 0x54, 0x04, 0x9c, 0xfa, 0xfa, 0x06, 0x52, 0x17,
+	0xf4, 0x20, 0xba, 0xb2, 0x4d, 0x84, 0xe2, 0x3d, 0xc3, 0xd1, 0x3b, 0xd4, 0x82, 0xb2, 0xf1, 0x8d,
+	0x70, 0x2b, 0xda, 0xbb, 0x67, 0x9c, 0xa8, 0x4b, 0xda, 0xa9, 0x09, 0xb2, 0xf7, 0x52, 0x13, 0xe4,
+	0x36, 0x40, 0xc8, 0x86, 0x03, 0x9f, 0x07, 0x53, 0xca, 0xdd, 0xaa, 0xe6, 0x1e, 0xe4, 0x71, 0xbb,
+	0x1a, 0x81, 0x9d, 0x90, 0x0d, 0x93, 0x65, 0xe3, 0x47, 0x0b, 0x5e, 0x5f, 0x4b, 0x0a, 0x7d, 0x04,
+	0x25, 0x93, 0xd6, 0xb6, 0x67, 0x83, 0xe1, 0xe1, 0x14, 0x8b, 0x0e, 0xc1, 0x51, 0x77, 0x84, 0x0a,
+	0x41, 0x93, 0xdb, 0xef, 0xe0, 0xd5, 0x0f, 0xc8, 0x85, 0x12, 0x09, 0x03, 0xa2, 0xce, 0x76, 0xf4,
+	0x59, 0xba, 0x6d, 0xfc, 0x54, 0x80, 0x92, 0x11, 0x3b, 0xef, 0x71, 0x6e, 0xc2, 0xae, 0xdd, 0xac,
+	0xbb, 0xb0, 0x97, 0xb4, 0xd3, 0x58, 0xc2, 0xfe, 0xdf, 0xa6, 0x56, 0x12, 0x7c, 0x62, 0x87, 0xbb,
+	0x60, 0x07, 0x31, 0x19, 0x9b, 0x51, 0x9e, 0x1b, 0xb9, 0xff, 0xa0, 0x75, 0xff, 0x9b, 0x38, 0x71,
+	0x76, 0x79, 0x31, 0xaf, 0xdb, 0xea, 0x07, 0xac, 0x69, 0x8d, 0x5f, 0x77, 0xa1, 0xd4, 0x09, 0x27,
+	0x42, 0x52, 0x7e, 0xde, 0x0d, 0x31, 0x61, 0xd7, 0x1a, 0xd2, 0x81, 0x12, 0x67, 0x4c, 0x0e, 0x3c,
+	0xb2, 0xad, 0x17, 0x98, 0x31, 0xd9, 0x69, 0xb5, 0xf7, 0x15, 0x51, 0x0d, 0x92, 0x64, 0x8f, 0x8b,
+	0x8a, 0xda, 0x21, 0xe8, 0x09, 0x5c, 0x4a, 0xc7, 0xef, 0x09, 0x63, 0x52, 0x48, 0x4e, 0xe2, 0xc1,
+	0x88, 0xce, 0xd4, 0xdf, 0xbc, 0x9d, 0x4d, 0x6f, 0x99, 0x5e, 0xe4, 0xf1, 0x99, 0x6e, 0xd4, 0x3d,
+	0x3a, 0xc3, 0x17, 0x8d, 0x40, 0x3b, 0xe5, 0xdf, 0xa3, 0x33, 0x81, 0x3e, 0x83, 0x43, 0xba, 0x84,
+	0x29, 0xc5, 0x41, 0x48, 0xc6, 0xea, 0x0f, 0xcb, 0xc0, 0x0b, 0x99, 0x37, 0xd2, 0xb3, 0xcd, 0xc6,
+	0x97, 0x69, 0x56, 0xea, 0xab, 0x04, 0xd1, 0x51, 0x00, 0x24, 0xc0, 0x3d, 0x09, 0x89, 0x37, 0x0a,
+	0x03, 0xa1, 0x5e, 0xac, 0x99, 0xe7, 0x89, 0x1a, 0x4f, 0x2a, 0xb7, 0xe3, 0x2d, 0xdd, 0x6a, 0xb6,
+	0x57, 0xdc, 0xcc, 0x63, 0x47, 0xf4, 0x22, 0xc9, 0x67, 0xf8, 0xcd, 0x93, 0xfc, 0x53, 0xd4, 0x86,
+	0xca, 0x24, 0x52, 0xe1, 0x93, 0x1e, 0x38, 0x67, 0xed, 0x01, 0x24, 0x2c, 0x55, 0xf9, 0xc1, 0x14,
+	0x0e, 0xb7, 0x05, 0x47, 0xaf, 0xc1, 0xce, 0x88, 0xce, 0x12, 0xff, 0x60, 0xb5, 0x44, 0x9f, 0xc3,
+	0xee, 0x94, 0x84, 0x13, 0x6a, 0x9c, 0xf3, 0x5e, 0x5e, 0xbc, 0x7c, 0x49, 0x9c, 0x10, 0x3f, 0x29,
+	0x1c, 0x5b, 0x8d, 0xdf, 0x2c, 0x28, 0x3e, 0xa4, 0x1e, 0xa7, 0xf2, 0x95, 0x3a, 0xf4, 0xf8, 0x94,
+	0x43, 0x6b, 0xf9, 0x8f, 0x17, 0x15, 0x75, 0xcd, 0xa0, 0x07, 0x50, 0x0e, 0x22, 0x49, 0x79, 0x44,
+	0x42, 0xed, 0xd0, 0x32, 0x5e, 0xee, 0xdb, 0x87, 0xcf, 0x5f, 0xd4, 0x2e, 0xfc, 0xfd, 0xa2, 0x76,
+	0xe1, 0xdf, 0x17, 0x35, 0xeb, 0x87, 0x45, 0xcd, 0x7a, 0xbe, 0xa8, 0x59, 0x7f, 0x2e, 0x6a, 0xd6,
+	0x3f, 0x8b, 0x9a, 0x75, 0x52, 0xd4, 0xff, 0x34, 0x7d, 0xf8, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff,
+	0x9f, 0x52, 0xfb, 0x4a, 0xa4, 0x0d, 0x00, 0x00,
 }

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

@@ -48,6 +48,17 @@ message Node {
 
 	// Certificate is the TLS certificate issued for the node, if any.
 	Certificate certificate = 8 [(gogoproto.nullable) = false];
+
+	// Role is the *observed* role for this node. It differs from the
+	// desired role set in Node.Spec.Role because the role here is only
+	// updated after the Raft member list has been reconciled with the
+	// desired role from the spec.
+	//
+	// This field represents the current reconciled state. If an action is
+	// to be performed, first verify the role in the cert. This field only
+	// shows the privilege level that the CA would currently grant when
+	// issuing or renewing the node's certificate.
+	NodeRole role = 9;
 }
 
 message Service {

+ 116 - 116
vendor/github.com/docker/swarmkit/api/specs.pb.go

@@ -112,8 +112,8 @@ func (EndpointSpec_ResolutionMode) EnumDescriptor() ([]byte, []int) {
 
 type NodeSpec struct {
 	Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"`
-	// Role defines the role the node should have.
-	Role NodeRole `protobuf:"varint,2,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"`
+	// DesiredRole defines the role the node should have.
+	DesiredRole NodeRole `protobuf:"varint,2,opt,name=desired_role,json=desiredRole,proto3,enum=docker.swarmkit.v1.NodeRole" json:"desired_role,omitempty"`
 	// Membership controls the admission of the node into the cluster.
 	Membership NodeSpec_Membership `protobuf:"varint,3,opt,name=membership,proto3,enum=docker.swarmkit.v1.NodeSpec_Membership" json:"membership,omitempty"`
 	// Availability allows a user to control the current scheduling status of a
@@ -645,7 +645,7 @@ func (m *NodeSpec) Copy() *NodeSpec {
 
 	o := &NodeSpec{
 		Annotations:  *m.Annotations.Copy(),
-		Role:         m.Role,
+		DesiredRole:  m.DesiredRole,
 		Membership:   m.Membership,
 		Availability: m.Availability,
 	}
@@ -941,7 +941,7 @@ func (this *NodeSpec) GoString() string {
 	s := make([]string, 0, 8)
 	s = append(s, "&api.NodeSpec{")
 	s = append(s, "Annotations: "+strings.Replace(this.Annotations.GoString(), `&`, ``, 1)+",\n")
-	s = append(s, "Role: "+fmt.Sprintf("%#v", this.Role)+",\n")
+	s = append(s, "DesiredRole: "+fmt.Sprintf("%#v", this.DesiredRole)+",\n")
 	s = append(s, "Membership: "+fmt.Sprintf("%#v", this.Membership)+",\n")
 	s = append(s, "Availability: "+fmt.Sprintf("%#v", this.Availability)+",\n")
 	s = append(s, "}")
@@ -1241,10 +1241,10 @@ func (m *NodeSpec) MarshalTo(data []byte) (int, error) {
 		return 0, err
 	}
 	i += n1
-	if m.Role != 0 {
+	if m.DesiredRole != 0 {
 		data[i] = 0x10
 		i++
-		i = encodeVarintSpecs(data, i, uint64(m.Role))
+		i = encodeVarintSpecs(data, i, uint64(m.DesiredRole))
 	}
 	if m.Membership != 0 {
 		data[i] = 0x18
@@ -2106,8 +2106,8 @@ func (m *NodeSpec) Size() (n int) {
 	_ = l
 	l = m.Annotations.Size()
 	n += 1 + l + sovSpecs(uint64(l))
-	if m.Role != 0 {
-		n += 1 + sovSpecs(uint64(m.Role))
+	if m.DesiredRole != 0 {
+		n += 1 + sovSpecs(uint64(m.DesiredRole))
 	}
 	if m.Membership != 0 {
 		n += 1 + sovSpecs(uint64(m.Membership))
@@ -2461,7 +2461,7 @@ func (this *NodeSpec) String() string {
 	}
 	s := strings.Join([]string{`&NodeSpec{`,
 		`Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`,
-		`Role:` + fmt.Sprintf("%v", this.Role) + `,`,
+		`DesiredRole:` + fmt.Sprintf("%v", this.DesiredRole) + `,`,
 		`Membership:` + fmt.Sprintf("%v", this.Membership) + `,`,
 		`Availability:` + fmt.Sprintf("%v", this.Availability) + `,`,
 		`}`,
@@ -2750,9 +2750,9 @@ func (m *NodeSpec) Unmarshal(data []byte) error {
 			iNdEx = postIndex
 		case 2:
 			if wireType != 0 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field DesiredRole", wireType)
 			}
-			m.Role = 0
+			m.DesiredRole = 0
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowSpecs
@@ -2762,7 +2762,7 @@ func (m *NodeSpec) Unmarshal(data []byte) error {
 				}
 				b := data[iNdEx]
 				iNdEx++
-				m.Role |= (NodeRole(b) & 0x7F) << shift
+				m.DesiredRole |= (NodeRole(b) & 0x7F) << shift
 				if b < 0x80 {
 					break
 				}
@@ -5283,108 +5283,108 @@ var (
 func init() { proto.RegisterFile("specs.proto", fileDescriptorSpecs) }
 
 var fileDescriptorSpecs = []byte{
-	// 1640 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x57, 0xdd, 0x72, 0xdb, 0xc6,
-	0x15, 0x26, 0x24, 0x8a, 0x3f, 0x07, 0x94, 0x4d, 0xed, 0xe4, 0x07, 0x66, 0x12, 0x8a, 0x66, 0xdc,
-	0x54, 0x69, 0xa6, 0x72, 0xab, 0x76, 0x52, 0xa7, 0x6e, 0xa6, 0x25, 0x45, 0x56, 0x56, 0x55, 0x29,
-	0x9c, 0x95, 0xe2, 0x8e, 0xaf, 0x38, 0x2b, 0x60, 0x45, 0x62, 0x04, 0x62, 0xd1, 0xdd, 0x05, 0x33,
-	0xbc, 0xeb, 0x65, 0xc6, 0x17, 0x7d, 0x03, 0x5f, 0xf5, 0x19, 0xfa, 0x0e, 0xbe, 0xec, 0x65, 0x7b,
-	0xa3, 0xa9, 0xf9, 0x04, 0x9d, 0xe9, 0x03, 0xb4, 0xb3, 0x8b, 0x05, 0x08, 0x26, 0x50, 0x9c, 0x99,
-	0xf8, 0x6e, 0xf7, 0xe0, 0xfb, 0x0e, 0xce, 0x9e, 0xfd, 0x70, 0xce, 0x01, 0xd8, 0x22, 0xa2, 0xae,
-	0xd8, 0x8f, 0x38, 0x93, 0x0c, 0x21, 0x8f, 0xb9, 0xd7, 0x94, 0xef, 0x8b, 0xaf, 0x08, 0x9f, 0x5d,
-	0xfb, 0x72, 0x7f, 0xfe, 0xf3, 0x96, 0x2d, 0x17, 0x11, 0x35, 0x80, 0xd6, 0x5b, 0x13, 0x36, 0x61,
-	0x7a, 0xf9, 0x50, 0xad, 0x8c, 0xf5, 0x5d, 0x2f, 0xe6, 0x44, 0xfa, 0x2c, 0x7c, 0x98, 0x2e, 0x92,
-	0x07, 0xdd, 0xbf, 0x96, 0xa1, 0x76, 0xc6, 0x3c, 0x7a, 0x1e, 0x51, 0x17, 0x1d, 0x81, 0x4d, 0xc2,
-	0x90, 0x49, 0x0d, 0x10, 0x8e, 0xd5, 0xb1, 0xf6, 0xec, 0x83, 0xdd, 0xfd, 0x6f, 0xbf, 0x72, 0xbf,
-	0xb7, 0x82, 0xf5, 0xcb, 0x2f, 0x6f, 0x76, 0x4b, 0x38, 0xcf, 0x44, 0x3f, 0x83, 0x32, 0x67, 0x01,
-	0x75, 0x36, 0x3a, 0xd6, 0xde, 0x9d, 0x83, 0xf7, 0x8b, 0x3c, 0xa8, 0x97, 0x62, 0x16, 0x50, 0xac,
-	0x91, 0xe8, 0x08, 0x60, 0x46, 0x67, 0x97, 0x94, 0x8b, 0xa9, 0x1f, 0x39, 0x9b, 0x9a, 0xf7, 0xe3,
-	0xdb, 0x78, 0x2a, 0xd8, 0xfd, 0xd3, 0x0c, 0x8e, 0x73, 0x54, 0x74, 0x0a, 0x0d, 0x32, 0x27, 0x7e,
-	0x40, 0x2e, 0xfd, 0xc0, 0x97, 0x0b, 0xa7, 0xac, 0x5d, 0x7d, 0xfc, 0x9d, 0xae, 0x7a, 0x39, 0x02,
-	0x5e, 0xa3, 0x77, 0x3d, 0x80, 0xd5, 0x8b, 0xd0, 0x47, 0x50, 0x1d, 0x0d, 0xcf, 0x06, 0xc7, 0x67,
-	0x47, 0xcd, 0x52, 0xeb, 0xde, 0xf3, 0x17, 0x9d, 0xb7, 0x95, 0x8f, 0x15, 0x60, 0x44, 0x43, 0xcf,
-	0x0f, 0x27, 0x68, 0x0f, 0x6a, 0xbd, 0xc3, 0xc3, 0xe1, 0xe8, 0x62, 0x38, 0x68, 0x5a, 0xad, 0xd6,
-	0xf3, 0x17, 0x9d, 0x77, 0xd6, 0x81, 0x3d, 0xd7, 0xa5, 0x91, 0xa4, 0x5e, 0xab, 0xfc, 0xf5, 0xdf,
-	0xda, 0xa5, 0xee, 0xd7, 0x16, 0x34, 0xf2, 0x41, 0xa0, 0x8f, 0xa0, 0xd2, 0x3b, 0xbc, 0x38, 0x7e,
-	0x3a, 0x6c, 0x96, 0x56, 0xf4, 0x3c, 0xa2, 0xe7, 0x4a, 0x7f, 0x4e, 0xd1, 0x03, 0xd8, 0x1a, 0xf5,
-	0xbe, 0x3c, 0x1f, 0x36, 0xad, 0x55, 0x38, 0x79, 0xd8, 0x88, 0xc4, 0x42, 0xa3, 0x06, 0xb8, 0x77,
-	0x7c, 0xd6, 0xdc, 0x28, 0x46, 0x0d, 0x38, 0xf1, 0x43, 0x13, 0xca, 0xab, 0x4d, 0xb0, 0xcf, 0x29,
-	0x9f, 0xfb, 0xee, 0x1b, 0xd6, 0xc4, 0xa7, 0x50, 0x96, 0x44, 0x5c, 0x6b, 0x4d, 0xd8, 0xc5, 0x9a,
-	0xb8, 0x20, 0xe2, 0x5a, 0xbd, 0xd4, 0xd0, 0x35, 0x5e, 0x29, 0x83, 0xd3, 0x28, 0xf0, 0x5d, 0x22,
-	0xa9, 0xa7, 0x95, 0x61, 0x1f, 0xfc, 0xa8, 0x88, 0x8d, 0x33, 0x94, 0x89, 0xff, 0x49, 0x09, 0xe7,
-	0xa8, 0xe8, 0x31, 0x54, 0x26, 0x01, 0xbb, 0x24, 0x81, 0xd6, 0x84, 0x7d, 0x70, 0xbf, 0xc8, 0xc9,
-	0x91, 0x46, 0xac, 0x1c, 0x18, 0x0a, 0x7a, 0x04, 0x95, 0x38, 0xf2, 0x88, 0xa4, 0x4e, 0x45, 0x93,
-	0x3b, 0x45, 0xe4, 0x2f, 0x35, 0xe2, 0x90, 0x85, 0x57, 0xfe, 0x04, 0x1b, 0x3c, 0x3a, 0x81, 0x5a,
-	0x48, 0xe5, 0x57, 0x8c, 0x5f, 0x0b, 0xa7, 0xda, 0xd9, 0xdc, 0xb3, 0x0f, 0x3e, 0x29, 0x14, 0x63,
-	0x82, 0xe9, 0x49, 0x49, 0xdc, 0xe9, 0x8c, 0x86, 0x32, 0x71, 0xd3, 0xdf, 0x70, 0x2c, 0x9c, 0x39,
-	0x40, 0xbf, 0x81, 0x1a, 0x0d, 0xbd, 0x88, 0xf9, 0xa1, 0x74, 0x6a, 0xb7, 0x07, 0x32, 0x34, 0x18,
-	0x95, 0x4c, 0x9c, 0x31, 0xfa, 0x15, 0x28, 0xcf, 0x98, 0x47, 0xbb, 0x0f, 0x61, 0xe7, 0x5b, 0xc9,
-	0x42, 0x2d, 0xa8, 0x99, 0x64, 0x25, 0xb7, 0x5c, 0xc6, 0xd9, 0xbe, 0x7b, 0x17, 0xb6, 0xd7, 0x12,
-	0xa3, 0xcb, 0x46, 0x7a, 0x5b, 0xa8, 0x07, 0x75, 0x97, 0x85, 0x92, 0xf8, 0x21, 0xe5, 0x46, 0x20,
-	0x85, 0xb9, 0x3d, 0x4c, 0x41, 0x8a, 0xf5, 0xa4, 0x84, 0x57, 0x2c, 0xf4, 0x7b, 0xa8, 0x73, 0x2a,
-	0x58, 0xcc, 0x5d, 0x2a, 0x8c, 0x42, 0xf6, 0x8a, 0xef, 0x38, 0x01, 0x61, 0xfa, 0xe7, 0xd8, 0xe7,
-	0x54, 0xe5, 0x49, 0xe0, 0x15, 0x15, 0x3d, 0x86, 0x2a, 0xa7, 0x42, 0x12, 0x2e, 0xbf, 0xeb, 0x92,
-	0x71, 0x02, 0x19, 0xb1, 0xc0, 0x77, 0x17, 0x38, 0x65, 0xa0, 0xc7, 0x50, 0x8f, 0x02, 0xe2, 0x6a,
-	0xaf, 0xce, 0x96, 0xa6, 0x7f, 0x50, 0x44, 0x1f, 0xa5, 0x20, 0xbc, 0xc2, 0xa3, 0xcf, 0x00, 0x02,
-	0x36, 0x19, 0x7b, 0xdc, 0x9f, 0x53, 0x6e, 0x44, 0xd2, 0x2a, 0x62, 0x0f, 0x34, 0x02, 0xd7, 0x03,
-	0x36, 0x49, 0x96, 0xe8, 0xe8, 0x07, 0x29, 0x24, 0xa7, 0x8e, 0x13, 0x00, 0x92, 0x3d, 0x35, 0xfa,
-	0xf8, 0xf8, 0x7b, 0xb9, 0x32, 0x37, 0x92, 0xa3, 0xa3, 0xfb, 0xd0, 0xb8, 0x62, 0xdc, 0xa5, 0x63,
-	0xa3, 0xfb, 0xba, 0xd6, 0x84, 0xad, 0x6d, 0x89, 0xd0, 0xfb, 0x75, 0xa8, 0xf2, 0x38, 0x94, 0xfe,
-	0x8c, 0x76, 0x4f, 0xe0, 0xed, 0x42, 0xa7, 0xe8, 0x00, 0x1a, 0xd9, 0x35, 0x8f, 0x7d, 0x4f, 0xeb,
-	0xa3, 0xde, 0xbf, 0xbb, 0xbc, 0xd9, 0xb5, 0x33, 0x3d, 0x1c, 0x0f, 0xb0, 0x9d, 0x81, 0x8e, 0xbd,
-	0xee, 0xbf, 0xaa, 0xb0, 0xbd, 0x26, 0x16, 0xf4, 0x16, 0x6c, 0xf9, 0x33, 0x32, 0xa1, 0x09, 0x1d,
-	0x27, 0x1b, 0x34, 0x84, 0x4a, 0x40, 0x2e, 0x69, 0xa0, 0x24, 0xa3, 0xd2, 0xf6, 0xd3, 0xd7, 0xaa,
-	0x6e, 0xff, 0x8f, 0x1a, 0x3f, 0x0c, 0x25, 0x5f, 0x60, 0x43, 0x46, 0x0e, 0x54, 0x5d, 0x36, 0x9b,
-	0x91, 0x50, 0x95, 0x97, 0xcd, 0xbd, 0x3a, 0x4e, 0xb7, 0x08, 0x41, 0x99, 0xf0, 0x89, 0x70, 0xca,
-	0xda, 0xac, 0xd7, 0xa8, 0x09, 0x9b, 0x34, 0x9c, 0x3b, 0x5b, 0xda, 0xa4, 0x96, 0xca, 0xe2, 0xf9,
-	0xc9, 0x9d, 0xd7, 0xb1, 0x5a, 0x2a, 0x5e, 0x2c, 0x28, 0x77, 0xaa, 0xda, 0xa4, 0xd7, 0xe8, 0x57,
-	0x50, 0x99, 0xb1, 0x38, 0x94, 0xc2, 0xa9, 0xe9, 0x60, 0xef, 0x15, 0x05, 0x7b, 0xaa, 0x10, 0xa6,
-	0xfc, 0x19, 0x38, 0x7a, 0x02, 0x3b, 0x42, 0xb2, 0x68, 0x3c, 0xe1, 0xc4, 0xa5, 0xe3, 0x88, 0x72,
-	0x9f, 0x79, 0xfa, 0x36, 0x6e, 0xa9, 0xa2, 0x03, 0xd3, 0xe1, 0xf1, 0x5d, 0x45, 0x3b, 0x52, 0xac,
-	0x91, 0x26, 0xa1, 0x11, 0x34, 0xa2, 0x38, 0x08, 0xc6, 0x2c, 0x4a, 0x8a, 0x39, 0x68, 0x27, 0xdf,
-	0x23, 0x6b, 0xa3, 0x38, 0x08, 0xbe, 0x48, 0x48, 0xd8, 0x8e, 0x56, 0x1b, 0xf4, 0x0e, 0x54, 0x26,
-	0x9c, 0xc5, 0x91, 0x70, 0x6c, 0x9d, 0x0f, 0xb3, 0x43, 0x9f, 0x43, 0x55, 0x50, 0x97, 0x53, 0x29,
-	0x9c, 0x86, 0x3e, 0xed, 0x87, 0x45, 0x2f, 0x39, 0xd7, 0x10, 0x4c, 0xaf, 0x28, 0xa7, 0xa1, 0x4b,
-	0x71, 0xca, 0x41, 0xf7, 0x60, 0x53, 0xca, 0x85, 0xb3, 0xdd, 0xb1, 0xf6, 0x6a, 0xfd, 0xea, 0xf2,
-	0x66, 0x77, 0xf3, 0xe2, 0xe2, 0x19, 0x56, 0x36, 0x55, 0xa6, 0xa6, 0x4c, 0xc8, 0x90, 0xcc, 0xa8,
-	0x73, 0x47, 0xa7, 0x37, 0xdb, 0xa3, 0x67, 0x00, 0x5e, 0x28, 0xc6, 0xae, 0xfe, 0x2e, 0x9c, 0xbb,
-	0xfa, 0x74, 0x9f, 0xbc, 0xfe, 0x74, 0x83, 0xb3, 0x73, 0x53, 0x6c, 0xb7, 0x97, 0x37, 0xbb, 0xf5,
-	0x6c, 0x8b, 0xeb, 0x5e, 0x28, 0x92, 0x25, 0xea, 0x83, 0x3d, 0xa5, 0x24, 0x90, 0x53, 0x77, 0x4a,
-	0xdd, 0x6b, 0xa7, 0x79, 0x7b, 0xed, 0x7d, 0xa2, 0x61, 0xc6, 0x43, 0x9e, 0xa4, 0x44, 0xac, 0x42,
-	0x15, 0xce, 0x8e, 0xce, 0x55, 0xb2, 0x41, 0x1f, 0x00, 0xb0, 0x88, 0x86, 0x63, 0x21, 0x3d, 0x3f,
-	0x74, 0x90, 0x3a, 0x32, 0xae, 0x2b, 0xcb, 0xb9, 0x32, 0xb4, 0x3e, 0x03, 0x3b, 0xa7, 0x59, 0xa5,
-	0xb5, 0x6b, 0xba, 0x30, 0x9f, 0x81, 0x5a, 0x2a, 0xaf, 0x73, 0x12, 0xc4, 0xc9, 0xb0, 0x55, 0xc7,
-	0xc9, 0xe6, 0xd7, 0x1b, 0x8f, 0xac, 0xd6, 0x01, 0xd8, 0xb9, 0x8b, 0x43, 0x1f, 0xc2, 0x36, 0xa7,
-	0x13, 0x5f, 0x48, 0xbe, 0x18, 0x93, 0x58, 0x4e, 0x9d, 0xdf, 0x69, 0x42, 0x23, 0x35, 0xf6, 0x62,
-	0x39, 0x6d, 0x8d, 0x61, 0x75, 0x7e, 0xd4, 0x01, 0x5b, 0xe5, 0x55, 0x50, 0x3e, 0xa7, 0x5c, 0x75,
-	0x05, 0x15, 0x76, 0xde, 0xa4, 0xee, 0x5f, 0x50, 0xc2, 0xdd, 0xa9, 0xfe, 0x02, 0xeb, 0xd8, 0xec,
-	0xd4, 0x27, 0x95, 0x8a, 0xcc, 0x7c, 0x52, 0x66, 0xdb, 0xfd, 0xaf, 0x05, 0x8d, 0x7c, 0x7b, 0x42,
-	0x87, 0x49, 0x53, 0xd2, 0x47, 0xba, 0x73, 0xf0, 0xf0, 0x75, 0xed, 0x4c, 0xb7, 0x80, 0x20, 0x56,
-	0xce, 0x4e, 0xd5, 0x08, 0xa9, 0xc9, 0xe8, 0x97, 0xb0, 0x15, 0x31, 0x2e, 0xd3, 0x42, 0xd0, 0x2e,
-	0x2c, 0xdb, 0x8c, 0xa7, 0x25, 0x33, 0x01, 0x77, 0xa7, 0x70, 0x67, 0xdd, 0x1b, 0x7a, 0x00, 0x9b,
-	0x4f, 0x8f, 0x47, 0xcd, 0x52, 0xeb, 0xbd, 0xe7, 0x2f, 0x3a, 0xef, 0xae, 0x3f, 0x7c, 0xea, 0x73,
-	0x19, 0x93, 0xe0, 0x78, 0x84, 0x7e, 0x02, 0x5b, 0x83, 0xb3, 0x73, 0x8c, 0x9b, 0x56, 0x6b, 0xf7,
-	0xf9, 0x8b, 0xce, 0x7b, 0xeb, 0x38, 0xf5, 0x88, 0xc5, 0xa1, 0x87, 0xd9, 0x65, 0x36, 0x55, 0xfd,
-	0x7d, 0x03, 0x6c, 0x53, 0x1f, 0xdf, 0xec, 0x54, 0xf5, 0x5b, 0xd8, 0x4e, 0x5a, 0x4e, 0xaa, 0xfa,
-	0x8d, 0xd7, 0x76, 0x9e, 0x46, 0x42, 0x30, 0x77, 0x7c, 0x1f, 0x1a, 0x7e, 0x34, 0xff, 0x74, 0x4c,
-	0x43, 0x72, 0x19, 0x98, 0x01, 0xab, 0x86, 0x6d, 0x65, 0x1b, 0x26, 0x26, 0xf5, 0xc9, 0xf9, 0xa1,
-	0xa4, 0x3c, 0x34, 0xa3, 0x53, 0x0d, 0x67, 0x7b, 0xf4, 0x39, 0x94, 0xfd, 0x88, 0xcc, 0x4c, 0xbb,
-	0x2c, 0x3c, 0xc1, 0xf1, 0xa8, 0x77, 0x6a, 0x34, 0xd8, 0xaf, 0x2d, 0x6f, 0x76, 0xcb, 0xca, 0x80,
-	0x35, 0x0d, 0xb5, 0xd3, 0x8e, 0xa5, 0xde, 0xa4, 0x2b, 0x68, 0x0d, 0xe7, 0x2c, 0xdd, 0xff, 0x95,
-	0xc1, 0x3e, 0x0c, 0x62, 0x21, 0x4d, 0x1f, 0x78, 0x63, 0x79, 0x7b, 0x06, 0x3b, 0x44, 0xcf, 0xe0,
-	0x24, 0x54, 0x45, 0x55, 0x4f, 0x02, 0x26, 0x77, 0x0f, 0x0a, 0xdd, 0x65, 0xe0, 0x64, 0x6a, 0xe8,
-	0x57, 0x94, 0x4f, 0xc7, 0xc2, 0x4d, 0xf2, 0x8d, 0x27, 0xe8, 0x1c, 0xb6, 0x19, 0x77, 0xa7, 0x54,
-	0xc8, 0xa4, 0x0e, 0x9b, 0x99, 0xb5, 0xf0, 0x6f, 0xe6, 0x8b, 0x3c, 0xd0, 0x14, 0xa1, 0x24, 0xda,
-	0x75, 0x1f, 0xe8, 0x11, 0x94, 0x39, 0xb9, 0x4a, 0xa7, 0x9a, 0x42, 0x7d, 0x63, 0x72, 0x25, 0xd7,
-	0x5c, 0x68, 0x06, 0xfa, 0x03, 0x80, 0xe7, 0x8b, 0x88, 0x48, 0x77, 0x4a, 0xb9, 0xb9, 0xa7, 0xc2,
-	0x23, 0x0e, 0x32, 0xd4, 0x9a, 0x97, 0x1c, 0x1b, 0x9d, 0x40, 0xdd, 0x25, 0xa9, 0xd2, 0x2a, 0xb7,
-	0xb7, 0xa0, 0xc3, 0x9e, 0x71, 0xd1, 0x54, 0x2e, 0x96, 0x37, 0xbb, 0xb5, 0xd4, 0x82, 0x6b, 0x2e,
-	0x31, 0xca, 0x3b, 0x81, 0x6d, 0x35, 0xe0, 0x8f, 0x3d, 0x7a, 0x45, 0xe2, 0x40, 0x0a, 0xdd, 0x2d,
-	0x6f, 0x29, 0xaa, 0x6a, 0xd6, 0x1c, 0x18, 0x9c, 0x89, 0xab, 0x21, 0x73, 0x36, 0xf4, 0x27, 0xd8,
-	0xa1, 0xa1, 0xcb, 0x17, 0x5a, 0x67, 0x69, 0x84, 0xb5, 0xdb, 0x0f, 0x3b, 0xcc, 0xc0, 0x6b, 0x87,
-	0x6d, 0xd2, 0x6f, 0xd8, 0xbb, 0x3e, 0x40, 0xd2, 0xa6, 0xde, 0xac, 0xfe, 0x10, 0x94, 0x3d, 0x22,
-	0x89, 0x96, 0x5c, 0x03, 0xeb, 0x75, 0xff, 0xfd, 0x97, 0xaf, 0xda, 0xa5, 0x7f, 0xbe, 0x6a, 0x97,
-	0xfe, 0xf3, 0xaa, 0x6d, 0xfd, 0x65, 0xd9, 0xb6, 0x5e, 0x2e, 0xdb, 0xd6, 0x3f, 0x96, 0x6d, 0xeb,
-	0xdf, 0xcb, 0xb6, 0x75, 0x59, 0xd1, 0x3f, 0xec, 0xbf, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff,
-	0xab, 0x02, 0x96, 0x72, 0x0f, 0x10, 0x00, 0x00,
+	// 1647 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x57, 0xdd, 0x6e, 0xe3, 0xc6,
+	0x15, 0x16, 0x6d, 0x59, 0x3f, 0x87, 0xf2, 0xae, 0x3c, 0xc8, 0x0f, 0x57, 0x49, 0x64, 0xad, 0xb2,
+	0x4d, 0x9d, 0x06, 0xf5, 0xa2, 0x6e, 0x91, 0x6e, 0xba, 0x0d, 0x5a, 0xc9, 0x52, 0xbd, 0xae, 0x6b,
+	0x47, 0x18, 0x3b, 0x5b, 0xec, 0x95, 0x30, 0x26, 0xc7, 0x12, 0x61, 0x8a, 0xc3, 0xce, 0x0c, 0x15,
+	0xf8, 0xae, 0x97, 0xc1, 0x5e, 0xf4, 0x0d, 0x7c, 0xd5, 0x67, 0xe8, 0x3b, 0xec, 0x65, 0x2f, 0xdb,
+	0x1b, 0xa3, 0xab, 0x27, 0x28, 0xd0, 0x07, 0x68, 0x31, 0xc3, 0x21, 0x45, 0x25, 0x74, 0x76, 0x81,
+	0xfa, 0x6e, 0xe6, 0xf0, 0xfb, 0x0e, 0xcf, 0x9c, 0xf9, 0x78, 0xce, 0x21, 0xd8, 0x22, 0xa2, 0xae,
+	0xd8, 0x8d, 0x38, 0x93, 0x0c, 0x21, 0x8f, 0xb9, 0x97, 0x94, 0xef, 0x8a, 0x6f, 0x08, 0x9f, 0x5d,
+	0xfa, 0x72, 0x77, 0xfe, 0xb3, 0x96, 0x2d, 0xaf, 0x22, 0x6a, 0x00, 0xad, 0x77, 0x26, 0x6c, 0xc2,
+	0xf4, 0xf2, 0xb1, 0x5a, 0x19, 0xeb, 0xfb, 0x5e, 0xcc, 0x89, 0xf4, 0x59, 0xf8, 0x38, 0x5d, 0x24,
+	0x0f, 0xba, 0xd7, 0x65, 0xa8, 0x9d, 0x30, 0x8f, 0x9e, 0x46, 0xd4, 0x45, 0x07, 0x60, 0x93, 0x30,
+	0x64, 0x52, 0x03, 0x84, 0x63, 0x75, 0xac, 0x1d, 0x7b, 0x6f, 0x7b, 0xf7, 0xfb, 0xaf, 0xdc, 0xed,
+	0x2d, 0x61, 0xfd, 0xf2, 0xab, 0x9b, 0xed, 0x12, 0xce, 0x33, 0xd1, 0x6f, 0xa0, 0xe1, 0x51, 0xe1,
+	0x73, 0xea, 0x8d, 0x39, 0x0b, 0xa8, 0xb3, 0xd6, 0xb1, 0x76, 0xee, 0xed, 0x7d, 0x58, 0xe4, 0x49,
+	0xbd, 0x1c, 0xb3, 0x80, 0x62, 0xdb, 0x30, 0xd4, 0x06, 0x1d, 0x00, 0xcc, 0xe8, 0xec, 0x9c, 0x72,
+	0x31, 0xf5, 0x23, 0x67, 0x5d, 0xd3, 0x7f, 0x7c, 0x1b, 0x5d, 0xc5, 0xbe, 0x7b, 0x9c, 0xc1, 0x71,
+	0x8e, 0x8a, 0x8e, 0xa1, 0x41, 0xe6, 0xc4, 0x0f, 0xc8, 0xb9, 0x1f, 0xf8, 0xf2, 0xca, 0x29, 0x6b,
+	0x57, 0x9f, 0xfe, 0xa0, 0xab, 0x5e, 0x8e, 0x80, 0x57, 0xe8, 0x5d, 0x0f, 0x60, 0xf9, 0x22, 0xf4,
+	0x09, 0x54, 0x47, 0xc3, 0x93, 0xc1, 0xe1, 0xc9, 0x41, 0xb3, 0xd4, 0x7a, 0xf0, 0xf2, 0xba, 0xf3,
+	0xae, 0xf2, 0xb1, 0x04, 0x8c, 0x68, 0xe8, 0xf9, 0xe1, 0x04, 0xed, 0x40, 0xad, 0xb7, 0xbf, 0x3f,
+	0x1c, 0x9d, 0x0d, 0x07, 0x4d, 0xab, 0xd5, 0x7a, 0x79, 0xdd, 0x79, 0x6f, 0x15, 0xd8, 0x73, 0x5d,
+	0x1a, 0x49, 0xea, 0xb5, 0xca, 0xdf, 0xfe, 0xb5, 0x5d, 0xea, 0x7e, 0x6b, 0x41, 0x23, 0x1f, 0x04,
+	0xfa, 0x04, 0x2a, 0xbd, 0xfd, 0xb3, 0xc3, 0xe7, 0xc3, 0x66, 0x69, 0x49, 0xcf, 0x23, 0x7a, 0xae,
+	0xf4, 0xe7, 0x14, 0x3d, 0x82, 0x8d, 0x51, 0xef, 0xeb, 0xd3, 0x61, 0xd3, 0x5a, 0x86, 0x93, 0x87,
+	0x8d, 0x48, 0x2c, 0x34, 0x6a, 0x80, 0x7b, 0x87, 0x27, 0xcd, 0xb5, 0x62, 0xd4, 0x80, 0x13, 0x3f,
+	0x34, 0xa1, 0xbc, 0x5e, 0x07, 0xfb, 0x94, 0xf2, 0xb9, 0xef, 0xde, 0xb1, 0x44, 0x3e, 0x87, 0xb2,
+	0x24, 0xe2, 0x52, 0x4b, 0xc3, 0x2e, 0x96, 0xc6, 0x19, 0x11, 0x97, 0xea, 0xa5, 0x86, 0xae, 0xf1,
+	0x4a, 0x19, 0x9c, 0x46, 0x81, 0xef, 0x12, 0x49, 0x3d, 0xad, 0x0c, 0x7b, 0xef, 0x47, 0x45, 0x6c,
+	0x9c, 0xa1, 0x4c, 0xfc, 0xcf, 0x4a, 0x38, 0x47, 0x45, 0x4f, 0xa1, 0x32, 0x09, 0xd8, 0x39, 0x09,
+	0xb4, 0x26, 0xec, 0xbd, 0x87, 0x45, 0x4e, 0x0e, 0x34, 0x62, 0xe9, 0xc0, 0x50, 0xd0, 0x13, 0xa8,
+	0xc4, 0x91, 0x47, 0x24, 0x75, 0x2a, 0x9a, 0xdc, 0x29, 0x22, 0x7f, 0xad, 0x11, 0xfb, 0x2c, 0xbc,
+	0xf0, 0x27, 0xd8, 0xe0, 0xd1, 0x11, 0xd4, 0x42, 0x2a, 0xbf, 0x61, 0xfc, 0x52, 0x38, 0xd5, 0xce,
+	0xfa, 0x8e, 0xbd, 0xf7, 0x59, 0xa1, 0x18, 0x13, 0x4c, 0x4f, 0x4a, 0xe2, 0x4e, 0x67, 0x34, 0x94,
+	0x89, 0x9b, 0xfe, 0x9a, 0x63, 0xe1, 0xcc, 0x01, 0xfa, 0x35, 0xd4, 0x68, 0xe8, 0x45, 0xcc, 0x0f,
+	0xa5, 0x53, 0xbb, 0x3d, 0x90, 0xa1, 0xc1, 0xa8, 0x64, 0xe2, 0x8c, 0xd1, 0xaf, 0x40, 0x79, 0xc6,
+	0x3c, 0xda, 0x7d, 0x0c, 0x5b, 0xdf, 0x4b, 0x16, 0x6a, 0x41, 0xcd, 0x24, 0x2b, 0xb9, 0xe5, 0x32,
+	0xce, 0xf6, 0xdd, 0xfb, 0xb0, 0xb9, 0x92, 0x98, 0xee, 0x5f, 0xca, 0x50, 0x4b, 0x6f, 0x0b, 0xf5,
+	0xa0, 0xee, 0xb2, 0x50, 0x12, 0x3f, 0xa4, 0xdc, 0x08, 0xa4, 0x30, 0xb7, 0xfb, 0x29, 0x48, 0xb1,
+	0x9e, 0x95, 0xf0, 0x92, 0x85, 0x7e, 0x07, 0x75, 0x4e, 0x05, 0x8b, 0xb9, 0x4b, 0x85, 0x51, 0xc8,
+	0x4e, 0xf1, 0x1d, 0x27, 0x20, 0x4c, 0xff, 0x14, 0xfb, 0x9c, 0xaa, 0x3c, 0x09, 0xbc, 0xa4, 0xa2,
+	0xa7, 0x50, 0xe5, 0x54, 0x48, 0xc2, 0xe5, 0x0f, 0x5d, 0x32, 0x4e, 0x20, 0x23, 0x16, 0xf8, 0xee,
+	0x15, 0x4e, 0x19, 0xe8, 0x29, 0xd4, 0xa3, 0x80, 0xb8, 0xda, 0xab, 0xb3, 0xa1, 0xe9, 0x1f, 0x15,
+	0xd1, 0x47, 0x29, 0x08, 0x2f, 0xf1, 0xe8, 0x0b, 0x80, 0x80, 0x4d, 0xc6, 0x1e, 0xf7, 0xe7, 0x94,
+	0x1b, 0x91, 0xb4, 0x8a, 0xd8, 0x03, 0x8d, 0xc0, 0xf5, 0x80, 0x4d, 0x92, 0x25, 0x3a, 0xf8, 0xbf,
+	0x14, 0x92, 0x53, 0xc7, 0x11, 0x00, 0xc9, 0x9e, 0x1a, 0x7d, 0x7c, 0xfa, 0x56, 0xae, 0xcc, 0x8d,
+	0xe4, 0xe8, 0xe8, 0x21, 0x34, 0x2e, 0x18, 0x77, 0xe9, 0xd8, 0xe8, 0xbe, 0xae, 0x35, 0x61, 0x6b,
+	0x5b, 0x22, 0xf4, 0x7e, 0x1d, 0xaa, 0x3c, 0x0e, 0xa5, 0x3f, 0xa3, 0xdd, 0x23, 0x78, 0xb7, 0xd0,
+	0x29, 0xda, 0x83, 0x46, 0x76, 0xcd, 0x63, 0xdf, 0xd3, 0xfa, 0xa8, 0xf7, 0xef, 0x2f, 0x6e, 0xb6,
+	0xed, 0x4c, 0x0f, 0x87, 0x03, 0x6c, 0x67, 0xa0, 0x43, 0xaf, 0xfb, 0xcf, 0x2a, 0x6c, 0xae, 0x88,
+	0x05, 0xbd, 0x03, 0x1b, 0xfe, 0x8c, 0x4c, 0x68, 0x42, 0xc7, 0xc9, 0x06, 0x0d, 0xa1, 0x12, 0x90,
+	0x73, 0x1a, 0x28, 0xc9, 0xa8, 0xb4, 0xfd, 0xf4, 0x8d, 0xaa, 0xdb, 0xfd, 0x83, 0xc6, 0x0f, 0x43,
+	0xc9, 0xaf, 0xb0, 0x21, 0x23, 0x07, 0xaa, 0x2e, 0x9b, 0xcd, 0x48, 0xa8, 0xca, 0xcb, 0xfa, 0x4e,
+	0x1d, 0xa7, 0x5b, 0x84, 0xa0, 0x4c, 0xf8, 0x44, 0x38, 0x65, 0x6d, 0xd6, 0x6b, 0xd4, 0x84, 0x75,
+	0x1a, 0xce, 0x9d, 0x0d, 0x6d, 0x52, 0x4b, 0x65, 0xf1, 0xfc, 0xe4, 0xce, 0xeb, 0x58, 0x2d, 0x15,
+	0x2f, 0x16, 0x94, 0x3b, 0x55, 0x6d, 0xd2, 0x6b, 0xf4, 0x4b, 0xa8, 0xcc, 0x58, 0x1c, 0x4a, 0xe1,
+	0xd4, 0x74, 0xb0, 0x0f, 0x8a, 0x82, 0x3d, 0x56, 0x08, 0x53, 0xfe, 0x0c, 0x1c, 0x3d, 0x83, 0x2d,
+	0x21, 0x59, 0x34, 0x9e, 0x70, 0xe2, 0xd2, 0x71, 0x44, 0xb9, 0xcf, 0x3c, 0x7d, 0x1b, 0xb7, 0x54,
+	0xd1, 0x81, 0x69, 0xf8, 0xf8, 0xbe, 0xa2, 0x1d, 0x28, 0xd6, 0x48, 0x93, 0xd0, 0x08, 0x1a, 0x51,
+	0x1c, 0x04, 0x63, 0x16, 0x25, 0xc5, 0x1c, 0xb4, 0x93, 0xb7, 0xc8, 0xda, 0x28, 0x0e, 0x82, 0xaf,
+	0x12, 0x12, 0xb6, 0xa3, 0xe5, 0x06, 0xbd, 0x07, 0x95, 0x09, 0x67, 0x71, 0x24, 0x1c, 0x5b, 0xe7,
+	0xc3, 0xec, 0xd0, 0x97, 0x50, 0x15, 0xd4, 0xe5, 0x54, 0x0a, 0xa7, 0xa1, 0x4f, 0xfb, 0x71, 0xd1,
+	0x4b, 0x4e, 0x35, 0x04, 0xd3, 0x0b, 0xca, 0x69, 0xe8, 0x52, 0x9c, 0x72, 0xd0, 0x03, 0x58, 0x97,
+	0xf2, 0xca, 0xd9, 0xec, 0x58, 0x3b, 0xb5, 0x7e, 0x75, 0x71, 0xb3, 0xbd, 0x7e, 0x76, 0xf6, 0x02,
+	0x2b, 0x9b, 0x2a, 0x53, 0x53, 0x26, 0x64, 0x48, 0x66, 0xd4, 0xb9, 0xa7, 0xd3, 0x9b, 0xed, 0xd1,
+	0x0b, 0x00, 0x2f, 0x14, 0x63, 0x57, 0x7f, 0x17, 0xce, 0x7d, 0x7d, 0xba, 0xcf, 0xde, 0x7c, 0xba,
+	0xc1, 0xc9, 0xa9, 0x29, 0xb6, 0x9b, 0x8b, 0x9b, 0xed, 0x7a, 0xb6, 0xc5, 0x75, 0x2f, 0x14, 0xc9,
+	0x12, 0xf5, 0xc1, 0x9e, 0x52, 0x12, 0xc8, 0xa9, 0x3b, 0xa5, 0xee, 0xa5, 0xd3, 0xbc, 0xbd, 0xf6,
+	0x3e, 0xd3, 0x30, 0xe3, 0x21, 0x4f, 0x52, 0x22, 0x56, 0xa1, 0x0a, 0x67, 0x4b, 0xe7, 0x2a, 0xd9,
+	0xa0, 0x8f, 0x00, 0x58, 0x44, 0xc3, 0xb1, 0x90, 0x9e, 0x1f, 0x3a, 0x48, 0x1d, 0x19, 0xd7, 0x95,
+	0xe5, 0x54, 0x19, 0x5a, 0x5f, 0x80, 0x9d, 0xd3, 0xac, 0xd2, 0xda, 0x25, 0xbd, 0x32, 0x9f, 0x81,
+	0x5a, 0x2a, 0xaf, 0x73, 0x12, 0xc4, 0xc9, 0xcc, 0x55, 0xc7, 0xc9, 0xe6, 0x57, 0x6b, 0x4f, 0xac,
+	0xd6, 0x1e, 0xd8, 0xb9, 0x8b, 0x43, 0x1f, 0xc3, 0x26, 0xa7, 0x13, 0x5f, 0x48, 0x7e, 0x35, 0x26,
+	0xb1, 0x9c, 0x3a, 0xbf, 0xd5, 0x84, 0x46, 0x6a, 0xec, 0xc5, 0x72, 0xda, 0x1a, 0xc3, 0xf2, 0xfc,
+	0xa8, 0x03, 0xb6, 0xca, 0xab, 0xa0, 0x7c, 0x4e, 0xb9, 0xea, 0x0a, 0x2a, 0xec, 0xbc, 0x49, 0xdd,
+	0xbf, 0xa0, 0x84, 0xbb, 0x53, 0xfd, 0x05, 0xd6, 0xb1, 0xd9, 0xa9, 0x4f, 0x2a, 0x15, 0x99, 0xf9,
+	0xa4, 0xcc, 0xb6, 0xfb, 0x1f, 0x0b, 0x1a, 0xf9, 0xf6, 0x84, 0xf6, 0x93, 0xa6, 0xa4, 0x8f, 0x74,
+	0x6f, 0xef, 0xf1, 0x9b, 0xda, 0x99, 0x6e, 0x01, 0x41, 0xac, 0x9c, 0x1d, 0xab, 0x49, 0x52, 0x93,
+	0xd1, 0x2f, 0x60, 0x23, 0x62, 0x5c, 0xa6, 0x85, 0xa0, 0x5d, 0x58, 0xb6, 0x19, 0x4f, 0x4b, 0x66,
+	0x02, 0xee, 0x4e, 0xe1, 0xde, 0xaa, 0x37, 0xf4, 0x08, 0xd6, 0x9f, 0x1f, 0x8e, 0x9a, 0xa5, 0xd6,
+	0x07, 0x2f, 0xaf, 0x3b, 0xef, 0xaf, 0x3e, 0x7c, 0xee, 0x73, 0x19, 0x93, 0xe0, 0x70, 0x84, 0x7e,
+	0x02, 0x1b, 0x83, 0x93, 0x53, 0x8c, 0x9b, 0x56, 0x6b, 0xfb, 0xe5, 0x75, 0xe7, 0x83, 0x55, 0x9c,
+	0x7a, 0xc4, 0xe2, 0xd0, 0xc3, 0xec, 0x3c, 0x9b, 0xaa, 0xfe, 0xb6, 0x06, 0xb6, 0xa9, 0x8f, 0x77,
+	0x3d, 0x78, 0x6f, 0x26, 0x2d, 0x27, 0x55, 0xfd, 0xda, 0x1b, 0x3b, 0x4f, 0x23, 0x21, 0x98, 0x3b,
+	0x7e, 0x08, 0x0d, 0x3f, 0x9a, 0x7f, 0x3e, 0xa6, 0x21, 0x39, 0x0f, 0xcc, 0x80, 0x55, 0xc3, 0xb6,
+	0xb2, 0x0d, 0x13, 0x93, 0xfa, 0xe4, 0xfc, 0x50, 0x52, 0x1e, 0x9a, 0xd1, 0xa9, 0x86, 0xb3, 0x3d,
+	0xfa, 0x12, 0xca, 0x7e, 0x44, 0x66, 0xa6, 0x5d, 0x16, 0x9e, 0xe0, 0x70, 0xd4, 0x3b, 0x36, 0x1a,
+	0xec, 0xd7, 0x16, 0x37, 0xdb, 0x65, 0x65, 0xc0, 0x9a, 0x86, 0xda, 0x69, 0xc7, 0x52, 0x6f, 0xd2,
+	0x15, 0xb4, 0x86, 0x73, 0x96, 0xee, 0x7f, 0xcb, 0x60, 0xef, 0x07, 0xb1, 0x90, 0xa6, 0x0f, 0xdc,
+	0x59, 0xde, 0x5e, 0xc0, 0x16, 0xd1, 0x33, 0x38, 0x09, 0x55, 0x51, 0xd5, 0x93, 0x80, 0xc9, 0xdd,
+	0xa3, 0x42, 0x77, 0x19, 0x38, 0x99, 0x1a, 0xfa, 0x15, 0xe5, 0xd3, 0xb1, 0x70, 0x93, 0x7c, 0xe7,
+	0x09, 0x3a, 0x85, 0x4d, 0xc6, 0xdd, 0x29, 0x15, 0x32, 0xa9, 0xc3, 0x66, 0x66, 0x2d, 0xfc, 0x9b,
+	0xf9, 0x2a, 0x0f, 0x34, 0x45, 0x28, 0x89, 0x76, 0xd5, 0x07, 0x7a, 0x02, 0x65, 0x4e, 0x2e, 0xd2,
+	0xa9, 0xa6, 0x50, 0xdf, 0x98, 0x5c, 0xc8, 0x15, 0x17, 0x9a, 0x81, 0x7e, 0x0f, 0xe0, 0xf9, 0x22,
+	0x22, 0xd2, 0x9d, 0x52, 0x6e, 0xee, 0xa9, 0xf0, 0x88, 0x83, 0x0c, 0xb5, 0xe2, 0x25, 0xc7, 0x46,
+	0x47, 0x50, 0x77, 0x49, 0xaa, 0xb4, 0xca, 0xed, 0x2d, 0x68, 0xbf, 0x67, 0x5c, 0x34, 0x95, 0x8b,
+	0xc5, 0xcd, 0x76, 0x2d, 0xb5, 0xe0, 0x9a, 0x4b, 0x8c, 0xf2, 0x8e, 0x60, 0x53, 0x0d, 0xf8, 0x63,
+	0x8f, 0x5e, 0x90, 0x38, 0x90, 0x42, 0x77, 0xcb, 0x5b, 0x8a, 0xaa, 0x9a, 0x35, 0x07, 0x06, 0x67,
+	0xe2, 0x6a, 0xc8, 0x9c, 0x0d, 0xfd, 0x11, 0xb6, 0x68, 0xe8, 0xf2, 0x2b, 0xad, 0xb3, 0x34, 0xc2,
+	0xda, 0xed, 0x87, 0x1d, 0x66, 0xe0, 0x95, 0xc3, 0x36, 0xe9, 0x77, 0xec, 0x5d, 0x1f, 0x20, 0x69,
+	0x53, 0x77, 0xab, 0x3f, 0x04, 0x65, 0x8f, 0x48, 0xa2, 0x25, 0xd7, 0xc0, 0x7a, 0xdd, 0xff, 0xf0,
+	0xd5, 0xeb, 0x76, 0xe9, 0x1f, 0xaf, 0xdb, 0xa5, 0x7f, 0xbf, 0x6e, 0x5b, 0x7f, 0x5e, 0xb4, 0xad,
+	0x57, 0x8b, 0xb6, 0xf5, 0xf7, 0x45, 0xdb, 0xfa, 0xd7, 0xa2, 0x6d, 0x9d, 0x57, 0xf4, 0xff, 0xfb,
+	0xcf, 0xff, 0x17, 0x00, 0x00, 0xff, 0xff, 0x66, 0xb4, 0xcd, 0xfe, 0x1e, 0x10, 0x00, 0x00,
 }

+ 2 - 2
vendor/github.com/docker/swarmkit/api/specs.proto

@@ -41,8 +41,8 @@ message NodeSpec {
 		DRAIN = 2 [(gogoproto.enumvalue_customname) = "NodeAvailabilityDrain"];
 	}
 
-	// Role defines the role the node should have.
-	NodeRole role = 2;
+	// DesiredRole defines the role the node should have.
+	NodeRole desired_role = 2;
 
 	// Membership controls the admission of the node into the cluster.
 	Membership membership = 3;

+ 2 - 2
vendor/github.com/docker/swarmkit/ca/certificates.go

@@ -20,11 +20,11 @@ import (
 	cflog "github.com/cloudflare/cfssl/log"
 	cfsigner "github.com/cloudflare/cfssl/signer"
 	"github.com/cloudflare/cfssl/signer/local"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/go-events"
 	"github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/ioutils"
 	"github.com/docker/swarmkit/remotes"
+	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 	"google.golang.org/grpc"
@@ -486,7 +486,7 @@ func GetRemoteCA(ctx context.Context, d digest.Digest, r remotes.Remotes) (RootC
 	}
 
 	if d != "" {
-		verifier, err := digest.NewDigestVerifier(d)
+		verifier := d.Verifier()
 		if err != nil {
 			return RootCA{}, errors.Wrap(err, "unexpected error getting digest verifier")
 		}

+ 2 - 2
vendor/github.com/docker/swarmkit/ca/config.go

@@ -15,11 +15,11 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	cfconfig "github.com/cloudflare/cfssl/config"
-	"github.com/docker/distribution/digest"
 	"github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/identity"
 	"github.com/docker/swarmkit/log"
 	"github.com/docker/swarmkit/remotes"
+	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"google.golang.org/grpc/credentials"
 
@@ -196,7 +196,7 @@ func getCAHashFromToken(token string) (digest.Digest, error) {
 	var digestInt big.Int
 	digestInt.SetString(split[2], joinTokenBase)
 
-	return digest.ParseDigest(fmt.Sprintf("sha256:%0[1]*s", 64, digestInt.Text(16)))
+	return digest.Parse(fmt.Sprintf("sha256:%0[1]*s", 64, digestInt.Text(16)))
 }
 
 // DownloadRootCA tries to retrieve a remote root CA and matches the digest against the provided token.

+ 4 - 3
vendor/github.com/docker/swarmkit/ca/server.go

@@ -250,7 +250,8 @@ func (s *Server) IssueNodeCertificate(ctx context.Context, request *api.IssueNod
 		// Create a new node
 		err := s.store.Update(func(tx store.Tx) error {
 			node := &api.Node{
-				ID: nodeID,
+				Role: role,
+				ID:   nodeID,
 				Certificate: api.Certificate{
 					CSR:  request.CSR,
 					CN:   nodeID,
@@ -260,7 +261,7 @@ func (s *Server) IssueNodeCertificate(ctx context.Context, request *api.IssueNod
 					},
 				},
 				Spec: api.NodeSpec{
-					Role:         role,
+					DesiredRole:  role,
 					Membership:   api.NodeMembershipAccepted,
 					Availability: request.Availability,
 				},
@@ -318,7 +319,7 @@ func (s *Server) issueRenewCertificate(ctx context.Context, nodeID string, csr [
 		cert = api.Certificate{
 			CSR:  csr,
 			CN:   node.ID,
-			Role: node.Spec.Role,
+			Role: node.Role,
 			Status: api.IssuanceStatus{
 				State: api.IssuanceStateRenew,
 			},

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

@@ -122,7 +122,7 @@ func NodeMatches(constraints []Constraint, n *api.Node) bool {
 				return false
 			}
 		case strings.EqualFold(constraint.key, "node.role"):
-			if !constraint.Match(n.Spec.Role.String()) {
+			if !constraint.Match(n.Role.String()) {
 				return false
 			}
 		case strings.EqualFold(constraint.key, "node.platform.os"):

+ 3 - 16
vendor/github.com/docker/swarmkit/manager/controlapi/node.go

@@ -146,7 +146,7 @@ func (s *Server) ListNodes(ctx context.Context, request *api.ListNodesRequest) (
 					return true
 				}
 				for _, c := range request.Filters.Roles {
-					if c == e.Spec.Role {
+					if c == e.Role {
 						return true
 					}
 				}
@@ -205,7 +205,6 @@ func (s *Server) UpdateNode(ctx context.Context, request *api.UpdateNodeRequest)
 	var (
 		node   *api.Node
 		member *membership.Member
-		demote bool
 	)
 
 	err := s.store.Update(func(tx store.Tx) error {
@@ -215,9 +214,7 @@ func (s *Server) UpdateNode(ctx context.Context, request *api.UpdateNodeRequest)
 		}
 
 		// Demotion sanity checks.
-		if node.Spec.Role == api.NodeRoleManager && request.Spec.Role == api.NodeRoleWorker {
-			demote = true
-
+		if node.Spec.DesiredRole == api.NodeRoleManager && request.Spec.DesiredRole == api.NodeRoleWorker {
 			// Check for manager entries in Store.
 			managers, err := store.FindNodes(tx, store.ByRole(api.NodeRoleManager))
 			if err != nil {
@@ -246,16 +243,6 @@ func (s *Server) UpdateNode(ctx context.Context, request *api.UpdateNodeRequest)
 		return nil, err
 	}
 
-	if demote && s.raft != nil {
-		// TODO(abronan): the remove can potentially fail and leave the node with
-		// an incorrect role (worker rather than manager), we need to reconcile the
-		// memberlist with the desired state rather than attempting to remove the
-		// member once.
-		if err := s.raft.RemoveMember(ctx, member.RaftID); err != nil {
-			return nil, grpc.Errorf(codes.Internal, "cannot demote manager to worker: %v", err)
-		}
-	}
-
 	return &api.UpdateNodeResponse{
 		Node: node,
 	}, nil
@@ -276,7 +263,7 @@ func (s *Server) RemoveNode(ctx context.Context, request *api.RemoveNodeRequest)
 		if node == nil {
 			return grpc.Errorf(codes.NotFound, "node %s not found", request.NodeID)
 		}
-		if node.Spec.Role == api.NodeRoleManager {
+		if node.Spec.DesiredRole == api.NodeRoleManager {
 			if s.raft == nil {
 				return grpc.Errorf(codes.FailedPrecondition, "node %s is a manager but cannot access node information from the raft memberlist", request.NodeID)
 			}

+ 32 - 22
vendor/github.com/docker/swarmkit/manager/manager.go

@@ -125,6 +125,7 @@ type Manager struct {
 	localserver            *grpc.Server
 	raftNode               *raft.Node
 	dekRotator             *RaftDEKManager
+	roleManager            *roleManager
 
 	cancelFunc context.CancelFunc
 
@@ -270,6 +271,12 @@ func New(config *Config) (*Manager, error) {
 	return m, nil
 }
 
+// RemovedFromRaft returns a channel that's closed if the manager is removed
+// from the raft cluster. This should be used to trigger a manager shutdown.
+func (m *Manager) RemovedFromRaft() <-chan struct{} {
+	return m.raftNode.RemovedFromRaft
+}
+
 // Addr returns tcp address on which remote api listens.
 func (m *Manager) Addr() string {
 	return m.config.RemoteAPI.ListenAddr
@@ -388,39 +395,26 @@ func (m *Manager) Run(parent context.Context) error {
 
 	close(m.started)
 
-	errCh := make(chan error, 1)
 	go func() {
 		err := m.raftNode.Run(ctx)
 		if err != nil {
-			errCh <- err
 			log.G(ctx).WithError(err).Error("raft node stopped")
-			m.Stop(ctx)
+			m.Stop(ctx, false)
 		}
 	}()
 
-	returnErr := func(err error) error {
-		select {
-		case runErr := <-errCh:
-			if runErr == raft.ErrMemberRemoved {
-				return runErr
-			}
-		default:
-		}
-		return err
-	}
-
 	if err := raft.WaitForLeader(ctx, m.raftNode); err != nil {
-		return returnErr(err)
+		return err
 	}
 
 	c, err := raft.WaitForCluster(ctx, m.raftNode)
 	if err != nil {
-		return returnErr(err)
+		return err
 	}
 	raftConfig := c.Spec.Raft
 
 	if err := m.watchForKEKChanges(ctx); err != nil {
-		return returnErr(err)
+		return err
 	}
 
 	if int(raftConfig.ElectionTick) != m.raftNode.Config.ElectionTick {
@@ -438,16 +432,17 @@ func (m *Manager) Run(parent context.Context) error {
 		return nil
 	}
 	m.mu.Unlock()
-	m.Stop(ctx)
+	m.Stop(ctx, false)
 
-	return returnErr(err)
+	return err
 }
 
 const stopTimeout = 8 * time.Second
 
 // Stop stops the manager. It immediately closes all open connections and
-// active RPCs as well as stopping the scheduler.
-func (m *Manager) Stop(ctx context.Context) {
+// active RPCs as well as stopping the scheduler. If clearData is set, the
+// raft logs, snapshots, and keys will be erased.
+func (m *Manager) Stop(ctx context.Context, clearData bool) {
 	log.G(ctx).Info("Stopping manager")
 	// It's not safe to start shutting down while the manager is still
 	// starting up.
@@ -472,6 +467,8 @@ func (m *Manager) Stop(ctx context.Context) {
 		close(localSrvDone)
 	}()
 
+	m.raftNode.Cancel()
+
 	m.dispatcher.Stop()
 	m.logbroker.Stop()
 	m.caserver.Stop()
@@ -494,10 +491,16 @@ func (m *Manager) Stop(ctx context.Context) {
 	if m.scheduler != nil {
 		m.scheduler.Stop()
 	}
+	if m.roleManager != nil {
+		m.roleManager.Stop()
+	}
 	if m.keyManager != nil {
 		m.keyManager.Stop()
 	}
 
+	if clearData {
+		m.raftNode.ClearData()
+	}
 	m.cancelFunc()
 	<-m.raftNode.Done()
 
@@ -778,6 +781,7 @@ func (m *Manager) becomeLeader(ctx context.Context) {
 	m.taskReaper = taskreaper.New(s)
 	m.scheduler = scheduler.New(s)
 	m.keyManager = keymanager.New(s, keymanager.DefaultConfig())
+	m.roleManager = newRoleManager(s, m.raftNode)
 
 	// TODO(stevvooe): Allocate a context that can be used to
 	// shutdown underlying manager processes when leadership is
@@ -853,6 +857,9 @@ func (m *Manager) becomeLeader(ctx context.Context) {
 		}
 	}(m.globalOrchestrator)
 
+	go func(roleManager *roleManager) {
+		roleManager.Run()
+	}(m.roleManager)
 }
 
 // becomeFollower shuts down the subsystems that are only run by the leader.
@@ -881,6 +888,9 @@ func (m *Manager) becomeFollower() {
 	m.scheduler.Stop()
 	m.scheduler = nil
 
+	m.roleManager.Stop()
+	m.roleManager = nil
+
 	if m.keyManager != nil {
 		m.keyManager.Stop()
 		m.keyManager = nil
@@ -937,7 +947,7 @@ func managerNode(nodeID string, availability api.NodeSpec_Availability) *api.Nod
 			},
 		},
 		Spec: api.NodeSpec{
-			Role:         api.NodeRoleManager,
+			DesiredRole:  api.NodeRoleManager,
 			Membership:   api.NodeMembershipAccepted,
 			Availability: availability,
 		},

+ 167 - 0
vendor/github.com/docker/swarmkit/manager/role_manager.go

@@ -0,0 +1,167 @@
+package manager
+
+import (
+	"time"
+
+	"github.com/docker/swarmkit/api"
+	"github.com/docker/swarmkit/log"
+	"github.com/docker/swarmkit/manager/state"
+	"github.com/docker/swarmkit/manager/state/raft"
+	"github.com/docker/swarmkit/manager/state/store"
+	"golang.org/x/net/context"
+)
+
+const roleReconcileInterval = 5 * time.Second
+
+// roleManager reconciles the raft member list with desired role changes.
+type roleManager struct {
+	ctx    context.Context
+	cancel func()
+
+	store    *store.MemoryStore
+	raft     *raft.Node
+	doneChan chan struct{}
+
+	// pending contains changed nodes that have not yet been reconciled in
+	// the raft member list.
+	pending map[string]*api.Node
+}
+
+// newRoleManager creates a new roleManager.
+func newRoleManager(store *store.MemoryStore, raftNode *raft.Node) *roleManager {
+	ctx, cancel := context.WithCancel(context.Background())
+	return &roleManager{
+		ctx:      ctx,
+		cancel:   cancel,
+		store:    store,
+		raft:     raftNode,
+		doneChan: make(chan struct{}),
+		pending:  make(map[string]*api.Node),
+	}
+}
+
+// Run is roleManager's main loop.
+func (rm *roleManager) Run() {
+	defer close(rm.doneChan)
+
+	var (
+		nodes    []*api.Node
+		ticker   *time.Ticker
+		tickerCh <-chan time.Time
+	)
+
+	watcher, cancelWatch, err := store.ViewAndWatch(rm.store,
+		func(readTx store.ReadTx) error {
+			var err error
+			nodes, err = store.FindNodes(readTx, store.All)
+			return err
+		},
+		state.EventUpdateNode{})
+	defer cancelWatch()
+
+	if err != nil {
+		log.L.WithError(err).Error("failed to check nodes for role changes")
+	} else {
+		for _, node := range nodes {
+			rm.pending[node.ID] = node
+			rm.reconcileRole(node)
+		}
+		if len(rm.pending) != 0 {
+			ticker = time.NewTicker(roleReconcileInterval)
+			tickerCh = ticker.C
+		}
+	}
+
+	for {
+		select {
+		case event := <-watcher:
+			node := event.(state.EventUpdateNode).Node
+			rm.pending[node.ID] = node
+			rm.reconcileRole(node)
+			if len(rm.pending) != 0 && ticker == nil {
+				ticker = time.NewTicker(roleReconcileInterval)
+				tickerCh = ticker.C
+			}
+		case <-tickerCh:
+			for _, node := range rm.pending {
+				rm.reconcileRole(node)
+			}
+			if len(rm.pending) == 0 {
+				ticker.Stop()
+				ticker = nil
+				tickerCh = nil
+			}
+		case <-rm.ctx.Done():
+			if ticker != nil {
+				ticker.Stop()
+			}
+			return
+		}
+	}
+}
+
+func (rm *roleManager) reconcileRole(node *api.Node) {
+	if node.Role == node.Spec.DesiredRole {
+		// Nothing to do.
+		delete(rm.pending, node.ID)
+		return
+	}
+
+	// Promotion can proceed right away.
+	if node.Spec.DesiredRole == api.NodeRoleManager && node.Role == api.NodeRoleWorker {
+		err := rm.store.Update(func(tx store.Tx) error {
+			updatedNode := store.GetNode(tx, node.ID)
+			if updatedNode == nil || updatedNode.Spec.DesiredRole != node.Spec.DesiredRole || updatedNode.Role != node.Role {
+				return nil
+			}
+			updatedNode.Role = api.NodeRoleManager
+			return store.UpdateNode(tx, updatedNode)
+		})
+		if err != nil {
+			log.L.WithError(err).Errorf("failed to promote node %s", node.ID)
+		} else {
+			delete(rm.pending, node.ID)
+		}
+	} else if node.Spec.DesiredRole == api.NodeRoleWorker && node.Role == api.NodeRoleManager {
+		// Check for node in memberlist
+		member := rm.raft.GetMemberByNodeID(node.ID)
+		if member != nil {
+			// Quorum safeguard
+			if !rm.raft.CanRemoveMember(member.RaftID) {
+				// TODO(aaronl): Retry later
+				log.L.Debugf("can't demote node %s at this time: removing member from raft would result in a loss of quorum", node.ID)
+				return
+			}
+
+			rmCtx, rmCancel := context.WithTimeout(rm.ctx, 5*time.Second)
+			defer rmCancel()
+
+			if err := rm.raft.RemoveMember(rmCtx, member.RaftID); err != nil {
+				// TODO(aaronl): Retry later
+				log.L.WithError(err).Debugf("can't demote node %s at this time", node.ID)
+				return
+			}
+		}
+
+		err := rm.store.Update(func(tx store.Tx) error {
+			updatedNode := store.GetNode(tx, node.ID)
+			if updatedNode == nil || updatedNode.Spec.DesiredRole != node.Spec.DesiredRole || updatedNode.Role != node.Role {
+				return nil
+			}
+			updatedNode.Role = api.NodeRoleWorker
+
+			return store.UpdateNode(tx, updatedNode)
+		})
+		if err != nil {
+			log.L.WithError(err).Errorf("failed to demote node %s", node.ID)
+		} else {
+			delete(rm.pending, node.ID)
+		}
+	}
+}
+
+// Stop stops the roleManager and waits for the main loop to exit.
+func (rm *roleManager) Stop() {
+	rm.cancel()
+	<-rm.doneChan
+}

+ 55 - 44
vendor/github.com/docker/swarmkit/manager/state/raft/raft.go

@@ -112,9 +112,10 @@ type Node struct {
 
 	ticker clock.Ticker
 	doneCh chan struct{}
-	// removeRaftCh notifies about node deletion from raft cluster
-	removeRaftCh        chan struct{}
+	// RemovedFromRaft notifies about node deletion from raft cluster
+	RemovedFromRaft     chan struct{}
 	removeRaftFunc      func()
+	cancelFunc          func()
 	leadershipBroadcast *watch.Queue
 
 	// used to coordinate shutdown
@@ -134,6 +135,7 @@ type Node struct {
 	raftLogger          *storage.EncryptedRaftLogger
 	keyRotator          EncryptionKeyRotator
 	rotationQueued      bool
+	clearData           bool
 	waitForAppliedIndex uint64
 }
 
@@ -199,7 +201,7 @@ func NewNode(opts NodeOptions) *Node {
 			Logger:          cfg.Logger,
 		},
 		doneCh:              make(chan struct{}),
-		removeRaftCh:        make(chan struct{}),
+		RemovedFromRaft:     make(chan struct{}),
 		stopped:             make(chan struct{}),
 		leadershipBroadcast: watch.NewQueue(),
 		lastSendToMember:    make(map[uint64]chan struct{}),
@@ -220,7 +222,17 @@ func NewNode(opts NodeOptions) *Node {
 		var removeRaftOnce sync.Once
 		return func() {
 			removeRaftOnce.Do(func() {
-				close(n.removeRaftCh)
+				atomic.StoreUint32(&n.isMember, 0)
+				close(n.RemovedFromRaft)
+			})
+		}
+	}(n)
+
+	n.cancelFunc = func(n *Node) func() {
+		var cancelOnce sync.Once
+		return func() {
+			cancelOnce.Do(func() {
+				close(n.stopped)
 			})
 		}
 	}(n)
@@ -364,6 +376,12 @@ func (n *Node) done() {
 	close(n.doneCh)
 }
 
+// ClearData tells the raft node to delete its WALs, snapshots, and keys on
+// shutdown.
+func (n *Node) ClearData() {
+	n.clearData = true
+}
+
 // Run is the main loop for a Raft node, it goes along the state machine,
 // acting on the messages received from other Raft nodes in the cluster.
 //
@@ -373,13 +391,10 @@ func (n *Node) Run(ctx context.Context) error {
 	ctx = log.WithLogger(ctx, logrus.WithField("raft_id", fmt.Sprintf("%x", n.Config.ID)))
 	ctx, cancel := context.WithCancel(ctx)
 
-	// nodeRemoved indicates that node was stopped due its removal.
-	nodeRemoved := false
-
 	defer func() {
 		cancel()
 		n.stop(ctx)
-		if nodeRemoved {
+		if n.clearData {
 			// Delete WAL and snapshots, since they are no longer
 			// usable.
 			if err := n.raftLogger.Clear(ctx); err != nil {
@@ -501,9 +516,7 @@ func (n *Node) Run(ctx context.Context) error {
 					n.campaignWhenAble = false
 				}
 				if len(members) == 1 && members[n.Config.ID] != nil {
-					if err := n.raftNode.Campaign(ctx); err != nil {
-						panic("raft: cannot campaign to be the leader on node restore")
-					}
+					n.raftNode.Campaign(ctx)
 				}
 			}
 
@@ -536,12 +549,6 @@ func (n *Node) Run(ctx context.Context) error {
 			case n.needsSnapshot(ctx):
 				n.doSnapshot(ctx, n.getCurrentRaftConfig())
 			}
-		case <-n.removeRaftCh:
-			nodeRemoved = true
-			// If the node was removed from other members,
-			// send back an error to the caller to start
-			// the shutdown process.
-			return ErrMemberRemoved
 		case <-ctx.Done():
 			return nil
 		}
@@ -605,6 +612,15 @@ func (n *Node) getCurrentRaftConfig() api.RaftConfig {
 	return raftConfig
 }
 
+// Cancel interrupts all ongoing proposals, and prevents new ones from
+// starting. This is useful for the shutdown sequence because it allows
+// the manager to shut down raft-dependent services that might otherwise
+// block on shutdown if quorum isn't met. Then the raft node can be completely
+// shut down once no more code is using it.
+func (n *Node) Cancel() {
+	n.cancelFunc()
+}
+
 // Done returns channel which is closed when raft node is fully stopped.
 func (n *Node) Done() <-chan struct{} {
 	return n.doneCh
@@ -614,8 +630,7 @@ func (n *Node) stop(ctx context.Context) {
 	n.stopMu.Lock()
 	defer n.stopMu.Unlock()
 
-	close(n.stopped)
-
+	n.Cancel()
 	n.waitProp.Wait()
 	n.asyncTasks.Wait()
 
@@ -1240,17 +1255,6 @@ func (n *Node) IsMember() bool {
 	return atomic.LoadUint32(&n.isMember) == 1
 }
 
-// canSubmitProposal defines if any more proposals
-// could be submitted and processed.
-func (n *Node) canSubmitProposal() bool {
-	select {
-	case <-n.stopped:
-		return false
-	default:
-		return true
-	}
-}
-
 // Saves a log entry to our Store
 func (n *Node) saveToStorage(
 	ctx context.Context,
@@ -1467,11 +1471,6 @@ func (n *Node) handleAddressChange(ctx context.Context, member *membership.Membe
 	return nil
 }
 
-type applyResult struct {
-	resp proto.Message
-	err  error
-}
-
 // processInternalRaftRequest sends a message to nodes participating
 // in the raft to apply a log entry and then waits for it to be applied
 // on the server. It will block until the update is performed, there is
@@ -1479,7 +1478,7 @@ type applyResult struct {
 // shutdown.
 func (n *Node) processInternalRaftRequest(ctx context.Context, r *api.InternalRaftRequest, cb func()) (proto.Message, error) {
 	n.stopMu.RLock()
-	if !n.canSubmitProposal() {
+	if !n.IsMember() {
 		n.stopMu.RUnlock()
 		return nil, ErrStopped
 	}
@@ -1519,15 +1518,27 @@ func (n *Node) processInternalRaftRequest(ctx context.Context, r *api.InternalRa
 	}
 
 	select {
-	case x := <-ch:
-		res := x.(*applyResult)
-		return res.resp, res.err
+	case x, ok := <-ch:
+		if !ok {
+			return nil, ErrLostLeadership
+		}
+		return x.(proto.Message), nil
 	case <-waitCtx.Done():
 		n.wait.cancel(r.ID)
-		return nil, ErrLostLeadership
+		// if channel is closed, wait item was canceled, otherwise it was triggered
+		x, ok := <-ch
+		if !ok {
+			return nil, ErrLostLeadership
+		}
+		return x.(proto.Message), nil
 	case <-ctx.Done():
 		n.wait.cancel(r.ID)
-		return nil, ctx.Err()
+		// if channel is closed, wait item was canceled, otherwise it was triggered
+		x, ok := <-ch
+		if !ok {
+			return nil, ctx.Err()
+		}
+		return x.(proto.Message), nil
 	}
 }
 
@@ -1588,7 +1599,7 @@ func (n *Node) processEntry(ctx context.Context, entry raftpb.Entry) error {
 		return nil
 	}
 
-	if !n.wait.trigger(r.ID, &applyResult{resp: r, err: nil}) {
+	if !n.wait.trigger(r.ID, r) {
 		// There was no wait on this ID, meaning we don't have a
 		// transaction in progress that would be committed to the
 		// memory store by the "trigger" call. Either a different node
@@ -1713,11 +1724,11 @@ func (n *Node) applyRemoveNode(ctx context.Context, cc raftpb.ConfChange) (err e
 	}
 
 	if cc.NodeID == n.Config.ID {
-		n.removeRaftFunc()
-
 		// wait the commit ack to be sent before closing connection
 		n.asyncTasks.Wait()
 
+		n.removeRaftFunc()
+
 		// if there are only 2 nodes in the cluster, and leader is leaving
 		// before closing the connection, leader has to ensure that follower gets
 		// noticed about this raft conf change commit. Otherwise, follower would

+ 6 - 2
vendor/github.com/docker/swarmkit/manager/state/raft/wait.go

@@ -55,8 +55,11 @@ func (w *wait) cancel(id uint64) {
 	waitItem, ok := w.m[id]
 	delete(w.m, id)
 	w.l.Unlock()
-	if ok && waitItem.cancel != nil {
-		waitItem.cancel()
+	if ok {
+		if waitItem.cancel != nil {
+			waitItem.cancel()
+		}
+		close(waitItem.ch)
 	}
 }
 
@@ -69,5 +72,6 @@ func (w *wait) cancelAll() {
 		if waitItem.cancel != nil {
 			waitItem.cancel()
 		}
+		close(waitItem.ch)
 	}
 }

+ 1 - 1
vendor/github.com/docker/swarmkit/manager/state/store/nodes.go

@@ -238,7 +238,7 @@ func (ni nodeIndexerByRole) FromObject(obj interface{}) (bool, []byte, error) {
 	}
 
 	// Add the null character as a terminator
-	return true, []byte(strconv.FormatInt(int64(n.Spec.Role), 10) + "\x00"), nil
+	return true, []byte(strconv.FormatInt(int64(n.Role), 10) + "\x00"), nil
 }
 
 type nodeIndexerByMembership struct{}

+ 52 - 40
vendor/github.com/docker/swarmkit/node/node.go

@@ -22,7 +22,6 @@ import (
 	"github.com/docker/swarmkit/log"
 	"github.com/docker/swarmkit/manager"
 	"github.com/docker/swarmkit/manager/encryption"
-	"github.com/docker/swarmkit/manager/state/raft"
 	"github.com/docker/swarmkit/remotes"
 	"github.com/docker/swarmkit/xnet"
 	"github.com/pkg/errors"
@@ -249,18 +248,13 @@ func (n *Node) run(ctx context.Context) (err error) {
 				// If we got a role change, renew
 				lastRole := n.role
 				role := ca.WorkerRole
-				if node.Spec.Role == api.NodeRoleManager {
+				if node.Role == api.NodeRoleManager {
 					role = ca.ManagerRole
 				}
 				if lastRole == role {
 					n.Unlock()
 					continue
 				}
-				// switch role to agent immediately to shutdown manager early
-				if role == ca.WorkerRole {
-					n.role = role
-					n.roleCond.Broadcast()
-				}
 				n.Unlock()
 				renewCert()
 			}
@@ -308,7 +302,18 @@ func (n *Node) run(ctx context.Context) (err error) {
 	go func() {
 		<-agentReady
 		if role == ca.ManagerRole {
-			<-managerReady
+			workerRole := make(chan struct{})
+			waitRoleCtx, waitRoleCancel := context.WithCancel(ctx)
+			go func() {
+				if n.waitRole(waitRoleCtx, ca.WorkerRole) == nil {
+					close(workerRole)
+				}
+			}()
+			select {
+			case <-managerReady:
+			case <-workerRole:
+			}
+			waitRoleCancel()
 		}
 		close(n.ready)
 	}()
@@ -632,7 +637,7 @@ func (n *Node) waitRole(ctx context.Context, role string) error {
 	return nil
 }
 
-func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig, ready chan struct{}) error {
+func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig, ready chan struct{}, workerRole <-chan struct{}) error {
 	remoteAddr, _ := n.remotes.Select(n.NodeID())
 	m, err := manager.New(&manager.Config{
 		ForceNewCluster: n.config.ForceNewCluster,
@@ -657,25 +662,18 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig
 	done := make(chan struct{})
 	var runErr error
 	go func() {
-		if err := m.Run(context.Background()); err != nil && err != raft.ErrMemberRemoved {
+		if err := m.Run(context.Background()); err != nil {
 			runErr = err
 		}
 		close(done)
 	}()
 
-	workerRole := make(chan struct{})
-	waitRoleCtx, waitRoleCancel := context.WithCancel(ctx)
-	defer waitRoleCancel()
-	go func() {
-		n.waitRole(waitRoleCtx, ca.WorkerRole)
-		close(workerRole)
-	}()
-
+	var clearData bool
 	defer func() {
 		n.Lock()
 		n.manager = nil
 		n.Unlock()
-		m.Stop(ctx)
+		m.Stop(ctx, clearData)
 		<-done
 		n.setControlSocket(nil)
 	}()
@@ -706,33 +704,19 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig
 	}
 
 	// wait for manager stop or for role change
-	// if manager stopped before role change, wait for new role for 16 seconds,
-	// then just restart manager, we might just miss that event.
-	// we need to wait for role to prevent manager to start again with wrong
-	// certificate
 	select {
 	case <-done:
-		timer := time.NewTimer(16 * time.Second)
-		defer timer.Stop()
-		select {
-		case <-timer.C:
-			log.G(ctx).Warn("failed to get worker role after manager stop, restart manager")
-		case <-workerRole:
-		case <-ctx.Done():
-			return ctx.Err()
-		}
 		return runErr
 	case <-workerRole:
-		log.G(ctx).Info("role changed to worker, wait for manager to stop")
-		select {
-		case <-done:
-			return runErr
-		case <-ctx.Done():
-			return ctx.Err()
-		}
+		log.G(ctx).Info("role changed to worker, stopping manager")
+		clearData = true
+	case <-m.RemovedFromRaft():
+		log.G(ctx).Info("manager removed from raft cluster, stopping manager")
+		clearData = true
 	case <-ctx.Done():
 		return ctx.Err()
 	}
+	return nil
 }
 
 func (n *Node) superviseManager(ctx context.Context, securityConfig *ca.SecurityConfig, ready chan struct{}) error {
@@ -740,9 +724,37 @@ func (n *Node) superviseManager(ctx context.Context, securityConfig *ca.Security
 		if err := n.waitRole(ctx, ca.ManagerRole); err != nil {
 			return err
 		}
-		if err := n.runManager(ctx, securityConfig, ready); err != nil {
+
+		workerRole := make(chan struct{})
+		waitRoleCtx, waitRoleCancel := context.WithCancel(ctx)
+		go func() {
+			if n.waitRole(waitRoleCtx, ca.WorkerRole) == nil {
+				close(workerRole)
+			}
+		}()
+
+		if err := n.runManager(ctx, securityConfig, ready, workerRole); err != nil {
+			waitRoleCancel()
 			return errors.Wrap(err, "manager stopped")
 		}
+
+		// If the manager stopped running and our role is still
+		// "manager", it's possible that the manager was demoted and
+		// the agent hasn't realized this yet. We should wait for the
+		// role to change instead of restarting the manager immediately.
+		timer := time.NewTimer(16 * time.Second)
+		select {
+		case <-timer.C:
+			log.G(ctx).Warn("failed to get worker role after manager stop, restarting manager")
+		case <-workerRole:
+		case <-ctx.Done():
+			timer.Stop()
+			waitRoleCancel()
+			return ctx.Err()
+		}
+		timer.Stop()
+		waitRoleCancel()
+
 		ready = nil
 	}
 }

+ 15 - 31
vendor/github.com/docker/distribution/digest/digester.go → vendor/github.com/opencontainers/go-digest/algorithm.go

@@ -39,7 +39,7 @@ var (
 )
 
 // Available returns true if the digest type is available for use. If this
-// returns false, New and Hash will return nil.
+// returns false, Digester and Hash will return nil.
 func (a Algorithm) Available() bool {
 	h, ok := algorithms[a]
 	if !ok {
@@ -72,13 +72,17 @@ func (a *Algorithm) Set(value string) error {
 		*a = Algorithm(value)
 	}
 
+	if !a.Available() {
+		return ErrDigestUnsupported
+	}
+
 	return nil
 }
 
-// New returns a new digester for the specified algorithm. If the algorithm
+// Digester returns a new digester for the specified algorithm. If the algorithm
 // does not have a digester implementation, nil will be returned. This can be
-// checked by calling Available before calling New.
-func (a Algorithm) New() Digester {
+// checked by calling Available before calling Digester.
+func (a Algorithm) Digester() Digester {
 	return &digester{
 		alg:  a,
 		hash: a.Hash(),
@@ -89,6 +93,11 @@ func (a Algorithm) New() Digester {
 // method will panic. Check Algorithm.Available() before calling.
 func (a Algorithm) Hash() hash.Hash {
 	if !a.Available() {
+		// Empty algorithm string is invalid
+		if a == "" {
+			panic(fmt.Sprintf("empty digest algorithm, validate before calling Algorithm.Hash()"))
+		}
+
 		// NOTE(stevvooe): A missing hash is usually a programming error that
 		// must be resolved at compile time. We don't import in the digest
 		// package to allow users to choose their hash implementation (such as
@@ -104,7 +113,7 @@ func (a Algorithm) Hash() hash.Hash {
 
 // FromReader returns the digest of the reader using the algorithm.
 func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
-	digester := a.New()
+	digester := a.Digester()
 
 	if _, err := io.Copy(digester.Hash(), rd); err != nil {
 		return "", err
@@ -115,7 +124,7 @@ func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
 
 // FromBytes digests the input and returns a Digest.
 func (a Algorithm) FromBytes(p []byte) Digest {
-	digester := a.New()
+	digester := a.Digester()
 
 	if _, err := digester.Hash().Write(p); err != nil {
 		// Writes to a Hash should never fail. None of the existing
@@ -133,28 +142,3 @@ func (a Algorithm) FromBytes(p []byte) Digest {
 func (a Algorithm) FromString(s string) Digest {
 	return a.FromBytes([]byte(s))
 }
-
-// TODO(stevvooe): Allow resolution of verifiers using the digest type and
-// this registration system.
-
-// Digester calculates the digest of written data. Writes should go directly
-// to the return value of Hash, while calling Digest will return the current
-// value of the digest.
-type Digester interface {
-	Hash() hash.Hash // provides direct access to underlying hash instance.
-	Digest() Digest
-}
-
-// digester provides a simple digester definition that embeds a hasher.
-type digester struct {
-	alg  Algorithm
-	hash hash.Hash
-}
-
-func (d *digester) Hash() hash.Hash {
-	return d.hash
-}
-
-func (d *digester) Digest() Digest {
-	return NewDigest(d.alg, d.hash)
-}

+ 23 - 27
vendor/github.com/docker/distribution/digest/digest.go → vendor/github.com/opencontainers/go-digest/digest.go

@@ -8,11 +8,6 @@ import (
 	"strings"
 )
 
-const (
-	// DigestSha256EmptyTar is the canonical sha256 digest of empty data
-	DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
-)
-
 // Digest allows simple protection of hex formatted digest strings, prefixed
 // by their algorithm. Strings of type Digest have some guarantee of being in
 // the correct format and it provides quick access to the components of a
@@ -61,16 +56,14 @@ var (
 	ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm")
 )
 
-// ParseDigest parses s and returns the validated digest object. An error will
+// Parse parses s and returns the validated digest object. An error will
 // be returned if the format is invalid.
-func ParseDigest(s string) (Digest, error) {
+func Parse(s string) (Digest, error) {
 	d := Digest(s)
-
 	return d, d.Validate()
 }
 
-// FromReader returns the most valid digest for the underlying content using
-// the canonical digest algorithm.
+// FromReader consumes the content of rd until io.EOF, returning canonical digest.
 func FromReader(rd io.Reader) (Digest, error) {
 	return Canonical.FromReader(rd)
 }
@@ -90,30 +83,24 @@ func FromString(s string) Digest {
 func (d Digest) Validate() error {
 	s := string(d)
 
-	if !DigestRegexpAnchored.MatchString(s) {
-		return ErrDigestInvalidFormat
-	}
-
 	i := strings.Index(s, ":")
-	if i < 0 {
-		return ErrDigestInvalidFormat
-	}
 
-	// case: "sha256:" with no hex.
-	if i+1 == len(s) {
+	// validate i then run through regexp
+	if i < 0 || i+1 == len(s) || !DigestRegexpAnchored.MatchString(s) {
 		return ErrDigestInvalidFormat
 	}
 
-	switch algorithm := Algorithm(s[:i]); algorithm {
-	case SHA256, SHA384, SHA512:
-		if algorithm.Size()*2 != len(s[i+1:]) {
-			return ErrDigestInvalidLength
-		}
-		break
-	default:
+	algorithm := Algorithm(s[:i])
+	if !algorithm.Available() {
 		return ErrDigestUnsupported
 	}
 
+	// Digests much always be hex-encoded, ensuring that their hex portion will
+	// always be size*2
+	if algorithm.Size()*2 != len(s[i+1:]) {
+		return ErrDigestInvalidLength
+	}
+
 	return nil
 }
 
@@ -123,6 +110,15 @@ func (d Digest) Algorithm() Algorithm {
 	return Algorithm(d[:d.sepIndex()])
 }
 
+// Verifier returns a writer object that can be used to verify a stream of
+// content against the digest. If the digest is invalid, the method will panic.
+func (d Digest) Verifier() Verifier {
+	return hashVerifier{
+		hash:   d.Algorithm().Hash(),
+		digest: d,
+	}
+}
+
 // Hex returns the hex digest portion of the digest. This will panic if the
 // underlying digest is not in a valid format.
 func (d Digest) Hex() string {
@@ -137,7 +133,7 @@ func (d Digest) sepIndex() int {
 	i := strings.Index(string(d), ":")
 
 	if i < 0 {
-		panic("could not find ':' in digest: " + d)
+		panic(fmt.Sprintf("no ':' separator in digest %q", d))
 	}
 
 	return i

+ 25 - 0
vendor/github.com/opencontainers/go-digest/digester.go

@@ -0,0 +1,25 @@
+package digest
+
+import "hash"
+
+// Digester calculates the digest of written data. Writes should go directly
+// to the return value of Hash, while calling Digest will return the current
+// value of the digest.
+type Digester interface {
+	Hash() hash.Hash // provides direct access to underlying hash instance.
+	Digest() Digest
+}
+
+// digester provides a simple digester definition that embeds a hasher.
+type digester struct {
+	alg  Algorithm
+	hash hash.Hash
+}
+
+func (d *digester) Hash() hash.Hash {
+	return d.hash
+}
+
+func (d *digester) Digest() Digest {
+	return NewDigest(d.alg, d.hash)
+}

+ 0 - 0
vendor/github.com/docker/distribution/digest/doc.go → vendor/github.com/opencontainers/go-digest/doc.go


+ 0 - 13
vendor/github.com/docker/distribution/digest/verifiers.go → vendor/github.com/opencontainers/go-digest/verifiers.go

@@ -17,19 +17,6 @@ type Verifier interface {
 	Verified() bool
 }
 
-// NewDigestVerifier returns a verifier that compares the written bytes
-// against a passed in digest.
-func NewDigestVerifier(d Digest) (Verifier, error) {
-	if err := d.Validate(); err != nil {
-		return nil, err
-	}
-
-	return hashVerifier{
-		hash:   d.Algorithm().Hash(),
-		digest: d,
-	}, nil
-}
-
 type hashVerifier struct {
 	digest Digest
 	hash   hash.Hash