github.phpd.cn/hashicorp/packer@v1.3.2/provisioner/chef-client/provisioner.go (about)

     1  // This package implements a provisioner for Packer that uses
     2  // Chef to provision the remote machine, specifically with chef-client (that is,
     3  // with a Chef server).
     4  package chefclient
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/hashicorp/packer/common"
    16  	"github.com/hashicorp/packer/common/uuid"
    17  	"github.com/hashicorp/packer/helper/config"
    18  	"github.com/hashicorp/packer/packer"
    19  	"github.com/hashicorp/packer/provisioner"
    20  	"github.com/hashicorp/packer/template/interpolate"
    21  )
    22  
    23  type guestOSTypeConfig struct {
    24  	executeCommand string
    25  	installCommand string
    26  	knifeCommand   string
    27  	stagingDir     string
    28  }
    29  
    30  var guestOSTypeConfigs = map[string]guestOSTypeConfig{
    31  	provisioner.UnixOSType: {
    32  		executeCommand: "{{if .Sudo}}sudo {{end}}chef-client --no-color -c {{.ConfigPath}} -j {{.JsonPath}}",
    33  		installCommand: "curl -L https://omnitruck.chef.io/install.sh | {{if .Sudo}}sudo {{end}}bash",
    34  		knifeCommand:   "{{if .Sudo}}sudo {{end}}knife {{.Args}} {{.Flags}}",
    35  		stagingDir:     "/tmp/packer-chef-client",
    36  	},
    37  	provisioner.WindowsOSType: {
    38  		executeCommand: "c:/opscode/chef/bin/chef-client.bat --no-color -c {{.ConfigPath}} -j {{.JsonPath}}",
    39  		installCommand: "powershell.exe -Command \". { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; install\"",
    40  		knifeCommand:   "c:/opscode/chef/bin/knife.bat {{.Args}} {{.Flags}}",
    41  		stagingDir:     "C:/Windows/Temp/packer-chef-client",
    42  	},
    43  }
    44  
    45  type Config struct {
    46  	common.PackerConfig `mapstructure:",squash"`
    47  
    48  	Json map[string]interface{}
    49  
    50  	ChefEnvironment            string   `mapstructure:"chef_environment"`
    51  	ClientKey                  string   `mapstructure:"client_key"`
    52  	ConfigTemplate             string   `mapstructure:"config_template"`
    53  	EncryptedDataBagSecretPath string   `mapstructure:"encrypted_data_bag_secret_path"`
    54  	ExecuteCommand             string   `mapstructure:"execute_command"`
    55  	GuestOSType                string   `mapstructure:"guest_os_type"`
    56  	InstallCommand             string   `mapstructure:"install_command"`
    57  	KnifeCommand               string   `mapstructure:"knife_command"`
    58  	NodeName                   string   `mapstructure:"node_name"`
    59  	PolicyGroup                string   `mapstructure:"policy_group"`
    60  	PolicyName                 string   `mapstructure:"policy_name"`
    61  	PreventSudo                bool     `mapstructure:"prevent_sudo"`
    62  	RunList                    []string `mapstructure:"run_list"`
    63  	ServerUrl                  string   `mapstructure:"server_url"`
    64  	SkipCleanClient            bool     `mapstructure:"skip_clean_client"`
    65  	SkipCleanNode              bool     `mapstructure:"skip_clean_node"`
    66  	SkipCleanStagingDirectory  bool     `mapstructure:"skip_clean_staging_directory"`
    67  	SkipInstall                bool     `mapstructure:"skip_install"`
    68  	SslVerifyMode              string   `mapstructure:"ssl_verify_mode"`
    69  	TrustedCertsDir            string   `mapstructure:"trusted_certs_dir"`
    70  	StagingDir                 string   `mapstructure:"staging_directory"`
    71  	ValidationClientName       string   `mapstructure:"validation_client_name"`
    72  	ValidationKeyPath          string   `mapstructure:"validation_key_path"`
    73  
    74  	ctx interpolate.Context
    75  }
    76  
    77  type Provisioner struct {
    78  	config            Config
    79  	guestOSTypeConfig guestOSTypeConfig
    80  	guestCommands     *provisioner.GuestCommands
    81  }
    82  
    83  type ConfigTemplate struct {
    84  	ChefEnvironment            string
    85  	ClientKey                  string
    86  	EncryptedDataBagSecretPath string
    87  	NodeName                   string
    88  	PolicyGroup                string
    89  	PolicyName                 string
    90  	ServerUrl                  string
    91  	SslVerifyMode              string
    92  	TrustedCertsDir            string
    93  	ValidationClientName       string
    94  	ValidationKeyPath          string
    95  }
    96  
    97  type ExecuteTemplate struct {
    98  	ConfigPath string
    99  	JsonPath   string
   100  	Sudo       bool
   101  }
   102  
   103  type InstallChefTemplate struct {
   104  	Sudo bool
   105  }
   106  
   107  type KnifeTemplate struct {
   108  	Sudo  bool
   109  	Flags string
   110  	Args  string
   111  }
   112  
   113  func (p *Provisioner) Prepare(raws ...interface{}) error {
   114  	err := config.Decode(&p.config, &config.DecodeOpts{
   115  		Interpolate:        true,
   116  		InterpolateContext: &p.config.ctx,
   117  		InterpolateFilter: &interpolate.RenderFilter{
   118  			Exclude: []string{
   119  				"execute_command",
   120  				"install_command",
   121  				"knife_command",
   122  			},
   123  		},
   124  	}, raws...)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	if p.config.GuestOSType == "" {
   130  		p.config.GuestOSType = provisioner.DefaultOSType
   131  	}
   132  	p.config.GuestOSType = strings.ToLower(p.config.GuestOSType)
   133  
   134  	var ok bool
   135  	p.guestOSTypeConfig, ok = guestOSTypeConfigs[p.config.GuestOSType]
   136  	if !ok {
   137  		return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
   138  	}
   139  
   140  	p.guestCommands, err = provisioner.NewGuestCommands(p.config.GuestOSType, !p.config.PreventSudo)
   141  	if err != nil {
   142  		return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
   143  	}
   144  
   145  	if p.config.ExecuteCommand == "" {
   146  		p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
   147  	}
   148  
   149  	if p.config.InstallCommand == "" {
   150  		p.config.InstallCommand = p.guestOSTypeConfig.installCommand
   151  	}
   152  
   153  	if p.config.RunList == nil {
   154  		p.config.RunList = make([]string, 0)
   155  	}
   156  
   157  	if p.config.StagingDir == "" {
   158  		p.config.StagingDir = p.guestOSTypeConfig.stagingDir
   159  	}
   160  
   161  	if p.config.KnifeCommand == "" {
   162  		p.config.KnifeCommand = p.guestOSTypeConfig.knifeCommand
   163  	}
   164  
   165  	var errs *packer.MultiError
   166  	if p.config.ConfigTemplate != "" {
   167  		fi, err := os.Stat(p.config.ConfigTemplate)
   168  		if err != nil {
   169  			errs = packer.MultiErrorAppend(
   170  				errs, fmt.Errorf("Bad config template path: %s", err))
   171  		} else if fi.IsDir() {
   172  			errs = packer.MultiErrorAppend(
   173  				errs, fmt.Errorf("Config template path must be a file: %s", err))
   174  		}
   175  	}
   176  
   177  	if p.config.ServerUrl == "" {
   178  		errs = packer.MultiErrorAppend(
   179  			errs, fmt.Errorf("server_url must be set"))
   180  	}
   181  
   182  	if p.config.EncryptedDataBagSecretPath != "" {
   183  		pFileInfo, err := os.Stat(p.config.EncryptedDataBagSecretPath)
   184  
   185  		if err != nil || pFileInfo.IsDir() {
   186  			errs = packer.MultiErrorAppend(
   187  				errs, fmt.Errorf("Bad encrypted data bag secret '%s': %s", p.config.EncryptedDataBagSecretPath, err))
   188  		}
   189  	}
   190  
   191  	if (p.config.PolicyName != "") != (p.config.PolicyGroup != "") {
   192  		errs = packer.MultiErrorAppend(errs, fmt.Errorf("If either policy_name or policy_group are set, they must both be set."))
   193  	}
   194  
   195  	jsonValid := true
   196  	for k, v := range p.config.Json {
   197  		p.config.Json[k], err = p.deepJsonFix(k, v)
   198  		if err != nil {
   199  			errs = packer.MultiErrorAppend(
   200  				errs, fmt.Errorf("Error processing JSON: %s", err))
   201  			jsonValid = false
   202  		}
   203  	}
   204  
   205  	if jsonValid {
   206  		// Process the user variables within the JSON and set the JSON.
   207  		// Do this early so that we can validate and show errors.
   208  		p.config.Json, err = p.processJsonUserVars()
   209  		if err != nil {
   210  			errs = packer.MultiErrorAppend(
   211  				errs, fmt.Errorf("Error processing user variables in JSON: %s", err))
   212  		}
   213  	}
   214  
   215  	if errs != nil && len(errs.Errors) > 0 {
   216  		return errs
   217  	}
   218  
   219  	return nil
   220  }
   221  
   222  func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
   223  
   224  	nodeName := p.config.NodeName
   225  	if nodeName == "" {
   226  		nodeName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
   227  	}
   228  	remoteValidationKeyPath := ""
   229  	serverUrl := p.config.ServerUrl
   230  
   231  	if !p.config.SkipInstall {
   232  		if err := p.installChef(ui, comm); err != nil {
   233  			return fmt.Errorf("Error installing Chef: %s", err)
   234  		}
   235  	}
   236  
   237  	if err := p.createDir(ui, comm, p.config.StagingDir); err != nil {
   238  		return fmt.Errorf("Error creating staging directory: %s", err)
   239  	}
   240  
   241  	if p.config.ClientKey == "" {
   242  		p.config.ClientKey = fmt.Sprintf("%s/client.pem", p.config.StagingDir)
   243  	}
   244  
   245  	encryptedDataBagSecretPath := ""
   246  	if p.config.EncryptedDataBagSecretPath != "" {
   247  		encryptedDataBagSecretPath = fmt.Sprintf("%s/encrypted_data_bag_secret", p.config.StagingDir)
   248  		if err := p.uploadFile(ui,
   249  			comm,
   250  			encryptedDataBagSecretPath,
   251  			p.config.EncryptedDataBagSecretPath); err != nil {
   252  			return fmt.Errorf("Error uploading encrypted data bag secret: %s", err)
   253  		}
   254  	}
   255  
   256  	if p.config.ValidationKeyPath != "" {
   257  		remoteValidationKeyPath = fmt.Sprintf("%s/validation.pem", p.config.StagingDir)
   258  		if err := p.uploadFile(ui, comm, remoteValidationKeyPath, p.config.ValidationKeyPath); err != nil {
   259  			return fmt.Errorf("Error copying validation key: %s", err)
   260  		}
   261  	}
   262  
   263  	configPath, err := p.createConfig(
   264  		ui,
   265  		comm,
   266  		nodeName,
   267  		serverUrl,
   268  		p.config.ClientKey,
   269  		encryptedDataBagSecretPath,
   270  		remoteValidationKeyPath,
   271  		p.config.ValidationClientName,
   272  		p.config.ChefEnvironment,
   273  		p.config.PolicyGroup,
   274  		p.config.PolicyName,
   275  		p.config.SslVerifyMode,
   276  		p.config.TrustedCertsDir)
   277  	if err != nil {
   278  		return fmt.Errorf("Error creating Chef config file: %s", err)
   279  	}
   280  
   281  	jsonPath, err := p.createJson(ui, comm)
   282  	if err != nil {
   283  		return fmt.Errorf("Error creating JSON attributes: %s", err)
   284  	}
   285  
   286  	err = p.executeChef(ui, comm, configPath, jsonPath)
   287  
   288  	if !(p.config.SkipCleanNode && p.config.SkipCleanClient) {
   289  
   290  		knifeConfigPath, knifeErr := p.createKnifeConfig(
   291  			ui, comm, nodeName, serverUrl, p.config.ClientKey, p.config.SslVerifyMode, p.config.TrustedCertsDir)
   292  
   293  		if knifeErr != nil {
   294  			return fmt.Errorf("Error creating knife config on node: %s", knifeErr)
   295  		}
   296  
   297  		if !p.config.SkipCleanNode {
   298  			if err := p.cleanNode(ui, comm, nodeName, knifeConfigPath); err != nil {
   299  				return fmt.Errorf("Error cleaning up chef node: %s", err)
   300  			}
   301  		}
   302  
   303  		if !p.config.SkipCleanClient {
   304  			if err := p.cleanClient(ui, comm, nodeName, knifeConfigPath); err != nil {
   305  				return fmt.Errorf("Error cleaning up chef client: %s", err)
   306  			}
   307  		}
   308  	}
   309  
   310  	if err != nil {
   311  		return fmt.Errorf("Error executing Chef: %s", err)
   312  	}
   313  
   314  	if !p.config.SkipCleanStagingDirectory {
   315  		if err := p.removeDir(ui, comm, p.config.StagingDir); err != nil {
   316  			return fmt.Errorf("Error removing %s: %s", p.config.StagingDir, err)
   317  		}
   318  	}
   319  
   320  	return nil
   321  }
   322  
   323  func (p *Provisioner) Cancel() {
   324  	// Just hard quit. It isn't a big deal if what we're doing keeps
   325  	// running on the other side.
   326  	os.Exit(0)
   327  }
   328  
   329  func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, remotePath string, localPath string) error {
   330  	ui.Message(fmt.Sprintf("Uploading %s...", localPath))
   331  
   332  	f, err := os.Open(localPath)
   333  	if err != nil {
   334  		return err
   335  	}
   336  	defer f.Close()
   337  
   338  	return comm.Upload(remotePath, f, nil)
   339  }
   340  
   341  func (p *Provisioner) createConfig(
   342  	ui packer.Ui,
   343  	comm packer.Communicator,
   344  	nodeName string,
   345  	serverUrl string,
   346  	clientKey string,
   347  	encryptedDataBagSecretPath,
   348  	remoteKeyPath string,
   349  	validationClientName string,
   350  	chefEnvironment string,
   351  	policyGroup string,
   352  	policyName string,
   353  	sslVerifyMode string,
   354  	trustedCertsDir string) (string, error) {
   355  
   356  	ui.Message("Creating configuration file 'client.rb'")
   357  
   358  	// Read the template
   359  	tpl := DefaultConfigTemplate
   360  	if p.config.ConfigTemplate != "" {
   361  		f, err := os.Open(p.config.ConfigTemplate)
   362  		if err != nil {
   363  			return "", err
   364  		}
   365  		defer f.Close()
   366  
   367  		tplBytes, err := ioutil.ReadAll(f)
   368  		if err != nil {
   369  			return "", err
   370  		}
   371  
   372  		tpl = string(tplBytes)
   373  	}
   374  
   375  	ctx := p.config.ctx
   376  	ctx.Data = &ConfigTemplate{
   377  		NodeName:                   nodeName,
   378  		ServerUrl:                  serverUrl,
   379  		ClientKey:                  clientKey,
   380  		ValidationKeyPath:          remoteKeyPath,
   381  		ValidationClientName:       validationClientName,
   382  		ChefEnvironment:            chefEnvironment,
   383  		PolicyGroup:                policyGroup,
   384  		PolicyName:                 policyName,
   385  		SslVerifyMode:              sslVerifyMode,
   386  		TrustedCertsDir:            trustedCertsDir,
   387  		EncryptedDataBagSecretPath: encryptedDataBagSecretPath,
   388  	}
   389  	configString, err := interpolate.Render(tpl, &ctx)
   390  	if err != nil {
   391  		return "", err
   392  	}
   393  
   394  	remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "client.rb"))
   395  	if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil {
   396  		return "", err
   397  	}
   398  
   399  	return remotePath, nil
   400  }
   401  
   402  func (p *Provisioner) createKnifeConfig(ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, clientKey string, sslVerifyMode string, trustedCertsDir string) (string, error) {
   403  	ui.Message("Creating configuration file 'knife.rb'")
   404  
   405  	// Read the template
   406  	tpl := DefaultKnifeTemplate
   407  
   408  	ctx := p.config.ctx
   409  	ctx.Data = &ConfigTemplate{
   410  		NodeName:        nodeName,
   411  		ServerUrl:       serverUrl,
   412  		ClientKey:       clientKey,
   413  		SslVerifyMode:   sslVerifyMode,
   414  		TrustedCertsDir: trustedCertsDir,
   415  	}
   416  	configString, err := interpolate.Render(tpl, &ctx)
   417  	if err != nil {
   418  		return "", err
   419  	}
   420  
   421  	remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "knife.rb"))
   422  	if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil {
   423  		return "", err
   424  	}
   425  
   426  	return remotePath, nil
   427  }
   428  
   429  func (p *Provisioner) createJson(ui packer.Ui, comm packer.Communicator) (string, error) {
   430  	ui.Message("Creating JSON attribute file")
   431  
   432  	jsonData := make(map[string]interface{})
   433  
   434  	// Copy the configured JSON
   435  	for k, v := range p.config.Json {
   436  		jsonData[k] = v
   437  	}
   438  
   439  	// Set the run list if it was specified
   440  	if len(p.config.RunList) > 0 {
   441  		jsonData["run_list"] = p.config.RunList
   442  	}
   443  
   444  	jsonBytes, err := json.MarshalIndent(jsonData, "", "  ")
   445  	if err != nil {
   446  		return "", err
   447  	}
   448  
   449  	// Upload the bytes
   450  	remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "first-boot.json"))
   451  	if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes), nil); err != nil {
   452  		return "", err
   453  	}
   454  
   455  	return remotePath, nil
   456  }
   457  
   458  func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
   459  	ui.Message(fmt.Sprintf("Creating directory: %s", dir))
   460  
   461  	cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
   462  	if err := cmd.StartWithUi(comm, ui); err != nil {
   463  		return err
   464  	}
   465  	if cmd.ExitStatus != 0 {
   466  		return fmt.Errorf("Non-zero exit status. See output above for more info.")
   467  	}
   468  
   469  	// Chmod the directory to 0777 just so that we can access it as our user
   470  	cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
   471  	if err := cmd.StartWithUi(comm, ui); err != nil {
   472  		return err
   473  	}
   474  	if cmd.ExitStatus != 0 {
   475  		return fmt.Errorf("Non-zero exit status. See output above for more info.")
   476  	}
   477  
   478  	return nil
   479  }
   480  
   481  func (p *Provisioner) cleanNode(ui packer.Ui, comm packer.Communicator, node string, knifeConfigPath string) error {
   482  	ui.Say("Cleaning up chef node...")
   483  	args := []string{"node", "delete", node}
   484  	if err := p.knifeExec(ui, comm, node, knifeConfigPath, args); err != nil {
   485  		return fmt.Errorf("Failed to cleanup node: %s", err)
   486  	}
   487  
   488  	return nil
   489  }
   490  
   491  func (p *Provisioner) cleanClient(ui packer.Ui, comm packer.Communicator, node string, knifeConfigPath string) error {
   492  	ui.Say("Cleaning up chef client...")
   493  	args := []string{"client", "delete", node}
   494  	if err := p.knifeExec(ui, comm, node, knifeConfigPath, args); err != nil {
   495  		return fmt.Errorf("Failed to cleanup client: %s", err)
   496  	}
   497  
   498  	return nil
   499  }
   500  
   501  func (p *Provisioner) knifeExec(ui packer.Ui, comm packer.Communicator, node string, knifeConfigPath string, args []string) error {
   502  	flags := []string{
   503  		"-y",
   504  		"-c", knifeConfigPath,
   505  	}
   506  
   507  	p.config.ctx.Data = &KnifeTemplate{
   508  		Sudo:  !p.config.PreventSudo,
   509  		Flags: strings.Join(flags, " "),
   510  		Args:  strings.Join(args, " "),
   511  	}
   512  
   513  	command, err := interpolate.Render(p.config.KnifeCommand, &p.config.ctx)
   514  	if err != nil {
   515  		return err
   516  	}
   517  
   518  	cmd := &packer.RemoteCmd{Command: command}
   519  	if err := cmd.StartWithUi(comm, ui); err != nil {
   520  		return err
   521  	}
   522  	if cmd.ExitStatus != 0 {
   523  		return fmt.Errorf(
   524  			"Non-zero exit status. See output above for more info.\n\n"+
   525  				"Command: %s",
   526  			command)
   527  	}
   528  
   529  	return nil
   530  }
   531  
   532  func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error {
   533  	ui.Message(fmt.Sprintf("Removing directory: %s", dir))
   534  
   535  	cmd := &packer.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)}
   536  	if err := cmd.StartWithUi(comm, ui); err != nil {
   537  		return err
   538  	}
   539  
   540  	return nil
   541  }
   542  
   543  func (p *Provisioner) executeChef(ui packer.Ui, comm packer.Communicator, config string, json string) error {
   544  	p.config.ctx.Data = &ExecuteTemplate{
   545  		ConfigPath: config,
   546  		JsonPath:   json,
   547  		Sudo:       !p.config.PreventSudo,
   548  	}
   549  	command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
   550  	if err != nil {
   551  		return err
   552  	}
   553  
   554  	ui.Message(fmt.Sprintf("Executing Chef: %s", command))
   555  
   556  	cmd := &packer.RemoteCmd{
   557  		Command: command,
   558  	}
   559  
   560  	if err := cmd.StartWithUi(comm, ui); err != nil {
   561  		return err
   562  	}
   563  
   564  	if cmd.ExitStatus != 0 {
   565  		return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus)
   566  	}
   567  
   568  	return nil
   569  }
   570  
   571  func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator) error {
   572  	ui.Message("Installing Chef...")
   573  
   574  	p.config.ctx.Data = &InstallChefTemplate{
   575  		Sudo: !p.config.PreventSudo,
   576  	}
   577  	command, err := interpolate.Render(p.config.InstallCommand, &p.config.ctx)
   578  	if err != nil {
   579  		return err
   580  	}
   581  
   582  	ui.Message(command)
   583  
   584  	cmd := &packer.RemoteCmd{Command: command}
   585  	if err := cmd.StartWithUi(comm, ui); err != nil {
   586  		return err
   587  	}
   588  
   589  	if cmd.ExitStatus != 0 {
   590  		return fmt.Errorf(
   591  			"Install script exited with non-zero exit status %d", cmd.ExitStatus)
   592  	}
   593  
   594  	return nil
   595  }
   596  
   597  func (p *Provisioner) deepJsonFix(key string, current interface{}) (interface{}, error) {
   598  	if current == nil {
   599  		return nil, nil
   600  	}
   601  
   602  	switch c := current.(type) {
   603  	case []interface{}:
   604  		val := make([]interface{}, len(c))
   605  		for i, v := range c {
   606  			var err error
   607  			val[i], err = p.deepJsonFix(fmt.Sprintf("%s[%d]", key, i), v)
   608  			if err != nil {
   609  				return nil, err
   610  			}
   611  		}
   612  
   613  		return val, nil
   614  	case []uint8:
   615  		return string(c), nil
   616  	case map[interface{}]interface{}:
   617  		val := make(map[string]interface{})
   618  		for k, v := range c {
   619  			ks, ok := k.(string)
   620  			if !ok {
   621  				return nil, fmt.Errorf("%s: key is not string", key)
   622  			}
   623  
   624  			var err error
   625  			val[ks], err = p.deepJsonFix(
   626  				fmt.Sprintf("%s.%s", key, ks), v)
   627  			if err != nil {
   628  				return nil, err
   629  			}
   630  		}
   631  
   632  		return val, nil
   633  	default:
   634  		return current, nil
   635  	}
   636  }
   637  
   638  func (p *Provisioner) processJsonUserVars() (map[string]interface{}, error) {
   639  	jsonBytes, err := json.Marshal(p.config.Json)
   640  	if err != nil {
   641  		// This really shouldn't happen since we literally just unmarshalled
   642  		panic(err)
   643  	}
   644  
   645  	// Copy the user variables so that we can restore them later, and
   646  	// make sure we make the quotes JSON-friendly in the user variables.
   647  	originalUserVars := make(map[string]string)
   648  	for k, v := range p.config.ctx.UserVariables {
   649  		originalUserVars[k] = v
   650  	}
   651  
   652  	// Make sure we reset them no matter what
   653  	defer func() {
   654  		p.config.ctx.UserVariables = originalUserVars
   655  	}()
   656  
   657  	// Make the current user variables JSON string safe.
   658  	for k, v := range p.config.ctx.UserVariables {
   659  		v = strings.Replace(v, `\`, `\\`, -1)
   660  		v = strings.Replace(v, `"`, `\"`, -1)
   661  		p.config.ctx.UserVariables[k] = v
   662  	}
   663  
   664  	// Process the bytes with the template processor
   665  	p.config.ctx.Data = nil
   666  	jsonBytesProcessed, err := interpolate.Render(string(jsonBytes), &p.config.ctx)
   667  	if err != nil {
   668  		return nil, err
   669  	}
   670  
   671  	var result map[string]interface{}
   672  	if err := json.Unmarshal([]byte(jsonBytesProcessed), &result); err != nil {
   673  		return nil, err
   674  	}
   675  
   676  	return result, nil
   677  }
   678  
   679  var DefaultConfigTemplate = `
   680  log_level        :info
   681  log_location     STDOUT
   682  chef_server_url  "{{.ServerUrl}}"
   683  client_key       "{{.ClientKey}}"
   684  {{if ne .EncryptedDataBagSecretPath ""}}
   685  encrypted_data_bag_secret "{{.EncryptedDataBagSecretPath}}"
   686  {{end}}
   687  {{if ne .ValidationClientName ""}}
   688  validation_client_name "{{.ValidationClientName}}"
   689  {{else}}
   690  validation_client_name "chef-validator"
   691  {{end}}
   692  {{if ne .ValidationKeyPath ""}}
   693  validation_key "{{.ValidationKeyPath}}"
   694  {{end}}
   695  node_name "{{.NodeName}}"
   696  {{if ne .ChefEnvironment ""}}
   697  environment "{{.ChefEnvironment}}"
   698  {{end}}
   699  {{if ne .PolicyGroup ""}}
   700  policy_group "{{.PolicyGroup}}"
   701  {{end}}
   702  {{if ne .PolicyName ""}}
   703  policy_name "{{.PolicyName}}"
   704  {{end}}
   705  {{if ne .SslVerifyMode ""}}
   706  ssl_verify_mode :{{.SslVerifyMode}}
   707  {{end}}
   708  {{if ne .TrustedCertsDir ""}}
   709  trusted_certs_dir "{{.TrustedCertsDir}}"
   710  {{end}}
   711  `
   712  
   713  var DefaultKnifeTemplate = `
   714  log_level        :info
   715  log_location     STDOUT
   716  chef_server_url  "{{.ServerUrl}}"
   717  client_key       "{{.ClientKey}}"
   718  node_name "{{.NodeName}}"
   719  {{if ne .SslVerifyMode ""}}
   720  ssl_verify_mode :{{.SslVerifyMode}}
   721  {{end}}
   722  {{if ne .TrustedCertsDir ""}}
   723  trusted_certs_dir "{{.TrustedCertsDir}}"
   724  {{end}}
   725  `