github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/common/download_test.go (about) 1 package common 2 3 import ( 4 "crypto/md5" 5 "encoding/hex" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "net/http/httptest" 10 "os" 11 "path/filepath" 12 "runtime" 13 "strings" 14 "testing" 15 ) 16 17 func TestDownloadClientVerifyChecksum(t *testing.T) { 18 tf, err := ioutil.TempFile("", "packer") 19 if err != nil { 20 t.Fatalf("tempfile error: %s", err) 21 } 22 defer os.Remove(tf.Name()) 23 24 // "foo" 25 checksum, err := hex.DecodeString("acbd18db4cc2f85cedef654fccc4a4d8") 26 if err != nil { 27 t.Fatalf("decode err: %s", err) 28 } 29 30 // Write the file 31 tf.Write([]byte("foo")) 32 tf.Close() 33 34 config := &DownloadConfig{ 35 Hash: md5.New(), 36 Checksum: checksum, 37 } 38 39 d := NewDownloadClient(config) 40 result, err := d.VerifyChecksum(tf.Name()) 41 if err != nil { 42 t.Fatalf("Verify err: %s", err) 43 } 44 45 if !result { 46 t.Fatal("didn't verify") 47 } 48 } 49 50 func TestDownloadClient_basic(t *testing.T) { 51 tf, _ := ioutil.TempFile("", "packer") 52 tf.Close() 53 defer os.Remove(tf.Name()) 54 55 ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root"))) 56 defer ts.Close() 57 58 client := NewDownloadClient(&DownloadConfig{ 59 Url: ts.URL + "/basic.txt", 60 TargetPath: tf.Name(), 61 CopyFile: true, 62 }) 63 64 path, err := client.Get() 65 if err != nil { 66 t.Fatalf("err: %s", err) 67 } 68 69 raw, err := ioutil.ReadFile(path) 70 if err != nil { 71 t.Fatalf("err: %s", err) 72 } 73 74 if string(raw) != "hello\n" { 75 t.Fatalf("bad: %s", string(raw)) 76 } 77 } 78 79 func TestDownloadClient_checksumBad(t *testing.T) { 80 checksum, err := hex.DecodeString("b2946ac92492d2347c6235b4d2611184") 81 if err != nil { 82 t.Fatalf("err: %s", err) 83 } 84 85 tf, _ := ioutil.TempFile("", "packer") 86 tf.Close() 87 defer os.Remove(tf.Name()) 88 89 ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root"))) 90 defer ts.Close() 91 92 client := NewDownloadClient(&DownloadConfig{ 93 Url: ts.URL + "/basic.txt", 94 TargetPath: tf.Name(), 95 Hash: HashForType("md5"), 96 Checksum: checksum, 97 CopyFile: true, 98 }) 99 100 if _, err := client.Get(); err == nil { 101 t.Fatal("should error") 102 } 103 } 104 105 func TestDownloadClient_checksumGood(t *testing.T) { 106 checksum, err := hex.DecodeString("b1946ac92492d2347c6235b4d2611184") 107 if err != nil { 108 t.Fatalf("err: %s", err) 109 } 110 111 tf, _ := ioutil.TempFile("", "packer") 112 tf.Close() 113 defer os.Remove(tf.Name()) 114 115 ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root"))) 116 defer ts.Close() 117 118 client := NewDownloadClient(&DownloadConfig{ 119 Url: ts.URL + "/basic.txt", 120 TargetPath: tf.Name(), 121 Hash: HashForType("md5"), 122 Checksum: checksum, 123 CopyFile: true, 124 }) 125 126 path, err := client.Get() 127 if err != nil { 128 t.Fatalf("err: %s", err) 129 } 130 131 raw, err := ioutil.ReadFile(path) 132 if err != nil { 133 t.Fatalf("err: %s", err) 134 } 135 136 if string(raw) != "hello\n" { 137 t.Fatalf("bad: %s", string(raw)) 138 } 139 } 140 141 func TestDownloadClient_checksumNoDownload(t *testing.T) { 142 checksum, err := hex.DecodeString("3740570a423feec44c2a759225a9fcf9") 143 if err != nil { 144 t.Fatalf("err: %s", err) 145 } 146 147 ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root"))) 148 defer ts.Close() 149 150 client := NewDownloadClient(&DownloadConfig{ 151 Url: ts.URL + "/basic.txt", 152 TargetPath: "./test-fixtures/root/another.txt", 153 Hash: HashForType("md5"), 154 Checksum: checksum, 155 CopyFile: true, 156 }) 157 path, err := client.Get() 158 if err != nil { 159 t.Fatalf("err: %s", err) 160 } 161 162 raw, err := ioutil.ReadFile(path) 163 if err != nil { 164 t.Fatalf("err: %s", err) 165 } 166 167 // If this says "hello" it means we downloaded it. We faked out 168 // the downloader above by giving it the checksum for "another", but 169 // requested the download of "hello" 170 if string(raw) != "another\n" { 171 t.Fatalf("bad: %s", string(raw)) 172 } 173 } 174 175 func TestDownloadClient_notFound(t *testing.T) { 176 tf, _ := ioutil.TempFile("", "packer") 177 tf.Close() 178 defer os.Remove(tf.Name()) 179 180 ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root"))) 181 defer ts.Close() 182 183 client := NewDownloadClient(&DownloadConfig{ 184 Url: ts.URL + "/not-found.txt", 185 TargetPath: tf.Name(), 186 }) 187 188 if _, err := client.Get(); err == nil { 189 t.Fatal("should error") 190 } 191 } 192 193 func TestDownloadClient_resume(t *testing.T) { 194 tf, _ := ioutil.TempFile("", "packer") 195 tf.Write([]byte("w")) 196 tf.Close() 197 defer os.Remove(tf.Name()) 198 199 ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 200 if r.Method == "HEAD" { 201 rw.Header().Set("Accept-Ranges", "bytes") 202 rw.WriteHeader(204) 203 return 204 } 205 206 http.ServeFile(rw, r, "./test-fixtures/root/basic.txt") 207 })) 208 defer ts.Close() 209 210 client := NewDownloadClient(&DownloadConfig{ 211 Url: ts.URL, 212 TargetPath: tf.Name(), 213 CopyFile: true, 214 }) 215 216 path, err := client.Get() 217 if err != nil { 218 t.Fatalf("err: %s", err) 219 } 220 221 raw, err := ioutil.ReadFile(path) 222 if err != nil { 223 t.Fatalf("err: %s", err) 224 } 225 226 if string(raw) != "wello\n" { 227 t.Fatalf("bad: %s", string(raw)) 228 } 229 } 230 231 func TestDownloadClient_usesDefaultUserAgent(t *testing.T) { 232 tf, err := ioutil.TempFile("", "packer") 233 if err != nil { 234 t.Fatalf("tempfile error: %s", err) 235 } 236 tf.Close() 237 defer os.Remove(tf.Name()) 238 239 defaultUserAgent := "" 240 asserted := false 241 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 242 if defaultUserAgent == "" { 243 defaultUserAgent = r.UserAgent() 244 } else { 245 incomingUserAgent := r.UserAgent() 246 if incomingUserAgent != defaultUserAgent { 247 t.Fatalf("Expected user agent %s, got: %s", defaultUserAgent, incomingUserAgent) 248 } 249 250 asserted = true 251 } 252 })) 253 254 req, err := http.NewRequest("GET", server.URL, nil) 255 if err != nil { 256 t.Fatal(err) 257 } 258 259 httpClient := &http.Client{ 260 Transport: &http.Transport{ 261 Proxy: http.ProxyFromEnvironment, 262 }, 263 } 264 265 _, err = httpClient.Do(req) 266 if err != nil { 267 t.Fatal(err) 268 } 269 270 config := &DownloadConfig{ 271 Url: server.URL, 272 TargetPath: tf.Name(), 273 CopyFile: true, 274 } 275 276 client := NewDownloadClient(config) 277 _, err = client.Get() 278 if err != nil { 279 t.Fatal(err) 280 } 281 282 if !asserted { 283 t.Fatal("User-Agent never observed") 284 } 285 } 286 287 func TestDownloadClient_setsUserAgent(t *testing.T) { 288 tf, err := ioutil.TempFile("", "packer") 289 if err != nil { 290 t.Fatalf("tempfile error: %s", err) 291 } 292 tf.Close() 293 defer os.Remove(tf.Name()) 294 295 asserted := false 296 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 297 asserted = true 298 if r.UserAgent() != "fancy user agent" { 299 t.Fatalf("Expected useragent fancy user agent, got: %s", r.UserAgent()) 300 } 301 })) 302 config := &DownloadConfig{ 303 Url: server.URL, 304 TargetPath: tf.Name(), 305 UserAgent: "fancy user agent", 306 CopyFile: true, 307 } 308 309 client := NewDownloadClient(config) 310 _, err = client.Get() 311 if err != nil { 312 t.Fatal(err) 313 } 314 315 if !asserted { 316 t.Fatal("HTTP request never made") 317 } 318 } 319 320 func TestHashForType(t *testing.T) { 321 if h := HashForType("md5"); h == nil { 322 t.Fatalf("md5 hash is nil") 323 } else { 324 h.Write([]byte("foo")) 325 result := h.Sum(nil) 326 327 expected := "acbd18db4cc2f85cedef654fccc4a4d8" 328 actual := hex.EncodeToString(result) 329 if actual != expected { 330 t.Fatalf("bad hash: %s", actual) 331 } 332 } 333 334 if h := HashForType("sha1"); h == nil { 335 t.Fatalf("sha1 hash is nil") 336 } else { 337 h.Write([]byte("foo")) 338 result := h.Sum(nil) 339 340 expected := "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" 341 actual := hex.EncodeToString(result) 342 if actual != expected { 343 t.Fatalf("bad hash: %s", actual) 344 } 345 } 346 347 if h := HashForType("sha256"); h == nil { 348 t.Fatalf("sha256 hash is nil") 349 } else { 350 h.Write([]byte("foo")) 351 result := h.Sum(nil) 352 353 expected := "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae" 354 actual := hex.EncodeToString(result) 355 if actual != expected { 356 t.Fatalf("bad hash: %s", actual) 357 } 358 } 359 360 if h := HashForType("sha512"); h == nil { 361 t.Fatalf("sha512 hash is nil") 362 } else { 363 h.Write([]byte("foo")) 364 result := h.Sum(nil) 365 366 expected := "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7" 367 actual := hex.EncodeToString(result) 368 if actual != expected { 369 t.Fatalf("bad hash: %s", actual) 370 } 371 } 372 373 if HashForType("fake") != nil { 374 t.Fatalf("fake hash is not nil") 375 } 376 } 377 378 // TestDownloadFileUrl tests a special case where we use a local file for 379 // iso_url. In this case we can still verify the checksum but we should not 380 // delete the file if the checksum fails. Instead we'll just error and let the 381 // user fix the checksum. 382 func TestDownloadFileUrl(t *testing.T) { 383 cwd, err := os.Getwd() 384 if err != nil { 385 t.Fatalf("Unable to detect working directory: %s", err) 386 } 387 cwd = filepath.ToSlash(cwd) 388 389 // source_path is a file path and source is a network path 390 sourcePath := fmt.Sprintf("%s/test-fixtures/fileurl/%s", cwd, "cake") 391 392 filePrefix := "file://" 393 if runtime.GOOS == "windows" { 394 filePrefix += "/" 395 } 396 397 source := fmt.Sprintf(filePrefix + sourcePath) 398 t.Logf("Trying to download %s", source) 399 400 config := &DownloadConfig{ 401 Url: source, 402 // This should be wrong. We want to make sure we don't delete 403 Checksum: []byte("nope"), 404 Hash: HashForType("sha256"), 405 CopyFile: false, 406 } 407 408 client := NewDownloadClient(config) 409 410 // Verify that we fail to match the checksum 411 _, err = client.Get() 412 if err.Error() != "checksums didn't match expected: 6e6f7065" { 413 t.Fatalf("Unexpected failure; expected checksum not to match. Error was \"%v\"", err) 414 } 415 416 if _, err = os.Stat(sourcePath); err != nil { 417 t.Errorf("Could not stat source file: %s", sourcePath) 418 } 419 } 420 421 // SimulateFileUriDownload is a simple utility function that converts a uri 422 // into a testable file path whilst ignoring a correct checksum match, stripping 423 // UNC path info, and then calling stat to ensure the correct file exists. 424 // (used by TestFileUriTransforms) 425 func SimulateFileUriDownload(t *testing.T, uri string) (string, error) { 426 // source_path is a file path and source is a network path 427 source := fmt.Sprintf(uri) 428 t.Logf("Trying to download %s", source) 429 430 config := &DownloadConfig{ 431 Url: source, 432 // This should be wrong. We want to make sure we don't delete 433 Checksum: []byte("nope"), 434 Hash: HashForType("sha256"), 435 CopyFile: false, 436 } 437 438 // go go go 439 client := NewDownloadClient(config) 440 path, err := client.Get() 441 442 // ignore any non-important checksum errors if it's not a unc path 443 if !strings.HasPrefix(path, "\\\\") && err.Error() != "checksums didn't match expected: 6e6f7065" { 444 t.Fatalf("Unexpected failure; expected checksum not to match") 445 } 446 447 // if it's a unc path, then remove the host and share name so we don't have 448 // to force the user to enable ADMIN$ and Windows File Sharing 449 if strings.HasPrefix(path, "\\\\") { 450 res := strings.SplitN(path, "/", 3) 451 path = "/" + res[2] 452 } 453 454 if _, err = os.Stat(path); err != nil { 455 t.Errorf("Could not stat source file: %s", path) 456 } 457 return path, err 458 } 459 460 // TestFileUriTransforms tests the case where we use a local file uri 461 // for iso_url. There's a few different formats that a file uri can exist as 462 // and so we try to test the most useful and common ones. 463 func TestFileUriTransforms(t *testing.T) { 464 const testpath = /* have your */ "test-fixtures/fileurl/cake" /* and eat it too */ 465 const host = "localhost" 466 467 var cwd string 468 var volume string 469 var share string 470 471 cwd, err := os.Getwd() 472 if err != nil { 473 t.Fatalf("Unable to detect working directory: %s", err) 474 return 475 } 476 cwd = filepath.ToSlash(cwd) 477 volume = filepath.VolumeName(cwd) 478 share = volume 479 480 // if a volume was found (on windows), replace the ':' from 481 // C: to C$ to convert it into a hidden windows share. 482 if len(share) > 1 && share[len(share)-1] == ':' { 483 share = share[:len(share)-1] + "$" 484 } 485 cwd = cwd[len(volume):] 486 487 t.Logf("TestFileUriTransforms : Running with cwd : '%s'", cwd) 488 t.Logf("TestFileUriTransforms : Running with volume : '%s'", volume) 489 490 // ./relative/path -> ./relative/path 491 // /absolute/path -> /absolute/path 492 // c:/windows/absolute -> c:/windows/absolute 493 testcases := []string{ 494 "./%s", 495 cwd + "/%s", 496 volume + cwd + "/%s", 497 } 498 499 // all regular slashed testcases 500 for _, testcase := range testcases { 501 uri := "file://" + fmt.Sprintf(testcase, testpath) 502 t.Logf("TestFileUriTransforms : Trying Uri '%s'", uri) 503 res, err := SimulateFileUriDownload(t, uri) 504 if err != nil { 505 t.Errorf("Unable to transform uri '%s' into a path : %v", uri, err) 506 } 507 t.Logf("TestFileUriTransforms : Result Path '%s'", res) 508 } 509 510 // smb protocol depends on platform support which currently 511 // only exists on windows. 512 if runtime.GOOS == "windows" { 513 // ...and finally the oddball windows native path 514 // smb://host/sharename/file -> \\host\sharename\file 515 testcase := host + "/" + share + "/" + cwd[1:] + "/%s" 516 uri := "smb://" + fmt.Sprintf(testcase, testpath) 517 t.Logf("TestFileUriTransforms : Trying Uri '%s'", uri) 518 res, err := SimulateFileUriDownload(t, uri) 519 if err != nil { 520 t.Errorf("Unable to transform uri '%s' into a path", uri) 521 return 522 } 523 t.Logf("TestFileUriTransforms : Result Path '%s'", res) 524 } 525 }