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  }