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