github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/images_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiserver_test
     5  
     6  import (
     7  	"crypto/sha256"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"net/http"
    12  	"net/url"
    13  	"strings"
    14  
    15  	"github.com/juju/testing"
    16  	jc "github.com/juju/testing/checkers"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	apihttp "github.com/juju/juju/apiserver/http"
    20  	"github.com/juju/juju/apiserver/params"
    21  	containertesting "github.com/juju/juju/container/testing"
    22  	"github.com/juju/juju/environs/jujutest"
    23  	"github.com/juju/juju/state"
    24  	"github.com/juju/juju/state/imagestorage"
    25  )
    26  
    27  type imageSuite struct {
    28  	authHttpSuite
    29  	archiveContentType string
    30  	imageData          string
    31  	imageChecksum      string
    32  }
    33  
    34  var _ = gc.Suite(&imageSuite{})
    35  
    36  func (s *imageSuite) SetUpSuite(c *gc.C) {
    37  	s.authHttpSuite.SetUpSuite(c)
    38  	s.archiveContentType = "application/x-tar-gz"
    39  	s.imageData = "abc"
    40  	s.imageChecksum = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
    41  	testRoundTripper.RegisterForScheme("test")
    42  }
    43  
    44  func (s *imageSuite) TestDownloadMissingEnvUUIDPath(c *gc.C) {
    45  	s.storeFakeImage(c, s.State, "lxc", "trusty", "amd64")
    46  
    47  	s.envUUID = ""
    48  	url := s.imageURL(c, "lxc", "trusty", "amd64")
    49  	c.Assert(url.Path, jc.HasPrefix, "/environment//images")
    50  
    51  	response, err := s.downloadRequest(c, url)
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	s.testDownload(c, response)
    54  }
    55  
    56  func (s *imageSuite) TestDownloadEnvironmentPath(c *gc.C) {
    57  	s.storeFakeImage(c, s.State, "lxc", "trusty", "amd64")
    58  
    59  	url := s.imageURL(c, "lxc", "trusty", "amd64")
    60  	c.Assert(url.Path, jc.HasPrefix, fmt.Sprintf("/environment/%s/", s.State.EnvironUUID()))
    61  
    62  	response, err := s.downloadRequest(c, url)
    63  	c.Assert(err, jc.ErrorIsNil)
    64  	s.testDownload(c, response)
    65  }
    66  
    67  func (s *imageSuite) TestDownloadOtherEnvironmentPath(c *gc.C) {
    68  	envState := s.setupOtherEnvironment(c)
    69  	s.storeFakeImage(c, envState, "lxc", "trusty", "amd64")
    70  
    71  	url := s.imageURL(c, "lxc", "trusty", "amd64")
    72  	c.Assert(url.Path, jc.HasPrefix, fmt.Sprintf("/environment/%s/", envState.EnvironUUID()))
    73  
    74  	response, err := s.downloadRequest(c, url)
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	s.testDownload(c, response)
    77  }
    78  
    79  func (s *imageSuite) TestDownloadRejectsWrongEnvUUIDPath(c *gc.C) {
    80  	s.envUUID = "dead-beef-123456"
    81  	url := s.imageURL(c, "lxc", "trusty", "amd64")
    82  	response, err := s.downloadRequest(c, url)
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	c.Assert(err, gc.IsNil)
    85  	s.assertErrorResponse(c, response, http.StatusNotFound, `unknown environment: "dead-beef-123456"`)
    86  }
    87  
    88  // This provides the content for code accessing test:///... URLs. This allows
    89  // us to set the responses for things like image queries.
    90  var testRoundTripper = &jujutest.ProxyRoundTripper{}
    91  
    92  func useTestImageData(files map[string]string) {
    93  	if files != nil {
    94  		testRoundTripper.Sub = jujutest.NewCannedRoundTripper(files, nil)
    95  	} else {
    96  		testRoundTripper.Sub = nil
    97  	}
    98  }
    99  
   100  func (s *imageSuite) TestDownloadFetchesAndCaches(c *gc.C) {
   101  	// Set up some image data for a fake server.
   102  	testing.PatchExecutable(c, s, "ubuntu-cloudimg-query", containertesting.FakeLxcURLScript)
   103  	useTestImageData(map[string]string{
   104  		"/trusty-released-amd64-root.tar.gz": s.imageData,
   105  		"/SHA256SUMS":                        s.imageChecksum + " *trusty-released-amd64-root.tar.gz",
   106  	})
   107  	defer func() {
   108  		useTestImageData(nil)
   109  	}()
   110  
   111  	// The image is not in imagestorage, so the download request causes
   112  	// the API server to search for the image on cloud-images, fetches it,
   113  	// and then cache it in imagestorage.
   114  	url := s.imageURL(c, "lxc", "trusty", "amd64")
   115  	response, err := s.downloadRequest(c, url)
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	data := s.testDownload(c, response)
   118  
   119  	metadata, cachedData := s.getImageFromStorage(c, s.State, "lxc", "trusty", "amd64")
   120  	c.Assert(metadata.Size, gc.Equals, int64(len(s.imageData)))
   121  	c.Assert(metadata.SHA256, gc.Equals, s.imageChecksum)
   122  	c.Assert(metadata.SourceURL, gc.Equals, "test://cloud-images/trusty-released-amd64-root.tar.gz")
   123  	c.Assert(string(data), gc.Equals, string(s.imageData))
   124  	c.Assert(string(data), gc.Equals, string(cachedData))
   125  }
   126  
   127  func (s *imageSuite) TestDownloadFetchChecksumMismatch(c *gc.C) {
   128  	// Set up some image data for a fake server.
   129  	testing.PatchExecutable(c, s, "ubuntu-cloudimg-query", containertesting.FakeLxcURLScript)
   130  	useTestImageData(map[string]string{
   131  		"/trusty-released-amd64-root.tar.gz": s.imageData,
   132  		"/SHA256SUMS":                        "different-checksum *trusty-released-amd64-root.tar.gz",
   133  	})
   134  	defer func() {
   135  		useTestImageData(nil)
   136  	}()
   137  
   138  	resp, err := s.downloadRequest(c, s.imageURL(c, "lxc", "trusty", "amd64"))
   139  	defer resp.Body.Close()
   140  	c.Assert(err, gc.IsNil)
   141  	s.assertErrorResponse(c, resp, http.StatusInternalServerError, ".* download checksum mismatch .*")
   142  }
   143  
   144  func (s *imageSuite) TestDownloadFetchNoSHA256File(c *gc.C) {
   145  	// Set up some image data for a fake server.
   146  	testing.PatchExecutable(c, s, "ubuntu-cloudimg-query", containertesting.FakeLxcURLScript)
   147  	useTestImageData(map[string]string{
   148  		"/trusty-released-amd64-root.tar.gz": s.imageData,
   149  	})
   150  	defer func() {
   151  		useTestImageData(nil)
   152  	}()
   153  
   154  	resp, err := s.downloadRequest(c, s.imageURL(c, "lxc", "trusty", "amd64"))
   155  	defer resp.Body.Close()
   156  	c.Assert(err, gc.IsNil)
   157  	s.assertErrorResponse(c, resp, http.StatusInternalServerError, ".* cannot find sha256 checksum .*")
   158  }
   159  
   160  func (s *imageSuite) testDownload(c *gc.C, resp *http.Response) []byte {
   161  	c.Check(resp.StatusCode, gc.Equals, http.StatusOK)
   162  	c.Check(resp.Header.Get("Digest"), gc.Equals, string(apihttp.DigestSHA)+"="+s.imageChecksum)
   163  	c.Check(resp.Header.Get("Content-Type"), gc.Equals, s.archiveContentType)
   164  	c.Check(resp.Header.Get("Content-Length"), gc.Equals, fmt.Sprintf("%v", len(s.imageData)))
   165  
   166  	defer resp.Body.Close()
   167  	data, err := ioutil.ReadAll(resp.Body)
   168  	c.Assert(err, gc.IsNil)
   169  
   170  	c.Assert(data, gc.HasLen, len(s.imageData))
   171  
   172  	hash := sha256.New()
   173  	hash.Write(data)
   174  	c.Assert(fmt.Sprintf("%x", hash.Sum(nil)), gc.Equals, s.imageChecksum)
   175  	return data
   176  }
   177  
   178  func (s *imageSuite) downloadRequest(c *gc.C, url *url.URL) (*http.Response, error) {
   179  	return s.sendRequest(c, "", "", "GET", url.String(), "", nil)
   180  }
   181  
   182  func (s *imageSuite) storeFakeImage(c *gc.C, st *state.State, kind, series, arch string) {
   183  	storage := st.ImageStorage()
   184  	metadata := &imagestorage.Metadata{
   185  		EnvUUID:   st.EnvironUUID(),
   186  		Kind:      kind,
   187  		Series:    series,
   188  		Arch:      arch,
   189  		Size:      int64(len(s.imageData)),
   190  		SHA256:    s.imageChecksum,
   191  		SourceURL: "http://path",
   192  	}
   193  	err := storage.AddImage(strings.NewReader(s.imageData), metadata)
   194  	c.Assert(err, gc.IsNil)
   195  }
   196  
   197  func (s *imageSuite) getImageFromStorage(c *gc.C, st *state.State, kind, series, arch string) (*imagestorage.Metadata, []byte) {
   198  	storage := st.ImageStorage()
   199  	metadata, r, err := storage.Image(kind, series, arch)
   200  	c.Assert(err, gc.IsNil)
   201  	data, err := ioutil.ReadAll(r)
   202  	r.Close()
   203  	c.Assert(err, gc.IsNil)
   204  	return metadata, data
   205  }
   206  
   207  func (s *imageSuite) imageURL(c *gc.C, kind, series, arch string) *url.URL {
   208  	uri := s.baseURL(c)
   209  	uri.Path = fmt.Sprintf("/environment/%s/images/%s/%s/%s/trusty-released-amd64-root.tar.gz", s.envUUID, kind, series, arch)
   210  	return uri
   211  }
   212  
   213  func (s *imageSuite) assertErrorResponse(c *gc.C, resp *http.Response, expCode int, expError string) {
   214  	body := assertResponse(c, resp, expCode, "application/json")
   215  	err := jsonImageResponse(c, body).Error
   216  	c.Assert(err, gc.NotNil)
   217  	c.Assert(err, gc.ErrorMatches, expError)
   218  }
   219  
   220  func jsonImageResponse(c *gc.C, body []byte) (jsonResponse params.ErrorResult) {
   221  	err := json.Unmarshal(body, &jsonResponse)
   222  	c.Assert(err, gc.IsNil)
   223  	return
   224  }