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