github.com/emate/nomad@v0.8.2-wo-binpacking/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 if args == nil { 166 return nil 167 } 168 169 replaced := make([]string, len(args)) 170 for i, arg := range args { 171 replaced[i] = hargs.ReplaceEnv(arg, t.EnvMap, t.NodeAttrs) 172 } 173 174 return replaced 175 } 176 177 // ReplaceEnv takes an arg and replaces all occurrences of environment variables 178 // and Nomad variables. If the variable is found in the passed map it is 179 // replaced, otherwise the original string is returned. 180 func (t *TaskEnv) ReplaceEnv(arg string) string { 181 return hargs.ReplaceEnv(arg, t.EnvMap, t.NodeAttrs) 182 } 183 184 // Builder is used to build task environment's and is safe for concurrent use. 185 type Builder struct { 186 // envvars are custom set environment variables 187 envvars map[string]string 188 189 // templateEnv are env vars set from templates 190 templateEnv map[string]string 191 192 // hostEnv are environment variables filtered from the host 193 hostEnv map[string]string 194 195 // nodeAttrs are Node attributes and metadata 196 nodeAttrs map[string]string 197 198 // taskMeta are the meta attributes on the task 199 taskMeta map[string]string 200 201 // allocDir from task's perspective; eg /alloc 202 allocDir string 203 204 // localDir from task's perspective; eg /local 205 localDir string 206 207 // secretsDir from task's perspective; eg /secrets 208 secretsDir string 209 210 cpuLimit int 211 memLimit int 212 taskName string 213 allocIndex int 214 datacenter string 215 region string 216 allocId string 217 allocName string 218 groupName string 219 vaultToken string 220 injectVaultToken bool 221 jobName string 222 223 // otherPorts for tasks in the same alloc 224 otherPorts map[string]string 225 226 // driverNetwork is the network defined by the driver (or nil if none 227 // was defined). 228 driverNetwork *cstructs.DriverNetwork 229 230 // network resources from the task; must be lazily turned into env vars 231 // because portMaps and advertiseIP can change after builder creation 232 // and affect network env vars. 233 networks []*structs.NetworkResource 234 235 mu *sync.RWMutex 236 } 237 238 // NewBuilder creates a new task environment builder. 239 func NewBuilder(node *structs.Node, alloc *structs.Allocation, task *structs.Task, region string) *Builder { 240 b := &Builder{ 241 region: region, 242 mu: &sync.RWMutex{}, 243 } 244 return b.setTask(task).setAlloc(alloc).setNode(node) 245 } 246 247 // NewEmptyBuilder creates a new environment builder. 248 func NewEmptyBuilder() *Builder { 249 return &Builder{ 250 mu: &sync.RWMutex{}, 251 } 252 } 253 254 // Build must be called after all the tasks environment values have been set. 255 func (b *Builder) Build() *TaskEnv { 256 nodeAttrs := make(map[string]string) 257 envMap := make(map[string]string) 258 259 b.mu.RLock() 260 defer b.mu.RUnlock() 261 262 // Add the directories 263 if b.allocDir != "" { 264 envMap[AllocDir] = b.allocDir 265 } 266 if b.localDir != "" { 267 envMap[TaskLocalDir] = b.localDir 268 } 269 if b.secretsDir != "" { 270 envMap[SecretsDir] = b.secretsDir 271 } 272 273 // Add the resource limits 274 if b.memLimit != 0 { 275 envMap[MemLimit] = strconv.Itoa(b.memLimit) 276 } 277 if b.cpuLimit != 0 { 278 envMap[CpuLimit] = strconv.Itoa(b.cpuLimit) 279 } 280 281 // Add the task metadata 282 if b.allocId != "" { 283 envMap[AllocID] = b.allocId 284 } 285 if b.allocName != "" { 286 envMap[AllocName] = b.allocName 287 } 288 if b.groupName != "" { 289 envMap[GroupName] = b.groupName 290 } 291 if b.allocIndex != -1 { 292 envMap[AllocIndex] = strconv.Itoa(b.allocIndex) 293 } 294 if b.taskName != "" { 295 envMap[TaskName] = b.taskName 296 } 297 if b.jobName != "" { 298 envMap[JobName] = b.jobName 299 } 300 if b.datacenter != "" { 301 envMap[Datacenter] = b.datacenter 302 } 303 if b.region != "" { 304 envMap[Region] = b.region 305 306 // Copy region over to node attrs 307 nodeAttrs[nodeRegionKey] = b.region 308 } 309 310 // Build the network related env vars 311 buildNetworkEnv(envMap, b.networks, b.driverNetwork) 312 313 // Build the addr of the other tasks 314 for k, v := range b.otherPorts { 315 envMap[k] = v 316 } 317 318 // Build the Vault Token 319 if b.injectVaultToken && b.vaultToken != "" { 320 envMap[VaultToken] = b.vaultToken 321 } 322 323 // Copy task meta 324 for k, v := range b.taskMeta { 325 envMap[k] = v 326 } 327 328 // Copy node attributes 329 for k, v := range b.nodeAttrs { 330 nodeAttrs[k] = v 331 } 332 333 // Interpolate and add environment variables 334 for k, v := range b.hostEnv { 335 envMap[k] = hargs.ReplaceEnv(v, nodeAttrs, envMap) 336 } 337 338 // Copy interpolated task env vars second as they override host env vars 339 for k, v := range b.envvars { 340 envMap[k] = hargs.ReplaceEnv(v, nodeAttrs, envMap) 341 } 342 343 // Copy template env vars third as they override task env vars 344 for k, v := range b.templateEnv { 345 envMap[k] = v 346 } 347 348 // Clean keys (see #2405) 349 cleanedEnv := make(map[string]string, len(envMap)) 350 for k, v := range envMap { 351 cleanedK := helper.CleanEnvVar(k, '_') 352 cleanedEnv[cleanedK] = v 353 } 354 355 return NewTaskEnv(cleanedEnv, nodeAttrs) 356 } 357 358 // Update task updates the environment based on a new alloc and task. 359 func (b *Builder) UpdateTask(alloc *structs.Allocation, task *structs.Task) *Builder { 360 b.mu.Lock() 361 defer b.mu.Unlock() 362 return b.setTask(task).setAlloc(alloc) 363 } 364 365 // setTask is called from NewBuilder to populate task related environment 366 // variables. 367 func (b *Builder) setTask(task *structs.Task) *Builder { 368 b.taskName = task.Name 369 b.envvars = make(map[string]string, len(task.Env)) 370 for k, v := range task.Env { 371 b.envvars[k] = v 372 } 373 if task.Resources == nil { 374 b.memLimit = 0 375 b.cpuLimit = 0 376 b.networks = []*structs.NetworkResource{} 377 } else { 378 b.memLimit = task.Resources.MemoryMB 379 b.cpuLimit = task.Resources.CPU 380 // Copy networks to prevent sharing 381 b.networks = make([]*structs.NetworkResource, len(task.Resources.Networks)) 382 for i, n := range task.Resources.Networks { 383 b.networks[i] = n.Copy() 384 } 385 } 386 return b 387 } 388 389 // setAlloc is called from NewBuilder to populate alloc related environment 390 // variables. 391 func (b *Builder) setAlloc(alloc *structs.Allocation) *Builder { 392 b.allocId = alloc.ID 393 b.allocName = alloc.Name 394 b.groupName = alloc.TaskGroup 395 b.allocIndex = int(alloc.Index()) 396 b.jobName = alloc.Job.Name 397 398 // Set meta 399 combined := alloc.Job.CombinedTaskMeta(alloc.TaskGroup, b.taskName) 400 b.taskMeta = make(map[string]string, len(combined)*2) 401 for k, v := range combined { 402 b.taskMeta[fmt.Sprintf("%s%s", MetaPrefix, strings.ToUpper(k))] = v 403 b.taskMeta[fmt.Sprintf("%s%s", MetaPrefix, k)] = v 404 } 405 406 // Add ports from other tasks 407 b.otherPorts = make(map[string]string, len(alloc.TaskResources)*2) 408 for taskName, resources := range alloc.TaskResources { 409 if taskName == b.taskName { 410 continue 411 } 412 for _, nw := range resources.Networks { 413 for _, p := range nw.ReservedPorts { 414 addPort(b.otherPorts, taskName, nw.IP, p.Label, p.Value) 415 } 416 for _, p := range nw.DynamicPorts { 417 addPort(b.otherPorts, taskName, nw.IP, p.Label, p.Value) 418 } 419 } 420 } 421 return b 422 } 423 424 // setNode is called from NewBuilder to populate node attributes. 425 func (b *Builder) setNode(n *structs.Node) *Builder { 426 b.nodeAttrs = make(map[string]string, 4+len(n.Attributes)+len(n.Meta)) 427 b.nodeAttrs[nodeIdKey] = n.ID 428 b.nodeAttrs[nodeNameKey] = n.Name 429 b.nodeAttrs[nodeClassKey] = n.NodeClass 430 b.nodeAttrs[nodeDcKey] = n.Datacenter 431 b.datacenter = n.Datacenter 432 433 // Set up the attributes. 434 for k, v := range n.Attributes { 435 b.nodeAttrs[fmt.Sprintf("%s%s", nodeAttributePrefix, k)] = v 436 } 437 438 // Set up the meta. 439 for k, v := range n.Meta { 440 b.nodeAttrs[fmt.Sprintf("%s%s", nodeMetaPrefix, k)] = v 441 } 442 return b 443 } 444 445 func (b *Builder) SetAllocDir(dir string) *Builder { 446 b.mu.Lock() 447 b.allocDir = dir 448 b.mu.Unlock() 449 return b 450 } 451 452 func (b *Builder) SetTaskLocalDir(dir string) *Builder { 453 b.mu.Lock() 454 b.localDir = dir 455 b.mu.Unlock() 456 return b 457 } 458 459 func (b *Builder) SetSecretsDir(dir string) *Builder { 460 b.mu.Lock() 461 b.secretsDir = dir 462 b.mu.Unlock() 463 return b 464 } 465 466 // SetDriverNetwork defined by the driver. 467 func (b *Builder) SetDriverNetwork(n *cstructs.DriverNetwork) *Builder { 468 ncopy := n.Copy() 469 b.mu.Lock() 470 b.driverNetwork = ncopy 471 b.mu.Unlock() 472 return b 473 } 474 475 // buildNetworkEnv env vars in the given map. 476 // 477 // Auto: NOMAD_PORT_<label> 478 // Host: NOMAD_IP_<label>, NOMAD_ADDR_<label>, NOMAD_HOST_PORT_<label> 479 // 480 // Handled by setAlloc -> otherPorts: 481 // 482 // Task: NOMAD_TASK_{IP,PORT,ADDR}_<task>_<label> # Always host values 483 // 484 func buildNetworkEnv(envMap map[string]string, nets structs.Networks, driverNet *cstructs.DriverNetwork) { 485 for _, n := range nets { 486 for _, p := range n.ReservedPorts { 487 buildPortEnv(envMap, p, n.IP, driverNet) 488 } 489 for _, p := range n.DynamicPorts { 490 buildPortEnv(envMap, p, n.IP, driverNet) 491 } 492 } 493 } 494 495 func buildPortEnv(envMap map[string]string, p structs.Port, ip string, driverNet *cstructs.DriverNetwork) { 496 // Host IP, port, and address 497 portStr := strconv.Itoa(p.Value) 498 envMap[IpPrefix+p.Label] = ip 499 envMap[HostPortPrefix+p.Label] = portStr 500 envMap[AddrPrefix+p.Label] = net.JoinHostPort(ip, portStr) 501 502 // Set Port to task's value if there's a port map 503 if driverNet != nil && driverNet.PortMap[p.Label] != 0 { 504 envMap[PortPrefix+p.Label] = strconv.Itoa(driverNet.PortMap[p.Label]) 505 } else { 506 // Default to host's 507 envMap[PortPrefix+p.Label] = portStr 508 } 509 } 510 511 // SetHostEnvvars adds the host environment variables to the tasks. The filter 512 // parameter can be use to filter host environment from entering the tasks. 513 func (b *Builder) SetHostEnvvars(filter []string) *Builder { 514 filterMap := make(map[string]struct{}, len(filter)) 515 for _, f := range filter { 516 filterMap[f] = struct{}{} 517 } 518 519 fullHostEnv := os.Environ() 520 filteredHostEnv := make(map[string]string, len(fullHostEnv)) 521 for _, e := range fullHostEnv { 522 parts := strings.SplitN(e, "=", 2) 523 key, value := parts[0], parts[1] 524 525 // Skip filtered environment variables 526 if _, filtered := filterMap[key]; filtered { 527 continue 528 } 529 530 filteredHostEnv[key] = value 531 } 532 533 b.mu.Lock() 534 b.hostEnv = filteredHostEnv 535 b.mu.Unlock() 536 return b 537 } 538 539 func (b *Builder) SetTemplateEnv(m map[string]string) *Builder { 540 b.mu.Lock() 541 b.templateEnv = m 542 b.mu.Unlock() 543 return b 544 } 545 546 func (b *Builder) SetVaultToken(token string, inject bool) *Builder { 547 b.mu.Lock() 548 b.vaultToken = token 549 b.injectVaultToken = inject 550 b.mu.Unlock() 551 return b 552 } 553 554 // addPort keys and values for other tasks to an env var map 555 func addPort(m map[string]string, taskName, ip, portLabel string, port int) { 556 key := fmt.Sprintf("%s%s_%s", AddrPrefix, taskName, portLabel) 557 m[key] = fmt.Sprintf("%s:%d", ip, port) 558 key = fmt.Sprintf("%s%s_%s", IpPrefix, taskName, portLabel) 559 m[key] = ip 560 key = fmt.Sprintf("%s%s_%s", PortPrefix, taskName, portLabel) 561 m[key] = strconv.Itoa(port) 562 }