github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/client/allocrunner/taskrunner/template/template.go (about)

     1  package template
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math/rand"
     8  	"os"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  	"sync"
    13  	"time"
    14  
    15  	ctconf "github.com/hashicorp/consul-template/config"
    16  	"github.com/hashicorp/consul-template/manager"
    17  	"github.com/hashicorp/consul-template/signals"
    18  	envparse "github.com/hashicorp/go-envparse"
    19  	multierror "github.com/hashicorp/go-multierror"
    20  	"github.com/hashicorp/nomad/client/allocrunner/taskrunner/interfaces"
    21  	"github.com/hashicorp/nomad/client/config"
    22  	"github.com/hashicorp/nomad/client/taskenv"
    23  	"github.com/hashicorp/nomad/helper"
    24  	"github.com/hashicorp/nomad/nomad/structs"
    25  )
    26  
    27  const (
    28  	// consulTemplateSourceName is the source name when using the TaskHooks.
    29  	consulTemplateSourceName = "Template"
    30  
    31  	// missingDepEventLimit is the number of missing dependencies that will be
    32  	// logged before we switch to showing just the number of missing
    33  	// dependencies.
    34  	missingDepEventLimit = 3
    35  
    36  	// DefaultMaxTemplateEventRate is the default maximum rate at which a
    37  	// template event should be fired.
    38  	DefaultMaxTemplateEventRate = 3 * time.Second
    39  )
    40  
    41  var (
    42  	sourceEscapesErr = errors.New("template source path escapes alloc directory")
    43  	destEscapesErr   = errors.New("template destination path escapes alloc directory")
    44  )
    45  
    46  // TaskTemplateManager is used to run a set of templates for a given task
    47  type TaskTemplateManager struct {
    48  	// config holds the template managers configuration
    49  	config *TaskTemplateManagerConfig
    50  
    51  	// lookup allows looking up the set of Nomad templates by their consul-template ID
    52  	lookup map[string][]*structs.Template
    53  
    54  	// runner is the consul-template runner
    55  	runner *manager.Runner
    56  
    57  	// signals is a lookup map from the string representation of a signal to its
    58  	// actual signal
    59  	signals map[string]os.Signal
    60  
    61  	// shutdownCh is used to signal and started goroutine to shutdown
    62  	shutdownCh chan struct{}
    63  
    64  	// shutdown marks whether the manager has been shutdown
    65  	shutdown     bool
    66  	shutdownLock sync.Mutex
    67  }
    68  
    69  // TaskTemplateManagerConfig is used to configure an instance of the
    70  // TaskTemplateManager
    71  type TaskTemplateManagerConfig struct {
    72  	// UnblockCh is closed when the template has been rendered
    73  	UnblockCh chan struct{}
    74  
    75  	// Lifecycle is used to interact with the task the template manager is being
    76  	// run for
    77  	Lifecycle interfaces.TaskLifecycle
    78  
    79  	// Events is used to emit events for the task
    80  	Events interfaces.EventEmitter
    81  
    82  	// Templates is the set of templates we are managing
    83  	Templates []*structs.Template
    84  
    85  	// ClientConfig is the Nomad Client configuration
    86  	ClientConfig *config.Config
    87  
    88  	// VaultToken is the Vault token for the task.
    89  	VaultToken string
    90  
    91  	// VaultNamespace is the Vault namespace for the task
    92  	VaultNamespace string
    93  
    94  	// TaskDir is the task's directory
    95  	TaskDir string
    96  
    97  	// EnvBuilder is the environment variable builder for the task.
    98  	EnvBuilder *taskenv.Builder
    99  
   100  	// MaxTemplateEventRate is the maximum rate at which we should emit events.
   101  	MaxTemplateEventRate time.Duration
   102  
   103  	// retryRate is only used for testing and is used to increase the retry rate
   104  	retryRate time.Duration
   105  }
   106  
   107  // Validate validates the configuration.
   108  func (c *TaskTemplateManagerConfig) Validate() error {
   109  	if c == nil {
   110  		return fmt.Errorf("Nil config passed")
   111  	} else if c.UnblockCh == nil {
   112  		return fmt.Errorf("Invalid unblock channel given")
   113  	} else if c.Lifecycle == nil {
   114  		return fmt.Errorf("Invalid lifecycle hooks given")
   115  	} else if c.Events == nil {
   116  		return fmt.Errorf("Invalid event hook given")
   117  	} else if c.ClientConfig == nil {
   118  		return fmt.Errorf("Invalid client config given")
   119  	} else if c.TaskDir == "" {
   120  		return fmt.Errorf("Invalid task directory given: %q", c.TaskDir)
   121  	} else if c.EnvBuilder == nil {
   122  		return fmt.Errorf("Invalid task environment given")
   123  	} else if c.MaxTemplateEventRate == 0 {
   124  		return fmt.Errorf("Invalid max template event rate given")
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  func NewTaskTemplateManager(config *TaskTemplateManagerConfig) (*TaskTemplateManager, error) {
   131  	// Check pre-conditions
   132  	if err := config.Validate(); err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	tm := &TaskTemplateManager{
   137  		config:     config,
   138  		shutdownCh: make(chan struct{}),
   139  	}
   140  
   141  	// Parse the signals that we need
   142  	for _, tmpl := range config.Templates {
   143  		if tmpl.ChangeSignal == "" {
   144  			continue
   145  		}
   146  
   147  		sig, err := signals.Parse(tmpl.ChangeSignal)
   148  		if err != nil {
   149  			return nil, fmt.Errorf("Failed to parse signal %q", tmpl.ChangeSignal)
   150  		}
   151  
   152  		if tm.signals == nil {
   153  			tm.signals = make(map[string]os.Signal)
   154  		}
   155  
   156  		tm.signals[tmpl.ChangeSignal] = sig
   157  	}
   158  
   159  	// Build the consul-template runner
   160  	runner, lookup, err := templateRunner(config)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	tm.runner = runner
   165  	tm.lookup = lookup
   166  
   167  	go tm.run()
   168  	return tm, nil
   169  }
   170  
   171  // Stop is used to stop the consul-template runner
   172  func (tm *TaskTemplateManager) Stop() {
   173  	tm.shutdownLock.Lock()
   174  	defer tm.shutdownLock.Unlock()
   175  
   176  	if tm.shutdown {
   177  		return
   178  	}
   179  
   180  	close(tm.shutdownCh)
   181  	tm.shutdown = true
   182  
   183  	// Stop the consul-template runner
   184  	if tm.runner != nil {
   185  		tm.runner.Stop()
   186  	}
   187  }
   188  
   189  // run is the long lived loop that handles errors and templates being rendered
   190  func (tm *TaskTemplateManager) run() {
   191  	// Runner is nil if there is no templates
   192  	if tm.runner == nil {
   193  		// Unblock the start if there is nothing to do
   194  		close(tm.config.UnblockCh)
   195  		return
   196  	}
   197  
   198  	// Start the runner
   199  	go tm.runner.Start()
   200  
   201  	// Block till all the templates have been rendered
   202  	tm.handleFirstRender()
   203  
   204  	// Detect if there was a shutdown.
   205  	select {
   206  	case <-tm.shutdownCh:
   207  		return
   208  	default:
   209  	}
   210  
   211  	// Read environment variables from env templates before we unblock
   212  	envMap, err := loadTemplateEnv(tm.config.Templates, tm.config.EnvBuilder.Build())
   213  	if err != nil {
   214  		tm.config.Lifecycle.Kill(context.Background(),
   215  			structs.NewTaskEvent(structs.TaskKilling).
   216  				SetFailsTask().
   217  				SetDisplayMessage(fmt.Sprintf("Template failed to read environment variables: %v", err)))
   218  		return
   219  	}
   220  	tm.config.EnvBuilder.SetTemplateEnv(envMap)
   221  
   222  	// Unblock the task
   223  	close(tm.config.UnblockCh)
   224  
   225  	// If all our templates are change mode no-op, then we can exit here
   226  	if tm.allTemplatesNoop() {
   227  		return
   228  	}
   229  
   230  	// handle all subsequent render events.
   231  	tm.handleTemplateRerenders(time.Now())
   232  }
   233  
   234  // handleFirstRender blocks till all templates have been rendered
   235  func (tm *TaskTemplateManager) handleFirstRender() {
   236  	// missingDependencies is the set of missing dependencies.
   237  	var missingDependencies map[string]struct{}
   238  
   239  	// eventTimer is used to trigger the firing of an event showing the missing
   240  	// dependencies.
   241  	eventTimer := time.NewTimer(tm.config.MaxTemplateEventRate)
   242  	if !eventTimer.Stop() {
   243  		<-eventTimer.C
   244  	}
   245  
   246  	// outstandingEvent tracks whether there is an outstanding event that should
   247  	// be fired.
   248  	outstandingEvent := false
   249  
   250  	// Wait till all the templates have been rendered
   251  WAIT:
   252  	for {
   253  		select {
   254  		case <-tm.shutdownCh:
   255  			return
   256  		case err, ok := <-tm.runner.ErrCh:
   257  			if !ok {
   258  				continue
   259  			}
   260  
   261  			tm.config.Lifecycle.Kill(context.Background(),
   262  				structs.NewTaskEvent(structs.TaskKilling).
   263  					SetFailsTask().
   264  					SetDisplayMessage(fmt.Sprintf("Template failed: %v", err)))
   265  		case <-tm.runner.TemplateRenderedCh():
   266  			// A template has been rendered, figure out what to do
   267  			events := tm.runner.RenderEvents()
   268  
   269  			// Not all templates have been rendered yet
   270  			if len(events) < len(tm.lookup) {
   271  				continue
   272  			}
   273  
   274  			dirty := false
   275  			for _, event := range events {
   276  				// This template hasn't been rendered
   277  				if event.LastWouldRender.IsZero() {
   278  					continue WAIT
   279  				}
   280  				if event.WouldRender && event.DidRender {
   281  					dirty = true
   282  				}
   283  			}
   284  
   285  			// if there's a driver handle then the task is already running and
   286  			// that changes how we want to behave on first render
   287  			if dirty && tm.config.Lifecycle.IsRunning() {
   288  				handledRenders := make(map[string]time.Time, len(tm.config.Templates))
   289  				tm.onTemplateRendered(handledRenders, time.Time{})
   290  			}
   291  
   292  			break WAIT
   293  		case <-tm.runner.RenderEventCh():
   294  			events := tm.runner.RenderEvents()
   295  			joinedSet := make(map[string]struct{})
   296  			for _, event := range events {
   297  				missing := event.MissingDeps
   298  				if missing == nil {
   299  					continue
   300  				}
   301  
   302  				for _, dep := range missing.List() {
   303  					joinedSet[dep.String()] = struct{}{}
   304  				}
   305  			}
   306  
   307  			// Check to see if the new joined set is the same as the old
   308  			different := len(joinedSet) != len(missingDependencies)
   309  			if !different {
   310  				for k := range joinedSet {
   311  					if _, ok := missingDependencies[k]; !ok {
   312  						different = true
   313  						break
   314  					}
   315  				}
   316  			}
   317  
   318  			// Nothing to do
   319  			if !different {
   320  				continue
   321  			}
   322  
   323  			// Update the missing set
   324  			missingDependencies = joinedSet
   325  
   326  			// Update the event timer channel
   327  			if !outstandingEvent {
   328  				// We got new data so reset
   329  				outstandingEvent = true
   330  				eventTimer.Reset(tm.config.MaxTemplateEventRate)
   331  			}
   332  		case <-eventTimer.C:
   333  			if missingDependencies == nil {
   334  				continue
   335  			}
   336  
   337  			// Clear the outstanding event
   338  			outstandingEvent = false
   339  
   340  			// Build the missing set
   341  			missingSlice := make([]string, 0, len(missingDependencies))
   342  			for k := range missingDependencies {
   343  				missingSlice = append(missingSlice, k)
   344  			}
   345  			sort.Strings(missingSlice)
   346  
   347  			if l := len(missingSlice); l > missingDepEventLimit {
   348  				missingSlice[missingDepEventLimit] = fmt.Sprintf("and %d more", l-missingDepEventLimit)
   349  				missingSlice = missingSlice[:missingDepEventLimit+1]
   350  			}
   351  
   352  			missingStr := strings.Join(missingSlice, ", ")
   353  			tm.config.Events.EmitEvent(structs.NewTaskEvent(consulTemplateSourceName).SetDisplayMessage(fmt.Sprintf("Missing: %s", missingStr)))
   354  		}
   355  	}
   356  }
   357  
   358  // handleTemplateRerenders is used to handle template render events after they
   359  // have all rendered. It takes action based on which set of templates re-render.
   360  // The passed allRenderedTime is the time at which all templates have rendered.
   361  // This is used to avoid signaling the task for any render event before hand.
   362  func (tm *TaskTemplateManager) handleTemplateRerenders(allRenderedTime time.Time) {
   363  	// A lookup for the last time the template was handled
   364  	handledRenders := make(map[string]time.Time, len(tm.config.Templates))
   365  
   366  	for {
   367  		select {
   368  		case <-tm.shutdownCh:
   369  			return
   370  		case err, ok := <-tm.runner.ErrCh:
   371  			if !ok {
   372  				continue
   373  			}
   374  
   375  			tm.config.Lifecycle.Kill(context.Background(),
   376  				structs.NewTaskEvent(structs.TaskKilling).
   377  					SetFailsTask().
   378  					SetDisplayMessage(fmt.Sprintf("Template failed: %v", err)))
   379  		case <-tm.runner.TemplateRenderedCh():
   380  			tm.onTemplateRendered(handledRenders, allRenderedTime)
   381  		}
   382  	}
   383  }
   384  
   385  func (tm *TaskTemplateManager) onTemplateRendered(handledRenders map[string]time.Time, allRenderedTime time.Time) {
   386  
   387  	var handling []string
   388  	signals := make(map[string]struct{})
   389  	restart := false
   390  	var splay time.Duration
   391  
   392  	events := tm.runner.RenderEvents()
   393  	for id, event := range events {
   394  
   395  		// First time through
   396  		if allRenderedTime.After(event.LastDidRender) || allRenderedTime.Equal(event.LastDidRender) {
   397  			handledRenders[id] = allRenderedTime
   398  			continue
   399  		}
   400  
   401  		// We have already handled this one
   402  		if htime := handledRenders[id]; htime.After(event.LastDidRender) || htime.Equal(event.LastDidRender) {
   403  			continue
   404  		}
   405  
   406  		// Lookup the template and determine what to do
   407  		tmpls, ok := tm.lookup[id]
   408  		if !ok {
   409  			tm.config.Lifecycle.Kill(context.Background(),
   410  				structs.NewTaskEvent(structs.TaskKilling).
   411  					SetFailsTask().
   412  					SetDisplayMessage(fmt.Sprintf("Template runner returned unknown template id %q", id)))
   413  			return
   414  		}
   415  
   416  		// Read environment variables from templates
   417  		envMap, err := loadTemplateEnv(tm.config.Templates, tm.config.EnvBuilder.Build())
   418  		if err != nil {
   419  			tm.config.Lifecycle.Kill(context.Background(),
   420  				structs.NewTaskEvent(structs.TaskKilling).
   421  					SetFailsTask().
   422  					SetDisplayMessage(fmt.Sprintf("Template failed to read environment variables: %v", err)))
   423  			return
   424  		}
   425  		tm.config.EnvBuilder.SetTemplateEnv(envMap)
   426  
   427  		for _, tmpl := range tmpls {
   428  			switch tmpl.ChangeMode {
   429  			case structs.TemplateChangeModeSignal:
   430  				signals[tmpl.ChangeSignal] = struct{}{}
   431  			case structs.TemplateChangeModeRestart:
   432  				restart = true
   433  			case structs.TemplateChangeModeNoop:
   434  				continue
   435  			}
   436  
   437  			if tmpl.Splay > splay {
   438  				splay = tmpl.Splay
   439  			}
   440  		}
   441  
   442  		handling = append(handling, id)
   443  	}
   444  
   445  	if restart || len(signals) != 0 {
   446  		if splay != 0 {
   447  			ns := splay.Nanoseconds()
   448  			offset := rand.Int63n(ns)
   449  			t := time.Duration(offset)
   450  
   451  			select {
   452  			case <-time.After(t):
   453  			case <-tm.shutdownCh:
   454  				return
   455  			}
   456  		}
   457  
   458  		// Update handle time
   459  		for _, id := range handling {
   460  			handledRenders[id] = events[id].LastDidRender
   461  		}
   462  
   463  		if restart {
   464  			tm.config.Lifecycle.Restart(context.Background(),
   465  				structs.NewTaskEvent(structs.TaskRestartSignal).
   466  					SetDisplayMessage("Template with change_mode restart re-rendered"), false)
   467  		} else if len(signals) != 0 {
   468  			var mErr multierror.Error
   469  			for signal := range signals {
   470  				s := tm.signals[signal]
   471  				event := structs.NewTaskEvent(structs.TaskSignaling).SetTaskSignal(s).SetDisplayMessage("Template re-rendered")
   472  				if err := tm.config.Lifecycle.Signal(event, signal); err != nil {
   473  					_ = multierror.Append(&mErr, err)
   474  				}
   475  			}
   476  
   477  			if err := mErr.ErrorOrNil(); err != nil {
   478  				flat := make([]os.Signal, 0, len(signals))
   479  				for signal := range signals {
   480  					flat = append(flat, tm.signals[signal])
   481  				}
   482  
   483  				tm.config.Lifecycle.Kill(context.Background(),
   484  					structs.NewTaskEvent(structs.TaskKilling).
   485  						SetFailsTask().
   486  						SetDisplayMessage(fmt.Sprintf("Template failed to send signals %v: %v", flat, err)))
   487  			}
   488  		}
   489  	}
   490  
   491  }
   492  
   493  // allTemplatesNoop returns whether all the managed templates have change mode noop.
   494  func (tm *TaskTemplateManager) allTemplatesNoop() bool {
   495  	for _, tmpl := range tm.config.Templates {
   496  		if tmpl.ChangeMode != structs.TemplateChangeModeNoop {
   497  			return false
   498  		}
   499  	}
   500  
   501  	return true
   502  }
   503  
   504  // templateRunner returns a consul-template runner for the given templates and a
   505  // lookup by destination to the template. If no templates are in the config, a
   506  // nil template runner and lookup is returned.
   507  func templateRunner(config *TaskTemplateManagerConfig) (
   508  	*manager.Runner, map[string][]*structs.Template, error) {
   509  
   510  	if len(config.Templates) == 0 {
   511  		return nil, nil, nil
   512  	}
   513  
   514  	// Parse the templates
   515  	ctmplMapping, err := parseTemplateConfigs(config)
   516  	if err != nil {
   517  		return nil, nil, err
   518  	}
   519  
   520  	// Create the runner configuration.
   521  	runnerConfig, err := newRunnerConfig(config, ctmplMapping)
   522  	if err != nil {
   523  		return nil, nil, err
   524  	}
   525  
   526  	runner, err := manager.NewRunner(runnerConfig, false)
   527  	if err != nil {
   528  		return nil, nil, err
   529  	}
   530  
   531  	// Set Nomad's environment variables.
   532  	// consul-template falls back to the host process environment if a
   533  	// variable isn't explicitly set in the configuration, so we need
   534  	// to mask the environment out to ensure only the task env vars are
   535  	// available.
   536  	runner.Env = maskProcessEnv(config.EnvBuilder.Build().All())
   537  
   538  	// Build the lookup
   539  	idMap := runner.TemplateConfigMapping()
   540  	lookup := make(map[string][]*structs.Template, len(idMap))
   541  	for id, ctmpls := range idMap {
   542  		for _, ctmpl := range ctmpls {
   543  			templates := lookup[id]
   544  			templates = append(templates, ctmplMapping[ctmpl])
   545  			lookup[id] = templates
   546  		}
   547  	}
   548  
   549  	return runner, lookup, nil
   550  }
   551  
   552  // maskProcessEnv masks away any environment variable not found in task env.
   553  // It manipulates the parameter directly and returns it without copying.
   554  func maskProcessEnv(env map[string]string) map[string]string {
   555  	procEnvs := os.Environ()
   556  	for _, e := range procEnvs {
   557  		ekv := strings.SplitN(e, "=", 2)
   558  		if _, ok := env[ekv[0]]; !ok {
   559  			env[ekv[0]] = ""
   560  		}
   561  	}
   562  
   563  	return env
   564  }
   565  
   566  // parseTemplateConfigs converts the tasks templates in the config into
   567  // consul-templates
   568  func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[*ctconf.TemplateConfig]*structs.Template, error) {
   569  	sandboxEnabled := !config.ClientConfig.TemplateConfig.DisableSandbox
   570  	taskEnv := config.EnvBuilder.Build()
   571  
   572  	ctmpls := make(map[*ctconf.TemplateConfig]*structs.Template, len(config.Templates))
   573  	for _, tmpl := range config.Templates {
   574  		var src, dest string
   575  		if tmpl.SourcePath != "" {
   576  			var escapes bool
   577  			src, escapes = taskEnv.ClientPath(tmpl.SourcePath, false)
   578  			if escapes && sandboxEnabled {
   579  				return nil, sourceEscapesErr
   580  			}
   581  		}
   582  
   583  		if tmpl.DestPath != "" {
   584  			var escapes bool
   585  			dest, escapes = taskEnv.ClientPath(tmpl.DestPath, true)
   586  			if escapes && sandboxEnabled {
   587  				return nil, destEscapesErr
   588  			}
   589  		}
   590  
   591  		ct := ctconf.DefaultTemplateConfig()
   592  		ct.Source = &src
   593  		ct.Destination = &dest
   594  		ct.Contents = &tmpl.EmbeddedTmpl
   595  		ct.LeftDelim = &tmpl.LeftDelim
   596  		ct.RightDelim = &tmpl.RightDelim
   597  		ct.FunctionDenylist = config.ClientConfig.TemplateConfig.FunctionDenylist
   598  		if sandboxEnabled {
   599  			ct.SandboxPath = &config.TaskDir
   600  		}
   601  
   602  		// Set the permissions
   603  		if tmpl.Perms != "" {
   604  			v, err := strconv.ParseUint(tmpl.Perms, 8, 12)
   605  			if err != nil {
   606  				return nil, fmt.Errorf("Failed to parse %q as octal: %v", tmpl.Perms, err)
   607  			}
   608  			m := os.FileMode(v)
   609  			ct.Perms = &m
   610  		}
   611  		ct.Finalize()
   612  
   613  		ctmpls[ct] = tmpl
   614  	}
   615  
   616  	return ctmpls, nil
   617  }
   618  
   619  // newRunnerConfig returns a consul-template runner configuration, setting the
   620  // Vault and Consul configurations based on the clients configs.
   621  func newRunnerConfig(config *TaskTemplateManagerConfig,
   622  	templateMapping map[*ctconf.TemplateConfig]*structs.Template) (*ctconf.Config, error) {
   623  
   624  	cc := config.ClientConfig
   625  	conf := ctconf.DefaultConfig()
   626  
   627  	// Gather the consul-template templates
   628  	flat := ctconf.TemplateConfigs(make([]*ctconf.TemplateConfig, 0, len(templateMapping)))
   629  	for ctmpl := range templateMapping {
   630  		local := ctmpl
   631  		flat = append(flat, local)
   632  	}
   633  	conf.Templates = &flat
   634  
   635  	// Force faster retries
   636  	if config.retryRate != 0 {
   637  		rate := config.retryRate
   638  		conf.Consul.Retry.Backoff = &rate
   639  	}
   640  
   641  	// Setup the Consul config
   642  	if cc.ConsulConfig != nil {
   643  		conf.Consul.Address = &cc.ConsulConfig.Addr
   644  		conf.Consul.Token = &cc.ConsulConfig.Token
   645  		conf.Consul.Namespace = &cc.ConsulConfig.Namespace
   646  
   647  		if cc.ConsulConfig.EnableSSL != nil && *cc.ConsulConfig.EnableSSL {
   648  			verify := cc.ConsulConfig.VerifySSL != nil && *cc.ConsulConfig.VerifySSL
   649  			conf.Consul.SSL = &ctconf.SSLConfig{
   650  				Enabled: helper.BoolToPtr(true),
   651  				Verify:  &verify,
   652  				Cert:    &cc.ConsulConfig.CertFile,
   653  				Key:     &cc.ConsulConfig.KeyFile,
   654  				CaCert:  &cc.ConsulConfig.CAFile,
   655  			}
   656  		}
   657  
   658  		if cc.ConsulConfig.Auth != "" {
   659  			parts := strings.SplitN(cc.ConsulConfig.Auth, ":", 2)
   660  			if len(parts) != 2 {
   661  				return nil, fmt.Errorf("Failed to parse Consul Auth config")
   662  			}
   663  
   664  			conf.Consul.Auth = &ctconf.AuthConfig{
   665  				Enabled:  helper.BoolToPtr(true),
   666  				Username: &parts[0],
   667  				Password: &parts[1],
   668  			}
   669  		}
   670  	}
   671  
   672  	// Setup the Vault config
   673  	// Always set these to ensure nothing is picked up from the environment
   674  	emptyStr := ""
   675  	conf.Vault.RenewToken = helper.BoolToPtr(false)
   676  	conf.Vault.Token = &emptyStr
   677  	if cc.VaultConfig != nil && cc.VaultConfig.IsEnabled() {
   678  		conf.Vault.Address = &cc.VaultConfig.Addr
   679  		conf.Vault.Token = &config.VaultToken
   680  
   681  		// Set the Vault Namespace. Passed in Task config has
   682  		// highest precedence.
   683  		if config.ClientConfig.VaultConfig.Namespace != "" {
   684  			conf.Vault.Namespace = &config.ClientConfig.VaultConfig.Namespace
   685  		}
   686  		if config.VaultNamespace != "" {
   687  			conf.Vault.Namespace = &config.VaultNamespace
   688  		}
   689  
   690  		if strings.HasPrefix(cc.VaultConfig.Addr, "https") || cc.VaultConfig.TLSCertFile != "" {
   691  			skipVerify := cc.VaultConfig.TLSSkipVerify != nil && *cc.VaultConfig.TLSSkipVerify
   692  			verify := !skipVerify
   693  			conf.Vault.SSL = &ctconf.SSLConfig{
   694  				Enabled:    helper.BoolToPtr(true),
   695  				Verify:     &verify,
   696  				Cert:       &cc.VaultConfig.TLSCertFile,
   697  				Key:        &cc.VaultConfig.TLSKeyFile,
   698  				CaCert:     &cc.VaultConfig.TLSCaFile,
   699  				CaPath:     &cc.VaultConfig.TLSCaPath,
   700  				ServerName: &cc.VaultConfig.TLSServerName,
   701  			}
   702  		} else {
   703  			conf.Vault.SSL = &ctconf.SSLConfig{
   704  				Enabled:    helper.BoolToPtr(false),
   705  				Verify:     helper.BoolToPtr(false),
   706  				Cert:       &emptyStr,
   707  				Key:        &emptyStr,
   708  				CaCert:     &emptyStr,
   709  				CaPath:     &emptyStr,
   710  				ServerName: &emptyStr,
   711  			}
   712  		}
   713  	}
   714  
   715  	conf.Finalize()
   716  	return conf, nil
   717  }
   718  
   719  // loadTemplateEnv loads task environment variables from all templates.
   720  func loadTemplateEnv(tmpls []*structs.Template, taskEnv *taskenv.TaskEnv) (map[string]string, error) {
   721  	all := make(map[string]string, 50)
   722  	for _, t := range tmpls {
   723  		if !t.Envvars {
   724  			continue
   725  		}
   726  
   727  		// we checked escape before we rendered the file
   728  		dest, _ := taskEnv.ClientPath(t.DestPath, true)
   729  		f, err := os.Open(dest)
   730  		if err != nil {
   731  			return nil, fmt.Errorf("error opening env template: %v", err)
   732  		}
   733  		defer f.Close()
   734  
   735  		// Parse environment fil
   736  		vars, err := envparse.Parse(f)
   737  		if err != nil {
   738  			return nil, fmt.Errorf("error parsing env template %q: %v", dest, err)
   739  		}
   740  		for k, v := range vars {
   741  			all[k] = v
   742  		}
   743  	}
   744  	return all, nil
   745  }