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  }