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 `