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