github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/drivers/java/driver_test.go (about) 1 package java 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "testing" 11 12 dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils" 13 14 "context" 15 "time" 16 17 ctestutil "github.com/hashicorp/nomad/client/testutil" 18 "github.com/hashicorp/nomad/helper/pluginutils/hclutils" 19 "github.com/hashicorp/nomad/helper/testlog" 20 "github.com/hashicorp/nomad/helper/uuid" 21 "github.com/hashicorp/nomad/nomad/structs" 22 "github.com/hashicorp/nomad/plugins/drivers" 23 "github.com/hashicorp/nomad/testutil" 24 "github.com/stretchr/testify/require" 25 ) 26 27 func javaCompatible(t *testing.T) { 28 ctestutil.JavaCompatible(t) 29 30 _, _, _, err := javaVersionInfo() 31 if err != nil { 32 t.Skipf("java not found; skipping: %v", err) 33 } 34 } 35 36 func TestJavaDriver_Fingerprint(t *testing.T) { 37 javaCompatible(t) 38 if !testutil.IsCI() { 39 t.Parallel() 40 } 41 42 ctx, cancel := context.WithCancel(context.Background()) 43 defer cancel() 44 45 d := NewDriver(ctx, testlog.HCLogger(t)) 46 harness := dtestutil.NewDriverHarness(t, d) 47 48 fpCh, err := harness.Fingerprint(context.Background()) 49 require.NoError(t, err) 50 51 select { 52 case fp := <-fpCh: 53 require.Equal(t, drivers.HealthStateHealthy, fp.Health) 54 detected, _ := fp.Attributes["driver.java"].GetBool() 55 require.True(t, detected) 56 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 57 require.Fail(t, "timeout receiving fingerprint") 58 } 59 } 60 61 func TestJavaDriver_Jar_Start_Wait(t *testing.T) { 62 javaCompatible(t) 63 if !testutil.IsCI() { 64 t.Parallel() 65 } 66 67 require := require.New(t) 68 ctx, cancel := context.WithCancel(context.Background()) 69 defer cancel() 70 71 d := NewDriver(ctx, testlog.HCLogger(t)) 72 harness := dtestutil.NewDriverHarness(t, d) 73 74 tc := &TaskConfig{ 75 JarPath: "demoapp.jar", 76 Args: []string{"1"}, 77 JvmOpts: []string{"-Xmx64m", "-Xms32m"}, 78 } 79 task := basicTask(t, "demo-app", tc) 80 81 cleanup := harness.MkAllocDir(task, true) 82 defer cleanup() 83 84 copyFile("./test-resources/demoapp.jar", filepath.Join(task.TaskDir().Dir, "demoapp.jar"), t) 85 86 handle, _, err := harness.StartTask(task) 87 require.NoError(err) 88 89 ch, err := harness.WaitTask(context.Background(), handle.Config.ID) 90 require.NoError(err) 91 result := <-ch 92 require.Nil(result.Err) 93 94 require.Zero(result.ExitCode) 95 96 // Get the stdout of the process and assert that it's not empty 97 stdout, err := ioutil.ReadFile(filepath.Join(task.TaskDir().LogDir, "demo-app.stdout.0")) 98 require.NoError(err) 99 require.Contains(string(stdout), "Hello") 100 101 require.NoError(harness.DestroyTask(task.ID, true)) 102 } 103 104 func TestJavaDriver_Jar_Stop_Wait(t *testing.T) { 105 javaCompatible(t) 106 if !testutil.IsCI() { 107 t.Parallel() 108 } 109 110 require := require.New(t) 111 ctx, cancel := context.WithCancel(context.Background()) 112 defer cancel() 113 114 d := NewDriver(ctx, testlog.HCLogger(t)) 115 harness := dtestutil.NewDriverHarness(t, d) 116 117 tc := &TaskConfig{ 118 JarPath: "demoapp.jar", 119 Args: []string{"600"}, 120 JvmOpts: []string{"-Xmx64m", "-Xms32m"}, 121 } 122 task := basicTask(t, "demo-app", tc) 123 124 cleanup := harness.MkAllocDir(task, true) 125 defer cleanup() 126 127 copyFile("./test-resources/demoapp.jar", filepath.Join(task.TaskDir().Dir, "demoapp.jar"), t) 128 129 handle, _, err := harness.StartTask(task) 130 require.NoError(err) 131 132 ch, err := harness.WaitTask(context.Background(), handle.Config.ID) 133 require.NoError(err) 134 135 require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second)) 136 137 go func() { 138 time.Sleep(10 * time.Millisecond) 139 harness.StopTask(task.ID, 2*time.Second, "SIGINT") 140 }() 141 142 select { 143 case result := <-ch: 144 require.False(result.Successful()) 145 case <-time.After(10 * time.Second): 146 require.Fail("timeout waiting for task to shutdown") 147 } 148 149 // Ensure that the task is marked as dead, but account 150 // for WaitTask() closing channel before internal state is updated 151 testutil.WaitForResult(func() (bool, error) { 152 status, err := harness.InspectTask(task.ID) 153 if err != nil { 154 return false, fmt.Errorf("inspecting task failed: %v", err) 155 } 156 if status.State != drivers.TaskStateExited { 157 return false, fmt.Errorf("task hasn't exited yet; status: %v", status.State) 158 } 159 160 return true, nil 161 }, func(err error) { 162 require.NoError(err) 163 }) 164 165 require.NoError(harness.DestroyTask(task.ID, true)) 166 } 167 168 func TestJavaDriver_Class_Start_Wait(t *testing.T) { 169 javaCompatible(t) 170 if !testutil.IsCI() { 171 t.Parallel() 172 } 173 174 require := require.New(t) 175 ctx, cancel := context.WithCancel(context.Background()) 176 defer cancel() 177 178 d := NewDriver(ctx, testlog.HCLogger(t)) 179 harness := dtestutil.NewDriverHarness(t, d) 180 181 tc := &TaskConfig{ 182 Class: "Hello", 183 Args: []string{"1"}, 184 } 185 task := basicTask(t, "demo-app", tc) 186 187 cleanup := harness.MkAllocDir(task, true) 188 defer cleanup() 189 190 copyFile("./test-resources/Hello.class", filepath.Join(task.TaskDir().Dir, "Hello.class"), t) 191 192 handle, _, err := harness.StartTask(task) 193 require.NoError(err) 194 195 ch, err := harness.WaitTask(context.Background(), handle.Config.ID) 196 require.NoError(err) 197 result := <-ch 198 require.Nil(result.Err) 199 200 require.Zero(result.ExitCode) 201 202 // Get the stdout of the process and assert that it's not empty 203 stdout, err := ioutil.ReadFile(filepath.Join(task.TaskDir().LogDir, "demo-app.stdout.0")) 204 require.NoError(err) 205 require.Contains(string(stdout), "Hello") 206 207 require.NoError(harness.DestroyTask(task.ID, true)) 208 } 209 210 func TestJavaCmdArgs(t *testing.T) { 211 cases := []struct { 212 name string 213 cfg TaskConfig 214 expected []string 215 }{ 216 { 217 "jar_path_full", 218 TaskConfig{ 219 JvmOpts: []string{"-Xmx512m", "-Xms128m"}, 220 JarPath: "/jar-path.jar", 221 Args: []string{"hello", "world"}, 222 }, 223 []string{"-Xmx512m", "-Xms128m", "-jar", "/jar-path.jar", "hello", "world"}, 224 }, 225 { 226 "class_full", 227 TaskConfig{ 228 JvmOpts: []string{"-Xmx512m", "-Xms128m"}, 229 Class: "ClassName", 230 ClassPath: "/classpath", 231 Args: []string{"hello", "world"}, 232 }, 233 []string{"-Xmx512m", "-Xms128m", "-cp", "/classpath", "ClassName", "hello", "world"}, 234 }, 235 { 236 "jar_path_slim", 237 TaskConfig{ 238 JarPath: "/jar-path.jar", 239 }, 240 []string{"-jar", "/jar-path.jar"}, 241 }, 242 { 243 "class_slim", 244 TaskConfig{ 245 Class: "ClassName", 246 }, 247 []string{"ClassName"}, 248 }, 249 } 250 251 for _, c := range cases { 252 t.Run(c.name, func(t *testing.T) { 253 found := javaCmdArgs(c.cfg) 254 require.Equal(t, c.expected, found) 255 }) 256 } 257 } 258 259 func TestJavaDriver_ExecTaskStreaming(t *testing.T) { 260 javaCompatible(t) 261 if !testutil.IsCI() { 262 t.Parallel() 263 } 264 265 require := require.New(t) 266 ctx, cancel := context.WithCancel(context.Background()) 267 defer cancel() 268 269 d := NewDriver(ctx, testlog.HCLogger(t)) 270 harness := dtestutil.NewDriverHarness(t, d) 271 defer harness.Kill() 272 273 tc := &TaskConfig{ 274 Class: "Hello", 275 Args: []string{"900"}, 276 } 277 task := basicTask(t, "demo-app", tc) 278 279 cleanup := harness.MkAllocDir(task, true) 280 defer cleanup() 281 282 copyFile("./test-resources/Hello.class", filepath.Join(task.TaskDir().Dir, "Hello.class"), t) 283 284 _, _, err := harness.StartTask(task) 285 require.NoError(err) 286 defer d.DestroyTask(task.ID, true) 287 288 dtestutil.ExecTaskStreamingConformanceTests(t, harness, task.ID) 289 290 } 291 func basicTask(t *testing.T, name string, taskConfig *TaskConfig) *drivers.TaskConfig { 292 t.Helper() 293 294 task := &drivers.TaskConfig{ 295 ID: uuid.Generate(), 296 Name: name, 297 Resources: &drivers.Resources{ 298 NomadResources: &structs.AllocatedTaskResources{ 299 Memory: structs.AllocatedMemoryResources{ 300 MemoryMB: 128, 301 }, 302 Cpu: structs.AllocatedCpuResources{ 303 CpuShares: 100, 304 }, 305 }, 306 LinuxResources: &drivers.LinuxResources{ 307 MemoryLimitBytes: 134217728, 308 CPUShares: 100, 309 }, 310 }, 311 } 312 313 require.NoError(t, task.EncodeConcreteDriverConfig(&taskConfig)) 314 return task 315 } 316 317 // copyFile moves an existing file to the destination 318 func copyFile(src, dst string, t *testing.T) { 319 in, err := os.Open(src) 320 if err != nil { 321 t.Fatalf("copying %v -> %v failed: %v", src, dst, err) 322 } 323 defer in.Close() 324 out, err := os.Create(dst) 325 if err != nil { 326 t.Fatalf("copying %v -> %v failed: %v", src, dst, err) 327 } 328 defer func() { 329 if err := out.Close(); err != nil { 330 t.Fatalf("copying %v -> %v failed: %v", src, dst, err) 331 } 332 }() 333 if _, err = io.Copy(out, in); err != nil { 334 t.Fatalf("copying %v -> %v failed: %v", src, dst, err) 335 } 336 } 337 338 func TestConfig_ParseAllHCL(t *testing.T) { 339 cfgStr := ` 340 config { 341 class = "java.main" 342 class_path = "/tmp/cp" 343 jar_path = "/tmp/jar.jar" 344 jvm_options = ["-Xmx600"] 345 args = ["arg1", "arg2"] 346 }` 347 348 expected := &TaskConfig{ 349 Class: "java.main", 350 ClassPath: "/tmp/cp", 351 JarPath: "/tmp/jar.jar", 352 JvmOpts: []string{"-Xmx600"}, 353 Args: []string{"arg1", "arg2"}, 354 } 355 356 var tc *TaskConfig 357 hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc) 358 359 require.EqualValues(t, expected, tc) 360 } 361 362 // Tests that a given DNSConfig properly configures dns 363 func Test_dnsConfig(t *testing.T) { 364 t.Parallel() 365 ctestutil.RequireRoot(t) 366 javaCompatible(t) 367 require := require.New(t) 368 ctx, cancel := context.WithCancel(context.Background()) 369 defer cancel() 370 371 d := NewDriver(ctx, testlog.HCLogger(t)) 372 harness := dtestutil.NewDriverHarness(t, d) 373 defer harness.Kill() 374 375 cases := []struct { 376 name string 377 cfg *drivers.DNSConfig 378 }{ 379 { 380 name: "nil DNSConfig", 381 }, 382 { 383 name: "basic", 384 cfg: &drivers.DNSConfig{ 385 Servers: []string{"1.1.1.1", "1.0.0.1"}, 386 }, 387 }, 388 { 389 name: "full", 390 cfg: &drivers.DNSConfig{ 391 Servers: []string{"1.1.1.1", "1.0.0.1"}, 392 Searches: []string{"local.test", "node.consul"}, 393 Options: []string{"ndots:2", "edns0"}, 394 }, 395 }, 396 } 397 398 for _, c := range cases { 399 tc := &TaskConfig{ 400 Class: "Hello", 401 Args: []string{"900"}, 402 } 403 task := basicTask(t, "demo-app", tc) 404 task.DNS = c.cfg 405 406 cleanup := harness.MkAllocDir(task, false) 407 defer cleanup() 408 409 _, _, err := harness.StartTask(task) 410 require.NoError(err) 411 defer d.DestroyTask(task.ID, true) 412 413 dtestutil.TestTaskDNSConfig(t, harness, task.ID, c.cfg) 414 } 415 416 } 417 418 func TestDriver_Config_validate(t *testing.T) { 419 for _, tc := range []struct { 420 pidMode, ipcMode string 421 exp error 422 }{ 423 {pidMode: "host", ipcMode: "host", exp: nil}, 424 {pidMode: "private", ipcMode: "host", exp: nil}, 425 {pidMode: "host", ipcMode: "private", exp: nil}, 426 {pidMode: "private", ipcMode: "private", exp: nil}, 427 {pidMode: "other", ipcMode: "private", exp: errors.New(`default_pid_mode must be "private" or "host", got "other"`)}, 428 {pidMode: "private", ipcMode: "other", exp: errors.New(`default_ipc_mode must be "private" or "host", got "other"`)}, 429 } { 430 require.Equal(t, tc.exp, (&Config{ 431 DefaultModePID: tc.pidMode, 432 DefaultModeIPC: tc.ipcMode, 433 }).validate()) 434 } 435 }