packer: allow environmental variables within user vars [GH-633]
This commit is contained in:
parent
8aabe01bad
commit
e38c0424b9
|
@ -26,6 +26,9 @@ FEATURES:
|
||||||
an existing OVF or OVA. [GH-201]
|
an existing OVF or OVA. [GH-201]
|
||||||
* **New builder:** "vmware-vmx" can build VMware images from an existing
|
* **New builder:** "vmware-vmx" can build VMware images from an existing
|
||||||
VMX. [GH-201]
|
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
|
* "description" field in templates: write a human-readable description
|
||||||
of what a template does. This will be shown in `packer inspect`.
|
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
|
* 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 = template.New("configTemplateRoot")
|
||||||
result.root.Funcs(template.FuncMap{
|
result.root.Funcs(template.FuncMap{
|
||||||
|
"env": templateDisableEnv,
|
||||||
"pwd": templatePwd,
|
"pwd": templatePwd,
|
||||||
"isotime": templateISOTime,
|
"isotime": templateISOTime,
|
||||||
"timestamp": templateTimestamp,
|
"timestamp": templateTimestamp,
|
||||||
|
@ -95,6 +96,20 @@ func (t *ConfigTemplate) templateUser(n string) (string, error) {
|
||||||
return result, nil
|
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 {
|
func templateISOTime() string {
|
||||||
return time.Now().UTC().Format(time.RFC3339)
|
return time.Now().UTC().Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,18 @@ import (
|
||||||
"time"
|
"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) {
|
func TestConfigTemplateProcess_isotime(t *testing.T) {
|
||||||
tpl, err := NewConfigTemplate()
|
tpl, err := NewConfigTemplate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -450,6 +451,18 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
|
||||||
return
|
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
|
// Prepare the variables
|
||||||
var varErrors []error
|
var varErrors []error
|
||||||
variables := make(map[string]string)
|
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))
|
fmt.Errorf("Required user variable '%s' not set", k))
|
||||||
}
|
}
|
||||||
|
|
||||||
var val string = v.Default
|
var val string
|
||||||
if v.HasValue {
|
if v.HasValue {
|
||||||
val = v.Value
|
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
|
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) {
|
func TestTemplateBuild_names(t *testing.T) {
|
||||||
data := `
|
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
|
builders, provisioners, _anything_. The user variable is available globally
|
||||||
within the template.
|
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
|
## Setting Variables
|
||||||
|
|
||||||
Now that we covered how to define and use variables within a template,
|
Now that we covered how to define and use variables within a template,
|
||||||
|
|
Loading…
Reference in New Issue