From 25c0b98363a8bbad9b14b1374af36bcf36477d6d Mon Sep 17 00:00:00 2001 From: Joel Vasallo Date: Thu, 18 Oct 2018 15:10:04 -0500 Subject: [PATCH 1/5] feat: ability to specify runtime OSs where shell-local should run --- common/shell-local/config.go | 3 + common/shell-local/run.go | 64 ++++++++++++------- .../docs/provisioners/shell-local.html.md | 8 +++ 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/common/shell-local/config.go b/common/shell-local/config.go index 56f9e05cd..bd895b547 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -29,6 +29,9 @@ type Config struct { // The shebang value used when running inline scripts. InlineShebang string `mapstructure:"inline_shebang"` + // An array of multiple Runtime OSs to run on. + OnlyOn []string + // The file extension to use for the file generated from the inline commands TempfileExtension string `mapstructure:"tempfile_extension"` diff --git a/common/shell-local/run.go b/common/shell-local/run.go index c8f6160fc..505823833 100644 --- a/common/shell-local/run.go +++ b/common/shell-local/run.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "log" "os" + "runtime" "sort" "strings" @@ -58,31 +59,50 @@ func Run(ui packer.Ui, config *Config) (bool, error) { if err != nil { return false, err } - ui.Say(fmt.Sprintf("Running local shell script: %s", script)) - comm := &Communicator{ - ExecuteCommand: interpolatedCmds, + // Check if command can execute against runtime. + runCommand := false + if len(config.OnlyOn) > 0 { + for _, os := range config.OnlyOn { + if os == runtime.GOOS { + runCommand = true + } + } + } else { + runCommand = true } - // The remoteCmd generated here isn't actually run, but it allows us to - // use the same interafce for the shell-local communicator as we use for - // the other communicators; ultimately, this command is just used for - // buffers and for reading the final exit status. - flattenedCmd := strings.Join(interpolatedCmds, " ") - cmd := &packer.RemoteCmd{Command: flattenedCmd} - log.Printf("[INFO] (shell-local): starting local command: %s", flattenedCmd) - if err := cmd.StartWithUi(comm, ui); err != nil { - return false, fmt.Errorf( - "Error executing script: %s\n\n"+ - "Please see output above for more information.", - script) - } - if cmd.ExitStatus != 0 { - return false, fmt.Errorf( - "Erroneous exit code %d while executing script: %s\n\n"+ - "Please see output above for more information.", - cmd.ExitStatus, - script) + if runCommand { + ui.Say(fmt.Sprintf("Running local shell script: %s", script)) + + comm := &Communicator{ + ExecuteCommand: interpolatedCmds, + } + + // The remoteCmd generated here isn't actually run, but it allows us to + // use the same interafce for the shell-local communicator as we use for + // the other communicators; ultimately, this command is just used for + // buffers and for reading the final exit status. + flattenedCmd := strings.Join(interpolatedCmds, " ") + + cmd := &packer.RemoteCmd{Command: flattenedCmd} + log.Printf("[INFO] (shell-local): starting local command: %s", flattenedCmd) + if err := cmd.StartWithUi(comm, ui); err != nil { + return false, fmt.Errorf( + "Error executing script: %s\n\n"+ + "Please see output above for more information.", + script) + } + if cmd.ExitStatus != 0 { + return false, fmt.Errorf( + "Erroneous exit code %d while executing script: %s\n\n"+ + "Please see output above for more information.", + cmd.ExitStatus, + script) + } + } else { + ui.Say(fmt.Sprintf("Skipping local shell script due to runtime OS: %s", script)) + log.Printf("[INFO] (shell-local): skipping command due to runtime OS not specified.") } } diff --git a/website/source/docs/provisioners/shell-local.html.md b/website/source/docs/provisioners/shell-local.html.md index 5932ed69d..99724cb86 100644 --- a/website/source/docs/provisioners/shell-local.html.md +++ b/website/source/docs/provisioners/shell-local.html.md @@ -108,6 +108,14 @@ 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. +- `onlyon` (array of strings) - This is an array of + [runtime operating systems](https://golang.org/doc/install/source#environment) + where `shell-local` will only run on. This allows you to run `shell-local` *only* + on specific compatible operating systems. If specified, shell-local will only + execute if runtime operating system is in the list; otherwise it will skip the + `shell-local` command. The default behavior is for `shell-local` to run if + `onlyon` is not specified. + - `use_linux_pathing` (bool) - This is only relevant to windows hosts. If you are running Packer in a Windows environment with the Windows Subsystem for Linux feature enabled, and would like to invoke a bash script rather than From 0912adea340e43680637b0aaae7402ccaf0e4e0f Mon Sep 17 00:00:00 2001 From: Joel Vasallo Date: Fri, 19 Oct 2018 00:46:50 -0500 Subject: [PATCH 2/5] fix: converted onlyon param to only_on and added validation step --- common/shell-local/config.go | 21 ++++++++++++++++++- .../docs/post-processors/shell-local.html.md | 6 ++++++ .../docs/provisioners/shell-local.html.md | 10 ++++----- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/common/shell-local/config.go b/common/shell-local/config.go index bd895b547..0d6fcb7fd 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -30,7 +30,7 @@ type Config struct { InlineShebang string `mapstructure:"inline_shebang"` // An array of multiple Runtime OSs to run on. - OnlyOn []string + OnlyOn []string `mapstructure:"only_on"` // The file extension to use for the file generated from the inline commands TempfileExtension string `mapstructure:"tempfile_extension"` @@ -162,6 +162,25 @@ func Validate(config *Config) error { fmt.Errorf("Bad script '%s': %s", path, err)) } } + + // Check for properly formatted go os types + supported_syslist := []string{"darwin", "freebsd", "linux", "openbsd", "solaris", "windows"} + supported_os := false + if len(config.OnlyOn) > 0 { + for _, provided_os := range config.OnlyOn { + for _, go_os := range supported_syslist { + if provided_os == go_os { + supported_os = true + break + } + if supported_os != true { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Invalid OS specified in only_on: '%s'", provided_os)) + } + } + } + } + if config.UseLinuxPathing { for index, script := range config.Scripts { scriptAbsPath, err := filepath.Abs(script) diff --git a/website/source/docs/post-processors/shell-local.html.md b/website/source/docs/post-processors/shell-local.html.md index 1c412180f..c21c7e908 100644 --- a/website/source/docs/post-processors/shell-local.html.md +++ b/website/source/docs/post-processors/shell-local.html.md @@ -91,6 +91,12 @@ 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. +- `only_on` (array of strings) - This is an array of + [runtime operating systems](https://golang.org/doc/install/source#environment) + where `shell-local` will execute. This allows you to execute `shell-local` + *only* on specific operating systems. By default, shell-local will always run + if `only_on` is not set." + - `use_linux_pathing` (bool) - This is only relevant to windows hosts. If you are running Packer in a Windows environment with the Windows Subsystem for Linux feature enabled, and would like to invoke a bash script rather than diff --git a/website/source/docs/provisioners/shell-local.html.md b/website/source/docs/provisioners/shell-local.html.md index 99724cb86..881d1759a 100644 --- a/website/source/docs/provisioners/shell-local.html.md +++ b/website/source/docs/provisioners/shell-local.html.md @@ -108,13 +108,11 @@ 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. -- `onlyon` (array of strings) - This is an array of +- `only_on` (array of strings) - This is an array of [runtime operating systems](https://golang.org/doc/install/source#environment) - where `shell-local` will only run on. This allows you to run `shell-local` *only* - on specific compatible operating systems. If specified, shell-local will only - execute if runtime operating system is in the list; otherwise it will skip the - `shell-local` command. The default behavior is for `shell-local` to run if - `onlyon` is not specified. + where `shell-local` will execute. This allows you to execute `shell-local` + *only* on specific operating systems. By default, shell-local will always run + if `only_on` is not set." - `use_linux_pathing` (bool) - This is only relevant to windows hosts. If you are running Packer in a Windows environment with the Windows Subsystem for From d408c9e69c6721d69245a88d62576019020c63ac Mon Sep 17 00:00:00 2001 From: Joel Vasallo Date: Fri, 19 Oct 2018 00:52:20 -0500 Subject: [PATCH 3/5] fix: moved os check to entry of func --- common/shell-local/config.go | 9 ++-- common/shell-local/run.go | 81 ++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 46 deletions(-) diff --git a/common/shell-local/config.go b/common/shell-local/config.go index 0d6fcb7fd..e3247f73a 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -165,18 +165,17 @@ func Validate(config *Config) error { // Check for properly formatted go os types supported_syslist := []string{"darwin", "freebsd", "linux", "openbsd", "solaris", "windows"} - supported_os := false if len(config.OnlyOn) > 0 { for _, provided_os := range config.OnlyOn { + supported_os := false for _, go_os := range supported_syslist { if provided_os == go_os { supported_os = true break } - if supported_os != true { - errs = packer.MultiErrorAppend(errs, - fmt.Errorf("Invalid OS specified in only_on: '%s'", provided_os)) - } + } + if supported_os != true { + return fmt.Errorf("Invalid OS specified in only_on: '%s'", provided_os) } } } diff --git a/common/shell-local/run.go b/common/shell-local/run.go index 505823833..11165a36c 100644 --- a/common/shell-local/run.go +++ b/common/shell-local/run.go @@ -28,6 +28,24 @@ type EnvVarsTemplate struct { } func Run(ui packer.Ui, config *Config) (bool, error) { + // Check if shell-local can even execute against this runtime OS + runCommand := false + if len(config.OnlyOn) > 0 { + for _, os := range config.OnlyOn { + if os == runtime.GOOS { + runCommand = true + break + } + } + } else { + runCommand = true + } + if !runCommand { + ui.Say(fmt.Sprintf("Skipping shell-local due to runtime OS")) + log.Printf("[INFO] (shell-local): skipping shell-local due to missing runtime OS") + return true, nil + } + scripts := make([]string, len(config.Scripts)) if len(config.Scripts) > 0 { copy(scripts, config.Scripts) @@ -59,50 +77,31 @@ func Run(ui packer.Ui, config *Config) (bool, error) { if err != nil { return false, err } + ui.Say(fmt.Sprintf("Running local shell script: %s", script)) - // Check if command can execute against runtime. - runCommand := false - if len(config.OnlyOn) > 0 { - for _, os := range config.OnlyOn { - if os == runtime.GOOS { - runCommand = true - } - } - } else { - runCommand = true + comm := &Communicator{ + ExecuteCommand: interpolatedCmds, } - if runCommand { - ui.Say(fmt.Sprintf("Running local shell script: %s", script)) - - comm := &Communicator{ - ExecuteCommand: interpolatedCmds, - } - - // The remoteCmd generated here isn't actually run, but it allows us to - // use the same interafce for the shell-local communicator as we use for - // the other communicators; ultimately, this command is just used for - // buffers and for reading the final exit status. - flattenedCmd := strings.Join(interpolatedCmds, " ") - - cmd := &packer.RemoteCmd{Command: flattenedCmd} - log.Printf("[INFO] (shell-local): starting local command: %s", flattenedCmd) - if err := cmd.StartWithUi(comm, ui); err != nil { - return false, fmt.Errorf( - "Error executing script: %s\n\n"+ - "Please see output above for more information.", - script) - } - if cmd.ExitStatus != 0 { - return false, fmt.Errorf( - "Erroneous exit code %d while executing script: %s\n\n"+ - "Please see output above for more information.", - cmd.ExitStatus, - script) - } - } else { - ui.Say(fmt.Sprintf("Skipping local shell script due to runtime OS: %s", script)) - log.Printf("[INFO] (shell-local): skipping command due to runtime OS not specified.") + // The remoteCmd generated here isn't actually run, but it allows us to + // use the same interafce for the shell-local communicator as we use for + // the other communicators; ultimately, this command is just used for + // buffers and for reading the final exit status. + flattenedCmd := strings.Join(interpolatedCmds, " ") + cmd := &packer.RemoteCmd{Command: flattenedCmd} + log.Printf("[INFO] (shell-local): starting local command: %s", flattenedCmd) + if err := cmd.StartWithUi(comm, ui); err != nil { + return false, fmt.Errorf( + "Error executing script: %s\n\n"+ + "Please see output above for more information.", + script) + } + if cmd.ExitStatus != 0 { + return false, fmt.Errorf( + "Erroneous exit code %d while executing script: %s\n\n"+ + "Please see output above for more information.", + cmd.ExitStatus, + script) } } From 23ad90f2c4dd4d810542a28ff030b1748a7e3a80 Mon Sep 17 00:00:00 2001 From: Joel Vasallo Date: Fri, 19 Oct 2018 13:48:30 -0500 Subject: [PATCH 4/5] fix: condensed and simplified os check logic and added validate output --- common/shell-local/config.go | 3 ++- common/shell-local/run.go | 14 ++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/common/shell-local/config.go b/common/shell-local/config.go index e3247f73a..b4fae91a4 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -175,7 +175,8 @@ func Validate(config *Config) error { } } if supported_os != true { - return fmt.Errorf("Invalid OS specified in only_on: '%s'", provided_os) + return fmt.Errorf("Invalid OS specified in only_on: '%s'\n"+ + "Supported OS names: %v", provided_os, supported_syslist) } } } diff --git a/common/shell-local/run.go b/common/shell-local/run.go index 11165a36c..0b87d3e71 100644 --- a/common/shell-local/run.go +++ b/common/shell-local/run.go @@ -29,21 +29,19 @@ type EnvVarsTemplate struct { func Run(ui packer.Ui, config *Config) (bool, error) { // Check if shell-local can even execute against this runtime OS - runCommand := false if len(config.OnlyOn) > 0 { + runCommand := false for _, os := range config.OnlyOn { if os == runtime.GOOS { runCommand = true break } } - } else { - runCommand = true - } - if !runCommand { - ui.Say(fmt.Sprintf("Skipping shell-local due to runtime OS")) - log.Printf("[INFO] (shell-local): skipping shell-local due to missing runtime OS") - return true, nil + if !runCommand { + ui.Say(fmt.Sprintf("Skipping shell-local due to runtime OS")) + log.Printf("[INFO] (shell-local): skipping shell-local due to missing runtime OS") + return true, nil + } } scripts := make([]string, len(config.Scripts)) From 5d17b3de84dda3aae4e9a0b0e33993a860967126 Mon Sep 17 00:00:00 2001 From: Joel Vasallo Date: Fri, 19 Oct 2018 19:52:11 -0500 Subject: [PATCH 5/5] fix: config validator output --- common/shell-local/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/shell-local/config.go b/common/shell-local/config.go index b4fae91a4..410f6bb43 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -176,7 +176,7 @@ func Validate(config *Config) error { } if supported_os != true { return fmt.Errorf("Invalid OS specified in only_on: '%s'\n"+ - "Supported OS names: %v", provided_os, supported_syslist) + "Supported OS names: %s", provided_os, strings.Join(supported_syslist, ", ")) } } }