529 lines
13 KiB
Go
529 lines
13 KiB
Go
package common
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/cheggaaa/pb"
|
|
)
|
|
|
|
func TestDownloadClientVerifyChecksum(t *testing.T) {
|
|
tf, err := ioutil.TempFile("", "packer")
|
|
if err != nil {
|
|
t.Fatalf("tempfile error: %s", err)
|
|
}
|
|
defer os.Remove(tf.Name())
|
|
|
|
// "foo"
|
|
checksum, err := hex.DecodeString("acbd18db4cc2f85cedef654fccc4a4d8")
|
|
if err != nil {
|
|
t.Fatalf("decode err: %s", err)
|
|
}
|
|
|
|
// Write the file
|
|
tf.Write([]byte("foo"))
|
|
tf.Close()
|
|
|
|
config := &DownloadConfig{
|
|
Hash: md5.New(),
|
|
Checksum: checksum,
|
|
}
|
|
|
|
d := NewDownloadClient(config, *pb.New64(0))
|
|
result, err := d.VerifyChecksum(tf.Name())
|
|
if err != nil {
|
|
t.Fatalf("Verify err: %s", err)
|
|
}
|
|
|
|
if !result {
|
|
t.Fatal("didn't verify")
|
|
}
|
|
}
|
|
|
|
func TestDownloadClient_basic(t *testing.T) {
|
|
tf, _ := ioutil.TempFile("", "packer")
|
|
tf.Close()
|
|
defer os.Remove(tf.Name())
|
|
|
|
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
|
|
defer ts.Close()
|
|
|
|
client := NewDownloadClient(&DownloadConfig{
|
|
Url: ts.URL + "/basic.txt",
|
|
TargetPath: tf.Name(),
|
|
CopyFile: true,
|
|
}, *pb.New64(0))
|
|
|
|
path, err := client.Get()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
raw, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if string(raw) != "hello\n" {
|
|
t.Fatalf("bad: %s", string(raw))
|
|
}
|
|
}
|
|
|
|
func TestDownloadClient_checksumBad(t *testing.T) {
|
|
checksum, err := hex.DecodeString("b2946ac92492d2347c6235b4d2611184")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
tf, _ := ioutil.TempFile("", "packer")
|
|
tf.Close()
|
|
defer os.Remove(tf.Name())
|
|
|
|
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
|
|
defer ts.Close()
|
|
|
|
client := NewDownloadClient(&DownloadConfig{
|
|
Url: ts.URL + "/basic.txt",
|
|
TargetPath: tf.Name(),
|
|
Hash: HashForType("md5"),
|
|
Checksum: checksum,
|
|
CopyFile: true,
|
|
}, *pb.New64(0))
|
|
|
|
if _, err := client.Get(); err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestDownloadClient_checksumGood(t *testing.T) {
|
|
checksum, err := hex.DecodeString("b1946ac92492d2347c6235b4d2611184")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
tf, _ := ioutil.TempFile("", "packer")
|
|
tf.Close()
|
|
defer os.Remove(tf.Name())
|
|
|
|
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
|
|
defer ts.Close()
|
|
|
|
client := NewDownloadClient(&DownloadConfig{
|
|
Url: ts.URL + "/basic.txt",
|
|
TargetPath: tf.Name(),
|
|
Hash: HashForType("md5"),
|
|
Checksum: checksum,
|
|
CopyFile: true,
|
|
}, *pb.New64(0))
|
|
|
|
path, err := client.Get()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
raw, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if string(raw) != "hello\n" {
|
|
t.Fatalf("bad: %s", string(raw))
|
|
}
|
|
}
|
|
|
|
func TestDownloadClient_checksumNoDownload(t *testing.T) {
|
|
checksum, err := hex.DecodeString("3740570a423feec44c2a759225a9fcf9")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
|
|
defer ts.Close()
|
|
|
|
client := NewDownloadClient(&DownloadConfig{
|
|
Url: ts.URL + "/basic.txt",
|
|
TargetPath: "./test-fixtures/root/another.txt",
|
|
Hash: HashForType("md5"),
|
|
Checksum: checksum,
|
|
CopyFile: true,
|
|
}, *pb.New64(0))
|
|
|
|
path, err := client.Get()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
raw, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// If this says "hello" it means we downloaded it. We faked out
|
|
// the downloader above by giving it the checksum for "another", but
|
|
// requested the download of "hello"
|
|
if string(raw) != "another\n" {
|
|
t.Fatalf("bad: %s", string(raw))
|
|
}
|
|
}
|
|
|
|
func TestDownloadClient_notFound(t *testing.T) {
|
|
tf, _ := ioutil.TempFile("", "packer")
|
|
tf.Close()
|
|
defer os.Remove(tf.Name())
|
|
|
|
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
|
|
defer ts.Close()
|
|
|
|
client := NewDownloadClient(&DownloadConfig{
|
|
Url: ts.URL + "/not-found.txt",
|
|
TargetPath: tf.Name(),
|
|
}, *pb.New64(0))
|
|
|
|
if _, err := client.Get(); err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestDownloadClient_resume(t *testing.T) {
|
|
tf, _ := ioutil.TempFile("", "packer")
|
|
tf.Write([]byte("w"))
|
|
tf.Close()
|
|
defer os.Remove(tf.Name())
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
|
if r.Method == "HEAD" {
|
|
rw.Header().Set("Accept-Ranges", "bytes")
|
|
rw.WriteHeader(204)
|
|
return
|
|
}
|
|
|
|
http.ServeFile(rw, r, "./test-fixtures/root/basic.txt")
|
|
}))
|
|
defer ts.Close()
|
|
|
|
client := NewDownloadClient(&DownloadConfig{
|
|
Url: ts.URL,
|
|
TargetPath: tf.Name(),
|
|
CopyFile: true,
|
|
}, *pb.New64(0))
|
|
|
|
path, err := client.Get()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
raw, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if string(raw) != "wello\n" {
|
|
t.Fatalf("bad: %s", string(raw))
|
|
}
|
|
}
|
|
|
|
func TestDownloadClient_usesDefaultUserAgent(t *testing.T) {
|
|
tf, err := ioutil.TempFile("", "packer")
|
|
if err != nil {
|
|
t.Fatalf("tempfile error: %s", err)
|
|
}
|
|
tf.Close()
|
|
defer os.Remove(tf.Name())
|
|
|
|
defaultUserAgent := ""
|
|
asserted := false
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if defaultUserAgent == "" {
|
|
defaultUserAgent = r.UserAgent()
|
|
} else {
|
|
incomingUserAgent := r.UserAgent()
|
|
if incomingUserAgent != defaultUserAgent {
|
|
t.Fatalf("Expected user agent %s, got: %s", defaultUserAgent, incomingUserAgent)
|
|
}
|
|
|
|
asserted = true
|
|
}
|
|
}))
|
|
|
|
req, err := http.NewRequest("GET", server.URL, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
httpClient := &http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
},
|
|
}
|
|
|
|
_, err = httpClient.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
config := &DownloadConfig{
|
|
Url: server.URL,
|
|
TargetPath: tf.Name(),
|
|
CopyFile: true,
|
|
}
|
|
|
|
client := NewDownloadClient(config, *pb.New64(0))
|
|
_, err = client.Get()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !asserted {
|
|
t.Fatal("User-Agent never observed")
|
|
}
|
|
}
|
|
|
|
func TestDownloadClient_setsUserAgent(t *testing.T) {
|
|
tf, err := ioutil.TempFile("", "packer")
|
|
if err != nil {
|
|
t.Fatalf("tempfile error: %s", err)
|
|
}
|
|
tf.Close()
|
|
defer os.Remove(tf.Name())
|
|
|
|
asserted := false
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
asserted = true
|
|
if r.UserAgent() != "fancy user agent" {
|
|
t.Fatalf("Expected useragent fancy user agent, got: %s", r.UserAgent())
|
|
}
|
|
}))
|
|
config := &DownloadConfig{
|
|
Url: server.URL,
|
|
TargetPath: tf.Name(),
|
|
UserAgent: "fancy user agent",
|
|
CopyFile: true,
|
|
}
|
|
|
|
client := NewDownloadClient(config, *pb.New64(0))
|
|
_, err = client.Get()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !asserted {
|
|
t.Fatal("HTTP request never made")
|
|
}
|
|
}
|
|
|
|
func TestHashForType(t *testing.T) {
|
|
if h := HashForType("md5"); h == nil {
|
|
t.Fatalf("md5 hash is nil")
|
|
} else {
|
|
h.Write([]byte("foo"))
|
|
result := h.Sum(nil)
|
|
|
|
expected := "acbd18db4cc2f85cedef654fccc4a4d8"
|
|
actual := hex.EncodeToString(result)
|
|
if actual != expected {
|
|
t.Fatalf("bad hash: %s", actual)
|
|
}
|
|
}
|
|
|
|
if h := HashForType("sha1"); h == nil {
|
|
t.Fatalf("sha1 hash is nil")
|
|
} else {
|
|
h.Write([]byte("foo"))
|
|
result := h.Sum(nil)
|
|
|
|
expected := "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
|
|
actual := hex.EncodeToString(result)
|
|
if actual != expected {
|
|
t.Fatalf("bad hash: %s", actual)
|
|
}
|
|
}
|
|
|
|
if h := HashForType("sha256"); h == nil {
|
|
t.Fatalf("sha256 hash is nil")
|
|
} else {
|
|
h.Write([]byte("foo"))
|
|
result := h.Sum(nil)
|
|
|
|
expected := "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
|
|
actual := hex.EncodeToString(result)
|
|
if actual != expected {
|
|
t.Fatalf("bad hash: %s", actual)
|
|
}
|
|
}
|
|
|
|
if h := HashForType("sha512"); h == nil {
|
|
t.Fatalf("sha512 hash is nil")
|
|
} else {
|
|
h.Write([]byte("foo"))
|
|
result := h.Sum(nil)
|
|
|
|
expected := "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7"
|
|
actual := hex.EncodeToString(result)
|
|
if actual != expected {
|
|
t.Fatalf("bad hash: %s", actual)
|
|
}
|
|
}
|
|
|
|
if HashForType("fake") != nil {
|
|
t.Fatalf("fake hash is not nil")
|
|
}
|
|
}
|
|
|
|
// TestDownloadFileUrl tests a special case where we use a local file for
|
|
// iso_url. In this case we can still verify the checksum but we should not
|
|
// delete the file if the checksum fails. Instead we'll just error and let the
|
|
// user fix the checksum.
|
|
func TestDownloadFileUrl(t *testing.T) {
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatalf("Unable to detect working directory: %s", err)
|
|
}
|
|
cwd = filepath.ToSlash(cwd)
|
|
|
|
// source_path is a file path and source is a network path
|
|
sourcePath := fmt.Sprintf("%s/test-fixtures/fileurl/%s", cwd, "cake")
|
|
|
|
filePrefix := "file://"
|
|
if runtime.GOOS == "windows" {
|
|
filePrefix += "/"
|
|
}
|
|
|
|
source := fmt.Sprintf(filePrefix + sourcePath)
|
|
t.Logf("Trying to download %s", source)
|
|
|
|
config := &DownloadConfig{
|
|
Url: source,
|
|
// This should be wrong. We want to make sure we don't delete
|
|
Checksum: []byte("nope"),
|
|
Hash: HashForType("sha256"),
|
|
CopyFile: false,
|
|
}
|
|
|
|
client := NewDownloadClient(config, *pb.New64(0))
|
|
|
|
// Verify that we fail to match the checksum
|
|
_, err = client.Get()
|
|
if err.Error() != "checksums didn't match expected: 6e6f7065" {
|
|
t.Fatalf("Unexpected failure; expected checksum not to match. Error was \"%v\"", err)
|
|
}
|
|
|
|
if _, err = os.Stat(sourcePath); err != nil {
|
|
t.Errorf("Could not stat source file: %s", sourcePath)
|
|
}
|
|
}
|
|
|
|
// SimulateFileUriDownload is a simple utility function that converts a uri
|
|
// into a testable file path whilst ignoring a correct checksum match, stripping
|
|
// UNC path info, and then calling stat to ensure the correct file exists.
|
|
// (used by TestFileUriTransforms)
|
|
func SimulateFileUriDownload(t *testing.T, uri string) (string, error) {
|
|
// source_path is a file path and source is a network path
|
|
source := fmt.Sprintf(uri)
|
|
t.Logf("Trying to download %s", source)
|
|
|
|
config := &DownloadConfig{
|
|
Url: source,
|
|
// This should be wrong. We want to make sure we don't delete
|
|
Checksum: []byte("nope"),
|
|
Hash: HashForType("sha256"),
|
|
CopyFile: false,
|
|
}
|
|
|
|
// go go go
|
|
client := NewDownloadClient(config, *pb.New64(0))
|
|
path, err := client.Get()
|
|
|
|
// ignore any non-important checksum errors if it's not a unc path
|
|
if !strings.HasPrefix(path, "\\\\") && err.Error() != "checksums didn't match expected: 6e6f7065" {
|
|
t.Fatalf("Unexpected failure; expected checksum not to match")
|
|
}
|
|
|
|
// if it's a unc path, then remove the host and share name so we don't have
|
|
// to force the user to enable ADMIN$ and Windows File Sharing
|
|
if strings.HasPrefix(path, "\\\\") {
|
|
res := strings.SplitN(path, "/", 3)
|
|
path = "/" + res[2]
|
|
}
|
|
|
|
if _, err = os.Stat(path); err != nil {
|
|
t.Errorf("Could not stat source file: %s", path)
|
|
}
|
|
return path, err
|
|
}
|
|
|
|
// TestFileUriTransforms tests the case where we use a local file uri
|
|
// for iso_url. There's a few different formats that a file uri can exist as
|
|
// and so we try to test the most useful and common ones.
|
|
func TestFileUriTransforms(t *testing.T) {
|
|
const testpath = /* have your */ "test-fixtures/fileurl/cake" /* and eat it too */
|
|
const host = "localhost"
|
|
|
|
var cwd string
|
|
var volume string
|
|
var share string
|
|
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatalf("Unable to detect working directory: %s", err)
|
|
return
|
|
}
|
|
cwd = filepath.ToSlash(cwd)
|
|
volume = filepath.VolumeName(cwd)
|
|
share = volume
|
|
|
|
// if a volume was found (on windows), replace the ':' from
|
|
// C: to C$ to convert it into a hidden windows share.
|
|
if len(share) > 1 && share[len(share)-1] == ':' {
|
|
share = share[:len(share)-1] + "$"
|
|
}
|
|
cwd = cwd[len(volume):]
|
|
|
|
t.Logf("TestFileUriTransforms : Running with cwd : '%s'", cwd)
|
|
t.Logf("TestFileUriTransforms : Running with volume : '%s'", volume)
|
|
|
|
// ./relative/path -> ./relative/path
|
|
// /absolute/path -> /absolute/path
|
|
// c:/windows/absolute -> c:/windows/absolute
|
|
testcases := []string{
|
|
"./%s",
|
|
cwd + "/%s",
|
|
volume + cwd + "/%s",
|
|
}
|
|
|
|
// all regular slashed testcases
|
|
for _, testcase := range testcases {
|
|
uri := "file://" + fmt.Sprintf(testcase, testpath)
|
|
t.Logf("TestFileUriTransforms : Trying Uri '%s'", uri)
|
|
res, err := SimulateFileUriDownload(t, uri)
|
|
if err != nil {
|
|
t.Errorf("Unable to transform uri '%s' into a path : %v", uri, err)
|
|
}
|
|
t.Logf("TestFileUriTransforms : Result Path '%s'", res)
|
|
}
|
|
|
|
// smb protocol depends on platform support which currently
|
|
// only exists on windows.
|
|
if runtime.GOOS == "windows" {
|
|
// ...and finally the oddball windows native path
|
|
// smb://host/sharename/file -> \\host\sharename\file
|
|
testcase := host + "/" + share + "/" + cwd[1:] + "/%s"
|
|
uri := "smb://" + fmt.Sprintf(testcase, testpath)
|
|
t.Logf("TestFileUriTransforms : Trying Uri '%s'", uri)
|
|
res, err := SimulateFileUriDownload(t, uri)
|
|
if err != nil {
|
|
t.Errorf("Unable to transform uri '%s' into a path", uri)
|
|
return
|
|
}
|
|
t.Logf("TestFileUriTransforms : Result Path '%s'", res)
|
|
}
|
|
}
|