github.com/rsyabuta/packer@v1.1.4-0.20180119234903-5ef0c2280f0b/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/hashicorp/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 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 relpath, err := filepath.Rel(filepath.Dir(checksumurl.Path), u.Path) 150 if err != nil { 151 return err 152 } 153 154 filename := filepath.Base(u.Path) 155 156 errNotFound := fmt.Errorf("No checksum for %q or %q found at: %s", filename, relpath, c.ISOChecksumURL) 157 for { 158 line, err := rd.ReadString('\n') 159 if err != nil && line == "" { 160 break 161 } 162 parts := strings.Fields(line) 163 if len(parts) < 2 { 164 continue 165 } 166 if strings.ToLower(parts[0]) == c.ISOChecksumType { 167 // BSD-style checksum 168 if parts[1] == fmt.Sprintf("(%s)", filename) || parts[1] == fmt.Sprintf("(%s)", relpath) || 169 parts[1] == fmt.Sprintf("(./%s)", relpath) { 170 c.ISOChecksum = parts[3] 171 return nil 172 } 173 } else { 174 // Standard checksum 175 if parts[1][0] == '*' { 176 // Binary mode 177 parts[1] = parts[1][1:] 178 } 179 if parts[1] == filename || parts[1] == relpath || parts[1] == "./"+relpath { 180 c.ISOChecksum = parts[0] 181 return nil 182 } 183 } 184 } 185 return errNotFound 186 }