github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/crossmodelrelations/crossmodelrelations_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package crossmodelrelations_test 5 6 import ( 7 "bytes" 8 "encoding/json" 9 10 "github.com/juju/clock" 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/macaroon.v2-unstable" 15 16 "github.com/juju/juju/api/base/testing" 17 "github.com/juju/juju/api/crossmodelrelations" 18 apitesting "github.com/juju/juju/api/testing" 19 "github.com/juju/juju/apiserver/params" 20 coretesting "github.com/juju/juju/testing" 21 ) 22 23 var _ = gc.Suite(&CrossModelRelationsSuite{}) 24 25 type CrossModelRelationsSuite struct { 26 coretesting.BaseSuite 27 28 cache *crossmodelrelations.MacaroonCache 29 } 30 31 func (s *CrossModelRelationsSuite) SetUpTest(c *gc.C) { 32 s.cache = crossmodelrelations.NewMacaroonCache(clock.WallClock) 33 } 34 35 func (s *CrossModelRelationsSuite) TestNewClient(c *gc.C) { 36 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 37 return nil 38 }) 39 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 40 c.Assert(client, gc.NotNil) 41 } 42 43 type mockDischargeAcquirer struct{} 44 45 func (m *mockDischargeAcquirer) AcquireDischarge(cav macaroon.Caveat) (*macaroon.Macaroon, error) { 46 if !bytes.Equal(cav.Id, []byte("third party caveat")) { 47 return nil, errors.New("permission denied") 48 } 49 return apitesting.NewMacaroon("discharge mac") 50 } 51 52 func (s *CrossModelRelationsSuite) newDischargeMacaroon(c *gc.C) *macaroon.Macaroon { 53 mac, err := apitesting.NewMacaroon("id") 54 c.Assert(err, jc.ErrorIsNil) 55 err = mac.AddThirdPartyCaveat(nil, []byte("third party caveat"), "third party location") 56 c.Assert(err, jc.ErrorIsNil) 57 return mac 58 } 59 60 func (s *CrossModelRelationsSuite) fillResponse(c *gc.C, resp interface{}, value interface{}) { 61 b, err := json.Marshal(value) 62 c.Assert(err, jc.ErrorIsNil) 63 err = json.Unmarshal(b, resp) 64 c.Assert(err, jc.ErrorIsNil) 65 } 66 67 func (s *CrossModelRelationsSuite) TestPublishRelationChange(c *gc.C) { 68 var callCount int 69 mac, err := apitesting.NewMacaroon("id") 70 c.Assert(err, jc.ErrorIsNil) 71 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 72 c.Check(objType, gc.Equals, "CrossModelRelations") 73 c.Check(version, gc.Equals, 0) 74 c.Check(id, gc.Equals, "") 75 c.Check(request, gc.Equals, "PublishRelationChanges") 76 c.Check(arg, gc.DeepEquals, params.RemoteRelationsChanges{ 77 Changes: []params.RemoteRelationChangeEvent{{ 78 RelationToken: "token", 79 DepartedUnits: []int{1}, Macaroons: macaroon.Slice{mac}}}, 80 }) 81 c.Assert(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) 82 *(result.(*params.ErrorResults)) = params.ErrorResults{ 83 Results: []params.ErrorResult{{ 84 Error: ¶ms.Error{Message: "FAIL"}, 85 }}, 86 } 87 callCount++ 88 return nil 89 }) 90 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 91 err = client.PublishRelationChange(params.RemoteRelationChangeEvent{ 92 RelationToken: "token", 93 DepartedUnits: []int{1}, 94 Macaroons: macaroon.Slice{mac}, 95 }) 96 c.Check(err, gc.ErrorMatches, "FAIL") 97 // Call again with a different macaroon but the first one will be 98 // cached and override the passed in macaroon. 99 different, err := apitesting.NewMacaroon("different") 100 c.Assert(err, jc.ErrorIsNil) 101 s.cache.Upsert("token", macaroon.Slice{mac}) 102 err = client.PublishRelationChange(params.RemoteRelationChangeEvent{ 103 RelationToken: "token", 104 DepartedUnits: []int{1}, 105 Macaroons: macaroon.Slice{different}, 106 }) 107 c.Check(err, gc.ErrorMatches, "FAIL") 108 c.Check(callCount, gc.Equals, 2) 109 } 110 111 func (s *CrossModelRelationsSuite) TestPublishRelationChangeDischargeRequired(c *gc.C) { 112 var ( 113 callCount int 114 mac *macaroon.Macaroon 115 dischargeMac macaroon.Slice 116 ) 117 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 118 var resultErr *params.Error 119 if callCount == 0 { 120 mac = s.newDischargeMacaroon(c) 121 resultErr = ¶ms.Error{ 122 Code: params.CodeDischargeRequired, 123 Info: ¶ms.ErrorInfo{ 124 Macaroon: mac, 125 }, 126 } 127 } 128 argParam := arg.(params.RemoteRelationsChanges) 129 dischargeMac = argParam.Changes[0].Macaroons 130 resp := params.ErrorResults{ 131 Results: []params.ErrorResult{{Error: resultErr}}, 132 } 133 s.fillResponse(c, result, resp) 134 callCount++ 135 return nil 136 }) 137 acquirer := &mockDischargeAcquirer{} 138 callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer) 139 client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache) 140 err := client.PublishRelationChange(params.RemoteRelationChangeEvent{ 141 RelationToken: "token", 142 DepartedUnits: []int{1}, 143 }) 144 c.Check(callCount, gc.Equals, 2) 145 c.Check(err, jc.ErrorIsNil) 146 c.Check(dischargeMac, gc.HasLen, 2) 147 apitesting.MacaroonEquals(c, dischargeMac[0], mac) 148 c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac")) 149 // Macaroon has been cached. 150 ms, ok := s.cache.Get("token") 151 c.Assert(ok, jc.IsTrue) 152 apitesting.MacaroonEquals(c, ms[0], mac) 153 c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac")) 154 } 155 156 func (s *CrossModelRelationsSuite) TestRegisterRemoteRelations(c *gc.C) { 157 var callCount int 158 mac, err := apitesting.NewMacaroon("id") 159 c.Assert(err, jc.ErrorIsNil) 160 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 161 c.Check(objType, gc.Equals, "CrossModelRelations") 162 c.Check(version, gc.Equals, 0) 163 c.Check(id, gc.Equals, "") 164 c.Check(request, gc.Equals, "RegisterRemoteRelations") 165 c.Check(arg, gc.DeepEquals, params.RegisterRemoteRelationArgs{ 166 Relations: []params.RegisterRemoteRelationArg{{ 167 RelationToken: "token", 168 OfferUUID: "offer-uuid", 169 Macaroons: macaroon.Slice{mac}}}}) 170 c.Assert(result, gc.FitsTypeOf, ¶ms.RegisterRemoteRelationResults{}) 171 *(result.(*params.RegisterRemoteRelationResults)) = params.RegisterRemoteRelationResults{ 172 Results: []params.RegisterRemoteRelationResult{{ 173 Error: ¶ms.Error{Message: "FAIL"}, 174 }}, 175 } 176 callCount++ 177 return nil 178 }) 179 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 180 result, err := client.RegisterRemoteRelations(params.RegisterRemoteRelationArg{ 181 RelationToken: "token", 182 OfferUUID: "offer-uuid", 183 Macaroons: macaroon.Slice{mac}, 184 }) 185 c.Check(err, jc.ErrorIsNil) 186 c.Assert(result, gc.HasLen, 1) 187 c.Check(result[0].Error, gc.ErrorMatches, "FAIL") 188 // Call again with a different macaroon but the first one will be 189 // cached and override the passed in macaroon. 190 different, err := apitesting.NewMacaroon("different") 191 c.Assert(err, jc.ErrorIsNil) 192 s.cache.Upsert("token", macaroon.Slice{mac}) 193 result, err = client.RegisterRemoteRelations(params.RegisterRemoteRelationArg{ 194 RelationToken: "token", 195 OfferUUID: "offer-uuid", 196 Macaroons: macaroon.Slice{different}, 197 }) 198 c.Check(err, jc.ErrorIsNil) 199 c.Assert(result, gc.HasLen, 1) 200 c.Check(result[0].Error, gc.ErrorMatches, "FAIL") 201 c.Check(callCount, gc.Equals, 2) 202 } 203 204 func (s *CrossModelRelationsSuite) TestRegisterRemoteRelationCount(c *gc.C) { 205 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 206 *(result.(*params.RegisterRemoteRelationResults)) = params.RegisterRemoteRelationResults{ 207 Results: []params.RegisterRemoteRelationResult{ 208 {Error: ¶ms.Error{Message: "FAIL"}}, 209 {Error: ¶ms.Error{Message: "FAIL"}}, 210 }, 211 } 212 return nil 213 }) 214 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 215 _, err := client.RegisterRemoteRelations(params.RegisterRemoteRelationArg{}) 216 c.Check(err, gc.ErrorMatches, `expected 1 result\(s\), got 2`) 217 } 218 219 func (s *CrossModelRelationsSuite) TestRegisterRemoteRelationDischargeRequired(c *gc.C) { 220 var ( 221 callCount int 222 mac *macaroon.Macaroon 223 dischargeMac macaroon.Slice 224 ) 225 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 226 var resultErr *params.Error 227 if callCount == 0 { 228 mac = s.newDischargeMacaroon(c) 229 resultErr = ¶ms.Error{ 230 Code: params.CodeDischargeRequired, 231 Info: ¶ms.ErrorInfo{ 232 Macaroon: mac, 233 }, 234 } 235 } 236 argParam := arg.(params.RegisterRemoteRelationArgs) 237 dischargeMac = argParam.Relations[0].Macaroons 238 resp := params.RegisterRemoteRelationResults{ 239 Results: []params.RegisterRemoteRelationResult{{Error: resultErr}}, 240 } 241 s.fillResponse(c, result, resp) 242 callCount++ 243 return nil 244 }) 245 acquirer := &mockDischargeAcquirer{} 246 callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer) 247 client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache) 248 result, err := client.RegisterRemoteRelations(params.RegisterRemoteRelationArg{ 249 RelationToken: "token", 250 OfferUUID: "offer-uuid"}) 251 c.Check(err, jc.ErrorIsNil) 252 c.Check(callCount, gc.Equals, 2) 253 c.Assert(result, gc.HasLen, 1) 254 c.Check(result[0].Error, gc.IsNil) 255 c.Check(dischargeMac, gc.HasLen, 2) 256 apitesting.MacaroonEquals(c, dischargeMac[0], mac) 257 c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac")) 258 // Macaroon has been cached. 259 ms, ok := s.cache.Get("token") 260 c.Assert(ok, jc.IsTrue) 261 apitesting.MacaroonEquals(c, ms[0], mac) 262 c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac")) 263 } 264 265 func (s *CrossModelRelationsSuite) TestWatchRelationUnits(c *gc.C) { 266 remoteRelationToken := "token" 267 mac, err := apitesting.NewMacaroon("id") 268 c.Assert(err, jc.ErrorIsNil) 269 var callCount int 270 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 271 c.Check(objType, gc.Equals, "CrossModelRelations") 272 c.Check(version, gc.Equals, 0) 273 c.Check(id, gc.Equals, "") 274 c.Check(arg, jc.DeepEquals, params.RemoteEntityArgs{Args: []params.RemoteEntityArg{{ 275 Token: remoteRelationToken, Macaroons: macaroon.Slice{mac}}}}) 276 c.Check(request, gc.Equals, "WatchRelationUnits") 277 c.Assert(result, gc.FitsTypeOf, ¶ms.RelationUnitsWatchResults{}) 278 *(result.(*params.RelationUnitsWatchResults)) = params.RelationUnitsWatchResults{ 279 Results: []params.RelationUnitsWatchResult{{ 280 Error: ¶ms.Error{Message: "FAIL"}, 281 }}, 282 } 283 callCount++ 284 return nil 285 }) 286 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 287 _, err = client.WatchRelationUnits(params.RemoteEntityArg{ 288 Token: remoteRelationToken, 289 Macaroons: macaroon.Slice{mac}, 290 }) 291 c.Check(err, gc.ErrorMatches, "FAIL") 292 // Call again with a different macaroon but the first one will be 293 // cached and override the passed in macaroon. 294 different, err := apitesting.NewMacaroon("different") 295 c.Assert(err, jc.ErrorIsNil) 296 s.cache.Upsert("token", macaroon.Slice{mac}) 297 _, err = client.WatchRelationUnits(params.RemoteEntityArg{ 298 Token: remoteRelationToken, 299 Macaroons: macaroon.Slice{different}, 300 }) 301 c.Check(err, gc.ErrorMatches, "FAIL") 302 c.Check(callCount, gc.Equals, 2) 303 } 304 305 func (s *CrossModelRelationsSuite) TestWatchRelationUnitsDischargeRequired(c *gc.C) { 306 var ( 307 callCount int 308 mac *macaroon.Macaroon 309 dischargeMac macaroon.Slice 310 ) 311 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 312 var resultErr *params.Error 313 switch callCount { 314 case 2, 3: //Watcher Next, Stop 315 return nil 316 case 0: 317 mac = s.newDischargeMacaroon(c) 318 resultErr = ¶ms.Error{ 319 Code: params.CodeDischargeRequired, 320 Info: ¶ms.ErrorInfo{ 321 Macaroon: mac, 322 }, 323 } 324 case 1: 325 argParam := arg.(params.RemoteEntityArgs) 326 dischargeMac = argParam.Args[0].Macaroons 327 } 328 resp := params.RelationUnitsWatchResults{ 329 Results: []params.RelationUnitsWatchResult{{Error: resultErr}}, 330 } 331 s.fillResponse(c, result, resp) 332 callCount++ 333 return nil 334 }) 335 acquirer := &mockDischargeAcquirer{} 336 callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer) 337 client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache) 338 _, err := client.WatchRelationUnits(params.RemoteEntityArg{Token: "token"}) 339 c.Check(callCount, gc.Equals, 2) 340 c.Check(err, jc.ErrorIsNil) 341 c.Assert(dischargeMac, gc.HasLen, 2) 342 apitesting.MacaroonEquals(c, dischargeMac[0], mac) 343 c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac")) 344 // Macaroon has been cached. 345 ms, ok := s.cache.Get("token") 346 c.Assert(ok, jc.IsTrue) 347 apitesting.MacaroonEquals(c, ms[0], mac) 348 c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac")) 349 } 350 351 func (s *CrossModelRelationsSuite) TestRelationUnitSettings(c *gc.C) { 352 remoteRelationToken := "token" 353 mac, err := apitesting.NewMacaroon("id") 354 c.Assert(err, jc.ErrorIsNil) 355 var callCount int 356 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 357 c.Check(objType, gc.Equals, "CrossModelRelations") 358 c.Check(version, gc.Equals, 0) 359 c.Check(id, gc.Equals, "") 360 c.Check(request, gc.Equals, "RelationUnitSettings") 361 c.Check(arg, gc.DeepEquals, params.RemoteRelationUnits{ 362 RelationUnits: []params.RemoteRelationUnit{{ 363 RelationToken: remoteRelationToken, Unit: "u", Macaroons: macaroon.Slice{mac}}}}) 364 c.Assert(result, gc.FitsTypeOf, ¶ms.SettingsResults{}) 365 *(result.(*params.SettingsResults)) = params.SettingsResults{ 366 Results: []params.SettingsResult{{ 367 Error: ¶ms.Error{Message: "FAIL"}, 368 }}, 369 } 370 callCount++ 371 return nil 372 }) 373 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 374 result, err := client.RelationUnitSettings([]params.RemoteRelationUnit{ 375 {RelationToken: remoteRelationToken, Unit: "u", Macaroons: macaroon.Slice{mac}}}) 376 c.Check(err, jc.ErrorIsNil) 377 c.Assert(result, gc.HasLen, 1) 378 c.Check(result[0].Error, gc.ErrorMatches, "FAIL") 379 // Call again with a different macaroon but the first one will be 380 // cached and override the passed in macaroon. 381 different, err := apitesting.NewMacaroon("id") 382 c.Assert(err, jc.ErrorIsNil) 383 s.cache.Upsert("token", macaroon.Slice{mac}) 384 result, err = client.RelationUnitSettings([]params.RemoteRelationUnit{ 385 {RelationToken: remoteRelationToken, Unit: "u", Macaroons: macaroon.Slice{different}}}) 386 c.Check(err, jc.ErrorIsNil) 387 c.Assert(result, gc.HasLen, 1) 388 c.Check(result[0].Error, gc.ErrorMatches, "FAIL") 389 c.Check(callCount, gc.Equals, 2) 390 } 391 392 func (s *CrossModelRelationsSuite) TestRelationUnitSettingsDischargeRequired(c *gc.C) { 393 var ( 394 callCount int 395 mac *macaroon.Macaroon 396 dischargeMac macaroon.Slice 397 ) 398 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 399 var resultErr *params.Error 400 if callCount == 0 { 401 mac = s.newDischargeMacaroon(c) 402 resultErr = ¶ms.Error{ 403 Code: params.CodeDischargeRequired, 404 Info: ¶ms.ErrorInfo{ 405 Macaroon: mac, 406 }, 407 } 408 } 409 argParam := arg.(params.RemoteRelationUnits) 410 dischargeMac = argParam.RelationUnits[0].Macaroons 411 resp := params.SettingsResults{ 412 Results: []params.SettingsResult{{Error: resultErr}}, 413 } 414 s.fillResponse(c, result, resp) 415 callCount++ 416 return nil 417 }) 418 acquirer := &mockDischargeAcquirer{} 419 callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer) 420 client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache) 421 result, err := client.RelationUnitSettings([]params.RemoteRelationUnit{ 422 {RelationToken: "token", Unit: "u"}}) 423 c.Check(err, jc.ErrorIsNil) 424 c.Check(callCount, gc.Equals, 2) 425 c.Assert(result, gc.HasLen, 1) 426 c.Check(result[0].Error, gc.IsNil) 427 c.Check(dischargeMac, gc.HasLen, 2) 428 apitesting.MacaroonEquals(c, dischargeMac[0], mac) 429 c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac")) 430 // Macaroon has been cached. 431 ms, ok := s.cache.Get("token") 432 c.Assert(ok, jc.IsTrue) 433 apitesting.MacaroonEquals(c, ms[0], mac) 434 c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac")) 435 } 436 437 func (s *CrossModelRelationsSuite) TestWatchRelationStatus(c *gc.C) { 438 remoteRelationToken := "token" 439 mac, err := apitesting.NewMacaroon("id") 440 c.Assert(err, jc.ErrorIsNil) 441 var callCount int 442 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 443 c.Check(objType, gc.Equals, "CrossModelRelations") 444 c.Check(version, gc.Equals, 0) 445 c.Check(id, gc.Equals, "") 446 c.Check(arg, jc.DeepEquals, params.RemoteEntityArgs{Args: []params.RemoteEntityArg{{ 447 Token: remoteRelationToken, Macaroons: macaroon.Slice{mac}}}}) 448 c.Check(request, gc.Equals, "WatchRelationsSuspendedStatus") 449 c.Assert(result, gc.FitsTypeOf, ¶ms.RelationStatusWatchResults{}) 450 *(result.(*params.RelationStatusWatchResults)) = params.RelationStatusWatchResults{ 451 Results: []params.RelationLifeSuspendedStatusWatchResult{{ 452 Error: ¶ms.Error{Message: "FAIL"}, 453 }}, 454 } 455 callCount++ 456 return nil 457 }) 458 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 459 _, err = client.WatchRelationSuspendedStatus(params.RemoteEntityArg{ 460 Token: remoteRelationToken, 461 Macaroons: macaroon.Slice{mac}, 462 }) 463 c.Check(err, gc.ErrorMatches, "FAIL") 464 // Call again with a different macaroon but the first one will be 465 // cached and override the passed in macaroon. 466 different, err := apitesting.NewMacaroon("different") 467 c.Assert(err, jc.ErrorIsNil) 468 s.cache.Upsert("token", macaroon.Slice{mac}) 469 _, err = client.WatchRelationSuspendedStatus(params.RemoteEntityArg{ 470 Token: remoteRelationToken, 471 Macaroons: macaroon.Slice{different}, 472 }) 473 c.Check(err, gc.ErrorMatches, "FAIL") 474 c.Check(callCount, gc.Equals, 2) 475 } 476 477 func (s *CrossModelRelationsSuite) TestWatchRelationStatusDischargeRequired(c *gc.C) { 478 var ( 479 callCount int 480 mac *macaroon.Macaroon 481 dischargeMac macaroon.Slice 482 ) 483 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 484 var resultErr *params.Error 485 switch callCount { 486 case 2, 3: //Watcher Next, Stop 487 return nil 488 case 0: 489 mac = s.newDischargeMacaroon(c) 490 resultErr = ¶ms.Error{ 491 Code: params.CodeDischargeRequired, 492 Info: ¶ms.ErrorInfo{ 493 Macaroon: mac, 494 }, 495 } 496 case 1: 497 argParam := arg.(params.RemoteEntityArgs) 498 dischargeMac = argParam.Args[0].Macaroons 499 } 500 resp := params.RelationStatusWatchResults{ 501 Results: []params.RelationLifeSuspendedStatusWatchResult{{Error: resultErr}}, 502 } 503 s.fillResponse(c, result, resp) 504 callCount++ 505 return nil 506 }) 507 acquirer := &mockDischargeAcquirer{} 508 callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer) 509 client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache) 510 _, err := client.WatchRelationSuspendedStatus(params.RemoteEntityArg{Token: "token"}) 511 c.Check(callCount, gc.Equals, 2) 512 c.Check(err, jc.ErrorIsNil) 513 c.Assert(dischargeMac, gc.HasLen, 2) 514 apitesting.MacaroonEquals(c, dischargeMac[0], mac) 515 c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac")) 516 // Macaroon has been cached. 517 ms, ok := s.cache.Get("token") 518 c.Assert(ok, jc.IsTrue) 519 apitesting.MacaroonEquals(c, ms[0], mac) 520 c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac")) 521 } 522 523 func (s *CrossModelRelationsSuite) TestPublishIngressNetworkChange(c *gc.C) { 524 mac, err := apitesting.NewMacaroon("id") 525 c.Assert(err, jc.ErrorIsNil) 526 var callCount int 527 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 528 c.Check(objType, gc.Equals, "CrossModelRelations") 529 c.Check(version, gc.Equals, 0) 530 c.Check(id, gc.Equals, "") 531 c.Check(request, gc.Equals, "PublishIngressNetworkChanges") 532 c.Check(arg, gc.DeepEquals, params.IngressNetworksChanges{ 533 Changes: []params.IngressNetworksChangeEvent{{ 534 RelationToken: "token", 535 Networks: []string{"1.2.3.4/32"}, Macaroons: macaroon.Slice{mac}}}, 536 }) 537 c.Assert(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) 538 *(result.(*params.ErrorResults)) = params.ErrorResults{ 539 Results: []params.ErrorResult{{ 540 Error: ¶ms.Error{Message: "FAIL"}, 541 }}, 542 } 543 callCount++ 544 return nil 545 }) 546 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 547 err = client.PublishIngressNetworkChange(params.IngressNetworksChangeEvent{ 548 RelationToken: "token", 549 Networks: []string{"1.2.3.4/32"}, Macaroons: macaroon.Slice{mac}}) 550 c.Check(err, gc.ErrorMatches, "FAIL") 551 // Call again with a different macaroon but the first one will be 552 // cached and override the passed in macaroon. 553 different, err := apitesting.NewMacaroon("different") 554 c.Assert(err, jc.ErrorIsNil) 555 s.cache.Upsert("token", macaroon.Slice{mac}) 556 err = client.PublishIngressNetworkChange(params.IngressNetworksChangeEvent{ 557 RelationToken: "token", 558 Networks: []string{"1.2.3.4/32"}, Macaroons: macaroon.Slice{different}}) 559 c.Check(err, gc.ErrorMatches, "FAIL") 560 c.Check(callCount, gc.Equals, 2) 561 } 562 563 func (s *CrossModelRelationsSuite) TestPublishIngressNetworkChangeDischargeRequired(c *gc.C) { 564 var ( 565 callCount int 566 mac *macaroon.Macaroon 567 dischargeMac macaroon.Slice 568 ) 569 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 570 var resultErr *params.Error 571 if callCount == 0 { 572 mac = s.newDischargeMacaroon(c) 573 resultErr = ¶ms.Error{ 574 Code: params.CodeDischargeRequired, 575 Info: ¶ms.ErrorInfo{ 576 Macaroon: mac, 577 }, 578 } 579 } 580 argParam := arg.(params.IngressNetworksChanges) 581 dischargeMac = argParam.Changes[0].Macaroons 582 resp := params.ErrorResults{ 583 Results: []params.ErrorResult{{Error: resultErr}}, 584 } 585 s.fillResponse(c, result, resp) 586 callCount++ 587 return nil 588 }) 589 acquirer := &mockDischargeAcquirer{} 590 callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer) 591 client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache) 592 err := client.PublishIngressNetworkChange(params.IngressNetworksChangeEvent{ 593 RelationToken: "token", 594 Networks: []string{"1.2.3.4/32"}}) 595 c.Check(callCount, gc.Equals, 2) 596 c.Check(err, jc.ErrorIsNil) 597 c.Check(dischargeMac, gc.HasLen, 2) 598 apitesting.MacaroonEquals(c, dischargeMac[0], mac) 599 c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac")) 600 // Macaroon has been cached. 601 ms, ok := s.cache.Get("token") 602 c.Assert(ok, jc.IsTrue) 603 apitesting.MacaroonEquals(c, ms[0], mac) 604 c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac")) 605 } 606 607 func (s *CrossModelRelationsSuite) TestWatchEgressAddressesForRelation(c *gc.C) { 608 var callCount int 609 remoteRelationToken := "token" 610 mac, err := apitesting.NewMacaroon("id") 611 relation := params.RemoteEntityArg{ 612 Token: remoteRelationToken, Macaroons: macaroon.Slice{mac}} 613 c.Check(err, jc.ErrorIsNil) 614 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 615 c.Check(objType, gc.Equals, "CrossModelRelations") 616 c.Check(version, gc.Equals, 0) 617 c.Check(id, gc.Equals, "") 618 c.Check(request, gc.Equals, "WatchEgressAddressesForRelations") 619 c.Check(arg, gc.DeepEquals, params.RemoteEntityArgs{ 620 Args: []params.RemoteEntityArg{relation}}) 621 c.Assert(result, gc.FitsTypeOf, ¶ms.StringsWatchResults{}) 622 *(result.(*params.StringsWatchResults)) = params.StringsWatchResults{ 623 Results: []params.StringsWatchResult{{ 624 Error: ¶ms.Error{Message: "FAIL"}, 625 }}, 626 } 627 callCount++ 628 return nil 629 }) 630 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 631 _, err = client.WatchEgressAddressesForRelation(relation) 632 c.Check(err, gc.ErrorMatches, "FAIL") 633 // Call again with a different macaroon but the first one will be 634 // cached and override the passed in macaroon. 635 different, err := apitesting.NewMacaroon("different") 636 c.Assert(err, jc.ErrorIsNil) 637 s.cache.Upsert("token", macaroon.Slice{mac}) 638 rel2 := relation 639 rel2.Macaroons = macaroon.Slice{different} 640 _, err = client.WatchEgressAddressesForRelation(rel2) 641 c.Check(err, gc.ErrorMatches, "FAIL") 642 c.Check(callCount, gc.Equals, 2) 643 } 644 645 func (s *CrossModelRelationsSuite) TestWatchEgressAddressesForRelationDischargeRequired(c *gc.C) { 646 var ( 647 callCount int 648 mac *macaroon.Macaroon 649 dischargeMac macaroon.Slice 650 ) 651 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 652 var resultErr *params.Error 653 switch callCount { 654 case 2, 3: //Watcher Next, Stop 655 return nil 656 case 0: 657 mac = s.newDischargeMacaroon(c) 658 resultErr = ¶ms.Error{ 659 Code: params.CodeDischargeRequired, 660 Info: ¶ms.ErrorInfo{ 661 Macaroon: mac, 662 }, 663 } 664 case 1: 665 argParam := arg.(params.RemoteEntityArgs) 666 dischargeMac = argParam.Args[0].Macaroons 667 } 668 resp := params.StringsWatchResults{ 669 Results: []params.StringsWatchResult{{Error: resultErr}}, 670 } 671 s.fillResponse(c, result, resp) 672 callCount++ 673 return nil 674 }) 675 acquirer := &mockDischargeAcquirer{} 676 callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer) 677 client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache) 678 _, err := client.WatchEgressAddressesForRelation(params.RemoteEntityArg{Token: "token"}) 679 c.Check(callCount, gc.Equals, 2) 680 c.Check(err, jc.ErrorIsNil) 681 c.Assert(dischargeMac, gc.HasLen, 2) 682 apitesting.MacaroonEquals(c, dischargeMac[0], mac) 683 c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac")) 684 // Macaroon has been cached. 685 ms, ok := s.cache.Get("token") 686 c.Assert(ok, jc.IsTrue) 687 apitesting.MacaroonEquals(c, ms[0], mac) 688 c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac")) 689 } 690 691 func (s *CrossModelRelationsSuite) TestWatchOfferStatus(c *gc.C) { 692 offerUUID := "offer-uuid" 693 mac, err := apitesting.NewMacaroon("id") 694 c.Assert(err, jc.ErrorIsNil) 695 var callCount int 696 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 697 c.Check(objType, gc.Equals, "CrossModelRelations") 698 c.Check(version, gc.Equals, 0) 699 c.Check(id, gc.Equals, "") 700 c.Check(arg, jc.DeepEquals, params.OfferArgs{Args: []params.OfferArg{{ 701 OfferUUID: offerUUID, Macaroons: macaroon.Slice{mac}}}}) 702 c.Check(request, gc.Equals, "WatchOfferStatus") 703 c.Assert(result, gc.FitsTypeOf, ¶ms.OfferStatusWatchResults{}) 704 *(result.(*params.OfferStatusWatchResults)) = params.OfferStatusWatchResults{ 705 Results: []params.OfferStatusWatchResult{{ 706 Error: ¶ms.Error{Message: "FAIL"}, 707 }}, 708 } 709 callCount++ 710 return nil 711 }) 712 client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache) 713 _, err = client.WatchOfferStatus(params.OfferArg{ 714 OfferUUID: offerUUID, 715 Macaroons: macaroon.Slice{mac}, 716 }) 717 c.Check(err, gc.ErrorMatches, "FAIL") 718 // Call again with a different macaroon but the first one will be 719 // cached and override the passed in macaroon. 720 different, err := apitesting.NewMacaroon("different") 721 c.Assert(err, jc.ErrorIsNil) 722 s.cache.Upsert("offer-uuid", macaroon.Slice{mac}) 723 _, err = client.WatchOfferStatus(params.OfferArg{ 724 OfferUUID: offerUUID, 725 Macaroons: macaroon.Slice{different}, 726 }) 727 c.Check(err, gc.ErrorMatches, "FAIL") 728 c.Check(callCount, gc.Equals, 2) 729 } 730 731 func (s *CrossModelRelationsSuite) TestWatchOfferStatusDischargeRequired(c *gc.C) { 732 var ( 733 callCount int 734 mac *macaroon.Macaroon 735 dischargeMac macaroon.Slice 736 ) 737 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 738 var resultErr *params.Error 739 switch callCount { 740 case 2, 3: //Watcher Next, Stop 741 return nil 742 case 0: 743 mac = s.newDischargeMacaroon(c) 744 resultErr = ¶ms.Error{ 745 Code: params.CodeDischargeRequired, 746 Info: ¶ms.ErrorInfo{ 747 Macaroon: mac, 748 }, 749 } 750 case 1: 751 argParam := arg.(params.OfferArgs) 752 dischargeMac = argParam.Args[0].Macaroons 753 } 754 resp := params.OfferStatusWatchResults{ 755 Results: []params.OfferStatusWatchResult{{Error: resultErr}}, 756 } 757 s.fillResponse(c, result, resp) 758 callCount++ 759 return nil 760 }) 761 acquirer := &mockDischargeAcquirer{} 762 callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer) 763 client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache) 764 _, err := client.WatchOfferStatus(params.OfferArg{OfferUUID: "offer-uuid"}) 765 c.Check(callCount, gc.Equals, 2) 766 c.Check(err, jc.ErrorIsNil) 767 c.Assert(dischargeMac, gc.HasLen, 2) 768 apitesting.MacaroonEquals(c, dischargeMac[0], mac) 769 c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac")) 770 // Macaroon has been cached. 771 ms, ok := s.cache.Get("offer-uuid") 772 c.Assert(ok, jc.IsTrue) 773 apitesting.MacaroonEquals(c, ms[0], mac) 774 c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac")) 775 }