Browse code

Vendor updated swarmkit, libnetwork, engine-api

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2016/07/01 05:34:48
Showing 71 changed files
... ...
@@ -60,12 +60,12 @@ clone git golang.org/x/net 2beffdc2e92c8a3027590f898fe88f69af48a3f8 https://gith
60 60
 clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git
61 61
 clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
62 62
 clone git github.com/docker/go-connections fa2850ff103453a9ad190da0df0af134f0314b3d
63
-clone git github.com/docker/engine-api 19b4fb48a86c3318e610e156ec06b684f79ac31d
63
+clone git github.com/docker/engine-api 62043eb79d581a32ea849645277023c550732e52
64 64
 clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
65 65
 clone git github.com/imdario/mergo 0.2.1
66 66
 
67 67
 #get libnetwork packages
68
-clone git github.com/docker/libnetwork  ed311d050fda7821f2e7c53a7e08a0205923aef5
68
+clone git github.com/docker/libnetwork 377a7337f2387cce3be1df7a4503446147b68ff1
69 69
 clone git github.com/docker/go-events 39718a26497694185f8fb58a7d6f31947f3dc42d
70 70
 clone git github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
71 71
 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
... ...
@@ -139,10 +139,10 @@ clone git github.com/docker/docker-credential-helpers v0.3.0
139 139
 clone git github.com/docker/containerd b93a33be39bc4ef0fb00bfcb79147a28c33d9d43
140 140
 
141 141
 # cluster
142
-clone git github.com/docker/swarmkit 3f135f206179ea157aeef2d1d401eb795f618da8
142
+clone git github.com/docker/swarmkit 036a4a1e934bd1bbb35c3ec7f85dea2ba6d4e336
143 143
 clone git github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
144 144
 clone git github.com/gogo/protobuf 43a2e0b1c32252bfbbdf81f7faa7a88fb3fa4028
145
-clone git github.com/cloudflare/cfssl 92f037e39eb103fb30f9151be40d9ed267fc4ae2
145
+clone git github.com/cloudflare/cfssl b895b0549c0ff676f92cf09ba971ae02bb41367b
146 146
 clone git github.com/google/certificate-transparency 025a5cab06f6a819c455d9fdc9e2a1b6d0982284
147 147
 clone git golang.org/x/crypto 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2 https://github.com/golang/crypto.git
148 148
 clone git github.com/mreiferson/go-httpclient 63fe23f7434723dc904c901043af07931f293c47
149 149
new file mode 100644
... ...
@@ -0,0 +1,231 @@
0
+// Package api implements an HTTP-based API and server for CFSSL.
1
+package api
2
+
3
+import (
4
+	"encoding/json"
5
+	"io/ioutil"
6
+	"net/http"
7
+
8
+	"github.com/cloudflare/cfssl/errors"
9
+	"github.com/cloudflare/cfssl/log"
10
+)
11
+
12
+// Handler is an interface providing a generic mechanism for handling HTTP requests.
13
+type Handler interface {
14
+	Handle(w http.ResponseWriter, r *http.Request) error
15
+}
16
+
17
+// HTTPHandler is a wrapper that encapsulates Handler interface as http.Handler.
18
+// HTTPHandler also enforces that the Handler only responds to requests with registered HTTP methods.
19
+type HTTPHandler struct {
20
+	Handler          // CFSSL handler
21
+	Methods []string // The associated HTTP methods
22
+}
23
+
24
+// HandlerFunc is similar to the http.HandlerFunc type; it serves as
25
+// an adapter allowing the use of ordinary functions as Handlers. If
26
+// f is a function with the appropriate signature, HandlerFunc(f) is a
27
+// Handler object that calls f.
28
+type HandlerFunc func(http.ResponseWriter, *http.Request) error
29
+
30
+// Handle calls f(w, r)
31
+func (f HandlerFunc) Handle(w http.ResponseWriter, r *http.Request) error {
32
+	w.Header().Set("Content-Type", "application/json")
33
+	return f(w, r)
34
+}
35
+
36
+// handleError is the centralised error handling and reporting.
37
+func handleError(w http.ResponseWriter, err error) (code int) {
38
+	if err == nil {
39
+		return http.StatusOK
40
+	}
41
+	msg := err.Error()
42
+	httpCode := http.StatusInternalServerError
43
+
44
+	// If it is recognized as HttpError emitted from cfssl,
45
+	// we rewrite the status code accordingly. If it is a
46
+	// cfssl error, set the http status to StatusBadRequest
47
+	switch err := err.(type) {
48
+	case *errors.HTTPError:
49
+		httpCode = err.StatusCode
50
+		code = err.StatusCode
51
+	case *errors.Error:
52
+		httpCode = http.StatusBadRequest
53
+		code = err.ErrorCode
54
+		msg = err.Message
55
+	}
56
+
57
+	response := NewErrorResponse(msg, code)
58
+	jsonMessage, err := json.Marshal(response)
59
+	if err != nil {
60
+		log.Errorf("Failed to marshal JSON: %v", err)
61
+	} else {
62
+		msg = string(jsonMessage)
63
+	}
64
+	http.Error(w, msg, httpCode)
65
+	return code
66
+}
67
+
68
+// ServeHTTP encapsulates the call to underlying Handler to handle the request
69
+// and return the response with proper HTTP status code
70
+func (h HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
71
+	var err error
72
+	var match bool
73
+	// Throw 405 when requested with an unsupported verb.
74
+	for _, m := range h.Methods {
75
+		if m == r.Method {
76
+			match = true
77
+		}
78
+	}
79
+	if match {
80
+		err = h.Handle(w, r)
81
+	} else {
82
+		err = errors.NewMethodNotAllowed(r.Method)
83
+	}
84
+	status := handleError(w, err)
85
+	log.Infof("%s - \"%s %s\" %d", r.RemoteAddr, r.Method, r.URL, status)
86
+}
87
+
88
+// readRequestBlob takes a JSON-blob-encoded response body in the form
89
+// map[string]string and returns it, the list of keywords presented,
90
+// and any error that occurred.
91
+func readRequestBlob(r *http.Request) (map[string]string, error) {
92
+	var blob map[string]string
93
+
94
+	body, err := ioutil.ReadAll(r.Body)
95
+	if err != nil {
96
+		return nil, err
97
+	}
98
+	r.Body.Close()
99
+
100
+	err = json.Unmarshal(body, &blob)
101
+	if err != nil {
102
+		return nil, err
103
+	}
104
+	return blob, nil
105
+}
106
+
107
+// ProcessRequestOneOf reads a JSON blob for the request and makes
108
+// sure it contains one of a set of keywords. For example, a request
109
+// might have the ('foo' && 'bar') keys, OR it might have the 'baz'
110
+// key.  In either case, we want to accept the request; however, if
111
+// none of these sets shows up, the request is a bad request, and it
112
+// should be returned.
113
+func ProcessRequestOneOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
114
+	blob, err := readRequestBlob(r)
115
+	if err != nil {
116
+		return nil, nil, err
117
+	}
118
+
119
+	var matched []string
120
+	for _, set := range keywordSets {
121
+		if matchKeywords(blob, set) {
122
+			if matched != nil {
123
+				return nil, nil, errors.NewBadRequestString("mismatched parameters")
124
+			}
125
+			matched = set
126
+		}
127
+	}
128
+	if matched == nil {
129
+		return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
130
+	}
131
+	return blob, matched, nil
132
+}
133
+
134
+// ProcessRequestFirstMatchOf reads a JSON blob for the request and returns
135
+// the first match of a set of keywords. For example, a request
136
+// might have one of the following combinations: (foo=1, bar=2), (foo=1), and (bar=2)
137
+// By giving a specific ordering of those combinations, we could decide how to accept
138
+// the request.
139
+func ProcessRequestFirstMatchOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
140
+	blob, err := readRequestBlob(r)
141
+	if err != nil {
142
+		return nil, nil, err
143
+	}
144
+
145
+	for _, set := range keywordSets {
146
+		if matchKeywords(blob, set) {
147
+			return blob, set, nil
148
+		}
149
+	}
150
+	return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
151
+}
152
+
153
+func matchKeywords(blob map[string]string, keywords []string) bool {
154
+	for _, keyword := range keywords {
155
+		if _, ok := blob[keyword]; !ok {
156
+			return false
157
+		}
158
+	}
159
+	return true
160
+}
161
+
162
+// ResponseMessage implements the standard for response errors and
163
+// messages. A message has a code and a string message.
164
+type ResponseMessage struct {
165
+	Code    int    `json:"code"`
166
+	Message string `json:"message"`
167
+}
168
+
169
+// Response implements the CloudFlare standard for API
170
+// responses.
171
+type Response struct {
172
+	Success  bool              `json:"success"`
173
+	Result   interface{}       `json:"result"`
174
+	Errors   []ResponseMessage `json:"errors"`
175
+	Messages []ResponseMessage `json:"messages"`
176
+}
177
+
178
+// NewSuccessResponse is a shortcut for creating new successul API
179
+// responses.
180
+func NewSuccessResponse(result interface{}) Response {
181
+	return Response{
182
+		Success:  true,
183
+		Result:   result,
184
+		Errors:   []ResponseMessage{},
185
+		Messages: []ResponseMessage{},
186
+	}
187
+}
188
+
189
+// NewSuccessResponseWithMessage is a shortcut for creating new successul API
190
+// responses that includes a message.
191
+func NewSuccessResponseWithMessage(result interface{}, message string, code int) Response {
192
+	return Response{
193
+		Success:  true,
194
+		Result:   result,
195
+		Errors:   []ResponseMessage{},
196
+		Messages: []ResponseMessage{{code, message}},
197
+	}
198
+}
199
+
200
+// NewErrorResponse is a shortcut for creating an error response for a
201
+// single error.
202
+func NewErrorResponse(message string, code int) Response {
203
+	return Response{
204
+		Success:  false,
205
+		Result:   nil,
206
+		Errors:   []ResponseMessage{{code, message}},
207
+		Messages: []ResponseMessage{},
208
+	}
209
+}
210
+
211
+// SendResponse builds a response from the result, sets the JSON
212
+// header, and writes to the http.ResponseWriter.
213
+func SendResponse(w http.ResponseWriter, result interface{}) error {
214
+	response := NewSuccessResponse(result)
215
+	w.Header().Set("Content-Type", "application/json")
216
+	enc := json.NewEncoder(w)
217
+	err := enc.Encode(response)
218
+	return err
219
+}
220
+
221
+// SendResponseWithMessage builds a response from the result and the
222
+// provided message, sets the JSON header, and writes to the
223
+// http.ResponseWriter.
224
+func SendResponseWithMessage(w http.ResponseWriter, result interface{}, message string, code int) error {
225
+	response := NewSuccessResponseWithMessage(result, message, code)
226
+	w.Header().Set("Content-Type", "application/json")
227
+	enc := json.NewEncoder(w)
228
+	err := enc.Encode(response)
229
+	return err
230
+}
... ...
@@ -16,21 +16,26 @@ A database is required for the following:
16 16
 
17 17
 This directory stores [goose](https://bitbucket.org/liamstask/goose/) db migration scripts for various DB backends.
18 18
 Currently supported:
19
- - SQLite in sqlite
19
+ - MySQL in mysql
20 20
  - PostgreSQL in pg
21
+ - SQLite in sqlite
21 22
 
22 23
 ### Get goose
23 24
 
24
-    go get https://bitbucket.org/liamstask/goose/
25
+    go get bitbucket.org/liamstask/goose/cmd/goose
25 26
 
26
-### Use goose to start and terminate a SQLite DB
27
-To start a SQLite DB using goose:
27
+### Use goose to start and terminate a MySQL DB
28
+To start a MySQL using goose:
28 29
 
29
-    goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite up'
30
+    goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/mysql up
30 31
 
31
-To tear down a SQLite DB using goose
32
+To tear down a MySQL DB using goose
32 33
 
33
-    goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite down
34
+    goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/mysql down
35
+
36
+Note: the administration of MySQL DB is not included. We assume
37
+the databases being connected to are already created and access control
38
+is properly handled.
34 39
 
35 40
 ### Use goose to start and terminate a PostgreSQL DB
36 41
 To start a PostgreSQL using goose:
... ...
@@ -43,7 +48,16 @@ To tear down a PostgreSQL DB using goose
43 43
 
44 44
 Note: the administration of PostgreSQL DB is not included. We assume
45 45
 the databases being connected to are already created and access control
46
-are properly handled.
46
+is properly handled.
47
+
48
+### Use goose to start and terminate a SQLite DB
49
+To start a SQLite DB using goose:
50
+
51
+    goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite up
52
+
53
+To tear down a SQLite DB using goose
54
+
55
+    goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite down
47 56
 
48 57
 ## CFSSL Configuration
49 58
 
... ...
@@ -55,4 +69,3 @@ JSON dictionary:
55 55
 or
56 56
 
57 57
     {"driver":"postgres","data_source":"postgres://user:password@host/db"}
58
-
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"crypto/rsa"
10 10
 	"crypto/x509"
11 11
 	"crypto/x509/pkix"
12
+	"encoding/asn1"
12 13
 	"encoding/pem"
13 14
 	"errors"
14 15
 	"net"
... ...
@@ -129,8 +130,9 @@ func (kr *BasicKeyRequest) SigAlgo() x509.SignatureAlgorithm {
129 129
 
130 130
 // CAConfig is a section used in the requests initialising a new CA.
131 131
 type CAConfig struct {
132
-	PathLength int    `json:"pathlen"`
133
-	Expiry     string `json:"expiry"`
132
+	PathLength  int    `json:"pathlen"`
133
+	PathLenZero bool   `json:"pathlenzero"`
134
+	Expiry      string `json:"expiry"`
134 135
 }
135 136
 
136 137
 // A CertificateRequest encapsulates the API interface to the
... ...
@@ -175,6 +177,12 @@ func (cr *CertificateRequest) Name() pkix.Name {
175 175
 	return name
176 176
 }
177 177
 
178
+// BasicConstraints CSR information RFC 5280, 4.2.1.9
179
+type BasicConstraints struct {
180
+	IsCA       bool `asn1:"optional"`
181
+	MaxPathLen int  `asn1:"optional,default:-1"`
182
+}
183
+
178 184
 // ParseRequest takes a certificate request and generates a key and
179 185
 // CSR from it. It does no validation -- caveat emptor. It will,
180 186
 // however, fail if the key request is not valid (i.e., an unsupported
... ...
@@ -217,34 +225,11 @@ func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) {
217 217
 		panic("Generate should have failed to produce a valid key.")
218 218
 	}
219 219
 
220
-	var tpl = x509.CertificateRequest{
221
-		Subject:            req.Name(),
222
-		SignatureAlgorithm: req.KeyRequest.SigAlgo(),
223
-	}
224
-
225
-	for i := range req.Hosts {
226
-		if ip := net.ParseIP(req.Hosts[i]); ip != nil {
227
-			tpl.IPAddresses = append(tpl.IPAddresses, ip)
228
-		} else if email, err := mail.ParseAddress(req.Hosts[i]); err == nil && email != nil {
229
-			tpl.EmailAddresses = append(tpl.EmailAddresses, req.Hosts[i])
230
-		} else {
231
-			tpl.DNSNames = append(tpl.DNSNames, req.Hosts[i])
232
-		}
233
-	}
234
-
235
-	csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv)
220
+	csr, err = Generate(priv.(crypto.Signer), req)
236 221
 	if err != nil {
237 222
 		log.Errorf("failed to generate a CSR: %v", err)
238 223
 		err = cferr.Wrap(cferr.CSRError, cferr.BadRequest, err)
239
-		return
240
-	}
241
-	block := pem.Block{
242
-		Type:  "CERTIFICATE REQUEST",
243
-		Bytes: csr,
244 224
 	}
245
-
246
-	log.Info("encoded CSR")
247
-	csr = pem.EncodeToMemory(&block)
248 225
 	return
249 226
 }
250 227
 
... ...
@@ -265,6 +250,7 @@ func ExtractCertificateRequest(cert *x509.Certificate) *CertificateRequest {
265 265
 		// issue date and expiry date.
266 266
 		req.CA.Expiry = cert.NotAfter.Sub(cert.NotBefore).String()
267 267
 		req.CA.PathLength = cert.MaxPathLen
268
+		req.CA.PathLenZero = cert.MaxPathLenZero
268 269
 	}
269 270
 
270 271
 	return req
... ...
@@ -377,7 +363,7 @@ func Regenerate(priv crypto.Signer, csr []byte) ([]byte, error) {
377 377
 // Generate creates a new CSR from a CertificateRequest structure and
378 378
 // an existing key. The KeyRequest field is ignored.
379 379
 func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err error) {
380
-	sigAlgo := helpers.SignerAlgo(priv, crypto.SHA256)
380
+	sigAlgo := helpers.SignerAlgo(priv)
381 381
 	if sigAlgo == x509.UnknownSignatureAlgorithm {
382 382
 		return nil, cferr.New(cferr.PrivateKeyError, cferr.Unavailable)
383 383
 	}
... ...
@@ -397,6 +383,14 @@ func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err erro
397 397
 		}
398 398
 	}
399 399
 
400
+	if req.CA != nil {
401
+		err = appendCAInfoToCSR(req.CA, &tpl)
402
+		if err != nil {
403
+			err = cferr.Wrap(cferr.CSRError, cferr.GenerationFailed, err)
404
+			return
405
+		}
406
+	}
407
+
400 408
 	csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv)
401 409
 	if err != nil {
402 410
 		log.Errorf("failed to generate a CSR: %v", err)
... ...
@@ -412,3 +406,26 @@ func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err erro
412 412
 	csr = pem.EncodeToMemory(&block)
413 413
 	return
414 414
 }
415
+
416
+// appendCAInfoToCSR appends CAConfig BasicConstraint extension to a CSR
417
+func appendCAInfoToCSR(reqConf *CAConfig, csr *x509.CertificateRequest) error {
418
+	pathlen := reqConf.PathLength
419
+	if pathlen == 0 && !reqConf.PathLenZero {
420
+		pathlen = -1
421
+	}
422
+	val, err := asn1.Marshal(BasicConstraints{true, pathlen})
423
+
424
+	if err != nil {
425
+		return err
426
+	}
427
+
428
+	csr.ExtraExtensions = []pkix.Extension{
429
+		{
430
+			Id:       asn1.ObjectIdentifier{2, 5, 29, 19},
431
+			Value:    val,
432
+			Critical: true,
433
+		},
434
+	}
435
+
436
+	return nil
437
+}
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"bytes"
7 7
 	"crypto"
8 8
 	"crypto/ecdsa"
9
+	"crypto/elliptic"
9 10
 	"crypto/rsa"
10 11
 	"crypto/x509"
11 12
 	"encoding/asn1"
... ...
@@ -410,7 +411,7 @@ func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error)
410 410
 	in = bytes.TrimSpace(in)
411 411
 	p, rest := pem.Decode(in)
412 412
 	if p != nil {
413
-		if p.Type != "CERTIFICATE REQUEST" {
413
+		if p.Type != "NEW CERTIFICATE REQUEST" && p.Type != "CERTIFICATE REQUEST" {
414 414
 			return nil, rest, cferr.New(cferr.CSRError, cferr.BadRequest)
415 415
 		}
416 416
 
... ...
@@ -446,28 +447,28 @@ func ParseCSRPEM(csrPEM []byte) (*x509.CertificateRequest, error) {
446 446
 	return csrObject, nil
447 447
 }
448 448
 
449
-// SignerAlgo returns an X.509 signature algorithm corresponding to
450
-// the crypto.Hash provided from a crypto.Signer.
451
-func SignerAlgo(priv crypto.Signer, h crypto.Hash) x509.SignatureAlgorithm {
452
-	switch priv.Public().(type) {
449
+// SignerAlgo returns an X.509 signature algorithm from a crypto.Signer.
450
+func SignerAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
451
+	switch pub := priv.Public().(type) {
453 452
 	case *rsa.PublicKey:
454
-		switch h {
455
-		case crypto.SHA512:
453
+		bitLength := pub.N.BitLen()
454
+		switch {
455
+		case bitLength >= 4096:
456 456
 			return x509.SHA512WithRSA
457
-		case crypto.SHA384:
457
+		case bitLength >= 3072:
458 458
 			return x509.SHA384WithRSA
459
-		case crypto.SHA256:
459
+		case bitLength >= 2048:
460 460
 			return x509.SHA256WithRSA
461 461
 		default:
462 462
 			return x509.SHA1WithRSA
463 463
 		}
464 464
 	case *ecdsa.PublicKey:
465
-		switch h {
466
-		case crypto.SHA512:
465
+		switch pub.Curve {
466
+		case elliptic.P521():
467 467
 			return x509.ECDSAWithSHA512
468
-		case crypto.SHA384:
468
+		case elliptic.P384():
469 469
 			return x509.ECDSAWithSHA384
470
-		case crypto.SHA256:
470
+		case elliptic.P256():
471 471
 			return x509.ECDSAWithSHA256
472 472
 		default:
473 473
 			return x509.ECDSAWithSHA1
... ...
@@ -5,14 +5,10 @@ package initca
5 5
 import (
6 6
 	"crypto"
7 7
 	"crypto/ecdsa"
8
-	"crypto/elliptic"
9
-	"crypto/rand"
10 8
 	"crypto/rsa"
11 9
 	"crypto/x509"
12
-	"encoding/pem"
13 10
 	"errors"
14 11
 	"io/ioutil"
15
-	"net"
16 12
 	"time"
17 13
 
18 14
 	"github.com/cloudflare/cfssl/config"
... ...
@@ -47,14 +43,18 @@ func validator(req *csr.CertificateRequest) error {
47 47
 
48 48
 // New creates a new root certificate from the certificate request.
49 49
 func New(req *csr.CertificateRequest) (cert, csrPEM, key []byte, err error) {
50
+	policy := CAPolicy()
50 51
 	if req.CA != nil {
51 52
 		if req.CA.Expiry != "" {
52
-			CAPolicy.Default.ExpiryString = req.CA.Expiry
53
-			CAPolicy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry)
53
+			policy.Default.ExpiryString = req.CA.Expiry
54
+			policy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry)
54 55
 		}
55 56
 
56
-		if req.CA.PathLength != 0 {
57
-			signer.MaxPathLen = req.CA.PathLength
57
+		signer.MaxPathLen = req.CA.PathLength
58
+		if req.CA.PathLength != 0 && req.CA.PathLenZero == true {
59
+			log.Infof("ignore invalid 'pathlenzero' value")
60
+		} else {
61
+			signer.MaxPathLenZero = req.CA.PathLenZero
58 62
 		}
59 63
 	}
60 64
 
... ...
@@ -77,7 +77,7 @@ func New(req *csr.CertificateRequest) (cert, csrPEM, key []byte, err error) {
77 77
 		log.Errorf("failed to create signer: %v", err)
78 78
 		return
79 79
 	}
80
-	s.SetPolicy(CAPolicy)
80
+	s.SetPolicy(policy)
81 81
 
82 82
 	signReq := signer.SignRequest{Hosts: req.Hosts, Request: string(csrPEM)}
83 83
 	cert, err = s.Sign(signReq)
... ...
@@ -133,92 +133,35 @@ func RenewFromPEM(caFile, keyFile string) ([]byte, error) {
133 133
 
134 134
 // NewFromSigner creates a new root certificate from a crypto.Signer.
135 135
 func NewFromSigner(req *csr.CertificateRequest, priv crypto.Signer) (cert, csrPEM []byte, err error) {
136
+	policy := CAPolicy()
136 137
 	if req.CA != nil {
137 138
 		if req.CA.Expiry != "" {
138
-			CAPolicy.Default.ExpiryString = req.CA.Expiry
139
-			CAPolicy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry)
139
+			policy.Default.ExpiryString = req.CA.Expiry
140
+			policy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry)
140 141
 			if err != nil {
141 142
 				return nil, nil, err
142 143
 			}
143 144
 		}
144 145
 
145
-		if req.CA.PathLength != 0 {
146
-			signer.MaxPathLen = req.CA.PathLength
147
-		}
148
-	}
149
-
150
-	var sigAlgo x509.SignatureAlgorithm
151
-	switch pub := priv.Public().(type) {
152
-	case *rsa.PublicKey:
153
-		bitLength := pub.N.BitLen()
154
-		switch {
155
-		case bitLength >= 4096:
156
-			sigAlgo = x509.SHA512WithRSA
157
-		case bitLength >= 3072:
158
-			sigAlgo = x509.SHA384WithRSA
159
-		case bitLength >= 2048:
160
-			sigAlgo = x509.SHA256WithRSA
161
-		default:
162
-			sigAlgo = x509.SHA1WithRSA
163
-		}
164
-	case *ecdsa.PublicKey:
165
-		switch pub.Curve {
166
-		case elliptic.P521():
167
-			sigAlgo = x509.ECDSAWithSHA512
168
-		case elliptic.P384():
169
-			sigAlgo = x509.ECDSAWithSHA384
170
-		case elliptic.P256():
171
-			sigAlgo = x509.ECDSAWithSHA256
172
-		default:
173
-			sigAlgo = x509.ECDSAWithSHA1
174
-		}
175
-	default:
176
-		sigAlgo = x509.UnknownSignatureAlgorithm
177
-	}
178
-
179
-	var tpl = x509.CertificateRequest{
180
-		Subject:            req.Name(),
181
-		SignatureAlgorithm: sigAlgo,
182
-	}
183
-
184
-	for i := range req.Hosts {
185
-		if ip := net.ParseIP(req.Hosts[i]); ip != nil {
186
-			tpl.IPAddresses = append(tpl.IPAddresses, ip)
146
+		signer.MaxPathLen = req.CA.PathLength
147
+		if req.CA.PathLength != 0 && req.CA.PathLenZero == true {
148
+			log.Infof("ignore invalid 'pathlenzero' value")
187 149
 		} else {
188
-			tpl.DNSNames = append(tpl.DNSNames, req.Hosts[i])
150
+			signer.MaxPathLenZero = req.CA.PathLenZero
189 151
 		}
190 152
 	}
191 153
 
192
-	return signWithCSR(&tpl, priv)
193
-}
194
-
195
-// signWithCSR creates a new root certificate from signing a X509.CertificateRequest
196
-// by a crypto.Signer.
197
-func signWithCSR(tpl *x509.CertificateRequest, priv crypto.Signer) (cert, csrPEM []byte, err error) {
198
-	csrPEM, err = x509.CreateCertificateRequest(rand.Reader, tpl, priv)
154
+	csrPEM, err = csr.Generate(priv, req)
199 155
 	if err != nil {
200
-		log.Errorf("failed to generate a CSR: %v", err)
201
-		// The use of CertificateError was a matter of some
202
-		// debate; it is the one edge case in which a new
203
-		// error category specifically for CSRs might be
204
-		// useful, but it was deemed that one edge case did
205
-		// not a new category justify.
206
-		err = cferr.Wrap(cferr.CertificateError, cferr.BadRequest, err)
207
-		return
208
-	}
209
-
210
-	p := &pem.Block{
211
-		Type:  "CERTIFICATE REQUEST",
212
-		Bytes: csrPEM,
156
+		return nil, nil, err
213 157
 	}
214
-	csrPEM = pem.EncodeToMemory(p)
215 158
 
216 159
 	s, err := local.NewSigner(priv, nil, signer.DefaultSigAlgo(priv), nil)
217 160
 	if err != nil {
218 161
 		log.Errorf("failed to create signer: %v", err)
219 162
 		return
220 163
 	}
221
-	s.SetPolicy(CAPolicy)
164
+	s.SetPolicy(policy)
222 165
 
223 166
 	signReq := signer.SignRequest{Request: string(csrPEM)}
224 167
 	cert, err = s.Sign(signReq)
... ...
@@ -268,11 +211,13 @@ func RenewFromSigner(ca *x509.Certificate, priv crypto.Signer) ([]byte, error) {
268 268
 }
269 269
 
270 270
 // CAPolicy contains the CA issuing policy as default policy.
271
-var CAPolicy = &config.Signing{
272
-	Default: &config.SigningProfile{
273
-		Usage:        []string{"cert sign", "crl sign"},
274
-		ExpiryString: "43800h",
275
-		Expiry:       5 * helpers.OneYear,
276
-		CA:           true,
277
-	},
271
+var CAPolicy = func() *config.Signing {
272
+	return &config.Signing{
273
+		Default: &config.SigningProfile{
274
+			Usage:        []string{"cert sign", "crl sign"},
275
+			ExpiryString: "43800h",
276
+			Expiry:       5 * helpers.OneYear,
277
+			CA:           true,
278
+		},
279
+	}
278 280
 }
... ...
@@ -45,12 +45,12 @@ var Level = LevelInfo
45 45
 //
46 46
 // SyslogWriter is satisfied by *syslog.Writer.
47 47
 type SyslogWriter interface {
48
-	Debug(string) error
49
-	Info(string) error
50
-	Warning(string) error
51
-	Err(string) error
52
-	Crit(string) error
53
-	Emerg(string) error
48
+	Debug(string)
49
+	Info(string)
50
+	Warning(string)
51
+	Err(string)
52
+	Crit(string)
53
+	Emerg(string)
54 54
 }
55 55
 
56 56
 // syslogWriter stores the SetLogger() parameter.
... ...
@@ -73,23 +73,19 @@ func init() {
73 73
 func print(l int, msg string) {
74 74
 	if l >= Level {
75 75
 		if syslogWriter != nil {
76
-			var err error
77 76
 			switch l {
78 77
 			case LevelDebug:
79
-				err = syslogWriter.Debug(msg)
78
+				syslogWriter.Debug(msg)
80 79
 			case LevelInfo:
81
-				err = syslogWriter.Info(msg)
80
+				syslogWriter.Info(msg)
82 81
 			case LevelWarning:
83
-				err = syslogWriter.Warning(msg)
82
+				syslogWriter.Warning(msg)
84 83
 			case LevelError:
85
-				err = syslogWriter.Err(msg)
84
+				syslogWriter.Err(msg)
86 85
 			case LevelCritical:
87
-				err = syslogWriter.Crit(msg)
86
+				syslogWriter.Crit(msg)
88 87
 			case LevelFatal:
89
-				err = syslogWriter.Emerg(msg)
90
-			}
91
-			if err != nil {
92
-				log.Printf("Unable to write syslog: %v for msg: %s\n", err, msg)
88
+				syslogWriter.Emerg(msg)
93 89
 			}
94 90
 		} else {
95 91
 			log.Printf("[%s] %s", levelPrefix[l], msg)
... ...
@@ -96,7 +96,11 @@ func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signe
96 96
 }
97 97
 
98 98
 func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile) (cert []byte, err error) {
99
+	var distPoints = template.CRLDistributionPoints
99 100
 	err = signer.FillTemplate(template, s.policy.Default, profile)
101
+	if distPoints != nil && len(distPoints) > 0 {
102
+		template.CRLDistributionPoints = distPoints
103
+	}
100 104
 	if err != nil {
101 105
 		return
102 106
 	}
... ...
@@ -111,9 +115,7 @@ func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile
111 111
 		template.EmailAddresses = nil
112 112
 		s.ca = template
113 113
 		initRoot = true
114
-		template.MaxPathLen = signer.MaxPathLen
115 114
 	} else if template.IsCA {
116
-		template.MaxPathLen = 1
117 115
 		template.DNSNames = nil
118 116
 		template.EmailAddresses = nil
119 117
 	}
... ...
@@ -203,7 +205,7 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
203 203
 		return nil, cferr.New(cferr.CSRError, cferr.DecodeFailed)
204 204
 	}
205 205
 
206
-	if block.Type != "CERTIFICATE REQUEST" {
206
+	if block.Type != "NEW CERTIFICATE REQUEST" && block.Type != "CERTIFICATE REQUEST" {
207 207
 		return nil, cferr.Wrap(cferr.CSRError,
208 208
 			cferr.BadRequest, errors.New("not a certificate or csr"))
209 209
 	}
... ...
@@ -243,6 +245,26 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
243 243
 		}
244 244
 	}
245 245
 
246
+	if req.CRLOverride != "" {
247
+		safeTemplate.CRLDistributionPoints = []string{req.CRLOverride}
248
+	}
249
+
250
+	if safeTemplate.IsCA {
251
+		if !profile.CA {
252
+			return nil, cferr.New(cferr.CertificateError, cferr.InvalidRequest)
253
+		}
254
+
255
+		if s.ca != nil && s.ca.MaxPathLen > 0 {
256
+			if safeTemplate.MaxPathLen >= s.ca.MaxPathLen {
257
+				// do not sign a cert with pathlen > current
258
+				return nil, cferr.New(cferr.CertificateError, cferr.InvalidRequest)
259
+			}
260
+		} else if s.ca != nil && s.ca.MaxPathLen == 0 && s.ca.MaxPathLenZero {
261
+			// signer has pathlen of 0, do not sign more intermediate CAs
262
+			return nil, cferr.New(cferr.CertificateError, cferr.InvalidRequest)
263
+		}
264
+	}
265
+
246 266
 	OverrideHosts(&safeTemplate, req.Hosts)
247 267
 	safeTemplate.Subject = PopulateSubjectFromCSR(req.Subject, safeTemplate.Subject)
248 268
 
... ...
@@ -26,6 +26,9 @@ import (
26 26
 // MaxPathLen is the default path length for a new CA certificate.
27 27
 var MaxPathLen = 2
28 28
 
29
+// MaxPathLenZero indicates whether a new CA certificate has pathlen=0
30
+var MaxPathLenZero = false
31
+
29 32
 // Subject contains the information that should be used to override the
30 33
 // subject information when signing a certificate.
31 34
 type Subject struct {
... ...
@@ -50,13 +53,14 @@ type Extension struct {
50 50
 // Extensions requested in the CSR are ignored, except for those processed by
51 51
 // ParseCertificateRequest (mainly subjectAltName).
52 52
 type SignRequest struct {
53
-	Hosts      []string    `json:"hosts"`
54
-	Request    string      `json:"certificate_request"`
55
-	Subject    *Subject    `json:"subject,omitempty"`
56
-	Profile    string      `json:"profile"`
57
-	Label      string      `json:"label"`
58
-	Serial     *big.Int    `json:"serial,omitempty"`
59
-	Extensions []Extension `json:"extensions,omitempty"`
53
+	Hosts       []string    `json:"hosts"`
54
+	Request     string      `json:"certificate_request"`
55
+	Subject     *Subject    `json:"subject,omitempty"`
56
+	Profile     string      `json:"profile"`
57
+	CRLOverride string      `json:"crl_override"`
58
+	Label       string      `json:"label"`
59
+	Serial      *big.Int    `json:"serial,omitempty"`
60
+	Extensions  []Extension `json:"extensions,omitempty"`
60 61
 }
61 62
 
62 63
 // appendIf appends to a if s is not an empty string.
... ...
@@ -157,26 +161,46 @@ func DefaultSigAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
157 157
 // ParseCertificateRequest takes an incoming certificate request and
158 158
 // builds a certificate template from it.
159 159
 func ParseCertificateRequest(s Signer, csrBytes []byte) (template *x509.Certificate, err error) {
160
-	csr, err := x509.ParseCertificateRequest(csrBytes)
160
+	csrv, err := x509.ParseCertificateRequest(csrBytes)
161 161
 	if err != nil {
162 162
 		err = cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err)
163 163
 		return
164 164
 	}
165 165
 
166
-	err = helpers.CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
166
+	err = helpers.CheckSignature(csrv, csrv.SignatureAlgorithm, csrv.RawTBSCertificateRequest, csrv.Signature)
167 167
 	if err != nil {
168 168
 		err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err)
169 169
 		return
170 170
 	}
171 171
 
172 172
 	template = &x509.Certificate{
173
-		Subject:            csr.Subject,
174
-		PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
175
-		PublicKey:          csr.PublicKey,
173
+		Subject:            csrv.Subject,
174
+		PublicKeyAlgorithm: csrv.PublicKeyAlgorithm,
175
+		PublicKey:          csrv.PublicKey,
176 176
 		SignatureAlgorithm: s.SigAlgo(),
177
-		DNSNames:           csr.DNSNames,
178
-		IPAddresses:        csr.IPAddresses,
179
-		EmailAddresses:     csr.EmailAddresses,
177
+		DNSNames:           csrv.DNSNames,
178
+		IPAddresses:        csrv.IPAddresses,
179
+		EmailAddresses:     csrv.EmailAddresses,
180
+	}
181
+
182
+	for _, val := range csrv.Extensions {
183
+		// Check the CSR for the X.509 BasicConstraints (RFC 5280, 4.2.1.9)
184
+		// extension and append to template if necessary
185
+		if val.Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 19}) {
186
+			var constraints csr.BasicConstraints
187
+			var rest []byte
188
+
189
+			if rest, err = asn1.Unmarshal(val.Value, &constraints); err != nil {
190
+				return nil, cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err)
191
+			} else if len(rest) != 0 {
192
+				return nil, cferr.Wrap(cferr.CSRError, cferr.ParseFailed, errors.New("x509: trailing data after X.509 BasicConstraints"))
193
+			}
194
+
195
+			template.BasicConstraintsValid = true
196
+			template.IsCA = constraints.IsCA
197
+			template.MaxPathLen = constraints.MaxPathLen
198
+			template.MaxPathLenZero = template.MaxPathLen == 0
199
+		}
180 200
 	}
181 201
 
182 202
 	return
... ...
@@ -222,6 +246,7 @@ func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.Si
222 222
 		notBefore       time.Time
223 223
 		notAfter        time.Time
224 224
 		crlURL, ocspURL string
225
+		issuerURL       = profile.IssuerURL
225 226
 	)
226 227
 
227 228
 	// The third value returned from Usages is a list of unknown key usages.
... ...
@@ -229,7 +254,7 @@ func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.Si
229 229
 	// here.
230 230
 	ku, eku, _ = profile.Usages()
231 231
 	if profile.IssuerURL == nil {
232
-		profile.IssuerURL = defaultProfile.IssuerURL
232
+		issuerURL = defaultProfile.IssuerURL
233 233
 	}
234 234
 
235 235
 	if ku == 0 && len(eku) == 0 {
... ...
@@ -279,8 +304,8 @@ func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.Si
279 279
 		template.CRLDistributionPoints = []string{crlURL}
280 280
 	}
281 281
 
282
-	if len(profile.IssuerURL) != 0 {
283
-		template.IssuingCertificateURL = profile.IssuerURL
282
+	if len(issuerURL) != 0 {
283
+		template.IssuingCertificateURL = issuerURL
284 284
 	}
285 285
 	if len(profile.Policies) != 0 {
286 286
 		err = addPolicies(template, profile.Policies)
... ...
@@ -27,7 +27,7 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (ty
27 27
 	return response, err
28 28
 }
29 29
 
30
-// ContainerInspectWithRaw returns the container information and it's raw representation.
30
+// ContainerInspectWithRaw returns the container information and its raw representation.
31 31
 func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID string, getSize bool) (types.ContainerJSON, []byte, error) {
32 32
 	query := url.Values{}
33 33
 	if getSize {
... ...
@@ -131,6 +131,11 @@ func (e nodeNotFoundError) Error() string {
131 131
 	return fmt.Sprintf("Error: No such node: %s", e.nodeID)
132 132
 }
133 133
 
134
+// NoFound indicates that this error type is of NotFound
135
+func (e nodeNotFoundError) NotFound() bool {
136
+	return true
137
+}
138
+
134 139
 // IsErrNodeNotFound returns true if the error is caused
135 140
 // when a node is not found.
136 141
 func IsErrNodeNotFound(err error) bool {
... ...
@@ -148,6 +153,11 @@ func (e serviceNotFoundError) Error() string {
148 148
 	return fmt.Sprintf("Error: No such service: %s", e.serviceID)
149 149
 }
150 150
 
151
+// NoFound indicates that this error type is of NotFound
152
+func (e serviceNotFoundError) NotFound() bool {
153
+	return true
154
+}
155
+
151 156
 // IsErrServiceNotFound returns true if the error is caused
152 157
 // when a service is not found.
153 158
 func IsErrServiceNotFound(err error) bool {
... ...
@@ -165,6 +175,11 @@ func (e taskNotFoundError) Error() string {
165 165
 	return fmt.Sprintf("Error: No such task: %s", e.taskID)
166 166
 }
167 167
 
168
+// NoFound indicates that this error type is of NotFound
169
+func (e taskNotFoundError) NotFound() bool {
170
+	return true
171
+}
172
+
168 173
 // IsErrTaskNotFound returns true if the error is caused
169 174
 // when a task is not found.
170 175
 func IsErrTaskNotFound(err error) bool {
... ...
@@ -92,7 +92,7 @@ type NetworkAPIClient interface {
92 92
 
93 93
 // NodeAPIClient defines API client methods for the nodes
94 94
 type NodeAPIClient interface {
95
-	NodeInspect(ctx context.Context, nodeID string) (swarm.Node, error)
95
+	NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error)
96 96
 	NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error)
97 97
 	NodeRemove(ctx context.Context, nodeID string) error
98 98
 	NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error
... ...
@@ -16,7 +16,7 @@ func (cli *Client) NetworkInspect(ctx context.Context, networkID string) (types.
16 16
 	return networkResource, err
17 17
 }
18 18
 
19
-// NetworkInspectWithRaw returns the information for a specific network configured in the docker host and it's raw representation.
19
+// NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
20 20
 func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string) (types.NetworkResource, []byte, error) {
21 21
 	var networkResource types.NetworkResource
22 22
 	resp, err := cli.get(ctx, "/networks/"+networkID, nil, nil)
... ...
@@ -1,25 +1,33 @@
1 1
 package client
2 2
 
3 3
 import (
4
+	"bytes"
4 5
 	"encoding/json"
6
+	"io/ioutil"
5 7
 	"net/http"
6 8
 
7 9
 	"github.com/docker/engine-api/types/swarm"
8 10
 	"golang.org/x/net/context"
9 11
 )
10 12
 
11
-// NodeInspect returns the node information.
12
-func (cli *Client) NodeInspect(ctx context.Context, nodeID string) (swarm.Node, error) {
13
+// NodeInspectWithRaw returns the node information.
14
+func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) {
13 15
 	serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
14 16
 	if err != nil {
15 17
 		if serverResp.statusCode == http.StatusNotFound {
16
-			return swarm.Node{}, nodeNotFoundError{nodeID}
18
+			return swarm.Node{}, nil, nodeNotFoundError{nodeID}
17 19
 		}
18
-		return swarm.Node{}, err
20
+		return swarm.Node{}, nil, err
21
+	}
22
+	defer ensureReaderClosed(serverResp)
23
+
24
+	body, err := ioutil.ReadAll(serverResp.body)
25
+	if err != nil {
26
+		return swarm.Node{}, nil, err
19 27
 	}
20 28
 
21 29
 	var response swarm.Node
22
-	err = json.NewDecoder(serverResp.body).Decode(&response)
23
-	ensureReaderClosed(serverResp)
24
-	return response, err
30
+	rdr := bytes.NewReader(body)
31
+	err = json.NewDecoder(rdr).Decode(&response)
32
+	return response, body, err
25 33
 }
... ...
@@ -31,6 +31,7 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
31 31
 	}
32 32
 	var privileges types.PluginPrivileges
33 33
 	if err := json.NewDecoder(resp.body).Decode(&privileges); err != nil {
34
+		ensureReaderClosed(resp)
34 35
 		return err
35 36
 	}
36 37
 	ensureReaderClosed(resp)
... ...
@@ -16,7 +16,7 @@ func (cli *Client) VolumeInspect(ctx context.Context, volumeID string) (types.Vo
16 16
 	return volume, err
17 17
 }
18 18
 
19
-// VolumeInspectWithRaw returns the information about a specific volume in the docker host and it's raw representation
19
+// VolumeInspectWithRaw returns the information about a specific volume in the docker host and its raw representation
20 20
 func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error) {
21 21
 	var volume types.Volume
22 22
 	resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil)
... ...
@@ -54,7 +54,7 @@ const (
54 54
 	MountPropagationSlave MountPropagation = "slave"
55 55
 )
56 56
 
57
-// BindOptions define options specific to mounts of type "bind".
57
+// BindOptions defines options specific to mounts of type "bind".
58 58
 type BindOptions struct {
59 59
 	Propagation MountPropagation `json:",omitempty"`
60 60
 }
... ...
@@ -15,7 +15,7 @@ type ServiceSpec struct {
15 15
 	Annotations
16 16
 
17 17
 	// TaskTemplate defines how the service should construct new tasks when
18
-	// ochestrating this service.
18
+	// orchestrating this service.
19 19
 	TaskTemplate TaskSpec                  `json:",omitempty"`
20 20
 	Mode         ServiceMode               `json:",omitempty"`
21 21
 	UpdateConfig *UpdateConfig             `json:",omitempty"`
... ...
@@ -32,7 +32,7 @@ type Policy struct {
32 32
 	Secret     *string `json:",omitempty"`
33 33
 }
34 34
 
35
-// OrchestrationConfig represents ochestration configuration.
35
+// OrchestrationConfig represents orchestration configuration.
36 36
 type OrchestrationConfig struct {
37 37
 	TaskHistoryRetentionLimit int64 `json:",omitempty"`
38 38
 }
... ...
@@ -54,6 +54,20 @@ type DispatcherConfig struct {
54 54
 // CAConfig represents CA configuration.
55 55
 type CAConfig struct {
56 56
 	NodeCertExpiry time.Duration `json:",omitempty"`
57
+	ExternalCAs    []*ExternalCA `json:",omitempty"`
58
+}
59
+
60
+// ExternalCAProtocol represents type of external CA.
61
+type ExternalCAProtocol string
62
+
63
+// ExternalCAProtocolCFSSL CFSSL
64
+const ExternalCAProtocolCFSSL ExternalCAProtocol = "cfssl"
65
+
66
+// ExternalCA defines external CA to be used by the cluster.
67
+type ExternalCA struct {
68
+	Protocol ExternalCAProtocol
69
+	URL      string
70
+	Options  map[string]string `json:",omitempty"`
57 71
 }
58 72
 
59 73
 // InitRequest is the request used to init a swarm.
... ...
@@ -504,10 +504,6 @@ type Checkpoint struct {
504 504
 	Name string // Name is the name of the checkpoint
505 505
 }
506 506
 
507
-// DefaultRuntimeName is the reserved name/alias used to represent the
508
-// OCI runtime being shipped with the docker daemon package.
509
-var DefaultRuntimeName = "default"
510
-
511 507
 // Runtime describes an OCI runtime
512 508
 type Runtime struct {
513 509
 	Path string   `json:"path"`
... ...
@@ -102,7 +102,7 @@ func (c *controller) handleKeyChange(keys []*types.EncryptionKey) error {
102 102
 				deleted = cKey.Key
103 103
 			}
104 104
 
105
-			if cKey.Subsystem == subsysGossip /* subsysIPSec */ {
105
+			if cKey.Subsystem == subsysIPSec {
106 106
 				drvEnc.Prune = cKey.Key
107 107
 				drvEnc.PruneTag = cKey.LamportTime
108 108
 			}
... ...
@@ -128,7 +128,7 @@ func (c *controller) handleKeyChange(keys []*types.EncryptionKey) error {
128 128
 				a.networkDB.SetKey(key.Key)
129 129
 			}
130 130
 
131
-			if key.Subsystem == subsysGossip /*subsysIPSec*/ {
131
+			if key.Subsystem == subsysIPSec {
132 132
 				drvEnc.Key = key.Key
133 133
 				drvEnc.Tag = key.LamportTime
134 134
 			}
... ...
@@ -138,7 +138,7 @@ func (c *controller) handleKeyChange(keys []*types.EncryptionKey) error {
138 138
 	key, tag := c.getPrimaryKeyTag(subsysGossip)
139 139
 	a.networkDB.SetPrimaryKey(key)
140 140
 
141
-	//key, tag = c.getPrimaryKeyTag(subsysIPSec)
141
+	key, tag = c.getPrimaryKeyTag(subsysIPSec)
142 142
 	drvEnc.Primary = key
143 143
 	drvEnc.PrimaryTag = tag
144 144
 
... ...
@@ -317,17 +317,12 @@ func (c *controller) agentInit(bindAddrOrInterface string) error {
317 317
 		return nil
318 318
 	}
319 319
 
320
-	drvEnc := discoverapi.DriverEncryptionConfig{}
321
-
322
-	keys, tags := c.getKeys(subsysGossip) // getKeys(subsysIPSec)
323
-	drvEnc.Keys = keys
324
-	drvEnc.Tags = tags
325
-
326 320
 	bindAddr, err := resolveAddr(bindAddrOrInterface)
327 321
 	if err != nil {
328 322
 		return err
329 323
 	}
330 324
 
325
+	keys, tags := c.getKeys(subsysGossip)
331 326
 	hostname, _ := os.Hostname()
332 327
 	nDB, err := networkdb.New(&networkdb.Config{
333 328
 		BindAddr: bindAddr,
... ...
@@ -350,6 +345,11 @@ func (c *controller) agentInit(bindAddrOrInterface string) error {
350 350
 
351 351
 	go c.handleTableEvents(ch, c.handleEpTableEvent)
352 352
 
353
+	drvEnc := discoverapi.DriverEncryptionConfig{}
354
+	keys, tags = c.getKeys(subsysIPSec)
355
+	drvEnc.Keys = keys
356
+	drvEnc.Tags = tags
357
+
353 358
 	c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
354 359
 		err := driver.DiscoverNew(discoverapi.EncryptionKeysConfig, drvEnc)
355 360
 		if err != nil {
... ...
@@ -380,7 +380,7 @@ func (c *controller) agentDriverNotify(d driverapi.Driver) {
380 380
 	})
381 381
 
382 382
 	drvEnc := discoverapi.DriverEncryptionConfig{}
383
-	keys, tags := c.getKeys(subsysGossip) // getKeys(subsysIPSec)
383
+	keys, tags := c.getKeys(subsysIPSec)
384 384
 	drvEnc.Keys = keys
385 385
 	drvEnc.Tags = tags
386 386
 
... ...
@@ -144,7 +144,7 @@ type controller struct {
144 144
 	unWatchCh              chan *endpoint
145 145
 	svcRecords             map[string]svcInfo
146 146
 	nmap                   map[string]*netWatch
147
-	serviceBindings        map[string]*service
147
+	serviceBindings        map[serviceKey]*service
148 148
 	defOsSbox              osl.Sandbox
149 149
 	ingressSandbox         *sandbox
150 150
 	sboxOnce               sync.Once
... ...
@@ -167,7 +167,7 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
167 167
 		cfg:             config.ParseConfigOptions(cfgOptions...),
168 168
 		sandboxes:       sandboxTable{},
169 169
 		svcRecords:      make(map[string]svcInfo),
170
-		serviceBindings: make(map[string]*service),
170
+		serviceBindings: make(map[serviceKey]*service),
171 171
 		agentInitDone:   make(chan struct{}),
172 172
 	}
173 173
 
... ...
@@ -54,11 +54,12 @@ var (
54 54
 )
55 55
 
56 56
 type datastore struct {
57
-	scope   string
58
-	store   store.Store
59
-	cache   *cache
60
-	watchCh chan struct{}
61
-	active  bool
57
+	scope      string
58
+	store      store.Store
59
+	cache      *cache
60
+	watchCh    chan struct{}
61
+	active     bool
62
+	sequential bool
62 63
 	sync.Mutex
63 64
 }
64 65
 
... ...
@@ -190,6 +191,10 @@ func newClient(scope string, kv string, addr string, config *store.Config, cache
190 190
 	if cached && scope != LocalScope {
191 191
 		return nil, fmt.Errorf("caching supported only for scope %s", LocalScope)
192 192
 	}
193
+	sequential := false
194
+	if scope == LocalScope {
195
+		sequential = true
196
+	}
193 197
 
194 198
 	if config == nil {
195 199
 		config = &store.Config{}
... ...
@@ -216,7 +221,7 @@ func newClient(scope string, kv string, addr string, config *store.Config, cache
216 216
 		return nil, err
217 217
 	}
218 218
 
219
-	ds := &datastore{scope: scope, store: store, active: true, watchCh: make(chan struct{})}
219
+	ds := &datastore{scope: scope, store: store, active: true, watchCh: make(chan struct{}), sequential: sequential}
220 220
 	if cached {
221 221
 		ds.cache = newCache(ds)
222 222
 	}
... ...
@@ -375,8 +380,10 @@ func (ds *datastore) PutObjectAtomic(kvObject KVObject) error {
375 375
 		pair     *store.KVPair
376 376
 		err      error
377 377
 	)
378
-	ds.Lock()
379
-	defer ds.Unlock()
378
+	if ds.sequential {
379
+		ds.Lock()
380
+		defer ds.Unlock()
381
+	}
380 382
 
381 383
 	if kvObject == nil {
382 384
 		return types.BadRequestErrorf("invalid KV Object : nil")
... ...
@@ -420,8 +427,10 @@ add_cache:
420 420
 
421 421
 // PutObject adds a new Record based on an object into the datastore
422 422
 func (ds *datastore) PutObject(kvObject KVObject) error {
423
-	ds.Lock()
424
-	defer ds.Unlock()
423
+	if ds.sequential {
424
+		ds.Lock()
425
+		defer ds.Unlock()
426
+	}
425 427
 
426 428
 	if kvObject == nil {
427 429
 		return types.BadRequestErrorf("invalid KV Object : nil")
... ...
@@ -456,8 +465,10 @@ func (ds *datastore) putObjectWithKey(kvObject KVObject, key ...string) error {
456 456
 
457 457
 // GetObject returns a record matching the key
458 458
 func (ds *datastore) GetObject(key string, o KVObject) error {
459
-	ds.Lock()
460
-	defer ds.Unlock()
459
+	if ds.sequential {
460
+		ds.Lock()
461
+		defer ds.Unlock()
462
+	}
461 463
 
462 464
 	if ds.cache != nil {
463 465
 		return ds.cache.get(key, o)
... ...
@@ -490,8 +501,10 @@ func (ds *datastore) ensureParent(parent string) error {
490 490
 }
491 491
 
492 492
 func (ds *datastore) List(key string, kvObject KVObject) ([]KVObject, error) {
493
-	ds.Lock()
494
-	defer ds.Unlock()
493
+	if ds.sequential {
494
+		ds.Lock()
495
+		defer ds.Unlock()
496
+	}
495 497
 
496 498
 	if ds.cache != nil {
497 499
 		return ds.cache.list(kvObject)
... ...
@@ -536,8 +549,10 @@ func (ds *datastore) List(key string, kvObject KVObject) ([]KVObject, error) {
536 536
 
537 537
 // DeleteObject unconditionally deletes a record from the store
538 538
 func (ds *datastore) DeleteObject(kvObject KVObject) error {
539
-	ds.Lock()
540
-	defer ds.Unlock()
539
+	if ds.sequential {
540
+		ds.Lock()
541
+		defer ds.Unlock()
542
+	}
541 543
 
542 544
 	// cleaup the cache first
543 545
 	if ds.cache != nil {
... ...
@@ -555,8 +570,10 @@ func (ds *datastore) DeleteObject(kvObject KVObject) error {
555 555
 
556 556
 // DeleteObjectAtomic performs atomic delete on a record
557 557
 func (ds *datastore) DeleteObjectAtomic(kvObject KVObject) error {
558
-	ds.Lock()
559
-	defer ds.Unlock()
558
+	if ds.sequential {
559
+		ds.Lock()
560
+		defer ds.Unlock()
561
+	}
560 562
 
561 563
 	if kvObject == nil {
562 564
 		return types.BadRequestErrorf("invalid KV Object : nil")
... ...
@@ -588,8 +605,10 @@ del_cache:
588 588
 
589 589
 // DeleteTree unconditionally deletes a record from the store
590 590
 func (ds *datastore) DeleteTree(kvObject KVObject) error {
591
-	ds.Lock()
592
-	defer ds.Unlock()
591
+	if ds.sequential {
592
+		ds.Lock()
593
+		defer ds.Unlock()
594
+	}
593 595
 
594 596
 	// cleaup the cache first
595 597
 	if ds.cache != nil {
... ...
@@ -1020,7 +1020,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
1020 1020
 	}
1021 1021
 
1022 1022
 	if err = d.storeUpdate(endpoint); err != nil {
1023
-		return fmt.Errorf("failed to save bridge endpoint %s to store: %v", ep.id[0:7], err)
1023
+		return fmt.Errorf("failed to save bridge endpoint %s to store: %v", endpoint.id[0:7], err)
1024 1024
 	}
1025 1025
 
1026 1026
 	return nil
... ...
@@ -183,7 +183,7 @@ func (ieie InvalidEndpointIDError) BadRequest() {}
183 183
 type InvalidSandboxIDError string
184 184
 
185 185
 func (isie InvalidSandboxIDError) Error() string {
186
-	return fmt.Sprintf("invalid sanbox id: %s", string(isie))
186
+	return fmt.Sprintf("invalid sandbox id: %s", string(isie))
187 187
 }
188 188
 
189 189
 // BadRequest denotes the type of this error
... ...
@@ -2,7 +2,6 @@ package ovmanager
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"log"
6 5
 	"net"
7 6
 	"strconv"
8 7
 	"strings"
... ...
@@ -20,7 +19,7 @@ import (
20 20
 const (
21 21
 	networkType  = "overlay"
22 22
 	vxlanIDStart = 256
23
-	vxlanIDEnd   = 1000
23
+	vxlanIDEnd   = (1 << 24) - 1
24 24
 )
25 25
 
26 26
 type networkTable map[string]*network
... ...
@@ -111,7 +110,8 @@ func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data,
111 111
 		}
112 112
 
113 113
 		if err := n.obtainVxlanID(s); err != nil {
114
-			log.Printf("Could not obtain vxlan id for pool %s: %v", s.subnetIP, err)
114
+			n.releaseVxlanID()
115
+			return nil, fmt.Errorf("could not obtain vxlan id for pool %s: %v", s.subnetIP, err)
115 116
 		}
116 117
 
117 118
 		n.subnets = append(n.subnets, s)
... ...
@@ -294,7 +294,7 @@ func (d *driver) Type() string {
294 294
 // DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
295 295
 func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
296 296
 	if dType != discoverapi.NodeDiscovery {
297
-		return fmt.Errorf("Unknown discovery type : %v", dType)
297
+		return nil
298 298
 	}
299 299
 	notif := &api.DiscoveryNotification{
300 300
 		DiscoveryType: dType,
... ...
@@ -306,7 +306,7 @@ func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{})
306 306
 // DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster
307 307
 func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
308 308
 	if dType != discoverapi.NodeDiscovery {
309
-		return fmt.Errorf("Unknown discovery type : %v", dType)
309
+		return nil
310 310
 	}
311 311
 	notif := &api.DiscoveryNotification{
312 312
 		DiscoveryType: dType,
... ...
@@ -722,18 +722,6 @@ func (ep *endpoint) sbLeave(sb *sandbox, force bool, options ...EndpointOption)
722 722
 	return nil
723 723
 }
724 724
 
725
-func (n *network) validateForceDelete(locator string) error {
726
-	if n.Scope() == datastore.LocalScope {
727
-		return nil
728
-	}
729
-
730
-	if locator == "" {
731
-		return fmt.Errorf("invalid endpoint locator identifier")
732
-	}
733
-
734
-	return nil
735
-}
736
-
737 725
 func (ep *endpoint) Delete(force bool) error {
738 726
 	var err error
739 727
 	n, err := ep.getNetworkFromStore()
... ...
@@ -750,15 +738,8 @@ func (ep *endpoint) Delete(force bool) error {
750 750
 	epid := ep.id
751 751
 	name := ep.name
752 752
 	sbid := ep.sandboxID
753
-	locator := ep.locator
754 753
 	ep.Unlock()
755 754
 
756
-	if force {
757
-		if err = n.validateForceDelete(locator); err != nil {
758
-			return fmt.Errorf("unable to force delete endpoint %s: %v", name, err)
759
-		}
760
-	}
761
-
762 755
 	sb, _ := n.getController().SandboxByID(sbid)
763 756
 	if sb != nil && !force {
764 757
 		return &ActiveContainerError{name: name, id: epid}
... ...
@@ -83,17 +83,29 @@ func (n *networkNamespace) programGateway(gw net.IP, isAdd bool) error {
83 83
 		return fmt.Errorf("route for the gateway %s could not be found: %v", gw, err)
84 84
 	}
85 85
 
86
+	var linkIndex int
87
+	for _, gwRoute := range gwRoutes {
88
+		if gwRoute.Gw == nil {
89
+			linkIndex = gwRoute.LinkIndex
90
+			break
91
+		}
92
+	}
93
+
94
+	if linkIndex == 0 {
95
+		return fmt.Errorf("Direct route for the gateway %s could not be found", gw)
96
+	}
97
+
86 98
 	if isAdd {
87 99
 		return n.nlHandle.RouteAdd(&netlink.Route{
88 100
 			Scope:     netlink.SCOPE_UNIVERSE,
89
-			LinkIndex: gwRoutes[0].LinkIndex,
101
+			LinkIndex: linkIndex,
90 102
 			Gw:        gw,
91 103
 		})
92 104
 	}
93 105
 
94 106
 	return n.nlHandle.RouteDel(&netlink.Route{
95 107
 		Scope:     netlink.SCOPE_UNIVERSE,
96
-		LinkIndex: gwRoutes[0].LinkIndex,
108
+		LinkIndex: linkIndex,
97 109
 		Gw:        gw,
98 110
 	})
99 111
 }
... ...
@@ -1,6 +1,7 @@
1 1
 package libnetwork
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"net"
5 6
 	"sync"
6 7
 )
... ...
@@ -12,6 +13,27 @@ var (
12 12
 	fwMarkCtrMu sync.Mutex
13 13
 )
14 14
 
15
+type portConfigs []*PortConfig
16
+
17
+func (p portConfigs) String() string {
18
+	if len(p) == 0 {
19
+		return ""
20
+	}
21
+
22
+	pc := p[0]
23
+	str := fmt.Sprintf("%d:%d/%s", pc.PublishedPort, pc.TargetPort, PortConfig_Protocol_name[int32(pc.Protocol)])
24
+	for _, pc := range p[1:] {
25
+		str = str + fmt.Sprintf(",%d:%d/%s", pc.PublishedPort, pc.TargetPort, PortConfig_Protocol_name[int32(pc.Protocol)])
26
+	}
27
+
28
+	return str
29
+}
30
+
31
+type serviceKey struct {
32
+	id    string
33
+	ports string
34
+}
35
+
15 36
 type service struct {
16 37
 	name string // Service Name
17 38
 	id   string // Service ID
... ...
@@ -21,7 +43,7 @@ type service struct {
21 21
 	loadBalancers map[string]*loadBalancer
22 22
 
23 23
 	// List of ingress ports exposed by the service
24
-	ingressPorts []*PortConfig
24
+	ingressPorts portConfigs
25 25
 
26 26
 	sync.Mutex
27 27
 }
... ...
@@ -48,13 +48,18 @@ func (c *controller) addServiceBinding(name, sid, nid, eid string, vip net.IP, i
48 48
 		return err
49 49
 	}
50 50
 
51
+	skey := serviceKey{
52
+		id:    sid,
53
+		ports: portConfigs(ingressPorts).String(),
54
+	}
55
+
51 56
 	c.Lock()
52
-	s, ok := c.serviceBindings[sid]
57
+	s, ok := c.serviceBindings[skey]
53 58
 	if !ok {
54 59
 		// Create a new service if we are seeing this service
55 60
 		// for the first time.
56 61
 		s = newService(name, sid, ingressPorts)
57
-		c.serviceBindings[sid] = s
62
+		c.serviceBindings[skey] = s
58 63
 	}
59 64
 	c.Unlock()
60 65
 
... ...
@@ -121,8 +126,13 @@ func (c *controller) rmServiceBinding(name, sid, nid, eid string, vip net.IP, in
121 121
 		return err
122 122
 	}
123 123
 
124
+	skey := serviceKey{
125
+		id:    sid,
126
+		ports: portConfigs(ingressPorts).String(),
127
+	}
128
+
124 129
 	c.Lock()
125
-	s, ok := c.serviceBindings[sid]
130
+	s, ok := c.serviceBindings[skey]
126 131
 	if !ok {
127 132
 		c.Unlock()
128 133
 		return nil
... ...
@@ -135,22 +145,19 @@ func (c *controller) rmServiceBinding(name, sid, nid, eid string, vip net.IP, in
135 135
 		n.(*network).deleteSvcRecords("tasks."+alias, ip, nil, false)
136 136
 	}
137 137
 
138
-	// Make sure to remove the right IP since if vip is
139
-	// not valid we would have added a DNS RR record.
140
-	svcIP := vip
141
-	if len(svcIP) == 0 {
142
-		svcIP = ip
143
-	}
144
-	n.(*network).deleteSvcRecords(name, svcIP, nil, false)
145
-	for _, alias := range aliases {
146
-		n.(*network).deleteSvcRecords(alias, svcIP, nil, false)
138
+	// If we are doing DNS RR add the endpoint IP to DNS record
139
+	// right away.
140
+	if len(vip) == 0 {
141
+		n.(*network).deleteSvcRecords(name, ip, nil, false)
142
+		for _, alias := range aliases {
143
+			n.(*network).deleteSvcRecords(alias, ip, nil, false)
144
+		}
147 145
 	}
148 146
 
149 147
 	s.Lock()
150
-	defer s.Unlock()
151
-
152 148
 	lb, ok := s.loadBalancers[nid]
153 149
 	if !ok {
150
+		s.Unlock()
154 151
 		return nil
155 152
 	}
156 153
 
... ...
@@ -167,7 +174,7 @@ func (c *controller) rmServiceBinding(name, sid, nid, eid string, vip net.IP, in
167 167
 	if len(s.loadBalancers) == 0 {
168 168
 		// All loadbalancers for the service removed. Time to
169 169
 		// remove the service itself.
170
-		delete(c.serviceBindings, sid)
170
+		delete(c.serviceBindings, skey)
171 171
 	}
172 172
 
173 173
 	// Remove loadbalancer service(if needed) and backend in all
... ...
@@ -175,6 +182,15 @@ func (c *controller) rmServiceBinding(name, sid, nid, eid string, vip net.IP, in
175 175
 	if len(vip) != 0 {
176 176
 		n.(*network).rmLBBackend(ip, vip, lb.fwMark, ingressPorts, rmService)
177 177
 	}
178
+	s.Unlock()
179
+
180
+	// Remove the DNS record for VIP only if we are removing the service
181
+	if rmService && len(vip) != 0 {
182
+		n.(*network).deleteSvcRecords(name, vip, nil, false)
183
+		for _, alias := range aliases {
184
+			n.(*network).deleteSvcRecords(alias, vip, nil, false)
185
+		}
186
+	}
178 187
 
179 188
 	return nil
180 189
 }
... ...
@@ -314,7 +330,7 @@ func (sb *sandbox) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*P
314 314
 	if addService {
315 315
 		var iPorts []*PortConfig
316 316
 		if sb.ingress {
317
-			iPorts = ingressPorts
317
+			iPorts = filterPortConfigs(ingressPorts, false)
318 318
 			if err := programIngress(gwIP, iPorts, false); err != nil {
319 319
 				logrus.Errorf("Failed to add ingress: %v", err)
320 320
 				return
... ...
@@ -383,7 +399,7 @@ func (sb *sandbox) rmLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*Po
383 383
 
384 384
 		var iPorts []*PortConfig
385 385
 		if sb.ingress {
386
-			iPorts = ingressPorts
386
+			iPorts = filterPortConfigs(ingressPorts, true)
387 387
 			if err := programIngress(gwIP, iPorts, true); err != nil {
388 388
 				logrus.Errorf("Failed to delete ingress: %v", err)
389 389
 			}
... ...
@@ -401,8 +417,47 @@ var (
401 401
 	ingressOnce     sync.Once
402 402
 	ingressProxyMu  sync.Mutex
403 403
 	ingressProxyTbl = make(map[string]io.Closer)
404
+	portConfigMu    sync.Mutex
405
+	portConfigTbl   = make(map[PortConfig]int)
404 406
 )
405 407
 
408
+func filterPortConfigs(ingressPorts []*PortConfig, isDelete bool) []*PortConfig {
409
+	portConfigMu.Lock()
410
+	iPorts := make([]*PortConfig, 0, len(ingressPorts))
411
+	for _, pc := range ingressPorts {
412
+		if isDelete {
413
+			if cnt, ok := portConfigTbl[*pc]; ok {
414
+				// This is the last reference to this
415
+				// port config. Delete the port config
416
+				// and add it to filtered list to be
417
+				// plumbed.
418
+				if cnt == 1 {
419
+					delete(portConfigTbl, *pc)
420
+					iPorts = append(iPorts, pc)
421
+					continue
422
+				}
423
+
424
+				portConfigTbl[*pc] = cnt - 1
425
+			}
426
+
427
+			continue
428
+		}
429
+
430
+		if cnt, ok := portConfigTbl[*pc]; ok {
431
+			portConfigTbl[*pc] = cnt + 1
432
+			continue
433
+		}
434
+
435
+		// We are adding it for the first time. Add it to the
436
+		// filter list to be plumbed.
437
+		portConfigTbl[*pc] = 1
438
+		iPorts = append(iPorts, pc)
439
+	}
440
+	portConfigMu.Unlock()
441
+
442
+	return iPorts
443
+}
444
+
406 445
 func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) error {
407 446
 	addDelOpt := "-I"
408 447
 	if isDelete {
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"fmt"
5 5
 	"math/rand"
6 6
 	"reflect"
7
-	"sync"
8 7
 	"time"
9 8
 
10 9
 	"github.com/docker/swarmkit/api"
... ...
@@ -37,7 +36,6 @@ type Agent struct {
37 37
 	stopped chan struct{} // requests shutdown
38 38
 	closed  chan struct{} // only closed in run
39 39
 	err     error         // read only after closed is closed
40
-	mu      sync.Mutex
41 40
 }
42 41
 
43 42
 // New returns a new agent, ready for task dispatch.
... ...
@@ -55,7 +55,7 @@ type ContainerStatuser interface {
55 55
 // correct status depending on the tasks current state according to the result.
56 56
 //
57 57
 // Unlike Do, if an error is returned, the status should still be reported. The
58
-// error merely reports the
58
+// error merely reports the failure at getting the controller.
59 59
 func Resolve(ctx context.Context, task *api.Task, executor Executor) (Controller, *api.TaskStatus, error) {
60 60
 	status := task.Status.Copy()
61 61
 
... ...
@@ -10,7 +10,7 @@ type Executor interface {
10 10
 	// Describe returns the underlying node description.
11 11
 	Describe(ctx context.Context) (*api.NodeDescription, error)
12 12
 
13
-	// Configure uses the node object state to propogate node
13
+	// Configure uses the node object state to propagate node
14 14
 	// state to the underlying executor.
15 15
 	Configure(ctx context.Context, node *api.Node) error
16 16
 
... ...
@@ -49,6 +49,10 @@ type NodeConfig struct {
49 49
 	// Secret to be used on the first certificate request.
50 50
 	Secret string
51 51
 
52
+	// ExternalCAs is a list of CAs to which a manager node
53
+	// will make certificate signing requests for node certificates.
54
+	ExternalCAs []*api.ExternalCA
55
+
52 56
 	// ForceNewCluster creates a new cluster from current raft state.
53 57
 	ForceNewCluster bool
54 58
 
... ...
@@ -81,7 +85,6 @@ type Node struct {
81 81
 	config               *NodeConfig
82 82
 	remotes              *persistentRemotes
83 83
 	role                 string
84
-	roleCond             *sync.Cond
85 84
 	conn                 *grpc.ClientConn
86 85
 	connCond             *sync.Cond
87 86
 	nodeID               string
... ...
@@ -95,6 +98,7 @@ type Node struct {
95 95
 	agent                *Agent
96 96
 	manager              *manager.Manager
97 97
 	roleChangeReq        chan api.NodeRole // used to send role updates from the dispatcher api on promotion/demotion
98
+	managerRoleCh        chan struct{}
98 99
 }
99 100
 
100 101
 // NewNode returns new Node instance.
... ...
@@ -124,8 +128,8 @@ func NewNode(c *NodeConfig) (*Node, error) {
124 124
 		ready:                make(chan struct{}),
125 125
 		certificateRequested: make(chan struct{}),
126 126
 		roleChangeReq:        make(chan api.NodeRole, 1),
127
+		managerRoleCh:        make(chan struct{}, 32), // 32 just for the case
127 128
 	}
128
-	n.roleCond = sync.NewCond(n.RLocker())
129 129
 	n.connCond = sync.NewCond(n.RLocker())
130 130
 	if err := n.loadCertificates(); err != nil {
131 131
 		return nil, err
... ...
@@ -174,6 +178,8 @@ func (n *Node) run(ctx context.Context) (err error) {
174 174
 		}
175 175
 	}()
176 176
 
177
+	// NOTE: When this node is created by NewNode(), our nodeID is set if
178
+	// n.loadCertificates() succeeded in loading TLS credentials.
177 179
 	if n.config.JoinAddr == "" && n.nodeID == "" {
178 180
 		if err := n.bootstrapCA(); err != nil {
179 181
 			return err
... ...
@@ -234,6 +240,10 @@ func (n *Node) run(ctx context.Context) (err error) {
234 234
 		return err
235 235
 	}
236 236
 
237
+	if n.role == ca.ManagerRole {
238
+		n.managerRoleCh <- struct{}{}
239
+	}
240
+
237 241
 	forceCertRenewal := make(chan struct{})
238 242
 	go func() {
239 243
 		n.RLock()
... ...
@@ -270,7 +280,9 @@ func (n *Node) run(ctx context.Context) (err error) {
270 270
 				}
271 271
 				n.Lock()
272 272
 				n.role = certUpdate.Role
273
-				n.roleCond.Broadcast()
273
+				if n.role == ca.ManagerRole {
274
+					n.managerRoleCh <- struct{}{}
275
+				}
274 276
 				n.Unlock()
275 277
 			case <-ctx.Done():
276 278
 				return
... ...
@@ -419,34 +431,6 @@ func (n *Node) CertificateRequested() <-chan struct{} {
419 419
 	return n.certificateRequested
420 420
 }
421 421
 
422
-func (n *Node) waitRole(ctx context.Context, role string) <-chan struct{} {
423
-	c := make(chan struct{})
424
-	n.roleCond.L.Lock()
425
-	if role == n.role {
426
-		close(c)
427
-		n.roleCond.L.Unlock()
428
-		return c
429
-	}
430
-	go func() {
431
-		select {
432
-		case <-ctx.Done():
433
-			n.roleCond.Broadcast()
434
-		case <-c:
435
-		}
436
-	}()
437
-	go func() {
438
-		defer n.roleCond.L.Unlock()
439
-		defer close(c)
440
-		for role != n.role {
441
-			n.roleCond.Wait()
442
-			if ctx.Err() != nil {
443
-				return
444
-			}
445
-		}
446
-	}()
447
-	return c
448
-}
449
-
450 422
 func (n *Node) setControlSocket(conn *grpc.ClientConn) {
451 423
 	n.Lock()
452 424
 	n.conn = conn
... ...
@@ -549,7 +533,6 @@ func (n *Node) loadCertificates() error {
549 549
 	n.role = clientTLSCreds.Role()
550 550
 	n.nodeID = clientTLSCreds.NodeID()
551 551
 	n.nodeMembership = api.NodeMembershipAccepted
552
-	n.roleCond.Broadcast()
553 552
 	n.Unlock()
554 553
 
555 554
 	return nil
... ...
@@ -599,10 +582,17 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig
599 599
 		select {
600 600
 		case <-ctx.Done():
601 601
 			return ctx.Err()
602
-		case <-n.waitRole(ctx, ca.ManagerRole):
602
+		case <-n.managerRoleCh:
603 603
 			if ctx.Err() != nil {
604 604
 				return ctx.Err()
605 605
 			}
606
+			n.Lock()
607
+			// in case if we missed some notifications
608
+			if n.role != ca.ManagerRole {
609
+				n.Unlock()
610
+				continue
611
+			}
612
+			n.Unlock()
606 613
 			remoteAddr, _ := n.remotes.Select(n.nodeID)
607 614
 			m, err := manager.New(&manager.Config{
608 615
 				ForceNewCluster: n.config.ForceNewCluster,
... ...
@@ -611,6 +601,7 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig
611 611
 					"unix": n.config.ListenControlAPI,
612 612
 				},
613 613
 				SecurityConfig: securityConfig,
614
+				ExternalCAs:    n.config.ExternalCAs,
614 615
 				JoinRaft:       remoteAddr.Addr,
615 616
 				StateDir:       n.config.StateDir,
616 617
 				HeartbeatTick:  n.config.HeartbeatTick,
... ...
@@ -629,17 +620,21 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig
629 629
 			n.manager = m
630 630
 			n.Unlock()
631 631
 
632
-			go n.initManagerConnection(ctx, ready)
632
+			connCtx, connCancel := context.WithCancel(ctx)
633
+			go n.initManagerConnection(connCtx, ready)
633 634
 
634
-			go func() {
635
-				select {
636
-				case <-ready:
637
-				case <-ctx.Done():
638
-				}
639
-				if ctx.Err() == nil {
640
-					n.remotes.Observe(api.Peer{NodeID: n.nodeID, Addr: n.config.ListenRemoteAPI}, 5)
641
-				}
642
-			}()
635
+			// this happens only on initial start
636
+			if ready != nil {
637
+				go func(ready chan struct{}) {
638
+					select {
639
+					case <-ready:
640
+						n.remotes.Observe(api.Peer{NodeID: n.nodeID, Addr: n.config.ListenRemoteAPI}, 5)
641
+					case <-connCtx.Done():
642
+					}
643
+				}(ready)
644
+			}
645
+
646
+			ready = nil
643 647
 
644 648
 			select {
645 649
 			case <-ctx.Done():
... ...
@@ -648,8 +643,8 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig
648 648
 			// in case of demotion manager will stop itself
649 649
 			case <-done:
650 650
 			}
651
+			connCancel()
651 652
 
652
-			ready = nil // ready event happens once, even on multiple starts
653 653
 			n.Lock()
654 654
 			n.manager = nil
655 655
 			if n.conn != nil {
... ...
@@ -669,7 +664,6 @@ type persistentRemotes struct {
669 669
 	c *sync.Cond
670 670
 	picker.Remotes
671 671
 	storePath      string
672
-	ch             []chan api.Peer
673 672
 	lastSavedState []api.Peer
674 673
 }
675 674
 
... ...
@@ -26,7 +26,6 @@ var (
26 26
 // agent through errs, messages and tasks.
27 27
 type session struct {
28 28
 	agent     *Agent
29
-	nodeID    string
30 29
 	sessionID string
31 30
 	session   api.Dispatcher_SessionClient
32 31
 	errs      chan error
... ...
@@ -1,8 +1,6 @@
1 1
 package agent
2 2
 
3 3
 import (
4
-	"bytes"
5
-
6 4
 	"github.com/boltdb/bolt"
7 5
 	"github.com/docker/swarmkit/api"
8 6
 	"github.com/gogo/protobuf/proto"
... ...
@@ -22,12 +20,6 @@ var (
22 22
 	bucketKeyStatus         = []byte("status")
23 23
 )
24 24
 
25
-type bucketKeyPath [][]byte
26
-
27
-func (bk bucketKeyPath) String() string {
28
-	return string(bytes.Join([][]byte(bk), []byte("/")))
29
-}
30
-
31 25
 // InitDB prepares a database for writing task data.
32 26
 //
33 27
 // Proper buckets will be created if they don't already exist.
34 28
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+### Notice
1
+
2
+Do not change .pb.go files directly. You need to change the corresponding .proto files and run the following command to regenerate the .pb.go files.
3
+```
4
+$ make generate
5
+```
6
+
7
+Click [here](https://github.com/google/protobuf) for more information about protobuf.
... ...
@@ -1,3 +1,3 @@
1 1
 package api
2 2
 
3
-//go:generate protoc -I.:../protobuf:../vendor:../vendor/github.com/gogo/protobuf --gogoswarm_out=plugins=grpc+deepcopy+raftproxy+authenticatedwrapper,import_path=github.com/docker/swarmkit/api,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto,Mtimestamp/timestamp.proto=github.com/docker/swarmkit/api/timestamp,Mduration/duration.proto=github.com/docker/swarmkit/api/duration,Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor,Mplugin/plugin.proto=github.com/docker/swarmkit/protobuf/plugin:. types.proto specs.proto objects.proto control.proto dispatcher.proto ca.proto snapshot.proto raft.proto
3
+//go:generate protoc -I.:../protobuf:../vendor:../vendor/github.com/gogo/protobuf --gogoswarm_out=plugins=grpc+deepcopy+raftproxy+authenticatedwrapper,import_path=github.com/docker/swarmkit/api,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto,Mtimestamp/timestamp.proto=github.com/docker/swarmkit/api/timestamp,Mduration/duration.proto=github.com/docker/swarmkit/api/duration,Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor,Mplugin/plugin.proto=github.com/docker/swarmkit/protobuf/plugin:. types.proto specs.proto objects.proto control.proto dispatcher.proto ca.proto snapshot.proto raft.proto health.proto
4 4
new file mode 100644
... ...
@@ -0,0 +1,714 @@
0
+// Code generated by protoc-gen-gogo.
1
+// source: health.proto
2
+// DO NOT EDIT!
3
+
4
+package api
5
+
6
+import proto "github.com/gogo/protobuf/proto"
7
+import fmt "fmt"
8
+import math "math"
9
+import _ "github.com/gogo/protobuf/gogoproto"
10
+import _ "github.com/docker/swarmkit/protobuf/plugin"
11
+
12
+import strings "strings"
13
+import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
14
+import sort "sort"
15
+import strconv "strconv"
16
+import reflect "reflect"
17
+
18
+import (
19
+	context "golang.org/x/net/context"
20
+	grpc "google.golang.org/grpc"
21
+)
22
+
23
+import raftpicker "github.com/docker/swarmkit/manager/raftpicker"
24
+import codes "google.golang.org/grpc/codes"
25
+import metadata "google.golang.org/grpc/metadata"
26
+import transport "google.golang.org/grpc/transport"
27
+
28
+import io "io"
29
+
30
+// Reference imports to suppress errors if they are not otherwise used.
31
+var _ = proto.Marshal
32
+var _ = fmt.Errorf
33
+var _ = math.Inf
34
+
35
+type HealthCheckResponse_ServingStatus int32
36
+
37
+const (
38
+	HealthCheckResponse_UNKNOWN     HealthCheckResponse_ServingStatus = 0
39
+	HealthCheckResponse_SERVING     HealthCheckResponse_ServingStatus = 1
40
+	HealthCheckResponse_NOT_SERVING HealthCheckResponse_ServingStatus = 2
41
+)
42
+
43
+var HealthCheckResponse_ServingStatus_name = map[int32]string{
44
+	0: "UNKNOWN",
45
+	1: "SERVING",
46
+	2: "NOT_SERVING",
47
+}
48
+var HealthCheckResponse_ServingStatus_value = map[string]int32{
49
+	"UNKNOWN":     0,
50
+	"SERVING":     1,
51
+	"NOT_SERVING": 2,
52
+}
53
+
54
+func (x HealthCheckResponse_ServingStatus) String() string {
55
+	return proto.EnumName(HealthCheckResponse_ServingStatus_name, int32(x))
56
+}
57
+func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) {
58
+	return fileDescriptorHealth, []int{1, 0}
59
+}
60
+
61
+type HealthCheckRequest struct {
62
+	Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
63
+}
64
+
65
+func (m *HealthCheckRequest) Reset()                    { *m = HealthCheckRequest{} }
66
+func (*HealthCheckRequest) ProtoMessage()               {}
67
+func (*HealthCheckRequest) Descriptor() ([]byte, []int) { return fileDescriptorHealth, []int{0} }
68
+
69
+type HealthCheckResponse struct {
70
+	Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=docker.swarmkit.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"`
71
+}
72
+
73
+func (m *HealthCheckResponse) Reset()                    { *m = HealthCheckResponse{} }
74
+func (*HealthCheckResponse) ProtoMessage()               {}
75
+func (*HealthCheckResponse) Descriptor() ([]byte, []int) { return fileDescriptorHealth, []int{1} }
76
+
77
+func init() {
78
+	proto.RegisterType((*HealthCheckRequest)(nil), "docker.swarmkit.v1.HealthCheckRequest")
79
+	proto.RegisterType((*HealthCheckResponse)(nil), "docker.swarmkit.v1.HealthCheckResponse")
80
+	proto.RegisterEnum("docker.swarmkit.v1.HealthCheckResponse_ServingStatus", HealthCheckResponse_ServingStatus_name, HealthCheckResponse_ServingStatus_value)
81
+}
82
+
83
+type authenticatedWrapperHealthServer struct {
84
+	local     HealthServer
85
+	authorize func(context.Context, []string) error
86
+}
87
+
88
+func NewAuthenticatedWrapperHealthServer(local HealthServer, authorize func(context.Context, []string) error) HealthServer {
89
+	return &authenticatedWrapperHealthServer{
90
+		local:     local,
91
+		authorize: authorize,
92
+	}
93
+}
94
+
95
+func (p *authenticatedWrapperHealthServer) Check(ctx context.Context, r *HealthCheckRequest) (*HealthCheckResponse, error) {
96
+
97
+	if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil {
98
+		return nil, err
99
+	}
100
+	return p.local.Check(ctx, r)
101
+}
102
+
103
+func (m *HealthCheckRequest) Copy() *HealthCheckRequest {
104
+	if m == nil {
105
+		return nil
106
+	}
107
+
108
+	o := &HealthCheckRequest{
109
+		Service: m.Service,
110
+	}
111
+
112
+	return o
113
+}
114
+
115
+func (m *HealthCheckResponse) Copy() *HealthCheckResponse {
116
+	if m == nil {
117
+		return nil
118
+	}
119
+
120
+	o := &HealthCheckResponse{
121
+		Status: m.Status,
122
+	}
123
+
124
+	return o
125
+}
126
+
127
+func (this *HealthCheckRequest) GoString() string {
128
+	if this == nil {
129
+		return "nil"
130
+	}
131
+	s := make([]string, 0, 5)
132
+	s = append(s, "&api.HealthCheckRequest{")
133
+	s = append(s, "Service: "+fmt.Sprintf("%#v", this.Service)+",\n")
134
+	s = append(s, "}")
135
+	return strings.Join(s, "")
136
+}
137
+func (this *HealthCheckResponse) GoString() string {
138
+	if this == nil {
139
+		return "nil"
140
+	}
141
+	s := make([]string, 0, 5)
142
+	s = append(s, "&api.HealthCheckResponse{")
143
+	s = append(s, "Status: "+fmt.Sprintf("%#v", this.Status)+",\n")
144
+	s = append(s, "}")
145
+	return strings.Join(s, "")
146
+}
147
+func valueToGoStringHealth(v interface{}, typ string) string {
148
+	rv := reflect.ValueOf(v)
149
+	if rv.IsNil() {
150
+		return "nil"
151
+	}
152
+	pv := reflect.Indirect(rv).Interface()
153
+	return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv)
154
+}
155
+func extensionToGoStringHealth(e map[int32]github_com_gogo_protobuf_proto.Extension) string {
156
+	if e == nil {
157
+		return "nil"
158
+	}
159
+	s := "map[int32]proto.Extension{"
160
+	keys := make([]int, 0, len(e))
161
+	for k := range e {
162
+		keys = append(keys, int(k))
163
+	}
164
+	sort.Ints(keys)
165
+	ss := []string{}
166
+	for _, k := range keys {
167
+		ss = append(ss, strconv.Itoa(k)+": "+e[int32(k)].GoString())
168
+	}
169
+	s += strings.Join(ss, ",") + "}"
170
+	return s
171
+}
172
+
173
+// Reference imports to suppress errors if they are not otherwise used.
174
+var _ context.Context
175
+var _ grpc.ClientConn
176
+
177
+// This is a compile-time assertion to ensure that this generated file
178
+// is compatible with the grpc package it is being compiled against.
179
+const _ = grpc.SupportPackageIsVersion2
180
+
181
+// Client API for Health service
182
+
183
+type HealthClient interface {
184
+	Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error)
185
+}
186
+
187
+type healthClient struct {
188
+	cc *grpc.ClientConn
189
+}
190
+
191
+func NewHealthClient(cc *grpc.ClientConn) HealthClient {
192
+	return &healthClient{cc}
193
+}
194
+
195
+func (c *healthClient) Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) {
196
+	out := new(HealthCheckResponse)
197
+	err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Health/Check", in, out, c.cc, opts...)
198
+	if err != nil {
199
+		return nil, err
200
+	}
201
+	return out, nil
202
+}
203
+
204
+// Server API for Health service
205
+
206
+type HealthServer interface {
207
+	Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error)
208
+}
209
+
210
+func RegisterHealthServer(s *grpc.Server, srv HealthServer) {
211
+	s.RegisterService(&_Health_serviceDesc, srv)
212
+}
213
+
214
+func _Health_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
215
+	in := new(HealthCheckRequest)
216
+	if err := dec(in); err != nil {
217
+		return nil, err
218
+	}
219
+	if interceptor == nil {
220
+		return srv.(HealthServer).Check(ctx, in)
221
+	}
222
+	info := &grpc.UnaryServerInfo{
223
+		Server:     srv,
224
+		FullMethod: "/docker.swarmkit.v1.Health/Check",
225
+	}
226
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
227
+		return srv.(HealthServer).Check(ctx, req.(*HealthCheckRequest))
228
+	}
229
+	return interceptor(ctx, in, info, handler)
230
+}
231
+
232
+var _Health_serviceDesc = grpc.ServiceDesc{
233
+	ServiceName: "docker.swarmkit.v1.Health",
234
+	HandlerType: (*HealthServer)(nil),
235
+	Methods: []grpc.MethodDesc{
236
+		{
237
+			MethodName: "Check",
238
+			Handler:    _Health_Check_Handler,
239
+		},
240
+	},
241
+	Streams: []grpc.StreamDesc{},
242
+}
243
+
244
+func (m *HealthCheckRequest) Marshal() (data []byte, err error) {
245
+	size := m.Size()
246
+	data = make([]byte, size)
247
+	n, err := m.MarshalTo(data)
248
+	if err != nil {
249
+		return nil, err
250
+	}
251
+	return data[:n], nil
252
+}
253
+
254
+func (m *HealthCheckRequest) MarshalTo(data []byte) (int, error) {
255
+	var i int
256
+	_ = i
257
+	var l int
258
+	_ = l
259
+	if len(m.Service) > 0 {
260
+		data[i] = 0xa
261
+		i++
262
+		i = encodeVarintHealth(data, i, uint64(len(m.Service)))
263
+		i += copy(data[i:], m.Service)
264
+	}
265
+	return i, nil
266
+}
267
+
268
+func (m *HealthCheckResponse) Marshal() (data []byte, err error) {
269
+	size := m.Size()
270
+	data = make([]byte, size)
271
+	n, err := m.MarshalTo(data)
272
+	if err != nil {
273
+		return nil, err
274
+	}
275
+	return data[:n], nil
276
+}
277
+
278
+func (m *HealthCheckResponse) MarshalTo(data []byte) (int, error) {
279
+	var i int
280
+	_ = i
281
+	var l int
282
+	_ = l
283
+	if m.Status != 0 {
284
+		data[i] = 0x8
285
+		i++
286
+		i = encodeVarintHealth(data, i, uint64(m.Status))
287
+	}
288
+	return i, nil
289
+}
290
+
291
+func encodeFixed64Health(data []byte, offset int, v uint64) int {
292
+	data[offset] = uint8(v)
293
+	data[offset+1] = uint8(v >> 8)
294
+	data[offset+2] = uint8(v >> 16)
295
+	data[offset+3] = uint8(v >> 24)
296
+	data[offset+4] = uint8(v >> 32)
297
+	data[offset+5] = uint8(v >> 40)
298
+	data[offset+6] = uint8(v >> 48)
299
+	data[offset+7] = uint8(v >> 56)
300
+	return offset + 8
301
+}
302
+func encodeFixed32Health(data []byte, offset int, v uint32) int {
303
+	data[offset] = uint8(v)
304
+	data[offset+1] = uint8(v >> 8)
305
+	data[offset+2] = uint8(v >> 16)
306
+	data[offset+3] = uint8(v >> 24)
307
+	return offset + 4
308
+}
309
+func encodeVarintHealth(data []byte, offset int, v uint64) int {
310
+	for v >= 1<<7 {
311
+		data[offset] = uint8(v&0x7f | 0x80)
312
+		v >>= 7
313
+		offset++
314
+	}
315
+	data[offset] = uint8(v)
316
+	return offset + 1
317
+}
318
+
319
+type raftProxyHealthServer struct {
320
+	local        HealthServer
321
+	connSelector *raftpicker.ConnSelector
322
+	cluster      raftpicker.RaftCluster
323
+	ctxMods      []func(context.Context) (context.Context, error)
324
+}
325
+
326
+func NewRaftProxyHealthServer(local HealthServer, connSelector *raftpicker.ConnSelector, cluster raftpicker.RaftCluster, ctxMod func(context.Context) (context.Context, error)) HealthServer {
327
+	redirectChecker := func(ctx context.Context) (context.Context, error) {
328
+		s, ok := transport.StreamFromContext(ctx)
329
+		if !ok {
330
+			return ctx, grpc.Errorf(codes.InvalidArgument, "remote addr is not found in context")
331
+		}
332
+		addr := s.ServerTransport().RemoteAddr().String()
333
+		md, ok := metadata.FromContext(ctx)
334
+		if ok && len(md["redirect"]) != 0 {
335
+			return ctx, grpc.Errorf(codes.ResourceExhausted, "more than one redirect to leader from: %s", md["redirect"])
336
+		}
337
+		if !ok {
338
+			md = metadata.New(map[string]string{})
339
+		}
340
+		md["redirect"] = append(md["redirect"], addr)
341
+		return metadata.NewContext(ctx, md), nil
342
+	}
343
+	mods := []func(context.Context) (context.Context, error){redirectChecker}
344
+	mods = append(mods, ctxMod)
345
+
346
+	return &raftProxyHealthServer{
347
+		local:        local,
348
+		cluster:      cluster,
349
+		connSelector: connSelector,
350
+		ctxMods:      mods,
351
+	}
352
+}
353
+func (p *raftProxyHealthServer) runCtxMods(ctx context.Context) (context.Context, error) {
354
+	var err error
355
+	for _, mod := range p.ctxMods {
356
+		ctx, err = mod(ctx)
357
+		if err != nil {
358
+			return ctx, err
359
+		}
360
+	}
361
+	return ctx, nil
362
+}
363
+
364
+func (p *raftProxyHealthServer) Check(ctx context.Context, r *HealthCheckRequest) (*HealthCheckResponse, error) {
365
+
366
+	if p.cluster.IsLeader() {
367
+		return p.local.Check(ctx, r)
368
+	}
369
+	ctx, err := p.runCtxMods(ctx)
370
+	if err != nil {
371
+		return nil, err
372
+	}
373
+	conn, err := p.connSelector.Conn()
374
+	if err != nil {
375
+		return nil, err
376
+	}
377
+	return NewHealthClient(conn).Check(ctx, r)
378
+}
379
+
380
+func (m *HealthCheckRequest) Size() (n int) {
381
+	var l int
382
+	_ = l
383
+	l = len(m.Service)
384
+	if l > 0 {
385
+		n += 1 + l + sovHealth(uint64(l))
386
+	}
387
+	return n
388
+}
389
+
390
+func (m *HealthCheckResponse) Size() (n int) {
391
+	var l int
392
+	_ = l
393
+	if m.Status != 0 {
394
+		n += 1 + sovHealth(uint64(m.Status))
395
+	}
396
+	return n
397
+}
398
+
399
+func sovHealth(x uint64) (n int) {
400
+	for {
401
+		n++
402
+		x >>= 7
403
+		if x == 0 {
404
+			break
405
+		}
406
+	}
407
+	return n
408
+}
409
+func sozHealth(x uint64) (n int) {
410
+	return sovHealth(uint64((x << 1) ^ uint64((int64(x) >> 63))))
411
+}
412
+func (this *HealthCheckRequest) String() string {
413
+	if this == nil {
414
+		return "nil"
415
+	}
416
+	s := strings.Join([]string{`&HealthCheckRequest{`,
417
+		`Service:` + fmt.Sprintf("%v", this.Service) + `,`,
418
+		`}`,
419
+	}, "")
420
+	return s
421
+}
422
+func (this *HealthCheckResponse) String() string {
423
+	if this == nil {
424
+		return "nil"
425
+	}
426
+	s := strings.Join([]string{`&HealthCheckResponse{`,
427
+		`Status:` + fmt.Sprintf("%v", this.Status) + `,`,
428
+		`}`,
429
+	}, "")
430
+	return s
431
+}
432
+func valueToStringHealth(v interface{}) string {
433
+	rv := reflect.ValueOf(v)
434
+	if rv.IsNil() {
435
+		return "nil"
436
+	}
437
+	pv := reflect.Indirect(rv).Interface()
438
+	return fmt.Sprintf("*%v", pv)
439
+}
440
+func (m *HealthCheckRequest) Unmarshal(data []byte) error {
441
+	l := len(data)
442
+	iNdEx := 0
443
+	for iNdEx < l {
444
+		preIndex := iNdEx
445
+		var wire uint64
446
+		for shift := uint(0); ; shift += 7 {
447
+			if shift >= 64 {
448
+				return ErrIntOverflowHealth
449
+			}
450
+			if iNdEx >= l {
451
+				return io.ErrUnexpectedEOF
452
+			}
453
+			b := data[iNdEx]
454
+			iNdEx++
455
+			wire |= (uint64(b) & 0x7F) << shift
456
+			if b < 0x80 {
457
+				break
458
+			}
459
+		}
460
+		fieldNum := int32(wire >> 3)
461
+		wireType := int(wire & 0x7)
462
+		if wireType == 4 {
463
+			return fmt.Errorf("proto: HealthCheckRequest: wiretype end group for non-group")
464
+		}
465
+		if fieldNum <= 0 {
466
+			return fmt.Errorf("proto: HealthCheckRequest: illegal tag %d (wire type %d)", fieldNum, wire)
467
+		}
468
+		switch fieldNum {
469
+		case 1:
470
+			if wireType != 2 {
471
+				return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType)
472
+			}
473
+			var stringLen uint64
474
+			for shift := uint(0); ; shift += 7 {
475
+				if shift >= 64 {
476
+					return ErrIntOverflowHealth
477
+				}
478
+				if iNdEx >= l {
479
+					return io.ErrUnexpectedEOF
480
+				}
481
+				b := data[iNdEx]
482
+				iNdEx++
483
+				stringLen |= (uint64(b) & 0x7F) << shift
484
+				if b < 0x80 {
485
+					break
486
+				}
487
+			}
488
+			intStringLen := int(stringLen)
489
+			if intStringLen < 0 {
490
+				return ErrInvalidLengthHealth
491
+			}
492
+			postIndex := iNdEx + intStringLen
493
+			if postIndex > l {
494
+				return io.ErrUnexpectedEOF
495
+			}
496
+			m.Service = string(data[iNdEx:postIndex])
497
+			iNdEx = postIndex
498
+		default:
499
+			iNdEx = preIndex
500
+			skippy, err := skipHealth(data[iNdEx:])
501
+			if err != nil {
502
+				return err
503
+			}
504
+			if skippy < 0 {
505
+				return ErrInvalidLengthHealth
506
+			}
507
+			if (iNdEx + skippy) > l {
508
+				return io.ErrUnexpectedEOF
509
+			}
510
+			iNdEx += skippy
511
+		}
512
+	}
513
+
514
+	if iNdEx > l {
515
+		return io.ErrUnexpectedEOF
516
+	}
517
+	return nil
518
+}
519
+func (m *HealthCheckResponse) Unmarshal(data []byte) error {
520
+	l := len(data)
521
+	iNdEx := 0
522
+	for iNdEx < l {
523
+		preIndex := iNdEx
524
+		var wire uint64
525
+		for shift := uint(0); ; shift += 7 {
526
+			if shift >= 64 {
527
+				return ErrIntOverflowHealth
528
+			}
529
+			if iNdEx >= l {
530
+				return io.ErrUnexpectedEOF
531
+			}
532
+			b := data[iNdEx]
533
+			iNdEx++
534
+			wire |= (uint64(b) & 0x7F) << shift
535
+			if b < 0x80 {
536
+				break
537
+			}
538
+		}
539
+		fieldNum := int32(wire >> 3)
540
+		wireType := int(wire & 0x7)
541
+		if wireType == 4 {
542
+			return fmt.Errorf("proto: HealthCheckResponse: wiretype end group for non-group")
543
+		}
544
+		if fieldNum <= 0 {
545
+			return fmt.Errorf("proto: HealthCheckResponse: illegal tag %d (wire type %d)", fieldNum, wire)
546
+		}
547
+		switch fieldNum {
548
+		case 1:
549
+			if wireType != 0 {
550
+				return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
551
+			}
552
+			m.Status = 0
553
+			for shift := uint(0); ; shift += 7 {
554
+				if shift >= 64 {
555
+					return ErrIntOverflowHealth
556
+				}
557
+				if iNdEx >= l {
558
+					return io.ErrUnexpectedEOF
559
+				}
560
+				b := data[iNdEx]
561
+				iNdEx++
562
+				m.Status |= (HealthCheckResponse_ServingStatus(b) & 0x7F) << shift
563
+				if b < 0x80 {
564
+					break
565
+				}
566
+			}
567
+		default:
568
+			iNdEx = preIndex
569
+			skippy, err := skipHealth(data[iNdEx:])
570
+			if err != nil {
571
+				return err
572
+			}
573
+			if skippy < 0 {
574
+				return ErrInvalidLengthHealth
575
+			}
576
+			if (iNdEx + skippy) > l {
577
+				return io.ErrUnexpectedEOF
578
+			}
579
+			iNdEx += skippy
580
+		}
581
+	}
582
+
583
+	if iNdEx > l {
584
+		return io.ErrUnexpectedEOF
585
+	}
586
+	return nil
587
+}
588
+func skipHealth(data []byte) (n int, err error) {
589
+	l := len(data)
590
+	iNdEx := 0
591
+	for iNdEx < l {
592
+		var wire uint64
593
+		for shift := uint(0); ; shift += 7 {
594
+			if shift >= 64 {
595
+				return 0, ErrIntOverflowHealth
596
+			}
597
+			if iNdEx >= l {
598
+				return 0, io.ErrUnexpectedEOF
599
+			}
600
+			b := data[iNdEx]
601
+			iNdEx++
602
+			wire |= (uint64(b) & 0x7F) << shift
603
+			if b < 0x80 {
604
+				break
605
+			}
606
+		}
607
+		wireType := int(wire & 0x7)
608
+		switch wireType {
609
+		case 0:
610
+			for shift := uint(0); ; shift += 7 {
611
+				if shift >= 64 {
612
+					return 0, ErrIntOverflowHealth
613
+				}
614
+				if iNdEx >= l {
615
+					return 0, io.ErrUnexpectedEOF
616
+				}
617
+				iNdEx++
618
+				if data[iNdEx-1] < 0x80 {
619
+					break
620
+				}
621
+			}
622
+			return iNdEx, nil
623
+		case 1:
624
+			iNdEx += 8
625
+			return iNdEx, nil
626
+		case 2:
627
+			var length int
628
+			for shift := uint(0); ; shift += 7 {
629
+				if shift >= 64 {
630
+					return 0, ErrIntOverflowHealth
631
+				}
632
+				if iNdEx >= l {
633
+					return 0, io.ErrUnexpectedEOF
634
+				}
635
+				b := data[iNdEx]
636
+				iNdEx++
637
+				length |= (int(b) & 0x7F) << shift
638
+				if b < 0x80 {
639
+					break
640
+				}
641
+			}
642
+			iNdEx += length
643
+			if length < 0 {
644
+				return 0, ErrInvalidLengthHealth
645
+			}
646
+			return iNdEx, nil
647
+		case 3:
648
+			for {
649
+				var innerWire uint64
650
+				var start int = iNdEx
651
+				for shift := uint(0); ; shift += 7 {
652
+					if shift >= 64 {
653
+						return 0, ErrIntOverflowHealth
654
+					}
655
+					if iNdEx >= l {
656
+						return 0, io.ErrUnexpectedEOF
657
+					}
658
+					b := data[iNdEx]
659
+					iNdEx++
660
+					innerWire |= (uint64(b) & 0x7F) << shift
661
+					if b < 0x80 {
662
+						break
663
+					}
664
+				}
665
+				innerWireType := int(innerWire & 0x7)
666
+				if innerWireType == 4 {
667
+					break
668
+				}
669
+				next, err := skipHealth(data[start:])
670
+				if err != nil {
671
+					return 0, err
672
+				}
673
+				iNdEx = start + next
674
+			}
675
+			return iNdEx, nil
676
+		case 4:
677
+			return iNdEx, nil
678
+		case 5:
679
+			iNdEx += 4
680
+			return iNdEx, nil
681
+		default:
682
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
683
+		}
684
+	}
685
+	panic("unreachable")
686
+}
687
+
688
+var (
689
+	ErrInvalidLengthHealth = fmt.Errorf("proto: negative length found during unmarshaling")
690
+	ErrIntOverflowHealth   = fmt.Errorf("proto: integer overflow")
691
+)
692
+
693
+var fileDescriptorHealth = []byte{
694
+	// 284 bytes of a gzipped FileDescriptorProto
695
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xc9, 0x48, 0x4d, 0xcc,
696
+	0x29, 0xc9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x4a, 0xc9, 0x4f, 0xce, 0x4e, 0x2d,
697
+	0xd2, 0x2b, 0x2e, 0x4f, 0x2c, 0xca, 0xcd, 0xce, 0x2c, 0xd1, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf,
698
+	0x4f, 0xcf, 0x07, 0x4b, 0xeb, 0x83, 0x58, 0x10, 0x95, 0x52, 0xc2, 0x05, 0x39, 0xa5, 0xe9, 0x99,
699
+	0x79, 0xfa, 0x10, 0x0a, 0x22, 0xa8, 0xa4, 0xc7, 0x25, 0xe4, 0x01, 0x36, 0xce, 0x39, 0x23, 0x35,
700
+	0x39, 0x3b, 0x28, 0xb5, 0xb0, 0x34, 0xb5, 0xb8, 0x44, 0x48, 0x82, 0x8b, 0xbd, 0x38, 0xb5, 0xa8,
701
+	0x2c, 0x33, 0x39, 0x55, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x55, 0x5a, 0xc0, 0xc8,
702
+	0x25, 0x8c, 0xa2, 0xa1, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0xc8, 0x97, 0x8b, 0xad, 0xb8, 0x24,
703
+	0xb1, 0xa4, 0xb4, 0x18, 0xac, 0x81, 0xcf, 0xc8, 0x54, 0x0f, 0xd3, 0x5d, 0x7a, 0x58, 0x34, 0xea,
704
+	0x05, 0x83, 0x0c, 0xce, 0x4b, 0x0f, 0x06, 0x6b, 0x0e, 0x82, 0x1a, 0xa2, 0x64, 0xc5, 0xc5, 0x8b,
705
+	0x22, 0x21, 0xc4, 0xcd, 0xc5, 0x1e, 0xea, 0xe7, 0xed, 0xe7, 0x1f, 0xee, 0x27, 0xc0, 0x00, 0xe2,
706
+	0x04, 0xbb, 0x06, 0x85, 0x79, 0xfa, 0xb9, 0x0b, 0x30, 0x0a, 0xf1, 0x73, 0x71, 0xfb, 0xf9, 0x87,
707
+	0xc4, 0xc3, 0x04, 0x98, 0x8c, 0x2a, 0xb9, 0xd8, 0x20, 0x16, 0x09, 0xe5, 0x73, 0xb1, 0x82, 0x2d,
708
+	0x13, 0x52, 0x23, 0xe8, 0x1a, 0xb0, 0xbf, 0xa5, 0xd4, 0x89, 0x74, 0xb5, 0x92, 0xe8, 0xa9, 0x75,
709
+	0xef, 0x66, 0x30, 0xf1, 0x73, 0xf1, 0x82, 0x15, 0xea, 0xe6, 0x26, 0xe6, 0x25, 0xa6, 0xa7, 0x16,
710
+	0x39, 0xc9, 0x9c, 0x78, 0x28, 0xc7, 0x70, 0x03, 0x88, 0x3f, 0x3c, 0x94, 0x63, 0x6c, 0x78, 0x24,
711
+	0xc7, 0x78, 0x02, 0x88, 0x2f, 0x00, 0xf1, 0x03, 0x20, 0x4e, 0x62, 0x03, 0x07, 0xb9, 0x31, 0x20,
712
+	0x00, 0x00, 0xff, 0xff, 0xf7, 0x14, 0x7c, 0x23, 0xc1, 0x01, 0x00, 0x00,
713
+}
0 714
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+syntax = "proto3";
1
+
2
+// See: https://github.com/grpc/grpc-go/blob/master/health/grpc_health_v1/health.proto
3
+//
4
+// We use the same health check service proto description defined in the gRPC documentation,
5
+// including the authorization check. This requires our own implementation of the health
6
+// package located in `manager/health`.
7
+//
8
+// For more infos, refer to:
9
+// https://github.com/grpc/grpc/blob/master/doc/health-checking.md
10
+
11
+package docker.swarmkit.v1;
12
+
13
+import "gogoproto/gogo.proto";
14
+import "plugin/plugin.proto";
15
+
16
+service Health {
17
+	rpc Check(HealthCheckRequest) returns (HealthCheckResponse) {
18
+		option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" };
19
+	};
20
+}
21
+
22
+message HealthCheckRequest {
23
+	string service = 1;
24
+}
25
+
26
+message HealthCheckResponse {
27
+	enum ServingStatus {
28
+		UNKNOWN = 0;
29
+		SERVING = 1;
30
+		NOT_SERVING = 2;
31
+	}
32
+	ServingStatus status = 1;
33
+}
... ...
@@ -90,8 +90,13 @@ func (*JoinRequest) ProtoMessage()               {}
90 90
 func (*JoinRequest) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{1} }
91 91
 
92 92
 type JoinResponse struct {
93
-	RaftID  uint64        `protobuf:"varint,1,opt,name=raft_id,json=raftId,proto3" json:"raft_id,omitempty"`
93
+	// RaftID is the ID assigned to the new member.
94
+	RaftID uint64 `protobuf:"varint,1,opt,name=raft_id,json=raftId,proto3" json:"raft_id,omitempty"`
95
+	// Members is the membership set of the cluster.
94 96
 	Members []*RaftMember `protobuf:"bytes,2,rep,name=members" json:"members,omitempty"`
97
+	// RemovedMembers is a list of members that have been removed from
98
+	// the cluster, so the new node can avoid communicating with them.
99
+	RemovedMembers []uint64 `protobuf:"varint,3,rep,name=removed_members,json=removedMembers" json:"removed_members,omitempty"`
95 100
 }
96 101
 
97 102
 func (m *JoinResponse) Reset()                    { *m = JoinResponse{} }
... ...
@@ -489,6 +494,13 @@ func (m *JoinResponse) Copy() *JoinResponse {
489 489
 		}
490 490
 	}
491 491
 
492
+	if m.RemovedMembers != nil {
493
+		o.RemovedMembers = make([]uint64, 0, len(m.RemovedMembers))
494
+		for _, v := range m.RemovedMembers {
495
+			o.RemovedMembers = append(o.RemovedMembers, v)
496
+		}
497
+	}
498
+
492 499
 	return o
493 500
 }
494 501
 
... ...
@@ -639,12 +651,13 @@ func (this *JoinResponse) GoString() string {
639 639
 	if this == nil {
640 640
 		return "nil"
641 641
 	}
642
-	s := make([]string, 0, 6)
642
+	s := make([]string, 0, 7)
643 643
 	s = append(s, "&api.JoinResponse{")
644 644
 	s = append(s, "RaftID: "+fmt.Sprintf("%#v", this.RaftID)+",\n")
645 645
 	if this.Members != nil {
646 646
 		s = append(s, "Members: "+fmt.Sprintf("%#v", this.Members)+",\n")
647 647
 	}
648
+	s = append(s, "RemovedMembers: "+fmt.Sprintf("%#v", this.RemovedMembers)+",\n")
648 649
 	s = append(s, "}")
649 650
 	return strings.Join(s, "")
650 651
 }
... ...
@@ -1111,6 +1124,13 @@ func (m *JoinResponse) MarshalTo(data []byte) (int, error) {
1111 1111
 			i += n
1112 1112
 		}
1113 1113
 	}
1114
+	if len(m.RemovedMembers) > 0 {
1115
+		for _, num := range m.RemovedMembers {
1116
+			data[i] = 0x18
1117
+			i++
1118
+			i = encodeVarintRaft(data, i, uint64(num))
1119
+		}
1120
+	}
1114 1121
 	return i, nil
1115 1122
 }
1116 1123
 
... ...
@@ -1611,6 +1631,11 @@ func (m *JoinResponse) Size() (n int) {
1611 1611
 			n += 1 + l + sovRaft(uint64(l))
1612 1612
 		}
1613 1613
 	}
1614
+	if len(m.RemovedMembers) > 0 {
1615
+		for _, e := range m.RemovedMembers {
1616
+			n += 1 + sovRaft(uint64(e))
1617
+		}
1618
+	}
1614 1619
 	return n
1615 1620
 }
1616 1621
 
... ...
@@ -1781,6 +1806,7 @@ func (this *JoinResponse) String() string {
1781 1781
 	s := strings.Join([]string{`&JoinResponse{`,
1782 1782
 		`RaftID:` + fmt.Sprintf("%v", this.RaftID) + `,`,
1783 1783
 		`Members:` + strings.Replace(fmt.Sprintf("%v", this.Members), "RaftMember", "RaftMember", 1) + `,`,
1784
+		`RemovedMembers:` + fmt.Sprintf("%v", this.RemovedMembers) + `,`,
1784 1785
 		`}`,
1785 1786
 	}, "")
1786 1787
 	return s
... ...
@@ -2238,6 +2264,26 @@ func (m *JoinResponse) Unmarshal(data []byte) error {
2238 2238
 				return err
2239 2239
 			}
2240 2240
 			iNdEx = postIndex
2241
+		case 3:
2242
+			if wireType != 0 {
2243
+				return fmt.Errorf("proto: wrong wireType = %d for field RemovedMembers", wireType)
2244
+			}
2245
+			var v uint64
2246
+			for shift := uint(0); ; shift += 7 {
2247
+				if shift >= 64 {
2248
+					return ErrIntOverflowRaft
2249
+				}
2250
+				if iNdEx >= l {
2251
+					return io.ErrUnexpectedEOF
2252
+				}
2253
+				b := data[iNdEx]
2254
+				iNdEx++
2255
+				v |= (uint64(b) & 0x7F) << shift
2256
+				if b < 0x80 {
2257
+					break
2258
+				}
2259
+			}
2260
+			m.RemovedMembers = append(m.RemovedMembers, v)
2241 2261
 		default:
2242 2262
 			iNdEx = preIndex
2243 2263
 			skippy, err := skipRaft(data[iNdEx:])
... ...
@@ -3108,58 +3154,59 @@ var (
3108 3108
 )
3109 3109
 
3110 3110
 var fileDescriptorRaft = []byte{
3111
-	// 833 bytes of a gzipped FileDescriptorProto
3112
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x95, 0x4d, 0x53, 0xdb, 0x46,
3113
-	0x18, 0xc7, 0x2d, 0x59, 0xc8, 0xed, 0x9a, 0xb7, 0x59, 0x0a, 0x35, 0x2a, 0x63, 0x40, 0x74, 0xa6,
3114
-	0x85, 0x29, 0xf2, 0xd4, 0x3d, 0xb4, 0xd3, 0xf6, 0x62, 0x1b, 0xcf, 0xd4, 0x05, 0x6c, 0x46, 0xd8,
3115
-	0x2d, 0x37, 0x2a, 0x4b, 0x8b, 0x51, 0x6d, 0x6b, 0x5d, 0xed, 0xda, 0x4c, 0x2f, 0x19, 0x8e, 0x19,
3116
-	0xae, 0x39, 0x24, 0x97, 0x9c, 0x92, 0x33, 0x1f, 0x20, 0x9f, 0x80, 0xc9, 0x29, 0xb7, 0xe4, 0x44,
3117
-	0x02, 0x1f, 0x20, 0xc9, 0x47, 0xc8, 0xae, 0x5e, 0x0c, 0x31, 0xb2, 0xf1, 0x41, 0xb0, 0xec, 0xfe,
3118
-	0xfe, 0xcf, 0xff, 0xd9, 0x67, 0xf7, 0x59, 0x00, 0x70, 0x8d, 0x23, 0xaa, 0x75, 0x5c, 0x4c, 0x31,
3119
-	0x84, 0x16, 0x36, 0x9b, 0xc8, 0xd5, 0xc8, 0x89, 0xe1, 0xb6, 0x9b, 0x36, 0xd5, 0x7a, 0x3f, 0x2a,
3120
-	0x53, 0xb8, 0xfe, 0x2f, 0x32, 0x29, 0xf1, 0x11, 0x25, 0x49, 0xff, 0xef, 0xa0, 0xf0, 0x8f, 0xcd,
3121
-	0x86, 0x4d, 0x8f, 0xbb, 0x75, 0xcd, 0xc4, 0xed, 0x8c, 0x89, 0x5d, 0x84, 0x49, 0x06, 0x51, 0xd3,
3122
-	0xca, 0xf0, 0x90, 0xde, 0x8f, 0x4e, 0x3d, 0x73, 0x13, 0x5e, 0xf9, 0xaa, 0x81, 0x1b, 0xd8, 0x1b,
3123
-	0x66, 0xf8, 0x28, 0x98, 0x9d, 0xeb, 0xb4, 0xba, 0x0d, 0xdb, 0xc9, 0xf8, 0xbf, 0xfc, 0x49, 0xf5,
3124
-	0x5c, 0x00, 0x40, 0x67, 0xca, 0x5d, 0xd4, 0xae, 0x23, 0x17, 0xae, 0x81, 0x04, 0x8f, 0x73, 0x68,
3125
-	0x5b, 0x29, 0x61, 0x45, 0xf8, 0x5e, 0xca, 0x83, 0xeb, 0xcb, 0x65, 0x99, 0x03, 0xa5, 0x2d, 0x5d,
3126
-	0xe6, 0x4b, 0x25, 0x8b, 0x43, 0x0e, 0xb6, 0x10, 0x87, 0x44, 0x06, 0x7d, 0xe9, 0x43, 0x65, 0x36,
3127
-	0xc5, 0x21, 0xbe, 0xc4, 0x20, 0x08, 0x24, 0xc3, 0xb2, 0xdc, 0x54, 0x9c, 0x13, 0xba, 0x37, 0x86,
3128
-	0x79, 0x20, 0x13, 0x6a, 0xd0, 0x2e, 0x49, 0x49, 0x6c, 0x36, 0x99, 0xfd, 0x56, 0xbb, 0x5b, 0x07,
3129
-	0xed, 0x26, 0x9b, 0x7d, 0x8f, 0xcd, 0x4b, 0x17, 0x97, 0xcb, 0x31, 0x3d, 0x50, 0xaa, 0xab, 0x20,
3130
-	0xf9, 0x27, 0xb6, 0x1d, 0x1d, 0xfd, 0xd7, 0x45, 0x84, 0xf6, 0x6d, 0x84, 0x1b, 0x1b, 0xb5, 0x0d,
3131
-	0x26, 0x7d, 0x84, 0x74, 0xb0, 0x43, 0xd0, 0x78, 0x9b, 0xfa, 0x05, 0x24, 0xda, 0x9e, 0x2b, 0x61,
3132
-	0x9b, 0x8a, 0xb3, 0xe4, 0xd2, 0xa3, 0x93, 0xd3, 0x43, 0x5c, 0xcd, 0x83, 0xc9, 0x1d, 0x64, 0xf4,
3133
-	0x50, 0x98, 0x52, 0x16, 0x48, 0xbc, 0x06, 0x9e, 0xd7, 0xfd, 0x61, 0x3c, 0x56, 0x9d, 0x01, 0x53,
3134
-	0x41, 0x0c, 0x3f, 0x67, 0x75, 0x07, 0x2c, 0xee, 0xb9, 0xd8, 0x44, 0x84, 0xf8, 0x2c, 0x21, 0x46,
3135
-	0xa3, 0xef, 0xb0, 0xce, 0x73, 0xf5, 0x66, 0x02, 0x93, 0x19, 0xcd, 0xbf, 0x04, 0x5a, 0x08, 0x86,
3136
-	0xeb, 0xbf, 0x4a, 0xa7, 0x8f, 0xd5, 0x98, 0xba, 0x04, 0x94, 0xa8, 0x68, 0x81, 0xd7, 0xef, 0x60,
3137
-	0x9e, 0x8d, 0x71, 0xab, 0x87, 0x72, 0xac, 0x7c, 0x1c, 0x0a, 0x7c, 0xc6, 0x29, 0x9c, 0xfa, 0x03,
3138
-	0x58, 0x18, 0x54, 0x07, 0x75, 0x8f, 0x3a, 0x9b, 0x23, 0x30, 0x57, 0x72, 0x28, 0x72, 0x1d, 0xa3,
3139
-	0xc5, 0xe3, 0x84, 0x4e, 0x0b, 0x40, 0xec, 0x9b, 0xc8, 0xcc, 0x44, 0x64, 0x06, 0x6c, 0x06, 0xfe,
3140
-	0x0c, 0x64, 0xc3, 0xa4, 0x36, 0x76, 0x82, 0x43, 0x59, 0x8e, 0xaa, 0xe6, 0x3e, 0x65, 0x2d, 0x91,
3141
-	0xf3, 0x30, 0x3d, 0xc0, 0xd5, 0xb7, 0x22, 0x48, 0xde, 0x9a, 0x87, 0xbf, 0xf5, 0x03, 0x71, 0x93,
3142
-	0xe9, 0xec, 0xda, 0x3d, 0x81, 0xb6, 0x6d, 0xc7, 0x0a, 0x83, 0x41, 0x2d, 0x38, 0x51, 0xd1, 0x2b,
3143
-	0x76, 0x2a, 0x4a, 0xca, 0x6f, 0xff, 0x1f, 0x31, 0xff, 0x34, 0x59, 0xd6, 0x09, 0x82, 0xdc, 0x9e,
3144
-	0x6d, 0x22, 0xef, 0xfa, 0x27, 0xb3, 0xdf, 0x44, 0xba, 0xf9, 0x08, 0x53, 0x85, 0x34, 0x37, 0xa2,
3145
-	0x06, 0x69, 0x06, 0xed, 0x11, 0x69, 0x54, 0x65, 0xeb, 0xdc, 0x88, 0x73, 0xdc, 0xc8, 0x41, 0xf4,
3146
-	0x04, 0xbb, 0xcd, 0xd4, 0xc4, 0x70, 0xa3, 0xb2, 0x8f, 0x70, 0xa3, 0x80, 0xe6, 0x42, 0xb3, 0xd5,
3147
-	0x25, 0xec, 0x20, 0x52, 0xf2, 0x70, 0x61, 0xc1, 0x47, 0xb8, 0x30, 0xa0, 0xf3, 0x5f, 0x00, 0x99,
3148
-	0x1a, 0x6e, 0x03, 0xd1, 0x8d, 0x0f, 0x02, 0x98, 0x19, 0x28, 0x18, 0xfc, 0x0e, 0x24, 0x6a, 0xe5,
3149
-	0xed, 0x72, 0xe5, 0xef, 0xf2, 0x6c, 0x4c, 0x51, 0xce, 0x9e, 0xae, 0x2c, 0x0c, 0x10, 0x35, 0xa7,
3150
-	0xe9, 0xe0, 0x13, 0x87, 0xf5, 0xc8, 0xdc, 0x7e, 0xb5, 0xa2, 0x17, 0x0f, 0x73, 0x85, 0x6a, 0xa9,
3151
-	0x52, 0x3e, 0x2c, 0xe8, 0xc5, 0x5c, 0xb5, 0x38, 0x2b, 0x28, 0x8b, 0x4c, 0x34, 0x3f, 0x20, 0x2a,
3152
-	0xb8, 0xc8, 0xa0, 0xe8, 0x8e, 0xa6, 0xb6, 0xb7, 0xc5, 0x35, 0x62, 0xa4, 0xa6, 0xd6, 0xb1, 0xa2,
3153
-	0x34, 0x7a, 0x71, 0xb7, 0xf2, 0x57, 0x71, 0x36, 0x1e, 0xa9, 0xd1, 0x51, 0x1b, 0xf7, 0x90, 0xf2,
3154
-	0xf5, 0xc3, 0x67, 0xe9, 0xd8, 0x8b, 0xe7, 0xe9, 0xc1, 0xdd, 0x65, 0x1f, 0x89, 0x40, 0xe2, 0x97,
3155
-	0x16, 0x9e, 0x09, 0x00, 0xde, 0xed, 0x27, 0xb8, 0x19, 0x55, 0xc3, 0xa1, 0x5d, 0xac, 0x68, 0xe3,
3156
-	0xe2, 0x41, 0x9b, 0xce, 0xbf, 0x3c, 0x7f, 0xff, 0x44, 0x64, 0x2f, 0x85, 0xc7, 0x6f, 0xb6, 0x0d,
3157
-	0x87, 0xad, 0xba, 0xf0, 0x01, 0x98, 0xfe, 0xbc, 0xff, 0xe0, 0x7a, 0xe4, 0x93, 0x13, 0xd5, 0xe1,
3158
-	0xca, 0xc6, 0x38, 0xe8, 0x48, 0xff, 0xec, 0x6b, 0x81, 0x25, 0xd0, 0x7f, 0xcf, 0xc8, 0xb1, 0xdd,
3159
-	0x81, 0xff, 0x00, 0x89, 0x3f, 0xc0, 0x30, 0xb2, 0x5b, 0x6f, 0xbd, 0xde, 0xca, 0xca, 0x70, 0x60,
3160
-	0xf4, 0xa6, 0x4d, 0x30, 0xe1, 0xbd, 0x97, 0x30, 0x32, 0xc2, 0xed, 0xe7, 0x58, 0x59, 0x1d, 0x41,
3161
-	0x8c, 0x34, 0xc9, 0x2f, 0x5d, 0x5c, 0xa5, 0x63, 0x6f, 0xd8, 0xf7, 0xf1, 0x2a, 0x2d, 0x9c, 0x5e,
3162
-	0xa7, 0x85, 0x0b, 0xf6, 0xbd, 0x62, 0xdf, 0x3b, 0xf6, 0x1d, 0xc4, 0x0f, 0xa4, 0xba, 0xec, 0xfd,
3163
-	0x13, 0xfd, 0xe9, 0x53, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa4, 0xfb, 0x14, 0x74, 0xdc, 0x07, 0x00,
3164
-	0x00,
3111
+	// 852 bytes of a gzipped FileDescriptorProto
3112
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x95, 0x4f, 0x53, 0x23, 0x45,
3113
+	0x18, 0xc6, 0x33, 0x93, 0x61, 0xa2, 0x1d, 0x20, 0x54, 0x23, 0x18, 0x46, 0x2a, 0xc0, 0x60, 0x95,
3114
+	0x42, 0xc9, 0xa4, 0x8c, 0x07, 0x2d, 0xf5, 0x92, 0x84, 0x54, 0x19, 0x81, 0x84, 0x1a, 0x12, 0xe5,
3115
+	0x16, 0x27, 0x33, 0x4d, 0x18, 0x93, 0x4c, 0xc7, 0xe9, 0x4e, 0x28, 0x2f, 0x16, 0x47, 0x8b, 0xab,
3116
+	0x55, 0xea, 0xc5, 0x93, 0x9e, 0xf9, 0x00, 0x7e, 0x02, 0x6a, 0x4f, 0x7b, 0xdb, 0x3d, 0xb1, 0x0b,
3117
+	0x1f, 0x60, 0x77, 0x3f, 0xc2, 0x76, 0xcf, 0x9f, 0xc0, 0x86, 0x49, 0xc8, 0xa1, 0xa1, 0x79, 0xfb,
3118
+	0xf7, 0xbc, 0x4f, 0xf7, 0xdb, 0xf3, 0x36, 0x00, 0xb8, 0xc6, 0x09, 0xd5, 0x7a, 0x2e, 0xa6, 0x18,
3119
+	0x42, 0x0b, 0x9b, 0x6d, 0xe4, 0x6a, 0xe4, 0xcc, 0x70, 0xbb, 0x6d, 0x9b, 0x6a, 0x83, 0xcf, 0x95,
3120
+	0x39, 0xdc, 0xfc, 0x19, 0x99, 0x94, 0xf8, 0x88, 0x92, 0xa4, 0xbf, 0xf6, 0x50, 0xf8, 0xc7, 0x4e,
3121
+	0xcb, 0xa6, 0xa7, 0xfd, 0xa6, 0x66, 0xe2, 0x6e, 0xd6, 0xc4, 0x2e, 0xc2, 0x24, 0x8b, 0xa8, 0x69,
3122
+	0x65, 0x79, 0x4a, 0xef, 0x47, 0xaf, 0x99, 0xbd, 0x4b, 0xaf, 0x7c, 0xd0, 0xc2, 0x2d, 0xec, 0x4d,
3123
+	0xb3, 0x7c, 0x16, 0x44, 0x17, 0x7b, 0x9d, 0x7e, 0xcb, 0x76, 0xb2, 0xfe, 0x2f, 0x3f, 0xa8, 0x5e,
3124
+	0x0a, 0x00, 0xe8, 0x4c, 0x79, 0x80, 0xba, 0x4d, 0xe4, 0xc2, 0x4d, 0x90, 0xe0, 0x79, 0x1a, 0xb6,
3125
+	0x95, 0x16, 0xd6, 0x85, 0x4f, 0xa5, 0x02, 0xb8, 0xbd, 0x5e, 0x93, 0x39, 0x50, 0xde, 0xd5, 0x65,
3126
+	0xbe, 0x54, 0xb6, 0x38, 0xe4, 0x60, 0x0b, 0x71, 0x48, 0x64, 0xd0, 0xfb, 0x3e, 0x54, 0x61, 0x21,
3127
+	0x0e, 0xf1, 0x25, 0x06, 0x41, 0x20, 0x19, 0x96, 0xe5, 0xa6, 0xe3, 0x9c, 0xd0, 0xbd, 0x39, 0x2c,
3128
+	0x00, 0x99, 0x50, 0x83, 0xf6, 0x49, 0x5a, 0x62, 0xd1, 0x64, 0xee, 0x63, 0xed, 0x61, 0x1d, 0xb4,
3129
+	0xbb, 0xdd, 0x1c, 0x79, 0x6c, 0x41, 0xba, 0xba, 0x5e, 0x8b, 0xe9, 0x81, 0x52, 0xdd, 0x00, 0xc9,
3130
+	0xef, 0xb1, 0xed, 0xe8, 0xe8, 0x97, 0x3e, 0x22, 0x74, 0x68, 0x23, 0xdc, 0xd9, 0xa8, 0x7f, 0x0a,
3131
+	0x60, 0xd6, 0x67, 0x48, 0x0f, 0x3b, 0x04, 0x4d, 0x77, 0xaa, 0xaf, 0x40, 0xa2, 0xeb, 0xd9, 0x12,
3132
+	0x76, 0xaa, 0x38, 0xdb, 0x5d, 0x66, 0xf2, 0xee, 0xf4, 0x10, 0x87, 0x9f, 0x80, 0x94, 0x8b, 0xba,
3133
+	0x78, 0x80, 0xac, 0x46, 0x98, 0x21, 0xce, 0x32, 0x48, 0xfa, 0x7c, 0x10, 0xf6, 0x05, 0x44, 0x2d,
3134
+	0x80, 0xd9, 0x7d, 0x64, 0x0c, 0x50, 0xb8, 0xf9, 0x1c, 0x90, 0x78, 0xb5, 0xbc, 0x4d, 0x3d, 0xee,
3135
+	0xe7, 0xb1, 0x6a, 0x0a, 0xcc, 0x05, 0x39, 0xfc, 0xc3, 0xa9, 0xfb, 0x60, 0xe5, 0xd0, 0xc5, 0x26,
3136
+	0x22, 0xc4, 0x67, 0x09, 0x31, 0x5a, 0x43, 0x87, 0x2d, 0x7e, 0x28, 0x2f, 0x12, 0x98, 0xa4, 0x34,
3137
+	0xff, 0x73, 0xd1, 0x42, 0x30, 0x5c, 0xff, 0x5a, 0x3a, 0xff, 0x4b, 0x8d, 0xa9, 0xab, 0x40, 0x89,
3138
+	0xca, 0x16, 0x78, 0x7d, 0x0b, 0x96, 0xd8, 0x1c, 0x77, 0x06, 0x28, 0xcf, 0x0a, 0xcd, 0xa1, 0xc0,
3139
+	0x67, 0x9a, 0x0a, 0xab, 0x9f, 0x81, 0xe5, 0x51, 0x75, 0x70, 0x41, 0x51, 0xb7, 0x78, 0x02, 0x16,
3140
+	0xcb, 0x0e, 0x45, 0xae, 0x63, 0x74, 0x78, 0x9e, 0xd0, 0x69, 0x19, 0x88, 0x43, 0x13, 0x99, 0x99,
3141
+	0x88, 0xcc, 0x80, 0x45, 0xe0, 0x97, 0x40, 0x36, 0x4c, 0x6a, 0x63, 0x27, 0xb8, 0xbd, 0xb5, 0xa8,
3142
+	0x6a, 0x1e, 0x51, 0xd6, 0x3c, 0x79, 0x0f, 0xd3, 0x03, 0x5c, 0x7d, 0x21, 0x82, 0xe4, 0xbd, 0x38,
3143
+	0xfc, 0x66, 0x98, 0x88, 0x9b, 0xcc, 0xe7, 0x36, 0x1f, 0x49, 0xb4, 0x67, 0x3b, 0x56, 0x98, 0x0c,
3144
+	0x6a, 0xc1, 0x8d, 0x8a, 0x5e, 0xb1, 0xd3, 0x51, 0x52, 0xde, 0x27, 0xdf, 0xc5, 0xfc, 0xdb, 0x64,
3145
+	0xbb, 0x4e, 0x10, 0xe4, 0x0e, 0x6c, 0x13, 0x79, 0x8d, 0x92, 0xcc, 0x7d, 0x14, 0xe9, 0xe6, 0x23,
3146
+	0x4c, 0x15, 0xd2, 0xdc, 0x88, 0x1a, 0xa4, 0x1d, 0x34, 0x52, 0xa4, 0x51, 0x8d, 0xad, 0x73, 0x23,
3147
+	0xce, 0x71, 0x23, 0x07, 0xd1, 0x33, 0xec, 0xb6, 0xd3, 0x33, 0xe3, 0x8d, 0x2a, 0x3e, 0xc2, 0x8d,
3148
+	0x02, 0x9a, 0x0b, 0xcd, 0x4e, 0x9f, 0xb0, 0x8b, 0x48, 0xcb, 0xe3, 0x85, 0x45, 0x1f, 0xe1, 0xc2,
3149
+	0x80, 0x2e, 0xbc, 0x07, 0x64, 0x6a, 0xb8, 0x2d, 0x44, 0xb7, 0x5f, 0x0b, 0x20, 0x35, 0x52, 0x30,
3150
+	0xd6, 0x33, 0x89, 0x7a, 0x65, 0xaf, 0x52, 0xfd, 0xb1, 0xb2, 0x10, 0x53, 0x94, 0x8b, 0x7f, 0xd6,
3151
+	0x97, 0x47, 0x88, 0xba, 0xd3, 0x76, 0xf0, 0x99, 0xc3, 0x7a, 0x64, 0xf1, 0xa8, 0x56, 0xd5, 0x4b,
3152
+	0x8d, 0x7c, 0xb1, 0x56, 0xae, 0x56, 0x1a, 0x45, 0xbd, 0x94, 0xaf, 0x95, 0x16, 0x04, 0x65, 0x85,
3153
+	0x89, 0x96, 0x46, 0x44, 0x45, 0x17, 0x19, 0x14, 0x3d, 0xd0, 0xd4, 0x0f, 0x77, 0xb9, 0x46, 0x8c,
3154
+	0xd4, 0xd4, 0x7b, 0x56, 0x94, 0x46, 0x2f, 0x1d, 0x54, 0x7f, 0x28, 0x2d, 0xc4, 0x23, 0x35, 0xba,
3155
+	0xd7, 0xd7, 0xca, 0x87, 0xbf, 0xff, 0x9b, 0x89, 0xfd, 0xff, 0x5f, 0x66, 0xf4, 0x74, 0xb9, 0x3f,
3156
+	0x44, 0x20, 0xf1, 0x8f, 0x16, 0x5e, 0x08, 0x00, 0x3e, 0xec, 0x27, 0xb8, 0x13, 0x55, 0xc3, 0xb1,
3157
+	0x5d, 0xac, 0x68, 0xd3, 0xe2, 0x41, 0x9b, 0x2e, 0x3d, 0xb9, 0x7c, 0xf5, 0xb7, 0xc8, 0x5e, 0x0a,
3158
+	0x8f, 0xdf, 0xe9, 0x1a, 0x0e, 0x5b, 0x75, 0xe1, 0x6f, 0x60, 0xfe, 0xdd, 0xfe, 0x83, 0x5b, 0x91,
3159
+	0x4f, 0x4e, 0x54, 0x87, 0x2b, 0xdb, 0xd3, 0xa0, 0x13, 0xfd, 0x73, 0xcf, 0x04, 0xb6, 0x81, 0xe1,
3160
+	0x7b, 0x46, 0x4e, 0xed, 0x1e, 0xfc, 0x09, 0x48, 0xfc, 0xa5, 0x86, 0x91, 0xdd, 0x7a, 0xef, 0x9d,
3161
+	0x57, 0xd6, 0xc7, 0x03, 0x93, 0x0f, 0x6d, 0x82, 0x19, 0xef, 0xbd, 0x84, 0x91, 0x19, 0xee, 0x3f,
3162
+	0xc7, 0xca, 0xc6, 0x04, 0x62, 0xa2, 0x49, 0x61, 0xf5, 0xea, 0x26, 0x13, 0x7b, 0xce, 0xc6, 0x9b,
3163
+	0x9b, 0x8c, 0x70, 0x7e, 0x9b, 0x11, 0xae, 0xd8, 0x78, 0xca, 0xc6, 0x4b, 0x36, 0x8e, 0xe3, 0xc7,
3164
+	0x52, 0x53, 0xf6, 0xfe, 0xdd, 0x7e, 0xf1, 0x36, 0x00, 0x00, 0xff, 0xff, 0xd7, 0x61, 0x3c, 0x43,
3165
+	0x06, 0x08, 0x00, 0x00,
3165 3166
 }
... ...
@@ -58,8 +58,15 @@ message JoinRequest {
58 58
 }
59 59
 
60 60
 message JoinResponse {
61
+	// RaftID is the ID assigned to the new member.
61 62
 	uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"];
63
+
64
+	// Members is the membership set of the cluster.
62 65
 	repeated RaftMember members = 2;
66
+
67
+	// RemovedMembers is a list of members that have been removed from
68
+	// the cluster, so the new node can avoid communicating with them.
69
+	repeated uint64 removed_members = 3;
63 70
 }
64 71
 
65 72
 message LeaveRequest {
... ...
@@ -268,7 +268,7 @@ func _ServiceSpec_OneofSizer(msg proto.Message) (n int) {
268 268
 // instructing Swarm on how this service should work on the particular
269 269
 // network.
270 270
 type ServiceSpec_NetworkAttachmentConfig struct {
271
-	// Target specifies the target network for attachement. This value may be a
271
+	// Target specifies the target network for attachment. This value may be a
272 272
 	// network name or identifier. Only identifiers are supported at this time.
273 273
 	Target string `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"`
274 274
 	// Aliases specifies a list of discoverable alternate names for the service on this Target.
... ...
@@ -281,7 +281,7 @@ func (*ServiceSpec_NetworkAttachmentConfig) Descriptor() ([]byte, []int) {
281 281
 	return fileDescriptorSpecs, []int{1, 0}
282 282
 }
283 283
 
284
-// ReplicatedService set the reconcilation target to certain number of replicas.
284
+// ReplicatedService sets the reconciliation target to certain number of replicas.
285 285
 type ReplicatedService struct {
286 286
 	Replicas uint64 `protobuf:"varint,1,opt,name=replicas,proto3" json:"replicas,omitempty"`
287 287
 }
... ...
@@ -290,7 +290,7 @@ func (m *ReplicatedService) Reset()                    { *m = ReplicatedService{
290 290
 func (*ReplicatedService) ProtoMessage()               {}
291 291
 func (*ReplicatedService) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{2} }
292 292
 
293
-// GlobalService represent global service.
293
+// GlobalService represents global service.
294 294
 type GlobalService struct {
295 295
 }
296 296
 
... ...
@@ -415,9 +415,12 @@ type ContainerSpec struct {
415 415
 	// executable and the following elements are treated as arguments.
416 416
 	//
417 417
 	// If command is empty, execution will fall back to the image's entrypoint.
418
+	//
419
+	// Command should only be used when overriding entrypoint.
418 420
 	Command []string `protobuf:"bytes,3,rep,name=command" json:"command,omitempty"`
419 421
 	// Args specifies arguments provided to the image's entrypoint.
420
-	// Ignored if command is specified.
422
+	//
423
+	// If Command and Args are provided, Args will be appended to Command.
421 424
 	Args []string `protobuf:"bytes,4,rep,name=args" json:"args,omitempty"`
422 425
 	// Env specifies the environment variables for the container in NAME=VALUE
423 426
 	// format. These must be compliant with  [IEEE Std
... ...
@@ -78,7 +78,7 @@ message ServiceSpec {
78 78
 	// instructing Swarm on how this service should work on the particular
79 79
 	// network.
80 80
 	message NetworkAttachmentConfig {
81
-		// Target specifies the target network for attachement. This value may be a
81
+		// Target specifies the target network for attachment. This value may be a
82 82
 		// network name or identifier. Only identifiers are supported at this time.
83 83
 		string target = 1;
84 84
 		// Aliases specifies a list of discoverable alternate names for the service on this Target.
... ...
@@ -91,12 +91,12 @@ message ServiceSpec {
91 91
 	EndpointSpec endpoint = 8;
92 92
 }
93 93
 
94
-// ReplicatedService set the reconcilation target to certain number of replicas.
94
+// ReplicatedService sets the reconciliation target to certain number of replicas.
95 95
 message ReplicatedService {
96 96
 	uint64 replicas = 1;
97 97
 }
98 98
 
99
-// GlobalService represent global service.
99
+// GlobalService represents global service.
100 100
 message GlobalService {
101 101
 	// Empty message for now.
102 102
 }
... ...
@@ -138,10 +138,13 @@ message ContainerSpec {
138 138
 	// executable and the following elements are treated as arguments.
139 139
 	//
140 140
 	// If command is empty, execution will fall back to the image's entrypoint.
141
+	//
142
+	// Command should only be used when overriding entrypoint.
141 143
 	repeated string command = 3;
142 144
 
143 145
 	// Args specifies arguments provided to the image's entrypoint.
144
-	// Ignored if command is specified.
146
+	//
147
+	// If Command and Args are provided, Args will be appended to Command.
145 148
 	repeated string args = 4;
146 149
 
147 150
 	// Env specifies the environment variables for the container in NAME=VALUE
... ...
@@ -14,6 +14,7 @@
14 14
 		ca.proto
15 15
 		snapshot.proto
16 16
 		raft.proto
17
+		health.proto
17 18
 
18 19
 	It has these top-level messages:
19 20
 		Version
... ...
@@ -40,6 +41,7 @@
40 40
 		WeightedPeer
41 41
 		IssuanceStatus
42 42
 		AcceptancePolicy
43
+		ExternalCA
43 44
 		CAConfig
44 45
 		OrchestrationConfig
45 46
 		DispatcherConfig
... ...
@@ -132,6 +134,8 @@
132 132
 		ResolveAddressResponse
133 133
 		InternalRaftRequest
134 134
 		StoreAction
135
+		HealthCheckRequest
136
+		HealthCheckResponse
135 137
 */
136 138
 package api
137 139
 
... ...
@@ -462,6 +466,26 @@ func (x IssuanceStatus_State) String() string {
462 462
 }
463 463
 func (IssuanceStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22, 0} }
464 464
 
465
+type ExternalCA_CAProtocol int32
466
+
467
+const (
468
+	ExternalCA_CAProtocolCFSSL ExternalCA_CAProtocol = 0
469
+)
470
+
471
+var ExternalCA_CAProtocol_name = map[int32]string{
472
+	0: "CFSSL",
473
+}
474
+var ExternalCA_CAProtocol_value = map[string]int32{
475
+	"CFSSL": 0,
476
+}
477
+
478
+func (x ExternalCA_CAProtocol) String() string {
479
+	return proto.EnumName(ExternalCA_CAProtocol_name, int32(x))
480
+}
481
+func (ExternalCA_CAProtocol) EnumDescriptor() ([]byte, []int) {
482
+	return fileDescriptorTypes, []int{24, 0}
483
+}
484
+
465 485
 // Encryption algorithm that can implemented using this key
466 486
 type EncryptionKey_Algorithm int32
467 487
 
... ...
@@ -480,7 +504,7 @@ func (x EncryptionKey_Algorithm) String() string {
480 480
 	return proto.EnumName(EncryptionKey_Algorithm_name, int32(x))
481 481
 }
482 482
 func (EncryptionKey_Algorithm) EnumDescriptor() ([]byte, []int) {
483
-	return fileDescriptorTypes, []int{31, 0}
483
+	return fileDescriptorTypes, []int{32, 0}
484 484
 }
485 485
 
486 486
 // Version tracks the last time an object in the store was updated.
... ...
@@ -955,14 +979,31 @@ func (*AcceptancePolicy_RoleAdmissionPolicy_HashedSecret) Descriptor() ([]byte,
955 955
 	return fileDescriptorTypes, []int{23, 0, 0}
956 956
 }
957 957
 
958
+type ExternalCA struct {
959
+	// Protocol is the protocol used by this external CA.
960
+	Protocol ExternalCA_CAProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=docker.swarmkit.v1.ExternalCA_CAProtocol" json:"protocol,omitempty"`
961
+	// URL is the URL where the external CA can be reached.
962
+	URL string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
963
+	// Options is a set of additional key/value pairs whose interpretation
964
+	// depends on the specified CA type.
965
+	Options map[string]string `protobuf:"bytes,3,rep,name=options" json:"options,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
966
+}
967
+
968
+func (m *ExternalCA) Reset()                    { *m = ExternalCA{} }
969
+func (*ExternalCA) ProtoMessage()               {}
970
+func (*ExternalCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} }
971
+
958 972
 type CAConfig struct {
959 973
 	// NodeCertExpiry is the duration certificates should be issued for
960 974
 	NodeCertExpiry *docker_swarmkit_v11.Duration `protobuf:"bytes,1,opt,name=node_cert_expiry,json=nodeCertExpiry" json:"node_cert_expiry,omitempty"`
975
+	// ExternalCAs is a list of CAs to which a manager node will make
976
+	// certificate signing requests for node certificates.
977
+	ExternalCAs []*ExternalCA `protobuf:"bytes,2,rep,name=external_cas,json=externalCas" json:"external_cas,omitempty"`
961 978
 }
962 979
 
963 980
 func (m *CAConfig) Reset()                    { *m = CAConfig{} }
964 981
 func (*CAConfig) ProtoMessage()               {}
965
-func (*CAConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} }
982
+func (*CAConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} }
966 983
 
967 984
 // OrchestrationConfig defines cluster-level orchestration settings.
968 985
 type OrchestrationConfig struct {
... ...
@@ -973,7 +1014,7 @@ type OrchestrationConfig struct {
973 973
 
974 974
 func (m *OrchestrationConfig) Reset()                    { *m = OrchestrationConfig{} }
975 975
 func (*OrchestrationConfig) ProtoMessage()               {}
976
-func (*OrchestrationConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} }
976
+func (*OrchestrationConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} }
977 977
 
978 978
 // DispatcherConfig defines cluster-level dispatcher settings.
979 979
 type DispatcherConfig struct {
... ...
@@ -984,7 +1025,7 @@ type DispatcherConfig struct {
984 984
 
985 985
 func (m *DispatcherConfig) Reset()                    { *m = DispatcherConfig{} }
986 986
 func (*DispatcherConfig) ProtoMessage()               {}
987
-func (*DispatcherConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} }
987
+func (*DispatcherConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} }
988 988
 
989 989
 // RaftConfig defines raft settings for the cluster.
990 990
 type RaftConfig struct {
... ...
@@ -1006,7 +1047,7 @@ type RaftConfig struct {
1006 1006
 
1007 1007
 func (m *RaftConfig) Reset()                    { *m = RaftConfig{} }
1008 1008
 func (*RaftConfig) ProtoMessage()               {}
1009
-func (*RaftConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} }
1009
+func (*RaftConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} }
1010 1010
 
1011 1011
 // Placement specifies task distribution constraints.
1012 1012
 type Placement struct {
... ...
@@ -1016,7 +1057,7 @@ type Placement struct {
1016 1016
 
1017 1017
 func (m *Placement) Reset()                    { *m = Placement{} }
1018 1018
 func (*Placement) ProtoMessage()               {}
1019
-func (*Placement) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} }
1019
+func (*Placement) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} }
1020 1020
 
1021 1021
 type RootCA struct {
1022 1022
 	// CAKey is the root CA private key.
... ...
@@ -1029,7 +1070,7 @@ type RootCA struct {
1029 1029
 
1030 1030
 func (m *RootCA) Reset()                    { *m = RootCA{} }
1031 1031
 func (*RootCA) ProtoMessage()               {}
1032
-func (*RootCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} }
1032
+func (*RootCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} }
1033 1033
 
1034 1034
 type Certificate struct {
1035 1035
 	Role        NodeRole       `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"`
... ...
@@ -1042,7 +1083,7 @@ type Certificate struct {
1042 1042
 
1043 1043
 func (m *Certificate) Reset()                    { *m = Certificate{} }
1044 1044
 func (*Certificate) ProtoMessage()               {}
1045
-func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} }
1045
+func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} }
1046 1046
 
1047 1047
 // Symmetric keys to encrypt inter-agent communication.
1048 1048
 type EncryptionKey struct {
... ...
@@ -1058,7 +1099,7 @@ type EncryptionKey struct {
1058 1058
 
1059 1059
 func (m *EncryptionKey) Reset()                    { *m = EncryptionKey{} }
1060 1060
 func (*EncryptionKey) ProtoMessage()               {}
1061
-func (*EncryptionKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} }
1061
+func (*EncryptionKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} }
1062 1062
 
1063 1063
 // ManagerStatus provides informations about the state of a manager in the cluster.
1064 1064
 type ManagerStatus struct {
... ...
@@ -1075,7 +1116,7 @@ type ManagerStatus struct {
1075 1075
 
1076 1076
 func (m *ManagerStatus) Reset()                    { *m = ManagerStatus{} }
1077 1077
 func (*ManagerStatus) ProtoMessage()               {}
1078
-func (*ManagerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} }
1078
+func (*ManagerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{33} }
1079 1079
 
1080 1080
 func init() {
1081 1081
 	proto.RegisterType((*Version)(nil), "docker.swarmkit.v1.Version")
... ...
@@ -1106,6 +1147,7 @@ func init() {
1106 1106
 	proto.RegisterType((*AcceptancePolicy)(nil), "docker.swarmkit.v1.AcceptancePolicy")
1107 1107
 	proto.RegisterType((*AcceptancePolicy_RoleAdmissionPolicy)(nil), "docker.swarmkit.v1.AcceptancePolicy.RoleAdmissionPolicy")
1108 1108
 	proto.RegisterType((*AcceptancePolicy_RoleAdmissionPolicy_HashedSecret)(nil), "docker.swarmkit.v1.AcceptancePolicy.RoleAdmissionPolicy.HashedSecret")
1109
+	proto.RegisterType((*ExternalCA)(nil), "docker.swarmkit.v1.ExternalCA")
1109 1110
 	proto.RegisterType((*CAConfig)(nil), "docker.swarmkit.v1.CAConfig")
1110 1111
 	proto.RegisterType((*OrchestrationConfig)(nil), "docker.swarmkit.v1.OrchestrationConfig")
1111 1112
 	proto.RegisterType((*DispatcherConfig)(nil), "docker.swarmkit.v1.DispatcherConfig")
... ...
@@ -1125,6 +1167,7 @@ func init() {
1125 1125
 	proto.RegisterEnum("docker.swarmkit.v1.IPAMConfig_AddressFamily", IPAMConfig_AddressFamily_name, IPAMConfig_AddressFamily_value)
1126 1126
 	proto.RegisterEnum("docker.swarmkit.v1.PortConfig_Protocol", PortConfig_Protocol_name, PortConfig_Protocol_value)
1127 1127
 	proto.RegisterEnum("docker.swarmkit.v1.IssuanceStatus_State", IssuanceStatus_State_name, IssuanceStatus_State_value)
1128
+	proto.RegisterEnum("docker.swarmkit.v1.ExternalCA_CAProtocol", ExternalCA_CAProtocol_name, ExternalCA_CAProtocol_value)
1128 1129
 	proto.RegisterEnum("docker.swarmkit.v1.EncryptionKey_Algorithm", EncryptionKey_Algorithm_name, EncryptionKey_Algorithm_value)
1129 1130
 }
1130 1131
 
... ...
@@ -1564,6 +1607,26 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_HashedSecret) Copy() *AcceptancePo
1564 1564
 	return o
1565 1565
 }
1566 1566
 
1567
+func (m *ExternalCA) Copy() *ExternalCA {
1568
+	if m == nil {
1569
+		return nil
1570
+	}
1571
+
1572
+	o := &ExternalCA{
1573
+		Protocol: m.Protocol,
1574
+		URL:      m.URL,
1575
+	}
1576
+
1577
+	if m.Options != nil {
1578
+		o.Options = make(map[string]string)
1579
+		for k, v := range m.Options {
1580
+			o.Options[k] = v
1581
+		}
1582
+	}
1583
+
1584
+	return o
1585
+}
1586
+
1567 1587
 func (m *CAConfig) Copy() *CAConfig {
1568 1588
 	if m == nil {
1569 1589
 		return nil
... ...
@@ -1573,6 +1636,13 @@ func (m *CAConfig) Copy() *CAConfig {
1573 1573
 		NodeCertExpiry: m.NodeCertExpiry.Copy(),
1574 1574
 	}
1575 1575
 
1576
+	if m.ExternalCAs != nil {
1577
+		o.ExternalCAs = make([]*ExternalCA, 0, len(m.ExternalCAs))
1578
+		for _, v := range m.ExternalCAs {
1579
+			o.ExternalCAs = append(o.ExternalCAs, v.Copy())
1580
+		}
1581
+	}
1582
+
1576 1583
 	return o
1577 1584
 }
1578 1585
 
... ...
@@ -2122,15 +2192,42 @@ func (this *AcceptancePolicy_RoleAdmissionPolicy_HashedSecret) GoString() string
2122 2122
 	s = append(s, "}")
2123 2123
 	return strings.Join(s, "")
2124 2124
 }
2125
+func (this *ExternalCA) GoString() string {
2126
+	if this == nil {
2127
+		return "nil"
2128
+	}
2129
+	s := make([]string, 0, 7)
2130
+	s = append(s, "&api.ExternalCA{")
2131
+	s = append(s, "Protocol: "+fmt.Sprintf("%#v", this.Protocol)+",\n")
2132
+	s = append(s, "URL: "+fmt.Sprintf("%#v", this.URL)+",\n")
2133
+	keysForOptions := make([]string, 0, len(this.Options))
2134
+	for k, _ := range this.Options {
2135
+		keysForOptions = append(keysForOptions, k)
2136
+	}
2137
+	github_com_gogo_protobuf_sortkeys.Strings(keysForOptions)
2138
+	mapStringForOptions := "map[string]string{"
2139
+	for _, k := range keysForOptions {
2140
+		mapStringForOptions += fmt.Sprintf("%#v: %#v,", k, this.Options[k])
2141
+	}
2142
+	mapStringForOptions += "}"
2143
+	if this.Options != nil {
2144
+		s = append(s, "Options: "+mapStringForOptions+",\n")
2145
+	}
2146
+	s = append(s, "}")
2147
+	return strings.Join(s, "")
2148
+}
2125 2149
 func (this *CAConfig) GoString() string {
2126 2150
 	if this == nil {
2127 2151
 		return "nil"
2128 2152
 	}
2129
-	s := make([]string, 0, 5)
2153
+	s := make([]string, 0, 6)
2130 2154
 	s = append(s, "&api.CAConfig{")
2131 2155
 	if this.NodeCertExpiry != nil {
2132 2156
 		s = append(s, "NodeCertExpiry: "+fmt.Sprintf("%#v", this.NodeCertExpiry)+",\n")
2133 2157
 	}
2158
+	if this.ExternalCAs != nil {
2159
+		s = append(s, "ExternalCAs: "+fmt.Sprintf("%#v", this.ExternalCAs)+",\n")
2160
+	}
2134 2161
 	s = append(s, "}")
2135 2162
 	return strings.Join(s, "")
2136 2163
 }
... ...
@@ -3341,6 +3438,52 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_HashedSecret) MarshalTo(data []byt
3341 3341
 	return i, nil
3342 3342
 }
3343 3343
 
3344
+func (m *ExternalCA) Marshal() (data []byte, err error) {
3345
+	size := m.Size()
3346
+	data = make([]byte, size)
3347
+	n, err := m.MarshalTo(data)
3348
+	if err != nil {
3349
+		return nil, err
3350
+	}
3351
+	return data[:n], nil
3352
+}
3353
+
3354
+func (m *ExternalCA) MarshalTo(data []byte) (int, error) {
3355
+	var i int
3356
+	_ = i
3357
+	var l int
3358
+	_ = l
3359
+	if m.Protocol != 0 {
3360
+		data[i] = 0x8
3361
+		i++
3362
+		i = encodeVarintTypes(data, i, uint64(m.Protocol))
3363
+	}
3364
+	if len(m.URL) > 0 {
3365
+		data[i] = 0x12
3366
+		i++
3367
+		i = encodeVarintTypes(data, i, uint64(len(m.URL)))
3368
+		i += copy(data[i:], m.URL)
3369
+	}
3370
+	if len(m.Options) > 0 {
3371
+		for k, _ := range m.Options {
3372
+			data[i] = 0x1a
3373
+			i++
3374
+			v := m.Options[k]
3375
+			mapSize := 1 + len(k) + sovTypes(uint64(len(k))) + 1 + len(v) + sovTypes(uint64(len(v)))
3376
+			i = encodeVarintTypes(data, i, uint64(mapSize))
3377
+			data[i] = 0xa
3378
+			i++
3379
+			i = encodeVarintTypes(data, i, uint64(len(k)))
3380
+			i += copy(data[i:], k)
3381
+			data[i] = 0x12
3382
+			i++
3383
+			i = encodeVarintTypes(data, i, uint64(len(v)))
3384
+			i += copy(data[i:], v)
3385
+		}
3386
+	}
3387
+	return i, nil
3388
+}
3389
+
3344 3390
 func (m *CAConfig) Marshal() (data []byte, err error) {
3345 3391
 	size := m.Size()
3346 3392
 	data = make([]byte, size)
... ...
@@ -3366,6 +3509,18 @@ func (m *CAConfig) MarshalTo(data []byte) (int, error) {
3366 3366
 		}
3367 3367
 		i += n18
3368 3368
 	}
3369
+	if len(m.ExternalCAs) > 0 {
3370
+		for _, msg := range m.ExternalCAs {
3371
+			data[i] = 0x12
3372
+			i++
3373
+			i = encodeVarintTypes(data, i, uint64(msg.Size()))
3374
+			n, err := msg.MarshalTo(data[i:])
3375
+			if err != nil {
3376
+				return 0, err
3377
+			}
3378
+			i += n
3379
+		}
3380
+	}
3369 3381
 	return i, nil
3370 3382
 }
3371 3383
 
... ...
@@ -4161,6 +4316,27 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_HashedSecret) Size() (n int) {
4161 4161
 	return n
4162 4162
 }
4163 4163
 
4164
+func (m *ExternalCA) Size() (n int) {
4165
+	var l int
4166
+	_ = l
4167
+	if m.Protocol != 0 {
4168
+		n += 1 + sovTypes(uint64(m.Protocol))
4169
+	}
4170
+	l = len(m.URL)
4171
+	if l > 0 {
4172
+		n += 1 + l + sovTypes(uint64(l))
4173
+	}
4174
+	if len(m.Options) > 0 {
4175
+		for k, v := range m.Options {
4176
+			_ = k
4177
+			_ = v
4178
+			mapEntrySize := 1 + len(k) + sovTypes(uint64(len(k))) + 1 + len(v) + sovTypes(uint64(len(v)))
4179
+			n += mapEntrySize + 1 + sovTypes(uint64(mapEntrySize))
4180
+		}
4181
+	}
4182
+	return n
4183
+}
4184
+
4164 4185
 func (m *CAConfig) Size() (n int) {
4165 4186
 	var l int
4166 4187
 	_ = l
... ...
@@ -4168,6 +4344,12 @@ func (m *CAConfig) Size() (n int) {
4168 4168
 		l = m.NodeCertExpiry.Size()
4169 4169
 		n += 1 + l + sovTypes(uint64(l))
4170 4170
 	}
4171
+	if len(m.ExternalCAs) > 0 {
4172
+		for _, e := range m.ExternalCAs {
4173
+			l = e.Size()
4174
+			n += 1 + l + sovTypes(uint64(l))
4175
+		}
4176
+	}
4171 4177
 	return n
4172 4178
 }
4173 4179
 
... ...
@@ -4701,12 +4883,35 @@ func (this *AcceptancePolicy_RoleAdmissionPolicy_HashedSecret) String() string {
4701 4701
 	}, "")
4702 4702
 	return s
4703 4703
 }
4704
+func (this *ExternalCA) String() string {
4705
+	if this == nil {
4706
+		return "nil"
4707
+	}
4708
+	keysForOptions := make([]string, 0, len(this.Options))
4709
+	for k, _ := range this.Options {
4710
+		keysForOptions = append(keysForOptions, k)
4711
+	}
4712
+	github_com_gogo_protobuf_sortkeys.Strings(keysForOptions)
4713
+	mapStringForOptions := "map[string]string{"
4714
+	for _, k := range keysForOptions {
4715
+		mapStringForOptions += fmt.Sprintf("%v: %v,", k, this.Options[k])
4716
+	}
4717
+	mapStringForOptions += "}"
4718
+	s := strings.Join([]string{`&ExternalCA{`,
4719
+		`Protocol:` + fmt.Sprintf("%v", this.Protocol) + `,`,
4720
+		`URL:` + fmt.Sprintf("%v", this.URL) + `,`,
4721
+		`Options:` + mapStringForOptions + `,`,
4722
+		`}`,
4723
+	}, "")
4724
+	return s
4725
+}
4704 4726
 func (this *CAConfig) String() string {
4705 4727
 	if this == nil {
4706 4728
 		return "nil"
4707 4729
 	}
4708 4730
 	s := strings.Join([]string{`&CAConfig{`,
4709 4731
 		`NodeCertExpiry:` + strings.Replace(fmt.Sprintf("%v", this.NodeCertExpiry), "Duration", "docker_swarmkit_v11.Duration", 1) + `,`,
4732
+		`ExternalCAs:` + strings.Replace(fmt.Sprintf("%v", this.ExternalCAs), "ExternalCA", "ExternalCA", 1) + `,`,
4710 4733
 		`}`,
4711 4734
 	}, "")
4712 4735
 	return s
... ...
@@ -8574,6 +8779,215 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_HashedSecret) Unmarshal(data []byt
8574 8574
 	}
8575 8575
 	return nil
8576 8576
 }
8577
+func (m *ExternalCA) Unmarshal(data []byte) error {
8578
+	l := len(data)
8579
+	iNdEx := 0
8580
+	for iNdEx < l {
8581
+		preIndex := iNdEx
8582
+		var wire uint64
8583
+		for shift := uint(0); ; shift += 7 {
8584
+			if shift >= 64 {
8585
+				return ErrIntOverflowTypes
8586
+			}
8587
+			if iNdEx >= l {
8588
+				return io.ErrUnexpectedEOF
8589
+			}
8590
+			b := data[iNdEx]
8591
+			iNdEx++
8592
+			wire |= (uint64(b) & 0x7F) << shift
8593
+			if b < 0x80 {
8594
+				break
8595
+			}
8596
+		}
8597
+		fieldNum := int32(wire >> 3)
8598
+		wireType := int(wire & 0x7)
8599
+		if wireType == 4 {
8600
+			return fmt.Errorf("proto: ExternalCA: wiretype end group for non-group")
8601
+		}
8602
+		if fieldNum <= 0 {
8603
+			return fmt.Errorf("proto: ExternalCA: illegal tag %d (wire type %d)", fieldNum, wire)
8604
+		}
8605
+		switch fieldNum {
8606
+		case 1:
8607
+			if wireType != 0 {
8608
+				return fmt.Errorf("proto: wrong wireType = %d for field Protocol", wireType)
8609
+			}
8610
+			m.Protocol = 0
8611
+			for shift := uint(0); ; shift += 7 {
8612
+				if shift >= 64 {
8613
+					return ErrIntOverflowTypes
8614
+				}
8615
+				if iNdEx >= l {
8616
+					return io.ErrUnexpectedEOF
8617
+				}
8618
+				b := data[iNdEx]
8619
+				iNdEx++
8620
+				m.Protocol |= (ExternalCA_CAProtocol(b) & 0x7F) << shift
8621
+				if b < 0x80 {
8622
+					break
8623
+				}
8624
+			}
8625
+		case 2:
8626
+			if wireType != 2 {
8627
+				return fmt.Errorf("proto: wrong wireType = %d for field URL", wireType)
8628
+			}
8629
+			var stringLen uint64
8630
+			for shift := uint(0); ; shift += 7 {
8631
+				if shift >= 64 {
8632
+					return ErrIntOverflowTypes
8633
+				}
8634
+				if iNdEx >= l {
8635
+					return io.ErrUnexpectedEOF
8636
+				}
8637
+				b := data[iNdEx]
8638
+				iNdEx++
8639
+				stringLen |= (uint64(b) & 0x7F) << shift
8640
+				if b < 0x80 {
8641
+					break
8642
+				}
8643
+			}
8644
+			intStringLen := int(stringLen)
8645
+			if intStringLen < 0 {
8646
+				return ErrInvalidLengthTypes
8647
+			}
8648
+			postIndex := iNdEx + intStringLen
8649
+			if postIndex > l {
8650
+				return io.ErrUnexpectedEOF
8651
+			}
8652
+			m.URL = string(data[iNdEx:postIndex])
8653
+			iNdEx = postIndex
8654
+		case 3:
8655
+			if wireType != 2 {
8656
+				return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType)
8657
+			}
8658
+			var msglen int
8659
+			for shift := uint(0); ; shift += 7 {
8660
+				if shift >= 64 {
8661
+					return ErrIntOverflowTypes
8662
+				}
8663
+				if iNdEx >= l {
8664
+					return io.ErrUnexpectedEOF
8665
+				}
8666
+				b := data[iNdEx]
8667
+				iNdEx++
8668
+				msglen |= (int(b) & 0x7F) << shift
8669
+				if b < 0x80 {
8670
+					break
8671
+				}
8672
+			}
8673
+			if msglen < 0 {
8674
+				return ErrInvalidLengthTypes
8675
+			}
8676
+			postIndex := iNdEx + msglen
8677
+			if postIndex > l {
8678
+				return io.ErrUnexpectedEOF
8679
+			}
8680
+			var keykey uint64
8681
+			for shift := uint(0); ; shift += 7 {
8682
+				if shift >= 64 {
8683
+					return ErrIntOverflowTypes
8684
+				}
8685
+				if iNdEx >= l {
8686
+					return io.ErrUnexpectedEOF
8687
+				}
8688
+				b := data[iNdEx]
8689
+				iNdEx++
8690
+				keykey |= (uint64(b) & 0x7F) << shift
8691
+				if b < 0x80 {
8692
+					break
8693
+				}
8694
+			}
8695
+			var stringLenmapkey uint64
8696
+			for shift := uint(0); ; shift += 7 {
8697
+				if shift >= 64 {
8698
+					return ErrIntOverflowTypes
8699
+				}
8700
+				if iNdEx >= l {
8701
+					return io.ErrUnexpectedEOF
8702
+				}
8703
+				b := data[iNdEx]
8704
+				iNdEx++
8705
+				stringLenmapkey |= (uint64(b) & 0x7F) << shift
8706
+				if b < 0x80 {
8707
+					break
8708
+				}
8709
+			}
8710
+			intStringLenmapkey := int(stringLenmapkey)
8711
+			if intStringLenmapkey < 0 {
8712
+				return ErrInvalidLengthTypes
8713
+			}
8714
+			postStringIndexmapkey := iNdEx + intStringLenmapkey
8715
+			if postStringIndexmapkey > l {
8716
+				return io.ErrUnexpectedEOF
8717
+			}
8718
+			mapkey := string(data[iNdEx:postStringIndexmapkey])
8719
+			iNdEx = postStringIndexmapkey
8720
+			var valuekey uint64
8721
+			for shift := uint(0); ; shift += 7 {
8722
+				if shift >= 64 {
8723
+					return ErrIntOverflowTypes
8724
+				}
8725
+				if iNdEx >= l {
8726
+					return io.ErrUnexpectedEOF
8727
+				}
8728
+				b := data[iNdEx]
8729
+				iNdEx++
8730
+				valuekey |= (uint64(b) & 0x7F) << shift
8731
+				if b < 0x80 {
8732
+					break
8733
+				}
8734
+			}
8735
+			var stringLenmapvalue uint64
8736
+			for shift := uint(0); ; shift += 7 {
8737
+				if shift >= 64 {
8738
+					return ErrIntOverflowTypes
8739
+				}
8740
+				if iNdEx >= l {
8741
+					return io.ErrUnexpectedEOF
8742
+				}
8743
+				b := data[iNdEx]
8744
+				iNdEx++
8745
+				stringLenmapvalue |= (uint64(b) & 0x7F) << shift
8746
+				if b < 0x80 {
8747
+					break
8748
+				}
8749
+			}
8750
+			intStringLenmapvalue := int(stringLenmapvalue)
8751
+			if intStringLenmapvalue < 0 {
8752
+				return ErrInvalidLengthTypes
8753
+			}
8754
+			postStringIndexmapvalue := iNdEx + intStringLenmapvalue
8755
+			if postStringIndexmapvalue > l {
8756
+				return io.ErrUnexpectedEOF
8757
+			}
8758
+			mapvalue := string(data[iNdEx:postStringIndexmapvalue])
8759
+			iNdEx = postStringIndexmapvalue
8760
+			if m.Options == nil {
8761
+				m.Options = make(map[string]string)
8762
+			}
8763
+			m.Options[mapkey] = mapvalue
8764
+			iNdEx = postIndex
8765
+		default:
8766
+			iNdEx = preIndex
8767
+			skippy, err := skipTypes(data[iNdEx:])
8768
+			if err != nil {
8769
+				return err
8770
+			}
8771
+			if skippy < 0 {
8772
+				return ErrInvalidLengthTypes
8773
+			}
8774
+			if (iNdEx + skippy) > l {
8775
+				return io.ErrUnexpectedEOF
8776
+			}
8777
+			iNdEx += skippy
8778
+		}
8779
+	}
8780
+
8781
+	if iNdEx > l {
8782
+		return io.ErrUnexpectedEOF
8783
+	}
8784
+	return nil
8785
+}
8577 8786
 func (m *CAConfig) Unmarshal(data []byte) error {
8578 8787
 	l := len(data)
8579 8788
 	iNdEx := 0
... ...
@@ -8636,6 +9050,37 @@ func (m *CAConfig) Unmarshal(data []byte) error {
8636 8636
 				return err
8637 8637
 			}
8638 8638
 			iNdEx = postIndex
8639
+		case 2:
8640
+			if wireType != 2 {
8641
+				return fmt.Errorf("proto: wrong wireType = %d for field ExternalCAs", wireType)
8642
+			}
8643
+			var msglen int
8644
+			for shift := uint(0); ; shift += 7 {
8645
+				if shift >= 64 {
8646
+					return ErrIntOverflowTypes
8647
+				}
8648
+				if iNdEx >= l {
8649
+					return io.ErrUnexpectedEOF
8650
+				}
8651
+				b := data[iNdEx]
8652
+				iNdEx++
8653
+				msglen |= (int(b) & 0x7F) << shift
8654
+				if b < 0x80 {
8655
+					break
8656
+				}
8657
+			}
8658
+			if msglen < 0 {
8659
+				return ErrInvalidLengthTypes
8660
+			}
8661
+			postIndex := iNdEx + msglen
8662
+			if postIndex > l {
8663
+				return io.ErrUnexpectedEOF
8664
+			}
8665
+			m.ExternalCAs = append(m.ExternalCAs, &ExternalCA{})
8666
+			if err := m.ExternalCAs[len(m.ExternalCAs)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
8667
+				return err
8668
+			}
8669
+			iNdEx = postIndex
8639 8670
 		default:
8640 8671
 			iNdEx = preIndex
8641 8672
 			skippy, err := skipTypes(data[iNdEx:])
... ...
@@ -9755,188 +10200,195 @@ var (
9755 9755
 )
9756 9756
 
9757 9757
 var fileDescriptorTypes = []byte{
9758
-	// 2925 bytes of a gzipped FileDescriptorProto
9758
+	// 3030 bytes of a gzipped FileDescriptorProto
9759 9759
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x58, 0x4d, 0x6c, 0x1b, 0xc7,
9760
-	0xf5, 0x37, 0x3f, 0x45, 0x0e, 0x29, 0x89, 0x5e, 0x3b, 0x8e, 0xcc, 0xe8, 0x2f, 0xfb, 0xbf, 0x89,
9761
-	0x1b, 0x37, 0x49, 0x99, 0x58, 0x49, 0x0b, 0x37, 0x41, 0x9b, 0x2c, 0x3f, 0x64, 0xb1, 0x96, 0x28,
9762
-	0x62, 0x48, 0xc9, 0x08, 0x8a, 0x96, 0x58, 0x2d, 0x47, 0xe2, 0x46, 0xcb, 0x5d, 0x76, 0x77, 0x29,
9763
-	0x99, 0x28, 0x0a, 0x38, 0xbd, 0xb4, 0xc8, 0xa9, 0xf7, 0x22, 0x28, 0x8a, 0xf6, 0xda, 0x73, 0x81,
9764
-	0x9e, 0x7c, 0xf4, 0xb1, 0x45, 0x81, 0x22, 0xa7, 0xa0, 0x49, 0x0f, 0xbd, 0x16, 0x68, 0xd1, 0x1c,
9765
-	0xda, 0x43, 0xdf, 0x9b, 0x8f, 0xe5, 0x87, 0xd7, 0x8a, 0xd3, 0xf4, 0x40, 0x70, 0xe7, 0xcd, 0xef,
9766
-	0xbd, 0x99, 0x37, 0xf3, 0xe6, 0xfd, 0xde, 0x0c, 0x29, 0x84, 0x93, 0x11, 0x0b, 0x2a, 0x23, 0xdf,
9767
-	0x0b, 0x3d, 0x4d, 0xeb, 0x7b, 0xd6, 0x09, 0xf3, 0x2b, 0xc1, 0x99, 0xe9, 0x0f, 0x4f, 0xec, 0xb0,
9768
-	0x72, 0x7a, 0xab, 0x7c, 0x35, 0xb4, 0x87, 0x2c, 0x08, 0xcd, 0xe1, 0xe8, 0xd5, 0xe8, 0x4b, 0xc0,
9769
-	0xcb, 0xcf, 0xf6, 0xc7, 0xbe, 0x19, 0xda, 0x9e, 0xfb, 0xaa, 0xfa, 0x90, 0x1d, 0x97, 0x8f, 0xbd,
9770
-	0x63, 0x8f, 0x7f, 0xbe, 0x8a, 0x5f, 0x42, 0xaa, 0x5f, 0x23, 0x4b, 0x07, 0xcc, 0x0f, 0x00, 0xa6,
9771
-	0x5d, 0x26, 0x19, 0xdb, 0xed, 0xb3, 0xfb, 0x6b, 0x89, 0xeb, 0x89, 0x9b, 0x69, 0x2a, 0x1a, 0xfa,
9772
-	0x2f, 0x13, 0xa4, 0x60, 0xb8, 0xae, 0x17, 0x72, 0x5b, 0x81, 0xa6, 0x91, 0xb4, 0x6b, 0x0e, 0x19,
9773
-	0x07, 0xe5, 0x29, 0xff, 0xd6, 0x6a, 0x24, 0xeb, 0x98, 0x87, 0xcc, 0x09, 0xd6, 0x92, 0xd7, 0x53,
9774
-	0x37, 0x0b, 0x9b, 0x2f, 0x57, 0x1e, 0x9f, 0x73, 0x65, 0xc6, 0x48, 0x65, 0x87, 0xa3, 0x1b, 0x6e,
9775
-	0xe8, 0x4f, 0xa8, 0x54, 0x2d, 0x7f, 0x93, 0x14, 0x66, 0xc4, 0x5a, 0x89, 0xa4, 0x4e, 0xd8, 0x44,
9776
-	0x0e, 0x83, 0x9f, 0x38, 0xbf, 0x53, 0xd3, 0x19, 0x33, 0x18, 0x04, 0x65, 0xa2, 0xf1, 0x66, 0xf2,
9777
-	0x76, 0x42, 0x7f, 0x97, 0xe4, 0x29, 0x0b, 0xbc, 0xb1, 0x6f, 0xb1, 0x40, 0xfb, 0x2a, 0xc9, 0xbb,
9778
-	0xa6, 0xeb, 0xf5, 0xac, 0xd1, 0x38, 0xe0, 0xea, 0xa9, 0x6a, 0xf1, 0xd3, 0x8f, 0xaf, 0xe5, 0x5a,
9779
-	0x20, 0xac, 0xb5, 0xf7, 0x03, 0x9a, 0xc3, 0xee, 0x1a, 0xf4, 0x6a, 0xff, 0x4f, 0x8a, 0x43, 0x36,
9780
-	0xf4, 0xfc, 0x49, 0xef, 0x70, 0x12, 0xb2, 0x80, 0x1b, 0x4e, 0xd1, 0x82, 0x90, 0x55, 0x51, 0xa4,
9781
-	0xff, 0x2c, 0x41, 0x2e, 0x2b, 0xdb, 0x94, 0xfd, 0x60, 0x6c, 0xfb, 0x6c, 0xc8, 0xdc, 0x30, 0xd0,
9782
-	0xbe, 0x0e, 0x3e, 0xdb, 0x43, 0x3b, 0x14, 0x63, 0x14, 0x36, 0xff, 0x2f, 0xce, 0xe7, 0x68, 0x56,
9783
-	0x54, 0x82, 0x35, 0x83, 0x14, 0x7d, 0x16, 0x30, 0xff, 0x54, 0xac, 0x04, 0x1f, 0xf2, 0x73, 0x95,
9784
-	0xe7, 0x54, 0xf4, 0x2d, 0x92, 0x6b, 0x3b, 0x66, 0x78, 0xe4, 0xf9, 0x43, 0x4d, 0x27, 0x45, 0xd3,
9785
-	0xb7, 0x06, 0x76, 0xc8, 0xac, 0x70, 0xec, 0xab, 0x5d, 0x99, 0x93, 0x69, 0x57, 0x48, 0xd2, 0x13,
9786
-	0x03, 0xe5, 0xab, 0x59, 0x58, 0x89, 0xe4, 0x5e, 0x87, 0x82, 0x44, 0x7f, 0x8b, 0x5c, 0x6c, 0x3b,
9787
-	0xe3, 0x63, 0xdb, 0xad, 0xb3, 0xc0, 0xf2, 0xed, 0x11, 0x5a, 0xc7, 0xed, 0xc5, 0xe0, 0x53, 0xdb,
9788
-	0x8b, 0xdf, 0xd1, 0x96, 0x27, 0xa7, 0x5b, 0xae, 0xff, 0x24, 0x49, 0x2e, 0x36, 0x5c, 0x50, 0x66,
9789
-	0xb3, 0xda, 0x37, 0xc8, 0x0a, 0xe3, 0xc2, 0xde, 0xa9, 0x08, 0x2a, 0x69, 0x67, 0x59, 0x48, 0x55,
9790
-	0xa4, 0x35, 0x17, 0xe2, 0xe5, 0x56, 0x9c, 0xfb, 0x8f, 0x59, 0x8f, 0x8b, 0x1a, 0xad, 0x41, 0x96,
9791
-	0x46, 0xdc, 0x89, 0x60, 0x2d, 0xc5, 0x6d, 0xdd, 0x88, 0xb3, 0xf5, 0x98, 0x9f, 0xd5, 0xf4, 0xa3,
9792
-	0x8f, 0xaf, 0x5d, 0xa0, 0x4a, 0xf7, 0xcb, 0x04, 0xdf, 0x5f, 0x12, 0x64, 0xb5, 0xe5, 0xf5, 0xe7,
9793
-	0xd6, 0xa1, 0x4c, 0x72, 0x03, 0x2f, 0x08, 0x67, 0x0e, 0x4a, 0xd4, 0xd6, 0x6e, 0x93, 0xdc, 0x48,
9794
-	0x6e, 0x9f, 0xdc, 0xfd, 0xf5, 0xf8, 0x29, 0x0b, 0x0c, 0x8d, 0xd0, 0xda, 0x5b, 0x24, 0xef, 0xab,
9795
-	0x98, 0x00, 0x6f, 0x9f, 0x22, 0x70, 0xa6, 0x78, 0xed, 0x5b, 0x24, 0x2b, 0x36, 0x61, 0x2d, 0xcd,
9796
-	0x35, 0x6f, 0x3c, 0xd5, 0x9a, 0x53, 0xa9, 0xa4, 0x7f, 0x94, 0x20, 0x25, 0x6a, 0x1e, 0x85, 0xbb,
9797
-	0x6c, 0x78, 0xc8, 0xfc, 0x0e, 0x1c, 0x64, 0x38, 0x3f, 0x57, 0x60, 0x1f, 0x99, 0xd9, 0x67, 0x3e,
9798
-	0x77, 0x32, 0x47, 0x65, 0x4b, 0xdb, 0xc7, 0x20, 0x37, 0xad, 0x81, 0x79, 0x68, 0x3b, 0x76, 0x38,
9799
-	0xe1, 0x6e, 0xae, 0xc4, 0xef, 0xf2, 0xa2, 0x4d, 0x98, 0xfc, 0x54, 0x91, 0xce, 0x99, 0xd1, 0xd6,
9800
-	0xc8, 0x12, 0xe4, 0xba, 0xc0, 0x3c, 0x66, 0xdc, 0xfb, 0x3c, 0x55, 0x4d, 0x08, 0xe5, 0xe2, 0xac,
9801
-	0x9e, 0x56, 0x20, 0x4b, 0xfb, 0xad, 0xbb, 0xad, 0xbd, 0x7b, 0xad, 0xd2, 0x05, 0x6d, 0x95, 0x14,
9802
-	0xf6, 0x5b, 0xb4, 0x61, 0xd4, 0xb6, 0x8d, 0xea, 0x4e, 0xa3, 0x94, 0xd0, 0x96, 0x21, 0x5d, 0x44,
9803
-	0xcd, 0xa4, 0xfe, 0x8b, 0x04, 0x21, 0xb8, 0x81, 0xd2, 0xa9, 0x37, 0x49, 0x06, 0xf2, 0x69, 0x28,
9804
-	0x36, 0x6e, 0x65, 0xf3, 0x85, 0xb8, 0x59, 0x4f, 0xe1, 0x15, 0xfc, 0x63, 0x54, 0xa8, 0xcc, 0xce,
9805
-	0x30, 0xb9, 0x38, 0xc3, 0x0c, 0x47, 0xce, 0x4f, 0x2d, 0x47, 0xd2, 0x75, 0xfc, 0x4a, 0x68, 0x79,
9806
-	0x92, 0x81, 0x39, 0xd5, 0xdf, 0x2d, 0x25, 0x21, 0xf8, 0x8a, 0xf5, 0x66, 0xa7, 0xb6, 0xd7, 0x6a,
9807
-	0x35, 0x6a, 0xdd, 0x46, 0xbd, 0x94, 0xd2, 0x6f, 0x90, 0x4c, 0x73, 0x08, 0x56, 0xb4, 0x75, 0x8c,
9808
-	0x80, 0x23, 0xe6, 0x33, 0xd7, 0x52, 0x81, 0x35, 0x15, 0xe8, 0xff, 0x5c, 0x22, 0x99, 0x5d, 0x6f,
9809
-	0xec, 0x86, 0xda, 0xe6, 0xcc, 0x29, 0x5e, 0xd9, 0xdc, 0x88, 0x73, 0x81, 0x03, 0x2b, 0x5d, 0x40,
9810
-	0xc9, 0x53, 0x0e, 0x9b, 0x29, 0x62, 0x45, 0x4e, 0x5d, 0xb6, 0x50, 0x1e, 0x9a, 0xfe, 0x31, 0x0b,
9811
-	0xe5, 0xa2, 0xcb, 0x16, 0xc6, 0xf8, 0x99, 0x6f, 0x87, 0xe6, 0xa1, 0x23, 0x42, 0x2a, 0x47, 0xa3,
9812
-	0xb6, 0xb6, 0x4d, 0x8a, 0x87, 0x40, 0x1f, 0x3d, 0x6f, 0x24, 0xb2, 0x5c, 0xe6, 0xc9, 0x21, 0x27,
9813
-	0xe6, 0x51, 0x05, 0xf4, 0x9e, 0x00, 0xd3, 0xc2, 0xe1, 0xb4, 0xa1, 0xb5, 0xc8, 0xca, 0xa9, 0xe7,
9814
-	0x8c, 0x87, 0x2c, 0xb2, 0x95, 0xe5, 0xb6, 0x5e, 0x7c, 0xb2, 0xad, 0x03, 0x8e, 0x57, 0xd6, 0x96,
9815
-	0x4f, 0x67, 0x9b, 0xe5, 0x1f, 0xa7, 0x48, 0x61, 0x66, 0x30, 0xad, 0x43, 0x0a, 0x40, 0x84, 0x23,
9816
-	0xf3, 0x98, 0x27, 0x57, 0xb9, 0x60, 0xb7, 0x9e, 0x6a, 0xa2, 0x95, 0xf6, 0x54, 0x91, 0xce, 0x5a,
9817
-	0xd1, 0x3f, 0x4c, 0x92, 0xc2, 0x4c, 0xa7, 0xf6, 0x12, 0xc9, 0xd1, 0x36, 0x6d, 0x1e, 0x18, 0xdd,
9818
-	0x46, 0xe9, 0x42, 0x79, 0xfd, 0x83, 0x0f, 0xaf, 0xaf, 0x71, 0x6b, 0xb3, 0x06, 0xda, 0xbe, 0x7d,
9819
-	0x8a, 0xf1, 0x71, 0x93, 0x2c, 0x29, 0x68, 0xa2, 0xfc, 0x1c, 0x40, 0x9f, 0x5d, 0x84, 0xce, 0x20,
9820
-	0x69, 0x67, 0xdb, 0xa0, 0x10, 0x22, 0xc9, 0x78, 0x24, 0xed, 0x0c, 0x4c, 0x9f, 0xf5, 0xb5, 0xaf,
9821
-	0x90, 0xac, 0x04, 0xa6, 0xca, 0x65, 0x00, 0x5e, 0x59, 0x04, 0x4e, 0x71, 0xb4, 0xb3, 0x63, 0x1c,
9822
-	0x34, 0x4a, 0xe9, 0x78, 0x1c, 0xed, 0x38, 0xe6, 0x29, 0xd3, 0x5e, 0x80, 0x60, 0xe6, 0xb0, 0x4c,
9823
-	0xf9, 0x2a, 0xc0, 0x9e, 0x79, 0xcc, 0x1c, 0xa2, 0xca, 0x6b, 0x3f, 0xfd, 0xd5, 0xc6, 0x85, 0xdf,
9824
-	0xfd, 0x7a, 0xa3, 0xb4, 0xd8, 0x5d, 0xfe, 0x47, 0x82, 0x2c, 0xcf, 0xed, 0x12, 0x06, 0xd3, 0xc8,
9825
-	0x1b, 0x8d, 0x1d, 0x75, 0xee, 0x20, 0x98, 0x54, 0x5b, 0xbb, 0xbb, 0xc0, 0x16, 0xaf, 0x3f, 0xe5,
9826
-	0xd6, 0xc7, 0xf2, 0xc5, 0xdb, 0x64, 0xb9, 0x0f, 0xeb, 0xc7, 0xfc, 0x9e, 0xe5, 0xb9, 0x47, 0xf6,
9827
-	0xb1, 0xcc, 0xa3, 0xe5, 0x38, 0x9b, 0x75, 0x0e, 0xa4, 0x45, 0xa1, 0x50, 0xe3, 0xf8, 0x2f, 0xc3,
9828
-	0x14, 0xf7, 0x48, 0x1a, 0xcf, 0x9b, 0xf6, 0x1c, 0x49, 0x57, 0x9b, 0xad, 0x3a, 0x84, 0xc2, 0x45,
9829
-	0x58, 0xbd, 0x65, 0x3e, 0x75, 0xec, 0xc0, 0xd8, 0xd2, 0xae, 0x91, 0xec, 0xc1, 0xde, 0xce, 0xfe,
9830
-	0x2e, 0x6e, 0xff, 0x25, 0xe8, 0x5e, 0x8d, 0xba, 0x85, 0x73, 0xe5, 0x8b, 0x72, 0x59, 0xf3, 0x51,
9831
-	0x87, 0xfe, 0xaf, 0x24, 0x59, 0xa6, 0x58, 0x05, 0xfa, 0x61, 0xdb, 0x73, 0x6c, 0x6b, 0xa2, 0xb5,
9832
-	0x49, 0x1e, 0xfc, 0xeb, 0xdb, 0x33, 0x41, 0xbd, 0xf9, 0x04, 0xaa, 0x98, 0x6a, 0xa9, 0x56, 0x4d,
9833
-	0x69, 0xd2, 0xa9, 0x11, 0x48, 0x29, 0x99, 0x3e, 0x73, 0xcc, 0xc9, 0x79, 0x9c, 0x55, 0x97, 0x15,
9834
-	0x27, 0x15, 0x50, 0x5e, 0x5f, 0x99, 0xf7, 0x7b, 0x66, 0x18, 0xb2, 0xe1, 0x28, 0x14, 0x9c, 0x95,
9835
-	0x86, 0xfa, 0xca, 0xbc, 0x6f, 0x48, 0x91, 0xf6, 0x06, 0xc9, 0x9e, 0x81, 0xdb, 0xde, 0x99, 0xa4,
9836
-	0xa5, 0xf3, 0xed, 0x4a, 0xac, 0xfe, 0x01, 0xb2, 0xd1, 0xc2, 0x64, 0x71, 0x59, 0x5b, 0x7b, 0xad,
9837
-	0x86, 0x5a, 0x56, 0xd9, 0xbf, 0xe7, 0xb6, 0x3c, 0x17, 0x43, 0x96, 0xec, 0xb5, 0x7a, 0x5b, 0x46,
9838
-	0x73, 0x67, 0x9f, 0xe2, 0xd2, 0x5e, 0x06, 0x48, 0x29, 0x82, 0x6c, 0x99, 0xb6, 0x83, 0xa5, 0xd2,
9839
-	0x55, 0x92, 0x32, 0x5a, 0x90, 0x83, 0xcb, 0x25, 0xe8, 0x2e, 0x46, 0xdd, 0x86, 0x3b, 0x99, 0x46,
9840
-	0xf3, 0xe2, 0xb8, 0xfa, 0x7b, 0xa4, 0xb8, 0x3f, 0xea, 0x43, 0xa4, 0x8a, 0x08, 0xd1, 0xae, 0x43,
9841
-	0x4a, 0x31, 0x7d, 0xd3, 0x71, 0x98, 0x63, 0x07, 0x43, 0x59, 0x4d, 0xcf, 0x8a, 0xa0, 0x04, 0x78,
9842
-	0xfa, 0xb5, 0x94, 0x95, 0x8a, 0x50, 0xd0, 0x7f, 0x44, 0x56, 0x61, 0x94, 0xd0, 0x04, 0x4a, 0x56,
9843
-	0x24, 0xbc, 0x49, 0x8a, 0x96, 0x12, 0xf5, 0xec, 0xbe, 0x08, 0xc5, 0xea, 0x2a, 0x14, 0x7a, 0x85,
9844
-	0x08, 0xda, 0xac, 0xd3, 0x42, 0x04, 0x6a, 0xf6, 0xd1, 0xcf, 0x11, 0x40, 0x71, 0xf8, 0x4c, 0x75,
9845
-	0x09, 0xa0, 0xa9, 0x36, 0x40, 0x50, 0x06, 0xab, 0x98, 0x67, 0xf7, 0xed, 0x10, 0x8e, 0x47, 0x5f,
9846
-	0xd0, 0x6c, 0x86, 0xe6, 0x50, 0x50, 0x83, 0xb6, 0xfe, 0x7e, 0x92, 0x90, 0xae, 0x19, 0x9c, 0xc8,
9847
-	0xa1, 0xa1, 0x20, 0x89, 0xae, 0x1f, 0xe7, 0x95, 0xc1, 0x5d, 0x05, 0xa2, 0x53, 0xbc, 0xf6, 0xba,
9848
-	0xe2, 0x59, 0x51, 0x1d, 0xc4, 0x2b, 0xca, 0xb1, 0xe2, 0x08, 0x76, 0xbe, 0x04, 0xc0, 0x83, 0xc8,
9849
-	0x7c, 0x9f, 0x47, 0x11, 0x1c, 0x44, 0xf8, 0x84, 0x5b, 0x49, 0x3e, 0xf2, 0x59, 0x32, 0xd0, 0xf3,
9850
-	0x71, 0x83, 0x2c, 0x2c, 0xe8, 0xf6, 0x05, 0x3a, 0xd5, 0xab, 0x96, 0xc8, 0x8a, 0x0f, 0xc7, 0x0c,
9851
-	0x66, 0xdd, 0x0b, 0x78, 0xb7, 0xfe, 0x47, 0x58, 0x83, 0x66, 0xdb, 0xd8, 0x95, 0xbb, 0x5d, 0x27,
9852
-	0xd9, 0x23, 0x73, 0x68, 0x3b, 0x13, 0x79, 0xcc, 0x5e, 0x89, 0x1b, 0x62, 0x8a, 0xaf, 0x18, 0xfd,
9853
-	0x3e, 0x14, 0x65, 0xc1, 0x16, 0xd7, 0xa1, 0x52, 0x97, 0x93, 0xef, 0xf8, 0xd0, 0x05, 0x92, 0x55,
9854
-	0xe4, 0xcb, 0x5b, 0x98, 0x4c, 0x7c, 0xd3, 0x8d, 0xbc, 0x15, 0x0d, 0x5c, 0x05, 0xc8, 0xa4, 0xec,
9855
-	0x0c, 0x22, 0x48, 0xf8, 0xab, 0x9a, 0x40, 0xbc, 0x39, 0x71, 0x57, 0x60, 0x7d, 0x70, 0x19, 0xb3,
9856
-	0xe5, 0xe7, 0xcd, 0x87, 0x4a, 0xb8, 0x48, 0x93, 0x91, 0x76, 0xf9, 0x2d, 0x9e, 0x52, 0xa6, 0x5d,
9857
-	0x5f, 0x28, 0xd3, 0xbd, 0x46, 0x96, 0xe7, 0xfc, 0x7c, 0xac, 0xea, 0x69, 0xb6, 0x0f, 0xde, 0x28,
9858
-	0xa5, 0xe5, 0xd7, 0x37, 0x4a, 0x59, 0xfd, 0xef, 0x50, 0x84, 0xb5, 0x3d, 0x7e, 0xac, 0x70, 0x55,
9859
-	0xe3, 0x6f, 0x99, 0x39, 0x7e, 0x67, 0xb5, 0x3c, 0x47, 0xc6, 0x4c, 0x6c, 0x11, 0x30, 0xb5, 0x82,
9860
-	0x04, 0xcd, 0xe1, 0x34, 0x52, 0x84, 0xf4, 0x5a, 0x10, 0xf5, 0x4b, 0x6f, 0x04, 0x38, 0xbe, 0xac,
9861
-	0xcb, 0x94, 0x08, 0x11, 0x6a, 0xe2, 0x15, 0x66, 0x34, 0x3e, 0x84, 0x63, 0x3a, 0x60, 0x7d, 0x81,
9862
-	0x49, 0x73, 0xcc, 0x72, 0x24, 0x45, 0x98, 0x5e, 0x87, 0x4b, 0x98, 0xb2, 0xb9, 0x46, 0x52, 0xdd,
9863
-	0x5a, 0x1b, 0xf2, 0xce, 0x2a, 0x64, 0x8d, 0x82, 0x12, 0x83, 0x08, 0x7b, 0xf6, 0xeb, 0x6d, 0x48,
9864
-	0x37, 0x73, 0x3d, 0x20, 0x2a, 0xa7, 0x31, 0x9d, 0xe8, 0x3f, 0x4f, 0x90, 0xac, 0x60, 0x99, 0x58,
9865
-	0x8f, 0x0d, 0xb2, 0xa4, 0xaa, 0x1e, 0x41, 0x7d, 0x2f, 0x3e, 0x99, 0xa6, 0x2a, 0x92, 0xf5, 0xc4,
9866
-	0x3e, 0x2a, 0xbd, 0xf2, 0x9b, 0xa4, 0x38, 0xdb, 0xf1, 0x85, 0x76, 0xf1, 0x87, 0xa4, 0x80, 0x81,
9867
-	0xa2, 0x38, 0x7a, 0x93, 0x64, 0x05, 0x13, 0xca, 0xa3, 0x7e, 0x1e, 0x67, 0x4a, 0x24, 0x64, 0xba,
9868
-	0x25, 0xc1, 0xb3, 0xea, 0x7a, 0xb6, 0x71, 0x7e, 0x38, 0x52, 0x05, 0xd7, 0xdf, 0x26, 0xe9, 0x36,
9869
-	0x03, 0x0b, 0xcf, 0x93, 0x25, 0x17, 0x52, 0xcf, 0x34, 0xb3, 0x11, 0x48, 0x57, 0x59, 0x2c, 0xc0,
9870
-	0x21, 0x63, 0x65, 0xb1, 0x0b, 0xf2, 0x19, 0x2c, 0x9e, 0x09, 0xf1, 0xa6, 0x6e, 0xa8, 0xf8, 0xad,
9871
-	0x77, 0x49, 0xf1, 0x1e, 0xb3, 0x8f, 0x07, 0x21, 0xec, 0x18, 0x1a, 0x7a, 0x85, 0xa4, 0x47, 0x2c,
9872
-	0x9a, 0xfc, 0x5a, 0x6c, 0xe8, 0x40, 0x3f, 0xe5, 0x28, 0x3c, 0x90, 0x67, 0x5c, 0x5b, 0x3e, 0x0a,
9873
-	0xc8, 0x96, 0xfe, 0x9b, 0x24, 0x59, 0x69, 0x06, 0xc1, 0xd8, 0x84, 0x82, 0x5b, 0x66, 0xc1, 0x6f,
9874
-	0xcf, 0x5f, 0x18, 0x6e, 0xc6, 0x7a, 0x38, 0xa7, 0x32, 0x7f, 0x69, 0x90, 0x99, 0x2b, 0x19, 0x65,
9875
-	0x2e, 0xfd, 0x51, 0x42, 0xdd, 0x16, 0x6e, 0xcc, 0x9c, 0x9b, 0xf2, 0x1a, 0x04, 0xd1, 0xe5, 0x59,
9876
-	0x4b, 0x6c, 0xdf, 0x3d, 0x71, 0xbd, 0x33, 0x17, 0x88, 0x16, 0x6e, 0x0f, 0xad, 0xc6, 0x3d, 0x88,
9877
-	0xb4, 0x2b, 0x00, 0xd2, 0xe6, 0x40, 0x94, 0xb9, 0xec, 0x0c, 0x2d, 0xb5, 0x1b, 0xad, 0x7a, 0xb3,
9878
-	0x75, 0x07, 0xe8, 0xed, 0x71, 0x4b, 0x6d, 0x06, 0x74, 0xe6, 0x1e, 0xc3, 0x72, 0x67, 0x9b, 0x9d,
9879
-	0xce, 0x3e, 0x2f, 0x15, 0x9f, 0x05, 0xd4, 0xa5, 0x39, 0x14, 0x36, 0xa0, 0x4e, 0x04, 0x10, 0x32,
9880
-	0x29, 0x80, 0xd2, 0x31, 0x20, 0x24, 0x53, 0x48, 0x20, 0x22, 0xc2, 0xff, 0x9a, 0x24, 0x25, 0xc3,
9881
-	0xb2, 0xd8, 0x28, 0xc4, 0x7e, 0x59, 0x9d, 0x74, 0xb1, 0xda, 0x83, 0x2f, 0x9b, 0xe1, 0xeb, 0x09,
9882
-	0x86, 0xc5, 0xed, 0xd8, 0x17, 0xa3, 0x05, 0xbd, 0x0a, 0xf5, 0x1c, 0x66, 0xf4, 0x87, 0x76, 0x80,
9883
-	0xaf, 0x08, 0x42, 0x46, 0x23, 0x4b, 0xe5, 0x7f, 0x27, 0xc8, 0xa5, 0x18, 0x84, 0xf6, 0x1a, 0x49,
9884
-	0xfb, 0x20, 0x96, 0xdb, 0xb3, 0xfe, 0xa4, 0xfb, 0x1c, 0xaa, 0x52, 0x8e, 0xd4, 0x36, 0x08, 0x31,
9885
-	0xc7, 0xa1, 0x67, 0xf2, 0xf1, 0xf9, 0xc6, 0xe4, 0xe8, 0x8c, 0x44, 0xfb, 0x1e, 0x64, 0x6b, 0x66,
9886
-	0xf9, 0xf2, 0x4a, 0x54, 0xd8, 0x6c, 0xfc, 0xb7, 0xb3, 0xaf, 0x6c, 0x9b, 0x98, 0x51, 0x3a, 0xdc,
9887
-	0x18, 0x95, 0x46, 0xcb, 0x6f, 0x90, 0xe2, 0xac, 0x1c, 0xa3, 0x1b, 0xca, 0x0b, 0x93, 0x3b, 0x50,
9888
-	0xa4, 0xfc, 0x1b, 0x83, 0xc6, 0x74, 0x8e, 0x55, 0xd0, 0xc0, 0xa7, 0x4e, 0x49, 0xae, 0x66, 0xc8,
9889
-	0xf4, 0xb9, 0x45, 0x4a, 0xfc, 0xd0, 0x58, 0xcc, 0x0f, 0x7b, 0xec, 0xfe, 0xc8, 0xf6, 0x27, 0x32,
9890
-	0xee, 0xcf, 0xaf, 0xaf, 0x56, 0x50, 0xab, 0x06, 0x4a, 0x0d, 0xae, 0xa3, 0x1f, 0x90, 0x4b, 0x7b,
9891
-	0xbe, 0x35, 0x00, 0xc6, 0x16, 0x00, 0x69, 0xfe, 0x6d, 0xb2, 0x1e, 0x02, 0x33, 0xf7, 0x06, 0x76,
9892
-	0x10, 0xe2, 0xeb, 0x19, 0x4c, 0x92, 0xb9, 0xd8, 0xdf, 0xe3, 0xaf, 0x5c, 0xe2, 0xd5, 0x8d, 0x5e,
9893
-	0x45, 0xcc, 0xb6, 0x80, 0x50, 0x85, 0xd8, 0x41, 0x80, 0xfe, 0x5d, 0x52, 0xaa, 0xdb, 0xc1, 0xc8,
9894
-	0x0c, 0xc1, 0xb6, 0x2c, 0xac, 0xb5, 0x3b, 0xa4, 0x34, 0x60, 0x50, 0x58, 0x1d, 0x32, 0x13, 0x92,
9895
-	0x33, 0xf3, 0x6d, 0xaf, 0xff, 0x54, 0x73, 0x5e, 0x8d, 0xb4, 0xda, 0x5c, 0x49, 0xff, 0x0c, 0xa8,
9896
-	0x04, 0x9f, 0x15, 0xa4, 0xdd, 0x97, 0xc9, 0xc5, 0xc0, 0x35, 0x47, 0xc1, 0xc0, 0x0b, 0x7b, 0xb6,
9897
-	0x1b, 0xe2, 0x3b, 0x9a, 0x23, 0x8b, 0xb2, 0x92, 0xea, 0x68, 0x4a, 0x39, 0x24, 0x09, 0xed, 0x84,
9898
-	0xb1, 0x51, 0xcf, 0x73, 0xfa, 0x3d, 0xd5, 0x29, 0xde, 0xce, 0x00, 0x8d, 0x3d, 0x7b, 0x4e, 0xbf,
9899
-	0xa3, 0xe4, 0x5a, 0x95, 0x6c, 0x38, 0xde, 0x71, 0x0f, 0x3c, 0xf3, 0x21, 0x00, 0x7b, 0x47, 0x9e,
9900
-	0xdf, 0x0b, 0x1c, 0xef, 0x0c, 0x3e, 0x1c, 0xf8, 0x63, 0xbe, 0xaa, 0x78, 0xcb, 0x80, 0x6a, 0x08,
9901
-	0xd0, 0x96, 0xe7, 0x77, 0xa0, 0x6f, 0x4b, 0x21, 0x90, 0x6f, 0xa6, 0x6e, 0x87, 0xb6, 0x75, 0xa2,
9902
-	0xf8, 0x26, 0x92, 0x76, 0x41, 0x08, 0x47, 0x6e, 0x99, 0x39, 0xcc, 0xe2, 0x8b, 0xcc, 0x51, 0x19,
9903
-	0x8e, 0x2a, 0x2a, 0x21, 0x82, 0xf4, 0xaf, 0x91, 0x7c, 0xdb, 0x31, 0x2d, 0xfe, 0x42, 0x89, 0x65,
9904
-	0x28, 0xe4, 0x52, 0xdc, 0x39, 0xf0, 0x5a, 0x9c, 0xb3, 0x3c, 0x9d, 0x15, 0xe9, 0xef, 0x03, 0xfb,
9905
-	0x50, 0xcf, 0x0b, 0x6b, 0x06, 0x80, 0xb3, 0x96, 0xd9, 0x53, 0xcc, 0x50, 0xac, 0xe6, 0x21, 0xc9,
9906
-	0x66, 0x6a, 0xc6, 0x5d, 0x36, 0xa1, 0x19, 0xcb, 0x84, 0x3f, 0xcc, 0xc3, 0x80, 0xc0, 0x80, 0xe2,
9907
-	0xcb, 0x51, 0x14, 0x79, 0x18, 0x22, 0x0e, 0x24, 0x14, 0x94, 0xf1, 0x1f, 0x8e, 0x5a, 0x51, 0x82,
9908
-	0x7a, 0x03, 0x88, 0x60, 0x51, 0xb5, 0x54, 0x57, 0x00, 0x49, 0x04, 0x12, 0xe3, 0x9a, 0x12, 0x81,
9909
-	0xc6, 0x6f, 0xfd, 0x4f, 0x09, 0x52, 0xc0, 0x86, 0x7d, 0x64, 0x5b, 0x98, 0xf0, 0xbe, 0xf8, 0x61,
9910
-	0x85, 0x5a, 0xd6, 0x0a, 0x7c, 0x39, 0x29, 0x5e, 0xcb, 0xd6, 0x3a, 0x94, 0xa2, 0x4c, 0x7b, 0x07,
9911
-	0xce, 0x29, 0x4f, 0xb8, 0xf2, 0x9c, 0xea, 0x9f, 0x9f, 0x9a, 0x65, 0xb9, 0x2d, 0xf5, 0xf8, 0x22,
9912
-	0x4e, 0x67, 0xc7, 0xb7, 0xa6, 0x48, 0x67, 0x45, 0xf8, 0xba, 0x6a, 0xb9, 0x7c, 0x37, 0xe4, 0xeb,
9913
-	0x6a, 0xad, 0x45, 0x41, 0xa2, 0xff, 0x01, 0xee, 0xb8, 0x0d, 0xd7, 0xf2, 0x27, 0x9c, 0x3d, 0x71,
9914
-	0x05, 0xd7, 0x49, 0x1e, 0xaa, 0xba, 0x60, 0x12, 0xc0, 0xd5, 0x47, 0x3d, 0xde, 0x44, 0x02, 0xad,
9915
-	0x49, 0xf2, 0x70, 0x8a, 0x3d, 0xdf, 0x0e, 0x07, 0x43, 0x59, 0xde, 0xbc, 0x1c, 0xff, 0x44, 0x37,
9916
-	0x63, 0xb3, 0x62, 0x28, 0x15, 0x3a, 0xd5, 0x56, 0x1c, 0x9f, 0xe2, 0x93, 0xe5, 0x1c, 0x0f, 0x17,
9917
-	0x31, 0x07, 0x6a, 0x6e, 0x28, 0x5c, 0x7a, 0x58, 0xca, 0x72, 0x3f, 0xe0, 0x4e, 0x22, 0x65, 0x58,
9918
-	0x9e, 0xeb, 0x3a, 0xc9, 0x47, 0xc6, 0xf0, 0xc9, 0xcc, 0x68, 0x74, 0x7a, 0xb7, 0x36, 0x6f, 0xf7,
9919
-	0xee, 0xd4, 0x76, 0x81, 0x7a, 0x44, 0x32, 0xff, 0x2d, 0xf8, 0xb4, 0x6b, 0xba, 0x50, 0x6e, 0xab,
9920
-	0xcb, 0x07, 0x44, 0x85, 0x0f, 0x47, 0x4d, 0xb1, 0x73, 0x5a, 0x44, 0x05, 0x9e, 0x3e, 0x64, 0x67,
9921
-	0xec, 0x8a, 0x67, 0xe7, 0x99, 0xa7, 0xc3, 0xd4, 0xb9, 0x4f, 0x87, 0xe9, 0xff, 0xc9, 0xd3, 0xe1,
9922
-	0x4b, 0x9f, 0xa5, 0x48, 0x3e, 0xba, 0x4c, 0x60, 0xc8, 0x20, 0x59, 0x5e, 0x10, 0xd7, 0xbc, 0x48,
9923
-	0xde, 0xe2, 0x34, 0x99, 0x37, 0x76, 0x76, 0xf6, 0x6a, 0x06, 0xbe, 0xbc, 0xbd, 0x23, 0xd8, 0x34,
9924
-	0x02, 0x18, 0x70, 0x68, 0x71, 0xd3, 0xfb, 0x9a, 0x3e, 0x65, 0xd3, 0x07, 0xf2, 0x32, 0x19, 0xa1,
9925
-	0x14, 0x95, 0xbe, 0x40, 0x72, 0x46, 0xa7, 0xd3, 0xbc, 0xd3, 0x02, 0x4b, 0x0f, 0x13, 0xe5, 0x67,
9926
-	0x00, 0x74, 0x71, 0x6a, 0x0a, 0x58, 0xe0, 0xd8, 0x05, 0x4b, 0x88, 0xaa, 0xd5, 0x1a, 0x6d, 0x1c,
9927
-	0xef, 0x41, 0x72, 0x11, 0xc5, 0x39, 0x84, 0xbf, 0xcc, 0xe4, 0xdb, 0xb4, 0xd1, 0x36, 0x28, 0x8e,
9928
-	0xf8, 0x30, 0xb9, 0x30, 0xaf, 0xb6, 0xcf, 0xe0, 0x7a, 0x89, 0x63, 0x6e, 0xa8, 0x67, 0xc4, 0x07,
9929
-	0xa9, 0xb2, 0x06, 0x98, 0x95, 0xe9, 0x0d, 0x0a, 0xd6, 0x77, 0x82, 0xa3, 0x75, 0xba, 0x06, 0xed,
9930
-	0x72, 0x33, 0xa9, 0x85, 0xd1, 0x3a, 0x78, 0xaf, 0x45, 0x2b, 0xe0, 0x1d, 0xdd, 0x6f, 0xb5, 0xb8,
9931
-	0x77, 0xe9, 0x05, 0xef, 0xe8, 0xd8, 0x75, 0x11, 0x73, 0x03, 0xe8, 0x66, 0x6f, 0xb7, 0xbd, 0xd3,
9932
-	0xe8, 0x36, 0x4a, 0x0f, 0xd3, 0x0b, 0x13, 0xaa, 0x79, 0xc3, 0x91, 0xc3, 0x42, 0xe1, 0x5e, 0x67,
9933
-	0x7b, 0xbf, 0xcb, 0x5f, 0x39, 0x1f, 0x64, 0x16, 0x07, 0x1c, 0x8c, 0xc3, 0x3e, 0xd6, 0x2f, 0xd7,
9934
-	0xa3, 0x82, 0xe2, 0x61, 0x46, 0xbc, 0x7a, 0x44, 0x18, 0x51, 0x4d, 0xa0, 0x1d, 0xda, 0xf8, 0x8e,
9935
-	0x78, 0x10, 0x7d, 0x90, 0x5d, 0xb0, 0x43, 0xd9, 0x7b, 0x90, 0x05, 0xa1, 0xe6, 0x88, 0xde, 0x46,
9936
-	0xa2, 0xae, 0x97, 0xbe, 0x4f, 0x72, 0x2a, 0x61, 0xc0, 0xea, 0x64, 0xef, 0xed, 0xd1, 0xbb, 0x0d,
9937
-	0x0a, 0x5b, 0xcf, 0x57, 0x47, 0xf5, 0xdc, 0xf3, 0x7c, 0x88, 0x2e, 0x98, 0xc6, 0xd2, 0xae, 0xd1,
9938
-	0x32, 0xee, 0x00, 0x40, 0x3e, 0xbe, 0x28, 0x80, 0x8c, 0xfa, 0x72, 0x49, 0x0e, 0x10, 0xd9, 0xac,
9939
-	0xae, 0x3f, 0xfa, 0x64, 0xe3, 0xc2, 0x47, 0xf0, 0xfb, 0xdb, 0x27, 0x1b, 0x89, 0x07, 0x9f, 0x6e,
9940
-	0x24, 0x1e, 0xc1, 0xef, 0xf7, 0xf0, 0xfb, 0x33, 0xfc, 0x0e, 0xb3, 0xfc, 0xe2, 0xf1, 0xfa, 0x7f,
9941
-	0x02, 0x00, 0x00, 0xff, 0xff, 0x35, 0xa0, 0x81, 0xdd, 0xd2, 0x1b, 0x00, 0x00,
9760
+	0xf5, 0x17, 0x3f, 0x45, 0x0e, 0x29, 0x89, 0x5e, 0x3b, 0x8e, 0xcc, 0xe8, 0x2f, 0xfb, 0xbf, 0x89,
9761
+	0x1b, 0xe7, 0xa3, 0x4c, 0xac, 0xa4, 0x85, 0x9b, 0xa0, 0x4d, 0x96, 0x1f, 0xb2, 0x58, 0x4b, 0x14,
9762
+	0x31, 0x94, 0x64, 0x04, 0x45, 0x4b, 0xac, 0x96, 0x23, 0x71, 0xa3, 0xe5, 0x2e, 0xbb, 0xbb, 0x94,
9763
+	0x4c, 0x14, 0x05, 0x9c, 0x5e, 0x5a, 0xe4, 0xd4, 0x7b, 0x11, 0x04, 0x45, 0x8b, 0xde, 0x7a, 0x2e,
9764
+	0xd0, 0x93, 0x8f, 0x3e, 0xb6, 0x28, 0x50, 0xe4, 0x14, 0x34, 0xe9, 0xa1, 0xd7, 0x02, 0x2d, 0x9a,
9765
+	0x43, 0x7b, 0xe8, 0x7b, 0xf3, 0xb1, 0xfc, 0xf0, 0x5a, 0x71, 0x9a, 0x1c, 0x08, 0xee, 0xbc, 0xf9,
9766
+	0xbd, 0x37, 0xf3, 0xde, 0xbc, 0x79, 0x1f, 0x43, 0x0a, 0xe1, 0x78, 0xc8, 0x82, 0xca, 0xd0, 0xf7,
9767
+	0x42, 0x4f, 0xd3, 0x7a, 0x9e, 0x75, 0xc2, 0xfc, 0x4a, 0x70, 0x66, 0xfa, 0x83, 0x13, 0x3b, 0xac,
9768
+	0x9c, 0xde, 0x2c, 0x5f, 0x09, 0xed, 0x01, 0x0b, 0x42, 0x73, 0x30, 0x7c, 0x25, 0xfa, 0x12, 0xf0,
9769
+	0xf2, 0xd3, 0xbd, 0x91, 0x6f, 0x86, 0xb6, 0xe7, 0xbe, 0xa2, 0x3e, 0xe4, 0xc4, 0xa5, 0x63, 0xef,
9770
+	0xd8, 0xe3, 0x9f, 0xaf, 0xe0, 0x97, 0xa0, 0xea, 0x57, 0xc9, 0xe2, 0x01, 0xf3, 0x03, 0x80, 0x69,
9771
+	0x97, 0x48, 0xc6, 0x76, 0x7b, 0xec, 0xde, 0x6a, 0xe2, 0x5a, 0xe2, 0x46, 0x9a, 0x8a, 0x81, 0xfe,
9772
+	0xcb, 0x04, 0x29, 0x18, 0xae, 0xeb, 0x85, 0x5c, 0x56, 0xa0, 0x69, 0x24, 0xed, 0x9a, 0x03, 0xc6,
9773
+	0x41, 0x79, 0xca, 0xbf, 0xb5, 0x1a, 0xc9, 0x3a, 0xe6, 0x21, 0x73, 0x82, 0xd5, 0xe4, 0xb5, 0xd4,
9774
+	0x8d, 0xc2, 0xc6, 0x4b, 0x95, 0x47, 0xf7, 0x5c, 0x99, 0x12, 0x52, 0xd9, 0xe6, 0xe8, 0x86, 0x1b,
9775
+	0xfa, 0x63, 0x2a, 0x59, 0xcb, 0xdf, 0x22, 0x85, 0x29, 0xb2, 0x56, 0x22, 0xa9, 0x13, 0x36, 0x96,
9776
+	0xcb, 0xe0, 0x27, 0xee, 0xef, 0xd4, 0x74, 0x46, 0x0c, 0x16, 0x41, 0x9a, 0x18, 0xbc, 0x91, 0xbc,
9777
+	0x95, 0xd0, 0xdf, 0x21, 0x79, 0xca, 0x02, 0x6f, 0xe4, 0x5b, 0x2c, 0xd0, 0x5e, 0x20, 0x79, 0xd7,
9778
+	0x74, 0xbd, 0xae, 0x35, 0x1c, 0x05, 0x9c, 0x3d, 0x55, 0x2d, 0x7e, 0xfa, 0xf1, 0xd5, 0x5c, 0x0b,
9779
+	0x88, 0xb5, 0xf6, 0x7e, 0x40, 0x73, 0x38, 0x5d, 0x83, 0x59, 0xed, 0xff, 0x49, 0x71, 0xc0, 0x06,
9780
+	0x9e, 0x3f, 0xee, 0x1e, 0x8e, 0x43, 0x16, 0x70, 0xc1, 0x29, 0x5a, 0x10, 0xb4, 0x2a, 0x92, 0xf4,
9781
+	0x9f, 0x27, 0xc8, 0x25, 0x25, 0x9b, 0xb2, 0x1f, 0x8e, 0x6c, 0x9f, 0x0d, 0x98, 0x1b, 0x06, 0xda,
9782
+	0x37, 0x40, 0x67, 0x7b, 0x60, 0x87, 0x62, 0x8d, 0xc2, 0xc6, 0xff, 0xc5, 0xe9, 0x1c, 0xed, 0x8a,
9783
+	0x4a, 0xb0, 0x66, 0x90, 0xa2, 0xcf, 0x02, 0xe6, 0x9f, 0x0a, 0x4b, 0xf0, 0x25, 0x3f, 0x97, 0x79,
9784
+	0x86, 0x45, 0xdf, 0x24, 0xb9, 0xb6, 0x63, 0x86, 0x47, 0x9e, 0x3f, 0xd0, 0x74, 0x52, 0x34, 0x7d,
9785
+	0xab, 0x6f, 0x87, 0xcc, 0x0a, 0x47, 0xbe, 0x3a, 0x95, 0x19, 0x9a, 0x76, 0x99, 0x24, 0x3d, 0xb1,
9786
+	0x50, 0xbe, 0x9a, 0x05, 0x4b, 0x24, 0x77, 0x3b, 0x14, 0x28, 0xfa, 0x9b, 0xe4, 0x42, 0xdb, 0x19,
9787
+	0x1d, 0xdb, 0x6e, 0x9d, 0x05, 0x96, 0x6f, 0x0f, 0x51, 0x3a, 0x1e, 0x2f, 0x3a, 0x9f, 0x3a, 0x5e,
9788
+	0xfc, 0x8e, 0x8e, 0x3c, 0x39, 0x39, 0x72, 0xfd, 0xa7, 0x49, 0x72, 0xa1, 0xe1, 0x02, 0x33, 0x9b,
9789
+	0xe6, 0xbe, 0x4e, 0x96, 0x19, 0x27, 0x76, 0x4f, 0x85, 0x53, 0x49, 0x39, 0x4b, 0x82, 0xaa, 0x3c,
9790
+	0xad, 0x39, 0xe7, 0x2f, 0x37, 0xe3, 0xd4, 0x7f, 0x44, 0x7a, 0x9c, 0xd7, 0x68, 0x0d, 0xb2, 0x38,
9791
+	0xe4, 0x4a, 0x04, 0xab, 0x29, 0x2e, 0xeb, 0x7a, 0x9c, 0xac, 0x47, 0xf4, 0xac, 0xa6, 0x1f, 0x7e,
9792
+	0x7c, 0x75, 0x81, 0x2a, 0xde, 0x2f, 0xe3, 0x7c, 0x7f, 0x4d, 0x90, 0x95, 0x96, 0xd7, 0x9b, 0xb1,
9793
+	0x43, 0x99, 0xe4, 0xfa, 0x5e, 0x10, 0x4e, 0x5d, 0x94, 0x68, 0xac, 0xdd, 0x22, 0xb9, 0xa1, 0x3c,
9794
+	0x3e, 0x79, 0xfa, 0x6b, 0xf1, 0x5b, 0x16, 0x18, 0x1a, 0xa1, 0xb5, 0x37, 0x49, 0xde, 0x57, 0x3e,
9795
+	0x01, 0xda, 0x3e, 0x81, 0xe3, 0x4c, 0xf0, 0xda, 0xb7, 0x49, 0x56, 0x1c, 0xc2, 0x6a, 0x9a, 0x73,
9796
+	0x5e, 0x7f, 0x22, 0x9b, 0x53, 0xc9, 0xa4, 0x7f, 0x94, 0x20, 0x25, 0x6a, 0x1e, 0x85, 0x3b, 0x6c,
9797
+	0x70, 0xc8, 0xfc, 0x0e, 0x5c, 0x64, 0xb8, 0x3f, 0x97, 0xe1, 0x1c, 0x99, 0xd9, 0x63, 0x3e, 0x57,
9798
+	0x32, 0x47, 0xe5, 0x48, 0xdb, 0x47, 0x27, 0x37, 0xad, 0xbe, 0x79, 0x68, 0x3b, 0x76, 0x38, 0xe6,
9799
+	0x6a, 0x2e, 0xc7, 0x9f, 0xf2, 0xbc, 0x4c, 0xd8, 0xfc, 0x84, 0x91, 0xce, 0x88, 0xd1, 0x56, 0xc9,
9800
+	0x22, 0xc4, 0xba, 0xc0, 0x3c, 0x66, 0x5c, 0xfb, 0x3c, 0x55, 0x43, 0x70, 0xe5, 0xe2, 0x34, 0x9f,
9801
+	0x56, 0x20, 0x8b, 0xfb, 0xad, 0x3b, 0xad, 0xdd, 0xbb, 0xad, 0xd2, 0x82, 0xb6, 0x42, 0x0a, 0xfb,
9802
+	0x2d, 0xda, 0x30, 0x6a, 0x5b, 0x46, 0x75, 0xbb, 0x51, 0x4a, 0x68, 0x4b, 0x10, 0x2e, 0xa2, 0x61,
9803
+	0x52, 0xff, 0x30, 0x41, 0x08, 0x1e, 0xa0, 0x54, 0xea, 0x0d, 0x92, 0x81, 0x78, 0x1a, 0x8a, 0x83,
9804
+	0x5b, 0xde, 0x78, 0x2e, 0x6e, 0xd7, 0x13, 0x78, 0x05, 0xff, 0x18, 0x15, 0x2c, 0xd3, 0x3b, 0x4c,
9805
+	0xce, 0xef, 0x30, 0xc3, 0x91, 0xb3, 0x5b, 0xcb, 0x91, 0x74, 0x1d, 0xbf, 0x12, 0x5a, 0x9e, 0x64,
9806
+	0x60, 0x4f, 0xf5, 0x77, 0x4a, 0x49, 0x70, 0xbe, 0x62, 0xbd, 0xd9, 0xa9, 0xed, 0xb6, 0x5a, 0x8d,
9807
+	0xda, 0x5e, 0xa3, 0x5e, 0x4a, 0xe9, 0xd7, 0x49, 0xa6, 0x39, 0x00, 0x29, 0xda, 0x1a, 0x7a, 0xc0,
9808
+	0x11, 0xf3, 0x99, 0x6b, 0x29, 0xc7, 0x9a, 0x10, 0xf4, 0x7f, 0x2d, 0x92, 0xcc, 0x8e, 0x37, 0x72,
9809
+	0x43, 0x6d, 0x63, 0xea, 0x16, 0x2f, 0x6f, 0xac, 0xc7, 0xa9, 0xc0, 0x81, 0x95, 0x3d, 0x40, 0xc9,
9810
+	0x5b, 0x0e, 0x87, 0x29, 0x7c, 0x45, 0x6e, 0x5d, 0x8e, 0x90, 0x1e, 0x9a, 0xfe, 0x31, 0x0b, 0xa5,
9811
+	0xd1, 0xe5, 0x08, 0x7d, 0xfc, 0xcc, 0xb7, 0x43, 0xf3, 0xd0, 0x11, 0x2e, 0x95, 0xa3, 0xd1, 0x58,
9812
+	0xdb, 0x22, 0xc5, 0x43, 0x48, 0x1f, 0x5d, 0x6f, 0x28, 0xa2, 0x5c, 0xe6, 0xf1, 0x2e, 0x27, 0xf6,
9813
+	0x51, 0x05, 0xf4, 0xae, 0x00, 0xd3, 0xc2, 0xe1, 0x64, 0xa0, 0xb5, 0xc8, 0xf2, 0xa9, 0xe7, 0x8c,
9814
+	0x06, 0x2c, 0x92, 0x95, 0xe5, 0xb2, 0x9e, 0x7f, 0xbc, 0xac, 0x03, 0x8e, 0x57, 0xd2, 0x96, 0x4e,
9815
+	0xa7, 0x87, 0xe5, 0x9f, 0xa4, 0x48, 0x61, 0x6a, 0x31, 0xad, 0x43, 0x0a, 0x90, 0x08, 0x87, 0xe6,
9816
+	0x31, 0x0f, 0xae, 0xd2, 0x60, 0x37, 0x9f, 0x68, 0xa3, 0x95, 0xf6, 0x84, 0x91, 0x4e, 0x4b, 0xd1,
9817
+	0x3f, 0x48, 0x92, 0xc2, 0xd4, 0xa4, 0xf6, 0x22, 0xc9, 0xd1, 0x36, 0x6d, 0x1e, 0x18, 0x7b, 0x8d,
9818
+	0xd2, 0x42, 0x79, 0xed, 0xfd, 0x0f, 0xae, 0xad, 0x72, 0x69, 0xd3, 0x02, 0xda, 0xbe, 0x7d, 0x8a,
9819
+	0xfe, 0x71, 0x83, 0x2c, 0x2a, 0x68, 0xa2, 0xfc, 0x0c, 0x40, 0x9f, 0x9e, 0x87, 0x4e, 0x21, 0x69,
9820
+	0x67, 0xcb, 0xa0, 0xe0, 0x22, 0xc9, 0x78, 0x24, 0xed, 0xf4, 0x4d, 0x9f, 0xf5, 0xb4, 0xaf, 0x91,
9821
+	0xac, 0x04, 0xa6, 0xca, 0x65, 0x00, 0x5e, 0x9e, 0x07, 0x4e, 0x70, 0xb4, 0xb3, 0x6d, 0x1c, 0x34,
9822
+	0x4a, 0xe9, 0x78, 0x1c, 0xed, 0x38, 0xe6, 0x29, 0xd3, 0x9e, 0x03, 0x67, 0xe6, 0xb0, 0x4c, 0xf9,
9823
+	0x0a, 0xc0, 0x9e, 0x7a, 0x44, 0x1c, 0xa2, 0xca, 0xab, 0x3f, 0xfb, 0xd5, 0xfa, 0xc2, 0xef, 0x7f,
9824
+	0xbd, 0x5e, 0x9a, 0x9f, 0x2e, 0xff, 0x33, 0x41, 0x96, 0x66, 0x4e, 0x09, 0x9d, 0x69, 0xe8, 0x0d,
9825
+	0x47, 0x8e, 0xba, 0x77, 0xe0, 0x4c, 0x6a, 0xac, 0xdd, 0x99, 0xcb, 0x16, 0xaf, 0x3d, 0xe1, 0xd1,
9826
+	0xc7, 0xe6, 0x8b, 0xb7, 0xc8, 0x52, 0x0f, 0xec, 0xc7, 0xfc, 0xae, 0xe5, 0xb9, 0x47, 0xf6, 0xb1,
9827
+	0x8c, 0xa3, 0xe5, 0x38, 0x99, 0x75, 0x0e, 0xa4, 0x45, 0xc1, 0x50, 0xe3, 0xf8, 0x2f, 0x93, 0x29,
9828
+	0xee, 0x92, 0x34, 0xde, 0x37, 0xed, 0x19, 0x92, 0xae, 0x36, 0x5b, 0x75, 0x70, 0x85, 0x0b, 0x60,
9829
+	0xbd, 0x25, 0xbe, 0x75, 0x9c, 0x40, 0xdf, 0xd2, 0xae, 0x92, 0xec, 0xc1, 0xee, 0xf6, 0xfe, 0x0e,
9830
+	0x1e, 0xff, 0x45, 0x98, 0x5e, 0x89, 0xa6, 0x85, 0x72, 0xe5, 0x0b, 0xd2, 0xac, 0xf9, 0x68, 0x42,
9831
+	0xff, 0x77, 0x92, 0x2c, 0x51, 0xac, 0x02, 0xfd, 0xb0, 0xed, 0x39, 0xb6, 0x35, 0xd6, 0xda, 0x24,
9832
+	0x0f, 0xfa, 0xf5, 0xec, 0x29, 0xa7, 0xde, 0x78, 0x4c, 0xaa, 0x98, 0x70, 0xa9, 0x51, 0x4d, 0x71,
9833
+	0xd2, 0x89, 0x10, 0x08, 0x29, 0x99, 0x1e, 0x73, 0xcc, 0xf1, 0x79, 0x39, 0xab, 0x2e, 0x2b, 0x4e,
9834
+	0x2a, 0xa0, 0xbc, 0xbe, 0x32, 0xef, 0x75, 0xcd, 0x30, 0x64, 0x83, 0x61, 0x28, 0x72, 0x56, 0x1a,
9835
+	0xea, 0x2b, 0xf3, 0x9e, 0x21, 0x49, 0xda, 0xeb, 0x24, 0x7b, 0x06, 0x6a, 0x7b, 0x67, 0x32, 0x2d,
9836
+	0x9d, 0x2f, 0x57, 0x62, 0xf5, 0xf7, 0x31, 0x1b, 0xcd, 0x6d, 0x16, 0xcd, 0xda, 0xda, 0x6d, 0x35,
9837
+	0x94, 0x59, 0xe5, 0xfc, 0xae, 0xdb, 0xf2, 0x5c, 0x74, 0x59, 0xb2, 0xdb, 0xea, 0x6e, 0x1a, 0xcd,
9838
+	0xed, 0x7d, 0x8a, 0xa6, 0xbd, 0x04, 0x90, 0x52, 0x04, 0xd9, 0x34, 0x6d, 0x07, 0x4b, 0xa5, 0x2b,
9839
+	0x24, 0x65, 0xb4, 0x20, 0x06, 0x97, 0x4b, 0x30, 0x5d, 0x8c, 0xa6, 0x0d, 0x77, 0x3c, 0xf1, 0xe6,
9840
+	0xf9, 0x75, 0xf5, 0x77, 0x49, 0x71, 0x7f, 0xd8, 0x03, 0x4f, 0x15, 0x1e, 0xa2, 0x5d, 0x83, 0x90,
9841
+	0x62, 0xfa, 0xa6, 0xe3, 0x30, 0xc7, 0x0e, 0x06, 0xb2, 0x9a, 0x9e, 0x26, 0x41, 0x09, 0xf0, 0xe4,
9842
+	0xb6, 0x94, 0x95, 0x8a, 0x60, 0xd0, 0x7f, 0x4c, 0x56, 0x60, 0x95, 0xd0, 0x84, 0x94, 0xac, 0x92,
9843
+	0xf0, 0x06, 0x29, 0x5a, 0x8a, 0xd4, 0xb5, 0x7b, 0xc2, 0x15, 0xab, 0x2b, 0x50, 0xe8, 0x15, 0x22,
9844
+	0x68, 0xb3, 0x4e, 0x0b, 0x11, 0xa8, 0xd9, 0x43, 0x3d, 0x87, 0x00, 0xc5, 0xe5, 0x33, 0xd5, 0x45,
9845
+	0x80, 0xa6, 0xda, 0x00, 0x41, 0x1a, 0x58, 0x31, 0xcf, 0xee, 0xd9, 0x21, 0x5c, 0x8f, 0x9e, 0x48,
9846
+	0xb3, 0x19, 0x9a, 0x43, 0x42, 0x0d, 0xc6, 0xfa, 0x7b, 0x49, 0x42, 0xf6, 0xcc, 0xe0, 0x44, 0x2e,
9847
+	0x0d, 0x05, 0x49, 0xd4, 0x7e, 0x9c, 0x57, 0x06, 0xef, 0x29, 0x10, 0x9d, 0xe0, 0xb5, 0xd7, 0x54,
9848
+	0x9e, 0x15, 0xd5, 0x41, 0x3c, 0xa3, 0x5c, 0x2b, 0x2e, 0xc1, 0xce, 0x96, 0x00, 0x78, 0x11, 0x99,
9849
+	0xef, 0x73, 0x2f, 0x82, 0x8b, 0x08, 0x9f, 0xd0, 0x95, 0xe4, 0x23, 0x9d, 0x65, 0x06, 0x7a, 0x36,
9850
+	0x6e, 0x91, 0x39, 0x83, 0x6e, 0x2d, 0xd0, 0x09, 0x5f, 0xb5, 0x44, 0x96, 0x7d, 0xb8, 0x66, 0xb0,
9851
+	0xeb, 0x6e, 0xc0, 0xa7, 0xf5, 0x3f, 0x81, 0x0d, 0x9a, 0x6d, 0x63, 0x47, 0x9e, 0x76, 0x9d, 0x64,
9852
+	0x8f, 0xcc, 0x81, 0xed, 0x8c, 0xe5, 0x35, 0x7b, 0x39, 0x6e, 0x89, 0x09, 0xbe, 0x62, 0xf4, 0x7a,
9853
+	0x50, 0x94, 0x05, 0x9b, 0x9c, 0x87, 0x4a, 0x5e, 0x9e, 0x7c, 0x47, 0x87, 0x2e, 0x24, 0x59, 0x95,
9854
+	0x7c, 0xf9, 0x08, 0x83, 0x89, 0x6f, 0xba, 0x91, 0xb6, 0x62, 0x80, 0x56, 0x80, 0x48, 0xca, 0xce,
9855
+	0xc0, 0x83, 0x84, 0xbe, 0x6a, 0x08, 0x89, 0x37, 0x27, 0x7a, 0x05, 0xd6, 0x03, 0x95, 0x31, 0x5a,
9856
+	0x7e, 0xde, 0x7e, 0xa8, 0x84, 0x8b, 0x30, 0x19, 0x71, 0x97, 0xdf, 0xe4, 0x21, 0x65, 0x32, 0xf5,
9857
+	0x85, 0x22, 0xdd, 0xab, 0x64, 0x69, 0x46, 0xcf, 0x47, 0xaa, 0x9e, 0x66, 0xfb, 0xe0, 0xf5, 0x52,
9858
+	0x5a, 0x7e, 0x7d, 0xb3, 0x94, 0xd5, 0xff, 0x01, 0x45, 0x58, 0xdb, 0xe3, 0xd7, 0x0a, 0xad, 0x1a,
9859
+	0xdf, 0x65, 0xe6, 0x78, 0xcf, 0x6a, 0x79, 0x8e, 0xf4, 0x99, 0xd8, 0x22, 0x60, 0x22, 0x05, 0x13,
9860
+	0x34, 0x87, 0xd3, 0x88, 0x11, 0xc2, 0x6b, 0x41, 0xd4, 0x2f, 0xdd, 0x21, 0xe0, 0xb8, 0x59, 0x97,
9861
+	0x28, 0x11, 0x24, 0xe4, 0xc4, 0x16, 0x66, 0x38, 0x3a, 0x84, 0x6b, 0xda, 0x67, 0x3d, 0x81, 0x49,
9862
+	0x73, 0xcc, 0x52, 0x44, 0x45, 0x98, 0x5e, 0x87, 0x26, 0x4c, 0xc9, 0x5c, 0x25, 0xa9, 0xbd, 0x5a,
9863
+	0x1b, 0xe2, 0xce, 0x0a, 0x44, 0x8d, 0x82, 0x22, 0x03, 0x09, 0x67, 0xf6, 0xeb, 0x6d, 0x08, 0x37,
9864
+	0x33, 0x33, 0x40, 0x2a, 0xa7, 0x31, 0x9c, 0xe8, 0xbf, 0x48, 0x90, 0xac, 0xc8, 0x32, 0xb1, 0x1a,
9865
+	0x1b, 0x64, 0x51, 0x55, 0x3d, 0x22, 0xf5, 0x3d, 0xff, 0xf8, 0x34, 0x55, 0x91, 0x59, 0x4f, 0x9c,
9866
+	0xa3, 0xe2, 0x2b, 0xbf, 0x41, 0x8a, 0xd3, 0x13, 0x5f, 0xe8, 0x14, 0x7f, 0x44, 0x0a, 0xe8, 0x28,
9867
+	0x2a, 0x47, 0x6f, 0x90, 0xac, 0xc8, 0x84, 0xf2, 0xaa, 0x9f, 0x97, 0x33, 0x25, 0x12, 0x22, 0xdd,
9868
+	0xa2, 0xc8, 0xb3, 0xaa, 0x3d, 0x5b, 0x3f, 0xdf, 0x1d, 0xa9, 0x82, 0xeb, 0x6f, 0x91, 0x74, 0x9b,
9869
+	0x81, 0x84, 0x67, 0xc9, 0xa2, 0x0b, 0xa1, 0x67, 0x12, 0xd9, 0x08, 0x84, 0xab, 0x2c, 0x16, 0xe0,
9870
+	0x10, 0xb1, 0xb2, 0x38, 0x05, 0xf1, 0x0c, 0x8c, 0x67, 0x82, 0xbf, 0xa9, 0x0e, 0x15, 0xbf, 0xf5,
9871
+	0x3d, 0x52, 0xbc, 0xcb, 0xec, 0xe3, 0x7e, 0x08, 0x27, 0x86, 0x82, 0x5e, 0x26, 0xe9, 0x21, 0x8b,
9872
+	0x36, 0xbf, 0x1a, 0xeb, 0x3a, 0x30, 0x4f, 0x39, 0x0a, 0x2f, 0xe4, 0x19, 0xe7, 0x96, 0x8f, 0x02,
9873
+	0x72, 0xa4, 0xff, 0x36, 0x49, 0x96, 0x9b, 0x41, 0x30, 0x32, 0xa1, 0xe0, 0x96, 0x51, 0xf0, 0x3b,
9874
+	0xb3, 0x0d, 0xc3, 0x8d, 0x58, 0x0d, 0x67, 0x58, 0x66, 0x9b, 0x06, 0x19, 0xb9, 0x92, 0x51, 0xe4,
9875
+	0xd2, 0x1f, 0x26, 0x54, 0xb7, 0x70, 0x7d, 0xea, 0xde, 0x94, 0x57, 0xc1, 0x89, 0x2e, 0x4d, 0x4b,
9876
+	0x62, 0xfb, 0xee, 0x89, 0xeb, 0x9d, 0xb9, 0x90, 0x68, 0xa1, 0x7b, 0x68, 0x35, 0xee, 0x82, 0xa7,
9877
+	0x5d, 0x06, 0x90, 0x36, 0x03, 0xa2, 0xcc, 0x65, 0x67, 0x28, 0xa9, 0xdd, 0x68, 0xd5, 0x9b, 0xad,
9878
+	0xdb, 0x90, 0xde, 0x1e, 0x95, 0xd4, 0x66, 0x90, 0xce, 0xdc, 0x63, 0x30, 0x77, 0xb6, 0xd9, 0xe9,
9879
+	0xec, 0xf3, 0x52, 0xf1, 0x69, 0x40, 0x5d, 0x9c, 0x41, 0xe1, 0x00, 0xea, 0x44, 0x00, 0x61, 0x26,
9880
+	0x05, 0x50, 0x3a, 0x06, 0x84, 0xc9, 0x14, 0x02, 0x88, 0xf0, 0xf0, 0xbf, 0x25, 0x49, 0xc9, 0xb0,
9881
+	0x2c, 0x36, 0x0c, 0x71, 0x5e, 0x56, 0x27, 0x7b, 0x58, 0xed, 0xc1, 0x97, 0xcd, 0xf0, 0xf5, 0x04,
9882
+	0xdd, 0xe2, 0x56, 0xec, 0x8b, 0xd1, 0x1c, 0x5f, 0x85, 0x7a, 0x0e, 0x33, 0x7a, 0x03, 0x3b, 0xc0,
9883
+	0x57, 0x04, 0x41, 0xa3, 0x91, 0xa4, 0xf2, 0x7f, 0x12, 0xe4, 0x62, 0x0c, 0x42, 0x7b, 0x95, 0xa4,
9884
+	0x7d, 0x20, 0xcb, 0xe3, 0x59, 0x7b, 0x5c, 0x3f, 0x87, 0xac, 0x94, 0x23, 0xb5, 0x75, 0x42, 0xcc,
9885
+	0x51, 0xe8, 0x99, 0x7c, 0x7d, 0x7e, 0x30, 0x39, 0x3a, 0x45, 0xd1, 0xbe, 0x0f, 0xd1, 0x9a, 0x59,
9886
+	0xbe, 0x6c, 0x89, 0x0a, 0x1b, 0x8d, 0xff, 0x75, 0xf7, 0x95, 0x2d, 0x13, 0x23, 0x4a, 0x87, 0x0b,
9887
+	0xa3, 0x52, 0x68, 0xf9, 0x75, 0x52, 0x9c, 0xa6, 0xa3, 0x77, 0x43, 0x79, 0x61, 0x72, 0x05, 0x8a,
9888
+	0x94, 0x7f, 0xa3, 0xd3, 0x98, 0xce, 0xb1, 0x72, 0x1a, 0xf8, 0xd4, 0x3f, 0x84, 0xbc, 0xd4, 0xb8,
9889
+	0x17, 0x32, 0xdf, 0x35, 0x9d, 0x9a, 0xa1, 0x35, 0xa6, 0xa2, 0xa5, 0xd0, 0xfc, 0x85, 0xd8, 0x8e,
9890
+	0x3f, 0xe2, 0xa8, 0xd4, 0x8c, 0x98, 0x78, 0x09, 0x95, 0xc2, 0xc8, 0x77, 0xe4, 0xeb, 0x11, 0xaf,
9891
+	0x14, 0xf6, 0xe9, 0x36, 0x45, 0x1a, 0x3e, 0xbd, 0xa8, 0xe8, 0x94, 0x7a, 0xfc, 0xb3, 0xdf, 0xd4,
9892
+	0x02, 0x5f, 0x7d, 0x84, 0x7a, 0x99, 0x90, 0xc9, 0xae, 0xe1, 0xd8, 0x32, 0xb5, 0xcd, 0x4e, 0x67,
9893
+	0x1b, 0xae, 0x0a, 0xaf, 0x9c, 0x27, 0x53, 0x9c, 0xac, 0xff, 0x26, 0x41, 0x72, 0x35, 0x43, 0x66,
9894
+	0x98, 0x4d, 0x52, 0xe2, 0x71, 0xc5, 0x62, 0x7e, 0xd8, 0x65, 0xf7, 0x86, 0xb6, 0x3f, 0x96, 0xa1,
9895
+	0xe1, 0xfc, 0x12, 0x74, 0x19, 0xb9, 0x6a, 0xc0, 0xd4, 0xe0, 0x3c, 0x1a, 0x25, 0x45, 0x26, 0x55,
9896
+	0xec, 0x5a, 0xa6, 0x0a, 0xd4, 0xeb, 0xe7, 0x9b, 0x42, 0x94, 0x67, 0x93, 0x31, 0x34, 0xbd, 0x4a,
9897
+	0x48, 0xcd, 0x0c, 0xf4, 0x03, 0x72, 0x71, 0xd7, 0xb7, 0xfa, 0x50, 0x28, 0x89, 0x45, 0xe5, 0x96,
9898
+	0xdf, 0x22, 0x6b, 0x21, 0x14, 0x44, 0xdd, 0xbe, 0x1d, 0x84, 0xf8, 0x68, 0x09, 0xbe, 0xc1, 0x5c,
9899
+	0x9c, 0xef, 0xf2, 0xc7, 0x45, 0xf1, 0xd8, 0x49, 0xaf, 0x20, 0x66, 0x4b, 0x40, 0xa8, 0x42, 0x6c,
9900
+	0x23, 0x40, 0xff, 0x1e, 0x29, 0xd5, 0xed, 0x60, 0x68, 0x86, 0x20, 0x5b, 0xf6, 0x33, 0xda, 0x6d,
9901
+	0x52, 0xea, 0x33, 0xa8, 0x67, 0x0f, 0x99, 0x09, 0x39, 0x91, 0xf9, 0xb6, 0xd7, 0x7b, 0x22, 0x3b,
9902
+	0xac, 0x44, 0x5c, 0x6d, 0xce, 0xa4, 0x7f, 0x06, 0x19, 0x1c, 0x5f, 0x73, 0xa4, 0xdc, 0x97, 0xc8,
9903
+	0x85, 0xc0, 0x35, 0x87, 0x41, 0xdf, 0x0b, 0xbb, 0xb6, 0x1b, 0xe2, 0xf3, 0xa5, 0x23, 0x6b, 0xe1,
9904
+	0x92, 0x9a, 0x68, 0x4a, 0x3a, 0xc4, 0x66, 0xed, 0x84, 0xb1, 0x61, 0xd7, 0x73, 0x7a, 0x5d, 0x35,
9905
+	0x29, 0x9e, 0x2c, 0x01, 0x8d, 0x33, 0xbb, 0x4e, 0xaf, 0xa3, 0xe8, 0x5a, 0x95, 0xac, 0x3b, 0xde,
9906
+	0x71, 0x17, 0x34, 0xf3, 0xe1, 0xde, 0x77, 0x8f, 0x3c, 0xbf, 0x1b, 0x38, 0xde, 0x19, 0x7c, 0x38,
9907
+	0xf0, 0xc7, 0x7c, 0xd5, 0x68, 0x94, 0x01, 0xd5, 0x10, 0xa0, 0x4d, 0xcf, 0xef, 0xc0, 0xdc, 0xa6,
9908
+	0x42, 0x60, 0x9a, 0x9f, 0xa8, 0x1d, 0xda, 0xd6, 0x89, 0x4a, 0xf3, 0x11, 0x75, 0x0f, 0x88, 0x10,
9909
+	0xe9, 0x96, 0x98, 0xc3, 0x2c, 0x6e, 0x64, 0x8e, 0xca, 0x70, 0x54, 0x51, 0x11, 0x11, 0xa4, 0x7f,
9910
+	0x9d, 0xe4, 0xdb, 0x8e, 0x69, 0xf1, 0x87, 0x61, 0xac, 0xfe, 0x21, 0x85, 0xe1, 0xc9, 0x81, 0xd6,
9911
+	0x22, 0xbc, 0xe5, 0xe9, 0x34, 0x49, 0x7f, 0x0f, 0x92, 0x3e, 0xf5, 0xbc, 0x10, 0x2e, 0xe9, 0x35,
9912
+	0x92, 0xb5, 0xcc, 0xae, 0x72, 0xf7, 0x62, 0x35, 0x0f, 0x6e, 0x91, 0xa9, 0x19, 0x77, 0xd8, 0x98,
9913
+	0x66, 0x2c, 0x13, 0xfe, 0x30, 0xfd, 0x01, 0x02, 0x9d, 0x94, 0x9b, 0xa3, 0x28, 0xd2, 0x1f, 0x78,
9914
+	0x31, 0x50, 0x28, 0x30, 0xe3, 0x3f, 0x44, 0xb8, 0xa2, 0x04, 0x75, 0xfb, 0x10, 0x38, 0x44, 0xb1,
9915
+	0x58, 0x5d, 0x06, 0x24, 0x11, 0x48, 0x0c, 0x27, 0x94, 0x08, 0x34, 0x7e, 0xeb, 0x7f, 0x4e, 0x90,
9916
+	0x02, 0x0e, 0xec, 0x23, 0xdb, 0xc2, 0x3c, 0xf3, 0xc5, 0x63, 0x24, 0x04, 0x06, 0x2b, 0xf0, 0xe5,
9917
+	0xa6, 0x78, 0x60, 0xa8, 0x75, 0x28, 0x45, 0x9a, 0xf6, 0x36, 0x84, 0x47, 0x9e, 0xe7, 0x64, 0x78,
9918
+	0xd4, 0x3f, 0x3f, 0x23, 0xca, 0x2e, 0x47, 0xf2, 0x71, 0x23, 0x4e, 0x76, 0xc7, 0x8f, 0xa6, 0x48,
9919
+	0xa7, 0x49, 0xf8, 0xa8, 0x6d, 0xb9, 0xfc, 0x34, 0xe4, 0xa3, 0x76, 0xad, 0x45, 0x81, 0xa2, 0xff,
9920
+	0x31, 0x41, 0x96, 0x1a, 0xae, 0xe5, 0x8f, 0x79, 0x48, 0x41, 0x0b, 0xae, 0x91, 0x3c, 0x14, 0xd3,
9921
+	0xc1, 0x38, 0x80, 0x8e, 0x53, 0xbd, 0x99, 0x45, 0x04, 0xad, 0x49, 0xf2, 0x10, 0x3c, 0x3d, 0xdf,
9922
+	0x0e, 0xfb, 0x03, 0x59, 0x55, 0xc6, 0x87, 0xb1, 0x69, 0x99, 0x15, 0x43, 0xb1, 0xd0, 0x09, 0xb7,
9923
+	0x0a, 0x5c, 0x29, 0xbe, 0x59, 0x1e, 0xb8, 0xa0, 0xff, 0x75, 0xa0, 0xd5, 0x81, 0x7a, 0xb1, 0x8b,
9924
+	0x1d, 0x04, 0xd7, 0x03, 0x5a, 0x41, 0x49, 0xc3, 0xae, 0x48, 0xd7, 0x49, 0x3e, 0x12, 0x86, 0x2f,
9925
+	0x95, 0x46, 0xa3, 0xd3, 0xbd, 0xb9, 0x71, 0xab, 0x7b, 0xbb, 0xb6, 0x03, 0x61, 0x4c, 0xe4, 0xd0,
9926
+	0xdf, 0x81, 0x4e, 0x3b, 0xa6, 0x0b, 0x5d, 0x8e, 0xea, 0xf9, 0xc0, 0x2b, 0x7c, 0xb8, 0x6a, 0xaa,
9927
+	0x28, 0x4a, 0x0b, 0xaf, 0xc0, 0xdb, 0x87, 0x45, 0x11, 0x4e, 0xc5, 0x17, 0x45, 0x53, 0x2f, 0xb6,
9928
+	0xa9, 0x73, 0x5f, 0x6c, 0xd3, 0x5f, 0xc9, 0x8b, 0xed, 0x8b, 0x9f, 0xa5, 0x48, 0x3e, 0xea, 0xe1,
9929
+	0xd0, 0x65, 0xb0, 0x46, 0x59, 0x10, 0xdd, 0x75, 0x44, 0x6f, 0xf1, 0xea, 0x24, 0x6f, 0x6c, 0x6f,
9930
+	0xef, 0xd6, 0x0c, 0x7c, 0xf0, 0x7c, 0x5b, 0x14, 0x31, 0x11, 0xc0, 0x80, 0x4b, 0x8b, 0x87, 0xde,
9931
+	0xd3, 0xf4, 0x49, 0x11, 0x73, 0x5f, 0xf6, 0xf0, 0x11, 0x4a, 0x55, 0x30, 0xcf, 0x91, 0x9c, 0xd1,
9932
+	0xe9, 0x34, 0x6f, 0xb7, 0x40, 0xd2, 0x83, 0x44, 0xf9, 0x29, 0x00, 0x5d, 0x98, 0x88, 0x82, 0xe4,
9933
+	0x7b, 0xec, 0x82, 0x24, 0x44, 0xd5, 0x6a, 0x8d, 0x36, 0xae, 0x77, 0x3f, 0x39, 0x8f, 0xe2, 0xa9,
9934
+	0x9b, 0x3f, 0x88, 0xe5, 0xdb, 0xb4, 0xd1, 0x36, 0x28, 0xae, 0xf8, 0x20, 0x39, 0xb7, 0xaf, 0xb6,
9935
+	0xcf, 0xa0, 0xab, 0xc7, 0x35, 0xd7, 0xd5, 0xeb, 0xed, 0xfd, 0x54, 0x59, 0x03, 0xcc, 0xf2, 0xa4,
9936
+	0x71, 0x05, 0xfb, 0x8e, 0x71, 0xb5, 0xce, 0x9e, 0x41, 0xf7, 0xb8, 0x98, 0xd4, 0xdc, 0x6a, 0x1d,
9937
+	0x7c, 0x4e, 0x40, 0x29, 0xa0, 0x1d, 0xdd, 0x6f, 0xb5, 0xb8, 0x76, 0xe9, 0x39, 0xed, 0xe8, 0xc8,
9938
+	0x75, 0x11, 0x73, 0x1d, 0x52, 0xd8, 0xee, 0x4e, 0x7b, 0xbb, 0xb1, 0xd7, 0x28, 0x3d, 0x48, 0xcf,
9939
+	0x6d, 0xa8, 0xe6, 0x0d, 0x86, 0x0e, 0x0b, 0x85, 0x7a, 0x9d, 0xad, 0xfd, 0x3d, 0xfe, 0xb8, 0x7c,
9940
+	0x3f, 0x33, 0xbf, 0x60, 0x7f, 0x14, 0xf6, 0xb0, 0x6c, 0xbc, 0x16, 0xd5, 0x71, 0x0f, 0x32, 0x22,
9941
+	0x65, 0x46, 0x18, 0x51, 0xc4, 0xa1, 0x1c, 0xda, 0xf8, 0xae, 0x78, 0x87, 0xbe, 0x9f, 0x9d, 0x93,
9942
+	0x43, 0xd9, 0xbb, 0x10, 0x05, 0xa1, 0xd4, 0x8b, 0x9e, 0xa4, 0xa2, 0xa9, 0x17, 0x7f, 0x40, 0x72,
9943
+	0x2a, 0x60, 0x80, 0x75, 0xb2, 0x77, 0x77, 0xe9, 0x9d, 0x06, 0x85, 0xa3, 0xe7, 0xd6, 0x51, 0x33,
9944
+	0x77, 0x3d, 0x1f, 0xbc, 0x0b, 0xb6, 0xb1, 0xb8, 0x63, 0xb4, 0x8c, 0xdb, 0x00, 0x90, 0x6f, 0x5e,
9945
+	0x0a, 0x20, 0xbd, 0xbe, 0x5c, 0x92, 0x0b, 0x44, 0x32, 0xab, 0x6b, 0x0f, 0x3f, 0x59, 0x5f, 0xf8,
9946
+	0x08, 0x7e, 0x7f, 0xff, 0x64, 0x3d, 0x71, 0xff, 0xd3, 0xf5, 0xc4, 0x43, 0xf8, 0xfd, 0x01, 0x7e,
9947
+	0x7f, 0x81, 0xdf, 0x61, 0x96, 0xd7, 0x2f, 0xaf, 0xfd, 0x37, 0x00, 0x00, 0xff, 0xff, 0x2c, 0xf4,
9948
+	0xf4, 0x16, 0x49, 0x1d, 0x00, 0x00,
9942 9949
 }
... ...
@@ -447,9 +447,29 @@ message AcceptancePolicy {
447 447
 }
448 448
 
449 449
 
450
+message ExternalCA {
451
+	enum CAProtocol {
452
+		CFSSL = 0 [(gogoproto.enumvalue_customname) = "CAProtocolCFSSL"];
453
+	}
454
+
455
+	// Protocol is the protocol used by this external CA.
456
+	CAProtocol protocol = 1;
457
+
458
+	// URL is the URL where the external CA can be reached.
459
+	string url = 2 [(gogoproto.customname) = "URL"];
460
+
461
+	// Options is a set of additional key/value pairs whose interpretation
462
+	// depends on the specified CA type.
463
+	map<string, string> options = 3;
464
+}
465
+
450 466
 message CAConfig {
451 467
 	// NodeCertExpiry is the duration certificates should be issued for
452 468
 	Duration node_cert_expiry = 1;
469
+
470
+	// ExternalCAs is a list of CAs to which a manager node will make
471
+	// certificate signing requests for node certificates.
472
+	repeated ExternalCA external_cas = 2 [(gogoproto.customname) = "ExternalCAs"];
453 473
 }
454 474
 
455 475
 // OrchestrationConfig defines cluster-level orchestration settings.
... ...
@@ -167,7 +167,16 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, paths Cert
167 167
 	}
168 168
 
169 169
 	// Get the remote manager to issue a CA signed certificate for this node
170
-	signedCert, err := GetRemoteSignedCertificate(ctx, csr, role, secret, rca.Pool, picker, transport, nodeInfo)
170
+	// Retry up to 5 times in case the manager we first try to contact isn't
171
+	// responding properly (for example, it may have just been demoted).
172
+	var signedCert []byte
173
+	for i := 0; i != 5; i++ {
174
+		signedCert, err = GetRemoteSignedCertificate(ctx, csr, role, secret, rca.Pool, picker, transport, nodeInfo)
175
+		if err == nil {
176
+			break
177
+		}
178
+		log.Warningf("error fetching signed node certificate: %v", err)
179
+	}
171 180
 	if err != nil {
172 181
 		return nil, err
173 182
 	}
... ...
@@ -192,6 +201,12 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, paths Cert
192 192
 		return nil, err
193 193
 	}
194 194
 
195
+	// Create a valid TLSKeyPair out of the PEM encoded private key and certificate
196
+	tlsKeyPair, err := tls.X509KeyPair(signedCert, key)
197
+	if err != nil {
198
+		return nil, err
199
+	}
200
+
195 201
 	log.Infof("Downloaded new TLS credentials with role: %s.", role)
196 202
 
197 203
 	// Ensure directory exists
... ...
@@ -210,40 +225,50 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, paths Cert
210 210
 		return nil, err
211 211
 	}
212 212
 
213
-	// Create a valid TLSKeyPair out of the PEM encoded private key and certificate
214
-	tlsKeyPair, err := tls.X509KeyPair(signedCert, key)
215
-	if err != nil {
216
-		return nil, err
217
-	}
218
-
219 213
 	return &tlsKeyPair, nil
220 214
 }
221 215
 
222
-// ParseValidateAndSignCSR returns a signed certificate from a particular rootCA and a CSR.
223
-func (rca *RootCA) ParseValidateAndSignCSR(csrBytes []byte, cn, ou, org string) ([]byte, error) {
224
-	if !rca.CanSign() {
225
-		return nil, ErrNoValidSigner
226
-	}
227
-
228
-	// All managers get added the subject-alt-name of CA, so they can be used for cert issuance
216
+// PrepareCSR creates a CFSSL Sign Request based on the given raw CSR and
217
+// overrides the Subject and Hosts with the given extra args.
218
+func PrepareCSR(csrBytes []byte, cn, ou, org string) cfsigner.SignRequest {
219
+	// All managers get added the subject-alt-name of CA, so they can be
220
+	// used for cert issuance.
229 221
 	hosts := []string{ou}
230 222
 	if ou == ManagerRole {
231 223
 		hosts = append(hosts, CARole)
232 224
 	}
233 225
 
234
-	cert, err := rca.Signer.Sign(cfsigner.SignRequest{
226
+	return cfsigner.SignRequest{
235 227
 		Request: string(csrBytes),
236 228
 		// OU is used for Authentication of the node type. The CN has the random
237 229
 		// node ID.
238 230
 		Subject: &cfsigner.Subject{CN: cn, Names: []cfcsr.Name{{OU: ou, O: org}}},
239 231
 		// Adding ou as DNS alt name, so clients can connect to ManagerRole and CARole
240 232
 		Hosts: hosts,
241
-	})
233
+	}
234
+}
235
+
236
+// ParseValidateAndSignCSR returns a signed certificate from a particular rootCA and a CSR.
237
+func (rca *RootCA) ParseValidateAndSignCSR(csrBytes []byte, cn, ou, org string) ([]byte, error) {
238
+	if !rca.CanSign() {
239
+		return nil, ErrNoValidSigner
240
+	}
241
+
242
+	signRequest := PrepareCSR(csrBytes, cn, ou, org)
243
+
244
+	cert, err := rca.Signer.Sign(signRequest)
242 245
 	if err != nil {
243 246
 		log.Debugf("failed to sign node certificate: %v", err)
244 247
 		return nil, err
245 248
 	}
246 249
 
250
+	return rca.AppendFirstRootPEM(cert)
251
+}
252
+
253
+// AppendFirstRootPEM appends the first certificate from this RootCA's cert
254
+// bundle to the given cert bundle (which should already be encoded as a series
255
+// of PEM-encoded certificate blocks).
256
+func (rca *RootCA) AppendFirstRootPEM(cert []byte) ([]byte, error) {
247 257
 	// Append the first root CA Cert to the certificate, to create a valid chain
248 258
 	// Get the first Root CA Cert on the bundle
249 259
 	firstRootCA, _, err := helpers.ParseOneCertificateFromPEM(rca.Cert)
... ...
@@ -390,7 +415,7 @@ func GetLocalRootCA(baseDir string) (RootCA, error) {
390 390
 
391 391
 	rootCA, err := NewRootCA(cert, key, DefaultNodeCertExpiration)
392 392
 	if err == nil {
393
-		log.Debugf("successfully loaded the signer for the Root CA: %s", paths.RootCA.Cert)
393
+		log.Debugf("successfully loaded the Root CA: %s", paths.RootCA.Cert)
394 394
 	}
395 395
 
396 396
 	return rootCA, err
... ...
@@ -602,7 +627,7 @@ func GetRemoteSignedCertificate(ctx context.Context, csr []byte, role, secret st
602 602
 	}
603 603
 	defer conn.Close()
604 604
 
605
-	// Create a CAClient to retreive a new Certificate
605
+	// Create a CAClient to retrieve a new Certificate
606 606
 	caClient := api.NewNodeCAClient(conn)
607 607
 
608 608
 	// Convert our internal string roles into an API role
... ...
@@ -644,7 +669,15 @@ func GetRemoteSignedCertificate(ctx context.Context, csr []byte, role, secret st
644 644
 			if statusResponse.Certificate == nil {
645 645
 				return nil, fmt.Errorf("no certificate in CertificateStatus response")
646 646
 			}
647
-			return statusResponse.Certificate.Certificate, nil
647
+
648
+			// The certificate in the response must match the CSR
649
+			// we submitted. If we are getting a response for a
650
+			// certificate that was previously issued, we need to
651
+			// retry until the certificate gets updated per our
652
+			// current request.
653
+			if bytes.Equal(statusResponse.Certificate.CSR, csr) {
654
+				return statusResponse.Certificate.Certificate, nil
655
+			}
648 656
 		}
649 657
 
650 658
 		// If we're still pending, the issuance failed, or the state is unknown
... ...
@@ -45,7 +45,8 @@ const (
45 45
 type SecurityConfig struct {
46 46
 	mu sync.Mutex
47 47
 
48
-	rootCA *RootCA
48
+	rootCA     *RootCA
49
+	externalCA *ExternalCA
49 50
 
50 51
 	ServerTLSCreds *MutableTLSCreds
51 52
 	ClientTLSCreds *MutableTLSCreds
... ...
@@ -60,8 +61,19 @@ type CertificateUpdate struct {
60 60
 
61 61
 // NewSecurityConfig initializes and returns a new SecurityConfig.
62 62
 func NewSecurityConfig(rootCA *RootCA, clientTLSCreds, serverTLSCreds *MutableTLSCreds) *SecurityConfig {
63
+	// Make a new TLS config for the external CA client without a
64
+	// ServerName value set.
65
+	clientTLSConfig := clientTLSCreds.Config()
66
+
67
+	externalCATLSConfig := &tls.Config{
68
+		Certificates: clientTLSConfig.Certificates,
69
+		RootCAs:      clientTLSConfig.RootCAs,
70
+		MinVersion:   tls.VersionTLS12,
71
+	}
72
+
63 73
 	return &SecurityConfig{
64 74
 		rootCA:         rootCA,
75
+		externalCA:     NewExternalCA(rootCA, externalCATLSConfig),
65 76
 		ClientTLSCreds: clientTLSCreds,
66 77
 		ServerTLSCreds: serverTLSCreds,
67 78
 	}
... ...
@@ -164,8 +176,18 @@ func LoadOrCreateSecurityConfig(ctx context.Context, baseCertDir, caHash, secret
164 164
 			return nil, err
165 165
 		}
166 166
 
167
-		// Get the remote CA certificate, verify integrity with the hash provided
168
-		rootCA, err = GetRemoteCA(ctx, d, picker)
167
+		// Get the remote CA certificate, verify integrity with the
168
+		// hash provided. Retry up to 5 times, in case the manager we
169
+		// first try to contact is not responding properly (it may have
170
+		// just been demoted, for example).
171
+
172
+		for i := 0; i != 5; i++ {
173
+			rootCA, err = GetRemoteCA(ctx, d, picker)
174
+			if err == nil {
175
+				break
176
+			}
177
+			log.Warningf("failed to retrieve remote root CA certificate: %v", err)
178
+		}
169 179
 		if err != nil {
170 180
 			return nil, err
171 181
 		}
... ...
@@ -180,9 +202,9 @@ func LoadOrCreateSecurityConfig(ctx context.Context, baseCertDir, caHash, secret
180 180
 		return nil, err
181 181
 	}
182 182
 
183
-	// At this point we've successfully loaded the CA details from disk, or successfully
184
-	// downloaded them remotely.
185
-	// The next step is to try to load our certificates.
183
+	// At this point we've successfully loaded the CA details from disk, or
184
+	// successfully downloaded them remotely. The next step is to try to
185
+	// load our certificates.
186 186
 	clientTLSCreds, serverTLSCreds, err = LoadTLSCreds(rootCA, paths.Node)
187 187
 	if err != nil {
188 188
 		log.Debugf("no valid local TLS credentials found: %v", err)
... ...
@@ -204,6 +226,9 @@ func LoadOrCreateSecurityConfig(ctx context.Context, baseCertDir, caHash, secret
204 204
 				}
205 205
 			}
206 206
 			tlsKeyPair, err = rootCA.IssueAndSaveNewCertificates(paths.Node, cn, proposedRole, org)
207
+			if err != nil {
208
+				return nil, err
209
+			}
207 210
 		} else {
208 211
 			// There was an error loading our Credentials, let's get a new certificate issued
209 212
 			// Last argument is nil because at this point we don't have any valid TLS creds
... ...
@@ -211,7 +236,6 @@ func LoadOrCreateSecurityConfig(ctx context.Context, baseCertDir, caHash, secret
211 211
 			if err != nil {
212 212
 				return nil, err
213 213
 			}
214
-
215 214
 		}
216 215
 		// Create the Server TLS Credentials for this node. These will not be used by agents.
217 216
 		serverTLSCreds, err = rootCA.NewServerTLSCredentials(tlsKeyPair)
... ...
@@ -236,12 +260,7 @@ func LoadOrCreateSecurityConfig(ctx context.Context, baseCertDir, caHash, secret
236 236
 		log.Debugf("loaded local TLS credentials: %s.", paths.Node.Cert)
237 237
 	}
238 238
 
239
-	return &SecurityConfig{
240
-		rootCA: &rootCA,
241
-
242
-		ServerTLSCreds: serverTLSCreds,
243
-		ClientTLSCreds: clientTLSCreds,
244
-	}, nil
239
+	return NewSecurityConfig(&rootCA, clientTLSCreds, serverTLSCreds), nil
245 240
 }
246 241
 
247 242
 // RenewTLSConfig will continuously monitor for the necessity of renewing the local certificates, either by
... ...
@@ -317,6 +336,14 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, baseCertDir string,
317 317
 				updates <- CertificateUpdate{Err: err}
318 318
 			}
319 319
 
320
+			// Update the external CA to use the new client TLS
321
+			// config using a copy without a serverName specified.
322
+			s.externalCA.UpdateTLSConfig(&tls.Config{
323
+				Certificates: clientTLSConfig.Certificates,
324
+				RootCAs:      clientTLSConfig.RootCAs,
325
+				MinVersion:   tls.VersionTLS12,
326
+			})
327
+
320 328
 			err = s.ServerTLSCreds.LoadNewTLSConfig(serverTLSConfig)
321 329
 			if err != nil {
322 330
 				log.Debugf("failed to update the server TLS credentials: %v", err)
... ...
@@ -405,7 +432,7 @@ func LoadTLSCreds(rootCA RootCA, paths CertPaths) (*MutableTLSCreds, *MutableTLS
405 405
 		}
406 406
 
407 407
 		keyPair, newErr = tls.X509KeyPair(cert, key)
408
-		if err != nil {
408
+		if newErr != nil {
409 409
 			return nil, nil, err
410 410
 		}
411 411
 	}
412 412
new file mode 100644
... ...
@@ -0,0 +1,141 @@
0
+package ca
1
+
2
+import (
3
+	"bytes"
4
+	"crypto/tls"
5
+	"encoding/json"
6
+	"errors"
7
+	"fmt"
8
+	"io/ioutil"
9
+	"net/http"
10
+	"sync"
11
+
12
+	log "github.com/Sirupsen/logrus"
13
+	"github.com/cloudflare/cfssl/api"
14
+	"github.com/cloudflare/cfssl/signer"
15
+)
16
+
17
+// ErrNoExternalCAURLs is an error used it indicate that an ExternalCA is
18
+// configured with no URLs to which it can proxy certificate signing requests.
19
+var ErrNoExternalCAURLs = errors.New("no external CA URLs")
20
+
21
+// ExternalCA is able to make certificate signing requests to one of a list
22
+// remote CFSSL API endpoints.
23
+type ExternalCA struct {
24
+	mu     sync.Mutex
25
+	rootCA *RootCA
26
+	urls   []string
27
+	client *http.Client
28
+}
29
+
30
+// NewExternalCA creates a new ExternalCA which uses the given tlsConfig to
31
+// authenticate to any of the given URLS of CFSSL API endpoints.
32
+func NewExternalCA(rootCA *RootCA, tlsConfig *tls.Config, urls ...string) *ExternalCA {
33
+	return &ExternalCA{
34
+		rootCA: rootCA,
35
+		urls:   urls,
36
+		client: &http.Client{
37
+			Transport: &http.Transport{
38
+				TLSClientConfig: tlsConfig,
39
+			},
40
+		},
41
+	}
42
+}
43
+
44
+// UpdateTLSConfig updates the HTTP Client for this ExternalCA by creating
45
+// a new client which uses the given tlsConfig.
46
+func (eca *ExternalCA) UpdateTLSConfig(tlsConfig *tls.Config) {
47
+	eca.mu.Lock()
48
+	defer eca.mu.Unlock()
49
+
50
+	eca.client = &http.Client{
51
+		Transport: &http.Transport{
52
+			TLSClientConfig: tlsConfig,
53
+		},
54
+	}
55
+}
56
+
57
+// UpdateURLs updates the list of CSR API endpoints by setting it to the given
58
+// urls.
59
+func (eca *ExternalCA) UpdateURLs(urls ...string) {
60
+	eca.mu.Lock()
61
+	defer eca.mu.Unlock()
62
+
63
+	eca.urls = urls
64
+}
65
+
66
+// Sign signs a new certificate by proxying the given certificate signing
67
+// request to an external CFSSL API server.
68
+func (eca *ExternalCA) Sign(req signer.SignRequest) (cert []byte, err error) {
69
+	// Get the current HTTP client and list of URLs in a small critical
70
+	// section. We will use these to make certificate signing requests.
71
+	eca.mu.Lock()
72
+	urls := eca.urls
73
+	client := eca.client
74
+	eca.mu.Unlock()
75
+
76
+	if len(urls) == 0 {
77
+		return nil, ErrNoExternalCAURLs
78
+	}
79
+
80
+	csrJSON, err := json.Marshal(req)
81
+	if err != nil {
82
+		return nil, fmt.Errorf("unable to JSON-encode CFSSL signing request: %s", err)
83
+	}
84
+
85
+	// Try each configured proxy URL. Return after the first success. If
86
+	// all fail then the last error will be returned.
87
+	for _, url := range urls {
88
+		cert, err = makeExternalSignRequest(client, url, csrJSON)
89
+		if err == nil {
90
+			return eca.rootCA.AppendFirstRootPEM(cert)
91
+		}
92
+
93
+		log.Debugf("unable to proxy certificate signing request to %s: %s", url, err)
94
+	}
95
+
96
+	return nil, err
97
+}
98
+
99
+func makeExternalSignRequest(client *http.Client, url string, csrJSON []byte) (cert []byte, err error) {
100
+	resp, err := client.Post(url, "application/json", bytes.NewReader(csrJSON))
101
+	if err != nil {
102
+		return nil, fmt.Errorf("unable to perform certificate signing request: %s", err)
103
+	}
104
+	defer resp.Body.Close()
105
+
106
+	body, err := ioutil.ReadAll(resp.Body)
107
+	if err != nil {
108
+		return nil, fmt.Errorf("unable to read CSR response body: %s", err)
109
+	}
110
+
111
+	if resp.StatusCode != http.StatusOK {
112
+		return nil, fmt.Errorf("unexpected status code in CSR response: %d - %s", resp.StatusCode, string(body))
113
+	}
114
+
115
+	var apiResponse api.Response
116
+	if err := json.Unmarshal(body, &apiResponse); err != nil {
117
+		log.Debugf("unable to JSON-parse CFSSL API response body: %s", string(body))
118
+		return nil, fmt.Errorf("unable to parse JSON response: %s", err)
119
+	}
120
+
121
+	if !apiResponse.Success || apiResponse.Result == nil {
122
+		if len(apiResponse.Errors) > 0 {
123
+			return nil, fmt.Errorf("response errors: %v", apiResponse.Errors)
124
+		}
125
+
126
+		return nil, fmt.Errorf("certificate signing request failed")
127
+	}
128
+
129
+	result, ok := apiResponse.Result.(map[string]interface{})
130
+	if !ok {
131
+		return nil, fmt.Errorf("invalid result type: %T", apiResponse.Result)
132
+	}
133
+
134
+	certPEM, ok := result["certificate"].(string)
135
+	if !ok {
136
+		return nil, fmt.Errorf("invalid result certificate field type: %T", result["certificate"])
137
+	}
138
+
139
+	return []byte(certPEM), nil
140
+}
... ...
@@ -355,7 +355,7 @@ func (s *Server) Run(ctx context.Context) error {
355 355
 	s.mu.Lock()
356 356
 	if s.isRunning() {
357 357
 		s.mu.Unlock()
358
-		return fmt.Errorf("CA signer is stopped")
358
+		return fmt.Errorf("CA signer is already running")
359 359
 	}
360 360
 	s.wg.Add(1)
361 361
 	defer s.wg.Done()
... ...
@@ -443,12 +443,14 @@ func (s *Server) Run(ctx context.Context) error {
443 443
 func (s *Server) Stop() error {
444 444
 	s.mu.Lock()
445 445
 	if !s.isRunning() {
446
+		s.mu.Unlock()
446 447
 		return fmt.Errorf("CA signer is already stopped")
447 448
 	}
448 449
 	s.cancel()
449 450
 	s.mu.Unlock()
450 451
 	// wait for all handlers to finish their CA deals,
451 452
 	s.wg.Wait()
453
+	s.started = make(chan struct{})
452 454
 	return nil
453 455
 }
454 456
 
... ...
@@ -530,6 +532,21 @@ func (s *Server) updateCluster(ctx context.Context, cluster *api.Cluster) {
530 530
 			}).Debugf("Root CA updated successfully")
531 531
 		}
532 532
 	}
533
+
534
+	// Update our security config with the list of External CA URLs
535
+	// from the new cluster state.
536
+
537
+	// TODO(aaronl): In the future, this will be abstracted with an
538
+	// ExternalCA interface that has different implementations for
539
+	// different CA types. At the moment, only CFSSL is supported.
540
+	var cfsslURLs []string
541
+	for _, ca := range cluster.Spec.CAConfig.ExternalCAs {
542
+		if ca.Protocol == api.ExternalCA_CAProtocolCFSSL {
543
+			cfsslURLs = append(cfsslURLs, ca.URL)
544
+		}
545
+	}
546
+
547
+	s.securityConfig.externalCA.UpdateURLs(cfsslURLs...)
533 548
 }
534 549
 
535 550
 // evaluateAndSignNodeCert implements the logic of which certificates to sign
... ...
@@ -555,13 +572,8 @@ func (s *Server) evaluateAndSignNodeCert(ctx context.Context, node *api.Node) {
555 555
 
556 556
 // signNodeCert does the bulk of the work for signing a certificate
557 557
 func (s *Server) signNodeCert(ctx context.Context, node *api.Node) {
558
-	if !s.securityConfig.RootCA().CanSign() {
559
-		log.G(ctx).WithFields(logrus.Fields{
560
-			"node.id": node.ID,
561
-			"method":  "(*Server).signNodeCert",
562
-		}).Errorf("no valid signer found")
563
-		return
564
-	}
558
+	rootCA := s.securityConfig.RootCA()
559
+	externalCA := s.securityConfig.externalCA
565 560
 
566 561
 	node = node.Copy()
567 562
 	nodeID := node.ID
... ...
@@ -576,7 +588,20 @@ func (s *Server) signNodeCert(ctx context.Context, node *api.Node) {
576 576
 	}
577 577
 
578 578
 	// Attempt to sign the CSR
579
-	cert, err := s.securityConfig.RootCA().ParseValidateAndSignCSR(node.Certificate.CSR, node.Certificate.CN, role, s.securityConfig.ClientTLSCreds.Organization())
579
+	var (
580
+		rawCSR = node.Certificate.CSR
581
+		cn     = node.Certificate.CN
582
+		ou     = role
583
+		org    = s.securityConfig.ClientTLSCreds.Organization()
584
+	)
585
+
586
+	// Try using the external CA first.
587
+	cert, err := externalCA.Sign(PrepareCSR(rawCSR, cn, ou, org))
588
+	if err == ErrNoExternalCAURLs {
589
+		// No external CA servers configured. Try using the local CA.
590
+		cert, err = rootCA.ParseValidateAndSignCSR(rawCSR, cn, ou, org)
591
+	}
592
+
580 593
 	if err != nil {
581 594
 		log.G(ctx).WithFields(logrus.Fields{
582 595
 			"node.id": node.ID,
... ...
@@ -124,6 +124,14 @@ func (c *MutableTLSCreds) LoadNewTLSConfig(newConfig *tls.Config) error {
124 124
 	return nil
125 125
 }
126 126
 
127
+// Config returns the current underlying TLS config.
128
+func (c *MutableTLSCreds) Config() *tls.Config {
129
+	c.Lock()
130
+	defer c.Unlock()
131
+
132
+	return c.config
133
+}
134
+
127 135
 // Role returns the OU for the certificate encapsulated in this TransportAuthenticator
128 136
 func (c *MutableTLSCreds) Role() string {
129 137
 	c.Lock()
... ...
@@ -52,6 +52,16 @@ type networkContext struct {
52 52
 	// A table of unallocated tasks which will be revisited if any thing
53 53
 	// changes in system state that might help task allocation.
54 54
 	unallocatedTasks map[string]*api.Task
55
+
56
+	// A table of unallocated services which will be revisited if
57
+	// any thing changes in system state that might help service
58
+	// allocation.
59
+	unallocatedServices map[string]*api.Service
60
+
61
+	// A table of unallocated networks which will be revisited if
62
+	// any thing changes in system state that might help network
63
+	// allocation.
64
+	unallocatedNetworks map[string]*api.Network
55 65
 }
56 66
 
57 67
 func (a *Allocator) doNetworkInit(ctx context.Context) error {
... ...
@@ -61,8 +71,10 @@ func (a *Allocator) doNetworkInit(ctx context.Context) error {
61 61
 	}
62 62
 
63 63
 	nc := &networkContext{
64
-		nwkAllocator:     na,
65
-		unallocatedTasks: make(map[string]*api.Task),
64
+		nwkAllocator:        na,
65
+		unallocatedTasks:    make(map[string]*api.Task),
66
+		unallocatedServices: make(map[string]*api.Service),
67
+		unallocatedNetworks: make(map[string]*api.Network),
66 68
 	}
67 69
 
68 70
 	// Check if we have the ingress network. If not found create
... ...
@@ -326,6 +338,8 @@ func (a *Allocator) doNetworkAlloc(ctx context.Context, ev events.Event) {
326 326
 	case state.EventCreateTask, state.EventUpdateTask, state.EventDeleteTask:
327 327
 		a.doTaskAlloc(ctx, nc, ev)
328 328
 	case state.EventCommit:
329
+		a.procUnallocatedNetworks(ctx, nc)
330
+		a.procUnallocatedServices(ctx, nc)
329 331
 		a.procUnallocatedTasksNetwork(ctx, nc)
330 332
 		return
331 333
 	}
... ...
@@ -554,29 +568,34 @@ func (a *Allocator) allocateNode(ctx context.Context, nc *networkContext, node *
554 554
 }
555 555
 
556 556
 func (a *Allocator) allocateService(ctx context.Context, nc *networkContext, s *api.Service) error {
557
-	// The service is trying to expose ports to the external
558
-	// world. Automatically attach the service to the ingress
559
-	// network only if it is not already done.
560
-	if s.Spec.Endpoint != nil && len(s.Spec.Endpoint.Ports) != 0 {
557
+	if s.Spec.Endpoint != nil {
561 558
 		if s.Endpoint == nil {
562
-			s.Endpoint = &api.Endpoint{}
559
+			s.Endpoint = &api.Endpoint{
560
+				Spec: s.Spec.Endpoint.Copy(),
561
+			}
563 562
 		}
564 563
 
565
-		var found bool
566
-		for _, vip := range s.Endpoint.VirtualIPs {
567
-			if vip.NetworkID == ingressNetwork.ID {
568
-				found = true
569
-				break
564
+		// The service is trying to expose ports to the external
565
+		// world. Automatically attach the service to the ingress
566
+		// network only if it is not already done.
567
+		if len(s.Spec.Endpoint.Ports) != 0 {
568
+			var found bool
569
+			for _, vip := range s.Endpoint.VirtualIPs {
570
+				if vip.NetworkID == ingressNetwork.ID {
571
+					found = true
572
+					break
573
+				}
570 574
 			}
571
-		}
572 575
 
573
-		if !found {
574
-			s.Endpoint.VirtualIPs = append(s.Endpoint.VirtualIPs,
575
-				&api.Endpoint_VirtualIP{NetworkID: ingressNetwork.ID})
576
+			if !found {
577
+				s.Endpoint.VirtualIPs = append(s.Endpoint.VirtualIPs,
578
+					&api.Endpoint_VirtualIP{NetworkID: ingressNetwork.ID})
579
+			}
576 580
 		}
577 581
 	}
578 582
 
579 583
 	if err := nc.nwkAllocator.ServiceAllocate(s); err != nil {
584
+		nc.unallocatedServices[s.ID] = s
580 585
 		return err
581 586
 	}
582 587
 
... ...
@@ -611,6 +630,7 @@ func (a *Allocator) allocateService(ctx context.Context, nc *networkContext, s *
611 611
 
612 612
 func (a *Allocator) allocateNetwork(ctx context.Context, nc *networkContext, n *api.Network) error {
613 613
 	if err := nc.nwkAllocator.Allocate(n); err != nil {
614
+		nc.unallocatedNetworks[n.ID] = n
614 615
 		return fmt.Errorf("failed during network allocation for network %s: %v", n.ID, err)
615 616
 	}
616 617
 
... ...
@@ -666,6 +686,8 @@ func (a *Allocator) allocateTask(ctx context.Context, nc *networkContext, tx sto
666 666
 			if !nc.nwkAllocator.IsAllocated(n) {
667 667
 				return nil, fmt.Errorf("network %s attached to task %s not allocated yet", n.ID, t.ID)
668 668
 			}
669
+
670
+			na.Network = n
669 671
 		}
670 672
 
671 673
 		if err := nc.nwkAllocator.AllocateTask(t); err != nil {
... ...
@@ -696,6 +718,32 @@ func (a *Allocator) allocateTask(ctx context.Context, nc *networkContext, tx sto
696 696
 	return storeT, nil
697 697
 }
698 698
 
699
+func (a *Allocator) procUnallocatedNetworks(ctx context.Context, nc *networkContext) {
700
+	for _, n := range nc.unallocatedNetworks {
701
+		if !nc.nwkAllocator.IsAllocated(n) {
702
+			if err := a.allocateNetwork(ctx, nc, n); err != nil {
703
+				log.G(ctx).Debugf("Failed allocation of unallocated network %s: %v", n.ID, err)
704
+				continue
705
+			}
706
+		}
707
+
708
+		delete(nc.unallocatedNetworks, n.ID)
709
+	}
710
+}
711
+
712
+func (a *Allocator) procUnallocatedServices(ctx context.Context, nc *networkContext) {
713
+	for _, s := range nc.unallocatedServices {
714
+		if serviceAllocationNeeded(s, nc) {
715
+			if err := a.allocateService(ctx, nc, s); err != nil {
716
+				log.G(ctx).Debugf("Failed allocation of unallocated service %s: %v", s.ID, err)
717
+				continue
718
+			}
719
+		}
720
+
721
+		delete(nc.unallocatedServices, s.ID)
722
+	}
723
+}
724
+
699 725
 func (a *Allocator) procUnallocatedTasksNetwork(ctx context.Context, nc *networkContext) {
700 726
 	tasks := make([]*api.Task, 0, len(nc.unallocatedTasks))
701 727
 
... ...
@@ -14,7 +14,10 @@ import (
14 14
 )
15 15
 
16 16
 const (
17
-	defaultDriver = "overlay"
17
+	// DefaultDriver defines the name of the driver to be used by
18
+	// default if a network without any driver name specified is
19
+	// created.
20
+	DefaultDriver = "overlay"
18 21
 )
19 22
 
20 23
 var (
... ...
@@ -69,7 +72,7 @@ func New() (*NetworkAllocator, error) {
69 69
 	}
70 70
 
71 71
 	// Add the manager component of overlay driver to the registry.
72
-	if err := reg.AddDriver(defaultDriver, defaultDriverInitFunc, nil); err != nil {
72
+	if err := reg.AddDriver(DefaultDriver, defaultDriverInitFunc, nil); err != nil {
73 73
 		return nil, err
74 74
 	}
75 75
 
... ...
@@ -96,6 +99,7 @@ func (na *NetworkAllocator) Allocate(n *api.Network) error {
96 96
 	}
97 97
 
98 98
 	if err := na.allocateDriverState(n); err != nil {
99
+		na.freePools(n, pools)
99 100
 		return fmt.Errorf("failed while allocating driver state for network %s: %v", n.ID, err)
100 101
 	}
101 102
 
... ...
@@ -146,7 +150,9 @@ func (na *NetworkAllocator) ServiceAllocate(s *api.Service) (err error) {
146 146
 	}
147 147
 
148 148
 	if s.Endpoint == nil {
149
-		s.Endpoint = &api.Endpoint{}
149
+		s.Endpoint = &api.Endpoint{
150
+			Spec: s.Spec.Endpoint.Copy(),
151
+		}
150 152
 	}
151 153
 
152 154
 	// First allocate VIPs for all the pre-populated endpoint attachments
... ...
@@ -520,7 +526,7 @@ func (na *NetworkAllocator) allocateDriverState(n *api.Network) error {
520 520
 
521 521
 // Resolve network driver
522 522
 func (na *NetworkAllocator) resolveDriver(n *api.Network) (driverapi.Driver, string, error) {
523
-	dName := defaultDriver
523
+	dName := DefaultDriver
524 524
 	if n.Spec.DriverConfig != nil && n.Spec.DriverConfig.Name != "" {
525 525
 		dName = n.Spec.DriverConfig.Name
526 526
 	}
... ...
@@ -3,8 +3,10 @@ package controlapi
3 3
 import (
4 4
 	"net"
5 5
 
6
+	"github.com/docker/libnetwork/ipamapi"
6 7
 	"github.com/docker/swarmkit/api"
7 8
 	"github.com/docker/swarmkit/identity"
9
+	"github.com/docker/swarmkit/manager/allocator/networkallocator"
8 10
 	"github.com/docker/swarmkit/manager/state/store"
9 11
 	"golang.org/x/net/context"
10 12
 	"google.golang.org/grpc"
... ...
@@ -57,6 +59,10 @@ func validateIPAM(ipam *api.IPAMOptions) error {
57 57
 		return err
58 58
 	}
59 59
 
60
+	if ipam.Driver != nil && ipam.Driver.Name != ipamapi.DefaultIPAM {
61
+		return grpc.Errorf(codes.InvalidArgument, "invalid IPAM specified")
62
+	}
63
+
60 64
 	for _, ipamConf := range ipam.Configs {
61 65
 		if err := validateIPAMConfiguration(ipamConf); err != nil {
62 66
 			return err
... ...
@@ -79,6 +85,10 @@ func validateNetworkSpec(spec *api.NetworkSpec) error {
79 79
 		return err
80 80
 	}
81 81
 
82
+	if spec.DriverConfig != nil && spec.DriverConfig.Name != networkallocator.DefaultDriver {
83
+		return grpc.Errorf(codes.InvalidArgument, "invalid driver specified")
84
+	}
85
+
82 86
 	if err := validateIPAM(spec.IPAM); err != nil {
83 87
 		return err
84 88
 	}
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/docker/swarmkit/api"
9 9
 	"github.com/docker/swarmkit/identity"
10 10
 	"github.com/docker/swarmkit/manager/state/store"
11
+	"github.com/docker/swarmkit/protobuf/ptypes"
11 12
 	"golang.org/x/net/context"
12 13
 	"google.golang.org/grpc"
13 14
 	"google.golang.org/grpc/codes"
... ...
@@ -15,6 +16,7 @@ import (
15 15
 
16 16
 var (
17 17
 	errNetworkUpdateNotSupported = errors.New("changing network in service is not supported")
18
+	errModeChangeNotAllowed      = errors.New("service mode change is not allowed")
18 19
 )
19 20
 
20 21
 func validateResources(r *api.Resources) error {
... ...
@@ -45,21 +47,70 @@ func validateResourceRequirements(r *api.ResourceRequirements) error {
45 45
 	return nil
46 46
 }
47 47
 
48
-func validateServiceSpecTemplate(spec *api.ServiceSpec) error {
49
-	if err := validateResourceRequirements(spec.Task.Resources); err != nil {
48
+func validateRestartPolicy(rp *api.RestartPolicy) error {
49
+	if rp == nil {
50
+		return nil
51
+	}
52
+
53
+	if rp.Delay != nil {
54
+		delay, err := ptypes.Duration(rp.Delay)
55
+		if err != nil {
56
+			return err
57
+		}
58
+		if delay < 0 {
59
+			return grpc.Errorf(codes.InvalidArgument, "TaskSpec: restart-delay cannot be negative")
60
+		}
61
+	}
62
+
63
+	if rp.Window != nil {
64
+		win, err := ptypes.Duration(rp.Window)
65
+		if err != nil {
66
+			return err
67
+		}
68
+		if win < 0 {
69
+			return grpc.Errorf(codes.InvalidArgument, "TaskSpec: restart-window cannot be negative")
70
+		}
71
+	}
72
+
73
+	return nil
74
+}
75
+
76
+func validateUpdate(uc *api.UpdateConfig) error {
77
+	if uc == nil {
78
+		return nil
79
+	}
80
+
81
+	delay, err := ptypes.Duration(&uc.Delay)
82
+	if err != nil {
83
+		return err
84
+	}
85
+
86
+	if delay < 0 {
87
+		return grpc.Errorf(codes.InvalidArgument, "TaskSpec: update-delay cannot be negative")
88
+	}
89
+
90
+	return nil
91
+}
92
+
93
+func validateTask(taskSpec api.TaskSpec) error {
94
+	if err := validateResourceRequirements(taskSpec.Resources); err != nil {
50 95
 		return err
51 96
 	}
52 97
 
53
-	if spec.Task.GetRuntime() == nil {
98
+	if err := validateRestartPolicy(taskSpec.Restart); err != nil {
99
+		return err
100
+	}
101
+
102
+	if taskSpec.GetRuntime() == nil {
54 103
 		return grpc.Errorf(codes.InvalidArgument, "TaskSpec: missing runtime")
55 104
 	}
56 105
 
57
-	_, ok := spec.Task.GetRuntime().(*api.TaskSpec_Container)
106
+	_, ok := taskSpec.GetRuntime().(*api.TaskSpec_Container)
58 107
 	if !ok {
59 108
 		return grpc.Errorf(codes.Unimplemented, "RuntimeSpec: unimplemented runtime in service spec")
60 109
 	}
61 110
 
62
-	container := spec.Task.GetContainer()
111
+	container := taskSpec.GetContainer()
63 112
 	if container == nil {
64 113
 		return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: missing in service spec")
65 114
 	}
... ...
@@ -99,7 +150,13 @@ func validateServiceSpec(spec *api.ServiceSpec) error {
99 99
 	if err := validateAnnotations(spec.Annotations); err != nil {
100 100
 		return err
101 101
 	}
102
-	if err := validateServiceSpecTemplate(spec); err != nil {
102
+	if err := validateTask(spec.Task); err != nil {
103
+		return err
104
+	}
105
+	if err := validateUpdate(spec.Update); err != nil {
106
+		return err
107
+	}
108
+	if err := validateEndpointSpec(spec.Endpoint); err != nil {
103 109
 		return err
104 110
 	}
105 111
 	return nil
... ...
@@ -179,6 +236,12 @@ func (s *Server) UpdateService(ctx context.Context, request *api.UpdateServiceRe
179 179
 			return errNetworkUpdateNotSupported
180 180
 		}
181 181
 
182
+		// orchestrator is designed to be stateless, so it should not deal
183
+		// with service mode change (comparing current config with previous config).
184
+		// proper way to change service mode is to delete and re-add.
185
+		if request.Spec != nil && reflect.TypeOf(service.Spec.Mode) != reflect.TypeOf(request.Spec.Mode) {
186
+			return errModeChangeNotAllowed
187
+		}
182 188
 		service.Meta.Version = *request.ServiceVersion
183 189
 		service.Spec = *request.Spec.Copy()
184 190
 		return store.UpdateService(tx, service)
... ...
@@ -29,6 +29,7 @@ const (
29 29
 	DefaultHeartBeatPeriod       = 5 * time.Second
30 30
 	defaultHeartBeatEpsilon      = 500 * time.Millisecond
31 31
 	defaultGracePeriodMultiplier = 3
32
+	defaultRateLimitPeriod       = 16 * time.Second
32 33
 
33 34
 	// maxBatchItems is the threshold of queued writes that should
34 35
 	// trigger an actual transaction to commit them to the shared store.
... ...
@@ -59,9 +60,12 @@ var (
59 59
 // DefautConfig.
60 60
 type Config struct {
61 61
 	// Addr configures the address the dispatcher reports to agents.
62
-	Addr                  string
63
-	HeartbeatPeriod       time.Duration
64
-	HeartbeatEpsilon      time.Duration
62
+	Addr             string
63
+	HeartbeatPeriod  time.Duration
64
+	HeartbeatEpsilon time.Duration
65
+	// RateLimitPeriod specifies how often node with same ID can try to register
66
+	// new session.
67
+	RateLimitPeriod       time.Duration
65 68
 	GracePeriodMultiplier int
66 69
 }
67 70
 
... ...
@@ -70,6 +74,7 @@ func DefaultConfig() *Config {
70 70
 	return &Config{
71 71
 		HeartbeatPeriod:       DefaultHeartBeatPeriod,
72 72
 		HeartbeatEpsilon:      defaultHeartBeatEpsilon,
73
+		RateLimitPeriod:       defaultRateLimitPeriod,
73 74
 		GracePeriodMultiplier: defaultGracePeriodMultiplier,
74 75
 	}
75 76
 }
... ...
@@ -116,12 +121,11 @@ func (b weightedPeerByNodeID) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
116 116
 func New(cluster Cluster, c *Config) *Dispatcher {
117 117
 	return &Dispatcher{
118 118
 		addr:                      c.Addr,
119
-		nodes:                     newNodeStore(c.HeartbeatPeriod, c.HeartbeatEpsilon, c.GracePeriodMultiplier),
119
+		nodes:                     newNodeStore(c.HeartbeatPeriod, c.HeartbeatEpsilon, c.GracePeriodMultiplier, c.RateLimitPeriod),
120 120
 		store:                     cluster.MemoryStore(),
121 121
 		cluster:                   cluster,
122 122
 		mgrQueue:                  watch.NewQueue(16),
123 123
 		keyMgrQueue:               watch.NewQueue(16),
124
-		lastSeenManagers:          getWeightedPeers(cluster),
125 124
 		taskUpdates:               make(map[string]*api.TaskStatus),
126 125
 		processTaskUpdatesTrigger: make(chan struct{}, 1),
127 126
 		config: c,
... ...
@@ -149,12 +153,12 @@ func (d *Dispatcher) Run(ctx context.Context) error {
149 149
 	d.mu.Lock()
150 150
 	if d.isRunning() {
151 151
 		d.mu.Unlock()
152
-		return fmt.Errorf("dispatcher is stopped")
152
+		return fmt.Errorf("dispatcher is already running")
153 153
 	}
154 154
 	logger := log.G(ctx).WithField("module", "dispatcher")
155 155
 	ctx = log.WithLogger(ctx, logger)
156 156
 	if err := d.markNodesUnknown(ctx); err != nil {
157
-		logger.Errorf("failed to mark all nodes unknown: %v", err)
157
+		logger.Errorf(`failed to move all nodes to "unknown" state: %v`, err)
158 158
 	}
159 159
 	configWatcher, cancel, err := store.ViewAndWatch(
160 160
 		d.store,
... ...
@@ -177,6 +181,7 @@ func (d *Dispatcher) Run(ctx context.Context) error {
177 177
 		state.EventUpdateCluster{},
178 178
 	)
179 179
 	if err != nil {
180
+		d.mu.Unlock()
180 181
 		return err
181 182
 	}
182 183
 	defer cancel()
... ...
@@ -238,6 +243,7 @@ func (d *Dispatcher) Run(ctx context.Context) error {
238 238
 func (d *Dispatcher) Stop() error {
239 239
 	d.mu.Lock()
240 240
 	if !d.isRunning() {
241
+		d.mu.Unlock()
241 242
 		return fmt.Errorf("dispatcher is already stopped")
242 243
 	}
243 244
 	d.cancel()
... ...
@@ -280,20 +286,20 @@ func (d *Dispatcher) markNodesUnknown(ctx context.Context) error {
280 280
 				}
281 281
 				node.Status = api.NodeStatus{
282 282
 					State:   api.NodeStatus_UNKNOWN,
283
-					Message: "Node marked as unknown due to leadership change in cluster",
283
+					Message: `Node moved to "unknown" state due to leadership change in cluster`,
284 284
 				}
285 285
 				nodeID := node.ID
286 286
 
287 287
 				expireFunc := func() {
288 288
 					log := log.WithField("node", nodeID)
289
-					nodeStatus := api.NodeStatus{State: api.NodeStatus_DOWN, Message: "heartbeat failure for unknown node"}
289
+					nodeStatus := api.NodeStatus{State: api.NodeStatus_DOWN, Message: `heartbeat failure for node in "unknown" state`}
290 290
 					log.Debugf("heartbeat expiration for unknown node")
291 291
 					if err := d.nodeRemove(nodeID, nodeStatus); err != nil {
292
-						log.WithError(err).Errorf("failed deregistering node after heartbeat expiration for unknown node")
292
+						log.WithError(err).Errorf(`failed deregistering node after heartbeat expiration for node in "unknown" state`)
293 293
 					}
294 294
 				}
295 295
 				if err := d.nodes.AddUnknown(node, expireFunc); err != nil {
296
-					return fmt.Errorf("add unknown node failed: %v", err)
296
+					return fmt.Errorf(`adding node in "unknown" state to node store failed: %v`, err)
297 297
 				}
298 298
 				if err := store.UpdateNode(tx, node); err != nil {
299 299
 					return fmt.Errorf("update failed %v", err)
... ...
@@ -301,7 +307,7 @@ func (d *Dispatcher) markNodesUnknown(ctx context.Context) error {
301 301
 				return nil
302 302
 			})
303 303
 			if err != nil {
304
-				log.WithField("node", n.ID).WithError(err).Errorf("failed to mark node as unknown")
304
+				log.WithField("node", n.ID).WithError(err).Errorf(`failed to move node to "unknown" state`)
305 305
 			}
306 306
 		}
307 307
 		return nil
... ...
@@ -328,6 +334,10 @@ func (d *Dispatcher) register(ctx context.Context, nodeID string, description *a
328 328
 		return "", "", err
329 329
 	}
330 330
 
331
+	if err := d.nodes.CheckRateLimit(nodeID); err != nil {
332
+		return "", "", err
333
+	}
334
+
331 335
 	// create or update node in store
332 336
 	// TODO(stevvooe): Validate node specification.
333 337
 	var node *api.Node
... ...
@@ -15,6 +15,7 @@ import (
15 15
 type registeredNode struct {
16 16
 	SessionID  string
17 17
 	Heartbeat  *heartbeat.Heartbeat
18
+	Registered time.Time
18 19
 	Node       *api.Node
19 20
 	Disconnect chan struct{} // signal to disconnect
20 21
 	mu         sync.Mutex
... ...
@@ -41,15 +42,17 @@ func (rn *registeredNode) checkSessionID(sessionID string) error {
41 41
 type nodeStore struct {
42 42
 	periodChooser         *periodChooser
43 43
 	gracePeriodMultiplier time.Duration
44
+	rateLimitPeriod       time.Duration
44 45
 	nodes                 map[string]*registeredNode
45 46
 	mu                    sync.RWMutex
46 47
 }
47 48
 
48
-func newNodeStore(hbPeriod, hbEpsilon time.Duration, graceMultiplier int) *nodeStore {
49
+func newNodeStore(hbPeriod, hbEpsilon time.Duration, graceMultiplier int, rateLimitPeriod time.Duration) *nodeStore {
49 50
 	return &nodeStore{
50 51
 		nodes:                 make(map[string]*registeredNode),
51 52
 		periodChooser:         newPeriodChooser(hbPeriod, hbEpsilon),
52 53
 		gracePeriodMultiplier: time.Duration(graceMultiplier),
54
+		rateLimitPeriod:       rateLimitPeriod,
53 55
 	}
54 56
 }
55 57
 
... ...
@@ -77,6 +80,19 @@ func (s *nodeStore) AddUnknown(n *api.Node, expireFunc func()) error {
77 77
 	return nil
78 78
 }
79 79
 
80
+// CheckRateLimit returs error if node with specified id is allowed to re-register
81
+// again.
82
+func (s *nodeStore) CheckRateLimit(id string) error {
83
+	s.mu.Lock()
84
+	defer s.mu.Unlock()
85
+	if existRn, ok := s.nodes[id]; ok {
86
+		if time.Since(existRn.Registered) < s.rateLimitPeriod {
87
+			return grpc.Errorf(codes.Unavailable, "node %s attempted registration too recently", id)
88
+		}
89
+	}
90
+	return nil
91
+}
92
+
80 93
 // Add adds new node and returns it, it replaces existing without notification.
81 94
 func (s *nodeStore) Add(n *api.Node, expireFunc func()) *registeredNode {
82 95
 	s.mu.Lock()
... ...
@@ -88,6 +104,7 @@ func (s *nodeStore) Add(n *api.Node, expireFunc func()) *registeredNode {
88 88
 	rn := &registeredNode{
89 89
 		SessionID:  identity.NewID(), // session ID is local to the dispatcher.
90 90
 		Node:       n,
91
+		Registered: time.Now(),
91 92
 		Disconnect: make(chan struct{}),
92 93
 	}
93 94
 	s.nodes[n.ID] = rn
94 95
new file mode 100644
... ...
@@ -0,0 +1,58 @@
0
+// Package health provides some utility functions to health-check a server. The implementation
1
+// is based on protobuf. Users need to write their own implementations if other IDLs are used.
2
+//
3
+// See original source: https://github.com/grpc/grpc-go/blob/master/health/health.go
4
+//
5
+// We use our own implementation of grpc server health check to include the authorization
6
+// wrapper necessary for the Managers.
7
+package health
8
+
9
+import (
10
+	"sync"
11
+
12
+	"github.com/docker/swarmkit/api"
13
+	"golang.org/x/net/context"
14
+	"google.golang.org/grpc"
15
+	"google.golang.org/grpc/codes"
16
+)
17
+
18
+// Server represents a Health Check server to check
19
+// if a service is running or not on some host.
20
+type Server struct {
21
+	mu sync.Mutex
22
+	// statusMap stores the serving status of the services this HealthServer monitors.
23
+	statusMap map[string]api.HealthCheckResponse_ServingStatus
24
+}
25
+
26
+// NewHealthServer creates a new health check server for grpc services.
27
+func NewHealthServer() *Server {
28
+	return &Server{
29
+		statusMap: make(map[string]api.HealthCheckResponse_ServingStatus),
30
+	}
31
+}
32
+
33
+// Check checks if the grpc server is healthy and running.
34
+func (s *Server) Check(ctx context.Context, in *api.HealthCheckRequest) (*api.HealthCheckResponse, error) {
35
+	s.mu.Lock()
36
+	defer s.mu.Unlock()
37
+	if in.Service == "" {
38
+		// check the server overall health status.
39
+		return &api.HealthCheckResponse{
40
+			Status: api.HealthCheckResponse_SERVING,
41
+		}, nil
42
+	}
43
+	if status, ok := s.statusMap[in.Service]; ok {
44
+		return &api.HealthCheckResponse{
45
+			Status: status,
46
+		}, nil
47
+	}
48
+	return nil, grpc.Errorf(codes.NotFound, "unknown service")
49
+}
50
+
51
+// SetServingStatus is called when need to reset the serving status of a service
52
+// or insert a new service entry into the statusMap.
53
+func (s *Server) SetServingStatus(service string, status api.HealthCheckResponse_ServingStatus) {
54
+	s.mu.Lock()
55
+	s.statusMap[service] = status
56
+	s.mu.Unlock()
57
+}
... ...
@@ -32,6 +32,8 @@ const (
32 32
 
33 33
 	// DefaultSubsystem is gossip
34 34
 	DefaultSubsystem = SubsystemGossip
35
+	// number of keys to mainrain in the key ring.
36
+	keyringSize = 3
35 37
 )
36 38
 
37 39
 // map of subsystems and corresponding encryption algorithm. Initially only
... ...
@@ -59,7 +61,6 @@ type KeyManager struct {
59 59
 	config  *Config
60 60
 	store   *store.MemoryStore
61 61
 	keyRing *keyRing
62
-	ticker  *time.Ticker
63 62
 	ctx     context.Context
64 63
 	cancel  context.CancelFunc
65 64
 
... ...
@@ -72,7 +73,7 @@ func DefaultConfig() *Config {
72 72
 		ClusterName:      store.DefaultClusterName,
73 73
 		Keylen:           DefaultKeyLen,
74 74
 		RotationInterval: DefaultKeyRotationInterval,
75
-		Subsystems:       []string{DefaultSubsystem},
75
+		Subsystems:       []string{SubsystemGossip, SubsystemIPSec},
76 76
 	}
77 77
 }
78 78
 
... ...
@@ -148,7 +149,7 @@ func (k *KeyManager) rotateKey(ctx context.Context) error {
148 148
 	// We maintain the latest key and the one before in the key ring to allow
149 149
 	// agents to communicate without disruption on key change.
150 150
 	for subsys, keys := range subsysKeys {
151
-		if len(keys) > 1 {
151
+		if len(keys) == keyringSize {
152 152
 			min := 0
153 153
 			for i, key := range keys[1:] {
154 154
 				if key.LamportTime < keys[min].LamportTime {
... ...
@@ -189,7 +190,9 @@ func (k *KeyManager) Run(ctx context.Context) error {
189 189
 	cluster := clusters[0]
190 190
 	if len(cluster.NetworkBootstrapKeys) == 0 {
191 191
 		for _, subsys := range k.config.Subsystems {
192
-			k.keyRing.keys = append(k.keyRing.keys, k.allocateKey(ctx, subsys))
192
+			for i := 0; i < keyringSize; i++ {
193
+				k.keyRing.keys = append(k.keyRing.keys, k.allocateKey(ctx, subsys))
194
+			}
193 195
 		}
194 196
 		if err := k.updateKey(cluster); err != nil {
195 197
 			log.Errorf("store update failed %v", err)
... ...
@@ -19,6 +19,7 @@ import (
19 19
 	"github.com/docker/swarmkit/manager/allocator"
20 20
 	"github.com/docker/swarmkit/manager/controlapi"
21 21
 	"github.com/docker/swarmkit/manager/dispatcher"
22
+	"github.com/docker/swarmkit/manager/health"
22 23
 	"github.com/docker/swarmkit/manager/keymanager"
23 24
 	"github.com/docker/swarmkit/manager/orchestrator"
24 25
 	"github.com/docker/swarmkit/manager/raftpicker"
... ...
@@ -39,6 +40,10 @@ const (
39 39
 type Config struct {
40 40
 	SecurityConfig *ca.SecurityConfig
41 41
 
42
+	// ExternalCAs is a list of initial CAs to which a manager node
43
+	// will make certificate signing requests for node certificates.
44
+	ExternalCAs []*api.ExternalCA
45
+
42 46
 	ProtoAddr map[string]string
43 47
 	// ProtoListener will be used for grpc serving if it's not nil,
44 48
 	// ProtoAddr fields will be used to create listeners otherwise.
... ...
@@ -83,8 +88,7 @@ type Manager struct {
83 83
 	localserver            *grpc.Server
84 84
 	RaftNode               *raft.Node
85 85
 
86
-	mu   sync.Mutex
87
-	once sync.Once
86
+	mu sync.Mutex
88 87
 
89 88
 	stopped chan struct{}
90 89
 }
... ...
@@ -202,13 +206,7 @@ func New(config *Config) (*Manager, error) {
202 202
 		ForceNewCluster: config.ForceNewCluster,
203 203
 		TLSCredentials:  config.SecurityConfig.ClientTLSCreds,
204 204
 	}
205
-	RaftNode, err := raft.NewNode(context.TODO(), newNodeOpts)
206
-	if err != nil {
207
-		for _, lis := range listeners {
208
-			lis.Close()
209
-		}
210
-		return nil, fmt.Errorf("can't create raft node: %v", err)
211
-	}
205
+	RaftNode := raft.NewNode(context.TODO(), newNodeOpts)
212 206
 
213 207
 	opts := []grpc.ServerOption{
214 208
 		grpc.Creds(config.SecurityConfig.ServerTLSCreds)}
... ...
@@ -275,6 +273,10 @@ func (m *Manager) Run(parent context.Context) error {
275 275
 				raftCfg.HeartbeatTick = uint32(m.RaftNode.Config.HeartbeatTick)
276 276
 
277 277
 				clusterID := m.config.SecurityConfig.ClientTLSCreds.Organization()
278
+
279
+				initialCAConfig := ca.DefaultCAConfig()
280
+				initialCAConfig.ExternalCAs = m.config.ExternalCAs
281
+
278 282
 				s.Update(func(tx store.Tx) error {
279 283
 					// Add a default cluster object to the
280 284
 					// store. Don't check the error because
... ...
@@ -294,7 +296,7 @@ func (m *Manager) Run(parent context.Context) error {
294 294
 								HeartbeatPeriod: ptypes.DurationProto(dispatcher.DefaultHeartBeatPeriod),
295 295
 							},
296 296
 							Raft:     raftCfg,
297
-							CAConfig: ca.DefaultCAConfig(),
297
+							CAConfig: initialCAConfig,
298 298
 						},
299 299
 						RootCA: api.RootCA{
300 300
 							CAKey:      rootCA.Key,
... ...
@@ -327,7 +329,7 @@ func (m *Manager) Run(parent context.Context) error {
327 327
 					log.G(ctx).WithError(err).Error("root key-encrypting-key rotation failed")
328 328
 				}
329 329
 
330
-				m.replicatedOrchestrator = orchestrator.New(s)
330
+				m.replicatedOrchestrator = orchestrator.NewReplicatedOrchestrator(s)
331 331
 				m.globalOrchestrator = orchestrator.NewGlobalOrchestrator(s)
332 332
 				m.taskReaper = orchestrator.NewTaskReaper(s)
333 333
 				m.scheduler = scheduler.New(s)
... ...
@@ -421,14 +423,6 @@ func (m *Manager) Run(parent context.Context) error {
421 421
 		}
422 422
 	}()
423 423
 
424
-	go func() {
425
-		err := m.RaftNode.Run(ctx)
426
-		if err != nil {
427
-			log.G(ctx).Error(err)
428
-			m.Stop(ctx)
429
-		}
430
-	}()
431
-
432 424
 	proxyOpts := []grpc.DialOption{
433 425
 		grpc.WithBackoffMaxDelay(2 * time.Second),
434 426
 		grpc.WithTransportCredentials(m.config.SecurityConfig.ClientTLSCreds),
... ...
@@ -443,12 +437,14 @@ func (m *Manager) Run(parent context.Context) error {
443 443
 	}
444 444
 
445 445
 	baseControlAPI := controlapi.NewServer(m.RaftNode.MemoryStore(), m.RaftNode)
446
+	healthServer := health.NewHealthServer()
446 447
 
447 448
 	authenticatedControlAPI := api.NewAuthenticatedWrapperControlServer(baseControlAPI, authorize)
448 449
 	authenticatedDispatcherAPI := api.NewAuthenticatedWrapperDispatcherServer(m.Dispatcher, authorize)
449 450
 	authenticatedCAAPI := api.NewAuthenticatedWrapperCAServer(m.caserver, authorize)
450 451
 	authenticatedNodeCAAPI := api.NewAuthenticatedWrapperNodeCAServer(m.caserver, authorize)
451 452
 	authenticatedRaftAPI := api.NewAuthenticatedWrapperRaftServer(m.RaftNode, authorize)
453
+	authenticatedHealthAPI := api.NewAuthenticatedWrapperHealthServer(healthServer, authorize)
452 454
 	authenticatedRaftMembershipAPI := api.NewAuthenticatedWrapperRaftMembershipServer(m.RaftNode, authorize)
453 455
 
454 456
 	proxyDispatcherAPI := api.NewRaftProxyDispatcherServer(authenticatedDispatcherAPI, cs, m.RaftNode, ca.WithMetadataForwardTLSInfo)
... ...
@@ -470,6 +466,7 @@ func (m *Manager) Run(parent context.Context) error {
470 470
 	api.RegisterCAServer(m.server, proxyCAAPI)
471 471
 	api.RegisterNodeCAServer(m.server, proxyNodeCAAPI)
472 472
 	api.RegisterRaftServer(m.server, authenticatedRaftAPI)
473
+	api.RegisterHealthServer(m.server, authenticatedHealthAPI)
473 474
 	api.RegisterRaftMembershipServer(m.server, proxyRaftMembershipAPI)
474 475
 	api.RegisterControlServer(m.localserver, localProxyControlAPI)
475 476
 	api.RegisterControlServer(m.server, authenticatedControlAPI)
... ...
@@ -492,6 +489,24 @@ func (m *Manager) Run(parent context.Context) error {
492 492
 		}(proto, l)
493 493
 	}
494 494
 
495
+	// Set the raft server as serving for the health server
496
+	healthServer.SetServingStatus("Raft", api.HealthCheckResponse_SERVING)
497
+
498
+	if err := m.RaftNode.JoinAndStart(); err != nil {
499
+		for _, lis := range m.listeners {
500
+			lis.Close()
501
+		}
502
+		return fmt.Errorf("can't initialize raft node: %v", err)
503
+	}
504
+
505
+	go func() {
506
+		err := m.RaftNode.Run(ctx)
507
+		if err != nil {
508
+			log.G(ctx).Error(err)
509
+			m.Stop(ctx)
510
+		}
511
+	}()
512
+
495 513
 	if err := raft.WaitForLeader(ctx, m.RaftNode); err != nil {
496 514
 		m.server.Stop()
497 515
 		return err
... ...
@@ -255,7 +255,7 @@ func (g *GlobalOrchestrator) reconcileOneNode(ctx context.Context, node *api.Nod
255 255
 		return
256 256
 	}
257 257
 	// typically there are only a few global services on a node
258
-	// iterate thru all of them one by one. If raft store visits become a concern,
258
+	// iterate through all of them one by one. If raft store visits become a concern,
259 259
 	// it can be optimized.
260 260
 	for _, service := range g.globalServices {
261 261
 		g.reconcileServiceOneNode(ctx, service.ID, node.ID)
... ...
@@ -29,8 +29,8 @@ type ReplicatedOrchestrator struct {
29 29
 	restarts *RestartSupervisor
30 30
 }
31 31
 
32
-// New creates a new ReplicatedOrchestrator.
33
-func New(store *store.MemoryStore) *ReplicatedOrchestrator {
32
+// NewReplicatedOrchestrator creates a new ReplicatedOrchestrator.
33
+func NewReplicatedOrchestrator(store *store.MemoryStore) *ReplicatedOrchestrator {
34 34
 	restartSupervisor := NewRestartSupervisor(store)
35 35
 	updater := NewUpdateSupervisor(store, restartSupervisor)
36 36
 	return &ReplicatedOrchestrator{
... ...
@@ -114,6 +114,9 @@ func newTask(service *api.Service, instance uint64) *api.Task {
114 114
 			Timestamp: ptypes.MustTimestampProto(time.Now()),
115 115
 			Message:   "created",
116 116
 		},
117
+		Endpoint: &api.Endpoint{
118
+			Spec: service.Spec.Endpoint.Copy(),
119
+		},
117 120
 		DesiredState: api.TaskStateRunning,
118 121
 	}
119 122
 }
... ...
@@ -1,6 +1,8 @@
1 1
 package orchestrator
2 2
 
3 3
 import (
4
+	"sort"
5
+
4 6
 	"github.com/docker/go-events"
5 7
 	"github.com/docker/swarmkit/api"
6 8
 	"github.com/docker/swarmkit/log"
... ...
@@ -68,6 +70,27 @@ func (r *ReplicatedOrchestrator) resolveService(ctx context.Context, task *api.T
68 68
 	return service
69 69
 }
70 70
 
71
+type taskWithIndex struct {
72
+	task *api.Task
73
+
74
+	// index is a counter that counts this task as the nth instance of
75
+	// the service on its node. This is used for sorting the tasks so that
76
+	// when scaling down we leave tasks more evenly balanced.
77
+	index int
78
+}
79
+
80
+type tasksByIndex []taskWithIndex
81
+
82
+func (ts tasksByIndex) Len() int      { return len(ts) }
83
+func (ts tasksByIndex) Swap(i, j int) { ts[i], ts[j] = ts[j], ts[i] }
84
+
85
+func (ts tasksByIndex) Less(i, j int) bool {
86
+	if ts[i].index < 0 {
87
+		return false
88
+	}
89
+	return ts[i].index < ts[j].index
90
+}
91
+
71 92
 func (r *ReplicatedOrchestrator) reconcile(ctx context.Context, service *api.Service) {
72 93
 	var (
73 94
 		tasks []*api.Task
... ...
@@ -97,8 +120,6 @@ func (r *ReplicatedOrchestrator) reconcile(ctx context.Context, service *api.Ser
97 97
 	deploy := service.Spec.GetMode().(*api.ServiceSpec_Replicated)
98 98
 	specifiedInstances := int(deploy.Replicated.Replicas)
99 99
 
100
-	// TODO(aaronl): Add support for restart delays.
101
-
102 100
 	switch {
103 101
 	case specifiedInstances > numTasks:
104 102
 		log.G(ctx).Debugf("Service %s was scaled up from %d to %d instances", service.ID, numTasks, specifiedInstances)
... ...
@@ -115,9 +136,35 @@ func (r *ReplicatedOrchestrator) reconcile(ctx context.Context, service *api.Ser
115 115
 	case specifiedInstances < numTasks:
116 116
 		// Update up to N tasks then remove the extra
117 117
 		log.G(ctx).Debugf("Service %s was scaled down from %d to %d instances", service.ID, numTasks, specifiedInstances)
118
-		r.updater.Update(ctx, service, runningTasks[:specifiedInstances])
118
+
119
+		// Preferentially remove tasks on the nodes that have the most
120
+		// copies of this service, to leave a more balanced result.
121
+		// Assign each task an index that counts it as the nth copy of
122
+		// of the service on its node (1, 2, 3, ...), and sort the
123
+		// tasks by this counter value.
124
+
125
+		instancesByNode := make(map[string]int)
126
+		tasksWithIndices := make(tasksByIndex, 0, numTasks)
127
+
128
+		for _, t := range runningTasks {
129
+			if t.NodeID != "" {
130
+				instancesByNode[t.NodeID]++
131
+				tasksWithIndices = append(tasksWithIndices, taskWithIndex{task: t, index: instancesByNode[t.NodeID]})
132
+			} else {
133
+				tasksWithIndices = append(tasksWithIndices, taskWithIndex{task: t, index: -1})
134
+			}
135
+		}
136
+
137
+		sort.Sort(tasksWithIndices)
138
+
139
+		sortedTasks := make([]*api.Task, 0, numTasks)
140
+		for _, t := range tasksWithIndices {
141
+			sortedTasks = append(sortedTasks, t.task)
142
+		}
143
+
144
+		r.updater.Update(ctx, service, sortedTasks[:specifiedInstances])
119 145
 		_, err = r.store.Batch(func(batch *store.Batch) error {
120
-			r.removeTasks(ctx, batch, service, runningTasks[specifiedInstances:])
146
+			r.removeTasks(ctx, batch, service, sortedTasks[specifiedInstances:])
121 147
 			return nil
122 148
 		})
123 149
 		if err != nil {
... ...
@@ -104,7 +104,8 @@ func (u *Updater) Run(ctx context.Context, service *api.Service, tasks []*api.Ta
104 104
 	dirtyTasks := []*api.Task{}
105 105
 	for _, t := range tasks {
106 106
 		if !reflect.DeepEqual(service.Spec.Task, t.Spec) ||
107
-			!reflect.DeepEqual(service.Endpoint, t.Endpoint) {
107
+			(t.Endpoint != nil &&
108
+				!reflect.DeepEqual(service.Spec.Endpoint, t.Endpoint.Spec)) {
108 109
 			dirtyTasks = append(dirtyTasks, t)
109 110
 		}
110 111
 	}
... ...
@@ -191,6 +192,9 @@ func (u *Updater) updateTask(ctx context.Context, service *api.Service, original
191 191
 		if t == nil {
192 192
 			return fmt.Errorf("task %s not found while trying to update it", original.ID)
193 193
 		}
194
+		if t.DesiredState > api.TaskStateRunning {
195
+			return fmt.Errorf("task %s was already shut down when reached by updater", original.ID)
196
+		}
194 197
 		t.DesiredState = api.TaskStateShutdown
195 198
 		if err := store.UpdateTask(tx, t); err != nil {
196 199
 			return err
... ...
@@ -48,13 +48,6 @@ func (nh *nodeHeap) alloc(n int) {
48 48
 	nh.index = make(map[string]int, n)
49 49
 }
50 50
 
51
-func (nh *nodeHeap) peek() *NodeInfo {
52
-	if len(nh.heap) == 0 {
53
-		return nil
54
-	}
55
-	return &nh.heap[0]
56
-}
57
-
58 51
 // nodeInfo returns the NodeInfo struct for a given node identified by its ID.
59 52
 func (nh *nodeHeap) nodeInfo(nodeID string) NodeInfo {
60 53
 	index, ok := nh.index[nodeID]
... ...
@@ -95,9 +88,7 @@ func (nh *nodeHeap) updateNode(n NodeInfo) {
95 95
 func (nh *nodeHeap) remove(nodeID string) {
96 96
 	index, ok := nh.index[nodeID]
97 97
 	if ok {
98
-		nh.heap[index].Tasks = nil
99
-		heap.Fix(nh, index)
100
-		heap.Pop(nh)
98
+		heap.Remove(nh, index)
101 99
 	}
102 100
 }
103 101
 
... ...
@@ -27,8 +27,6 @@ var (
27 27
 // Cluster represents a set of active
28 28
 // raft Members
29 29
 type Cluster struct {
30
-	id uint64
31
-
32 30
 	mu      sync.RWMutex
33 31
 	members map[uint64]*Member
34 32
 
... ...
@@ -103,17 +101,15 @@ func (c *Cluster) RemoveMember(id uint64) error {
103 103
 	c.mu.Lock()
104 104
 	defer c.mu.Unlock()
105 105
 
106
-	if c.members[id] == nil {
107
-		return ErrIDNotFound
108
-	}
109
-
110
-	conn := c.members[id].Conn
111
-	if conn != nil {
112
-		_ = conn.Close()
106
+	if c.members[id] != nil {
107
+		conn := c.members[id].Conn
108
+		if conn != nil {
109
+			_ = conn.Close()
110
+		}
111
+		delete(c.members, id)
113 112
 	}
114 113
 
115 114
 	c.removed[id] = true
116
-	delete(c.members, id)
117 115
 	return nil
118 116
 }
119 117
 
... ...
@@ -31,6 +31,11 @@ import (
31 31
 )
32 32
 
33 33
 var (
34
+	// ErrHealthCheckFailure is returned when there is an issue with the initial handshake which means
35
+	// that the address provided must be invalid or there is ongoing connectivity issues at join time.
36
+	ErrHealthCheckFailure = errors.New("raft: could not connect to prospective new cluster member using its advertised address")
37
+	// ErrNoRaftMember is thrown when the node is not yet part of a raft cluster
38
+	ErrNoRaftMember = errors.New("raft: node is not yet part of a raft cluster")
34 39
 	// ErrConfChangeRefused is returned when there is an issue with the configuration change
35 40
 	ErrConfChangeRefused = errors.New("raft: propose configuration change refused")
36 41
 	// ErrApplyNotSpecified is returned during the creation of a raft node when no apply method was provided
... ...
@@ -83,12 +88,13 @@ type Node struct {
83 83
 	raftStore   *raft.MemoryStorage
84 84
 	memoryStore *store.MemoryStore
85 85
 	Config      *raft.Config
86
+	opts        NewNodeOptions
86 87
 	reqIDGen    *idutil.Generator
87 88
 	wait        *wait
88 89
 	wal         *wal.WAL
89 90
 	snapshotter *snap.Snapshotter
90 91
 	wasLeader   bool
91
-	removed     uint32
92
+	isMember    uint32
92 93
 	joinAddr    string
93 94
 
94 95
 	// waitProp waits for all the proposals to be terminated before
... ...
@@ -103,14 +109,15 @@ type Node struct {
103 103
 	appliedIndex  uint64
104 104
 	snapshotIndex uint64
105 105
 
106
-	ticker              clock.Ticker
107
-	sendTimeout         time.Duration
108
-	stopCh              chan struct{}
109
-	doneCh              chan struct{}
106
+	ticker      clock.Ticker
107
+	sendTimeout time.Duration
108
+	stopCh      chan struct{}
109
+	doneCh      chan struct{}
110
+	// removeRaftCh notifies about node deletion from raft cluster
111
+	removeRaftCh        chan struct{}
112
+	removeRaftOnce      sync.Once
110 113
 	leadershipBroadcast *events.Broadcaster
111 114
 
112
-	startNodePeers []raft.Peer
113
-
114 115
 	// used to coordinate shutdown
115 116
 	stopMu sync.RWMutex
116 117
 	// used for membership management checks
... ...
@@ -153,7 +160,7 @@ func init() {
153 153
 }
154 154
 
155 155
 // NewNode generates a new Raft node
156
-func NewNode(ctx context.Context, opts NewNodeOptions) (*Node, error) {
156
+func NewNode(ctx context.Context, opts NewNodeOptions) *Node {
157 157
 	cfg := opts.Config
158 158
 	if cfg == nil {
159 159
 		cfg = DefaultNodeConfig()
... ...
@@ -173,6 +180,7 @@ func NewNode(ctx context.Context, opts NewNodeOptions) (*Node, error) {
173 173
 		tlsCredentials: opts.TLSCredentials,
174 174
 		raftStore:      raftStore,
175 175
 		Address:        opts.Addr,
176
+		opts:           opts,
176 177
 		Config: &raft.Config{
177 178
 			ElectionTick:    cfg.ElectionTick,
178 179
 			HeartbeatTick:   cfg.HeartbeatTick,
... ...
@@ -184,6 +192,7 @@ func NewNode(ctx context.Context, opts NewNodeOptions) (*Node, error) {
184 184
 		forceNewCluster:     opts.ForceNewCluster,
185 185
 		stopCh:              make(chan struct{}),
186 186
 		doneCh:              make(chan struct{}),
187
+		removeRaftCh:        make(chan struct{}),
187 188
 		StateDir:            opts.StateDir,
188 189
 		joinAddr:            opts.JoinAddr,
189 190
 		sendTimeout:         2 * time.Second,
... ...
@@ -200,13 +209,21 @@ func NewNode(ctx context.Context, opts NewNodeOptions) (*Node, error) {
200 200
 		n.sendTimeout = opts.SendTimeout
201 201
 	}
202 202
 
203
-	loadAndStartErr := n.loadAndStart(ctx, opts.ForceNewCluster)
203
+	n.reqIDGen = idutil.NewGenerator(uint16(n.Config.ID), time.Now())
204
+	n.wait = newWait()
205
+
206
+	return n
207
+}
208
+
209
+// JoinAndStart joins and starts the raft server
210
+func (n *Node) JoinAndStart() error {
211
+	loadAndStartErr := n.loadAndStart(n.Ctx, n.opts.ForceNewCluster)
204 212
 	if loadAndStartErr != nil && loadAndStartErr != errNoWAL {
205 213
 		n.ticker.Stop()
206
-		return nil, loadAndStartErr
214
+		return loadAndStartErr
207 215
 	}
208 216
 
209
-	snapshot, err := raftStore.Snapshot()
217
+	snapshot, err := n.raftStore.Snapshot()
210 218
 	// Snapshot never returns an error
211 219
 	if err != nil {
212 220
 		panic("could not get snapshot of raft store")
... ...
@@ -215,14 +232,12 @@ func NewNode(ctx context.Context, opts NewNodeOptions) (*Node, error) {
215 215
 	n.confState = snapshot.Metadata.ConfState
216 216
 	n.appliedIndex = snapshot.Metadata.Index
217 217
 	n.snapshotIndex = snapshot.Metadata.Index
218
-	n.reqIDGen = idutil.NewGenerator(uint16(n.Config.ID), time.Now())
219
-	n.wait = newWait()
220 218
 
221 219
 	if loadAndStartErr == errNoWAL {
222 220
 		if n.joinAddr != "" {
223 221
 			c, err := n.ConnectToMember(n.joinAddr, 10*time.Second)
224 222
 			if err != nil {
225
-				return nil, err
223
+				return err
226 224
 			}
227 225
 			client := api.NewRaftMembershipClient(c.Conn)
228 226
 			defer func() {
... ...
@@ -235,40 +250,42 @@ func NewNode(ctx context.Context, opts NewNodeOptions) (*Node, error) {
235 235
 				Addr: n.Address,
236 236
 			})
237 237
 			if err != nil {
238
-				return nil, err
238
+				return err
239 239
 			}
240 240
 
241 241
 			n.Config.ID = resp.RaftID
242 242
 
243
-			if _, err := n.createWAL(opts.ID); err != nil {
244
-				return nil, err
243
+			if _, err := n.createWAL(n.opts.ID); err != nil {
244
+				return err
245 245
 			}
246 246
 
247 247
 			n.Node = raft.StartNode(n.Config, []raft.Peer{})
248 248
 
249 249
 			if err := n.registerNodes(resp.Members); err != nil {
250
-				return nil, err
250
+				return err
251 251
 			}
252 252
 		} else {
253 253
 			// First member in the cluster, self-assign ID
254 254
 			n.Config.ID = uint64(rand.Int63()) + 1
255
-			peer, err := n.createWAL(opts.ID)
255
+			peer, err := n.createWAL(n.opts.ID)
256 256
 			if err != nil {
257
-				return nil, err
257
+				return err
258 258
 			}
259 259
 			n.Node = raft.StartNode(n.Config, []raft.Peer{peer})
260 260
 			if err := n.Campaign(n.Ctx); err != nil {
261
-				return nil, err
261
+				return err
262 262
 			}
263 263
 		}
264
-		return n, nil
264
+		atomic.StoreUint32(&n.isMember, 1)
265
+		return nil
265 266
 	}
266 267
 
267 268
 	if n.joinAddr != "" {
268 269
 		n.Config.Logger.Warning("ignoring request to join cluster, because raft state already exists")
269 270
 	}
270 271
 	n.Node = raft.RestartNode(n.Config)
271
-	return n, nil
272
+	atomic.StoreUint32(&n.isMember, 1)
273
+	return nil
272 274
 }
273 275
 
274 276
 // DefaultNodeConfig returns the default config for a
... ...
@@ -377,21 +394,6 @@ func (n *Node) Run(ctx context.Context) error {
377 377
 				}
378 378
 			}
379 379
 
380
-			// If the node was removed from other members,
381
-			// send back an error to the caller to start
382
-			// the shutdown process.
383
-			if n.mustStop() {
384
-				n.stop()
385
-
386
-				// Move WAL and snapshot out of the way, since
387
-				// they are no longer usable.
388
-				if err := n.moveWALAndSnap(); err != nil {
389
-					n.Config.Logger.Error(err)
390
-				}
391
-
392
-				return ErrMemberRemoved
393
-			}
394
-
395 380
 			// Advance the state machine
396 381
 			n.Advance()
397 382
 
... ...
@@ -400,6 +402,19 @@ func (n *Node) Run(ctx context.Context) error {
400 400
 				n.snapshotIndex = snapshotIndex
401 401
 			}
402 402
 			n.snapshotInProgress = nil
403
+		case <-n.removeRaftCh:
404
+			// If the node was removed from other members,
405
+			// send back an error to the caller to start
406
+			// the shutdown process.
407
+			n.stop()
408
+
409
+			// Move WAL and snapshot out of the way, since
410
+			// they are no longer usable.
411
+			if err := n.moveWALAndSnap(); err != nil {
412
+				n.Config.Logger.Error(err)
413
+			}
414
+
415
+			return ErrMemberRemoved
403 416
 		case <-n.stopCh:
404 417
 			n.stop()
405 418
 			return nil
... ...
@@ -434,6 +449,7 @@ func (n *Node) stop() {
434 434
 		}
435 435
 	}
436 436
 	n.Stop()
437
+	n.ticker.Stop()
437 438
 	if err := n.wal.Close(); err != nil {
438 439
 		n.Config.Logger.Errorf("raft: error closing WAL: %v", err)
439 440
 	}
... ...
@@ -442,6 +458,10 @@ func (n *Node) stop() {
442 442
 
443 443
 // IsLeader checks if we are the leader or not
444 444
 func (n *Node) IsLeader() bool {
445
+	if !n.IsMember() {
446
+		return false
447
+	}
448
+
445 449
 	if n.Node.Status().Lead == n.Config.ID {
446 450
 		return true
447 451
 	}
... ...
@@ -450,6 +470,9 @@ func (n *Node) IsLeader() bool {
450 450
 
451 451
 // Leader returns the id of the leader
452 452
 func (n *Node) Leader() uint64 {
453
+	if !n.IsMember() {
454
+		return 0
455
+	}
453 456
 	return n.Node.Status().Lead
454 457
 }
455 458
 
... ...
@@ -479,7 +502,11 @@ func (n *Node) Join(ctx context.Context, req *api.JoinRequest) (*api.JoinRespons
479 479
 	n.membershipLock.Lock()
480 480
 	defer n.membershipLock.Unlock()
481 481
 
482
-	if n.Node == nil {
482
+	if !n.IsMember() {
483
+		return nil, ErrNoRaftMember
484
+	}
485
+
486
+	if n.IsStopped() {
483 487
 		log.WithError(ErrStopped).Errorf(ErrStopped.Error())
484 488
 		return nil, ErrStopped
485 489
 	}
... ...
@@ -497,6 +524,12 @@ func (n *Node) Join(ctx context.Context, req *api.JoinRequest) (*api.JoinRespons
497 497
 		}
498 498
 	}
499 499
 
500
+	// We do not bother submitting a configuration change for the
501
+	// new member if we can't contact it back using its address
502
+	if err := n.checkHealth(ctx, req.Addr, 5*time.Second); err != nil {
503
+		return nil, err
504
+	}
505
+
500 506
 	err = n.addMember(ctx, req.Addr, raftID, nodeInfo.NodeID)
501 507
 	if err != nil {
502 508
 		log.WithError(err).Errorf("failed to add member")
... ...
@@ -516,6 +549,28 @@ func (n *Node) Join(ctx context.Context, req *api.JoinRequest) (*api.JoinRespons
516 516
 	return &api.JoinResponse{Members: nodes, RaftID: raftID}, nil
517 517
 }
518 518
 
519
+// checkHealth tries to contact an aspiring member through its advertised address
520
+// and checks if its raft server is running.
521
+func (n *Node) checkHealth(ctx context.Context, addr string, timeout time.Duration) error {
522
+	conn, err := dial(addr, "tcp", n.tlsCredentials, timeout)
523
+	if err != nil {
524
+		return err
525
+	}
526
+
527
+	client := api.NewHealthClient(conn)
528
+	defer conn.Close()
529
+
530
+	resp, err := client.Check(ctx, &api.HealthCheckRequest{Service: "Raft"})
531
+	if err != nil {
532
+		return ErrHealthCheckFailure
533
+	}
534
+	if resp != nil && resp.Status != api.HealthCheckResponse_SERVING {
535
+		return ErrHealthCheckFailure
536
+	}
537
+
538
+	return nil
539
+}
540
+
519 541
 // addMember submits a configuration change to add a new member on the raft cluster.
520 542
 func (n *Node) addMember(ctx context.Context, addr string, raftID uint64, nodeID string) error {
521 543
 	node := api.RaftMember{
... ...
@@ -563,7 +618,11 @@ func (n *Node) Leave(ctx context.Context, req *api.LeaveRequest) (*api.LeaveResp
563 563
 	n.stopMu.RLock()
564 564
 	defer n.stopMu.RUnlock()
565 565
 
566
-	if n.Node == nil {
566
+	if !n.IsMember() {
567
+		return nil, ErrNoRaftMember
568
+	}
569
+
570
+	if n.IsStopped() {
567 571
 		return nil, ErrStopped
568 572
 	}
569 573
 
... ...
@@ -612,7 +671,12 @@ func (n *Node) ProcessRaftMessage(ctx context.Context, msg *api.ProcessRaftMessa
612 612
 	// can't stop the raft node while an async RPC is in progress
613 613
 	n.stopMu.RLock()
614 614
 	defer n.stopMu.RUnlock()
615
-	if n.Node == nil {
615
+
616
+	if !n.IsMember() {
617
+		return nil, ErrNoRaftMember
618
+	}
619
+
620
+	if n.IsStopped() {
616 621
 		return nil, ErrStopped
617 622
 	}
618 623
 
... ...
@@ -625,6 +689,10 @@ func (n *Node) ProcessRaftMessage(ctx context.Context, msg *api.ProcessRaftMessa
625 625
 
626 626
 // ResolveAddress returns the address reaching for a given node ID.
627 627
 func (n *Node) ResolveAddress(ctx context.Context, msg *api.ResolveAddressRequest) (*api.ResolveAddressResponse, error) {
628
+	if !n.IsMember() {
629
+		return nil, ErrNoRaftMember
630
+	}
631
+
628 632
 	nodeInfo, err := ca.RemoteNode(ctx)
629 633
 	if err != nil {
630 634
 		return nil, err
... ...
@@ -656,7 +724,7 @@ func (n *Node) LeaderAddr() (string, error) {
656 656
 	if err := WaitForLeader(ctx, n); err != nil {
657 657
 		return "", ErrNoClusterLeader
658 658
 	}
659
-	if n.Node == nil {
659
+	if n.IsStopped() {
660 660
 		return "", ErrStopped
661 661
 	}
662 662
 	ms := n.cluster.Members()
... ...
@@ -671,7 +739,7 @@ func (n *Node) LeaderAddr() (string, error) {
671 671
 func (n *Node) registerNode(node *api.RaftMember) error {
672 672
 	member := &membership.Member{}
673 673
 
674
-	if n.cluster.GetMember(node.RaftID) != nil {
674
+	if n.cluster.GetMember(node.RaftID) != nil || n.cluster.IsIDRemoved(node.RaftID) {
675 675
 		// member already exists
676 676
 		return nil
677 677
 	}
... ...
@@ -760,11 +828,18 @@ func (n *Node) GetMemberlist() map[uint64]*api.RaftMember {
760 760
 	return memberlist
761 761
 }
762 762
 
763
-// mustStop checks if the raft node must be stopped
764
-// because it was removed from the cluster from
765
-// other members
766
-func (n *Node) mustStop() bool {
767
-	return atomic.LoadUint32(&n.removed) == 1
763
+// IsMember checks if the raft node has effectively joined
764
+// a cluster of existing members.
765
+func (n *Node) IsMember() bool {
766
+	return atomic.LoadUint32(&n.isMember) == 1
767
+}
768
+
769
+// IsStopped checks if the raft node is stopped or not
770
+func (n *Node) IsStopped() bool {
771
+	if n.Node == nil {
772
+		return true
773
+	}
774
+	return false
768 775
 }
769 776
 
770 777
 // canSubmitProposal defines if any more proposals
... ...
@@ -882,12 +957,14 @@ func (n *Node) sendToMember(members map[uint64]*membership.Member, m raftpb.Mess
882 882
 	_, err := conn.ProcessRaftMessage(ctx, &api.ProcessRaftMessageRequest{Message: &m})
883 883
 	if err != nil {
884 884
 		if grpc.ErrorDesc(err) == ErrMemberRemoved.Error() {
885
-			atomic.StoreUint32(&n.removed, 1)
885
+			n.removeRaftOnce.Do(func() {
886
+				close(n.removeRaftCh)
887
+			})
886 888
 		}
887 889
 		if m.Type == raftpb.MsgSnap {
888 890
 			n.ReportSnapshot(m.To, raft.SnapshotFailure)
889 891
 		}
890
-		if n.Node == nil {
892
+		if n.IsStopped() {
891 893
 			panic("node is nil")
892 894
 		}
893 895
 		n.ReportUnreachable(m.To)
... ...
@@ -162,6 +162,22 @@ func (n *Node) readWAL(ctx context.Context, snapshot *raftpb.Snapshot, forceNewC
162 162
 	}
163 163
 	n.Config.ID = raftNode.RaftID
164 164
 
165
+	// All members that are no longer part of the cluster must be added to
166
+	// the removed list right away, so that we don't try to connect to them
167
+	// before processing the configuration change entries, which could make
168
+	// us get stuck.
169
+	for _, ent := range ents {
170
+		if ent.Index <= st.Commit && ent.Type == raftpb.EntryConfChange {
171
+			var cc raftpb.ConfChange
172
+			if err := cc.Unmarshal(ent.Data); err != nil {
173
+				return fmt.Errorf("error unmarshalling config change: %v", err)
174
+			}
175
+			if cc.Type == raftpb.ConfChangeRemoveNode {
176
+				n.cluster.RemoveMember(cc.NodeID)
177
+			}
178
+		}
179
+	}
180
+
165 181
 	if forceNewCluster {
166 182
 		// discard the previously uncommitted entries
167 183
 		for i, ent := range ents {
... ...
@@ -174,6 +190,23 @@ func (n *Node) readWAL(ctx context.Context, snapshot *raftpb.Snapshot, forceNewC
174 174
 
175 175
 		// force append the configuration change entries
176 176
 		toAppEnts := createConfigChangeEnts(getIDs(snapshot, ents), uint64(n.Config.ID), st.Term, st.Commit)
177
+
178
+		// All members that are being removed as part of the
179
+		// force-new-cluster process must be added to the
180
+		// removed list right away, so that we don't try to
181
+		// connect to them before processing the configuration
182
+		// change entries, which could make us get stuck.
183
+		for _, ccEnt := range toAppEnts {
184
+			if ccEnt.Type == raftpb.EntryConfChange {
185
+				var cc raftpb.ConfChange
186
+				if err := cc.Unmarshal(ccEnt.Data); err != nil {
187
+					return fmt.Errorf("error unmarshalling force-new-cluster config change: %v", err)
188
+				}
189
+				if cc.Type == raftpb.ConfChangeRemoveNode {
190
+					n.cluster.RemoveMember(cc.NodeID)
191
+				}
192
+			}
193
+		}
177 194
 		ents = append(ents, toAppEnts...)
178 195
 
179 196
 		// force commit newly appended entries
... ...
@@ -347,9 +380,10 @@ func (n *Node) restoreFromSnapshot(data []byte, forceNewCluster bool) error {
347 347
 				return err
348 348
 			}
349 349
 		}
350
-		for _, removedMember := range snapshot.Membership.Removed {
351
-			n.cluster.RemoveMember(removedMember)
352
-		}
350
+	}
351
+
352
+	for _, removedMember := range snapshot.Membership.Removed {
353
+		n.cluster.RemoveMember(removedMember)
353 354
 	}
354 355
 
355 356
 	return nil
... ...
@@ -23,7 +23,6 @@ const (
23 23
 	indexID           = "id"
24 24
 	indexName         = "name"
25 25
 	indexServiceID    = "serviceid"
26
-	indexServiceMode  = "servicemode"
27 26
 	indexNodeID       = "nodeid"
28 27
 	indexSlot         = "slot"
29 28
 	indexCN           = "cn"
... ...
@@ -272,7 +272,7 @@ func (p *Picker) PickAddr() (string, error) {
272 272
 	p.mu.Lock()
273 273
 	p.peer = peer
274 274
 	p.mu.Unlock()
275
-	return p.peer.Addr, err
275
+	return peer.Addr, err
276 276
 }
277 277
 
278 278
 // State returns the connectivity state of the underlying connections.