diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index 572b88212..b85388b6b 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -23,6 +23,7 @@ import ( type guestOSTypeConfig struct { executeCommand string installCommand string + knifeCommand string stagingDir string } @@ -30,11 +31,13 @@ var guestOSTypeConfigs = map[string]guestOSTypeConfig{ provisioner.UnixOSType: guestOSTypeConfig{ executeCommand: "{{if .Sudo}}sudo {{end}}chef-client --no-color -c {{.ConfigPath}} -j {{.JsonPath}}", installCommand: "curl -L https://www.chef.io/chef/install.sh | {{if .Sudo}}sudo {{end}}bash", + knifeCommand: "{{if .Sudo}}sudo {{end}}knife {{.Args}} {{.Flags}}", stagingDir: "/tmp/packer-chef-client", }, provisioner.WindowsOSType: guestOSTypeConfig{ executeCommand: "c:/opscode/chef/bin/chef-client.bat --no-color -c {{.ConfigPath}} -j {{.JsonPath}}", 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\"", + knifeCommand: "c:/opscode/chef/bin/knife.bat {{.Args}} {{.Flags}}", stagingDir: "C:/Windows/Temp/packer-chef-client", }, } @@ -51,6 +54,7 @@ type Config struct { ExecuteCommand string `mapstructure:"execute_command"` GuestOSType string `mapstructure:"guest_os_type"` InstallCommand string `mapstructure:"install_command"` + KnifeCommand string `mapstructure:"knife_command"` NodeName string `mapstructure:"node_name"` PreventSudo bool `mapstructure:"prevent_sudo"` RunList []string `mapstructure:"run_list"` @@ -93,6 +97,12 @@ type InstallChefTemplate struct { Sudo bool } +type KnifeTemplate struct { + Sudo bool + Flags string + Args string +} + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, @@ -101,6 +111,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { Exclude: []string{ "execute_command", "install_command", + "knife_command", }, }, }, raws...) @@ -140,6 +151,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.config.StagingDir = p.guestOSTypeConfig.stagingDir } + if p.config.KnifeCommand == "" { + p.config.KnifeCommand = p.guestOSTypeConfig.knifeCommand + } + var errs *packer.MultiError if p.config.ConfigTemplate != "" { fi, err := os.Stat(p.config.ConfigTemplate) @@ -484,13 +499,18 @@ func (p *Provisioner) knifeExec(ui packer.Ui, comm packer.Communicator, node str "-c", knifeConfigPath, } - cmdText := fmt.Sprintf( - "knife %s %s", strings.Join(args, " "), strings.Join(flags, " ")) - if !p.config.PreventSudo { - cmdText = "sudo " + cmdText + p.config.ctx.Data = &KnifeTemplate{ + Sudo: !p.config.PreventSudo, + Flags: strings.Join(flags, " "), + Args: strings.Join(args, " "), } - cmd := &packer.RemoteCmd{Command: cmdText} + command, err := interpolate.Render(p.config.KnifeCommand, &p.config.ctx) + if err != nil { + return err + } + + cmd := &packer.RemoteCmd{Command: command} if err := cmd.StartWithUi(comm, ui); err != nil { return err } @@ -498,7 +518,7 @@ func (p *Provisioner) knifeExec(ui packer.Ui, comm packer.Communicator, node str return fmt.Errorf( "Non-zero exit status. See output above for more info.\n\n"+ "Command: %s", - cmdText) + command) } return nil diff --git a/provisioner/chef-client/provisioner_test.go b/provisioner/chef-client/provisioner_test.go index 89eb05a6c..e84c81c72 100644 --- a/provisioner/chef-client/provisioner_test.go +++ b/provisioner/chef-client/provisioner_test.go @@ -87,6 +87,7 @@ func TestProvisionerPrepare_commands(t *testing.T) { commands := []string{ "execute_command", "install_command", + "knife_command", } for _, command := range commands { diff --git a/website/source/docs/provisioners/chef-client.html.md b/website/source/docs/provisioners/chef-client.html.md index 47b68ca7b..f26a4036b 100644 --- a/website/source/docs/provisioners/chef-client.html.md +++ b/website/source/docs/provisioners/chef-client.html.md @@ -71,6 +71,11 @@ configuration is actually required. - `json` (object) - An arbitrary mapping of JSON that will be available as node attributes while running Chef. +- `knife_command` (string) - The command used to run Knife during node clean-up. This has + various [configuration template + variables](/docs/templates/configuration-templates.html) available. See + below for more information. + - `node_name` (string) - The name of the node to register with the Chef Server. This is optional and by default is packer-{{uuid}}. @@ -194,7 +199,7 @@ This command can be customized using the `execute_command` configuration. As you can see from the default value above, the value of this configuration can contain various template variables, defined below: -- `ConfigPath` - The path to the Chef configuration file. file. +- `ConfigPath` - The path to the Chef configuration file. - `JsonPath` - The path to the JSON attributes file for the node. - `Sudo` - A boolean of whether to `sudo` the command or not, depending on the value of the `prevent_sudo` configuration. @@ -219,6 +224,37 @@ powershell.exe -Command "(New-Object System.Net.WebClient).DownloadFile('http:// This command can be customized using the `install_command` configuration. +## Knife Command + +By default, Packer uses the following command (broken across multiple lines for +readability) to execute Chef: + +``` {.liquid} +{{if .Sudo}}sudo {{end}}knife \ + {{.Args}} \ + {{.Flags}} +``` + +When guest_os_type is set to "windows", Packer uses the following command to +execute Chef. The full path to Chef is required because the PATH environment +variable changes don't immediately propogate to running processes. + +``` {.liquid} +c:/opscode/chef/bin/knife.bat \ + {{.Args}} \ + {{.Flags}} +``` + +This command can be customized using the `knife_command` configuration. As you +can see from the default value above, the value of this configuration can +contain various template variables, defined below: + +- `Args` - The command arguments that are getting passed to the Knife command. +- `Flags` - The command flags that are getting passed to the Knife command.. +- `Sudo` - A boolean of whether to `sudo` the command or not, depending on the + value of the `prevent_sudo` configuration. + + ## Folder Permissions !> The `chef-client` provisioner will chmod the directory with your Chef keys