Merge pull request #7385 from hashicorp/windows_shell_allow_exit_code
allowed_exit_codes for windows-shell and shell provisioners
This commit is contained in:
commit
8853fc1946
|
@ -0,0 +1,39 @@
|
|||
package shell
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (p *Provisioner) ValidExitCode(code int) error {
|
||||
// Check exit code against allowed codes (likely just 0)
|
||||
validCodes := p.ValidExitCodes
|
||||
if len(validCodes) == 0 {
|
||||
validCodes = []int{0}
|
||||
}
|
||||
validExitCode := false
|
||||
for _, v := range validCodes {
|
||||
if code == v {
|
||||
validExitCode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validExitCode {
|
||||
return &ErrorInvalidExitCode{
|
||||
Code: code,
|
||||
Allowed: validCodes,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ErrorInvalidExitCode struct {
|
||||
Code int
|
||||
Allowed []int
|
||||
}
|
||||
|
||||
func (e *ErrorInvalidExitCode) Error() string {
|
||||
if e == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return fmt.Sprintf("Script exited with non-zero exit status: %d."+
|
||||
"Allowed exit codes are: %v",
|
||||
e.Code, e.Allowed)
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProvisioner_ValidExitCode(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
exitCodes []int
|
||||
code int
|
||||
wantErr bool
|
||||
}{
|
||||
{nil, 0, false},
|
||||
{nil, 1, true},
|
||||
{[]int{2}, 2, false},
|
||||
{[]int{2}, 3, true},
|
||||
}
|
||||
for n := range tests {
|
||||
tt := tests[n]
|
||||
t.Run(fmt.Sprintf("%v - %v - %v", tt.exitCodes, tt.code, tt.wantErr), func(t *testing.T) {
|
||||
p := Provisioner{
|
||||
ValidExitCodes: tt.exitCodes,
|
||||
}
|
||||
if err := p.ValidExitCode(tt.code); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Provisioner.ValidExitCode() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Package shell defines code that is common in shells
|
||||
package shell
|
||||
|
||||
import "github.com/hashicorp/packer/common"
|
||||
|
||||
// Provisioner contains common fields to all shell provisioners
|
||||
type Provisioner struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
// If true, the script contains binary and line endings will not be
|
||||
// converted from Windows to Unix-style.
|
||||
Binary bool
|
||||
|
||||
// The command used to execute the script. The '{{ .Path }}' variable
|
||||
// should be used to specify where the script goes, {{ .Vars }}
|
||||
// can be used to inject the environment_vars into the environment.
|
||||
ExecuteCommand string `mapstructure:"execute_command"`
|
||||
|
||||
// An inline script to execute. Multiple strings are all executed
|
||||
// in the context of a single shell.
|
||||
Inline []string
|
||||
|
||||
// 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 local path of the shell script to upload and execute.
|
||||
Script string
|
||||
|
||||
// An array of multiple scripts to run.
|
||||
Scripts []string
|
||||
|
||||
// Valid Exit Codes - 0 is not always the only valid error code! See
|
||||
// http://www.symantec.com/connect/articles/windows-system-error-codes-exit-codes-description
|
||||
// for examples such as 3010 - "The requested operation is successful.
|
||||
ValidExitCodes []int `mapstructure:"valid_exit_codes"`
|
||||
|
||||
// An array of environment variables that will be injected before
|
||||
// your command(s) are executed.
|
||||
Vars []string `mapstructure:"environment_vars"`
|
||||
}
|
|
@ -13,6 +13,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/shell"
|
||||
"github.com/hashicorp/packer/common/uuid"
|
||||
commonhelper "github.com/hashicorp/packer/helper/common"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
|
@ -32,41 +33,13 @@ var psEscape = strings.NewReplacer(
|
|||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
// If true, the script contains binary and line endings will not be
|
||||
// converted from Windows to Unix-style.
|
||||
Binary bool
|
||||
|
||||
// An inline script to execute. Multiple strings are all executed in the
|
||||
// context of a single shell.
|
||||
Inline []string
|
||||
|
||||
// The local path of the powershell script to upload and execute.
|
||||
Script string
|
||||
|
||||
// An array of multiple scripts to run.
|
||||
Scripts []string
|
||||
|
||||
// An array of environment variables that will be injected before your
|
||||
// command(s) are executed.
|
||||
Vars []string `mapstructure:"environment_vars"`
|
||||
|
||||
// The remote path where the local powershell script will be uploaded to.
|
||||
// This should be set to a writable file that is in a pre-existing
|
||||
// directory.
|
||||
RemotePath string `mapstructure:"remote_path"`
|
||||
shell.Provisioner `mapstructure:",squash"`
|
||||
|
||||
// The remote path where the file containing the environment variables
|
||||
// will be uploaded to. This should be set to a writable file that is in a
|
||||
// pre-existing directory.
|
||||
RemoteEnvVarPath string `mapstructure:"remote_env_var_path"`
|
||||
|
||||
// The command used to execute the script. The '{{ .Path }}' variable
|
||||
// should be used to specify where the script goes, {{ .Vars }} can be
|
||||
// used to inject the environment_vars into the environment.
|
||||
ExecuteCommand string `mapstructure:"execute_command"`
|
||||
|
||||
// The command used to execute the elevated script. The '{{ .Path }}'
|
||||
// variable should be used to specify where the script goes, {{ .Vars }}
|
||||
// can be used to inject the environment_vars into the environment.
|
||||
|
@ -91,12 +64,6 @@ type Config struct {
|
|||
ElevatedUser string `mapstructure:"elevated_user"`
|
||||
ElevatedPassword string `mapstructure:"elevated_password"`
|
||||
|
||||
// Valid Exit Codes - 0 is not always the only valid error code! See
|
||||
// http://www.symantec.com/connect/articles/windows-system-error-codes-exit-codes-description
|
||||
// for examples such as 3010 - "The requested operation is successful.
|
||||
// Changes will not be effective until the system is rebooted."
|
||||
ValidExitCodes []int `mapstructure:"valid_exit_codes"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
|
@ -179,10 +146,6 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||
p.config.Vars = make([]string, 0)
|
||||
}
|
||||
|
||||
if p.config.ValidExitCodes == nil {
|
||||
p.config.ValidExitCodes = []int{0}
|
||||
}
|
||||
|
||||
var errs error
|
||||
if p.config.Script != "" && len(p.config.Scripts) > 0 {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
|
@ -307,17 +270,8 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
|||
// Close the original file since we copied it
|
||||
f.Close()
|
||||
|
||||
// Check exit code against allowed codes (likely just 0)
|
||||
validExitCode := false
|
||||
for _, v := range p.config.ValidExitCodes {
|
||||
if cmd.ExitStatus == v {
|
||||
validExitCode = true
|
||||
}
|
||||
}
|
||||
if !validExitCode {
|
||||
return fmt.Errorf(
|
||||
"Script exited with non-zero exit status: %d. Allowed exit codes are: %v",
|
||||
cmd.ExitStatus, p.config.ValidExitCodes)
|
||||
if err := p.config.ValidExitCode(cmd.ExitStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -87,18 +87,6 @@ func TestProvisionerPrepare_Defaults(t *testing.T) {
|
|||
t.Fatalf(`Default command should be 'powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"', but got '%s'`, p.config.ElevatedExecuteCommand)
|
||||
}
|
||||
|
||||
if p.config.ValidExitCodes == nil {
|
||||
t.Fatalf("ValidExitCodes should not be nil")
|
||||
}
|
||||
if p.config.ValidExitCodes != nil {
|
||||
expCodes := []int{0}
|
||||
for i, v := range p.config.ValidExitCodes {
|
||||
if v != expCodes[i] {
|
||||
t.Fatalf("Expected ValidExitCodes don't match actual")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.config.ElevatedEnvVarFormat != `$env:%s="%s"; ` {
|
||||
t.Fatalf(`Default command should be powershell '$env:%%s="%%s"; ', but got %s`, p.config.ElevatedEnvVarFormat)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/shell"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/packer/tmp"
|
||||
|
@ -22,29 +23,11 @@ import (
|
|||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
// If true, the script contains binary and line endings will not be
|
||||
// converted from Windows to Unix-style.
|
||||
Binary bool
|
||||
|
||||
// An inline script to execute. Multiple strings are all executed
|
||||
// in the context of a single shell.
|
||||
Inline []string
|
||||
shell.Provisioner `mapstructure:",squash"`
|
||||
|
||||
// The shebang value used when running inline scripts.
|
||||
InlineShebang string `mapstructure:"inline_shebang"`
|
||||
|
||||
// The local path of the shell script to upload and execute.
|
||||
Script string
|
||||
|
||||
// An array of multiple scripts to run.
|
||||
Scripts []string
|
||||
|
||||
// An array of environment variables that will be injected before
|
||||
// your command(s) are executed.
|
||||
Vars []string `mapstructure:"environment_vars"`
|
||||
|
||||
// A duration of how long to pause after the provisioner
|
||||
RawPauseAfter string `mapstructure:"pause_after"`
|
||||
|
||||
|
@ -62,16 +45,6 @@ type Config struct {
|
|||
// 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
|
||||
// should be used to specify where the script goes, {{ .Vars }}
|
||||
// can be used to inject the environment_vars into the environment.
|
||||
ExecuteCommand string `mapstructure:"execute_command"`
|
||||
|
||||
// The timeout for retrying to start the process. Until this timeout
|
||||
// is reached, if the provisioner can't start a process, it retries.
|
||||
// This can be set high to allow for reboots.
|
||||
|
@ -362,10 +335,11 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
|||
return fmt.Errorf("Script disconnected unexpectedly. " +
|
||||
"If you expected your script to disconnect, i.e. from a " +
|
||||
"restart, you can try adding `\"expect_disconnect\": true` " +
|
||||
"to the shell provisioner parameters.")
|
||||
"or `\"valid_exit_codes\": [0, 2300218]` to the shell " +
|
||||
"provisioner parameters.")
|
||||
}
|
||||
} else if cmd.ExitStatus != 0 {
|
||||
return fmt.Errorf("Script exited with non-zero exit status: %d", cmd.ExitStatus)
|
||||
} else if err := p.config.ValidExitCode(cmd.ExitStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !p.config.SkipClean {
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/shell"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/packer/tmp"
|
||||
|
@ -25,29 +26,7 @@ const DefaultRemotePath = "c:/Windows/Temp/script.bat"
|
|||
var retryableSleep = 2 * time.Second
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
// If true, the script contains binary and line endings will not be
|
||||
// converted from Windows to Unix-style.
|
||||
Binary bool
|
||||
|
||||
// An inline script to execute. Multiple strings are all executed
|
||||
// in the context of a single shell.
|
||||
Inline []string
|
||||
|
||||
// The local path of the shell script to upload and execute.
|
||||
Script string
|
||||
|
||||
// An array of multiple scripts to run.
|
||||
Scripts []string
|
||||
|
||||
// An array of environment variables that will be injected before
|
||||
// your command(s) are executed.
|
||||
Vars []string `mapstructure:"environment_vars"`
|
||||
|
||||
// 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.
|
||||
RemotePath string `mapstructure:"remote_path"`
|
||||
shell.Provisioner `mapstructure:",squash"`
|
||||
|
||||
// The command used to execute the script. The '{{ .Path }}' variable
|
||||
// should be used to specify where the script goes, {{ .Vars }}
|
||||
|
@ -243,8 +222,8 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
|||
// Close the original file since we copied it
|
||||
f.Close()
|
||||
|
||||
if cmd.ExitStatus != 0 {
|
||||
return fmt.Errorf("Script exited with non-zero exit status: %d", cmd.ExitStatus)
|
||||
if err := p.config.ValidExitCode(cmd.ExitStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,33 +31,7 @@ The example below is fully functional.
|
|||
|
||||
## Configuration Reference
|
||||
|
||||
The reference of available configuration options is listed below. The only
|
||||
required element is either "inline" or "script". Every other option is
|
||||
optional.
|
||||
|
||||
Exactly *one* of the following is required:
|
||||
|
||||
- `inline` (array of strings) - This is an array of commands to execute. The
|
||||
commands are concatenated by newlines and turned into a single file, so
|
||||
they are all executed within the same context. This allows you to change
|
||||
directories in one command and use something in the directory in the next
|
||||
and so on. Inline scripts are the easiest way to pull off simple tasks
|
||||
within the machine.
|
||||
|
||||
- `script` (string) - The path to a script to upload and execute in the
|
||||
machine. This path can be absolute or relative. If it is relative, it is
|
||||
relative to the working directory when Packer is executed.
|
||||
|
||||
- `scripts` (array of strings) - An array of scripts to execute. The scripts
|
||||
will be uploaded and executed in the order specified. Each script is
|
||||
executed in isolation, so state such as variables from one script won't
|
||||
carry on to the next.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- `binary` (boolean) - If true, specifies that the script(s) are binary
|
||||
files, and Packer should therefore not convert Windows line endings to Unix
|
||||
line endings (if there are any). By default this is false.
|
||||
<%= partial "partials/provisioners/shell-config" %>
|
||||
|
||||
- `elevated_execute_command` (string) - The command to use to execute the
|
||||
elevated script. By default this is as follows:
|
||||
|
@ -155,9 +129,6 @@ Optional parameters:
|
|||
exists in order to deal with times when SSH may restart, such as a system
|
||||
reboot. Set this to a higher value if reboots take a longer amount of time.
|
||||
|
||||
- `valid_exit_codes` (list of ints) - Valid exit codes for the script. By
|
||||
default this is just 0.
|
||||
|
||||
## Default Environmental Variables
|
||||
|
||||
In addition to being able to specify custom environmental variables using the
|
|
@ -33,33 +33,7 @@ The example below is fully functional.
|
|||
|
||||
## Configuration Reference
|
||||
|
||||
The reference of available configuration options is listed below. The only
|
||||
required element is either "inline" or "script". Every other option is
|
||||
optional.
|
||||
|
||||
Exactly *one* of the following is required:
|
||||
|
||||
- `inline` (array of strings) - This is an array of commands to execute. The
|
||||
commands are concatenated by newlines and turned into a single file, so
|
||||
they are all executed within the same context. This allows you to change
|
||||
directories in one command and use something in the directory in the next
|
||||
and so on. Inline scripts are the easiest way to pull off simple tasks
|
||||
within the machine.
|
||||
|
||||
- `script` (string) - The path to a script to upload and execute in the
|
||||
machine. This path can be absolute or relative. If it is relative, it is
|
||||
relative to the working directory when Packer is executed.
|
||||
|
||||
- `scripts` (array of strings) - An array of scripts to execute. The scripts
|
||||
will be uploaded and executed in the order specified. Each script is
|
||||
executed in isolation, so state such as variables from one script won't
|
||||
carry on to the next.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- `binary` (boolean) - If true, specifies that the script(s) are binary
|
||||
files, and Packer should therefore not convert Windows line endings to Unix
|
||||
line endings (if there are any). By default this is false.
|
||||
<%= partial "partials/provisioners/shell-config" %>
|
||||
|
||||
- `environment_vars` (array of strings) - An array of key/value pairs to
|
||||
inject prior to the execute\_command. The format should be `key=value`.
|
|
@ -27,33 +27,7 @@ The example below is fully functional.
|
|||
|
||||
## Configuration Reference
|
||||
|
||||
The reference of available configuration options is listed below. The only
|
||||
required element is either "inline" or "script". Every other option is
|
||||
optional.
|
||||
|
||||
Exactly *one* of the following is required:
|
||||
|
||||
- `inline` (array of strings) - This is an array of commands to execute. The
|
||||
commands are concatenated by newlines and turned into a single file, so
|
||||
they are all executed within the same context. This allows you to change
|
||||
directories in one command and use something in the directory in the next
|
||||
and so on. Inline scripts are the easiest way to pull off simple tasks
|
||||
within the machine.
|
||||
|
||||
- `script` (string) - The path to a script to upload and execute in the
|
||||
machine. This path can be absolute or relative. If it is relative, it is
|
||||
relative to the working directory when Packer is executed.
|
||||
|
||||
- `scripts` (array of strings) - An array of scripts to execute. The scripts
|
||||
will be uploaded and executed in the order specified. Each script is
|
||||
executed in isolation, so state such as variables from one script won't
|
||||
carry on to the next.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- `binary` (boolean) - If true, specifies that the script(s) are binary
|
||||
files, and Packer should therefore not convert Windows line endings to Unix
|
||||
line endings (if there are any). By default this is false.
|
||||
<%= partial "partials/provisioners/shell-config" %>
|
||||
|
||||
- `environment_vars` (array of strings) - An array of key/value pairs to
|
||||
inject prior to the execute\_command. The format should be `key=value`.
|
|
@ -0,0 +1,30 @@
|
|||
The reference of available configuration options is listed below. The only
|
||||
required element is either "inline" or "script". Every other option is
|
||||
optional.
|
||||
|
||||
Exactly *one* of the following is required:
|
||||
|
||||
- `inline` (array of strings) - This is an array of commands to execute. The
|
||||
commands are concatenated by newlines and turned into a single file, so
|
||||
they are all executed within the same context. This allows you to change
|
||||
directories in one command and use something in the directory in the next
|
||||
and so on. Inline scripts are the easiest way to pull off simple tasks
|
||||
within the machine.
|
||||
|
||||
- `script` (string) - The path to a script to upload and execute in the
|
||||
machine. This path can be absolute or relative. If it is relative, it is
|
||||
relative to the working directory when Packer is executed.
|
||||
|
||||
- `scripts` (array of strings) - An array of scripts to execute. The scripts
|
||||
will be uploaded and executed in the order specified. Each script is
|
||||
executed in isolation, so state such as variables from one script won't
|
||||
carry on to the next.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- `binary` (boolean) - If true, specifies that the script(s) are binary
|
||||
files, and Packer should therefore not convert Windows line endings to Unix
|
||||
line endings (if there are any). By default this is false.
|
||||
|
||||
- `valid_exit_codes` (list of ints) - Valid exit codes for the script. By
|
||||
default this is just 0.
|
Loading…
Reference in New Issue