github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/migration_import_tasks_test.go (about) 1 // Copyright 2019 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 9 "github.com/juju/description/v5" 10 "github.com/juju/errors" 11 "github.com/juju/mgo/v3/txn" 12 "github.com/juju/names/v5" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils/v3" 15 "go.uber.org/mock/gomock" 16 gc "gopkg.in/check.v1" 17 18 "github.com/juju/juju/core/permission" 19 "github.com/juju/juju/environs/config" 20 ) 21 22 type MigrationImportTasksSuite struct{} 23 24 var _ = gc.Suite(&MigrationImportTasksSuite{}) 25 26 func (s *MigrationImportTasksSuite) TestImportApplicationOffers(c *gc.C) { 27 ctrl := gomock.NewController(c) 28 defer ctrl.Finish() 29 30 offerUUID, err := utils.NewUUID() 31 c.Assert(err, jc.ErrorIsNil) 32 offerUUID2, err := utils.NewUUID() 33 c.Assert(err, jc.ErrorIsNil) 34 35 runner := ImportApplicationOfferRunner{ 36 OfferUUID: offerUUID.String(), 37 model: NewMockApplicationOfferInput(ctrl), 38 runner: NewMockTransactionRunner(ctrl), 39 } 40 41 entity := s.applicationOffer(ctrl) 42 offerDoc := applicationOfferDoc{ 43 DocID: fmt.Sprintf("%s:%s", offerUUID.String(), "offer-name-foo"), 44 OfferUUID: offerUUID.String(), 45 OfferName: "offer-name-foo", 46 ApplicationName: "foo", 47 ApplicationDescription: "foo app description", 48 Endpoints: map[string]string{ 49 "db": "db", 50 }, 51 } 52 secondOfferDoc := offerDoc 53 secondOfferDoc.DocID = fmt.Sprintf("%s:%s", offerUUID2.String(), "second-offer") 54 secondOfferDoc.OfferUUID = offerUUID2.String() 55 secondOfferDoc.OfferName = "second-offer" 56 57 entity.EXPECT().ApplicationName().Return(offerDoc.ApplicationName).Times(2) 58 runner.Add(runner.applicationOffers(entity, entity)) 59 runner.Add(runner.docID(offerDoc.OfferName, offerDoc.DocID)) 60 runner.Add(runner.docID(secondOfferDoc.OfferName, secondOfferDoc.DocID)) 61 runner.Add(runner.applicationOfferDoc(offerDoc, entity)) 62 runner.Add(runner.applicationOfferDoc(secondOfferDoc, entity)) 63 64 refOp := txn.Op{ 65 Assert: txn.DocMissing, 66 } 67 runner.Add(runner.applicationOffersRefOp(refOp, 2)) 68 69 entity.EXPECT().ACL().Return(map[string]string{"fred": "consume"}).Times(2) 70 permissionOp := createPermissionOp(applicationOfferKey( 71 offerUUID.String()), userGlobalKey(userAccessID(names.NewUserTag("fred"))), permission.ConsumeAccess) 72 permissionOp2 := createPermissionOp(applicationOfferKey( 73 offerUUID2.String()), userGlobalKey(userAccessID(names.NewUserTag("fred"))), permission.ConsumeAccess) 74 75 runner.Add(runner.transaction([]applicationOfferDoc{offerDoc, secondOfferDoc}, []txn.Op{permissionOp, permissionOp2}, refOp)) 76 77 err = runner.Run(ctrl) 78 c.Assert(err, jc.ErrorIsNil) 79 } 80 81 func (s *MigrationImportTasksSuite) TestImportApplicationOffersTransactionFailure(c *gc.C) { 82 ctrl := gomock.NewController(c) 83 defer ctrl.Finish() 84 85 offerUUID, err := utils.NewUUID() 86 c.Assert(err, jc.ErrorIsNil) 87 88 runner := ImportApplicationOfferRunner{ 89 OfferUUID: offerUUID.String(), 90 model: NewMockApplicationOfferInput(ctrl), 91 runner: NewMockTransactionRunner(ctrl), 92 } 93 94 entity := s.applicationOffer(ctrl) 95 offerDoc := applicationOfferDoc{ 96 DocID: fmt.Sprintf("%s:%s", offerUUID.String(), "offer-name-foo"), 97 OfferUUID: offerUUID.String(), 98 OfferName: "offer-name-foo", 99 ApplicationName: "foo", 100 ApplicationDescription: "foo app description", 101 Endpoints: map[string]string{ 102 "db": "db", 103 }, 104 } 105 106 entity.EXPECT().ACL().Return(map[string]string{}) 107 entity.EXPECT().ApplicationName().Return(offerDoc.ApplicationName) 108 runner.Add(runner.applicationOffers(entity)) 109 runner.Add(runner.applicationOfferDoc(offerDoc, entity)) 110 runner.Add(runner.docID(offerDoc.OfferName, offerDoc.DocID)) 111 112 refOp := txn.Op{ 113 Assert: txn.DocMissing, 114 } 115 runner.Add(runner.applicationOffersRefOp(refOp, 1)) 116 runner.Add(runner.transactionWithError(errors.New("fail"))) 117 118 err = runner.Run(ctrl) 119 c.Assert(err, gc.ErrorMatches, "fail") 120 } 121 122 type ImportApplicationOfferRunner struct { 123 OfferUUID string 124 model *MockApplicationOfferInput 125 runner *MockTransactionRunner 126 ops []func(*gomock.Controller) 127 } 128 129 func (s *ImportApplicationOfferRunner) Add(fn func(*gomock.Controller)) { 130 s.ops = append(s.ops, fn) 131 } 132 133 func (s *ImportApplicationOfferRunner) Run(ctrl *gomock.Controller) error { 134 for _, v := range s.ops { 135 v(ctrl) 136 } 137 138 m := ImportApplicationOffer{} 139 return m.Execute(s.model, s.runner) 140 } 141 142 func (s *ImportApplicationOfferRunner) applicationOffers(entities ...description.ApplicationOffer) func(ctrl *gomock.Controller) { 143 return func(ctrl *gomock.Controller) { 144 s.model.EXPECT().Offers().Return(entities) 145 } 146 } 147 148 func (s *ImportApplicationOfferRunner) applicationOfferDoc(offerDoc applicationOfferDoc, entity description.ApplicationOffer) func(ctrl *gomock.Controller) { 149 return func(ctrl *gomock.Controller) { 150 s.model.EXPECT().MakeApplicationOfferDoc(entity).Return(offerDoc, nil) 151 } 152 } 153 154 func (s *ImportApplicationOfferRunner) applicationOffersRefOp(op txn.Op, cnt int) func(ctrl *gomock.Controller) { 155 return func(ctrl *gomock.Controller) { 156 s.model.EXPECT().MakeApplicationOffersRefOp("foo", cnt).Return(op, nil) 157 } 158 } 159 160 func (s *MigrationImportTasksSuite) applicationOffer(ctrl *gomock.Controller) *MockApplicationOffer { 161 return NewMockApplicationOffer(ctrl) 162 } 163 164 func (s *ImportApplicationOfferRunner) docID(offerName, docID string) func(ctrl *gomock.Controller) { 165 return func(ctrl *gomock.Controller) { 166 s.model.EXPECT().DocID(offerName).Return(docID) 167 } 168 } 169 170 func (s *ImportApplicationOfferRunner) transaction(offerDocs []applicationOfferDoc, permissionOps []txn.Op, ops ...txn.Op) func(ctrl *gomock.Controller) { 171 return func(ctrl *gomock.Controller) { 172 useOps := make([]txn.Op, 0) 173 174 for i, doc := range offerDocs { 175 useOps = append(useOps, []txn.Op{ 176 { 177 C: applicationOffersC, 178 Id: doc.DocID, 179 Assert: txn.DocMissing, 180 Insert: doc, 181 }, 182 permissionOps[i], 183 }...) 184 } 185 useOps = append(useOps, ops...) 186 187 s.runner.EXPECT().RunTransaction(useOps).Return(nil) 188 } 189 } 190 191 func (s *ImportApplicationOfferRunner) transactionWithError(err error) func(ctrl *gomock.Controller) { 192 return func(ctrl *gomock.Controller) { 193 s.runner.EXPECT().RunTransaction(gomock.Any()).Return(err) 194 } 195 } 196 197 func (s *MigrationImportTasksSuite) TestImportRemoteApplications(c *gc.C) { 198 ctrl := gomock.NewController(c) 199 defer ctrl.Finish() 200 201 status := s.status(ctrl) 202 203 entity0 := s.remoteApplication(ctrl, status) 204 entities := []description.RemoteApplication{ 205 entity0, 206 } 207 208 appDoc := &remoteApplicationDoc{ 209 Name: "remote-application", 210 URL: "me/model.rainbow", 211 OfferUUID: "offer-uuid", 212 Endpoints: []remoteEndpointDoc{ 213 {Name: "db", Interface: "mysql"}, 214 {Name: "db-admin", Interface: "mysql-root"}, 215 }, 216 Spaces: []remoteSpaceDoc{ 217 {CloudType: "ec2"}, 218 }, 219 } 220 statusDoc := statusDoc{} 221 statusOp := txn.Op{} 222 223 model := NewMockRemoteApplicationsInput(ctrl) 224 model.EXPECT().RemoteApplications().Return(entities) 225 model.EXPECT().MakeRemoteApplicationDoc(entity0).Return(appDoc) 226 model.EXPECT().NewRemoteApplication(appDoc).Return(&RemoteApplication{ 227 doc: *appDoc, 228 }) 229 model.EXPECT().MakeStatusDoc(status).Return(statusDoc) 230 model.EXPECT().MakeStatusOp("c#remote-application", statusDoc).Return(statusOp) 231 model.EXPECT().DocID("remote-application").Return("c#remote-application") 232 233 runner := NewMockTransactionRunner(ctrl) 234 runner.EXPECT().RunTransaction([]txn.Op{ 235 { 236 C: applicationsC, 237 Id: "remote-application", 238 Assert: txn.DocMissing, 239 }, 240 { 241 C: remoteApplicationsC, 242 Id: "c#remote-application", 243 Assert: txn.DocMissing, 244 Insert: appDoc, 245 }, 246 statusOp, 247 }).Return(nil) 248 249 m := ImportRemoteApplications{} 250 err := m.Execute(model, runner) 251 c.Assert(err, jc.ErrorIsNil) 252 } 253 254 // A Remote Application with a missing status field is a valid remote 255 // application and should be correctly imported. 256 func (s *MigrationImportTasksSuite) TestImportRemoteApplicationsWithMissingStatusField(c *gc.C) { 257 ctrl := gomock.NewController(c) 258 defer ctrl.Finish() 259 260 entity0 := s.remoteApplication(ctrl, nil) 261 262 entities := []description.RemoteApplication{ 263 entity0, 264 } 265 266 appDoc := &remoteApplicationDoc{ 267 Name: "remote-application", 268 URL: "me/model.rainbow", 269 OfferUUID: "offer-uuid", 270 Endpoints: []remoteEndpointDoc{ 271 {Name: "db", Interface: "mysql"}, 272 {Name: "db-admin", Interface: "mysql-root"}, 273 }, 274 Spaces: []remoteSpaceDoc{ 275 {CloudType: "ec2"}, 276 }, 277 } 278 279 model := NewMockRemoteApplicationsInput(ctrl) 280 model.EXPECT().RemoteApplications().Return(entities) 281 model.EXPECT().MakeRemoteApplicationDoc(entity0).Return(appDoc) 282 model.EXPECT().NewRemoteApplication(appDoc).Return(&RemoteApplication{ 283 doc: *appDoc, 284 }) 285 model.EXPECT().DocID("remote-application").Return("c#remote-application") 286 287 runner := NewMockTransactionRunner(ctrl) 288 runner.EXPECT().RunTransaction([]txn.Op{ 289 { 290 C: applicationsC, 291 Id: "remote-application", 292 Assert: txn.DocMissing, 293 }, 294 { 295 C: remoteApplicationsC, 296 Id: "c#remote-application", 297 Assert: txn.DocMissing, 298 Insert: appDoc, 299 }, 300 }).Return(nil) 301 302 m := ImportRemoteApplications{} 303 err := m.Execute(model, runner) 304 c.Assert(err, jc.ErrorIsNil) 305 } 306 307 func (s *MigrationImportTasksSuite) remoteApplication(ctrl *gomock.Controller, status description.Status) description.RemoteApplication { 308 entity := NewMockRemoteApplication(ctrl) 309 entity.EXPECT().Status().Return(status) 310 return entity 311 } 312 313 func (s *MigrationImportTasksSuite) status(ctrl *gomock.Controller) description.Status { 314 entity := NewMockStatus(ctrl) 315 return entity 316 } 317 318 func (s *MigrationImportTasksSuite) TestImportRemoteEntities(c *gc.C) { 319 ctrl := gomock.NewController(c) 320 defer ctrl.Finish() 321 322 entity0 := s.remoteEntity(ctrl, "application-app2", "xxx-yyy-ccc") 323 entity1 := s.remoteEntity(ctrl, "applicationoffer-offer3", "aaa-bbb-zzz") 324 325 entities := []description.RemoteEntity{ 326 entity0, 327 entity1, 328 } 329 330 model := NewMockRemoteEntitiesInput(ctrl) 331 model.EXPECT().RemoteEntities().Return(entities) 332 model.EXPECT().OfferUUIDForApp("app2").Return("uuid2", nil) 333 model.EXPECT().OfferUUID("offer3").Return("uuid3", true) 334 model.EXPECT().DocID("applicationoffer-uuid2").Return("doc-uuid2") 335 model.EXPECT().DocID("applicationoffer-uuid3").Return("doc-uuid3") 336 337 runner := NewMockTransactionRunner(ctrl) 338 runner.EXPECT().RunTransaction([]txn.Op{ 339 { 340 C: remoteEntitiesC, 341 Id: "doc-uuid2", 342 Assert: txn.DocMissing, 343 Insert: &remoteEntityDoc{ 344 DocID: "doc-uuid2", 345 Token: "xxx-yyy-ccc", 346 }, 347 }, 348 { 349 C: remoteEntitiesC, 350 Id: "doc-uuid3", 351 Assert: txn.DocMissing, 352 Insert: &remoteEntityDoc{ 353 DocID: "doc-uuid3", 354 Token: "aaa-bbb-zzz", 355 }, 356 }, 357 }).Return(nil) 358 359 m := ImportRemoteEntities{} 360 err := m.Execute(model, runner) 361 c.Assert(err, jc.ErrorIsNil) 362 } 363 364 func (s *MigrationImportTasksSuite) TestImportRemoteEntitiesWithNoEntities(c *gc.C) { 365 ctrl := gomock.NewController(c) 366 defer ctrl.Finish() 367 368 entities := []description.RemoteEntity{} 369 370 model := NewMockRemoteEntitiesInput(ctrl) 371 model.EXPECT().RemoteEntities().Return(entities) 372 373 runner := NewMockTransactionRunner(ctrl) 374 // No call to RunTransaction if there are no operations. 375 376 m := ImportRemoteEntities{} 377 err := m.Execute(model, runner) 378 c.Assert(err, jc.ErrorIsNil) 379 } 380 381 func (s *MigrationImportTasksSuite) TestImportRemoteEntitiesWithTransactionRunnerReturnsError(c *gc.C) { 382 ctrl := gomock.NewController(c) 383 defer ctrl.Finish() 384 385 entity0 := s.remoteEntity(ctrl, "application-uuid2", "xxx-yyy-ccc") 386 387 entities := []description.RemoteEntity{ 388 entity0, 389 } 390 391 model := NewMockRemoteEntitiesInput(ctrl) 392 model.EXPECT().RemoteEntities().Return(entities) 393 model.EXPECT().OfferUUIDForApp("uuid2").Return("offeruuid2", nil) 394 model.EXPECT().DocID("applicationoffer-offeruuid2").Return("doc-uuid2") 395 396 runner := NewMockTransactionRunner(ctrl) 397 runner.EXPECT().RunTransaction([]txn.Op{ 398 { 399 C: remoteEntitiesC, 400 Id: "doc-uuid2", 401 Assert: txn.DocMissing, 402 Insert: &remoteEntityDoc{ 403 DocID: "doc-uuid2", 404 Token: "xxx-yyy-ccc", 405 }, 406 }, 407 }).Return(errors.New("fail")) 408 409 m := ImportRemoteEntities{} 410 err := m.Execute(model, runner) 411 c.Assert(err, gc.ErrorMatches, "fail") 412 } 413 414 func (s *MigrationImportTasksSuite) remoteEntity(ctrl *gomock.Controller, id, token string) *MockRemoteEntity { 415 entity := NewMockRemoteEntity(ctrl) 416 entity.EXPECT().ID().Return(id).AnyTimes() 417 entity.EXPECT().Token().Return(token) 418 return entity 419 } 420 421 func (s *MigrationImportTasksSuite) TestImportRelationNetworks(c *gc.C) { 422 ctrl := gomock.NewController(c) 423 defer ctrl.Finish() 424 425 entity0 := s.relationNetwork(ctrl, "ctrl-uuid-2", "xxx-yyy-ccc", []string{"10.0.1.0/16"}) 426 entity1 := s.relationNetwork(ctrl, "ctrl-uuid-3", "aaa-bbb-zzz", []string{"10.0.0.1/24"}) 427 428 entities := []description.RelationNetwork{ 429 entity0, 430 entity1, 431 } 432 433 model := NewMockRelationNetworksInput(ctrl) 434 model.EXPECT().RelationNetworks().Return(entities) 435 model.EXPECT().DocID("ctrl-uuid-2").Return("ctrl-uuid-2") 436 model.EXPECT().DocID("ctrl-uuid-3").Return("ctrl-uuid-3") 437 438 runner := NewMockTransactionRunner(ctrl) 439 runner.EXPECT().RunTransaction([]txn.Op{ 440 { 441 C: relationNetworksC, 442 Id: "ctrl-uuid-2", 443 Assert: txn.DocMissing, 444 Insert: relationNetworksDoc{ 445 Id: "ctrl-uuid-2", 446 RelationKey: "xxx-yyy-ccc", 447 CIDRS: []string{"10.0.1.0/16"}, 448 }, 449 }, 450 { 451 C: relationNetworksC, 452 Id: "ctrl-uuid-3", 453 Assert: txn.DocMissing, 454 Insert: relationNetworksDoc{ 455 Id: "ctrl-uuid-3", 456 RelationKey: "aaa-bbb-zzz", 457 CIDRS: []string{"10.0.0.1/24"}, 458 }, 459 }, 460 }).Return(nil) 461 462 m := ImportRelationNetworks{} 463 err := m.Execute(model, runner) 464 c.Assert(err, jc.ErrorIsNil) 465 } 466 467 func (s *MigrationImportTasksSuite) TestImportRelationNetworksWithNoEntities(c *gc.C) { 468 ctrl := gomock.NewController(c) 469 defer ctrl.Finish() 470 471 entities := []description.RelationNetwork{} 472 473 model := NewMockRelationNetworksInput(ctrl) 474 model.EXPECT().RelationNetworks().Return(entities) 475 476 runner := NewMockTransactionRunner(ctrl) 477 // No call to RunTransaction if there are no operations. 478 479 m := ImportRelationNetworks{} 480 err := m.Execute(model, runner) 481 c.Assert(err, jc.ErrorIsNil) 482 } 483 484 func (s *MigrationImportTasksSuite) TestImportRelationNetworksWithTransactionRunnerReturnsError(c *gc.C) { 485 ctrl := gomock.NewController(c) 486 defer ctrl.Finish() 487 488 entity0 := s.relationNetwork(ctrl, "ctrl-uuid-2", "xxx-yyy-ccc", []string{"10.0.1.0/16"}) 489 490 entities := []description.RelationNetwork{ 491 entity0, 492 } 493 494 model := NewMockRelationNetworksInput(ctrl) 495 model.EXPECT().RelationNetworks().Return(entities) 496 model.EXPECT().DocID("ctrl-uuid-2").Return("ctrl-uuid-2") 497 498 runner := NewMockTransactionRunner(ctrl) 499 runner.EXPECT().RunTransaction([]txn.Op{ 500 { 501 C: relationNetworksC, 502 Id: "ctrl-uuid-2", 503 Assert: txn.DocMissing, 504 Insert: relationNetworksDoc{ 505 Id: "ctrl-uuid-2", 506 RelationKey: "xxx-yyy-ccc", 507 CIDRS: []string{"10.0.1.0/16"}, 508 }, 509 }, 510 }).Return(errors.New("fail")) 511 512 m := ImportRelationNetworks{} 513 err := m.Execute(model, runner) 514 c.Assert(err, gc.ErrorMatches, "fail") 515 } 516 517 func (s *MigrationImportTasksSuite) relationNetwork(ctrl *gomock.Controller, id, key string, cidrs []string) *MockRelationNetwork { 518 entity := NewMockRelationNetwork(ctrl) 519 entity.EXPECT().ID().Return(id) 520 entity.EXPECT().RelationKey().Return(key) 521 entity.EXPECT().CIDRS().Return(cidrs) 522 return entity 523 } 524 525 func (s *MigrationImportTasksSuite) TestImportExternalControllers(c *gc.C) { 526 ctrl := gomock.NewController(c) 527 defer ctrl.Finish() 528 529 entity0 := s.externalController(ctrl, "ctrl-uuid-2", "magic", "magic-cert", []string{"10.0.1.1"}, []string{"xxxx-yyyy-zzzz"}) 530 entity1 := s.externalController(ctrl, "ctrl-uuid-3", "foo", "foo-cert", []string{"10.0.2.24"}, []string{"aaaa-bbbb-cccc"}) 531 532 entities := []description.ExternalController{ 533 entity0, 534 entity1, 535 } 536 537 doc0 := externalControllerDoc{ 538 Id: "ctrl-uuid-2", 539 Addrs: []string{"10.0.1.1"}, 540 Alias: "magic", 541 CACert: "magic-cert", 542 Models: []string{"xxxx-yyyy-zzzz"}, 543 } 544 doc1 := externalControllerDoc{ 545 Id: "ctrl-uuid-3", 546 Addrs: []string{"10.0.2.24"}, 547 Alias: "foo", 548 CACert: "foo-cert", 549 Models: []string{"aaaa-bbbb-cccc"}, 550 } 551 ops := []txn.Op{ 552 { 553 C: externalControllersC, 554 Id: "ctrl-uuid-2", 555 Assert: txn.DocMissing, 556 Insert: doc0, 557 }, 558 { 559 C: externalControllersC, 560 Id: "ctrl-uuid-3", 561 Assert: txn.DocMissing, 562 Insert: doc1, 563 }, 564 } 565 566 model := NewMockExternalControllersInput(ctrl) 567 model.EXPECT().ExternalControllers().Return(entities) 568 gomock.InOrder( 569 model.EXPECT().ExternalControllerDoc("ctrl-uuid-2").Return(nil, nil), 570 model.EXPECT().MakeExternalControllerOp(doc0, nil).Return(ops[0]), 571 model.EXPECT().ExternalControllerDoc("ctrl-uuid-3").Return(nil, nil), 572 model.EXPECT().MakeExternalControllerOp(doc1, nil).Return(ops[1]), 573 ) 574 575 runner := NewMockTransactionRunner(ctrl) 576 runner.EXPECT().RunTransaction(ops).Return(nil) 577 578 m := ImportExternalControllers{} 579 err := m.Execute(model, runner) 580 c.Assert(err, jc.ErrorIsNil) 581 } 582 583 func (s *MigrationImportTasksSuite) TestImportExternalControllersWithNoEntities(c *gc.C) { 584 ctrl := gomock.NewController(c) 585 defer ctrl.Finish() 586 587 entities := []description.ExternalController{} 588 589 model := NewMockExternalControllersInput(ctrl) 590 model.EXPECT().ExternalControllers().Return(entities) 591 592 runner := NewMockTransactionRunner(ctrl) 593 // No call to RunTransaction if there are no operations. 594 595 m := ImportExternalControllers{} 596 err := m.Execute(model, runner) 597 c.Assert(err, jc.ErrorIsNil) 598 } 599 600 func (s *MigrationImportTasksSuite) TestImportExternalControllersWithTransactionRunnerReturnsError(c *gc.C) { 601 ctrl := gomock.NewController(c) 602 defer ctrl.Finish() 603 604 entity0 := s.externalController(ctrl, "ctrl-uuid-2", "magic", "magic-cert", []string{"10.0.1.1"}, []string{"xxxx-yyyy-zzzz"}) 605 606 entities := []description.ExternalController{ 607 entity0, 608 } 609 610 doc0 := externalControllerDoc{ 611 Id: "ctrl-uuid-2", 612 Addrs: []string{"10.0.1.1"}, 613 Alias: "magic", 614 CACert: "magic-cert", 615 Models: []string{"xxxx-yyyy-zzzz"}, 616 } 617 ops := []txn.Op{ 618 { 619 C: externalControllersC, 620 Id: "ctrl-uuid-2", 621 Assert: txn.DocMissing, 622 Insert: doc0, 623 }, 624 } 625 626 model := NewMockExternalControllersInput(ctrl) 627 model.EXPECT().ExternalControllers().Return(entities) 628 model.EXPECT().ExternalControllerDoc("ctrl-uuid-2").Return(nil, nil) 629 model.EXPECT().MakeExternalControllerOp(doc0, nil).Return(ops[0]) 630 631 runner := NewMockTransactionRunner(ctrl) 632 runner.EXPECT().RunTransaction([]txn.Op{ 633 { 634 C: externalControllersC, 635 Id: "ctrl-uuid-2", 636 Assert: txn.DocMissing, 637 Insert: externalControllerDoc{ 638 Id: "ctrl-uuid-2", 639 Addrs: []string{"10.0.1.1"}, 640 Alias: "magic", 641 CACert: "magic-cert", 642 Models: []string{"xxxx-yyyy-zzzz"}, 643 }, 644 }, 645 }).Return(errors.New("fail")) 646 647 m := ImportExternalControllers{} 648 err := m.Execute(model, runner) 649 c.Assert(err, gc.ErrorMatches, "fail") 650 } 651 652 func (s *MigrationImportTasksSuite) externalController(ctrl *gomock.Controller, id, alias, caCert string, addrs, models []string) *MockExternalController { 653 entity := NewMockExternalController(ctrl) 654 entity.EXPECT().ID().Return(names.NewControllerTag(id)) 655 entity.EXPECT().Alias().Return(alias) 656 entity.EXPECT().CACert().Return(caCert) 657 entity.EXPECT().Addrs().Return(addrs) 658 entity.EXPECT().Models().Return(models) 659 return entity 660 } 661 662 func (s *MigrationImportTasksSuite) TestImportFirewallRules(c *gc.C) { 663 ctrl := gomock.NewController(c) 664 defer ctrl.Finish() 665 666 entity0 := s.firewallRule(ctrl, "ssh", "ssh", []string{"192.168.0.1/24", "192.168.3.0/24"}) 667 entity1 := s.firewallRule(ctrl, "juju-application-offer", "juju-application-offer", []string{"10.0.0.1/16"}) 668 669 entities := []description.FirewallRule{ 670 entity0, 671 entity1, 672 } 673 674 modelIn := NewMockFirewallRulesInput(ctrl) 675 modelIn.EXPECT().FirewallRules().Return(entities) 676 677 modelOut := NewMockFirewallRulesOutput(ctrl) 678 modelOut.EXPECT().UpdateModelConfig(map[string]interface{}{ 679 config.SSHAllowKey: "192.168.0.1/24,192.168.3.0/24", 680 }, nil) 681 modelOut.EXPECT().UpdateModelConfig(map[string]interface{}{ 682 config.SAASIngressAllowKey: "10.0.0.1/16", 683 }, nil) 684 685 m := ImportFirewallRules{} 686 err := m.Execute(modelIn, modelOut) 687 c.Assert(err, jc.ErrorIsNil) 688 } 689 690 func (s *MigrationImportTasksSuite) TestImportFirewallRulesEmptyJujuApplicationOffer(c *gc.C) { 691 ctrl := gomock.NewController(c) 692 defer ctrl.Finish() 693 694 entity0 := s.firewallRule(ctrl, "juju-application-offer", "juju-application-offer", []string{}) 695 696 entities := []description.FirewallRule{ 697 entity0, 698 } 699 700 model := NewMockFirewallRulesInput(ctrl) 701 model.EXPECT().FirewallRules().Return(entities) 702 703 // No call to UpdateModeConfig since juju-application-offer is empty 704 705 m := ImportFirewallRules{} 706 err := m.Execute(model, nil) 707 c.Assert(err, jc.ErrorIsNil) 708 } 709 710 func (s *MigrationImportTasksSuite) TestImportFirewallRulesWithNoEntities(c *gc.C) { 711 ctrl := gomock.NewController(c) 712 defer ctrl.Finish() 713 714 entities := []description.FirewallRule{} 715 716 model := NewMockFirewallRulesInput(ctrl) 717 model.EXPECT().FirewallRules().Return(entities) 718 719 // No call to UpdateModeConfig if there are no operations. 720 721 m := ImportFirewallRules{} 722 err := m.Execute(model, nil) 723 c.Assert(err, jc.ErrorIsNil) 724 } 725 726 func (s *MigrationImportTasksSuite) TestImportFirewallRulesWithTransactionRunnerReturnsError(c *gc.C) { 727 ctrl := gomock.NewController(c) 728 defer ctrl.Finish() 729 730 entity0 := s.firewallRule(ctrl, "ssh", "ssh", []string{"192.168.0.1/24"}) 731 732 entities := []description.FirewallRule{ 733 entity0, 734 } 735 736 modelIn := NewMockFirewallRulesInput(ctrl) 737 modelIn.EXPECT().FirewallRules().Return(entities) 738 739 modelOut := NewMockFirewallRulesOutput(ctrl) 740 modelOut.EXPECT().UpdateModelConfig(map[string]interface{}{ 741 config.SSHAllowKey: "192.168.0.1/24", 742 }, nil).Return(errors.New("fail")) 743 744 m := ImportFirewallRules{} 745 err := m.Execute(modelIn, modelOut) 746 c.Assert(err, gc.ErrorMatches, "fail") 747 } 748 749 func (s *MigrationImportTasksSuite) firewallRule(ctrl *gomock.Controller, id, service string, whitelist []string) *MockFirewallRule { 750 entity := NewMockFirewallRule(ctrl) 751 entity.EXPECT().WellKnownService().Return(service) 752 entity.EXPECT().WhitelistCIDRs().Return(whitelist) 753 return entity 754 }