github.com/go-chef/chef@v0.30.1/cookbook_download_test.go (about)

     1  //
     2  //  Author:: Salim Afiune <afiune@chef.io>
     3  //
     4  
     5  package chef
     6  
     7  import (
     8  	"fmt"
     9  	"net/http"
    10  	"os"
    11  	"path"
    12  	"testing"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  const emptyCookbookResponseFile = "test/empty_cookbook.json"
    18  
    19  func TestCookbooksDownloadThatDoesNotExist(t *testing.T) {
    20  	setup()
    21  	defer teardown()
    22  
    23  	mux.HandleFunc("/cookbooks/foo/2.1.0", func(w http.ResponseWriter, r *http.Request) {
    24  		http.Error(w, "Not Found", 404)
    25  	})
    26  
    27  	err := client.Cookbooks.Download("foo", "2.1.0")
    28  	if assert.NotNil(t, err) {
    29  		assert.Contains(t, err.Error(), "404")
    30  	}
    31  }
    32  
    33  func TestCookbooksDownloadCorrectsLatestVersion(t *testing.T) {
    34  	setup()
    35  	defer teardown()
    36  
    37  	mux.HandleFunc("/cookbooks/foo/_latest", func(w http.ResponseWriter, r *http.Request) {
    38  		http.Error(w, "Not Found", 404)
    39  	})
    40  
    41  	err := client.Cookbooks.Download("foo", "")
    42  	if assert.NotNil(t, err) {
    43  		assert.Contains(t, err.Error(), "404")
    44  	}
    45  
    46  	err = client.Cookbooks.Download("foo", "latest")
    47  	if assert.NotNil(t, err) {
    48  		assert.Contains(t, err.Error(), "404")
    49  	}
    50  
    51  	err = client.Cookbooks.Download("foo", "_latest")
    52  	if assert.NotNil(t, err) {
    53  		assert.Contains(t, err.Error(), "404")
    54  	}
    55  }
    56  
    57  func TestCookbooksDownloadEmptyWithVersion(t *testing.T) {
    58  	setup()
    59  	defer teardown()
    60  
    61  	cbookResp, err := os.ReadFile(emptyCookbookResponseFile)
    62  	if err != nil {
    63  		t.Error(err)
    64  	}
    65  
    66  	mux.HandleFunc("/cookbooks/foo/0.2.0", func(w http.ResponseWriter, r *http.Request) {
    67  		fmt.Fprintf(w, string(cbookResp))
    68  	})
    69  
    70  	err = client.Cookbooks.Download("foo", "0.2.0")
    71  	assert.Nil(t, err)
    72  }
    73  
    74  func cookbookData() string {
    75  	return `
    76  
    77  {
    78    "version": "0.2.1",
    79    "name": "foo-0.2.1",
    80    "cookbook_name": "foo",
    81    "frozen?": false,
    82    "chef_type": "cookbook_version",
    83    "json_class": "Chef::CookbookVersion",
    84    "attributes": [],
    85    "definitions": [],
    86    "files": [],
    87    "libraries": [],
    88    "providers": [],
    89    "recipes": [
    90      {
    91        "name": "default.rb",
    92        "path": "recipes/default.rb",
    93        "checksum": "8e751ed8663cb9b97499956b6a20b0de",
    94        "specificity": "default",
    95        "url": "` + server.URL + `/bookshelf/foo/default_rb"
    96      }
    97    ],
    98    "resources": [],
    99    "root_files": [
   100      {
   101        "name": "metadata.rb",
   102        "path": "metadata.rb",
   103        "checksum": "6607f3131919e82dc4ba4c026fcfee9f",
   104        "specificity": "default",
   105        "url": "` + server.URL + `/bookshelf/foo/metadata_rb"
   106      }
   107    ],
   108    "templates": [],
   109    "metadata": {},
   110    "access": {}
   111  } `
   112  }
   113  
   114  func TestCookbooksDownloadTo(t *testing.T) {
   115  	setup()
   116  	defer teardown()
   117  
   118  	mockedCookbookResponseFile := cookbookData()
   119  	tempDir, err := os.MkdirTemp("", "foo-cookbook")
   120  	if err != nil {
   121  		t.Error(err)
   122  	}
   123  	defer os.RemoveAll(tempDir) // clean up
   124  
   125  	mux.HandleFunc("/cookbooks/foo/0.2.1", func(w http.ResponseWriter, r *http.Request) {
   126  		fmt.Fprintf(w, string(mockedCookbookResponseFile))
   127  	})
   128  	mux.HandleFunc("/bookshelf/foo/metadata_rb", func(w http.ResponseWriter, r *http.Request) {
   129  		fmt.Fprintf(w, "name 'foo'")
   130  	})
   131  	mux.HandleFunc("/bookshelf/foo/default_rb", func(w http.ResponseWriter, r *http.Request) {
   132  		fmt.Fprintf(w, "log 'this is a resource'")
   133  	})
   134  
   135  	err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir)
   136  	assert.Nil(t, err)
   137  
   138  	var (
   139  		cookbookPath = path.Join(tempDir, "foo-0.2.1")
   140  		metadataPath = path.Join(cookbookPath, "metadata.rb")
   141  		recipesPath  = path.Join(cookbookPath, "recipes")
   142  		defaultPath  = path.Join(recipesPath, "default.rb")
   143  	)
   144  	assert.DirExistsf(t, cookbookPath, "the cookbook directory should exist")
   145  	assert.DirExistsf(t, recipesPath, "the recipes directory should exist")
   146  	if assert.FileExistsf(t, metadataPath, "a metadata.rb file should exist") {
   147  		metadataBytes, err := os.ReadFile(metadataPath)
   148  		assert.Nil(t, err)
   149  		assert.Equal(t, "name 'foo'", string(metadataBytes))
   150  	}
   151  	if assert.FileExistsf(t, defaultPath, "the default.rb recipes should exist") {
   152  		recipeBytes, err := os.ReadFile(defaultPath)
   153  		assert.Nil(t, err)
   154  		assert.Equal(t, "log 'this is a resource'", string(recipeBytes))
   155  	}
   156  
   157  }
   158  
   159  func TestCookbooksDownloadTo_caching(t *testing.T) {
   160  	setup()
   161  	defer teardown()
   162  
   163  	mockedCookbookResponseFile := cookbookData()
   164  	tempDir, err := os.MkdirTemp("", "foo-cookbook")
   165  	if err != nil {
   166  		t.Error(err)
   167  	}
   168  	defer os.RemoveAll(tempDir) // clean up
   169  
   170  	mux.HandleFunc("/cookbooks/foo/0.2.1", func(w http.ResponseWriter, r *http.Request) {
   171  		fmt.Fprintf(w, string(mockedCookbookResponseFile))
   172  	})
   173  	mux.HandleFunc("/bookshelf/foo/metadata_rb", func(w http.ResponseWriter, r *http.Request) {
   174  		fmt.Fprintf(w, "name 'foo'")
   175  	})
   176  	mux.HandleFunc("/bookshelf/foo/default_rb", func(w http.ResponseWriter, r *http.Request) {
   177  		fmt.Fprintf(w, "log 'this is a resource'")
   178  	})
   179  
   180  	err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir)
   181  	assert.Nil(t, err)
   182  
   183  	var (
   184  		cookbookPath = path.Join(tempDir, "foo-0.2.1")
   185  		metadataPath = path.Join(cookbookPath, "metadata.rb")
   186  		recipesPath  = path.Join(cookbookPath, "recipes")
   187  		defaultPath  = path.Join(recipesPath, "default.rb")
   188  	)
   189  	assert.DirExistsf(t, cookbookPath, "the cookbook directory should exist")
   190  	assert.DirExistsf(t, recipesPath, "the recipes directory should exist")
   191  	if assert.FileExistsf(t, metadataPath, "a metadata.rb file should exist") {
   192  		metadataBytes, err := os.ReadFile(metadataPath)
   193  		assert.Nil(t, err)
   194  		assert.Equal(t, "name 'foo'", string(metadataBytes))
   195  	}
   196  	if assert.FileExistsf(t, defaultPath, "the default.rb recipes should exist") {
   197  		recipeBytes, err := os.ReadFile(defaultPath)
   198  		assert.Nil(t, err)
   199  		assert.Equal(t, "log 'this is a resource'", string(recipeBytes))
   200  	}
   201  
   202  	// Capture the timestamps to ensure that on-redownload of unchanged cookook,
   203  	// they show no modification (using this as a proxy to determine whether
   204  	// the file has been re-downloaded).
   205  	defaultPathInfo, err := os.Stat(defaultPath)
   206  	assert.Nil(t, err)
   207  
   208  	metaDataInfo, err := os.Stat(metadataPath)
   209  	assert.Nil(t, err)
   210  
   211  	err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir)
   212  	assert.Nil(t, err)
   213  
   214  	defaultPathNewInfo, err := os.Stat(defaultPath)
   215  	assert.Nil(t, err)
   216  
   217  	metaDataNewInfo, err := os.Stat(metadataPath)
   218  	assert.Nil(t, err)
   219  
   220  	err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir)
   221  	assert.Nil(t, err)
   222  
   223  	// If the file was not re-downloaded, we would expect the timestamp
   224  	// to remain unchanged.
   225  	assert.Equal(t, defaultPathInfo.ModTime(), defaultPathNewInfo.ModTime())
   226  	assert.Equal(t, metaDataInfo.ModTime(), metaDataNewInfo.ModTime())
   227  
   228  	err = os.Truncate(metadataPath, 1)
   229  	assert.Nil(t, err)
   230  
   231  	err = os.Chtimes(metadataPath, metaDataInfo.ModTime(), metaDataInfo.ModTime())
   232  	assert.Nil(t, err)
   233  
   234  	err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir)
   235  	assert.Nil(t, err)
   236  
   237  	metaDataNewInfo, err = os.Stat(metadataPath)
   238  	assert.Nil(t, err)
   239  
   240  	assert.NotEqual(t, metaDataInfo.ModTime(), metaDataNewInfo.ModTime())
   241  
   242  	// Finally, make sure the modified-and-replaced metadata.rb is matching
   243  	// what we expect after we have redownloaded the cookbook:
   244  	if assert.FileExistsf(t, metadataPath, "a metadata.rb file should exist") {
   245  		metadataBytes, err := os.ReadFile(metadataPath)
   246  		assert.Nil(t, err)
   247  		assert.Equal(t, "name 'foo'", string(metadataBytes))
   248  	}
   249  }
   250  
   251  func TestVerifyMD5Checksum(t *testing.T) {
   252  	tempDir, err := os.MkdirTemp("", "md5-test")
   253  	if err != nil {
   254  		t.Error(err)
   255  	}
   256  	defer os.RemoveAll(tempDir) // clean up
   257  
   258  	var (
   259  		// if someone changes the test data,
   260  		// you have to also update the below md5 sum
   261  		testData = []byte("hello\nchef\n")
   262  		filePath = path.Join(tempDir, "dat")
   263  	)
   264  	err = os.WriteFile(filePath, testData, 0644)
   265  	assert.Nil(t, err)
   266  	assert.True(t, verifyMD5Checksum(filePath, "70bda176ac4db06f1f66f96ae0693be1"))
   267  }