github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/controlapi/node_test.go (about) 1 package controlapi 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "testing" 8 9 "github.com/docker/swarmkit/api" 10 cautils "github.com/docker/swarmkit/ca/testutils" 11 "github.com/docker/swarmkit/identity" 12 raftutils "github.com/docker/swarmkit/manager/state/raft/testutils" 13 "github.com/docker/swarmkit/manager/state/store" 14 "github.com/docker/swarmkit/testutils" 15 "github.com/sirupsen/logrus" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 "google.golang.org/grpc/codes" 19 "google.golang.org/grpc/grpclog" 20 ) 21 22 func createNode(t *testing.T, ts *testServer, id string, role api.NodeRole, membership api.NodeSpec_Membership, state api.NodeStatus_State) *api.Node { 23 node := &api.Node{ 24 ID: id, 25 Spec: api.NodeSpec{ 26 Membership: membership, 27 }, 28 Status: api.NodeStatus{ 29 State: state, 30 }, 31 Role: role, 32 } 33 err := ts.Store.Update(func(tx store.Tx) error { 34 return store.CreateNode(tx, node) 35 }) 36 assert.NoError(t, err) 37 return node 38 } 39 40 func TestGetNode(t *testing.T) { 41 ts := newTestServer(t) 42 defer ts.Stop() 43 44 _, err := ts.Client.GetNode(context.Background(), &api.GetNodeRequest{}) 45 assert.Error(t, err) 46 assert.Equal(t, codes.InvalidArgument, testutils.ErrorCode(err)) 47 48 _, err = ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: "invalid"}) 49 assert.Error(t, err) 50 assert.Equal(t, codes.NotFound, testutils.ErrorCode(err)) 51 52 node := createNode(t, ts, "id", api.NodeRoleManager, api.NodeMembershipAccepted, api.NodeStatus_READY) 53 r, err := ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: node.ID}) 54 assert.NoError(t, err) 55 assert.Equal(t, node.ID, r.Node.ID) 56 } 57 58 func TestListNodes(t *testing.T) { 59 60 ts := newTestServer(t) 61 defer ts.Stop() 62 r, err := ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 63 assert.NoError(t, err) 64 assert.Empty(t, r.Nodes) 65 66 createNode(t, ts, "id1", api.NodeRoleManager, api.NodeMembershipAccepted, api.NodeStatus_READY) 67 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 68 assert.NoError(t, err) 69 assert.Equal(t, 1, len(r.Nodes)) 70 71 createNode(t, ts, "id2", api.NodeRoleWorker, api.NodeMembershipAccepted, api.NodeStatus_READY) 72 createNode(t, ts, "id3", api.NodeRoleWorker, api.NodeMembershipPending, api.NodeStatus_READY) 73 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 74 assert.NoError(t, err) 75 assert.Equal(t, 3, len(r.Nodes)) 76 77 // List by role. 78 r, err = ts.Client.ListNodes(context.Background(), 79 &api.ListNodesRequest{ 80 Filters: &api.ListNodesRequest_Filters{ 81 Roles: []api.NodeRole{api.NodeRoleManager}, 82 }, 83 }, 84 ) 85 assert.NoError(t, err) 86 assert.Equal(t, 1, len(r.Nodes)) 87 r, err = ts.Client.ListNodes(context.Background(), 88 &api.ListNodesRequest{ 89 Filters: &api.ListNodesRequest_Filters{ 90 Roles: []api.NodeRole{api.NodeRoleWorker}, 91 }, 92 }, 93 ) 94 assert.NoError(t, err) 95 assert.Equal(t, 2, len(r.Nodes)) 96 r, err = ts.Client.ListNodes(context.Background(), 97 &api.ListNodesRequest{ 98 Filters: &api.ListNodesRequest_Filters{ 99 Roles: []api.NodeRole{api.NodeRoleManager, api.NodeRoleWorker}, 100 }, 101 }, 102 ) 103 assert.NoError(t, err) 104 assert.Equal(t, 3, len(r.Nodes)) 105 106 // List by membership. 107 r, err = ts.Client.ListNodes(context.Background(), 108 &api.ListNodesRequest{ 109 Filters: &api.ListNodesRequest_Filters{ 110 Memberships: []api.NodeSpec_Membership{api.NodeMembershipAccepted}, 111 }, 112 }, 113 ) 114 assert.NoError(t, err) 115 assert.Equal(t, 2, len(r.Nodes)) 116 r, err = ts.Client.ListNodes(context.Background(), 117 &api.ListNodesRequest{ 118 Filters: &api.ListNodesRequest_Filters{ 119 Memberships: []api.NodeSpec_Membership{api.NodeMembershipPending}, 120 }, 121 }, 122 ) 123 assert.NoError(t, err) 124 assert.Equal(t, 1, len(r.Nodes)) 125 r, err = ts.Client.ListNodes(context.Background(), 126 &api.ListNodesRequest{ 127 Filters: &api.ListNodesRequest_Filters{ 128 Memberships: []api.NodeSpec_Membership{api.NodeMembershipAccepted, api.NodeMembershipPending}, 129 }, 130 }, 131 ) 132 assert.NoError(t, err) 133 assert.Equal(t, 3, len(r.Nodes)) 134 r, err = ts.Client.ListNodes(context.Background(), 135 &api.ListNodesRequest{ 136 Filters: &api.ListNodesRequest_Filters{ 137 Roles: []api.NodeRole{api.NodeRoleWorker}, 138 Memberships: []api.NodeSpec_Membership{api.NodeMembershipPending}, 139 }, 140 }, 141 ) 142 assert.NoError(t, err) 143 assert.Equal(t, 1, len(r.Nodes)) 144 } 145 146 func TestListNodesWithLabelFilter(t *testing.T) { 147 ts := newTestServer(t) 148 defer ts.Stop() 149 150 // satify these test cases: 151 // Filtering on engine labels 152 // - returns all nodes with matching engine labels 153 // - does not return nodes with matching node labels 154 // - does not return nodes with non-matching engine labels 155 // Filtering on nodes: 156 // - returns all nodes with matching node labels 157 // - does not return nodes with matching engine labels 158 // - does not return nodes with non-matching node labels 159 160 // we'll need 3 nodes for this test. 161 nodes := make([]*api.Node, 3) 162 nodes[0] = &api.Node{ 163 ID: "node0", 164 Spec: api.NodeSpec{ 165 Annotations: api.Annotations{ 166 Labels: map[string]string{ 167 "allcommon": "node", 168 "nodelabel1": "shouldmatch", 169 "nodelabel2": "unique1", 170 }, 171 }, 172 }, 173 Description: &api.NodeDescription{ 174 Engine: &api.EngineDescription{ 175 Labels: map[string]string{ 176 "allcommon": "engine", 177 "enginelabel1": "shouldmatch", 178 "enginelabel2": "unique1", 179 }, 180 }, 181 }, 182 } 183 184 nodes[1] = &api.Node{ 185 ID: "node1", 186 Spec: api.NodeSpec{ 187 Annotations: api.Annotations{ 188 Labels: map[string]string{ 189 "allcommon": "node", 190 "nodelabel1": "shouldmatch", 191 "nodelabel2": "unique2", 192 }, 193 }, 194 }, 195 Description: &api.NodeDescription{ 196 Engine: &api.EngineDescription{ 197 Labels: map[string]string{ 198 "allcommon": "engine", 199 "enginelabel1": "shouldmatch", 200 "enginelabel2": "unique2", 201 }, 202 }, 203 }, 204 } 205 nodes[2] = &api.Node{ 206 ID: "node2", 207 Spec: api.NodeSpec{ 208 Annotations: api.Annotations{ 209 Labels: map[string]string{ 210 "allcommon": "node", 211 "nodelabel1": "shouldnevermatch", 212 "nodelabel2": "unique1", 213 }, 214 }, 215 }, 216 Description: &api.NodeDescription{ 217 Engine: &api.EngineDescription{ 218 Labels: map[string]string{ 219 "allcommon": "engine", 220 "enginelabel1": "shouldnevermatch", 221 "enginelabel2": "unique1", 222 }, 223 }, 224 }, 225 } 226 227 // createNode gives us a bunch of fields we don't care about. instead, do a 228 // store update directly 229 err := ts.Store.Update(func(tx store.Tx) error { 230 for _, node := range nodes { 231 if err := store.CreateNode(tx, node); err != nil { 232 return err 233 } 234 } 235 return nil 236 }) 237 require.NoError(t, err, "error creating nodes") 238 239 // now try listing nodes 240 241 // listing with an empty set of labels should return all nodes 242 t.Log("list nodes with no filters") 243 r, err := ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 244 Filters: &api.ListNodesRequest_Filters{}, 245 }) 246 assert.NoError(t, err) 247 assert.Len(t, r.Nodes, 3) 248 249 t.Log("list nodes with allcommon=engine engine label filter") 250 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 251 Filters: &api.ListNodesRequest_Filters{ 252 Labels: map[string]string{"allcommon": "engine"}, 253 }, 254 }) 255 assert.NoError(t, err) 256 assert.Len(t, r.Nodes, 3) 257 258 t.Log("list nodes with allcommon=node engine label filter") 259 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 260 Filters: &api.ListNodesRequest_Filters{ 261 Labels: map[string]string{"allcommon": "node"}, 262 }, 263 }) 264 // nothing should be returned; allcommon=engine on engine labels 265 assert.NoError(t, err) 266 assert.Len(t, r.Nodes, 0) 267 268 t.Log("list nodes with allcommon=node node filter") 269 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 270 Filters: &api.ListNodesRequest_Filters{ 271 NodeLabels: map[string]string{"allcommon": "node"}, 272 }, 273 }) 274 assert.NoError(t, err) 275 assert.Len(t, r.Nodes, 3) 276 277 t.Log("list nodes with allcommon=engine node filter") 278 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 279 Filters: &api.ListNodesRequest_Filters{ 280 NodeLabels: map[string]string{"allcommon": "engine"}, 281 }, 282 }) 283 assert.NoError(t, err) 284 assert.Len(t, r.Nodes, 0) 285 286 t.Log("list nodes with nodelabel1=shouldmatch node filter") 287 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 288 Filters: &api.ListNodesRequest_Filters{ 289 NodeLabels: map[string]string{"nodelabel1": "shouldmatch"}, 290 }, 291 }) 292 // should only return the first 2 nodes 293 assert.NoError(t, err) 294 assert.Len(t, r.Nodes, 2) 295 assert.Contains(t, r.Nodes, nodes[0]) 296 assert.Contains(t, r.Nodes, nodes[1]) 297 298 t.Log("list nodes with enginelabel1=shouldmatch engine filter") 299 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 300 Filters: &api.ListNodesRequest_Filters{ 301 Labels: map[string]string{"enginelabel1": "shouldmatch"}, 302 }, 303 }) 304 // should only return the first 2 nodes 305 assert.NoError(t, err) 306 assert.Len(t, r.Nodes, 2) 307 assert.Contains(t, r.Nodes, nodes[0]) 308 assert.Contains(t, r.Nodes, nodes[1]) 309 310 t.Log("list nodes with node two engine filters") 311 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 312 Filters: &api.ListNodesRequest_Filters{ 313 Labels: map[string]string{ 314 "enginelabel1": "shouldmatch", 315 "enginelabel2": "unique1", 316 }, 317 }, 318 }) 319 // should only return the first node 320 assert.NoError(t, err) 321 assert.Len(t, r.Nodes, 1) 322 assert.Contains(t, r.Nodes, nodes[0]) 323 324 t.Log("list nodes with node two node filters") 325 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 326 Filters: &api.ListNodesRequest_Filters{ 327 NodeLabels: map[string]string{ 328 "nodelabel1": "shouldmatch", 329 "nodelabel2": "unique1", 330 }, 331 }, 332 }) 333 // should only return the first node 334 assert.NoError(t, err) 335 assert.Len(t, r.Nodes, 1) 336 assert.Contains(t, r.Nodes, nodes[0]) 337 338 t.Log("list nodes with both engine and node filters") 339 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{ 340 Filters: &api.ListNodesRequest_Filters{ 341 // all nodes pass this filter 342 Labels: map[string]string{ 343 "enginelabel1": "", 344 }, 345 // only 0 and 2 pass this filter 346 NodeLabels: map[string]string{ 347 "nodelabel2": "unique1", 348 }, 349 }, 350 }) 351 assert.NoError(t, err) 352 assert.Len(t, r.Nodes, 2) 353 assert.Contains(t, r.Nodes, nodes[0]) 354 assert.Contains(t, r.Nodes, nodes[2]) 355 } 356 357 func TestRemoveNodes(t *testing.T) { 358 ts := newTestServer(t) 359 defer ts.Stop() 360 361 ts.Store.Update(func(tx store.Tx) error { 362 store.CreateCluster(tx, &api.Cluster{ 363 ID: identity.NewID(), 364 Spec: api.ClusterSpec{ 365 Annotations: api.Annotations{ 366 Name: store.DefaultClusterName, 367 }, 368 }, 369 }) 370 return nil 371 }) 372 373 r, err := ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 374 assert.NoError(t, err) 375 assert.Empty(t, r.Nodes) 376 377 createNode(t, ts, "id1", api.NodeRoleManager, api.NodeMembershipAccepted, api.NodeStatus_READY) 378 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 379 assert.NoError(t, err) 380 assert.Len(t, r.Nodes, 1) 381 382 createNode(t, ts, "id2", api.NodeRoleWorker, api.NodeMembershipAccepted, api.NodeStatus_READY) 383 createNode(t, ts, "id3", api.NodeRoleWorker, api.NodeMembershipPending, api.NodeStatus_UNKNOWN) 384 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 385 assert.NoError(t, err) 386 assert.Len(t, r.Nodes, 3) 387 388 // Attempt to remove a ready node without force 389 _, err = ts.Client.RemoveNode(context.Background(), 390 &api.RemoveNodeRequest{ 391 NodeID: "id2", 392 Force: false, 393 }, 394 ) 395 assert.Error(t, err) 396 397 r, err = ts.Client.ListNodes(context.Background(), 398 &api.ListNodesRequest{ 399 Filters: &api.ListNodesRequest_Filters{ 400 Roles: []api.NodeRole{api.NodeRoleManager, api.NodeRoleWorker}, 401 }, 402 }, 403 ) 404 assert.NoError(t, err) 405 assert.Len(t, r.Nodes, 3) 406 407 // Attempt to remove a ready node with force 408 _, err = ts.Client.RemoveNode(context.Background(), 409 &api.RemoveNodeRequest{ 410 NodeID: "id2", 411 Force: true, 412 }, 413 ) 414 assert.NoError(t, err) 415 416 r, err = ts.Client.ListNodes(context.Background(), 417 &api.ListNodesRequest{ 418 Filters: &api.ListNodesRequest_Filters{ 419 Roles: []api.NodeRole{api.NodeRoleManager, api.NodeRoleWorker}, 420 }, 421 }, 422 ) 423 assert.NoError(t, err) 424 assert.Len(t, r.Nodes, 2) 425 426 clusterResp, err := ts.Client.ListClusters(context.Background(), &api.ListClustersRequest{}) 427 assert.NoError(t, err) 428 require.Len(t, clusterResp.Clusters, 1) 429 require.Len(t, clusterResp.Clusters[0].BlacklistedCertificates, 1) 430 _, ok := clusterResp.Clusters[0].BlacklistedCertificates["id2"] 431 assert.True(t, ok) 432 433 // Attempt to remove a non-ready node without force 434 _, err = ts.Client.RemoveNode(context.Background(), 435 &api.RemoveNodeRequest{ 436 NodeID: "id3", 437 Force: false, 438 }, 439 ) 440 assert.NoError(t, err) 441 442 r, err = ts.Client.ListNodes(context.Background(), 443 &api.ListNodesRequest{ 444 Filters: &api.ListNodesRequest_Filters{ 445 Roles: []api.NodeRole{api.NodeRoleManager, api.NodeRoleWorker}, 446 }, 447 }, 448 ) 449 assert.NoError(t, err) 450 assert.Len(t, r.Nodes, 1) 451 } 452 453 func init() { 454 grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard)) 455 logrus.SetOutput(ioutil.Discard) 456 } 457 458 func getMap(t *testing.T, nodes []*api.Node) map[uint64]*api.ManagerStatus { 459 m := make(map[uint64]*api.ManagerStatus) 460 for _, n := range nodes { 461 if n.ManagerStatus != nil { 462 m[n.ManagerStatus.RaftID] = n.ManagerStatus 463 } 464 } 465 return m 466 } 467 468 func TestListManagerNodes(t *testing.T) { 469 t.Parallel() 470 471 tc := cautils.NewTestCA(t) 472 defer tc.Stop() 473 ts := newTestServer(t) 474 defer ts.Stop() 475 476 nodes, clockSource := raftutils.NewRaftCluster(t, tc) 477 defer raftutils.TeardownCluster(nodes) 478 479 // Create a node object for each of the managers 480 assert.NoError(t, nodes[1].MemoryStore().Update(func(tx store.Tx) error { 481 assert.NoError(t, store.CreateNode(tx, &api.Node{ID: nodes[1].SecurityConfig.ClientTLSCreds.NodeID()})) 482 assert.NoError(t, store.CreateNode(tx, &api.Node{ID: nodes[2].SecurityConfig.ClientTLSCreds.NodeID()})) 483 assert.NoError(t, store.CreateNode(tx, &api.Node{ID: nodes[3].SecurityConfig.ClientTLSCreds.NodeID()})) 484 return nil 485 })) 486 487 // Assign one of the raft node to the test server 488 ts.Server.raft = nodes[1].Node 489 ts.Server.store = nodes[1].MemoryStore() 490 491 // There should be 3 reachable managers listed 492 r, err := ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 493 assert.NoError(t, err) 494 assert.NotNil(t, r) 495 managers := getMap(t, r.Nodes) 496 assert.Len(t, ts.Server.raft.GetMemberlist(), 3) 497 assert.Len(t, r.Nodes, 3) 498 499 // Node 1 should be the leader 500 for i := 1; i <= 3; i++ { 501 if i == 1 { 502 assert.True(t, managers[nodes[uint64(i)].Config.ID].Leader) 503 continue 504 } 505 assert.False(t, managers[nodes[uint64(i)].Config.ID].Leader) 506 } 507 508 // All nodes should be reachable 509 for i := 1; i <= 3; i++ { 510 assert.Equal(t, api.RaftMemberStatus_REACHABLE, managers[nodes[uint64(i)].Config.ID].Reachability) 511 } 512 513 // Add two more nodes to the cluster 514 raftutils.AddRaftNode(t, clockSource, nodes, tc) 515 raftutils.AddRaftNode(t, clockSource, nodes, tc) 516 raftutils.WaitForCluster(t, clockSource, nodes) 517 518 // Add node entries for these 519 assert.NoError(t, nodes[1].MemoryStore().Update(func(tx store.Tx) error { 520 assert.NoError(t, store.CreateNode(tx, &api.Node{ID: nodes[4].SecurityConfig.ClientTLSCreds.NodeID()})) 521 assert.NoError(t, store.CreateNode(tx, &api.Node{ID: nodes[5].SecurityConfig.ClientTLSCreds.NodeID()})) 522 return nil 523 })) 524 525 // There should be 5 reachable managers listed 526 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 527 assert.NoError(t, err) 528 assert.NotNil(t, r) 529 managers = getMap(t, r.Nodes) 530 assert.Len(t, ts.Server.raft.GetMemberlist(), 5) 531 assert.Len(t, r.Nodes, 5) 532 for i := 1; i <= 5; i++ { 533 assert.Equal(t, api.RaftMemberStatus_REACHABLE, managers[nodes[uint64(i)].Config.ID].Reachability) 534 } 535 536 // Stops 2 nodes 537 nodes[4].Server.Stop() 538 nodes[4].ShutdownRaft() 539 nodes[5].Server.Stop() 540 nodes[5].ShutdownRaft() 541 542 // Node 4 and Node 5 should be listed as Unreachable 543 assert.NoError(t, testutils.PollFunc(clockSource, func() error { 544 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 545 if err != nil { 546 return err 547 } 548 549 managers = getMap(t, r.Nodes) 550 551 if len(r.Nodes) != 5 { 552 return fmt.Errorf("expected 5 nodes, got %d", len(r.Nodes)) 553 } 554 555 if managers[nodes[4].Config.ID].Reachability == api.RaftMemberStatus_REACHABLE { 556 return fmt.Errorf("expected node 4 to be unreachable") 557 } 558 559 if managers[nodes[5].Config.ID].Reachability == api.RaftMemberStatus_REACHABLE { 560 return fmt.Errorf("expected node 5 to be unreachable") 561 } 562 563 return nil 564 })) 565 566 // Restart the 2 nodes 567 nodes[4] = raftutils.RestartNode(t, clockSource, nodes[4], false) 568 nodes[5] = raftutils.RestartNode(t, clockSource, nodes[5], false) 569 raftutils.WaitForCluster(t, clockSource, nodes) 570 571 assert.Len(t, ts.Server.raft.GetMemberlist(), 5) 572 // All the nodes should be reachable again 573 assert.NoError(t, testutils.PollFunc(clockSource, func() error { 574 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 575 if err != nil { 576 return err 577 } 578 managers = getMap(t, r.Nodes) 579 for i := 1; i <= 5; i++ { 580 if managers[nodes[uint64(i)].Config.ID].Reachability != api.RaftMemberStatus_REACHABLE { 581 return fmt.Errorf("node %x is unreachable", nodes[uint64(i)].Config.ID) 582 } 583 } 584 return nil 585 })) 586 587 // Stop node 1 (leader) 588 nodes[1].Server.Stop() 589 nodes[1].ShutdownRaft() 590 591 newCluster := map[uint64]*raftutils.TestNode{ 592 2: nodes[2], 593 3: nodes[3], 594 4: nodes[4], 595 5: nodes[5], 596 } 597 598 // Wait for the re-election to occur 599 raftutils.WaitForCluster(t, clockSource, newCluster) 600 601 var leaderNode *raftutils.TestNode 602 for _, node := range newCluster { 603 if node.IsLeader() { 604 leaderNode = node 605 } 606 } 607 608 // Switch the raft node used by the server 609 ts.Server.raft = leaderNode.Node 610 611 // Node 1 should not be the leader anymore 612 assert.NoError(t, testutils.PollFunc(clockSource, func() error { 613 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 614 if err != nil { 615 return err 616 } 617 618 managers = getMap(t, r.Nodes) 619 620 if managers[nodes[1].Config.ID].Leader { 621 return fmt.Errorf("expected node 1 not to be the leader") 622 } 623 624 if managers[nodes[1].Config.ID].Reachability == api.RaftMemberStatus_REACHABLE { 625 return fmt.Errorf("expected node 1 to be unreachable") 626 } 627 628 return nil 629 })) 630 631 // Restart node 1 632 nodes[1].ShutdownRaft() 633 nodes[1] = raftutils.RestartNode(t, clockSource, nodes[1], false) 634 raftutils.WaitForCluster(t, clockSource, nodes) 635 636 // Ensure that node 1 is not the leader 637 assert.False(t, managers[nodes[uint64(1)].Config.ID].Leader) 638 639 // Check that another node got the leader status 640 var leader uint64 641 leaderCount := 0 642 for i := 1; i <= 5; i++ { 643 if managers[nodes[uint64(i)].Config.ID].Leader { 644 leader = nodes[uint64(i)].Config.ID 645 leaderCount++ 646 } 647 } 648 649 // There should be only one leader after node 1 recovery and it 650 // should be different than node 1 651 assert.Equal(t, 1, leaderCount) 652 assert.NotEqual(t, leader, nodes[1].Config.ID) 653 } 654 655 func TestUpdateNode(t *testing.T) { 656 tc := cautils.NewTestCA(t) 657 defer tc.Stop() 658 ts := newTestServer(t) 659 defer ts.Stop() 660 661 nodes := make(map[uint64]*raftutils.TestNode) 662 nodes[1], _ = raftutils.NewInitNode(t, tc, nil) 663 defer raftutils.TeardownCluster(nodes) 664 665 nodeID := nodes[1].SecurityConfig.ClientTLSCreds.NodeID() 666 667 // Assign one of the raft node to the test server 668 ts.Server.raft = nodes[1].Node 669 ts.Server.store = nodes[1].MemoryStore() 670 671 _, err := ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{ 672 NodeID: nodeID, 673 Spec: &api.NodeSpec{ 674 Availability: api.NodeAvailabilityDrain, 675 }, 676 NodeVersion: &api.Version{}, 677 }) 678 assert.Error(t, err) 679 assert.Equal(t, codes.NotFound, testutils.ErrorCode(err)) 680 681 // Create a node object for the manager 682 assert.NoError(t, nodes[1].MemoryStore().Update(func(tx store.Tx) error { 683 assert.NoError(t, store.CreateNode(tx, &api.Node{ 684 ID: nodes[1].SecurityConfig.ClientTLSCreds.NodeID(), 685 Spec: api.NodeSpec{ 686 Membership: api.NodeMembershipAccepted, 687 }, 688 Role: api.NodeRoleManager, 689 })) 690 return nil 691 })) 692 693 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{}) 694 assert.Error(t, err) 695 assert.Equal(t, codes.InvalidArgument, testutils.ErrorCode(err)) 696 697 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{NodeID: "invalid", Spec: &api.NodeSpec{}, NodeVersion: &api.Version{}}) 698 assert.Error(t, err) 699 assert.Equal(t, codes.NotFound, testutils.ErrorCode(err)) 700 701 r, err := ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: nodeID}) 702 assert.NoError(t, err) 703 if !assert.NotNil(t, r) { 704 assert.FailNow(t, "got unexpected nil response from GetNode") 705 } 706 assert.NotNil(t, r.Node) 707 708 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{NodeID: nodeID}) 709 assert.Error(t, err) 710 assert.Equal(t, codes.InvalidArgument, testutils.ErrorCode(err)) 711 712 spec := r.Node.Spec.Copy() 713 spec.Availability = api.NodeAvailabilityDrain 714 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{ 715 NodeID: nodeID, 716 Spec: spec, 717 }) 718 assert.Error(t, err) 719 assert.Equal(t, codes.InvalidArgument, testutils.ErrorCode(err)) 720 721 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{ 722 NodeID: nodeID, 723 Spec: spec, 724 NodeVersion: &r.Node.Meta.Version, 725 }) 726 assert.NoError(t, err) 727 728 r, err = ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: nodeID}) 729 assert.NoError(t, err) 730 if !assert.NotNil(t, r) { 731 assert.FailNow(t, "got unexpected nil response from GetNode") 732 } 733 assert.NotNil(t, r.Node) 734 assert.NotNil(t, r.Node.Spec) 735 assert.Equal(t, api.NodeAvailabilityDrain, r.Node.Spec.Availability) 736 737 version := &r.Node.Meta.Version 738 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{NodeID: nodeID, Spec: &r.Node.Spec, NodeVersion: version}) 739 assert.NoError(t, err) 740 741 // Perform an update with the "old" version. 742 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{NodeID: nodeID, Spec: &r.Node.Spec, NodeVersion: version}) 743 assert.Error(t, err) 744 } 745 746 func testUpdateNodeDemote(t *testing.T) { 747 tc := cautils.NewTestCA(t) 748 defer tc.Stop() 749 ts := newTestServer(t) 750 defer ts.Stop() 751 752 nodes, clockSource := raftutils.NewRaftCluster(t, tc) 753 defer raftutils.TeardownCluster(nodes) 754 755 // Assign one of the raft node to the test server 756 ts.Server.raft = nodes[1].Node 757 ts.Server.store = nodes[1].MemoryStore() 758 759 // Create a node object for each of the managers 760 assert.NoError(t, nodes[1].MemoryStore().Update(func(tx store.Tx) error { 761 assert.NoError(t, store.CreateNode(tx, &api.Node{ 762 ID: nodes[1].SecurityConfig.ClientTLSCreds.NodeID(), 763 Spec: api.NodeSpec{ 764 DesiredRole: api.NodeRoleManager, 765 Membership: api.NodeMembershipAccepted, 766 }, 767 Role: api.NodeRoleManager, 768 })) 769 assert.NoError(t, store.CreateNode(tx, &api.Node{ 770 ID: nodes[2].SecurityConfig.ClientTLSCreds.NodeID(), 771 Spec: api.NodeSpec{ 772 DesiredRole: api.NodeRoleManager, 773 Membership: api.NodeMembershipAccepted, 774 }, 775 Role: api.NodeRoleManager, 776 })) 777 assert.NoError(t, store.CreateNode(tx, &api.Node{ 778 ID: nodes[3].SecurityConfig.ClientTLSCreds.NodeID(), 779 Spec: api.NodeSpec{ 780 DesiredRole: api.NodeRoleManager, 781 Membership: api.NodeMembershipAccepted, 782 }, 783 Role: api.NodeRoleManager, 784 })) 785 return nil 786 })) 787 788 // Stop Node 3 (1 node out of 3) 789 nodes[3].Server.Stop() 790 nodes[3].ShutdownRaft() 791 792 // Node 3 should be listed as Unreachable 793 assert.NoError(t, testutils.PollFunc(clockSource, func() error { 794 members := nodes[1].GetMemberlist() 795 if len(members) != 3 { 796 return fmt.Errorf("expected 3 nodes, got %d", len(members)) 797 } 798 if members[nodes[3].Config.ID].Status.Reachability == api.RaftMemberStatus_REACHABLE { 799 return fmt.Errorf("expected node 3 to be unreachable") 800 } 801 return nil 802 })) 803 804 // Try to demote Node 2, this should fail because of the quorum safeguard 805 r, err := ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: nodes[2].SecurityConfig.ClientTLSCreds.NodeID()}) 806 assert.NoError(t, err) 807 spec := r.Node.Spec.Copy() 808 spec.DesiredRole = api.NodeRoleWorker 809 version := &r.Node.Meta.Version 810 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{ 811 NodeID: nodes[2].SecurityConfig.ClientTLSCreds.NodeID(), 812 Spec: spec, 813 NodeVersion: version, 814 }) 815 assert.Error(t, err) 816 assert.Equal(t, codes.FailedPrecondition, testutils.ErrorCode(err)) 817 818 // Restart Node 3 819 nodes[3] = raftutils.RestartNode(t, clockSource, nodes[3], false) 820 raftutils.WaitForCluster(t, clockSource, nodes) 821 822 // Node 3 should be listed as Reachable 823 assert.NoError(t, testutils.PollFunc(clockSource, func() error { 824 members := nodes[1].GetMemberlist() 825 if len(members) != 3 { 826 return fmt.Errorf("expected 3 nodes, got %d", len(members)) 827 } 828 if members[nodes[3].Config.ID].Status.Reachability == api.RaftMemberStatus_UNREACHABLE { 829 return fmt.Errorf("expected node 3 to be reachable") 830 } 831 return nil 832 })) 833 834 raftMember := ts.Server.raft.GetMemberByNodeID(nodes[3].SecurityConfig.ClientTLSCreds.NodeID()) 835 assert.NotNil(t, raftMember) 836 837 // Try to demote Node 3, this should succeed 838 r, err = ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: nodes[3].SecurityConfig.ClientTLSCreds.NodeID()}) 839 assert.NoError(t, err) 840 spec = r.Node.Spec.Copy() 841 spec.DesiredRole = api.NodeRoleWorker 842 version = &r.Node.Meta.Version 843 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{ 844 NodeID: nodes[3].SecurityConfig.ClientTLSCreds.NodeID(), 845 Spec: spec, 846 NodeVersion: version, 847 }) 848 assert.NoError(t, err) 849 850 newCluster := map[uint64]*raftutils.TestNode{ 851 1: nodes[1], 852 2: nodes[2], 853 } 854 855 ts.Server.raft.RemoveMember(context.Background(), raftMember.RaftID) 856 857 raftutils.WaitForCluster(t, clockSource, newCluster) 858 859 // Server should list 2 members 860 assert.NoError(t, testutils.PollFunc(clockSource, func() error { 861 members := nodes[1].GetMemberlist() 862 if len(members) != 2 { 863 return fmt.Errorf("expected 2 nodes, got %d", len(members)) 864 } 865 return nil 866 })) 867 868 demoteNode := nodes[2] 869 lastNode := nodes[1] 870 871 raftMember = ts.Server.raft.GetMemberByNodeID(demoteNode.SecurityConfig.ClientTLSCreds.NodeID()) 872 assert.NotNil(t, raftMember) 873 874 // Try to demote a Node and scale down to 1 875 r, err = ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: demoteNode.SecurityConfig.ClientTLSCreds.NodeID()}) 876 assert.NoError(t, err) 877 spec = r.Node.Spec.Copy() 878 spec.DesiredRole = api.NodeRoleWorker 879 version = &r.Node.Meta.Version 880 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{ 881 NodeID: demoteNode.SecurityConfig.ClientTLSCreds.NodeID(), 882 Spec: spec, 883 NodeVersion: version, 884 }) 885 assert.NoError(t, err) 886 887 ts.Server.raft.RemoveMember(context.Background(), raftMember.RaftID) 888 889 // Update the server 890 ts.Server.raft = lastNode.Node 891 ts.Server.store = lastNode.MemoryStore() 892 893 newCluster = map[uint64]*raftutils.TestNode{ 894 1: lastNode, 895 } 896 897 raftutils.WaitForCluster(t, clockSource, newCluster) 898 899 assert.NoError(t, testutils.PollFunc(clockSource, func() error { 900 members := lastNode.GetMemberlist() 901 if len(members) != 1 { 902 return fmt.Errorf("expected 1 node, got %d", len(members)) 903 } 904 return nil 905 })) 906 907 // Make sure we can't demote the last manager. 908 r, err = ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: lastNode.SecurityConfig.ClientTLSCreds.NodeID()}) 909 assert.NoError(t, err) 910 spec = r.Node.Spec.Copy() 911 spec.DesiredRole = api.NodeRoleWorker 912 version = &r.Node.Meta.Version 913 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{ 914 NodeID: lastNode.SecurityConfig.ClientTLSCreds.NodeID(), 915 Spec: spec, 916 NodeVersion: version, 917 }) 918 assert.Error(t, err) 919 assert.Equal(t, codes.FailedPrecondition, testutils.ErrorCode(err)) 920 921 // Propose a change in the spec and check if the remaining node can still process updates 922 r, err = ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: lastNode.SecurityConfig.ClientTLSCreds.NodeID()}) 923 assert.NoError(t, err) 924 spec = r.Node.Spec.Copy() 925 spec.Availability = api.NodeAvailabilityDrain 926 version = &r.Node.Meta.Version 927 _, err = ts.Client.UpdateNode(context.Background(), &api.UpdateNodeRequest{ 928 NodeID: lastNode.SecurityConfig.ClientTLSCreds.NodeID(), 929 Spec: spec, 930 NodeVersion: version, 931 }) 932 assert.NoError(t, err) 933 934 // Get node information and check that the availability is set to drain 935 r, err = ts.Client.GetNode(context.Background(), &api.GetNodeRequest{NodeID: lastNode.SecurityConfig.ClientTLSCreds.NodeID()}) 936 assert.NoError(t, err) 937 assert.Equal(t, r.Node.Spec.Availability, api.NodeAvailabilityDrain) 938 } 939 940 func TestUpdateNodeDemote(t *testing.T) { 941 t.Parallel() 942 testUpdateNodeDemote(t) 943 } 944 945 // TestRemoveNodeAttachments tests the unexported orphanNodeTasks 946 func TestOrphanNodeTasks(t *testing.T) { 947 // first, set up a store and all that 948 ts := newTestServer(t) 949 defer ts.Stop() 950 951 ts.Store.Update(func(tx store.Tx) error { 952 store.CreateCluster(tx, &api.Cluster{ 953 ID: identity.NewID(), 954 Spec: api.ClusterSpec{ 955 Annotations: api.Annotations{ 956 Name: store.DefaultClusterName, 957 }, 958 }, 959 }) 960 return nil 961 }) 962 963 // make sure before we start that our server is in a good (empty) state 964 r, err := ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 965 assert.NoError(t, err) 966 assert.Empty(t, r.Nodes) 967 968 // create a manager 969 createNode(t, ts, "id1", api.NodeRoleManager, api.NodeMembershipAccepted, api.NodeStatus_READY) 970 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 971 assert.NoError(t, err) 972 assert.Len(t, r.Nodes, 1) 973 974 // create a worker. put it in the DOWN state, which is the state it will be 975 // in to remove it anyway 976 createNode(t, ts, "id2", api.NodeRoleWorker, api.NodeMembershipAccepted, api.NodeStatus_DOWN) 977 r, err = ts.Client.ListNodes(context.Background(), &api.ListNodesRequest{}) 978 assert.NoError(t, err) 979 assert.Len(t, r.Nodes, 2) 980 981 // create a network we can "attach" to 982 err = ts.Store.Update(func(tx store.Tx) error { 983 n := &api.Network{ 984 ID: "net1id", 985 Spec: api.NetworkSpec{ 986 Annotations: api.Annotations{ 987 Name: "net1name", 988 }, 989 Attachable: true, 990 }, 991 } 992 return store.CreateNetwork(tx, n) 993 }) 994 require.NoError(t, err) 995 996 // create some tasks: 997 err = ts.Store.Update(func(tx store.Tx) error { 998 // 1.) A network attachment on the node we're gonna remove 999 task1 := &api.Task{ 1000 ID: "task1", 1001 NodeID: "id2", 1002 DesiredState: api.TaskStateRunning, 1003 Status: api.TaskStatus{ 1004 State: api.TaskStateRunning, 1005 }, 1006 Spec: api.TaskSpec{ 1007 Runtime: &api.TaskSpec_Attachment{ 1008 Attachment: &api.NetworkAttachmentSpec{ 1009 ContainerID: "container1", 1010 }, 1011 }, 1012 Networks: []*api.NetworkAttachmentConfig{ 1013 { 1014 Target: "net1id", 1015 Addresses: []string{}, // just leave this empty, we don't need it 1016 }, 1017 }, 1018 }, 1019 // we probably don't care about the rest of the fields. 1020 } 1021 if err := store.CreateTask(tx, task1); err != nil { 1022 return err 1023 } 1024 1025 // 2.) A network attachment on the node we're not going to remove 1026 task2 := &api.Task{ 1027 ID: "task2", 1028 NodeID: "id1", 1029 DesiredState: api.TaskStateRunning, 1030 Status: api.TaskStatus{ 1031 State: api.TaskStateRunning, 1032 }, 1033 Spec: api.TaskSpec{ 1034 Runtime: &api.TaskSpec_Attachment{ 1035 Attachment: &api.NetworkAttachmentSpec{ 1036 ContainerID: "container2", 1037 }, 1038 }, 1039 Networks: []*api.NetworkAttachmentConfig{ 1040 { 1041 Target: "net1id", 1042 Addresses: []string{}, // just leave this empty, we don't need it 1043 }, 1044 }, 1045 }, 1046 // we probably don't care about the rest of the fields. 1047 } 1048 if err := store.CreateTask(tx, task2); err != nil { 1049 return err 1050 } 1051 1052 // 3.) A regular task on the node we're going to remove 1053 task3 := &api.Task{ 1054 ID: "task3", 1055 NodeID: "id2", 1056 DesiredState: api.TaskStateRunning, 1057 Status: api.TaskStatus{ 1058 State: api.TaskStateRunning, 1059 }, 1060 Spec: api.TaskSpec{ 1061 Runtime: &api.TaskSpec_Container{ 1062 Container: &api.ContainerSpec{}, 1063 }, 1064 }, 1065 } 1066 if err := store.CreateTask(tx, task3); err != nil { 1067 return err 1068 } 1069 1070 // 4.) A regular task on the node we're not going to remove 1071 task4 := &api.Task{ 1072 ID: "task4", 1073 NodeID: "id1", 1074 DesiredState: api.TaskStateRunning, 1075 Status: api.TaskStatus{ 1076 State: api.TaskStateRunning, 1077 }, 1078 Spec: api.TaskSpec{ 1079 Runtime: &api.TaskSpec_Container{ 1080 Container: &api.ContainerSpec{}, 1081 }, 1082 }, 1083 } 1084 if err := store.CreateTask(tx, task4); err != nil { 1085 return err 1086 } 1087 1088 // 5.) A regular task that's already in a terminal state on the node, 1089 // which does not need to be updated. 1090 task5 := &api.Task{ 1091 ID: "task5", 1092 NodeID: "id2", 1093 DesiredState: api.TaskStateRunning, 1094 Status: api.TaskStatus{ 1095 // use TaskStateCompleted, as this is the earliest terminal 1096 // state (this ensures we don't actually use <= instead of <) 1097 State: api.TaskStateCompleted, 1098 }, 1099 Spec: api.TaskSpec{ 1100 Runtime: &api.TaskSpec_Container{ 1101 Container: &api.ContainerSpec{}, 1102 }, 1103 }, 1104 } 1105 return store.CreateTask(tx, task5) 1106 }) 1107 require.NoError(t, err) 1108 1109 // Now, call the function with our nodeID. make sure it returns no error 1110 err = ts.Store.Update(func(tx store.Tx) error { 1111 return orphanNodeTasks(tx, "id2") 1112 }) 1113 require.NoError(t, err) 1114 1115 // Now, make sure only tasks 1 and 3, the tasks on the node we're deleting 1116 // removed, are removed 1117 ts.Store.View(func(tx store.ReadTx) { 1118 tasks, err := store.FindTasks(tx, store.All) 1119 require.NoError(t, err) 1120 require.Len(t, tasks, 5) 1121 // and the list should not contain task1 or task2 1122 for _, task := range tasks { 1123 require.NotNil(t, task) 1124 if task.ID == "task1" || task.ID == "task3" { 1125 require.Equal(t, task.Status.State, api.TaskStateOrphaned) 1126 } else { 1127 require.NotEqual(t, task.Status.State, api.TaskStateOrphaned) 1128 } 1129 } 1130 }) 1131 }