From 361d7fbf8ec246e789d2823691e7e541e35b8fe4 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sun, 21 Jul 2013 21:20:39 -0700 Subject: [PATCH 1/9] provisioner/salt: install salt --- config.go | 3 +- plugin/provisioner-salt/main.go | 10 +++ provisioner/salt/provisioner.go | 147 ++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 plugin/provisioner-salt/main.go create mode 100644 provisioner/salt/provisioner.go diff --git a/config.go b/config.go index 4660fad1c..dac02d0cd 100644 --- a/config.go +++ b/config.go @@ -38,7 +38,8 @@ const defaultConfig = ` "provisioners": { "file": "packer-provisioner-file", - "shell": "packer-provisioner-shell" + "shell": "packer-provisioner-shell", + "salt": "packer-provisioner-salt" } } ` diff --git a/plugin/provisioner-salt/main.go b/plugin/provisioner-salt/main.go new file mode 100644 index 000000000..622611e67 --- /dev/null +++ b/plugin/provisioner-salt/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/mitchellh/packer/packer/plugin" + "github.com/mitchellh/packer/provisioner/salt" +) + +func main() { + plugin.ServeProvisioner(new(salt.Provisioner)) +} diff --git a/provisioner/salt/provisioner.go b/provisioner/salt/provisioner.go new file mode 100644 index 000000000..2912a3df8 --- /dev/null +++ b/provisioner/salt/provisioner.go @@ -0,0 +1,147 @@ +// This package implements a provisioner for Packer that executes a +// saltstack highstate within the remote machine +package salt + +import ( + "fmt" + "github.com/mitchellh/iochan" + "github.com/mitchellh/mapstructure" + "github.com/mitchellh/packer/packer" + "io" + "log" + "sort" + "strings" +) + +var Ui packer.Ui + +type config struct { + // If true, skips installing Salt. Defaults to false. + SkipInstall bool `mapstructure:"skip_install"` +} + +type Provisioner struct { + config config +} + +func (p *Provisioner) Prepare(raws ...interface{}) error { + var md mapstructure.Metadata + decoderConfig := &mapstructure.DecoderConfig{ + Metadata: &md, + Result: &p.config, + } + + decoder, err := mapstructure.NewDecoder(decoderConfig) + if err != nil { + return err + } + + for _, raw := range raws { + err := decoder.Decode(raw) + if err != nil { + return err + } + } + + // Accumulate any errors + errs := make([]error, 0) + + // Unused keys are errors + if len(md.Unused) > 0 { + sort.Strings(md.Unused) + for _, unused := range md.Unused { + if unused != "type" && !strings.HasPrefix(unused, "packer_") { + errs = append( + errs, fmt.Errorf("Unknown configuration key: %s", unused)) + } + } + } + + if len(errs) > 0 { + return &packer.MultiError{errs} + } + + return nil +} + +func InstallSalt(comm packer.Communicator) (err error) { + Ui.Say("Installing Salt") + cmd := "wget -O - http://bootstrap.saltstack.org | sudo sh" + if err = executeCommand(cmd, comm); err != nil { + return fmt.Errorf("Unable to install Salt: %d", err) + } + + return nil +} + +func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { + var err error + Ui = ui + + if !p.config.SkipInstall { + if err = InstallSalt(comm); err != nil { + return fmt.Errorf("Error installing Salt: %s", err) + } + } + + return nil +} + +func executeCommand(command string, comm packer.Communicator) (err error) { + // Setup the remote command + stdout_r, stdout_w := io.Pipe() + stderr_r, stderr_w := io.Pipe() + + var cmd packer.RemoteCmd + cmd.Command = command + cmd.Stdout = stdout_w + cmd.Stderr = stderr_w + + log.Printf("Executing command: %s", cmd.Command) + err = comm.Start(&cmd) + if err != nil { + return fmt.Errorf("Failed executing command: %s", err) + } + + exitChan := make(chan int, 1) + stdoutChan := iochan.DelimReader(stdout_r, '\n') + stderrChan := iochan.DelimReader(stderr_r, '\n') + + go func() { + defer stdout_w.Close() + defer stderr_w.Close() + + cmd.Wait() + exitChan <- cmd.ExitStatus + }() + +OutputLoop: + for { + select { + case output := <-stderrChan: + Ui.Message(strings.TrimSpace(output)) + case output := <-stdoutChan: + Ui.Message(strings.TrimSpace(output)) + case exitStatus := <-exitChan: + log.Printf("Chef Solo provisioner exited with status %d", exitStatus) + + if exitStatus != 0 { + return fmt.Errorf("Command exited with non-zero exit status: %d", exitStatus) + } + + break OutputLoop + } + } + + // Make sure we finish off stdout/stderr because we may have gotten + // a message from the exit channel first. + for output := range stdoutChan { + Ui.Message(output) + } + + for output := range stderrChan { + Ui.Message(output) + } + + return nil +} From a74a515aa7048ea66f0869ed47e45ac6c6cba5c4 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Fri, 26 Jul 2013 14:14:41 -0700 Subject: [PATCH 2/9] provisioner/salt: pass args to bootstrap --- provisioner/salt/provisioner.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/provisioner/salt/provisioner.go b/provisioner/salt/provisioner.go index 2912a3df8..c934cf509 100644 --- a/provisioner/salt/provisioner.go +++ b/provisioner/salt/provisioner.go @@ -16,8 +16,9 @@ import ( var Ui packer.Ui type config struct { - // If true, skips installing Salt. Defaults to false. - SkipInstall bool `mapstructure:"skip_install"` + // If true, run the salt-bootstrap script + SkipBootstrap bool `mapstructure:"skip_bootstrap"` + BootstrapArgs string `mapstructure:"bootstrap_args"` } type Provisioner struct { @@ -64,26 +65,17 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } -func InstallSalt(comm packer.Communicator) (err error) { - Ui.Say("Installing Salt") - cmd := "wget -O - http://bootstrap.saltstack.org | sudo sh" - if err = executeCommand(cmd, comm); err != nil { - return fmt.Errorf("Unable to install Salt: %d", err) - } - - return nil -} - func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { var err error Ui = ui - if !p.config.SkipInstall { - if err = InstallSalt(comm); err != nil { - return fmt.Errorf("Error installing Salt: %s", err) + if !p.config.SkipBootstrap { + cmd := fmt.Sprintf("wget -O - http://bootstrap.saltstack.org | sudo sh -s %s", p.config.BootstrapArgs) + Ui.Say(fmt.Sprintf("Installing Salt with command %s", cmd)) + if err = executeCommand(cmd, comm); err != nil { + return fmt.Errorf("Unable to install Salt: %d", err) } } - return nil } From 9478a75cdb623b75532f399c1e011e11dc86042c Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Fri, 26 Jul 2013 14:15:02 -0700 Subject: [PATCH 3/9] provisioner/salt: docs --- .../docs/provisioners/salt.html.markdown | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 website/source/docs/provisioners/salt.html.markdown diff --git a/website/source/docs/provisioners/salt.html.markdown b/website/source/docs/provisioners/salt.html.markdown new file mode 100644 index 000000000..827d67eca --- /dev/null +++ b/website/source/docs/provisioners/salt.html.markdown @@ -0,0 +1,29 @@ +--- +layout: "docs" +--- + +# Salt Provisioner + +Type: `salt` + +The salt provisioner provisions machines built by Packer using [Salt](http://saltstack.com/) states. + +## Basic Example + +The example below is fully functional. + +
+{
+    "type": "salt",
+    "bootstrap_args": "git v0.14.0"
+}
+
+ +## Configuration Reference + +The reference of available configuration options is listed below: + +* `skip_bootstrap` (boolean) - By default Packer runs [salt bootstrap](https://github.com/saltstack/salt-bootstrap) to install salt. Set this to true to skip this step. + +* `boostrap_args` (string) - + Arguments to send to the bootstrap script. Usage is somewhat documented on [github](https://github.com/saltstack/salt-bootstrap), but the [script itself](https://github.com/saltstack/salt-bootstrap/blob/develop/bootstrap-salt.sh) has more detailed usage instructions. Default is no arguments. From 8f0d4890d900bb1d3c8ec2c9db1b18cfd1b0a2f5 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Fri, 26 Jul 2013 23:35:43 -0700 Subject: [PATCH 4/9] provisioner/salt: copy local state tree to remote, run highstate --- provisioner/salt/provisioner.go | 81 +++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/provisioner/salt/provisioner.go b/provisioner/salt/provisioner.go index c934cf509..3bac91921 100644 --- a/provisioner/salt/provisioner.go +++ b/provisioner/salt/provisioner.go @@ -3,12 +3,15 @@ package salt import ( + "errors" "fmt" "github.com/mitchellh/iochan" "github.com/mitchellh/mapstructure" "github.com/mitchellh/packer/packer" "io" "log" + "os" + "path/filepath" "sort" "strings" ) @@ -19,6 +22,9 @@ type config struct { // If true, run the salt-bootstrap script SkipBootstrap bool `mapstructure:"skip_bootstrap"` BootstrapArgs string `mapstructure:"bootstrap_args"` + + // Local path to the salt state tree + LocalStateTree string `mapstructure:"local_state_tree"` } type Provisioner struct { @@ -58,6 +64,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } } + if p.config.LocalStateTree == "" { + errs = append(errs, errors.New("Please specify a local_state_tree")) + } + if len(errs) > 0 { return &packer.MultiError{errs} } @@ -72,14 +82,79 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { if !p.config.SkipBootstrap { cmd := fmt.Sprintf("wget -O - http://bootstrap.saltstack.org | sudo sh -s %s", p.config.BootstrapArgs) Ui.Say(fmt.Sprintf("Installing Salt with command %s", cmd)) - if err = executeCommand(cmd, comm); err != nil { + if err = ExecuteCommand(cmd, comm); err != nil { return fmt.Errorf("Unable to install Salt: %d", err) } } + + remoteTempDir := "salt" + Ui.Say(fmt.Sprintf("Creating remote directory: %s", remoteTempDir)) + if err = ExecuteCommand(fmt.Sprintf("mkdir -p %s", remoteTempDir), comm); err != nil { + return fmt.Errorf("Error creating remote salt state directory: %s", err) + } + + Ui.Say(fmt.Sprintf("Uploading local state tree: %s", p.config.LocalStateTree)) + if err = UploadLocalDirectory(p.config.LocalStateTree, remoteTempDir, comm); err != nil { + return fmt.Errorf("Error uploading local state tree to remote: %s", err) + } + + Ui.Say(fmt.Sprintf("Moving %s to /srv/salt", remoteTempDir)) + if err = ExecuteCommand(fmt.Sprintf("sudo mv %s /srv/salt", remoteTempDir), comm); err != nil { + return fmt.Errorf("Unable to move %s to /srv/salt: %d", remoteTempDir, err) + } + + Ui.Say("Running highstate") + if err = ExecuteCommand("sudo salt-call --local state.highstate -l debug", comm); err != nil { + return fmt.Errorf("Error executing highstate: %s", err) + } + + Ui.Say("Removing /srv/salt") + if err = ExecuteCommand("sudo rm -r /srv/salt", comm); err != nil { + return fmt.Errorf("Unable to remove /srv/salt: %d", err) + } + + return nil +} + +func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communicator) (err error) { + visitPath := func(localPath string, f os.FileInfo, err error) (err2 error) { + localRelPath := strings.Replace(localPath, localDir, "", 1) + remotePath := fmt.Sprintf("%s/%s", remoteDir, localRelPath) + if f.IsDir() && f.Name() == ".git" { + return filepath.SkipDir + } + if f.IsDir() { + // Make remote directory + err = ExecuteCommand(fmt.Sprintf("mkdir -p %s", remotePath), comm) + if err != nil { + return err + } + } else { + // Upload file to existing directory + file, err := os.Open(localPath) + if err != nil { + return fmt.Errorf("Error opening file: %s", err) + } + defer file.Close() + + Ui.Say(fmt.Sprintf("Uploading file %s: %s", localPath, remotePath)) + err = comm.Upload(remotePath, file) + if err != nil { + return fmt.Errorf("Error uploading file: %s", err) + } + } + return + } + + err = filepath.Walk(localDir, visitPath) + if err != nil { + return fmt.Errorf("Error uploading local directory %s: %s", localDir, err) + } + return nil } -func executeCommand(command string, comm packer.Communicator) (err error) { +func ExecuteCommand(command string, comm packer.Communicator) (err error) { // Setup the remote command stdout_r, stdout_w := io.Pipe() stderr_r, stderr_w := io.Pipe() @@ -115,7 +190,7 @@ OutputLoop: case output := <-stdoutChan: Ui.Message(strings.TrimSpace(output)) case exitStatus := <-exitChan: - log.Printf("Chef Solo provisioner exited with status %d", exitStatus) + log.Printf("Salt provisioner exited with status %d", exitStatus) if exitStatus != 0 { return fmt.Errorf("Command exited with non-zero exit status: %d", exitStatus) From 1289437565fe0e50df5036ad0054d766f1514422 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Fri, 26 Jul 2013 23:36:14 -0700 Subject: [PATCH 5/9] provisioner/salt: document 3c1f142 --- .../source/docs/provisioners/salt.html.markdown | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/website/source/docs/provisioners/salt.html.markdown b/website/source/docs/provisioners/salt.html.markdown index 827d67eca..e93797c47 100644 --- a/website/source/docs/provisioners/salt.html.markdown +++ b/website/source/docs/provisioners/salt.html.markdown @@ -15,15 +15,22 @@ The example below is fully functional.
 {
     "type": "salt",
-    "bootstrap_args": "git v0.14.0"
+    "bootstrap_args": "git v0.16.0"
+    "local_state_tree": "/Users/me/salt"
 }
 
## Configuration Reference -The reference of available configuration options is listed below: +The reference of available configuration options is listed below. The only required argument is the path to your local salt state tree. -* `skip_bootstrap` (boolean) - By default Packer runs [salt bootstrap](https://github.com/saltstack/salt-bootstrap) to install salt. Set this to true to skip this step. +Required: + +* `local_state_tree` (string) - The path to your local [state tree](http://docs.saltstack.com/ref/states/highstate.html#the-salt-state-tree). This will be uploaded to the `/srv/salt` on the remote, and removed before shutdown. + +Optional: + +* `skip_bootstrap` (boolean) - By default the salt provisioner runs [salt bootstrap](https://github.com/saltstack/salt-bootstrap) to install salt. Set this to true to skip this step. * `boostrap_args` (string) - - Arguments to send to the bootstrap script. Usage is somewhat documented on [github](https://github.com/saltstack/salt-bootstrap), but the [script itself](https://github.com/saltstack/salt-bootstrap/blob/develop/bootstrap-salt.sh) has more detailed usage instructions. Default is no arguments. + Arguments to send to the bootstrap script. Usage is somewhat documented on [github](https://github.com/saltstack/salt-bootstrap), but the [script itself](https://github.com/saltstack/salt-bootstrap/blob/develop/bootstrap-salt.sh) has more detailed usage instructions. By default, no arguments are sent to the script. From 9c25bb5cd7757317171fae8dbc3f305d438e6bc9 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 27 Jul 2013 00:00:01 -0700 Subject: [PATCH 6/9] provisioner/salt: put state tree temporarily in /tmp/salt by default --- provisioner/salt/provisioner.go | 24 ++++++++++++------- .../docs/provisioners/salt.html.markdown | 2 ++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/provisioner/salt/provisioner.go b/provisioner/salt/provisioner.go index 3bac91921..833200edf 100644 --- a/provisioner/salt/provisioner.go +++ b/provisioner/salt/provisioner.go @@ -25,6 +25,9 @@ type config struct { // Local path to the salt state tree LocalStateTree string `mapstructure:"local_state_tree"` + + // Where files will be copied before moving to the /srv/salt directory + TempConfigDir string `mapstructure:"temp_config_dir"` } type Provisioner struct { @@ -68,6 +71,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { errs = append(errs, errors.New("Please specify a local_state_tree")) } + if p.config.TempConfigDir == "" { + p.config.TempConfigDir = "/tmp/salt" + } + if len(errs) > 0 { return &packer.MultiError{errs} } @@ -87,24 +94,23 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { } } - remoteTempDir := "salt" - Ui.Say(fmt.Sprintf("Creating remote directory: %s", remoteTempDir)) - if err = ExecuteCommand(fmt.Sprintf("mkdir -p %s", remoteTempDir), comm); err != nil { + Ui.Say(fmt.Sprintf("Creating remote directory: %s", p.config.TempConfigDir)) + if err = ExecuteCommand(fmt.Sprintf("mkdir -p %s", p.config.TempConfigDir), comm); err != nil { return fmt.Errorf("Error creating remote salt state directory: %s", err) } Ui.Say(fmt.Sprintf("Uploading local state tree: %s", p.config.LocalStateTree)) - if err = UploadLocalDirectory(p.config.LocalStateTree, remoteTempDir, comm); err != nil { + if err = UploadLocalDirectory(p.config.LocalStateTree, p.config.TempConfigDir, comm); err != nil { return fmt.Errorf("Error uploading local state tree to remote: %s", err) } - Ui.Say(fmt.Sprintf("Moving %s to /srv/salt", remoteTempDir)) - if err = ExecuteCommand(fmt.Sprintf("sudo mv %s /srv/salt", remoteTempDir), comm); err != nil { - return fmt.Errorf("Unable to move %s to /srv/salt: %d", remoteTempDir, err) + Ui.Say(fmt.Sprintf("Moving %s to /srv/salt", p.config.TempConfigDir)) + if err = ExecuteCommand(fmt.Sprintf("sudo mv %s /srv/salt", p.config.TempConfigDir), comm); err != nil { + return fmt.Errorf("Unable to move %s to /srv/salt: %d", p.config.TempConfigDir, err) } Ui.Say("Running highstate") - if err = ExecuteCommand("sudo salt-call --local state.highstate -l debug", comm); err != nil { + if err = ExecuteCommand("sudo salt-call --local state.highstate -l info", comm); err != nil { return fmt.Errorf("Error executing highstate: %s", err) } @@ -119,7 +125,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communicator) (err error) { visitPath := func(localPath string, f os.FileInfo, err error) (err2 error) { localRelPath := strings.Replace(localPath, localDir, "", 1) - remotePath := fmt.Sprintf("%s/%s", remoteDir, localRelPath) + remotePath := fmt.Sprintf("%s%s", remoteDir, localRelPath) if f.IsDir() && f.Name() == ".git" { return filepath.SkipDir } diff --git a/website/source/docs/provisioners/salt.html.markdown b/website/source/docs/provisioners/salt.html.markdown index e93797c47..239ad36b7 100644 --- a/website/source/docs/provisioners/salt.html.markdown +++ b/website/source/docs/provisioners/salt.html.markdown @@ -34,3 +34,5 @@ Optional: * `boostrap_args` (string) - Arguments to send to the bootstrap script. Usage is somewhat documented on [github](https://github.com/saltstack/salt-bootstrap), but the [script itself](https://github.com/saltstack/salt-bootstrap/blob/develop/bootstrap-salt.sh) has more detailed usage instructions. By default, no arguments are sent to the script. + +* `temp_config_dir` (string) - Where your local state tree will be copied before moving to the `/srv/salt` directory. Default is `/tmp/salt`. From 5feadedba24ab4d067c0e87cd83fc209148ad6b5 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 27 Jul 2013 00:11:04 -0700 Subject: [PATCH 7/9] provisioner/salt: simple tests --- provisioner/salt/provisioner.go | 4 ++- provisioner/salt/provisioner_test.go | 46 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 provisioner/salt/provisioner_test.go diff --git a/provisioner/salt/provisioner.go b/provisioner/salt/provisioner.go index 833200edf..9f641828c 100644 --- a/provisioner/salt/provisioner.go +++ b/provisioner/salt/provisioner.go @@ -18,6 +18,8 @@ import ( var Ui packer.Ui +const DefaultTempConfigDir = "/tmp/salt" + type config struct { // If true, run the salt-bootstrap script SkipBootstrap bool `mapstructure:"skip_bootstrap"` @@ -72,7 +74,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } if p.config.TempConfigDir == "" { - p.config.TempConfigDir = "/tmp/salt" + p.config.TempConfigDir = DefaultTempConfigDir } if len(errs) > 0 { diff --git a/provisioner/salt/provisioner_test.go b/provisioner/salt/provisioner_test.go new file mode 100644 index 000000000..0e3242d1f --- /dev/null +++ b/provisioner/salt/provisioner_test.go @@ -0,0 +1,46 @@ +package salt + +import ( + "github.com/mitchellh/packer/packer" + "testing" +) + +func testConfig() map[string]interface{} { + return map[string]interface{}{ + "local_state_tree": "/Users/me/salt", + } +} + +func TestProvisioner_Impl(t *testing.T) { + var raw interface{} + raw = &Provisioner{} + if _, ok := raw.(packer.Provisioner); !ok { + t.Fatalf("must be a Provisioner") + } +} + +func TestProvisionerPrepare_Defaults(t *testing.T) { + var p Provisioner + config := testConfig() + + err := p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } + + if p.config.TempConfigDir != DefaultTempConfigDir { + t.Errorf("unexpected temp config dir: %s", p.config.TempConfigDir) + } +} + +func TestProvisionerPrepare_InvalidKey(t *testing.T) { + var p Provisioner + config := testConfig() + + // Add a random key + config["i_should_not_be_valid"] = true + err := p.Prepare(config) + if err == nil { + t.Fatal("should have error") + } +} From 7019281ad645d09e58b3d3029af9212fcf52400e Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sat, 27 Jul 2013 18:12:18 -0700 Subject: [PATCH 8/9] provisioner/salt: use cmd.StartWithUi --- provisioner/salt/provisioner.go | 87 ++++++--------------------------- 1 file changed, 15 insertions(+), 72 deletions(-) diff --git a/provisioner/salt/provisioner.go b/provisioner/salt/provisioner.go index 9f641828c..961fe351a 100644 --- a/provisioner/salt/provisioner.go +++ b/provisioner/salt/provisioner.go @@ -5,11 +5,8 @@ package salt import ( "errors" "fmt" - "github.com/mitchellh/iochan" "github.com/mitchellh/mapstructure" "github.com/mitchellh/packer/packer" - "io" - "log" "os" "path/filepath" "sort" @@ -89,15 +86,18 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { Ui = ui if !p.config.SkipBootstrap { - cmd := fmt.Sprintf("wget -O - http://bootstrap.saltstack.org | sudo sh -s %s", p.config.BootstrapArgs) + cmd := &packer.RemoteCmd{ + Command: fmt.Sprintf("wget -O - http://bootstrap.saltstack.org | sudo sh -s %s", p.config.BootstrapArgs), + } Ui.Say(fmt.Sprintf("Installing Salt with command %s", cmd)) - if err = ExecuteCommand(cmd, comm); err != nil { + if err = cmd.StartWithUi(comm, ui); err != nil { return fmt.Errorf("Unable to install Salt: %d", err) } } Ui.Say(fmt.Sprintf("Creating remote directory: %s", p.config.TempConfigDir)) - if err = ExecuteCommand(fmt.Sprintf("mkdir -p %s", p.config.TempConfigDir), comm); err != nil { + cmd := &packer.RemoteCmd{Command: fmt.Sprintf("mkdir -p %s", p.config.TempConfigDir)} + if err = cmd.StartWithUi(comm, ui); err != nil { return fmt.Errorf("Error creating remote salt state directory: %s", err) } @@ -107,17 +107,20 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { } Ui.Say(fmt.Sprintf("Moving %s to /srv/salt", p.config.TempConfigDir)) - if err = ExecuteCommand(fmt.Sprintf("sudo mv %s /srv/salt", p.config.TempConfigDir), comm); err != nil { + cmd = &packer.RemoteCmd{Command: fmt.Sprintf("sudo mv %s /srv/salt", p.config.TempConfigDir)} + if err = cmd.StartWithUi(comm, ui); err != nil { return fmt.Errorf("Unable to move %s to /srv/salt: %d", p.config.TempConfigDir, err) } Ui.Say("Running highstate") - if err = ExecuteCommand("sudo salt-call --local state.highstate -l info", comm); err != nil { + cmd = &packer.RemoteCmd{Command: "sudo salt-call --local state.highstate -l info"} + if err = cmd.StartWithUi(comm, ui); err != nil { return fmt.Errorf("Error executing highstate: %s", err) } Ui.Say("Removing /srv/salt") - if err = ExecuteCommand("sudo rm -r /srv/salt", comm); err != nil { + cmd = &packer.RemoteCmd{Command: "sudo rm -r /srv/salt"} + if err = cmd.StartWithUi(comm, ui); err != nil { return fmt.Errorf("Unable to remove /srv/salt: %d", err) } @@ -133,8 +136,8 @@ func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communi } if f.IsDir() { // Make remote directory - err = ExecuteCommand(fmt.Sprintf("mkdir -p %s", remotePath), comm) - if err != nil { + cmd := &packer.RemoteCmd{Command: fmt.Sprintf("mkdir -p %s", remotePath)} + if err = cmd.StartWithUi(comm, Ui); err != nil { return err } } else { @@ -146,8 +149,7 @@ func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communi defer file.Close() Ui.Say(fmt.Sprintf("Uploading file %s: %s", localPath, remotePath)) - err = comm.Upload(remotePath, file) - if err != nil { + if err = comm.Upload(remotePath, file); err != nil { return fmt.Errorf("Error uploading file: %s", err) } } @@ -161,62 +163,3 @@ func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communi return nil } - -func ExecuteCommand(command string, comm packer.Communicator) (err error) { - // Setup the remote command - stdout_r, stdout_w := io.Pipe() - stderr_r, stderr_w := io.Pipe() - - var cmd packer.RemoteCmd - cmd.Command = command - cmd.Stdout = stdout_w - cmd.Stderr = stderr_w - - log.Printf("Executing command: %s", cmd.Command) - err = comm.Start(&cmd) - if err != nil { - return fmt.Errorf("Failed executing command: %s", err) - } - - exitChan := make(chan int, 1) - stdoutChan := iochan.DelimReader(stdout_r, '\n') - stderrChan := iochan.DelimReader(stderr_r, '\n') - - go func() { - defer stdout_w.Close() - defer stderr_w.Close() - - cmd.Wait() - exitChan <- cmd.ExitStatus - }() - -OutputLoop: - for { - select { - case output := <-stderrChan: - Ui.Message(strings.TrimSpace(output)) - case output := <-stdoutChan: - Ui.Message(strings.TrimSpace(output)) - case exitStatus := <-exitChan: - log.Printf("Salt provisioner exited with status %d", exitStatus) - - if exitStatus != 0 { - return fmt.Errorf("Command exited with non-zero exit status: %d", exitStatus) - } - - break OutputLoop - } - } - - // Make sure we finish off stdout/stderr because we may have gotten - // a message from the exit channel first. - for output := range stdoutChan { - Ui.Message(output) - } - - for output := range stderrChan { - Ui.Message(output) - } - - return nil -} From 29993b30e442c3c1ae2cca6353044f759a3c12f3 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Sun, 28 Jul 2013 23:30:03 -0700 Subject: [PATCH 9/9] provisioner/salt-masterless: rename --- config.go | 2 +- plugin/provisioner-salt-masterless/main.go | 10 ++++++++++ plugin/provisioner-salt/main.go | 10 ---------- provisioner/{salt => salt-masterless}/provisioner.go | 2 +- .../{salt => salt-masterless}/provisioner_test.go | 2 +- website/source/docs/provisioners/salt.html.markdown | 8 ++++---- 6 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 plugin/provisioner-salt-masterless/main.go delete mode 100644 plugin/provisioner-salt/main.go rename provisioner/{salt => salt-masterless}/provisioner.go (99%) rename provisioner/{salt => salt-masterless}/provisioner_test.go (97%) diff --git a/config.go b/config.go index dac02d0cd..a53aecafa 100644 --- a/config.go +++ b/config.go @@ -39,7 +39,7 @@ const defaultConfig = ` "provisioners": { "file": "packer-provisioner-file", "shell": "packer-provisioner-shell", - "salt": "packer-provisioner-salt" + "salt-masterless": "packer-provisioner-salt-masterless" } } ` diff --git a/plugin/provisioner-salt-masterless/main.go b/plugin/provisioner-salt-masterless/main.go new file mode 100644 index 000000000..8547a7a88 --- /dev/null +++ b/plugin/provisioner-salt-masterless/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/mitchellh/packer/packer/plugin" + "github.com/mitchellh/packer/provisioner/salt-masterless" +) + +func main() { + plugin.ServeProvisioner(new(saltMasterless.Provisioner)) +} diff --git a/plugin/provisioner-salt/main.go b/plugin/provisioner-salt/main.go deleted file mode 100644 index 622611e67..000000000 --- a/plugin/provisioner-salt/main.go +++ /dev/null @@ -1,10 +0,0 @@ -package main - -import ( - "github.com/mitchellh/packer/packer/plugin" - "github.com/mitchellh/packer/provisioner/salt" -) - -func main() { - plugin.ServeProvisioner(new(salt.Provisioner)) -} diff --git a/provisioner/salt/provisioner.go b/provisioner/salt-masterless/provisioner.go similarity index 99% rename from provisioner/salt/provisioner.go rename to provisioner/salt-masterless/provisioner.go index 961fe351a..6a0b1f189 100644 --- a/provisioner/salt/provisioner.go +++ b/provisioner/salt-masterless/provisioner.go @@ -1,6 +1,6 @@ // This package implements a provisioner for Packer that executes a // saltstack highstate within the remote machine -package salt +package saltMasterless import ( "errors" diff --git a/provisioner/salt/provisioner_test.go b/provisioner/salt-masterless/provisioner_test.go similarity index 97% rename from provisioner/salt/provisioner_test.go rename to provisioner/salt-masterless/provisioner_test.go index 0e3242d1f..99ce05d25 100644 --- a/provisioner/salt/provisioner_test.go +++ b/provisioner/salt-masterless/provisioner_test.go @@ -1,4 +1,4 @@ -package salt +package saltMasterless import ( "github.com/mitchellh/packer/packer" diff --git a/website/source/docs/provisioners/salt.html.markdown b/website/source/docs/provisioners/salt.html.markdown index 239ad36b7..0ed1ea51e 100644 --- a/website/source/docs/provisioners/salt.html.markdown +++ b/website/source/docs/provisioners/salt.html.markdown @@ -2,11 +2,11 @@ layout: "docs" --- -# Salt Provisioner +# Salt Masterless Provisioner -Type: `salt` +Type: `salt-masterless` -The salt provisioner provisions machines built by Packer using [Salt](http://saltstack.com/) states. +The salt-masterless provisioner provisions machines built by Packer using [Salt](http://saltstack.com/) states. ## Basic Example @@ -14,7 +14,7 @@ The example below is fully functional.
 {
-    "type": "salt",
+    "type": "salt-masterless",
     "bootstrap_args": "git v0.16.0"
     "local_state_tree": "/Users/me/salt"
 }