github.com/tonnydourado/packer@v0.6.1-0.20140701134019-5d0cd9676a37/post-processor/vagrant/virtualbox.go (about)

     1  package vagrant
     2  
     3  import (
     4  	"archive/tar"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/mitchellh/packer/packer"
     8  	"io"
     9  	"io/ioutil"
    10  	"log"
    11  	"os"
    12  	"path/filepath"
    13  	"regexp"
    14  )
    15  
    16  type VBoxProvider struct{}
    17  
    18  func (p *VBoxProvider) KeepInputArtifact() bool {
    19  	return false
    20  }
    21  
    22  func (p *VBoxProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) {
    23  	// Create the metadata
    24  	metadata = map[string]interface{}{"provider": "virtualbox"}
    25  
    26  	// Copy all of the original contents into the temporary directory
    27  	for _, path := range artifact.Files() {
    28  		// We treat OVA files specially, we unpack those into the temporary
    29  		// directory so we can get the resulting disk and OVF.
    30  		if extension := filepath.Ext(path); extension == ".ova" {
    31  			ui.Message(fmt.Sprintf("Unpacking OVA: %s", path))
    32  			if err = DecompressOva(dir, path); err != nil {
    33  				return
    34  			}
    35  		} else {
    36  			ui.Message(fmt.Sprintf("Copying from artifact: %s", path))
    37  			dstPath := filepath.Join(dir, filepath.Base(path))
    38  			if err = CopyContents(dstPath, path); err != nil {
    39  				return
    40  			}
    41  		}
    42  
    43  	}
    44  
    45  	// Rename the OVF file to box.ovf, as required by Vagrant
    46  	ui.Message("Renaming the OVF to box.ovf...")
    47  	if err = p.renameOVF(dir); err != nil {
    48  		return
    49  	}
    50  
    51  	// Create the Vagrantfile from the template
    52  	var baseMacAddress string
    53  	baseMacAddress, err = p.findBaseMacAddress(dir)
    54  	if err != nil {
    55  		return
    56  	}
    57  
    58  	vagrantfile = fmt.Sprintf(vboxVagrantfile, baseMacAddress)
    59  	return
    60  }
    61  
    62  func (p *VBoxProvider) findOvf(dir string) (string, error) {
    63  	log.Println("Looking for OVF in artifact...")
    64  	file_matches, err := filepath.Glob(filepath.Join(dir, "*.ovf"))
    65  	if err != nil {
    66  		return "", err
    67  	}
    68  
    69  	if len(file_matches) > 1 {
    70  		return "", errors.New("More than one OVF file in VirtualBox artifact.")
    71  	}
    72  
    73  	if len(file_matches) < 1 {
    74  		return "", errors.New("ovf file couldn't be found")
    75  	}
    76  
    77  	return file_matches[0], err
    78  }
    79  
    80  func (p *VBoxProvider) renameOVF(dir string) error {
    81  	log.Println("Looking for OVF to rename...")
    82  	ovf, err := p.findOvf(dir)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	log.Printf("Renaming: '%s' => box.ovf", ovf)
    88  	return os.Rename(ovf, filepath.Join(dir, "box.ovf"))
    89  }
    90  
    91  func (p *VBoxProvider) findBaseMacAddress(dir string) (string, error) {
    92  	log.Println("Looking for OVF for base mac address...")
    93  	ovf, err := p.findOvf(dir)
    94  	if err != nil {
    95  		return "", err
    96  	}
    97  
    98  	f, err := os.Open(ovf)
    99  	if err != nil {
   100  		return "", err
   101  	}
   102  	defer f.Close()
   103  
   104  	data, err := ioutil.ReadAll(f)
   105  	if err != nil {
   106  		return "", err
   107  	}
   108  
   109  	re := regexp.MustCompile(`<Adapter slot="0".+?MACAddress="(.+?)"`)
   110  	matches := re.FindSubmatch(data)
   111  	if matches == nil {
   112  		return "", errors.New("can't find base mac address in OVF")
   113  	}
   114  
   115  	log.Printf("Base mac address: %s", string(matches[1]))
   116  	return string(matches[1]), nil
   117  }
   118  
   119  // DecompressOva takes an ova file and decompresses it into the target
   120  // directory.
   121  func DecompressOva(dir, src string) error {
   122  	log.Printf("Turning ova to dir: %s => %s", src, dir)
   123  	srcF, err := os.Open(src)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	defer srcF.Close()
   128  
   129  	tarReader := tar.NewReader(srcF)
   130  	for {
   131  		hdr, err := tarReader.Next()
   132  		if hdr == nil || err == io.EOF {
   133  			break
   134  		}
   135  
   136  		info := hdr.FileInfo()
   137  
   138  		// Shouldn't be any directories, skip them
   139  		if info.IsDir() {
   140  			continue
   141  		}
   142  
   143  		// We wrap this in an anonymous function so that the defers
   144  		// inside are handled more quickly so we can give up file handles.
   145  		err = func() error {
   146  			path := filepath.Join(dir, info.Name())
   147  			output, err := os.Create(path)
   148  			if err != nil {
   149  				return err
   150  			}
   151  			defer output.Close()
   152  
   153  			os.Chmod(path, info.Mode())
   154  			os.Chtimes(path, hdr.AccessTime, hdr.ModTime)
   155  			_, err = io.Copy(output, tarReader)
   156  			return err
   157  		}()
   158  		if err != nil {
   159  			return err
   160  		}
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  var vboxVagrantfile = `
   167  Vagrant.configure("2") do |config|
   168    config.vm.base_mac = "%s"
   169  end
   170  `