github.com/HashDataInc/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  }