github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/client/spaces/spaces_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package spaces_test 5 6 import ( 7 "errors" 8 "fmt" 9 "math/rand" 10 11 "github.com/juju/names/v5" 12 jc "github.com/juju/testing/checkers" 13 "go.uber.org/mock/gomock" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/api/base/mocks" 17 "github.com/juju/juju/api/client/spaces" 18 "github.com/juju/juju/rpc/params" 19 ) 20 21 // spacesSuite are using mocks instead of the apicaller stubs 22 type spacesSuite struct { 23 fCaller *mocks.MockFacadeCaller 24 API *spaces.API 25 } 26 27 var _ = gc.Suite(&spacesSuite{}) 28 29 func (s *spacesSuite) SetUpTest(c *gc.C) { 30 } 31 32 func (s *spacesSuite) TearDownTest(c *gc.C) { 33 s.fCaller = nil 34 } 35 36 func (s *spacesSuite) setUpMocks(c *gc.C) *gomock.Controller { 37 ctrl := gomock.NewController(c) 38 39 caller := mocks.NewMockAPICallCloser(ctrl) 40 41 s.fCaller = mocks.NewMockFacadeCaller(ctrl) 42 s.fCaller.EXPECT().RawAPICaller().Return(caller).AnyTimes() 43 s.API = spaces.NewAPIFromCaller(s.fCaller) 44 return ctrl 45 } 46 47 func (s *spacesSuite) TestRemoveSpace(c *gc.C) { 48 defer s.setUpMocks(c).Finish() 49 name := "myspace" 50 resultSource := params.RemoveSpaceResults{ 51 Results: []params.RemoveSpaceResult{}, 52 } 53 args := getRemoveSpaceArgs(name, false, false) 54 55 s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil) 56 57 bounds, err := s.API.RemoveSpace(name, false, false) 58 c.Assert(err, gc.ErrorMatches, "0 results, expected 1") 59 c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{}) 60 } 61 62 func (s *spacesSuite) TestRemoveSpaceUnexpectedError(c *gc.C) { 63 defer s.setUpMocks(c).Finish() 64 name := "myspace" 65 resultSource := params.RemoveSpaceResults{ 66 Results: []params.RemoveSpaceResult{{ 67 Constraints: nil, 68 Bindings: nil, 69 ControllerSettings: nil, 70 Error: ¶ms.Error{ 71 Message: "bam", 72 Code: "500", 73 }, 74 }}, 75 } 76 args := getRemoveSpaceArgs(name, false, false) 77 78 s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil) 79 80 bounds, err := s.API.RemoveSpace(name, false, false) 81 c.Assert(err, gc.ErrorMatches, "bam") 82 c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{}) 83 } 84 85 func (s *spacesSuite) TestRemoveSpaceUnexpectedErrorAPICall(c *gc.C) { 86 defer s.setUpMocks(c).Finish() 87 name := "myspace" 88 resultSource := params.RemoveSpaceResults{ 89 Results: []params.RemoveSpaceResult{}} 90 args := getRemoveSpaceArgs(name, false, false) 91 92 bam := errors.New("bam") 93 s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(bam) 94 95 bounds, err := s.API.RemoveSpace(name, false, false) 96 c.Assert(err, gc.ErrorMatches, bam.Error()) 97 c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{}) 98 } 99 100 func (s *spacesSuite) TestRemoveSpaceUnexpectedErrorAPICallNotSupported(c *gc.C) { 101 defer s.setUpMocks(c).Finish() 102 name := "myspace" 103 resultSource := params.RemoveSpaceResults{ 104 Results: []params.RemoveSpaceResult{}} 105 args := getRemoveSpaceArgs(name, false, false) 106 107 bam := params.Error{ 108 Message: "not supported", 109 Code: params.CodeNotSupported, 110 Info: nil, 111 } 112 s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(bam) 113 114 bounds, err := s.API.RemoveSpace(name, false, false) 115 c.Assert(err, gc.ErrorMatches, bam.Error()) 116 c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{}) 117 } 118 119 func (s *spacesSuite) TestRemoveSpaceConstraintsBindings(c *gc.C) { 120 defer s.setUpMocks(c).Finish() 121 name := "myspace" 122 resultSource := params.RemoveSpaceResults{ 123 Results: []params.RemoveSpaceResult{{ 124 Constraints: []params.Entity{ 125 {Tag: "model-42c4f770-86ed-4fcc-8e39-697063d082bc:e"}, 126 {Tag: "application-mysql"}, 127 }, 128 Bindings: []params.Entity{ 129 {Tag: "application-mysql"}, 130 {Tag: "application-mediawiki"}, 131 }, 132 ControllerSettings: []string{"jujuhaspace", "juuuu-space"}, 133 Error: nil, 134 }}} 135 args := getRemoveSpaceArgs(name, false, false) 136 137 s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil) 138 139 bounds, err := s.API.RemoveSpace(name, false, false) 140 141 expectedBounds := params.RemoveSpaceResult{ 142 Constraints: []params.Entity{ 143 {Tag: "model-42c4f770-86ed-4fcc-8e39-697063d082bc:e"}, 144 {Tag: "application-mysql"}, 145 }, 146 Bindings: []params.Entity{ 147 {Tag: "application-mysql"}, 148 {Tag: "application-mediawiki"}, 149 }, 150 ControllerSettings: []string{"jujuhaspace", "juuuu-space"}, 151 } 152 c.Assert(err, jc.ErrorIsNil) 153 c.Assert(bounds, jc.DeepEquals, expectedBounds) 154 } 155 func (s *spacesSuite) TestRemoveSpaceConstraints(c *gc.C) { 156 defer s.setUpMocks(c).Finish() 157 name := "myspace" 158 resultSource := params.RemoveSpaceResults{ 159 Results: []params.RemoveSpaceResult{{ 160 Constraints: []params.Entity{ 161 {Tag: "model-42c4f770-86ed-4fcc-8e39-697063d082bc:e"}, 162 {Tag: "application-mysql"}, 163 }, 164 Error: nil, 165 }}} 166 args := getRemoveSpaceArgs(name, false, false) 167 s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil) 168 169 bounds, err := s.API.RemoveSpace(name, false, false) 170 expectedBounds := params.RemoveSpaceResult{ 171 Constraints: []params.Entity{ 172 {Tag: "model-42c4f770-86ed-4fcc-8e39-697063d082bc:e"}, 173 {Tag: "application-mysql"}, 174 }, 175 } 176 c.Assert(err, jc.ErrorIsNil) 177 c.Assert(bounds, jc.DeepEquals, expectedBounds) 178 } 179 180 func (s *spacesSuite) TestRemoveSpaceForce(c *gc.C) { 181 defer s.setUpMocks(c).Finish() 182 name := "myspace" 183 resultSource := params.RemoveSpaceResults{ 184 Results: []params.RemoveSpaceResult{{}}} 185 args := getRemoveSpaceArgs(name, true, false) 186 s.fCaller.EXPECT().FacadeCall("RemoveSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil) 187 188 bounds, err := s.API.RemoveSpace(name, true, false) 189 190 c.Assert(err, gc.IsNil) 191 c.Assert(bounds, gc.DeepEquals, params.RemoveSpaceResult{}) 192 } 193 194 func getRemoveSpaceArgs(spaceName string, force, dryRun bool) params.RemoveSpaceParams { 195 return params.RemoveSpaceParams{SpaceParams: []params.RemoveSpaceParam{ 196 { 197 Space: params.Entity{Tag: names.NewSpaceTag(spaceName).String()}, 198 Force: force, 199 DryRun: dryRun, 200 }, 201 }} 202 } 203 204 func (s *spacesSuite) TestRenameSpace(c *gc.C) { 205 defer s.setUpMocks(c).Finish() 206 from, to := "from", "to" 207 resultSource := params.ErrorResults{} 208 args := params.RenameSpacesParams{ 209 Changes: []params.RenameSpaceParams{{ 210 FromSpaceTag: names.NewSpaceTag(from).String(), 211 ToSpaceTag: names.NewSpaceTag(to).String(), 212 }}, 213 } 214 s.fCaller.EXPECT().FacadeCall("RenameSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil) 215 216 err := s.API.RenameSpace(from, to) 217 c.Assert(err, gc.IsNil) 218 } 219 220 func (s *spacesSuite) TestRenameSpaceError(c *gc.C) { 221 defer s.setUpMocks(c).Finish() 222 from, to := "from", "to" 223 resultSource := params.ErrorResults{Results: []params.ErrorResult{{ 224 Error: ¶ms.Error{ 225 Message: "bam", 226 Code: "500", 227 }, 228 }}} 229 args := params.RenameSpacesParams{ 230 Changes: []params.RenameSpaceParams{{ 231 FromSpaceTag: names.NewSpaceTag(from).String(), 232 ToSpaceTag: names.NewSpaceTag(to).String(), 233 }}, 234 } 235 s.fCaller.EXPECT().FacadeCall("RenameSpace", args, gomock.Any()).SetArg(2, resultSource).Return(nil) 236 237 err := s.API.RenameSpace(from, to) 238 c.Assert(err, gc.ErrorMatches, "bam") 239 } 240 241 func (s *spacesSuite) TestCreateSpace(c *gc.C) { 242 defer s.setUpMocks(c).Finish() 243 name := "foo" 244 subnets := []string{} 245 r := rand.New(rand.NewSource(0xdeadbeef)) 246 for i := 0; i < 100; i++ { 247 for j := 0; j < 10; j++ { 248 n := r.Uint32() 249 newSubnet := fmt.Sprintf("%d.%d.%d.0/24", uint8(n>>16), uint8(n>>8), uint8(n)) 250 subnets = append(subnets, newSubnet) 251 } 252 args := params.CreateSpacesParams{ 253 Spaces: []params.CreateSpaceParams{ 254 { 255 SpaceTag: names.NewSpaceTag(name).String(), 256 CIDRs: subnets, 257 Public: true, 258 }, 259 }, 260 } 261 res := new(params.ErrorResults) 262 ress := params.ErrorResults{ 263 Results: []params.ErrorResult{{}}, 264 } 265 s.fCaller.EXPECT().FacadeCall("CreateSpaces", args, res).SetArg(2, ress).Return(nil) 266 err := s.API.CreateSpace(name, subnets, true) 267 c.Assert(err, jc.ErrorIsNil) 268 } 269 } 270 271 func (s *spacesSuite) TestCreateSpaceEmptyResults(c *gc.C) { 272 defer s.setUpMocks(c).Finish() 273 args := params.CreateSpacesParams{ 274 Spaces: []params.CreateSpaceParams{ 275 { 276 SpaceTag: names.NewSpaceTag("foo").String(), 277 CIDRs: nil, 278 Public: true, 279 }, 280 }, 281 } 282 res := new(params.ErrorResults) 283 ress := params.ErrorResults{ 284 Results: []params.ErrorResult{{ 285 Error: ¶ms.Error{Message: "expected 1 result, got 0"}, 286 }}, 287 } 288 289 s.fCaller.EXPECT().FacadeCall("CreateSpaces", args, res).SetArg(2, ress).Return(nil) 290 err := s.API.CreateSpace("foo", nil, true) 291 c.Assert(err, gc.ErrorMatches, "expected 1 result, got 0") 292 } 293 294 func (s *spacesSuite) TestCreateSpaceFails(c *gc.C) { 295 defer s.setUpMocks(c).Finish() 296 args := params.CreateSpacesParams{ 297 Spaces: []params.CreateSpaceParams{ 298 { 299 SpaceTag: names.NewSpaceTag("foo").String(), 300 CIDRs: []string{"1.1.1.0/24"}, 301 Public: true, 302 }, 303 }, 304 } 305 res := new(params.ErrorResults) 306 ress := params.ErrorResults{ 307 Results: []params.ErrorResult{{ 308 Error: ¶ms.Error{Message: "bang"}, 309 }}, 310 } 311 312 s.fCaller.EXPECT().FacadeCall("CreateSpaces", args, res).SetArg(2, ress).Return(nil) 313 err := s.API.CreateSpace("foo", []string{"1.1.1.0/24"}, true) 314 c.Assert(err, gc.ErrorMatches, "bang") 315 } 316 317 func (s *spacesSuite) testShowSpaces(c *gc.C, spaceName string, results []params.ShowSpaceResult, err error, expectErr string) { 318 defer s.setUpMocks(c).Finish() 319 320 var expectResults params.ShowSpaceResults 321 if results != nil { 322 expectResults = params.ShowSpaceResults{ 323 Results: results, 324 } 325 } 326 327 args := params.Entities{ 328 Entities: []params.Entity{{Tag: names.NewSpaceTag(spaceName).String()}}, 329 } 330 res := new(params.ShowSpaceResults) 331 332 s.fCaller.EXPECT().FacadeCall("ShowSpace", args, res).SetArg(2, expectResults).Return(err) 333 gotResults, gotErr := s.API.ShowSpace(spaceName) 334 if expectErr != "" { 335 c.Assert(gotErr, gc.ErrorMatches, expectErr) 336 return 337 } else { 338 c.Assert(results, gc.NotNil) 339 c.Assert(len(results), gc.Equals, 1) 340 c.Assert(gotResults, jc.DeepEquals, results[0]) 341 } 342 if err != nil { 343 c.Assert(gotErr, jc.DeepEquals, err) 344 } else { 345 c.Assert(gotErr, jc.ErrorIsNil) 346 } 347 } 348 349 func (s *spacesSuite) TestShowSpaceTooManyResults(c *gc.C) { 350 s.testShowSpaces(c, "empty", 351 []params.ShowSpaceResult{ 352 { 353 Space: params.Space{}, 354 }, 355 { 356 Space: params.Space{}, 357 }, 358 }, nil, "expected 1 result, got 2") 359 } 360 361 func (s *spacesSuite) TestShowSpaceNoResultsResults(c *gc.C) { 362 s.testShowSpaces(c, "empty", nil, nil, "expected 1 result, got 0") 363 } 364 365 func (s *spacesSuite) TestShowSpaceResult(c *gc.C) { 366 result := []params.ShowSpaceResult{{ 367 Space: params.Space{Id: "1", Name: "default"}, 368 Applications: []string{}, 369 MachineCount: 0, 370 }} 371 s.testShowSpaces(c, "default", result, nil, "") 372 } 373 374 func (s *spacesSuite) TestShowSpaceServerError(c *gc.C) { 375 s.testShowSpaces(c, "nil", nil, errors.New("boom"), "boom") 376 } 377 378 func (s *spacesSuite) TestShowSpaceError(c *gc.C) { 379 defer s.setUpMocks(c).Finish() 380 381 arg := "space" 382 args := params.Entities{ 383 Entities: []params.Entity{{Tag: names.NewSpaceTag(arg).String()}}, 384 } 385 res := new(params.ShowSpaceResults) 386 ress := params.ShowSpaceResults{ 387 Results: []params.ShowSpaceResult{}, 388 } 389 390 s.fCaller.EXPECT().FacadeCall("ShowSpace", args, res).SetArg(2, ress).Return(nil) 391 392 _, err := s.API.ShowSpace(arg) 393 c.Assert(err, gc.ErrorMatches, "expected 1 result, got 0") 394 } 395 396 func (s *spacesSuite) testListSpaces(c *gc.C, results []params.Space, err error, expectErr string) { 397 defer s.setUpMocks(c).Finish() 398 399 var expectResults params.ListSpacesResults 400 if results != nil { 401 expectResults = params.ListSpacesResults{ 402 Results: results, 403 } 404 } 405 406 res := new(params.ListSpacesResults) 407 408 s.fCaller.EXPECT().FacadeCall("ListSpaces", nil, res).SetArg(2, expectResults).Return(err) 409 gotResults, gotErr := s.API.ListSpaces() 410 c.Assert(gotResults, jc.DeepEquals, results) 411 if expectErr != "" { 412 c.Assert(gotErr, gc.ErrorMatches, expectErr) 413 return 414 } 415 if err != nil { 416 c.Assert(gotErr, jc.DeepEquals, err) 417 } else { 418 c.Assert(gotErr, jc.ErrorIsNil) 419 } 420 } 421 422 func (s *spacesSuite) TestListSpacesEmptyResults(c *gc.C) { 423 s.testListSpaces(c, []params.Space{}, nil, "") 424 } 425 426 func (s *spacesSuite) TestListSpacesManyResults(c *gc.C) { 427 spaces := []params.Space{{ 428 Name: "space1", 429 Subnets: []params.Subnet{{ 430 CIDR: "foo", 431 }, { 432 CIDR: "bar", 433 }}, 434 }, { 435 Name: "space2", 436 }, { 437 Name: "space3", 438 Subnets: []params.Subnet{}, 439 }} 440 s.testListSpaces(c, spaces, nil, "") 441 } 442 443 func (s *spacesSuite) TestListSpacesServerError(c *gc.C) { 444 s.testListSpaces(c, nil, errors.New("boom"), "boom") 445 } 446 447 func (s *spacesSuite) testMoveSubnets(c *gc.C, 448 space names.SpaceTag, 449 subnets []names.SubnetTag, 450 results []params.MoveSubnetsResult, 451 err error, expectErr string, 452 ) { 453 defer s.setUpMocks(c).Finish() 454 455 var expectedResults params.MoveSubnetsResults 456 if results != nil { 457 expectedResults.Results = results 458 } 459 460 subnetTags := make([]string, len(subnets)) 461 for k, subnet := range subnets { 462 subnetTags[k] = subnet.String() 463 } 464 args := params.MoveSubnetsParams{ 465 Args: []params.MoveSubnetsParam{{ 466 SubnetTags: subnetTags, 467 SpaceTag: space.String(), 468 Force: false, 469 }}, 470 } 471 res := new(params.MoveSubnetsResults) 472 473 s.fCaller.EXPECT().FacadeCall("MoveSubnets", args, res).SetArg(2, expectedResults).Return(err) 474 gotResult, gotErr := s.API.MoveSubnets(space, subnets, false) 475 if len(results) > 0 { 476 c.Assert(gotResult, jc.DeepEquals, results[0]) 477 } else { 478 c.Assert(gotResult, jc.DeepEquals, params.MoveSubnetsResult{}) 479 } 480 481 if expectErr != "" { 482 c.Assert(gotErr, gc.ErrorMatches, expectErr) 483 return 484 } 485 486 if err != nil { 487 c.Assert(gotErr, jc.DeepEquals, err) 488 } else { 489 c.Assert(gotErr, jc.ErrorIsNil) 490 } 491 } 492 493 func (s *spacesSuite) TestMoveSubnetsEmptyResults(c *gc.C) { 494 space := names.NewSpaceTag("aaabbb") 495 subnets := []names.SubnetTag{names.NewSubnetTag("1")} 496 497 s.testMoveSubnets(c, space, subnets, []params.MoveSubnetsResult{}, nil, "expected 1 result, got 0") 498 } 499 500 func (s *spacesSuite) TestMoveSubnets(c *gc.C) { 501 space := names.NewSpaceTag("aaabbb") 502 subnets := []names.SubnetTag{names.NewSubnetTag("1")} 503 504 s.testMoveSubnets(c, space, subnets, []params.MoveSubnetsResult{{ 505 MovedSubnets: []params.MovedSubnet{{ 506 SubnetTag: "2", 507 OldSpaceTag: "aaabbb", 508 }}, 509 NewSpaceTag: "xxxyyy", 510 }}, nil, "") 511 } 512 513 func (s *spacesSuite) TestMoveSubnetsServerError(c *gc.C) { 514 space := names.NewSpaceTag("aaabbb") 515 subnets := []names.SubnetTag{names.NewSubnetTag("1")} 516 517 s.testMoveSubnets(c, space, subnets, nil, errors.New("boom"), "boom") 518 }