2014-05-04 11:56:57 -04:00
|
|
|
package common
|
2013-06-24 02:05:32 -04:00
|
|
|
|
|
|
|
import (
|
2013-06-24 02:44:03 -04:00
|
|
|
"bytes"
|
2018-01-22 18:32:33 -05:00
|
|
|
"context"
|
2013-06-24 02:05:32 -04:00
|
|
|
"fmt"
|
2013-06-24 02:44:03 -04:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2013-06-24 02:05:32 -04:00
|
|
|
"log"
|
2013-06-24 02:44:03 -04:00
|
|
|
"os"
|
|
|
|
"strings"
|
2015-05-27 17:01:08 -04:00
|
|
|
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/common"
|
2018-01-19 19:18:44 -05:00
|
|
|
"github.com/hashicorp/packer/helper/multistep"
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/packer"
|
|
|
|
"github.com/hashicorp/packer/template/interpolate"
|
2013-06-24 02:05:32 -04:00
|
|
|
)
|
|
|
|
|
2013-06-25 15:31:06 -04:00
|
|
|
var additionsVersionMap = map[string]string{
|
|
|
|
"4.2.1": "4.2.0",
|
2013-06-24 12:24:16 -04:00
|
|
|
"4.1.23": "4.1.22",
|
|
|
|
}
|
|
|
|
|
2013-08-13 22:11:15 -04:00
|
|
|
type guestAdditionsUrlTemplate struct {
|
|
|
|
Version string
|
|
|
|
}
|
|
|
|
|
2013-06-24 02:05:32 -04:00
|
|
|
// 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.
|
2014-05-04 11:56:57 -04:00
|
|
|
type StepDownloadGuestAdditions struct {
|
|
|
|
GuestAdditionsMode string
|
|
|
|
GuestAdditionsURL string
|
|
|
|
GuestAdditionsSHA256 string
|
2015-05-27 17:01:08 -04:00
|
|
|
Ctx interpolate.Context
|
2014-05-04 11:56:57 -04:00
|
|
|
}
|
2013-06-24 02:05:32 -04:00
|
|
|
|
2018-01-22 18:31:41 -05:00
|
|
|
func (s *StepDownloadGuestAdditions) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
2013-06-24 02:44:03 -04:00
|
|
|
var action multistep.StepAction
|
2014-05-04 11:56:57 -04:00
|
|
|
driver := state.Get("driver").(Driver)
|
2013-08-31 15:44:58 -04:00
|
|
|
ui := state.Get("ui").(packer.Ui)
|
2013-06-24 02:05:32 -04:00
|
|
|
|
2013-12-18 14:22:02 -05:00
|
|
|
// If we've disabled guest additions, don't download
|
2014-05-04 11:56:57 -04:00
|
|
|
if s.GuestAdditionsMode == GuestAdditionsModeDisable {
|
2013-12-18 14:22:02 -05:00
|
|
|
log.Println("Not downloading guest additions since it is disabled.")
|
|
|
|
return multistep.ActionContinue
|
|
|
|
}
|
|
|
|
|
2013-07-07 12:14:41 -04:00
|
|
|
// Get VBox version
|
2013-06-24 02:05:32 -04:00
|
|
|
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))
|
2013-06-24 02:05:32 -04:00
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2013-06-24 12:24:16 -04:00
|
|
|
if newVersion, ok := additionsVersionMap[version]; ok {
|
|
|
|
log.Printf("Rewriting guest additions version: %s to %s", version, newVersion)
|
|
|
|
version = newVersion
|
|
|
|
}
|
|
|
|
|
2013-06-24 02:44:03 -04:00
|
|
|
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
|
2013-06-24 02:44:03 -04:00
|
|
|
|
2014-05-06 18:37:49 -04:00
|
|
|
checksumType := "sha256"
|
2013-06-24 02:44:03 -04:00
|
|
|
|
2018-01-02 22:07:01 -05:00
|
|
|
// Grab the guest_additions_url as specified by the user.
|
2014-05-04 11:56:57 -04:00
|
|
|
url := s.GuestAdditionsURL
|
2013-08-13 22:11:15 -04:00
|
|
|
|
2018-01-02 22:07:01 -05:00
|
|
|
// Initialize the template context so we can interpolate some variables..
|
|
|
|
s.Ctx.Data = &guestAdditionsUrlTemplate{
|
|
|
|
Version: version,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interpolate any user-variables specified within the guest_additions_url
|
|
|
|
url, err = interpolate.Render(s.GuestAdditionsURL, &s.Ctx)
|
|
|
|
if err != nil {
|
|
|
|
err := fmt.Errorf("Error preparing guest additions url: %s", err)
|
|
|
|
state.Put("error", err)
|
|
|
|
ui.Error(err.Error())
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this resulted in an empty url, then ask the driver about it.
|
|
|
|
if url == "" {
|
2018-01-05 12:19:38 -05:00
|
|
|
log.Printf("guest_additions_url is blank; querying driver for iso.")
|
2014-05-06 18:37:49 -04:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2018-01-02 22:07:01 -05:00
|
|
|
|
|
|
|
// The driver couldn't even figure it out, so fail hard.
|
2014-09-03 23:30:16 -04:00
|
|
|
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
|
2014-09-03 23:30:16 -04:00
|
|
|
}
|
2014-05-06 18:37:49 -04:00
|
|
|
|
2018-01-02 22:07:01 -05:00
|
|
|
// Figure out a default checksum here
|
2014-05-06 18:37:49 -04:00
|
|
|
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
|
|
|
}
|
2013-07-06 05:28:56 -04:00
|
|
|
|
2018-01-02 22:07:01 -05:00
|
|
|
// Convert the file/url to an actual URL for step_download to process.
|
2013-08-14 09:59:09 -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)
|
2013-08-14 09:59:09 -04:00
|
|
|
ui.Error(err.Error())
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2013-06-24 02:05:32 -04:00
|
|
|
log.Printf("Guest additions URL: %s", url)
|
|
|
|
|
2018-01-02 22:07:01 -05:00
|
|
|
// We're good, so let's go ahead and download this thing..
|
2013-08-15 14:15:32 -04:00
|
|
|
downStep := &common.StepDownload{
|
|
|
|
Checksum: checksum,
|
2014-05-06 18:37:49 -04:00
|
|
|
ChecksumType: checksumType,
|
2013-08-15 14:15:32 -04:00
|
|
|
Description: "Guest additions",
|
|
|
|
ResultKey: "guest_additions_path",
|
|
|
|
Url: []string{url},
|
2013-06-24 02:05:32 -04:00
|
|
|
}
|
|
|
|
|
2013-08-15 14:15:32 -04:00
|
|
|
return downStep.Run(state)
|
2013-06-24 02:44:03 -04:00
|
|
|
}
|
|
|
|
|
2014-05-04 11:56:57 -04:00
|
|
|
func (s *StepDownloadGuestAdditions) Cleanup(state multistep.StateBag) {}
|
2013-06-24 02:44:03 -04:00
|
|
|
|
2014-05-04 11:56:57 -04:00
|
|
|
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
|
2013-07-06 05:28:56 -04:00
|
|
|
// for this version.
|
2013-08-15 14:15:32 -04:00
|
|
|
checksumsUrl := fmt.Sprintf(
|
|
|
|
"http://download.virtualbox.org/virtualbox/%s/SHA256SUMS",
|
|
|
|
additionsVersion)
|
2013-07-07 12:14:41 -04:00
|
|
|
|
2013-07-07 12:17:27 -04:00
|
|
|
checksumsFile, err := ioutil.TempFile("", "packer")
|
2013-07-06 05:28:56 -04:00
|
|
|
if err != nil {
|
2013-08-31 15:44:58 -04:00
|
|
|
state.Put("error", fmt.Errorf(
|
2013-07-06 05:28:56 -04:00
|
|
|
"Failed creating temporary file to store guest addition checksums: %s",
|
2013-08-31 15:44:58 -04:00
|
|
|
err))
|
2013-07-06 05:28:56 -04:00
|
|
|
return "", multistep.ActionHalt
|
|
|
|
}
|
2013-07-07 12:17:27 -04:00
|
|
|
defer os.Remove(checksumsFile.Name())
|
2013-07-06 05:28:56 -04:00
|
|
|
checksumsFile.Close()
|
|
|
|
|
2013-08-15 14:15:32 -04:00
|
|
|
downStep := &common.StepDownload{
|
|
|
|
Description: "Guest additions checksums",
|
|
|
|
ResultKey: "guest_additions_checksums_path",
|
|
|
|
TargetPath: checksumsFile.Name(),
|
|
|
|
Url: []string{checksumsUrl},
|
2013-07-06 05:28:56 -04:00
|
|
|
}
|
|
|
|
|
2013-08-15 14:15:32 -04:00
|
|
|
action := downStep.Run(state)
|
|
|
|
if action == multistep.ActionHalt {
|
2013-07-06 05:28:56 -04:00
|
|
|
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))
|
2013-07-06 05:28:56 -04:00
|
|
|
if err != nil {
|
2013-08-31 15:44:58 -04:00
|
|
|
state.Put("error", fmt.Errorf("Error opening guest addition checksums: %s", err))
|
2013-07-06 05:28:56 -04:00
|
|
|
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))
|
2013-07-06 05:28:56 -04:00
|
|
|
return "", multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2013-07-07 12:14:41 -04:00
|
|
|
return checksum, multistep.ActionContinue
|
2013-07-06 05:28:56 -04:00
|
|
|
|
|
|
|
}
|