github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/driver/env/env_test.go (about) 1 package env 2 3 import ( 4 "fmt" 5 "os" 6 "reflect" 7 "sort" 8 "strings" 9 "testing" 10 11 cstructs "github.com/hashicorp/nomad/client/structs" 12 "github.com/hashicorp/nomad/nomad/mock" 13 "github.com/hashicorp/nomad/nomad/structs" 14 ) 15 16 const ( 17 // Node values that tests can rely on 18 metaKey = "instance" 19 metaVal = "t2-micro" 20 attrKey = "arch" 21 attrVal = "amd64" 22 nodeName = "test node" 23 nodeClass = "test class" 24 25 // Environment variable values that tests can rely on 26 envOneKey = "NOMAD_IP" 27 envOneVal = "127.0.0.1" 28 envTwoKey = "NOMAD_PORT_WEB" 29 envTwoVal = ":80" 30 ) 31 32 var ( 33 // portMap for use in tests as its set after Builder creation 34 portMap = map[string]int{ 35 "https": 443, 36 } 37 ) 38 39 func testEnvBuilder() *Builder { 40 n := mock.Node() 41 n.Attributes = map[string]string{ 42 attrKey: attrVal, 43 } 44 n.Meta = map[string]string{ 45 metaKey: metaVal, 46 } 47 n.Name = nodeName 48 n.NodeClass = nodeClass 49 50 task := mock.Job().TaskGroups[0].Tasks[0] 51 task.Env = map[string]string{ 52 envOneKey: envOneVal, 53 envTwoKey: envTwoVal, 54 } 55 return NewBuilder(n, mock.Alloc(), task, "global") 56 } 57 58 func TestEnvironment_ParseAndReplace_Env(t *testing.T) { 59 env := testEnvBuilder() 60 61 input := []string{fmt.Sprintf(`"${%v}"!`, envOneKey), fmt.Sprintf("${%s}${%s}", envOneKey, envTwoKey)} 62 act := env.Build().ParseAndReplace(input) 63 exp := []string{fmt.Sprintf(`"%s"!`, envOneVal), fmt.Sprintf("%s%s", envOneVal, envTwoVal)} 64 65 if !reflect.DeepEqual(act, exp) { 66 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 67 } 68 } 69 70 func TestEnvironment_ParseAndReplace_Meta(t *testing.T) { 71 input := []string{fmt.Sprintf("${%v%v}", nodeMetaPrefix, metaKey)} 72 exp := []string{metaVal} 73 env := testEnvBuilder() 74 act := env.Build().ParseAndReplace(input) 75 76 if !reflect.DeepEqual(act, exp) { 77 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 78 } 79 } 80 81 func TestEnvironment_ParseAndReplace_Attr(t *testing.T) { 82 input := []string{fmt.Sprintf("${%v%v}", nodeAttributePrefix, attrKey)} 83 exp := []string{attrVal} 84 env := testEnvBuilder() 85 act := env.Build().ParseAndReplace(input) 86 87 if !reflect.DeepEqual(act, exp) { 88 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 89 } 90 } 91 92 func TestEnvironment_ParseAndReplace_Node(t *testing.T) { 93 input := []string{fmt.Sprintf("${%v}", nodeNameKey), fmt.Sprintf("${%v}", nodeClassKey)} 94 exp := []string{nodeName, nodeClass} 95 env := testEnvBuilder() 96 act := env.Build().ParseAndReplace(input) 97 98 if !reflect.DeepEqual(act, exp) { 99 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 100 } 101 } 102 103 func TestEnvironment_ParseAndReplace_Mixed(t *testing.T) { 104 input := []string{ 105 fmt.Sprintf("${%v}${%v%v}", nodeNameKey, nodeAttributePrefix, attrKey), 106 fmt.Sprintf("${%v}${%v%v}", nodeClassKey, nodeMetaPrefix, metaKey), 107 fmt.Sprintf("${%v}${%v}", envTwoKey, nodeClassKey), 108 } 109 exp := []string{ 110 fmt.Sprintf("%v%v", nodeName, attrVal), 111 fmt.Sprintf("%v%v", nodeClass, metaVal), 112 fmt.Sprintf("%v%v", envTwoVal, nodeClass), 113 } 114 env := testEnvBuilder() 115 act := env.Build().ParseAndReplace(input) 116 117 if !reflect.DeepEqual(act, exp) { 118 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 119 } 120 } 121 122 func TestEnvironment_ReplaceEnv_Mixed(t *testing.T) { 123 input := fmt.Sprintf("${%v}${%v%v}", nodeNameKey, nodeAttributePrefix, attrKey) 124 exp := fmt.Sprintf("%v%v", nodeName, attrVal) 125 env := testEnvBuilder() 126 act := env.Build().ReplaceEnv(input) 127 128 if act != exp { 129 t.Fatalf("ParseAndReplace(%v) returned %#v; want %#v", input, act, exp) 130 } 131 } 132 133 func TestEnvironment_AsList(t *testing.T) { 134 n := mock.Node() 135 n.Meta = map[string]string{ 136 "metaKey": "metaVal", 137 } 138 a := mock.Alloc() 139 a.Resources.Networks[0].ReservedPorts = append(a.Resources.Networks[0].ReservedPorts, 140 structs.Port{Label: "ssh", Value: 22}, 141 structs.Port{Label: "other", Value: 1234}, 142 ) 143 a.TaskResources["web"].Networks[0].DynamicPorts[0].Value = 2000 144 a.TaskResources["ssh"] = &structs.Resources{ 145 Networks: []*structs.NetworkResource{ 146 { 147 Device: "eth0", 148 IP: "192.168.0.100", 149 MBits: 50, 150 ReservedPorts: []structs.Port{ 151 {Label: "ssh", Value: 22}, 152 {Label: "other", Value: 1234}, 153 }, 154 }, 155 }, 156 } 157 task := a.Job.TaskGroups[0].Tasks[0] 158 task.Env = map[string]string{ 159 "taskEnvKey": "taskEnvVal", 160 } 161 task.Resources.Networks = []*structs.NetworkResource{ 162 &structs.NetworkResource{ 163 IP: "127.0.0.1", 164 ReservedPorts: []structs.Port{{Label: "http", Value: 80}}, 165 DynamicPorts: []structs.Port{{Label: "https", Value: 8080}}, 166 }, 167 } 168 env := NewBuilder(n, a, task, "global").SetDriverNetwork( 169 &cstructs.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 if len(act) != len(exp) { 212 t.Fatalf("expected %d vars != %d actual, actual:\n%s\n\nexpected:\n%s\n", 213 len(act), len(exp), strings.Join(act, "\n"), strings.Join(exp, "\n")) 214 } 215 for i := range act { 216 if act[i] != exp[i] { 217 t.Errorf("%d actual %q != %q expected", i, act[i], exp[i]) 218 } 219 } 220 } 221 222 func TestEnvironment_VaultToken(t *testing.T) { 223 n := mock.Node() 224 a := mock.Alloc() 225 env := NewBuilder(n, a, a.Job.TaskGroups[0].Tasks[0], "global") 226 env.SetVaultToken("123", false) 227 228 { 229 act := env.Build().All() 230 if act[VaultToken] != "" { 231 t.Fatalf("Unexpected environment variables: %s=%q", VaultToken, act[VaultToken]) 232 } 233 } 234 235 { 236 act := env.SetVaultToken("123", true).Build().List() 237 exp := "VAULT_TOKEN=123" 238 found := false 239 for _, entry := range act { 240 if entry == exp { 241 found = true 242 break 243 } 244 } 245 if !found { 246 t.Fatalf("did not find %q in:\n%s", exp, strings.Join(act, "\n")) 247 } 248 } 249 } 250 251 func TestEnvironment_Envvars(t *testing.T) { 252 envMap := map[string]string{"foo": "baz", "bar": "bang"} 253 n := mock.Node() 254 a := mock.Alloc() 255 task := a.Job.TaskGroups[0].Tasks[0] 256 task.Env = envMap 257 net := &cstructs.DriverNetwork{PortMap: portMap} 258 act := NewBuilder(n, a, task, "global").SetDriverNetwork(net).Build().All() 259 for k, v := range envMap { 260 actV, ok := act[k] 261 if !ok { 262 t.Fatalf("missing %q in %#v", k, act) 263 } 264 if v != actV { 265 t.Fatalf("expected %s=%q but found %q", k, v, actV) 266 } 267 } 268 } 269 270 func TestEnvironment_Interpolate(t *testing.T) { 271 n := mock.Node() 272 n.Attributes["arch"] = "x86" 273 n.NodeClass = "test class" 274 a := mock.Alloc() 275 task := a.Job.TaskGroups[0].Tasks[0] 276 task.Env = map[string]string{"test": "${node.class}", "test2": "${attr.arch}"} 277 env := NewBuilder(n, a, task, "global").Build() 278 279 exp := []string{fmt.Sprintf("test=%s", n.NodeClass), fmt.Sprintf("test2=%s", n.Attributes["arch"])} 280 found1, found2 := false, false 281 for _, entry := range env.List() { 282 switch entry { 283 case exp[0]: 284 found1 = true 285 case exp[1]: 286 found2 = true 287 } 288 } 289 if !found1 || !found2 { 290 t.Fatalf("expected to find %q and %q but got:\n%s", 291 exp[0], exp[1], strings.Join(env.List(), "\n")) 292 } 293 } 294 295 func TestEnvironment_AppendHostEnvvars(t *testing.T) { 296 host := os.Environ() 297 if len(host) < 2 { 298 t.Skip("No host environment variables. Can't test") 299 } 300 skip := strings.Split(host[0], "=")[0] 301 env := testEnvBuilder(). 302 SetHostEnvvars([]string{skip}). 303 Build() 304 305 act := env.Map() 306 if len(act) < 1 { 307 t.Fatalf("Host environment variables not properly set") 308 } 309 if _, ok := act[skip]; ok { 310 t.Fatalf("Didn't filter environment variable %q", skip) 311 } 312 } 313 314 // TestEnvironment_DashesInTaskName asserts dashes in port labels are properly 315 // converted to underscores in environment variables. 316 // See: https://github.com/hashicorp/nomad/issues/2405 317 func TestEnvironment_DashesInTaskName(t *testing.T) { 318 a := mock.Alloc() 319 task := a.Job.TaskGroups[0].Tasks[0] 320 task.Env = map[string]string{"test-one-two": "three-four"} 321 envMap := NewBuilder(mock.Node(), a, task, "global").Build().Map() 322 323 if envMap["test_one_two"] != "three-four" { 324 t.Fatalf("Expected test_one_two=three-four in TaskEnv; found:\n%#v", envMap) 325 } 326 } 327 328 // TestEnvironment_UpdateTask asserts env vars and task meta are updated when a 329 // task is updated. 330 func TestEnvironment_UpdateTask(t *testing.T) { 331 a := mock.Alloc() 332 a.Job.TaskGroups[0].Meta = map[string]string{"tgmeta": "tgmetaval"} 333 task := a.Job.TaskGroups[0].Tasks[0] 334 task.Name = "orig" 335 task.Env = map[string]string{"taskenv": "taskenvval"} 336 task.Meta = map[string]string{"taskmeta": "taskmetaval"} 337 builder := NewBuilder(mock.Node(), a, task, "global") 338 339 origMap := builder.Build().Map() 340 if origMap["NOMAD_TASK_NAME"] != "orig" { 341 t.Errorf("Expected NOMAD_TASK_NAME=orig but found %q", origMap["NOMAD_TASK_NAME"]) 342 } 343 if origMap["NOMAD_META_taskmeta"] != "taskmetaval" { 344 t.Errorf("Expected NOMAD_META_taskmeta=taskmetaval but found %q", origMap["NOMAD_META_taskmeta"]) 345 } 346 if origMap["taskenv"] != "taskenvval" { 347 t.Errorf("Expected taskenv=taskenvva but found %q", origMap["taskenv"]) 348 } 349 if origMap["NOMAD_META_tgmeta"] != "tgmetaval" { 350 t.Errorf("Expected NOMAD_META_tgmeta=tgmetaval but found %q", origMap["NOMAD_META_tgmeta"]) 351 } 352 353 a.Job.TaskGroups[0].Meta = map[string]string{"tgmeta2": "tgmetaval2"} 354 task.Name = "new" 355 task.Env = map[string]string{"taskenv2": "taskenvval2"} 356 task.Meta = map[string]string{"taskmeta2": "taskmetaval2"} 357 358 newMap := builder.UpdateTask(a, task).Build().Map() 359 if newMap["NOMAD_TASK_NAME"] != "new" { 360 t.Errorf("Expected NOMAD_TASK_NAME=new but found %q", newMap["NOMAD_TASK_NAME"]) 361 } 362 if newMap["NOMAD_META_taskmeta2"] != "taskmetaval2" { 363 t.Errorf("Expected NOMAD_META_taskmeta=taskmetaval but found %q", newMap["NOMAD_META_taskmeta2"]) 364 } 365 if newMap["taskenv2"] != "taskenvval2" { 366 t.Errorf("Expected taskenv=taskenvva but found %q", newMap["taskenv2"]) 367 } 368 if newMap["NOMAD_META_tgmeta2"] != "tgmetaval2" { 369 t.Errorf("Expected NOMAD_META_tgmeta=tgmetaval but found %q", newMap["NOMAD_META_tgmeta2"]) 370 } 371 if v, ok := newMap["NOMAD_META_taskmeta"]; ok { 372 t.Errorf("Expected NOMAD_META_taskmeta to be unset but found: %q", v) 373 } 374 }