github.com/rothwerx/packer@v0.9.0/builder/virtualbox/common/step_download_guest_additions.go (about)

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