From bcd30ad2f22e204bf96347f2872f46753e281e9b Mon Sep 17 00:00:00 2001 From: Sam Kerr Date: Mon, 2 May 2016 11:17:04 -0400 Subject: [PATCH] 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 =', a '&&' is needed between the SET commands and the actual Puppet invocation. --- provisioner/puppet-masterless/provisioner.go | 104 +++++++++++++++---- 1 file changed, 85 insertions(+), 19 deletions(-) diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index 6f1b6eee4..f3fd7cd18 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -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