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