github.com/homburg/packer@v0.6.1-0.20140528012651-1dcaf1716848/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 95 if checksumType != "none" { 96 if s.GuestAdditionsSHA256 != "" { 97 checksum = s.GuestAdditionsSHA256 98 } else { 99 checksum, action = s.downloadAdditionsSHA256(state, version, additionsName) 100 if action != multistep.ActionContinue { 101 return action 102 } 103 } 104 } 105 106 url, err = common.DownloadableURL(url) 107 if err != nil { 108 err := fmt.Errorf("Error preparing guest additions url: %s", err) 109 state.Put("error", err) 110 ui.Error(err.Error()) 111 return multistep.ActionHalt 112 } 113 114 log.Printf("Guest additions URL: %s", url) 115 116 downStep := &common.StepDownload{ 117 Checksum: checksum, 118 ChecksumType: checksumType, 119 Description: "Guest additions", 120 ResultKey: "guest_additions_path", 121 Url: []string{url}, 122 } 123 124 return downStep.Run(state) 125 } 126 127 func (s *StepDownloadGuestAdditions) Cleanup(state multistep.StateBag) {} 128 129 func (s *StepDownloadGuestAdditions) downloadAdditionsSHA256(state multistep.StateBag, additionsVersion string, additionsName string) (string, multistep.StepAction) { 130 // First things first, we get the list of checksums for the files available 131 // for this version. 132 checksumsUrl := fmt.Sprintf( 133 "http://download.virtualbox.org/virtualbox/%s/SHA256SUMS", 134 additionsVersion) 135 136 checksumsFile, err := ioutil.TempFile("", "packer") 137 if err != nil { 138 state.Put("error", fmt.Errorf( 139 "Failed creating temporary file to store guest addition checksums: %s", 140 err)) 141 return "", multistep.ActionHalt 142 } 143 defer os.Remove(checksumsFile.Name()) 144 checksumsFile.Close() 145 146 downStep := &common.StepDownload{ 147 Description: "Guest additions checksums", 148 ResultKey: "guest_additions_checksums_path", 149 TargetPath: checksumsFile.Name(), 150 Url: []string{checksumsUrl}, 151 } 152 153 action := downStep.Run(state) 154 if action == multistep.ActionHalt { 155 return "", action 156 } 157 158 // Next, we find the checksum for the file we're looking to download. 159 // It is an error if the checksum cannot be found. 160 checksumsF, err := os.Open(state.Get("guest_additions_checksums_path").(string)) 161 if err != nil { 162 state.Put("error", fmt.Errorf("Error opening guest addition checksums: %s", err)) 163 return "", multistep.ActionHalt 164 } 165 defer checksumsF.Close() 166 167 // We copy the contents of the file into memory. In general this file 168 // is quite small so that is okay. In the future, we probably want to 169 // use bufio and iterate line by line. 170 var contents bytes.Buffer 171 io.Copy(&contents, checksumsF) 172 173 checksum := "" 174 for _, line := range strings.Split(contents.String(), "\n") { 175 parts := strings.Fields(line) 176 log.Printf("Checksum file parts: %#v", parts) 177 if len(parts) != 2 { 178 // Bogus line 179 continue 180 } 181 182 if strings.HasSuffix(parts[1], additionsName) { 183 checksum = parts[0] 184 log.Printf("Guest additions checksum: %s", checksum) 185 break 186 } 187 } 188 189 if checksum == "" { 190 state.Put("error", fmt.Errorf( 191 "The checksum for the file '%s' could not be found.", additionsName)) 192 return "", multistep.ActionHalt 193 } 194 195 return checksum, multistep.ActionContinue 196 197 }