Commit 7ff549ec authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

Merge pull request #733 from mitchellh/f-vagrant-pp-revamp

Vagrant Post-Processor Refactor + "Include" feature
parents 930b844b 99cbe1fc
...@@ -49,15 +49,8 @@ func (c Command) Run(env packer.Environment, args []string) int { ...@@ -49,15 +49,8 @@ func (c Command) Run(env packer.Environment, args []string) int {
// Close the file since we're done with that // Close the file since we're done with that
tplF.Close() tplF.Close()
// Run the template through the various fixers
fixers := []string{
"iso-md5",
"createtime",
"virtualbox-gaattach",
}
input := templateData input := templateData
for _, name := range fixers { for _, name := range FixerOrder {
var err error var err error
fixer, ok := Fixers[name] fixer, ok := Fixers[name]
if !ok { if !ok {
......
...@@ -15,10 +15,21 @@ type Fixer interface { ...@@ -15,10 +15,21 @@ type Fixer interface {
// Fixers is the map of all available fixers, by name. // Fixers is the map of all available fixers, by name.
var Fixers map[string]Fixer var Fixers map[string]Fixer
// FixerOrder is the default order the fixers should be run.
var FixerOrder []string
func init() { func init() {
Fixers = map[string]Fixer{ Fixers = map[string]Fixer{
"iso-md5": new(FixerISOMD5), "iso-md5": new(FixerISOMD5),
"createtime": new(FixerCreateTime), "createtime": new(FixerCreateTime),
"pp-vagrant-override": new(FixerVagrantPPOverride),
"virtualbox-gaattach": new(FixerVirtualBoxGAAttach), "virtualbox-gaattach": new(FixerVirtualBoxGAAttach),
} }
FixerOrder = []string{
"iso-md5",
"createtime",
"virtualbox-gaattach",
"pp-vagrant-override",
}
} }
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`
}
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)
}
}
...@@ -11,6 +11,11 @@ Usage: packer fix [options] TEMPLATE ...@@ -11,6 +11,11 @@ Usage: packer fix [options] TEMPLATE
Fixes that are run: 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.
` `
...@@ -2,16 +2,26 @@ package packer ...@@ -2,16 +2,26 @@ package packer
// MockArtifact is an implementation of Artifact that can be used for tests. // MockArtifact is an implementation of Artifact that can be used for tests.
type MockArtifact struct { type MockArtifact struct {
IdValue string BuilderIdValue string
DestroyCalled bool FilesValue []string
IdValue string
DestroyCalled bool
} }
func (*MockArtifact) BuilderId() string { func (a *MockArtifact) BuilderId() string {
return "bid" if a.BuilderIdValue == "" {
return "bid"
}
return a.BuilderIdValue
} }
func (*MockArtifact) Files() []string { func (a *MockArtifact) Files() []string {
return []string{"a", "b"} if a.FilesValue == nil {
return []string{"a", "b"}
}
return a.FilesValue
} }
func (a *MockArtifact) Id() string { func (a *MockArtifact) Id() string {
......
package vagrant package vagrant
import ( import (
"compress/flate" "bytes"
"fmt" "fmt"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings" "strings"
) "text/template"
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,
}
for n, ptr := range validates { "github.com/mitchellh/packer/packer"
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 { type AWSProvider struct{}
return errs
}
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) { // Build up the template data to build our Vagrantfile
// Determine the regions... tplData := &awsVagrantfileTemplate{
tplData := &AWSVagrantfileTemplate{
Images: make(map[string]string), Images: make(map[string]string),
} }
for _, regions := range strings.Split(artifact.Id(), ",") { for _, regions := range strings.Split(artifact.Id(), ",") {
parts := strings.Split(regions, ":") parts := strings.Split(regions, ":")
if len(parts) != 2 { 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] tplData.Images[parts[0]] = parts[1]
} }
// Compile the output path // Build up the contents
outputPath, err := p.config.tpl.Process(p.config.OutputPath, &OutputPathTemplate{ var contents bytes.Buffer
ArtifactId: artifact.Id(), t := template.Must(template.New("vf").Parse(defaultAWSVagrantfile))
BuildName: p.config.PackerBuildName, err = t.Execute(&contents, tplData)
Provider: "aws", vagrantfile = contents.String()
}) return
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
}
return NewArtifact("aws", outputPath), true, nil type awsVagrantfileTemplate struct {
Images map[string]string
} }
var defaultAWSVagrantfile = ` var defaultAWSVagrantfile = `
......
package vagrant package vagrant
import ( import (
"github.com/mitchellh/packer/packer"
"testing" "testing"
) )
func TestAWSBoxPostProcessor_ImplementsPostProcessor(t *testing.T) { func TestAWSProvider_impl(t *testing.T) {
var raw interface{} var _ Provider = new(AWSProvider)
raw = &AWSBoxPostProcessor{}
if _, ok := raw.(packer.PostProcessor); !ok {
t.Fatalf("AWS PostProcessor should be a PostProcessor")
}
} }
package vagrant package vagrant
import ( import (
"compress/flate" "bytes"
"fmt" "fmt"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings" "strings"
"text/template"
) )
type DigitalOceanBoxConfig struct { type digitalOceanVagrantfileTemplate 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 {
Image string "" Image string ""
Region string "" Region string ""
} }
type DigitalOceanBoxPostProcessor struct { type DigitalOceanProvider 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)
validates := map[string]*string{ func (p *DigitalOceanProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) {
"output": &p.config.OutputPath, // Create the metadata
"vagrantfile_template": &p.config.VagrantfileTemplate, metadata = map[string]interface{}{"provider": "digital_ocean"}
"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 *DigitalOceanBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
// Determine the image and region... // Determine the image and region...
tplData := &DigitalOceanVagrantfileTemplate{} tplData := &digitalOceanVagrantfileTemplate{}
parts := strings.Split(artifact.Id(), ":") parts := strings.Split(artifact.Id(), ":")
if len(parts) != 2 { 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.Region = parts[0]
tplData.Image = parts[1] tplData.Image = parts[1]
// Compile the output path // Build up the Vagrantfile
outputPath, err := p.config.tpl.Process(p.config.OutputPath, &OutputPathTemplate{ var contents bytes.Buffer
ArtifactId: artifact.Id(), t := template.Must(template.New("vf").Parse(defaultDigitalOceanVagrantfile))
BuildName: p.config.PackerBuildName, err = t.Execute(&contents, tplData)
Provider: "digitalocean", vagrantfile = contents.String()
}) return
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
} }
var defaultDigitalOceanVagrantfile = ` var defaultDigitalOceanVagrantfile = `
...@@ -159,5 +45,4 @@ Vagrant.configure("2") do |config| ...@@ -159,5 +45,4 @@ Vagrant.configure("2") do |config|
digital_ocean.region = "{{ .Region }}" digital_ocean.region = "{{ .Region }}"
end end
end end
` `
package vagrant package vagrant
import ( import (
"github.com/mitchellh/packer/packer"
"testing" "testing"
) )
func TestDigitalOceanBoxPostProcessor_ImplementsPostProcessor(t *testing.T) { func TestDigitalOceanProvider_impl(t *testing.T) {
var raw interface{} var _ Provider = new(DigitalOceanProvider)
raw = &DigitalOceanBoxPostProcessor{}
if _, ok := raw.(packer.PostProcessor); !ok {
t.Fatalf("Digitalocean PostProcessor should be a PostProcessor")
}
} }
...@@ -4,11 +4,15 @@ ...@@ -4,11 +4,15 @@
package vagrant package vagrant
import ( import (
"compress/flate"
"fmt" "fmt"
"github.com/mitchellh/mapstructure" "io/ioutil"
"os"
"path/filepath"
"text/template"
"github.com/mitchellh/packer/common" "github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log"
) )
var builtins = map[string]string{ var builtins = map[string]string{
...@@ -22,133 +26,213 @@ var builtins = map[string]string{ ...@@ -22,133 +26,213 @@ var builtins = map[string]string{
type Config struct { type Config struct {
common.PackerConfig `mapstructure:",squash"` 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 { type PostProcessor struct {
config Config configs map[string]*Config
premade map[string]packer.PostProcessor
extraConfig map[string]interface{}
} }
func (p *PostProcessor) Configure(raws ...interface{}) error { func (p *PostProcessor) Configure(raws ...interface{}) error {
_, err := common.DecodeConfig(&p.config, raws...) p.configs = make(map[string]*Config)
if err != nil { p.configs[""] = new(Config)
if err := p.configureSingle(p.configs[""], raws...); err != nil {
return err 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 { if err != nil {
return err return nil, false, err
} }
tpl.UserVars = p.config.PackerUserVars
// Defaults // Create a temporary directory for us to build the contents of the box in
if p.config.OutputPath == "" { dir, err := ioutil.TempDir("", "packer")
p.config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box" 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 // Run the provider processing step
errs := new(packer.MultiError) vagrantfile, metadata, err := provider.Process(ui, artifact, dir)
if err := tpl.Validate(p.config.OutputPath); err != nil { if err != nil {
errs = packer.MultiErrorAppend( return nil, false, err
errs, fmt.Errorf("Error parsing output template: %s", err)) }
}
// Write the metadata we got
// Store extra configuration we'll send to each post-processor type if err := WriteMetadata(dir, metadata); err != nil {
p.extraConfig = make(map[string]interface{}) return nil, false, err
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
} }
p.premade = make(map[string]packer.PostProcessor) // Write our Vagrantfile
for k, raw := range mapConfig { var customVagrantfile string
pp, err := p.subPostProcessor(k, raw, p.extraConfig) if config.VagrantfileTemplate != "" {
ui.Message(fmt.Sprintf(
"Using custom Vagrantfile: %s", config.VagrantfileTemplate))
customBytes, err := ioutil.ReadFile(config.VagrantfileTemplate)
if err != nil { if err != nil {
errs = packer.MultiErrorAppend(errs, err) return nil, false, err
continue
} }
if pp == nil { customVagrantfile = string(customBytes)
continue }
}
p.premade[k] = pp f, err := os.Create(filepath.Join(dir, "Vagrantfile"))
if err != nil {
return nil, false, err
} }
if len(errs.Errors) > 0 { t := template.Must(template.New("root").Parse(boxVagrantfileContents))
return errs 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) { func (p *PostProcessor) configureSingle(config *Config, raws ...interface{}) error {
ppName, ok := builtins[artifact.BuilderId()] md, err := common.DecodeConfig(config, raws...)
if !ok { if err != nil {
return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId()) return err
} }
// Use the premade PostProcessor if we have one. Otherwise, we config.tpl, err = packer.NewConfigTemplate()
// create it and configure it here. if err != nil {
pp, ok := p.premade[ppName] return err
if !ok { }
log.Printf("Premade post-processor for '%s' not found. Creating.", ppName) config.tpl.UserVars = config.PackerUserVars
var err error // Defaults
pp, err = p.subPostProcessor(ppName, nil, p.extraConfig) if config.OutputPath == "" {
if err != nil { config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box"
return nil, false, err }
}
if pp == nil { found := false
return nil, false, fmt.Errorf("Vagrant box post-processor not found: %s", ppName) for _, k := range md.Keys {
if k == "compression_level" {
found = true
break
} }
} }
ui.Say(fmt.Sprintf("Creating Vagrant box for '%s' provider", ppName)) if !found {
return pp.PostProcess(ui, artifact) 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) { for n, ptr := range validates {
pp := keyToPostProcessor(key) if err := config.tpl.Validate(*ptr); err != nil {
if pp == nil { errs = packer.MultiErrorAppend(
return nil, nil errs, fmt.Errorf("Error parsing %s: %s", n, err))
}
} }
if err := pp.Configure(extra, specific); err != nil { if errs != nil && len(errs.Errors) > 0 {
return nil, err return errs
} }
return pp, nil return nil
} }
// keyToPostProcessor maps a configuration key to the actual post-processor func providerForName(name string) Provider {
// it will be configuring. This returns a new instance of that post-processor. switch name {
func keyToPostProcessor(key string) packer.PostProcessor {
switch key {
case "aws":
return new(AWSBoxPostProcessor)
case "digitalocean":
return new(DigitalOceanBoxPostProcessor)
case "virtualbox": case "virtualbox":
return new(VBoxBoxPostProcessor) return new(VBoxProvider)
case "vmware":
return new(VMwareBoxPostProcessor)
default: default:
return nil 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 }}
`
package vagrant package vagrant
import ( import (
"bytes"
"compress/flate"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"strings"
"testing" "testing"
) )
...@@ -9,15 +12,55 @@ func testConfig() map[string]interface{} { ...@@ -9,15 +12,55 @@ func testConfig() map[string]interface{} {
return 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) { func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
var raw interface{} var _ packer.PostProcessor = new(PostProcessor)
raw = &PostProcessor{} }
if _, ok := raw.(packer.PostProcessor); !ok {
t.Fatalf("AWS PostProcessor should be a 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 var p PostProcessor
// Default // Default
...@@ -36,14 +79,57 @@ func TestBuilderPrepare_OutputPath(t *testing.T) { ...@@ -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 var p PostProcessor
// Default // Default
c := testConfig() 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) err := p.Configure(c)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) 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")
}
} }
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)
}
...@@ -13,14 +13,6 @@ import ( ...@@ -13,14 +13,6 @@ import (
"path/filepath" "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. // Copies a file by copying the contents of the file to another place.
func CopyContents(dst, src string) error { func CopyContents(dst, src string) error {
srcF, err := os.Open(src) srcF, err := os.Open(src)
......
...@@ -2,10 +2,8 @@ package vagrant ...@@ -2,10 +2,8 @@ package vagrant
import ( import (
"archive/tar" "archive/tar"
"compress/flate"
"errors" "errors"
"fmt" "fmt"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"io" "io"
"io/ioutil" "io/ioutil"
...@@ -15,183 +13,49 @@ import ( ...@@ -15,183 +13,49 @@ import (
"regexp" "regexp"
) )
type VBoxBoxConfig struct { type VBoxProvider struct{}
common.PackerConfig `mapstructure:",squash"`
Include []string `mapstructure:"include"` func (p *VBoxProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) {
OutputPath string `mapstructure:"output"` // Create the metadata
VagrantfileTemplate string `mapstructure:"vagrantfile_template"` metadata = map[string]interface{}{"provider": "virtualbox"}
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
}
}
// Copy all of the original contents into the temporary directory // Copy all of the original contents into the temporary directory
for _, path := range artifact.Files() { for _, path := range artifact.Files() {
// We treat OVA files specially, we unpack those into the temporary // We treat OVA files specially, we unpack those into the temporary
// directory so we can get the resulting disk and OVF. // directory so we can get the resulting disk and OVF.
if extension := filepath.Ext(path); extension == ".ova" { if extension := filepath.Ext(path); extension == ".ova" {
ui.Message(fmt.Sprintf("Unpacking OVA: %s", path)) ui.Message(fmt.Sprintf("Unpacking OVA: %s", path))
if err := DecompressOva(dir, path); err != nil { if err = DecompressOva(dir, path); err != nil {
return nil, false, err return
} }
} else { } else {
ui.Message(fmt.Sprintf("Copying from artifact: %s", path)) ui.Message(fmt.Sprintf("Copying from artifact: %s", path))
dstPath := filepath.Join(dir, filepath.Base(path)) dstPath := filepath.Join(dir, filepath.Base(path))
if err := CopyContents(dstPath, path); err != nil { if err = CopyContents(dstPath, path); err != nil {
return nil, false, err 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 // Rename the OVF file to box.ovf, as required by Vagrant
ui.Message("Renaming the OVF to box.ovf...") ui.Message("Renaming the OVF to box.ovf...")
if err := p.renameOVF(dir); err != nil { if err = p.renameOVF(dir); err != nil {
return nil, false, err return
} }
// Compress the directory to the given output path // Create the Vagrantfile from the template
ui.Message(fmt.Sprintf("Compressing box...")) var baseMacAddress string
if err := DirToBox(outputPath, dir, ui, p.config.CompressionLevel); err != nil { baseMacAddress, err = p.findBaseMacAddress(dir)
return nil, false, err 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...") log.Println("Looking for OVF in artifact...")
file_matches, err := filepath.Glob(filepath.Join(dir, "*.ovf")) file_matches, err := filepath.Glob(filepath.Join(dir, "*.ovf"))
if err != nil { if err != nil {
...@@ -209,7 +73,7 @@ func (p *VBoxBoxPostProcessor) findOvf(dir string) (string, error) { ...@@ -209,7 +73,7 @@ func (p *VBoxBoxPostProcessor) findOvf(dir string) (string, error) {
return file_matches[0], err 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...") log.Println("Looking for OVF to rename...")
ovf, err := p.findOvf(dir) ovf, err := p.findOvf(dir)
if err != nil { if err != nil {
...@@ -220,7 +84,7 @@ func (p *VBoxBoxPostProcessor) renameOVF(dir string) error { ...@@ -220,7 +84,7 @@ func (p *VBoxBoxPostProcessor) renameOVF(dir string) error {
return os.Rename(ovf, filepath.Join(dir, "box.ovf")) 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...") log.Println("Looking for OVF for base mac address...")
ovf, err := p.findOvf(dir) ovf, err := p.findOvf(dir)
if err != nil { if err != nil {
...@@ -295,8 +159,8 @@ func DecompressOva(dir, src string) error { ...@@ -295,8 +159,8 @@ func DecompressOva(dir, src string) error {
return nil return nil
} }
var defaultVBoxVagrantfile = ` var vboxVagrantfile = `
Vagrant.configure("2") do |config| Vagrant.configure("2") do |config|
config.vm.base_mac = "{{ .BaseMacAddress }}" config.vm.base_mac = "%s"
end end
` `
package vagrant package vagrant
import ( import (
"github.com/mitchellh/packer/packer"
"testing" "testing"
) )
func TestVBoxBoxPostProcessor_ImplementsPostProcessor(t *testing.T) { func TestVBoxProvider_impl(t *testing.T) {
var raw interface{} var _ Provider = new(VBoxProvider)
raw = &VBoxBoxPostProcessor{}
if _, ok := raw.(packer.PostProcessor); !ok {
t.Fatalf("VBox PostProcessor should be a PostProcessor")
}
} }
package vagrant package vagrant
import ( import (
"compress/flate"
"fmt" "fmt"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"io/ioutil"
"os"
"path/filepath" "path/filepath"
"strconv"
) )
type VMwareBoxConfig struct { type VMwareProvider struct{}
common.PackerConfig `mapstructure:",squash"`
OutputPath string `mapstructure:"output"` func (p *VMwareProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) {
VagrantfileTemplate string `mapstructure:"vagrantfile_template"` // Create the metadata
CompressionLevel string `mapstructure:"compression_level"` metadata = map[string]interface{}{"provider": "vmware_desktop"}
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)
// Copy all of the original contents into the temporary directory // Copy all of the original contents into the temporary directory
for _, path := range artifact.Files() { for _, path := range artifact.Files() {
ui.Message(fmt.Sprintf("Copying: %s", path)) ui.Message(fmt.Sprintf("Copying: %s", path))
dstPath := filepath.Join(dir, filepath.Base(path)) dstPath := filepath.Join(dir, filepath.Base(path))
if err := CopyContents(dstPath, path); err != nil { if err = CopyContents(dstPath, path); err != nil {
return nil, false, err return
}
}
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
} }
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
} }
package vagrant package vagrant
import ( import (
"github.com/mitchellh/packer/packer"
"testing" "testing"
) )
func TestVMwareBoxPostProcessor_ImplementsPostProcessor(t *testing.T) { func TestVMwareProvider_impl(t *testing.T) {
var raw interface{} var _ Provider = new(VMwareProvider)
raw = &VMwareBoxPostProcessor{}
if _, ok := raw.(packer.PostProcessor); !ok {
t.Fatalf("VMware PostProcessor should be a PostProcessor")
}
} }
...@@ -18,7 +18,7 @@ documentation on [using post-processors](/docs/templates/post-processors.html) ...@@ -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 in templates. This knowledge will be expected for the remainder of
this document. 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 Vagrant post-processor is hardcoded to understand how to convert
the artifacts of certain builders into proper boxes for their the artifacts of certain builders into proper boxes for their
respective providers. respective providers.
...@@ -27,6 +27,7 @@ Currently, the Vagrant post-processor can create boxes for the following ...@@ -27,6 +27,7 @@ Currently, the Vagrant post-processor can create boxes for the following
providers. providers.
* AWS * AWS
* DigitalOcean
* VirtualBox * VirtualBox
* VMware * VMware
...@@ -47,82 +48,52 @@ However, if you want to configure things a bit more, the post-processor ...@@ -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 does expose some configuration options. The available options are listed
below, with more details about certain options in following sections. 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 * `output` (string) - The full path to the box file that will be created
by this post-processor. This is a by this post-processor. This is a
[configuration template](/docs/templates/configuration-templates.html). [configuration template](/docs/templates/configuration-templates.html).
The variable `Provider` is replaced by the Vagrant provider the box is for. 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 `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`. 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_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 Vagrantfile that is packaged with the box.
[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
```
### VirtualBox Provider ## Provider-Specific Overrides
The VirtualBox provider itself can be configured with specific options: 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.
* `vagrantfile_template` (string) - Path to a template to use for the For example, the contents of the Vagrantfile for a Vagrant box for AWS might
Vagrantfile that is packaged with the box. The contents of the file must be a valid Go be different from the contents of the Vagrantfile you want for VMware.
[text template](http://golang.org/pkg/text/template). By default this is The post-processor lets you do this.
a template that just sets the base MAC address so that networking works.
* `compression_level` (integer) - An integer repesenting the Specify overrides within the `override` configuration by provider name:
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 `BaseMACAddress` variable which is a string ```json
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 "type": "vagrant",
Vagrantfile template is shown below:
"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: In the example above, the compression level will be set to 1 except for
VMware, where it will be set to 0.
* `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.
* `compression_level` (integer) - An integer repesenting the The available provider names are: `aws`, `digitalocean`, `virtualbox`,
compression level to use when creating the Vagrant box. Valid and `vmware`.
values range from 0 to 9, with 0 being no compression and 9 being
the best compression.
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