diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index 4b01c84d3..3254a6134 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -20,6 +20,12 @@ type Config struct { common.PackerConfig `mapstructure:",squash"` ctx interpolate.Context + // If true, staging directory is removed after executing puppet. + CleanStagingDir bool `mapstructure:"clean_staging_directory"` + + // The Guest OS Type (unix or windows) + GuestOSType string `mapstructure:"guest_os_type"` + // The command used to execute Puppet. ExecuteCommand string `mapstructure:"execute_command"` @@ -32,6 +38,9 @@ type Config struct { // Path to a hiera configuration file to upload and use. HieraConfigPath string `mapstructure:"hiera_config_path"` + // If true, packer will ignore all exit-codes from a puppet run + IgnoreExitCodes bool `mapstructure:"ignore_exit_codes"` + // An array of local paths of modules to upload. ModulePaths []string `mapstructure:"module_paths"` @@ -45,47 +54,42 @@ type Config struct { // If true, `sudo` will NOT be used to execute Puppet. PreventSudo bool `mapstructure:"prevent_sudo"` + // The directory that contains the puppet binary. + // E.g. if it can't be found on the standard path. + PuppetBinDir string `mapstructure:"puppet_bin_dir"` + // The directory where files will be uploaded. Packer requires write // permissions in this directory. StagingDir string `mapstructure:"staging_directory"` - // If true, staging directory is removed after executing puppet. - CleanStagingDir bool `mapstructure:"clean_staging_directory"` - // The directory from which the command will be executed. // Packer requires the directory to exist when running puppet. WorkingDir string `mapstructure:"working_directory"` - - // The directory that contains the puppet binary. - // E.g. if it can't be found on the standard path. - PuppetBinDir string `mapstructure:"puppet_bin_dir"` - - // If true, packer will ignore all exit-codes from a puppet run - IgnoreExitCodes bool `mapstructure:"ignore_exit_codes"` - - // The Guest OS Type (unix or windows) - GuestOSType string `mapstructure:"guest_os_type"` } type guestOSTypeConfig struct { - stagingDir string executeCommand string facterVarsFmt string facterVarsJoiner string modulePathJoiner string + stagingDir string + tempDir string } +// FIXME assumes both Packer host and target are same OS var guestOSTypeConfigs = map[string]guestOSTypeConfig{ provisioner.UnixOSType: { + tempDir: "/tmp", stagingDir: "/tmp/packer-puppet-masterless", executeCommand: "cd {{.WorkingDir}} && " + `{{if ne .FacterVars ""}}{{.FacterVars}} {{end}}` + "{{if .Sudo}}sudo -E {{end}}" + `{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}` + - `puppet apply --verbose --modulepath='{{.ModulePath}}' ` + + "puppet apply --detailed-exitcodes " + + "{{if .Debug}}--debug {{end}}" + + `{{if ne .ModulePath ""}}--modulepath='{{.ModulePath}}' {{end}}` + `{{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}}` + `{{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}}` + - "--detailed-exitcodes " + `{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}` + "{{.ManifestFile}}", facterVarsFmt: "FACTER_%s='%s'", @@ -93,14 +97,16 @@ var guestOSTypeConfigs = map[string]guestOSTypeConfig{ modulePathJoiner: ":", }, provisioner.WindowsOSType: { - stagingDir: "C:/Windows/Temp/packer-puppet-masterless", + tempDir: filepath.ToSlash(os.Getenv("TEMP")), + stagingDir: filepath.ToSlash(os.Getenv("SYSTEMROOT")) + "/Temp/packer-puppet-masterless", executeCommand: "cd {{.WorkingDir}} && " + - "{{.FacterVars}} && " + + `{{if ne .FacterVars ""}}{{.FacterVars}} && {{end}}` + `{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}` + - `puppet apply --verbose --modulepath='{{.ModulePath}}' ` + + "puppet apply --detailed-exitcodes " + + "{{if .Debug}}--debug {{end}}" + + `{{if ne .ModulePath ""}}--modulepath='{{.ModulePath}}' {{end}}` + `{{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}}` + `{{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}}` + - "--detailed-exitcodes " + `{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}` + "{{.ManifestFile}}", facterVarsFmt: `SET "FACTER_%s=%s"`, @@ -116,15 +122,17 @@ type Provisioner struct { } type ExecuteTemplate struct { - WorkingDir string - FacterVars string - HieraConfigPath string - ModulePath string - ManifestFile string - ManifestDir string - PuppetBinDir string - Sudo bool - ExtraArguments string + Debug bool + ExtraArguments string + FacterVars string + HieraConfigPath string + ModulePath string + ModulePathJoiner string + ManifestFile string + ManifestDir string + PuppetBinDir string + Sudo bool + WorkingDir string } func (p *Provisioner) Prepare(raws ...interface{}) error { @@ -134,6 +142,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { InterpolateFilter: &interpolate.RenderFilter{ Exclude: []string{ "execute_command", + "extra_arguments", }, }, }, raws...) @@ -162,10 +171,6 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand } - if p.config.ExecuteCommand == "" { - p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand - } - if p.config.StagingDir == "" { p.config.StagingDir = p.guestOSTypeConfig.stagingDir } @@ -286,18 +291,26 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { facterVars = append(facterVars, fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, k, v)) } - // Execute Puppet - p.config.ctx.Data = &ExecuteTemplate{ - FacterVars: strings.Join(facterVars, p.guestOSTypeConfig.facterVarsJoiner), - HieraConfigPath: remoteHieraConfigPath, - ManifestDir: remoteManifestDir, - ManifestFile: remoteManifestFile, - ModulePath: strings.Join(modulePaths, p.guestOSTypeConfig.modulePathJoiner), - PuppetBinDir: p.config.PuppetBinDir, - Sudo: !p.config.PreventSudo, - WorkingDir: p.config.WorkingDir, - ExtraArguments: strings.Join(p.config.ExtraArguments, " "), + data := ExecuteTemplate{ + ExtraArguments: "", + FacterVars: strings.Join(facterVars, p.guestOSTypeConfig.facterVarsJoiner), + HieraConfigPath: remoteHieraConfigPath, + ManifestDir: remoteManifestDir, + ManifestFile: remoteManifestFile, + ModulePath: strings.Join(modulePaths, p.guestOSTypeConfig.modulePathJoiner), + ModulePathJoiner: p.guestOSTypeConfig.modulePathJoiner, + PuppetBinDir: p.config.PuppetBinDir, + Sudo: !p.config.PreventSudo, + WorkingDir: p.config.WorkingDir, } + + p.config.ctx.Data = &data + _ExtraArguments, err := interpolate.Render(strings.Join(p.config.ExtraArguments, " "), &p.config.ctx) + if err != nil { + return err + } + data.ExtraArguments = _ExtraArguments + command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) if err != nil { return err diff --git a/provisioner/puppet-masterless/provisioner_test.go b/provisioner/puppet-masterless/provisioner_test.go index 294534779..c8adce89e 100644 --- a/provisioner/puppet-masterless/provisioner_test.go +++ b/provisioner/puppet-masterless/provisioner_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "log" "os" + "path/filepath" "strings" "testing" @@ -58,7 +59,7 @@ func TestGuestOSConfig_empty_unix(t *testing.T) { } expected := "cd /tmp/packer-puppet-masterless && " + - "sudo -E puppet apply --verbose --modulepath='' --detailed-exitcodes /r/m/f" + "sudo -E puppet apply --detailed-exitcodes /r/m/f" assert.Equal(t, expected, command) } @@ -97,8 +98,8 @@ func TestGuestOSConfig_full_unix(t *testing.T) { expected := "cd /tmp/packer-puppet-masterless && FACTER_lhs='rhs' FACTER_foo='bar' " + "sudo -E puppet apply " + - "--verbose --modulepath='/m/p:/a/b' --hiera_config='/h/c/p' " + - "--manifestdir='/r/m/d' --detailed-exitcodes /r/m/f" + "--detailed-exitcodes --modulepath='/m/p:/a/b' --hiera_config='/h/c/p' " + + "--manifestdir='/r/m/d' /r/m/f" assert.Equal(t, expected, command) } @@ -126,7 +127,7 @@ func TestGuestOSConfig_empty_windows(t *testing.T) { t.Fatalf("err: %s", err) } - expected := "cd C:/Windows/Temp/packer-puppet-masterless && && puppet apply --verbose --modulepath='' --detailed-exitcodes /r/m/f" + expected := "cd " + filepath.ToSlash(os.Getenv("SYSTEMROOT")) + "/Temp/packer-puppet-masterless && puppet apply --detailed-exitcodes /r/m/f" assert.Equal(t, expected, command) } @@ -164,10 +165,10 @@ func TestGuestOSConfig_full_windows(t *testing.T) { t.Fatalf("err: %s", err) } - expected := "cd C:/Windows/Temp/packer-puppet-masterless && " + + expected := "cd " + filepath.ToSlash(os.Getenv("SYSTEMROOT")) + "/Temp/packer-puppet-masterless && " + "SET \"FACTER_lhs=rhs\" & SET \"FACTER_foo=bar\" && " + - "puppet apply --verbose --modulepath='/m/p;/a/b' --hiera_config='/h/c/p' " + - "--manifestdir='/r/m/d' --detailed-exitcodes /r/m/f" + "puppet apply --detailed-exitcodes --modulepath='/m/p;/a/b' --hiera_config='/h/c/p' " + + "--manifestdir='/r/m/d' /r/m/f" assert.Equal(t, expected, command) } diff --git a/provisioner/puppet-server/provisioner.go b/provisioner/puppet-server/provisioner.go index d7744952e..b7ec34ea7 100644 --- a/provisioner/puppet-server/provisioner.go +++ b/provisioner/puppet-server/provisioner.go @@ -5,6 +5,7 @@ package puppetserver import ( "fmt" "os" + "path/filepath" "strings" "github.com/hashicorp/packer/common" @@ -14,62 +15,40 @@ import ( "github.com/hashicorp/packer/template/interpolate" ) -type guestOSTypeConfig struct { - executeCommand string - facterVarsFmt string - facterVarsJoiner string - stagingDir string -} - -var guestOSTypeConfigs = map[string]guestOSTypeConfig{ - provisioner.UnixOSType: { - executeCommand: "{{.FacterVars}} {{if .Sudo}}sudo -E {{end}}" + - "{{if ne .PuppetBinDir \"\"}}{{.PuppetBinDir}}/{{end}}puppet agent " + - "--onetime --no-daemonize " + - "{{if ne .PuppetServer \"\"}}--server='{{.PuppetServer}}' {{end}}" + - "{{if ne .Options \"\"}}{{.Options}} {{end}}" + - "{{if ne .PuppetNode \"\"}}--certname={{.PuppetNode}} {{end}}" + - "{{if ne .ClientCertPath \"\"}}--certdir='{{.ClientCertPath}}' {{end}}" + - "{{if ne .ClientPrivateKeyPath \"\"}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}" + - "--detailed-exitcodes", - facterVarsFmt: "FACTER_%s='%s'", - facterVarsJoiner: " ", - stagingDir: "/tmp/packer-puppet-server", - }, - provisioner.WindowsOSType: { - executeCommand: "{{.FacterVars}} " + - "{{if ne .PuppetBinDir \"\"}}{{.PuppetBinDir}}/{{end}}puppet agent " + - "--onetime --no-daemonize " + - "{{if ne .PuppetServer \"\"}}--server='{{.PuppetServer}}' {{end}}" + - "{{if ne .Options \"\"}}{{.Options}} {{end}}" + - "{{if ne .PuppetNode \"\"}}--certname={{.PuppetNode}} {{end}}" + - "{{if ne .ClientCertPath \"\"}}--certdir='{{.ClientCertPath}}' {{end}}" + - "{{if ne .ClientPrivateKeyPath \"\"}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}" + - "--detailed-exitcodes", - facterVarsFmt: "SET \"FACTER_%s=%s\"", - facterVarsJoiner: " & ", - stagingDir: "C:/Windows/Temp/packer-puppet-server", - }, -} - type Config struct { common.PackerConfig `mapstructure:",squash"` ctx interpolate.Context + // If true, staging directory is removed after executing puppet. + CleanStagingDir bool `mapstructure:"clean_staging_directory"` + + // A path to the client certificate + ClientCertPath string `mapstructure:"client_cert_path"` + + // A path to a directory containing the client private keys + ClientPrivateKeyPath string `mapstructure:"client_private_key_path"` + // The command used to execute Puppet. ExecuteCommand string `mapstructure:"execute_command"` - // The Guest OS Type (unix or windows) - GuestOSType string `mapstructure:"guest_os_type"` + // Additional argument to pass when executing Puppet. + ExtraArguments []string `mapstructure:"extra_arguments"` // Additional facts to set when executing Puppet Facter map[string]string - // A path to the client certificate - ClientCertPath string `mapstructure:"client_cert_path"` + // The Guest OS Type (unix or windows) + GuestOSType string `mapstructure:"guest_os_type"` - // A path to a directory containing the client private keys - ClientPrivateKeyPath string `mapstructure:"client_private_key_path"` + // If true, packer will ignore all exit-codes from a puppet run + IgnoreExitCodes bool `mapstructure:"ignore_exit_codes"` + + // If true, `sudo` will NOT be used to execute Puppet. + PreventSudo bool `mapstructure:"prevent_sudo"` + + // The directory that contains the puppet binary. + // E.g. if it can't be found on the standard path. + PuppetBinDir string `mapstructure:"puppet_bin_dir"` // The hostname of the Puppet node. PuppetNode string `mapstructure:"puppet_node"` @@ -77,22 +56,58 @@ type Config struct { // The hostname of the Puppet server. PuppetServer string `mapstructure:"puppet_server"` - // Additional options to be passed to `puppet agent`. - Options string `mapstructure:"options"` - - // If true, `sudo` will NOT be used to execute Puppet. - PreventSudo bool `mapstructure:"prevent_sudo"` - // The directory where files will be uploaded. Packer requires write // permissions in this directory. StagingDir string `mapstructure:"staging_dir"` - // The directory that contains the puppet binary. - // E.g. if it can't be found on the standard path. - PuppetBinDir string `mapstructure:"puppet_bin_dir"` + // The directory from which the command will be executed. + // Packer requires the directory to exist when running puppet. + WorkingDir string `mapstructure:"working_directory"` +} - // If true, packer will ignore all exit-codes from a puppet run - IgnoreExitCodes bool `mapstructure:"ignore_exit_codes"` +type guestOSTypeConfig struct { + executeCommand string + facterVarsFmt string + facterVarsJoiner string + stagingDir string + tempDir string +} + +// FIXME assumes both Packer host and target are same OS +var guestOSTypeConfigs = map[string]guestOSTypeConfig{ + provisioner.UnixOSType: { + tempDir: "/tmp", + stagingDir: "/tmp/packer-puppet-server", + executeCommand: "cd {{.WorkingDir}} && " + + `{{if ne .FacterVars ""}}{{.FacterVars}} {{end}}` + + "{{if .Sudo}}sudo -E {{end}}" + + `{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}` + + "puppet agent --onetime --no-daemonize --detailed-exitcodes " + + "{{if .Debug}}--debug {{end}}" + + `{{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}}` + + `{{if ne .PuppetNode ""}}--certname='{{.PuppetNode}}' {{end}}` + + `{{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}}` + + `{{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}` + + `{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}`, + facterVarsFmt: "FACTER_%s='%s'", + facterVarsJoiner: " ", + }, + provisioner.WindowsOSType: { + tempDir: filepath.ToSlash(os.Getenv("TEMP")), + stagingDir: filepath.ToSlash(os.Getenv("SYSTEMROOT")) + "/Temp/packer-puppet-server", + executeCommand: "cd {{.WorkingDir}} && " + + `{{if ne .FacterVars ""}}{{.FacterVars}} && {{end}}` + + `{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}` + + "puppet agent --onetime --no-daemonize --detailed-exitcodes " + + "{{if .Debug}}--debug {{end}}" + + `{{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}}` + + `{{if ne .PuppetNode ""}}--certname='{{.PuppetNode}}' {{end}}` + + `{{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}}` + + `{{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}` + + `{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}`, + facterVarsFmt: `SET "FACTER_%s=%s"`, + facterVarsJoiner: " & ", + }, } type Provisioner struct { @@ -102,14 +117,16 @@ type Provisioner struct { } type ExecuteTemplate struct { - FacterVars string ClientCertPath string ClientPrivateKeyPath string + Debug bool + ExtraArguments string + FacterVars string PuppetNode string PuppetServer string - Options string PuppetBinDir string Sudo bool + WorkingDir string } func (p *Provisioner) Prepare(raws ...interface{}) error { @@ -119,6 +136,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { InterpolateFilter: &interpolate.RenderFilter{ Exclude: []string{ "execute_command", + "extra_arguments", }, }, }, raws...) @@ -150,6 +168,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.config.StagingDir = p.guestOSTypeConfig.stagingDir } + if p.config.WorkingDir == "" { + p.config.WorkingDir = p.config.StagingDir + } + if p.config.Facter == nil { p.config.Facter = make(map[string]string) } @@ -223,17 +245,25 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { facterVars = append(facterVars, fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, k, v)) } - // Execute Puppet - p.config.ctx.Data = &ExecuteTemplate{ - FacterVars: strings.Join(facterVars, p.guestOSTypeConfig.facterVarsJoiner), + data := ExecuteTemplate{ ClientCertPath: remoteClientCertPath, ClientPrivateKeyPath: remoteClientPrivateKeyPath, + ExtraArguments: "", + FacterVars: strings.Join(facterVars, p.guestOSTypeConfig.facterVarsJoiner), PuppetNode: p.config.PuppetNode, PuppetServer: p.config.PuppetServer, - Options: p.config.Options, PuppetBinDir: p.config.PuppetBinDir, Sudo: !p.config.PreventSudo, + WorkingDir: p.config.WorkingDir, + } + + p.config.ctx.Data = &data + _ExtraArguments, err := interpolate.Render(strings.Join(p.config.ExtraArguments, " "), &p.config.ctx) + if err != nil { + return err } + data.ExtraArguments = _ExtraArguments + command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) if err != nil { return err @@ -252,6 +282,12 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { return fmt.Errorf("Puppet exited with a non-zero exit status: %d", cmd.ExitStatus) } + if p.config.CleanStagingDir { + if err := p.removeDir(ui, comm, p.config.StagingDir); err != nil { + return fmt.Errorf("Error removing staging directory: %s", err) + } + } + return nil } @@ -284,6 +320,22 @@ func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir stri return nil } +func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error { + cmd := &packer.RemoteCmd{ + Command: fmt.Sprintf("rm -fr '%s'", dir), + } + + if err := cmd.StartWithUi(comm, ui); err != nil { + return err + } + + if cmd.ExitStatus != 0 { + return fmt.Errorf("Non-zero exit status.") + } + + return nil +} + func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, dst string, src string) error { if err := p.createDir(ui, comm, dst); err != nil { return err diff --git a/website/source/docs/provisioners/puppet-masterless.html.md b/website/source/docs/provisioners/puppet-masterless.html.md index 1e1689d03..9afb2f759 100644 --- a/website/source/docs/provisioners/puppet-masterless.html.md +++ b/website/source/docs/provisioners/puppet-masterless.html.md @@ -54,68 +54,64 @@ Required parameters: Optional parameters: -- `execute_command` (string) - The command used to execute Puppet. This has - various [configuration template - variables](/docs/templates/engine.html) available. See - below for more information. - -- `extra_arguments` (array of strings) - This is an array of additional options to - pass to the puppet command when executing puppet. This allows for - customization of the `execute_command` without having to completely replace - or include it's contents, making forward-compatible customizations much - easier. - -- `facter` (object of key/value strings) - Additional +- `execute_command` (string) - The command-line to execute Puppet. This also has + various [configuration template variables](/docs/templates/engine.html) available. + +- `extra_arguments` (array of strings) - Additional options to + pass to the Puppet command. This allows for customization of + `execute_command` without having to completely replace + or subsume its contents, making forward-compatible customizations much + easier to maintain. + + This string is lazy-evaluated so one can incorporate logic driven by template variables as + well as private elements of ExecuteTemplate (see source: provisioner/puppet-masterless/provisioner.go). +``` +[ + {{if ne "{{user environment}}" ""}}--environment={{user environment}}{{end}}, + {{if ne ".ModulePath" ""}}--modulepath="{{.ModulePath}}{{.ModulePathJoiner}}$(puppet config print {{if ne "{{user `environment`}}" ""}}--environment={{user `environment`}}{{end}} modulepath)"{{end}} +] +``` + +- `facter` (object of key:value strings) - Additional [facts](https://puppetlabs.com/facter) to make - available when Puppet is running. + available to the Puppet run. -- `guest_os_type` (string) - The target guest OS type, either "unix" or - "windows". Setting this to "windows" will cause the provisioner to use - Windows friendly paths and commands. By default, this is "unix". +- `guest_os_type` (string) - The remote host's OS type ('windows' or 'unix') to + tailor command-line and path separators. (default: unix). -- `hiera_config_path` (string) - The path to a local file with hiera - configuration to be uploaded to the remote machine. Hiera data directories - must be uploaded using the file provisioner separately. +- `hiera_config_path` (string) - Local path to self-contained Hiera + data to be uploaded. NOTE: If you need data directories + they must be previously transferred with a File provisioner. -- `ignore_exit_codes` (boolean) - If true, Packer will never consider the - provisioner a failure. +- `ignore_exit_codes` (boolean) - If true, Packer will ignore failures. -- `manifest_dir` (string) - The path to a local directory with manifests to be - uploaded to the remote machine. This is useful if your main manifest file - uses imports. This directory doesn't necessarily contain the - `manifest_file`. It is a separate directory that will be set as the - "manifestdir" setting on Puppet. +- `manifest_dir` (string) - Local directory with manifests to be + uploaded. This is useful if your main manifest uses imports, but the + directory might not contain the `manifest_file` itself. -~> `manifest_dir` is passed to `puppet apply` as the `--manifestdir` option. +~> `manifest_dir` is passed to Puppet as `--manifestdir` option. This option was deprecated in puppet 3.6, and removed in puppet 4.0. If you have multiple manifests you should use `manifest_file` instead. -- `module_paths` (array of strings) - This is an array of paths to module - directories on your local filesystem. These will be uploaded to the - remote machine. By default, this is empty. - -- `prevent_sudo` (boolean) - By default, the configured commands that are - executed to run Puppet are executed with `sudo`. If this is true, then the - sudo will be omitted. - -- `puppet_bin_dir` (string) - The path to the directory that contains the puppet - binary for running `puppet apply`. Usually, this would be found via the `$PATH` - or `%PATH%` environment variable, but some builders (notably, the Docker one) do - not run profile-setup scripts, therefore the path is usually empty. - -- `staging_directory` (string) - This is the directory where all the configuration - of Puppet by Packer will be placed. By default this is "/tmp/packer-puppet-masterless" - when guest OS type is unix and "C:/Windows/Temp/packer-puppet-masterless" when windows. - This directory doesn't need to exist but must have proper permissions so that the SSH - user that Packer uses is able to create directories and write into this folder. - If the permissions are not correct, use a shell provisioner prior to this to configure - it properly. - -- `working_directory` (string) - This is the directory from which the puppet - command will be run. When using hiera with a relative path, this option - allows to ensure that the paths are working properly. If not specified, - defaults to the value of specified `staging_directory` (or its default value - if not specified either). +- `module_paths` (array of strings) - Array of local module + directories to be uploaded. + +- `prevent_sudo` (boolean) - On Unix platforms Puppet is typically invoked with `sudo`. If true, + it will be omitted. (default: false) + +- `puppet_bin_dir` (string) - Path to the Puppet binary. Ideally the program + should be on the system (unix: `$PATH`, windows: `%PATH%`), but some builders (eg. Docker) do + not run profile-setup scripts and therefore PATH might be empty or minimal. + +- `staging_directory` (string) - Directory to where uploaded files + will be placed (unix: "/tmp/packer-puppet-masterless", + windows: "%SYSTEMROOT%/Temp/packer-puppet-masterless"). + It doesn't need to pre-exist, but the parent must have permissions sufficient + for the account Packer connects as to create directories and write files. + Use a Shell provisioner to prepare the way if needed. + +- `working_directory` (string) - Directory from which `execute_command` will be run. + If using Hiera files with relative paths, this option can be helpful. (default: `staging_directory`) ## Execute Command @@ -124,45 +120,33 @@ readability) to execute Puppet: ``` cd {{.WorkingDir}} && - {{if ne .FacterVars ""}}{{.FacterVars}} {{end}} -{{if .Sudo}}sudo -E {{end}} -{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}} -puppet apply --verbose --modulepath='{{.ModulePath}}' - {{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}} -{{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}} ---detailed-exitcodes - {{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}} -{{.ManifestFile}} + {{if ne .FacterVars ""}}{{.FacterVars}} {{end}} + {{if .Sudo}}sudo -E {{end}} + {{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}} + puppet apply --detailed-exitcodes + {{if .Debug}}--debug {{end}} + {{if ne .ModulePath ""}}--modulepath='{{.ModulePath}}' {{end}} + {{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}} + {{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}} + {{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}} + {{.ManifestFile}} ``` The following command is used if guest OS type is windows: ``` cd {{.WorkingDir}} && - {{.FacterVars}} && - {{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}} -puppet apply --verbose --modulepath='{{.ModulePath}}' - {{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}} -{{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}} ---detailed-exitcodes - {{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}} -{{.ManifestFile}} + {{if ne .FacterVars ""}}{{.FacterVars}} && {{end}} + {{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}} + puppet apply --detailed-exitcodes + {{if .Debug}}--debug {{end}} + {{if ne .ModulePath ""}}--modulepath='{{.ModulePath}}' {{end}} + {{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}} + {{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}} + {{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}} + {{.ManifestFile}} ``` -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: - -- `WorkingDir` - The path from which Puppet will be executed. -- `FacterVars` - Shell-friendly string of environmental variables used to set - custom facts configured for this provisioner. -- `HieraConfigPath` - The path to a hiera configuration file. -- `ManifestFile` - The path on the remote machine to the manifest file for - Puppet to use. -- `ModulePath` - The paths to the module directories. -- `Sudo` - A boolean of whether to `sudo` the command or not, depending on the - value of the `prevent_sudo` configuration. - ## Default Facts In addition to being able to specify custom Facter facts using the `facter` diff --git a/website/source/docs/provisioners/puppet-server.html.md b/website/source/docs/provisioners/puppet-server.html.md index 180f52b02..b8528822a 100644 --- a/website/source/docs/provisioners/puppet-server.html.md +++ b/website/source/docs/provisioners/puppet-server.html.md @@ -50,31 +50,38 @@ listed below: contains the client private key for the node. This defaults to nothing, in which case a client private key won't be uploaded. -- `execute_command` (string) - The command used to execute Puppet. This has +- `execute_command` (string) - The command-line to execute Puppet. This also has various [configuration template variables](/docs/templates/engine.html) available. - See below for more information. -- `facter` (object of key/value strings) - Additional Facter facts to make - available to the Puppet run. +- `extra_arguments` (array of strings) - Additional options to + pass to the Puppet command. This allows for customization of + `execute_command` without having to completely replace + or subsume its contents, making forward-compatible customizations much + easier to maintain. + + This string is lazy-evaluated so one can incorporate logic driven by template variables as + well as private elements of ExecuteTemplate (see source: provisioner/puppet-server/provisioner.go). +``` +[ + {{if ne "{{user environment}}" ""}}--environment={{user environment}}{{end}} +] +``` -- `guest_os_type` (string) - The target guest OS type, either "unix" or - "windows". Setting this to "windows" will cause the provisioner to use - Windows friendly paths and commands. By default, this is "unix". +- `facter` (object of key/value strings) - Additional + [facts](https://puppetlabs.com/facter) to make + available to the Puppet run. -- `ignore_exit_codes` (boolean) - If true, Packer will never consider the - provisioner a failure. +- `guest_os_type` (string) - The remote host's OS type ('windows' or 'unix') to + tailor command-line and path separators. (default: unix). -- `options` (string) - Additional command line options to pass to - `puppet agent` when Puppet is run. +- `ignore_exit_codes` (boolean) - If true, Packer will ignore failures. -- `prevent_sudo` (boolean) - By default, the configured commands that are - executed to run Puppet are executed with `sudo`. If this is true, then the - sudo will be omitted. +- `prevent_sudo` (boolean) - On Unix platforms Puppet is typically invoked with `sudo`. If true, + it will be omitted. (default: false) -- `puppet_bin_dir` (string) - The path to the directory that contains the puppet - binary for running `puppet agent`. Usually, this would be found via the `$PATH` - or `%PATH%` environment variable, but some builders (notably, the Docker one) do - not run profile-setup scripts, therefore the path is usually empty. +- `puppet_bin_dir` (string) - Path to the Puppet binary. Ideally the program + should be on the system (unix: `$PATH`, windows: `%PATH%`), but some builders (eg. Docker) do + not run profile-setup scripts and therefore PATH might be empty or minimal. - `puppet_node` (string) - The name of the node. If this isn't set, the fully qualified domain name will be used. @@ -82,42 +89,48 @@ listed below: - `puppet_server` (string) - Hostname of the Puppet server. By default "puppet" will be used. -- `staging_dir` (string) - This is the directory where all the - configuration of Puppet by Packer will be placed. By default this - is /tmp/packer-puppet-server. This directory doesn't need to exist but - must have proper permissions so that the SSH user that Packer uses is able - to create directories and write into this folder. If the permissions are not - correct, use a shell provisioner prior to this to configure it properly. +- `staging_dir` (string) - Directory to where uploaded files + will be placed (unix: "/tmp/packer-puppet-masterless", + windows: "%SYSTEMROOT%/Temp/packer-puppet-masterless"). + It doesn't need to pre-exist, but the parent must have permissions sufficient + for the account Packer connects as to create directories and write files. + Use a Shell provisioner to prepare the way if needed. + +- `working_directory` (string) - Directory from which `execute_command` will be run. + If using Hiera files with relative paths, this option can be helpful. (default: `staging_directory`) -## Execute Command + ## Execute Command By default, Packer uses the following command (broken across multiple lines for readability) to execute Puppet: ``` -{{.FacterVars}} {{if .Sudo}}sudo -E {{end}} -{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}puppet agent ---onetime --no-daemonize -{{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}} -{{if ne .Options ""}}{{.Options}} {{end}} -{{if ne .PuppetNode ""}}--certname={{.PuppetNode}} {{end}} -{{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}} -{{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}} ---detailed-exitcodes +cd {{.WorkingDir}} && + {{if ne .FacterVars ""}}{{.FacterVars}} {{end}} + {{if .Sudo}}sudo -E {{end}} + {{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}} + puppet agent --onetime --no-daemonize --detailed-exitcodes + {{if .Debug}}--debug {{end}} + {{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}} + {{if ne .PuppetNode ""}}--certname='{{.PuppetNode}}' {{end}} + {{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}} + {{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}} + {{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}} ``` The following command is used if guest OS type is windows: ``` -{{.FacterVars}} -{{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}puppet agent ---onetime --no-daemonize -{{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}} -{{if ne .Options ""}}{{.Options}} {{end}} -{{if ne .PuppetNode ""}}--certname={{.PuppetNode}} {{end}} -{{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}} -{{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}} ---detailed-exitcodes +cd {{.WorkingDir}} && + {{if ne .FacterVars ""}}{{.FacterVars}} && {{end}} + {{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}} + puppet agent --onetime --no-daemonize --detailed-exitcodes + {{if .Debug}}--debug {{end}} + {{if ne .PuppetServer ""}}--server='{{.PuppetServer}}' {{end}} + {{if ne .PuppetNode ""}}--certname='{{.PuppetNode}}' {{end}} + {{if ne .ClientCertPath ""}}--certdir='{{.ClientCertPath}}' {{end}} + {{if ne .ClientPrivateKeyPath ""}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}} + {{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}} ``` ## Default Facts