Browse code

Merge pull request #27287 from mavenugo/pluginv2-sk2

Allow multiple handlers to support network plugins in swarm-mode

Aaron Lehmann authored on 2016/10/21 05:43:04
Showing 35 changed files
... ...
@@ -362,9 +362,8 @@ func (daemon *Daemon) GetNetworkDriverList() []string {
362 362
 		return nil
363 363
 	}
364 364
 
365
-	// TODO: Replace this with proper libnetwork API
366
-	pluginList := []string{"overlay"}
367
-	pluginMap := map[string]bool{"overlay": true}
365
+	pluginList := daemon.netController.BuiltinDrivers()
366
+	pluginMap := make(map[string]bool)
368 367
 
369 368
 	networks := daemon.netController.Networks()
370 369
 
... ...
@@ -146,7 +146,7 @@ clone git github.com/docker/docker-credential-helpers v0.3.0
146 146
 clone git github.com/docker/containerd 52ef1ceb4b660c42cf4ea9013180a5663968d4c7
147 147
 
148 148
 # cluster
149
-clone git github.com/docker/swarmkit 7e63bdefb94e5bea2641e8bdebae2cfa61a0ed44
149
+clone git github.com/docker/swarmkit 1fed8d2a2ccd2a9b6d6fb864d4ad3461fc6dc3eb
150 150
 clone git github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
151 151
 clone git github.com/gogo/protobuf v0.3
152 152
 clone git github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
... ...
@@ -2,6 +2,7 @@ package main
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"net/http/httptest"
5 6
 	"os"
6 7
 	"path/filepath"
7 8
 	"sync"
... ...
@@ -240,6 +241,7 @@ func init() {
240 240
 }
241 241
 
242 242
 type DockerSwarmSuite struct {
243
+	server      *httptest.Server
243 244
 	ds          *DockerSuite
244 245
 	daemons     []*SwarmDaemon
245 246
 	daemonsLock sync.Mutex // protect access to daemons
... ...
@@ -3,13 +3,22 @@
3 3
 package main
4 4
 
5 5
 import (
6
+	"encoding/json"
7
+	"fmt"
6 8
 	"io/ioutil"
9
+	"net/http"
10
+	"net/http/httptest"
11
+	"os"
7 12
 	"strings"
8 13
 	"time"
9 14
 
10 15
 	"github.com/docker/docker/api/types/swarm"
11 16
 	"github.com/docker/docker/pkg/integration/checker"
17
+	"github.com/docker/libnetwork/driverapi"
18
+	"github.com/docker/libnetwork/ipamapi"
19
+	remoteipam "github.com/docker/libnetwork/ipams/remote/api"
12 20
 	"github.com/go-check/check"
21
+	"github.com/vishvananda/netlink"
13 22
 )
14 23
 
15 24
 func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
... ...
@@ -364,3 +373,198 @@ func (s *DockerSwarmSuite) TestPsListContainersFilterIsTask(c *check.C) {
364 364
 	c.Assert(lines, checker.HasLen, 1)
365 365
 	c.Assert(lines[0], checker.Not(checker.Equals), bareID, check.Commentf("Expected not %s, but got it for is-task label, output %q", bareID, out))
366 366
 }
367
+
368
+const globalNetworkPlugin = "global-network-plugin"
369
+const globalIPAMPlugin = "global-ipam-plugin"
370
+
371
+func (s *DockerSwarmSuite) SetUpSuite(c *check.C) {
372
+	mux := http.NewServeMux()
373
+	s.server = httptest.NewServer(mux)
374
+	c.Assert(s.server, check.NotNil, check.Commentf("Failed to start an HTTP Server"))
375
+	setupRemoteGlobalNetworkPlugin(c, mux, s.server.URL, globalNetworkPlugin, globalIPAMPlugin)
376
+}
377
+
378
+func setupRemoteGlobalNetworkPlugin(c *check.C, mux *http.ServeMux, url, netDrv, ipamDrv string) {
379
+
380
+	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
381
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
382
+		fmt.Fprintf(w, `{"Implements": ["%s", "%s"]}`, driverapi.NetworkPluginEndpointType, ipamapi.PluginEndpointType)
383
+	})
384
+
385
+	// Network driver implementation
386
+	mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
387
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
388
+		fmt.Fprintf(w, `{"Scope":"global"}`)
389
+	})
390
+
391
+	mux.HandleFunc(fmt.Sprintf("/%s.AllocateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
392
+		err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest)
393
+		if err != nil {
394
+			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
395
+			return
396
+		}
397
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
398
+		fmt.Fprintf(w, "null")
399
+	})
400
+
401
+	mux.HandleFunc(fmt.Sprintf("/%s.FreeNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
402
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
403
+		fmt.Fprintf(w, "null")
404
+	})
405
+
406
+	mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
407
+		err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest)
408
+		if err != nil {
409
+			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
410
+			return
411
+		}
412
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
413
+		fmt.Fprintf(w, "null")
414
+	})
415
+
416
+	mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
417
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
418
+		fmt.Fprintf(w, "null")
419
+	})
420
+
421
+	mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
422
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
423
+		fmt.Fprintf(w, `{"Interface":{"MacAddress":"a0:b1:c2:d3:e4:f5"}}`)
424
+	})
425
+
426
+	mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
427
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
428
+
429
+		veth := &netlink.Veth{
430
+			LinkAttrs: netlink.LinkAttrs{Name: "randomIfName", TxQLen: 0}, PeerName: "cnt0"}
431
+		if err := netlink.LinkAdd(veth); err != nil {
432
+			fmt.Fprintf(w, `{"Error":"failed to add veth pair: `+err.Error()+`"}`)
433
+		} else {
434
+			fmt.Fprintf(w, `{"InterfaceName":{ "SrcName":"cnt0", "DstPrefix":"veth"}}`)
435
+		}
436
+	})
437
+
438
+	mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
439
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
440
+		fmt.Fprintf(w, "null")
441
+	})
442
+
443
+	mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
444
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
445
+		if link, err := netlink.LinkByName("cnt0"); err == nil {
446
+			netlink.LinkDel(link)
447
+		}
448
+		fmt.Fprintf(w, "null")
449
+	})
450
+
451
+	// IPAM Driver implementation
452
+	var (
453
+		poolRequest       remoteipam.RequestPoolRequest
454
+		poolReleaseReq    remoteipam.ReleasePoolRequest
455
+		addressRequest    remoteipam.RequestAddressRequest
456
+		addressReleaseReq remoteipam.ReleaseAddressRequest
457
+		lAS               = "localAS"
458
+		gAS               = "globalAS"
459
+		pool              = "172.28.0.0/16"
460
+		poolID            = lAS + "/" + pool
461
+		gw                = "172.28.255.254/16"
462
+	)
463
+
464
+	mux.HandleFunc(fmt.Sprintf("/%s.GetDefaultAddressSpaces", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
465
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
466
+		fmt.Fprintf(w, `{"LocalDefaultAddressSpace":"`+lAS+`", "GlobalDefaultAddressSpace": "`+gAS+`"}`)
467
+	})
468
+
469
+	mux.HandleFunc(fmt.Sprintf("/%s.RequestPool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
470
+		err := json.NewDecoder(r.Body).Decode(&poolRequest)
471
+		if err != nil {
472
+			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
473
+			return
474
+		}
475
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
476
+		if poolRequest.AddressSpace != lAS && poolRequest.AddressSpace != gAS {
477
+			fmt.Fprintf(w, `{"Error":"Unknown address space in pool request: `+poolRequest.AddressSpace+`"}`)
478
+		} else if poolRequest.Pool != "" && poolRequest.Pool != pool {
479
+			fmt.Fprintf(w, `{"Error":"Cannot handle explicit pool requests yet"}`)
480
+		} else {
481
+			fmt.Fprintf(w, `{"PoolID":"`+poolID+`", "Pool":"`+pool+`"}`)
482
+		}
483
+	})
484
+
485
+	mux.HandleFunc(fmt.Sprintf("/%s.RequestAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
486
+		err := json.NewDecoder(r.Body).Decode(&addressRequest)
487
+		if err != nil {
488
+			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
489
+			return
490
+		}
491
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
492
+		// make sure libnetwork is now querying on the expected pool id
493
+		if addressRequest.PoolID != poolID {
494
+			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
495
+		} else if addressRequest.Address != "" {
496
+			fmt.Fprintf(w, `{"Error":"Cannot handle explicit address requests yet"}`)
497
+		} else {
498
+			fmt.Fprintf(w, `{"Address":"`+gw+`"}`)
499
+		}
500
+	})
501
+
502
+	mux.HandleFunc(fmt.Sprintf("/%s.ReleaseAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
503
+		err := json.NewDecoder(r.Body).Decode(&addressReleaseReq)
504
+		if err != nil {
505
+			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
506
+			return
507
+		}
508
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
509
+		// make sure libnetwork is now asking to release the expected address from the expected poolid
510
+		if addressRequest.PoolID != poolID {
511
+			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
512
+		} else if addressReleaseReq.Address != gw {
513
+			fmt.Fprintf(w, `{"Error":"unknown address"}`)
514
+		} else {
515
+			fmt.Fprintf(w, "null")
516
+		}
517
+	})
518
+
519
+	mux.HandleFunc(fmt.Sprintf("/%s.ReleasePool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
520
+		err := json.NewDecoder(r.Body).Decode(&poolReleaseReq)
521
+		if err != nil {
522
+			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
523
+			return
524
+		}
525
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
526
+		// make sure libnetwork is now asking to release the expected poolid
527
+		if addressRequest.PoolID != poolID {
528
+			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
529
+		} else {
530
+			fmt.Fprintf(w, "null")
531
+		}
532
+	})
533
+
534
+	err := os.MkdirAll("/etc/docker/plugins", 0755)
535
+	c.Assert(err, checker.IsNil)
536
+
537
+	fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv)
538
+	err = ioutil.WriteFile(fileName, []byte(url), 0644)
539
+	c.Assert(err, checker.IsNil)
540
+
541
+	ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv)
542
+	err = ioutil.WriteFile(ipamFileName, []byte(url), 0644)
543
+	c.Assert(err, checker.IsNil)
544
+}
545
+
546
+func (s *DockerSwarmSuite) TestSwarmNetworkPlugin(c *check.C) {
547
+	d := s.AddDaemon(c, true, true)
548
+
549
+	out, err := d.Cmd("network", "create", "-d", globalNetworkPlugin, "foo")
550
+	c.Assert(err, checker.IsNil)
551
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
552
+
553
+	name := "top"
554
+	out, err = d.Cmd("service", "create", "--name", name, "--network", "foo", "busybox", "top")
555
+	c.Assert(err, checker.IsNil)
556
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
557
+
558
+	out, err = d.Cmd("service", "inspect", "--format", "{{range .Spec.Networks}}{{.Target}}{{end}}", name)
559
+	c.Assert(err, checker.IsNil)
560
+	c.Assert(strings.TrimSpace(out), checker.Equals, "foo")
561
+}
... ...
@@ -43,7 +43,7 @@ type plugins struct {
43 43
 
44 44
 var (
45 45
 	storage          = plugins{plugins: make(map[string]*Plugin)}
46
-	extpointHandlers = make(map[string]func(string, *Client))
46
+	extpointHandlers = make(map[string][]func(string, *Client))
47 47
 )
48 48
 
49 49
 // Manifest lists what a plugin implements.
... ...
@@ -129,11 +129,13 @@ func (p *Plugin) activateWithLock() error {
129 129
 	p.Manifest = m
130 130
 
131 131
 	for _, iface := range m.Implements {
132
-		handler, handled := extpointHandlers[iface]
132
+		handlers, handled := extpointHandlers[iface]
133 133
 		if !handled {
134 134
 			continue
135 135
 		}
136
-		handler(p.name, p.client)
136
+		for _, handler := range handlers {
137
+			handler(p.name, p.client)
138
+		}
137 139
 	}
138 140
 	return nil
139 141
 }
... ...
@@ -226,7 +228,16 @@ func Get(name, imp string) (*Plugin, error) {
226 226
 
227 227
 // Handle adds the specified function to the extpointHandlers.
228 228
 func Handle(iface string, fn func(string, *Client)) {
229
-	extpointHandlers[iface] = fn
229
+	handlers, ok := extpointHandlers[iface]
230
+	if !ok {
231
+		handlers = []func(string, *Client){}
232
+	}
233
+
234
+	handlers = append(handlers, fn)
235
+	extpointHandlers[iface] = handlers
236
+	for _, p := range storage.plugins {
237
+		p.activated = false
238
+	}
230 239
 }
231 240
 
232 241
 // GetAll returns all the plugins for the specified implementation
... ...
@@ -15,7 +15,7 @@ type Store struct {
15 15
 	/* handlers are necessary for transition path of legacy plugins
16 16
 	 * to the new model. Legacy plugins use Handle() for registering an
17 17
 	 * activation callback.*/
18
-	handlers map[string]func(string, *plugins.Client)
18
+	handlers map[string][]func(string, *plugins.Client)
19 19
 	nameToID map[string]string
20 20
 	plugindb string
21 21
 }
... ...
@@ -24,7 +24,7 @@ type Store struct {
24 24
 func NewStore(libRoot string) *Store {
25 25
 	return &Store{
26 26
 		plugins:  make(map[string]*v2.Plugin),
27
-		handlers: make(map[string]func(string, *plugins.Client)),
27
+		handlers: make(map[string][]func(string, *plugins.Client)),
28 28
 		nameToID: make(map[string]string),
29 29
 		plugindb: filepath.Join(libRoot, "plugins", "plugins.json"),
30 30
 	}
... ...
@@ -218,7 +218,12 @@ func (ps *Store) Handle(capability string, callback func(string, *plugins.Client
218 218
 
219 219
 	// Register callback with new plugin model.
220 220
 	ps.Lock()
221
-	ps.handlers[pluginType] = callback
221
+	handlers, ok := ps.handlers[pluginType]
222
+	if !ok {
223
+		handlers = []func(string, *plugins.Client){}
224
+	}
225
+	handlers = append(handlers, callback)
226
+	ps.handlers[pluginType] = handlers
222 227
 	ps.Unlock()
223 228
 
224 229
 	// Register callback with legacy plugin model.
... ...
@@ -230,7 +235,7 @@ func (ps *Store) Handle(capability string, callback func(string, *plugins.Client
230 230
 // CallHandler calls the registered callback. It is invoked during plugin enable.
231 231
 func (ps *Store) CallHandler(p *v2.Plugin) {
232 232
 	for _, typ := range p.GetTypes() {
233
-		if handler := ps.handlers[typ.String()]; handler != nil {
233
+		for _, handler := range ps.handlers[typ.String()] {
234 234
 			handler(p.Name(), p.Client())
235 235
 		}
236 236
 	}
... ...
@@ -171,11 +171,12 @@ func (a *Agent) run(ctx context.Context) {
171 171
 		case msg := <-session.assignments:
172 172
 			switch msg.Type {
173 173
 			case api.AssignmentsMessage_COMPLETE:
174
-				if err := a.worker.AssignTasks(ctx, msg.UpdateTasks); err != nil {
174
+				// Need to assign secrets before tasks, because tasks might depend on new secrets
175
+				if err := a.worker.Assign(ctx, msg.Changes); err != nil {
175 176
 					log.G(ctx).WithError(err).Error("failed to synchronize worker assignments")
176 177
 				}
177 178
 			case api.AssignmentsMessage_INCREMENTAL:
178
-				if err := a.worker.UpdateTasks(ctx, msg.UpdateTasks, msg.RemoveTasks); err != nil {
179
+				if err := a.worker.Update(ctx, msg.Changes); err != nil {
179 180
 					log.G(ctx).WithError(err).Error("failed to update worker assignments")
180 181
 				}
181 182
 			}
... ...
@@ -101,6 +101,21 @@ type Node struct {
101 101
 	roleChangeReq        chan api.NodeRole // used to send role updates from the dispatcher api on promotion/demotion
102 102
 }
103 103
 
104
+// RemoteAPIAddr returns address on which remote manager api listens.
105
+// Returns nil if node is not manager.
106
+func (n *Node) RemoteAPIAddr() (string, error) {
107
+	n.RLock()
108
+	defer n.RUnlock()
109
+	if n.manager == nil {
110
+		return "", errors.Errorf("node is not manager")
111
+	}
112
+	addr := n.manager.Addr()
113
+	if addr == nil {
114
+		return "", errors.Errorf("manager addr is not set")
115
+	}
116
+	return addr.String(), nil
117
+}
118
+
104 119
 // NewNode returns new Node instance.
105 120
 func NewNode(c *NodeConfig) (*Node, error) {
106 121
 	if err := os.MkdirAll(c.StateDir, 0700); err != nil {
... ...
@@ -627,7 +642,12 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig
627 627
 			go func(ready chan struct{}) {
628 628
 				select {
629 629
 				case <-ready:
630
-					n.remotes.Observe(api.Peer{NodeID: n.NodeID(), Addr: n.config.ListenRemoteAPI}, remotes.DefaultObservationWeight)
630
+					addr, err := n.RemoteAPIAddr()
631
+					if err != nil {
632
+						log.G(ctx).WithError(err).Errorf("get remote api addr")
633
+					} else {
634
+						n.remotes.Observe(api.Peer{NodeID: n.NodeID(), Addr: addr}, remotes.DefaultObservationWeight)
635
+					}
631 636
 				case <-connCtx.Done():
632 637
 				}
633 638
 			}(ready)
634 639
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+package agent
1
+
2
+import (
3
+	"sync"
4
+
5
+	"github.com/docker/swarmkit/api"
6
+)
7
+
8
+// secrets is a map that keeps all the currenty available secrets to the agent
9
+// mapped by secret ID
10
+type secrets struct {
11
+	mu sync.RWMutex
12
+	m  map[string]api.Secret
13
+}
14
+
15
+func newSecrets() *secrets {
16
+	return &secrets{
17
+		m: make(map[string]api.Secret),
18
+	}
19
+}
20
+
21
+// Get returns a secret by ID.  If the secret doesn't exist, returns nil.
22
+func (s *secrets) Get(secretID string) api.Secret {
23
+	s.mu.RLock()
24
+	defer s.mu.RUnlock()
25
+	return s.m[secretID]
26
+}
27
+
28
+// Add adds one or more secrets to the secret map
29
+func (s *secrets) Add(secrets ...api.Secret) {
30
+	s.mu.Lock()
31
+	defer s.mu.Unlock()
32
+	for _, secret := range secrets {
33
+		s.m[secret.ID] = secret
34
+	}
35
+}
36
+
37
+// Remove removes one or more secrets by ID from the secret map.  Succeeds
38
+// whether or not the given IDs are in the map.
39
+func (s *secrets) Remove(secrets []string) {
40
+	s.mu.Lock()
41
+	defer s.mu.Unlock()
42
+	for _, secret := range secrets {
43
+		delete(s.m, secret)
44
+	}
45
+}
46
+
47
+// Reset removes all the secrets
48
+func (s *secrets) Reset() {
49
+	s.mu.Lock()
50
+	defer s.mu.Unlock()
51
+	s.m = make(map[string]api.Secret)
52
+}
... ...
@@ -252,12 +252,26 @@ func (s *session) watch(ctx context.Context) error {
252 252
 			}
253 253
 		}
254 254
 		if tasksWatch != nil {
255
+			// When falling back to Tasks because of an old managers, we wrap the tasks in assignments.
255 256
 			var taskResp *api.TasksMessage
257
+			var assignmentChanges []*api.AssignmentChange
256 258
 			taskResp, err = tasksWatch.Recv()
257 259
 			if err != nil {
258 260
 				return err
259 261
 			}
260
-			resp = &api.AssignmentsMessage{Type: api.AssignmentsMessage_COMPLETE, UpdateTasks: taskResp.Tasks}
262
+			for _, t := range taskResp.Tasks {
263
+				taskChange := &api.AssignmentChange{
264
+					Assignment: &api.Assignment{
265
+						Item: &api.Assignment_Task{
266
+							Task: t,
267
+						},
268
+					},
269
+					Action: api.AssignmentChange_AssignmentActionUpdate,
270
+				}
271
+
272
+				assignmentChanges = append(assignmentChanges, taskChange)
273
+			}
274
+			resp = &api.AssignmentsMessage{Type: api.AssignmentsMessage_COMPLETE, Changes: assignmentChanges}
261 275
 		}
262 276
 
263 277
 		// If there seems to be a gap in the stream, let's break out of the inner for and
... ...
@@ -17,13 +17,13 @@ type Worker interface {
17 17
 	// Init prepares the worker for task assignment.
18 18
 	Init(ctx context.Context) error
19 19
 
20
-	// AssignTasks assigns a complete set of tasks to a worker. Any task not included in
20
+	// Assign assigns a complete set of tasks and secrets to a worker. Any task or secrets not included in
21 21
 	// this set will be removed.
22
-	AssignTasks(ctx context.Context, tasks []*api.Task) error
22
+	Assign(ctx context.Context, assignments []*api.AssignmentChange) error
23 23
 
24
-	// UpdateTasks updates an incremental set of tasks to the worker. Any task not included
24
+	// Updates updates an incremental set of tasks or secrets of the worker. Any task/secret not included
25 25
 	// either in added or removed will remain untouched.
26
-	UpdateTasks(ctx context.Context, added []*api.Task, removed []string) error
26
+	Update(ctx context.Context, assignments []*api.AssignmentChange) error
27 27
 
28 28
 	// Listen to updates about tasks controlled by the worker. When first
29 29
 	// called, the reporter will receive all updates for all tasks controlled
... ...
@@ -42,6 +42,7 @@ type worker struct {
42 42
 	db        *bolt.DB
43 43
 	executor  exec.Executor
44 44
 	listeners map[*statusReporterKey]struct{}
45
+	secrets   *secrets
45 46
 
46 47
 	taskManagers map[string]*taskManager
47 48
 	mu           sync.RWMutex
... ...
@@ -53,6 +54,7 @@ func newWorker(db *bolt.DB, executor exec.Executor) *worker {
53 53
 		executor:     executor,
54 54
 		listeners:    make(map[*statusReporterKey]struct{}),
55 55
 		taskManagers: make(map[string]*taskManager),
56
+		secrets:      newSecrets(),
56 57
 	}
57 58
 }
58 59
 
... ...
@@ -90,37 +92,70 @@ func (w *worker) Init(ctx context.Context) error {
90 90
 	})
91 91
 }
92 92
 
93
-// AssignTasks assigns  the set of tasks to the worker. Any tasks not previously known will
94
-// be started. Any tasks that are in the task set and already running will be
95
-// updated, if possible. Any tasks currently running on the
96
-// worker outside the task set will be terminated.
97
-func (w *worker) AssignTasks(ctx context.Context, tasks []*api.Task) error {
93
+// Assign assigns a full set of tasks and secrets to the worker.
94
+// Any tasks not previously known will be started. Any tasks that are in the task set
95
+// and already running will be updated, if possible. Any tasks currently running on
96
+// the worker outside the task set will be terminated.
97
+// Any secrets not in the set of assignments will be removed.
98
+func (w *worker) Assign(ctx context.Context, assignments []*api.AssignmentChange) error {
98 99
 	w.mu.Lock()
99 100
 	defer w.mu.Unlock()
100 101
 
101 102
 	log.G(ctx).WithFields(logrus.Fields{
102
-		"len(tasks)": len(tasks),
103
-	}).Debug("(*worker).AssignTasks")
103
+		"len(assignments)": len(assignments),
104
+	}).Debug("(*worker).Assign")
104 105
 
105
-	return reconcileTaskState(ctx, w, tasks, nil, true)
106
+	// Need to update secrets before tasks, because tasks might depend on new secrets
107
+	err := reconcileSecrets(ctx, w, assignments, true)
108
+	if err != nil {
109
+		return err
110
+	}
111
+
112
+	return reconcileTaskState(ctx, w, assignments, true)
106 113
 }
107 114
 
108
-// UpdateTasks the set of tasks to the worker.
115
+// Update updates the set of tasks and secret for the worker.
109 116
 // Tasks in the added set will be added to the worker, and tasks in the removed set
110 117
 // will be removed from the worker
111
-func (w *worker) UpdateTasks(ctx context.Context, added []*api.Task, removed []string) error {
118
+// Serets in the added set will be added to the worker, and secrets in the removed set
119
+// will be removed from the worker.
120
+func (w *worker) Update(ctx context.Context, assignments []*api.AssignmentChange) error {
112 121
 	w.mu.Lock()
113 122
 	defer w.mu.Unlock()
114 123
 
115 124
 	log.G(ctx).WithFields(logrus.Fields{
116
-		"len(added)":   len(added),
117
-		"len(removed)": len(removed),
118
-	}).Debug("(*worker).UpdateTasks")
125
+		"len(assignments)": len(assignments),
126
+	}).Debug("(*worker).Update")
127
+
128
+	err := reconcileSecrets(ctx, w, assignments, false)
129
+	if err != nil {
130
+		return err
131
+	}
119 132
 
120
-	return reconcileTaskState(ctx, w, added, removed, false)
133
+	return reconcileTaskState(ctx, w, assignments, false)
121 134
 }
122 135
 
123
-func reconcileTaskState(ctx context.Context, w *worker, added []*api.Task, removed []string, fullSnapshot bool) error {
136
+func reconcileTaskState(ctx context.Context, w *worker, assignments []*api.AssignmentChange, fullSnapshot bool) error {
137
+	var (
138
+		updatedTasks []*api.Task
139
+		removedTasks []*api.Task
140
+	)
141
+	for _, a := range assignments {
142
+		if t := a.Assignment.GetTask(); t != nil {
143
+			switch a.Action {
144
+			case api.AssignmentChange_AssignmentActionUpdate:
145
+				updatedTasks = append(updatedTasks, t)
146
+			case api.AssignmentChange_AssignmentActionRemove:
147
+				removedTasks = append(removedTasks, t)
148
+			}
149
+		}
150
+	}
151
+
152
+	log.G(ctx).WithFields(logrus.Fields{
153
+		"len(updatedTasks)": len(updatedTasks),
154
+		"len(removedTasks)": len(removedTasks),
155
+	}).Debug("(*worker).reconcileTaskState")
156
+
124 157
 	tx, err := w.db.Begin(true)
125 158
 	if err != nil {
126 159
 		log.G(ctx).WithError(err).Error("failed starting transaction against task database")
... ...
@@ -130,7 +165,7 @@ func reconcileTaskState(ctx context.Context, w *worker, added []*api.Task, remov
130 130
 
131 131
 	assigned := map[string]struct{}{}
132 132
 
133
-	for _, task := range added {
133
+	for _, task := range updatedTasks {
134 134
 		log.G(ctx).WithFields(
135 135
 			logrus.Fields{
136 136
 				"task.id":           task.ID,
... ...
@@ -202,15 +237,15 @@ func reconcileTaskState(ctx context.Context, w *worker, added []*api.Task, remov
202 202
 	} else {
203 203
 		// If this was an incremental set of assignments, we're going to remove only the tasks
204 204
 		// in the removed set
205
-		for _, taskID := range removed {
206
-			err := removeTaskAssignment(taskID)
205
+		for _, task := range removedTasks {
206
+			err := removeTaskAssignment(task.ID)
207 207
 			if err != nil {
208 208
 				continue
209 209
 			}
210 210
 
211
-			tm, ok := w.taskManagers[taskID]
211
+			tm, ok := w.taskManagers[task.ID]
212 212
 			if ok {
213
-				delete(w.taskManagers, taskID)
213
+				delete(w.taskManagers, task.ID)
214 214
 				go closeManager(tm)
215 215
 			}
216 216
 		}
... ...
@@ -219,6 +254,39 @@ func reconcileTaskState(ctx context.Context, w *worker, added []*api.Task, remov
219 219
 	return tx.Commit()
220 220
 }
221 221
 
222
+func reconcileSecrets(ctx context.Context, w *worker, assignments []*api.AssignmentChange, fullSnapshot bool) error {
223
+	var (
224
+		updatedSecrets []api.Secret
225
+		removedSecrets []string
226
+	)
227
+	for _, a := range assignments {
228
+		if s := a.Assignment.GetSecret(); s != nil {
229
+			switch a.Action {
230
+			case api.AssignmentChange_AssignmentActionUpdate:
231
+				updatedSecrets = append(updatedSecrets, *s)
232
+			case api.AssignmentChange_AssignmentActionRemove:
233
+				removedSecrets = append(removedSecrets, s.ID)
234
+			}
235
+
236
+		}
237
+	}
238
+
239
+	log.G(ctx).WithFields(logrus.Fields{
240
+		"len(updatedSecrets)": len(updatedSecrets),
241
+		"len(removedSecrets)": len(removedSecrets),
242
+	}).Debug("(*worker).reconcileSecrets")
243
+
244
+	// If this was a complete set of secrets, we're going to clear the secrets map and add all of them
245
+	if fullSnapshot {
246
+		w.secrets.Reset()
247
+	} else {
248
+		w.secrets.Remove(removedSecrets)
249
+	}
250
+	w.secrets.Add(updatedSecrets...)
251
+
252
+	return nil
253
+}
254
+
222 255
 func (w *worker) Listen(ctx context.Context, reporter StatusReporter) {
223 256
 	w.mu.Lock()
224 257
 	defer w.mu.Unlock()
... ...
@@ -35,6 +35,29 @@ var _ = proto.Marshal
35 35
 var _ = fmt.Errorf
36 36
 var _ = math.Inf
37 37
 
38
+type AssignmentChange_AssignmentAction int32
39
+
40
+const (
41
+	AssignmentChange_AssignmentActionUpdate AssignmentChange_AssignmentAction = 0
42
+	AssignmentChange_AssignmentActionRemove AssignmentChange_AssignmentAction = 1
43
+)
44
+
45
+var AssignmentChange_AssignmentAction_name = map[int32]string{
46
+	0: "UPDATE",
47
+	1: "REMOVE",
48
+}
49
+var AssignmentChange_AssignmentAction_value = map[string]int32{
50
+	"UPDATE": 0,
51
+	"REMOVE": 1,
52
+}
53
+
54
+func (x AssignmentChange_AssignmentAction) String() string {
55
+	return proto.EnumName(AssignmentChange_AssignmentAction_name, int32(x))
56
+}
57
+func (AssignmentChange_AssignmentAction) EnumDescriptor() ([]byte, []int) {
58
+	return fileDescriptorDispatcher, []int{10, 0}
59
+}
60
+
38 61
 // AssignmentType specifies whether this assignment message carries
39 62
 // the full state, or is an update to an existing state.
40 63
 type AssignmentsMessage_Type int32
... ...
@@ -57,7 +80,7 @@ func (x AssignmentsMessage_Type) String() string {
57 57
 	return proto.EnumName(AssignmentsMessage_Type_name, int32(x))
58 58
 }
59 59
 func (AssignmentsMessage_Type) EnumDescriptor() ([]byte, []int) {
60
-	return fileDescriptorDispatcher, []int{9, 0}
60
+	return fileDescriptorDispatcher, []int{11, 0}
61 61
 }
62 62
 
63 63
 // SessionRequest starts a session.
... ...
@@ -214,6 +237,137 @@ func (m *AssignmentsRequest) Reset()                    { *m = AssignmentsReques
214 214
 func (*AssignmentsRequest) ProtoMessage()               {}
215 215
 func (*AssignmentsRequest) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{8} }
216 216
 
217
+type Assignment struct {
218
+	// Types that are valid to be assigned to Item:
219
+	//	*Assignment_Task
220
+	//	*Assignment_Secret
221
+	Item isAssignment_Item `protobuf_oneof:"item"`
222
+}
223
+
224
+func (m *Assignment) Reset()                    { *m = Assignment{} }
225
+func (*Assignment) ProtoMessage()               {}
226
+func (*Assignment) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{9} }
227
+
228
+type isAssignment_Item interface {
229
+	isAssignment_Item()
230
+	MarshalTo([]byte) (int, error)
231
+	Size() int
232
+}
233
+
234
+type Assignment_Task struct {
235
+	Task *Task `protobuf:"bytes,1,opt,name=task,oneof"`
236
+}
237
+type Assignment_Secret struct {
238
+	Secret *Secret `protobuf:"bytes,2,opt,name=secret,oneof"`
239
+}
240
+
241
+func (*Assignment_Task) isAssignment_Item()   {}
242
+func (*Assignment_Secret) isAssignment_Item() {}
243
+
244
+func (m *Assignment) GetItem() isAssignment_Item {
245
+	if m != nil {
246
+		return m.Item
247
+	}
248
+	return nil
249
+}
250
+
251
+func (m *Assignment) GetTask() *Task {
252
+	if x, ok := m.GetItem().(*Assignment_Task); ok {
253
+		return x.Task
254
+	}
255
+	return nil
256
+}
257
+
258
+func (m *Assignment) GetSecret() *Secret {
259
+	if x, ok := m.GetItem().(*Assignment_Secret); ok {
260
+		return x.Secret
261
+	}
262
+	return nil
263
+}
264
+
265
+// XXX_OneofFuncs is for the internal use of the proto package.
266
+func (*Assignment) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
267
+	return _Assignment_OneofMarshaler, _Assignment_OneofUnmarshaler, _Assignment_OneofSizer, []interface{}{
268
+		(*Assignment_Task)(nil),
269
+		(*Assignment_Secret)(nil),
270
+	}
271
+}
272
+
273
+func _Assignment_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
274
+	m := msg.(*Assignment)
275
+	// item
276
+	switch x := m.Item.(type) {
277
+	case *Assignment_Task:
278
+		_ = b.EncodeVarint(1<<3 | proto.WireBytes)
279
+		if err := b.EncodeMessage(x.Task); err != nil {
280
+			return err
281
+		}
282
+	case *Assignment_Secret:
283
+		_ = b.EncodeVarint(2<<3 | proto.WireBytes)
284
+		if err := b.EncodeMessage(x.Secret); err != nil {
285
+			return err
286
+		}
287
+	case nil:
288
+	default:
289
+		return fmt.Errorf("Assignment.Item has unexpected type %T", x)
290
+	}
291
+	return nil
292
+}
293
+
294
+func _Assignment_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
295
+	m := msg.(*Assignment)
296
+	switch tag {
297
+	case 1: // item.task
298
+		if wire != proto.WireBytes {
299
+			return true, proto.ErrInternalBadWireType
300
+		}
301
+		msg := new(Task)
302
+		err := b.DecodeMessage(msg)
303
+		m.Item = &Assignment_Task{msg}
304
+		return true, err
305
+	case 2: // item.secret
306
+		if wire != proto.WireBytes {
307
+			return true, proto.ErrInternalBadWireType
308
+		}
309
+		msg := new(Secret)
310
+		err := b.DecodeMessage(msg)
311
+		m.Item = &Assignment_Secret{msg}
312
+		return true, err
313
+	default:
314
+		return false, nil
315
+	}
316
+}
317
+
318
+func _Assignment_OneofSizer(msg proto.Message) (n int) {
319
+	m := msg.(*Assignment)
320
+	// item
321
+	switch x := m.Item.(type) {
322
+	case *Assignment_Task:
323
+		s := proto.Size(x.Task)
324
+		n += proto.SizeVarint(1<<3 | proto.WireBytes)
325
+		n += proto.SizeVarint(uint64(s))
326
+		n += s
327
+	case *Assignment_Secret:
328
+		s := proto.Size(x.Secret)
329
+		n += proto.SizeVarint(2<<3 | proto.WireBytes)
330
+		n += proto.SizeVarint(uint64(s))
331
+		n += s
332
+	case nil:
333
+	default:
334
+		panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
335
+	}
336
+	return n
337
+}
338
+
339
+type AssignmentChange struct {
340
+	Assignment *Assignment                       `protobuf:"bytes,1,opt,name=assignment" json:"assignment,omitempty"`
341
+	Action     AssignmentChange_AssignmentAction `protobuf:"varint,2,opt,name=action,proto3,enum=docker.swarmkit.v1.AssignmentChange_AssignmentAction" json:"action,omitempty"`
342
+}
343
+
344
+func (m *AssignmentChange) Reset()                    { *m = AssignmentChange{} }
345
+func (*AssignmentChange) ProtoMessage()               {}
346
+func (*AssignmentChange) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{10} }
347
+
217 348
 type AssignmentsMessage struct {
218 349
 	Type AssignmentsMessage_Type `protobuf:"varint,1,opt,name=type,proto3,enum=docker.swarmkit.v1.AssignmentsMessage_Type" json:"type,omitempty"`
219 350
 	// AppliesTo references the previous ResultsIn value, to chain
... ...
@@ -226,28 +380,13 @@ type AssignmentsMessage struct {
226 226
 	// match against the next message's AppliesTo value and protect
227 227
 	// against missed messages.
228 228
 	ResultsIn string `protobuf:"bytes,3,opt,name=results_in,json=resultsIn,proto3" json:"results_in,omitempty"`
229
-	// UpdateTasks is a set of new or updated tasks to run on this node.
230
-	// In the first assignments message, it contains all of the tasks
231
-	// to run on this node. Tasks outside of this set running on the node
232
-	// should be terminated.
233
-	UpdateTasks []*Task `protobuf:"bytes,4,rep,name=update_tasks,json=updateTasks" json:"update_tasks,omitempty"`
234
-	// RemoveTasks is a set of previously-assigned task IDs to remove from the
235
-	// assignment set. It is not used in the first assignments message of
236
-	// a stream.
237
-	RemoveTasks []string `protobuf:"bytes,5,rep,name=remove_tasks,json=removeTasks" json:"remove_tasks,omitempty"`
238
-	// UpdateSecrets is a set of new or updated secrets for this node.
239
-	// In the first assignments message, it contains all of the secrets
240
-	// the node needs for itself and its assigned tasks.
241
-	UpdateSecrets []*Secret `protobuf:"bytes,6,rep,name=update_secrets,json=updateSecrets" json:"update_secrets,omitempty"`
242
-	// RemoveSecrets is a set of previously-assigned secret names to remove
243
-	// from memory. It is not used in the first assignments message of
244
-	// a stream.
245
-	RemoveSecrets []string `protobuf:"bytes,7,rep,name=remove_secrets,json=removeSecrets" json:"remove_secrets,omitempty"`
229
+	// AssignmentChange is a set of changes to apply on this node.
230
+	Changes []*AssignmentChange `protobuf:"bytes,4,rep,name=changes" json:"changes,omitempty"`
246 231
 }
247 232
 
248 233
 func (m *AssignmentsMessage) Reset()                    { *m = AssignmentsMessage{} }
249 234
 func (*AssignmentsMessage) ProtoMessage()               {}
250
-func (*AssignmentsMessage) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{9} }
235
+func (*AssignmentsMessage) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{11} }
251 236
 
252 237
 func init() {
253 238
 	proto.RegisterType((*SessionRequest)(nil), "docker.swarmkit.v1.SessionRequest")
... ...
@@ -260,7 +399,10 @@ func init() {
260 260
 	proto.RegisterType((*TasksRequest)(nil), "docker.swarmkit.v1.TasksRequest")
261 261
 	proto.RegisterType((*TasksMessage)(nil), "docker.swarmkit.v1.TasksMessage")
262 262
 	proto.RegisterType((*AssignmentsRequest)(nil), "docker.swarmkit.v1.AssignmentsRequest")
263
+	proto.RegisterType((*Assignment)(nil), "docker.swarmkit.v1.Assignment")
264
+	proto.RegisterType((*AssignmentChange)(nil), "docker.swarmkit.v1.AssignmentChange")
263 265
 	proto.RegisterType((*AssignmentsMessage)(nil), "docker.swarmkit.v1.AssignmentsMessage")
266
+	proto.RegisterEnum("docker.swarmkit.v1.AssignmentChange_AssignmentAction", AssignmentChange_AssignmentAction_name, AssignmentChange_AssignmentAction_value)
264 267
 	proto.RegisterEnum("docker.swarmkit.v1.AssignmentsMessage_Type", AssignmentsMessage_Type_name, AssignmentsMessage_Type_value)
265 268
 }
266 269
 
... ...
@@ -463,42 +605,59 @@ func (m *AssignmentsRequest) Copy() *AssignmentsRequest {
463 463
 	return o
464 464
 }
465 465
 
466
-func (m *AssignmentsMessage) Copy() *AssignmentsMessage {
466
+func (m *Assignment) Copy() *Assignment {
467 467
 	if m == nil {
468 468
 		return nil
469 469
 	}
470 470
 
471
-	o := &AssignmentsMessage{
472
-		Type:      m.Type,
473
-		AppliesTo: m.AppliesTo,
474
-		ResultsIn: m.ResultsIn,
475
-	}
471
+	o := &Assignment{}
476 472
 
477
-	if m.UpdateTasks != nil {
478
-		o.UpdateTasks = make([]*Task, 0, len(m.UpdateTasks))
479
-		for _, v := range m.UpdateTasks {
480
-			o.UpdateTasks = append(o.UpdateTasks, v.Copy())
473
+	switch m.Item.(type) {
474
+	case *Assignment_Task:
475
+		i := &Assignment_Task{
476
+			Task: m.GetTask().Copy(),
481 477
 		}
482
-	}
483 478
 
484
-	if m.RemoveTasks != nil {
485
-		o.RemoveTasks = make([]string, 0, len(m.RemoveTasks))
486
-		for _, v := range m.RemoveTasks {
487
-			o.RemoveTasks = append(o.RemoveTasks, v)
479
+		o.Item = i
480
+	case *Assignment_Secret:
481
+		i := &Assignment_Secret{
482
+			Secret: m.GetSecret().Copy(),
488 483
 		}
484
+
485
+		o.Item = i
489 486
 	}
490 487
 
491
-	if m.UpdateSecrets != nil {
492
-		o.UpdateSecrets = make([]*Secret, 0, len(m.UpdateSecrets))
493
-		for _, v := range m.UpdateSecrets {
494
-			o.UpdateSecrets = append(o.UpdateSecrets, v.Copy())
495
-		}
488
+	return o
489
+}
490
+
491
+func (m *AssignmentChange) Copy() *AssignmentChange {
492
+	if m == nil {
493
+		return nil
494
+	}
495
+
496
+	o := &AssignmentChange{
497
+		Assignment: m.Assignment.Copy(),
498
+		Action:     m.Action,
499
+	}
500
+
501
+	return o
502
+}
503
+
504
+func (m *AssignmentsMessage) Copy() *AssignmentsMessage {
505
+	if m == nil {
506
+		return nil
507
+	}
508
+
509
+	o := &AssignmentsMessage{
510
+		Type:      m.Type,
511
+		AppliesTo: m.AppliesTo,
512
+		ResultsIn: m.ResultsIn,
496 513
 	}
497 514
 
498
-	if m.RemoveSecrets != nil {
499
-		o.RemoveSecrets = make([]string, 0, len(m.RemoveSecrets))
500
-		for _, v := range m.RemoveSecrets {
501
-			o.RemoveSecrets = append(o.RemoveSecrets, v)
515
+	if m.Changes != nil {
516
+		o.Changes = make([]*AssignmentChange, 0, len(m.Changes))
517
+		for _, v := range m.Changes {
518
+			o.Changes = append(o.Changes, v.Copy())
502 519
 		}
503 520
 	}
504 521
 
... ...
@@ -624,23 +783,59 @@ func (this *AssignmentsRequest) GoString() string {
624 624
 	s = append(s, "}")
625 625
 	return strings.Join(s, "")
626 626
 }
627
+func (this *Assignment) GoString() string {
628
+	if this == nil {
629
+		return "nil"
630
+	}
631
+	s := make([]string, 0, 6)
632
+	s = append(s, "&api.Assignment{")
633
+	if this.Item != nil {
634
+		s = append(s, "Item: "+fmt.Sprintf("%#v", this.Item)+",\n")
635
+	}
636
+	s = append(s, "}")
637
+	return strings.Join(s, "")
638
+}
639
+func (this *Assignment_Task) GoString() string {
640
+	if this == nil {
641
+		return "nil"
642
+	}
643
+	s := strings.Join([]string{`&api.Assignment_Task{` +
644
+		`Task:` + fmt.Sprintf("%#v", this.Task) + `}`}, ", ")
645
+	return s
646
+}
647
+func (this *Assignment_Secret) GoString() string {
648
+	if this == nil {
649
+		return "nil"
650
+	}
651
+	s := strings.Join([]string{`&api.Assignment_Secret{` +
652
+		`Secret:` + fmt.Sprintf("%#v", this.Secret) + `}`}, ", ")
653
+	return s
654
+}
655
+func (this *AssignmentChange) GoString() string {
656
+	if this == nil {
657
+		return "nil"
658
+	}
659
+	s := make([]string, 0, 6)
660
+	s = append(s, "&api.AssignmentChange{")
661
+	if this.Assignment != nil {
662
+		s = append(s, "Assignment: "+fmt.Sprintf("%#v", this.Assignment)+",\n")
663
+	}
664
+	s = append(s, "Action: "+fmt.Sprintf("%#v", this.Action)+",\n")
665
+	s = append(s, "}")
666
+	return strings.Join(s, "")
667
+}
627 668
 func (this *AssignmentsMessage) GoString() string {
628 669
 	if this == nil {
629 670
 		return "nil"
630 671
 	}
631
-	s := make([]string, 0, 11)
672
+	s := make([]string, 0, 8)
632 673
 	s = append(s, "&api.AssignmentsMessage{")
633 674
 	s = append(s, "Type: "+fmt.Sprintf("%#v", this.Type)+",\n")
634 675
 	s = append(s, "AppliesTo: "+fmt.Sprintf("%#v", this.AppliesTo)+",\n")
635 676
 	s = append(s, "ResultsIn: "+fmt.Sprintf("%#v", this.ResultsIn)+",\n")
636
-	if this.UpdateTasks != nil {
637
-		s = append(s, "UpdateTasks: "+fmt.Sprintf("%#v", this.UpdateTasks)+",\n")
677
+	if this.Changes != nil {
678
+		s = append(s, "Changes: "+fmt.Sprintf("%#v", this.Changes)+",\n")
638 679
 	}
639
-	s = append(s, "RemoveTasks: "+fmt.Sprintf("%#v", this.RemoveTasks)+",\n")
640
-	if this.UpdateSecrets != nil {
641
-		s = append(s, "UpdateSecrets: "+fmt.Sprintf("%#v", this.UpdateSecrets)+",\n")
642
-	}
643
-	s = append(s, "RemoveSecrets: "+fmt.Sprintf("%#v", this.RemoveSecrets)+",\n")
644 680
 	s = append(s, "}")
645 681
 	return strings.Join(s, "")
646 682
 }
... ...
@@ -1313,6 +1508,92 @@ func (m *AssignmentsRequest) MarshalTo(data []byte) (int, error) {
1313 1313
 	return i, nil
1314 1314
 }
1315 1315
 
1316
+func (m *Assignment) Marshal() (data []byte, err error) {
1317
+	size := m.Size()
1318
+	data = make([]byte, size)
1319
+	n, err := m.MarshalTo(data)
1320
+	if err != nil {
1321
+		return nil, err
1322
+	}
1323
+	return data[:n], nil
1324
+}
1325
+
1326
+func (m *Assignment) MarshalTo(data []byte) (int, error) {
1327
+	var i int
1328
+	_ = i
1329
+	var l int
1330
+	_ = l
1331
+	if m.Item != nil {
1332
+		nn5, err := m.Item.MarshalTo(data[i:])
1333
+		if err != nil {
1334
+			return 0, err
1335
+		}
1336
+		i += nn5
1337
+	}
1338
+	return i, nil
1339
+}
1340
+
1341
+func (m *Assignment_Task) MarshalTo(data []byte) (int, error) {
1342
+	i := 0
1343
+	if m.Task != nil {
1344
+		data[i] = 0xa
1345
+		i++
1346
+		i = encodeVarintDispatcher(data, i, uint64(m.Task.Size()))
1347
+		n6, err := m.Task.MarshalTo(data[i:])
1348
+		if err != nil {
1349
+			return 0, err
1350
+		}
1351
+		i += n6
1352
+	}
1353
+	return i, nil
1354
+}
1355
+func (m *Assignment_Secret) MarshalTo(data []byte) (int, error) {
1356
+	i := 0
1357
+	if m.Secret != nil {
1358
+		data[i] = 0x12
1359
+		i++
1360
+		i = encodeVarintDispatcher(data, i, uint64(m.Secret.Size()))
1361
+		n7, err := m.Secret.MarshalTo(data[i:])
1362
+		if err != nil {
1363
+			return 0, err
1364
+		}
1365
+		i += n7
1366
+	}
1367
+	return i, nil
1368
+}
1369
+func (m *AssignmentChange) Marshal() (data []byte, err error) {
1370
+	size := m.Size()
1371
+	data = make([]byte, size)
1372
+	n, err := m.MarshalTo(data)
1373
+	if err != nil {
1374
+		return nil, err
1375
+	}
1376
+	return data[:n], nil
1377
+}
1378
+
1379
+func (m *AssignmentChange) MarshalTo(data []byte) (int, error) {
1380
+	var i int
1381
+	_ = i
1382
+	var l int
1383
+	_ = l
1384
+	if m.Assignment != nil {
1385
+		data[i] = 0xa
1386
+		i++
1387
+		i = encodeVarintDispatcher(data, i, uint64(m.Assignment.Size()))
1388
+		n8, err := m.Assignment.MarshalTo(data[i:])
1389
+		if err != nil {
1390
+			return 0, err
1391
+		}
1392
+		i += n8
1393
+	}
1394
+	if m.Action != 0 {
1395
+		data[i] = 0x10
1396
+		i++
1397
+		i = encodeVarintDispatcher(data, i, uint64(m.Action))
1398
+	}
1399
+	return i, nil
1400
+}
1401
+
1316 1402
 func (m *AssignmentsMessage) Marshal() (data []byte, err error) {
1317 1403
 	size := m.Size()
1318 1404
 	data = make([]byte, size)
... ...
@@ -1345,8 +1626,8 @@ func (m *AssignmentsMessage) MarshalTo(data []byte) (int, error) {
1345 1345
 		i = encodeVarintDispatcher(data, i, uint64(len(m.ResultsIn)))
1346 1346
 		i += copy(data[i:], m.ResultsIn)
1347 1347
 	}
1348
-	if len(m.UpdateTasks) > 0 {
1349
-		for _, msg := range m.UpdateTasks {
1348
+	if len(m.Changes) > 0 {
1349
+		for _, msg := range m.Changes {
1350 1350
 			data[i] = 0x22
1351 1351
 			i++
1352 1352
 			i = encodeVarintDispatcher(data, i, uint64(msg.Size()))
... ...
@@ -1357,48 +1638,6 @@ func (m *AssignmentsMessage) MarshalTo(data []byte) (int, error) {
1357 1357
 			i += n
1358 1358
 		}
1359 1359
 	}
1360
-	if len(m.RemoveTasks) > 0 {
1361
-		for _, s := range m.RemoveTasks {
1362
-			data[i] = 0x2a
1363
-			i++
1364
-			l = len(s)
1365
-			for l >= 1<<7 {
1366
-				data[i] = uint8(uint64(l)&0x7f | 0x80)
1367
-				l >>= 7
1368
-				i++
1369
-			}
1370
-			data[i] = uint8(l)
1371
-			i++
1372
-			i += copy(data[i:], s)
1373
-		}
1374
-	}
1375
-	if len(m.UpdateSecrets) > 0 {
1376
-		for _, msg := range m.UpdateSecrets {
1377
-			data[i] = 0x32
1378
-			i++
1379
-			i = encodeVarintDispatcher(data, i, uint64(msg.Size()))
1380
-			n, err := msg.MarshalTo(data[i:])
1381
-			if err != nil {
1382
-				return 0, err
1383
-			}
1384
-			i += n
1385
-		}
1386
-	}
1387
-	if len(m.RemoveSecrets) > 0 {
1388
-		for _, s := range m.RemoveSecrets {
1389
-			data[i] = 0x3a
1390
-			i++
1391
-			l = len(s)
1392
-			for l >= 1<<7 {
1393
-				data[i] = uint8(uint64(l)&0x7f | 0x80)
1394
-				l >>= 7
1395
-				i++
1396
-			}
1397
-			data[i] = uint8(l)
1398
-			i++
1399
-			i += copy(data[i:], s)
1400
-		}
1401
-	}
1402 1360
 	return i, nil
1403 1361
 }
1404 1362
 
... ...
@@ -1789,6 +2028,46 @@ func (m *AssignmentsRequest) Size() (n int) {
1789 1789
 	return n
1790 1790
 }
1791 1791
 
1792
+func (m *Assignment) Size() (n int) {
1793
+	var l int
1794
+	_ = l
1795
+	if m.Item != nil {
1796
+		n += m.Item.Size()
1797
+	}
1798
+	return n
1799
+}
1800
+
1801
+func (m *Assignment_Task) Size() (n int) {
1802
+	var l int
1803
+	_ = l
1804
+	if m.Task != nil {
1805
+		l = m.Task.Size()
1806
+		n += 1 + l + sovDispatcher(uint64(l))
1807
+	}
1808
+	return n
1809
+}
1810
+func (m *Assignment_Secret) Size() (n int) {
1811
+	var l int
1812
+	_ = l
1813
+	if m.Secret != nil {
1814
+		l = m.Secret.Size()
1815
+		n += 1 + l + sovDispatcher(uint64(l))
1816
+	}
1817
+	return n
1818
+}
1819
+func (m *AssignmentChange) Size() (n int) {
1820
+	var l int
1821
+	_ = l
1822
+	if m.Assignment != nil {
1823
+		l = m.Assignment.Size()
1824
+		n += 1 + l + sovDispatcher(uint64(l))
1825
+	}
1826
+	if m.Action != 0 {
1827
+		n += 1 + sovDispatcher(uint64(m.Action))
1828
+	}
1829
+	return n
1830
+}
1831
+
1792 1832
 func (m *AssignmentsMessage) Size() (n int) {
1793 1833
 	var l int
1794 1834
 	_ = l
... ...
@@ -1803,30 +2082,12 @@ func (m *AssignmentsMessage) Size() (n int) {
1803 1803
 	if l > 0 {
1804 1804
 		n += 1 + l + sovDispatcher(uint64(l))
1805 1805
 	}
1806
-	if len(m.UpdateTasks) > 0 {
1807
-		for _, e := range m.UpdateTasks {
1806
+	if len(m.Changes) > 0 {
1807
+		for _, e := range m.Changes {
1808 1808
 			l = e.Size()
1809 1809
 			n += 1 + l + sovDispatcher(uint64(l))
1810 1810
 		}
1811 1811
 	}
1812
-	if len(m.RemoveTasks) > 0 {
1813
-		for _, s := range m.RemoveTasks {
1814
-			l = len(s)
1815
-			n += 1 + l + sovDispatcher(uint64(l))
1816
-		}
1817
-	}
1818
-	if len(m.UpdateSecrets) > 0 {
1819
-		for _, e := range m.UpdateSecrets {
1820
-			l = e.Size()
1821
-			n += 1 + l + sovDispatcher(uint64(l))
1822
-		}
1823
-	}
1824
-	if len(m.RemoveSecrets) > 0 {
1825
-		for _, s := range m.RemoveSecrets {
1826
-			l = len(s)
1827
-			n += 1 + l + sovDispatcher(uint64(l))
1828
-		}
1829
-	}
1830 1812
 	return n
1831 1813
 }
1832 1814
 
... ...
@@ -1948,6 +2209,47 @@ func (this *AssignmentsRequest) String() string {
1948 1948
 	}, "")
1949 1949
 	return s
1950 1950
 }
1951
+func (this *Assignment) String() string {
1952
+	if this == nil {
1953
+		return "nil"
1954
+	}
1955
+	s := strings.Join([]string{`&Assignment{`,
1956
+		`Item:` + fmt.Sprintf("%v", this.Item) + `,`,
1957
+		`}`,
1958
+	}, "")
1959
+	return s
1960
+}
1961
+func (this *Assignment_Task) String() string {
1962
+	if this == nil {
1963
+		return "nil"
1964
+	}
1965
+	s := strings.Join([]string{`&Assignment_Task{`,
1966
+		`Task:` + strings.Replace(fmt.Sprintf("%v", this.Task), "Task", "Task", 1) + `,`,
1967
+		`}`,
1968
+	}, "")
1969
+	return s
1970
+}
1971
+func (this *Assignment_Secret) String() string {
1972
+	if this == nil {
1973
+		return "nil"
1974
+	}
1975
+	s := strings.Join([]string{`&Assignment_Secret{`,
1976
+		`Secret:` + strings.Replace(fmt.Sprintf("%v", this.Secret), "Secret", "Secret", 1) + `,`,
1977
+		`}`,
1978
+	}, "")
1979
+	return s
1980
+}
1981
+func (this *AssignmentChange) String() string {
1982
+	if this == nil {
1983
+		return "nil"
1984
+	}
1985
+	s := strings.Join([]string{`&AssignmentChange{`,
1986
+		`Assignment:` + strings.Replace(fmt.Sprintf("%v", this.Assignment), "Assignment", "Assignment", 1) + `,`,
1987
+		`Action:` + fmt.Sprintf("%v", this.Action) + `,`,
1988
+		`}`,
1989
+	}, "")
1990
+	return s
1991
+}
1951 1992
 func (this *AssignmentsMessage) String() string {
1952 1993
 	if this == nil {
1953 1994
 		return "nil"
... ...
@@ -1956,10 +2258,7 @@ func (this *AssignmentsMessage) String() string {
1956 1956
 		`Type:` + fmt.Sprintf("%v", this.Type) + `,`,
1957 1957
 		`AppliesTo:` + fmt.Sprintf("%v", this.AppliesTo) + `,`,
1958 1958
 		`ResultsIn:` + fmt.Sprintf("%v", this.ResultsIn) + `,`,
1959
-		`UpdateTasks:` + strings.Replace(fmt.Sprintf("%v", this.UpdateTasks), "Task", "Task", 1) + `,`,
1960
-		`RemoveTasks:` + fmt.Sprintf("%v", this.RemoveTasks) + `,`,
1961
-		`UpdateSecrets:` + strings.Replace(fmt.Sprintf("%v", this.UpdateSecrets), "Secret", "Secret", 1) + `,`,
1962
-		`RemoveSecrets:` + fmt.Sprintf("%v", this.RemoveSecrets) + `,`,
1959
+		`Changes:` + strings.Replace(fmt.Sprintf("%v", this.Changes), "AssignmentChange", "AssignmentChange", 1) + `,`,
1963 1960
 		`}`,
1964 1961
 	}, "")
1965 1962
 	return s
... ...
@@ -2928,7 +3227,7 @@ func (m *AssignmentsRequest) Unmarshal(data []byte) error {
2928 2928
 	}
2929 2929
 	return nil
2930 2930
 }
2931
-func (m *AssignmentsMessage) Unmarshal(data []byte) error {
2931
+func (m *Assignment) Unmarshal(data []byte) error {
2932 2932
 	l := len(data)
2933 2933
 	iNdEx := 0
2934 2934
 	for iNdEx < l {
... ...
@@ -2951,17 +3250,17 @@ func (m *AssignmentsMessage) Unmarshal(data []byte) error {
2951 2951
 		fieldNum := int32(wire >> 3)
2952 2952
 		wireType := int(wire & 0x7)
2953 2953
 		if wireType == 4 {
2954
-			return fmt.Errorf("proto: AssignmentsMessage: wiretype end group for non-group")
2954
+			return fmt.Errorf("proto: Assignment: wiretype end group for non-group")
2955 2955
 		}
2956 2956
 		if fieldNum <= 0 {
2957
-			return fmt.Errorf("proto: AssignmentsMessage: illegal tag %d (wire type %d)", fieldNum, wire)
2957
+			return fmt.Errorf("proto: Assignment: illegal tag %d (wire type %d)", fieldNum, wire)
2958 2958
 		}
2959 2959
 		switch fieldNum {
2960 2960
 		case 1:
2961
-			if wireType != 0 {
2962
-				return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
2961
+			if wireType != 2 {
2962
+				return fmt.Errorf("proto: wrong wireType = %d for field Task", wireType)
2963 2963
 			}
2964
-			m.Type = 0
2964
+			var msglen int
2965 2965
 			for shift := uint(0); ; shift += 7 {
2966 2966
 				if shift >= 64 {
2967 2967
 					return ErrIntOverflowDispatcher
... ...
@@ -2971,16 +3270,29 @@ func (m *AssignmentsMessage) Unmarshal(data []byte) error {
2971 2971
 				}
2972 2972
 				b := data[iNdEx]
2973 2973
 				iNdEx++
2974
-				m.Type |= (AssignmentsMessage_Type(b) & 0x7F) << shift
2974
+				msglen |= (int(b) & 0x7F) << shift
2975 2975
 				if b < 0x80 {
2976 2976
 					break
2977 2977
 				}
2978 2978
 			}
2979
+			if msglen < 0 {
2980
+				return ErrInvalidLengthDispatcher
2981
+			}
2982
+			postIndex := iNdEx + msglen
2983
+			if postIndex > l {
2984
+				return io.ErrUnexpectedEOF
2985
+			}
2986
+			v := &Task{}
2987
+			if err := v.Unmarshal(data[iNdEx:postIndex]); err != nil {
2988
+				return err
2989
+			}
2990
+			m.Item = &Assignment_Task{v}
2991
+			iNdEx = postIndex
2979 2992
 		case 2:
2980 2993
 			if wireType != 2 {
2981
-				return fmt.Errorf("proto: wrong wireType = %d for field AppliesTo", wireType)
2994
+				return fmt.Errorf("proto: wrong wireType = %d for field Secret", wireType)
2982 2995
 			}
2983
-			var stringLen uint64
2996
+			var msglen int
2984 2997
 			for shift := uint(0); ; shift += 7 {
2985 2998
 				if shift >= 64 {
2986 2999
 					return ErrIntOverflowDispatcher
... ...
@@ -2990,26 +3302,79 @@ func (m *AssignmentsMessage) Unmarshal(data []byte) error {
2990 2990
 				}
2991 2991
 				b := data[iNdEx]
2992 2992
 				iNdEx++
2993
-				stringLen |= (uint64(b) & 0x7F) << shift
2993
+				msglen |= (int(b) & 0x7F) << shift
2994 2994
 				if b < 0x80 {
2995 2995
 					break
2996 2996
 				}
2997 2997
 			}
2998
-			intStringLen := int(stringLen)
2999
-			if intStringLen < 0 {
2998
+			if msglen < 0 {
3000 2999
 				return ErrInvalidLengthDispatcher
3001 3000
 			}
3002
-			postIndex := iNdEx + intStringLen
3001
+			postIndex := iNdEx + msglen
3003 3002
 			if postIndex > l {
3004 3003
 				return io.ErrUnexpectedEOF
3005 3004
 			}
3006
-			m.AppliesTo = string(data[iNdEx:postIndex])
3005
+			v := &Secret{}
3006
+			if err := v.Unmarshal(data[iNdEx:postIndex]); err != nil {
3007
+				return err
3008
+			}
3009
+			m.Item = &Assignment_Secret{v}
3007 3010
 			iNdEx = postIndex
3008
-		case 3:
3011
+		default:
3012
+			iNdEx = preIndex
3013
+			skippy, err := skipDispatcher(data[iNdEx:])
3014
+			if err != nil {
3015
+				return err
3016
+			}
3017
+			if skippy < 0 {
3018
+				return ErrInvalidLengthDispatcher
3019
+			}
3020
+			if (iNdEx + skippy) > l {
3021
+				return io.ErrUnexpectedEOF
3022
+			}
3023
+			iNdEx += skippy
3024
+		}
3025
+	}
3026
+
3027
+	if iNdEx > l {
3028
+		return io.ErrUnexpectedEOF
3029
+	}
3030
+	return nil
3031
+}
3032
+func (m *AssignmentChange) Unmarshal(data []byte) error {
3033
+	l := len(data)
3034
+	iNdEx := 0
3035
+	for iNdEx < l {
3036
+		preIndex := iNdEx
3037
+		var wire uint64
3038
+		for shift := uint(0); ; shift += 7 {
3039
+			if shift >= 64 {
3040
+				return ErrIntOverflowDispatcher
3041
+			}
3042
+			if iNdEx >= l {
3043
+				return io.ErrUnexpectedEOF
3044
+			}
3045
+			b := data[iNdEx]
3046
+			iNdEx++
3047
+			wire |= (uint64(b) & 0x7F) << shift
3048
+			if b < 0x80 {
3049
+				break
3050
+			}
3051
+		}
3052
+		fieldNum := int32(wire >> 3)
3053
+		wireType := int(wire & 0x7)
3054
+		if wireType == 4 {
3055
+			return fmt.Errorf("proto: AssignmentChange: wiretype end group for non-group")
3056
+		}
3057
+		if fieldNum <= 0 {
3058
+			return fmt.Errorf("proto: AssignmentChange: illegal tag %d (wire type %d)", fieldNum, wire)
3059
+		}
3060
+		switch fieldNum {
3061
+		case 1:
3009 3062
 			if wireType != 2 {
3010
-				return fmt.Errorf("proto: wrong wireType = %d for field ResultsIn", wireType)
3063
+				return fmt.Errorf("proto: wrong wireType = %d for field Assignment", wireType)
3011 3064
 			}
3012
-			var stringLen uint64
3065
+			var msglen int
3013 3066
 			for shift := uint(0); ; shift += 7 {
3014 3067
 				if shift >= 64 {
3015 3068
 					return ErrIntOverflowDispatcher
... ...
@@ -3019,26 +3384,30 @@ func (m *AssignmentsMessage) Unmarshal(data []byte) error {
3019 3019
 				}
3020 3020
 				b := data[iNdEx]
3021 3021
 				iNdEx++
3022
-				stringLen |= (uint64(b) & 0x7F) << shift
3022
+				msglen |= (int(b) & 0x7F) << shift
3023 3023
 				if b < 0x80 {
3024 3024
 					break
3025 3025
 				}
3026 3026
 			}
3027
-			intStringLen := int(stringLen)
3028
-			if intStringLen < 0 {
3027
+			if msglen < 0 {
3029 3028
 				return ErrInvalidLengthDispatcher
3030 3029
 			}
3031
-			postIndex := iNdEx + intStringLen
3030
+			postIndex := iNdEx + msglen
3032 3031
 			if postIndex > l {
3033 3032
 				return io.ErrUnexpectedEOF
3034 3033
 			}
3035
-			m.ResultsIn = string(data[iNdEx:postIndex])
3034
+			if m.Assignment == nil {
3035
+				m.Assignment = &Assignment{}
3036
+			}
3037
+			if err := m.Assignment.Unmarshal(data[iNdEx:postIndex]); err != nil {
3038
+				return err
3039
+			}
3036 3040
 			iNdEx = postIndex
3037
-		case 4:
3038
-			if wireType != 2 {
3039
-				return fmt.Errorf("proto: wrong wireType = %d for field UpdateTasks", wireType)
3041
+		case 2:
3042
+			if wireType != 0 {
3043
+				return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType)
3040 3044
 			}
3041
-			var msglen int
3045
+			m.Action = 0
3042 3046
 			for shift := uint(0); ; shift += 7 {
3043 3047
 				if shift >= 64 {
3044 3048
 					return ErrIntOverflowDispatcher
... ...
@@ -3048,26 +3417,83 @@ func (m *AssignmentsMessage) Unmarshal(data []byte) error {
3048 3048
 				}
3049 3049
 				b := data[iNdEx]
3050 3050
 				iNdEx++
3051
-				msglen |= (int(b) & 0x7F) << shift
3051
+				m.Action |= (AssignmentChange_AssignmentAction(b) & 0x7F) << shift
3052 3052
 				if b < 0x80 {
3053 3053
 					break
3054 3054
 				}
3055 3055
 			}
3056
-			if msglen < 0 {
3056
+		default:
3057
+			iNdEx = preIndex
3058
+			skippy, err := skipDispatcher(data[iNdEx:])
3059
+			if err != nil {
3060
+				return err
3061
+			}
3062
+			if skippy < 0 {
3057 3063
 				return ErrInvalidLengthDispatcher
3058 3064
 			}
3059
-			postIndex := iNdEx + msglen
3060
-			if postIndex > l {
3065
+			if (iNdEx + skippy) > l {
3061 3066
 				return io.ErrUnexpectedEOF
3062 3067
 			}
3063
-			m.UpdateTasks = append(m.UpdateTasks, &Task{})
3064
-			if err := m.UpdateTasks[len(m.UpdateTasks)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
3065
-				return err
3068
+			iNdEx += skippy
3069
+		}
3070
+	}
3071
+
3072
+	if iNdEx > l {
3073
+		return io.ErrUnexpectedEOF
3074
+	}
3075
+	return nil
3076
+}
3077
+func (m *AssignmentsMessage) Unmarshal(data []byte) error {
3078
+	l := len(data)
3079
+	iNdEx := 0
3080
+	for iNdEx < l {
3081
+		preIndex := iNdEx
3082
+		var wire uint64
3083
+		for shift := uint(0); ; shift += 7 {
3084
+			if shift >= 64 {
3085
+				return ErrIntOverflowDispatcher
3066 3086
 			}
3067
-			iNdEx = postIndex
3068
-		case 5:
3087
+			if iNdEx >= l {
3088
+				return io.ErrUnexpectedEOF
3089
+			}
3090
+			b := data[iNdEx]
3091
+			iNdEx++
3092
+			wire |= (uint64(b) & 0x7F) << shift
3093
+			if b < 0x80 {
3094
+				break
3095
+			}
3096
+		}
3097
+		fieldNum := int32(wire >> 3)
3098
+		wireType := int(wire & 0x7)
3099
+		if wireType == 4 {
3100
+			return fmt.Errorf("proto: AssignmentsMessage: wiretype end group for non-group")
3101
+		}
3102
+		if fieldNum <= 0 {
3103
+			return fmt.Errorf("proto: AssignmentsMessage: illegal tag %d (wire type %d)", fieldNum, wire)
3104
+		}
3105
+		switch fieldNum {
3106
+		case 1:
3107
+			if wireType != 0 {
3108
+				return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
3109
+			}
3110
+			m.Type = 0
3111
+			for shift := uint(0); ; shift += 7 {
3112
+				if shift >= 64 {
3113
+					return ErrIntOverflowDispatcher
3114
+				}
3115
+				if iNdEx >= l {
3116
+					return io.ErrUnexpectedEOF
3117
+				}
3118
+				b := data[iNdEx]
3119
+				iNdEx++
3120
+				m.Type |= (AssignmentsMessage_Type(b) & 0x7F) << shift
3121
+				if b < 0x80 {
3122
+					break
3123
+				}
3124
+			}
3125
+		case 2:
3069 3126
 			if wireType != 2 {
3070
-				return fmt.Errorf("proto: wrong wireType = %d for field RemoveTasks", wireType)
3127
+				return fmt.Errorf("proto: wrong wireType = %d for field AppliesTo", wireType)
3071 3128
 			}
3072 3129
 			var stringLen uint64
3073 3130
 			for shift := uint(0); ; shift += 7 {
... ...
@@ -3092,13 +3518,13 @@ func (m *AssignmentsMessage) Unmarshal(data []byte) error {
3092 3092
 			if postIndex > l {
3093 3093
 				return io.ErrUnexpectedEOF
3094 3094
 			}
3095
-			m.RemoveTasks = append(m.RemoveTasks, string(data[iNdEx:postIndex]))
3095
+			m.AppliesTo = string(data[iNdEx:postIndex])
3096 3096
 			iNdEx = postIndex
3097
-		case 6:
3097
+		case 3:
3098 3098
 			if wireType != 2 {
3099
-				return fmt.Errorf("proto: wrong wireType = %d for field UpdateSecrets", wireType)
3099
+				return fmt.Errorf("proto: wrong wireType = %d for field ResultsIn", wireType)
3100 3100
 			}
3101
-			var msglen int
3101
+			var stringLen uint64
3102 3102
 			for shift := uint(0); ; shift += 7 {
3103 3103
 				if shift >= 64 {
3104 3104
 					return ErrIntOverflowDispatcher
... ...
@@ -3108,28 +3534,26 @@ func (m *AssignmentsMessage) Unmarshal(data []byte) error {
3108 3108
 				}
3109 3109
 				b := data[iNdEx]
3110 3110
 				iNdEx++
3111
-				msglen |= (int(b) & 0x7F) << shift
3111
+				stringLen |= (uint64(b) & 0x7F) << shift
3112 3112
 				if b < 0x80 {
3113 3113
 					break
3114 3114
 				}
3115 3115
 			}
3116
-			if msglen < 0 {
3116
+			intStringLen := int(stringLen)
3117
+			if intStringLen < 0 {
3117 3118
 				return ErrInvalidLengthDispatcher
3118 3119
 			}
3119
-			postIndex := iNdEx + msglen
3120
+			postIndex := iNdEx + intStringLen
3120 3121
 			if postIndex > l {
3121 3122
 				return io.ErrUnexpectedEOF
3122 3123
 			}
3123
-			m.UpdateSecrets = append(m.UpdateSecrets, &Secret{})
3124
-			if err := m.UpdateSecrets[len(m.UpdateSecrets)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
3125
-				return err
3126
-			}
3124
+			m.ResultsIn = string(data[iNdEx:postIndex])
3127 3125
 			iNdEx = postIndex
3128
-		case 7:
3126
+		case 4:
3129 3127
 			if wireType != 2 {
3130
-				return fmt.Errorf("proto: wrong wireType = %d for field RemoveSecrets", wireType)
3128
+				return fmt.Errorf("proto: wrong wireType = %d for field Changes", wireType)
3131 3129
 			}
3132
-			var stringLen uint64
3130
+			var msglen int
3133 3131
 			for shift := uint(0); ; shift += 7 {
3134 3132
 				if shift >= 64 {
3135 3133
 					return ErrIntOverflowDispatcher
... ...
@@ -3139,20 +3563,22 @@ func (m *AssignmentsMessage) Unmarshal(data []byte) error {
3139 3139
 				}
3140 3140
 				b := data[iNdEx]
3141 3141
 				iNdEx++
3142
-				stringLen |= (uint64(b) & 0x7F) << shift
3142
+				msglen |= (int(b) & 0x7F) << shift
3143 3143
 				if b < 0x80 {
3144 3144
 					break
3145 3145
 				}
3146 3146
 			}
3147
-			intStringLen := int(stringLen)
3148
-			if intStringLen < 0 {
3147
+			if msglen < 0 {
3149 3148
 				return ErrInvalidLengthDispatcher
3150 3149
 			}
3151
-			postIndex := iNdEx + intStringLen
3150
+			postIndex := iNdEx + msglen
3152 3151
 			if postIndex > l {
3153 3152
 				return io.ErrUnexpectedEOF
3154 3153
 			}
3155
-			m.RemoveSecrets = append(m.RemoveSecrets, string(data[iNdEx:postIndex]))
3154
+			m.Changes = append(m.Changes, &AssignmentChange{})
3155
+			if err := m.Changes[len(m.Changes)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
3156
+				return err
3157
+			}
3156 3158
 			iNdEx = postIndex
3157 3159
 		default:
3158 3160
 			iNdEx = preIndex
... ...
@@ -3283,59 +3709,64 @@ var (
3283 3283
 func init() { proto.RegisterFile("dispatcher.proto", fileDescriptorDispatcher) }
3284 3284
 
3285 3285
 var fileDescriptorDispatcher = []byte{
3286
-	// 858 bytes of a gzipped FileDescriptorProto
3287
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0x41, 0x6f, 0x1b, 0x45,
3288
-	0x14, 0xce, 0xd8, 0x8e, 0x53, 0xbf, 0xb5, 0x83, 0x19, 0x2a, 0xba, 0xb2, 0x5a, 0xc7, 0xdd, 0x90,
3289
-	0x28, 0x52, 0x83, 0x53, 0x8c, 0xc4, 0x01, 0x22, 0x20, 0xae, 0x2d, 0x61, 0xb5, 0x49, 0xab, 0x8d,
3290
-	0xa1, 0x47, 0x6b, 0xe3, 0x7d, 0x72, 0x17, 0x27, 0x3b, 0xcb, 0xcc, 0x6c, 0x8b, 0x0f, 0x48, 0x48,
3291
-	0x14, 0x89, 0x23, 0xe2, 0xd4, 0x5f, 0xc1, 0xef, 0x88, 0x38, 0x71, 0xe4, 0x14, 0x11, 0xff, 0x00,
3292
-	0xc4, 0x4f, 0x40, 0xbb, 0x33, 0xeb, 0x18, 0x67, 0xdd, 0x38, 0x39, 0x65, 0xe7, 0xcd, 0xf7, 0x7d,
3293
-	0xef, 0xd3, 0x7b, 0xf3, 0x5e, 0x0c, 0x65, 0xd7, 0x13, 0x81, 0x23, 0xfb, 0x2f, 0x90, 0xd7, 0x03,
3294
-	0xce, 0x24, 0xa3, 0xd4, 0x65, 0xfd, 0x21, 0xf2, 0xba, 0x78, 0xe5, 0xf0, 0x93, 0xa1, 0x27, 0xeb,
3295
-	0x2f, 0x3f, 0xaa, 0x18, 0x72, 0x14, 0xa0, 0x50, 0x80, 0x4a, 0x89, 0x1d, 0x7d, 0x8b, 0x7d, 0x99,
3296
-	0x1c, 0x6f, 0x0f, 0xd8, 0x80, 0xc5, 0x9f, 0x3b, 0xd1, 0x97, 0x8e, 0xbe, 0x17, 0x1c, 0x87, 0x03,
3297
-	0xcf, 0xdf, 0x51, 0x7f, 0x74, 0xf0, 0x8e, 0x1b, 0x72, 0x47, 0x7a, 0xcc, 0xdf, 0x49, 0x3e, 0xd4,
3298
-	0x85, 0xf5, 0x33, 0x81, 0xd5, 0x43, 0x14, 0xc2, 0x63, 0xbe, 0x8d, 0xdf, 0x85, 0x28, 0x24, 0x6d,
3299
-	0x83, 0xe1, 0xa2, 0xe8, 0x73, 0x2f, 0x88, 0x70, 0x26, 0xa9, 0x91, 0x2d, 0xa3, 0xb1, 0x5e, 0xbf,
3300
-	0x6c, 0xae, 0x7e, 0xc0, 0x5c, 0x6c, 0x5d, 0x40, 0xed, 0x69, 0x1e, 0xdd, 0x06, 0x10, 0x4a, 0xb8,
3301
-	0xe7, 0xb9, 0x66, 0xa6, 0x46, 0xb6, 0x0a, 0xcd, 0xd2, 0xf8, 0x6c, 0xad, 0xa0, 0xd3, 0x75, 0x5a,
3302
-	0x76, 0x41, 0x03, 0x3a, 0xae, 0xf5, 0x53, 0x66, 0xe2, 0x63, 0x1f, 0x85, 0x70, 0x06, 0x38, 0x23,
3303
-	0x40, 0xde, 0x2e, 0x40, 0xb7, 0x21, 0xe7, 0x33, 0x17, 0xe3, 0x44, 0x46, 0xc3, 0x9c, 0x67, 0xd7,
3304
-	0x8e, 0x51, 0x74, 0x17, 0x6e, 0x9d, 0x38, 0xbe, 0x33, 0x40, 0x2e, 0xcc, 0x6c, 0x2d, 0xbb, 0x65,
3305
-	0x34, 0x6a, 0x69, 0x8c, 0xe7, 0xe8, 0x0d, 0x5e, 0x48, 0x74, 0x9f, 0x21, 0x72, 0x7b, 0xc2, 0xa0,
3306
-	0xcf, 0xe1, 0x7d, 0x1f, 0xe5, 0x2b, 0xc6, 0x87, 0xbd, 0x23, 0xc6, 0xa4, 0x90, 0xdc, 0x09, 0x7a,
3307
-	0x43, 0x1c, 0x09, 0x33, 0x17, 0x6b, 0xdd, 0x4f, 0xd3, 0x6a, 0xfb, 0x7d, 0x3e, 0x8a, 0x4b, 0xf3,
3308
-	0x18, 0x47, 0xf6, 0x6d, 0x2d, 0xd0, 0x4c, 0xf8, 0x8f, 0x71, 0x24, 0xac, 0x2f, 0xa1, 0xfc, 0x15,
3309
-	0x3a, 0x5c, 0x1e, 0xa1, 0x23, 0x93, 0x76, 0x5c, 0xab, 0x0c, 0xd6, 0x53, 0x78, 0x77, 0x4a, 0x41,
3310
-	0x04, 0xcc, 0x17, 0x48, 0x3f, 0x85, 0x7c, 0x80, 0xdc, 0x63, 0xae, 0x6e, 0xe6, 0xdd, 0x34, 0x7f,
3311
-	0x2d, 0xfd, 0x30, 0x9a, 0xb9, 0xd3, 0xb3, 0xb5, 0x25, 0x5b, 0x33, 0xac, 0x5f, 0x33, 0x70, 0xe7,
3312
-	0xeb, 0xc0, 0x75, 0x24, 0x76, 0x1d, 0x31, 0x3c, 0x94, 0x8e, 0x0c, 0xc5, 0x8d, 0xac, 0xd1, 0x6f,
3313
-	0x60, 0x25, 0x8c, 0x85, 0x92, 0x92, 0xef, 0xa6, 0xd9, 0x98, 0x93, 0xab, 0x7e, 0x11, 0x51, 0x08,
3314
-	0x3b, 0x11, 0xab, 0x30, 0x28, 0xcf, 0x5e, 0xd2, 0x75, 0x58, 0x91, 0x8e, 0x18, 0x5e, 0xd8, 0x82,
3315
-	0xf1, 0xd9, 0x5a, 0x3e, 0x82, 0x75, 0x5a, 0x76, 0x3e, 0xba, 0xea, 0xb8, 0xf4, 0x13, 0xc8, 0x8b,
3316
-	0x98, 0xa4, 0x1f, 0x4d, 0x35, 0xcd, 0xcf, 0x94, 0x13, 0x8d, 0xb6, 0x2a, 0x60, 0x5e, 0x76, 0xa9,
3317
-	0x4a, 0x6d, 0xed, 0x42, 0x31, 0x8a, 0xde, 0xac, 0x44, 0xd6, 0xe7, 0x9a, 0x9d, 0x8c, 0x40, 0x1d,
3318
-	0x96, 0x23, 0xaf, 0xc2, 0x24, 0x71, 0xc1, 0xcc, 0x79, 0x06, 0x6d, 0x05, 0xb3, 0x9a, 0x40, 0xf7,
3319
-	0x84, 0xf0, 0x06, 0xfe, 0x09, 0xfa, 0xf2, 0x86, 0x1e, 0x5e, 0x67, 0xff, 0x27, 0x92, 0x58, 0xf9,
3320
-	0x02, 0x72, 0xd1, 0x2a, 0x8a, 0xe9, 0xab, 0x8d, 0x07, 0x69, 0x4e, 0x2e, 0xb3, 0xea, 0xdd, 0x51,
3321
-	0x80, 0x76, 0x4c, 0xa4, 0xf7, 0x00, 0x9c, 0x20, 0x38, 0xf6, 0x50, 0xf4, 0x24, 0x53, 0xfb, 0xc0,
3322
-	0x2e, 0xe8, 0x48, 0x97, 0x45, 0xd7, 0x1c, 0x45, 0x78, 0x2c, 0x45, 0xcf, 0xf3, 0xcd, 0xac, 0xba,
3323
-	0xd6, 0x91, 0x8e, 0x4f, 0x3f, 0x83, 0xa2, 0xea, 0x77, 0x4f, 0x15, 0x24, 0x77, 0x45, 0x41, 0x8c,
3324
-	0x70, 0xd2, 0x21, 0x41, 0xef, 0x43, 0x91, 0xe3, 0x09, 0x7b, 0x99, 0x90, 0x97, 0x6b, 0xd9, 0xad,
3325
-	0x82, 0x6d, 0xa8, 0x98, 0x82, 0xec, 0xc1, 0xaa, 0xd6, 0x17, 0xd8, 0xe7, 0x28, 0x85, 0x99, 0x8f,
3326
-	0x33, 0x54, 0xd2, 0x32, 0x1c, 0xc6, 0x10, 0xbb, 0xa4, 0x18, 0xea, 0x24, 0xe8, 0x06, 0xac, 0xea,
3327
-	0x2c, 0x89, 0xc4, 0x4a, 0x9c, 0xa7, 0xa4, 0xa2, 0x1a, 0x66, 0x6d, 0x40, 0x2e, 0xaa, 0x0a, 0x2d,
3328
-	0xc2, 0xad, 0x47, 0x4f, 0xf7, 0x9f, 0x3d, 0x69, 0x77, 0xdb, 0xe5, 0x25, 0xfa, 0x0e, 0x18, 0x9d,
3329
-	0x83, 0x47, 0x76, 0x7b, 0xbf, 0x7d, 0xd0, 0xdd, 0x7b, 0x52, 0x26, 0x8d, 0x37, 0xcb, 0x00, 0xad,
3330
-	0xc9, 0x7f, 0x08, 0xfa, 0x3d, 0xac, 0xe8, 0x6e, 0x51, 0x2b, 0xdd, 0xd2, 0xf4, 0x0e, 0xaf, 0xbc,
3331
-	0x0d, 0xa3, 0x7b, 0x63, 0xad, 0xff, 0xf1, 0xfb, 0x3f, 0x6f, 0x32, 0xf7, 0xa0, 0x18, 0x63, 0x3e,
3332
-	0x8c, 0xb6, 0x11, 0x72, 0x28, 0xa9, 0x93, 0xde, 0x75, 0x0f, 0x09, 0xfd, 0x01, 0x0a, 0x93, 0x8d,
3333
-	0x42, 0x3f, 0x48, 0xd3, 0x9d, 0x5d, 0x59, 0x95, 0x8d, 0x2b, 0x50, 0x7a, 0x56, 0x16, 0x31, 0x40,
3334
-	0x7f, 0x23, 0x50, 0x9e, 0x9d, 0x36, 0xfa, 0xe0, 0x1a, 0x9b, 0xa3, 0xb2, 0xbd, 0x18, 0xf8, 0x3a,
3335
-	0xa6, 0x42, 0x58, 0x56, 0xcf, 0xa6, 0x36, 0xef, 0x01, 0x4e, 0xb2, 0xcf, 0x47, 0x24, 0x7d, 0xd8,
3336
-	0x5c, 0x20, 0xe3, 0x2f, 0x19, 0xf2, 0x90, 0xd0, 0xd7, 0x04, 0x8c, 0xa9, 0x21, 0xa3, 0x9b, 0x57,
3337
-	0x4c, 0x61, 0xe2, 0x61, 0x73, 0xb1, 0x69, 0x5d, 0xf0, 0x45, 0x34, 0xef, 0x9e, 0x9e, 0x57, 0x97,
3338
-	0xfe, 0x3a, 0xaf, 0x2e, 0xfd, 0x7b, 0x5e, 0x25, 0x3f, 0x8e, 0xab, 0xe4, 0x74, 0x5c, 0x25, 0x7f,
3339
-	0x8e, 0xab, 0xe4, 0xef, 0x71, 0x95, 0x1c, 0xe5, 0xe3, 0x1f, 0x16, 0x1f, 0xff, 0x17, 0x00, 0x00,
3340
-	0xff, 0xff, 0xf5, 0xa0, 0x46, 0x49, 0xe0, 0x08, 0x00, 0x00,
3286
+	// 939 bytes of a gzipped FileDescriptorProto
3287
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0x4f, 0x6f, 0xdb, 0xc6,
3288
+	0x13, 0xd5, 0xca, 0x32, 0x1d, 0x8f, 0x6c, 0xff, 0xf4, 0xdb, 0x06, 0x89, 0x40, 0x24, 0xb2, 0x4a,
3289
+	0x37, 0x82, 0x81, 0xb8, 0x72, 0xaa, 0xfe, 0x39, 0x14, 0x86, 0x5b, 0xcb, 0x12, 0x60, 0x21, 0x91,
3290
+	0x6d, 0xac, 0x95, 0xe4, 0x28, 0x50, 0xe2, 0x40, 0x66, 0x65, 0x71, 0x59, 0xee, 0x2a, 0xa9, 0x0a,
3291
+	0x14, 0x28, 0xd0, 0x06, 0x28, 0x7a, 0x2a, 0x7a, 0xca, 0xa5, 0x5f, 0xa1, 0x9f, 0xc3, 0xe8, 0xa9,
3292
+	0xc7, 0x9e, 0x8c, 0x5a, 0x1f, 0xa0, 0xe8, 0xb9, 0xa7, 0x82, 0xe4, 0x52, 0x52, 0x15, 0xca, 0x91,
3293
+	0x7d, 0x12, 0x39, 0xf3, 0xde, 0xcc, 0xe3, 0xec, 0xf0, 0x51, 0x90, 0xb1, 0x6c, 0xe1, 0x9a, 0xb2,
3294
+	0x7d, 0x8a, 0x5e, 0xd1, 0xf5, 0xb8, 0xe4, 0x94, 0x5a, 0xbc, 0xdd, 0x45, 0xaf, 0x28, 0x5e, 0x9a,
3295
+	0x5e, 0xaf, 0x6b, 0xcb, 0xe2, 0x8b, 0x0f, 0xf4, 0xb4, 0x1c, 0xb8, 0x28, 0x42, 0x80, 0xbe, 0xca,
3296
+	0x5b, 0x5f, 0x60, 0x5b, 0x46, 0xb7, 0xb7, 0x3b, 0xbc, 0xc3, 0x83, 0xcb, 0x6d, 0xff, 0x4a, 0x45,
3297
+	0xdf, 0x71, 0xcf, 0xfa, 0x1d, 0xdb, 0xd9, 0x0e, 0x7f, 0x54, 0xf0, 0xae, 0xd5, 0xf7, 0x4c, 0x69,
3298
+	0x73, 0x67, 0x3b, 0xba, 0x08, 0x13, 0xc6, 0x2b, 0x02, 0x6b, 0x27, 0x28, 0x84, 0xcd, 0x1d, 0x86,
3299
+	0x5f, 0xf6, 0x51, 0x48, 0x5a, 0x85, 0xb4, 0x85, 0xa2, 0xed, 0xd9, 0xae, 0x8f, 0xcb, 0x92, 0x3c,
3300
+	0xd9, 0x4c, 0x97, 0x36, 0x8a, 0x6f, 0x8a, 0x2b, 0x1e, 0x72, 0x0b, 0x2b, 0x63, 0x28, 0x9b, 0xe4,
3301
+	0xd1, 0x2d, 0x00, 0x11, 0x16, 0x6e, 0xda, 0x56, 0x36, 0x99, 0x27, 0x9b, 0xcb, 0xe5, 0xd5, 0xe1,
3302
+	0xc5, 0xfa, 0xb2, 0x6a, 0x57, 0xab, 0xb0, 0x65, 0x05, 0xa8, 0x59, 0xc6, 0x77, 0xc9, 0x91, 0x8e,
3303
+	0x3a, 0x0a, 0x61, 0x76, 0x70, 0xaa, 0x00, 0xb9, 0xba, 0x00, 0xdd, 0x82, 0x94, 0xc3, 0x2d, 0x0c,
3304
+	0x1a, 0xa5, 0x4b, 0xd9, 0x59, 0x72, 0x59, 0x80, 0xa2, 0x3b, 0x70, 0xab, 0x67, 0x3a, 0x66, 0x07,
3305
+	0x3d, 0x91, 0x5d, 0xc8, 0x2f, 0x6c, 0xa6, 0x4b, 0xf9, 0x38, 0xc6, 0x73, 0xb4, 0x3b, 0xa7, 0x12,
3306
+	0xad, 0x63, 0x44, 0x8f, 0x8d, 0x18, 0xf4, 0x39, 0xdc, 0x71, 0x50, 0xbe, 0xe4, 0x5e, 0xb7, 0xd9,
3307
+	0xe2, 0x5c, 0x0a, 0xe9, 0x99, 0x6e, 0xb3, 0x8b, 0x03, 0x91, 0x4d, 0x05, 0xb5, 0xde, 0x8d, 0xab,
3308
+	0x55, 0x75, 0xda, 0xde, 0x20, 0x18, 0xcd, 0x63, 0x1c, 0xb0, 0xdb, 0xaa, 0x40, 0x39, 0xe2, 0x3f,
3309
+	0xc6, 0x81, 0x30, 0x3e, 0x87, 0xcc, 0x01, 0x9a, 0x9e, 0x6c, 0xa1, 0x29, 0xa3, 0xe3, 0xb8, 0xd6,
3310
+	0x18, 0x8c, 0x23, 0xf8, 0xff, 0x44, 0x05, 0xe1, 0x72, 0x47, 0x20, 0xfd, 0x14, 0x34, 0x17, 0x3d,
3311
+	0x9b, 0x5b, 0xea, 0x30, 0xef, 0xc5, 0xe9, 0xab, 0xa8, 0xc5, 0x28, 0xa7, 0xce, 0x2f, 0xd6, 0x13,
3312
+	0x4c, 0x31, 0x8c, 0x9f, 0x92, 0x70, 0xf7, 0xa9, 0x6b, 0x99, 0x12, 0x1b, 0xa6, 0xe8, 0x9e, 0x48,
3313
+	0x53, 0xf6, 0xc5, 0x8d, 0xa4, 0xd1, 0x67, 0xb0, 0xd4, 0x0f, 0x0a, 0x45, 0x23, 0xdf, 0x89, 0x93,
3314
+	0x31, 0xa3, 0x57, 0x71, 0x1c, 0x09, 0x11, 0x2c, 0x2a, 0xa6, 0x73, 0xc8, 0x4c, 0x27, 0xe9, 0x06,
3315
+	0x2c, 0x49, 0x53, 0x74, 0xc7, 0xb2, 0x60, 0x78, 0xb1, 0xae, 0xf9, 0xb0, 0x5a, 0x85, 0x69, 0x7e,
3316
+	0xaa, 0x66, 0xd1, 0x4f, 0x40, 0x13, 0x01, 0x49, 0x2d, 0x4d, 0x2e, 0x4e, 0xcf, 0x84, 0x12, 0x85,
3317
+	0x36, 0x74, 0xc8, 0xbe, 0xa9, 0x32, 0x1c, 0xb5, 0xb1, 0x03, 0x2b, 0x7e, 0xf4, 0x66, 0x23, 0x32,
3318
+	0x76, 0x15, 0x3b, 0x7a, 0x05, 0x8a, 0xb0, 0xe8, 0x6b, 0x15, 0x59, 0x12, 0x0c, 0x2c, 0x3b, 0x4b,
3319
+	0x20, 0x0b, 0x61, 0x46, 0x19, 0xe8, 0x9e, 0x10, 0x76, 0xc7, 0xe9, 0xa1, 0x23, 0x6f, 0xa8, 0xe1,
3320
+	0x6b, 0x80, 0x71, 0x0d, 0x5a, 0x84, 0x94, 0x5f, 0x5a, 0x2d, 0xce, 0x4c, 0x01, 0x07, 0x09, 0x16,
3321
+	0xe0, 0xe8, 0x47, 0xa0, 0x09, 0x6c, 0x7b, 0x28, 0xd5, 0x4c, 0xf5, 0x38, 0xc6, 0x49, 0x80, 0x38,
3322
+	0x48, 0x30, 0x85, 0x2d, 0x6b, 0x90, 0xb2, 0x25, 0xf6, 0x8c, 0x57, 0x49, 0xc8, 0x8c, 0x9b, 0xef,
3323
+	0x9f, 0x9a, 0x4e, 0x07, 0xe9, 0x2e, 0x80, 0x39, 0x8a, 0x29, 0x21, 0xb1, 0x47, 0x35, 0x66, 0xb2,
3324
+	0x09, 0x06, 0xad, 0x83, 0x66, 0xb6, 0x03, 0x2b, 0xf3, 0x25, 0xad, 0x95, 0x3e, 0xbe, 0x9a, 0x1b,
3325
+	0x76, 0x9d, 0x08, 0xec, 0x05, 0x64, 0xa6, 0x8a, 0x18, 0xad, 0x49, 0x89, 0x61, 0x8e, 0x16, 0x40,
3326
+	0x7b, 0x7a, 0x5c, 0xd9, 0x6b, 0x54, 0x33, 0x09, 0x5d, 0xff, 0xf1, 0x97, 0xfc, 0x9d, 0x69, 0x84,
3327
+	0x5a, 0xcb, 0x02, 0x68, 0xac, 0x5a, 0x3f, 0x7a, 0x56, 0xcd, 0x90, 0x78, 0x1c, 0xc3, 0x1e, 0x7f,
3328
+	0x81, 0xc6, 0x3f, 0xe4, 0x3f, 0x07, 0x19, 0xad, 0xc3, 0x67, 0x90, 0xf2, 0x3f, 0x07, 0xc1, 0x0c,
3329
+	0xd6, 0x4a, 0x0f, 0xaf, 0x7e, 0x8e, 0x88, 0x55, 0x6c, 0x0c, 0x5c, 0x64, 0x01, 0x91, 0xde, 0x07,
3330
+	0x30, 0x5d, 0xf7, 0xcc, 0x46, 0xd1, 0x94, 0x3c, 0xf4, 0x64, 0xb6, 0xac, 0x22, 0x0d, 0xee, 0xa7,
3331
+	0x3d, 0x14, 0xfd, 0x33, 0x29, 0x9a, 0xb6, 0x93, 0x5d, 0x08, 0xd3, 0x2a, 0x52, 0x73, 0xe8, 0x2e,
3332
+	0x2c, 0xb5, 0x83, 0xe1, 0x44, 0x3e, 0xf7, 0xde, 0x3c, 0x93, 0x64, 0x11, 0xc9, 0x78, 0x00, 0x29,
3333
+	0x5f, 0x0b, 0x5d, 0x81, 0x5b, 0xfb, 0x47, 0xf5, 0xe3, 0x27, 0x55, 0x7f, 0x5e, 0xf4, 0x7f, 0x90,
3334
+	0xae, 0x1d, 0xee, 0xb3, 0x6a, 0xbd, 0x7a, 0xd8, 0xd8, 0x7b, 0x92, 0x21, 0xa5, 0xd7, 0x8b, 0x00,
3335
+	0x95, 0xd1, 0xb7, 0x91, 0x7e, 0x05, 0x4b, 0x6a, 0x4f, 0xa9, 0x11, 0xbf, 0x4c, 0x93, 0x5f, 0x2f,
3336
+	0xfd, 0x2a, 0x8c, 0x9a, 0x88, 0xb1, 0xf1, 0xdb, 0xaf, 0x7f, 0xbd, 0x4e, 0xde, 0x87, 0x95, 0x00,
3337
+	0xf3, 0xbe, 0xef, 0xc3, 0xe8, 0xc1, 0x6a, 0x78, 0xa7, 0x5c, 0xfe, 0x11, 0xa1, 0xdf, 0xc0, 0xf2,
3338
+	0xc8, 0x4b, 0x69, 0xec, 0xb3, 0x4e, 0x9b, 0xb5, 0xfe, 0xe0, 0x2d, 0x28, 0xe5, 0x12, 0xf3, 0x08,
3339
+	0xa0, 0x3f, 0x13, 0xc8, 0x4c, 0xfb, 0x0c, 0x7d, 0x78, 0x0d, 0xcf, 0xd4, 0xb7, 0xe6, 0x03, 0x5f,
3340
+	0x47, 0x54, 0x1f, 0x16, 0x03, 0x87, 0xa2, 0xf9, 0x59, 0x56, 0x30, 0xea, 0x3e, 0x1b, 0x11, 0x9d,
3341
+	0x43, 0x61, 0x8e, 0x8e, 0x3f, 0x24, 0xc9, 0x23, 0x42, 0xbf, 0x27, 0x90, 0x9e, 0x58, 0x6d, 0x5a,
3342
+	0x78, 0xcb, 0xee, 0x47, 0x1a, 0x0a, 0xf3, 0xbd, 0x23, 0x73, 0x6e, 0x44, 0xf9, 0xde, 0xf9, 0x65,
3343
+	0x2e, 0xf1, 0xc7, 0x65, 0x2e, 0xf1, 0xf7, 0x65, 0x8e, 0x7c, 0x3b, 0xcc, 0x91, 0xf3, 0x61, 0x8e,
3344
+	0xfc, 0x3e, 0xcc, 0x91, 0x3f, 0x87, 0x39, 0xd2, 0xd2, 0x82, 0xbf, 0x54, 0x1f, 0xfe, 0x1b, 0x00,
3345
+	0x00, 0xff, 0xff, 0x1d, 0x6e, 0x3d, 0x14, 0xda, 0x09, 0x00, 0x00,
3341 3346
 }
... ...
@@ -170,6 +170,23 @@ message AssignmentsRequest {
170 170
 	string session_id = 1 [(gogoproto.customname) = "SessionID"];
171 171
 }
172 172
 
173
+message Assignment {
174
+	oneof item {
175
+		Task task = 1;
176
+		Secret secret = 2;
177
+	}
178
+}
179
+
180
+message AssignmentChange {
181
+	enum AssignmentAction {
182
+		UPDATE = 0 [(gogoproto.enumvalue_customname) = "AssignmentActionUpdate"];
183
+		REMOVE = 1 [(gogoproto.enumvalue_customname) = "AssignmentActionRemove"];
184
+	}
185
+
186
+	Assignment assignment = 1;
187
+	AssignmentAction action = 2;
188
+}
189
+
173 190
 message AssignmentsMessage {
174 191
 	// AssignmentType specifies whether this assignment message carries
175 192
 	// the full state, or is an update to an existing state.
... ...
@@ -192,24 +209,6 @@ message AssignmentsMessage {
192 192
 	// against missed messages.
193 193
 	string results_in = 3;
194 194
 
195
-	// UpdateTasks is a set of new or updated tasks to run on this node.
196
-	// In the first assignments message, it contains all of the tasks
197
-	// to run on this node. Tasks outside of this set running on the node
198
-	// should be terminated.
199
-	repeated Task update_tasks = 4;
200
-
201
-	// RemoveTasks is a set of previously-assigned task IDs to remove from the
202
-	// assignment set. It is not used in the first assignments message of
203
-	// a stream.
204
-	repeated string remove_tasks = 5;
205
-
206
-	// UpdateSecrets is a set of new or updated secrets for this node.
207
-	// In the first assignments message, it contains all of the secrets
208
-	// the node needs for itself and its assigned tasks.
209
-	repeated Secret update_secrets = 6;
210
-
211
-	// RemoveSecrets is a set of previously-assigned secret names to remove
212
-	// from memory. It is not used in the first assignments message of
213
-	// a stream.
214
-	repeated string remove_secrets = 7;
195
+	// AssignmentChange is a set of changes to apply on this node.
196
+	repeated AssignmentChange changes = 4;
215 197
 }
... ...
@@ -473,7 +473,7 @@ type ContainerSpec struct {
473 473
 	StopGracePeriod *docker_swarmkit_v11.Duration `protobuf:"bytes,9,opt,name=stop_grace_period,json=stopGracePeriod" json:"stop_grace_period,omitempty"`
474 474
 	// PullOptions parameterize the behavior of image pulls.
475 475
 	PullOptions *ContainerSpec_PullOptions `protobuf:"bytes,10,opt,name=pull_options,json=pullOptions" json:"pull_options,omitempty"`
476
-	// Secrets contains references to zero or more secrets that
476
+	// SecretReference contains references to zero or more secrets that
477 477
 	// will be exposed to the container.
478 478
 	Secrets []*SecretReference `protobuf:"bytes,12,rep,name=secrets" json:"secrets,omitempty"`
479 479
 }
... ...
@@ -189,7 +189,7 @@ message ContainerSpec {
189 189
 	// PullOptions parameterize the behavior of image pulls.
190 190
 	PullOptions pull_options = 10;
191 191
 
192
-	// Secrets contains references to zero or more secrets that
192
+	// SecretReference contains references to zero or more secrets that
193 193
 	// will be exposed to the container.
194 194
 	repeated SecretReference secrets = 12;
195 195
 }
... ...
@@ -133,6 +133,8 @@
133 133
 		TasksRequest
134 134
 		TasksMessage
135 135
 		AssignmentsRequest
136
+		Assignment
137
+		AssignmentChange
136 138
 		AssignmentsMessage
137 139
 		NodeCertificateStatusRequest
138 140
 		NodeCertificateStatusResponse
... ...
@@ -1053,8 +1055,8 @@ func _TaskStatus_OneofSizer(msg proto.Message) (n int) {
1053 1053
 // instructing Swarm on how this service should work on the particular
1054 1054
 // network.
1055 1055
 type NetworkAttachmentConfig struct {
1056
-	// Target specifies the target network for attachment. This value may be a
1057
-	// network name or identifier. Only identifiers are supported at this time.
1056
+	// Target specifies the target network for attachment. This value must be a
1057
+	// network ID.
1058 1058
 	Target string `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"`
1059 1059
 	// Aliases specifies a list of discoverable alternate names for the service on this Target.
1060 1060
 	Aliases []string `protobuf:"bytes,2,rep,name=aliases" json:"aliases,omitempty"`
... ...
@@ -447,8 +447,8 @@ message TaskStatus {
447 447
 // instructing Swarm on how this service should work on the particular
448 448
 // network.
449 449
 message NetworkAttachmentConfig {
450
-	// Target specifies the target network for attachment. This value may be a
451
-	// network name or identifier. Only identifiers are supported at this time.
450
+	// Target specifies the target network for attachment. This value must be a
451
+	// network ID.
452 452
 	string target = 1;
453 453
 	// Aliases specifies a list of discoverable alternate names for the service on this Target.
454 454
 	repeated string aliases = 2;
455 455
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+package networkallocator
1
+
2
+import (
3
+	"github.com/docker/libnetwork/drvregistry"
4
+	"github.com/docker/libnetwork/ipamapi"
5
+	builtinIpam "github.com/docker/libnetwork/ipams/builtin"
6
+	nullIpam "github.com/docker/libnetwork/ipams/null"
7
+	remoteIpam "github.com/docker/libnetwork/ipams/remote"
8
+)
9
+
10
+func initIPAMDrivers(r *drvregistry.DrvRegistry) error {
11
+	for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){
12
+		builtinIpam.Init,
13
+		remoteIpam.Init,
14
+		nullIpam.Init,
15
+	} {
16
+		if err := fn(r, nil, nil); err != nil {
17
+			return err
18
+		}
19
+	}
20
+
21
+	return nil
22
+}
0 23
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+package networkallocator
1
+
2
+import (
3
+	"github.com/docker/libnetwork/drivers/overlay/ovmanager"
4
+	"github.com/docker/libnetwork/drivers/remote"
5
+)
6
+
7
+func getInitializers() []initializer {
8
+	return []initializer{
9
+		{remote.Init, "remote"},
10
+		{ovmanager.Init, "overlay"},
11
+	}
12
+}
0 13
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build !linux
1
+
2
+package networkallocator
3
+
4
+func getInitializers() []initializer {
5
+	return nil
6
+}
... ...
@@ -4,12 +4,11 @@ import (
4 4
 	"fmt"
5 5
 	"net"
6 6
 
7
+	"github.com/docker/docker/pkg/plugins"
8
+	"github.com/docker/libnetwork/datastore"
7 9
 	"github.com/docker/libnetwork/driverapi"
8
-	"github.com/docker/libnetwork/drivers/overlay/ovmanager"
9 10
 	"github.com/docker/libnetwork/drvregistry"
10 11
 	"github.com/docker/libnetwork/ipamapi"
11
-	builtinIpam "github.com/docker/libnetwork/ipams/builtin"
12
-	nullIpam "github.com/docker/libnetwork/ipams/null"
13 12
 	"github.com/docker/swarmkit/api"
14 13
 	"github.com/docker/swarmkit/log"
15 14
 	"github.com/pkg/errors"
... ...
@@ -23,10 +22,6 @@ const (
23 23
 	DefaultDriver = "overlay"
24 24
 )
25 25
 
26
-var (
27
-	defaultDriverInitFunc = ovmanager.Init
28
-)
29
-
30 26
 // NetworkAllocator acts as the controller for all network related operations
31 27
 // like managing network and IPAM drivers and also creating and
32 28
 // deleting networks and the associated resources.
... ...
@@ -68,6 +63,11 @@ type network struct {
68 68
 	endpoints map[string]string
69 69
 }
70 70
 
71
+type initializer struct {
72
+	fn    drvregistry.InitFunc
73
+	ntype string
74
+}
75
+
71 76
 // New returns a new NetworkAllocator handle
72 77
 func New() (*NetworkAllocator, error) {
73 78
 	na := &NetworkAllocator{
... ...
@@ -84,18 +84,12 @@ func New() (*NetworkAllocator, error) {
84 84
 		return nil, err
85 85
 	}
86 86
 
87
-	// Add the manager component of overlay driver to the registry.
88
-	if err := reg.AddDriver(DefaultDriver, defaultDriverInitFunc, nil); err != nil {
87
+	if err := initializeDrivers(reg); err != nil {
89 88
 		return nil, err
90 89
 	}
91 90
 
92
-	for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){
93
-		builtinIpam.Init,
94
-		nullIpam.Init,
95
-	} {
96
-		if err := fn(reg, nil, nil); err != nil {
97
-			return nil, err
98
-		}
91
+	if err = initIPAMDrivers(reg); err != nil {
92
+		return nil, err
99 93
 	}
100 94
 
101 95
 	pa, err := newPortAllocator()
... ...
@@ -631,14 +625,33 @@ func (na *NetworkAllocator) resolveDriver(n *api.Network) (driverapi.Driver, str
631 631
 		dName = n.Spec.DriverConfig.Name
632 632
 	}
633 633
 
634
-	d, _ := na.drvRegistry.Driver(dName)
634
+	d, drvcap := na.drvRegistry.Driver(dName)
635 635
 	if d == nil {
636
-		return nil, "", fmt.Errorf("could not resolve network driver %s", dName)
636
+		var err error
637
+		err = na.loadDriver(dName)
638
+		if err != nil {
639
+			return nil, "", err
640
+		}
641
+
642
+		d, drvcap = na.drvRegistry.Driver(dName)
643
+		if d == nil {
644
+			return nil, "", fmt.Errorf("could not resolve network driver %s", dName)
645
+		}
646
+
647
+	}
648
+
649
+	if drvcap.DataScope != datastore.GlobalScope {
650
+		return nil, "", fmt.Errorf("swarm can allocate network resources only for global scoped networks. network driver (%s) is scoped %s", dName, drvcap.DataScope)
637 651
 	}
638 652
 
639 653
 	return d, dName, nil
640 654
 }
641 655
 
656
+func (na *NetworkAllocator) loadDriver(name string) error {
657
+	_, err := plugins.Get(name, driverapi.NetworkPluginEndpointType)
658
+	return err
659
+}
660
+
642 661
 // Resolve the IPAM driver
643 662
 func (na *NetworkAllocator) resolveIPAM(n *api.Network) (ipamapi.Ipam, string, error) {
644 663
 	dName := ipamapi.DefaultIPAM
... ...
@@ -746,3 +759,12 @@ func (na *NetworkAllocator) allocatePools(n *api.Network) (map[string]string, er
746 746
 
747 747
 	return pools, nil
748 748
 }
749
+
750
+func initializeDrivers(reg *drvregistry.DrvRegistry) error {
751
+	for _, i := range getInitializers() {
752
+		if err := reg.AddDriver(i.ntype, i.fn, nil); err != nil {
753
+			return err
754
+		}
755
+	}
756
+	return nil
757
+}
749 758
new file mode 100644
... ...
@@ -0,0 +1,164 @@
0
+package constraint
1
+
2
+import (
3
+	"fmt"
4
+	"regexp"
5
+	"strings"
6
+
7
+	"github.com/docker/swarmkit/api"
8
+)
9
+
10
+const (
11
+	eq = iota
12
+	noteq
13
+
14
+	nodeLabelPrefix   = "node.labels."
15
+	engineLabelPrefix = "engine.labels."
16
+)
17
+
18
+var (
19
+	alphaNumeric = regexp.MustCompile(`^(?i)[a-z_][a-z0-9\-_.]+$`)
20
+	// value can be alphanumeric and some special characters. it shouldn't container
21
+	// current or future operators like '>, <, ~', etc.
22
+	valuePattern = regexp.MustCompile(`^(?i)[a-z0-9:\-_\s\.\*\(\)\?\+\[\]\\\^\$\|\/]+$`)
23
+
24
+	// operators defines list of accepted operators
25
+	operators = []string{"==", "!="}
26
+)
27
+
28
+// Constraint defines a constraint.
29
+type Constraint struct {
30
+	key      string
31
+	operator int
32
+	exp      string
33
+}
34
+
35
+// Parse parses list of constraints.
36
+func Parse(env []string) ([]Constraint, error) {
37
+	exprs := []Constraint{}
38
+	for _, e := range env {
39
+		found := false
40
+		// each expr is in the form of "key op value"
41
+		for i, op := range operators {
42
+			if !strings.Contains(e, op) {
43
+				continue
44
+			}
45
+			// split with the op
46
+			parts := strings.SplitN(e, op, 2)
47
+
48
+			if len(parts) < 2 {
49
+				return nil, fmt.Errorf("invalid expr: %s", e)
50
+			}
51
+
52
+			part0 := strings.TrimSpace(parts[0])
53
+			// validate key
54
+			matched := alphaNumeric.MatchString(part0)
55
+			if matched == false {
56
+				return nil, fmt.Errorf("key '%s' is invalid", part0)
57
+			}
58
+
59
+			part1 := strings.TrimSpace(parts[1])
60
+
61
+			// validate Value
62
+			matched = valuePattern.MatchString(part1)
63
+			if matched == false {
64
+				return nil, fmt.Errorf("value '%s' is invalid", part1)
65
+			}
66
+			// TODO(dongluochen): revisit requirements to see if globing or regex are useful
67
+			exprs = append(exprs, Constraint{key: part0, operator: i, exp: part1})
68
+
69
+			found = true
70
+			break // found an op, move to next entry
71
+		}
72
+		if !found {
73
+			return nil, fmt.Errorf("constraint expected one operator from %s", strings.Join(operators, ", "))
74
+		}
75
+	}
76
+	return exprs, nil
77
+}
78
+
79
+// Match checks if the Constraint matches the target strings.
80
+func (c *Constraint) Match(whats ...string) bool {
81
+	var match bool
82
+
83
+	// full string match
84
+	for _, what := range whats {
85
+		// case insensitive compare
86
+		if strings.EqualFold(c.exp, what) {
87
+			match = true
88
+			break
89
+		}
90
+	}
91
+
92
+	switch c.operator {
93
+	case eq:
94
+		return match
95
+	case noteq:
96
+		return !match
97
+	}
98
+
99
+	return false
100
+}
101
+
102
+// NodeMatches returns true if the node satisfies the given constraints.
103
+func NodeMatches(constraints []Constraint, n *api.Node) bool {
104
+	for _, constraint := range constraints {
105
+		switch {
106
+		case strings.EqualFold(constraint.key, "node.id"):
107
+			if !constraint.Match(n.ID) {
108
+				return false
109
+			}
110
+		case strings.EqualFold(constraint.key, "node.hostname"):
111
+			// if this node doesn't have hostname
112
+			// it's equivalent to match an empty hostname
113
+			// where '==' would fail, '!=' matches
114
+			if n.Description == nil {
115
+				if !constraint.Match("") {
116
+					return false
117
+				}
118
+				continue
119
+			}
120
+			if !constraint.Match(n.Description.Hostname) {
121
+				return false
122
+			}
123
+		case strings.EqualFold(constraint.key, "node.role"):
124
+			if !constraint.Match(n.Spec.Role.String()) {
125
+				return false
126
+			}
127
+
128
+		// node labels constraint in form like 'node.labels.key==value'
129
+		case len(constraint.key) > len(nodeLabelPrefix) && strings.EqualFold(constraint.key[:len(nodeLabelPrefix)], nodeLabelPrefix):
130
+			if n.Spec.Annotations.Labels == nil {
131
+				if !constraint.Match("") {
132
+					return false
133
+				}
134
+				continue
135
+			}
136
+			label := constraint.key[len(nodeLabelPrefix):]
137
+			// label itself is case sensitive
138
+			val := n.Spec.Annotations.Labels[label]
139
+			if !constraint.Match(val) {
140
+				return false
141
+			}
142
+
143
+		// engine labels constraint in form like 'engine.labels.key!=value'
144
+		case len(constraint.key) > len(engineLabelPrefix) && strings.EqualFold(constraint.key[:len(engineLabelPrefix)], engineLabelPrefix):
145
+			if n.Description == nil || n.Description.Engine == nil || n.Description.Engine.Labels == nil {
146
+				if !constraint.Match("") {
147
+					return false
148
+				}
149
+				continue
150
+			}
151
+			label := constraint.key[len(engineLabelPrefix):]
152
+			val := n.Description.Engine.Labels[label]
153
+			if !constraint.Match(val) {
154
+				return false
155
+			}
156
+		default:
157
+			// key doesn't match predefined syntax
158
+			return false
159
+		}
160
+	}
161
+
162
+	return true
163
+}
... ...
@@ -4,10 +4,8 @@ import (
4 4
 	"fmt"
5 5
 	"net"
6 6
 
7
-	"github.com/docker/libnetwork/ipamapi"
8 7
 	"github.com/docker/swarmkit/api"
9 8
 	"github.com/docker/swarmkit/identity"
10
-	"github.com/docker/swarmkit/manager/allocator/networkallocator"
11 9
 	"github.com/docker/swarmkit/manager/state/store"
12 10
 	"golang.org/x/net/context"
13 11
 	"google.golang.org/grpc"
... ...
@@ -60,10 +58,6 @@ func validateIPAM(ipam *api.IPAMOptions) error {
60 60
 		return err
61 61
 	}
62 62
 
63
-	if ipam.Driver != nil && ipam.Driver.Name != ipamapi.DefaultIPAM {
64
-		return grpc.Errorf(codes.InvalidArgument, "invalid IPAM specified")
65
-	}
66
-
67 63
 	for _, ipamConf := range ipam.Configs {
68 64
 		if err := validateIPAMConfiguration(ipamConf); err != nil {
69 65
 			return err
... ...
@@ -86,10 +80,6 @@ func validateNetworkSpec(spec *api.NetworkSpec) error {
86 86
 		return err
87 87
 	}
88 88
 
89
-	if spec.DriverConfig != nil && spec.DriverConfig.Name != networkallocator.DefaultDriver {
90
-		return grpc.Errorf(codes.InvalidArgument, "invalid driver specified")
91
-	}
92
-
93 89
 	if err := validateIPAM(spec.IPAM); err != nil {
94 90
 		return err
95 91
 	}
... ...
@@ -8,7 +8,7 @@ import (
8 8
 	"github.com/docker/distribution/reference"
9 9
 	"github.com/docker/swarmkit/api"
10 10
 	"github.com/docker/swarmkit/identity"
11
-	"github.com/docker/swarmkit/manager/scheduler"
11
+	"github.com/docker/swarmkit/manager/constraint"
12 12
 	"github.com/docker/swarmkit/manager/state/store"
13 13
 	"github.com/docker/swarmkit/protobuf/ptypes"
14 14
 	"golang.org/x/net/context"
... ...
@@ -81,7 +81,7 @@ func validatePlacement(placement *api.Placement) error {
81 81
 	if placement == nil {
82 82
 		return nil
83 83
 	}
84
-	_, err := scheduler.ParseExprs(placement.Constraints)
84
+	_, err := constraint.Parse(placement.Constraints)
85 85
 	return err
86 86
 }
87 87
 
... ...
@@ -170,6 +170,24 @@ func validateEndpointSpec(epSpec *api.EndpointSpec) error {
170 170
 	return nil
171 171
 }
172 172
 
173
+func (s *Server) validateNetworks(networks []*api.NetworkAttachmentConfig) error {
174
+	for _, na := range networks {
175
+		var network *api.Network
176
+		s.store.View(func(tx store.ReadTx) {
177
+			network = store.GetNetwork(tx, na.Target)
178
+		})
179
+		if network == nil {
180
+			continue
181
+		}
182
+		if _, ok := network.Spec.Annotations.Labels["com.docker.swarm.internal"]; ok {
183
+			return grpc.Errorf(codes.InvalidArgument,
184
+				"Service cannot be explicitly attached to %q network which is a swarm internal network",
185
+				network.Spec.Annotations.Name)
186
+		}
187
+	}
188
+	return nil
189
+}
190
+
173 191
 func validateServiceSpec(spec *api.ServiceSpec) error {
174 192
 	if spec == nil {
175 193
 		return grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
... ...
@@ -259,6 +277,10 @@ func (s *Server) CreateService(ctx context.Context, request *api.CreateServiceRe
259 259
 		return nil, err
260 260
 	}
261 261
 
262
+	if err := s.validateNetworks(request.Spec.Networks); err != nil {
263
+		return nil, err
264
+	}
265
+
262 266
 	if err := s.checkPortConflicts(request.Spec, ""); err != nil {
263 267
 		return nil, err
264 268
 	}
... ...
@@ -759,6 +759,7 @@ func (d *Dispatcher) Assignments(r *api.AssignmentsRequest, stream api.Dispatche
759 759
 		initial   api.AssignmentsMessage
760 760
 	)
761 761
 	tasksMap := make(map[string]*api.Task)
762
+	tasksUsingSecret := make(map[string]map[string]struct{})
762 763
 
763 764
 	sendMessage := func(msg api.AssignmentsMessage, assignmentType api.AssignmentsMessage_Type) error {
764 765
 		sequence++
... ...
@@ -773,6 +774,45 @@ func (d *Dispatcher) Assignments(r *api.AssignmentsRequest, stream api.Dispatche
773 773
 		return nil
774 774
 	}
775 775
 
776
+	// returns a slice of new secrets to send down
777
+	addSecretsForTask := func(readTx store.ReadTx, t *api.Task) []*api.Secret {
778
+		container := t.Spec.GetContainer()
779
+		if container == nil {
780
+			return nil
781
+		}
782
+		var newSecrets []*api.Secret
783
+		for _, secretRef := range container.Secrets {
784
+			secretID := secretRef.SecretID
785
+			log := log.WithFields(logrus.Fields{
786
+				"secret.id":   secretID,
787
+				"secret.name": secretRef.SecretName,
788
+			})
789
+
790
+			if tasksUsingSecret[secretID] == nil {
791
+				tasksUsingSecret[secretID] = make(map[string]struct{})
792
+
793
+				secrets, err := store.FindSecrets(readTx, store.ByIDPrefix(secretID))
794
+				if err != nil {
795
+					log.WithError(err).Errorf("error retrieving secret")
796
+					continue
797
+				}
798
+				if len(secrets) != 1 {
799
+					log.Debugf("secret not found")
800
+					continue
801
+				}
802
+
803
+				// If the secret was found and there was one result
804
+				// (there should never be more than one because of the
805
+				// uniqueness constraint), add this secret to our
806
+				// initial set that we send down.
807
+				newSecrets = append(newSecrets, secrets[0])
808
+			}
809
+			tasksUsingSecret[secretID][t.ID] = struct{}{}
810
+		}
811
+
812
+		return newSecrets
813
+	}
814
+
776 815
 	// TODO(aaronl): Also send node secrets that should be exposed to
777 816
 	// this node.
778 817
 	nodeTasks, cancel, err := store.ViewAndWatch(
... ...
@@ -794,7 +834,31 @@ func (d *Dispatcher) Assignments(r *api.AssignmentsRequest, stream api.Dispatche
794 794
 				}
795 795
 
796 796
 				tasksMap[t.ID] = t
797
-				initial.UpdateTasks = append(initial.UpdateTasks, t)
797
+				taskChange := &api.AssignmentChange{
798
+					Assignment: &api.Assignment{
799
+						Item: &api.Assignment_Task{
800
+							Task: t,
801
+						},
802
+					},
803
+					Action: api.AssignmentChange_AssignmentActionUpdate,
804
+				}
805
+				initial.Changes = append(initial.Changes, taskChange)
806
+				// Only send secrets down if these tasks are in < RUNNING
807
+				if t.Status.State <= api.TaskStateRunning {
808
+					newSecrets := addSecretsForTask(readTx, t)
809
+					for _, secret := range newSecrets {
810
+						secretChange := &api.AssignmentChange{
811
+							Assignment: &api.Assignment{
812
+								Item: &api.Assignment_Secret{
813
+									Secret: secret,
814
+								},
815
+							},
816
+							Action: api.AssignmentChange_AssignmentActionUpdate,
817
+						}
818
+
819
+						initial.Changes = append(initial.Changes, secretChange)
820
+					}
821
+				}
798 822
 			}
799 823
 			return nil
800 824
 		},
... ...
@@ -802,6 +866,8 @@ func (d *Dispatcher) Assignments(r *api.AssignmentsRequest, stream api.Dispatche
802 802
 			Checks: []state.TaskCheckFunc{state.TaskCheckNodeID}},
803 803
 		state.EventDeleteTask{Task: &api.Task{NodeID: nodeID},
804 804
 			Checks: []state.TaskCheckFunc{state.TaskCheckNodeID}},
805
+		state.EventUpdateSecret{},
806
+		state.EventDeleteSecret{},
805 807
 	)
806 808
 	if err != nil {
807 809
 		return err
... ...
@@ -825,7 +891,9 @@ func (d *Dispatcher) Assignments(r *api.AssignmentsRequest, stream api.Dispatche
825 825
 			batchingTimer   *time.Timer
826 826
 			batchingTimeout <-chan time.Time
827 827
 			updateTasks     = make(map[string]*api.Task)
828
+			updateSecrets   = make(map[string]*api.Secret)
828 829
 			removeTasks     = make(map[string]struct{})
830
+			removeSecrets   = make(map[string]struct{})
829 831
 		)
830 832
 
831 833
 		oneModification := func() {
... ...
@@ -839,6 +907,28 @@ func (d *Dispatcher) Assignments(r *api.AssignmentsRequest, stream api.Dispatche
839 839
 			}
840 840
 		}
841 841
 
842
+		// Release the secrets references from this task
843
+		releaseSecretsForTask := func(t *api.Task) bool {
844
+			var modified bool
845
+			container := t.Spec.GetContainer()
846
+			if container == nil {
847
+				return modified
848
+			}
849
+
850
+			for _, secretRef := range container.Secrets {
851
+				secretID := secretRef.SecretID
852
+				delete(tasksUsingSecret[secretID], t.ID)
853
+				if len(tasksUsingSecret[secretID]) == 0 {
854
+					// No tasks are using the secret anymore
855
+					delete(tasksUsingSecret, secretID)
856
+					removeSecrets[secretID] = struct{}{}
857
+					modified = true
858
+				}
859
+			}
860
+
861
+			return modified
862
+		}
863
+
842 864
 		// The batching loop waits for 50 ms after the most recent
843 865
 		// change, or until modificationBatchLimit is reached. The
844 866
 		// worst case latency is modificationBatchLimit * batchingWaitTime,
... ...
@@ -867,15 +957,35 @@ func (d *Dispatcher) Assignments(r *api.AssignmentsRequest, stream api.Dispatche
867 867
 						if equality.TasksEqualStable(oldTask, v.Task) && v.Task.Status.State > api.TaskStateAssigned {
868 868
 							// this update should not trigger a task change for the agent
869 869
 							tasksMap[v.Task.ID] = v.Task
870
+							// If this task got updated to a final state, let's release
871
+							// the secrets that are being used by the task
872
+							if v.Task.Status.State > api.TaskStateRunning {
873
+								// If releasing the secrets caused a secret to be
874
+								// removed from an agent, mark one modification
875
+								if releaseSecretsForTask(v.Task) {
876
+									oneModification()
877
+								}
878
+							}
870 879
 							continue
871 880
 						}
881
+					} else if v.Task.Status.State <= api.TaskStateRunning {
882
+						// If this task wasn't part of the assignment set before, and it's <= RUNNING
883
+						// add the secrets it references to the secrets assignment.
884
+						// Task states > RUNNING are worker reported only, are never created in
885
+						// a > RUNNING state.
886
+						var newSecrets []*api.Secret
887
+						d.store.View(func(readTx store.ReadTx) {
888
+							newSecrets = addSecretsForTask(readTx, v.Task)
889
+						})
890
+						for _, secret := range newSecrets {
891
+							updateSecrets[secret.ID] = secret
892
+						}
872 893
 					}
873 894
 					tasksMap[v.Task.ID] = v.Task
874 895
 					updateTasks[v.Task.ID] = v.Task
875 896
 
876 897
 					oneModification()
877 898
 				case state.EventDeleteTask:
878
-
879 899
 					if _, exists := tasksMap[v.Task.ID]; !exists {
880 900
 						continue
881 901
 					}
... ...
@@ -884,7 +994,28 @@ func (d *Dispatcher) Assignments(r *api.AssignmentsRequest, stream api.Dispatche
884 884
 
885 885
 					delete(tasksMap, v.Task.ID)
886 886
 
887
+					// Release the secrets being used by this task
888
+					// Ignoring the return here. We will always mark
889
+					// this as a modification, since a task is being
890
+					// removed.
891
+					releaseSecretsForTask(v.Task)
892
+
887 893
 					oneModification()
894
+				// TODO(aaronl): For node secrets, we'll need to handle
895
+				// EventCreateSecret.
896
+				case state.EventUpdateSecret:
897
+					if _, exists := tasksUsingSecret[v.Secret.ID]; !exists {
898
+						continue
899
+					}
900
+					log.Debugf("Secret %s (ID: %d) was updated though it was still referenced by one or more tasks",
901
+						v.Secret.Spec.Annotations.Name, v.Secret.ID)
902
+
903
+				case state.EventDeleteSecret:
904
+					if _, exists := tasksUsingSecret[v.Secret.ID]; !exists {
905
+						continue
906
+					}
907
+					log.Debugf("Secret %s (ID: %d) was deleted though it was still referenced by one or more tasks",
908
+						v.Secret.Spec.Annotations.Name, v.Secret.ID)
888 909
 				}
889 910
 			case <-batchingTimeout:
890 911
 				break batchingLoop
... ...
@@ -902,12 +1033,57 @@ func (d *Dispatcher) Assignments(r *api.AssignmentsRequest, stream api.Dispatche
902 902
 		if modificationCnt > 0 {
903 903
 			for id, task := range updateTasks {
904 904
 				if _, ok := removeTasks[id]; !ok {
905
-					update.UpdateTasks = append(update.UpdateTasks, task)
905
+					taskChange := &api.AssignmentChange{
906
+						Assignment: &api.Assignment{
907
+							Item: &api.Assignment_Task{
908
+								Task: task,
909
+							},
910
+						},
911
+						Action: api.AssignmentChange_AssignmentActionUpdate,
912
+					}
913
+
914
+					update.Changes = append(update.Changes, taskChange)
915
+				}
916
+			}
917
+			for id, secret := range updateSecrets {
918
+				if _, ok := removeSecrets[id]; !ok {
919
+					secretChange := &api.AssignmentChange{
920
+						Assignment: &api.Assignment{
921
+							Item: &api.Assignment_Secret{
922
+								Secret: secret,
923
+							},
924
+						},
925
+						Action: api.AssignmentChange_AssignmentActionUpdate,
926
+					}
927
+
928
+					update.Changes = append(update.Changes, secretChange)
906 929
 				}
907 930
 			}
908 931
 			for id := range removeTasks {
909
-				update.RemoveTasks = append(update.RemoveTasks, id)
932
+				taskChange := &api.AssignmentChange{
933
+					Assignment: &api.Assignment{
934
+						Item: &api.Assignment_Task{
935
+							Task: &api.Task{ID: id},
936
+						},
937
+					},
938
+					Action: api.AssignmentChange_AssignmentActionRemove,
939
+				}
940
+
941
+				update.Changes = append(update.Changes, taskChange)
910 942
 			}
943
+			for id := range removeSecrets {
944
+				secretChange := &api.AssignmentChange{
945
+					Assignment: &api.Assignment{
946
+						Item: &api.Assignment_Secret{
947
+							Secret: &api.Secret{ID: id},
948
+						},
949
+					},
950
+					Action: api.AssignmentChange_AssignmentActionRemove,
951
+				}
952
+
953
+				update.Changes = append(update.Changes, secretChange)
954
+			}
955
+
911 956
 			if err := sendMessage(update, api.AssignmentsMessage_INCREMENTAL); err != nil {
912 957
 				return err
913 958
 			}
... ...
@@ -184,6 +184,10 @@ func New(config *Config) (*Manager, error) {
184 184
 			} else if err != nil {
185 185
 				return nil, err
186 186
 			}
187
+			if proto == "tcp" {
188
+				// in case of 0 port
189
+				tcpAddr = l.Addr().String()
190
+			}
187 191
 			listeners[proto] = l
188 192
 		}
189 193
 	}
... ...
@@ -197,7 +201,7 @@ func New(config *Config) (*Manager, error) {
197 197
 		raftCfg.HeartbeatTick = int(config.HeartbeatTick)
198 198
 	}
199 199
 
200
-	newNodeOpts := raft.NewNodeOptions{
200
+	newNodeOpts := raft.NodeOptions{
201 201
 		ID:              config.SecurityConfig.ClientTLSCreds.NodeID(),
202 202
 		Addr:            tcpAddr,
203 203
 		JoinAddr:        config.JoinRaft,
... ...
@@ -226,6 +230,14 @@ func New(config *Config) (*Manager, error) {
226 226
 	return m, nil
227 227
 }
228 228
 
229
+// Addr returns tcp address on which remote api listens.
230
+func (m *Manager) Addr() net.Addr {
231
+	if l, ok := m.listeners["tcp"]; ok {
232
+		return l.Addr()
233
+	}
234
+	return nil
235
+}
236
+
229 237
 // Run starts all manager sub-systems and the gRPC server at the configured
230 238
 // address.
231 239
 // The call never returns unless an error occurs or `Stop()` is called.
... ...
@@ -3,19 +3,27 @@ package orchestrator
3 3
 import (
4 4
 	"github.com/docker/swarmkit/api"
5 5
 	"github.com/docker/swarmkit/log"
6
+	"github.com/docker/swarmkit/manager/constraint"
6 7
 	"github.com/docker/swarmkit/manager/state"
7 8
 	"github.com/docker/swarmkit/manager/state/store"
8 9
 	"golang.org/x/net/context"
9 10
 )
10 11
 
12
+type globalService struct {
13
+	*api.Service
14
+
15
+	// Compiled constraints
16
+	constraints []constraint.Constraint
17
+}
18
+
11 19
 // GlobalOrchestrator runs a reconciliation loop to create and destroy
12 20
 // tasks as necessary for global services.
13 21
 type GlobalOrchestrator struct {
14 22
 	store *store.MemoryStore
15
-	// nodes contains nodeID of all valid nodes in the cluster
16
-	nodes map[string]struct{}
17
-	// globalServices have all the global services in the cluster, indexed by ServiceID
18
-	globalServices map[string]*api.Service
23
+	// nodes is the set of non-drained nodes in the cluster, indexed by node ID
24
+	nodes map[string]*api.Node
25
+	// globalServices has all the global services in the cluster, indexed by ServiceID
26
+	globalServices map[string]globalService
19 27
 
20 28
 	// stopChan signals to the state machine to stop running.
21 29
 	stopChan chan struct{}
... ...
@@ -34,8 +42,8 @@ func NewGlobalOrchestrator(store *store.MemoryStore) *GlobalOrchestrator {
34 34
 	updater := NewUpdateSupervisor(store, restartSupervisor)
35 35
 	return &GlobalOrchestrator{
36 36
 		store:          store,
37
-		nodes:          make(map[string]struct{}),
38
-		globalServices: make(map[string]*api.Service),
37
+		nodes:          make(map[string]*api.Node),
38
+		globalServices: make(map[string]globalService),
39 39
 		stopChan:       make(chan struct{}),
40 40
 		doneChan:       make(chan struct{}),
41 41
 		updater:        updater,
... ...
@@ -76,10 +84,7 @@ func (g *GlobalOrchestrator) Run(ctx context.Context) error {
76 76
 		return err
77 77
 	}
78 78
 	for _, n := range nodes {
79
-		// if a node is in drain state, do not add it
80
-		if isValidNode(n) {
81
-			g.nodes[n.ID] = struct{}{}
82
-		}
79
+		g.updateNode(n)
83 80
 	}
84 81
 
85 82
 	// Lookup global services
... ...
@@ -90,12 +95,15 @@ func (g *GlobalOrchestrator) Run(ctx context.Context) error {
90 90
 	if err != nil {
91 91
 		return err
92 92
 	}
93
+
94
+	var reconcileServiceIDs []string
93 95
 	for _, s := range existingServices {
94 96
 		if isGlobalService(s) {
95
-			g.globalServices[s.ID] = s
96
-			g.reconcileOneService(ctx, s)
97
+			g.updateService(s)
98
+			reconcileServiceIDs = append(reconcileServiceIDs, s.ID)
97 99
 		}
98 100
 	}
101
+	g.reconcileServices(ctx, reconcileServiceIDs)
99 102
 
100 103
 	for {
101 104
 		select {
... ...
@@ -108,14 +116,14 @@ func (g *GlobalOrchestrator) Run(ctx context.Context) error {
108 108
 				if !isGlobalService(v.Service) {
109 109
 					continue
110 110
 				}
111
-				g.globalServices[v.Service.ID] = v.Service
112
-				g.reconcileOneService(ctx, v.Service)
111
+				g.updateService(v.Service)
112
+				g.reconcileServices(ctx, []string{v.Service.ID})
113 113
 			case state.EventUpdateService:
114 114
 				if !isGlobalService(v.Service) {
115 115
 					continue
116 116
 				}
117
-				g.globalServices[v.Service.ID] = v.Service
118
-				g.reconcileOneService(ctx, v.Service)
117
+				g.updateService(v.Service)
118
+				g.reconcileServices(ctx, []string{v.Service.ID})
119 119
 			case state.EventDeleteService:
120 120
 				if !isGlobalService(v.Service) {
121 121
 					continue
... ...
@@ -125,8 +133,10 @@ func (g *GlobalOrchestrator) Run(ctx context.Context) error {
125 125
 				delete(g.globalServices, v.Service.ID)
126 126
 				g.restarts.ClearServiceHistory(v.Service.ID)
127 127
 			case state.EventCreateNode:
128
+				g.updateNode(v.Node)
128 129
 				g.reconcileOneNode(ctx, v.Node)
129 130
 			case state.EventUpdateNode:
131
+				g.updateNode(v.Node)
130 132
 				switch v.Node.Status.State {
131 133
 				// NodeStatus_DISCONNECTED is a transient state, no need to make any change
132 134
 				case api.NodeStatus_DOWN:
... ...
@@ -153,7 +163,7 @@ func (g *GlobalOrchestrator) Run(ctx context.Context) error {
153 153
 				if _, exists := g.globalServices[v.Task.ServiceID]; !exists {
154 154
 					continue
155 155
 				}
156
-				g.reconcileServiceOneNode(ctx, v.Task.ServiceID, v.Task.NodeID)
156
+				g.reconcileServicesOneNode(ctx, []string{v.Task.ServiceID}, v.Task.NodeID)
157 157
 			}
158 158
 		case <-g.stopChan:
159 159
 			return nil
... ...
@@ -196,138 +206,225 @@ func (g *GlobalOrchestrator) removeTasksFromNode(ctx context.Context, node *api.
196 196
 	}
197 197
 }
198 198
 
199
-func (g *GlobalOrchestrator) reconcileOneService(ctx context.Context, service *api.Service) {
200
-	var (
201
-		tasks []*api.Task
202
-		err   error
203
-	)
199
+func (g *GlobalOrchestrator) reconcileServices(ctx context.Context, serviceIDs []string) {
200
+	nodeCompleted := make(map[string]map[string]struct{})
201
+	nodeTasks := make(map[string]map[string][]*api.Task)
202
+
204 203
 	g.store.View(func(tx store.ReadTx) {
205
-		tasks, err = store.FindTasks(tx, store.ByServiceID(service.ID))
206
-	})
207
-	if err != nil {
208
-		log.G(ctx).WithError(err).Errorf("global orchestrator: reconcileOneService failed finding tasks")
209
-		return
210
-	}
211
-	// a node may have completed this service
212
-	nodeCompleted := make(map[string]struct{})
213
-	// nodeID -> task list
214
-	nodeTasks := make(map[string][]*api.Task)
204
+		for _, serviceID := range serviceIDs {
205
+			tasks, err := store.FindTasks(tx, store.ByServiceID(serviceID))
206
+			if err != nil {
207
+				log.G(ctx).WithError(err).Errorf("global orchestrator: reconcileServices failed finding tasks for service %s", serviceID)
208
+				continue
209
+			}
215 210
 
216
-	for _, t := range tasks {
217
-		if isTaskRunning(t) {
218
-			// Collect all running instances of this service
219
-			nodeTasks[t.NodeID] = append(nodeTasks[t.NodeID], t)
220
-		} else {
221
-			// for finished tasks, check restartPolicy
222
-			if isTaskCompleted(t, restartCondition(t)) {
223
-				nodeCompleted[t.NodeID] = struct{}{}
211
+			// a node may have completed this service
212
+			nodeCompleted[serviceID] = make(map[string]struct{})
213
+			// nodeID -> task list
214
+			nodeTasks[serviceID] = make(map[string][]*api.Task)
215
+
216
+			for _, t := range tasks {
217
+				if isTaskRunning(t) {
218
+					// Collect all running instances of this service
219
+					nodeTasks[serviceID][t.NodeID] = append(nodeTasks[serviceID][t.NodeID], t)
220
+				} else {
221
+					// for finished tasks, check restartPolicy
222
+					if isTaskCompleted(t, restartCondition(t)) {
223
+						nodeCompleted[serviceID][t.NodeID] = struct{}{}
224
+					}
225
+				}
224 226
 			}
225 227
 		}
226
-	}
228
+	})
227 229
 
228
-	_, err = g.store.Batch(func(batch *store.Batch) error {
230
+	_, err := g.store.Batch(func(batch *store.Batch) error {
229 231
 		var updateTasks []slot
230
-		for nodeID := range g.nodes {
231
-			ntasks := nodeTasks[nodeID]
232
-			// if restart policy considers this node has finished its task
233
-			// it should remove all running tasks
234
-			if _, exists := nodeCompleted[nodeID]; exists {
235
-				g.removeTasks(ctx, batch, service, ntasks)
236
-				return nil
232
+		for _, serviceID := range serviceIDs {
233
+			if _, exists := nodeTasks[serviceID]; !exists {
234
+				continue
237 235
 			}
238
-			// this node needs to run 1 copy of the task
239
-			if len(ntasks) == 0 {
240
-				g.addTask(ctx, batch, service, nodeID)
241
-			} else {
242
-				updateTasks = append(updateTasks, ntasks)
236
+
237
+			service := g.globalServices[serviceID]
238
+
239
+			for nodeID, node := range g.nodes {
240
+				meetsConstraints := constraint.NodeMatches(service.constraints, node)
241
+				ntasks := nodeTasks[serviceID][nodeID]
242
+				delete(nodeTasks[serviceID], nodeID)
243
+
244
+				// if restart policy considers this node has finished its task
245
+				// it should remove all running tasks
246
+				if _, exists := nodeCompleted[serviceID][nodeID]; exists || !meetsConstraints {
247
+					g.removeTasks(ctx, batch, ntasks)
248
+					continue
249
+				}
250
+
251
+				if node.Spec.Availability == api.NodeAvailabilityPause {
252
+					// the node is paused, so we won't add or update
253
+					// any tasks
254
+					continue
255
+				}
256
+
257
+				// this node needs to run 1 copy of the task
258
+				if len(ntasks) == 0 {
259
+					g.addTask(ctx, batch, service.Service, nodeID)
260
+				} else {
261
+					updateTasks = append(updateTasks, ntasks)
262
+				}
263
+			}
264
+			if len(updateTasks) > 0 {
265
+				g.updater.Update(ctx, g.cluster, service.Service, updateTasks)
266
+			}
267
+
268
+			// Remove any tasks assigned to nodes not found in g.nodes.
269
+			// These must be associated with nodes that are drained, or
270
+			// nodes that no longer exist.
271
+			for _, ntasks := range nodeTasks[serviceID] {
272
+				g.removeTasks(ctx, batch, ntasks)
243 273
 			}
244
-		}
245
-		if len(updateTasks) > 0 {
246
-			g.updater.Update(ctx, g.cluster, service, updateTasks)
247 274
 		}
248 275
 		return nil
249 276
 	})
250 277
 	if err != nil {
251
-		log.G(ctx).WithError(err).Errorf("global orchestrator: reconcileOneService transaction failed")
278
+		log.G(ctx).WithError(err).Errorf("global orchestrator: reconcileServices transaction failed")
279
+	}
280
+}
281
+
282
+// updateNode updates g.nodes based on the current node value
283
+func (g *GlobalOrchestrator) updateNode(node *api.Node) {
284
+	if node.Spec.Availability == api.NodeAvailabilityDrain {
285
+		delete(g.nodes, node.ID)
286
+	} else {
287
+		g.nodes[node.ID] = node
288
+	}
289
+}
290
+
291
+// updateService updates g.globalServices based on the current service value
292
+func (g *GlobalOrchestrator) updateService(service *api.Service) {
293
+	var constraints []constraint.Constraint
294
+
295
+	if service.Spec.Task.Placement != nil && len(service.Spec.Task.Placement.Constraints) != 0 {
296
+		constraints, _ = constraint.Parse(service.Spec.Task.Placement.Constraints)
297
+	}
298
+
299
+	g.globalServices[service.ID] = globalService{
300
+		Service:     service,
301
+		constraints: constraints,
252 302
 	}
253 303
 }
254 304
 
255 305
 // reconcileOneNode checks all global services on one node
256 306
 func (g *GlobalOrchestrator) reconcileOneNode(ctx context.Context, node *api.Node) {
257
-	switch node.Spec.Availability {
258
-	case api.NodeAvailabilityDrain:
307
+	if node.Spec.Availability == api.NodeAvailabilityDrain {
259 308
 		log.G(ctx).Debugf("global orchestrator: node %s in drain state, removing tasks from it", node.ID)
260 309
 		g.removeTasksFromNode(ctx, node)
261
-		delete(g.nodes, node.ID)
262
-		return
263
-	case api.NodeAvailabilityActive:
264
-		if _, exists := g.nodes[node.ID]; !exists {
265
-			log.G(ctx).Debugf("global orchestrator: node %s not in current node list, adding it", node.ID)
266
-			g.nodes[node.ID] = struct{}{}
267
-		}
268
-	default:
269
-		log.G(ctx).Debugf("global orchestrator: node %s in %s state, doing nothing", node.ID, node.Spec.Availability.String())
270 310
 		return
271 311
 	}
272
-	// typically there are only a few global services on a node
273
-	// iterate through all of them one by one. If raft store visits become a concern,
274
-	// it can be optimized.
275
-	for _, service := range g.globalServices {
276
-		g.reconcileServiceOneNode(ctx, service.ID, node.ID)
312
+
313
+	var serviceIDs []string
314
+	for id := range g.globalServices {
315
+		serviceIDs = append(serviceIDs, id)
277 316
 	}
317
+	g.reconcileServicesOneNode(ctx, serviceIDs, node.ID)
278 318
 }
279 319
 
280
-// reconcileServiceOneNode checks one service on one node
281
-func (g *GlobalOrchestrator) reconcileServiceOneNode(ctx context.Context, serviceID string, nodeID string) {
282
-	_, exists := g.nodes[nodeID]
283
-	if !exists {
284
-		return
285
-	}
286
-	service, exists := g.globalServices[serviceID]
320
+// reconcileServicesOneNode checks the specified services on one node
321
+func (g *GlobalOrchestrator) reconcileServicesOneNode(ctx context.Context, serviceIDs []string, nodeID string) {
322
+	node, exists := g.nodes[nodeID]
287 323
 	if !exists {
288 324
 		return
289 325
 	}
290
-	// the node has completed this servie
291
-	completed := false
292
-	// tasks for this node and service
326
+
327
+	// whether each service has completed on the node
328
+	completed := make(map[string]bool)
329
+	// tasks by service
330
+	tasks := make(map[string][]*api.Task)
331
+
293 332
 	var (
294
-		tasks []*api.Task
295
-		err   error
333
+		tasksOnNode []*api.Task
334
+		err         error
296 335
 	)
336
+
297 337
 	g.store.View(func(tx store.ReadTx) {
298
-		var tasksOnNode []*api.Task
299 338
 		tasksOnNode, err = store.FindTasks(tx, store.ByNodeID(nodeID))
300
-		if err != nil {
301
-			return
302
-		}
339
+	})
340
+	if err != nil {
341
+		log.G(ctx).WithError(err).Errorf("global orchestrator: reconcile failed finding tasks on node %s", nodeID)
342
+		return
343
+	}
344
+
345
+	for _, serviceID := range serviceIDs {
303 346
 		for _, t := range tasksOnNode {
304
-			// only interested in one service
305 347
 			if t.ServiceID != serviceID {
306 348
 				continue
307 349
 			}
308 350
 			if isTaskRunning(t) {
309
-				tasks = append(tasks, t)
351
+				tasks[serviceID] = append(tasks[serviceID], t)
310 352
 			} else {
311 353
 				if isTaskCompleted(t, restartCondition(t)) {
312
-					completed = true
354
+					completed[serviceID] = true
313 355
 				}
314 356
 			}
315 357
 		}
316
-	})
317
-	if err != nil {
318
-		log.G(ctx).WithError(err).Errorf("global orchestrator: reconcile failed finding tasks")
319
-		return
320 358
 	}
321 359
 
322 360
 	_, err = g.store.Batch(func(batch *store.Batch) error {
323
-		// if restart policy considers this node has finished its task
324
-		// it should remove all running tasks
325
-		if completed {
326
-			g.removeTasks(ctx, batch, service, tasks)
327
-			return nil
328
-		}
329
-		if len(tasks) == 0 {
330
-			g.addTask(ctx, batch, service, nodeID)
361
+		for _, serviceID := range serviceIDs {
362
+			service, exists := g.globalServices[serviceID]
363
+			if !exists {
364
+				continue
365
+			}
366
+
367
+			meetsConstraints := constraint.NodeMatches(service.constraints, node)
368
+
369
+			// if restart policy considers this node has finished its task
370
+			// it should remove all running tasks
371
+			if completed[serviceID] || !meetsConstraints {
372
+				g.removeTasks(ctx, batch, tasks[serviceID])
373
+				continue
374
+			}
375
+
376
+			if node.Spec.Availability == api.NodeAvailabilityPause {
377
+				// the node is paused, so we won't add or update tasks
378
+				continue
379
+			}
380
+
381
+			if len(tasks) == 0 {
382
+				g.addTask(ctx, batch, service.Service, nodeID)
383
+			} else {
384
+				// If task is out of date, update it. This can happen
385
+				// on node reconciliation if, for example, we pause a
386
+				// node, update the service, and then activate the node
387
+				// later.
388
+
389
+				// We don't use g.updater here for two reasons:
390
+				// - This is not a rolling update. Since it was not
391
+				//   triggered directly by updating the service, it
392
+				//   should not observe the rolling update parameters
393
+				//   or show status in UpdateStatus.
394
+				// - Calling Update cancels any current rolling updates
395
+				//   for the service, such as one triggered by service
396
+				//   reconciliation.
397
+
398
+				var (
399
+					dirtyTasks []*api.Task
400
+					cleanTasks []*api.Task
401
+				)
402
+
403
+				for _, t := range tasks[serviceID] {
404
+					if isTaskDirty(service.Service, t) {
405
+						dirtyTasks = append(dirtyTasks, t)
406
+					} else {
407
+						cleanTasks = append(cleanTasks, t)
408
+					}
409
+				}
410
+
411
+				if len(cleanTasks) == 0 {
412
+					g.addTask(ctx, batch, service.Service, nodeID)
413
+				} else {
414
+					dirtyTasks = append(dirtyTasks, cleanTasks[1:]...)
415
+				}
416
+				g.removeTasks(ctx, batch, dirtyTasks)
417
+			}
331 418
 		}
332 419
 		return nil
333 420
 	})
... ...
@@ -383,7 +480,7 @@ func (g *GlobalOrchestrator) addTask(ctx context.Context, batch *store.Batch, se
383 383
 	}
384 384
 }
385 385
 
386
-func (g *GlobalOrchestrator) removeTasks(ctx context.Context, batch *store.Batch, service *api.Service, tasks []*api.Task) {
386
+func (g *GlobalOrchestrator) removeTasks(ctx context.Context, batch *store.Batch, tasks []*api.Task) {
387 387
 	for _, t := range tasks {
388 388
 		g.removeTask(ctx, batch, t)
389 389
 	}
... ...
@@ -393,11 +490,6 @@ func isTaskRunning(t *api.Task) bool {
393 393
 	return t != nil && t.DesiredState <= api.TaskStateRunning && t.Status.State <= api.TaskStateRunning
394 394
 }
395 395
 
396
-func isValidNode(n *api.Node) bool {
397
-	// current simulation spec could be nil
398
-	return n != nil && n.Spec.Availability != api.NodeAvailabilityDrain
399
-}
400
-
401 396
 func isTaskCompleted(t *api.Task, restartPolicy api.RestartPolicy_RestartCondition) bool {
402 397
 	if t == nil || isTaskRunning(t) {
403 398
 		return false
... ...
@@ -489,9 +489,13 @@ func (u *Updater) removeOldTasks(ctx context.Context, batch *store.Batch, remove
489 489
 	return removedTask, nil
490 490
 }
491 491
 
492
+func isTaskDirty(s *api.Service, t *api.Task) bool {
493
+	return !reflect.DeepEqual(s.Spec.Task, t.Spec) ||
494
+		(t.Endpoint != nil && !reflect.DeepEqual(s.Spec.Endpoint, t.Endpoint.Spec))
495
+}
496
+
492 497
 func (u *Updater) isTaskDirty(t *api.Task) bool {
493
-	return !reflect.DeepEqual(u.newService.Spec.Task, t.Spec) ||
494
-		(t.Endpoint != nil && !reflect.DeepEqual(u.newService.Spec.Endpoint, t.Endpoint.Spec))
498
+	return isTaskDirty(u.newService, t)
495 499
 }
496 500
 
497 501
 func (u *Updater) isSlotDirty(slot slot) bool {
498 502
deleted file mode 100644
... ...
@@ -1,97 +0,0 @@
1
-package scheduler
2
-
3
-import (
4
-	"strings"
5
-
6
-	"github.com/docker/swarmkit/api"
7
-)
8
-
9
-const (
10
-	nodeLabelPrefix   = "node.labels."
11
-	engineLabelPrefix = "engine.labels."
12
-)
13
-
14
-// ConstraintFilter selects only nodes that match certain labels.
15
-type ConstraintFilter struct {
16
-	constraints []Expr
17
-}
18
-
19
-// SetTask returns true when the filter is enable for a given task.
20
-func (f *ConstraintFilter) SetTask(t *api.Task) bool {
21
-	if t.Spec.Placement == nil || len(t.Spec.Placement.Constraints) == 0 {
22
-		return false
23
-	}
24
-
25
-	constraints, err := ParseExprs(t.Spec.Placement.Constraints)
26
-	if err != nil {
27
-		// constraints have been validated at controlapi
28
-		// if in any case it finds an error here, treat this task
29
-		// as constraint filter disabled.
30
-		return false
31
-	}
32
-	f.constraints = constraints
33
-	return true
34
-}
35
-
36
-// Check returns true if the task's constraint is supported by the given node.
37
-func (f *ConstraintFilter) Check(n *NodeInfo) bool {
38
-	for _, constraint := range f.constraints {
39
-		switch {
40
-		case strings.EqualFold(constraint.Key, "node.id"):
41
-			if !constraint.Match(n.ID) {
42
-				return false
43
-			}
44
-		case strings.EqualFold(constraint.Key, "node.hostname"):
45
-			// if this node doesn't have hostname
46
-			// it's equivalent to match an empty hostname
47
-			// where '==' would fail, '!=' matches
48
-			if n.Description == nil {
49
-				if !constraint.Match("") {
50
-					return false
51
-				}
52
-				continue
53
-			}
54
-			if !constraint.Match(n.Description.Hostname) {
55
-				return false
56
-			}
57
-		case strings.EqualFold(constraint.Key, "node.role"):
58
-			if !constraint.Match(n.Spec.Role.String()) {
59
-				return false
60
-			}
61
-
62
-		// node labels constraint in form like 'node.labels.key==value'
63
-		case len(constraint.Key) > len(nodeLabelPrefix) && strings.EqualFold(constraint.Key[:len(nodeLabelPrefix)], nodeLabelPrefix):
64
-			if n.Spec.Annotations.Labels == nil {
65
-				if !constraint.Match("") {
66
-					return false
67
-				}
68
-				continue
69
-			}
70
-			label := constraint.Key[len(nodeLabelPrefix):]
71
-			// label itself is case sensitive
72
-			val := n.Spec.Annotations.Labels[label]
73
-			if !constraint.Match(val) {
74
-				return false
75
-			}
76
-
77
-		// engine labels constraint in form like 'engine.labels.key!=value'
78
-		case len(constraint.Key) > len(engineLabelPrefix) && strings.EqualFold(constraint.Key[:len(engineLabelPrefix)], engineLabelPrefix):
79
-			if n.Description == nil || n.Description.Engine == nil || n.Description.Engine.Labels == nil {
80
-				if !constraint.Match("") {
81
-					return false
82
-				}
83
-				continue
84
-			}
85
-			label := constraint.Key[len(engineLabelPrefix):]
86
-			val := n.Description.Engine.Labels[label]
87
-			if !constraint.Match(val) {
88
-				return false
89
-			}
90
-		default:
91
-			// key doesn't match predefined syntax
92
-			return false
93
-		}
94
-	}
95
-
96
-	return true
97
-}
98 1
deleted file mode 100644
... ...
@@ -1,96 +0,0 @@
1
-package scheduler
2
-
3
-import (
4
-	"fmt"
5
-	"regexp"
6
-	"strings"
7
-)
8
-
9
-const (
10
-	eq = iota
11
-	noteq
12
-)
13
-
14
-var (
15
-	alphaNumeric = regexp.MustCompile(`^(?i)[a-z_][a-z0-9\-_.]+$`)
16
-	// value can be alphanumeric and some special characters. it shouldn't container
17
-	// current or future operators like '>, <, ~', etc.
18
-	valuePattern = regexp.MustCompile(`^(?i)[a-z0-9:\-_\s\.\*\(\)\?\+\[\]\\\^\$\|\/]+$`)
19
-
20
-	// operators defines list of accepted operators
21
-	operators = []string{"==", "!="}
22
-)
23
-
24
-// Expr defines a constraint
25
-type Expr struct {
26
-	Key      string
27
-	operator int
28
-	exp      string
29
-}
30
-
31
-// ParseExprs parses list of constraints into Expr list
32
-func ParseExprs(env []string) ([]Expr, error) {
33
-	exprs := []Expr{}
34
-	for _, e := range env {
35
-		found := false
36
-		// each expr is in the form of "key op value"
37
-		for i, op := range operators {
38
-			if !strings.Contains(e, op) {
39
-				continue
40
-			}
41
-			// split with the op
42
-			parts := strings.SplitN(e, op, 2)
43
-
44
-			if len(parts) < 2 {
45
-				return nil, fmt.Errorf("invalid expr: %s", e)
46
-			}
47
-
48
-			part0 := strings.TrimSpace(parts[0])
49
-			// validate Key
50
-			matched := alphaNumeric.MatchString(part0)
51
-			if matched == false {
52
-				return nil, fmt.Errorf("key '%s' is invalid", part0)
53
-			}
54
-
55
-			part1 := strings.TrimSpace(parts[1])
56
-
57
-			// validate Value
58
-			matched = valuePattern.MatchString(part1)
59
-			if matched == false {
60
-				return nil, fmt.Errorf("value '%s' is invalid", part1)
61
-			}
62
-			// TODO(dongluochen): revisit requirements to see if globing or regex are useful
63
-			exprs = append(exprs, Expr{Key: part0, operator: i, exp: part1})
64
-
65
-			found = true
66
-			break // found an op, move to next entry
67
-		}
68
-		if !found {
69
-			return nil, fmt.Errorf("constraint expected one operator from %s", strings.Join(operators, ", "))
70
-		}
71
-	}
72
-	return exprs, nil
73
-}
74
-
75
-// Match checks if the Expr matches the target strings.
76
-func (e *Expr) Match(whats ...string) bool {
77
-	var match bool
78
-
79
-	// full string match
80
-	for _, what := range whats {
81
-		// case insensitive compare
82
-		if strings.EqualFold(e.exp, what) {
83
-			match = true
84
-			break
85
-		}
86
-	}
87
-
88
-	switch e.operator {
89
-	case eq:
90
-		return match
91
-	case noteq:
92
-		return !match
93
-	}
94
-
95
-	return false
96
-}
... ...
@@ -1,6 +1,9 @@
1 1
 package scheduler
2 2
 
3
-import "github.com/docker/swarmkit/api"
3
+import (
4
+	"github.com/docker/swarmkit/api"
5
+	"github.com/docker/swarmkit/manager/constraint"
6
+)
4 7
 
5 8
 // Filter checks whether the given task can run on the given node.
6 9
 // A filter may only operate
... ...
@@ -129,3 +132,30 @@ func (f *PluginFilter) pluginExistsOnNode(pluginType string, pluginName string,
129 129
 	}
130 130
 	return false
131 131
 }
132
+
133
+// ConstraintFilter selects only nodes that match certain labels.
134
+type ConstraintFilter struct {
135
+	constraints []constraint.Constraint
136
+}
137
+
138
+// SetTask returns true when the filter is enable for a given task.
139
+func (f *ConstraintFilter) SetTask(t *api.Task) bool {
140
+	if t.Spec.Placement == nil || len(t.Spec.Placement.Constraints) == 0 {
141
+		return false
142
+	}
143
+
144
+	constraints, err := constraint.Parse(t.Spec.Placement.Constraints)
145
+	if err != nil {
146
+		// constraints have been validated at controlapi
147
+		// if in any case it finds an error here, treat this task
148
+		// as constraint filter disabled.
149
+		return false
150
+	}
151
+	f.constraints = constraints
152
+	return true
153
+}
154
+
155
+// Check returns true if the task's constraint is supported by the given node.
156
+func (f *ConstraintFilter) Check(n *NodeInfo) bool {
157
+	return constraint.NodeMatches(f.constraints, n.Node)
158
+}
... ...
@@ -27,11 +27,19 @@ var (
27 27
 	ErrCannotUnmarshalConfig = errors.New("membership: cannot unmarshal configuration change")
28 28
 )
29 29
 
30
+// deferredConn used to store removed members connection for some time.
31
+// We need this in case if removed node is redirector or endpoint of ControlAPI call.
32
+type deferredConn struct {
33
+	tick int
34
+	conn *grpc.ClientConn
35
+}
36
+
30 37
 // Cluster represents a set of active
31 38
 // raft Members
32 39
 type Cluster struct {
33
-	mu      sync.RWMutex
34
-	members map[uint64]*Member
40
+	mu           sync.RWMutex
41
+	members      map[uint64]*Member
42
+	deferedConns map[*deferredConn]struct{}
35 43
 
36 44
 	// removed contains the list of removed Members,
37 45
 	// those ids cannot be reused
... ...
@@ -73,16 +81,13 @@ func NewCluster(heartbeatTicks int) *Cluster {
73 73
 	return &Cluster{
74 74
 		members:        make(map[uint64]*Member),
75 75
 		removed:        make(map[uint64]bool),
76
+		deferedConns:   make(map[*deferredConn]struct{}),
76 77
 		heartbeatTicks: heartbeatTicks,
77 78
 		PeersBroadcast: watch.NewQueue(),
78 79
 	}
79 80
 }
80 81
 
81
-// Tick increases ticks for all members. After heartbeatTicks node marked as
82
-// inactive.
83
-func (c *Cluster) Tick() {
84
-	c.mu.Lock()
85
-	defer c.mu.Unlock()
82
+func (c *Cluster) handleInactive() {
86 83
 	for _, m := range c.members {
87 84
 		if !m.active {
88 85
 			continue
... ...
@@ -97,6 +102,25 @@ func (c *Cluster) Tick() {
97 97
 	}
98 98
 }
99 99
 
100
+func (c *Cluster) handleDeferredConns() {
101
+	for dc := range c.deferedConns {
102
+		dc.tick++
103
+		if dc.tick > c.heartbeatTicks {
104
+			dc.conn.Close()
105
+			delete(c.deferedConns, dc)
106
+		}
107
+	}
108
+}
109
+
110
+// Tick increases ticks for all members. After heartbeatTicks node marked as
111
+// inactive.
112
+func (c *Cluster) Tick() {
113
+	c.mu.Lock()
114
+	defer c.mu.Unlock()
115
+	c.handleInactive()
116
+	c.handleDeferredConns()
117
+}
118
+
100 119
 // Members returns the list of raft Members in the Cluster.
101 120
 func (c *Cluster) Members() map[uint64]*Member {
102 121
 	members := make(map[uint64]*Member)
... ...
@@ -177,7 +201,9 @@ func (c *Cluster) clearMember(id uint64) error {
177 177
 	m, ok := c.members[id]
178 178
 	if ok {
179 179
 		if m.Conn != nil {
180
-			m.Conn.Close()
180
+			// defer connection close to after heartbeatTicks
181
+			dConn := &deferredConn{conn: m.Conn}
182
+			c.deferedConns[dConn] = struct{}{}
181 183
 		}
182 184
 		delete(c.members, id)
183 185
 	}
... ...
@@ -232,8 +258,13 @@ func (c *Cluster) Clear() {
232 232
 		}
233 233
 	}
234 234
 
235
+	for dc := range c.deferedConns {
236
+		dc.conn.Close()
237
+	}
238
+
235 239
 	c.members = make(map[uint64]*Member)
236 240
 	c.removed = make(map[uint64]bool)
241
+	c.deferedConns = make(map[*deferredConn]struct{})
237 242
 	c.mu.Unlock()
238 243
 }
239 244
 
... ...
@@ -78,29 +78,24 @@ const (
78 78
 // Node represents the Raft Node useful
79 79
 // configuration.
80 80
 type Node struct {
81
-	raft.Node
82
-	cluster *membership.Cluster
81
+	raftNode raft.Node
82
+	cluster  *membership.Cluster
83 83
 
84
-	Server         *grpc.Server
85
-	Ctx            context.Context
86
-	cancel         func()
87
-	tlsCredentials credentials.TransportCredentials
88
-
89
-	Address  string
90
-	StateDir string
84
+	Server *grpc.Server
85
+	Ctx    context.Context
86
+	cancel func()
91 87
 
92 88
 	raftStore           *raft.MemoryStorage
93 89
 	memoryStore         *store.MemoryStore
94 90
 	Config              *raft.Config
95
-	opts                NewNodeOptions
91
+	opts                NodeOptions
96 92
 	reqIDGen            *idutil.Generator
97 93
 	wait                *wait
98 94
 	wal                 *wal.WAL
99 95
 	snapshotter         *snap.Snapshotter
100
-	restored            bool
96
+	campaignWhenAble    bool
101 97
 	signalledLeadership uint32
102 98
 	isMember            uint32
103
-	joinAddr            string
104 99
 
105 100
 	// waitProp waits for all the proposals to be terminated before
106 101
 	// shutting down the node.
... ...
@@ -110,10 +105,9 @@ type Node struct {
110 110
 	appliedIndex  uint64
111 111
 	snapshotIndex uint64
112 112
 
113
-	ticker      clock.Ticker
114
-	sendTimeout time.Duration
115
-	stopCh      chan struct{}
116
-	doneCh      chan struct{}
113
+	ticker clock.Ticker
114
+	stopCh chan struct{}
115
+	doneCh chan struct{}
117 116
 	// removeRaftCh notifies about node deletion from raft cluster
118 117
 	removeRaftCh        chan struct{}
119 118
 	removeRaftFunc      func()
... ...
@@ -129,8 +123,8 @@ type Node struct {
129 129
 	asyncTasks         sync.WaitGroup
130 130
 }
131 131
 
132
-// NewNodeOptions provides arguments for NewNode
133
-type NewNodeOptions struct {
132
+// NodeOptions provides node-level options.
133
+type NodeOptions struct {
134 134
 	// ID is the node's ID, from its certificate's CN field.
135 135
 	ID string
136 136
 	// Addr is the address of this node's listener
... ...
@@ -161,8 +155,8 @@ func init() {
161 161
 	rand.Seed(time.Now().UnixNano())
162 162
 }
163 163
 
164
-// NewNode generates a new Raft node
165
-func NewNode(ctx context.Context, opts NewNodeOptions) *Node {
164
+// NewNode generates a new Raft node.
165
+func NewNode(ctx context.Context, opts NodeOptions) *Node {
166 166
 	cfg := opts.Config
167 167
 	if cfg == nil {
168 168
 		cfg = DefaultNodeConfig()
... ...
@@ -170,19 +164,20 @@ func NewNode(ctx context.Context, opts NewNodeOptions) *Node {
170 170
 	if opts.TickInterval == 0 {
171 171
 		opts.TickInterval = time.Second
172 172
 	}
173
+	if opts.SendTimeout == 0 {
174
+		opts.SendTimeout = 2 * time.Second
175
+	}
173 176
 
174 177
 	raftStore := raft.NewMemoryStorage()
175 178
 
176 179
 	ctx, cancel := context.WithCancel(ctx)
177 180
 
178 181
 	n := &Node{
179
-		Ctx:            ctx,
180
-		cancel:         cancel,
181
-		cluster:        membership.NewCluster(2 * cfg.ElectionTick),
182
-		tlsCredentials: opts.TLSCredentials,
183
-		raftStore:      raftStore,
184
-		Address:        opts.Addr,
185
-		opts:           opts,
182
+		Ctx:       ctx,
183
+		cancel:    cancel,
184
+		cluster:   membership.NewCluster(2 * cfg.ElectionTick),
185
+		raftStore: raftStore,
186
+		opts:      opts,
186 187
 		Config: &raft.Config{
187 188
 			ElectionTick:    cfg.ElectionTick,
188 189
 			HeartbeatTick:   cfg.HeartbeatTick,
... ...
@@ -194,9 +189,6 @@ func NewNode(ctx context.Context, opts NewNodeOptions) *Node {
194 194
 		stopCh:              make(chan struct{}),
195 195
 		doneCh:              make(chan struct{}),
196 196
 		removeRaftCh:        make(chan struct{}),
197
-		StateDir:            opts.StateDir,
198
-		joinAddr:            opts.JoinAddr,
199
-		sendTimeout:         2 * time.Second,
200 197
 		leadershipBroadcast: watch.NewQueue(),
201 198
 	}
202 199
 	n.memoryStore = store.NewMemoryStore(n)
... ...
@@ -206,9 +198,6 @@ func NewNode(ctx context.Context, opts NewNodeOptions) *Node {
206 206
 	} else {
207 207
 		n.ticker = opts.ClockSource.NewTicker(opts.TickInterval)
208 208
 	}
209
-	if opts.SendTimeout != 0 {
210
-		n.sendTimeout = opts.SendTimeout
211
-	}
212 209
 
213 210
 	n.reqIDGen = idutil.NewGenerator(uint16(n.Config.ID), time.Now())
214 211
 	n.wait = newWait()
... ...
@@ -249,8 +238,8 @@ func (n *Node) JoinAndStart() (err error) {
249 249
 	n.snapshotIndex = snapshot.Metadata.Index
250 250
 
251 251
 	if loadAndStartErr == errNoWAL {
252
-		if n.joinAddr != "" {
253
-			c, err := n.ConnectToMember(n.joinAddr, 10*time.Second)
252
+		if n.opts.JoinAddr != "" {
253
+			c, err := n.ConnectToMember(n.opts.JoinAddr, 10*time.Second)
254 254
 			if err != nil {
255 255
 				return err
256 256
 			}
... ...
@@ -262,7 +251,7 @@ func (n *Node) JoinAndStart() (err error) {
262 262
 			ctx, cancel := context.WithTimeout(n.Ctx, 10*time.Second)
263 263
 			defer cancel()
264 264
 			resp, err := client.Join(ctx, &api.JoinRequest{
265
-				Addr: n.Address,
265
+				Addr: n.opts.Addr,
266 266
 			})
267 267
 			if err != nil {
268 268
 				return err
... ...
@@ -274,7 +263,7 @@ func (n *Node) JoinAndStart() (err error) {
274 274
 				return err
275 275
 			}
276 276
 
277
-			n.Node = raft.StartNode(n.Config, []raft.Peer{})
277
+			n.raftNode = raft.StartNode(n.Config, []raft.Peer{})
278 278
 
279 279
 			if err := n.registerNodes(resp.Members); err != nil {
280 280
 				if walErr := n.wal.Close(); err != nil {
... ...
@@ -289,22 +278,18 @@ func (n *Node) JoinAndStart() (err error) {
289 289
 			if err != nil {
290 290
 				return err
291 291
 			}
292
-			n.Node = raft.StartNode(n.Config, []raft.Peer{peer})
293
-			if err := n.Campaign(n.Ctx); err != nil {
294
-				if walErr := n.wal.Close(); err != nil {
295
-					n.Config.Logger.Errorf("raft: error closing WAL: %v", walErr)
296
-				}
297
-				return err
298
-			}
292
+			n.raftNode = raft.StartNode(n.Config, []raft.Peer{peer})
293
+			n.campaignWhenAble = true
299 294
 		}
300 295
 		atomic.StoreUint32(&n.isMember, 1)
301 296
 		return nil
302 297
 	}
303 298
 
304
-	if n.joinAddr != "" {
299
+	if n.opts.JoinAddr != "" {
305 300
 		n.Config.Logger.Warning("ignoring request to join cluster, because raft state already exists")
306 301
 	}
307
-	n.Node = raft.RestartNode(n.Config)
302
+	n.campaignWhenAble = true
303
+	n.raftNode = raft.RestartNode(n.Config)
308 304
 	atomic.StoreUint32(&n.isMember, 1)
309 305
 	return nil
310 306
 }
... ...
@@ -362,9 +347,9 @@ func (n *Node) Run(ctx context.Context) error {
362 362
 	for {
363 363
 		select {
364 364
 		case <-n.ticker.C():
365
-			n.Tick()
365
+			n.raftNode.Tick()
366 366
 			n.cluster.Tick()
367
-		case rd := <-n.Ready():
367
+		case rd := <-n.raftNode.Ready():
368 368
 			raftConfig := DefaultRaftConfig()
369 369
 			n.memoryStore.View(func(readTx store.ReadTx) {
370 370
 				clusters, err := store.FindClusters(readTx, store.ByName(store.DefaultClusterName))
... ...
@@ -457,19 +442,21 @@ func (n *Node) Run(ctx context.Context) error {
457 457
 			}
458 458
 
459 459
 			// Advance the state machine
460
-			n.Advance()
461
-
462
-			// If we are the only registered member after
463
-			// restoring from the state, campaign to be the
464
-			// leader.
465
-			if !n.restored {
466
-				// Node ID should be in the progress list to Campaign
467
-				if len(n.cluster.Members()) <= 1 {
468
-					if err := n.Campaign(n.Ctx); err != nil {
460
+			n.raftNode.Advance()
461
+
462
+			// On the first startup, or if we are the only
463
+			// registered member after restoring from the state,
464
+			// campaign to be the leader.
465
+			if n.campaignWhenAble {
466
+				members := n.cluster.Members()
467
+				if len(members) >= 1 {
468
+					n.campaignWhenAble = false
469
+				}
470
+				if len(members) == 1 && members[n.Config.ID] != nil {
471
+					if err := n.raftNode.Campaign(n.Ctx); err != nil {
469 472
 						panic("raft: cannot campaign to be the leader on node restore")
470 473
 					}
471 474
 				}
472
-				n.restored = true
473 475
 			}
474 476
 
475 477
 		case snapshotIndex := <-n.snapshotInProgress:
... ...
@@ -517,7 +504,7 @@ func (n *Node) stop() {
517 517
 	n.waitProp.Wait()
518 518
 	n.asyncTasks.Wait()
519 519
 
520
-	n.Stop()
520
+	n.raftNode.Stop()
521 521
 	n.ticker.Stop()
522 522
 	if err := n.wal.Close(); err != nil {
523 523
 		n.Config.Logger.Errorf("raft: error closing WAL: %v", err)
... ...
@@ -532,7 +519,7 @@ func (n *Node) isLeader() bool {
532 532
 		return false
533 533
 	}
534 534
 
535
-	if n.Node.Status().Lead == n.Config.ID {
535
+	if n.Status().Lead == n.Config.ID {
536 536
 		return true
537 537
 	}
538 538
 	return false
... ...
@@ -549,7 +536,7 @@ func (n *Node) IsLeader() bool {
549 549
 // leader returns the id of the leader, without the protection of lock and
550 550
 // membership check, so it's caller task.
551 551
 func (n *Node) leader() uint64 {
552
-	return n.Node.Status().Lead
552
+	return n.Status().Lead
553 553
 }
554 554
 
555 555
 // Leader returns the id of the leader, with the protection of lock
... ...
@@ -859,7 +846,7 @@ func (n *Node) ProcessRaftMessage(ctx context.Context, msg *api.ProcessRaftMessa
859 859
 		return nil, ErrNoRaftMember
860 860
 	}
861 861
 
862
-	if err := n.Step(n.Ctx, *msg.Message); err != nil {
862
+	if err := n.raftNode.Step(n.Ctx, *msg.Message); err != nil {
863 863
 		return nil, err
864 864
 	}
865 865
 
... ...
@@ -988,6 +975,7 @@ func (n *Node) registerNode(node *api.RaftMember) error {
988 988
 		}
989 989
 		return err
990 990
 	}
991
+
991 992
 	return nil
992 993
 }
993 994
 
... ...
@@ -1021,7 +1009,7 @@ func (n *Node) GetVersion() *api.Version {
1021 1021
 		return nil
1022 1022
 	}
1023 1023
 
1024
-	status := n.Node.Status()
1024
+	status := n.Status()
1025 1025
 	return &api.Version{Index: status.Commit}
1026 1026
 }
1027 1027
 
... ...
@@ -1068,6 +1056,11 @@ func (n *Node) GetMemberlist() map[uint64]*api.RaftMember {
1068 1068
 	return memberlist
1069 1069
 }
1070 1070
 
1071
+// Status returns status of underlying etcd.Node.
1072
+func (n *Node) Status() raft.Status {
1073
+	return n.raftNode.Status()
1074
+}
1075
+
1071 1076
 // GetMemberByNodeID returns member information based
1072 1077
 // on its generic Node ID.
1073 1078
 func (n *Node) GetMemberByNodeID(nodeID string) *membership.Member {
... ...
@@ -1131,7 +1124,7 @@ func (n *Node) send(messages []raftpb.Message) error {
1131 1131
 	for _, m := range messages {
1132 1132
 		// Process locally
1133 1133
 		if m.To == n.Config.ID {
1134
-			if err := n.Step(n.Ctx, m); err != nil {
1134
+			if err := n.raftNode.Step(n.Ctx, m); err != nil {
1135 1135
 				return err
1136 1136
 			}
1137 1137
 			continue
... ...
@@ -1160,7 +1153,7 @@ func (n *Node) sendToMember(members map[uint64]*membership.Member, m raftpb.Mess
1160 1160
 		return
1161 1161
 	}
1162 1162
 
1163
-	ctx, cancel := context.WithTimeout(n.Ctx, n.sendTimeout)
1163
+	ctx, cancel := context.WithTimeout(n.Ctx, n.opts.SendTimeout)
1164 1164
 	defer cancel()
1165 1165
 
1166 1166
 	var (
... ...
@@ -1195,7 +1188,7 @@ func (n *Node) sendToMember(members map[uint64]*membership.Member, m raftpb.Mess
1195 1195
 			n.Config.Logger.Errorf("could not resolve address of member ID %x: %v", m.To, err)
1196 1196
 			return
1197 1197
 		}
1198
-		conn, err = n.ConnectToMember(resp.Addr, n.sendTimeout)
1198
+		conn, err = n.ConnectToMember(resp.Addr, n.opts.SendTimeout)
1199 1199
 		if err != nil {
1200 1200
 			n.Config.Logger.Errorf("could connect to member ID %x at %s: %v", m.To, resp.Addr, err)
1201 1201
 			return
... ...
@@ -1212,13 +1205,13 @@ func (n *Node) sendToMember(members map[uint64]*membership.Member, m raftpb.Mess
1212 1212
 			n.removeRaftFunc()
1213 1213
 		}
1214 1214
 		if m.Type == raftpb.MsgSnap {
1215
-			n.ReportSnapshot(m.To, raft.SnapshotFailure)
1215
+			n.raftNode.ReportSnapshot(m.To, raft.SnapshotFailure)
1216 1216
 		}
1217 1217
 		if !n.IsMember() {
1218 1218
 			// node is removed from cluster or stopped
1219 1219
 			return
1220 1220
 		}
1221
-		n.ReportUnreachable(m.To)
1221
+		n.raftNode.ReportUnreachable(m.To)
1222 1222
 
1223 1223
 		lastSeenHost := n.cluster.LastSeenHost(m.To)
1224 1224
 		if lastSeenHost != "" {
... ...
@@ -1246,7 +1239,7 @@ func (n *Node) sendToMember(members map[uint64]*membership.Member, m raftpb.Mess
1246 1246
 			newConn.Conn.Close()
1247 1247
 		}
1248 1248
 	} else if m.Type == raftpb.MsgSnap {
1249
-		n.ReportSnapshot(m.To, raft.SnapshotFinish)
1249
+		n.raftNode.ReportSnapshot(m.To, raft.SnapshotFinish)
1250 1250
 	}
1251 1251
 }
1252 1252
 
... ...
@@ -1323,7 +1316,7 @@ func (n *Node) processInternalRaftRequest(ctx context.Context, r *api.InternalRa
1323 1323
 		return nil, ErrRequestTooLarge
1324 1324
 	}
1325 1325
 
1326
-	err = n.Propose(waitCtx, data)
1326
+	err = n.raftNode.Propose(waitCtx, data)
1327 1327
 	if err != nil {
1328 1328
 		n.wait.cancel(r.ID)
1329 1329
 		return nil, err
... ...
@@ -1351,7 +1344,7 @@ func (n *Node) configure(ctx context.Context, cc raftpb.ConfChange) error {
1351 1351
 	ctx, cancel := context.WithCancel(ctx)
1352 1352
 	ch := n.wait.register(cc.ID, nil, cancel)
1353 1353
 
1354
-	if err := n.ProposeConfChange(ctx, cc); err != nil {
1354
+	if err := n.raftNode.ProposeConfChange(ctx, cc); err != nil {
1355 1355
 		n.wait.cancel(cc.ID)
1356 1356
 		return err
1357 1357
 	}
... ...
@@ -1449,7 +1442,7 @@ func (n *Node) processConfChange(entry raftpb.Entry) {
1449 1449
 		n.wait.trigger(cc.ID, err)
1450 1450
 	}
1451 1451
 
1452
-	n.confState = *n.ApplyConfChange(cc)
1452
+	n.confState = *n.raftNode.ApplyConfChange(cc)
1453 1453
 	n.wait.trigger(cc.ID, nil)
1454 1454
 }
1455 1455
 
... ...
@@ -1520,7 +1513,7 @@ func (n *Node) applyRemoveNode(cc raftpb.ConfChange) (err error) {
1520 1520
 	// to be the leader.
1521 1521
 
1522 1522
 	if cc.NodeID == n.leader() && !n.isLeader() {
1523
-		if err = n.Campaign(n.Ctx); err != nil {
1523
+		if err = n.raftNode.Campaign(n.Ctx); err != nil {
1524 1524
 			return err
1525 1525
 		}
1526 1526
 	}
... ...
@@ -1548,7 +1541,7 @@ func (n *Node) applyRemoveNode(cc raftpb.ConfChange) (err error) {
1548 1548
 // ConnectToMember returns a member object with an initialized
1549 1549
 // connection to communicate with other raft members
1550 1550
 func (n *Node) ConnectToMember(addr string, timeout time.Duration) (*membership.Member, error) {
1551
-	conn, err := dial(addr, "tcp", n.tlsCredentials, timeout)
1551
+	conn, err := dial(addr, "tcp", n.opts.TLSCredentials, timeout)
1552 1552
 	if err != nil {
1553 1553
 		return nil, err
1554 1554
 	}
... ...
@@ -26,19 +26,19 @@ import (
26 26
 var errNoWAL = errors.New("no WAL present")
27 27
 
28 28
 func (n *Node) legacyWALDir() string {
29
-	return filepath.Join(n.StateDir, "wal")
29
+	return filepath.Join(n.opts.StateDir, "wal")
30 30
 }
31 31
 
32 32
 func (n *Node) walDir() string {
33
-	return filepath.Join(n.StateDir, "wal-v3")
33
+	return filepath.Join(n.opts.StateDir, "wal-v3")
34 34
 }
35 35
 
36 36
 func (n *Node) legacySnapDir() string {
37
-	return filepath.Join(n.StateDir, "snap")
37
+	return filepath.Join(n.opts.StateDir, "snap")
38 38
 }
39 39
 
40 40
 func (n *Node) snapDir() string {
41
-	return filepath.Join(n.StateDir, "snap-v3")
41
+	return filepath.Join(n.opts.StateDir, "snap-v3")
42 42
 }
43 43
 
44 44
 func (n *Node) loadAndStart(ctx context.Context, forceNewCluster bool) error {
... ...
@@ -189,7 +189,7 @@ func (n *Node) createWAL(nodeID string) (raft.Peer, error) {
189 189
 	raftNode := &api.RaftMember{
190 190
 		RaftID: n.Config.ID,
191 191
 		NodeID: nodeID,
192
-		Addr:   n.Address,
192
+		Addr:   n.opts.Addr,
193 193
 	}
194 194
 	metadata, err := raftNode.Marshal()
195 195
 	if err != nil {
... ...
@@ -207,7 +207,7 @@ func (n *Node) createWAL(nodeID string) (raft.Peer, error) {
207 207
 // moveWALAndSnap moves away the WAL and snapshot because we were removed
208 208
 // from the cluster and will need to recreate them if we are readded.
209 209
 func (n *Node) moveWALAndSnap() error {
210
-	newWALDir, err := ioutil.TempDir(n.StateDir, "wal.")
210
+	newWALDir, err := ioutil.TempDir(n.opts.StateDir, "wal.")
211 211
 	if err != nil {
212 212
 		return err
213 213
 	}
... ...
@@ -216,7 +216,7 @@ func (n *Node) moveWALAndSnap() error {
216 216
 		return err
217 217
 	}
218 218
 
219
-	newSnapDir, err := ioutil.TempDir(n.StateDir, "snap.")
219
+	newSnapDir, err := ioutil.TempDir(n.opts.StateDir, "snap.")
220 220
 	if err != nil {
221 221
 		return err
222 222
 	}