Commit e38c0424 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer: allow environmental variables within user vars [GH-633]

parent 8aabe01b
......@@ -26,6 +26,9 @@ FEATURES:
an existing OVF or OVA. [GH-201]
* **New builder:** "vmware-vmx" can build VMware images from an existing
VMX. [GH-201]
* Environmental variables can now be accessed as default values for
user variables using the "env" function. See the documentation for more
information.
* "description" field in templates: write a human-readable description
of what a template does. This will be shown in `packer inspect`.
* Vagrant post-processor now accepts a list of files to include in the
......
......@@ -37,6 +37,7 @@ func NewConfigTemplate() (*ConfigTemplate, error) {
result.root = template.New("configTemplateRoot")
result.root.Funcs(template.FuncMap{
"env": templateDisableEnv,
"pwd": templatePwd,
"isotime": templateISOTime,
"timestamp": templateTimestamp,
......@@ -95,6 +96,20 @@ func (t *ConfigTemplate) templateUser(n string) (string, error) {
return result, nil
}
func templateDisableEnv(n string) (string, error) {
return "", fmt.Errorf(
"Environmental variables can only be used as default values for user variables.")
}
func templateDisableUser(n string) (string, error) {
return "", fmt.Errorf(
"User variable can't be used within a default value for a user variable: %s", n)
}
func templateEnv(n string) string {
return os.Getenv(n)
}
func templateISOTime() string {
return time.Now().UTC().Format(time.RFC3339)
}
......
......@@ -8,6 +8,18 @@ import (
"time"
)
func TestConfigTemplateProcess_env(t *testing.T) {
tpl, err := NewConfigTemplate()
if err != nil {
t.Fatalf("err: %s", err)
}
_, err = tpl.Process(`{{env "foo"}}`, nil)
if err == nil {
t.Fatal("should error")
}
}
func TestConfigTemplateProcess_isotime(t *testing.T) {
tpl, err := NewConfigTemplate()
if err != nil {
......
......@@ -9,6 +9,7 @@ import (
"io/ioutil"
"os"
"sort"
"text/template"
"time"
)
......@@ -450,6 +451,18 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
return
}
// Prepare the variable template processor, which is a bit unique
// because we don't allow user variable usage and we add a function
// to read from the environment.
varTpl, err := NewConfigTemplate()
if err != nil {
return nil, err
}
varTpl.Funcs(template.FuncMap{
"env": templateEnv,
"user": templateDisableUser,
})
// Prepare the variables
var varErrors []error
variables := make(map[string]string)
......@@ -459,9 +472,15 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
fmt.Errorf("Required user variable '%s' not set", k))
}
var val string = v.Default
var val string
if v.HasValue {
val = v.Value
} else {
val, err = varTpl.Process(v.Default, nil)
if err != nil {
varErrors = append(varErrors,
fmt.Errorf("Error processing user variable '%s': %s'", k, err))
}
}
variables[k] = val
......
......@@ -688,6 +688,47 @@ func TestTemplate_BuildUnknownBuilder(t *testing.T) {
}
}
func TestTemplateBuild_envInVars(t *testing.T) {
data := `
{
"variables": {
"foo": "{{env \"foo\"}}"
},
"builders": [
{
"name": "test1",
"type": "test-builder"
}
]
}
`
defer os.Setenv("foo", os.Getenv("foo"))
if err := os.Setenv("foo", "bar"); err != nil {
t.Fatalf("err: %s", err)
}
template, err := ParseTemplate([]byte(data), map[string]string{})
if err != nil {
t.Fatalf("err: %s", err)
}
b, err := template.Build("test1", testComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
coreBuild, ok := b.(*coreBuild)
if !ok {
t.Fatal("should be ok")
}
if coreBuild.variables["foo"] != "bar" {
t.Fatalf("bad: %#v", coreBuild.variables)
}
}
func TestTemplateBuild_names(t *testing.T) {
data := `
{
......
......@@ -58,6 +58,36 @@ This function can be used in _any value_ within the template, in
builders, provisioners, _anything_. The user variable is available globally
within the template.
## Environmental Variables
Environmental variables can be used within your template using user
variables. The `env` function is available _only_ within the default value
of a user variable, allowing you to default a user variable to an
environmental variable. An example is shown below:
<pre class="prettyprint">
{
"variables": {
"my_secret": "{{env `MY_SECRET`}}",
},
...
}
</pre>
This will default "my\_secret" to be the value of the "MY\_SECRET"
environmental variable (or the empty string if it does not exist).
<div class="alert alert-info">
<strong>Why can't I use environmental variables elsewhere?</strong>
User variables are the single source of configurable input to a template.
We felt that having environmental variables used <em>anywhere</em> in a
template would confuse the user about the possible inputs to a template.
By allowing environmental variables only within default values for user
variables, user variables remain as the single source of input to a template
that a user can easily discover using <code>packer inspect</code>.
</div>
## Setting Variables
Now that we covered how to define and use variables within a template,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment