github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/allocrunner/taskrunner/template/template.go (about)

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