github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/driver/env/env.go (about) 1 package env 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 "strconv" 8 "strings" 9 "sync" 10 11 cstructs "github.com/hashicorp/nomad/client/structs" 12 "github.com/hashicorp/nomad/helper" 13 hargs "github.com/hashicorp/nomad/helper/args" 14 "github.com/hashicorp/nomad/nomad/structs" 15 ) 16 17 // A set of environment variables that are exported by each driver. 18 const ( 19 // AllocDir is the environment variable with the path to the alloc directory 20 // that is shared across tasks within a task group. 21 AllocDir = "NOMAD_ALLOC_DIR" 22 23 // TaskLocalDir is the environment variable with the path to the tasks local 24 // directory where it can store data that is persisted to the alloc is 25 // removed. 26 TaskLocalDir = "NOMAD_TASK_DIR" 27 28 // SecretsDir is the environment variable with the path to the tasks secret 29 // directory where it can store sensitive data. 30 SecretsDir = "NOMAD_SECRETS_DIR" 31 32 // MemLimit is the environment variable with the tasks memory limit in MBs. 33 MemLimit = "NOMAD_MEMORY_LIMIT" 34 35 // CpuLimit is the environment variable with the tasks CPU limit in MHz. 36 CpuLimit = "NOMAD_CPU_LIMIT" 37 38 // AllocID is the environment variable for passing the allocation ID. 39 AllocID = "NOMAD_ALLOC_ID" 40 41 // AllocName is the environment variable for passing the allocation name. 42 AllocName = "NOMAD_ALLOC_NAME" 43 44 // TaskName is the environment variable for passing the task name. 45 TaskName = "NOMAD_TASK_NAME" 46 47 // GroupName is the environment variable for passing the task group name. 48 GroupName = "NOMAD_GROUP_NAME" 49 50 // JobName is the environment variable for passing the job name. 51 JobName = "NOMAD_JOB_NAME" 52 53 // AllocIndex is the environment variable for passing the allocation index. 54 AllocIndex = "NOMAD_ALLOC_INDEX" 55 56 // Datacenter is the environment variable for passing the datacenter in which the alloc is running. 57 Datacenter = "NOMAD_DC" 58 59 // Region is the environment variable for passing the region in which the alloc is running. 60 Region = "NOMAD_REGION" 61 62 // AddrPrefix is the prefix for passing both dynamic and static port 63 // allocations to tasks. 64 // E.g $NOMAD_ADDR_http=127.0.0.1:80 65 // 66 // The ip:port are always the host's. 67 AddrPrefix = "NOMAD_ADDR_" 68 69 // IpPrefix is the prefix for passing the host IP of a port allocation 70 // to a task. 71 IpPrefix = "NOMAD_IP_" 72 73 // PortPrefix is the prefix for passing the port allocation to a task. 74 // It will be the task's port if a port map is specified. Task's should 75 // bind to this port. 76 PortPrefix = "NOMAD_PORT_" 77 78 // HostPortPrefix is the prefix for passing the host port when a port 79 // map is specified. 80 HostPortPrefix = "NOMAD_HOST_PORT_" 81 82 // MetaPrefix is the prefix for passing task meta data. 83 MetaPrefix = "NOMAD_META_" 84 85 // VaultToken is the environment variable for passing the Vault token 86 VaultToken = "VAULT_TOKEN" 87 ) 88 89 // The node values that can be interpreted. 90 const ( 91 nodeIdKey = "node.unique.id" 92 nodeDcKey = "node.datacenter" 93 nodeRegionKey = "node.region" 94 nodeNameKey = "node.unique.name" 95 nodeClassKey = "node.class" 96 97 // Prefixes used for lookups. 98 nodeAttributePrefix = "attr." 99 nodeMetaPrefix = "meta." 100 ) 101 102 // TaskEnv is a task's environment as well as node attribute's for 103 // interpolation. 104 type TaskEnv struct { 105 // NodeAttrs is the map of node attributes for interpolation 106 NodeAttrs map[string]string 107 108 // EnvMap is the map of environment variables 109 EnvMap map[string]string 110 111 // envList is a memoized list created by List() 112 envList []string 113 } 114 115 // NewTaskEnv creates a new task environment with the given environment and 116 // node attribute maps. 117 func NewTaskEnv(env, node map[string]string) *TaskEnv { 118 return &TaskEnv{ 119 NodeAttrs: node, 120 EnvMap: env, 121 } 122 } 123 124 // List returns the task's environment as a slice of NAME=value pair strings. 125 func (t *TaskEnv) List() []string { 126 if t.envList != nil { 127 return t.envList 128 } 129 130 env := []string{} 131 for k, v := range t.EnvMap { 132 env = append(env, fmt.Sprintf("%s=%s", k, v)) 133 } 134 135 return env 136 } 137 138 // Map of the task's environment variables. 139 func (t *TaskEnv) Map() map[string]string { 140 m := make(map[string]string, len(t.EnvMap)) 141 for k, v := range t.EnvMap { 142 m[k] = v 143 } 144 145 return m 146 } 147 148 // All of the task's environment variables and the node's attributes in a 149 // single map. 150 func (t *TaskEnv) All() map[string]string { 151 m := make(map[string]string, len(t.EnvMap)+len(t.NodeAttrs)) 152 for k, v := range t.EnvMap { 153 m[k] = v 154 } 155 for k, v := range t.NodeAttrs { 156 m[k] = v 157 } 158 159 return m 160 } 161 162 // ParseAndReplace takes the user supplied args replaces any instance of an 163 // environment variable or Nomad variable in the args with the actual value. 164 func (t *TaskEnv) ParseAndReplace(args []string) []string { 165 replaced := make([]string, len(args)) 166 for i, arg := range args { 167 replaced[i] = hargs.ReplaceEnv(arg, t.EnvMap, t.NodeAttrs) 168 } 169 170 return replaced 171 } 172 173 // ReplaceEnv takes an arg and replaces all occurrences of environment variables 174 // and Nomad variables. If the variable is found in the passed map it is 175 // replaced, otherwise the original string is returned. 176 func (t *TaskEnv) ReplaceEnv(arg string) string { 177 return hargs.ReplaceEnv(arg, t.EnvMap, t.NodeAttrs) 178 } 179 180 // Builder is used to build task environment's and is safe for concurrent use. 181 type Builder struct { 182 // envvars are custom set environment variables 183 envvars map[string]string 184 185 // templateEnv are env vars set from templates 186 templateEnv map[string]string 187 188 // hostEnv are environment variables filtered from the host 189 hostEnv map[string]string 190 191 // nodeAttrs are Node attributes and metadata 192 nodeAttrs map[string]string 193 194 // taskMeta are the meta attributes on the task 195 taskMeta map[string]string 196 197 // allocDir from task's perspective; eg /alloc 198 allocDir string 199 200 // localDir from task's perspective; eg /local 201 localDir string 202 203 // secrestsDir from task's perspective; eg /secrets 204 secretsDir string 205 206 cpuLimit int 207 memLimit int 208 taskName string 209 allocIndex int 210 datacenter string 211 region string 212 allocId string 213 allocName string 214 groupName string 215 vaultToken string 216 injectVaultToken bool 217 jobName string 218 219 // otherPorts for tasks in the same alloc 220 otherPorts map[string]string 221 222 // driverNetwork is the network defined by the driver (or nil if none 223 // was defined). 224 driverNetwork *cstructs.DriverNetwork 225 226 // network resources from the task; must be lazily turned into env vars 227 // because portMaps and advertiseIP can change after builder creation 228 // and affect network env vars. 229 networks []*structs.NetworkResource 230 231 mu *sync.RWMutex 232 } 233 234 // NewBuilder creates a new task environment builder. 235 func NewBuilder(node *structs.Node, alloc *structs.Allocation, task *structs.Task, region string) *Builder { 236 b := &Builder{ 237 region: region, 238 mu: &sync.RWMutex{}, 239 } 240 return b.setTask(task).setAlloc(alloc).setNode(node) 241 } 242 243 // NewEmptyBuilder creates a new environment builder. 244 func NewEmptyBuilder() *Builder { 245 return &Builder{ 246 mu: &sync.RWMutex{}, 247 } 248 } 249 250 // Build must be called after all the tasks environment values have been set. 251 func (b *Builder) Build() *TaskEnv { 252 nodeAttrs := make(map[string]string) 253 envMap := make(map[string]string) 254 255 b.mu.RLock() 256 defer b.mu.RUnlock() 257 258 // Add the directories 259 if b.allocDir != "" { 260 envMap[AllocDir] = b.allocDir 261 } 262 if b.localDir != "" { 263 envMap[TaskLocalDir] = b.localDir 264 } 265 if b.secretsDir != "" { 266 envMap[SecretsDir] = b.secretsDir 267 } 268 269 // Add the resource limits 270 if b.memLimit != 0 { 271 envMap[MemLimit] = strconv.Itoa(b.memLimit) 272 } 273 if b.cpuLimit != 0 { 274 envMap[CpuLimit] = strconv.Itoa(b.cpuLimit) 275 } 276 277 // Add the task metadata 278 if b.allocId != "" { 279 envMap[AllocID] = b.allocId 280 } 281 if b.allocName != "" { 282 envMap[AllocName] = b.allocName 283 } 284 if b.groupName != "" { 285 envMap[GroupName] = b.groupName 286 } 287 if b.allocIndex != -1 { 288 envMap[AllocIndex] = strconv.Itoa(b.allocIndex) 289 } 290 if b.taskName != "" { 291 envMap[TaskName] = b.taskName 292 } 293 if b.jobName != "" { 294 envMap[JobName] = b.jobName 295 } 296 if b.datacenter != "" { 297 envMap[Datacenter] = b.datacenter 298 } 299 if b.region != "" { 300 envMap[Region] = b.region 301 302 // Copy region over to node attrs 303 nodeAttrs[nodeRegionKey] = b.region 304 } 305 306 // Build the network related env vars 307 buildNetworkEnv(envMap, b.networks, b.driverNetwork) 308 309 // Build the addr of the other tasks 310 for k, v := range b.otherPorts { 311 envMap[k] = v 312 } 313 314 // Build the Vault Token 315 if b.injectVaultToken && b.vaultToken != "" { 316 envMap[VaultToken] = b.vaultToken 317 } 318 319 // Copy task meta 320 for k, v := range b.taskMeta { 321 envMap[k] = v 322 } 323 324 // Copy node attributes 325 for k, v := range b.nodeAttrs { 326 nodeAttrs[k] = v 327 } 328 329 // Interpolate and add environment variables 330 for k, v := range b.hostEnv { 331 envMap[k] = hargs.ReplaceEnv(v, nodeAttrs, envMap) 332 } 333 334 // Copy interpolated task env vars second as they override host env vars 335 for k, v := range b.envvars { 336 envMap[k] = hargs.ReplaceEnv(v, nodeAttrs, envMap) 337 } 338 339 // Copy template env vars third as they override task env vars 340 for k, v := range b.templateEnv { 341 envMap[k] = v 342 } 343 344 // Clean keys (see #2405) 345 cleanedEnv := make(map[string]string, len(envMap)) 346 for k, v := range envMap { 347 cleanedK := helper.CleanEnvVar(k, '_') 348 cleanedEnv[cleanedK] = v 349 } 350 351 return NewTaskEnv(cleanedEnv, nodeAttrs) 352 } 353 354 // Update task updates the environment based on a new alloc and task. 355 func (b *Builder) UpdateTask(alloc *structs.Allocation, task *structs.Task) *Builder { 356 b.mu.Lock() 357 defer b.mu.Unlock() 358 return b.setTask(task).setAlloc(alloc) 359 } 360 361 // setTask is called from NewBuilder to populate task related environment 362 // variables. 363 func (b *Builder) setTask(task *structs.Task) *Builder { 364 b.taskName = task.Name 365 b.envvars = make(map[string]string, len(task.Env)) 366 for k, v := range task.Env { 367 b.envvars[k] = v 368 } 369 if task.Resources == nil { 370 b.memLimit = 0 371 b.cpuLimit = 0 372 b.networks = []*structs.NetworkResource{} 373 } else { 374 b.memLimit = task.Resources.MemoryMB 375 b.cpuLimit = task.Resources.CPU 376 // Copy networks to prevent sharing 377 b.networks = make([]*structs.NetworkResource, len(task.Resources.Networks)) 378 for i, n := range task.Resources.Networks { 379 b.networks[i] = n.Copy() 380 } 381 } 382 return b 383 } 384 385 // setAlloc is called from NewBuilder to populate alloc related environment 386 // variables. 387 func (b *Builder) setAlloc(alloc *structs.Allocation) *Builder { 388 b.allocId = alloc.ID 389 b.allocName = alloc.Name 390 b.groupName = alloc.TaskGroup 391 b.allocIndex = int(alloc.Index()) 392 b.jobName = alloc.Job.Name 393 394 // Set meta 395 combined := alloc.Job.CombinedTaskMeta(alloc.TaskGroup, b.taskName) 396 b.taskMeta = make(map[string]string, len(combined)*2) 397 for k, v := range combined { 398 b.taskMeta[fmt.Sprintf("%s%s", MetaPrefix, strings.ToUpper(k))] = v 399 b.taskMeta[fmt.Sprintf("%s%s", MetaPrefix, k)] = v 400 } 401 402 // Add ports from other tasks 403 b.otherPorts = make(map[string]string, len(alloc.TaskResources)*2) 404 for taskName, resources := range alloc.TaskResources { 405 if taskName == b.taskName { 406 continue 407 } 408 for _, nw := range resources.Networks { 409 for _, p := range nw.ReservedPorts { 410 addPort(b.otherPorts, taskName, nw.IP, p.Label, p.Value) 411 } 412 for _, p := range nw.DynamicPorts { 413 addPort(b.otherPorts, taskName, nw.IP, p.Label, p.Value) 414 } 415 } 416 } 417 return b 418 } 419 420 // setNode is called from NewBuilder to populate node attributes. 421 func (b *Builder) setNode(n *structs.Node) *Builder { 422 b.nodeAttrs = make(map[string]string, 4+len(n.Attributes)+len(n.Meta)) 423 b.nodeAttrs[nodeIdKey] = n.ID 424 b.nodeAttrs[nodeNameKey] = n.Name 425 b.nodeAttrs[nodeClassKey] = n.NodeClass 426 b.nodeAttrs[nodeDcKey] = n.Datacenter 427 b.datacenter = n.Datacenter 428 429 // Set up the attributes. 430 for k, v := range n.Attributes { 431 b.nodeAttrs[fmt.Sprintf("%s%s", nodeAttributePrefix, k)] = v 432 } 433 434 // Set up the meta. 435 for k, v := range n.Meta { 436 b.nodeAttrs[fmt.Sprintf("%s%s", nodeMetaPrefix, k)] = v 437 } 438 return b 439 } 440 441 func (b *Builder) SetAllocDir(dir string) *Builder { 442 b.mu.Lock() 443 b.allocDir = dir 444 b.mu.Unlock() 445 return b 446 } 447 448 func (b *Builder) SetTaskLocalDir(dir string) *Builder { 449 b.mu.Lock() 450 b.localDir = dir 451 b.mu.Unlock() 452 return b 453 } 454 455 func (b *Builder) SetSecretsDir(dir string) *Builder { 456 b.mu.Lock() 457 b.secretsDir = dir 458 b.mu.Unlock() 459 return b 460 } 461 462 // SetDriverNetwork defined by the driver. 463 func (b *Builder) SetDriverNetwork(n *cstructs.DriverNetwork) *Builder { 464 ncopy := n.Copy() 465 b.mu.Lock() 466 b.driverNetwork = ncopy 467 b.mu.Unlock() 468 return b 469 } 470 471 // buildNetworkEnv env vars in the given map. 472 // 473 // Auto: NOMAD_PORT_<label> 474 // Host: NOMAD_IP_<label>, NOMAD_ADDR_<label>, NOMAD_HOST_PORT_<label> 475 // 476 // Handled by setAlloc -> otherPorts: 477 // 478 // Task: NOMAD_TASK_{IP,PORT,ADDR}_<task>_<label> # Always host values 479 // 480 func buildNetworkEnv(envMap map[string]string, nets structs.Networks, driverNet *cstructs.DriverNetwork) { 481 for _, n := range nets { 482 for _, p := range n.ReservedPorts { 483 buildPortEnv(envMap, p, n.IP, driverNet) 484 } 485 for _, p := range n.DynamicPorts { 486 buildPortEnv(envMap, p, n.IP, driverNet) 487 } 488 } 489 } 490 491 func buildPortEnv(envMap map[string]string, p structs.Port, ip string, driverNet *cstructs.DriverNetwork) { 492 // Host IP, port, and address 493 portStr := strconv.Itoa(p.Value) 494 envMap[IpPrefix+p.Label] = ip 495 envMap[HostPortPrefix+p.Label] = portStr 496 envMap[AddrPrefix+p.Label] = net.JoinHostPort(ip, portStr) 497 498 // Set Port to task's value if there's a port map 499 if driverNet != nil && driverNet.PortMap[p.Label] != 0 { 500 envMap[PortPrefix+p.Label] = strconv.Itoa(driverNet.PortMap[p.Label]) 501 } else { 502 // Default to host's 503 envMap[PortPrefix+p.Label] = portStr 504 } 505 } 506 507 // SetHostEnvvars adds the host environment variables to the tasks. The filter 508 // parameter can be use to filter host environment from entering the tasks. 509 func (b *Builder) SetHostEnvvars(filter []string) *Builder { 510 filterMap := make(map[string]struct{}, len(filter)) 511 for _, f := range filter { 512 filterMap[f] = struct{}{} 513 } 514 515 fullHostEnv := os.Environ() 516 filteredHostEnv := make(map[string]string, len(fullHostEnv)) 517 for _, e := range fullHostEnv { 518 parts := strings.SplitN(e, "=", 2) 519 key, value := parts[0], parts[1] 520 521 // Skip filtered environment variables 522 if _, filtered := filterMap[key]; filtered { 523 continue 524 } 525 526 filteredHostEnv[key] = value 527 } 528 529 b.mu.Lock() 530 b.hostEnv = filteredHostEnv 531 b.mu.Unlock() 532 return b 533 } 534 535 func (b *Builder) SetTemplateEnv(m map[string]string) *Builder { 536 b.mu.Lock() 537 b.templateEnv = m 538 b.mu.Unlock() 539 return b 540 } 541 542 func (b *Builder) SetVaultToken(token string, inject bool) *Builder { 543 b.mu.Lock() 544 b.vaultToken = token 545 b.injectVaultToken = inject 546 b.mu.Unlock() 547 return b 548 } 549 550 // addPort keys and values for other tasks to an env var map 551 func addPort(m map[string]string, taskName, ip, portLabel string, port int) { 552 key := fmt.Sprintf("%s%s_%s", AddrPrefix, taskName, portLabel) 553 m[key] = fmt.Sprintf("%s:%d", ip, port) 554 key = fmt.Sprintf("%s%s_%s", IpPrefix, taskName, portLabel) 555 m[key] = ip 556 key = fmt.Sprintf("%s%s_%s", PortPrefix, taskName, portLabel) 557 m[key] = strconv.Itoa(port) 558 }