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