github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/builder/virtualbox/common/step_download_guest_additions.go (about)

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