github.com/bigcommerce/nomad@v0.9.3-bc/client/taskenv/env.go (about)

     1  package taskenv
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/hashicorp/nomad/helper"
    12  	hargs "github.com/hashicorp/nomad/helper/args"
    13  	"github.com/hashicorp/nomad/nomad/structs"
    14  	"github.com/hashicorp/nomad/plugins/drivers"
    15  	"github.com/zclconf/go-cty/cty"
    16  )
    17  
    18  // A set of environment variables that are exported by each driver.
    19  const (
    20  	// AllocDir is the environment variable with the path to the alloc directory
    21  	// that is shared across tasks within a task group.
    22  	AllocDir = "NOMAD_ALLOC_DIR"
    23  
    24  	// TaskLocalDir is the environment variable with the path to the tasks local
    25  	// directory where it can store data that is persisted to the alloc is
    26  	// removed.
    27  	TaskLocalDir = "NOMAD_TASK_DIR"
    28  
    29  	// SecretsDir is the environment variable with the path to the tasks secret
    30  	// directory where it can store sensitive data.
    31  	SecretsDir = "NOMAD_SECRETS_DIR"
    32  
    33  	// MemLimit is the environment variable with the tasks memory limit in MBs.
    34  	MemLimit = "NOMAD_MEMORY_LIMIT"
    35  
    36  	// CpuLimit is the environment variable with the tasks CPU limit in MHz.
    37  	CpuLimit = "NOMAD_CPU_LIMIT"
    38  
    39  	// AllocID is the environment variable for passing the allocation ID.
    40  	AllocID = "NOMAD_ALLOC_ID"
    41  
    42  	// AllocName is the environment variable for passing the allocation name.
    43  	AllocName = "NOMAD_ALLOC_NAME"
    44  
    45  	// TaskName is the environment variable for passing the task name.
    46  	TaskName = "NOMAD_TASK_NAME"
    47  
    48  	// GroupName is the environment variable for passing the task group name.
    49  	GroupName = "NOMAD_GROUP_NAME"
    50  
    51  	// JobName is the environment variable for passing the job name.
    52  	JobName = "NOMAD_JOB_NAME"
    53  
    54  	// AllocIndex is the environment variable for passing the allocation index.
    55  	AllocIndex = "NOMAD_ALLOC_INDEX"
    56  
    57  	// Datacenter is the environment variable for passing the datacenter in which the alloc is running.
    58  	Datacenter = "NOMAD_DC"
    59  
    60  	// Region is the environment variable for passing the region in which the alloc is running.
    61  	Region = "NOMAD_REGION"
    62  
    63  	// AddrPrefix is the prefix for passing both dynamic and static port
    64  	// allocations to tasks.
    65  	// E.g $NOMAD_ADDR_http=127.0.0.1:80
    66  	//
    67  	// The ip:port are always the host's.
    68  	AddrPrefix = "NOMAD_ADDR_"
    69  
    70  	// IpPrefix is the prefix for passing the host IP of a port allocation
    71  	// to a task.
    72  	IpPrefix = "NOMAD_IP_"
    73  
    74  	// PortPrefix is the prefix for passing the port allocation to a task.
    75  	// It will be the task's port if a port map is specified. Task's should
    76  	// bind to this port.
    77  	PortPrefix = "NOMAD_PORT_"
    78  
    79  	// HostPortPrefix is the prefix for passing the host port when a port
    80  	// map is specified.
    81  	HostPortPrefix = "NOMAD_HOST_PORT_"
    82  
    83  	// MetaPrefix is the prefix for passing task meta data.
    84  	MetaPrefix = "NOMAD_META_"
    85  
    86  	// VaultToken is the environment variable for passing the Vault token
    87  	VaultToken = "VAULT_TOKEN"
    88  
    89  	// VaultNamespace is the environment variable for passing the Vault namespace, if applicable
    90  	VaultNamespace = "VAULT_NAMESPACE"
    91  )
    92  
    93  // The node values that can be interpreted.
    94  const (
    95  	nodeIdKey     = "node.unique.id"
    96  	nodeDcKey     = "node.datacenter"
    97  	nodeRegionKey = "node.region"
    98  	nodeNameKey   = "node.unique.name"
    99  	nodeClassKey  = "node.class"
   100  
   101  	// Prefixes used for lookups.
   102  	nodeAttributePrefix = "attr."
   103  	nodeMetaPrefix      = "meta."
   104  )
   105  
   106  // TaskEnv is a task's environment as well as node attribute's for
   107  // interpolation.
   108  type TaskEnv struct {
   109  	// NodeAttrs is the map of node attributes for interpolation
   110  	NodeAttrs map[string]string
   111  
   112  	// EnvMap is the map of environment variables
   113  	EnvMap map[string]string
   114  
   115  	// deviceEnv is the environment variables populated from the device hooks.
   116  	deviceEnv map[string]string
   117  
   118  	// envList is a memoized list created by List()
   119  	envList []string
   120  }
   121  
   122  // NewTaskEnv creates a new task environment with the given environment, device
   123  // environment and node attribute maps.
   124  func NewTaskEnv(env, deviceEnv, node map[string]string) *TaskEnv {
   125  	return &TaskEnv{
   126  		NodeAttrs: node,
   127  		deviceEnv: deviceEnv,
   128  		EnvMap:    env,
   129  	}
   130  }
   131  
   132  // NewEmptyTaskEnv creates a new empty task environment.
   133  func NewEmptyTaskEnv() *TaskEnv {
   134  	return &TaskEnv{
   135  		NodeAttrs: make(map[string]string),
   136  		deviceEnv: make(map[string]string),
   137  		EnvMap:    make(map[string]string),
   138  	}
   139  }
   140  
   141  // List returns the task's environment as a slice of NAME=value pair strings.
   142  func (t *TaskEnv) List() []string {
   143  	if t.envList != nil {
   144  		return t.envList
   145  	}
   146  
   147  	env := []string{}
   148  	for k, v := range t.EnvMap {
   149  		env = append(env, fmt.Sprintf("%s=%s", k, v))
   150  	}
   151  
   152  	return env
   153  }
   154  
   155  // DeviceEnv returns the task's environment variables set by device hooks.
   156  func (t *TaskEnv) DeviceEnv() map[string]string {
   157  	m := make(map[string]string, len(t.deviceEnv))
   158  	for k, v := range t.deviceEnv {
   159  		m[k] = v
   160  	}
   161  
   162  	return m
   163  }
   164  
   165  // Map of the task's environment variables.
   166  func (t *TaskEnv) Map() map[string]string {
   167  	m := make(map[string]string, len(t.EnvMap))
   168  	for k, v := range t.EnvMap {
   169  		m[k] = v
   170  	}
   171  
   172  	return m
   173  }
   174  
   175  // All of the task's environment variables and the node's attributes in a
   176  // single map.
   177  func (t *TaskEnv) All() map[string]string {
   178  	m := make(map[string]string, len(t.EnvMap)+len(t.NodeAttrs))
   179  	for k, v := range t.EnvMap {
   180  		m[k] = v
   181  	}
   182  	for k, v := range t.NodeAttrs {
   183  		m[k] = v
   184  	}
   185  
   186  	return m
   187  }
   188  
   189  // AllValues is a map of the task's environment variables and the node's
   190  // attributes with cty.Value (String) values. Errors including keys are
   191  // returned in a map by key name.
   192  //
   193  // In the rare case of a fatal error, only an error value is returned. This is
   194  // likely a programming error as user input should not be able to cause a fatal
   195  // error.
   196  func (t *TaskEnv) AllValues() (map[string]cty.Value, map[string]error, error) {
   197  	errs := make(map[string]error)
   198  
   199  	// Intermediate map for building up nested go types
   200  	allMap := make(map[string]interface{}, len(t.EnvMap)+len(t.NodeAttrs))
   201  
   202  	// Intermediate map for all env vars including those whose keys that
   203  	// cannot be nested (eg foo...bar)
   204  	envMap := make(map[string]cty.Value, len(t.EnvMap))
   205  
   206  	// Prepare job-based variables (eg job.meta, job.group.task.env, etc)
   207  	for k, v := range t.EnvMap {
   208  		if err := addNestedKey(allMap, k, v); err != nil {
   209  			errs[k] = err
   210  		}
   211  		envMap[k] = cty.StringVal(v)
   212  	}
   213  
   214  	// Prepare node-based variables (eg node.*, attr.*, meta.*)
   215  	for k, v := range t.NodeAttrs {
   216  		if err := addNestedKey(allMap, k, v); err != nil {
   217  			errs[k] = err
   218  		}
   219  	}
   220  
   221  	// Add flat envMap as a Map to allMap so users can access any key via
   222  	// HCL2's indexing syntax: ${env["foo...bar"]}
   223  	allMap["env"] = cty.MapVal(envMap)
   224  
   225  	// Add meta and attr to node if they exist to properly namespace things
   226  	// a bit.
   227  	nodeMapI, ok := allMap["node"]
   228  	if !ok {
   229  		return nil, nil, fmt.Errorf("missing node variable")
   230  	}
   231  	nodeMap, ok := nodeMapI.(map[string]interface{})
   232  	if !ok {
   233  		return nil, nil, fmt.Errorf("invalid type for node variable: %T", nodeMapI)
   234  	}
   235  	if attrMap, ok := allMap["attr"]; ok {
   236  		nodeMap["attr"] = attrMap
   237  	}
   238  	if metaMap, ok := allMap["meta"]; ok {
   239  		nodeMap["meta"] = metaMap
   240  	}
   241  
   242  	// ctyify the entire tree of strings and maps
   243  	tree, err := ctyify(allMap)
   244  	if err != nil {
   245  		// This should not be possible and is likely a programming
   246  		// error. Invalid user input should be cleaned earlier.
   247  		return nil, nil, err
   248  	}
   249  
   250  	return tree, errs, nil
   251  }
   252  
   253  // ParseAndReplace takes the user supplied args replaces any instance of an
   254  // environment variable or Nomad variable in the args with the actual value.
   255  func (t *TaskEnv) ParseAndReplace(args []string) []string {
   256  	if args == nil {
   257  		return nil
   258  	}
   259  
   260  	replaced := make([]string, len(args))
   261  	for i, arg := range args {
   262  		replaced[i] = hargs.ReplaceEnv(arg, t.EnvMap, t.NodeAttrs)
   263  	}
   264  
   265  	return replaced
   266  }
   267  
   268  // ReplaceEnv takes an arg and replaces all occurrences of environment variables
   269  // and Nomad variables.  If the variable is found in the passed map it is
   270  // replaced, otherwise the original string is returned.
   271  func (t *TaskEnv) ReplaceEnv(arg string) string {
   272  	return hargs.ReplaceEnv(arg, t.EnvMap, t.NodeAttrs)
   273  }
   274  
   275  // Builder is used to build task environment's and is safe for concurrent use.
   276  type Builder struct {
   277  	// envvars are custom set environment variables
   278  	envvars map[string]string
   279  
   280  	// templateEnv are env vars set from templates
   281  	templateEnv map[string]string
   282  
   283  	// hostEnv are environment variables filtered from the host
   284  	hostEnv map[string]string
   285  
   286  	// nodeAttrs are Node attributes and metadata
   287  	nodeAttrs map[string]string
   288  
   289  	// taskMeta are the meta attributes on the task
   290  	taskMeta map[string]string
   291  
   292  	// allocDir from task's perspective; eg /alloc
   293  	allocDir string
   294  
   295  	// localDir from task's perspective; eg /local
   296  	localDir string
   297  
   298  	// secretsDir from task's perspective; eg /secrets
   299  	secretsDir string
   300  
   301  	cpuLimit         int64
   302  	memLimit         int64
   303  	taskName         string
   304  	allocIndex       int
   305  	datacenter       string
   306  	region           string
   307  	allocId          string
   308  	allocName        string
   309  	groupName        string
   310  	vaultToken       string
   311  	vaultNamespace   string
   312  	injectVaultToken bool
   313  	jobName          string
   314  
   315  	// otherPorts for tasks in the same alloc
   316  	otherPorts map[string]string
   317  
   318  	// driverNetwork is the network defined by the driver (or nil if none
   319  	// was defined).
   320  	driverNetwork *drivers.DriverNetwork
   321  
   322  	// network resources from the task; must be lazily turned into env vars
   323  	// because portMaps and advertiseIP can change after builder creation
   324  	// and affect network env vars.
   325  	networks []*structs.NetworkResource
   326  
   327  	// hookEnvs are env vars set by hooks and stored by hook name to
   328  	// support adding/removing vars from multiple hooks (eg HookA adds A:1,
   329  	// HookB adds A:2, HookA removes A, A should equal 2)
   330  	hookEnvs map[string]map[string]string
   331  
   332  	// hookNames is a slice of hooks in hookEnvs to apply hookEnvs in the
   333  	// order the hooks are run.
   334  	hookNames []string
   335  
   336  	// deviceHookName is the device hook name. It is set only if device hooks
   337  	// are set. While a bit round about, this enables us to return device hook
   338  	// environment variables without having to hardcode the name of the hook.
   339  	deviceHookName string
   340  
   341  	mu *sync.RWMutex
   342  }
   343  
   344  // NewBuilder creates a new task environment builder.
   345  func NewBuilder(node *structs.Node, alloc *structs.Allocation, task *structs.Task, region string) *Builder {
   346  	b := NewEmptyBuilder()
   347  	b.region = region
   348  	return b.setTask(task).setAlloc(alloc).setNode(node)
   349  }
   350  
   351  // NewEmptyBuilder creates a new environment builder.
   352  func NewEmptyBuilder() *Builder {
   353  	return &Builder{
   354  		mu:       &sync.RWMutex{},
   355  		hookEnvs: map[string]map[string]string{},
   356  		envvars:  make(map[string]string),
   357  	}
   358  }
   359  
   360  // Build must be called after all the tasks environment values have been set.
   361  func (b *Builder) Build() *TaskEnv {
   362  	nodeAttrs := make(map[string]string)
   363  	envMap := make(map[string]string)
   364  	var deviceEnvs map[string]string
   365  
   366  	b.mu.RLock()
   367  	defer b.mu.RUnlock()
   368  
   369  	// Add the directories
   370  	if b.allocDir != "" {
   371  		envMap[AllocDir] = b.allocDir
   372  	}
   373  	if b.localDir != "" {
   374  		envMap[TaskLocalDir] = b.localDir
   375  	}
   376  	if b.secretsDir != "" {
   377  		envMap[SecretsDir] = b.secretsDir
   378  	}
   379  
   380  	// Add the resource limits
   381  	if b.memLimit != 0 {
   382  		envMap[MemLimit] = strconv.FormatInt(b.memLimit, 10)
   383  	}
   384  	if b.cpuLimit != 0 {
   385  		envMap[CpuLimit] = strconv.FormatInt(b.cpuLimit, 10)
   386  	}
   387  
   388  	// Add the task metadata
   389  	if b.allocId != "" {
   390  		envMap[AllocID] = b.allocId
   391  	}
   392  	if b.allocName != "" {
   393  		envMap[AllocName] = b.allocName
   394  	}
   395  	if b.groupName != "" {
   396  		envMap[GroupName] = b.groupName
   397  	}
   398  	if b.allocIndex != -1 {
   399  		envMap[AllocIndex] = strconv.Itoa(b.allocIndex)
   400  	}
   401  	if b.taskName != "" {
   402  		envMap[TaskName] = b.taskName
   403  	}
   404  	if b.jobName != "" {
   405  		envMap[JobName] = b.jobName
   406  	}
   407  	if b.datacenter != "" {
   408  		envMap[Datacenter] = b.datacenter
   409  	}
   410  	if b.region != "" {
   411  		envMap[Region] = b.region
   412  
   413  		// Copy region over to node attrs
   414  		nodeAttrs[nodeRegionKey] = b.region
   415  	}
   416  
   417  	// Build the network related env vars
   418  	buildNetworkEnv(envMap, b.networks, b.driverNetwork)
   419  
   420  	// Build the addr of the other tasks
   421  	for k, v := range b.otherPorts {
   422  		envMap[k] = v
   423  	}
   424  
   425  	// Build the Vault Token
   426  	if b.injectVaultToken && b.vaultToken != "" {
   427  		envMap[VaultToken] = b.vaultToken
   428  	}
   429  
   430  	// Build the Vault Namespace
   431  	if b.injectVaultToken && b.vaultNamespace != "" {
   432  		envMap[VaultNamespace] = b.vaultNamespace
   433  	}
   434  
   435  	// Copy task meta
   436  	for k, v := range b.taskMeta {
   437  		envMap[k] = v
   438  	}
   439  
   440  	// Copy node attributes
   441  	for k, v := range b.nodeAttrs {
   442  		nodeAttrs[k] = v
   443  	}
   444  
   445  	// Interpolate and add environment variables
   446  	for k, v := range b.hostEnv {
   447  		envMap[k] = hargs.ReplaceEnv(v, nodeAttrs, envMap)
   448  	}
   449  
   450  	// Copy interpolated task env vars second as they override host env vars
   451  	for k, v := range b.envvars {
   452  		envMap[k] = hargs.ReplaceEnv(v, nodeAttrs, envMap)
   453  	}
   454  
   455  	// Copy hook env vars in the order the hooks were run
   456  	for _, h := range b.hookNames {
   457  		for k, v := range b.hookEnvs[h] {
   458  			e := hargs.ReplaceEnv(v, nodeAttrs, envMap)
   459  			envMap[k] = e
   460  
   461  			if h == b.deviceHookName {
   462  				if deviceEnvs == nil {
   463  					deviceEnvs = make(map[string]string, len(b.hookEnvs[h]))
   464  				}
   465  
   466  				deviceEnvs[k] = e
   467  			}
   468  		}
   469  	}
   470  
   471  	// Copy template env vars as they override task env vars
   472  	for k, v := range b.templateEnv {
   473  		envMap[k] = v
   474  	}
   475  
   476  	// Clean keys (see #2405)
   477  	cleanedEnv := make(map[string]string, len(envMap))
   478  	for k, v := range envMap {
   479  		cleanedK := helper.CleanEnvVar(k, '_')
   480  		cleanedEnv[cleanedK] = v
   481  	}
   482  
   483  	var cleanedDeviceEnvs map[string]string
   484  	if deviceEnvs != nil {
   485  		cleanedDeviceEnvs = make(map[string]string, len(deviceEnvs))
   486  		for k, v := range deviceEnvs {
   487  			cleanedK := helper.CleanEnvVar(k, '_')
   488  			cleanedDeviceEnvs[cleanedK] = v
   489  		}
   490  	}
   491  
   492  	return NewTaskEnv(cleanedEnv, cleanedDeviceEnvs, nodeAttrs)
   493  }
   494  
   495  // Update task updates the environment based on a new alloc and task.
   496  func (b *Builder) UpdateTask(alloc *structs.Allocation, task *structs.Task) *Builder {
   497  	b.mu.Lock()
   498  	defer b.mu.Unlock()
   499  	return b.setTask(task).setAlloc(alloc)
   500  }
   501  
   502  // SetHookEnv sets environment variables from a hook. Variables are
   503  // Last-Write-Wins, so if a hook writes a variable that's also written by a
   504  // later hook, the later hooks value always gets used.
   505  func (b *Builder) SetHookEnv(hook string, envs map[string]string) *Builder {
   506  	b.mu.Lock()
   507  	defer b.mu.Unlock()
   508  	return b.setHookEnvLocked(hook, envs)
   509  }
   510  
   511  // setHookEnvLocked is the implementation of setting hook environment variables
   512  // and should be called with the lock held
   513  func (b *Builder) setHookEnvLocked(hook string, envs map[string]string) *Builder {
   514  	if _, exists := b.hookEnvs[hook]; !exists {
   515  		b.hookNames = append(b.hookNames, hook)
   516  	}
   517  	b.hookEnvs[hook] = envs
   518  
   519  	return b
   520  }
   521  
   522  // SetDeviceHookEnv sets environment variables from a device hook. Variables are
   523  // Last-Write-Wins, so if a hook writes a variable that's also written by a
   524  // later hook, the later hooks value always gets used.
   525  func (b *Builder) SetDeviceHookEnv(hookName string, envs map[string]string) *Builder {
   526  	b.mu.Lock()
   527  	defer b.mu.Unlock()
   528  
   529  	// Store the device hook name
   530  	b.deviceHookName = hookName
   531  	return b.setHookEnvLocked(hookName, envs)
   532  }
   533  
   534  // setTask is called from NewBuilder to populate task related environment
   535  // variables.
   536  func (b *Builder) setTask(task *structs.Task) *Builder {
   537  	b.taskName = task.Name
   538  	b.envvars = make(map[string]string, len(task.Env))
   539  	for k, v := range task.Env {
   540  		b.envvars[k] = v
   541  	}
   542  
   543  	// COMPAT(0.11): Remove in 0.11
   544  	if task.Resources == nil {
   545  		b.memLimit = 0
   546  		b.cpuLimit = 0
   547  	} else {
   548  		b.memLimit = int64(task.Resources.MemoryMB)
   549  		b.cpuLimit = int64(task.Resources.CPU)
   550  	}
   551  	return b
   552  }
   553  
   554  // setAlloc is called from NewBuilder to populate alloc related environment
   555  // variables.
   556  func (b *Builder) setAlloc(alloc *structs.Allocation) *Builder {
   557  	b.allocId = alloc.ID
   558  	b.allocName = alloc.Name
   559  	b.groupName = alloc.TaskGroup
   560  	b.allocIndex = int(alloc.Index())
   561  	b.jobName = alloc.Job.Name
   562  
   563  	// Set meta
   564  	combined := alloc.Job.CombinedTaskMeta(alloc.TaskGroup, b.taskName)
   565  	// taskMetaSize is double to total meta keys to account for given and upper
   566  	// cased values
   567  	taskMetaSize := len(combined) * 2
   568  
   569  	// if job is parameterized initialize optional meta to empty strings
   570  	if alloc.Job.Dispatched {
   571  		optionalMetaCount := len(alloc.Job.ParameterizedJob.MetaOptional)
   572  		b.taskMeta = make(map[string]string, taskMetaSize+optionalMetaCount*2)
   573  
   574  		for _, k := range alloc.Job.ParameterizedJob.MetaOptional {
   575  			b.taskMeta[fmt.Sprintf("%s%s", MetaPrefix, strings.ToUpper(k))] = ""
   576  			b.taskMeta[fmt.Sprintf("%s%s", MetaPrefix, k)] = ""
   577  		}
   578  	} else {
   579  		b.taskMeta = make(map[string]string, taskMetaSize)
   580  	}
   581  
   582  	for k, v := range combined {
   583  		b.taskMeta[fmt.Sprintf("%s%s", MetaPrefix, strings.ToUpper(k))] = v
   584  		b.taskMeta[fmt.Sprintf("%s%s", MetaPrefix, k)] = v
   585  	}
   586  
   587  	// COMPAT(0.11): Remove in 0.11
   588  	b.otherPorts = make(map[string]string, len(alloc.Job.LookupTaskGroup(alloc.TaskGroup).Tasks)*2)
   589  	if alloc.AllocatedResources != nil {
   590  		// Populate task resources
   591  		if tr, ok := alloc.AllocatedResources.Tasks[b.taskName]; ok {
   592  			b.cpuLimit = tr.Cpu.CpuShares
   593  			b.memLimit = tr.Memory.MemoryMB
   594  
   595  			// Copy networks to prevent sharing
   596  			b.networks = make([]*structs.NetworkResource, len(tr.Networks))
   597  			for i, n := range tr.Networks {
   598  				b.networks[i] = n.Copy()
   599  			}
   600  		}
   601  
   602  		// Add ports from other tasks
   603  		for taskName, resources := range alloc.AllocatedResources.Tasks {
   604  			// Add ports from other tasks
   605  			if taskName == b.taskName {
   606  				continue
   607  			}
   608  
   609  			for _, nw := range resources.Networks {
   610  				for _, p := range nw.ReservedPorts {
   611  					addPort(b.otherPorts, taskName, nw.IP, p.Label, p.Value)
   612  				}
   613  				for _, p := range nw.DynamicPorts {
   614  					addPort(b.otherPorts, taskName, nw.IP, p.Label, p.Value)
   615  				}
   616  			}
   617  		}
   618  	} else if alloc.TaskResources != nil {
   619  		if tr, ok := alloc.TaskResources[b.taskName]; ok {
   620  			// Copy networks to prevent sharing
   621  			b.networks = make([]*structs.NetworkResource, len(tr.Networks))
   622  			for i, n := range tr.Networks {
   623  				b.networks[i] = n.Copy()
   624  			}
   625  
   626  		}
   627  
   628  		for taskName, resources := range alloc.TaskResources {
   629  			// Add ports from other tasks
   630  			if taskName == b.taskName {
   631  				continue
   632  			}
   633  			for _, nw := range resources.Networks {
   634  				for _, p := range nw.ReservedPorts {
   635  					addPort(b.otherPorts, taskName, nw.IP, p.Label, p.Value)
   636  				}
   637  				for _, p := range nw.DynamicPorts {
   638  					addPort(b.otherPorts, taskName, nw.IP, p.Label, p.Value)
   639  				}
   640  			}
   641  		}
   642  	}
   643  	return b
   644  }
   645  
   646  // setNode is called from NewBuilder to populate node attributes.
   647  func (b *Builder) setNode(n *structs.Node) *Builder {
   648  	b.nodeAttrs = make(map[string]string, 4+len(n.Attributes)+len(n.Meta))
   649  	b.nodeAttrs[nodeIdKey] = n.ID
   650  	b.nodeAttrs[nodeNameKey] = n.Name
   651  	b.nodeAttrs[nodeClassKey] = n.NodeClass
   652  	b.nodeAttrs[nodeDcKey] = n.Datacenter
   653  	b.datacenter = n.Datacenter
   654  
   655  	// Set up the attributes.
   656  	for k, v := range n.Attributes {
   657  		b.nodeAttrs[fmt.Sprintf("%s%s", nodeAttributePrefix, k)] = v
   658  	}
   659  
   660  	// Set up the meta.
   661  	for k, v := range n.Meta {
   662  		b.nodeAttrs[fmt.Sprintf("%s%s", nodeMetaPrefix, k)] = v
   663  	}
   664  	return b
   665  }
   666  
   667  func (b *Builder) SetAllocDir(dir string) *Builder {
   668  	b.mu.Lock()
   669  	b.allocDir = dir
   670  	b.mu.Unlock()
   671  	return b
   672  }
   673  
   674  func (b *Builder) SetTaskLocalDir(dir string) *Builder {
   675  	b.mu.Lock()
   676  	b.localDir = dir
   677  	b.mu.Unlock()
   678  	return b
   679  }
   680  
   681  func (b *Builder) SetSecretsDir(dir string) *Builder {
   682  	b.mu.Lock()
   683  	b.secretsDir = dir
   684  	b.mu.Unlock()
   685  	return b
   686  }
   687  
   688  // SetDriverNetwork defined by the driver.
   689  func (b *Builder) SetDriverNetwork(n *drivers.DriverNetwork) *Builder {
   690  	ncopy := n.Copy()
   691  	b.mu.Lock()
   692  	b.driverNetwork = ncopy
   693  	b.mu.Unlock()
   694  	return b
   695  }
   696  
   697  // buildNetworkEnv env vars in the given map.
   698  //
   699  //	Auto:   NOMAD_PORT_<label>
   700  //	Host:   NOMAD_IP_<label>, NOMAD_ADDR_<label>, NOMAD_HOST_PORT_<label>
   701  //
   702  // Handled by setAlloc -> otherPorts:
   703  //
   704  //	Task:   NOMAD_TASK_{IP,PORT,ADDR}_<task>_<label> # Always host values
   705  //
   706  func buildNetworkEnv(envMap map[string]string, nets structs.Networks, driverNet *drivers.DriverNetwork) {
   707  	for _, n := range nets {
   708  		for _, p := range n.ReservedPorts {
   709  			buildPortEnv(envMap, p, n.IP, driverNet)
   710  		}
   711  		for _, p := range n.DynamicPorts {
   712  			buildPortEnv(envMap, p, n.IP, driverNet)
   713  		}
   714  	}
   715  }
   716  
   717  func buildPortEnv(envMap map[string]string, p structs.Port, ip string, driverNet *drivers.DriverNetwork) {
   718  	// Host IP, port, and address
   719  	portStr := strconv.Itoa(p.Value)
   720  	envMap[IpPrefix+p.Label] = ip
   721  	envMap[HostPortPrefix+p.Label] = portStr
   722  	envMap[AddrPrefix+p.Label] = net.JoinHostPort(ip, portStr)
   723  
   724  	// Set Port to task's value if there's a port map
   725  	if driverNet != nil && driverNet.PortMap[p.Label] != 0 {
   726  		envMap[PortPrefix+p.Label] = strconv.Itoa(driverNet.PortMap[p.Label])
   727  	} else {
   728  		// Default to host's
   729  		envMap[PortPrefix+p.Label] = portStr
   730  	}
   731  }
   732  
   733  // SetHostEnvvars adds the host environment variables to the tasks. The filter
   734  // parameter can be use to filter host environment from entering the tasks.
   735  func (b *Builder) SetHostEnvvars(filter []string) *Builder {
   736  	filterMap := make(map[string]struct{}, len(filter))
   737  	for _, f := range filter {
   738  		filterMap[f] = struct{}{}
   739  	}
   740  
   741  	fullHostEnv := os.Environ()
   742  	filteredHostEnv := make(map[string]string, len(fullHostEnv))
   743  	for _, e := range fullHostEnv {
   744  		parts := strings.SplitN(e, "=", 2)
   745  		key, value := parts[0], parts[1]
   746  
   747  		// Skip filtered environment variables
   748  		if _, filtered := filterMap[key]; filtered {
   749  			continue
   750  		}
   751  
   752  		filteredHostEnv[key] = value
   753  	}
   754  
   755  	b.mu.Lock()
   756  	b.hostEnv = filteredHostEnv
   757  	b.mu.Unlock()
   758  	return b
   759  }
   760  
   761  func (b *Builder) SetTemplateEnv(m map[string]string) *Builder {
   762  	b.mu.Lock()
   763  	b.templateEnv = m
   764  	b.mu.Unlock()
   765  	return b
   766  }
   767  
   768  func (b *Builder) SetVaultToken(token, namespace string, inject bool) *Builder {
   769  	b.mu.Lock()
   770  	b.vaultToken = token
   771  	b.vaultNamespace = namespace
   772  	b.injectVaultToken = inject
   773  	b.mu.Unlock()
   774  	return b
   775  }
   776  
   777  // addPort keys and values for other tasks to an env var map
   778  func addPort(m map[string]string, taskName, ip, portLabel string, port int) {
   779  	key := fmt.Sprintf("%s%s_%s", AddrPrefix, taskName, portLabel)
   780  	m[key] = fmt.Sprintf("%s:%d", ip, port)
   781  	key = fmt.Sprintf("%s%s_%s", IpPrefix, taskName, portLabel)
   782  	m[key] = ip
   783  	key = fmt.Sprintf("%s%s_%s", PortPrefix, taskName, portLabel)
   784  	m[key] = strconv.Itoa(port)
   785  }