packer-cn/builder/virtualbox/common/step_download_guest_additio...

207 lines
5.4 KiB
Go
Raw Normal View History

package common
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strings"
2017-04-04 16:39:01 -04:00
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
)
2013-06-25 15:31:06 -04:00
var additionsVersionMap = map[string]string{
"4.2.1": "4.2.0",
"4.1.23": "4.1.22",
}
type guestAdditionsUrlTemplate struct {
Version string
}
// This step uploads a file containing the VirtualBox version, which
// can be useful for various provisioning reasons.
//
// Produces:
// guest_additions_path string - Path to the guest additions.
type StepDownloadGuestAdditions struct {
GuestAdditionsMode string
GuestAdditionsURL string
GuestAdditionsSHA256 string
Ctx interpolate.Context
}
func (s *StepDownloadGuestAdditions) Run(state multistep.StateBag) multistep.StepAction {
var action multistep.StepAction
driver := state.Get("driver").(Driver)
2013-08-31 15:44:58 -04:00
ui := state.Get("ui").(packer.Ui)
// If we've disabled guest additions, don't download
if s.GuestAdditionsMode == GuestAdditionsModeDisable {
log.Println("Not downloading guest additions since it is disabled.")
return multistep.ActionContinue
}
2013-07-07 12:14:41 -04:00
// Get VBox version
version, err := driver.Version()
if err != nil {
2013-08-31 15:44:58 -04:00
state.Put("error", fmt.Errorf("Error reading version for guest additions download: %s", err))
return multistep.ActionHalt
}
if newVersion, ok := additionsVersionMap[version]; ok {
log.Printf("Rewriting guest additions version: %s to %s", version, newVersion)
version = newVersion
}
additionsName := fmt.Sprintf("VBoxGuestAdditions_%s.iso", version)
2013-07-07 12:14:41 -04:00
// Use provided version or get it from virtualbox.org
var checksum string
checksumType := "sha256"
2013-07-07 12:14:41 -04:00
// Use the provided source (URL or file path) or generate it
url := s.GuestAdditionsURL
if url != "" {
s.Ctx.Data = &guestAdditionsUrlTemplate{
Version: version,
}
url, err = interpolate.Render(url, &s.Ctx)
if err != nil {
err := fmt.Errorf("Error preparing guest additions url: %s", err)
2013-08-31 15:44:58 -04:00
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
} else {
url, err = driver.Iso()
if err == nil {
checksumType = "none"
} else {
ui.Error(err.Error())
url = fmt.Sprintf(
"http://download.virtualbox.org/virtualbox/%s/%s",
version,
additionsName)
}
}
if url == "" {
err := fmt.Errorf("Couldn't detect guest additions URL.\n" +
"Please specify `guest_additions_url` manually.")
state.Put("error", err)
ui.Error(err.Error())
2014-09-03 23:33:22 -04:00
return multistep.ActionHalt
}
if checksumType != "none" {
if s.GuestAdditionsSHA256 != "" {
checksum = s.GuestAdditionsSHA256
} else {
checksum, action = s.downloadAdditionsSHA256(state, version, additionsName)
if action != multistep.ActionContinue {
return action
}
}
2013-07-07 12:14:41 -04:00
}
url, err = common.DownloadableURL(url)
if err != nil {
err := fmt.Errorf("Error preparing guest additions url: %s", err)
2013-08-31 15:44:58 -04:00
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
log.Printf("Guest additions URL: %s", url)
downStep := &common.StepDownload{
Checksum: checksum,
ChecksumType: checksumType,
Description: "Guest additions",
ResultKey: "guest_additions_path",
Url: []string{url},
}
return downStep.Run(state)
}
func (s *StepDownloadGuestAdditions) Cleanup(state multistep.StateBag) {}
func (s *StepDownloadGuestAdditions) downloadAdditionsSHA256(state multistep.StateBag, additionsVersion string, additionsName string) (string, multistep.StepAction) {
2013-07-07 12:14:41 -04:00
// First things first, we get the list of checksums for the files available
// for this version.
checksumsUrl := fmt.Sprintf(
"http://download.virtualbox.org/virtualbox/%s/SHA256SUMS",
additionsVersion)
2013-07-07 12:14:41 -04:00
checksumsFile, err := ioutil.TempFile("", "packer")
if err != nil {
2013-08-31 15:44:58 -04:00
state.Put("error", fmt.Errorf(
"Failed creating temporary file to store guest addition checksums: %s",
2013-08-31 15:44:58 -04:00
err))
return "", multistep.ActionHalt
}
defer os.Remove(checksumsFile.Name())
checksumsFile.Close()
downStep := &common.StepDownload{
Description: "Guest additions checksums",
ResultKey: "guest_additions_checksums_path",
TargetPath: checksumsFile.Name(),
Url: []string{checksumsUrl},
}
action := downStep.Run(state)
if action == multistep.ActionHalt {
return "", action
}
// Next, we find the checksum for the file we're looking to download.
// It is an error if the checksum cannot be found.
2013-08-31 15:44:58 -04:00
checksumsF, err := os.Open(state.Get("guest_additions_checksums_path").(string))
if err != nil {
2013-08-31 15:44:58 -04:00
state.Put("error", fmt.Errorf("Error opening guest addition checksums: %s", err))
return "", multistep.ActionHalt
}
defer checksumsF.Close()
// We copy the contents of the file into memory. In general this file
// is quite small so that is okay. In the future, we probably want to
// use bufio and iterate line by line.
var contents bytes.Buffer
io.Copy(&contents, checksumsF)
checksum := ""
for _, line := range strings.Split(contents.String(), "\n") {
parts := strings.Fields(line)
log.Printf("Checksum file parts: %#v", parts)
if len(parts) != 2 {
// Bogus line
continue
}
if strings.HasSuffix(parts[1], additionsName) {
checksum = parts[0]
log.Printf("Guest additions checksum: %s", checksum)
break
}
}
if checksum == "" {
2013-08-31 15:44:58 -04:00
state.Put("error", fmt.Errorf(
"The checksum for the file '%s' could not be found.", additionsName))
return "", multistep.ActionHalt
}
2013-07-07 12:14:41 -04:00
return checksum, multistep.ActionContinue
}