github.com/jmitchell/nomad@v0.1.3-0.20151007230021-7ab84c2862d8/client/client_test.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "net" 8 "os" 9 "path/filepath" 10 "reflect" 11 "sync/atomic" 12 "testing" 13 "time" 14 15 "github.com/hashicorp/nomad/client/config" 16 "github.com/hashicorp/nomad/nomad" 17 "github.com/hashicorp/nomad/nomad/mock" 18 "github.com/hashicorp/nomad/nomad/structs" 19 "github.com/hashicorp/nomad/testutil" 20 21 ctestutil "github.com/hashicorp/nomad/client/testutil" 22 ) 23 24 var nextPort uint32 = 16000 25 26 func getPort() int { 27 return int(atomic.AddUint32(&nextPort, 1)) 28 } 29 30 func testServer(t *testing.T, cb func(*nomad.Config)) (*nomad.Server, string) { 31 // Setup the default settings 32 config := nomad.DefaultConfig() 33 config.Build = "unittest" 34 config.DevMode = true 35 config.RPCAddr = &net.TCPAddr{ 36 IP: []byte{127, 0, 0, 1}, 37 Port: getPort(), 38 } 39 config.NodeName = fmt.Sprintf("Node %d", config.RPCAddr.Port) 40 41 // Tighten the Serf timing 42 config.SerfConfig.MemberlistConfig.BindAddr = "127.0.0.1" 43 config.SerfConfig.MemberlistConfig.BindPort = getPort() 44 config.SerfConfig.MemberlistConfig.SuspicionMult = 2 45 config.SerfConfig.MemberlistConfig.RetransmitMult = 2 46 config.SerfConfig.MemberlistConfig.ProbeTimeout = 50 * time.Millisecond 47 config.SerfConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond 48 config.SerfConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond 49 50 // Tighten the Raft timing 51 config.RaftConfig.LeaderLeaseTimeout = 20 * time.Millisecond 52 config.RaftConfig.HeartbeatTimeout = 40 * time.Millisecond 53 config.RaftConfig.ElectionTimeout = 40 * time.Millisecond 54 config.RaftConfig.StartAsLeader = true 55 config.RaftTimeout = 500 * time.Millisecond 56 57 // Invoke the callback if any 58 if cb != nil { 59 cb(config) 60 } 61 62 // Create server 63 server, err := nomad.NewServer(config) 64 if err != nil { 65 t.Fatalf("err: %v", err) 66 } 67 return server, config.RPCAddr.String() 68 } 69 70 func testClient(t *testing.T, cb func(c *config.Config)) *Client { 71 conf := DefaultConfig() 72 if cb != nil { 73 cb(conf) 74 } 75 conf.DevMode = true 76 77 client, err := NewClient(conf) 78 if err != nil { 79 t.Fatalf("err: %v", err) 80 } 81 return client 82 } 83 84 func TestClient_StartStop(t *testing.T) { 85 client := testClient(t, nil) 86 if err := client.Shutdown(); err != nil { 87 t.Fatalf("err: %v", err) 88 } 89 } 90 91 func TestClient_RPC(t *testing.T) { 92 s1, addr := testServer(t, nil) 93 defer s1.Shutdown() 94 95 c1 := testClient(t, func(c *config.Config) { 96 c.Servers = []string{addr} 97 }) 98 defer c1.Shutdown() 99 100 // RPC should succeed 101 testutil.WaitForResult(func() (bool, error) { 102 var out struct{} 103 err := c1.RPC("Status.Ping", struct{}{}, &out) 104 return err == nil, err 105 }, func(err error) { 106 t.Fatalf("err: %v", err) 107 }) 108 } 109 110 func TestClient_RPC_Passthrough(t *testing.T) { 111 s1, _ := testServer(t, nil) 112 defer s1.Shutdown() 113 114 c1 := testClient(t, func(c *config.Config) { 115 c.RPCHandler = s1 116 }) 117 defer c1.Shutdown() 118 119 // RPC should succeed 120 testutil.WaitForResult(func() (bool, error) { 121 var out struct{} 122 err := c1.RPC("Status.Ping", struct{}{}, &out) 123 return err == nil, err 124 }, func(err error) { 125 t.Fatalf("err: %v", err) 126 }) 127 } 128 129 func TestClient_Fingerprint(t *testing.T) { 130 c := testClient(t, nil) 131 defer c.Shutdown() 132 133 // Ensure kernel and arch are always present 134 node := c.Node() 135 if node.Attributes["kernel.name"] == "" { 136 t.Fatalf("missing kernel.name") 137 } 138 if node.Attributes["arch"] == "" { 139 t.Fatalf("missing arch") 140 } 141 } 142 143 func TestClient_Drivers(t *testing.T) { 144 ctestutil.ExecCompatible(t) 145 c := testClient(t, nil) 146 defer c.Shutdown() 147 148 node := c.Node() 149 if node.Attributes["driver.exec"] == "" { 150 t.Fatalf("missing exec driver") 151 } 152 } 153 154 func TestClient_Register(t *testing.T) { 155 s1, _ := testServer(t, nil) 156 defer s1.Shutdown() 157 testutil.WaitForLeader(t, s1.RPC) 158 159 c1 := testClient(t, func(c *config.Config) { 160 c.RPCHandler = s1 161 }) 162 defer c1.Shutdown() 163 164 req := structs.NodeSpecificRequest{ 165 NodeID: c1.Node().ID, 166 QueryOptions: structs.QueryOptions{Region: "global"}, 167 } 168 var out structs.SingleNodeResponse 169 170 // Register should succeed 171 testutil.WaitForResult(func() (bool, error) { 172 err := s1.RPC("Node.GetNode", &req, &out) 173 if err != nil { 174 return false, err 175 } 176 if out.Node == nil { 177 return false, fmt.Errorf("missing reg") 178 } 179 return out.Node.ID == req.NodeID, nil 180 }, func(err error) { 181 t.Fatalf("err: %v", err) 182 }) 183 } 184 185 func TestClient_Heartbeat(t *testing.T) { 186 s1, _ := testServer(t, func(c *nomad.Config) { 187 c.MinHeartbeatTTL = 50 * time.Millisecond 188 }) 189 defer s1.Shutdown() 190 testutil.WaitForLeader(t, s1.RPC) 191 192 c1 := testClient(t, func(c *config.Config) { 193 c.RPCHandler = s1 194 }) 195 defer c1.Shutdown() 196 197 req := structs.NodeSpecificRequest{ 198 NodeID: c1.Node().ID, 199 QueryOptions: structs.QueryOptions{Region: "global"}, 200 } 201 var out structs.SingleNodeResponse 202 203 // Register should succeed 204 testutil.WaitForResult(func() (bool, error) { 205 err := s1.RPC("Node.GetNode", &req, &out) 206 if err != nil { 207 return false, err 208 } 209 if out.Node == nil { 210 return false, fmt.Errorf("missing reg") 211 } 212 return out.Node.Status == structs.NodeStatusReady, nil 213 }, func(err error) { 214 t.Fatalf("err: %v", err) 215 }) 216 } 217 218 func TestClient_UpdateAllocStatus(t *testing.T) { 219 s1, _ := testServer(t, nil) 220 defer s1.Shutdown() 221 testutil.WaitForLeader(t, s1.RPC) 222 223 c1 := testClient(t, func(c *config.Config) { 224 c.RPCHandler = s1 225 }) 226 defer c1.Shutdown() 227 228 alloc := mock.Alloc() 229 alloc.NodeID = c1.Node().ID 230 231 state := s1.State() 232 state.UpsertAllocs(100, []*structs.Allocation{alloc}) 233 234 newAlloc := new(structs.Allocation) 235 *newAlloc = *alloc 236 newAlloc.ClientStatus = structs.AllocClientStatusRunning 237 238 err := c1.updateAllocStatus(newAlloc) 239 if err != nil { 240 t.Fatalf("err: %v", err) 241 } 242 243 out, err := state.AllocByID(alloc.ID) 244 if err != nil { 245 t.Fatalf("err: %v", err) 246 } 247 248 if out == nil || out.ClientStatus != structs.AllocClientStatusRunning { 249 t.Fatalf("bad: %#v", out) 250 } 251 } 252 253 func TestClient_WatchAllocs(t *testing.T) { 254 ctestutil.ExecCompatible(t) 255 s1, _ := testServer(t, nil) 256 defer s1.Shutdown() 257 testutil.WaitForLeader(t, s1.RPC) 258 259 c1 := testClient(t, func(c *config.Config) { 260 c.RPCHandler = s1 261 }) 262 defer c1.Shutdown() 263 264 // Create mock allocations 265 alloc1 := mock.Alloc() 266 alloc1.NodeID = c1.Node().ID 267 alloc2 := mock.Alloc() 268 alloc2.NodeID = c1.Node().ID 269 270 state := s1.State() 271 err := state.UpsertAllocs(100, 272 []*structs.Allocation{alloc1, alloc2}) 273 if err != nil { 274 t.Fatalf("err: %v", err) 275 } 276 277 // Both allocations should get registered 278 testutil.WaitForResult(func() (bool, error) { 279 c1.allocLock.RLock() 280 num := len(c1.allocs) 281 c1.allocLock.RUnlock() 282 return num == 2, nil 283 }, func(err error) { 284 t.Fatalf("err: %v", err) 285 }) 286 287 // Delete one allocation 288 err = state.DeleteEval(101, nil, []string{alloc1.ID}) 289 if err != nil { 290 t.Fatalf("err: %v", err) 291 } 292 293 // Update the other allocation 294 alloc2.DesiredStatus = structs.AllocDesiredStatusStop 295 err = state.UpsertAllocs(102, 296 []*structs.Allocation{alloc2}) 297 if err != nil { 298 t.Fatalf("err: %v", err) 299 } 300 301 // One allocations should get de-registered 302 testutil.WaitForResult(func() (bool, error) { 303 c1.allocLock.RLock() 304 num := len(c1.allocs) 305 c1.allocLock.RUnlock() 306 return num == 1, nil 307 }, func(err error) { 308 t.Fatalf("err: %v", err) 309 }) 310 311 // One allocations should get updated 312 testutil.WaitForResult(func() (bool, error) { 313 c1.allocLock.RLock() 314 ar := c1.allocs[alloc2.ID] 315 c1.allocLock.RUnlock() 316 return ar.Alloc().DesiredStatus == structs.AllocDesiredStatusStop, nil 317 }, func(err error) { 318 t.Fatalf("err: %v", err) 319 }) 320 } 321 322 /* 323 TODO: This test is disabled til a follow-up api changes the restore state interface. 324 The driver/executor interface will be changed from Open to Cleanup, in which 325 clean-up tears down previous allocs. 326 327 func TestClient_SaveRestoreState(t *testing.T) { 328 ctestutil.ExecCompatible(t) 329 s1, _ := testServer(t, nil) 330 defer s1.Shutdown() 331 testutil.WaitForLeader(t, s1.RPC) 332 333 c1 := testClient(t, func(c *config.Config) { 334 c.RPCHandler = s1 335 }) 336 defer c1.Shutdown() 337 338 // Create mock allocations 339 alloc1 := mock.Alloc() 340 alloc1.NodeID = c1.Node().ID 341 task := alloc1.Job.TaskGroups[0].Tasks[0] 342 task.Config["command"] = "/bin/sleep" 343 task.Config["args"] = "10" 344 345 state := s1.State() 346 err := state.UpsertAllocs(100, 347 []*structs.Allocation{alloc1}) 348 if err != nil { 349 t.Fatalf("err: %v", err) 350 } 351 352 // Allocations should get registered 353 testutil.WaitForResult(func() (bool, error) { 354 c1.allocLock.RLock() 355 num := len(c1.allocs) 356 c1.allocLock.RUnlock() 357 return num == 1, nil 358 }, func(err error) { 359 t.Fatalf("err: %v", err) 360 }) 361 362 // Shutdown the client, saves state 363 err = c1.Shutdown() 364 if err != nil { 365 t.Fatalf("err: %v", err) 366 } 367 368 // Create a new client 369 c2, err := NewClient(c1.config) 370 if err != nil { 371 t.Fatalf("err: %v", err) 372 } 373 defer c2.Shutdown() 374 375 // Ensure the allocation is running 376 c2.allocLock.RLock() 377 ar := c1.allocs[alloc1.ID] 378 c2.allocLock.RUnlock() 379 if ar.Alloc().ClientStatus != structs.AllocClientStatusRunning { 380 t.Fatalf("bad: %#v", ar.Alloc()) 381 } 382 } 383 */ 384 385 func TestClient_Init(t *testing.T) { 386 dir, err := ioutil.TempDir("", "nomad") 387 if err != nil { 388 t.Fatalf("err: %s", err) 389 } 390 defer os.RemoveAll(dir) 391 allocDir := filepath.Join(dir, "alloc") 392 393 client := &Client{ 394 config: &config.Config{ 395 AllocDir: allocDir, 396 }, 397 logger: log.New(os.Stderr, "", log.LstdFlags), 398 } 399 if err := client.init(); err != nil { 400 t.Fatalf("err: %s", err) 401 } 402 403 if _, err := os.Stat(allocDir); err != nil { 404 t.Fatalf("err: %s", err) 405 } 406 } 407 408 func TestClient_SetServers(t *testing.T) { 409 client := testClient(t, nil) 410 411 // Sets an empty list 412 client.SetServers(nil) 413 if client.servers == nil { 414 t.Fatalf("should not be nil") 415 } 416 417 // Set the initial servers list 418 expect := []string{"foo"} 419 client.SetServers(expect) 420 if !reflect.DeepEqual(client.servers, expect) { 421 t.Fatalf("expect %v, got %v", expect, client.servers) 422 } 423 424 // Add a server 425 expect = []string{"foo", "bar"} 426 client.SetServers(expect) 427 if !reflect.DeepEqual(client.servers, expect) { 428 t.Fatalf("expect %v, got %v", expect, client.servers) 429 } 430 431 // Remove a server 432 expect = []string{"bar"} 433 client.SetServers(expect) 434 if !reflect.DeepEqual(client.servers, expect) { 435 t.Fatalf("expect %v, got %v", expect, client.servers) 436 } 437 438 // Add and remove a server 439 expect = []string{"baz", "zip"} 440 client.SetServers(expect) 441 if !reflect.DeepEqual(client.servers, expect) { 442 t.Fatalf("expect %v, got %v", expect, client.servers) 443 } 444 445 // Query the servers list 446 if servers := client.Servers(); !reflect.DeepEqual(servers, expect) { 447 t.Fatalf("expect %v, got %v", expect, servers) 448 } 449 }