From edcb8fea300498c17d660234f17504504e70bad4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 26 Apr 2014 20:51:36 -0700 Subject: [PATCH] packer: min_packer_version [GH-487] --- CHANGELOG.md | 2 + packer/template.go | 26 +++++++- packer/template_test.go | 63 +++++++++++++++++++ .../docs/templates/introduction.html.markdown | 6 ++ 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1fb43056..1230bd046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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: diff --git a/packer/template.go b/packer/template.go index 3ba9ad483..a96b6758d 100644 --- a/packer/template.go +++ b/packer/template.go @@ -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 { diff --git a/packer/template_test.go b/packer/template_test.go index eff68612d..547b29daf 100644 --- a/packer/template_test.go +++ b/packer/template_test.go @@ -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 := ` { diff --git a/website/source/docs/templates/introduction.html.markdown b/website/source/docs/templates/introduction.html.markdown index 83ca0faf2..6b11cc851 100644 --- a/website/source/docs/templates/introduction.html.markdown +++ b/website/source/docs/templates/introduction.html.markdown @@ -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