github.com/rothwerx/packer@v0.9.0/common/iso_config.go (about)

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