github.com/phobos182/packer@v0.2.3-0.20130819023704-c84d2aeffc68/post-processor/vagrant/virtualbox.go (about)

     1  package vagrant
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/mitchellh/packer/common"
     7  	"github.com/mitchellh/packer/packer"
     8  	"io/ioutil"
     9  	"log"
    10  	"os"
    11  	"path/filepath"
    12  	"regexp"
    13  	"strings"
    14  )
    15  
    16  type VBoxBoxConfig struct {
    17  	common.PackerConfig `mapstructure:",squash"`
    18  
    19  	OutputPath          string `mapstructure:"output"`
    20  	VagrantfileTemplate string `mapstructure:"vagrantfile_template"`
    21  
    22  	tpl *packer.ConfigTemplate
    23  }
    24  
    25  type VBoxVagrantfileTemplate struct {
    26  	BaseMacAddress string
    27  }
    28  
    29  type VBoxBoxPostProcessor struct {
    30  	config VBoxBoxConfig
    31  }
    32  
    33  func (p *VBoxBoxPostProcessor) Configure(raws ...interface{}) error {
    34  	md, err := common.DecodeConfig(&p.config, raws...)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	p.config.tpl, err = packer.NewConfigTemplate()
    40  	if err != nil {
    41  		return err
    42  	}
    43  	p.config.tpl.UserVars = p.config.PackerUserVars
    44  
    45  	// Accumulate any errors
    46  	errs := common.CheckUnusedConfig(md)
    47  
    48  	validates := map[string]*string{
    49  		"output":               &p.config.OutputPath,
    50  		"vagrantfile_template": &p.config.VagrantfileTemplate,
    51  	}
    52  
    53  	for n, ptr := range validates {
    54  		if err := p.config.tpl.Validate(*ptr); err != nil {
    55  			errs = packer.MultiErrorAppend(
    56  				errs, fmt.Errorf("Error parsing %s: %s", n, err))
    57  		}
    58  	}
    59  
    60  	if errs != nil && len(errs.Errors) > 0 {
    61  		return errs
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  func (p *VBoxBoxPostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
    68  	var err error
    69  	tplData := &VBoxVagrantfileTemplate{}
    70  	tplData.BaseMacAddress, err = p.findBaseMacAddress(artifact)
    71  	if err != nil {
    72  		return nil, false, err
    73  	}
    74  
    75  	// Compile the output path
    76  	outputPath, err := p.config.tpl.Process(p.config.OutputPath, &OutputPathTemplate{
    77  		ArtifactId: artifact.Id(),
    78  		BuildName:  p.config.PackerBuildName,
    79  		Provider:   "virtualbox",
    80  	})
    81  	if err != nil {
    82  		return nil, false, err
    83  	}
    84  
    85  	// Create a temporary directory for us to build the contents of the box in
    86  	dir, err := ioutil.TempDir("", "packer")
    87  	if err != nil {
    88  		return nil, false, err
    89  	}
    90  	defer os.RemoveAll(dir)
    91  
    92  	// Copy all of the original contents into the temporary directory
    93  	for _, path := range artifact.Files() {
    94  		ui.Message(fmt.Sprintf("Copying: %s", path))
    95  
    96  		dstPath := filepath.Join(dir, filepath.Base(path))
    97  		if err := CopyContents(dstPath, path); err != nil {
    98  			return nil, false, err
    99  		}
   100  	}
   101  
   102  	// Create the Vagrantfile from the template
   103  	vf, err := os.Create(filepath.Join(dir, "Vagrantfile"))
   104  	if err != nil {
   105  		return nil, false, err
   106  	}
   107  	defer vf.Close()
   108  
   109  	vagrantfileContents := defaultVBoxVagrantfile
   110  	if p.config.VagrantfileTemplate != "" {
   111  		f, err := os.Open(p.config.VagrantfileTemplate)
   112  		if err != nil {
   113  			return nil, false, err
   114  		}
   115  		defer f.Close()
   116  
   117  		contents, err := ioutil.ReadAll(f)
   118  		if err != nil {
   119  			return nil, false, err
   120  		}
   121  
   122  		vagrantfileContents = string(contents)
   123  	}
   124  
   125  	vagrantfileContents, err = p.config.tpl.Process(vagrantfileContents, tplData)
   126  	if err != nil {
   127  		return nil, false, fmt.Errorf("Error writing Vagrantfile: %s", err)
   128  	}
   129  	vf.Write([]byte(vagrantfileContents))
   130  	vf.Close()
   131  
   132  	// Create the metadata
   133  	metadata := map[string]string{"provider": "virtualbox"}
   134  	if err := WriteMetadata(dir, metadata); err != nil {
   135  		return nil, false, err
   136  	}
   137  
   138  	// Rename the OVF file to box.ovf, as required by Vagrant
   139  	ui.Message("Renaming the OVF to box.ovf...")
   140  	if err := p.renameOVF(dir); err != nil {
   141  		return nil, false, err
   142  	}
   143  
   144  	// Compress the directory to the given output path
   145  	ui.Message(fmt.Sprintf("Compressing box..."))
   146  	if err := DirToBox(outputPath, dir); err != nil {
   147  		return nil, false, err
   148  	}
   149  
   150  	return NewArtifact("virtualbox", outputPath), false, nil
   151  }
   152  
   153  func (p *VBoxBoxPostProcessor) findBaseMacAddress(a packer.Artifact) (string, error) {
   154  	log.Println("Looking for OVF for base mac address...")
   155  	var ovf string
   156  	for _, f := range a.Files() {
   157  		if strings.HasSuffix(f, ".ovf") {
   158  			log.Printf("OVF found: %s", f)
   159  			ovf = f
   160  			break
   161  		}
   162  	}
   163  
   164  	if ovf == "" {
   165  		return "", errors.New("ovf file couldn't be found")
   166  	}
   167  
   168  	f, err := os.Open(ovf)
   169  	if err != nil {
   170  		return "", err
   171  	}
   172  	defer f.Close()
   173  
   174  	data, err := ioutil.ReadAll(f)
   175  	if err != nil {
   176  		return "", err
   177  	}
   178  
   179  	re := regexp.MustCompile(`<Adapter slot="0".+?MACAddress="(.+?)"`)
   180  	matches := re.FindSubmatch(data)
   181  	if matches == nil {
   182  		return "", errors.New("can't find base mac address in OVF")
   183  	}
   184  
   185  	log.Printf("Base mac address: %s", string(matches[1]))
   186  	return string(matches[1]), nil
   187  }
   188  
   189  func (p *VBoxBoxPostProcessor) renameOVF(dir string) error {
   190  	log.Println("Looking for OVF to rename...")
   191  	matches, err := filepath.Glob(filepath.Join(dir, "*.ovf"))
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	if len(matches) > 1 {
   197  		return errors.New("More than one OVF file in VirtualBox artifact.")
   198  	}
   199  
   200  	log.Printf("Renaming: '%s' => box.ovf", matches[0])
   201  	return os.Rename(matches[0], filepath.Join(dir, "box.ovf"))
   202  }
   203  
   204  var defaultVBoxVagrantfile = `
   205  Vagrant.configure("2") do |config|
   206  config.vm.base_mac = "{{ .BaseMacAddress }}"
   207  end
   208  `