Merge branch 'master' of https://github.com/hashicorp/packer into vsphere-tpl

This commit is contained in:
bugbuilder 2017-08-31 22:22:51 -03:00
commit abc21406cb
19 changed files with 561 additions and 185 deletions

View File

@ -5,6 +5,9 @@
* provisioner/salt-masterless: Also use sudo to clean up if we used sudo to install. [GH-5240] * provisioner/salt-masterless: Also use sudo to clean up if we used sudo to install. [GH-5240]
* builder/profitbricks: added support for Cloud API v4. [GH-5233] * builder/profitbricks: added support for Cloud API v4. [GH-5233]
* builder/vmware: Set artifact ID to `VMName`. [GH-5187] * builder/vmware: Set artifact ID to `VMName`. [GH-5187]
* core: Fix issue where some builders wouldn't respect `-on-error` behavior. [GH-5297]
* builder/cloudstack: Add support for Security Groups. [GH-5175]
* provisioner/puppet: Add `guest_os_type` option to add support for Windows. [GH-5252]
### BACKWARDS INCOMPATIBILITIES: ### BACKWARDS INCOMPATIBILITIES:

View File

@ -1,6 +1,8 @@
package cloudstack package cloudstack
import ( import (
"fmt"
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
@ -61,8 +63,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
HTTPPortMin: b.config.HTTPPortMin, HTTPPortMin: b.config.HTTPPortMin,
HTTPPortMax: b.config.HTTPPortMax, HTTPPortMax: b.config.HTTPPortMax,
}, },
&stepKeypair{
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("cs_%s.pem", b.config.PackerBuildName),
KeyPair: b.config.Keypair,
PrivateKeyFile: b.config.Comm.SSHPrivateKey,
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
TemporaryKeyPairName: b.config.TemporaryKeypairName,
},
&stepCreateSecurityGroup{},
&stepCreateInstance{ &stepCreateInstance{
Ctx: b.config.ctx, Ctx: b.config.ctx,
Debug: b.config.PackerDebug,
}, },
&stepSetupNetworking{}, &stepSetupNetworking{},
&communicator.StepConnect{ &communicator.StepConnect{
@ -78,17 +90,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&stepCreateTemplate{}, &stepCreateTemplate{},
} }
// Configure the runner. // Configure the runner and run the steps.
if b.config.PackerDebug { b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
b.runner = &multistep.DebugRunner{
Steps: steps,
PauseFn: common.MultistepDebugFn(ui),
}
} else {
b.runner = &multistep.BasicRunner{Steps: steps}
}
// Run the steps.
b.runner.Run(state) b.runner.Run(state)
// If there was an error, return that // If there was an error, return that

View File

@ -27,23 +27,26 @@ type Config struct {
HTTPGetOnly bool `mapstructure:"http_get_only"` HTTPGetOnly bool `mapstructure:"http_get_only"`
SSLNoVerify bool `mapstructure:"ssl_no_verify"` SSLNoVerify bool `mapstructure:"ssl_no_verify"`
CIDRList []string `mapstructure:"cidr_list"` CIDRList []string `mapstructure:"cidr_list"`
DiskOffering string `mapstructure:"disk_offering"` CreateSecurityGroup bool `mapstructure:"create_security_group"`
DiskSize int64 `mapstructure:"disk_size"` DiskOffering string `mapstructure:"disk_offering"`
Expunge bool `mapstructure:"expunge"` DiskSize int64 `mapstructure:"disk_size"`
Hypervisor string `mapstructure:"hypervisor"` Expunge bool `mapstructure:"expunge"`
InstanceName string `mapstructure:"instance_name"` Hypervisor string `mapstructure:"hypervisor"`
Keypair string `mapstructure:"keypair"` InstanceName string `mapstructure:"instance_name"`
Network string `mapstructure:"network"` Keypair string `mapstructure:"keypair"`
Project string `mapstructure:"project"` Network string `mapstructure:"network"`
PublicIPAddress string `mapstructure:"public_ip_address"` Project string `mapstructure:"project"`
ServiceOffering string `mapstructure:"service_offering"` PublicIPAddress string `mapstructure:"public_ip_address"`
SourceTemplate string `mapstructure:"source_template"` SecurityGroups []string `mapstructure:"security_groups"`
SourceISO string `mapstructure:"source_iso"` ServiceOffering string `mapstructure:"service_offering"`
UserData string `mapstructure:"user_data"` SourceISO string `mapstructure:"source_iso"`
UserDataFile string `mapstructure:"user_data_file"` SourceTemplate string `mapstructure:"source_template"`
UseLocalIPAddress bool `mapstructure:"use_local_ip_address"` TemporaryKeypairName string `mapstructure:"temporary_keypair_name"`
Zone string `mapstructure:"zone"` UseLocalIPAddress bool `mapstructure:"use_local_ip_address"`
UserData string `mapstructure:"user_data"`
UserDataFile string `mapstructure:"user_data_file"`
Zone string `mapstructure:"zone"`
TemplateName string `mapstructure:"template_name"` TemplateName string `mapstructure:"template_name"`
TemplateDisplayText string `mapstructure:"template_display_text"` TemplateDisplayText string `mapstructure:"template_display_text"`
@ -98,7 +101,7 @@ func NewConfig(raws ...interface{}) (*Config, error) {
c.AsyncTimeout = 30 * time.Minute c.AsyncTimeout = 30 * time.Minute
} }
if len(c.CIDRList) == 0 && !c.UseLocalIPAddress { if len(c.CIDRList) == 0 {
c.CIDRList = []string{"0.0.0.0/0"} c.CIDRList = []string{"0.0.0.0/0"}
} }
@ -120,6 +123,14 @@ func NewConfig(raws ...interface{}) (*Config, error) {
c.TemplateDisplayText = c.TemplateName c.TemplateDisplayText = c.TemplateName
} }
// If we are not given an explicit keypair, ssh_password or ssh_private_key_file,
// then create a temporary one, but only if the temporary_keypair_name has not
// been provided.
if c.Keypair == "" && c.TemporaryKeypairName == "" &&
c.Comm.SSHPrivateKey == "" && c.Comm.SSHPassword == "" {
c.TemporaryKeypairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
}
// Process required parameters. // Process required parameters.
if c.APIURL == "" { if c.APIURL == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a api_url must be specified")) errs = packer.MultiErrorAppend(errs, errors.New("a api_url must be specified"))
@ -137,6 +148,10 @@ func NewConfig(raws ...interface{}) (*Config, error) {
errs = packer.MultiErrorAppend(errs, errors.New("a network must be specified")) errs = packer.MultiErrorAppend(errs, errors.New("a network must be specified"))
} }
if c.CreateSecurityGroup && !c.Expunge {
errs = packer.MultiErrorAppend(errs, errors.New("auto creating a temporary security group requires expunge"))
}
if c.ServiceOffering == "" { if c.ServiceOffering == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a service_offering must be specified")) errs = packer.MultiErrorAppend(errs, errors.New("a service_offering must be specified"))
} }

View File

@ -22,7 +22,8 @@ type userDataTemplateData struct {
// stepCreateInstance represents a Packer build step that creates CloudStack instances. // stepCreateInstance represents a Packer build step that creates CloudStack instances.
type stepCreateInstance struct { type stepCreateInstance struct {
Ctx interpolate.Context Debug bool
Ctx interpolate.Context
} }
// Run executes the Packer build step that creates a CloudStack instance. // Run executes the Packer build step that creates a CloudStack instance.
@ -44,8 +45,12 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
p.SetName(config.InstanceName) p.SetName(config.InstanceName)
p.SetDisplayname("Created by Packer") p.SetDisplayname("Created by Packer")
if config.Keypair != "" { if keypair, ok := state.GetOk("keypair"); ok {
p.SetKeypair(config.Keypair) p.SetKeypair(keypair.(string))
}
if securitygroups, ok := state.GetOk("security_groups"); ok {
p.SetSecuritygroupids(securitygroups.([]string))
} }
// If we use an ISO, configure the disk offering. // If we use an ISO, configure the disk offering.
@ -115,6 +120,12 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
ui.Message("Instance has been created!") ui.Message("Instance has been created!")
// In debug-mode, we output the password
if s.Debug {
ui.Message(fmt.Sprintf(
"Password (since debug is enabled) \"%s\"", instance.Password))
}
// Set the auto generated password if a password was not explicitly configured. // Set the auto generated password if a password was not explicitly configured.
switch config.Comm.Type { switch config.Comm.Type {
case "ssh": case "ssh":

View File

@ -0,0 +1,94 @@
package cloudstack
import (
"fmt"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/xanzy/go-cloudstack/cloudstack"
)
type stepCreateSecurityGroup struct {
tempSG string
}
func (s *stepCreateSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*cloudstack.CloudStackClient)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
if len(config.SecurityGroups) > 0 {
state.Put("security_groups", config.SecurityGroups)
return multistep.ActionContinue
}
if !config.CreateSecurityGroup {
return multistep.ActionContinue
}
ui.Say("Creating temporary Security Group...")
p := client.SecurityGroup.NewCreateSecurityGroupParams(
fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()),
)
p.SetDescription("Temporary SG created by Packer")
if config.Project != "" {
p.SetProjectid(config.Project)
}
sg, err := client.SecurityGroup.CreateSecurityGroup(p)
if err != nil {
err := fmt.Errorf("Failed to create security group: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.tempSG = sg.Id
state.Put("security_groups", []string{sg.Id})
// Create Ingress rule
i := client.SecurityGroup.NewAuthorizeSecurityGroupIngressParams()
i.SetCidrlist(config.CIDRList)
i.SetProtocol("TCP")
i.SetSecuritygroupid(sg.Id)
i.SetStartport(config.Comm.Port())
i.SetEndport(config.Comm.Port())
if config.Project != "" {
i.SetProjectid(config.Project)
}
_, err = client.SecurityGroup.AuthorizeSecurityGroupIngress(i)
if err != nil {
err := fmt.Errorf("Failed to authorize security group ingress rule: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
// Cleanup any resources that may have been created during the Run phase.
func (s *stepCreateSecurityGroup) Cleanup(state multistep.StateBag) {
client := state.Get("client").(*cloudstack.CloudStackClient)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
if s.tempSG == "" {
return
}
ui.Say(fmt.Sprintf("Cleanup temporary security group: %s ...", s.tempSG))
p := client.SecurityGroup.NewDeleteSecurityGroupParams()
p.SetId(s.tempSG)
if config.Project != "" {
p.SetProjectid(config.Project)
}
if _, err := client.SecurityGroup.DeleteSecurityGroup(p); err != nil {
ui.Error(err.Error())
ui.Error(fmt.Sprintf("Error deleting security group: %s. Please destroy it manually.\n", s.tempSG))
}
}

View File

@ -0,0 +1,133 @@
package cloudstack
import (
"fmt"
"io/ioutil"
"os"
"runtime"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/xanzy/go-cloudstack/cloudstack"
)
type stepKeypair struct {
Debug bool
DebugKeyPath string
KeyPair string
PrivateKeyFile string
SSHAgentAuth bool
TemporaryKeyPairName string
}
func (s *stepKeypair) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if s.PrivateKeyFile != "" {
privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile)
if err != nil {
state.Put("error", fmt.Errorf(
"Error loading configured private key file: %s", err))
return multistep.ActionHalt
}
state.Put("keypair", s.KeyPair)
state.Put("privateKey", string(privateKeyBytes))
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPair == "" {
ui.Say("Using SSH Agent with keypair in Source image")
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPair != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing keypair %s", s.KeyPair))
state.Put("keypair", s.KeyPair)
return multistep.ActionContinue
}
if s.TemporaryKeyPairName == "" {
ui.Say("Not using a keypair")
state.Put("keypair", "")
return multistep.ActionContinue
}
client := state.Get("client").(*cloudstack.CloudStackClient)
ui.Say(fmt.Sprintf("Creating temporary keypair: %s ...", s.TemporaryKeyPairName))
p := client.SSH.NewCreateSSHKeyPairParams(s.TemporaryKeyPairName)
keypair, err := client.SSH.CreateSSHKeyPair(p)
if err != nil {
err := fmt.Errorf("Error creating temporary keypair: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if keypair.Privatekey == "" {
err := fmt.Errorf("The temporary keypair returned was blank")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Created temporary keypair: %s", s.TemporaryKeyPairName))
// If we're in debug mode, output the private key to the working directory.
if s.Debug {
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
f, err := os.Create(s.DebugKeyPath)
if err != nil {
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
return multistep.ActionHalt
}
defer f.Close()
// Write the key out
if _, err := f.Write([]byte(keypair.Privatekey)); err != nil {
err := fmt.Errorf("Error saving debug key: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Chmod it so that it is SSH ready
if runtime.GOOS != "windows" {
if err := f.Chmod(0600); err != nil {
err := fmt.Errorf("Error setting permissions of debug key: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
// Set some state data for use in future steps
state.Put("keypair", s.TemporaryKeyPairName)
state.Put("privateKey", keypair.Privatekey)
return multistep.ActionContinue
}
func (s *stepKeypair) Cleanup(state multistep.StateBag) {
if s.TemporaryKeyPairName == "" {
return
}
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(*cloudstack.CloudStackClient)
ui.Say(fmt.Sprintf("Deleting temporary keypair: %s ...", s.TemporaryKeyPairName))
_, err := client.SSH.DeleteSSHKeyPair(client.SSH.NewDeleteSSHKeyPairParams(
s.TemporaryKeyPairName,
))
if err != nil {
ui.Error(err.Error())
ui.Error(fmt.Sprintf(
"Error cleaning up keypair. Please delete the key manually: %s", s.TemporaryKeyPairName))
}
}

View File

@ -22,15 +22,6 @@ func (s *stepPrepareConfig) Run(state multistep.StateBag) multistep.StepAction {
var err error var err error
var errs *packer.MultiError var errs *packer.MultiError
if config.Comm.SSHPrivateKey != "" {
privateKey, err := ioutil.ReadFile(config.Comm.SSHPrivateKey)
if err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error loading configured private key file: %s", err))
}
state.Put("privateKey", privateKey)
}
// First get the project and zone UUID's so we can use them in other calls when needed. // First get the project and zone UUID's so we can use them in other calls when needed.
if config.Project != "" && !isUUID(config.Project) { if config.Project != "" && !isUUID(config.Project) {
config.Project, _, err = client.Project.GetProjectID(config.Project) config.Project, _, err = client.Project.GetProjectID(config.Project)
@ -92,6 +83,18 @@ func (s *stepPrepareConfig) Run(state multistep.StateBag) multistep.StepAction {
} }
} }
// Then try to get the SG's UUID's.
if len(config.SecurityGroups) > 0 {
for i := range config.SecurityGroups {
if !isUUID(config.SecurityGroups[i]) {
config.SecurityGroups[i], _, err = client.SecurityGroup.GetSecurityGroupID(config.SecurityGroups[i], cloudstack.WithProject(config.Project))
if err != nil {
errs = packer.MultiErrorAppend(errs, &retrieveErr{"network", config.SecurityGroups[i], err})
}
}
}
}
if !isUUID(config.ServiceOffering) { if !isUUID(config.ServiceOffering) {
config.ServiceOffering, _, err = client.ServiceOffering.GetServiceOfferingID(config.ServiceOffering) config.ServiceOffering, _, err = client.ServiceOffering.GetServiceOfferingID(config.ServiceOffering)
if err != nil { if err != nil {

View File

@ -405,17 +405,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
} }
// Run the steps. // Run the steps.
if b.config.PackerDebug { b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
pauseFn := common.MultistepDebugFn(ui)
state.Put("pauseFn", pauseFn)
b.runner = &multistep.DebugRunner{
Steps: steps,
PauseFn: pauseFn,
}
} else {
b.runner = &multistep.BasicRunner{Steps: steps}
}
b.runner.Run(state) b.runner.Run(state)
// Report any errors. // Report any errors.

View File

@ -50,15 +50,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
new(stepTakeSnapshot), new(stepTakeSnapshot),
} }
if b.config.PackerDebug { b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
b.runner = &multistep.DebugRunner{
Steps: steps,
PauseFn: common.MultistepDebugFn(ui),
}
} else {
b.runner = &multistep.BasicRunner{Steps: steps}
}
b.runner.Run(state) b.runner.Run(state)
if rawErr, ok := state.GetOk("error"); ok { if rawErr, ok := state.GetOk("error"); ok {

View File

@ -49,15 +49,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
if b.config.PackerDebug { b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
b.runner = &multistep.DebugRunner{
Steps: steps,
PauseFn: common.MultistepDebugFn(ui),
}
} else {
b.runner = &multistep.BasicRunner{Steps: steps}
}
b.runner.Run(state) b.runner.Run(state)
if rawErr, ok := state.GetOk("error"); ok { if rawErr, ok := state.GetOk("error"); ok {

View File

@ -200,6 +200,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
default: default:
dir = new(vmwcommon.LocalOutputDir) dir = new(vmwcommon.LocalOutputDir)
} }
exportOutputPath := b.config.OutputDir
if b.config.RemoteType != "" && b.config.Format != "" { if b.config.RemoteType != "" && b.config.Format != "" {
b.config.OutputDir = b.config.VMName b.config.OutputDir = b.config.VMName
} }
@ -307,6 +310,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepExport{ &StepExport{
Format: b.config.Format, Format: b.config.Format,
SkipExport: b.config.SkipExport, SkipExport: b.config.SkipExport,
OutputDir: exportOutputPath,
}, },
} }
@ -332,7 +336,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
var files []string var files []string
if b.config.RemoteType != "" && b.config.Format != "" { if b.config.RemoteType != "" && b.config.Format != "" {
dir = new(vmwcommon.LocalOutputDir) dir = new(vmwcommon.LocalOutputDir)
dir.SetOutputDir(b.config.OutputDir) dir.SetOutputDir(exportOutputPath)
files, err = dir.ListFiles() files, err = dir.ListFiles()
} else { } else {
files, err = state.Get("dir").(OutputDir).ListFiles() files, err = state.Get("dir").(OutputDir).ListFiles()

View File

@ -6,7 +6,6 @@ import (
"net/url" "net/url"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"runtime" "runtime"
"strings" "strings"
@ -17,9 +16,10 @@ import (
type StepExport struct { type StepExport struct {
Format string Format string
SkipExport bool SkipExport bool
OutputDir string
} }
func (s *StepExport) generateArgs(c *Config, outputPath string, hidePassword bool) []string { func (s *StepExport) generateArgs(c *Config, hidePassword bool) []string {
password := url.QueryEscape(c.RemotePassword) password := url.QueryEscape(c.RemotePassword)
if hidePassword { if hidePassword {
password = "****" password = "****"
@ -29,7 +29,7 @@ func (s *StepExport) generateArgs(c *Config, outputPath string, hidePassword boo
"--skipManifestCheck", "--skipManifestCheck",
"-tt=" + s.Format, "-tt=" + s.Format,
"vi://" + c.RemoteUser + ":" + password + "@" + c.RemoteHost + "/" + c.VMName, "vi://" + c.RemoteUser + ":" + password + "@" + c.RemoteHost + "/" + c.VMName,
outputPath, s.OutputDir,
} }
return append(c.OVFToolOptions, args...) return append(c.OVFToolOptions, args...)
} }
@ -62,16 +62,18 @@ func (s *StepExport) Run(state multistep.StateBag) multistep.StepAction {
} }
// Export the VM // Export the VM
outputPath := filepath.Join(c.VMName, c.VMName+"."+s.Format) if s.OutputDir == "" {
s.OutputDir = c.VMName + "." + s.Format
}
if s.Format == "ova" { if s.Format == "ova" {
os.MkdirAll(outputPath, 0755) os.MkdirAll(s.OutputDir, 0755)
} }
ui.Say("Exporting virtual machine...") ui.Say("Exporting virtual machine...")
ui.Message(fmt.Sprintf("Executing: %s %s", ovftool, strings.Join(s.generateArgs(c, outputPath, true), " "))) ui.Message(fmt.Sprintf("Executing: %s %s", ovftool, strings.Join(s.generateArgs(c, true), " ")))
var out bytes.Buffer var out bytes.Buffer
cmd := exec.Command(ovftool, s.generateArgs(c, outputPath, false)...) cmd := exec.Command(ovftool, s.generateArgs(c, false)...)
cmd.Stdout = &out cmd.Stdout = &out
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
err := fmt.Errorf("Error exporting virtual machine: %s\n%s\n", err, out.String()) err := fmt.Errorf("Error exporting virtual machine: %s\n%s\n", err, out.String())
@ -82,8 +84,6 @@ func (s *StepExport) Run(state multistep.StateBag) multistep.StepAction {
ui.Message(fmt.Sprintf("%s", out.String())) ui.Message(fmt.Sprintf("%s", out.String()))
state.Put("exportPath", outputPath)
return multistep.ActionContinue return multistep.ActionContinue
} }

View File

@ -120,14 +120,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
} }
// Run the steps. // Run the steps.
if p.config.PackerDebug { p.runner = common.NewRunner(steps, p.config.PackerConfig, ui)
p.runner = &multistep.DebugRunner{
Steps: steps,
PauseFn: common.MultistepDebugFn(ui),
}
} else {
p.runner = &multistep.BasicRunner{Steps: steps}
}
p.runner.Run(state) p.runner.Run(state)
} }

View File

@ -164,15 +164,7 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
} }
// Run the steps // Run the steps
if p.config.PackerDebug { p.runner = common.NewRunner(steps, p.config.PackerConfig, ui)
p.runner = &multistep.DebugRunner{
Steps: steps,
PauseFn: common.MultistepDebugFn(ui),
}
} else {
p.runner = &multistep.BasicRunner{Steps: steps}
}
p.runner.Run(state) p.runner.Run(state)
// If there was an error, return that // If there was an error, return that

View File

@ -1,4 +1,4 @@
// This package implements a provisioner for Packer that executes // Package puppetmasterless implements a provisioner for Packer that executes
// Puppet on the remote machine, configured to apply a local manifest // Puppet on the remote machine, configured to apply a local manifest
// versus connecting to a Puppet master. // versus connecting to a Puppet master.
package puppetmasterless package puppetmasterless
@ -12,6 +12,7 @@ import (
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/provisioner"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
) )
@ -61,10 +62,51 @@ type Config struct {
// If true, packer will ignore all exit-codes from a puppet run // If true, packer will ignore all exit-codes from a puppet run
IgnoreExitCodes bool `mapstructure:"ignore_exit_codes"` 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
modulePathJoiner string
}
var guestOSTypeConfigs = map[string]guestOSTypeConfig{
provisioner.UnixOSType: {
stagingDir: "/tmp/packer-puppet-masterless",
executeCommand: "cd {{.WorkingDir}} && " +
"{{.FacterVars}} {{if .Sudo}} sudo -E {{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}}",
facterVarsFmt: "FACTER_%s='%s'",
modulePathJoiner: ":",
},
provisioner.WindowsOSType: {
stagingDir: "C:/Windows/Temp/packer-puppet-masterless",
executeCommand: "cd {{.WorkingDir}} && " +
"{{.FacterVars}} && " +
"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}}",
facterVarsFmt: "SET \"FACTER_%s=%s\" &",
modulePathJoiner: ";",
},
} }
type Provisioner struct { type Provisioner struct {
config Config config Config
guestOSTypeConfig guestOSTypeConfig
guestCommands *provisioner.GuestCommands
} }
type ExecuteTemplate struct { type ExecuteTemplate struct {
@ -94,20 +136,32 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
} }
// Set some defaults // Set some defaults
if p.config.GuestOSType == "" {
p.config.GuestOSType = provisioner.DefaultOSType
}
p.config.GuestOSType = strings.ToLower(p.config.GuestOSType)
var ok bool
p.guestOSTypeConfig, ok = guestOSTypeConfigs[p.config.GuestOSType]
if !ok {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
p.guestCommands, err = provisioner.NewGuestCommands(p.config.GuestOSType, !p.config.PreventSudo)
if err != nil {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
if p.config.ExecuteCommand == "" { if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = "cd {{.WorkingDir}} && " + p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
"{{.FacterVars}} {{if .Sudo}} sudo -E {{end}}" + }
"{{if ne .PuppetBinDir \"\"}}{{.PuppetBinDir}}/{{end}}puppet apply " +
"--verbose --modulepath='{{.ModulePath}}' " + if p.config.ExecuteCommand == "" {
"{{if ne .HieraConfigPath \"\"}}--hiera_config='{{.HieraConfigPath}}' {{end}}" + p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
"{{if ne .ManifestDir \"\"}}--manifestdir='{{.ManifestDir}}' {{end}}" +
"--detailed-exitcodes " +
"{{if ne .ExtraArguments \"\"}}{{.ExtraArguments}} {{end}}" +
"{{.ManifestFile}}"
} }
if p.config.StagingDir == "" { if p.config.StagingDir == "" {
p.config.StagingDir = "/tmp/packer-puppet-masterless" p.config.StagingDir = p.guestOSTypeConfig.stagingDir
} }
if p.config.WorkingDir == "" { if p.config.WorkingDir == "" {
@ -223,7 +277,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
// Compile the facter variables // Compile the facter variables
facterVars := make([]string, 0, len(p.config.Facter)) facterVars := make([]string, 0, len(p.config.Facter))
for k, v := range p.config.Facter { for k, v := range p.config.Facter {
facterVars = append(facterVars, fmt.Sprintf("FACTER_%s='%s'", k, v)) facterVars = append(facterVars, fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, k, v))
} }
// Execute Puppet // Execute Puppet
@ -232,7 +286,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
HieraConfigPath: remoteHieraConfigPath, HieraConfigPath: remoteHieraConfigPath,
ManifestDir: remoteManifestDir, ManifestDir: remoteManifestDir,
ManifestFile: remoteManifestFile, ManifestFile: remoteManifestFile,
ModulePath: strings.Join(modulePaths, ":"), ModulePath: strings.Join(modulePaths, p.guestOSTypeConfig.modulePathJoiner),
PuppetBinDir: p.config.PuppetBinDir, PuppetBinDir: p.config.PuppetBinDir,
Sudo: !p.config.PreventSudo, Sudo: !p.config.PreventSudo,
WorkingDir: p.config.WorkingDir, WorkingDir: p.config.WorkingDir,
@ -249,7 +303,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
ui.Message(fmt.Sprintf("Running Puppet: %s", command)) ui.Message(fmt.Sprintf("Running Puppet: %s", command))
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.StartWithUi(comm, ui); err != nil {
return err return fmt.Errorf("Got an error starting command: %s", err)
} }
if cmd.ExitStatus != 0 && cmd.ExitStatus != 2 && !p.config.IgnoreExitCodes { if cmd.ExitStatus != 0 && cmd.ExitStatus != 2 && !p.config.IgnoreExitCodes {
@ -314,30 +368,29 @@ func (p *Provisioner) uploadManifests(ui packer.Ui, comm packer.Communicator) (s
return "", fmt.Errorf("Error uploading manifest dir: %s", err) return "", fmt.Errorf("Error uploading manifest dir: %s", err)
} }
return remoteManifestDir, nil return remoteManifestDir, nil
} else {
// Otherwise manifest_file is a file and we'll upload it
ui.Message(fmt.Sprintf(
"Uploading manifest file from: %s", p.config.ManifestFile))
f, err := os.Open(p.config.ManifestFile)
if err != nil {
return "", err
}
defer f.Close()
manifestFilename := filepath.Base(p.config.ManifestFile)
remoteManifestFile := fmt.Sprintf("%s/%s", remoteManifestsPath, manifestFilename)
if err := comm.Upload(remoteManifestFile, f, nil); err != nil {
return "", err
}
return remoteManifestFile, nil
} }
// Otherwise manifest_file is a file and we'll upload it
ui.Message(fmt.Sprintf(
"Uploading manifest file from: %s", p.config.ManifestFile))
f, err := os.Open(p.config.ManifestFile)
if err != nil {
return "", err
}
defer f.Close()
manifestFilename := filepath.Base(p.config.ManifestFile)
remoteManifestFile := fmt.Sprintf("%s/%s", remoteManifestsPath, manifestFilename)
if err := comm.Upload(remoteManifestFile, f, nil); err != nil {
return "", err
}
return remoteManifestFile, nil
} }
func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
cmd := &packer.RemoteCmd{ ui.Message(fmt.Sprintf("Creating directory: %s", dir))
Command: fmt.Sprintf("mkdir -p '%s'", dir),
} cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.StartWithUi(comm, ui); err != nil {
return err return err

View File

@ -1,4 +1,4 @@
// This package implements a provisioner for Packer that executes // Package puppetserver implements a provisioner for Packer that executes
// Puppet on the remote machine connecting to a Puppet master. // Puppet on the remote machine connecting to a Puppet master.
package puppetserver package puppetserver
@ -10,9 +10,45 @@ import (
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/provisioner"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
) )
type guestOSTypeConfig struct {
executeCommand string
facterVarsFmt 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'",
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\" &",
stagingDir: "C:/Windows/Temp/packer-puppet-server",
},
}
type Config struct { type Config struct {
common.PackerConfig `mapstructure:",squash"` common.PackerConfig `mapstructure:",squash"`
ctx interpolate.Context ctx interpolate.Context
@ -20,6 +56,9 @@ type Config struct {
// The command used to execute Puppet. // The command used to execute Puppet.
ExecuteCommand string `mapstructure:"execute_command"` ExecuteCommand string `mapstructure:"execute_command"`
// The Guest OS Type (unix or windows)
GuestOSType string `mapstructure:"guest_os_type"`
// Additional facts to set when executing Puppet // Additional facts to set when executing Puppet
Facter map[string]string Facter map[string]string
@ -54,7 +93,9 @@ type Config struct {
} }
type Provisioner struct { type Provisioner struct {
config Config config Config
guestOSTypeConfig guestOSTypeConfig
guestCommands *provisioner.GuestCommands
} }
type ExecuteTemplate struct { type ExecuteTemplate struct {
@ -82,12 +123,28 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
return err return err
} }
if p.config.GuestOSType == "" {
p.config.GuestOSType = provisioner.DefaultOSType
}
p.config.GuestOSType = strings.ToLower(p.config.GuestOSType)
var ok bool
p.guestOSTypeConfig, ok = guestOSTypeConfigs[p.config.GuestOSType]
if !ok {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
p.guestCommands, err = provisioner.NewGuestCommands(p.config.GuestOSType, !p.config.PreventSudo)
if err != nil {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
if p.config.ExecuteCommand == "" { if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = p.commandTemplate() p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
} }
if p.config.StagingDir == "" { if p.config.StagingDir == "" {
p.config.StagingDir = "/tmp/packer-puppet-server" p.config.StagingDir = p.guestOSTypeConfig.stagingDir
} }
if p.config.Facter == nil { if p.config.Facter == nil {
@ -160,7 +217,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
// Compile the facter variables // Compile the facter variables
facterVars := make([]string, 0, len(p.config.Facter)) facterVars := make([]string, 0, len(p.config.Facter))
for k, v := range p.config.Facter { for k, v := range p.config.Facter {
facterVars = append(facterVars, fmt.Sprintf("FACTER_%s='%s'", k, v)) facterVars = append(facterVars, fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, k, v))
} }
// Execute Puppet // Execute Puppet
@ -202,16 +259,23 @@ func (p *Provisioner) Cancel() {
} }
func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
cmd := &packer.RemoteCmd{ ui.Message(fmt.Sprintf("Creating directory: %s", dir))
Command: fmt.Sprintf("mkdir -p '%s'", dir),
}
cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.StartWithUi(comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus != 0 {
return fmt.Errorf("Non-zero exit status.") return fmt.Errorf("Non-zero exit status. See output above for more info.")
}
// Chmod the directory to 0777 just so that we can access it as our user
cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.StartWithUi(comm, ui); err != nil {
return err
}
if cmd.ExitStatus != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.")
} }
return nil return nil
@ -230,15 +294,3 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds
return comm.UploadDir(dst, src, nil) return comm.UploadDir(dst, src, nil)
} }
func (p *Provisioner) commandTemplate() string {
return "{{.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"
}

View File

@ -74,6 +74,11 @@ builder.
connect to the instance. Defaults to `[ "0.0.0.0/0" ]`. Only required connect to the instance. Defaults to `[ "0.0.0.0/0" ]`. Only required
when `use_local_ip_address` is `false`. when `use_local_ip_address` is `false`.
- `create_security_group` (boolean) - If `true` a temporary security group
will be created which allows traffic towards the instance from the
`cidr_list`. This option will be ignored if `security_groups` is also
defined. Requires `expunge` set to `true`. Defaults to `false`.
- `disk_offering` (string) - The name or ID of the disk offering used for the - `disk_offering` (string) - The name or ID of the disk offering used for the
instance. This option is only available (and also required) when using instance. This option is only available (and also required) when using
`source_iso`. `source_iso`.
@ -118,6 +123,9 @@ builder.
connecting any provisioners to. If not provided, a temporary public IP connecting any provisioners to. If not provided, a temporary public IP
address will be associated and released during the Packer run. address will be associated and released during the Packer run.
- `security_groups` (array of strings) - A list of security group IDs or names
to associate the instance with.
- `ssh_agent_auth` (boolean) - If true, the local SSH agent will be used to - `ssh_agent_auth` (boolean) - If true, the local SSH agent will be used to
authenticate connections to the source instance. No temporary keypair will authenticate connections to the source instance. No temporary keypair will
be created, and the values of `ssh_password` and `ssh_private_key_file` will be created, and the values of `ssh_password` and `ssh_private_key_file` will
@ -149,6 +157,10 @@ builder.
- `template_scalable` (boolean) - Set to `true` to indicate that the template - `template_scalable` (boolean) - Set to `true` to indicate that the template
contains tools to support dynamic scaling of VM cpu/memory. Defaults to `false`. contains tools to support dynamic scaling of VM cpu/memory. Defaults to `false`.
- `temporary_keypair_name` (string) - The name of the temporary SSH key pair
to generate. By default, Packer generates a name that looks like
`packer_<UUID>`, where &lt;UUID&gt; is a 36 character unique identifier.
- `user_data` (string) - User data to launch with the instance. This is a - `user_data` (string) - User data to launch with the instance. This is a
[template engine](/docs/templates/engine.html) see _User Data_ bellow for more [template engine](/docs/templates/engine.html) see _User Data_ bellow for more
details. details.

View File

@ -59,6 +59,10 @@ Optional parameters:
variables](/docs/templates/engine.html) available. See variables](/docs/templates/engine.html) available. See
below for more information. below for more information.
- `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".
- `extra_arguments` (array of strings) - This is an array of additional options to - `extra_arguments` (array of strings) - This is an array of additional options to
pass to the puppet command when executing puppet. This allows for pass to the puppet command when executing puppet. This allows for
customization of the `execute_command` without having to completely replace customization of the `execute_command` without having to completely replace
@ -99,12 +103,13 @@ multiple manifests you should use `manifest_file` instead.
executed to run Puppet are executed with `sudo`. If this is true, then the executed to run Puppet are executed with `sudo`. If this is true, then the
sudo will be omitted. sudo will be omitted.
- `staging_directory` (string) - This is the directory where all the - `staging_directory` (string) - This is the directory where all the configuration
configuration of Puppet by Packer will be placed. By default this of Puppet by Packer will be placed. By default this is "/tmp/packer-puppet-masterless"
is "/tmp/packer-puppet-masterless". This directory doesn't need to exist but when guest OS type is unix and "C:/Windows/Temp/packer-puppet-masterless" when windows.
must have proper permissions so that the SSH user that Packer uses is able This directory doesn't need to exist but must have proper permissions so that the SSH
to create directories and write into this folder. If the permissions are not user that Packer uses is able to create directories and write into this folder.
correct, use a shell provisioner prior to this to configure it properly. 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 - `working_directory` (string) - This is the directory from which the puppet
command will be run. When using hiera with a relative path, this option command will be run. When using hiera with a relative path, this option
@ -117,17 +122,28 @@ multiple manifests you should use `manifest_file` instead.
By default, Packer uses the following command (broken across multiple lines for By default, Packer uses the following command (broken across multiple lines for
readability) to execute Puppet: readability) to execute Puppet:
``` liquid ```
cd {{.WorkingDir}} && \ cd {{.WorkingDir}} &&
{{.FacterVars}}{{if .Sudo}} sudo -E {{end}} \ {{.FacterVars}} {{if .Sudo}} sudo -E {{end}}
{{if ne .PuppetBinDir \"\"}}{{.PuppetBinDir}}{{end}}puppet apply \ puppet apply --verbose --modulepath='{{.ModulePath}}'
--verbose \ {{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}}
--modulepath='{{.ModulePath}}' \ {{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}}
{{if ne .HieraConfigPath ""}}--hiera_config='{{.HieraConfigPath}}' {{end}} \ --detailed-exitcodes
{{if ne .ManifestDir ""}}--manifestdir='{{.ManifestDir}}' {{end}} \ {{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}}
--detailed-exitcodes \ {{.ManifestFile}}
{{if ne .ExtraArguments ""}}{{.ExtraArguments}} {{end}} \ ```
{{.ManifestFile}}
The following command is used if guest OS type is windows:
```
cd {{.WorkingDir}} &&
{{.FacterVars}} &&
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}}
``` ```
This command can be customized using the `execute_command` configuration. As you This command can be customized using the `execute_command` configuration. As you

View File

@ -81,20 +81,38 @@ listed below:
or `%PATH%` environment variable, but some builders (notably, the Docker one) do or `%PATH%` environment variable, but some builders (notably, the Docker one) do
not run profile-setup scripts, therefore the path is usually empty. not run profile-setup scripts, therefore the path is usually empty.
- `execute_command` (string) - This is optional. The command used to execute Puppet. This has - `guest_os_type` (string) - The target guest OS type, either "unix" or
various [configuration template "windows". Setting this to "windows" will cause the provisioner to use
variables](/docs/templates/engine.html) available. See Windows friendly paths and commands. By default, this is "unix".
below for more information. By default, Packer uses the following command:
``` liquid - `execute_command` (string) - This is optional. The command used to execute Puppet. This has
{{.FacterVars}} {{if .Sudo}} sudo -E {{end}} \ various [configuration template variables](/docs/templates/engine.html) available. By default,
{{if ne .PuppetBinDir \"\"}}{{.PuppetBinDir}}/{{end}}puppet agent --onetime --no-daemonize \ Packer uses the following command (broken across multiple lines for readability) to execute Puppet:
{{if ne .PuppetServer \"\"}}--server='{{.PuppetServer}}' {{end}} \
{{if ne .Options \"\"}}{{.Options}} {{end}} \ ```
{{if ne .PuppetNode \"\"}}--certname={{.PuppetNode}} {{end}} \ {{.FacterVars}} {{if .Sudo}}sudo -E {{end}}
{{if ne .ClientCertPath \"\"}}--certdir='{{.ClientCertPath}}' {{end}} \ {{if ne .PuppetBinDir ""}}{{.PuppetBinDir}}/{{end}}puppet agent
{{if ne .ClientPrivateKeyPath \"\"}}--privatekeydir='{{.ClientPrivateKeyPath}}' \ --onetime --no-daemonize
{{end}} --detailed-exitcodes {{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
```
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
``` ```
## Default Facts ## Default Facts