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 }