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 `