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  }