Unused keys are invalid in templates [GH-104]

This commit is contained in:
Mitchell Hashimoto 2013-07-14 09:28:56 +09:00
parent 245deaf599
commit 3b4ef72e47
13 changed files with 238 additions and 24 deletions

View File

@ -13,6 +13,8 @@ FEATURES:
IMPROVEMENTS: IMPROVEMENTS:
* everything: invalid keys in configuration are now considered validation
errors. [GH-104]
* amazon-ebs: Verify the source AMI is EBS-backed before launching. [GH-169] * amazon-ebs: Verify the source AMI is EBS-backed before launching. [GH-169]
* vmware: error if shutdown command has non-zero exit status. * vmware: error if shutdown command has non-zero exit status.

View File

@ -16,6 +16,8 @@ import (
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log" "log"
"os" "os"
"sort"
"strings"
"text/template" "text/template"
"time" "time"
) )
@ -50,15 +52,38 @@ type Builder struct {
} }
func (b *Builder) Prepare(raws ...interface{}) error { func (b *Builder) Prepare(raws ...interface{}) error {
var err error var md mapstructure.Metadata
decoderConfig := &mapstructure.DecoderConfig{
Metadata: &md,
Result: &b.config,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
return err
}
for _, raw := range raws { for _, raw := range raws {
err := mapstructure.Decode(raw, &b.config) err := decoder.Decode(raw)
if err != nil { if err != nil {
return err return err
} }
} }
// Accumulate any errors
errs := make([]error, 0)
// Unused keys are errors
if len(md.Unused) > 0 {
sort.Strings(md.Unused)
for _, unused := range md.Unused {
if unused != "type" && !strings.HasPrefix(unused, "packer_") {
errs = append(
errs, fmt.Errorf("Unknown configuration key: %s", unused))
}
}
}
if b.config.AccessKey == "" { if b.config.AccessKey == "" {
b.config.AccessKey = os.Getenv("AWS_ACCESS_KEY_ID") b.config.AccessKey = os.Getenv("AWS_ACCESS_KEY_ID")
} }
@ -84,8 +109,6 @@ func (b *Builder) Prepare(raws ...interface{}) error {
} }
// Accumulate any errors // Accumulate any errors
errs := make([]error, 0)
if b.config.AccessKey == "" { if b.config.AccessKey == "" {
errs = append(errs, errors.New("An access_key must be specified")) errs = append(errs, errors.New("An access_key must be specified"))
} }

View File

@ -148,6 +148,18 @@ func TestBuilderPrepare_InstanceType(t *testing.T) {
} }
} }
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testConfig()
// Add a random key
config["i_should_not_be_valid"] = true
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_Region(t *testing.T) { func TestBuilderPrepare_Region(t *testing.T) {
var b Builder var b Builder
config := testConfig() config := testConfig()

View File

@ -13,7 +13,9 @@ import (
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log" "log"
"os" "os"
"sort"
"strconv" "strconv"
"strings"
"text/template" "text/template"
"time" "time"
) )
@ -56,15 +58,39 @@ type Builder struct {
} }
func (b *Builder) Prepare(raws ...interface{}) error { func (b *Builder) Prepare(raws ...interface{}) error {
var md mapstructure.Metadata
decoderConfig := &mapstructure.DecoderConfig{
Metadata: &md,
Result: &b.config,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
return err
}
for _, raw := range raws { for _, raw := range raws {
err := mapstructure.Decode(raw, &b.config) err := decoder.Decode(raw)
if err != nil { if err != nil {
return err return err
} }
} }
// Accumulate any errors
errs := make([]error, 0)
// Unused keys are errors
if len(md.Unused) > 0 {
sort.Strings(md.Unused)
for _, unused := range md.Unused {
if unused != "type" && !strings.HasPrefix(unused, "packer_") {
errs = append(
errs, fmt.Errorf("Unknown configuration key: %s", unused))
}
}
}
// Optional configuration with defaults // Optional configuration with defaults
//
if b.config.APIKey == "" { if b.config.APIKey == "" {
// Default to environment variable for api_key, if it exists // Default to environment variable for api_key, if it exists
b.config.APIKey = os.Getenv("DIGITALOCEAN_API_KEY") b.config.APIKey = os.Getenv("DIGITALOCEAN_API_KEY")
@ -123,11 +149,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
b.config.RawStateTimeout = "6m" b.config.RawStateTimeout = "6m"
} }
// A list of errors on the configuration
errs := make([]error, 0)
// Required configurations that will display errors if not set // Required configurations that will display errors if not set
//
if b.config.ClientID == "" { if b.config.ClientID == "" {
errs = append(errs, errors.New("a client_id must be specified")) errs = append(errs, errors.New("a client_id must be specified"))
} }

View File

@ -106,6 +106,18 @@ func TestBuilderPrepare_ClientID(t *testing.T) {
} }
} }
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testConfig()
// Add a random key
config["i_should_not_be_valid"] = true
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_RegionID(t *testing.T) { func TestBuilderPrepare_RegionID(t *testing.T) {
var b Builder var b Builder
config := testConfig() config := testConfig()

View File

@ -12,6 +12,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"sort"
"strings" "strings"
"time" "time"
) )
@ -62,15 +63,38 @@ type config struct {
} }
func (b *Builder) Prepare(raws ...interface{}) error { func (b *Builder) Prepare(raws ...interface{}) error {
var err error var md mapstructure.Metadata
decoderConfig := &mapstructure.DecoderConfig{
Metadata: &md,
Result: &b.config,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
return err
}
for _, raw := range raws { for _, raw := range raws {
err := mapstructure.Decode(raw, &b.config) err := decoder.Decode(raw)
if err != nil { if err != nil {
return err return err
} }
} }
// Accumulate any errors
errs := make([]error, 0)
// Unused keys are errors
if len(md.Unused) > 0 {
sort.Strings(md.Unused)
for _, unused := range md.Unused {
if unused != "type" && !strings.HasPrefix(unused, "packer_") {
errs = append(
errs, fmt.Errorf("Unknown configuration key: %s", unused))
}
}
}
if b.config.DiskSize == 0 { if b.config.DiskSize == 0 {
b.config.DiskSize = 40000 b.config.DiskSize = 40000
} }
@ -127,8 +151,6 @@ func (b *Builder) Prepare(raws ...interface{}) error {
b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName) b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName)
} }
errs := make([]error, 0)
if b.config.HTTPPortMin > b.config.HTTPPortMax { if b.config.HTTPPortMin > b.config.HTTPPortMax {
errs = append(errs, errors.New("http_port_min must be less than http_port_max")) errs = append(errs, errors.New("http_port_min must be less than http_port_max"))
} }

View File

@ -271,6 +271,18 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) {
} }
} }
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testConfig()
// Add a random key
config["i_should_not_be_valid"] = true
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_ISOMD5(t *testing.T) { func TestBuilderPrepare_ISOMD5(t *testing.T) {
var b Builder var b Builder
config := testConfig() config := testConfig()

View File

@ -12,6 +12,7 @@ import (
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"sort"
"strings" "strings"
"text/template" "text/template"
"time" "time"
@ -63,13 +64,38 @@ type config struct {
} }
func (b *Builder) Prepare(raws ...interface{}) error { func (b *Builder) Prepare(raws ...interface{}) error {
var md mapstructure.Metadata
decoderConfig := &mapstructure.DecoderConfig{
Metadata: &md,
Result: &b.config,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
return err
}
for _, raw := range raws { for _, raw := range raws {
err := mapstructure.Decode(raw, &b.config) err := decoder.Decode(raw)
if err != nil { if err != nil {
return err return err
} }
} }
// Accumulate any errors
errs := make([]error, 0)
// Unused keys are errors
if len(md.Unused) > 0 {
sort.Strings(md.Unused)
for _, unused := range md.Unused {
if unused != "type" && !strings.HasPrefix(unused, "packer_") {
errs = append(
errs, fmt.Errorf("Unknown configuration key: %s", unused))
}
}
}
if b.config.DiskName == "" { if b.config.DiskName == "" {
b.config.DiskName = "disk" b.config.DiskName = "disk"
} }
@ -122,10 +148,6 @@ func (b *Builder) Prepare(raws ...interface{}) error {
b.config.ToolsUploadPath = "{{ .Flavor }}.iso" b.config.ToolsUploadPath = "{{ .Flavor }}.iso"
} }
// Accumulate any errors
var err error
errs := make([]error, 0)
if b.config.HTTPPortMin > b.config.HTTPPortMax { if b.config.HTTPPortMin > b.config.HTTPPortMax {
errs = append(errs, errors.New("http_port_min must be less than http_port_max")) errs = append(errs, errors.New("http_port_min must be less than http_port_max"))
} }

View File

@ -163,6 +163,18 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) {
} }
} }
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testConfig()
// Add a random key
config["i_should_not_be_valid"] = true
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_ISOMD5(t *testing.T) { func TestBuilderPrepare_ISOMD5(t *testing.T) {
var b Builder var b Builder
config := testConfig() config := testConfig()

View File

@ -6,6 +6,8 @@ import (
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"os" "os"
"sort"
"strings"
) )
type config struct { type config struct {
@ -21,13 +23,37 @@ type Provisioner struct {
} }
func (p *Provisioner) Prepare(raws ...interface{}) error { func (p *Provisioner) Prepare(raws ...interface{}) error {
var md mapstructure.Metadata
decoderConfig := &mapstructure.DecoderConfig{
Metadata: &md,
Result: &p.config,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
return err
}
for _, raw := range raws { for _, raw := range raws {
if err := mapstructure.Decode(raw, &p.config); err != nil { err := decoder.Decode(raw)
if err != nil {
return err return err
} }
} }
errs := []error{} // Accumulate any errors
errs := make([]error, 0)
// Unused keys are errors
if len(md.Unused) > 0 {
sort.Strings(md.Unused)
for _, unused := range md.Unused {
if unused != "type" && !strings.HasPrefix(unused, "packer_") {
errs = append(
errs, fmt.Errorf("Unknown configuration key: %s", unused))
}
}
}
if _, err := os.Stat(p.config.Source); err != nil { if _, err := os.Stat(p.config.Source); err != nil {
errs = append(errs, errs = append(errs,

View File

@ -23,6 +23,18 @@ func TestProvisioner_Impl(t *testing.T) {
} }
} }
func TestProvisionerPrepare_InvalidKey(t *testing.T) {
var p Provisioner
config := testConfig()
// Add a random key
config["i_should_not_be_valid"] = true
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestProvisionerPrepare_InvalidSource(t *testing.T) { func TestProvisionerPrepare_InvalidSource(t *testing.T) {
var p Provisioner var p Provisioner
config := testConfig() config := testConfig()

View File

@ -14,6 +14,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"sort"
"strings" "strings"
"text/template" "text/template"
) )
@ -58,12 +59,38 @@ type ExecuteCommandTemplate struct {
} }
func (p *Provisioner) Prepare(raws ...interface{}) error { func (p *Provisioner) Prepare(raws ...interface{}) error {
var md mapstructure.Metadata
decoderConfig := &mapstructure.DecoderConfig{
Metadata: &md,
Result: &p.config,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
return err
}
for _, raw := range raws { for _, raw := range raws {
if err := mapstructure.Decode(raw, &p.config); err != nil { err := decoder.Decode(raw)
if err != nil {
return err return err
} }
} }
// Accumulate any errors
errs := make([]error, 0)
// Unused keys are errors
if len(md.Unused) > 0 {
sort.Strings(md.Unused)
for _, unused := range md.Unused {
if unused != "type" && !strings.HasPrefix(unused, "packer_") {
errs = append(
errs, fmt.Errorf("Unknown configuration key: %s", unused))
}
}
}
if p.config.ExecuteCommand == "" { if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}" p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}"
} }
@ -88,8 +115,6 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
p.config.Vars = make([]string, 0) p.config.Vars = make([]string, 0)
} }
errs := make([]error, 0)
if p.config.Script != "" && len(p.config.Scripts) > 0 { if p.config.Script != "" && len(p.config.Scripts) > 0 {
errs = append(errs, errors.New("Only one of script or scripts can be specified.")) errs = append(errs, errors.New("Only one of script or scripts can be specified."))
} }

View File

@ -62,6 +62,18 @@ func TestProvisionerPrepare_InlineShebang(t *testing.T) {
} }
} }
func TestProvisionerPrepare_InvalidKey(t *testing.T) {
var p Provisioner
config := testConfig()
// Add a random key
config["i_should_not_be_valid"] = true
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestProvisionerPrepare_Script(t *testing.T) { func TestProvisionerPrepare_Script(t *testing.T) {
config := testConfig() config := testConfig()
delete(config, "inline") delete(config, "inline")