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
// your command(s) are executed.
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.
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
// inside the `ElevatedExecuteCommand` template.
ElevatedEnvVarFormat string `mapstructure:"elevated_env_var_format"`

View File

@ -24,10 +24,10 @@ type FlatConfig struct {
Scripts []string `cty:"scripts"`
ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes"`
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"`
ElevatedExecuteCommand *string `mapstructure:"elevated_execute_command" cty:"elevated_execute_command"`
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"`
ElevatedUser *string `mapstructure:"elevated_user" cty:"elevated_user"`
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},
"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},
"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},
"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},
"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_user": &hcldec.AttrSpec{Name: "elevated_user", 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"`
ctx interpolate.Context
// name of the tmp environment variable file, if UseEnvVarFile is true
envVarFile string
ctx interpolate.Context
}
type Provisioner struct {
@ -82,10 +83,19 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
},
},
}, raws...)
if err != nil {
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 == "" {
p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}"
if p.config.UseEnvVarFile == true {
@ -430,21 +440,22 @@ func (p *Provisioner) escapeEnvVars() ([]string, map[string]string) {
func (p *Provisioner) createEnvVarFileContent() string {
keys, envVars := p.escapeEnvVars()
flattened := ""
// Re-assemble vars surrounding value with single quotes and flatten
var flattened string
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
}
func (p *Provisioner) createFlattenedEnvVars() (flattened string) {
func (p *Provisioner) createFlattenedEnvVars() string {
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 {
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"`
ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes"`
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"`
PauseAfter *string `mapstructure:"pause_after" cty:"pause_after"`
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},
"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},
"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},
"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},

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) {
var flattenedEnvVars string
config := testConfig()
@ -326,6 +365,7 @@ export PACKER_BUILD_NAME='vmware'
}
p := new(Provisioner)
p.config.UseEnvVarFile = true
p.Prepare(config)
// 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) {
config := testConfig()

View File

@ -37,10 +37,6 @@ type Config struct {
// This can be set high to allow for reboots.
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
}

View File

@ -24,8 +24,8 @@ type FlatConfig struct {
Scripts []string `cty:"scripts"`
ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes"`
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"`
StartRetryTimeout *string `mapstructure:"start_retry_timeout" cty:"start_retry_timeout"`
}
// 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},
"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},
"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},
"start_retry_timeout": &hcldec.AttrSpec{Name: "start_retry_timeout", Type: cty.String, Required: false},
}
return s
}

View File

@ -40,6 +40,11 @@ The example below is fully functional.
Packer injects some environmental variables by default into the
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
variables to a tempfile and source them from that file, rather than
declaring them inline in our execute\_command. The default