github.com/raghuse92/packer@v1.3.2/post-processor/vagrant/virtualbox.go (about)

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