github.phpd.cn/hashicorp/packer@v1.3.2/common/iso_config.go (about)

     1  package common
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"net/http"
     9  	"net/url"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  
    15  	"github.com/hashicorp/packer/template/interpolate"
    16  )
    17  
    18  // ISOConfig contains configuration for downloading ISO images.
    19  type ISOConfig struct {
    20  	ISOChecksum     string   `mapstructure:"iso_checksum"`
    21  	ISOChecksumURL  string   `mapstructure:"iso_checksum_url"`
    22  	ISOChecksumType string   `mapstructure:"iso_checksum_type"`
    23  	ISOUrls         []string `mapstructure:"iso_urls"`
    24  	TargetPath      string   `mapstructure:"iso_target_path"`
    25  	TargetExtension string   `mapstructure:"iso_target_extension"`
    26  	RawSingleISOUrl string   `mapstructure:"iso_url"`
    27  }
    28  
    29  func (c *ISOConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs []error) {
    30  	if c.RawSingleISOUrl == "" && len(c.ISOUrls) == 0 {
    31  		errs = append(
    32  			errs, errors.New("One of iso_url or iso_urls must be specified."))
    33  		return
    34  	} else if c.RawSingleISOUrl != "" && len(c.ISOUrls) > 0 {
    35  		errs = append(
    36  			errs, errors.New("Only one of iso_url or iso_urls may be specified."))
    37  		return
    38  	} else if c.RawSingleISOUrl != "" {
    39  		c.ISOUrls = []string{c.RawSingleISOUrl}
    40  	}
    41  
    42  	if c.ISOChecksumType == "" {
    43  		errs = append(
    44  			errs, errors.New("The iso_checksum_type must be specified."))
    45  	} else {
    46  		c.ISOChecksumType = strings.ToLower(c.ISOChecksumType)
    47  		if c.ISOChecksumType != "none" {
    48  			if c.ISOChecksum == "" && c.ISOChecksumURL == "" {
    49  				errs = append(
    50  					errs, errors.New("Due to large file sizes, an iso_checksum is required"))
    51  				return warnings, errs
    52  			} else {
    53  				if h := HashForType(c.ISOChecksumType); h == nil {
    54  					errs = append(
    55  						errs, fmt.Errorf("Unsupported checksum type: %s", c.ISOChecksumType))
    56  					return warnings, errs
    57  				}
    58  
    59  				// If iso_checksum has no value use iso_checksum_url instead.
    60  				if c.ISOChecksum == "" {
    61  					u, err := url.Parse(c.ISOChecksumURL)
    62  					if err != nil {
    63  						errs = append(errs,
    64  							fmt.Errorf("Error parsing checksum: %s", err))
    65  						return warnings, errs
    66  					}
    67  					switch u.Scheme {
    68  					case "http", "https":
    69  						res, err := http.Get(c.ISOChecksumURL)
    70  						c.ISOChecksum = ""
    71  						if err != nil {
    72  							errs = append(errs,
    73  								fmt.Errorf("Error getting checksum from url: %s", c.ISOChecksumURL))
    74  							return warnings, errs
    75  						}
    76  						defer res.Body.Close()
    77  						err = c.parseCheckSumFile(bufio.NewReader(res.Body))
    78  						if err != nil {
    79  							errs = append(errs, err)
    80  							return warnings, errs
    81  						}
    82  					case "file":
    83  						path := u.Path
    84  
    85  						if runtime.GOOS == "windows" && len(path) > 2 && path[0] == '/' && path[2] == ':' {
    86  							path = strings.TrimLeft(path, "/")
    87  						}
    88  
    89  						file, err := os.Open(path)
    90  						if err != nil {
    91  							errs = append(errs, err)
    92  							return warnings, errs
    93  						}
    94  						err = c.parseCheckSumFile(bufio.NewReader(file))
    95  						if err != nil {
    96  							errs = append(errs, err)
    97  							return warnings, errs
    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 := ValidatedURL(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  	u, err := url.Parse(c.ISOUrls[0])
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	checksumurl, err := url.Parse(c.ISOChecksumURL)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	absPath, err := filepath.Abs(u.Path)
   150  	if err != nil {
   151  		log.Printf("Unable to generate absolute path from provided iso_url: %s", err)
   152  		absPath = ""
   153  	}
   154  
   155  	relpath, err := filepath.Rel(filepath.Dir(checksumurl.Path), absPath)
   156  	if err != nil {
   157  		log.Printf("Unable to determine relative pathing; continuing with abspath.")
   158  		relpath = ""
   159  	}
   160  
   161  	filename := filepath.Base(u.Path)
   162  
   163  	errNotFound := fmt.Errorf("No checksum for %q, %q or %q found at: %s",
   164  		filename, relpath, u.Path, c.ISOChecksumURL)
   165  	for {
   166  		line, err := rd.ReadString('\n')
   167  		if err != nil && line == "" {
   168  			break
   169  		}
   170  		parts := strings.Fields(line)
   171  		if len(parts) < 2 {
   172  			continue
   173  		}
   174  		options := []string{filename, relpath, "./" + relpath, absPath}
   175  		if strings.ToLower(parts[0]) == c.ISOChecksumType {
   176  			// BSD-style checksum
   177  			for _, match := range options {
   178  				if parts[1] == fmt.Sprintf("(%s)", match) {
   179  					c.ISOChecksum = parts[3]
   180  					return nil
   181  				}
   182  			}
   183  		} else {
   184  			// Standard checksum
   185  			if parts[1][0] == '*' {
   186  				// Binary mode
   187  				parts[1] = parts[1][1:]
   188  			}
   189  			for _, match := range options {
   190  				if parts[1] == match {
   191  					c.ISOChecksum = parts[0]
   192  					return nil
   193  				}
   194  			}
   195  		}
   196  	}
   197  	return errNotFound
   198  }