Merge pull request #8319 from hashicorp/f-shell-provisioner-env-var-format

provisioners/shell: Add support for the `env_var_format` parameter
This commit is contained in:
Wilken Rivera 2019-11-06 16:58:49 -05:00 committed by GitHub
commit fdd12c4e9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 123 additions and 20 deletions

View File

@ -39,4 +39,8 @@ type Provisioner struct {
// An array of environment variables that will be injected before // An array of environment variables that will be injected before
// your command(s) are executed. // your command(s) are executed.
Vars []string `mapstructure:"environment_vars"` Vars []string `mapstructure:"environment_vars"`
// This is used in the template generation to format environment variables
// inside the `ExecuteCommand` template.
EnvVarFormat string `mapstructure:"env_var_format"`
} }

View File

@ -55,10 +55,6 @@ type Config struct {
// can be set high to allow for reboots. // can be set high to allow for reboots.
StartRetryTimeout time.Duration `mapstructure:"start_retry_timeout"` StartRetryTimeout time.Duration `mapstructure:"start_retry_timeout"`
// This is used in the template generation to format environment variables
// inside the `ExecuteCommand` template.
EnvVarFormat string
// This is used in the template generation to format environment variables // This is used in the template generation to format environment variables
// inside the `ElevatedExecuteCommand` template. // inside the `ElevatedExecuteCommand` template.
ElevatedEnvVarFormat string `mapstructure:"elevated_env_var_format"` ElevatedEnvVarFormat string `mapstructure:"elevated_env_var_format"`

View File

@ -24,10 +24,10 @@ type FlatConfig struct {
Scripts []string `cty:"scripts"` Scripts []string `cty:"scripts"`
ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes"` ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes"`
Vars []string `mapstructure:"environment_vars" cty:"environment_vars"` Vars []string `mapstructure:"environment_vars" cty:"environment_vars"`
EnvVarFormat *string `mapstructure:"env_var_format" cty:"env_var_format"`
RemoteEnvVarPath *string `mapstructure:"remote_env_var_path" cty:"remote_env_var_path"` RemoteEnvVarPath *string `mapstructure:"remote_env_var_path" cty:"remote_env_var_path"`
ElevatedExecuteCommand *string `mapstructure:"elevated_execute_command" cty:"elevated_execute_command"` ElevatedExecuteCommand *string `mapstructure:"elevated_execute_command" cty:"elevated_execute_command"`
StartRetryTimeout *string `mapstructure:"start_retry_timeout" cty:"start_retry_timeout"` StartRetryTimeout *string `mapstructure:"start_retry_timeout" cty:"start_retry_timeout"`
EnvVarFormat *string `cty:"env_var_format"`
ElevatedEnvVarFormat *string `mapstructure:"elevated_env_var_format" cty:"elevated_env_var_format"` ElevatedEnvVarFormat *string `mapstructure:"elevated_env_var_format" cty:"elevated_env_var_format"`
ElevatedUser *string `mapstructure:"elevated_user" cty:"elevated_user"` ElevatedUser *string `mapstructure:"elevated_user" cty:"elevated_user"`
ElevatedPassword *string `mapstructure:"elevated_password" cty:"elevated_password"` ElevatedPassword *string `mapstructure:"elevated_password" cty:"elevated_password"`
@ -58,10 +58,10 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"scripts": &hcldec.AttrSpec{Name: "scripts", Type: cty.List(cty.String), Required: false}, "scripts": &hcldec.AttrSpec{Name: "scripts", Type: cty.List(cty.String), Required: false},
"valid_exit_codes": &hcldec.AttrSpec{Name: "valid_exit_codes", Type: cty.List(cty.Number), Required: false}, "valid_exit_codes": &hcldec.AttrSpec{Name: "valid_exit_codes", Type: cty.List(cty.Number), Required: false},
"environment_vars": &hcldec.AttrSpec{Name: "environment_vars", Type: cty.List(cty.String), Required: false}, "environment_vars": &hcldec.AttrSpec{Name: "environment_vars", Type: cty.List(cty.String), Required: false},
"env_var_format": &hcldec.AttrSpec{Name: "env_var_format", Type: cty.String, Required: false},
"remote_env_var_path": &hcldec.AttrSpec{Name: "remote_env_var_path", Type: cty.String, Required: false}, "remote_env_var_path": &hcldec.AttrSpec{Name: "remote_env_var_path", Type: cty.String, Required: false},
"elevated_execute_command": &hcldec.AttrSpec{Name: "elevated_execute_command", Type: cty.String, Required: false}, "elevated_execute_command": &hcldec.AttrSpec{Name: "elevated_execute_command", Type: cty.String, Required: false},
"start_retry_timeout": &hcldec.AttrSpec{Name: "start_retry_timeout", Type: cty.String, Required: false}, "start_retry_timeout": &hcldec.AttrSpec{Name: "start_retry_timeout", Type: cty.String, Required: false},
"env_var_format": &hcldec.AttrSpec{Name: "env_var_format", Type: cty.String, Required: false},
"elevated_env_var_format": &hcldec.AttrSpec{Name: "elevated_env_var_format", Type: cty.String, Required: false}, "elevated_env_var_format": &hcldec.AttrSpec{Name: "elevated_env_var_format", Type: cty.String, Required: false},
"elevated_user": &hcldec.AttrSpec{Name: "elevated_user", Type: cty.String, Required: false}, "elevated_user": &hcldec.AttrSpec{Name: "elevated_user", Type: cty.String, Required: false},
"elevated_password": &hcldec.AttrSpec{Name: "elevated_password", Type: cty.String, Required: false}, "elevated_password": &hcldec.AttrSpec{Name: "elevated_password", Type: cty.String, Required: false},

View File

@ -57,9 +57,10 @@ type Config struct {
ExpectDisconnect bool `mapstructure:"expect_disconnect"` ExpectDisconnect bool `mapstructure:"expect_disconnect"`
ctx interpolate.Context
// name of the tmp environment variable file, if UseEnvVarFile is true // name of the tmp environment variable file, if UseEnvVarFile is true
envVarFile string envVarFile string
ctx interpolate.Context
} }
type Provisioner struct { type Provisioner struct {
@ -82,10 +83,19 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
}, },
}, },
}, raws...) }, raws...)
if err != nil { if err != nil {
return err return err
} }
if p.config.EnvVarFormat == "" {
p.config.EnvVarFormat = "%s='%s' "
if p.config.UseEnvVarFile == true {
p.config.EnvVarFormat = "export %s='%s'\n"
}
}
if p.config.ExecuteCommand == "" { if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}" p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}"
if p.config.UseEnvVarFile == true { if p.config.UseEnvVarFile == true {
@ -430,21 +440,22 @@ func (p *Provisioner) escapeEnvVars() ([]string, map[string]string) {
func (p *Provisioner) createEnvVarFileContent() string { func (p *Provisioner) createEnvVarFileContent() string {
keys, envVars := p.escapeEnvVars() keys, envVars := p.escapeEnvVars()
flattened := "" var flattened string
// Re-assemble vars surrounding value with single quotes and flatten
for _, key := range keys { for _, key := range keys {
flattened += fmt.Sprintf("export %s='%s'\n", key, envVars[key]) flattened += fmt.Sprintf(p.config.EnvVarFormat, key, envVars[key])
} }
return flattened return flattened
} }
func (p *Provisioner) createFlattenedEnvVars() (flattened string) { func (p *Provisioner) createFlattenedEnvVars() string {
keys, envVars := p.escapeEnvVars() keys, envVars := p.escapeEnvVars()
// Re-assemble vars surrounding value with single quotes and flatten // Re-assemble vars into specified format and flatten
var flattened string
for _, key := range keys { for _, key := range keys {
flattened += fmt.Sprintf("%s='%s' ", key, envVars[key]) flattened += fmt.Sprintf(p.config.EnvVarFormat, key, envVars[key])
} }
return
return flattened
} }

View File

@ -24,6 +24,7 @@ type FlatConfig struct {
Scripts []string `cty:"scripts"` Scripts []string `cty:"scripts"`
ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes"` ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes"`
Vars []string `mapstructure:"environment_vars" cty:"environment_vars"` Vars []string `mapstructure:"environment_vars" cty:"environment_vars"`
EnvVarFormat *string `mapstructure:"env_var_format" cty:"env_var_format"`
InlineShebang *string `mapstructure:"inline_shebang" cty:"inline_shebang"` InlineShebang *string `mapstructure:"inline_shebang" cty:"inline_shebang"`
PauseAfter *string `mapstructure:"pause_after" cty:"pause_after"` PauseAfter *string `mapstructure:"pause_after" cty:"pause_after"`
UseEnvVarFile *bool `mapstructure:"use_env_var_file" cty:"use_env_var_file"` UseEnvVarFile *bool `mapstructure:"use_env_var_file" cty:"use_env_var_file"`
@ -58,6 +59,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"scripts": &hcldec.AttrSpec{Name: "scripts", Type: cty.List(cty.String), Required: false}, "scripts": &hcldec.AttrSpec{Name: "scripts", Type: cty.List(cty.String), Required: false},
"valid_exit_codes": &hcldec.AttrSpec{Name: "valid_exit_codes", Type: cty.List(cty.Number), Required: false}, "valid_exit_codes": &hcldec.AttrSpec{Name: "valid_exit_codes", Type: cty.List(cty.Number), Required: false},
"environment_vars": &hcldec.AttrSpec{Name: "environment_vars", Type: cty.List(cty.String), Required: false}, "environment_vars": &hcldec.AttrSpec{Name: "environment_vars", Type: cty.List(cty.String), Required: false},
"env_var_format": &hcldec.AttrSpec{Name: "env_var_format", Type: cty.String, Required: false},
"inline_shebang": &hcldec.AttrSpec{Name: "inline_shebang", Type: cty.String, Required: false}, "inline_shebang": &hcldec.AttrSpec{Name: "inline_shebang", Type: cty.String, Required: false},
"pause_after": &hcldec.AttrSpec{Name: "pause_after", Type: cty.String, Required: false}, "pause_after": &hcldec.AttrSpec{Name: "pause_after", Type: cty.String, Required: false},
"use_env_var_file": &hcldec.AttrSpec{Name: "use_env_var_file", Type: cty.Bool, Required: false}, "use_env_var_file": &hcldec.AttrSpec{Name: "use_env_var_file", Type: cty.Bool, Required: false},

View File

@ -286,6 +286,45 @@ func TestProvisioner_createFlattenedEnvVars(t *testing.T) {
} }
} }
func TestProvisioner_createFlattenedEnvVars_withEnvVarFormat(t *testing.T) {
var flattenedEnvVars string
config := testConfig()
userEnvVarTests := [][]string{
{}, // No user env var
{"FOO=bar"}, // Single user env var
{"FOO=bar's"}, // User env var with single quote in value
{"FOO=bar", "BAZ=qux"}, // Multiple user env vars
{"FOO=bar=baz"}, // User env var with value containing equals
{"FOO==bar"}, // User env var with value starting with equals
}
expected := []string{
`PACKER_BUILDER_TYPE=iso PACKER_BUILD_NAME=vmware `,
`FOO=bar PACKER_BUILDER_TYPE=iso PACKER_BUILD_NAME=vmware `,
`FOO=bar'"'"'s PACKER_BUILDER_TYPE=iso PACKER_BUILD_NAME=vmware `,
`BAZ=qux FOO=bar PACKER_BUILDER_TYPE=iso PACKER_BUILD_NAME=vmware `,
`FOO=bar=baz PACKER_BUILDER_TYPE=iso PACKER_BUILD_NAME=vmware `,
`FOO==bar PACKER_BUILDER_TYPE=iso PACKER_BUILD_NAME=vmware `,
}
p := new(Provisioner)
p.config.EnvVarFormat = "%s=%s "
p.Prepare(config)
// Defaults provided by Packer
p.config.PackerBuildName = "vmware"
p.config.PackerBuilderType = "iso"
for i, expectedValue := range expected {
p.config.Vars = userEnvVarTests[i]
flattenedEnvVars = p.createFlattenedEnvVars()
if flattenedEnvVars != expectedValue {
t.Fatalf("expected flattened env vars to be: %s, got %s.", expectedValue, flattenedEnvVars)
}
}
}
func TestProvisioner_createEnvVarFileContent(t *testing.T) { func TestProvisioner_createEnvVarFileContent(t *testing.T) {
var flattenedEnvVars string var flattenedEnvVars string
config := testConfig() config := testConfig()
@ -326,6 +365,7 @@ export PACKER_BUILD_NAME='vmware'
} }
p := new(Provisioner) p := new(Provisioner)
p.config.UseEnvVarFile = true
p.Prepare(config) p.Prepare(config)
// Defaults provided by Packer // Defaults provided by Packer
@ -341,6 +381,55 @@ export PACKER_BUILD_NAME='vmware'
} }
} }
func TestProvisioner_createEnvVarFileContent_withEnvVarFormat(t *testing.T) {
var flattenedEnvVars string
config := testConfig()
userEnvVarTests := [][]string{
{}, // No user env var
{"FOO=bar", "BAZ=qux"}, // Multiple user env vars
{"FOO=bar=baz"}, // User env var with value containing equals
{"FOO==bar"}, // User env var with value starting with equals
}
expected := []string{
`PACKER_BUILDER_TYPE=iso
PACKER_BUILD_NAME=vmware
`,
`BAZ=qux
FOO=bar
PACKER_BUILDER_TYPE=iso
PACKER_BUILD_NAME=vmware
`,
`FOO=bar=baz
PACKER_BUILDER_TYPE=iso
PACKER_BUILD_NAME=vmware
`,
`FOO==bar
PACKER_BUILDER_TYPE=iso
PACKER_BUILD_NAME=vmware
`,
}
p := new(Provisioner)
p.config.UseEnvVarFile = true
//User provided env_var_format without export prefix
p.config.EnvVarFormat = "%s=%s\n"
p.Prepare(config)
// Defaults provided by Packer
p.config.PackerBuildName = "vmware"
p.config.PackerBuilderType = "iso"
for i, expectedValue := range expected {
p.config.Vars = userEnvVarTests[i]
flattenedEnvVars = p.createEnvVarFileContent()
if flattenedEnvVars != expectedValue {
t.Fatalf("expected flattened env vars to be: %q, got %q.", expectedValue, flattenedEnvVars)
}
}
}
func TestProvisioner_RemoteFolderSetSuccessfully(t *testing.T) { func TestProvisioner_RemoteFolderSetSuccessfully(t *testing.T) {
config := testConfig() config := testConfig()

View File

@ -37,10 +37,6 @@ type Config struct {
// This can be set high to allow for reboots. // This can be set high to allow for reboots.
StartRetryTimeout time.Duration `mapstructure:"start_retry_timeout"` StartRetryTimeout time.Duration `mapstructure:"start_retry_timeout"`
// This is used in the template generation to format environment variables
// inside the `ExecuteCommand` template.
EnvVarFormat string `mapstructure:"env_var_format"`
ctx interpolate.Context ctx interpolate.Context
} }

View File

@ -24,8 +24,8 @@ type FlatConfig struct {
Scripts []string `cty:"scripts"` Scripts []string `cty:"scripts"`
ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes"` ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes"`
Vars []string `mapstructure:"environment_vars" cty:"environment_vars"` Vars []string `mapstructure:"environment_vars" cty:"environment_vars"`
StartRetryTimeout *string `mapstructure:"start_retry_timeout" cty:"start_retry_timeout"`
EnvVarFormat *string `mapstructure:"env_var_format" cty:"env_var_format"` EnvVarFormat *string `mapstructure:"env_var_format" cty:"env_var_format"`
StartRetryTimeout *string `mapstructure:"start_retry_timeout" cty:"start_retry_timeout"`
} }
// FlatMapstructure returns a new FlatConfig. // FlatMapstructure returns a new FlatConfig.
@ -52,8 +52,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"scripts": &hcldec.AttrSpec{Name: "scripts", Type: cty.List(cty.String), Required: false}, "scripts": &hcldec.AttrSpec{Name: "scripts", Type: cty.List(cty.String), Required: false},
"valid_exit_codes": &hcldec.AttrSpec{Name: "valid_exit_codes", Type: cty.List(cty.Number), Required: false}, "valid_exit_codes": &hcldec.AttrSpec{Name: "valid_exit_codes", Type: cty.List(cty.Number), Required: false},
"environment_vars": &hcldec.AttrSpec{Name: "environment_vars", Type: cty.List(cty.String), Required: false}, "environment_vars": &hcldec.AttrSpec{Name: "environment_vars", Type: cty.List(cty.String), Required: false},
"start_retry_timeout": &hcldec.AttrSpec{Name: "start_retry_timeout", Type: cty.String, Required: false},
"env_var_format": &hcldec.AttrSpec{Name: "env_var_format", Type: cty.String, Required: false}, "env_var_format": &hcldec.AttrSpec{Name: "env_var_format", Type: cty.String, Required: false},
"start_retry_timeout": &hcldec.AttrSpec{Name: "start_retry_timeout", Type: cty.String, Required: false},
} }
return s return s
} }

View File

@ -40,6 +40,11 @@ The example below is fully functional.
Packer injects some environmental variables by default into the Packer injects some environmental variables by default into the
environment, as well, which are covered in the section below. environment, as well, which are covered in the section below.
- `env_var_format` (string) - When we parse the environment\_vars that you
provide, this gives us a string template to use in order to make sure that
we are setting the environment vars correctly. By default it is `"%s='%s' "`.
When used in conjunction with `use_env_var_file` the default is `"export %s='%s'\n"`
- `use_env_var_file` (boolean) - If true, Packer will write your environment - `use_env_var_file` (boolean) - If true, Packer will write your environment
variables to a tempfile and source them from that file, rather than variables to a tempfile and source them from that file, rather than
declaring them inline in our execute\_command. The default declaring them inline in our execute\_command. The default