Update puppet-masterless commands to be OS specific

Previous implementation hardcoded "mkdir -p" which is fine for Unix, but
fails on Windows. This change draws on the example in the chef-solo
provisioner on how to detect the OS in use and use an appropriate mkdir
command.

In addition to updating the mkdir command, the actual executeCommand
needs to be OS specific, since Windows doesn't have sudo and Unix
doesn't require 'SET' when trying to change the value of a variable.

Modify the actual Windows command used to run Puppet.

Since the Facter vars on Windows are set with 'SET <varname>=<value>', a
'&&' is needed between the SET commands and the actual Puppet
invocation.
This commit is contained in:
Sam Kerr 2016-05-02 11:17:04 -04:00 committed by c22
parent 1c592f291e
commit bcd30ad2f2
1 changed files with 85 additions and 19 deletions

View File

@ -9,10 +9,11 @@ import (
"path/filepath"
"strings"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/provisioner"
"github.com/mitchellh/packer/template/interpolate"
)
type Config struct {
@ -61,10 +62,45 @@ type Config struct {
// If true, packer will ignore all exit-codes from a puppet run
IgnoreExitCodes bool `mapstructure:"ignore_exit_codes"`
GuestOSType string `mapstructure:"guest_os_type"`
ConfigTemplate string `mapstructure:"config_template"`
}
type guestOSTypeConfig struct {
stagingDir string
executeCommand string
}
var guestOSTypeConfigs = map[string]guestOSTypeConfig{
provisioner.UnixOSType: guestOSTypeConfig{
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}}",
},
provisioner.WindowsOSType: guestOSTypeConfig{
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}}",
},
}
type Provisioner struct {
config Config
config Config
guestOSTypeConfig guestOSTypeConfig
guestCommands *provisioner.GuestCommands
}
type ExecuteTemplate struct {
@ -95,15 +131,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
// Set some defaults
if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = "cd {{.WorkingDir}} && " +
"{{.FacterVars}} {{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}}"
p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
}
if p.config.StagingDir == "" {
@ -166,6 +194,37 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
}
}
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 == "" {
p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
}
if p.config.ConfigTemplate != "" {
fi, err := os.Stat(p.config.ConfigTemplate)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Bad config template path: %s", err))
} else if fi.IsDir() {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Config template path must be a file: %s", err))
}
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
@ -227,8 +286,15 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
}
// Execute Puppet
var facterVarsString string
if p.config.GuestOSType == provisioner.UnixOSType {
facterVarsString = strings.Join(facterVars, " ")
} else {
facterVarsString = "SET " + strings.Join(facterVars, " && SET ")
}
p.config.ctx.Data = &ExecuteTemplate{
FacterVars: strings.Join(facterVars, " "),
FacterVars: facterVarsString,
HieraConfigPath: remoteHieraConfigPath,
ManifestDir: remoteManifestDir,
ManifestFile: remoteManifestFile,
@ -249,7 +315,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
ui.Message(fmt.Sprintf("Running Puppet: %s", command))
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 {
@ -335,9 +401,9 @@ func (p *Provisioner) uploadManifests(ui packer.Ui, comm packer.Communicator) (s
}
func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
cmd := &packer.RemoteCmd{
Command: fmt.Sprintf("mkdir -p '%s'", dir),
}
ui.Message(fmt.Sprintf("Creating directory: %s", dir))
cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil {
return err