diff --git a/command/fix/command.go b/command/fix/command.go index 5de003cea..c62d3676b 100644 --- a/command/fix/command.go +++ b/command/fix/command.go @@ -49,15 +49,8 @@ func (c Command) Run(env packer.Environment, args []string) int { // Close the file since we're done with that tplF.Close() - // Run the template through the various fixers - fixers := []string{ - "iso-md5", - "createtime", - "virtualbox-gaattach", - } - input := templateData - for _, name := range fixers { + for _, name := range FixerOrder { var err error fixer, ok := Fixers[name] if !ok { diff --git a/command/fix/fixer.go b/command/fix/fixer.go index fca464c5a..fd4d947da 100644 --- a/command/fix/fixer.go +++ b/command/fix/fixer.go @@ -15,10 +15,21 @@ type Fixer interface { // Fixers is the map of all available fixers, by name. var Fixers map[string]Fixer +// FixerOrder is the default order the fixers should be run. +var FixerOrder []string + func init() { Fixers = map[string]Fixer{ "iso-md5": new(FixerISOMD5), "createtime": new(FixerCreateTime), + "pp-vagrant-override": new(FixerVagrantPPOverride), "virtualbox-gaattach": new(FixerVirtualBoxGAAttach), } + + FixerOrder = []string{ + "iso-md5", + "createtime", + "virtualbox-gaattach", + "pp-vagrant-override", + } } diff --git a/command/fix/fixer_pp_vagrant_override.go b/command/fix/fixer_pp_vagrant_override.go new file mode 100644 index 000000000..36aadfa46 --- /dev/null +++ b/command/fix/fixer_pp_vagrant_override.go @@ -0,0 +1,75 @@ +package fix + +import ( + "github.com/mitchellh/mapstructure" +) + +// FixerVagrantPPOvveride is a Fixer that replaces the provider-specific +// overrides for the Vagrant post-processor with the new style introduced +// as part of Packer 0.5.0. +type FixerVagrantPPOverride struct{} + +func (FixerVagrantPPOverride) Fix(input map[string]interface{}) (map[string]interface{}, error) { + // Our template type we'll use for this fixer only + type template struct { + PostProcessors []interface{} `mapstructure:"post-processors"` + } + + // Decode the input into our structure, if we can + var tpl template + if err := mapstructure.Decode(input, &tpl); err != nil { + return nil, err + } + + // Go through each post-processor and get out all the complex configs + pps := make([]map[string]interface{}, 0, len(tpl.PostProcessors)) + for _, rawPP := range tpl.PostProcessors { + switch pp := rawPP.(type) { + case string: + case map[string]interface{}: + pps = append(pps, pp) + case []interface{}: + for _, innerRawPP := range pp { + if innerPP, ok := innerRawPP.(map[string]interface{}); ok { + pps = append(pps, innerPP) + } + } + } + } + + // Go through each post-processor and make the fix if necessary + possible := []string{"aws", "digitalocean", "virtualbox", "vmware"} + for _, pp := range pps { + typeRaw, ok := pp["type"] + if !ok { + continue + } + + if typeName, ok := typeRaw.(string); !ok { + continue + } else if typeName != "vagrant" { + continue + } + + overrides := make(map[string]interface{}) + for _, name := range possible { + if _, ok := pp[name]; !ok { + continue + } + + overrides[name] = pp[name] + delete(pp, name) + } + + if len(overrides) > 0 { + pp["override"] = overrides + } + } + + input["post-processors"] = tpl.PostProcessors + return input, nil +} + +func (FixerVagrantPPOverride) Synopsis() string { + return `Fixes provider-specific overrides for Vagrant post-processor` +} diff --git a/command/fix/fixer_pp_vagrant_override_test.go b/command/fix/fixer_pp_vagrant_override_test.go new file mode 100644 index 000000000..ae344911e --- /dev/null +++ b/command/fix/fixer_pp_vagrant_override_test.go @@ -0,0 +1,79 @@ +package fix + +import ( + "reflect" + "testing" +) + +func TestFixerVagrantPPOverride_Impl(t *testing.T) { + var _ Fixer = new(FixerVagrantPPOverride) +} + +func TestFixerVagrantPPOverride_Fix(t *testing.T) { + var f FixerVagrantPPOverride + + input := map[string]interface{}{ + "post-processors": []interface{}{ + "foo", + + map[string]interface{}{ + "type": "vagrant", + "aws": map[string]interface{}{ + "foo": "bar", + }, + }, + + map[string]interface{}{ + "type": "vsphere", + }, + + []interface{}{ + map[string]interface{}{ + "type": "vagrant", + "vmware": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + }, + } + + expected := map[string]interface{}{ + "post-processors": []interface{}{ + "foo", + + map[string]interface{}{ + "type": "vagrant", + "override": map[string]interface{}{ + "aws": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + + map[string]interface{}{ + "type": "vsphere", + }, + + []interface{}{ + map[string]interface{}{ + "type": "vagrant", + "override": map[string]interface{}{ + "vmware": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + }, + }, + } + + output, err := f.Fix(input) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(output, expected) { + t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected) + } +} diff --git a/command/fix/help.go b/command/fix/help.go index 94b5c6a11..88ab34070 100644 --- a/command/fix/help.go +++ b/command/fix/help.go @@ -11,6 +11,11 @@ Usage: packer fix [options] TEMPLATE Fixes that are run: - iso-md5 Replaces "iso_md5" in builders with newer "iso_checksum" + iso-md5 Replaces "iso_md5" in builders with newer "iso_checksum" + createtime Replaces ".CreateTime" in builder configs with "{{timestamp}}" + virtualbox-gaattach Updates VirtualBox builders using "guest_additions_attach" + to use "guest_additions_mode" + pp-vagrant-override Replaces old-style provider overrides for the Vagrant + post-processor to new-style as of Packer 0.5.0. ` diff --git a/packer/artifact_mock.go b/packer/artifact_mock.go index ec88007f3..a59af5331 100644 --- a/packer/artifact_mock.go +++ b/packer/artifact_mock.go @@ -2,16 +2,26 @@ package packer // MockArtifact is an implementation of Artifact that can be used for tests. type MockArtifact struct { - IdValue string - DestroyCalled bool + BuilderIdValue string + FilesValue []string + IdValue string + DestroyCalled bool } -func (*MockArtifact) BuilderId() string { - return "bid" +func (a *MockArtifact) BuilderId() string { + if a.BuilderIdValue == "" { + return "bid" + } + + return a.BuilderIdValue } -func (*MockArtifact) Files() []string { - return []string{"a", "b"} +func (a *MockArtifact) Files() []string { + if a.FilesValue == nil { + return []string{"a", "b"} + } + + return a.FilesValue } func (a *MockArtifact) Id() string { diff --git a/post-processor/vagrant/aws.go b/post-processor/vagrant/aws.go index 04ef43ec0..cddadd7e6 100644 --- a/post-processor/vagrant/aws.go +++ b/post-processor/vagrant/aws.go @@ -1,157 +1,45 @@ package vagrant import ( - "compress/flate" + "bytes" "fmt" - "github.com/mitchellh/packer/common" - "github.com/mitchellh/packer/packer" - "io/ioutil" - "log" - "os" - "path/filepath" - "strconv" "strings" -) - -type AWSBoxConfig struct { - common.PackerConfig `mapstructure:",squash"` - - OutputPath string `mapstructure:"output"` - VagrantfileTemplate string `mapstructure:"vagrantfile_template"` - CompressionLevel string `mapstructure:"compression_level"` - - tpl *packer.ConfigTemplate -} - -type AWSVagrantfileTemplate struct { - Images map[string]string -} - -type AWSBoxPostProcessor struct { - config AWSBoxConfig -} - -func (p *AWSBoxPostProcessor) Configure(raws ...interface{}) error { - md, err := common.DecodeConfig(&p.config, raws...) - if err != nil { - return err - } - - p.config.tpl, err = packer.NewConfigTemplate() - if err != nil { - return err - } - p.config.tpl.UserVars = p.config.PackerUserVars - - // Accumulate any errors - errs := common.CheckUnusedConfig(md) - - validates := map[string]*string{ - "output": &p.config.OutputPath, - "vagrantfile_template": &p.config.VagrantfileTemplate, - "compression_level": &p.config.CompressionLevel, - } + "text/template" - for n, ptr := range validates { - if err := p.config.tpl.Validate(*ptr); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Error parsing %s: %s", n, err)) - } - } + "github.com/mitchellh/packer/packer" +) - if errs != nil && len(errs.Errors) > 0 { - return errs - } +type AWSProvider struct{} - return nil -} +func (p *AWSProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) { + // Create the metadata + metadata = map[string]interface{}{"provider": "aws"} -func (p *AWSBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { - // Determine the regions... - tplData := &AWSVagrantfileTemplate{ + // Build up the template data to build our Vagrantfile + tplData := &awsVagrantfileTemplate{ Images: make(map[string]string), } for _, regions := range strings.Split(artifact.Id(), ",") { parts := strings.Split(regions, ":") if len(parts) != 2 { - return nil, false, fmt.Errorf("Poorly formatted artifact ID: %s", artifact.Id()) + err = fmt.Errorf("Poorly formatted artifact ID: %s", artifact.Id()) + return } tplData.Images[parts[0]] = parts[1] } - // Compile the output path - outputPath, err := p.config.tpl.Process(p.config.OutputPath, &OutputPathTemplate{ - ArtifactId: artifact.Id(), - BuildName: p.config.PackerBuildName, - Provider: "aws", - }) - if err != nil { - return nil, false, err - } - - // Create a temporary directory for us to build the contents of the box in - dir, err := ioutil.TempDir("", "packer") - if err != nil { - return nil, false, err - } - defer os.RemoveAll(dir) - - // Create the Vagrantfile from the template - vf, err := os.Create(filepath.Join(dir, "Vagrantfile")) - if err != nil { - return nil, false, err - } - defer vf.Close() - - vagrantfileContents := defaultAWSVagrantfile - if p.config.VagrantfileTemplate != "" { - log.Printf("Using vagrantfile template: %s", p.config.VagrantfileTemplate) - f, err := os.Open(p.config.VagrantfileTemplate) - if err != nil { - err = fmt.Errorf("error opening vagrantfile template: %s", err) - return nil, false, err - } - defer f.Close() - - contents, err := ioutil.ReadAll(f) - if err != nil { - err = fmt.Errorf("error reading vagrantfile template: %s", err) - return nil, false, err - } - - vagrantfileContents = string(contents) - } - - vagrantfileContents, err = p.config.tpl.Process(vagrantfileContents, tplData) - if err != nil { - return nil, false, fmt.Errorf("Error writing Vagrantfile: %s", err) - } - vf.Write([]byte(vagrantfileContents)) - vf.Close() - - var level int = flate.DefaultCompression - if p.config.CompressionLevel != "" { - level, err = strconv.Atoi(p.config.CompressionLevel) - if err != nil { - return nil, false, err - } - } - - // Create the metadata - metadata := map[string]string{"provider": "aws"} - if err := WriteMetadata(dir, metadata); err != nil { - return nil, false, err - } - - // Compress the directory to the given output path - if err := DirToBox(outputPath, dir, ui, level); err != nil { - err = fmt.Errorf("error creating box: %s", err) - return nil, false, err - } + // Build up the contents + var contents bytes.Buffer + t := template.Must(template.New("vf").Parse(defaultAWSVagrantfile)) + err = t.Execute(&contents, tplData) + vagrantfile = contents.String() + return +} - return NewArtifact("aws", outputPath), true, nil +type awsVagrantfileTemplate struct { + Images map[string]string } var defaultAWSVagrantfile = ` diff --git a/post-processor/vagrant/aws_test.go b/post-processor/vagrant/aws_test.go index 7c7d6fcc3..f685e030e 100644 --- a/post-processor/vagrant/aws_test.go +++ b/post-processor/vagrant/aws_test.go @@ -1,14 +1,9 @@ package vagrant import ( - "github.com/mitchellh/packer/packer" "testing" ) -func TestAWSBoxPostProcessor_ImplementsPostProcessor(t *testing.T) { - var raw interface{} - raw = &AWSBoxPostProcessor{} - if _, ok := raw.(packer.PostProcessor); !ok { - t.Fatalf("AWS PostProcessor should be a PostProcessor") - } +func TestAWSProvider_impl(t *testing.T) { + var _ Provider = new(AWSProvider) } diff --git a/post-processor/vagrant/digitalocean.go b/post-processor/vagrant/digitalocean.go index 3ff83d04e..aa7a757ff 100644 --- a/post-processor/vagrant/digitalocean.go +++ b/post-processor/vagrant/digitalocean.go @@ -1,155 +1,41 @@ package vagrant import ( - "compress/flate" + "bytes" "fmt" - "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" - "io/ioutil" - "log" - "os" - "path/filepath" - "strconv" "strings" + "text/template" ) -type DigitalOceanBoxConfig struct { - common.PackerConfig `mapstructure:",squash"` - - OutputPath string `mapstructure:"output"` - VagrantfileTemplate string `mapstructure:"vagrantfile_template"` - CompressionLevel string `mapstructure:"compression_level"` - - tpl *packer.ConfigTemplate -} - -type DigitalOceanVagrantfileTemplate struct { +type digitalOceanVagrantfileTemplate struct { Image string "" Region string "" } -type DigitalOceanBoxPostProcessor struct { - config DigitalOceanBoxConfig -} - -func (p *DigitalOceanBoxPostProcessor) Configure(rDigitalOcean ...interface{}) error { - md, err := common.DecodeConfig(&p.config, rDigitalOcean...) - if err != nil { - return err - } - - p.config.tpl, err = packer.NewConfigTemplate() - if err != nil { - return err - } - p.config.tpl.UserVars = p.config.PackerUserVars - - // Accumulate any errors - errs := common.CheckUnusedConfig(md) +type DigitalOceanProvider struct{} - validates := map[string]*string{ - "output": &p.config.OutputPath, - "vagrantfile_template": &p.config.VagrantfileTemplate, - "compression_level": &p.config.CompressionLevel, - } - - for n, ptr := range validates { - if err := p.config.tpl.Validate(*ptr); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Error parsing %s: %s", n, err)) - } - } - - if errs != nil && len(errs.Errors) > 0 { - return errs - } - - return nil -} +func (p *DigitalOceanProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) { + // Create the metadata + metadata = map[string]interface{}{"provider": "digital_ocean"} -func (p *DigitalOceanBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { // Determine the image and region... - tplData := &DigitalOceanVagrantfileTemplate{} + tplData := &digitalOceanVagrantfileTemplate{} parts := strings.Split(artifact.Id(), ":") if len(parts) != 2 { - return nil, false, fmt.Errorf("Poorly formatted artifact ID: %s", artifact.Id()) + err = fmt.Errorf("Poorly formatted artifact ID: %s", artifact.Id()) + return } - tplData.Region = parts[0] tplData.Image = parts[1] - // Compile the output path - outputPath, err := p.config.tpl.Process(p.config.OutputPath, &OutputPathTemplate{ - ArtifactId: artifact.Id(), - BuildName: p.config.PackerBuildName, - Provider: "digitalocean", - }) - if err != nil { - return nil, false, err - } - - // Create a temporary directory for us to build the contents of the box in - dir, err := ioutil.TempDir("", "packer") - if err != nil { - return nil, false, err - } - defer os.RemoveAll(dir) - - // Create the Vagrantfile from the template - vf, err := os.Create(filepath.Join(dir, "Vagrantfile")) - if err != nil { - return nil, false, err - } - defer vf.Close() - - vagrantfileContents := defaultDigitalOceanVagrantfile - if p.config.VagrantfileTemplate != "" { - log.Printf("Using vagrantfile template: %s", p.config.VagrantfileTemplate) - f, err := os.Open(p.config.VagrantfileTemplate) - if err != nil { - err = fmt.Errorf("error opening vagrantfile template: %s", err) - return nil, false, err - } - defer f.Close() - - contents, err := ioutil.ReadAll(f) - if err != nil { - err = fmt.Errorf("error reading vagrantfile template: %s", err) - return nil, false, err - } - - vagrantfileContents = string(contents) - } - - vagrantfileContents, err = p.config.tpl.Process(vagrantfileContents, tplData) - if err != nil { - return nil, false, fmt.Errorf("Error writing Vagrantfile: %s", err) - } - vf.Write([]byte(vagrantfileContents)) - vf.Close() - - // Create the metadata - metadata := map[string]string{"provider": "digital_ocean"} - if err := WriteMetadata(dir, metadata); err != nil { - return nil, false, err - } - - // Compress the directory to the given output path - var level int = flate.DefaultCompression - if p.config.CompressionLevel != "" { - level, err = strconv.Atoi(p.config.CompressionLevel) - if err != nil { - return nil, false, err - } - } - - if err := DirToBox(outputPath, dir, ui, level); err != nil { - err = fmt.Errorf("error creating box: %s", err) - return nil, false, err - } - - return NewArtifact("DigitalOcean", outputPath), true, nil + // Build up the Vagrantfile + var contents bytes.Buffer + t := template.Must(template.New("vf").Parse(defaultDigitalOceanVagrantfile)) + err = t.Execute(&contents, tplData) + vagrantfile = contents.String() + return } var defaultDigitalOceanVagrantfile = ` @@ -159,5 +45,4 @@ Vagrant.configure("2") do |config| digital_ocean.region = "{{ .Region }}" end end - ` diff --git a/post-processor/vagrant/digitalocean_test.go b/post-processor/vagrant/digitalocean_test.go index 44451c6a4..09ca6fdf4 100644 --- a/post-processor/vagrant/digitalocean_test.go +++ b/post-processor/vagrant/digitalocean_test.go @@ -1,14 +1,9 @@ package vagrant import ( - "github.com/mitchellh/packer/packer" "testing" ) -func TestDigitalOceanBoxPostProcessor_ImplementsPostProcessor(t *testing.T) { - var raw interface{} - raw = &DigitalOceanBoxPostProcessor{} - if _, ok := raw.(packer.PostProcessor); !ok { - t.Fatalf("Digitalocean PostProcessor should be a PostProcessor") - } +func TestDigitalOceanProvider_impl(t *testing.T) { + var _ Provider = new(DigitalOceanProvider) } diff --git a/post-processor/vagrant/post-processor.go b/post-processor/vagrant/post-processor.go index 75d99215a..2329eba3c 100644 --- a/post-processor/vagrant/post-processor.go +++ b/post-processor/vagrant/post-processor.go @@ -4,11 +4,15 @@ package vagrant import ( + "compress/flate" "fmt" - "github.com/mitchellh/mapstructure" + "io/ioutil" + "os" + "path/filepath" + "text/template" + "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" - "log" ) var builtins = map[string]string{ @@ -22,133 +26,213 @@ var builtins = map[string]string{ type Config struct { common.PackerConfig `mapstructure:",squash"` - OutputPath string `mapstructure:"output"` + CompressionLevel int `mapstructure:"compression_level"` + Include []string `mapstructure:"include"` + OutputPath string `mapstructure:"output"` + Override map[string]interface{} + VagrantfileTemplate string `mapstructure:"vagrantfile_template"` + + tpl *packer.ConfigTemplate } type PostProcessor struct { - config Config - premade map[string]packer.PostProcessor - extraConfig map[string]interface{} + configs map[string]*Config } func (p *PostProcessor) Configure(raws ...interface{}) error { - _, err := common.DecodeConfig(&p.config, raws...) - if err != nil { + p.configs = make(map[string]*Config) + p.configs[""] = new(Config) + if err := p.configureSingle(p.configs[""], raws...); err != nil { return err } - tpl, err := packer.NewConfigTemplate() + // Go over any of the provider-specific overrides and load those up. + for name, override := range p.configs[""].Override { + subRaws := make([]interface{}, len(raws)+1) + copy(subRaws, raws) + subRaws[len(raws)] = override + + config := new(Config) + p.configs[name] = config + if err := p.configureSingle(config, subRaws...); err != nil { + return fmt.Errorf("Error configuring %s: %s", name, err) + } + } + + return nil +} + +func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { + name, ok := builtins[artifact.BuilderId()] + if !ok { + return nil, false, fmt.Errorf( + "Unknown artifact type, can't build box: %s", artifact.BuilderId()) + } + + provider := providerForName(name) + if provider == nil { + // This shouldn't happen since we hard code all of these ourselves + panic(fmt.Sprintf("bad provider name: %s", name)) + } + + config := p.configs[""] + if specificConfig, ok := p.configs[name]; ok { + config = specificConfig + } + + ui.Say(fmt.Sprintf("Creating Vagrant box for '%s' provider", name)) + + outputPath, err := config.tpl.Process(config.OutputPath, &outputPathTemplate{ + ArtifactId: artifact.Id(), + BuildName: config.PackerBuildName, + Provider: name, + }) if err != nil { - return err + return nil, false, err } - tpl.UserVars = p.config.PackerUserVars - // Defaults - if p.config.OutputPath == "" { - p.config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box" + // Create a temporary directory for us to build the contents of the box in + dir, err := ioutil.TempDir("", "packer") + if err != nil { + return nil, false, err + } + defer os.RemoveAll(dir) + + // Copy all of the includes files into the temporary directory + for _, src := range config.Include { + ui.Message(fmt.Sprintf("Copying from include: %s", src)) + dst := filepath.Join(dir, filepath.Base(src)) + if err := CopyContents(dst, src); err != nil { + err = fmt.Errorf("Error copying include file: %s\n\n%s", src, err) + return nil, false, err + } } - // Accumulate any errors - errs := new(packer.MultiError) - if err := tpl.Validate(p.config.OutputPath); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Error parsing output template: %s", err)) - } - - // Store extra configuration we'll send to each post-processor type - p.extraConfig = make(map[string]interface{}) - p.extraConfig["output"] = p.config.OutputPath - p.extraConfig["packer_build_name"] = p.config.PackerBuildName - p.extraConfig["packer_builder_type"] = p.config.PackerBuilderType - p.extraConfig["packer_debug"] = p.config.PackerDebug - p.extraConfig["packer_force"] = p.config.PackerForce - p.extraConfig["packer_user_variables"] = p.config.PackerUserVars - - // TODO(mitchellh): Properly handle multiple raw configs. This isn't - // very pressing at the moment because at the time of this comment - // only the first member of raws can contain the actual type-overrides. - var mapConfig map[string]interface{} - if err := mapstructure.Decode(raws[0], &mapConfig); err != nil { - errs = packer.MultiErrorAppend(errs, - fmt.Errorf("Failed to decode config: %s", err)) - return errs + // Run the provider processing step + vagrantfile, metadata, err := provider.Process(ui, artifact, dir) + if err != nil { + return nil, false, err + } + + // Write the metadata we got + if err := WriteMetadata(dir, metadata); err != nil { + return nil, false, err } - p.premade = make(map[string]packer.PostProcessor) - for k, raw := range mapConfig { - pp, err := p.subPostProcessor(k, raw, p.extraConfig) + // Write our Vagrantfile + var customVagrantfile string + if config.VagrantfileTemplate != "" { + ui.Message(fmt.Sprintf( + "Using custom Vagrantfile: %s", config.VagrantfileTemplate)) + customBytes, err := ioutil.ReadFile(config.VagrantfileTemplate) if err != nil { - errs = packer.MultiErrorAppend(errs, err) - continue + return nil, false, err } - if pp == nil { - continue - } + customVagrantfile = string(customBytes) + } - p.premade[k] = pp + f, err := os.Create(filepath.Join(dir, "Vagrantfile")) + if err != nil { + return nil, false, err } - if len(errs.Errors) > 0 { - return errs + t := template.Must(template.New("root").Parse(boxVagrantfileContents)) + err = t.Execute(f, &vagrantfileTemplate{ + ProviderVagrantfile: vagrantfile, + CustomVagrantfile: customVagrantfile, + }) + f.Close() + if err != nil { + return nil, false, err } - return nil + // Create the box + if err := DirToBox(outputPath, dir, ui, config.CompressionLevel); err != nil { + return nil, false, err + } + + return nil, false, nil } -func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { - ppName, ok := builtins[artifact.BuilderId()] - if !ok { - return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) +func (p *PostProcessor) configureSingle(config *Config, raws ...interface{}) error { + md, err := common.DecodeConfig(config, raws...) + if err != nil { + return err } - // Use the premade PostProcessor if we have one. Otherwise, we - // create it and configure it here. - pp, ok := p.premade[ppName] - if !ok { - log.Printf("Premade post-processor for '%s' not found. Creating.", ppName) + config.tpl, err = packer.NewConfigTemplate() + if err != nil { + return err + } + config.tpl.UserVars = config.PackerUserVars - var err error - pp, err = p.subPostProcessor(ppName, nil, p.extraConfig) - if err != nil { - return nil, false, err - } + // Defaults + if config.OutputPath == "" { + config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box" + } - if pp == nil { - return nil, false, fmt.Errorf("Vagrant box post-processor not found: %s", ppName) + found := false + for _, k := range md.Keys { + if k == "compression_level" { + found = true + break } } - ui.Say(fmt.Sprintf("Creating Vagrant box for '%s' provider", ppName)) - return pp.PostProcess(ui, artifact) -} + if !found { + config.CompressionLevel = flate.DefaultCompression + } + + // Accumulate any errors + errs := common.CheckUnusedConfig(md) + + validates := map[string]*string{ + "output": &config.OutputPath, + "vagrantfile_template": &config.VagrantfileTemplate, + } -func (p *PostProcessor) subPostProcessor(key string, specific interface{}, extra map[string]interface{}) (packer.PostProcessor, error) { - pp := keyToPostProcessor(key) - if pp == nil { - return nil, nil + for n, ptr := range validates { + if err := config.tpl.Validate(*ptr); err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Error parsing %s: %s", n, err)) + } } - if err := pp.Configure(extra, specific); err != nil { - return nil, err + if errs != nil && len(errs.Errors) > 0 { + return errs } - return pp, nil + return nil } -// keyToPostProcessor maps a configuration key to the actual post-processor -// it will be configuring. This returns a new instance of that post-processor. -func keyToPostProcessor(key string) packer.PostProcessor { - switch key { - case "aws": - return new(AWSBoxPostProcessor) - case "digitalocean": - return new(DigitalOceanBoxPostProcessor) +func providerForName(name string) Provider { + switch name { case "virtualbox": - return new(VBoxBoxPostProcessor) - case "vmware": - return new(VMwareBoxPostProcessor) + return new(VBoxProvider) default: return nil } } + +// OutputPathTemplate is the structure that is availalable within the +// OutputPath variables. +type outputPathTemplate struct { + ArtifactId string + BuildName string + Provider string +} + +type vagrantfileTemplate struct { + ProviderVagrantfile string + CustomVagrantfile string +} + +const boxVagrantfileContents string = ` +# The contents below were provided by the Packer Vagrant post-processor +{{ .ProviderVagrantfile }} + +# The contents below (if any) are custom contents provided by the +# Packer template during image build. +{{ .CustomVagrantfile }} +` diff --git a/post-processor/vagrant/post-processor_test.go b/post-processor/vagrant/post-processor_test.go index 9a0ac876b..0ef151ebe 100644 --- a/post-processor/vagrant/post-processor_test.go +++ b/post-processor/vagrant/post-processor_test.go @@ -1,7 +1,10 @@ package vagrant import ( + "bytes" + "compress/flate" "github.com/mitchellh/packer/packer" + "strings" "testing" ) @@ -9,15 +12,55 @@ func testConfig() map[string]interface{} { return map[string]interface{}{} } +func testPP(t *testing.T) *PostProcessor { + var p PostProcessor + if err := p.Configure(testConfig()); err != nil { + t.Fatalf("err: %s", err) + } + + return &p +} + +func testUi() *packer.BasicUi { + return &packer.BasicUi{ + Reader: new(bytes.Buffer), + Writer: new(bytes.Buffer), + } +} + func TestPostProcessor_ImplementsPostProcessor(t *testing.T) { - var raw interface{} - raw = &PostProcessor{} - if _, ok := raw.(packer.PostProcessor); !ok { - t.Fatalf("AWS PostProcessor should be a PostProcessor") + var _ packer.PostProcessor = new(PostProcessor) +} + +func TestPostProcessorPrepare_compressionLevel(t *testing.T) { + var p PostProcessor + + // Default + c := testConfig() + delete(c, "compression_level") + if err := p.Configure(c); err != nil { + t.Fatalf("err: %s", err) + } + + config := p.configs[""] + if config.CompressionLevel != flate.DefaultCompression { + t.Fatalf("bad: %#v", config.CompressionLevel) + } + + // Set + c = testConfig() + c["compression_level"] = 7 + if err := p.Configure(c); err != nil { + t.Fatalf("err: %s", err) + } + + config = p.configs[""] + if config.CompressionLevel != 7 { + t.Fatalf("bad: %#v", config.CompressionLevel) } } -func TestBuilderPrepare_OutputPath(t *testing.T) { +func TestPostProcessorPrepare_outputPath(t *testing.T) { var p PostProcessor // Default @@ -36,14 +79,57 @@ func TestBuilderPrepare_OutputPath(t *testing.T) { } } -func TestBuilderPrepare_PPConfig(t *testing.T) { +func TestPostProcessorPrepare_subConfigs(t *testing.T) { var p PostProcessor // Default c := testConfig() - c["aws"] = map[string]interface{}{} + c["compression_level"] = 42 + c["vagrantfile_template"] = "foo" + c["override"] = map[string]interface{}{ + "aws": map[string]interface{}{ + "compression_level": 7, + }, + } err := p.Configure(c) if err != nil { t.Fatalf("err: %s", err) } + + if p.configs[""].CompressionLevel != 42 { + t.Fatalf("bad: %#v", p.configs[""].CompressionLevel) + } + + if p.configs[""].VagrantfileTemplate != "foo" { + t.Fatalf("bad: %#v", p.configs[""].VagrantfileTemplate) + } + + if p.configs["aws"].CompressionLevel != 7 { + t.Fatalf("bad: %#v", p.configs["aws"].CompressionLevel) + } + + if p.configs["aws"].VagrantfileTemplate != "foo" { + t.Fatalf("bad: %#v", p.configs["aws"].VagrantfileTemplate) + } +} + +func TestPostProcessorPostProcess_badId(t *testing.T) { + artifact := &packer.MockArtifact{ + BuilderIdValue: "invalid.packer", + } + + _, _, err := testPP(t).PostProcess(testUi(), artifact) + if !strings.Contains(err.Error(), "artifact type") { + t.Fatalf("err: %s", err) + } +} + +func TestProviderForName(t *testing.T) { + if v, ok := providerForName("virtualbox").(*VBoxProvider); !ok { + t.Fatalf("bad: %#v", v) + } + + if providerForName("nope") != nil { + t.Fatal("should be nil if bad provider") + } } diff --git a/post-processor/vagrant/provider.go b/post-processor/vagrant/provider.go new file mode 100644 index 000000000..00201cb9c --- /dev/null +++ b/post-processor/vagrant/provider.go @@ -0,0 +1,18 @@ +package vagrant + +import ( + "github.com/mitchellh/packer/packer" +) + +// Provider is the interface that each provider must implement in order +// to package the artifacts into a Vagrant-compatible box. +type Provider interface { + // Process is called to process an artifact into a Vagrant box. The + // artifact is given as well as the temporary directory path to + // put things. + // + // The Provider should return the contents for the Vagrantfile, + // any metadata (including the provider type in that), and an error + // if any. + Process(packer.Ui, packer.Artifact, string) (vagrantfile string, metadata map[string]interface{}, err error) +} diff --git a/post-processor/vagrant/util.go b/post-processor/vagrant/util.go index e8be84e43..d6a1a750a 100644 --- a/post-processor/vagrant/util.go +++ b/post-processor/vagrant/util.go @@ -13,14 +13,6 @@ import ( "path/filepath" ) -// OutputPathTemplate is the structure that is availalable within the -// OutputPath variables. -type OutputPathTemplate struct { - ArtifactId string - BuildName string - Provider string -} - // Copies a file by copying the contents of the file to another place. func CopyContents(dst, src string) error { srcF, err := os.Open(src) diff --git a/post-processor/vagrant/virtualbox.go b/post-processor/vagrant/virtualbox.go index 2711c9159..eeaf401c6 100644 --- a/post-processor/vagrant/virtualbox.go +++ b/post-processor/vagrant/virtualbox.go @@ -2,10 +2,8 @@ package vagrant import ( "archive/tar" - "compress/flate" "errors" "fmt" - "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" "io" "io/ioutil" @@ -15,183 +13,49 @@ import ( "regexp" ) -type VBoxBoxConfig struct { - common.PackerConfig `mapstructure:",squash"` +type VBoxProvider struct{} - Include []string `mapstructure:"include"` - OutputPath string `mapstructure:"output"` - VagrantfileTemplate string `mapstructure:"vagrantfile_template"` - CompressionLevel int `mapstructure:"compression_level"` - - tpl *packer.ConfigTemplate -} - -type VBoxVagrantfileTemplate struct { - BaseMacAddress string -} - -type VBoxBoxPostProcessor struct { - config VBoxBoxConfig -} - -func (p *VBoxBoxPostProcessor) Configure(raws ...interface{}) error { - md, err := common.DecodeConfig(&p.config, raws...) - if err != nil { - return err - } - - p.config.tpl, err = packer.NewConfigTemplate() - if err != nil { - return err - } - p.config.tpl.UserVars = p.config.PackerUserVars - - // Defaults - found := false - for _, k := range md.Keys { - println(k) - if k == "compression_level" { - found = true - break - } - } - - if !found { - p.config.CompressionLevel = flate.DefaultCompression - } - - // Accumulate any errors - errs := common.CheckUnusedConfig(md) - - validates := map[string]*string{ - "output": &p.config.OutputPath, - "vagrantfile_template": &p.config.VagrantfileTemplate, - } - - for n, ptr := range validates { - if err := p.config.tpl.Validate(*ptr); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Error parsing %s: %s", n, err)) - } - } - - if errs != nil && len(errs.Errors) > 0 { - return errs - } - - return nil -} - -func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { - var err error - - // Compile the output path - outputPath, err := p.config.tpl.Process(p.config.OutputPath, &OutputPathTemplate{ - ArtifactId: artifact.Id(), - BuildName: p.config.PackerBuildName, - Provider: "virtualbox", - }) - if err != nil { - return nil, false, err - } - - // Create a temporary directory for us to build the contents of the box in - dir, err := ioutil.TempDir("", "packer") - if err != nil { - return nil, false, err - } - defer os.RemoveAll(dir) - - // Copy all of the includes files into the temporary directory - for _, src := range p.config.Include { - ui.Message(fmt.Sprintf("Copying from include: %s", src)) - dst := filepath.Join(dir, filepath.Base(src)) - if err := CopyContents(dst, src); err != nil { - err = fmt.Errorf("Error copying include file: %s\n\n%s", src, err) - return nil, false, err - } - } +func (p *VBoxProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) { + // Create the metadata + metadata = map[string]interface{}{"provider": "virtualbox"} // Copy all of the original contents into the temporary directory for _, path := range artifact.Files() { - // We treat OVA files specially, we unpack those into the temporary // directory so we can get the resulting disk and OVF. if extension := filepath.Ext(path); extension == ".ova" { ui.Message(fmt.Sprintf("Unpacking OVA: %s", path)) - if err := DecompressOva(dir, path); err != nil { - return nil, false, err + if err = DecompressOva(dir, path); err != nil { + return } } else { ui.Message(fmt.Sprintf("Copying from artifact: %s", path)) dstPath := filepath.Join(dir, filepath.Base(path)) - if err := CopyContents(dstPath, path); err != nil { - return nil, false, err + if err = CopyContents(dstPath, path); err != nil { + return } } } - // Create the Vagrantfile from the template - tplData := &VBoxVagrantfileTemplate{} - tplData.BaseMacAddress, err = p.findBaseMacAddress(dir) - if err != nil { - return nil, false, err - } - - vf, err := os.Create(filepath.Join(dir, "Vagrantfile")) - if err != nil { - return nil, false, err - } - defer vf.Close() - - vagrantfileContents := defaultVBoxVagrantfile - if p.config.VagrantfileTemplate != "" { - ui.Message(fmt.Sprintf( - "Using Vagrantfile template: %s", p.config.VagrantfileTemplate)) - f, err := os.Open(p.config.VagrantfileTemplate) - if err != nil { - return nil, false, err - } - defer f.Close() - - contents, err := ioutil.ReadAll(f) - if err != nil { - return nil, false, err - } - - vagrantfileContents = string(contents) - } - - vagrantfileContents, err = p.config.tpl.Process(vagrantfileContents, tplData) - if err != nil { - return nil, false, fmt.Errorf("Error writing Vagrantfile: %s", err) - } - vf.Write([]byte(vagrantfileContents)) - vf.Close() - - // Create the metadata - metadata := map[string]string{"provider": "virtualbox"} - if err := WriteMetadata(dir, metadata); err != nil { - return nil, false, err - } - // Rename the OVF file to box.ovf, as required by Vagrant ui.Message("Renaming the OVF to box.ovf...") - if err := p.renameOVF(dir); err != nil { - return nil, false, err + if err = p.renameOVF(dir); err != nil { + return } - // Compress the directory to the given output path - ui.Message(fmt.Sprintf("Compressing box...")) - if err := DirToBox(outputPath, dir, ui, p.config.CompressionLevel); err != nil { - return nil, false, err + // Create the Vagrantfile from the template + var baseMacAddress string + baseMacAddress, err = p.findBaseMacAddress(dir) + if err != nil { + return } - return NewArtifact("virtualbox", outputPath), false, nil + vagrantfile = fmt.Sprintf(vboxVagrantfile, baseMacAddress) + return } -func (p *VBoxBoxPostProcessor) findOvf(dir string) (string, error) { +func (p *VBoxProvider) findOvf(dir string) (string, error) { log.Println("Looking for OVF in artifact...") file_matches, err := filepath.Glob(filepath.Join(dir, "*.ovf")) if err != nil { @@ -209,7 +73,7 @@ func (p *VBoxBoxPostProcessor) findOvf(dir string) (string, error) { return file_matches[0], err } -func (p *VBoxBoxPostProcessor) renameOVF(dir string) error { +func (p *VBoxProvider) renameOVF(dir string) error { log.Println("Looking for OVF to rename...") ovf, err := p.findOvf(dir) if err != nil { @@ -220,7 +84,7 @@ func (p *VBoxBoxPostProcessor) renameOVF(dir string) error { return os.Rename(ovf, filepath.Join(dir, "box.ovf")) } -func (p *VBoxBoxPostProcessor) findBaseMacAddress(dir string) (string, error) { +func (p *VBoxProvider) findBaseMacAddress(dir string) (string, error) { log.Println("Looking for OVF for base mac address...") ovf, err := p.findOvf(dir) if err != nil { @@ -295,8 +159,8 @@ func DecompressOva(dir, src string) error { return nil } -var defaultVBoxVagrantfile = ` +var vboxVagrantfile = ` Vagrant.configure("2") do |config| -config.vm.base_mac = "{{ .BaseMacAddress }}" + config.vm.base_mac = "%s" end ` diff --git a/post-processor/vagrant/virtualbox_test.go b/post-processor/vagrant/virtualbox_test.go index eb525c29b..c3815ce77 100644 --- a/post-processor/vagrant/virtualbox_test.go +++ b/post-processor/vagrant/virtualbox_test.go @@ -1,14 +1,9 @@ package vagrant import ( - "github.com/mitchellh/packer/packer" "testing" ) -func TestVBoxBoxPostProcessor_ImplementsPostProcessor(t *testing.T) { - var raw interface{} - raw = &VBoxBoxPostProcessor{} - if _, ok := raw.(packer.PostProcessor); !ok { - t.Fatalf("VBox PostProcessor should be a PostProcessor") - } +func TestVBoxProvider_impl(t *testing.T) { + var _ Provider = new(VBoxProvider) } diff --git a/post-processor/vagrant/vmware.go b/post-processor/vagrant/vmware.go index b12a2d2f6..d3ff50d24 100644 --- a/post-processor/vagrant/vmware.go +++ b/post-processor/vagrant/vmware.go @@ -1,139 +1,26 @@ package vagrant import ( - "compress/flate" "fmt" - "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" - "io/ioutil" - "os" "path/filepath" - "strconv" ) -type VMwareBoxConfig struct { - common.PackerConfig `mapstructure:",squash"` +type VMwareProvider struct{} - OutputPath string `mapstructure:"output"` - VagrantfileTemplate string `mapstructure:"vagrantfile_template"` - CompressionLevel string `mapstructure:"compression_level"` - - tpl *packer.ConfigTemplate -} - -type VMwareBoxPostProcessor struct { - config VMwareBoxConfig -} - -func (p *VMwareBoxPostProcessor) Configure(raws ...interface{}) error { - md, err := common.DecodeConfig(&p.config, raws...) - if err != nil { - return err - } - - p.config.tpl, err = packer.NewConfigTemplate() - if err != nil { - return err - } - p.config.tpl.UserVars = p.config.PackerUserVars - - // Accumulate any errors - errs := common.CheckUnusedConfig(md) - - validates := map[string]*string{ - "output": &p.config.OutputPath, - "vagrantfile_template": &p.config.VagrantfileTemplate, - "compression_level": &p.config.CompressionLevel, - } - - for n, ptr := range validates { - if err := p.config.tpl.Validate(*ptr); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Error parsing %s: %s", n, err)) - } - } - - if errs != nil && len(errs.Errors) > 0 { - return errs - } - - return nil -} - -func (p *VMwareBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { - // Compile the output path - outputPath, err := p.config.tpl.Process(p.config.OutputPath, &OutputPathTemplate{ - ArtifactId: artifact.Id(), - BuildName: p.config.PackerBuildName, - Provider: "vmware", - }) - if err != nil { - return nil, false, err - } - - // Create a temporary directory for us to build the contents of the box in - dir, err := ioutil.TempDir("", "packer") - if err != nil { - return nil, false, err - } - defer os.RemoveAll(dir) +func (p *VMwareProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) { + // Create the metadata + metadata = map[string]interface{}{"provider": "vmware_desktop"} // Copy all of the original contents into the temporary directory for _, path := range artifact.Files() { ui.Message(fmt.Sprintf("Copying: %s", path)) dstPath := filepath.Join(dir, filepath.Base(path)) - if err := CopyContents(dstPath, path); err != nil { - return nil, false, err - } - } - - if p.config.VagrantfileTemplate != "" { - f, err := os.Open(p.config.VagrantfileTemplate) - if err != nil { - return nil, false, err - } - defer f.Close() - - contents, err := ioutil.ReadAll(f) - if err != nil { - return nil, false, err - } - - // Create the Vagrantfile from the template - vf, err := os.Create(filepath.Join(dir, "Vagrantfile")) - if err != nil { - return nil, false, err + if err = CopyContents(dstPath, path); err != nil { + return } - defer vf.Close() - - vagrantfileContents, err := p.config.tpl.Process(string(contents), nil) - if err != nil { - return nil, false, fmt.Errorf("Error writing Vagrantfile: %s", err) - } - vf.Write([]byte(vagrantfileContents)) - vf.Close() - } - - var level int = flate.DefaultCompression - if p.config.CompressionLevel != "" { - level, err = strconv.Atoi(p.config.CompressionLevel) - if err != nil { - return nil, false, err - } - } - - // Create the metadata - metadata := map[string]string{"provider": "vmware_desktop"} - if err := WriteMetadata(dir, metadata); err != nil { - return nil, false, err - } - - // Compress the directory to the given output path - ui.Message(fmt.Sprintf("Compressing box...")) - if err := DirToBox(outputPath, dir, ui, level); err != nil { - return nil, false, err } - return NewArtifact("vmware", outputPath), false, nil + return } diff --git a/post-processor/vagrant/vmware_test.go b/post-processor/vagrant/vmware_test.go index ca3cbe7cd..e47dd3080 100644 --- a/post-processor/vagrant/vmware_test.go +++ b/post-processor/vagrant/vmware_test.go @@ -1,14 +1,9 @@ package vagrant import ( - "github.com/mitchellh/packer/packer" "testing" ) -func TestVMwareBoxPostProcessor_ImplementsPostProcessor(t *testing.T) { - var raw interface{} - raw = &VMwareBoxPostProcessor{} - if _, ok := raw.(packer.PostProcessor); !ok { - t.Fatalf("VMware PostProcessor should be a PostProcessor") - } +func TestVMwareProvider_impl(t *testing.T) { + var _ Provider = new(VMwareProvider) } diff --git a/website/source/docs/post-processors/vagrant.html.markdown b/website/source/docs/post-processors/vagrant.html.markdown index b91b16970..d83a2b265 100644 --- a/website/source/docs/post-processors/vagrant.html.markdown +++ b/website/source/docs/post-processors/vagrant.html.markdown @@ -18,7 +18,7 @@ documentation on [using post-processors](/docs/templates/post-processors.html) in templates. This knowledge will be expected for the remainder of this document. -Because Vagrant boxes are [provider-specific](#), +Because Vagrant boxes are [provider-specific](http://docs.vagrantup.com/v2/boxes/format.html), the Vagrant post-processor is hardcoded to understand how to convert the artifacts of certain builders into proper boxes for their respective providers. @@ -27,6 +27,7 @@ Currently, the Vagrant post-processor can create boxes for the following providers. * AWS +* DigitalOcean * VirtualBox * VMware @@ -47,82 +48,52 @@ However, if you want to configure things a bit more, the post-processor does expose some configuration options. The available options are listed below, with more details about certain options in following sections. +* `compression_level` (integer) - An integer repesenting the + compression level to use when creating the Vagrant box. Valid + values range from 0 to 9, with 0 being no compression and 9 being + the best compression. By default, compression is enabled at level 1. + +* `include` (array of strings) - Paths to files to include in the + Vagrant box. These files will each be copied into the top level directory + of the Vagrant box (regardless of their paths). They can then be used + from the Vagrantfile. + * `output` (string) - The full path to the box file that will be created by this post-processor. This is a [configuration template](/docs/templates/configuration-templates.html). The variable `Provider` is replaced by the Vagrant provider the box is for. The variable `ArtifactId` is replaced by the ID of the input artifact. + The variable `BuildName` is replaced with the name of the build. By default, the value of this config is `packer_{{.BuildName}}_{{.Provider}}.box`. -* `aws`, `virtualbox`, or `vmware` (objects) - These are used to configure - the specific options for certain providers. A reference of available - configuration parameters for each is in the section below. - -### AWS Provider - -The AWS provider itself can be configured with specific options: - * `vagrantfile_template` (string) - Path to a template to use for the - Vagrantfile that is packaged with the box. The contents of the file must be a valid Go - [text template](http://golang.org/pkg/text/template). By default - this is a template that simply sets the AMIs for the various regions - of the AWS build. - -* `compression_level` (integer) - An integer repesenting the - compression level to use when creating the Vagrant box. Valid - values range from 0 to 9, with 0 being no compression and 9 being - the best compression. - -The `vagrantfile_template` has the `Images` variable which is a map -of region (string) to AMI ID (string). An example Vagrantfile template for -AWS is shown below. The example simply sets the AMI for each region. - -``` -Vagrant.configure("2") do |config| - config.vm.provider "aws" do |aws| - {{ range $region, $ami := .Images }} - aws.region_config "{{ $region }}", ami: "{{ $ami }}" - {{ end }} - end -end -``` + Vagrantfile that is packaged with the box. -### VirtualBox Provider +## Provider-Specific Overrides -The VirtualBox provider itself can be configured with specific options: - -* `vagrantfile_template` (string) - Path to a template to use for the - Vagrantfile that is packaged with the box. The contents of the file must be a valid Go - [text template](http://golang.org/pkg/text/template). By default this is - a template that just sets the base MAC address so that networking works. +If you have a Packer template with multiple builder types within it, +you may want to configure the box creation for each type a little differently. +For example, the contents of the Vagrantfile for a Vagrant box for AWS might +be different from the contents of the Vagrantfile you want for VMware. +The post-processor lets you do this. -* `compression_level` (integer) - An integer repesenting the - compression level to use when creating the Vagrant box. Valid - values range from 0 to 9, with 0 being no compression and 9 being - the best compression. +Specify overrides within the `override` configuration by provider name: -The `vagrantfile_template` has the `BaseMACAddress` variable which is a string -containing the MAC address of the first network interface. This must be set -in the Vagrantfile for networking to work properly with Vagrant. An example -Vagrantfile template is shown below: +```json +{ + "type": "vagrant", + "compression_level": 1, + "override": { + "vmware": { + "compression_level": 0 + } + } +} ``` -Vagrant.configure("2") do |config| - config.vm.base_mac = "{{ .BaseMacAddress }}" -end -``` - -### VMware Provider -The VMware provider itself can be configured with specific options: - -* `vagrantfile_template` (string) - Path to a template to use for the - Vagrantfile that is packaged with the box. The contents of the file must be a valid Go - [text template](http://golang.org/pkg/text/template). By default no - Vagrantfile is packaged with the box. Note that currently no variables - are available in the template, but this may change in the future. +In the example above, the compression level will be set to 1 except for +VMware, where it will be set to 0. -* `compression_level` (integer) - An integer repesenting the - compression level to use when creating the Vagrant box. Valid - values range from 0 to 9, with 0 being no compression and 9 being - the best compression. +The available provider names are: `aws`, `digitalocean`, `virtualbox`, +and `vmware`.