Commit edcb8fea authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer: min_packer_version [GH-487]

parent 9ef50487
......@@ -8,6 +8,8 @@ FEATURES:
command, which talks to a Chef Server. [GH-855]
* **New provisioner:** `puppet-server` - Provision using Puppet by
communicating to a Puppet master. [GH-796]
* `min_packer_version` can be specified in a Packer template to force
a minimum version. [GH-487]
IMPROVEMENTS:
......
......@@ -3,6 +3,7 @@ package packer
import (
"bytes"
"fmt"
"github.com/hashicorp/go-version"
"github.com/mitchellh/mapstructure"
jsonutil "github.com/mitchellh/packer/common/json"
"io"
......@@ -18,12 +19,14 @@ import (
// "interface{}" pointers since we actually don't know what their contents
// are until we read the "type" field.
type rawTemplate struct {
MinimumPackerVersion string `mapstructure:"min_packer_version"`
Description string
Variables map[string]interface{}
Builders []map[string]interface{}
Hooks map[string][]string
Provisioners []map[string]interface{}
PostProcessors []interface{} `mapstructure:"post-processors"`
Provisioners []map[string]interface{}
Variables map[string]interface{}
}
// The Template struct represents a parsed template, parsed into the most
......@@ -115,6 +118,25 @@ func ParseTemplate(data []byte, vars map[string]string) (t *Template, err error)
return
}
if rawTpl.MinimumPackerVersion != "" {
vCur, err := version.NewVersion(Version)
if err != nil {
panic(err)
}
vReq, err := version.NewVersion(rawTpl.MinimumPackerVersion)
if err != nil {
return nil, fmt.Errorf(
"'minimum_packer_version' error: %s", err)
}
if vCur.LessThan(vReq) {
return nil, fmt.Errorf(
"Template requires Packer version %s. " +
"Running version is %s.",
vReq, vCur)
}
}
errors := make([]error, 0)
if len(md.Unused) > 0 {
......
......@@ -60,6 +60,69 @@ func TestParseTemplateFile_basic(t *testing.T) {
}
}
func TestParseTemplateFile_minPackerVersionBad(t *testing.T) {
data := `
{
"min_packer_version": "27.0.0",
"builders": [{"type": "something"}]
}
`
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
tf.Write([]byte(data))
tf.Close()
_, err = ParseTemplateFile(tf.Name(), nil)
if err == nil {
t.Fatal("expects error")
}
}
func TestParseTemplateFile_minPackerVersionFormat(t *testing.T) {
data := `
{
"min_packer_version": "NOPE NOPE NOPE",
"builders": [{"type": "something"}]
}
`
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
tf.Write([]byte(data))
tf.Close()
_, err = ParseTemplateFile(tf.Name(), nil)
if err == nil {
t.Fatal("expects error")
}
}
func TestParseTemplateFile_minPackerVersionGood(t *testing.T) {
data := `
{
"min_packer_version": "0.1",
"builders": [{"type": "something"}]
}
`
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
tf.Write([]byte(data))
tf.Close()
_, err = ParseTemplateFile(tf.Name(), nil)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestParseTemplateFile_stdin(t *testing.T) {
data := `
{
......
......@@ -43,6 +43,12 @@ Along with each key, it is noted whether it is required or not.
information on what post-processors do and how they're defined, read the
sub-section on [configuring post-processors in templates](/docs/templates/post-processors.html).
* `min_packer_version` (optional) is a string that has a minimum Packer
version that is required to parse the template. This can be used to
ensure that proper versions of Packer are used with the template. A
max version can't be specified because Packer retains backwards
compatibility with `packer fix`.
## Example Template
Below is an example of a basic template that is nearly fully functional. It is just
......
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