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