github.com/benphegan/packer@v1.0.0-rc1/common/iso_config.go (about)

     1  package common
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"strings"
    13  
    14  	"github.com/mitchellh/packer/template/interpolate"
    15  )
    16  
    17  // ISOConfig contains configuration for downloading ISO images.
    18  type ISOConfig struct {
    19  	ISOChecksum     string   `mapstructure:"iso_checksum"`
    20  	ISOChecksumURL  string   `mapstructure:"iso_checksum_url"`
    21  	ISOChecksumType string   `mapstructure:"iso_checksum_type"`
    22  	ISOUrls         []string `mapstructure:"iso_urls"`
    23  	TargetPath      string   `mapstructure:"iso_target_path"`
    24  	TargetExtension string   `mapstructure:"iso_target_extension"`
    25  	RawSingleISOUrl string   `mapstructure:"iso_url"`
    26  }
    27  
    28  func (c *ISOConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs []error) {
    29  	if c.RawSingleISOUrl == "" && len(c.ISOUrls) == 0 {
    30  		errs = append(
    31  			errs, errors.New("One of iso_url or iso_urls must be specified."))
    32  		return
    33  	} else if c.RawSingleISOUrl != "" && len(c.ISOUrls) > 0 {
    34  		errs = append(
    35  			errs, errors.New("Only one of iso_url or iso_urls may be specified."))
    36  		return
    37  	} else if c.RawSingleISOUrl != "" {
    38  		c.ISOUrls = []string{c.RawSingleISOUrl}
    39  	}
    40  
    41  	if c.ISOChecksumType == "" {
    42  		errs = append(
    43  			errs, errors.New("The iso_checksum_type must be specified."))
    44  	} else {
    45  		c.ISOChecksumType = strings.ToLower(c.ISOChecksumType)
    46  		if c.ISOChecksumType != "none" {
    47  			if c.ISOChecksum == "" && c.ISOChecksumURL == "" {
    48  				errs = append(
    49  					errs, errors.New("Due to large file sizes, an iso_checksum is required"))
    50  				return warnings, errs
    51  			} else {
    52  				if h := HashForType(c.ISOChecksumType); h == nil {
    53  					errs = append(
    54  						errs, fmt.Errorf("Unsupported checksum type: %s", c.ISOChecksumType))
    55  					return warnings, errs
    56  				}
    57  
    58  				// If iso_checksum has no value use iso_checksum_url instead.
    59  				if c.ISOChecksum == "" {
    60  					u, err := url.Parse(c.ISOChecksumURL)
    61  					if err != nil {
    62  						errs = append(errs,
    63  							fmt.Errorf("Error parsing checksum: %s", err))
    64  						return warnings, errs
    65  					}
    66  					switch u.Scheme {
    67  					case "http", "https":
    68  						res, err := http.Get(c.ISOChecksumURL)
    69  						c.ISOChecksum = ""
    70  						if err != nil {
    71  							errs = append(errs,
    72  								fmt.Errorf("Error getting checksum from url: %s", c.ISOChecksumURL))
    73  							return warnings, errs
    74  						}
    75  						defer res.Body.Close()
    76  						err = c.parseCheckSumFile(bufio.NewReader(res.Body))
    77  						if err != nil {
    78  							errs = append(errs, err)
    79  							return warnings, errs
    80  						}
    81  					case "file":
    82  						path := u.Path
    83  
    84  						if runtime.GOOS == "windows" && len(path) > 2 && path[0] == '/' && path[2] == ':' {
    85  							path = strings.TrimLeft(path, "/")
    86  						}
    87  
    88  						file, err := os.Open(path)
    89  						if err != nil {
    90  							errs = append(errs, err)
    91  							return warnings, errs
    92  						}
    93  						err = c.parseCheckSumFile(bufio.NewReader(file))
    94  						if err != nil {
    95  							errs = append(errs, err)
    96  							return warnings, errs
    97  						}
    98  
    99  					case "":
   100  						break
   101  					default:
   102  						errs = append(errs,
   103  							fmt.Errorf("Error parsing checksum url: %s, scheme not supported: %s", c.ISOChecksumURL, u.Scheme))
   104  						return warnings, errs
   105  					}
   106  				}
   107  			}
   108  		}
   109  	}
   110  
   111  	c.ISOChecksum = strings.ToLower(c.ISOChecksum)
   112  
   113  	for i, url := range c.ISOUrls {
   114  		url, err := DownloadableURL(url)
   115  		if err != nil {
   116  			errs = append(
   117  				errs, fmt.Errorf("Failed to parse iso_url %d: %s", i+1, err))
   118  		} else {
   119  			c.ISOUrls[i] = url
   120  		}
   121  	}
   122  
   123  	if c.TargetExtension == "" {
   124  		c.TargetExtension = "iso"
   125  	}
   126  	c.TargetExtension = strings.ToLower(c.TargetExtension)
   127  
   128  	// Warnings
   129  	if c.ISOChecksumType == "none" {
   130  		warnings = append(warnings,
   131  			"A checksum type of 'none' was specified. Since ISO files are so big,\n"+
   132  				"a checksum is highly recommended.")
   133  	}
   134  
   135  	return warnings, errs
   136  }
   137  
   138  func (c *ISOConfig) parseCheckSumFile(rd *bufio.Reader) error {
   139  	errNotFound := fmt.Errorf("No checksum for %q found at: %s", filepath.Base(c.ISOUrls[0]), c.ISOChecksumURL)
   140  	for {
   141  		line, err := rd.ReadString('\n')
   142  		if err != nil && line == "" {
   143  			break
   144  		}
   145  		parts := strings.Fields(line)
   146  		if len(parts) < 2 {
   147  			continue
   148  		}
   149  		if strings.ToLower(parts[0]) == c.ISOChecksumType {
   150  			// BSD-style checksum
   151  			if parts[1] == fmt.Sprintf("(%s)", filepath.Base(c.ISOUrls[0])) {
   152  				c.ISOChecksum = parts[3]
   153  				return nil
   154  			}
   155  		} else {
   156  			// Standard checksum
   157  			if parts[1][0] == '*' {
   158  				// Binary mode
   159  				parts[1] = parts[1][1:]
   160  			}
   161  			if parts[1] == filepath.Base(c.ISOUrls[0]) {
   162  				c.ISOChecksum = parts[0]
   163  				return nil
   164  			}
   165  		}
   166  	}
   167  	return errNotFound
   168  }