diff --git a/provisioner/shell/provisioner.go b/provisioner/shell/provisioner.go index 78826b638..82224922d 100644 --- a/provisioner/shell/provisioner.go +++ b/provisioner/shell/provisioner.go @@ -44,8 +44,17 @@ type Config struct { // your command(s) are executed. Vars []string `mapstructure:"environment_vars"` + // The remote folder where the local shell script will be uploaded to. + // This should be set to a pre-existing directory, it defaults to /tmp + RemoteFolder string `mapstructure:"remote_folder"` + + // The remote file name of the local shell script. + // This defaults to script_nnn.sh + RemoteFile string `mapstructure:"remote_file"` + // The remote path where the local shell script will be uploaded to. // This should be set to a writable file that is in a pre-existing directory. + // This defaults to remote_folder/remote_file RemotePath string `mapstructure:"remote_path"` // The command used to execute the script. The '{{ .Path }}' variable @@ -104,9 +113,17 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.config.RawStartRetryTimeout = "5m" } + if p.config.RemoteFolder == "" { + p.config.RemoteFolder = "/tmp" + } + + if p.config.RemoteFile == "" { + p.config.RemoteFile = fmt.Sprintf("script_%d.sh", rand.Intn(9999)) + } + if p.config.RemotePath == "" { p.config.RemotePath = fmt.Sprintf( - "/tmp/script_%d.sh", rand.Intn(9999)) + "%s/%s", p.config.RemoteFolder, p.config.RemoteFile) } if p.config.Scripts == nil { diff --git a/provisioner/shell/provisioner_test.go b/provisioner/shell/provisioner_test.go index 165490e2a..6f6b5424a 100644 --- a/provisioner/shell/provisioner_test.go +++ b/provisioner/shell/provisioner_test.go @@ -5,6 +5,8 @@ import ( "io/ioutil" "os" "testing" + "strings" + "regexp" ) func testConfig() map[string]interface{} { @@ -217,3 +219,131 @@ func TestProvisionerQuote_EnvironmentVars(t *testing.T) { t.Fatalf("%s should be equal to %s", p.config.Vars[1], expectedValue) } } + +func TestProvisioner_RemoteFolderSetSuccessfully(t *testing.T) { + config := testConfig() + + expectedRemoteFolder := "/example/path" + config["remote_folder"] = expectedRemoteFolder + + p := new(Provisioner) + err := p.Prepare(config) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if !strings.Contains(p.config.RemotePath, expectedRemoteFolder) { + t.Fatalf("remote path does not contain remote_folder") + } +} + +func TestProvisioner_RemoteFolderDefaultsToTmp(t *testing.T) { + config := testConfig() + + p := new(Provisioner) + err := p.Prepare(config) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if p.config.RemoteFolder != "/tmp" { + t.Fatalf("remote_folder did not default to /tmp") + } + + if !strings.Contains(p.config.RemotePath, "/tmp") { + t.Fatalf("remote path does not contain remote_folder") + } +} + +func TestProvisioner_RemoteFileSetSuccessfully(t *testing.T) { + config := testConfig() + + expectedRemoteFile := "example.sh" + config["remote_file"] = expectedRemoteFile + + p := new(Provisioner) + err := p.Prepare(config) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if !strings.Contains(p.config.RemotePath, expectedRemoteFile) { + t.Fatalf("remote path does not contain remote_file") + } +} + +func TestProvisioner_RemoteFileDefaultsToScriptnnnn(t *testing.T) { + config := testConfig() + + p := new(Provisioner) + err := p.Prepare(config) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + remoteFileRegex := regexp.MustCompile("script_[0-9]{4}.sh") + + if !remoteFileRegex.MatchString(p.config.RemoteFile) { + t.Fatalf("remote_file did not default to script_nnnn.sh") + } + + if !remoteFileRegex.MatchString(p.config.RemotePath) { + t.Fatalf("remote_path did not match script_nnnn.sh") + } +} + +func TestProvisioner_RemotePathSetViaRemotePathAndRemoteFile(t *testing.T) { + config := testConfig() + + expectedRemoteFile := "example.sh" + expectedRemoteFolder := "/example/path" + config["remote_file"] = expectedRemoteFile + config["remote_folder"] = expectedRemoteFolder + + p := new(Provisioner) + err := p.Prepare(config) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if p.config.RemotePath != expectedRemoteFolder + "/" + expectedRemoteFile { + t.Fatalf("remote path does not contain remote_file") + } +} + +func TestProvisioner_RemotePathOverridesRemotePathAndRemoteFile(t *testing.T) { + config := testConfig() + + expectedRemoteFile := "example.sh" + expectedRemoteFolder := "/example/path" + expectedRemotePath := "/example/remote/path/script.sh" + config["remote_file"] = expectedRemoteFile + config["remote_folder"] = expectedRemoteFolder + config["remote_path"] = expectedRemotePath + + p := new(Provisioner) + err := p.Prepare(config) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if p.config.RemotePath != expectedRemotePath { + t.Fatalf("remote path does not contain remote_path") + } +} + +func TestProvisionerRemotePathDefaultsSuccessfully(t *testing.T) { + config := testConfig() + + p := new(Provisioner) + err := p.Prepare(config) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + remotePathRegex := regexp.MustCompile("/tmp/script_[0-9]{4}.sh") + + if !remotePathRegex.MatchString(p.config.RemotePath) { + t.Fatalf("remote path does not match the expected default regex") + } +} diff --git a/website/source/docs/provisioners/shell.html.md b/website/source/docs/provisioners/shell.html.md index 78e85c3cf..4df9321d6 100644 --- a/website/source/docs/provisioners/shell.html.md +++ b/website/source/docs/provisioners/shell.html.md @@ -78,10 +78,15 @@ Optional parameters: **Important:** If you customize this, be sure to include something like the `-e` flag, otherwise individual steps failing won't fail the provisioner. -- `remote_path` (string) - The filename where the script will be uploaded - to in the machine. This defaults to `/tmp/script_nnn.sh` where "nnn" is - a randomly generated number. This value must be a writable location and - any parent directories must already exist. +- `remote_folder` (string) - The folder where the uploaded script will reside on + the machine. This defaults to '/tmp'. + +- `remote_file` (string) - The filename the uploaded script will have on the machine. + This defaults to 'script_nnn.sh'. + +- `remote_path` (string) - The full path to the uploaded script will have on the + machine. By default this is remote_folder/remote_file, if set this option will + override both remote_folder and remote_file. - `skip_clean` (boolean) - If true, specifies that the helper scripts uploaded to the system will not be removed by Packer. This defaults to