github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/migrationtarget/client_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package migrationtarget_test 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "net/http" 12 "net/textproto" 13 "net/url" 14 "strings" 15 "time" 16 17 "github.com/juju/errors" 18 "github.com/juju/httprequest" 19 jujutesting "github.com/juju/testing" 20 jc "github.com/juju/testing/checkers" 21 "github.com/juju/version" 22 gc "gopkg.in/check.v1" 23 "gopkg.in/juju/charm.v6" 24 "gopkg.in/juju/names.v2" 25 26 "github.com/juju/juju/api/base" 27 apitesting "github.com/juju/juju/api/base/testing" 28 "github.com/juju/juju/api/migrationtarget" 29 "github.com/juju/juju/apiserver/params" 30 coremigration "github.com/juju/juju/core/migration" 31 "github.com/juju/juju/resource/resourcetesting" 32 "github.com/juju/juju/tools" 33 jujuversion "github.com/juju/juju/version" 34 ) 35 36 type ClientSuite struct { 37 jujutesting.IsolationSuite 38 } 39 40 var _ = gc.Suite(&ClientSuite{}) 41 42 func (s *ClientSuite) getClientAndStub(c *gc.C) (*migrationtarget.Client, *jujutesting.Stub) { 43 var stub jujutesting.Stub 44 apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 45 stub.AddCall(objType+"."+request, id, arg) 46 return errors.New("boom") 47 }) 48 client := migrationtarget.NewClient(apiCaller) 49 return client, &stub 50 } 51 52 func (s *ClientSuite) TestPrechecks(c *gc.C) { 53 client, stub := s.getClientAndStub(c) 54 55 ownerTag := names.NewUserTag("owner") 56 vers := version.MustParse("1.2.3") 57 controllerVers := version.MustParse("1.2.5") 58 59 err := client.Prechecks(coremigration.ModelInfo{ 60 UUID: "uuid", 61 Owner: ownerTag, 62 Name: "name", 63 AgentVersion: vers, 64 ControllerAgentVersion: controllerVers, 65 }) 66 c.Assert(err, gc.ErrorMatches, "boom") 67 68 expectedArg := params.MigrationModelInfo{ 69 UUID: "uuid", 70 Name: "name", 71 OwnerTag: ownerTag.String(), 72 AgentVersion: vers, 73 ControllerAgentVersion: controllerVers, 74 } 75 stub.CheckCalls(c, []jujutesting.StubCall{ 76 {"MigrationTarget.Prechecks", []interface{}{"", expectedArg}}, 77 }) 78 } 79 80 func (s *ClientSuite) TestImport(c *gc.C) { 81 client, stub := s.getClientAndStub(c) 82 83 err := client.Import([]byte("foo")) 84 85 expectedArg := params.SerializedModel{Bytes: []byte("foo")} 86 stub.CheckCalls(c, []jujutesting.StubCall{ 87 {"MigrationTarget.Import", []interface{}{"", expectedArg}}, 88 }) 89 c.Assert(err, gc.ErrorMatches, "boom") 90 } 91 92 func (s *ClientSuite) TestAbort(c *gc.C) { 93 client, stub := s.getClientAndStub(c) 94 95 uuid := "fake" 96 err := client.Abort(uuid) 97 s.AssertModelCall(c, stub, names.NewModelTag(uuid), "Abort", err, true) 98 } 99 100 func (s *ClientSuite) TestActivate(c *gc.C) { 101 client, stub := s.getClientAndStub(c) 102 103 uuid := "fake" 104 err := client.Activate(uuid) 105 s.AssertModelCall(c, stub, names.NewModelTag(uuid), "Activate", err, true) 106 } 107 108 func (s *ClientSuite) TestOpenLogTransferStream(c *gc.C) { 109 caller := fakeConnector{Stub: &jujutesting.Stub{}} 110 client := migrationtarget.NewClient(caller) 111 stream, err := client.OpenLogTransferStream("bad-dad") 112 c.Assert(stream, gc.IsNil) 113 c.Assert(err, gc.ErrorMatches, "sound hound") 114 115 caller.Stub.CheckCall(c, 0, "ConnectControllerStream", "/migrate/logtransfer", 116 url.Values{"jujuclientversion": {jujuversion.Current.String()}}, 117 http.Header{textproto.CanonicalMIMEHeaderKey(params.MigrationModelHTTPHeader): {"bad-dad"}}, 118 ) 119 } 120 121 func (s *ClientSuite) TestLatestLogTime(c *gc.C) { 122 var stub jujutesting.Stub 123 t1 := time.Date(2016, 12, 1, 10, 31, 0, 0, time.UTC) 124 125 apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 126 target, ok := result.(*time.Time) 127 c.Assert(ok, jc.IsTrue) 128 *target = t1 129 stub.AddCall(objType+"."+request, id, arg) 130 return nil 131 }) 132 client := migrationtarget.NewClient(apiCaller) 133 result, err := client.LatestLogTime("fake") 134 135 c.Assert(result, gc.Equals, t1) 136 s.AssertModelCall(c, &stub, names.NewModelTag("fake"), "LatestLogTime", err, false) 137 } 138 139 func (s *ClientSuite) TestLatestLogTimeError(c *gc.C) { 140 client, stub := s.getClientAndStub(c) 141 result, err := client.LatestLogTime("fake") 142 143 c.Assert(result, gc.Equals, time.Time{}) 144 s.AssertModelCall(c, stub, names.NewModelTag("fake"), "LatestLogTime", err, true) 145 } 146 147 func (s *ClientSuite) TestAdoptResources(c *gc.C) { 148 client, stub := s.getClientAndStub(c) 149 err := client.AdoptResources("the-model") 150 c.Assert(err, gc.ErrorMatches, "boom") 151 stub.CheckCall(c, 0, "MigrationTarget.AdoptResources", "", params.AdoptResourcesArgs{ 152 ModelTag: "model-the-model", 153 SourceControllerVersion: jujuversion.Current, 154 }) 155 } 156 157 func (s *ClientSuite) TestCheckMachines(c *gc.C) { 158 var stub jujutesting.Stub 159 apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 160 target, ok := result.(*params.ErrorResults) 161 c.Assert(ok, jc.IsTrue) 162 *target = params.ErrorResults{Results: []params.ErrorResult{ 163 {Error: ¶ms.Error{Message: "oops"}}, 164 {Error: ¶ms.Error{Message: "oh no"}}, 165 }} 166 stub.AddCall(objType+"."+request, id, arg) 167 return nil 168 }) 169 client := migrationtarget.NewClient(apiCaller) 170 results, err := client.CheckMachines("django") 171 c.Assert(results, gc.HasLen, 2) 172 c.Assert(results[0], gc.ErrorMatches, "oops") 173 c.Assert(results[1], gc.ErrorMatches, "oh no") 174 s.AssertModelCall(c, &stub, names.NewModelTag("django"), "CheckMachines", err, false) 175 } 176 177 func (s *ClientSuite) TestUploadCharm(c *gc.C) { 178 const charmBody = "charming" 179 curl := charm.MustParseURL("cs:~user/foo-2") 180 doer := newFakeDoer(c, params.CharmsResponse{ 181 CharmURL: curl.String(), 182 }) 183 caller := &fakeHTTPCaller{ 184 httpClient: &httprequest.Client{Doer: doer}, 185 } 186 client := migrationtarget.NewClient(caller) 187 outCurl, err := client.UploadCharm("uuid", curl, strings.NewReader(charmBody)) 188 c.Assert(err, jc.ErrorIsNil) 189 c.Assert(outCurl, gc.DeepEquals, curl) 190 c.Assert(doer.method, gc.Equals, "POST") 191 c.Assert(doer.url, gc.Equals, "/migrate/charms?revision=2&schema=cs&series=&user=user") 192 c.Assert(doer.body, gc.Equals, charmBody) 193 } 194 195 func (s *ClientSuite) TestUploadTools(c *gc.C) { 196 const toolsBody = "toolie" 197 vers := version.MustParseBinary("2.0.0-xenial-amd64") 198 someTools := &tools.Tools{Version: vers} 199 doer := newFakeDoer(c, params.ToolsResult{ 200 ToolsList: []*tools.Tools{someTools}, 201 }) 202 caller := &fakeHTTPCaller{ 203 httpClient: &httprequest.Client{Doer: doer}, 204 } 205 client := migrationtarget.NewClient(caller) 206 toolsList, err := client.UploadTools( 207 "uuid", 208 strings.NewReader(toolsBody), 209 vers, 210 "trusty", "warty", 211 ) 212 c.Assert(err, jc.ErrorIsNil) 213 c.Assert(toolsList, gc.HasLen, 1) 214 c.Assert(toolsList[0], gc.DeepEquals, someTools) 215 c.Assert(doer.method, gc.Equals, "POST") 216 c.Assert(doer.url, gc.Equals, "/migrate/tools?binaryVersion=2.0.0-xenial-amd64&series=trusty,warty") 217 c.Assert(doer.body, gc.Equals, toolsBody) 218 } 219 220 func (s *ClientSuite) TestUploadResource(c *gc.C) { 221 const resourceBody = "resourceful" 222 doer := newFakeDoer(c, "") 223 caller := &fakeHTTPCaller{ 224 httpClient: &httprequest.Client{Doer: doer}, 225 } 226 client := migrationtarget.NewClient(caller) 227 228 res := resourcetesting.NewResource(c, nil, "blob", "app", resourceBody).Resource 229 res.Revision = 1 230 231 err := client.UploadResource("uuid", res, strings.NewReader(resourceBody)) 232 c.Assert(err, jc.ErrorIsNil) 233 c.Assert(doer.method, gc.Equals, "POST") 234 expectedURL := fmt.Sprintf("/migrate/resources?application=app&description=blob+description&fingerprint=%s&name=blob&origin=upload&path=blob.tgz&revision=1&size=11×tamp=%d&type=file&user=a-user", res.Fingerprint.Hex(), res.Timestamp.UnixNano()) 235 c.Assert(doer.url, gc.Equals, expectedURL) 236 c.Assert(doer.body, gc.Equals, resourceBody) 237 } 238 239 func (s *ClientSuite) TestSetUnitResource(c *gc.C) { 240 const resourceBody = "resourceful" 241 doer := newFakeDoer(c, "") 242 caller := &fakeHTTPCaller{ 243 httpClient: &httprequest.Client{Doer: doer}, 244 } 245 client := migrationtarget.NewClient(caller) 246 247 res := resourcetesting.NewResource(c, nil, "blob", "app", resourceBody).Resource 248 res.Revision = 2 249 250 err := client.SetUnitResource("uuid", "app/0", res) 251 c.Assert(err, jc.ErrorIsNil) 252 c.Assert(doer.method, gc.Equals, "POST") 253 expectedURL := fmt.Sprintf("/migrate/resources?description=blob+description&fingerprint=%s&name=blob&origin=upload&path=blob.tgz&revision=2&size=11×tamp=%d&type=file&unit=app%%2F0&user=a-user", res.Fingerprint.Hex(), res.Timestamp.UnixNano()) 254 c.Assert(doer.url, gc.Equals, expectedURL) 255 c.Assert(doer.body, gc.Equals, "") 256 } 257 258 func (s *ClientSuite) TestPlaceholderResource(c *gc.C) { 259 doer := newFakeDoer(c, "") 260 caller := &fakeHTTPCaller{ 261 httpClient: &httprequest.Client{Doer: doer}, 262 } 263 client := migrationtarget.NewClient(caller) 264 265 res := resourcetesting.NewPlaceholderResource(c, "blob", "app") 266 res.Revision = 3 267 res.Size = 123 268 269 err := client.SetPlaceholderResource("uuid", res) 270 c.Assert(err, jc.ErrorIsNil) 271 c.Assert(doer.method, gc.Equals, "POST") 272 expectedURL := fmt.Sprintf("/migrate/resources?application=app&description=blob+description&fingerprint=%s&name=blob&origin=upload&path=blob.tgz&revision=3&size=123&type=file", res.Fingerprint.Hex()) 273 c.Assert(doer.url, gc.Equals, expectedURL) 274 c.Assert(doer.body, gc.Equals, "") 275 } 276 277 func (s *ClientSuite) TestCACert(c *gc.C) { 278 call := func(objType string, version int, id, request string, args, response interface{}) error { 279 c.Check(objType, gc.Equals, "MigrationTarget") 280 c.Check(request, gc.Equals, "CACert") 281 c.Check(args, gc.Equals, nil) 282 c.Check(response, gc.FitsTypeOf, (*params.BytesResult)(nil)) 283 response.(*params.BytesResult).Result = []byte("foo cert") 284 return nil 285 } 286 client := migrationtarget.NewClient(apitesting.APICallerFunc(call)) 287 r, err := client.CACert() 288 c.Assert(err, jc.ErrorIsNil) 289 c.Assert(r, gc.Equals, "foo cert") 290 } 291 292 func (s *ClientSuite) AssertModelCall(c *gc.C, stub *jujutesting.Stub, tag names.ModelTag, call string, err error, expectError bool) { 293 expectedArg := params.ModelArgs{ModelTag: tag.String()} 294 stub.CheckCalls(c, []jujutesting.StubCall{ 295 {"MigrationTarget." + call, []interface{}{"", expectedArg}}, 296 }) 297 if expectError { 298 c.Assert(err, gc.ErrorMatches, "boom") 299 } else { 300 c.Assert(err, jc.ErrorIsNil) 301 } 302 } 303 304 type fakeConnector struct { 305 base.APICaller 306 307 *jujutesting.Stub 308 } 309 310 func (fakeConnector) BestFacadeVersion(string) int { 311 return 0 312 } 313 314 func (c fakeConnector) ConnectControllerStream(path string, attrs url.Values, headers http.Header) (base.Stream, error) { 315 c.Stub.AddCall("ConnectControllerStream", path, attrs, headers) 316 return nil, errors.New("sound hound") 317 } 318 319 type fakeHTTPCaller struct { 320 base.APICaller 321 httpClient *httprequest.Client 322 err error 323 } 324 325 func (fakeHTTPCaller) BestFacadeVersion(string) int { 326 return 0 327 } 328 329 func (c fakeHTTPCaller) HTTPClient() (*httprequest.Client, error) { 330 return c.httpClient, c.err 331 } 332 333 func newFakeDoer(c *gc.C, respBody interface{}) *fakeDoer { 334 body, err := json.Marshal(respBody) 335 c.Assert(err, jc.ErrorIsNil) 336 resp := &http.Response{ 337 StatusCode: 200, 338 Body: ioutil.NopCloser(bytes.NewReader(body)), 339 } 340 resp.Header = make(http.Header) 341 resp.Header.Add("Content-Type", "application/json") 342 return &fakeDoer{ 343 response: resp, 344 } 345 } 346 347 type fakeDoer struct { 348 response *http.Response 349 350 method string 351 url string 352 body string 353 } 354 355 func (d *fakeDoer) Do(req *http.Request) (*http.Response, error) { 356 d.method = req.Method 357 d.url = req.URL.String() 358 body, err := ioutil.ReadAll(req.Body) 359 if err != nil { 360 panic(err) 361 } 362 d.body = string(body) 363 return d.response, nil 364 }