github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/apiserver/tools_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver_test 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "net/url" 12 "path" 13 14 "github.com/juju/utils" 15 gc "launchpad.net/gocheck" 16 17 "github.com/juju/juju/environs/tools" 18 toolstesting "github.com/juju/juju/environs/tools/testing" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/state/api/params" 21 coretools "github.com/juju/juju/tools" 22 "github.com/juju/juju/version" 23 ) 24 25 type toolsSuite struct { 26 authHttpSuite 27 } 28 29 var _ = gc.Suite(&toolsSuite{}) 30 31 func (s *toolsSuite) SetUpSuite(c *gc.C) { 32 s.JujuConnSuite.SetUpSuite(c) 33 s.archiveContentType = "application/x-tar-gz" 34 } 35 36 func (s *toolsSuite) TestToolsUploadedSecurely(c *gc.C) { 37 _, info, err := s.APIConn.Environ.StateInfo() 38 c.Assert(err, gc.IsNil) 39 uri := "http://" + info.Addrs[0] + "/tools" 40 _, err = s.sendRequest(c, "", "", "PUT", uri, "", nil) 41 c.Assert(err, gc.ErrorMatches, `.*malformed HTTP response.*`) 42 } 43 44 func (s *toolsSuite) TestRequiresAuth(c *gc.C) { 45 resp, err := s.sendRequest(c, "", "", "GET", s.toolsURI(c, ""), "", nil) 46 c.Assert(err, gc.IsNil) 47 s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized") 48 } 49 50 func (s *toolsSuite) TestRequiresPOST(c *gc.C) { 51 resp, err := s.authRequest(c, "PUT", s.toolsURI(c, ""), "", nil) 52 c.Assert(err, gc.IsNil) 53 s.assertErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "PUT"`) 54 } 55 56 func (s *toolsSuite) TestAuthRequiresUser(c *gc.C) { 57 // Add a machine and try to login. 58 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 59 c.Assert(err, gc.IsNil) 60 err = machine.SetProvisioned("foo", "fake_nonce", nil) 61 c.Assert(err, gc.IsNil) 62 password, err := utils.RandomPassword() 63 c.Assert(err, gc.IsNil) 64 err = machine.SetPassword(password) 65 c.Assert(err, gc.IsNil) 66 67 resp, err := s.sendRequest(c, machine.Tag(), password, "POST", s.toolsURI(c, ""), "", nil) 68 c.Assert(err, gc.IsNil) 69 s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized") 70 71 // Now try a user login. 72 resp, err = s.authRequest(c, "POST", s.toolsURI(c, ""), "", nil) 73 c.Assert(err, gc.IsNil) 74 s.assertErrorResponse(c, resp, http.StatusBadRequest, "expected binaryVersion argument") 75 } 76 77 func (s *toolsSuite) TestUploadRequiresVersion(c *gc.C) { 78 resp, err := s.authRequest(c, "POST", s.toolsURI(c, ""), "", nil) 79 c.Assert(err, gc.IsNil) 80 s.assertErrorResponse(c, resp, http.StatusBadRequest, "expected binaryVersion argument") 81 } 82 83 func (s *toolsSuite) TestUploadFailsWithNoTools(c *gc.C) { 84 // Create an empty file. 85 tempFile, err := ioutil.TempFile(c.MkDir(), "tools") 86 c.Assert(err, gc.IsNil) 87 88 resp, err := s.uploadRequest(c, s.toolsURI(c, "?binaryVersion=1.18.0-quantal-amd64"), true, tempFile.Name()) 89 c.Assert(err, gc.IsNil) 90 s.assertErrorResponse(c, resp, http.StatusBadRequest, "no tools uploaded") 91 } 92 93 func (s *toolsSuite) TestUploadFailsWithInvalidContentType(c *gc.C) { 94 // Create an empty file. 95 tempFile, err := ioutil.TempFile(c.MkDir(), "tools") 96 c.Assert(err, gc.IsNil) 97 98 // Now try with the default Content-Type. 99 resp, err := s.uploadRequest(c, s.toolsURI(c, "?binaryVersion=1.18.0-quantal-amd64"), false, tempFile.Name()) 100 c.Assert(err, gc.IsNil) 101 s.assertErrorResponse( 102 c, resp, http.StatusBadRequest, "expected Content-Type: application/x-tar-gz, got: application/octet-stream") 103 } 104 105 func (s *toolsSuite) setupToolsForUpload(c *gc.C) (coretools.List, version.Binary, string) { 106 localStorage := c.MkDir() 107 vers := version.MustParseBinary("1.9.0-quantal-amd64") 108 versionStrings := []string{vers.String()} 109 expectedTools := toolstesting.MakeToolsWithCheckSum(c, localStorage, "releases", versionStrings) 110 toolsFile := tools.StorageName(vers) 111 return expectedTools, vers, path.Join(localStorage, toolsFile) 112 } 113 114 func (s *toolsSuite) TestUpload(c *gc.C) { 115 // Make some fake tools. 116 expectedTools, vers, toolPath := s.setupToolsForUpload(c) 117 // Now try uploading them. 118 resp, err := s.uploadRequest( 119 c, s.toolsURI(c, "?binaryVersion="+vers.String()), true, toolPath) 120 c.Assert(err, gc.IsNil) 121 122 // Check the response. 123 stor := s.Conn.Environ.Storage() 124 toolsURL, err := stor.URL(tools.StorageName(vers)) 125 c.Assert(err, gc.IsNil) 126 expectedTools[0].URL = toolsURL 127 s.assertUploadResponse(c, resp, expectedTools[0]) 128 129 // Check the contents. 130 r, err := stor.Get(tools.StorageName(vers)) 131 c.Assert(err, gc.IsNil) 132 uploadedData, err := ioutil.ReadAll(r) 133 c.Assert(err, gc.IsNil) 134 expectedData, err := ioutil.ReadFile(toolPath) 135 c.Assert(err, gc.IsNil) 136 c.Assert(uploadedData, gc.DeepEquals, expectedData) 137 } 138 139 func (s *toolsSuite) TestUploadAllowsTopLevelPath(c *gc.C) { 140 // Backwards compatibility check, that we can upload tools to 141 // https://host:port/tools 142 expectedTools, vers, toolPath := s.setupToolsForUpload(c) 143 url := s.toolsURL(c, "binaryVersion="+vers.String()) 144 url.Path = "/tools" 145 resp, err := s.uploadRequest(c, url.String(), true, toolPath) 146 c.Assert(err, gc.IsNil) 147 // Check the response. 148 stor := s.Conn.Environ.Storage() 149 toolsURL, err := stor.URL(tools.StorageName(vers)) 150 c.Assert(err, gc.IsNil) 151 expectedTools[0].URL = toolsURL 152 s.assertUploadResponse(c, resp, expectedTools[0]) 153 } 154 155 func (s *toolsSuite) TestUploadAllowsEnvUUIDPath(c *gc.C) { 156 // Check that we can upload tools to https://host:port/ENVUUID/tools 157 environ, err := s.State.Environment() 158 c.Assert(err, gc.IsNil) 159 expectedTools, vers, toolPath := s.setupToolsForUpload(c) 160 url := s.toolsURL(c, "binaryVersion="+vers.String()) 161 url.Path = fmt.Sprintf("/environment/%s/tools", environ.UUID()) 162 resp, err := s.uploadRequest(c, url.String(), true, toolPath) 163 c.Assert(err, gc.IsNil) 164 // Check the response. 165 stor := s.Conn.Environ.Storage() 166 toolsURL, err := stor.URL(tools.StorageName(vers)) 167 c.Assert(err, gc.IsNil) 168 expectedTools[0].URL = toolsURL 169 s.assertUploadResponse(c, resp, expectedTools[0]) 170 } 171 172 func (s *toolsSuite) TestUploadRejectsWrongEnvUUIDPath(c *gc.C) { 173 // Check that we cannot access the tools at https://host:port/BADENVUUID/tools 174 url := s.toolsURL(c, "") 175 url.Path = "/environment/dead-beef-123456/tools" 176 resp, err := s.authRequest(c, "POST", url.String(), "", nil) 177 c.Assert(err, gc.IsNil) 178 s.assertErrorResponse(c, resp, http.StatusNotFound, `unknown environment: "dead-beef-123456"`) 179 } 180 181 func (s *toolsSuite) TestUploadFakeSeries(c *gc.C) { 182 // Make some fake tools. 183 expectedTools, vers, toolPath := s.setupToolsForUpload(c) 184 // Now try uploading them. 185 params := "?binaryVersion=" + vers.String() + "&series=precise,trusty" 186 resp, err := s.uploadRequest(c, s.toolsURI(c, params), true, toolPath) 187 c.Assert(err, gc.IsNil) 188 189 // Check the response. 190 stor := s.Conn.Environ.Storage() 191 toolsURL, err := stor.URL(tools.StorageName(vers)) 192 c.Assert(err, gc.IsNil) 193 expectedTools[0].URL = toolsURL 194 s.assertUploadResponse(c, resp, expectedTools[0]) 195 196 // Check the contents. 197 for _, series := range []string{"precise", "quantal", "trusty"} { 198 toolsVersion := vers 199 toolsVersion.Series = series 200 r, err := stor.Get(tools.StorageName(toolsVersion)) 201 c.Assert(err, gc.IsNil) 202 uploadedData, err := ioutil.ReadAll(r) 203 c.Assert(err, gc.IsNil) 204 expectedData, err := ioutil.ReadFile(toolPath) 205 c.Assert(err, gc.IsNil) 206 c.Assert(uploadedData, gc.DeepEquals, expectedData) 207 } 208 } 209 210 func (s *toolsSuite) toolsURL(c *gc.C, query string) *url.URL { 211 uri := s.baseURL(c) 212 uri.Path += "/tools" 213 uri.RawQuery = query 214 return uri 215 } 216 217 func (s *toolsSuite) toolsURI(c *gc.C, query string) string { 218 if query != "" && query[0] == '?' { 219 query = query[1:] 220 } 221 return s.toolsURL(c, query).String() 222 } 223 224 func (s *toolsSuite) assertUploadResponse(c *gc.C, resp *http.Response, agentTools *coretools.Tools) { 225 body := assertResponse(c, resp, http.StatusOK, "application/json") 226 toolsResult := jsonToolsResponse(c, body) 227 c.Check(toolsResult.Error, gc.IsNil) 228 c.Check(toolsResult.Tools, gc.DeepEquals, agentTools) 229 } 230 231 func (s *toolsSuite) assertGetFileResponse(c *gc.C, resp *http.Response, expBody, expContentType string) { 232 body := assertResponse(c, resp, http.StatusOK, expContentType) 233 c.Check(string(body), gc.Equals, expBody) 234 } 235 236 func (s *toolsSuite) assertErrorResponse(c *gc.C, resp *http.Response, expCode int, expError string) { 237 body := assertResponse(c, resp, expCode, "application/json") 238 err := jsonToolsResponse(c, body).Error 239 c.Assert(err, gc.NotNil) 240 c.Check(err, gc.ErrorMatches, expError) 241 } 242 243 func jsonToolsResponse(c *gc.C, body []byte) (jsonResponse params.ToolsResult) { 244 err := json.Unmarshal(body, &jsonResponse) 245 c.Assert(err, gc.IsNil) 246 return 247 }