github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/dispatcher/dispatcher_test.go (about) 1 package dispatcher 2 3 import ( 4 "context" 5 "crypto/tls" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io/ioutil" 10 "net" 11 "net/http" 12 "net/http/httptest" 13 "sync" 14 "testing" 15 "time" 16 17 "google.golang.org/grpc" 18 "google.golang.org/grpc/codes" 19 "google.golang.org/grpc/credentials" 20 21 "github.com/docker/docker/pkg/plugingetter" 22 "github.com/docker/docker/pkg/plugins" 23 "github.com/docker/go-events" 24 "github.com/docker/swarmkit/api" 25 "github.com/docker/swarmkit/ca" 26 cautils "github.com/docker/swarmkit/ca/testutils" 27 "github.com/docker/swarmkit/identity" 28 "github.com/docker/swarmkit/manager/drivers" 29 "github.com/docker/swarmkit/manager/state/store" 30 "github.com/docker/swarmkit/testutils" 31 digest "github.com/opencontainers/go-digest" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 ) 35 36 type grpcDispatcher struct { 37 Clients []api.DispatcherClient 38 SecurityConfigs []*ca.SecurityConfig 39 Store *store.MemoryStore 40 grpcServer *grpc.Server 41 dispatcherServer *Dispatcher 42 conns []*grpc.ClientConn 43 testCA *cautils.TestCA 44 testCluster *testCluster 45 PluginGetter *mockPluginGetter 46 } 47 48 func (gd *grpcDispatcher) Close() { 49 // Close the client connection. 50 for _, conn := range gd.conns { 51 conn.Close() 52 } 53 gd.dispatcherServer.Stop() 54 gd.grpcServer.Stop() 55 gd.PluginGetter.Close() 56 gd.testCA.Stop() 57 } 58 59 type testCluster struct { 60 mu sync.Mutex 61 addr string 62 store *store.MemoryStore 63 subscriptions map[string]chan events.Event 64 peers []*api.Peer 65 members map[uint64]*api.RaftMember 66 } 67 68 func newTestCluster(addr string, s *store.MemoryStore) *testCluster { 69 return &testCluster{ 70 addr: addr, 71 store: s, 72 subscriptions: make(map[string]chan events.Event), 73 peers: []*api.Peer{ 74 { 75 Addr: addr, 76 NodeID: "1", 77 }, 78 }, 79 members: map[uint64]*api.RaftMember{ 80 1: { 81 NodeID: "1", 82 Addr: addr, 83 }, 84 }, 85 } 86 } 87 88 func (t *testCluster) GetMemberlist() map[uint64]*api.RaftMember { 89 t.mu.Lock() 90 defer t.mu.Unlock() 91 return t.members 92 } 93 94 func (t *testCluster) SubscribePeers() (chan events.Event, func()) { 95 t.mu.Lock() 96 defer t.mu.Unlock() 97 ch := make(chan events.Event, 1) 98 id := identity.NewID() 99 t.subscriptions[id] = ch 100 ch <- t.peers 101 return ch, func() { 102 t.mu.Lock() 103 defer t.mu.Unlock() 104 delete(t.subscriptions, id) 105 close(ch) 106 } 107 } 108 109 func (t *testCluster) addMember(addr string) { 110 t.mu.Lock() 111 defer t.mu.Unlock() 112 id := uint64(len(t.members) + 1) 113 strID := fmt.Sprintf("%d", id) 114 t.members[id] = &api.RaftMember{ 115 NodeID: strID, 116 Addr: addr, 117 } 118 t.peers = append(t.peers, &api.Peer{ 119 Addr: addr, 120 NodeID: strID, 121 }) 122 for _, ch := range t.subscriptions { 123 ch <- t.peers 124 } 125 } 126 127 func (t *testCluster) MemoryStore() *store.MemoryStore { 128 return t.store 129 } 130 131 func startDispatcher(t *testing.T, c *Config) *grpcDispatcher { 132 t.Helper() 133 134 l, err := net.Listen("tcp", "127.0.0.1:0") 135 assert.NoError(t, err) 136 137 tca := cautils.NewTestCA(t) 138 tca.CAServer.Stop() // there is no need for the CA server to be running 139 agentSecurityConfig1, err := tca.NewNodeConfig(ca.WorkerRole) 140 assert.NoError(t, err) 141 142 agentSecurityConfig2, err := tca.NewNodeConfig(ca.WorkerRole) 143 assert.NoError(t, err) 144 145 managerSecurityConfig, err := tca.NewNodeConfig(ca.ManagerRole) 146 assert.NoError(t, err) 147 148 serverOpts := []grpc.ServerOption{grpc.Creds(managerSecurityConfig.ServerTLSCreds)} 149 150 s := grpc.NewServer(serverOpts...) 151 tc := newTestCluster(l.Addr().String(), tca.MemoryStore) 152 driverGetter := &mockPluginGetter{} 153 d := New() 154 d.Init(tc, c, drivers.New(driverGetter), managerSecurityConfig) 155 authorize := func(ctx context.Context, roles []string) error { 156 _, err := ca.AuthorizeForwardedRoleAndOrg(ctx, roles, []string{ca.ManagerRole}, tca.Organization, nil) 157 return err 158 } 159 authenticatedDispatcherAPI := api.NewAuthenticatedWrapperDispatcherServer(d, authorize) 160 161 api.RegisterDispatcherServer(s, authenticatedDispatcherAPI) 162 go func() { 163 // Serve will always return an error (even when properly stopped). 164 // Explicitly ignore it. 165 _ = s.Serve(l) 166 }() 167 go d.Run(context.Background()) 168 err = testutils.PollFuncWithTimeout(nil, func() error { 169 d.mu.Lock() 170 defer d.mu.Unlock() 171 if !d.isRunning() { 172 return fmt.Errorf("dispatcher is not running") 173 } 174 return nil 175 }, 5*time.Second) 176 assert.NoError(t, err) 177 178 clientOpts := []grpc.DialOption{grpc.WithTimeout(10 * time.Second)} 179 clientOpts1 := append(clientOpts, grpc.WithTransportCredentials(agentSecurityConfig1.ClientTLSCreds)) 180 clientOpts2 := append(clientOpts, grpc.WithTransportCredentials(agentSecurityConfig2.ClientTLSCreds)) 181 clientOpts3 := append(clientOpts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}))) 182 183 conn1, err := grpc.Dial(l.Addr().String(), clientOpts1...) 184 assert.NoError(t, err) 185 186 conn2, err := grpc.Dial(l.Addr().String(), clientOpts2...) 187 assert.NoError(t, err) 188 189 conn3, err := grpc.Dial(l.Addr().String(), clientOpts3...) 190 assert.NoError(t, err) 191 192 clients := []api.DispatcherClient{api.NewDispatcherClient(conn1), api.NewDispatcherClient(conn2), api.NewDispatcherClient(conn3)} 193 securityConfigs := []*ca.SecurityConfig{agentSecurityConfig1, agentSecurityConfig2, managerSecurityConfig} 194 conns := []*grpc.ClientConn{conn1, conn2, conn3} 195 return &grpcDispatcher{ 196 Clients: clients, 197 SecurityConfigs: securityConfigs, 198 Store: tc.MemoryStore(), 199 dispatcherServer: d, 200 conns: conns, 201 grpcServer: s, 202 testCA: tca, 203 testCluster: tc, 204 PluginGetter: driverGetter, 205 } 206 } 207 208 func TestRegisterTwice(t *testing.T) { 209 cfg := DefaultConfig() 210 cfg.RateLimitPeriod = 0 211 gd := startDispatcher(t, cfg) 212 defer gd.Close() 213 214 var expectedSessionID string 215 { 216 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 217 assert.NoError(t, err) 218 msg, err := stream.Recv() 219 assert.NoError(t, err) 220 assert.NotEmpty(t, msg.SessionID) 221 expectedSessionID = msg.SessionID 222 stream.CloseSend() 223 } 224 { 225 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 226 assert.NoError(t, err) 227 msg, err := stream.Recv() 228 229 assert.NoError(t, err) 230 // session should be different! 231 assert.NotEqual(t, msg.SessionID, expectedSessionID) 232 stream.CloseSend() 233 } 234 } 235 236 func TestRegisterExceedRateLimit(t *testing.T) { 237 t.Parallel() 238 239 gd := startDispatcher(t, DefaultConfig()) 240 defer gd.Close() 241 242 for i := 0; i < 3; i++ { 243 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 244 assert.NoError(t, err) 245 msg, err := stream.Recv() 246 assert.NoError(t, err) 247 assert.NotEmpty(t, msg.SessionID) 248 stream.CloseSend() 249 } 250 { 251 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 252 defer stream.CloseSend() 253 assert.NoError(t, err) 254 _, err = stream.Recv() 255 assert.Error(t, err) 256 assert.Equal(t, codes.Unavailable, testutils.ErrorCode(err), err.Error()) 257 } 258 } 259 260 func TestRegisterNoCert(t *testing.T) { 261 gd := startDispatcher(t, DefaultConfig()) 262 defer gd.Close() 263 264 // This client has no certificates, this should fail 265 stream, err := gd.Clients[2].Session(context.Background(), &api.SessionRequest{}) 266 assert.NoError(t, err) 267 defer stream.CloseSend() 268 resp, err := stream.Recv() 269 assert.Nil(t, resp) 270 assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = Permission denied: unauthorized peer role: rpc error: code = PermissionDenied desc = no client certificates in request") 271 } 272 273 func TestHeartbeat(t *testing.T) { 274 cfg := DefaultConfig() 275 cfg.HeartbeatPeriod = 500 * time.Millisecond 276 cfg.HeartbeatEpsilon = 0 277 gd := startDispatcher(t, DefaultConfig()) 278 defer gd.Close() 279 280 var expectedSessionID string 281 { 282 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 283 assert.NoError(t, err) 284 defer stream.CloseSend() 285 286 resp, err := stream.Recv() 287 assert.NoError(t, err) 288 assert.NotEmpty(t, resp.SessionID) 289 expectedSessionID = resp.SessionID 290 } 291 time.Sleep(250 * time.Millisecond) 292 293 { 294 // heartbeat without correct SessionID should fail 295 resp, err := gd.Clients[0].Heartbeat(context.Background(), &api.HeartbeatRequest{}) 296 assert.Nil(t, resp) 297 assert.Error(t, err) 298 assert.Equal(t, testutils.ErrorCode(err), codes.InvalidArgument) 299 } 300 301 resp, err := gd.Clients[0].Heartbeat(context.Background(), &api.HeartbeatRequest{SessionID: expectedSessionID}) 302 assert.NoError(t, err) 303 assert.NotZero(t, resp.Period) 304 time.Sleep(300 * time.Millisecond) 305 306 gd.Store.View(func(readTx store.ReadTx) { 307 storeNodes, err := store.FindNodes(readTx, store.All) 308 assert.NoError(t, err) 309 assert.NotEmpty(t, storeNodes) 310 found := false 311 for _, node := range storeNodes { 312 if node.ID == gd.SecurityConfigs[0].ClientTLSCreds.NodeID() { 313 found = true 314 assert.Equal(t, api.NodeStatus_READY, node.Status.State) 315 } 316 } 317 assert.True(t, found) 318 }) 319 } 320 321 func TestHeartbeatNoCert(t *testing.T) { 322 gd := startDispatcher(t, DefaultConfig()) 323 defer gd.Close() 324 325 // heartbeat without correct SessionID should fail 326 resp, err := gd.Clients[2].Heartbeat(context.Background(), &api.HeartbeatRequest{}) 327 assert.Nil(t, resp) 328 assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = Permission denied: unauthorized peer role: rpc error: code = PermissionDenied desc = no client certificates in request") 329 } 330 331 func TestHeartbeatTimeout(t *testing.T) { 332 t.Parallel() 333 334 cfg := DefaultConfig() 335 cfg.HeartbeatPeriod = 100 * time.Millisecond 336 cfg.HeartbeatEpsilon = 0 337 gd := startDispatcher(t, cfg) 338 defer gd.Close() 339 340 var expectedSessionID string 341 { 342 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 343 assert.NoError(t, err) 344 resp, err := stream.Recv() 345 assert.NoError(t, err) 346 assert.NotEmpty(t, resp.SessionID) 347 expectedSessionID = resp.SessionID 348 349 } 350 351 assert.NoError(t, testutils.PollFunc(nil, func() error { 352 var storeNode *api.Node 353 gd.Store.View(func(readTx store.ReadTx) { 354 storeNode = store.GetNode(readTx, gd.SecurityConfigs[0].ClientTLSCreds.NodeID()) 355 }) 356 if storeNode == nil { 357 return errors.New("node not found") 358 } 359 if storeNode.Status.State != api.NodeStatus_DOWN { 360 return errors.New("node is not down") 361 } 362 return nil 363 })) 364 365 // check that node is deregistered 366 resp, err := gd.Clients[0].Heartbeat(context.Background(), &api.HeartbeatRequest{SessionID: expectedSessionID}) 367 assert.Nil(t, resp) 368 assert.Error(t, err) 369 assert.Equal(t, testutils.ErrorDesc(err), ErrNodeNotRegistered.Error()) 370 } 371 372 func TestHeartbeatUnregistered(t *testing.T) { 373 gd := startDispatcher(t, DefaultConfig()) 374 defer gd.Close() 375 resp, err := gd.Clients[0].Heartbeat(context.Background(), &api.HeartbeatRequest{}) 376 assert.Nil(t, resp) 377 assert.Error(t, err) 378 assert.Equal(t, ErrSessionInvalid.Error(), testutils.ErrorDesc(err)) 379 } 380 381 // If the session ID is not sent as part of the Assignments request, an error is returned to the stream 382 func TestAssignmentsErrorsIfNoSessionID(t *testing.T) { 383 t.Parallel() 384 385 gd := startDispatcher(t, DefaultConfig()) 386 defer gd.Close() 387 388 // without correct SessionID should fail 389 stream, err := gd.Clients[0].Assignments(context.Background(), &api.AssignmentsRequest{}) 390 assert.NoError(t, err) 391 assert.NotNil(t, stream) 392 defer stream.CloseSend() 393 394 resp, err := stream.Recv() 395 assert.Nil(t, resp) 396 assert.Error(t, err) 397 assert.Equal(t, testutils.ErrorCode(err), codes.InvalidArgument) 398 } 399 400 func TestAssignmentsSecretDriver(t *testing.T) { 401 t.Parallel() 402 403 const ( 404 secretDriver = "secret-driver" 405 existingSecretName = "existing-secret" 406 doNotReuseExistingSecretName = "do-not-reuse-existing-secret" 407 errSecretName = "err-secret" 408 serviceName = "service-name" 409 serviceHostname = "service-hostname" 410 serviceEndpointMode = 2 411 ) 412 secretValue := []byte("custom-secret-value") 413 doNotReuseSecretValue := []byte("custom-do-not-reuse-secret-value") 414 serviceLabels := map[string]string{ 415 "label-name": "label-value", 416 } 417 418 portConfig := drivers.PortConfig{Name: "port", PublishMode: 5, TargetPort: 80, Protocol: 10, PublishedPort: 8080} 419 420 responses := map[string]*drivers.SecretsProviderResponse{ 421 existingSecretName: {Value: secretValue}, 422 doNotReuseExistingSecretName: {Value: doNotReuseSecretValue, DoNotReuse: true}, 423 errSecretName: {Err: "Error from driver"}, 424 } 425 426 mux := http.NewServeMux() 427 mux.HandleFunc(drivers.SecretsProviderAPI, func(w http.ResponseWriter, r *http.Request) { 428 defer r.Body.Close() 429 body, err := ioutil.ReadAll(r.Body) 430 var request drivers.SecretsProviderRequest 431 assert.NoError(t, err) 432 assert.NoError(t, json.Unmarshal(body, &request)) 433 response := responses[request.SecretName] 434 assert.Equal(t, serviceName, request.ServiceName) 435 assert.Equal(t, serviceHostname, request.ServiceHostname) 436 assert.Equal(t, int32(serviceEndpointMode), request.ServiceEndpointSpec.Mode) 437 assert.Len(t, request.ServiceEndpointSpec.Ports, 1) 438 assert.EqualValues(t, portConfig, request.ServiceEndpointSpec.Ports[0]) 439 assert.EqualValues(t, serviceLabels, request.ServiceLabels) 440 assert.NotNil(t, response) 441 resp, err := json.Marshal(response) 442 assert.NoError(t, err) 443 w.Write(resp) 444 }) 445 446 gd := startDispatcher(t, DefaultConfig()) 447 defer gd.Close() 448 assert.NoError(t, gd.PluginGetter.SetupPlugin(secretDriver, mux)) 449 450 expectedSessionID, nodeID := getSessionAndNodeID(t, gd.Clients[0]) 451 452 secret := &api.Secret{ 453 ID: "driverSecret", 454 Spec: api.SecretSpec{ 455 Annotations: api.Annotations{Name: existingSecretName}, 456 Driver: &api.Driver{Name: secretDriver}, 457 }, 458 } 459 doNotReuseSecret := &api.Secret{ 460 ID: "driverDoNotReuseSecret", 461 Spec: api.SecretSpec{ 462 Annotations: api.Annotations{Name: doNotReuseExistingSecretName}, 463 Driver: &api.Driver{Name: secretDriver}, 464 }, 465 } 466 errSecret := &api.Secret{ 467 ID: "driverErrSecret", 468 Spec: api.SecretSpec{ 469 Annotations: api.Annotations{Name: errSecretName}, 470 Driver: &api.Driver{Name: secretDriver}, 471 }, 472 } 473 config := &api.Config{ 474 ID: "config", 475 Spec: api.ConfigSpec{ 476 Data: []byte("config"), 477 }, 478 } 479 spec := taskSpecFromDependencies(secret, doNotReuseSecret, errSecret, config) 480 spec.GetContainer().Hostname = serviceHostname 481 task := &api.Task{ 482 NodeID: nodeID, 483 ID: "secretTask", 484 Status: api.TaskStatus{State: api.TaskStateReady}, 485 DesiredState: api.TaskStateNew, 486 Spec: spec, 487 Endpoint: &api.Endpoint{ 488 Spec: &api.EndpointSpec{ 489 Mode: serviceEndpointMode, 490 Ports: []*api.PortConfig{ 491 { 492 Name: portConfig.Name, 493 PublishedPort: portConfig.PublishedPort, 494 Protocol: api.PortConfig_Protocol(portConfig.Protocol), 495 TargetPort: portConfig.TargetPort, 496 PublishMode: api.PortConfig_PublishMode(portConfig.PublishMode), 497 }, 498 }, 499 }, 500 }, 501 ServiceAnnotations: api.Annotations{ 502 Name: serviceName, 503 Labels: serviceLabels, 504 }, 505 } 506 507 err := gd.Store.Update(func(tx store.Tx) error { 508 assert.NoError(t, store.CreateSecret(tx, secret)) 509 assert.NoError(t, store.CreateSecret(tx, doNotReuseSecret)) 510 assert.NoError(t, store.CreateSecret(tx, errSecret)) 511 assert.NoError(t, store.CreateConfig(tx, config)) 512 assert.NoError(t, store.CreateTask(tx, task)) 513 return nil 514 }) 515 assert.NoError(t, err) 516 517 stream, err := gd.Clients[0].Assignments(context.Background(), &api.AssignmentsRequest{SessionID: expectedSessionID}) 518 assert.NoError(t, err) 519 defer stream.CloseSend() 520 521 resp, err := stream.Recv() 522 assert.NoError(t, err) 523 524 _, _, secretChanges := splitChanges(resp.Changes) 525 assert.Len(t, secretChanges, 2) 526 for _, s := range secretChanges { 527 if s.ID == "driverSecret" { 528 assert.Equal(t, secretValue, s.Spec.Data) 529 } else if s.ID == "driverDoNotReuseSecret" { 530 assert.Fail(t, "Secret with DoNotReuse==true should not retain its original ID in the assignment", "%s != %s", "driverDoNotReuseSecret", s.ID) 531 } else { 532 taskSpecificID := fmt.Sprintf("%s.%s", "driverDoNotReuseSecret", task.ID) 533 assert.Equal(t, taskSpecificID, s.ID) 534 assert.Equal(t, doNotReuseSecretValue, s.Spec.Data) 535 } 536 } 537 } 538 539 // When connecting to a dispatcher to get Assignments, if there are tasks already in the store, 540 // Assignments will send down any existing node tasks > ASSIGNED, and any secrets 541 // for said tasks that are <= RUNNING (if the secrets exist) 542 func TestAssignmentsInitialNodeTasks(t *testing.T) { 543 t.Parallel() 544 testFuncs := []taskGeneratorFunc{ 545 makeTasksAndDependenciesWithResourceReferences, 546 makeTasksAndDependenciesNoResourceReferences, 547 makeTasksAndDependenciesOnlyResourceReferences, 548 makeTasksAndDependenciesWithRedundantReferences, 549 } 550 for _, testFunc := range testFuncs { 551 testAssignmentsInitialNodeTasksWithGivenTasks(t, testFunc) 552 } 553 } 554 555 func testAssignmentsInitialNodeTasksWithGivenTasks(t *testing.T, genTasks taskGeneratorFunc) { 556 gd := startDispatcher(t, DefaultConfig()) 557 defer gd.Close() 558 559 expectedSessionID, nodeID := getSessionAndNodeID(t, gd.Clients[0]) 560 561 // create the relevant secrets and tasks 562 secrets, configs, resourceRefs, tasks := genTasks(t, nodeID) 563 err := gd.Store.Update(func(tx store.Tx) error { 564 for _, secret := range secrets { 565 assert.NoError(t, store.CreateSecret(tx, secret)) 566 } 567 for _, config := range configs { 568 assert.NoError(t, store.CreateConfig(tx, config)) 569 } 570 // make dummy secrets and configs for resourceRefs 571 for _, resourceRef := range resourceRefs { 572 assert.NoError(t, makeMockResource(tx, resourceRef)) 573 } 574 575 for _, task := range tasks { 576 assert.NoError(t, store.CreateTask(tx, task)) 577 } 578 return nil 579 }) 580 assert.NoError(t, err) 581 582 stream, err := gd.Clients[0].Assignments(context.Background(), &api.AssignmentsRequest{SessionID: expectedSessionID}) 583 assert.NoError(t, err) 584 defer stream.CloseSend() 585 586 time.Sleep(100 * time.Millisecond) 587 588 // check the initial task and secret stream 589 resp, err := stream.Recv() 590 assert.NoError(t, err) 591 592 assignedToRunningTasks := filterTasks(tasks, func(s api.TaskState) bool { 593 return s >= api.TaskStateAssigned && s <= api.TaskStateRunning 594 }) 595 pastRunningTasks := filterTasks(tasks, func(s api.TaskState) bool { 596 return s > api.TaskStateRunning 597 }) 598 atLeastAssignedTasks := filterTasks(tasks, func(s api.TaskState) bool { 599 return s >= api.TaskStateAssigned 600 }) 601 602 // dispatcher sends dependencies for all tasks >= ASSIGNED and <= RUNNING 603 referencedSecrets, referencedConfigs := getResourcesFromReferences(gd, resourceRefs) 604 secrets = append(secrets, referencedSecrets...) 605 configs = append(configs, referencedConfigs...) 606 updatedSecrets, updatedConfigs := filterDependencies(secrets, configs, assignedToRunningTasks, nil) 607 verifyChanges(t, resp.Changes, []changeExpectations{ 608 { 609 action: api.AssignmentChange_AssignmentActionUpdate, 610 tasks: atLeastAssignedTasks, // dispatcher sends task updates for all tasks >= ASSIGNED 611 secrets: updatedSecrets, 612 configs: updatedConfigs, 613 }, 614 }) 615 616 // updating all the tasks will attempt to remove all the secrets for the tasks that are in state > running 617 err = gd.Store.Update(func(tx store.Tx) error { 618 for _, task := range tasks { 619 assert.NoError(t, store.UpdateTask(tx, task)) 620 } 621 return nil 622 623 }) 624 assert.NoError(t, err) 625 626 resp, err = stream.Recv() 627 assert.NoError(t, err) 628 629 // dependencies for tasks > RUNNING are removed, but only if they are not currently being used 630 // by a task >= ASSIGNED and <= RUNNING 631 updatedSecrets, updatedConfigs = filterDependencies(secrets, configs, pastRunningTasks, assignedToRunningTasks) 632 verifyChanges(t, resp.Changes, []changeExpectations{ 633 { 634 // ASSIGNED tasks are always sent down even if they haven't changed 635 action: api.AssignmentChange_AssignmentActionUpdate, 636 tasks: filterTasks(tasks, func(s api.TaskState) bool { return s == api.TaskStateAssigned }), 637 }, 638 { 639 action: api.AssignmentChange_AssignmentActionRemove, 640 secrets: updatedSecrets, 641 configs: updatedConfigs, 642 }, 643 }) 644 645 // deleting the tasks removes all the secrets for every single task, no matter 646 // what state it's in 647 err = gd.Store.Update(func(tx store.Tx) error { 648 for _, task := range tasks { 649 assert.NoError(t, store.DeleteTask(tx, task.ID)) 650 } 651 return nil 652 }) 653 assert.NoError(t, err) 654 655 resp, err = stream.Recv() 656 assert.NoError(t, err) 657 658 // tasks >= ASSIGNED and their dependencies have all been removed; 659 // task < ASSIGNED and their dependencies were never sent in the first place, so don't need to be removed 660 updatedSecrets, updatedConfigs = filterDependencies(secrets, configs, atLeastAssignedTasks, nil) 661 verifyChanges(t, resp.Changes, []changeExpectations{ 662 { 663 action: api.AssignmentChange_AssignmentActionRemove, 664 tasks: atLeastAssignedTasks, 665 secrets: updatedSecrets, 666 configs: updatedConfigs, 667 }, 668 }) 669 } 670 671 func mockNumberedConfig(i int) *api.Config { 672 return &api.Config{ 673 ID: fmt.Sprintf("IDconfig%d", i), 674 Spec: api.ConfigSpec{ 675 Annotations: api.Annotations{ 676 Name: fmt.Sprintf("config%d", i), 677 }, 678 Data: []byte(fmt.Sprintf("config%d", i)), 679 }, 680 } 681 } 682 683 func mockNumberedSecret(i int) *api.Secret { 684 return &api.Secret{ 685 ID: fmt.Sprintf("IDsecret%d", i), 686 Spec: api.SecretSpec{ 687 Annotations: api.Annotations{ 688 Name: fmt.Sprintf("secret%d", i), 689 }, 690 Data: []byte(fmt.Sprintf("secret%d", i)), 691 }, 692 } 693 } 694 695 func mockNumberedReadyTask(i int, nodeID string, taskState api.TaskState, spec api.TaskSpec) *api.Task { 696 return &api.Task{ 697 NodeID: nodeID, 698 ID: fmt.Sprintf("testTask%d", i), 699 Status: api.TaskStatus{State: taskState}, 700 DesiredState: api.TaskStateReady, 701 Spec: spec, 702 } 703 } 704 705 func makeMockResource(tx store.Tx, resourceRef *api.ResourceReference) error { 706 switch resourceRef.ResourceType { 707 case api.ResourceType_SECRET: 708 dummySecret := &api.Secret{ 709 ID: resourceRef.ResourceID, 710 Spec: api.SecretSpec{ 711 Annotations: api.Annotations{ 712 Name: fmt.Sprintf("dummy_secret_%s", resourceRef.ResourceID), 713 }, 714 Data: []byte(fmt.Sprintf("secret_%s", resourceRef.ResourceID)), 715 }, 716 } 717 if store.GetSecret(tx, dummySecret.ID) == nil { 718 return store.CreateSecret(tx, dummySecret) 719 } 720 // the resource already exists 721 return nil 722 case api.ResourceType_CONFIG: 723 dummyConfig := &api.Config{ 724 ID: resourceRef.ResourceID, 725 Spec: api.ConfigSpec{ 726 Annotations: api.Annotations{ 727 Name: fmt.Sprintf("dummy_config_%s", resourceRef.ResourceID), 728 }, 729 Data: []byte(fmt.Sprintf("config_%s", resourceRef.ResourceID)), 730 }, 731 } 732 if store.GetConfig(tx, dummyConfig.ID) == nil { 733 return store.CreateConfig(tx, dummyConfig) 734 } 735 // the resource already exists 736 return nil 737 default: 738 return fmt.Errorf("unsupported mock resource type") 739 } 740 } 741 742 // When connecting to a dispatcher with no tasks or assignments, when tasks are updated, assignments will send down 743 // tasks > ASSIGNED, and any secrets for said tasks that are <= RUNNING (but only if the secrets/configs exist - if 744 // they don't, even if they are referenced, the task is still sent down) 745 func TestAssignmentsAddingTasks(t *testing.T) { 746 t.Parallel() 747 testFuncs := []taskGeneratorFunc{ 748 makeTasksAndDependenciesWithResourceReferences, 749 makeTasksAndDependenciesNoResourceReferences, 750 makeTasksAndDependenciesOnlyResourceReferences, 751 makeTasksAndDependenciesWithRedundantReferences, 752 } 753 for _, testFunc := range testFuncs { 754 testAssignmentsAddingTasksWithGivenTasks(t, testFunc) 755 } 756 } 757 758 func testAssignmentsAddingTasksWithGivenTasks(t *testing.T, genTasks taskGeneratorFunc) { 759 gd := startDispatcher(t, DefaultConfig()) 760 defer gd.Close() 761 762 expectedSessionID, nodeID := getSessionAndNodeID(t, gd.Clients[0]) 763 764 stream, err := gd.Clients[0].Assignments(context.Background(), &api.AssignmentsRequest{SessionID: expectedSessionID}) 765 assert.NoError(t, err) 766 defer stream.CloseSend() 767 768 time.Sleep(100 * time.Millisecond) 769 770 // There are no initial tasks or secrets 771 resp, err := stream.Recv() 772 assert.NoError(t, err) 773 assert.Empty(t, resp.Changes) 774 775 // create the relevant secrets, configs, and tasks and update the tasks 776 secrets, configs, resourceRefs, tasks := genTasks(t, nodeID) 777 var createdSecrets []*api.Secret 778 var createdConfigs []*api.Config 779 if len(secrets) > 0 { 780 createdSecrets = secrets[:len(secrets)-1] 781 } 782 if len(configs) > 0 { 783 createdConfigs = configs[:len(configs)-1] 784 } 785 err = gd.Store.Update(func(tx store.Tx) error { 786 for _, secret := range createdSecrets { 787 if store.GetSecret(tx, secret.ID) == nil { 788 assert.NoError(t, store.CreateSecret(tx, secret)) 789 } 790 } 791 for _, config := range createdConfigs { 792 if store.GetConfig(tx, config.ID) == nil { 793 assert.NoError(t, store.CreateConfig(tx, config)) 794 } 795 } 796 // make dummy secrets and configs for resourceRefs 797 for _, resourceRef := range resourceRefs { 798 assert.NoError(t, makeMockResource(tx, resourceRef)) 799 } 800 801 for _, task := range tasks { 802 assert.NoError(t, store.CreateTask(tx, task)) 803 } 804 return nil 805 }) 806 assert.NoError(t, err) 807 808 // Nothing happens until we update. Updating all the tasks will send updates for all the tasks >= ASSIGNED, 809 // and secrets for all the tasks >= ASSIGNED and <= RUNNING. 810 err = gd.Store.Update(func(tx store.Tx) error { 811 for _, task := range tasks { 812 assert.NoError(t, store.UpdateTask(tx, task)) 813 } 814 return nil 815 816 }) 817 assert.NoError(t, err) 818 819 resp, err = stream.Recv() 820 assert.NoError(t, err) 821 822 assignedToRunningTasks := filterTasks(tasks, func(s api.TaskState) bool { 823 return s >= api.TaskStateAssigned && s <= api.TaskStateRunning 824 }) 825 atLeastAssignedTasks := filterTasks(tasks, func(s api.TaskState) bool { 826 return s >= api.TaskStateAssigned 827 }) 828 829 // dispatcher sends dependencies for all tasks >= ASSIGNED and <= RUNNING, but only if they exist in 830 // the store - if a dependency is referenced by a task but does not exist, that's fine, it just won't be 831 // included in the changes 832 referencedSecrets, referencedConfigs := getResourcesFromReferences(gd, resourceRefs) 833 createdSecrets = append(createdSecrets, referencedSecrets...) 834 createdConfigs = append(createdConfigs, referencedConfigs...) 835 updatedSecrets, updatedConfigs := filterDependencies(createdSecrets, createdConfigs, assignedToRunningTasks, nil) 836 verifyChanges(t, resp.Changes, []changeExpectations{ 837 { 838 action: api.AssignmentChange_AssignmentActionUpdate, 839 tasks: atLeastAssignedTasks, // dispatcher sends task updates for all tasks >= ASSIGNED 840 secrets: updatedSecrets, 841 configs: updatedConfigs, 842 }, 843 }) 844 845 // deleting the tasks removes all the secrets for every single task, no matter 846 // what state it's in 847 err = gd.Store.Update(func(tx store.Tx) error { 848 for _, task := range tasks { 849 assert.NoError(t, store.DeleteTask(tx, task.ID)) 850 } 851 return nil 852 853 }) 854 assert.NoError(t, err) 855 856 resp, err = stream.Recv() 857 assert.NoError(t, err) 858 859 // tasks >= ASSIGNED and their dependencies have all been removed, even if they don't exist in the store; 860 // task < ASSIGNED and their dependencies were never sent in the first place, so don't need to be removed 861 secrets = append(secrets, referencedSecrets...) 862 configs = append(configs, referencedConfigs...) 863 updatedSecrets, updatedConfigs = filterDependencies(secrets, configs, atLeastAssignedTasks, nil) 864 verifyChanges(t, resp.Changes, []changeExpectations{ 865 { 866 action: api.AssignmentChange_AssignmentActionRemove, 867 tasks: atLeastAssignedTasks, 868 secrets: updatedSecrets, 869 configs: updatedConfigs, 870 }, 871 }) 872 } 873 874 // If a secret or config is updated or deleted, even if it's for an existing task, no changes will be sent down 875 func TestAssignmentsDependencyUpdateAndDeletion(t *testing.T) { 876 t.Parallel() 877 testFuncs := []taskGeneratorFunc{ 878 makeTasksAndDependenciesWithResourceReferences, 879 makeTasksAndDependenciesNoResourceReferences, 880 makeTasksAndDependenciesOnlyResourceReferences, 881 makeTasksAndDependenciesWithRedundantReferences, 882 } 883 for _, testFunc := range testFuncs { 884 testAssignmentsDependencyUpdateAndDeletionWithGivenTasks(t, testFunc) 885 } 886 } 887 888 func testAssignmentsDependencyUpdateAndDeletionWithGivenTasks(t *testing.T, genTasks taskGeneratorFunc) { 889 gd := startDispatcher(t, DefaultConfig()) 890 defer gd.Close() 891 892 expectedSessionID, nodeID := getSessionAndNodeID(t, gd.Clients[0]) 893 894 // create the relevant secrets and tasks 895 secrets, configs, resourceRefs, tasks := genTasks(t, nodeID) 896 err := gd.Store.Update(func(tx store.Tx) error { 897 for _, secret := range secrets { 898 if store.GetSecret(tx, secret.ID) == nil { 899 assert.NoError(t, store.CreateSecret(tx, secret)) 900 } 901 } 902 for _, config := range configs { 903 if store.GetConfig(tx, config.ID) == nil { 904 assert.NoError(t, store.CreateConfig(tx, config)) 905 } 906 } 907 // make dummy secrets and configs for resourceRefs 908 for _, resourceRef := range resourceRefs { 909 assert.NoError(t, makeMockResource(tx, resourceRef)) 910 } 911 912 for _, task := range tasks { 913 assert.NoError(t, store.CreateTask(tx, task)) 914 } 915 return nil 916 }) 917 assert.NoError(t, err) 918 919 stream, err := gd.Clients[0].Assignments(context.Background(), &api.AssignmentsRequest{SessionID: expectedSessionID}) 920 assert.NoError(t, err) 921 defer stream.CloseSend() 922 923 time.Sleep(100 * time.Millisecond) 924 925 // check the initial task and secret stream 926 resp, err := stream.Recv() 927 assert.NoError(t, err) 928 929 assignedToRunningTasks := filterTasks(tasks, func(s api.TaskState) bool { 930 return s >= api.TaskStateAssigned && s <= api.TaskStateRunning 931 }) 932 atLeastAssignedTasks := filterTasks(tasks, func(s api.TaskState) bool { 933 return s >= api.TaskStateAssigned 934 }) 935 936 // dispatcher sends dependencies for all tasks >= ASSIGNED and <= RUNNING 937 referencedSecrets, referencedConfigs := getResourcesFromReferences(gd, resourceRefs) 938 secrets = append(secrets, referencedSecrets...) 939 configs = append(configs, referencedConfigs...) 940 updatedSecrets, updatedConfigs := filterDependencies(secrets, configs, assignedToRunningTasks, nil) 941 verifyChanges(t, resp.Changes, []changeExpectations{ 942 { 943 action: api.AssignmentChange_AssignmentActionUpdate, 944 tasks: atLeastAssignedTasks, // dispatcher sends task updates for all tasks >= ASSIGNED 945 secrets: updatedSecrets, 946 configs: updatedConfigs, 947 }, 948 }) 949 950 // updating secrets and configs, used by tasks or not, do not cause any changes 951 uniqueSecrets := uniquifySecrets(secrets) 952 uniqueConfigs := uniquifyConfigs(configs) 953 assert.NoError(t, gd.Store.Update(func(tx store.Tx) error { 954 for _, s := range uniqueSecrets { 955 s.Spec.Data = []byte("new secret data") 956 if err := store.UpdateSecret(tx, s); err != nil { 957 return err 958 } 959 } 960 for _, c := range uniqueConfigs { 961 c.Spec.Data = []byte("new config data") 962 if err := store.UpdateConfig(tx, c); err != nil { 963 return err 964 } 965 } 966 return nil 967 })) 968 969 recvChan := make(chan struct{}) 970 go func() { 971 _, _ = stream.Recv() 972 recvChan <- struct{}{} 973 }() 974 975 select { 976 case <-recvChan: 977 assert.Fail(t, "secret update should not trigger dispatcher update") 978 case <-time.After(250 * time.Millisecond): 979 } 980 981 // deleting secrets and configs, used by tasks or not, do not cause any changes 982 err = gd.Store.Update(func(tx store.Tx) error { 983 for _, secret := range uniqueSecrets { 984 assert.NoError(t, store.DeleteSecret(tx, secret.ID)) 985 } 986 for _, config := range uniqueConfigs { 987 assert.NoError(t, store.DeleteConfig(tx, config.ID)) 988 } 989 return nil 990 }) 991 assert.NoError(t, err) 992 993 select { 994 case <-recvChan: 995 assert.Fail(t, "secret delete should not trigger dispatcher update") 996 case <-time.After(250 * time.Millisecond): 997 } 998 } 999 1000 func TestTasksStatusChange(t *testing.T) { 1001 t.Parallel() 1002 1003 gd := startDispatcher(t, DefaultConfig()) 1004 defer gd.Close() 1005 1006 var expectedSessionID string 1007 var nodeID string 1008 { 1009 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 1010 assert.NoError(t, err) 1011 defer stream.CloseSend() 1012 resp, err := stream.Recv() 1013 assert.NoError(t, err) 1014 assert.NotEmpty(t, resp.SessionID) 1015 expectedSessionID = resp.SessionID 1016 nodeID = resp.Node.ID 1017 } 1018 1019 testTask1 := &api.Task{ 1020 NodeID: nodeID, 1021 ID: "testTask1", 1022 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1023 DesiredState: api.TaskStateReady, 1024 } 1025 testTask2 := &api.Task{ 1026 NodeID: nodeID, 1027 ID: "testTask2", 1028 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1029 DesiredState: api.TaskStateReady, 1030 } 1031 1032 stream, err := gd.Clients[0].Assignments(context.Background(), &api.AssignmentsRequest{SessionID: expectedSessionID}) 1033 assert.NoError(t, err) 1034 1035 time.Sleep(100 * time.Millisecond) 1036 1037 resp, err := stream.Recv() 1038 assert.NoError(t, err) 1039 // initially no tasks 1040 assert.Equal(t, 0, len(resp.Changes)) 1041 1042 // Creating the tasks will not create an event for assignments 1043 err = gd.Store.Update(func(tx store.Tx) error { 1044 assert.NoError(t, store.CreateTask(tx, testTask1)) 1045 assert.NoError(t, store.CreateTask(tx, testTask2)) 1046 return nil 1047 }) 1048 assert.NoError(t, err) 1049 err = gd.Store.Update(func(tx store.Tx) error { 1050 assert.NoError(t, store.UpdateTask(tx, testTask1)) 1051 assert.NoError(t, store.UpdateTask(tx, testTask2)) 1052 return nil 1053 }) 1054 assert.NoError(t, err) 1055 1056 resp, err = stream.Recv() 1057 assert.NoError(t, err) 1058 1059 verifyChanges(t, resp.Changes, []changeExpectations{ 1060 { 1061 action: api.AssignmentChange_AssignmentActionUpdate, 1062 tasks: []*api.Task{testTask1, testTask2}, 1063 }, 1064 }) 1065 1066 assert.NoError(t, gd.Store.Update(func(tx store.Tx) error { 1067 task := store.GetTask(tx, testTask1.ID) 1068 if task == nil { 1069 return errors.New("no task") 1070 } 1071 task.NodeID = nodeID 1072 // only Status is changed for task1 1073 task.Status = api.TaskStatus{State: api.TaskStateFailed, Err: "1234"} 1074 task.DesiredState = api.TaskStateReady 1075 return store.UpdateTask(tx, task) 1076 })) 1077 1078 // dispatcher shouldn't send snapshot for this update 1079 recvChan := make(chan struct{}) 1080 go func() { 1081 _, _ = stream.Recv() 1082 recvChan <- struct{}{} 1083 }() 1084 1085 select { 1086 case <-recvChan: 1087 assert.Fail(t, "task.Status update should not trigger dispatcher update") 1088 case <-time.After(250 * time.Millisecond): 1089 } 1090 } 1091 1092 func TestTasksBatch(t *testing.T) { 1093 gd := startDispatcher(t, DefaultConfig()) 1094 defer gd.Close() 1095 1096 var expectedSessionID string 1097 var nodeID string 1098 { 1099 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 1100 assert.NoError(t, err) 1101 defer stream.CloseSend() 1102 resp, err := stream.Recv() 1103 assert.NoError(t, err) 1104 assert.NotEmpty(t, resp.SessionID) 1105 expectedSessionID = resp.SessionID 1106 nodeID = resp.Node.ID 1107 } 1108 1109 testTask1 := &api.Task{ 1110 NodeID: nodeID, 1111 ID: "testTask1", 1112 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1113 } 1114 testTask2 := &api.Task{ 1115 NodeID: nodeID, 1116 ID: "testTask2", 1117 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1118 } 1119 1120 stream, err := gd.Clients[0].Assignments(context.Background(), &api.AssignmentsRequest{SessionID: expectedSessionID}) 1121 assert.NoError(t, err) 1122 1123 resp, err := stream.Recv() 1124 assert.NoError(t, err) 1125 // initially no tasks 1126 assert.Equal(t, 0, len(resp.Changes)) 1127 1128 // Create, Update and Delete tasks. 1129 err = gd.Store.Update(func(tx store.Tx) error { 1130 assert.NoError(t, store.CreateTask(tx, testTask1)) 1131 assert.NoError(t, store.CreateTask(tx, testTask2)) 1132 return nil 1133 }) 1134 assert.NoError(t, err) 1135 err = gd.Store.Update(func(tx store.Tx) error { 1136 assert.NoError(t, store.UpdateTask(tx, testTask1)) 1137 assert.NoError(t, store.UpdateTask(tx, testTask2)) 1138 return nil 1139 }) 1140 assert.NoError(t, err) 1141 1142 err = gd.Store.Update(func(tx store.Tx) error { 1143 assert.NoError(t, store.DeleteTask(tx, testTask1.ID)) 1144 assert.NoError(t, store.DeleteTask(tx, testTask2.ID)) 1145 return nil 1146 }) 1147 assert.NoError(t, err) 1148 1149 resp, err = stream.Recv() 1150 assert.NoError(t, err) 1151 1152 // all tasks have been deleted 1153 verifyChanges(t, resp.Changes, []changeExpectations{ 1154 { 1155 action: api.AssignmentChange_AssignmentActionRemove, 1156 tasks: []*api.Task{testTask1, testTask2}, 1157 }, 1158 }) 1159 } 1160 1161 func TestTasksNoCert(t *testing.T) { 1162 gd := startDispatcher(t, DefaultConfig()) 1163 defer gd.Close() 1164 1165 stream, err := gd.Clients[2].Assignments(context.Background(), &api.AssignmentsRequest{}) 1166 assert.NoError(t, err) 1167 assert.NotNil(t, stream) 1168 resp, err := stream.Recv() 1169 assert.Nil(t, resp) 1170 assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = Permission denied: unauthorized peer role: rpc error: code = PermissionDenied desc = no client certificates in request") 1171 } 1172 1173 func TestTaskUpdate(t *testing.T) { 1174 gd := startDispatcher(t, DefaultConfig()) 1175 defer gd.Close() 1176 1177 var ( 1178 expectedSessionID string 1179 nodeID string 1180 ) 1181 { 1182 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 1183 assert.NoError(t, err) 1184 defer stream.CloseSend() 1185 resp, err := stream.Recv() 1186 assert.NoError(t, err) 1187 assert.NotEmpty(t, resp.SessionID) 1188 expectedSessionID = resp.SessionID 1189 nodeID = resp.Node.ID 1190 1191 } 1192 // testTask1 and testTask2 are advanced from NEW to ASSIGNED. 1193 testTask1 := &api.Task{ 1194 ID: "testTask1", 1195 NodeID: nodeID, 1196 } 1197 testTask2 := &api.Task{ 1198 ID: "testTask2", 1199 NodeID: nodeID, 1200 } 1201 // testTask3 is used to confirm that status updates for a task not 1202 // assigned to the node sending the update are rejected. 1203 testTask3 := &api.Task{ 1204 ID: "testTask3", 1205 NodeID: "differentnode", 1206 } 1207 // testTask4 is used to confirm that a task's state is not allowed to 1208 // move backwards. 1209 testTask4 := &api.Task{ 1210 ID: "testTask4", 1211 NodeID: nodeID, 1212 Status: api.TaskStatus{ 1213 State: api.TaskStateShutdown, 1214 }, 1215 } 1216 err := gd.Store.Update(func(tx store.Tx) error { 1217 assert.NoError(t, store.CreateTask(tx, testTask1)) 1218 assert.NoError(t, store.CreateTask(tx, testTask2)) 1219 assert.NoError(t, store.CreateTask(tx, testTask3)) 1220 assert.NoError(t, store.CreateTask(tx, testTask4)) 1221 return nil 1222 }) 1223 assert.NoError(t, err) 1224 1225 testTask1.Status = api.TaskStatus{State: api.TaskStateAssigned} 1226 testTask2.Status = api.TaskStatus{State: api.TaskStateAssigned} 1227 testTask3.Status = api.TaskStatus{State: api.TaskStateAssigned} 1228 testTask4.Status = api.TaskStatus{State: api.TaskStateRunning} 1229 updReq := &api.UpdateTaskStatusRequest{ 1230 Updates: []*api.UpdateTaskStatusRequest_TaskStatusUpdate{ 1231 { 1232 TaskID: testTask1.ID, 1233 Status: &testTask1.Status, 1234 }, 1235 { 1236 TaskID: testTask2.ID, 1237 Status: &testTask2.Status, 1238 }, 1239 { 1240 TaskID: testTask4.ID, 1241 Status: &testTask4.Status, 1242 }, 1243 }, 1244 } 1245 1246 { 1247 // without correct SessionID should fail 1248 resp, err := gd.Clients[0].UpdateTaskStatus(context.Background(), updReq) 1249 assert.Nil(t, resp) 1250 assert.Error(t, err) 1251 assert.Equal(t, testutils.ErrorCode(err), codes.InvalidArgument) 1252 } 1253 1254 updReq.SessionID = expectedSessionID 1255 _, err = gd.Clients[0].UpdateTaskStatus(context.Background(), updReq) 1256 assert.NoError(t, err) 1257 1258 { 1259 // updating a task not assigned to us should fail 1260 updReq.Updates = []*api.UpdateTaskStatusRequest_TaskStatusUpdate{ 1261 { 1262 TaskID: testTask3.ID, 1263 Status: &testTask3.Status, 1264 }, 1265 } 1266 1267 resp, err := gd.Clients[0].UpdateTaskStatus(context.Background(), updReq) 1268 assert.Nil(t, resp) 1269 assert.Error(t, err) 1270 assert.Equal(t, testutils.ErrorCode(err), codes.PermissionDenied) 1271 } 1272 1273 gd.dispatcherServer.processUpdates(context.Background()) 1274 1275 gd.Store.View(func(readTx store.ReadTx) { 1276 storeTask1 := store.GetTask(readTx, testTask1.ID) 1277 assert.NotNil(t, storeTask1) 1278 storeTask2 := store.GetTask(readTx, testTask2.ID) 1279 assert.NotNil(t, storeTask2) 1280 assert.Equal(t, storeTask1.Status.State, api.TaskStateAssigned) 1281 assert.Equal(t, storeTask2.Status.State, api.TaskStateAssigned) 1282 1283 storeTask3 := store.GetTask(readTx, testTask3.ID) 1284 assert.NotNil(t, storeTask3) 1285 assert.Equal(t, storeTask3.Status.State, api.TaskStateNew) 1286 1287 // The update to task4's state should be ignored because it 1288 // would have moved backwards. 1289 storeTask4 := store.GetTask(readTx, testTask4.ID) 1290 assert.NotNil(t, storeTask4) 1291 assert.Equal(t, storeTask4.Status.State, api.TaskStateShutdown) 1292 }) 1293 1294 } 1295 1296 func TestTaskUpdateNoCert(t *testing.T) { 1297 gd := startDispatcher(t, DefaultConfig()) 1298 defer gd.Close() 1299 1300 testTask1 := &api.Task{ 1301 ID: "testTask1", 1302 } 1303 err := gd.Store.Update(func(tx store.Tx) error { 1304 assert.NoError(t, store.CreateTask(tx, testTask1)) 1305 return nil 1306 }) 1307 assert.NoError(t, err) 1308 1309 testTask1.Status = api.TaskStatus{State: api.TaskStateAssigned} 1310 updReq := &api.UpdateTaskStatusRequest{ 1311 Updates: []*api.UpdateTaskStatusRequest_TaskStatusUpdate{ 1312 { 1313 TaskID: testTask1.ID, 1314 Status: &testTask1.Status, 1315 }, 1316 }, 1317 } 1318 // without correct SessionID should fail 1319 resp, err := gd.Clients[2].UpdateTaskStatus(context.Background(), updReq) 1320 assert.Nil(t, resp) 1321 assert.Error(t, err) 1322 assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = Permission denied: unauthorized peer role: rpc error: code = PermissionDenied desc = no client certificates in request") 1323 } 1324 1325 func TestSession(t *testing.T) { 1326 gd := startDispatcher(t, DefaultConfig()) 1327 defer gd.Close() 1328 1329 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 1330 assert.NoError(t, err) 1331 stream.CloseSend() 1332 resp, err := stream.Recv() 1333 assert.NoError(t, err) 1334 assert.NotEmpty(t, resp.SessionID) 1335 assert.Equal(t, 1, len(resp.Managers)) 1336 } 1337 1338 func TestSessionNoCert(t *testing.T) { 1339 gd := startDispatcher(t, DefaultConfig()) 1340 defer gd.Close() 1341 1342 stream, err := gd.Clients[2].Session(context.Background(), &api.SessionRequest{}) 1343 assert.NoError(t, err) 1344 msg, err := stream.Recv() 1345 assert.Nil(t, msg) 1346 assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = Permission denied: unauthorized peer role: rpc error: code = PermissionDenied desc = no client certificates in request") 1347 } 1348 1349 func getSessionAndNodeID(t *testing.T, c api.DispatcherClient) (string, string) { 1350 stream, err := c.Session(context.Background(), &api.SessionRequest{}) 1351 assert.NoError(t, err) 1352 defer stream.CloseSend() 1353 resp, err := stream.Recv() 1354 assert.NoError(t, err) 1355 assert.NotEmpty(t, resp.SessionID) 1356 return resp.SessionID, resp.Node.ID 1357 } 1358 1359 type idAndAction struct { 1360 id string 1361 action api.AssignmentChange_AssignmentAction 1362 } 1363 1364 func splitChanges(changes []*api.AssignmentChange) (map[idAndAction]*api.Task, map[idAndAction]*api.Config, map[idAndAction]*api.Secret) { 1365 tasks := make(map[idAndAction]*api.Task) 1366 secrets := make(map[idAndAction]*api.Secret) 1367 configs := make(map[idAndAction]*api.Config) 1368 for _, change := range changes { 1369 task := change.Assignment.GetTask() 1370 if task != nil { 1371 tasks[idAndAction{id: task.ID, action: change.Action}] = task 1372 } 1373 secret := change.Assignment.GetSecret() 1374 if secret != nil { 1375 secrets[idAndAction{id: secret.ID, action: change.Action}] = secret 1376 } 1377 config := change.Assignment.GetConfig() 1378 if config != nil { 1379 configs[idAndAction{id: config.ID, action: change.Action}] = config 1380 } 1381 } 1382 1383 return tasks, configs, secrets 1384 } 1385 1386 type changeExpectations struct { 1387 tasks []*api.Task 1388 secrets []*api.Secret 1389 configs []*api.Config 1390 action api.AssignmentChange_AssignmentAction 1391 } 1392 1393 // Ensures that the changes contain the following actions for the following tasks/secrets/configs 1394 func verifyChanges(t *testing.T, changes []*api.AssignmentChange, expectations []changeExpectations) { 1395 taskChanges, configChanges, secretChanges := splitChanges(changes) 1396 1397 var expectedTasks, expectedSecrets, expectedConfigs int 1398 for _, c := range expectations { 1399 for _, task := range c.tasks { 1400 expectedTasks++ 1401 index := idAndAction{id: task.ID, action: c.action} 1402 require.NotNil(t, taskChanges[index], "missing task change %v", index) 1403 } 1404 1405 for _, secret := range c.secrets { 1406 expectedSecrets++ 1407 index := idAndAction{id: secret.ID, action: c.action} 1408 require.NotNil(t, secretChanges[index], "missing secret change %v", index) 1409 } 1410 1411 for _, config := range c.configs { 1412 expectedConfigs++ 1413 index := idAndAction{id: config.ID, action: c.action} 1414 require.NotNil(t, configChanges[index], "missing config change %v", index) 1415 } 1416 } 1417 1418 require.Len(t, taskChanges, expectedTasks) 1419 require.Len(t, secretChanges, expectedSecrets) 1420 require.Len(t, configChanges, expectedConfigs) 1421 require.Len(t, changes, expectedTasks+expectedSecrets+expectedConfigs) 1422 } 1423 1424 // filter all tasks by task state, which is given by a function because it's hard to take a range of constants 1425 func filterTasks(tasks []*api.Task, include func(api.TaskState) bool) []*api.Task { 1426 var result []*api.Task 1427 for _, t := range tasks { 1428 if include(t.Status.State) { 1429 result = append(result, t) 1430 } 1431 } 1432 return result 1433 } 1434 1435 func getResourcesFromReferences(gd *grpcDispatcher, resourceRefs []*api.ResourceReference) ([]*api.Secret, []*api.Config) { 1436 var ( 1437 referencedSecrets []*api.Secret 1438 referencedConfigs []*api.Config 1439 ) 1440 for _, ref := range resourceRefs { 1441 switch ref.ResourceType { 1442 case api.ResourceType_SECRET: 1443 gd.Store.View(func(readTx store.ReadTx) { 1444 referencedSecrets = append(referencedSecrets, store.GetSecret(readTx, ref.ResourceID)) 1445 }) 1446 case api.ResourceType_CONFIG: 1447 gd.Store.View(func(readTx store.ReadTx) { 1448 referencedConfigs = append(referencedConfigs, store.GetConfig(readTx, ref.ResourceID)) 1449 }) 1450 } 1451 } 1452 return referencedSecrets, referencedConfigs 1453 } 1454 1455 // filters all dependencies (secrets, configs); dependencies should be in `inTasks`, but not be in `notInTasks`` 1456 func filterDependencies(secrets []*api.Secret, configs []*api.Config, inTasks, notInTasks []*api.Task) ([]*api.Secret, []*api.Config) { 1457 var ( 1458 wantSecrets, wantConfigs = make(map[string]struct{}), make(map[string]struct{}) 1459 filteredSecrets []*api.Secret 1460 filteredConfigs []*api.Config 1461 ) 1462 for _, t := range inTasks { 1463 for _, s := range t.Spec.GetContainer().Secrets { 1464 wantSecrets[s.SecretID] = struct{}{} 1465 } 1466 for _, s := range t.Spec.GetContainer().Configs { 1467 wantConfigs[s.ConfigID] = struct{}{} 1468 } 1469 for _, ref := range t.Spec.ResourceReferences { 1470 switch ref.ResourceType { 1471 case api.ResourceType_SECRET: 1472 wantSecrets[ref.ResourceID] = struct{}{} 1473 case api.ResourceType_CONFIG: 1474 wantConfigs[ref.ResourceID] = struct{}{} 1475 } 1476 } 1477 } 1478 for _, t := range notInTasks { 1479 for _, s := range t.Spec.GetContainer().Secrets { 1480 delete(wantSecrets, s.SecretID) 1481 } 1482 for _, s := range t.Spec.GetContainer().Configs { 1483 delete(wantConfigs, s.ConfigID) 1484 } 1485 for _, ref := range t.Spec.ResourceReferences { 1486 switch ref.ResourceType { 1487 case api.ResourceType_SECRET: 1488 delete(wantSecrets, ref.ResourceID) 1489 case api.ResourceType_CONFIG: 1490 delete(wantConfigs, ref.ResourceID) 1491 } 1492 } 1493 } 1494 for _, s := range secrets { 1495 if _, ok := wantSecrets[s.ID]; ok { 1496 filteredSecrets = append(filteredSecrets, s) 1497 } 1498 } 1499 for _, c := range configs { 1500 if _, ok := wantConfigs[c.ID]; ok { 1501 filteredConfigs = append(filteredConfigs, c) 1502 } 1503 } 1504 return uniquifySecrets(filteredSecrets), uniquifyConfigs(filteredConfigs) 1505 } 1506 1507 func uniquifySecrets(secrets []*api.Secret) []*api.Secret { 1508 uniqueSecrets := make(map[string]struct{}) 1509 var finalSecrets []*api.Secret 1510 for _, secret := range secrets { 1511 if _, ok := uniqueSecrets[secret.ID]; !ok { 1512 uniqueSecrets[secret.ID] = struct{}{} 1513 finalSecrets = append(finalSecrets, secret) 1514 } 1515 } 1516 return finalSecrets 1517 } 1518 1519 func uniquifyConfigs(configs []*api.Config) []*api.Config { 1520 uniqueConfigs := make(map[string]struct{}) 1521 var finalConfigs []*api.Config 1522 for _, config := range configs { 1523 if _, ok := uniqueConfigs[config.ID]; !ok { 1524 uniqueConfigs[config.ID] = struct{}{} 1525 finalConfigs = append(finalConfigs, config) 1526 } 1527 } 1528 return finalConfigs 1529 } 1530 1531 type taskGeneratorFunc func(t *testing.T, nodeID string) ([]*api.Secret, []*api.Config, []*api.ResourceReference, []*api.Task) 1532 1533 // Creates 1 task for every possible task state, so there are 12 tasks, ID=0-11 inclusive. 1534 // Creates 1 secret and 1 config for every single task state + 1, so there are 13 secrets, 13 configs, ID=0-12 inclusive 1535 // Creates 1 secret and 1 config per task by resource reference so there are an additional of each eventually created 1536 // For each task, the dependencies assigned to it are: secret, secret12, config, config12, resourceRefSecret, resourceRefConfig 1537 func makeTasksAndDependenciesWithResourceReferences(t *testing.T, nodeID string) ([]*api.Secret, []*api.Config, []*api.ResourceReference, []*api.Task) { 1538 var ( 1539 secrets []*api.Secret 1540 configs []*api.Config 1541 resourceRefs []*api.ResourceReference 1542 tasks []*api.Task 1543 ) 1544 for i := 0; i <= len(taskStatesInOrder); i++ { 1545 secrets = append(secrets, mockNumberedSecret(i)) 1546 configs = append(configs, mockNumberedConfig(i)) 1547 1548 resourceRefs = append(resourceRefs, &api.ResourceReference{ 1549 ResourceID: fmt.Sprintf("IDresourceRefSecret%d", i), 1550 ResourceType: api.ResourceType_SECRET, 1551 }, &api.ResourceReference{ 1552 ResourceID: fmt.Sprintf("IDresourceRefConfig%d", i), 1553 ResourceType: api.ResourceType_CONFIG, 1554 }) 1555 } 1556 1557 for i, taskState := range taskStatesInOrder { 1558 spec := taskSpecFromDependencies(secrets[i], secrets[len(secrets)-1], configs[i], configs[len(configs)-1], resourceRefs[2*i], resourceRefs[2*i+1]) 1559 tasks = append(tasks, mockNumberedReadyTask(i, nodeID, taskState, spec)) 1560 } 1561 return secrets, configs, resourceRefs, tasks 1562 } 1563 1564 // Creates 1 task for every possible task state, so there are 12 tasks, ID=0-11 inclusive. 1565 // Creates 1 secret and 1 config for every single task state + 1, so there are 13 secrets, 13 configs, ID=0-12 inclusive 1566 // For each task, the dependencies assigned to it are: secret<i>, secret12, config<i>, config12. 1567 // There are no ResourceReferences in these TaskSpecs 1568 func makeTasksAndDependenciesNoResourceReferences(t *testing.T, nodeID string) ([]*api.Secret, []*api.Config, []*api.ResourceReference, []*api.Task) { 1569 var ( 1570 secrets []*api.Secret 1571 configs []*api.Config 1572 resourceRefs []*api.ResourceReference 1573 tasks []*api.Task 1574 ) 1575 for i := 0; i <= len(taskStatesInOrder); i++ { 1576 secrets = append(secrets, mockNumberedSecret(i)) 1577 configs = append(configs, mockNumberedConfig(i)) 1578 } 1579 for i, taskState := range taskStatesInOrder { 1580 spec := taskSpecFromDependencies(secrets[i], secrets[len(secrets)-1], configs[i], configs[len(configs)-1]) 1581 tasks = append(tasks, mockNumberedReadyTask(i, nodeID, taskState, spec)) 1582 } 1583 return secrets, configs, resourceRefs, tasks 1584 } 1585 1586 // Creates 1 secret and 1 config per task by resource reference 1587 // For each task, the dependencies assigned to it are: resourceRefSecret<i>, resourceRefConfig<i>,. 1588 func makeTasksAndDependenciesOnlyResourceReferences(t *testing.T, nodeID string) ([]*api.Secret, []*api.Config, []*api.ResourceReference, []*api.Task) { 1589 var ( 1590 secrets []*api.Secret 1591 configs []*api.Config 1592 resourceRefs []*api.ResourceReference 1593 tasks []*api.Task 1594 ) 1595 for i := 0; i <= len(taskStatesInOrder); i++ { 1596 resourceRefs = append(resourceRefs, &api.ResourceReference{ 1597 ResourceID: fmt.Sprintf("IDresourceRefSecret%d", i), 1598 ResourceType: api.ResourceType_SECRET, 1599 }, &api.ResourceReference{ 1600 ResourceID: fmt.Sprintf("IDresourceRefConfig%d", i), 1601 ResourceType: api.ResourceType_CONFIG, 1602 }) 1603 } 1604 for i, taskState := range taskStatesInOrder { 1605 spec := taskSpecFromDependencies(resourceRefs[2*i], resourceRefs[2*i+1]) 1606 tasks = append(tasks, mockNumberedReadyTask(i, nodeID, taskState, spec)) 1607 } 1608 return secrets, configs, resourceRefs, tasks 1609 } 1610 1611 // Creates 1 task for every possible task state, so there are 12 tasks, ID=0-11 inclusive. 1612 // Creates 1 secret and 1 config for every single task state + 1, so there are 13 secrets, 13 configs, ID=0-12 inclusive 1613 // Creates 1 secret and 1 config per task by resource reference, however they point to existing ID=0-12 secrets and configs so they are not created 1614 // For each task, the dependencies assigned to it are: secret<i>, secret12, config<i>, config12. 1615 func makeTasksAndDependenciesWithRedundantReferences(t *testing.T, nodeID string) ([]*api.Secret, []*api.Config, []*api.ResourceReference, []*api.Task) { 1616 var ( 1617 secrets []*api.Secret 1618 configs []*api.Config 1619 resourceRefs []*api.ResourceReference 1620 tasks []*api.Task 1621 ) 1622 for i := 0; i <= len(taskStatesInOrder); i++ { 1623 secrets = append(secrets, mockNumberedSecret(i)) 1624 configs = append(configs, mockNumberedConfig(i)) 1625 1626 // Note that the IDs here will match the original secret and config reference IDs 1627 resourceRefs = append(resourceRefs, &api.ResourceReference{ 1628 ResourceID: fmt.Sprintf("IDsecret%d", i), 1629 ResourceType: api.ResourceType_SECRET, 1630 }, &api.ResourceReference{ 1631 ResourceID: fmt.Sprintf("IDconfig%d", i), 1632 ResourceType: api.ResourceType_CONFIG, 1633 }) 1634 } 1635 1636 for i, taskState := range taskStatesInOrder { 1637 spec := taskSpecFromDependencies(secrets[i], secrets[len(secrets)-1], configs[i], configs[len(configs)-1], resourceRefs[2*i], resourceRefs[2*i+1]) 1638 tasks = append(tasks, mockNumberedReadyTask(i, nodeID, taskState, spec)) 1639 } 1640 return secrets, configs, resourceRefs, tasks 1641 } 1642 1643 func taskSpecFromDependencies(dependencies ...interface{}) api.TaskSpec { 1644 var secretRefs []*api.SecretReference 1645 var configRefs []*api.ConfigReference 1646 var resourceRefs []api.ResourceReference 1647 for _, d := range dependencies { 1648 switch v := d.(type) { 1649 case *api.Secret: 1650 secretRefs = append(secretRefs, &api.SecretReference{ 1651 SecretName: v.Spec.Annotations.Name, 1652 SecretID: v.ID, 1653 Target: &api.SecretReference_File{ 1654 File: &api.FileTarget{ 1655 Name: "target.txt", 1656 UID: "0", 1657 GID: "0", 1658 Mode: 0666, 1659 }, 1660 }, 1661 }) 1662 case *api.Config: 1663 configRefs = append(configRefs, &api.ConfigReference{ 1664 ConfigName: v.Spec.Annotations.Name, 1665 ConfigID: v.ID, 1666 Target: &api.ConfigReference_File{ 1667 File: &api.FileTarget{ 1668 Name: "target.txt", 1669 UID: "0", 1670 GID: "0", 1671 Mode: 0666, 1672 }, 1673 }, 1674 }) 1675 case *api.ResourceReference: 1676 resourceRefs = append(resourceRefs, api.ResourceReference{ 1677 ResourceID: v.ResourceID, 1678 ResourceType: v.ResourceType, 1679 }) 1680 default: 1681 panic("unexpected dependency type") 1682 } 1683 } 1684 return api.TaskSpec{ 1685 ResourceReferences: resourceRefs, 1686 Runtime: &api.TaskSpec_Container{ 1687 Container: &api.ContainerSpec{ 1688 Secrets: secretRefs, 1689 Configs: configRefs, 1690 }, 1691 }, 1692 } 1693 } 1694 1695 var taskStatesInOrder = []api.TaskState{ 1696 api.TaskStateNew, 1697 api.TaskStatePending, 1698 api.TaskStateAssigned, 1699 api.TaskStateAccepted, 1700 api.TaskStatePreparing, 1701 api.TaskStateReady, 1702 api.TaskStateStarting, 1703 api.TaskStateRunning, 1704 api.TaskStateCompleted, 1705 api.TaskStateShutdown, 1706 api.TaskStateFailed, 1707 api.TaskStateRejected, 1708 } 1709 1710 // Ensure we test the old Tasks() API for backwards compat 1711 1712 func TestOldTasks(t *testing.T) { 1713 t.Parallel() 1714 1715 gd := startDispatcher(t, DefaultConfig()) 1716 defer gd.Close() 1717 1718 var expectedSessionID string 1719 var nodeID string 1720 { 1721 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 1722 assert.NoError(t, err) 1723 defer stream.CloseSend() 1724 resp, err := stream.Recv() 1725 assert.NoError(t, err) 1726 assert.NotEmpty(t, resp.SessionID) 1727 expectedSessionID = resp.SessionID 1728 nodeID = resp.Node.ID 1729 } 1730 1731 testTask1 := &api.Task{ 1732 NodeID: nodeID, 1733 ID: "testTask1", 1734 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1735 DesiredState: api.TaskStateReady, 1736 } 1737 testTask2 := &api.Task{ 1738 NodeID: nodeID, 1739 ID: "testTask2", 1740 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1741 DesiredState: api.TaskStateReady, 1742 } 1743 1744 { 1745 // without correct SessionID should fail 1746 stream, err := gd.Clients[0].Tasks(context.Background(), &api.TasksRequest{}) 1747 assert.NoError(t, err) 1748 assert.NotNil(t, stream) 1749 resp, err := stream.Recv() 1750 assert.Nil(t, resp) 1751 assert.Error(t, err) 1752 assert.Equal(t, testutils.ErrorCode(err), codes.InvalidArgument) 1753 } 1754 1755 stream, err := gd.Clients[0].Tasks(context.Background(), &api.TasksRequest{SessionID: expectedSessionID}) 1756 assert.NoError(t, err) 1757 1758 time.Sleep(100 * time.Millisecond) 1759 1760 resp, err := stream.Recv() 1761 assert.NoError(t, err) 1762 // initially no tasks 1763 assert.Equal(t, 0, len(resp.Tasks)) 1764 1765 err = gd.Store.Update(func(tx store.Tx) error { 1766 assert.NoError(t, store.CreateTask(tx, testTask1)) 1767 assert.NoError(t, store.CreateTask(tx, testTask2)) 1768 return nil 1769 }) 1770 assert.NoError(t, err) 1771 1772 resp, err = stream.Recv() 1773 assert.NoError(t, err) 1774 assert.Equal(t, len(resp.Tasks), 2) 1775 assert.True(t, resp.Tasks[0].ID == "testTask1" && resp.Tasks[1].ID == "testTask2" || resp.Tasks[0].ID == "testTask2" && resp.Tasks[1].ID == "testTask1") 1776 1777 assert.NoError(t, gd.Store.Update(func(tx store.Tx) error { 1778 task := store.GetTask(tx, testTask1.ID) 1779 if task == nil { 1780 return errors.New("no task") 1781 } 1782 task.NodeID = nodeID 1783 task.Status = api.TaskStatus{State: api.TaskStateAssigned} 1784 task.DesiredState = api.TaskStateRunning 1785 return store.UpdateTask(tx, task) 1786 })) 1787 1788 resp, err = stream.Recv() 1789 assert.NoError(t, err) 1790 assert.Equal(t, len(resp.Tasks), 2) 1791 for _, task := range resp.Tasks { 1792 if task.ID == "testTask1" { 1793 assert.Equal(t, task.DesiredState, api.TaskStateRunning) 1794 } 1795 } 1796 1797 err = gd.Store.Update(func(tx store.Tx) error { 1798 assert.NoError(t, store.DeleteTask(tx, testTask1.ID)) 1799 assert.NoError(t, store.DeleteTask(tx, testTask2.ID)) 1800 return nil 1801 }) 1802 assert.NoError(t, err) 1803 1804 resp, err = stream.Recv() 1805 assert.NoError(t, err) 1806 assert.Equal(t, len(resp.Tasks), 0) 1807 } 1808 1809 func TestOldTasksStatusChange(t *testing.T) { 1810 t.Parallel() 1811 1812 gd := startDispatcher(t, DefaultConfig()) 1813 defer gd.Close() 1814 1815 var expectedSessionID string 1816 var nodeID string 1817 { 1818 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 1819 assert.NoError(t, err) 1820 defer stream.CloseSend() 1821 resp, err := stream.Recv() 1822 assert.NoError(t, err) 1823 assert.NotEmpty(t, resp.SessionID) 1824 expectedSessionID = resp.SessionID 1825 nodeID = resp.Node.ID 1826 } 1827 1828 testTask1 := &api.Task{ 1829 NodeID: nodeID, 1830 ID: "testTask1", 1831 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1832 DesiredState: api.TaskStateReady, 1833 } 1834 testTask2 := &api.Task{ 1835 NodeID: nodeID, 1836 ID: "testTask2", 1837 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1838 DesiredState: api.TaskStateReady, 1839 } 1840 1841 { 1842 // without correct SessionID should fail 1843 stream, err := gd.Clients[0].Tasks(context.Background(), &api.TasksRequest{}) 1844 assert.NoError(t, err) 1845 assert.NotNil(t, stream) 1846 resp, err := stream.Recv() 1847 assert.Nil(t, resp) 1848 assert.Error(t, err) 1849 assert.Equal(t, testutils.ErrorCode(err), codes.InvalidArgument) 1850 } 1851 1852 stream, err := gd.Clients[0].Tasks(context.Background(), &api.TasksRequest{SessionID: expectedSessionID}) 1853 assert.NoError(t, err) 1854 1855 time.Sleep(100 * time.Millisecond) 1856 1857 resp, err := stream.Recv() 1858 assert.NoError(t, err) 1859 // initially no tasks 1860 assert.Equal(t, 0, len(resp.Tasks)) 1861 1862 err = gd.Store.Update(func(tx store.Tx) error { 1863 assert.NoError(t, store.CreateTask(tx, testTask1)) 1864 assert.NoError(t, store.CreateTask(tx, testTask2)) 1865 return nil 1866 }) 1867 assert.NoError(t, err) 1868 1869 resp, err = stream.Recv() 1870 assert.NoError(t, err) 1871 assert.Equal(t, len(resp.Tasks), 2) 1872 assert.True(t, resp.Tasks[0].ID == "testTask1" && resp.Tasks[1].ID == "testTask2" || resp.Tasks[0].ID == "testTask2" && resp.Tasks[1].ID == "testTask1") 1873 1874 assert.NoError(t, gd.Store.Update(func(tx store.Tx) error { 1875 task := store.GetTask(tx, testTask1.ID) 1876 if task == nil { 1877 return errors.New("no task") 1878 } 1879 task.NodeID = nodeID 1880 // only Status is changed for task1 1881 task.Status = api.TaskStatus{State: api.TaskStateFailed, Err: "1234"} 1882 task.DesiredState = api.TaskStateReady 1883 return store.UpdateTask(tx, task) 1884 })) 1885 1886 // dispatcher shouldn't send snapshot for this update 1887 recvChan := make(chan struct{}) 1888 go func() { 1889 _, _ = stream.Recv() 1890 recvChan <- struct{}{} 1891 }() 1892 1893 select { 1894 case <-recvChan: 1895 assert.Fail(t, "task.Status update should not trigger dispatcher update") 1896 case <-time.After(250 * time.Millisecond): 1897 } 1898 } 1899 1900 func TestOldTasksBatch(t *testing.T) { 1901 gd := startDispatcher(t, DefaultConfig()) 1902 defer gd.Close() 1903 1904 var expectedSessionID string 1905 var nodeID string 1906 { 1907 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 1908 assert.NoError(t, err) 1909 defer stream.CloseSend() 1910 resp, err := stream.Recv() 1911 assert.NoError(t, err) 1912 assert.NotEmpty(t, resp.SessionID) 1913 expectedSessionID = resp.SessionID 1914 nodeID = resp.Node.ID 1915 } 1916 1917 testTask1 := &api.Task{ 1918 NodeID: nodeID, 1919 ID: "testTask1", 1920 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1921 } 1922 testTask2 := &api.Task{ 1923 NodeID: nodeID, 1924 ID: "testTask2", 1925 Status: api.TaskStatus{State: api.TaskStateAssigned}, 1926 } 1927 1928 stream, err := gd.Clients[0].Tasks(context.Background(), &api.TasksRequest{SessionID: expectedSessionID}) 1929 assert.NoError(t, err) 1930 1931 resp, err := stream.Recv() 1932 assert.NoError(t, err) 1933 // initially no tasks 1934 assert.Equal(t, 0, len(resp.Tasks)) 1935 1936 err = gd.Store.Update(func(tx store.Tx) error { 1937 assert.NoError(t, store.CreateTask(tx, testTask1)) 1938 assert.NoError(t, store.CreateTask(tx, testTask2)) 1939 return nil 1940 }) 1941 assert.NoError(t, err) 1942 1943 err = gd.Store.Update(func(tx store.Tx) error { 1944 assert.NoError(t, store.DeleteTask(tx, testTask1.ID)) 1945 assert.NoError(t, store.DeleteTask(tx, testTask2.ID)) 1946 return nil 1947 }) 1948 assert.NoError(t, err) 1949 1950 resp, err = stream.Recv() 1951 assert.NoError(t, err) 1952 // all tasks have been deleted 1953 assert.Equal(t, len(resp.Tasks), 0) 1954 } 1955 1956 func TestOldTasksNoCert(t *testing.T) { 1957 gd := startDispatcher(t, DefaultConfig()) 1958 defer gd.Close() 1959 1960 stream, err := gd.Clients[2].Tasks(context.Background(), &api.TasksRequest{}) 1961 assert.NoError(t, err) 1962 assert.NotNil(t, stream) 1963 resp, err := stream.Recv() 1964 assert.Nil(t, resp) 1965 assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = Permission denied: unauthorized peer role: rpc error: code = PermissionDenied desc = no client certificates in request") 1966 } 1967 1968 func TestClusterUpdatesSendMessages(t *testing.T) { 1969 cfg := DefaultConfig() 1970 cfg.RateLimitPeriod = 0 1971 gd := startDispatcher(t, cfg) 1972 defer gd.Close() 1973 1974 stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) 1975 require.NoError(t, err) 1976 defer stream.CloseSend() 1977 1978 var msg *api.SessionMessage 1979 { 1980 msg, err = stream.Recv() 1981 require.NoError(t, err) 1982 require.NotEmpty(t, msg.SessionID) 1983 require.NotNil(t, msg.Node) 1984 require.Len(t, msg.Managers, 1) 1985 require.Empty(t, msg.NetworkBootstrapKeys) 1986 require.Equal(t, gd.testCA.RootCA.Certs, msg.RootCA) 1987 } 1988 1989 // changing the network bootstrap keys results in a new message with updated keys 1990 expected := msg.Copy() 1991 expected.NetworkBootstrapKeys = []*api.EncryptionKey{ 1992 {Key: []byte("network key1")}, 1993 {Key: []byte("network key2")}, 1994 } 1995 require.NoError(t, gd.Store.Update(func(tx store.Tx) error { 1996 cluster := store.GetCluster(tx, gd.testCA.Organization) 1997 if cluster == nil { 1998 return errors.New("no cluster") 1999 } 2000 cluster.NetworkBootstrapKeys = expected.NetworkBootstrapKeys 2001 return store.UpdateCluster(tx, cluster) 2002 })) 2003 time.Sleep(100 * time.Millisecond) 2004 { 2005 msg, err = stream.Recv() 2006 require.NoError(t, err) 2007 require.Equal(t, expected, msg) 2008 } 2009 2010 // changing the peers results in a new message with updated managers 2011 gd.testCluster.addMember("1.1.1.1") 2012 time.Sleep(100 * time.Millisecond) 2013 { 2014 msg, err = stream.Recv() 2015 require.NoError(t, err) 2016 require.Len(t, msg.Managers, 2) 2017 expected.Managers = msg.Managers 2018 require.Equal(t, expected, msg) 2019 } 2020 2021 // changing the rootCA cert and has in the cluster results in a new message with an updated cert 2022 expected = msg.Copy() 2023 expected.RootCA = cautils.ECDSA256SHA256Cert 2024 require.NoError(t, gd.Store.Update(func(tx store.Tx) error { 2025 cluster := store.GetCluster(tx, gd.testCA.Organization) 2026 if cluster == nil { 2027 return errors.New("no cluster") 2028 } 2029 cluster.RootCA.CACert = cautils.ECDSA256SHA256Cert 2030 cluster.RootCA.CACertHash = digest.FromBytes(cautils.ECDSA256SHA256Cert).String() 2031 return store.UpdateCluster(tx, cluster) 2032 })) 2033 time.Sleep(100 * time.Millisecond) 2034 { 2035 msg, err = stream.Recv() 2036 require.NoError(t, err) 2037 require.Equal(t, expected, msg) 2038 } 2039 } 2040 2041 // mockPluginGetter enables mocking the server plugin getter with customized plugins 2042 type mockPluginGetter struct { 2043 addr string 2044 server *httptest.Server 2045 name string 2046 plugin plugingetter.CompatPlugin 2047 } 2048 2049 // SetupPlugin setup a new plugin - the same plugin wil always return in all calls 2050 func (m *mockPluginGetter) SetupPlugin(name string, handler http.Handler) error { 2051 m.server = httptest.NewServer(handler) 2052 client, err := plugins.NewClient(m.server.URL, nil) 2053 if err != nil { 2054 return err 2055 } 2056 m.plugin = NewMockPlugin(m.name, client) 2057 m.name = name 2058 return nil 2059 } 2060 2061 // Close closes the mock plugin getter 2062 func (m *mockPluginGetter) Close() { 2063 if m.server == nil { 2064 return 2065 } 2066 m.server.Close() 2067 } 2068 2069 func (m *mockPluginGetter) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) { 2070 if name != m.name { 2071 return nil, fmt.Errorf("plugin with name %s not defined", name) 2072 } 2073 return m.plugin, nil 2074 } 2075 func (m *mockPluginGetter) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) { 2076 return nil, nil 2077 } 2078 func (m *mockPluginGetter) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin { 2079 return nil 2080 } 2081 func (m *mockPluginGetter) Handle(capability string, callback func(string, *plugins.Client)) { 2082 } 2083 2084 // MockPlugin mocks a v2 docker plugin 2085 type MockPlugin struct { 2086 client *plugins.Client 2087 name string 2088 } 2089 2090 // NewMockPlugin creates a new v2 plugin fake (returns the specified client and name for all calls) 2091 func NewMockPlugin(name string, client *plugins.Client) *MockPlugin { 2092 return &MockPlugin{name: name, client: client} 2093 } 2094 2095 func (m *MockPlugin) Client() *plugins.Client { 2096 return m.client 2097 } 2098 func (m *MockPlugin) Name() string { 2099 return m.name 2100 } 2101 func (m *MockPlugin) ScopedPath(_ string) string { 2102 return "" 2103 } 2104 func (m *MockPlugin) BasePath() string { 2105 return "" 2106 2107 } 2108 func (m *MockPlugin) IsV1() bool { 2109 return false 2110 }