github.com/djenriquez/nomad-1@v0.8.1/command/agent/node_endpoint_test.go (about) 1 package agent 2 3 import ( 4 "net/http" 5 "net/http/httptest" 6 "testing" 7 "time" 8 9 "github.com/hashicorp/nomad/api" 10 "github.com/hashicorp/nomad/nomad/mock" 11 "github.com/hashicorp/nomad/nomad/structs" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestHTTP_NodesList(t *testing.T) { 17 t.Parallel() 18 httpTest(t, nil, func(s *TestAgent) { 19 for i := 0; i < 3; i++ { 20 // Create the node 21 node := mock.Node() 22 args := structs.NodeRegisterRequest{ 23 Node: node, 24 WriteRequest: structs.WriteRequest{Region: "global"}, 25 } 26 var resp structs.NodeUpdateResponse 27 if err := s.Agent.RPC("Node.Register", &args, &resp); err != nil { 28 t.Fatalf("err: %v", err) 29 } 30 } 31 32 // Make the HTTP request 33 req, err := http.NewRequest("GET", "/v1/nodes", nil) 34 if err != nil { 35 t.Fatalf("err: %v", err) 36 } 37 respW := httptest.NewRecorder() 38 39 // Make the request 40 obj, err := s.Server.NodesRequest(respW, req) 41 if err != nil { 42 t.Fatalf("err: %v", err) 43 } 44 45 // Check for the index 46 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 47 t.Fatalf("missing index") 48 } 49 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 50 t.Fatalf("missing known leader") 51 } 52 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 53 t.Fatalf("missing last contact") 54 } 55 56 // Check the nodes 57 n := obj.([]*structs.NodeListStub) 58 if len(n) < 3 { // Maybe 4 including client 59 t.Fatalf("bad: %#v", n) 60 } 61 }) 62 } 63 64 func TestHTTP_NodesPrefixList(t *testing.T) { 65 t.Parallel() 66 httpTest(t, nil, func(s *TestAgent) { 67 ids := []string{ 68 "12345678-abcd-efab-cdef-123456789abc", 69 "12345678-aaaa-efab-cdef-123456789abc", 70 "1234aaaa-abcd-efab-cdef-123456789abc", 71 "1234bbbb-abcd-efab-cdef-123456789abc", 72 "1234cccc-abcd-efab-cdef-123456789abc", 73 "1234dddd-abcd-efab-cdef-123456789abc", 74 } 75 for i := 0; i < 5; i++ { 76 // Create the node 77 node := mock.Node() 78 node.ID = ids[i] 79 args := structs.NodeRegisterRequest{ 80 Node: node, 81 WriteRequest: structs.WriteRequest{Region: "global"}, 82 } 83 var resp structs.NodeUpdateResponse 84 if err := s.Agent.RPC("Node.Register", &args, &resp); err != nil { 85 t.Fatalf("err: %v", err) 86 } 87 } 88 89 // Make the HTTP request 90 req, err := http.NewRequest("GET", "/v1/nodes?prefix=12345678", nil) 91 if err != nil { 92 t.Fatalf("err: %v", err) 93 } 94 respW := httptest.NewRecorder() 95 96 // Make the request 97 obj, err := s.Server.NodesRequest(respW, req) 98 if err != nil { 99 t.Fatalf("err: %v", err) 100 } 101 102 // Check for the index 103 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 104 t.Fatalf("missing index") 105 } 106 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 107 t.Fatalf("missing known leader") 108 } 109 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 110 t.Fatalf("missing last contact") 111 } 112 113 // Check the nodes 114 n := obj.([]*structs.NodeListStub) 115 if len(n) != 2 { 116 t.Fatalf("bad: %#v", n) 117 } 118 }) 119 } 120 121 func TestHTTP_NodeForceEval(t *testing.T) { 122 t.Parallel() 123 httpTest(t, nil, func(s *TestAgent) { 124 // Create the node 125 node := mock.Node() 126 args := structs.NodeRegisterRequest{ 127 Node: node, 128 WriteRequest: structs.WriteRequest{Region: "global"}, 129 } 130 var resp structs.NodeUpdateResponse 131 if err := s.Agent.RPC("Node.Register", &args, &resp); err != nil { 132 t.Fatalf("err: %v", err) 133 } 134 135 // Directly manipulate the state 136 state := s.Agent.server.State() 137 alloc1 := mock.Alloc() 138 alloc1.NodeID = node.ID 139 if err := state.UpsertJobSummary(999, mock.JobSummary(alloc1.JobID)); err != nil { 140 t.Fatal(err) 141 } 142 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) 143 if err != nil { 144 t.Fatalf("err: %v", err) 145 } 146 147 // Make the HTTP request 148 req, err := http.NewRequest("POST", "/v1/node/"+node.ID+"/evaluate", nil) 149 if err != nil { 150 t.Fatalf("err: %v", err) 151 } 152 respW := httptest.NewRecorder() 153 154 // Make the request 155 obj, err := s.Server.NodeSpecificRequest(respW, req) 156 if err != nil { 157 t.Fatalf("err: %v", err) 158 } 159 160 // Check for the index 161 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 162 t.Fatalf("missing index") 163 } 164 165 // Check the response 166 upd := obj.(structs.NodeUpdateResponse) 167 if len(upd.EvalIDs) == 0 { 168 t.Fatalf("bad: %v", upd) 169 } 170 }) 171 } 172 173 func TestHTTP_NodeAllocations(t *testing.T) { 174 t.Parallel() 175 httpTest(t, nil, func(s *TestAgent) { 176 // Create the job 177 node := mock.Node() 178 args := structs.NodeRegisterRequest{ 179 Node: node, 180 WriteRequest: structs.WriteRequest{Region: "global"}, 181 } 182 var resp structs.NodeUpdateResponse 183 if err := s.Agent.RPC("Node.Register", &args, &resp); err != nil { 184 t.Fatalf("err: %v", err) 185 } 186 187 // Directly manipulate the state 188 state := s.Agent.server.State() 189 alloc1 := mock.Alloc() 190 alloc1.NodeID = node.ID 191 if err := state.UpsertJobSummary(999, mock.JobSummary(alloc1.JobID)); err != nil { 192 t.Fatal(err) 193 } 194 // Create a test event for the allocation 195 testEvent := structs.NewTaskEvent(structs.TaskStarted) 196 var events []*structs.TaskEvent 197 events = append(events, testEvent) 198 taskState := &structs.TaskState{Events: events} 199 alloc1.TaskStates = make(map[string]*structs.TaskState) 200 alloc1.TaskStates["test"] = taskState 201 202 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) 203 if err != nil { 204 t.Fatalf("err: %v", err) 205 } 206 207 // Make the HTTP request 208 req, err := http.NewRequest("GET", "/v1/node/"+node.ID+"/allocations", nil) 209 if err != nil { 210 t.Fatalf("err: %v", err) 211 } 212 respW := httptest.NewRecorder() 213 214 // Make the request 215 obj, err := s.Server.NodeSpecificRequest(respW, req) 216 if err != nil { 217 t.Fatalf("err: %v", err) 218 } 219 220 // Check for the index 221 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 222 t.Fatalf("missing index") 223 } 224 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 225 t.Fatalf("missing known leader") 226 } 227 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 228 t.Fatalf("missing last contact") 229 } 230 231 // Check the node 232 allocs := obj.([]*structs.Allocation) 233 if len(allocs) != 1 || allocs[0].ID != alloc1.ID { 234 t.Fatalf("bad: %#v", allocs) 235 } 236 expectedDisplayMsg := "Task started by client" 237 displayMsg := allocs[0].TaskStates["test"].Events[0].DisplayMessage 238 assert.Equal(t, expectedDisplayMsg, displayMsg) 239 }) 240 } 241 242 func TestHTTP_NodeDrain(t *testing.T) { 243 t.Parallel() 244 require := require.New(t) 245 httpTest(t, nil, func(s *TestAgent) { 246 // Create the node 247 node := mock.Node() 248 args := structs.NodeRegisterRequest{ 249 Node: node, 250 WriteRequest: structs.WriteRequest{Region: "global"}, 251 } 252 var resp structs.NodeUpdateResponse 253 require.Nil(s.Agent.RPC("Node.Register", &args, &resp)) 254 255 drainReq := api.NodeUpdateDrainRequest{ 256 NodeID: node.ID, 257 DrainSpec: &api.DrainSpec{ 258 Deadline: 10 * time.Second, 259 }, 260 } 261 262 // Make the HTTP request 263 buf := encodeReq(drainReq) 264 req, err := http.NewRequest("POST", "/v1/node/"+node.ID+"/drain", buf) 265 require.Nil(err) 266 respW := httptest.NewRecorder() 267 268 // Make the request 269 obj, err := s.Server.NodeSpecificRequest(respW, req) 270 require.Nil(err) 271 272 // Check for the index 273 require.NotZero(respW.HeaderMap.Get("X-Nomad-Index")) 274 275 // Check the response 276 _, ok := obj.(structs.NodeDrainUpdateResponse) 277 require.True(ok) 278 279 // Check that the node has been updated 280 state := s.Agent.server.State() 281 out, err := state.NodeByID(nil, node.ID) 282 require.Nil(err) 283 require.True(out.Drain) 284 require.NotNil(out.DrainStrategy) 285 require.Equal(10*time.Second, out.DrainStrategy.Deadline) 286 287 // Make the HTTP request to unset drain 288 drainReq.DrainSpec = nil 289 buf = encodeReq(drainReq) 290 req, err = http.NewRequest("POST", "/v1/node/"+node.ID+"/drain", buf) 291 require.Nil(err) 292 respW = httptest.NewRecorder() 293 294 // Make the request 295 _, err = s.Server.NodeSpecificRequest(respW, req) 296 require.Nil(err) 297 298 out, err = state.NodeByID(nil, node.ID) 299 require.Nil(err) 300 require.False(out.Drain) 301 require.Nil(out.DrainStrategy) 302 }) 303 } 304 305 func TestHTTP_NodeEligible(t *testing.T) { 306 t.Parallel() 307 require := require.New(t) 308 httpTest(t, nil, func(s *TestAgent) { 309 // Create the node 310 node := mock.Node() 311 args := structs.NodeRegisterRequest{ 312 Node: node, 313 WriteRequest: structs.WriteRequest{Region: "global"}, 314 } 315 var resp structs.NodeUpdateResponse 316 require.Nil(s.Agent.RPC("Node.Register", &args, &resp)) 317 318 drainReq := api.NodeUpdateEligibilityRequest{ 319 NodeID: node.ID, 320 Eligibility: structs.NodeSchedulingIneligible, 321 } 322 323 // Make the HTTP request 324 buf := encodeReq(drainReq) 325 req, err := http.NewRequest("POST", "/v1/node/"+node.ID+"/eligibility", buf) 326 require.Nil(err) 327 respW := httptest.NewRecorder() 328 329 // Make the request 330 obj, err := s.Server.NodeSpecificRequest(respW, req) 331 require.Nil(err) 332 333 // Check for the index 334 require.NotZero(respW.HeaderMap.Get("X-Nomad-Index")) 335 336 // Check the response 337 _, ok := obj.(structs.NodeEligibilityUpdateResponse) 338 require.True(ok) 339 340 // Check that the node has been updated 341 state := s.Agent.server.State() 342 out, err := state.NodeByID(nil, node.ID) 343 require.Nil(err) 344 require.Equal(structs.NodeSchedulingIneligible, out.SchedulingEligibility) 345 346 // Make the HTTP request to set something invalid 347 drainReq.Eligibility = "foo" 348 buf = encodeReq(drainReq) 349 req, err = http.NewRequest("POST", "/v1/node/"+node.ID+"/eligibility", buf) 350 require.Nil(err) 351 respW = httptest.NewRecorder() 352 353 // Make the request 354 _, err = s.Server.NodeSpecificRequest(respW, req) 355 require.NotNil(err) 356 require.Contains(err.Error(), "invalid") 357 }) 358 } 359 360 func TestHTTP_NodePurge(t *testing.T) { 361 t.Parallel() 362 httpTest(t, nil, func(s *TestAgent) { 363 // Create the node 364 node := mock.Node() 365 args := structs.NodeRegisterRequest{ 366 Node: node, 367 WriteRequest: structs.WriteRequest{Region: "global"}, 368 } 369 var resp structs.NodeUpdateResponse 370 if err := s.Agent.RPC("Node.Register", &args, &resp); err != nil { 371 t.Fatalf("err: %v", err) 372 } 373 374 // Add some allocations to the node 375 state := s.Agent.server.State() 376 alloc1 := mock.Alloc() 377 alloc1.NodeID = node.ID 378 if err := state.UpsertJobSummary(999, mock.JobSummary(alloc1.JobID)); err != nil { 379 t.Fatal(err) 380 } 381 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) 382 if err != nil { 383 t.Fatalf("err: %v", err) 384 } 385 386 // Make the HTTP request to purge it 387 req, err := http.NewRequest("POST", "/v1/node/"+node.ID+"/purge", nil) 388 if err != nil { 389 t.Fatalf("err: %v", err) 390 } 391 respW := httptest.NewRecorder() 392 393 // Make the request 394 obj, err := s.Server.NodeSpecificRequest(respW, req) 395 if err != nil { 396 t.Fatalf("err: %v", err) 397 } 398 399 // Check for the index 400 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 401 t.Fatalf("missing index") 402 } 403 404 // Check the response 405 upd := obj.(structs.NodeUpdateResponse) 406 if len(upd.EvalIDs) == 0 { 407 t.Fatalf("bad: %v", upd) 408 } 409 410 // Ensure that the node is not present anymore 411 args1 := structs.NodeSpecificRequest{ 412 NodeID: node.ID, 413 QueryOptions: structs.QueryOptions{Region: "global"}, 414 } 415 var resp1 structs.SingleNodeResponse 416 if err := s.Agent.RPC("Node.GetNode", &args1, &resp1); err != nil { 417 t.Fatalf("err: %v", err) 418 } 419 if resp1.Node != nil { 420 t.Fatalf("node still exists after purging: %#v", resp1.Node) 421 } 422 }) 423 } 424 425 func TestHTTP_NodeQuery(t *testing.T) { 426 t.Parallel() 427 httpTest(t, nil, func(s *TestAgent) { 428 // Create the job 429 node := mock.Node() 430 args := structs.NodeRegisterRequest{ 431 Node: node, 432 WriteRequest: structs.WriteRequest{Region: "global"}, 433 } 434 var resp structs.NodeUpdateResponse 435 if err := s.Agent.RPC("Node.Register", &args, &resp); err != nil { 436 t.Fatalf("err: %v", err) 437 } 438 439 // Make the HTTP request 440 req, err := http.NewRequest("GET", "/v1/node/"+node.ID, nil) 441 if err != nil { 442 t.Fatalf("err: %v", err) 443 } 444 respW := httptest.NewRecorder() 445 446 // Make the request 447 obj, err := s.Server.NodeSpecificRequest(respW, req) 448 if err != nil { 449 t.Fatalf("err: %v", err) 450 } 451 452 // Check for the index 453 if respW.HeaderMap.Get("X-Nomad-Index") == "" { 454 t.Fatalf("missing index") 455 } 456 if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" { 457 t.Fatalf("missing known leader") 458 } 459 if respW.HeaderMap.Get("X-Nomad-LastContact") == "" { 460 t.Fatalf("missing last contact") 461 } 462 463 // Check the node 464 n := obj.(*structs.Node) 465 if n.ID != node.ID { 466 t.Fatalf("bad: %#v", n) 467 } 468 if len(n.Events) < 1 { 469 t.Fatalf("Expected node registration event to be populated: %#v", n) 470 } 471 if n.Events[0].Message != "Node Registered" { 472 t.Fatalf("Expected node registration event to be first node event: %#v", n) 473 } 474 }) 475 }