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