github.com/smithx10/nomad@v0.9.1-rc1/client/taskenv/env_test.go (about) 1 package taskenv 2 3 import ( 4 "fmt" 5 "os" 6 "reflect" 7 "sort" 8 "strings" 9 "testing" 10 11 "github.com/hashicorp/hcl2/gohcl" 12 "github.com/hashicorp/hcl2/hcl" 13 "github.com/hashicorp/hcl2/hcl/hclsyntax" 14 "github.com/hashicorp/nomad/nomad/mock" 15 "github.com/hashicorp/nomad/nomad/structs" 16 "github.com/hashicorp/nomad/plugins/drivers" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 const ( 22 // Node values that tests can rely on 23 metaKey = "instance" 24 metaVal = "t2-micro" 25 attrKey = "arch" 26 attrVal = "amd64" 27 nodeName = "test node" 28 nodeClass = "test class" 29 30 // Environment variable values that tests can rely on 31 envOneKey = "NOMAD_IP" 32 envOneVal = "127.0.0.1" 33 envTwoKey = "NOMAD_PORT_WEB" 34 envTwoVal = ":80" 35 ) 36 37 var ( 38 // portMap for use in tests as its set after Builder creation 39 portMap = map[string]int{ 40 "https": 443, 41 } 42 ) 43 44 func testEnvBuilder() *Builder { 45 n := mock.Node() 46 n.Attributes = map[string]string{ 47 attrKey: attrVal, 48 } 49 n.Meta = map[string]string{ 50 metaKey: metaVal, 51 } 52 n.Name = nodeName 53 n.NodeClass = nodeClass 54 55 task := mock.Job().TaskGroups[0].Tasks[0] 56 task.Env = map[string]string{ 57 envOneKey: envOneVal, 58 envTwoKey: envTwoVal, 59 } 60 return NewBuilder(n, mock.Alloc(), task, "global") 61 } 62 63 func TestEnvironment_ParseAndReplace_Env(t *testing.T) { 64 env := testEnvBuilder() 65 66 input := []string{fmt.Sprintf(`"${%v}"!`, envOneKey), fmt.Sprintf("${%s}${%s}", envOneKey, envTwoKey)} 67 act := env.Build().ParseAndReplace(input) 68 exp := []string{fmt.Sprintf(`"%s"!`, envOneVal), fmt.Sprintf("%s%s", envOneVal, envTwoVal)} 69 70 if !reflect.DeepEqual(act, exp) { 71 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 72 } 73 } 74 75 func TestEnvironment_ParseAndReplace_Meta(t *testing.T) { 76 input := []string{fmt.Sprintf("${%v%v}", nodeMetaPrefix, metaKey)} 77 exp := []string{metaVal} 78 env := testEnvBuilder() 79 act := env.Build().ParseAndReplace(input) 80 81 if !reflect.DeepEqual(act, exp) { 82 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 83 } 84 } 85 86 func TestEnvironment_ParseAndReplace_Attr(t *testing.T) { 87 input := []string{fmt.Sprintf("${%v%v}", nodeAttributePrefix, attrKey)} 88 exp := []string{attrVal} 89 env := testEnvBuilder() 90 act := env.Build().ParseAndReplace(input) 91 92 if !reflect.DeepEqual(act, exp) { 93 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 94 } 95 } 96 97 func TestEnvironment_ParseAndReplace_Node(t *testing.T) { 98 input := []string{fmt.Sprintf("${%v}", nodeNameKey), fmt.Sprintf("${%v}", nodeClassKey)} 99 exp := []string{nodeName, nodeClass} 100 env := testEnvBuilder() 101 act := env.Build().ParseAndReplace(input) 102 103 if !reflect.DeepEqual(act, exp) { 104 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 105 } 106 } 107 108 func TestEnvironment_ParseAndReplace_Mixed(t *testing.T) { 109 input := []string{ 110 fmt.Sprintf("${%v}${%v%v}", nodeNameKey, nodeAttributePrefix, attrKey), 111 fmt.Sprintf("${%v}${%v%v}", nodeClassKey, nodeMetaPrefix, metaKey), 112 fmt.Sprintf("${%v}${%v}", envTwoKey, nodeClassKey), 113 } 114 exp := []string{ 115 fmt.Sprintf("%v%v", nodeName, attrVal), 116 fmt.Sprintf("%v%v", nodeClass, metaVal), 117 fmt.Sprintf("%v%v", envTwoVal, nodeClass), 118 } 119 env := testEnvBuilder() 120 act := env.Build().ParseAndReplace(input) 121 122 if !reflect.DeepEqual(act, exp) { 123 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 124 } 125 } 126 127 func TestEnvironment_ReplaceEnv_Mixed(t *testing.T) { 128 input := fmt.Sprintf("${%v}${%v%v}", nodeNameKey, nodeAttributePrefix, attrKey) 129 exp := fmt.Sprintf("%v%v", nodeName, attrVal) 130 env := testEnvBuilder() 131 act := env.Build().ReplaceEnv(input) 132 133 if act != exp { 134 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 135 } 136 } 137 138 func TestEnvironment_AsList(t *testing.T) { 139 n := mock.Node() 140 n.Meta = map[string]string{ 141 "metaKey": "metaVal", 142 } 143 a := mock.Alloc() 144 a.AllocatedResources.Tasks["web"].Networks[0] = &structs.NetworkResource{ 145 Device: "eth0", 146 IP: "127.0.0.1", 147 ReservedPorts: []structs.Port{{Label: "https", Value: 8080}}, 148 MBits: 50, 149 DynamicPorts: []structs.Port{{Label: "http", Value: 80}}, 150 } 151 a.AllocatedResources.Tasks["ssh"] = &structs.AllocatedTaskResources{ 152 Networks: []*structs.NetworkResource{ 153 { 154 Device: "eth0", 155 IP: "192.168.0.100", 156 MBits: 50, 157 ReservedPorts: []structs.Port{ 158 {Label: "ssh", Value: 22}, 159 {Label: "other", Value: 1234}, 160 }, 161 }, 162 }, 163 } 164 task := a.Job.TaskGroups[0].Tasks[0] 165 task.Env = map[string]string{ 166 "taskEnvKey": "taskEnvVal", 167 } 168 env := NewBuilder(n, a, task, "global").SetDriverNetwork( 169 &drivers.DriverNetwork{PortMap: map[string]int{"https": 443}}, 170 ) 171 172 act := env.Build().List() 173 exp := []string{ 174 "taskEnvKey=taskEnvVal", 175 "NOMAD_ADDR_http=127.0.0.1:80", 176 "NOMAD_PORT_http=80", 177 "NOMAD_IP_http=127.0.0.1", 178 "NOMAD_ADDR_https=127.0.0.1:8080", 179 "NOMAD_PORT_https=443", 180 "NOMAD_IP_https=127.0.0.1", 181 "NOMAD_HOST_PORT_http=80", 182 "NOMAD_HOST_PORT_https=8080", 183 "NOMAD_TASK_NAME=web", 184 "NOMAD_GROUP_NAME=web", 185 "NOMAD_ADDR_ssh_other=192.168.0.100:1234", 186 "NOMAD_ADDR_ssh_ssh=192.168.0.100:22", 187 "NOMAD_IP_ssh_other=192.168.0.100", 188 "NOMAD_IP_ssh_ssh=192.168.0.100", 189 "NOMAD_PORT_ssh_other=1234", 190 "NOMAD_PORT_ssh_ssh=22", 191 "NOMAD_CPU_LIMIT=500", 192 "NOMAD_DC=dc1", 193 "NOMAD_REGION=global", 194 "NOMAD_MEMORY_LIMIT=256", 195 "NOMAD_META_ELB_CHECK_INTERVAL=30s", 196 "NOMAD_META_ELB_CHECK_MIN=3", 197 "NOMAD_META_ELB_CHECK_TYPE=http", 198 "NOMAD_META_FOO=bar", 199 "NOMAD_META_OWNER=armon", 200 "NOMAD_META_elb_check_interval=30s", 201 "NOMAD_META_elb_check_min=3", 202 "NOMAD_META_elb_check_type=http", 203 "NOMAD_META_foo=bar", 204 "NOMAD_META_owner=armon", 205 "NOMAD_JOB_NAME=my-job", 206 fmt.Sprintf("NOMAD_ALLOC_ID=%s", a.ID), 207 "NOMAD_ALLOC_INDEX=0", 208 } 209 sort.Strings(act) 210 sort.Strings(exp) 211 require.Equal(t, exp, act) 212 } 213 214 // COMPAT(0.11): Remove in 0.11 215 func TestEnvironment_AsList_Old(t *testing.T) { 216 n := mock.Node() 217 n.Meta = map[string]string{ 218 "metaKey": "metaVal", 219 } 220 a := mock.Alloc() 221 a.AllocatedResources = nil 222 a.Resources = &structs.Resources{ 223 CPU: 500, 224 MemoryMB: 256, 225 DiskMB: 150, 226 Networks: []*structs.NetworkResource{ 227 { 228 Device: "eth0", 229 IP: "192.168.0.100", 230 ReservedPorts: []structs.Port{ 231 {Label: "admin", Value: 5000}, 232 {Label: "ssh", Value: 22}, 233 {Label: "other", Value: 1234}, 234 }, 235 MBits: 50, 236 DynamicPorts: []structs.Port{{Label: "http"}}, 237 }, 238 }, 239 } 240 a.TaskResources = map[string]*structs.Resources{ 241 "web": { 242 CPU: 500, 243 MemoryMB: 256, 244 Networks: []*structs.NetworkResource{ 245 { 246 Device: "eth0", 247 IP: "192.168.0.100", 248 ReservedPorts: []structs.Port{{Label: "admin", Value: 5000}}, 249 MBits: 50, 250 DynamicPorts: []structs.Port{{Label: "http", Value: 2000}}, 251 }, 252 }, 253 }, 254 } 255 a.TaskResources["ssh"] = &structs.Resources{ 256 Networks: []*structs.NetworkResource{ 257 { 258 Device: "eth0", 259 IP: "192.168.0.100", 260 MBits: 50, 261 ReservedPorts: []structs.Port{ 262 {Label: "ssh", Value: 22}, 263 {Label: "other", Value: 1234}, 264 }, 265 }, 266 }, 267 } 268 task := a.Job.TaskGroups[0].Tasks[0] 269 task.Env = map[string]string{ 270 "taskEnvKey": "taskEnvVal", 271 } 272 task.Resources.Networks = []*structs.NetworkResource{ 273 { 274 IP: "127.0.0.1", 275 ReservedPorts: []structs.Port{{Label: "http", Value: 80}}, 276 DynamicPorts: []structs.Port{{Label: "https", Value: 8080}}, 277 }, 278 } 279 env := NewBuilder(n, a, task, "global").SetDriverNetwork( 280 &drivers.DriverNetwork{PortMap: map[string]int{"https": 443}}, 281 ) 282 283 act := env.Build().List() 284 exp := []string{ 285 "taskEnvKey=taskEnvVal", 286 "NOMAD_ADDR_http=127.0.0.1:80", 287 "NOMAD_PORT_http=80", 288 "NOMAD_IP_http=127.0.0.1", 289 "NOMAD_ADDR_https=127.0.0.1:8080", 290 "NOMAD_PORT_https=443", 291 "NOMAD_IP_https=127.0.0.1", 292 "NOMAD_HOST_PORT_http=80", 293 "NOMAD_HOST_PORT_https=8080", 294 "NOMAD_TASK_NAME=web", 295 "NOMAD_GROUP_NAME=web", 296 "NOMAD_ADDR_ssh_other=192.168.0.100:1234", 297 "NOMAD_ADDR_ssh_ssh=192.168.0.100:22", 298 "NOMAD_IP_ssh_other=192.168.0.100", 299 "NOMAD_IP_ssh_ssh=192.168.0.100", 300 "NOMAD_PORT_ssh_other=1234", 301 "NOMAD_PORT_ssh_ssh=22", 302 "NOMAD_CPU_LIMIT=500", 303 "NOMAD_DC=dc1", 304 "NOMAD_REGION=global", 305 "NOMAD_MEMORY_LIMIT=256", 306 "NOMAD_META_ELB_CHECK_INTERVAL=30s", 307 "NOMAD_META_ELB_CHECK_MIN=3", 308 "NOMAD_META_ELB_CHECK_TYPE=http", 309 "NOMAD_META_FOO=bar", 310 "NOMAD_META_OWNER=armon", 311 "NOMAD_META_elb_check_interval=30s", 312 "NOMAD_META_elb_check_min=3", 313 "NOMAD_META_elb_check_type=http", 314 "NOMAD_META_foo=bar", 315 "NOMAD_META_owner=armon", 316 "NOMAD_JOB_NAME=my-job", 317 fmt.Sprintf("NOMAD_ALLOC_ID=%s", a.ID), 318 "NOMAD_ALLOC_INDEX=0", 319 } 320 sort.Strings(act) 321 sort.Strings(exp) 322 require.Equal(t, exp, act) 323 } 324 325 func TestEnvironment_AllValues(t *testing.T) { 326 t.Parallel() 327 328 n := mock.Node() 329 n.Meta = map[string]string{ 330 "metaKey": "metaVal", 331 "nested.meta.key": "a", 332 "invalid...metakey": "b", 333 } 334 a := mock.Alloc() 335 a.AllocatedResources.Tasks["web"].Networks[0] = &structs.NetworkResource{ 336 Device: "eth0", 337 IP: "127.0.0.1", 338 ReservedPorts: []structs.Port{{Label: "https", Value: 8080}}, 339 MBits: 50, 340 DynamicPorts: []structs.Port{{Label: "http", Value: 80}}, 341 } 342 a.AllocatedResources.Tasks["ssh"] = &structs.AllocatedTaskResources{ 343 Networks: []*structs.NetworkResource{ 344 { 345 Device: "eth0", 346 IP: "192.168.0.100", 347 MBits: 50, 348 ReservedPorts: []structs.Port{ 349 {Label: "ssh", Value: 22}, 350 {Label: "other", Value: 1234}, 351 }, 352 }, 353 }, 354 } 355 task := a.Job.TaskGroups[0].Tasks[0] 356 task.Env = map[string]string{ 357 "taskEnvKey": "taskEnvVal", 358 "nested.task.key": "x", 359 "invalid...taskkey": "y", 360 ".a": "a", 361 "b.": "b", 362 ".": "c", 363 } 364 env := NewBuilder(n, a, task, "global").SetDriverNetwork( 365 &drivers.DriverNetwork{PortMap: map[string]int{"https": 443}}, 366 ) 367 368 values, errs, err := env.Build().AllValues() 369 require.NoError(t, err) 370 371 // Assert the keys we couldn't nest were reported 372 require.Len(t, errs, 5) 373 require.Contains(t, errs, "invalid...taskkey") 374 require.Contains(t, errs, "meta.invalid...metakey") 375 require.Contains(t, errs, ".a") 376 require.Contains(t, errs, "b.") 377 require.Contains(t, errs, ".") 378 379 exp := map[string]string{ 380 // Node 381 "node.unique.id": n.ID, 382 "node.region": "global", 383 "node.datacenter": n.Datacenter, 384 "node.unique.name": n.Name, 385 "node.class": n.NodeClass, 386 "meta.metaKey": "metaVal", 387 "attr.arch": "x86", 388 "attr.driver.exec": "1", 389 "attr.driver.mock_driver": "1", 390 "attr.kernel.name": "linux", 391 "attr.nomad.version": "0.5.0", 392 393 // 0.9 style meta and attr 394 "node.meta.metaKey": "metaVal", 395 "node.attr.arch": "x86", 396 "node.attr.driver.exec": "1", 397 "node.attr.driver.mock_driver": "1", 398 "node.attr.kernel.name": "linux", 399 "node.attr.nomad.version": "0.5.0", 400 401 // Env 402 "taskEnvKey": "taskEnvVal", 403 "NOMAD_ADDR_http": "127.0.0.1:80", 404 "NOMAD_PORT_http": "80", 405 "NOMAD_IP_http": "127.0.0.1", 406 "NOMAD_ADDR_https": "127.0.0.1:8080", 407 "NOMAD_PORT_https": "443", 408 "NOMAD_IP_https": "127.0.0.1", 409 "NOMAD_HOST_PORT_http": "80", 410 "NOMAD_HOST_PORT_https": "8080", 411 "NOMAD_TASK_NAME": "web", 412 "NOMAD_GROUP_NAME": "web", 413 "NOMAD_ADDR_ssh_other": "192.168.0.100:1234", 414 "NOMAD_ADDR_ssh_ssh": "192.168.0.100:22", 415 "NOMAD_IP_ssh_other": "192.168.0.100", 416 "NOMAD_IP_ssh_ssh": "192.168.0.100", 417 "NOMAD_PORT_ssh_other": "1234", 418 "NOMAD_PORT_ssh_ssh": "22", 419 "NOMAD_CPU_LIMIT": "500", 420 "NOMAD_DC": "dc1", 421 "NOMAD_REGION": "global", 422 "NOMAD_MEMORY_LIMIT": "256", 423 "NOMAD_META_ELB_CHECK_INTERVAL": "30s", 424 "NOMAD_META_ELB_CHECK_MIN": "3", 425 "NOMAD_META_ELB_CHECK_TYPE": "http", 426 "NOMAD_META_FOO": "bar", 427 "NOMAD_META_OWNER": "armon", 428 "NOMAD_META_elb_check_interval": "30s", 429 "NOMAD_META_elb_check_min": "3", 430 "NOMAD_META_elb_check_type": "http", 431 "NOMAD_META_foo": "bar", 432 "NOMAD_META_owner": "armon", 433 "NOMAD_JOB_NAME": "my-job", 434 "NOMAD_ALLOC_ID": a.ID, 435 "NOMAD_ALLOC_INDEX": "0", 436 437 // 0.9 style env map 438 `env["taskEnvKey"]`: "taskEnvVal", 439 `env["NOMAD_ADDR_http"]`: "127.0.0.1:80", 440 `env["nested.task.key"]`: "x", 441 `env["invalid...taskkey"]`: "y", 442 `env[".a"]`: "a", 443 `env["b."]`: "b", 444 `env["."]`: "c", 445 } 446 447 evalCtx := &hcl.EvalContext{ 448 Variables: values, 449 } 450 451 for k, expectedVal := range exp { 452 t.Run(k, func(t *testing.T) { 453 // Parse HCL containing the test key 454 hclStr := fmt.Sprintf(`"${%s}"`, k) 455 expr, diag := hclsyntax.ParseExpression([]byte(hclStr), "test.hcl", hcl.Pos{}) 456 require.Empty(t, diag) 457 458 // Decode with the TaskEnv values 459 out := "" 460 diag = gohcl.DecodeExpression(expr, evalCtx, &out) 461 require.Empty(t, diag) 462 require.Equal(t, out, expectedVal) 463 }) 464 } 465 } 466 467 func TestEnvironment_VaultToken(t *testing.T) { 468 n := mock.Node() 469 a := mock.Alloc() 470 env := NewBuilder(n, a, a.Job.TaskGroups[0].Tasks[0], "global") 471 env.SetVaultToken("123", false) 472 473 { 474 act := env.Build().All() 475 if act[VaultToken] != "" { 476 t.Fatalf("Unexpected environment variables: %s=%q", VaultToken, act[VaultToken]) 477 } 478 } 479 480 { 481 act := env.SetVaultToken("123", true).Build().List() 482 exp := "VAULT_TOKEN=123" 483 found := false 484 for _, entry := range act { 485 if entry == exp { 486 found = true 487 break 488 } 489 } 490 if !found { 491 t.Fatalf("did not find %q in:\n%s", exp, strings.Join(act, "\n")) 492 } 493 } 494 } 495 496 func TestEnvironment_Envvars(t *testing.T) { 497 envMap := map[string]string{"foo": "baz", "bar": "bang"} 498 n := mock.Node() 499 a := mock.Alloc() 500 task := a.Job.TaskGroups[0].Tasks[0] 501 task.Env = envMap 502 net := &drivers.DriverNetwork{PortMap: portMap} 503 act := NewBuilder(n, a, task, "global").SetDriverNetwork(net).Build().All() 504 for k, v := range envMap { 505 actV, ok := act[k] 506 if !ok { 507 t.Fatalf("missing %q in %#v", k, act) 508 } 509 if v != actV { 510 t.Fatalf("expected %s=%q but found %q", k, v, actV) 511 } 512 } 513 } 514 515 // TestEnvironment_HookVars asserts hook env vars are LWW and deletes of later 516 // writes allow earlier hook's values to be visible. 517 func TestEnvironment_HookVars(t *testing.T) { 518 n := mock.Node() 519 a := mock.Alloc() 520 builder := NewBuilder(n, a, a.Job.TaskGroups[0].Tasks[0], "global") 521 522 // Add vars from two hooks and assert the second one wins on 523 // conflicting keys. 524 builder.SetHookEnv("hookA", map[string]string{ 525 "foo": "bar", 526 "baz": "quux", 527 }) 528 builder.SetHookEnv("hookB", map[string]string{ 529 "foo": "123", 530 "hookB": "wins", 531 }) 532 533 { 534 out := builder.Build().All() 535 assert.Equal(t, "123", out["foo"]) 536 assert.Equal(t, "quux", out["baz"]) 537 assert.Equal(t, "wins", out["hookB"]) 538 } 539 540 // Asserting overwriting hook vars allows the first hooks original 541 // value to be used. 542 builder.SetHookEnv("hookB", nil) 543 { 544 out := builder.Build().All() 545 assert.Equal(t, "bar", out["foo"]) 546 assert.Equal(t, "quux", out["baz"]) 547 assert.NotContains(t, out, "hookB") 548 } 549 } 550 551 // TestEnvironment_DeviceHookVars asserts device hook env vars are accessible 552 // separately. 553 func TestEnvironment_DeviceHookVars(t *testing.T) { 554 require := require.New(t) 555 n := mock.Node() 556 a := mock.Alloc() 557 builder := NewBuilder(n, a, a.Job.TaskGroups[0].Tasks[0], "global") 558 559 // Add vars from two hooks and assert the second one wins on 560 // conflicting keys. 561 builder.SetHookEnv("hookA", map[string]string{ 562 "foo": "bar", 563 "baz": "quux", 564 }) 565 builder.SetDeviceHookEnv("devices", map[string]string{ 566 "hook": "wins", 567 }) 568 569 b := builder.Build() 570 deviceEnv := b.DeviceEnv() 571 require.Len(deviceEnv, 1) 572 require.Contains(deviceEnv, "hook") 573 574 all := b.Map() 575 require.Contains(all, "foo") 576 } 577 578 func TestEnvironment_Interpolate(t *testing.T) { 579 n := mock.Node() 580 n.Attributes["arch"] = "x86" 581 n.NodeClass = "test class" 582 a := mock.Alloc() 583 task := a.Job.TaskGroups[0].Tasks[0] 584 task.Env = map[string]string{"test": "${node.class}", "test2": "${attr.arch}"} 585 env := NewBuilder(n, a, task, "global").Build() 586 587 exp := []string{fmt.Sprintf("test=%s", n.NodeClass), fmt.Sprintf("test2=%s", n.Attributes["arch"])} 588 found1, found2 := false, false 589 for _, entry := range env.List() { 590 switch entry { 591 case exp[0]: 592 found1 = true 593 case exp[1]: 594 found2 = true 595 } 596 } 597 if !found1 || !found2 { 598 t.Fatalf("expected to find %q and %q but got:\n%s", 599 exp[0], exp[1], strings.Join(env.List(), "\n")) 600 } 601 } 602 603 func TestEnvironment_AppendHostEnvvars(t *testing.T) { 604 host := os.Environ() 605 if len(host) < 2 { 606 t.Skip("No host environment variables. Can't test") 607 } 608 skip := strings.Split(host[0], "=")[0] 609 env := testEnvBuilder(). 610 SetHostEnvvars([]string{skip}). 611 Build() 612 613 act := env.Map() 614 if len(act) < 1 { 615 t.Fatalf("Host environment variables not properly set") 616 } 617 if _, ok := act[skip]; ok { 618 t.Fatalf("Didn't filter environment variable %q", skip) 619 } 620 } 621 622 // TestEnvironment_DashesInTaskName asserts dashes in port labels are properly 623 // converted to underscores in environment variables. 624 // See: https://github.com/hashicorp/nomad/issues/2405 625 func TestEnvironment_DashesInTaskName(t *testing.T) { 626 a := mock.Alloc() 627 task := a.Job.TaskGroups[0].Tasks[0] 628 task.Env = map[string]string{"test-one-two": "three-four"} 629 envMap := NewBuilder(mock.Node(), a, task, "global").Build().Map() 630 631 if envMap["test_one_two"] != "three-four" { 632 t.Fatalf("Expected test_one_two=three-four in TaskEnv; found:\n%#v", envMap) 633 } 634 } 635 636 // TestEnvironment_UpdateTask asserts env vars and task meta are updated when a 637 // task is updated. 638 func TestEnvironment_UpdateTask(t *testing.T) { 639 a := mock.Alloc() 640 a.Job.TaskGroups[0].Meta = map[string]string{"tgmeta": "tgmetaval"} 641 task := a.Job.TaskGroups[0].Tasks[0] 642 task.Name = "orig" 643 task.Env = map[string]string{"env": "envval"} 644 task.Meta = map[string]string{"taskmeta": "taskmetaval"} 645 builder := NewBuilder(mock.Node(), a, task, "global") 646 647 origMap := builder.Build().Map() 648 if origMap["NOMAD_TASK_NAME"] != "orig" { 649 t.Errorf("Expected NOMAD_TASK_NAME=orig but found %q", origMap["NOMAD_TASK_NAME"]) 650 } 651 if origMap["NOMAD_META_taskmeta"] != "taskmetaval" { 652 t.Errorf("Expected NOMAD_META_taskmeta=taskmetaval but found %q", origMap["NOMAD_META_taskmeta"]) 653 } 654 if origMap["env"] != "envval" { 655 t.Errorf("Expected env=envva but found %q", origMap["env"]) 656 } 657 if origMap["NOMAD_META_tgmeta"] != "tgmetaval" { 658 t.Errorf("Expected NOMAD_META_tgmeta=tgmetaval but found %q", origMap["NOMAD_META_tgmeta"]) 659 } 660 661 a.Job.TaskGroups[0].Meta = map[string]string{"tgmeta2": "tgmetaval2"} 662 task.Name = "new" 663 task.Env = map[string]string{"env2": "envval2"} 664 task.Meta = map[string]string{"taskmeta2": "taskmetaval2"} 665 666 newMap := builder.UpdateTask(a, task).Build().Map() 667 if newMap["NOMAD_TASK_NAME"] != "new" { 668 t.Errorf("Expected NOMAD_TASK_NAME=new but found %q", newMap["NOMAD_TASK_NAME"]) 669 } 670 if newMap["NOMAD_META_taskmeta2"] != "taskmetaval2" { 671 t.Errorf("Expected NOMAD_META_taskmeta=taskmetaval but found %q", newMap["NOMAD_META_taskmeta2"]) 672 } 673 if newMap["env2"] != "envval2" { 674 t.Errorf("Expected env=envva but found %q", newMap["env2"]) 675 } 676 if newMap["NOMAD_META_tgmeta2"] != "tgmetaval2" { 677 t.Errorf("Expected NOMAD_META_tgmeta=tgmetaval but found %q", newMap["NOMAD_META_tgmeta2"]) 678 } 679 if v, ok := newMap["NOMAD_META_taskmeta"]; ok { 680 t.Errorf("Expected NOMAD_META_taskmeta to be unset but found: %q", v) 681 } 682 } 683 684 // TestEnvironment_InterpolateEmptyOptionalMeta asserts that in a parameterized 685 // job, if an optional meta field is not set, it will get interpolated as an 686 // empty string. 687 func TestEnvironment_InterpolateEmptyOptionalMeta(t *testing.T) { 688 require := require.New(t) 689 a := mock.Alloc() 690 a.Job.ParameterizedJob = &structs.ParameterizedJobConfig{ 691 MetaOptional: []string{"metaopt1", "metaopt2"}, 692 } 693 a.Job.Dispatched = true 694 task := a.Job.TaskGroups[0].Tasks[0] 695 task.Meta = map[string]string{"metaopt1": "metaopt1val"} 696 env := NewBuilder(mock.Node(), a, task, "global").Build() 697 require.Equal("metaopt1val", env.ReplaceEnv("${NOMAD_META_metaopt1}")) 698 require.Empty(env.ReplaceEnv("${NOMAD_META_metaopt2}")) 699 }