github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/builder/virtualbox/common/step_download_guest_additions.go (about) 1 package common 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/mitchellh/multistep" 7 "github.com/mitchellh/packer/common" 8 "github.com/mitchellh/packer/packer" 9 "io" 10 "io/ioutil" 11 "log" 12 "os" 13 "strings" 14 ) 15 16 var additionsVersionMap = map[string]string{ 17 "4.2.1": "4.2.0", 18 "4.1.23": "4.1.22", 19 } 20 21 type guestAdditionsUrlTemplate struct { 22 Version string 23 } 24 25 // This step uploads a file containing the VirtualBox version, which 26 // can be useful for various provisioning reasons. 27 // 28 // Produces: 29 // guest_additions_path string - Path to the guest additions. 30 type StepDownloadGuestAdditions struct { 31 GuestAdditionsMode string 32 GuestAdditionsURL string 33 GuestAdditionsSHA256 string 34 Tpl *packer.ConfigTemplate 35 } 36 37 func (s *StepDownloadGuestAdditions) Run(state multistep.StateBag) multistep.StepAction { 38 var action multistep.StepAction 39 driver := state.Get("driver").(Driver) 40 ui := state.Get("ui").(packer.Ui) 41 42 // If we've disabled guest additions, don't download 43 if s.GuestAdditionsMode == GuestAdditionsModeDisable { 44 log.Println("Not downloading guest additions since it is disabled.") 45 return multistep.ActionContinue 46 } 47 48 // Get VBox version 49 version, err := driver.Version() 50 if err != nil { 51 state.Put("error", fmt.Errorf("Error reading version for guest additions download: %s", err)) 52 return multistep.ActionHalt 53 } 54 55 if newVersion, ok := additionsVersionMap[version]; ok { 56 log.Printf("Rewriting guest additions version: %s to %s", version, newVersion) 57 version = newVersion 58 } 59 60 additionsName := fmt.Sprintf("VBoxGuestAdditions_%s.iso", version) 61 62 // Use provided version or get it from virtualbox.org 63 var checksum string 64 65 checksumType := "sha256" 66 67 // Use the provided source (URL or file path) or generate it 68 url := s.GuestAdditionsURL 69 if url != "" { 70 tplData := &guestAdditionsUrlTemplate{ 71 Version: version, 72 } 73 74 url, err = s.Tpl.Process(url, tplData) 75 if err != nil { 76 err := fmt.Errorf("Error preparing guest additions url: %s", err) 77 state.Put("error", err) 78 ui.Error(err.Error()) 79 return multistep.ActionHalt 80 } 81 } else { 82 url, err = driver.Iso() 83 84 if err == nil { 85 checksumType = "none" 86 } else { 87 ui.Error(err.Error()) 88 url = fmt.Sprintf( 89 "http://download.virtualbox.org/virtualbox/%s/%s", 90 version, 91 additionsName) 92 } 93 } 94 if url == "" { 95 err := fmt.Errorf("Couldn't detect guest additions URL.\n" + 96 "Please specify `guest_additions_url` manually.") 97 state.Put("error", err) 98 ui.Error(err.Error()) 99 return multistep.ActionHalt 100 } 101 102 if checksumType != "none" { 103 if s.GuestAdditionsSHA256 != "" { 104 checksum = s.GuestAdditionsSHA256 105 } else { 106 checksum, action = s.downloadAdditionsSHA256(state, version, additionsName) 107 if action != multistep.ActionContinue { 108 return action 109 } 110 } 111 } 112 113 url, err = common.DownloadableURL(url) 114 if err != nil { 115 err := fmt.Errorf("Error preparing guest additions url: %s", err) 116 state.Put("error", err) 117 ui.Error(err.Error()) 118 return multistep.ActionHalt 119 } 120 121 log.Printf("Guest additions URL: %s", url) 122 123 downStep := &common.StepDownload{ 124 Checksum: checksum, 125 ChecksumType: checksumType, 126 Description: "Guest additions", 127 ResultKey: "guest_additions_path", 128 Url: []string{url}, 129 } 130 131 return downStep.Run(state) 132 } 133 134 func (s *StepDownloadGuestAdditions) Cleanup(state multistep.StateBag) {} 135 136 func (s *StepDownloadGuestAdditions) downloadAdditionsSHA256(state multistep.StateBag, additionsVersion string, additionsName string) (string, multistep.StepAction) { 137 // First things first, we get the list of checksums for the files available 138 // for this version. 139 checksumsUrl := fmt.Sprintf( 140 "http://download.virtualbox.org/virtualbox/%s/SHA256SUMS", 141 additionsVersion) 142 143 checksumsFile, err := ioutil.TempFile("", "packer") 144 if err != nil { 145 state.Put("error", fmt.Errorf( 146 "Failed creating temporary file to store guest addition checksums: %s", 147 err)) 148 return "", multistep.ActionHalt 149 } 150 defer os.Remove(checksumsFile.Name()) 151 checksumsFile.Close() 152 153 downStep := &common.StepDownload{ 154 Description: "Guest additions checksums", 155 ResultKey: "guest_additions_checksums_path", 156 TargetPath: checksumsFile.Name(), 157 Url: []string{checksumsUrl}, 158 } 159 160 action := downStep.Run(state) 161 if action == multistep.ActionHalt { 162 return "", action 163 } 164 165 // Next, we find the checksum for the file we're looking to download. 166 // It is an error if the checksum cannot be found. 167 checksumsF, err := os.Open(state.Get("guest_additions_checksums_path").(string)) 168 if err != nil { 169 state.Put("error", fmt.Errorf("Error opening guest addition checksums: %s", err)) 170 return "", multistep.ActionHalt 171 } 172 defer checksumsF.Close() 173 174 // We copy the contents of the file into memory. In general this file 175 // is quite small so that is okay. In the future, we probably want to 176 // use bufio and iterate line by line. 177 var contents bytes.Buffer 178 io.Copy(&contents, checksumsF) 179 180 checksum := "" 181 for _, line := range strings.Split(contents.String(), "\n") { 182 parts := strings.Fields(line) 183 log.Printf("Checksum file parts: %#v", parts) 184 if len(parts) != 2 { 185 // Bogus line 186 continue 187 } 188 189 if strings.HasSuffix(parts[1], additionsName) { 190 checksum = parts[0] 191 log.Printf("Guest additions checksum: %s", checksum) 192 break 193 } 194 } 195 196 if checksum == "" { 197 state.Put("error", fmt.Errorf( 198 "The checksum for the file '%s' could not be found.", additionsName)) 199 return "", multistep.ActionHalt 200 } 201 202 return checksum, multistep.ActionContinue 203 204 }