github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/client/driver/java_test.go (about) 1 package driver 2 3 import ( 4 "os" 5 "os/exec" 6 "path/filepath" 7 "runtime" 8 "strings" 9 "syscall" 10 "testing" 11 "time" 12 13 "github.com/hashicorp/nomad/client/config" 14 "github.com/hashicorp/nomad/nomad/structs" 15 "github.com/hashicorp/nomad/testutil" 16 "github.com/stretchr/testify/assert" 17 18 ctestutils "github.com/hashicorp/nomad/client/testutil" 19 ) 20 21 var ( 22 osJavaDriverSupport = map[string]bool{ 23 "linux": true, 24 } 25 ) 26 27 // javaLocated checks whether java is installed so we can run java stuff. 28 func javaLocated() bool { 29 _, err := exec.Command("java", "-version").CombinedOutput() 30 return err == nil 31 } 32 33 // The fingerprinter test should always pass, even if Java is not installed. 34 func TestJavaDriver_Fingerprint(t *testing.T) { 35 if !testutil.IsTravis() { 36 t.Parallel() 37 } 38 ctestutils.JavaCompatible(t) 39 task := &structs.Task{ 40 Name: "foo", 41 Driver: "java", 42 Resources: structs.DefaultResources(), 43 } 44 ctx := testDriverContexts(t, task) 45 defer ctx.AllocDir.Destroy() 46 d := NewJavaDriver(ctx.DriverCtx) 47 node := &structs.Node{ 48 Attributes: map[string]string{ 49 "unique.cgroup.mountpoint": "/sys/fs/cgroups", 50 }, 51 } 52 apply, err := d.Fingerprint(&config.Config{}, node) 53 if err != nil { 54 t.Fatalf("err: %v", err) 55 } 56 if apply != javaLocated() { 57 t.Fatalf("Fingerprinter should detect Java when it is installed") 58 } 59 if node.Attributes["driver.java"] != "1" { 60 if v, ok := osJavaDriverSupport[runtime.GOOS]; v && ok { 61 t.Fatalf("missing java driver") 62 } else { 63 t.Skipf("missing java driver, no OS support") 64 } 65 } 66 for _, key := range []string{"driver.java.version", "driver.java.runtime", "driver.java.vm"} { 67 if node.Attributes[key] == "" { 68 t.Fatalf("missing driver key (%s)", key) 69 } 70 } 71 } 72 73 func TestJavaDriver_StartOpen_Wait(t *testing.T) { 74 if !testutil.IsTravis() { 75 t.Parallel() 76 } 77 if !javaLocated() { 78 t.Skip("Java not found; skipping") 79 } 80 81 ctestutils.JavaCompatible(t) 82 task := &structs.Task{ 83 Name: "demo-app", 84 Driver: "java", 85 Config: map[string]interface{}{ 86 "jar_path": "demoapp.jar", 87 "jvm_options": []string{"-Xmx64m", "-Xms32m"}, 88 }, 89 LogConfig: &structs.LogConfig{ 90 MaxFiles: 10, 91 MaxFileSizeMB: 10, 92 }, 93 Resources: basicResources, 94 } 95 96 ctx := testDriverContexts(t, task) 97 defer ctx.AllocDir.Destroy() 98 d := NewJavaDriver(ctx.DriverCtx) 99 100 // Copy the test jar into the task's directory 101 dst := ctx.ExecCtx.TaskDir.Dir 102 copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t) 103 104 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 105 t.Fatalf("prestart err: %v", err) 106 } 107 resp, err := d.Start(ctx.ExecCtx, task) 108 if err != nil { 109 t.Fatalf("err: %v", err) 110 } 111 112 // Attempt to open 113 handle2, err := d.Open(ctx.ExecCtx, resp.Handle.ID()) 114 if err != nil { 115 t.Fatalf("err: %v", err) 116 } 117 if handle2 == nil { 118 t.Fatalf("missing handle") 119 } 120 121 time.Sleep(2 * time.Second) 122 123 // There is a race condition between the handle waiting and killing. One 124 // will return an error. 125 resp.Handle.Kill() 126 handle2.Kill() 127 } 128 129 func TestJavaDriver_Start_Wait(t *testing.T) { 130 if !testutil.IsTravis() { 131 t.Parallel() 132 } 133 if !javaLocated() { 134 t.Skip("Java not found; skipping") 135 } 136 137 ctestutils.JavaCompatible(t) 138 task := &structs.Task{ 139 Name: "demo-app", 140 Driver: "java", 141 Config: map[string]interface{}{ 142 "jar_path": "demoapp.jar", 143 "args": []string{"1"}, 144 }, 145 LogConfig: &structs.LogConfig{ 146 MaxFiles: 10, 147 MaxFileSizeMB: 10, 148 }, 149 Resources: basicResources, 150 } 151 152 ctx := testDriverContexts(t, task) 153 defer ctx.AllocDir.Destroy() 154 d := NewJavaDriver(ctx.DriverCtx) 155 156 // Copy the test jar into the task's directory 157 dst := ctx.ExecCtx.TaskDir.Dir 158 copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t) 159 160 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 161 t.Fatalf("prestart err: %v", err) 162 } 163 resp, err := d.Start(ctx.ExecCtx, task) 164 if err != nil { 165 t.Fatalf("err: %v", err) 166 } 167 168 // Task should terminate after 1 seconds 169 select { 170 case res := <-resp.Handle.WaitCh(): 171 if !res.Successful() { 172 t.Fatalf("err: %v", res.String()) 173 } 174 case <-time.After(5 * time.Second): 175 t.Fatalf("timeout") 176 } 177 178 // Get the stdout of the process and assrt that it's not empty 179 stdout := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "demo-app.stdout.0") 180 fInfo, err := os.Stat(stdout) 181 if err != nil { 182 t.Fatalf("failed to get stdout of process: %v", err) 183 } 184 if fInfo.Size() == 0 { 185 t.Fatalf("stdout of process is empty") 186 } 187 188 // need to kill long lived process 189 err = resp.Handle.Kill() 190 if err != nil { 191 t.Fatalf("Error: %s", err) 192 } 193 } 194 195 func TestJavaDriver_Start_Kill_Wait(t *testing.T) { 196 if !testutil.IsTravis() { 197 t.Parallel() 198 } 199 if !javaLocated() { 200 t.Skip("Java not found; skipping") 201 } 202 203 ctestutils.JavaCompatible(t) 204 task := &structs.Task{ 205 Name: "demo-app", 206 Driver: "java", 207 Config: map[string]interface{}{ 208 "jar_path": "demoapp.jar", 209 "args": []string{"5"}, 210 }, 211 LogConfig: &structs.LogConfig{ 212 MaxFiles: 10, 213 MaxFileSizeMB: 10, 214 }, 215 Resources: basicResources, 216 } 217 218 ctx := testDriverContexts(t, task) 219 defer ctx.AllocDir.Destroy() 220 d := NewJavaDriver(ctx.DriverCtx) 221 222 // Copy the test jar into the task's directory 223 dst := ctx.ExecCtx.TaskDir.Dir 224 copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t) 225 226 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 227 t.Fatalf("prestart err: %v", err) 228 } 229 resp, err := d.Start(ctx.ExecCtx, task) 230 if err != nil { 231 t.Fatalf("err: %v", err) 232 } 233 234 errCh := make(chan error, 1) 235 go func() { 236 time.Sleep(10 * time.Millisecond) 237 err := resp.Handle.Kill() 238 if err != nil { 239 errCh <- err 240 } 241 }() 242 243 // Task should terminate quickly 244 select { 245 case err := <-errCh: 246 t.Fatalf("err: %v", err) 247 case res := <-resp.Handle.WaitCh(): 248 if res.Successful() { 249 t.Fatal("should err") 250 } 251 case <-time.After(time.Duration(testutil.TestMultiplier()*10) * time.Second): 252 t.Fatalf("timeout") 253 254 // Need to kill long lived process 255 if err = resp.Handle.Kill(); err != nil { 256 t.Fatalf("Error: %s", err) 257 } 258 } 259 } 260 261 func TestJavaDriver_Signal(t *testing.T) { 262 if !testutil.IsTravis() { 263 t.Parallel() 264 } 265 if !javaLocated() { 266 t.Skip("Java not found; skipping") 267 } 268 269 ctestutils.JavaCompatible(t) 270 task := &structs.Task{ 271 Name: "demo-app", 272 Driver: "java", 273 Config: map[string]interface{}{ 274 "jar_path": "demoapp.jar", 275 "args": []string{"5"}, 276 }, 277 LogConfig: &structs.LogConfig{ 278 MaxFiles: 10, 279 MaxFileSizeMB: 10, 280 }, 281 Resources: basicResources, 282 } 283 284 ctx := testDriverContexts(t, task) 285 defer ctx.AllocDir.Destroy() 286 d := NewJavaDriver(ctx.DriverCtx) 287 288 // Copy the test jar into the task's directory 289 dst := ctx.ExecCtx.TaskDir.Dir 290 copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t) 291 292 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 293 t.Fatalf("prestart err: %v", err) 294 } 295 resp, err := d.Start(ctx.ExecCtx, task) 296 if err != nil { 297 t.Fatalf("err: %v", err) 298 } 299 300 errCh := make(chan error, 1) 301 go func() { 302 time.Sleep(10 * time.Millisecond) 303 err := resp.Handle.Signal(syscall.SIGHUP) 304 if err != nil { 305 errCh <- err 306 } 307 }() 308 309 // Task should terminate quickly 310 select { 311 case err := <-errCh: 312 t.Fatalf("err: %v", err) 313 case res := <-resp.Handle.WaitCh(): 314 if res.Successful() { 315 t.Fatal("should err") 316 } 317 case <-time.After(time.Duration(testutil.TestMultiplier()*10) * time.Second): 318 t.Fatalf("timeout") 319 320 // Need to kill long lived process 321 if err = resp.Handle.Kill(); err != nil { 322 t.Fatalf("Error: %s", err) 323 } 324 } 325 } 326 327 func TestJavaDriver_User(t *testing.T) { 328 if !testutil.IsTravis() { 329 t.Parallel() 330 } 331 if !javaLocated() { 332 t.Skip("Java not found; skipping") 333 } 334 if runtime.GOOS != "linux" { 335 t.Skip("Linux only test") 336 } 337 338 ctestutils.JavaCompatible(t) 339 task := &structs.Task{ 340 Name: "demo-app", 341 Driver: "java", 342 User: "alice", 343 Config: map[string]interface{}{ 344 "jar_path": "demoapp.jar", 345 }, 346 LogConfig: &structs.LogConfig{ 347 MaxFiles: 10, 348 MaxFileSizeMB: 10, 349 }, 350 Resources: basicResources, 351 } 352 353 ctx := testDriverContexts(t, task) 354 defer ctx.AllocDir.Destroy() 355 d := NewJavaDriver(ctx.DriverCtx) 356 357 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 358 t.Fatalf("prestart err: %v", err) 359 } 360 resp, err := d.Start(ctx.ExecCtx, task) 361 if err == nil { 362 resp.Handle.Kill() 363 t.Fatalf("Should've failed") 364 } 365 msg := "user alice" 366 if !strings.Contains(err.Error(), msg) { 367 t.Fatalf("Expecting '%v' in '%v'", msg, err) 368 } 369 } 370 371 func TestJavaDriver_Start_Wait_Class(t *testing.T) { 372 if !testutil.IsTravis() { 373 t.Parallel() 374 } 375 if !javaLocated() { 376 t.Skip("Java not found; skipping") 377 } 378 379 ctestutils.JavaCompatible(t) 380 task := &structs.Task{ 381 Name: "demo-app", 382 Driver: "java", 383 Config: map[string]interface{}{ 384 "class_path": "${NOMAD_TASK_DIR}", 385 "class": "Hello", 386 "args": []string{"1"}, 387 }, 388 LogConfig: &structs.LogConfig{ 389 MaxFiles: 10, 390 MaxFileSizeMB: 10, 391 }, 392 Resources: basicResources, 393 } 394 395 ctx := testDriverContexts(t, task) 396 defer ctx.AllocDir.Destroy() 397 d := NewJavaDriver(ctx.DriverCtx) 398 399 // Copy the test jar into the task's directory 400 dst := ctx.ExecCtx.TaskDir.LocalDir 401 copyFile("./test-resources/java/Hello.class", filepath.Join(dst, "Hello.class"), t) 402 403 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 404 t.Fatalf("prestart err: %v", err) 405 } 406 resp, err := d.Start(ctx.ExecCtx, task) 407 if err != nil { 408 t.Fatalf("err: %v", err) 409 } 410 411 // Task should terminate after 1 seconds 412 select { 413 case res := <-resp.Handle.WaitCh(): 414 if !res.Successful() { 415 t.Fatalf("err: %v", res.String()) 416 } 417 case <-time.After(5 * time.Second): 418 t.Fatalf("timeout") 419 } 420 421 // Get the stdout of the process and assrt that it's not empty 422 stdout := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "demo-app.stdout.0") 423 fInfo, err := os.Stat(stdout) 424 if err != nil { 425 t.Fatalf("failed to get stdout of process: %v", err) 426 } 427 if fInfo.Size() == 0 { 428 t.Fatalf("stdout of process is empty") 429 } 430 431 // need to kill long lived process 432 if err := resp.Handle.Kill(); err != nil { 433 t.Fatalf("Error: %s", err) 434 } 435 } 436 437 func TestJavaDriver_Start_Kill(t *testing.T) { 438 assert := assert.New(t) 439 440 if !testutil.IsTravis() { 441 t.Parallel() 442 } 443 if !javaLocated() { 444 t.Skip("Java not found; skipping") 445 } 446 447 // Test that a valid kill signal will successfully stop the process 448 { 449 ctestutils.JavaCompatible(t) 450 task := &structs.Task{ 451 Name: "demo-app", 452 Driver: "java", 453 KillSignal: "SIGKILL", 454 Config: map[string]interface{}{ 455 "jar_path": "demoapp.jar", 456 "args": []string{"5"}, 457 }, 458 LogConfig: &structs.LogConfig{ 459 MaxFiles: 10, 460 MaxFileSizeMB: 10, 461 }, 462 Resources: basicResources, 463 } 464 465 ctx := testDriverContexts(t, task) 466 defer ctx.AllocDir.Destroy() 467 d := NewJavaDriver(ctx.DriverCtx) 468 469 // Copy the test jar into the task's directory 470 dst := ctx.ExecCtx.TaskDir.Dir 471 copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t) 472 473 _, err := d.Prestart(ctx.ExecCtx, task) 474 assert.Nil(err) 475 476 resp, err := d.Start(ctx.ExecCtx, task) 477 assert.Nil(err) 478 479 assert.NotNil(resp.Handle) 480 err = resp.Handle.Kill() 481 assert.Nil(err) 482 } 483 484 // Test that an unsupported kill signal will return an error 485 { 486 ctestutils.JavaCompatible(t) 487 task := &structs.Task{ 488 Name: "demo-app", 489 Driver: "java", 490 KillSignal: "ABCDEF", 491 Config: map[string]interface{}{ 492 "jar_path": "demoapp.jar", 493 "args": []string{"5"}, 494 }, 495 LogConfig: &structs.LogConfig{ 496 MaxFiles: 10, 497 MaxFileSizeMB: 10, 498 }, 499 Resources: basicResources, 500 } 501 502 ctx := testDriverContexts(t, task) 503 defer ctx.AllocDir.Destroy() 504 d := NewJavaDriver(ctx.DriverCtx) 505 506 // Copy the test jar into the task's directory 507 dst := ctx.ExecCtx.TaskDir.Dir 508 copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t) 509 510 _, err := d.Prestart(ctx.ExecCtx, task) 511 assert.Nil(err) 512 513 _, err = d.Start(ctx.ExecCtx, task) 514 assert.NotNil(err) 515 assert.Contains(err.Error(), "Signal ABCDEF is not supported") 516 } 517 }