github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/migration_import_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "fmt" 8 "sort" 9 "strconv" 10 "time" // only uses time.Time values 11 12 "github.com/juju/charm/v12" 13 "github.com/juju/description/v5" 14 "github.com/juju/errors" 15 "github.com/juju/names/v5" 16 jc "github.com/juju/testing/checkers" 17 "github.com/juju/utils/v3" 18 "github.com/juju/version/v2" 19 "github.com/kr/pretty" 20 "go.uber.org/mock/gomock" 21 gc "gopkg.in/check.v1" 22 "gopkg.in/juju/environschema.v1" 23 "gopkg.in/yaml.v2" 24 25 "github.com/juju/juju/core/arch" 26 corecharm "github.com/juju/juju/core/charm" 27 "github.com/juju/juju/core/constraints" 28 "github.com/juju/juju/core/crossmodel" 29 "github.com/juju/juju/core/instance" 30 "github.com/juju/juju/core/model" 31 "github.com/juju/juju/core/network" 32 "github.com/juju/juju/core/payloads" 33 "github.com/juju/juju/core/permission" 34 "github.com/juju/juju/core/secrets" 35 "github.com/juju/juju/core/status" 36 "github.com/juju/juju/environs" 37 "github.com/juju/juju/environs/config" 38 "github.com/juju/juju/state" 39 "github.com/juju/juju/state/cloudimagemetadata" 40 "github.com/juju/juju/state/mocks" 41 "github.com/juju/juju/storage" 42 "github.com/juju/juju/storage/poolmanager" 43 "github.com/juju/juju/storage/provider" 44 coretesting "github.com/juju/juju/testing" 45 "github.com/juju/juju/testing/factory" 46 jujuversion "github.com/juju/juju/version" 47 ) 48 49 type MigrationImportSuite struct { 50 MigrationBaseSuite 51 } 52 53 var _ = gc.Suite(&MigrationImportSuite{}) 54 55 func (s *MigrationImportSuite) checkStatusHistory(c *gc.C, exported, imported status.StatusHistoryGetter, size int) { 56 exportedHistory, err := exported.StatusHistory(status.StatusHistoryFilter{Size: size}) 57 c.Assert(err, jc.ErrorIsNil) 58 importedHistory, err := imported.StatusHistory(status.StatusHistoryFilter{Size: size}) 59 c.Assert(err, jc.ErrorIsNil) 60 for i := 0; i < size; i++ { 61 c.Check(importedHistory[i].Status, gc.Equals, exportedHistory[i].Status) 62 c.Check(importedHistory[i].Message, gc.Equals, exportedHistory[i].Message) 63 c.Check(importedHistory[i].Data, jc.DeepEquals, exportedHistory[i].Data) 64 c.Check(importedHistory[i].Since, jc.DeepEquals, exportedHistory[i].Since) 65 } 66 } 67 68 func (s *MigrationImportSuite) TestExisting(c *gc.C) { 69 out, err := s.State.Export(map[string]string{}) 70 c.Assert(err, jc.ErrorIsNil) 71 72 _, _, err = s.Controller.Import(out) 73 c.Assert(err, jc.Satisfies, errors.IsAlreadyExists) 74 } 75 76 func (s *MigrationImportSuite) importModel( 77 c *gc.C, st *state.State, transform ...func(map[string]interface{}), 78 ) (*state.Model, *state.State) { 79 desc, err := st.Export(map[string]string{}) 80 c.Assert(err, jc.ErrorIsNil) 81 return s.importModelDescription(c, desc, transform...) 82 } 83 84 func (s *MigrationImportSuite) importModelDescription( 85 c *gc.C, desc description.Model, transform ...func(map[string]interface{}), 86 ) (*state.Model, *state.State) { 87 88 // When working with importing models, it becomes very handy to read the 89 // model in a human-readable format. 90 // yaml.Marshal will do this in a decent manor. 91 // bytes, _ := yaml.Marshal(desc) 92 // fmt.Println(string(bytes)) 93 94 if len(transform) > 0 { 95 var outM map[string]interface{} 96 outYaml, err := description.Serialize(desc) 97 c.Assert(err, jc.ErrorIsNil) 98 err = yaml.Unmarshal(outYaml, &outM) 99 c.Assert(err, jc.ErrorIsNil) 100 101 for _, transform := range transform { 102 transform(outM) 103 } 104 105 outYaml, err = yaml.Marshal(outM) 106 c.Assert(err, jc.ErrorIsNil) 107 desc, err = description.Deserialize(outYaml) 108 c.Assert(err, jc.ErrorIsNil) 109 } 110 111 uuid := utils.MustNewUUID().String() 112 in := newModel(desc, uuid, "new") 113 114 newModel, newSt, err := s.Controller.Import(in) 115 c.Assert(err, jc.ErrorIsNil) 116 117 s.AddCleanup(func(c *gc.C) { 118 c.Check(newSt.Close(), jc.ErrorIsNil) 119 }) 120 return newModel, newSt 121 } 122 123 func (s *MigrationImportSuite) assertAnnotations(c *gc.C, model *state.Model, entity state.GlobalEntity) { 124 annotations, err := model.Annotations(entity) 125 c.Assert(err, jc.ErrorIsNil) 126 c.Assert(annotations, jc.DeepEquals, testAnnotations) 127 } 128 129 func (s *MigrationImportSuite) TestNewModel(c *gc.C) { 130 cons := constraints.MustParse("arch=amd64 mem=8G") 131 latestTools := version.MustParse("2.0.1") 132 s.setLatestTools(c, latestTools) 133 c.Assert(s.State.SetModelConstraints(cons), jc.ErrorIsNil) 134 machineSeq := s.setRandSequenceValue(c, "machine") 135 fooSeq := s.setRandSequenceValue(c, "application-foo") 136 s.State.SwitchBlockOn(state.ChangeBlock, "locked down") 137 138 original, err := s.State.Model() 139 c.Assert(err, jc.ErrorIsNil) 140 141 environVersion := 123 142 err = original.SetEnvironVersion(environVersion) 143 c.Assert(err, jc.ErrorIsNil) 144 145 err = original.SetPassword("supersecret1111111111111") 146 c.Assert(err, jc.ErrorIsNil) 147 148 err = s.Model.SetAnnotations(original, testAnnotations) 149 c.Assert(err, jc.ErrorIsNil) 150 151 out, err := s.State.Export(map[string]string{}) 152 c.Assert(err, jc.ErrorIsNil) 153 154 uuid := utils.MustNewUUID().String() 155 in := newModel(out, uuid, "new") 156 157 newModel, newSt, err := s.Controller.Import(in) 158 c.Assert(err, jc.ErrorIsNil) 159 defer newSt.Close() 160 161 c.Assert(newModel.PasswordHash(), gc.Equals, utils.AgentPasswordHash("supersecret1111111111111")) 162 c.Assert(newModel.Type(), gc.Equals, original.Type()) 163 c.Assert(newModel.Owner(), gc.Equals, original.Owner()) 164 c.Assert(newModel.LatestToolsVersion(), gc.Equals, latestTools) 165 c.Assert(newModel.MigrationMode(), gc.Equals, state.MigrationModeImporting) 166 c.Assert(newModel.EnvironVersion(), gc.Equals, environVersion) 167 s.assertAnnotations(c, newModel, newModel) 168 169 statusInfo, err := newModel.Status() 170 c.Check(err, jc.ErrorIsNil) 171 c.Check(statusInfo.Status, gc.Equals, status.Busy) 172 c.Check(statusInfo.Message, gc.Equals, "importing") 173 // One for original "available", one for "busy (importing)" 174 history, err := newModel.StatusHistory(status.StatusHistoryFilter{Size: 5}) 175 c.Assert(err, jc.ErrorIsNil) 176 c.Check(history, gc.HasLen, 2) 177 c.Check(history[0].Status, gc.Equals, status.Busy) 178 c.Check(history[1].Status, gc.Equals, status.Available) 179 180 originalConfig, err := original.Config() 181 c.Assert(err, jc.ErrorIsNil) 182 originalAttrs := originalConfig.AllAttrs() 183 184 newConfig, err := newModel.Config() 185 c.Assert(err, jc.ErrorIsNil) 186 newAttrs := newConfig.AllAttrs() 187 188 c.Assert(newAttrs["uuid"], gc.Equals, uuid) 189 c.Assert(newAttrs["name"], gc.Equals, "new") 190 191 // Now drop the uuid and name and the rest of the attributes should match. 192 delete(newAttrs, "uuid") 193 delete(newAttrs, "name") 194 delete(originalAttrs, "uuid") 195 delete(originalAttrs, "name") 196 c.Assert(newAttrs, jc.DeepEquals, originalAttrs) 197 198 newCons, err := newSt.ModelConstraints() 199 c.Assert(err, jc.ErrorIsNil) 200 // Can't test the constraints directly, so go through the string repr. 201 c.Assert(newCons.String(), gc.Equals, cons.String()) 202 203 seq, err := state.Sequence(newSt, "machine") 204 c.Assert(err, jc.ErrorIsNil) 205 c.Assert(seq, gc.Equals, machineSeq) 206 seq, err = state.Sequence(newSt, "application-foo") 207 c.Assert(err, jc.ErrorIsNil) 208 c.Assert(seq, gc.Equals, fooSeq) 209 210 blocks, err := newSt.AllBlocks() 211 c.Assert(err, jc.ErrorIsNil) 212 c.Assert(blocks, gc.HasLen, 1) 213 c.Assert(blocks[0].Type(), gc.Equals, state.ChangeBlock) 214 c.Assert(blocks[0].Message(), gc.Equals, "locked down") 215 } 216 217 func (s *MigrationImportSuite) newModelUser(c *gc.C, name string, readOnly bool, lastConnection time.Time) permission.UserAccess { 218 access := permission.AdminAccess 219 if readOnly { 220 access = permission.ReadAccess 221 } 222 user, err := s.Model.AddUser(state.UserAccessSpec{ 223 User: names.NewUserTag(name), 224 CreatedBy: s.Owner, 225 Access: access, 226 }) 227 c.Assert(err, jc.ErrorIsNil) 228 if !lastConnection.IsZero() { 229 err = state.UpdateModelUserLastConnection(s.State, user, lastConnection) 230 c.Assert(err, jc.ErrorIsNil) 231 } 232 return user 233 } 234 235 func (s *MigrationImportSuite) AssertUserEqual(c *gc.C, newUser, oldUser permission.UserAccess) { 236 c.Assert(newUser.UserName, gc.Equals, oldUser.UserName) 237 c.Assert(newUser.DisplayName, gc.Equals, oldUser.DisplayName) 238 c.Assert(newUser.CreatedBy, gc.Equals, oldUser.CreatedBy) 239 c.Assert(newUser.DateCreated, gc.Equals, oldUser.DateCreated) 240 c.Assert(newUser.Access, gc.Equals, newUser.Access) 241 242 connTime, err := s.Model.LastModelConnection(oldUser.UserTag) 243 if state.IsNeverConnectedError(err) { 244 _, err := s.Model.LastModelConnection(newUser.UserTag) 245 // The new user should also return an error for last connection. 246 c.Assert(err, jc.Satisfies, state.IsNeverConnectedError) 247 } else { 248 c.Assert(err, jc.ErrorIsNil) 249 newTime, err := s.Model.LastModelConnection(newUser.UserTag) 250 c.Assert(err, jc.ErrorIsNil) 251 c.Assert(newTime, gc.Equals, connTime) 252 } 253 } 254 255 func (s *MigrationImportSuite) TestModelUsers(c *gc.C) { 256 // To be sure with this test, we create three env users, and remove 257 // the owner. 258 err := s.State.RemoveUserAccess(s.Owner, s.modelTag) 259 c.Assert(err, jc.ErrorIsNil) 260 261 lastConnection := state.NowToTheSecond(s.State) 262 263 bravo := s.newModelUser(c, "bravo@external", false, lastConnection) 264 charlie := s.newModelUser(c, "charlie@external", true, lastConnection) 265 delta := s.newModelUser(c, "delta@external", true, coretesting.ZeroTime()) 266 267 newModel, newSt := s.importModel(c, s.State) 268 269 // Check the import values of the users. 270 for _, user := range []permission.UserAccess{bravo, charlie, delta} { 271 newUser, err := newSt.UserAccess(user.UserTag, newModel.Tag()) 272 c.Assert(err, jc.ErrorIsNil) 273 s.AssertUserEqual(c, newUser, user) 274 } 275 276 // Also make sure that there aren't any more. 277 allUsers, err := newModel.Users() 278 c.Assert(err, jc.ErrorIsNil) 279 c.Assert(allUsers, gc.HasLen, 3) 280 } 281 282 func (s *MigrationImportSuite) TestSLA(c *gc.C) { 283 err := s.State.SetSLA("essential", "bob", []byte("creds")) 284 c.Assert(err, jc.ErrorIsNil) 285 newModel, newSt := s.importModel(c, s.State) 286 287 c.Assert(newModel.SLALevel(), gc.Equals, "essential") 288 c.Assert(newModel.SLACredential(), jc.DeepEquals, []byte("creds")) 289 level, err := newSt.SLALevel() 290 c.Assert(err, jc.ErrorIsNil) 291 c.Assert(level, gc.Equals, "essential") 292 creds, err := newSt.SLACredential() 293 c.Assert(err, jc.ErrorIsNil) 294 c.Assert(creds, jc.DeepEquals, []byte("creds")) 295 } 296 297 func (s *MigrationImportSuite) TestMeterStatus(c *gc.C) { 298 err := s.State.SetModelMeterStatus("RED", "info message") 299 c.Assert(err, jc.ErrorIsNil) 300 newModel, newSt := s.importModel(c, s.State) 301 302 ms := newModel.MeterStatus() 303 c.Assert(ms.Code.String(), gc.Equals, "RED") 304 c.Assert(ms.Info, gc.Equals, "info message") 305 ms, err = newSt.ModelMeterStatus() 306 c.Assert(err, jc.ErrorIsNil) 307 c.Assert(ms.Code.String(), gc.Equals, "RED") 308 c.Assert(ms.Info, gc.Equals, "info message") 309 } 310 311 func (s *MigrationImportSuite) TestMeterStatusNotAvailable(c *gc.C) { 312 newModel, newSt := s.importModel(c, s.State, func(desc map[string]interface{}) { 313 c.Log(desc["meter-status"]) 314 desc["meter-status"].(map[interface{}]interface{})["code"] = "" 315 }) 316 317 ms := newModel.MeterStatus() 318 c.Assert(ms.Code.String(), gc.Equals, "NOT AVAILABLE") 319 c.Assert(ms.Info, gc.Equals, "") 320 ms, err := newSt.ModelMeterStatus() 321 c.Assert(err, jc.ErrorIsNil) 322 c.Assert(ms.Code.String(), gc.Equals, "NOT AVAILABLE") 323 c.Assert(ms.Info, gc.Equals, "") 324 } 325 326 func (s *MigrationImportSuite) AssertMachineEqual(c *gc.C, newMachine, oldMachine *state.Machine) { 327 c.Assert(newMachine.Id(), gc.Equals, oldMachine.Id()) 328 c.Assert(newMachine.Principals(), jc.DeepEquals, oldMachine.Principals()) 329 c.Assert(newMachine.Base().String(), gc.Equals, oldMachine.Base().String()) 330 c.Assert(newMachine.ContainerType(), gc.Equals, oldMachine.ContainerType()) 331 newHardware, err := newMachine.HardwareCharacteristics() 332 c.Assert(err, jc.ErrorIsNil) 333 oldHardware, err := oldMachine.HardwareCharacteristics() 334 c.Assert(err, jc.ErrorIsNil) 335 c.Assert(newHardware, jc.DeepEquals, oldHardware) 336 c.Assert(newMachine.Jobs(), jc.DeepEquals, oldMachine.Jobs()) 337 c.Assert(newMachine.Life(), gc.Equals, oldMachine.Life()) 338 newTools, err := newMachine.AgentTools() 339 c.Assert(err, jc.ErrorIsNil) 340 oldTools, err := oldMachine.AgentTools() 341 c.Assert(err, jc.ErrorIsNil) 342 c.Assert(newTools, jc.DeepEquals, oldTools) 343 344 oldStatus, err := oldMachine.Status() 345 c.Assert(err, jc.ErrorIsNil) 346 newStatus, err := newMachine.Status() 347 c.Assert(err, jc.ErrorIsNil) 348 c.Assert(newStatus, jc.DeepEquals, oldStatus) 349 350 oldInstID, oldInstDisplayName, err := oldMachine.InstanceNames() 351 c.Assert(err, jc.ErrorIsNil) 352 newInstID, newInstDisplayName, err := newMachine.InstanceNames() 353 c.Assert(err, jc.ErrorIsNil) 354 c.Assert(newInstID, gc.Equals, oldInstID) 355 c.Assert(newInstDisplayName, gc.Equals, oldInstDisplayName) 356 357 oldStatus, err = oldMachine.InstanceStatus() 358 c.Assert(err, jc.ErrorIsNil) 359 newStatus, err = newMachine.InstanceStatus() 360 c.Assert(err, jc.ErrorIsNil) 361 c.Assert(newStatus, jc.DeepEquals, oldStatus) 362 } 363 364 func (s *MigrationImportSuite) TestMachines(c *gc.C) { 365 // Add a machine with an LXC container. 366 cons := constraints.MustParse("arch=amd64 mem=8G root-disk-source=bunyan") 367 source := "bunyan" 368 displayName := "test-display-name" 369 370 addr := network.NewSpaceAddress("1.1.1.1") 371 addr.SpaceID = "9" 372 373 machine1 := s.Factory.MakeMachine(c, &factory.MachineParams{ 374 Constraints: cons, 375 Characteristics: &instance.HardwareCharacteristics{ 376 RootDiskSource: &source, 377 }, 378 DisplayName: displayName, 379 Addresses: network.SpaceAddresses{addr}, 380 }) 381 err := s.Model.SetAnnotations(machine1, testAnnotations) 382 c.Assert(err, jc.ErrorIsNil) 383 s.primeStatusHistory(c, machine1, status.Started, 5) 384 385 // machine1 should have some instance data. 386 hardware, err := machine1.HardwareCharacteristics() 387 c.Assert(err, jc.ErrorIsNil) 388 c.Assert(hardware, gc.NotNil) 389 390 _ = s.Factory.MakeMachineNested(c, machine1.Id(), nil) 391 392 allMachines, err := s.State.AllMachines() 393 c.Assert(err, jc.ErrorIsNil) 394 c.Assert(allMachines, gc.HasLen, 2) 395 396 newModel, newSt := s.importModel(c, s.State) 397 398 importedMachines, err := newSt.AllMachines() 399 c.Assert(err, jc.ErrorIsNil) 400 c.Assert(importedMachines, gc.HasLen, 2) 401 402 // AllMachines returns the machines in the same order, yay us. 403 for i, newMachine := range importedMachines { 404 s.AssertMachineEqual(c, newMachine, allMachines[i]) 405 } 406 407 // And a few extra checks. 408 parent := importedMachines[0] 409 container := importedMachines[1] 410 containers, err := parent.Containers() 411 c.Assert(err, jc.ErrorIsNil) 412 c.Assert(containers, jc.DeepEquals, []string{container.Id()}) 413 parentId, isContainer := container.ParentId() 414 c.Assert(parentId, gc.Equals, parent.Id()) 415 c.Assert(isContainer, jc.IsTrue) 416 417 s.assertAnnotations(c, newModel, parent) 418 s.checkStatusHistory(c, machine1, parent, 5) 419 420 newCons, err := parent.Constraints() 421 c.Assert(err, jc.ErrorIsNil) 422 // Can't test the constraints directly, so go through the string repr. 423 c.Assert(newCons.String(), gc.Equals, cons.String()) 424 425 // Test the modification status is set to the initial state. 426 modStatus, err := parent.ModificationStatus() 427 c.Assert(err, jc.ErrorIsNil) 428 c.Assert(modStatus.Status, gc.Equals, status.Idle) 429 430 characteristics, err := parent.HardwareCharacteristics() 431 c.Assert(err, jc.ErrorIsNil) 432 c.Assert(*characteristics.RootDiskSource, gc.Equals, "bunyan") 433 } 434 435 func (s *MigrationImportSuite) TestMachineDevices(c *gc.C) { 436 machine := s.Factory.MakeMachine(c, nil) 437 // Create two devices, first with all fields set, second just to show that 438 // we do both. 439 sda := state.BlockDeviceInfo{ 440 DeviceName: "sda", 441 DeviceLinks: []string{"some", "data"}, 442 Label: "sda-label", 443 UUID: "some-uuid", 444 HardwareId: "magic", 445 WWN: "drbr", 446 BusAddress: "bus stop", 447 Size: 16 * 1024 * 1024 * 1024, 448 FilesystemType: "ext4", 449 InUse: true, 450 MountPoint: "/", 451 } 452 sdb := state.BlockDeviceInfo{DeviceName: "sdb", MountPoint: "/var/lib/lxd"} 453 err := machine.SetMachineBlockDevices(sda, sdb) 454 c.Assert(err, jc.ErrorIsNil) 455 456 _, newSt := s.importModel(c, s.State) 457 458 imported, err := newSt.Machine(machine.Id()) 459 c.Assert(err, jc.ErrorIsNil) 460 461 sb, err := state.NewStorageBackend(s.State) 462 c.Assert(err, jc.ErrorIsNil) 463 devices, err := sb.BlockDevices(imported.MachineTag()) 464 c.Assert(err, jc.ErrorIsNil) 465 466 c.Check(devices, jc.DeepEquals, []state.BlockDeviceInfo{sda, sdb}) 467 } 468 469 func (s *MigrationImportSuite) TestMachinePortOps(c *gc.C) { 470 ctrl, mockMachine := setupMockOpenedPortRanges(c, "3") 471 defer ctrl.Finish() 472 473 ops, err := state.MachinePortOps(s.State, mockMachine) 474 c.Assert(err, jc.ErrorIsNil) 475 c.Assert(ops, gc.HasLen, 1) 476 c.Assert(ops[0].Id, gc.Equals, "3") 477 } 478 479 func (s *MigrationImportSuite) ApplicationPortOps(c *gc.C) { 480 ctrl := gomock.NewController(c) 481 mockApplication := mocks.NewMockApplication(ctrl) 482 mockApplicationPortRanges := mocks.NewMockPortRanges(ctrl) 483 484 aExp := mockApplication.EXPECT() 485 aExp.Name().Return("gitlab") 486 aExp.OpenedPortRanges().Return(mockApplicationPortRanges) 487 488 opExp := mockApplicationPortRanges.EXPECT() 489 opExp.ByUnit().Return(nil) 490 491 ops, err := state.ApplicationPortOps(s.State, mockApplication) 492 c.Assert(err, jc.ErrorIsNil) 493 c.Assert(ops, gc.HasLen, 1) 494 c.Assert(ops[0].Id, gc.Equals, "gitlab") 495 } 496 497 //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/description_mock.go github.com/juju/description/v5 Application,Machine,PortRanges,UnitPortRanges 498 func setupMockOpenedPortRanges(c *gc.C, mID string) (*gomock.Controller, *mocks.MockMachine) { 499 ctrl := gomock.NewController(c) 500 mockMachine := mocks.NewMockMachine(ctrl) 501 mockMachinePortRanges := mocks.NewMockPortRanges(ctrl) 502 503 mExp := mockMachine.EXPECT() 504 mExp.Id().Return(mID) 505 mExp.OpenedPortRanges().Return(mockMachinePortRanges) 506 507 opExp := mockMachinePortRanges.EXPECT() 508 opExp.ByUnit().Return(nil) 509 510 return ctrl, mockMachine 511 } 512 513 func (s *MigrationImportSuite) setupSourceApplications( 514 c *gc.C, st *state.State, cons constraints.Value, 515 platform *state.Platform, primeStatusHistory bool, 516 ) (*state.Charm, *state.Application, string) { 517 // Add a application with charm settings, app config, and leadership settings. 518 f := factory.NewFactory(st, s.StatePool) 519 520 serverSpace, err := s.State.AddSpace("server", "", nil, true) 521 c.Assert(err, jc.ErrorIsNil) 522 exposedSpaceIDs := []string{serverSpace.Id()} 523 524 testModel, err := st.Model() 525 c.Assert(err, jc.ErrorIsNil) 526 series := "quantal" 527 if testModel.Type() == state.ModelTypeCAAS { 528 series = "kubernetes" 529 exposedSpaceIDs = nil 530 } 531 // Add a application with charm settings, app config, and leadership settings. 532 testCharm := f.MakeCharm(c, &factory.CharmParams{ 533 Name: "starsay", // it has resources 534 Series: series, 535 }) 536 c.Assert(testCharm.Meta().Resources, gc.HasLen, 3) 537 application, pwd := f.MakeApplicationReturningPassword(c, &factory.ApplicationParams{ 538 Charm: testCharm, 539 CharmOrigin: &state.CharmOrigin{ 540 Source: "charm-hub", 541 Type: "charm", 542 Revision: &charm.MustParseURL(testCharm.URL()).Revision, 543 Channel: &state.Channel{ 544 Risk: "edge", 545 }, 546 Hash: "some-hash", 547 ID: "some-id", 548 Platform: platform, 549 }, 550 CharmConfig: map[string]interface{}{ 551 "foo": "bar", 552 }, 553 ApplicationConfig: map[string]interface{}{ 554 "app foo": "app bar", 555 }, 556 ApplicationConfigFields: environschema.Fields{ 557 "app foo": environschema.Attr{Type: environschema.Tstring}}, 558 Constraints: cons, 559 DesiredScale: 3, 560 }) 561 err = application.UpdateLeaderSettings(&goodToken{}, map[string]string{ 562 "leader": "true", 563 }) 564 c.Assert(err, jc.ErrorIsNil) 565 err = application.SetMetricCredentials([]byte("sekrit")) 566 c.Assert(err, jc.ErrorIsNil) 567 // Expose the application. 568 err = application.MergeExposeSettings(map[string]state.ExposedEndpoint{ 569 "admin": { 570 ExposeToSpaceIDs: exposedSpaceIDs, 571 ExposeToCIDRs: []string{"13.37.0.0/16"}, 572 }, 573 }) 574 c.Assert(err, jc.ErrorIsNil) 575 err = testModel.SetAnnotations(application, testAnnotations) 576 c.Assert(err, jc.ErrorIsNil) 577 if testModel.Type() == state.ModelTypeCAAS { 578 application.SetOperatorStatus(status.StatusInfo{Status: status.Running}) 579 } 580 if primeStatusHistory { 581 s.primeStatusHistory(c, application, status.Active, 5) 582 } 583 return testCharm, application, pwd 584 } 585 586 func (s *MigrationImportSuite) assertImportedApplication( 587 c *gc.C, application *state.Application, pwd string, cons constraints.Value, 588 exported *state.Application, newModel *state.Model, newSt *state.State, checkStatusHistory bool, 589 ) { 590 importedApplications, err := newSt.AllApplications() 591 c.Assert(err, jc.ErrorIsNil) 592 c.Assert(importedApplications, gc.HasLen, 1) 593 imported := importedApplications[0] 594 595 c.Assert(imported.ApplicationTag(), gc.Equals, exported.ApplicationTag()) 596 c.Assert(imported.IsExposed(), gc.Equals, exported.IsExposed()) 597 c.Assert(imported.ExposedEndpoints(), gc.DeepEquals, exported.ExposedEndpoints()) 598 c.Assert(imported.MetricCredentials(), jc.DeepEquals, exported.MetricCredentials()) 599 c.Assert(imported.PasswordValid(pwd), jc.IsTrue) 600 exportedOrigin := exported.CharmOrigin() 601 if corecharm.CharmHub.Matches(exportedOrigin.Source) && exportedOrigin.Channel.Track == "" { 602 exportedOrigin.Channel.Track = "latest" 603 } 604 c.Assert(imported.CharmOrigin(), jc.DeepEquals, exportedOrigin) 605 606 exportedCharmConfig, err := exported.CharmConfig(model.GenerationMaster) 607 c.Assert(err, jc.ErrorIsNil) 608 importedCharmConfig, err := imported.CharmConfig(model.GenerationMaster) 609 c.Assert(err, jc.ErrorIsNil) 610 c.Assert(importedCharmConfig, jc.DeepEquals, exportedCharmConfig) 611 612 exportedAppConfig, err := exported.ApplicationConfig() 613 c.Assert(err, jc.ErrorIsNil) 614 importedAppConfig, err := imported.ApplicationConfig() 615 c.Assert(err, jc.ErrorIsNil) 616 c.Assert(importedAppConfig, jc.DeepEquals, exportedAppConfig) 617 618 exportedLeaderSettings, err := exported.LeaderSettings() 619 c.Assert(err, jc.ErrorIsNil) 620 importedLeaderSettings, err := imported.LeaderSettings() 621 c.Assert(err, jc.ErrorIsNil) 622 c.Assert(importedLeaderSettings, jc.DeepEquals, exportedLeaderSettings) 623 624 s.assertAnnotations(c, newModel, imported) 625 if checkStatusHistory { 626 s.checkStatusHistory(c, application, imported, 5) 627 } 628 629 newCons, err := imported.Constraints() 630 c.Assert(err, jc.ErrorIsNil) 631 // Can't test the constraints directly, so go through the string repr. 632 c.Assert(newCons.String(), gc.Equals, cons.String()) 633 634 rSt := newSt.Resources() 635 resources, err := rSt.ListResources(imported.Name()) 636 c.Assert(err, jc.ErrorIsNil) 637 c.Assert(resources.Resources, gc.HasLen, 3) 638 639 if newModel.Type() == state.ModelTypeCAAS { 640 agentTools := version.Binary{ 641 Number: jujuversion.Current, 642 Arch: arch.HostArch(), 643 Release: application.CharmOrigin().Platform.OS, 644 } 645 646 tools, err := imported.AgentTools() 647 c.Assert(err, jc.ErrorIsNil) 648 c.Assert(tools.Version, gc.Equals, agentTools) 649 } 650 } 651 652 func (s *MigrationImportSuite) TestApplications(c *gc.C) { 653 cons := constraints.MustParse("arch=amd64 mem=8G root-disk-source=tralfamadore") 654 platform := &state.Platform{Architecture: arch.DefaultArchitecture, OS: "ubuntu", Channel: "12.10/stable"} 655 testCharm, application, pwd := s.setupSourceApplications(c, s.State, cons, platform, true) 656 657 allApplications, err := s.State.AllApplications() 658 c.Assert(err, jc.ErrorIsNil) 659 c.Assert(allApplications, gc.HasLen, 1) 660 exported := allApplications[0] 661 662 newModel, newSt := s.importModel(c, s.State) 663 // Manually copy across the charm from the old model 664 // as it's normally done later. 665 f := factory.NewFactory(newSt, s.StatePool) 666 f.MakeCharm(c, &factory.CharmParams{ 667 Name: "starsay", // it has resources 668 URL: testCharm.URL(), 669 Revision: strconv.Itoa(testCharm.Revision()), 670 }) 671 s.assertImportedApplication(c, application, pwd, cons, exported, newModel, newSt, true) 672 } 673 674 func (s *MigrationImportSuite) TestApplicationsUpdateSeriesNotPlatform(c *gc.C) { 675 // The application series should be quantal, the origin platform series should 676 // be focal. After migration, the platform series should be quantal as well. 677 cons := constraints.MustParse("arch=amd64 mem=8G root-disk-source=tralfamadore") 678 platform := &state.Platform{ 679 Architecture: arch.DefaultArchitecture, 680 OS: "ubuntu", 681 Channel: "20.04/stable", 682 } 683 _, _, _ = s.setupSourceApplications(c, s.State, cons, platform, true) 684 685 allApplications, err := s.State.AllApplications() 686 c.Assert(err, jc.ErrorIsNil) 687 c.Assert(allApplications, gc.HasLen, 1) 688 exportedApp := allApplications[0] 689 origin := exportedApp.CharmOrigin() 690 c.Check(origin, gc.NotNil) 691 c.Check(origin.Platform, gc.NotNil) 692 c.Check(origin.Platform.Channel, gc.Equals, "20.04/stable") 693 694 _, newSt := s.importModel(c, s.State) 695 696 obtainedApp, err := newSt.Application("starsay") 697 c.Assert(err, jc.ErrorIsNil) 698 obtainedOrigin := obtainedApp.CharmOrigin() 699 c.Assert(obtainedOrigin, gc.NotNil) 700 c.Assert(obtainedOrigin.Platform, gc.NotNil) 701 c.Assert(obtainedOrigin.Platform.Architecture, gc.Equals, arch.DefaultArchitecture) 702 c.Assert(obtainedOrigin.Platform.OS, gc.Equals, "ubuntu") 703 c.Assert(obtainedOrigin.Platform.Channel, gc.Equals, "20.04/stable") 704 } 705 706 func (s *MigrationImportSuite) TestCharmhubApplicationCharmOriginNormalised(c *gc.C) { 707 platform := &state.Platform{Architecture: arch.DefaultArchitecture, OS: "ubuntu", Channel: "12.10/stable"} 708 f := factory.NewFactory(s.State, s.StatePool) 709 710 testCharm := f.MakeCharm(c, &factory.CharmParams{Revision: "8", URL: "ch:mysql-8"}) 711 wrongRev := 4 712 _ = f.MakeApplication(c, &factory.ApplicationParams{ 713 Charm: testCharm, 714 Name: "mysql", 715 CharmOrigin: &state.CharmOrigin{ 716 Source: "charm-hub", 717 Type: "charm", 718 Platform: platform, 719 Revision: &wrongRev, 720 Channel: &state.Channel{Track: "20.04", Risk: "stable", Branch: "deadbeef"}, 721 Hash: "some-hash", 722 ID: "some-id", 723 }, 724 }) 725 726 _, newSt := s.importModel(c, s.State) 727 newApp, err := newSt.Application("mysql") 728 c.Assert(err, jc.ErrorIsNil) 729 rev := 8 730 c.Assert(newApp.CharmOrigin(), gc.DeepEquals, &state.CharmOrigin{ 731 Source: "charm-hub", 732 Type: "charm", 733 Platform: platform, 734 Revision: &rev, 735 Channel: &state.Channel{Track: "20.04", Risk: "stable", Branch: "deadbeef"}, 736 Hash: "some-hash", 737 ID: "some-id", 738 }) 739 } 740 741 func (s *MigrationImportSuite) TestLocalApplicationCharmOriginNormalised(c *gc.C) { 742 platform := &state.Platform{Architecture: arch.DefaultArchitecture, OS: "ubuntu", Channel: "12.10/stable"} 743 f := factory.NewFactory(s.State, s.StatePool) 744 745 testCharm := f.MakeCharm(c, &factory.CharmParams{Revision: "8", URL: "local:mysql-8"}) 746 wrongRev := 4 747 _ = f.MakeApplication(c, &factory.ApplicationParams{ 748 Charm: testCharm, 749 Name: "mysql", 750 CharmOrigin: &state.CharmOrigin{ 751 Source: "charm-hub", 752 Type: "charm", 753 Platform: platform, 754 Revision: &wrongRev, 755 Channel: &state.Channel{Track: "20.04", Risk: "stable", Branch: "deadbeef"}, 756 Hash: "some-hash", 757 ID: "some-id", 758 }, 759 }) 760 761 _, newSt := s.importModel(c, s.State) 762 newApp, err := newSt.Application("mysql") 763 c.Assert(err, jc.ErrorIsNil) 764 rev := 8 765 c.Assert(newApp.CharmOrigin(), gc.DeepEquals, &state.CharmOrigin{ 766 Source: "local", 767 Type: "charm", 768 Platform: platform, 769 Revision: &rev, 770 }) 771 } 772 773 func (s *MigrationImportSuite) TestApplicationStatus(c *gc.C) { 774 cons := constraints.MustParse("arch=amd64 mem=8G") 775 platform := &state.Platform{Architecture: arch.DefaultArchitecture, OS: "ubuntu", Channel: "12.10/stable"} 776 testCharm, application, pwd := s.setupSourceApplications(c, s.State, cons, platform, false) 777 778 s.Factory.MakeUnit(c, &factory.UnitParams{ 779 Application: application, 780 Status: &status.StatusInfo{ 781 Status: status.Active, 782 Message: "unit active", 783 }, 784 }) 785 786 allApplications, err := s.State.AllApplications() 787 c.Assert(err, jc.ErrorIsNil) 788 c.Assert(allApplications, gc.HasLen, 1) 789 exported := allApplications[0] 790 791 newModel, newSt := s.importModel(c, s.State) 792 // Manually copy across the charm from the old model 793 // as it's normally done later. 794 f := factory.NewFactory(newSt, s.StatePool) 795 f.MakeCharm(c, &factory.CharmParams{ 796 Name: "starsay", // it has resources 797 URL: testCharm.URL(), 798 Revision: strconv.Itoa(testCharm.Revision()), 799 }) 800 s.assertImportedApplication(c, application, pwd, cons, exported, newModel, newSt, false) 801 newApp, err := newSt.Application(application.Name()) 802 c.Assert(err, jc.ErrorIsNil) 803 // Has unset application status. 804 appStatus, err := newApp.Status() 805 c.Assert(err, jc.ErrorIsNil) 806 c.Assert(appStatus.Status, gc.Equals, status.Unset) 807 c.Assert(appStatus.Message, gc.Equals, "") 808 } 809 810 func (s *MigrationImportSuite) TestCAASApplications(c *gc.C) { 811 caasSt := s.Factory.MakeCAASModel(c, nil) 812 s.AddCleanup(func(_ *gc.C) { caasSt.Close() }) 813 814 cons := constraints.MustParse("arch=amd64 mem=8G") 815 platform := &state.Platform{Architecture: arch.DefaultArchitecture, OS: "ubuntu", Channel: "20.04/stable"} 816 charm, application, pwd := s.setupSourceApplications(c, caasSt, cons, platform, true) 817 818 model, err := caasSt.Model() 819 c.Assert(err, jc.ErrorIsNil) 820 caasModel, err := model.CAASModel() 821 c.Assert(err, jc.ErrorIsNil) 822 err = caasModel.SetPodSpec(nil, application.ApplicationTag(), strPtr("pod spec")) 823 c.Assert(err, jc.ErrorIsNil) 824 addr := network.NewSpaceAddress("192.168.1.1", network.WithScope(network.ScopeCloudLocal)) 825 addr.SpaceID = "0" 826 err = application.UpdateCloudService("provider-id", []network.SpaceAddress{addr}) 827 c.Assert(err, jc.ErrorIsNil) 828 829 allApplications, err := caasSt.AllApplications() 830 c.Assert(err, jc.ErrorIsNil) 831 c.Assert(allApplications, gc.HasLen, 1) 832 exported := allApplications[0] 833 834 newModel, newSt := s.importModel(c, caasSt) 835 // Manually copy across the charm from the old model 836 // as it's normally done later. 837 f := factory.NewFactory(newSt, s.StatePool) 838 f.MakeCharm(c, &factory.CharmParams{ 839 Name: "starsay", // it has resources 840 Series: "kubernetes", 841 URL: charm.URL(), 842 Revision: strconv.Itoa(charm.Revision()), 843 }) 844 s.assertImportedApplication(c, application, pwd, cons, exported, newModel, newSt, true) 845 newCAASModel, err := newModel.CAASModel() 846 c.Assert(err, jc.ErrorIsNil) 847 podSpec, err := newCAASModel.PodSpec(application.ApplicationTag()) 848 c.Assert(err, jc.ErrorIsNil) 849 c.Assert(podSpec, gc.Equals, "pod spec") 850 newApp, err := newSt.Application(application.Name()) 851 c.Assert(err, jc.ErrorIsNil) 852 cloudService, err := newApp.ServiceInfo() 853 c.Assert(err, jc.ErrorIsNil) 854 c.Assert(cloudService.ProviderId(), gc.Equals, "provider-id") 855 c.Assert(cloudService.Addresses(), jc.DeepEquals, network.SpaceAddresses{addr}) 856 c.Assert(newApp.GetScale(), gc.Equals, 3) 857 c.Assert(newApp.GetPlacement(), gc.Equals, "") 858 c.Assert(state.GetApplicationHasResources(newApp), jc.IsTrue) 859 } 860 861 func (s *MigrationImportSuite) TestCAASApplicationStatus(c *gc.C) { 862 // Caas application status that is derived from unit statuses must survive migration. 863 caasSt := s.Factory.MakeCAASModel(c, nil) 864 s.AddCleanup(func(_ *gc.C) { caasSt.Close() }) 865 866 cons := constraints.MustParse("arch=amd64 mem=8G") 867 platform := &state.Platform{Architecture: arch.DefaultArchitecture, OS: "ubuntu", Channel: "20.04"} 868 testCharm, application, _ := s.setupSourceApplications(c, caasSt, cons, platform, false) 869 ss, err := application.Status() 870 c.Assert(err, jc.ErrorIsNil) 871 c.Logf("status: %s", ss) 872 873 addUnitFactory := factory.NewFactory(caasSt, s.StatePool) 874 unit := addUnitFactory.MakeUnit(c, &factory.UnitParams{ 875 Application: application, 876 Status: &status.StatusInfo{ 877 Status: status.Active, 878 Message: "unit active", 879 }, 880 }) 881 var updateUnits state.UpdateUnitsOperation 882 updateUnits.Updates = []*state.UpdateUnitOperation{ 883 unit.UpdateOperation(state.UnitUpdateProperties{ 884 ProviderId: strPtr("provider-id"), 885 Address: strPtr("192.168.1.2"), 886 Ports: &[]string{"80"}, 887 CloudContainerStatus: &status.StatusInfo{ 888 Status: status.Active, 889 Message: "cloud container active", 890 }, 891 })} 892 err = application.UpdateUnits(&updateUnits) 893 c.Assert(err, jc.ErrorIsNil) 894 895 testModel, err := caasSt.Model() 896 c.Assert(err, jc.ErrorIsNil) 897 caasModel, err := testModel.CAASModel() 898 c.Assert(err, jc.ErrorIsNil) 899 err = caasModel.SetPodSpec(nil, application.ApplicationTag(), strPtr("pod spec")) 900 c.Assert(err, jc.ErrorIsNil) 901 addr := network.NewSpaceAddress("192.168.1.1", network.WithScope(network.ScopeCloudLocal)) 902 err = application.UpdateCloudService("provider-id", []network.SpaceAddress{addr}) 903 c.Assert(err, jc.ErrorIsNil) 904 905 allApplications, err := caasSt.AllApplications() 906 c.Assert(err, jc.ErrorIsNil) 907 c.Assert(allApplications, gc.HasLen, 1) 908 909 _, newSt := s.importModel(c, caasSt) 910 // Manually copy across the charm from the old model 911 // as it's normally done later. 912 f := factory.NewFactory(newSt, s.StatePool) 913 f.MakeCharm(c, &factory.CharmParams{ 914 Name: "starsay", // it has resources 915 Series: "kubernetes", 916 URL: testCharm.URL(), 917 Revision: strconv.Itoa(testCharm.Revision()), 918 }) 919 newApp, err := newSt.Application(application.Name()) 920 c.Assert(err, jc.ErrorIsNil) 921 // Has unset application status. 922 appStatus, err := newApp.Status() 923 c.Assert(err, jc.ErrorIsNil) 924 c.Assert(appStatus.Status, gc.Equals, status.Unset) 925 c.Assert(appStatus.Message, gc.Equals, "") 926 } 927 928 func (s *MigrationImportSuite) TestApplicationsWithExposedOffers(c *gc.C) { 929 _ = s.Factory.MakeUser(c, &factory.UserParams{Name: "admin"}) 930 fooUser := s.Factory.MakeUser(c, &factory.UserParams{Name: "foo"}) 931 serverSpace, err := s.State.AddSpace("server", "", nil, true) 932 c.Assert(err, jc.ErrorIsNil) 933 934 wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 935 wordpressEP, err := wordpress.Endpoint("db") 936 c.Assert(err, jc.ErrorIsNil) 937 938 testCharm := s.AddTestingCharm(c, "mysql") 939 application := s.AddTestingApplicationWithBindings(c, "mysql", 940 testCharm, 941 map[string]string{ 942 "server": serverSpace.Id(), 943 }, 944 ) 945 applicationEP, err := application.Endpoint("server") 946 c.Assert(err, jc.ErrorIsNil) 947 _, err = s.State.AddRelation(wordpressEP, applicationEP) 948 c.Assert(err, jc.ErrorIsNil) 949 950 stOffers := state.NewApplicationOffers(s.State) 951 stOffer, err := stOffers.AddOffer( 952 crossmodel.AddApplicationOfferArgs{ 953 OfferName: "my-offer", 954 Owner: "admin", 955 ApplicationDescription: "my app", 956 ApplicationName: application.Name(), 957 Endpoints: map[string]string{ 958 "server": serverSpace.Name(), 959 }, 960 }, 961 ) 962 c.Assert(err, jc.ErrorIsNil) 963 964 // Allow "foo" to consume offer 965 err = s.State.CreateOfferAccess( 966 names.NewApplicationOfferTag(stOffer.OfferUUID), 967 fooUser.UserTag(), 968 permission.ConsumeAccess, 969 ) 970 c.Assert(err, jc.ErrorIsNil) 971 972 stateOffers := state.NewApplicationOffers(s.State) 973 exportedOffers, err := stateOffers.AllApplicationOffers() 974 c.Assert(err, jc.ErrorIsNil) 975 c.Assert(exportedOffers, gc.HasLen, 1) 976 exported := exportedOffers[0] 977 978 _, newSt := s.importModel(c, s.State, func(_ map[string]interface{}) { 979 // Application offer permissions are keyed on the offer uuid 980 // rather than a model uuid. 981 // If we import and the permissions still exist, the txn will fail 982 // as imports all assume any records do not already exist. 983 err = s.State.RemoveOfferAccess( 984 names.NewApplicationOfferTag(stOffer.OfferUUID), 985 fooUser.UserTag(), 986 ) 987 c.Assert(err, jc.ErrorIsNil) 988 err = s.State.RemoveOfferAccess( 989 names.NewApplicationOfferTag(stOffer.OfferUUID), 990 names.NewUserTag("admin"), 991 ) 992 c.Assert(err, jc.ErrorIsNil) 993 }) 994 995 // The following is required because we don't add charms during an import, 996 // these are added at a later date. When constructing an application offer, 997 // the charm is required for the charm.Relation, so we need to inject it 998 // into the new state. 999 state.AddTestingCharm(c, newSt, "mysql") 1000 1001 newStateOffers := state.NewApplicationOffers(newSt) 1002 importedOffers, err := newStateOffers.AllApplicationOffers() 1003 c.Assert(err, jc.ErrorIsNil) 1004 c.Assert(importedOffers, gc.HasLen, 1) 1005 imported := importedOffers[0] 1006 c.Assert(exported, gc.DeepEquals, imported) 1007 1008 users, err := newSt.GetOfferUsers(stOffer.OfferUUID) 1009 c.Assert(err, jc.ErrorIsNil) 1010 c.Assert(users, gc.HasLen, 2) 1011 c.Assert(users, gc.DeepEquals, map[string]permission.Access{ 1012 "admin": "admin", 1013 "foo": "consume", 1014 }) 1015 } 1016 1017 func (s *MigrationImportSuite) TestExternalControllers(c *gc.C) { 1018 remoteApp, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 1019 Name: "gravy-rainbow", 1020 URL: "me/model.rainbow", 1021 SourceModel: s.Model.ModelTag(), 1022 Token: "charisma", 1023 }) 1024 c.Assert(err, jc.ErrorIsNil) 1025 err = remoteApp.SetStatus(status.StatusInfo{Status: status.Active}) 1026 c.Assert(err, jc.ErrorIsNil) 1027 1028 stateExternalCtrl := state.NewExternalControllers(s.State) 1029 crossModelController, err := stateExternalCtrl.Save(crossmodel.ControllerInfo{ 1030 ControllerTag: s.Model.ControllerTag(), 1031 Addrs: []string{"192.168.1.1:8080"}, 1032 Alias: "magic", 1033 CACert: "magic-ca-cert", 1034 }, s.Model.UUID()) 1035 c.Assert(err, jc.ErrorIsNil) 1036 1037 stateExternalController, err := s.State.ExternalControllerForModel(s.Model.UUID()) 1038 c.Assert(err, jc.ErrorIsNil) 1039 1040 _, newSt := s.importModel(c, s.State, func(map[string]interface{}) { 1041 err := stateExternalCtrl.Remove(s.Model.ControllerTag().Id()) 1042 c.Assert(err, jc.ErrorIsNil) 1043 }) 1044 1045 newExternalCtrl := state.NewExternalControllers(newSt) 1046 1047 newCtrl, err := newExternalCtrl.ControllerForModel(s.Model.UUID()) 1048 c.Assert(err, jc.ErrorIsNil) 1049 c.Assert(newCtrl.ControllerInfo(), jc.DeepEquals, crossModelController.ControllerInfo()) 1050 1051 newExternalController, err := newSt.ExternalControllerForModel(s.Model.UUID()) 1052 c.Assert(err, jc.ErrorIsNil) 1053 c.Assert(stateExternalController, gc.DeepEquals, newExternalController) 1054 } 1055 1056 func (s *MigrationImportSuite) TestCharmRevSequencesNotImported(c *gc.C) { 1057 s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1058 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{ 1059 Name: "mysql", 1060 URL: "local:trusty/mysql-2", 1061 }), 1062 }) 1063 // Sequence should be set in the source model. 1064 const charmSeqName = "charmrev-local:trusty/mysql" 1065 nextVal, err := state.Sequence(s.State, charmSeqName) 1066 c.Assert(err, jc.ErrorIsNil) 1067 c.Check(nextVal, gc.Equals, 3) 1068 1069 out, err := s.State.Export(map[string]string{}) 1070 c.Assert(err, jc.ErrorIsNil) 1071 1072 c.Assert(len(out.Applications()), gc.Equals, 1) 1073 1074 uuid := utils.MustNewUUID().String() 1075 in := newModel(out, uuid, "new") 1076 1077 _, newSt, err := s.Controller.Import(in) 1078 c.Assert(err, jc.ErrorIsNil) 1079 defer newSt.Close() 1080 1081 // Charm revision sequence shouldn't have been imported. The 1082 // import of the charm binaries (done separately later) will 1083 // handle this. 1084 nextVal, err = state.Sequence(newSt, charmSeqName) 1085 c.Assert(err, jc.ErrorIsNil) 1086 c.Check(nextVal, gc.Equals, 0) 1087 } 1088 1089 func (s *MigrationImportSuite) TestApplicationsSubordinatesAfter(c *gc.C) { 1090 // Test for https://bugs.launchpad.net/juju/+bug/1650249 1091 subordinate := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1092 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{Name: "logging"}), 1093 }) 1094 1095 principal := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 1096 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{Name: "mysql"}), 1097 }) 1098 unit := s.Factory.MakeUnit(c, &factory.UnitParams{Application: principal}) 1099 1100 sEndpoint, err := subordinate.Endpoint("info") 1101 c.Assert(err, jc.ErrorIsNil) 1102 pEndpoint, err := principal.Endpoint("juju-info") 1103 c.Assert(err, jc.ErrorIsNil) 1104 relation := s.Factory.MakeRelation(c, &factory.RelationParams{ 1105 Endpoints: []state.Endpoint{sEndpoint, pEndpoint}, 1106 }) 1107 1108 ru, err := relation.Unit(unit) 1109 c.Assert(err, jc.ErrorIsNil) 1110 1111 // Ensure the subordinate unit is created. 1112 err = ru.EnterScope(nil) 1113 c.Assert(err, jc.ErrorIsNil) 1114 1115 tools, err := unit.AgentTools() 1116 c.Assert(err, jc.ErrorIsNil) 1117 1118 sUnits, err := subordinate.AllUnits() 1119 c.Assert(err, jc.ErrorIsNil) 1120 for _, u := range sUnits { 1121 // For some reason the EnterScope call doesn't set up the 1122 // version or enter the scope for the subordinate unit on the 1123 // other side of the relation. 1124 err := u.SetAgentVersion(tools.Version) 1125 c.Assert(err, jc.ErrorIsNil) 1126 ru, err := relation.Unit(u) 1127 c.Assert(err, jc.ErrorIsNil) 1128 err = ru.EnterScope(nil) 1129 c.Assert(err, jc.ErrorIsNil) 1130 } 1131 1132 out, err := s.State.Export(map[string]string{}) 1133 c.Assert(err, jc.ErrorIsNil) 1134 1135 apps := out.Applications() 1136 c.Assert(len(apps), gc.Equals, 2) 1137 1138 // This test is only valid if the subordinate logging application 1139 // comes first in the model output. 1140 if apps[0].Name() != "logging" { 1141 out = &swapModel{out, c} 1142 } 1143 1144 uuid := utils.MustNewUUID().String() 1145 in := newModel(out, uuid, "new") 1146 1147 _, newSt, err := s.Controller.Import(in) 1148 c.Assert(err, jc.ErrorIsNil) 1149 // add the cleanup here to close the model. 1150 s.AddCleanup(func(c *gc.C) { 1151 c.Check(newSt.Close(), jc.ErrorIsNil) 1152 }) 1153 } 1154 1155 func (s *MigrationImportSuite) TestUnits(c *gc.C) { 1156 s.assertUnitsMigrated(c, s.State, constraints.MustParse("arch=amd64 mem=8G")) 1157 } 1158 1159 func (s *MigrationImportSuite) TestCAASUnits(c *gc.C) { 1160 caasSt := s.Factory.MakeCAASModel(c, nil) 1161 s.AddCleanup(func(_ *gc.C) { caasSt.Close() }) 1162 1163 s.assertUnitsMigrated(c, caasSt, constraints.MustParse("arch=amd64 mem=8G")) 1164 } 1165 1166 func (s *MigrationImportSuite) TestUnitsWithVirtConstraint(c *gc.C) { 1167 s.assertUnitsMigrated(c, s.State, constraints.MustParse("arch=amd64 mem=8G virt-type=kvm")) 1168 } 1169 1170 func (s *MigrationImportSuite) TestUnitWithoutAnyPersistedState(c *gc.C) { 1171 f := factory.NewFactory(s.State, s.StatePool) 1172 1173 // Export unit without any controller-persisted state 1174 exported := f.MakeUnit(c, &factory.UnitParams{ 1175 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 1176 }) 1177 1178 exportedState, err := exported.State() 1179 c.Assert(err, jc.ErrorIsNil) 1180 _, isSet := exportedState.CharmState() 1181 c.Assert(isSet, jc.IsFalse, gc.Commentf("expected charm state to be empty")) 1182 _, isSet = exportedState.RelationState() 1183 c.Assert(isSet, jc.IsFalse, gc.Commentf("expected uniter relation state to be empty")) 1184 _, isSet = exportedState.UniterState() 1185 c.Assert(isSet, jc.IsFalse, gc.Commentf("expected uniter state to be empty")) 1186 _, isSet = exportedState.StorageState() 1187 c.Assert(isSet, jc.IsFalse, gc.Commentf("expected uniter storage state to be empty")) 1188 _, isSet = exportedState.MeterStatusState() 1189 c.Assert(isSet, jc.IsFalse, gc.Commentf("expected meter status state to be empty")) 1190 1191 // Import model and ensure that its UnitState was not mutated. 1192 _, newSt := s.importModel(c, s.State) 1193 1194 importedApplications, err := newSt.AllApplications() 1195 c.Assert(err, jc.ErrorIsNil) 1196 c.Assert(importedApplications, gc.HasLen, 1) 1197 1198 importedUnits, err := importedApplications[0].AllUnits() 1199 c.Assert(err, jc.ErrorIsNil) 1200 c.Assert(importedUnits, gc.HasLen, 1) 1201 imported := importedUnits[0] 1202 1203 c.Assert(imported.UnitTag(), gc.Equals, exported.UnitTag()) 1204 1205 unitState, err := imported.State() 1206 c.Assert(err, jc.ErrorIsNil) 1207 _, isSet = unitState.CharmState() 1208 c.Assert(isSet, jc.IsFalse, gc.Commentf("unexpected charm state after import; SetState should not have been called")) 1209 _, isSet = unitState.RelationState() 1210 c.Assert(isSet, jc.IsFalse, gc.Commentf("unexpected uniter relation state after import; SetState should not have been called")) 1211 _, isSet = unitState.UniterState() 1212 c.Assert(isSet, jc.IsFalse, gc.Commentf("unexpected uniter state after import; SetState should not have been called")) 1213 _, isSet = unitState.StorageState() 1214 c.Assert(isSet, jc.IsFalse, gc.Commentf("unexpected uniter storage state after import; SetState should not have been called")) 1215 _, isSet = unitState.MeterStatusState() 1216 c.Assert(isSet, jc.IsFalse, gc.Commentf("unexpected meter status state after import; SetState should not have been called")) 1217 } 1218 1219 func (s *MigrationImportSuite) assertUnitsMigrated(c *gc.C, st *state.State, cons constraints.Value) { 1220 f := factory.NewFactory(st, s.StatePool) 1221 1222 exported, pwd := f.MakeUnitReturningPassword(c, &factory.UnitParams{ 1223 Constraints: cons, 1224 }) 1225 err := exported.SetMeterStatus("GREEN", "some info") 1226 c.Assert(err, jc.ErrorIsNil) 1227 err = exported.SetWorkloadVersion("amethyst") 1228 c.Assert(err, jc.ErrorIsNil) 1229 testModel, err := st.Model() 1230 c.Assert(err, jc.ErrorIsNil) 1231 err = testModel.SetAnnotations(exported, testAnnotations) 1232 c.Assert(err, jc.ErrorIsNil) 1233 us := state.NewUnitState() 1234 us.SetCharmState(map[string]string{"payload": "0xb4c0ffee"}) 1235 us.SetRelationState(map[int]string{42: "magic"}) 1236 us.SetUniterState("uniter state") 1237 us.SetStorageState("storage state") 1238 us.SetMeterStatusState("meter status state") 1239 err = exported.SetState(us, state.UnitStateSizeLimits{}) 1240 c.Assert(err, jc.ErrorIsNil) 1241 1242 if testModel.Type() == state.ModelTypeCAAS { 1243 var updateUnits state.UpdateUnitsOperation 1244 // need to set a cloud container status so that SetStatus for 1245 // the unit doesn't throw away the history writes. 1246 updateUnits.Updates = []*state.UpdateUnitOperation{ 1247 exported.UpdateOperation(state.UnitUpdateProperties{ 1248 ProviderId: strPtr("provider-id"), 1249 Address: strPtr("192.168.1.2"), 1250 Ports: &[]string{"80"}, 1251 CloudContainerStatus: &status.StatusInfo{ 1252 Status: status.Active, 1253 Message: "cloud container active", 1254 }, 1255 })} 1256 app, err := exported.Application() 1257 c.Assert(err, jc.ErrorIsNil) 1258 err = app.UpdateUnits(&updateUnits) 1259 c.Assert(err, jc.ErrorIsNil) 1260 } 1261 s.primeStatusHistory(c, exported, status.Active, 5) 1262 s.primeStatusHistory(c, exported.Agent(), status.Idle, 5) 1263 1264 newModel, newSt := s.importModel(c, st) 1265 1266 importedApplications, err := newSt.AllApplications() 1267 c.Assert(err, jc.ErrorIsNil) 1268 c.Assert(importedApplications, gc.HasLen, 1) 1269 1270 importedUnits, err := importedApplications[0].AllUnits() 1271 c.Assert(err, jc.ErrorIsNil) 1272 c.Assert(importedUnits, gc.HasLen, 1) 1273 imported := importedUnits[0] 1274 1275 c.Assert(imported.UnitTag(), gc.Equals, exported.UnitTag()) 1276 c.Assert(imported.PasswordValid(pwd), jc.IsTrue) 1277 v, err := imported.WorkloadVersion() 1278 c.Assert(err, jc.ErrorIsNil) 1279 c.Assert(v, gc.Equals, "amethyst") 1280 1281 if newModel.Type() == state.ModelTypeIAAS { 1282 exportedMachineId, err := exported.AssignedMachineId() 1283 c.Assert(err, jc.ErrorIsNil) 1284 importedMachineId, err := imported.AssignedMachineId() 1285 c.Assert(err, jc.ErrorIsNil) 1286 c.Assert(importedMachineId, gc.Equals, exportedMachineId) 1287 1288 // Confirm machine Principals are set. 1289 exportedMachine, err := st.Machine(exportedMachineId) 1290 c.Assert(err, jc.ErrorIsNil) 1291 importedMachine, err := newSt.Machine(importedMachineId) 1292 c.Assert(err, jc.ErrorIsNil) 1293 s.AssertMachineEqual(c, importedMachine, exportedMachine) 1294 } 1295 if newModel.Type() == state.ModelTypeCAAS { 1296 containerInfo, err := imported.ContainerInfo() 1297 c.Assert(err, jc.ErrorIsNil) 1298 c.Assert(containerInfo.ProviderId(), gc.Equals, "provider-id") 1299 c.Assert(containerInfo.Ports(), jc.DeepEquals, []string{"80"}) 1300 addr := network.NewSpaceAddress("192.168.1.2", network.WithScope(network.ScopeMachineLocal)) 1301 addr.SpaceID = "0" 1302 c.Assert(containerInfo.Address(), jc.DeepEquals, &addr) 1303 } 1304 1305 meterStatus, err := imported.GetMeterStatus() 1306 c.Assert(err, jc.ErrorIsNil) 1307 c.Assert(meterStatus, gc.Equals, state.MeterStatus{state.MeterGreen, "some info"}) 1308 s.assertAnnotations(c, newModel, imported) 1309 s.checkStatusHistory(c, exported, imported, 5) 1310 s.checkStatusHistory(c, exported.Agent(), imported.Agent(), 5) 1311 s.checkStatusHistory(c, exported.WorkloadVersionHistory(), imported.WorkloadVersionHistory(), 1) 1312 1313 unitState, err := imported.State() 1314 c.Assert(err, jc.ErrorIsNil) 1315 charmState, _ := unitState.CharmState() 1316 c.Assert(charmState, jc.DeepEquals, map[string]string{"payload": "0xb4c0ffee"}, gc.Commentf("persisted charm state not migrated")) 1317 relationState, _ := unitState.RelationState() 1318 c.Assert(relationState, jc.DeepEquals, map[int]string{42: "magic"}, gc.Commentf("persisted relation state not migrated")) 1319 uniterState, _ := unitState.UniterState() 1320 c.Assert(uniterState, gc.Equals, "uniter state", gc.Commentf("persisted uniter state not migrated")) 1321 storageState, _ := unitState.StorageState() 1322 c.Assert(storageState, gc.Equals, "storage state", gc.Commentf("persisted uniter storage state not migrated")) 1323 meterStatusState, _ := unitState.MeterStatusState() 1324 c.Assert(meterStatusState, gc.Equals, "meter status state", gc.Commentf("persisted meter status state not migrated")) 1325 1326 newCons, err := imported.Constraints() 1327 c.Assert(err, jc.ErrorIsNil) 1328 // Can't test the constraints directly, so go through the string repr. 1329 c.Assert(newCons.String(), gc.Equals, cons.String()) 1330 } 1331 1332 func (s *MigrationImportSuite) TestRemoteEntities(c *gc.C) { 1333 srcRemoteEntities := s.State.RemoteEntities() 1334 err := srcRemoteEntities.ImportRemoteEntity(names.NewApplicationTag("uuid3"), "xxx-aaa-bbb") 1335 c.Assert(err, jc.ErrorIsNil) 1336 err = srcRemoteEntities.ImportRemoteEntity(names.NewApplicationTag("uuid4"), "ccc-ddd-zzz") 1337 c.Assert(err, jc.ErrorIsNil) 1338 1339 _, newSt := s.importModel(c, s.State) 1340 1341 newRemoteEntities := newSt.RemoteEntities() 1342 token, err := newRemoteEntities.GetToken(names.NewApplicationTag("uuid3")) 1343 c.Assert(err, jc.ErrorIsNil) 1344 c.Assert(token, gc.Equals, "xxx-aaa-bbb") 1345 1346 token, err = newRemoteEntities.GetToken(names.NewApplicationTag("uuid4")) 1347 c.Assert(err, jc.ErrorIsNil) 1348 c.Assert(token, gc.Equals, "ccc-ddd-zzz") 1349 } 1350 1351 func (s *MigrationImportSuite) TestRelationNetworks(c *gc.C) { 1352 wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 1353 wordpressEP, err := wordpress.Endpoint("db") 1354 c.Assert(err, jc.ErrorIsNil) 1355 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1356 mysqlEP, err := mysql.Endpoint("server") 1357 c.Assert(err, jc.ErrorIsNil) 1358 _, err = s.State.AddRelation(wordpressEP, mysqlEP) 1359 c.Assert(err, jc.ErrorIsNil) 1360 1361 srcRelationNetworks := state.NewRelationIngressNetworks(s.State) 1362 _, err = srcRelationNetworks.Save("wordpress:db mysql:server", false, []string{"192.168.1.0/16"}) 1363 c.Assert(err, jc.ErrorIsNil) 1364 1365 _, newSt := s.importModel(c, s.State) 1366 1367 newRelationNetworks := state.NewRelationNetworks(newSt) 1368 networks, err := newRelationNetworks.AllRelationNetworks() 1369 c.Assert(err, jc.ErrorIsNil) 1370 c.Assert(networks, gc.HasLen, 1) 1371 1372 entity0 := networks[0] 1373 c.Assert(entity0.RelationKey(), gc.Equals, "wordpress:db mysql:server") 1374 c.Assert(entity0.CIDRS(), gc.DeepEquals, []string{"192.168.1.0/16"}) 1375 } 1376 1377 func (s *MigrationImportSuite) TestRelations(c *gc.C) { 1378 wordpress := state.AddTestingApplication(c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress")) 1379 state.AddTestingApplication(c, s.State, "mysql", state.AddTestingCharm(c, s.State, "mysql")) 1380 eps, err := s.State.InferEndpoints("mysql", "wordpress") 1381 c.Assert(err, jc.ErrorIsNil) 1382 1383 rel, err := s.State.AddRelation(eps...) 1384 c.Assert(err, jc.ErrorIsNil) 1385 err = rel.SetStatus(status.StatusInfo{Status: status.Joined}) 1386 c.Assert(err, jc.ErrorIsNil) 1387 1388 wordpress0 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: wordpress}) 1389 ru, err := rel.Unit(wordpress0) 1390 c.Assert(err, jc.ErrorIsNil) 1391 1392 relSettings := map[string]interface{}{ 1393 "name": "wordpress/0", 1394 } 1395 err = ru.EnterScope(relSettings) 1396 c.Assert(err, jc.ErrorIsNil) 1397 1398 _, newSt := s.importModel(c, s.State) 1399 1400 newWordpress, err := newSt.Application("wordpress") 1401 c.Assert(err, jc.ErrorIsNil) 1402 c.Assert(state.RelationCount(newWordpress), gc.Equals, 1) 1403 rels, err := newWordpress.Relations() 1404 c.Assert(err, jc.ErrorIsNil) 1405 c.Assert(rels, gc.HasLen, 1) 1406 units, err := newWordpress.AllUnits() 1407 c.Assert(err, jc.ErrorIsNil) 1408 c.Assert(units, gc.HasLen, 1) 1409 1410 relStatus, err := rels[0].Status() 1411 c.Assert(err, jc.ErrorIsNil) 1412 c.Assert(relStatus.Status, gc.Equals, status.Joined) 1413 1414 ru, err = rels[0].Unit(units[0]) 1415 c.Assert(err, jc.ErrorIsNil) 1416 1417 settings, err := ru.Settings() 1418 c.Assert(err, jc.ErrorIsNil) 1419 c.Assert(settings.Map(), gc.DeepEquals, relSettings) 1420 } 1421 1422 func (s *MigrationImportSuite) TestCMRRemoteRelationScope(c *gc.C) { 1423 _, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 1424 Name: "gravy-rainbow", 1425 URL: "me/model.rainbow", 1426 SourceModel: s.Model.ModelTag(), 1427 Token: "charisma", 1428 OfferUUID: "offer-uuid", 1429 Endpoints: []charm.Relation{{ 1430 Interface: "mysql", 1431 Name: "db", 1432 Role: charm.RoleProvider, 1433 Scope: charm.ScopeGlobal, 1434 }}, 1435 }) 1436 c.Assert(err, jc.ErrorIsNil) 1437 1438 wordpress := state.AddTestingApplication(c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress")) 1439 eps, err := s.State.InferEndpoints("gravy-rainbow", "wordpress") 1440 c.Assert(err, jc.ErrorIsNil) 1441 rel, err := s.State.AddRelation(eps...) 1442 c.Assert(err, jc.ErrorIsNil) 1443 1444 wordpress0 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: wordpress}) 1445 localRU, err := rel.Unit(wordpress0) 1446 c.Assert(err, jc.ErrorIsNil) 1447 1448 wordpressSettings := map[string]interface{}{"name": "wordpress/0"} 1449 err = localRU.EnterScope(wordpressSettings) 1450 c.Assert(err, jc.ErrorIsNil) 1451 1452 remoteRU, err := rel.RemoteUnit("gravy-rainbow/0") 1453 c.Assert(err, jc.ErrorIsNil) 1454 1455 gravySettings := map[string]interface{}{"name": "gravy-rainbow/0"} 1456 err = remoteRU.EnterScope(gravySettings) 1457 c.Assert(err, jc.ErrorIsNil) 1458 1459 _, newSt := s.importModel(c, s.State) 1460 1461 newWordpress, err := newSt.Application("wordpress") 1462 c.Assert(err, jc.ErrorIsNil) 1463 1464 rels, err := newWordpress.Relations() 1465 c.Assert(err, jc.ErrorIsNil) 1466 c.Assert(rels, gc.HasLen, 1) 1467 1468 ru, err := rels[0].RemoteUnit("gravy-rainbow/0") 1469 c.Assert(err, jc.ErrorIsNil) 1470 1471 inScope, err := ru.InScope() 1472 c.Assert(err, jc.ErrorIsNil) 1473 c.Assert(inScope, jc.IsTrue) 1474 } 1475 1476 func (s *MigrationImportSuite) assertRelationsMissingStatus(c *gc.C, hasUnits bool) { 1477 wordpress := state.AddTestingApplication(c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress")) 1478 state.AddTestingApplication(c, s.State, "mysql", state.AddTestingCharm(c, s.State, "mysql")) 1479 eps, err := s.State.InferEndpoints("mysql", "wordpress") 1480 c.Assert(err, jc.ErrorIsNil) 1481 rel, err := s.State.AddRelation(eps...) 1482 c.Assert(err, jc.ErrorIsNil) 1483 1484 if hasUnits { 1485 wordpress_0 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: wordpress}) 1486 ru, err := rel.Unit(wordpress_0) 1487 c.Assert(err, jc.ErrorIsNil) 1488 relSettings := map[string]interface{}{ 1489 "name": "wordpress/0", 1490 } 1491 err = ru.EnterScope(relSettings) 1492 c.Assert(err, jc.ErrorIsNil) 1493 } 1494 1495 _, newSt := s.importModel(c, s.State, func(desc map[string]interface{}) { 1496 relations := desc["relations"].(map[interface{}]interface{}) 1497 for _, item := range relations["relations"].([]interface{}) { 1498 relation := item.(map[interface{}]interface{}) 1499 delete(relation, "status") 1500 } 1501 }) 1502 1503 newWordpress, err := newSt.Application("wordpress") 1504 c.Assert(err, jc.ErrorIsNil) 1505 c.Assert(state.RelationCount(newWordpress), gc.Equals, 1) 1506 rels, err := newWordpress.Relations() 1507 c.Assert(err, jc.ErrorIsNil) 1508 c.Assert(rels, gc.HasLen, 1) 1509 1510 relStatus, err := rels[0].Status() 1511 c.Assert(err, jc.ErrorIsNil) 1512 if hasUnits { 1513 c.Assert(relStatus.Status, gc.Equals, status.Joined) 1514 } else { 1515 c.Assert(relStatus.Status, gc.Equals, status.Joining) 1516 } 1517 } 1518 1519 func (s *MigrationImportSuite) TestRelationsMissingStatusWithUnits(c *gc.C) { 1520 s.assertRelationsMissingStatus(c, true) 1521 } 1522 1523 func (s *MigrationImportSuite) TestRelationsMissingStatusNoUnits(c *gc.C) { 1524 s.assertRelationsMissingStatus(c, false) 1525 } 1526 1527 func (s *MigrationImportSuite) TestEndpointBindings(c *gc.C) { 1528 // Endpoint bindings need both valid charms, applications, and spaces. 1529 space := s.Factory.MakeSpace(c, &factory.SpaceParams{ 1530 Name: "one", ProviderID: "provider", IsPublic: true}) 1531 state.AddTestingApplicationWithBindings( 1532 c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress"), 1533 map[string]string{"db": space.Id()}) 1534 1535 _, newSt := s.importModel(c, s.State) 1536 1537 newWordpress, err := newSt.Application("wordpress") 1538 c.Assert(err, jc.ErrorIsNil) 1539 1540 bindings, err := newWordpress.EndpointBindings() 1541 c.Assert(err, jc.ErrorIsNil) 1542 // Check the "db" endpoint has the correct space ID, the others 1543 // should have the AlphaSpaceId 1544 c.Assert(bindings.Map()["db"], gc.Equals, space.Id()) 1545 c.Assert(bindings.Map()[""], gc.Equals, network.AlphaSpaceId) 1546 } 1547 1548 func (s *MigrationImportSuite) TestIncompleteEndpointBindings(c *gc.C) { 1549 // Ensure we handle the case coming from an early 2.7 controller 1550 // where the default binding is missing. 1551 space := s.Factory.MakeSpace(c, &factory.SpaceParams{ 1552 Name: "one", ProviderID: "provider", IsPublic: true}) 1553 state.AddTestingApplicationWithBindings( 1554 c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress"), 1555 map[string]string{"db": space.Id()}) 1556 1557 _, newSt := s.importModel(c, s.State, func(desc map[string]interface{}) { 1558 apps := desc["applications"].(map[interface{}]interface{}) 1559 for _, item := range apps["applications"].([]interface{}) { 1560 bindings, ok := item.(map[interface{}]interface{})["endpoint-bindings"].(map[interface{}]interface{}) 1561 if !ok { 1562 continue 1563 } 1564 delete(bindings, "") 1565 } 1566 }) 1567 1568 newWordpress, err := newSt.Application("wordpress") 1569 c.Assert(err, jc.ErrorIsNil) 1570 1571 bindings, err := newWordpress.EndpointBindings() 1572 c.Assert(err, jc.ErrorIsNil) 1573 c.Assert(bindings.Map()["db"], gc.Equals, space.Id()) 1574 c.Assert(bindings.Map()[""], gc.Equals, network.AlphaSpaceId) 1575 } 1576 1577 func (s *MigrationImportSuite) TestNilEndpointBindings(c *gc.C) { 1578 app := state.AddTestingApplicationWithEmptyBindings( 1579 c, s.State, "dummy", state.AddTestingCharm(c, s.State, "dummy")) 1580 1581 bindings, err := app.EndpointBindings() 1582 c.Assert(err, jc.ErrorIsNil) 1583 c.Assert(bindings.Map(), gc.HasLen, 0) 1584 1585 _, newSt := s.importModel(c, s.State) 1586 1587 newApp, err := newSt.Application("dummy") 1588 c.Assert(err, jc.ErrorIsNil) 1589 1590 newBindings, err := newApp.EndpointBindings() 1591 c.Assert(err, jc.ErrorIsNil) 1592 c.Assert(newBindings.Map()[""], gc.Equals, network.AlphaSpaceId) 1593 } 1594 1595 func (s *MigrationImportSuite) TestUnitsOpenPorts(c *gc.C) { 1596 unit := s.Factory.MakeUnit(c, nil) 1597 1598 unitPortRanges, err := unit.OpenedPortRanges() 1599 c.Assert(err, jc.ErrorIsNil) 1600 unitPortRanges.Open(allEndpoints, network.MustParsePortRange("1234-2345/tcp")) 1601 c.Assert(s.State.ApplyOperation(unitPortRanges.Changes()), jc.ErrorIsNil) 1602 1603 _, newSt := s.importModel(c, s.State) 1604 1605 // Even though the opened ports document is stored with the 1606 // machine, the only way to easily access it is through the units. 1607 imported, err := newSt.Unit(unit.Name()) 1608 c.Assert(err, jc.ErrorIsNil) 1609 1610 unitPortRanges, err = imported.OpenedPortRanges() 1611 c.Assert(err, jc.ErrorIsNil) 1612 c.Assert(unitPortRanges.UniquePortRanges(), gc.HasLen, 1) 1613 1614 portRanges := unitPortRanges.ForEndpoint(allEndpoints) 1615 c.Assert(portRanges, gc.HasLen, 1) 1616 c.Assert(portRanges[0], gc.Equals, network.PortRange{ 1617 FromPort: 1234, 1618 ToPort: 2345, 1619 Protocol: "tcp", 1620 }) 1621 } 1622 1623 func (s *MigrationImportSuite) TestSpaces(c *gc.C) { 1624 space := s.Factory.MakeSpace(c, &factory.SpaceParams{ 1625 Name: "one", ProviderID: network.Id("provider"), IsPublic: true}) 1626 1627 spaceNoID := s.Factory.MakeSpace(c, &factory.SpaceParams{ 1628 Name: "no-id", ProviderID: network.Id("provider2"), IsPublic: true}) 1629 1630 // Blank the ID from the second space to check that import creates it. 1631 _, newSt := s.importModel(c, s.State, func(desc map[string]interface{}) { 1632 spaces := desc["spaces"].(map[interface{}]interface{}) 1633 for _, item := range spaces["spaces"].([]interface{}) { 1634 sp := item.(map[interface{}]interface{}) 1635 if sp["name"] == spaceNoID.Name() { 1636 sp["id"] = "" 1637 } 1638 } 1639 }) 1640 1641 imported, err := newSt.SpaceByName(space.Name()) 1642 c.Assert(err, jc.ErrorIsNil) 1643 1644 c.Check(imported.Id(), gc.Equals, space.Id()) 1645 c.Check(imported.Name(), gc.Equals, space.Name()) 1646 c.Check(imported.ProviderId(), gc.Equals, space.ProviderId()) 1647 c.Check(imported.IsPublic(), gc.Equals, space.IsPublic()) 1648 1649 imported, err = newSt.SpaceByName(spaceNoID.Name()) 1650 c.Assert(err, jc.ErrorIsNil) 1651 c.Check(imported.Id(), gc.Not(gc.Equals), "") 1652 } 1653 1654 func (s *MigrationImportSuite) TestFirewallRules(c *gc.C) { 1655 ctrl := gomock.NewController(c) 1656 defer ctrl.Finish() 1657 1658 sshCIDRs := []string{"192.168.1.0/24", "192.0.2.1/24"} 1659 sshRule := state.NewMockFirewallRule(ctrl) 1660 sshRule.EXPECT().WellKnownService().Return("ssh") 1661 sshRule.EXPECT().WhitelistCIDRs().Return(sshCIDRs) 1662 1663 saasCIDRs := []string{"10.0.0.0/16"} 1664 saasRule := state.NewMockFirewallRule(ctrl) 1665 saasRule.EXPECT().WellKnownService().Return("juju-application-offer") 1666 saasRule.EXPECT().WhitelistCIDRs().Return(saasCIDRs) 1667 1668 base, err := s.State.Export(map[string]string{}) 1669 c.Assert(err, jc.ErrorIsNil) 1670 uuid := utils.MustNewUUID().String() 1671 model := newModel(base, uuid, "new") 1672 model.fwRules = []description.FirewallRule{sshRule, saasRule} 1673 1674 _, newSt := s.importModelDescription(c, model) 1675 1676 m, err := newSt.Model() 1677 c.Assert(err, jc.ErrorIsNil) 1678 cfg, err := m.ModelConfig() 1679 c.Assert(err, jc.ErrorIsNil) 1680 1681 c.Assert(cfg.SSHAllow(), gc.DeepEquals, sshCIDRs) 1682 c.Assert(cfg.SAASIngressAllow(), gc.DeepEquals, saasCIDRs) 1683 } 1684 1685 func (s *MigrationImportSuite) TestDestroyEmptyModel(c *gc.C) { 1686 newModel, _ := s.importModel(c, s.State) 1687 s.assertDestroyModelAdvancesLife(c, newModel, state.Dying) 1688 } 1689 1690 func (s *MigrationImportSuite) TestDestroyModelWithMachine(c *gc.C) { 1691 s.Factory.MakeMachine(c, nil) 1692 newModel, _ := s.importModel(c, s.State) 1693 s.assertDestroyModelAdvancesLife(c, newModel, state.Dying) 1694 } 1695 1696 func (s *MigrationImportSuite) TestDestroyModelWithApplication(c *gc.C) { 1697 s.Factory.MakeApplication(c, nil) 1698 newModel, _ := s.importModel(c, s.State) 1699 s.assertDestroyModelAdvancesLife(c, newModel, state.Dying) 1700 } 1701 1702 func (s *MigrationImportSuite) assertDestroyModelAdvancesLife(c *gc.C, m *state.Model, life state.Life) { 1703 c.Assert(m.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 1704 c.Assert(m.Refresh(), jc.ErrorIsNil) 1705 c.Assert(m.Life(), gc.Equals, life) 1706 } 1707 1708 func (s *MigrationImportSuite) TestLinkLayerDevice(c *gc.C) { 1709 machine := s.Factory.MakeMachine(c, &factory.MachineParams{ 1710 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 1711 }) 1712 deviceArgs := state.LinkLayerDeviceArgs{ 1713 Name: "foo", 1714 Type: network.EthernetDevice, 1715 VirtualPortType: network.OvsPort, 1716 } 1717 err := machine.SetLinkLayerDevices(deviceArgs) 1718 c.Assert(err, jc.ErrorIsNil) 1719 _, newSt := s.importModel(c, s.State) 1720 1721 devices, err := newSt.AllLinkLayerDevices() 1722 c.Assert(err, jc.ErrorIsNil) 1723 c.Assert(devices, gc.HasLen, 1) 1724 device := devices[0] 1725 c.Assert(device.Name(), gc.Equals, "foo") 1726 c.Assert(device.Type(), gc.Equals, network.EthernetDevice) 1727 c.Assert(device.VirtualPortType(), gc.Equals, network.OvsPort, gc.Commentf("VirtualPortType not migrated correctly")) 1728 } 1729 1730 func (s *MigrationImportSuite) TestLinkLayerDeviceMigratesReferences(c *gc.C) { 1731 machine := s.Factory.MakeMachine(c, &factory.MachineParams{ 1732 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 1733 }) 1734 machine2 := s.Factory.MakeMachineNested(c, machine.Id(), &factory.MachineParams{ 1735 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 1736 }) 1737 deviceArgs := []state.LinkLayerDeviceArgs{{ 1738 Name: "foo", 1739 Type: network.BridgeDevice, 1740 }, { 1741 Name: "bar", 1742 ParentName: "foo", 1743 Type: network.EthernetDevice, 1744 }} 1745 for _, args := range deviceArgs { 1746 err := machine.SetLinkLayerDevices(args) 1747 c.Assert(err, jc.ErrorIsNil) 1748 } 1749 machine2DeviceArgs := state.LinkLayerDeviceArgs{ 1750 Name: "baz", 1751 ParentName: fmt.Sprintf("m#%v#d#foo", machine.Id()), 1752 Type: network.EthernetDevice, 1753 } 1754 err := machine2.SetLinkLayerDevices(machine2DeviceArgs) 1755 c.Assert(err, jc.ErrorIsNil) 1756 _, newSt := s.importModel(c, s.State) 1757 1758 devices, err := newSt.AllLinkLayerDevices() 1759 c.Assert(err, jc.ErrorIsNil) 1760 c.Assert(devices, gc.HasLen, 3) 1761 var parent *state.LinkLayerDevice 1762 others := []*state.LinkLayerDevice{} 1763 for _, device := range devices { 1764 if device.Name() == "foo" { 1765 parent = device 1766 } else { 1767 others = append(others, device) 1768 } 1769 } 1770 // Assert we found the parent. 1771 c.Assert(others, gc.HasLen, 2) 1772 err = parent.Remove() 1773 c.Assert(err, gc.ErrorMatches, `.*parent device "foo" has 2 children.*`) 1774 err = others[0].Remove() 1775 c.Assert(err, jc.ErrorIsNil) 1776 err = parent.Remove() 1777 c.Assert(err, gc.ErrorMatches, `.*parent device "foo" has 1 children.*`) 1778 err = others[1].Remove() 1779 c.Assert(err, jc.ErrorIsNil) 1780 err = parent.Remove() 1781 c.Assert(err, jc.ErrorIsNil) 1782 } 1783 1784 func (s *MigrationImportSuite) TestSubnets(c *gc.C) { 1785 sp, err := s.State.AddSpace("bam", "", nil, true) 1786 c.Assert(err, jc.ErrorIsNil) 1787 original, err := s.State.AddSubnet(network.SubnetInfo{ 1788 CIDR: "10.0.0.0/24", 1789 ProviderId: "foo", 1790 ProviderNetworkId: "elm", 1791 VLANTag: 64, 1792 SpaceID: sp.Id(), 1793 AvailabilityZones: []string{"bar"}, 1794 IsPublic: true, 1795 }) 1796 c.Assert(err, jc.ErrorIsNil) 1797 originalNoID, err := s.State.AddSubnet(network.SubnetInfo{ 1798 CIDR: "10.76.0.0/24", 1799 ProviderId: "bar", 1800 ProviderNetworkId: "oak", 1801 VLANTag: 64, 1802 SpaceID: sp.Id(), 1803 AvailabilityZones: []string{"bar"}, 1804 }) 1805 c.Assert(err, jc.ErrorIsNil) 1806 1807 _, newSt := s.importModel(c, s.State, func(desc map[string]interface{}) { 1808 subnets := desc["subnets"].(map[interface{}]interface{}) 1809 for _, item := range subnets["subnets"].([]interface{}) { 1810 sn := item.(map[interface{}]interface{}) 1811 1812 if sn["subnet-id"] == originalNoID.ID() { 1813 // Remove the subnet ID, to check that it is created by import. 1814 sn["subnet-id"] = "" 1815 1816 // Swap the space ID for a space name to check migrating from 1817 // a pre-2.7 model. 1818 sn["space-id"] = "" 1819 sn["space-name"] = sp.Name() 1820 } 1821 } 1822 }) 1823 1824 subnet, err := newSt.Subnet(original.ID()) 1825 c.Assert(err, jc.ErrorIsNil) 1826 1827 c.Assert(subnet.CIDR(), gc.Equals, "10.0.0.0/24") 1828 c.Assert(subnet.ProviderId(), gc.Equals, network.Id("foo")) 1829 c.Assert(subnet.ProviderNetworkId(), gc.Equals, network.Id("elm")) 1830 c.Assert(subnet.VLANTag(), gc.Equals, 64) 1831 c.Assert(subnet.AvailabilityZones(), gc.DeepEquals, []string{"bar"}) 1832 c.Assert(subnet.SpaceID(), gc.Equals, sp.Id()) 1833 c.Assert(subnet.FanLocalUnderlay(), gc.Equals, "") 1834 c.Assert(subnet.FanOverlay(), gc.Equals, "") 1835 c.Assert(subnet.IsPublic(), gc.Equals, true) 1836 1837 imported, err := newSt.SubnetByCIDR(originalNoID.CIDR()) 1838 c.Assert(err, jc.ErrorIsNil) 1839 c.Check(imported, gc.Not(gc.Equals), "") 1840 } 1841 1842 func (s *MigrationImportSuite) TestSubnetsWithFan(c *gc.C) { 1843 subnet, err := s.State.AddSubnet(network.SubnetInfo{ 1844 CIDR: "100.2.0.0/16", 1845 }) 1846 c.Assert(err, jc.ErrorIsNil) 1847 sp, err := s.State.AddSpace("bam", "", []string{subnet.ID()}, true) 1848 c.Assert(err, jc.ErrorIsNil) 1849 1850 sn := network.SubnetInfo{ 1851 CIDR: "10.0.0.0/24", 1852 ProviderId: network.Id("foo"), 1853 ProviderNetworkId: network.Id("elm"), 1854 VLANTag: 64, 1855 AvailabilityZones: []string{"bar"}, 1856 } 1857 sn.SetFan("100.2.0.0/16", "253.0.0.0/8") 1858 1859 original, err := s.State.AddSubnet(sn) 1860 c.Assert(err, jc.ErrorIsNil) 1861 1862 _, newSt := s.importModel(c, s.State) 1863 1864 subnet, err = newSt.SubnetByCIDR(original.CIDR()) 1865 c.Assert(err, jc.ErrorIsNil) 1866 1867 c.Assert(subnet.CIDR(), gc.Equals, "10.0.0.0/24") 1868 c.Assert(subnet.ProviderId(), gc.Equals, network.Id("foo")) 1869 c.Assert(subnet.ProviderNetworkId(), gc.Equals, network.Id("elm")) 1870 c.Assert(subnet.VLANTag(), gc.Equals, 64) 1871 c.Assert(subnet.AvailabilityZones(), gc.DeepEquals, []string{"bar"}) 1872 c.Assert(subnet.SpaceID(), gc.Equals, sp.Id()) 1873 c.Assert(subnet.FanLocalUnderlay(), gc.Equals, "100.2.0.0/16") 1874 c.Assert(subnet.FanOverlay(), gc.Equals, "253.0.0.0/8") 1875 } 1876 1877 func (s *MigrationImportSuite) TestIPAddress(c *gc.C) { 1878 machine := s.Factory.MakeMachine(c, &factory.MachineParams{ 1879 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 1880 }) 1881 space, err := s.State.AddSpace("testme", "", nil, true) 1882 c.Assert(err, jc.ErrorIsNil) 1883 _, err = s.State.AddSubnet(network.SubnetInfo{CIDR: "0.1.2.0/24", SpaceID: space.Id()}) 1884 c.Assert(err, jc.ErrorIsNil) 1885 deviceArgs := state.LinkLayerDeviceArgs{ 1886 Name: "foo", 1887 Type: network.EthernetDevice, 1888 } 1889 err = machine.SetLinkLayerDevices(deviceArgs) 1890 c.Assert(err, jc.ErrorIsNil) 1891 args := state.LinkLayerDeviceAddress{ 1892 DeviceName: "foo", 1893 ConfigMethod: network.ConfigStatic, 1894 CIDRAddress: "0.1.2.3/24", 1895 ProviderID: "bar", 1896 DNSServers: []string{"bam", "mam"}, 1897 DNSSearchDomains: []string{"weeee"}, 1898 GatewayAddress: "0.1.2.1", 1899 ProviderNetworkID: "p-net-id", 1900 ProviderSubnetID: "p-sub-id", 1901 Origin: network.OriginProvider, 1902 } 1903 err = machine.SetDevicesAddresses(args) 1904 c.Assert(err, jc.ErrorIsNil) 1905 1906 _, newSt := s.importModel(c, s.State) 1907 1908 addresses, _ := newSt.AllIPAddresses() 1909 c.Assert(addresses, gc.HasLen, 1) 1910 c.Assert(err, jc.ErrorIsNil) 1911 addr := addresses[0] 1912 c.Assert(addr.Value(), gc.Equals, "0.1.2.3") 1913 c.Assert(addr.MachineID(), gc.Equals, machine.Id()) 1914 c.Assert(addr.DeviceName(), gc.Equals, "foo") 1915 c.Assert(addr.ConfigMethod(), gc.Equals, network.ConfigStatic) 1916 c.Assert(addr.SubnetCIDR(), gc.Equals, "0.1.2.0/24") 1917 c.Assert(addr.ProviderID(), gc.Equals, network.Id("bar")) 1918 c.Assert(addr.DNSServers(), jc.DeepEquals, []string{"bam", "mam"}) 1919 c.Assert(addr.DNSSearchDomains(), jc.DeepEquals, []string{"weeee"}) 1920 c.Assert(addr.GatewayAddress(), gc.Equals, "0.1.2.1") 1921 c.Assert(addr.ProviderNetworkID().String(), gc.Equals, "p-net-id") 1922 c.Assert(addr.ProviderSubnetID().String(), gc.Equals, "p-sub-id") 1923 c.Assert(addr.Origin(), gc.Equals, network.OriginProvider) 1924 } 1925 1926 func (s *MigrationImportSuite) TestIPAddressCompatibility(c *gc.C) { 1927 machine := s.Factory.MakeMachine(c, &factory.MachineParams{ 1928 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 1929 }) 1930 1931 _, err := s.State.AddSubnet(network.SubnetInfo{CIDR: "0.1.2.0/24"}) 1932 c.Assert(err, jc.ErrorIsNil) 1933 deviceArgs := state.LinkLayerDeviceArgs{ 1934 Name: "foo", 1935 Type: network.EthernetDevice, 1936 } 1937 err = machine.SetLinkLayerDevices(deviceArgs) 1938 c.Assert(err, jc.ErrorIsNil) 1939 args := state.LinkLayerDeviceAddress{ 1940 DeviceName: "foo", 1941 ConfigMethod: "dynamic", 1942 CIDRAddress: "0.1.2.3/24", 1943 Origin: network.OriginProvider, 1944 } 1945 err = machine.SetDevicesAddresses(args) 1946 c.Assert(err, jc.ErrorIsNil) 1947 1948 _, newSt := s.importModel(c, s.State) 1949 1950 addresses, _ := newSt.AllIPAddresses() 1951 c.Assert(addresses, gc.HasLen, 1) 1952 c.Assert(addresses[0].ConfigMethod(), gc.Equals, network.ConfigDHCP) 1953 } 1954 1955 func (s *MigrationImportSuite) TestSSHHostKey(c *gc.C) { 1956 machine := s.Factory.MakeMachine(c, &factory.MachineParams{ 1957 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 1958 }) 1959 err := s.State.SetSSHHostKeys(machine.MachineTag(), []string{"bam", "mam"}) 1960 c.Assert(err, jc.ErrorIsNil) 1961 1962 _, newSt := s.importModel(c, s.State) 1963 1964 machine2, err := newSt.Machine(machine.Id()) 1965 c.Assert(err, jc.ErrorIsNil) 1966 keys, err := newSt.GetSSHHostKeys(machine2.MachineTag()) 1967 c.Assert(err, jc.ErrorIsNil) 1968 c.Assert(keys, jc.DeepEquals, state.SSHHostKeys{"bam", "mam"}) 1969 } 1970 1971 func (s *MigrationImportSuite) TestCloudImageMetadata(c *gc.C) { 1972 storageSize := uint64(3) 1973 attrs := cloudimagemetadata.MetadataAttributes{ 1974 Stream: "stream", 1975 Region: "region-test", 1976 Version: "22.04", 1977 Arch: "arch", 1978 VirtType: "virtType-test", 1979 RootStorageType: "rootStorageType-test", 1980 RootStorageSize: &storageSize, 1981 Source: "test", 1982 } 1983 attrsCustom := cloudimagemetadata.MetadataAttributes{ 1984 Stream: "stream", 1985 Region: "region-custom", 1986 Version: "22.04", 1987 Arch: "arch", 1988 VirtType: "virtType-test", 1989 RootStorageType: "rootStorageType-test", 1990 RootStorageSize: &storageSize, 1991 Source: "custom", 1992 } 1993 metadata := []cloudimagemetadata.Metadata{ 1994 {attrs, 2, "1", 2}, 1995 {attrsCustom, 3, "2", 3}, 1996 } 1997 1998 err := s.State.CloudImageMetadataStorage.SaveMetadata(metadata) 1999 c.Assert(err, jc.ErrorIsNil) 2000 2001 _, newSt := s.importModel(c, s.State, func(map[string]interface{}) { 2002 // Image metadata collection is global so we need to delete it 2003 // to properly test import. 2004 all, err := s.State.CloudImageMetadataStorage.AllCloudImageMetadata() 2005 c.Assert(err, jc.ErrorIsNil) 2006 for _, m := range all { 2007 err := s.State.CloudImageMetadataStorage.DeleteMetadata(m.ImageId) 2008 c.Assert(err, jc.ErrorIsNil) 2009 } 2010 }) 2011 defer func() { 2012 c.Assert(newSt.Close(), jc.ErrorIsNil) 2013 }() 2014 2015 images, err := newSt.CloudImageMetadataStorage.AllCloudImageMetadata() 2016 c.Assert(err, jc.ErrorIsNil) 2017 c.Assert(images, gc.HasLen, 1) 2018 image := images[0] 2019 c.Check(image.Stream, gc.Equals, "stream") 2020 c.Check(image.Region, gc.Equals, "region-custom") 2021 c.Check(image.Version, gc.Equals, "22.04") 2022 c.Check(image.Arch, gc.Equals, "arch") 2023 c.Check(image.VirtType, gc.Equals, "virtType-test") 2024 c.Check(image.RootStorageType, gc.Equals, "rootStorageType-test") 2025 c.Check(*image.RootStorageSize, gc.Equals, uint64(3)) 2026 c.Check(image.Source, gc.Equals, "custom") 2027 c.Check(image.Priority, gc.Equals, 3) 2028 c.Check(image.ImageId, gc.Equals, "2") 2029 c.Check(image.DateCreated, gc.Equals, int64(3)) 2030 } 2031 2032 func (s *MigrationImportSuite) TestAction(c *gc.C) { 2033 machine := s.Factory.MakeMachine(c, &factory.MachineParams{ 2034 Constraints: constraints.MustParse("arch=amd64 mem=8G"), 2035 }) 2036 2037 m, err := s.State.Model() 2038 c.Assert(err, jc.ErrorIsNil) 2039 2040 // pending action. 2041 operationIDPending, err := m.EnqueueOperation("a test", 2) 2042 c.Assert(err, jc.ErrorIsNil) 2043 actionPending, err := m.EnqueueAction(operationIDPending, machine.MachineTag(), "action-pending", nil, true, "group", nil) 2044 c.Assert(err, jc.ErrorIsNil) 2045 c.Assert(actionPending.Status(), gc.Equals, state.ActionPending) 2046 2047 // running action. 2048 operationIDRunning, err := m.EnqueueOperation("another test", 2) 2049 c.Assert(err, jc.ErrorIsNil) 2050 actionRunning, err := m.EnqueueAction(operationIDRunning, machine.MachineTag(), "action-running", nil, true, "group", nil) 2051 c.Assert(err, jc.ErrorIsNil) 2052 c.Assert(actionRunning.Status(), gc.Equals, state.ActionPending) 2053 actionRunning, err = actionRunning.Begin() 2054 c.Assert(err, jc.ErrorIsNil) 2055 c.Assert(actionRunning.Status(), gc.Equals, state.ActionRunning) 2056 2057 // aborting action. 2058 operationIDAborting, err := m.EnqueueOperation("another test", 2) 2059 c.Assert(err, jc.ErrorIsNil) 2060 actionAborting, err := m.EnqueueAction(operationIDAborting, machine.MachineTag(), "action-aborting", nil, true, "group", nil) 2061 c.Assert(err, jc.ErrorIsNil) 2062 c.Assert(actionAborting.Status(), gc.Equals, state.ActionPending) 2063 actionAborting, err = actionAborting.Begin() 2064 c.Assert(err, jc.ErrorIsNil) 2065 c.Assert(actionAborting.Status(), gc.Equals, state.ActionRunning) 2066 actionAborting, err = actionAborting.Finish(state.ActionResults{Status: state.ActionAborting}) 2067 c.Assert(err, jc.ErrorIsNil) 2068 c.Assert(actionAborting.Status(), gc.Equals, state.ActionAborting) 2069 2070 // aborted action. 2071 operationIDAborted, err := m.EnqueueOperation("another test", 2) 2072 c.Assert(err, jc.ErrorIsNil) 2073 actionAborted, err := m.EnqueueAction(operationIDAborted, machine.MachineTag(), "action-aborted", nil, true, "group", nil) 2074 c.Assert(err, jc.ErrorIsNil) 2075 c.Assert(actionAborted.Status(), gc.Equals, state.ActionPending) 2076 actionAborted, err = actionAborted.Begin() 2077 c.Assert(err, jc.ErrorIsNil) 2078 c.Assert(actionAborted.Status(), gc.Equals, state.ActionRunning) 2079 actionAborted, err = actionAborted.Finish(state.ActionResults{Status: state.ActionAborted}) 2080 c.Assert(err, jc.ErrorIsNil) 2081 c.Assert(actionAborted.Status(), gc.Equals, state.ActionAborted) 2082 2083 // completed action. 2084 operationIDCompleted, err := m.EnqueueOperation("another test", 2) 2085 c.Assert(err, jc.ErrorIsNil) 2086 actionCompleted, err := m.EnqueueAction(operationIDCompleted, machine.MachineTag(), "action-completed", nil, true, "group", nil) 2087 c.Assert(err, jc.ErrorIsNil) 2088 c.Assert(actionCompleted.Status(), gc.Equals, state.ActionPending) 2089 actionCompleted, err = actionCompleted.Begin() 2090 c.Assert(err, jc.ErrorIsNil) 2091 c.Assert(actionCompleted.Status(), gc.Equals, state.ActionRunning) 2092 actionCompleted, err = actionCompleted.Finish(state.ActionResults{Status: state.ActionCompleted}) 2093 c.Assert(err, jc.ErrorIsNil) 2094 c.Assert(actionCompleted.Status(), gc.Equals, state.ActionCompleted) 2095 2096 newModel, newState := s.importModel(c, s.State) 2097 defer func() { 2098 c.Assert(newState.Close(), jc.ErrorIsNil) 2099 }() 2100 2101 actions, err := newModel.AllActions() 2102 c.Assert(err, jc.ErrorIsNil) 2103 c.Assert(actions, gc.HasLen, 5) 2104 2105 actionPending, err = newModel.ActionByTag(actionPending.ActionTag()) 2106 c.Assert(err, jc.ErrorIsNil) 2107 c.Check(actionPending.Receiver(), gc.Equals, machine.Id()) 2108 c.Check(actionPending.Name(), gc.Equals, "action-pending") 2109 c.Check(state.ActionOperationId(actionPending), gc.Equals, operationIDPending) 2110 c.Check(actionPending.Status(), gc.Equals, state.ActionPending) 2111 c.Check(actionPending.Parallel(), jc.IsTrue) 2112 c.Check(actionPending.ExecutionGroup(), gc.Equals, "group") 2113 2114 actionRunning, err = newModel.ActionByTag(actionRunning.ActionTag()) 2115 c.Assert(err, jc.ErrorIsNil) 2116 c.Check(actionRunning.Receiver(), gc.Equals, machine.Id()) 2117 c.Check(actionRunning.Name(), gc.Equals, "action-running") 2118 c.Check(state.ActionOperationId(actionRunning), gc.Equals, operationIDRunning) 2119 c.Check(actionRunning.Status(), gc.Equals, state.ActionRunning) 2120 c.Check(actionRunning.Parallel(), jc.IsTrue) 2121 c.Check(actionRunning.ExecutionGroup(), gc.Equals, "group") 2122 2123 actionAborting, err = newModel.ActionByTag(actionAborting.ActionTag()) 2124 c.Assert(err, jc.ErrorIsNil) 2125 c.Check(actionAborting.Receiver(), gc.Equals, machine.Id()) 2126 c.Check(actionAborting.Name(), gc.Equals, "action-aborting") 2127 c.Check(state.ActionOperationId(actionAborting), gc.Equals, operationIDAborting) 2128 c.Check(actionAborting.Status(), gc.Equals, state.ActionAborting) 2129 c.Check(actionAborting.Parallel(), jc.IsTrue) 2130 c.Check(actionAborting.ExecutionGroup(), gc.Equals, "group") 2131 2132 actionAborted, err = newModel.ActionByTag(actionAborted.ActionTag()) 2133 c.Assert(err, jc.ErrorIsNil) 2134 c.Check(actionAborted.Receiver(), gc.Equals, machine.Id()) 2135 c.Check(actionAborted.Name(), gc.Equals, "action-aborted") 2136 c.Check(state.ActionOperationId(actionAborted), gc.Equals, operationIDAborted) 2137 c.Check(actionAborted.Status(), gc.Equals, state.ActionAborted) 2138 c.Check(actionAborted.Parallel(), jc.IsTrue) 2139 c.Check(actionAborted.ExecutionGroup(), gc.Equals, "group") 2140 2141 actionCompleted, err = newModel.ActionByTag(actionCompleted.ActionTag()) 2142 c.Assert(err, jc.ErrorIsNil) 2143 c.Check(actionCompleted.Receiver(), gc.Equals, machine.Id()) 2144 c.Check(actionCompleted.Name(), gc.Equals, "action-completed") 2145 c.Check(state.ActionOperationId(actionCompleted), gc.Equals, operationIDCompleted) 2146 c.Check(actionCompleted.Status(), gc.Equals, state.ActionCompleted) 2147 c.Check(actionCompleted.Parallel(), jc.IsTrue) 2148 c.Check(actionCompleted.ExecutionGroup(), gc.Equals, "group") 2149 2150 // Only pending/running/aborting actions will have action notification docs imported. 2151 actionIDs, err := newModel.AllActionIDsHasActionNotifications() 2152 c.Assert(err, jc.ErrorIsNil) 2153 sort.Strings(actionIDs) 2154 expectedIDs := []string{ 2155 actionRunning.Id(), 2156 actionPending.Id(), 2157 actionAborting.Id(), 2158 } 2159 sort.Strings(expectedIDs) 2160 c.Check(actionIDs, gc.DeepEquals, expectedIDs) 2161 } 2162 2163 func (s *MigrationImportSuite) TestOperation(c *gc.C) { 2164 m, err := s.State.Model() 2165 c.Assert(err, jc.ErrorIsNil) 2166 2167 operationID, err := m.EnqueueOperation("a test", 2) 2168 c.Assert(err, jc.ErrorIsNil) 2169 err = m.FailOperationEnqueuing(operationID, "fail", 1) 2170 c.Assert(err, jc.ErrorIsNil) 2171 2172 newModel, newState := s.importModel(c, s.State) 2173 defer func() { 2174 c.Assert(newState.Close(), jc.ErrorIsNil) 2175 }() 2176 2177 operations, _ := newModel.AllOperations() 2178 c.Assert(operations, gc.HasLen, 1) 2179 op := operations[0] 2180 c.Check(op.Summary(), gc.Equals, "a test") 2181 c.Check(op.Fail(), gc.Equals, "fail") 2182 c.Check(op.Id(), gc.Equals, operationID) 2183 c.Check(op.Status(), gc.Equals, state.ActionPending) 2184 c.Check(op.SpawnedTaskCount(), gc.Equals, 1) 2185 } 2186 2187 func (s *MigrationImportSuite) TestVolumes(c *gc.C) { 2188 machine := s.Factory.MakeMachine(c, &factory.MachineParams{ 2189 Volumes: []state.HostVolumeParams{{ 2190 Volume: state.VolumeParams{Size: 1234}, 2191 Attachment: state.VolumeAttachmentParams{ReadOnly: true}, 2192 }, { 2193 Volume: state.VolumeParams{Size: 4000}, 2194 Attachment: state.VolumeAttachmentParams{ReadOnly: true}, 2195 }, { 2196 Volume: state.VolumeParams{Size: 3000}, 2197 Attachment: state.VolumeAttachmentParams{ReadOnly: true}, 2198 }}, 2199 }) 2200 machineTag := machine.MachineTag() 2201 2202 // We know that the first volume is called "0/0" - although I don't know why. 2203 volTag := names.NewVolumeTag("0/0") 2204 volInfo := state.VolumeInfo{ 2205 HardwareId: "magic", 2206 WWN: "drbr", 2207 Size: 1500, 2208 Pool: "loop", 2209 VolumeId: "volume id", 2210 Persistent: true, 2211 } 2212 sb, err := state.NewStorageBackend(s.State) 2213 c.Assert(err, jc.ErrorIsNil) 2214 err = sb.SetVolumeInfo(volTag, volInfo) 2215 c.Assert(err, jc.ErrorIsNil) 2216 volAttachmentInfo := state.VolumeAttachmentInfo{ 2217 DeviceName: "device name", 2218 DeviceLink: "device link", 2219 BusAddress: "bus address", 2220 ReadOnly: true, 2221 } 2222 2223 err = sb.SetVolumeAttachmentInfo(machineTag, volTag, volAttachmentInfo) 2224 c.Assert(err, jc.ErrorIsNil) 2225 2226 // attach a iSCSI volume 2227 iscsiVolTag := names.NewVolumeTag("0/2") 2228 iscsiVolInfo := state.VolumeInfo{ 2229 HardwareId: "magic", 2230 WWN: "iscsi", 2231 Size: 1500, 2232 Pool: "loop", 2233 VolumeId: "iscsi id", 2234 Persistent: true, 2235 } 2236 2237 deviceAttrs := map[string]string{ 2238 "iqn": "bogusIQN", 2239 "address": "192.168.1.1", 2240 "port": "9999", 2241 "chap-user": "example", 2242 "chap-secret": "supersecretpassword", 2243 } 2244 2245 attachmentPlanInfo := state.VolumeAttachmentPlanInfo{ 2246 DeviceType: storage.DeviceTypeISCSI, 2247 DeviceAttributes: deviceAttrs, 2248 } 2249 2250 iscsiVolAttachmentInfo := state.VolumeAttachmentInfo{ 2251 DeviceName: "iscsi device", 2252 DeviceLink: "iscsi link", 2253 BusAddress: "iscsi address", 2254 ReadOnly: true, 2255 PlanInfo: &attachmentPlanInfo, 2256 } 2257 2258 err = sb.SetVolumeInfo(iscsiVolTag, iscsiVolInfo) 2259 c.Assert(err, jc.ErrorIsNil) 2260 2261 err = sb.SetVolumeAttachmentInfo(machineTag, iscsiVolTag, iscsiVolAttachmentInfo) 2262 c.Assert(err, jc.ErrorIsNil) 2263 2264 err = sb.CreateVolumeAttachmentPlan(machineTag, iscsiVolTag, attachmentPlanInfo) 2265 c.Assert(err, jc.ErrorIsNil) 2266 2267 deviceLinks := []string{"/dev/sdb", "/dev/mapper/testDevice"} 2268 2269 blockInfo := state.BlockDeviceInfo{ 2270 WWN: "testWWN", 2271 DeviceLinks: deviceLinks, 2272 HardwareId: "test-id", 2273 } 2274 2275 err = sb.SetVolumeAttachmentPlanBlockInfo(machineTag, iscsiVolTag, blockInfo) 2276 c.Assert(err, jc.ErrorIsNil) 2277 2278 _, newSt := s.importModel(c, s.State) 2279 newSb, err := state.NewStorageBackend(newSt) 2280 c.Assert(err, jc.ErrorIsNil) 2281 2282 volume, err := newSb.Volume(volTag) 2283 c.Assert(err, jc.ErrorIsNil) 2284 2285 // TODO: check status 2286 // TODO: check storage instance 2287 info, err := volume.Info() 2288 c.Assert(err, jc.ErrorIsNil) 2289 c.Check(info, jc.DeepEquals, volInfo) 2290 2291 attachment, err := newSb.VolumeAttachment(machineTag, volTag) 2292 c.Assert(err, jc.ErrorIsNil) 2293 attInfo, err := attachment.Info() 2294 c.Assert(err, jc.ErrorIsNil) 2295 c.Check(attInfo, jc.DeepEquals, volAttachmentInfo) 2296 2297 _, err = newSb.VolumeAttachmentPlan(machineTag, volTag) 2298 c.Assert(err, jc.Satisfies, errors.IsNotFound) 2299 2300 volTag = names.NewVolumeTag("0/1") 2301 volume, err = newSb.Volume(volTag) 2302 c.Assert(err, jc.ErrorIsNil) 2303 2304 params, needsProvisioning := volume.Params() 2305 c.Check(needsProvisioning, jc.IsTrue) 2306 c.Check(params.Pool, gc.Equals, "loop") 2307 c.Check(params.Size, gc.Equals, uint64(4000)) 2308 2309 attachment, err = newSb.VolumeAttachment(machineTag, volTag) 2310 c.Assert(err, jc.ErrorIsNil) 2311 attParams, needsProvisioning := attachment.Params() 2312 c.Check(needsProvisioning, jc.IsTrue) 2313 c.Check(attParams.ReadOnly, jc.IsTrue) 2314 2315 iscsiVolume, err := newSb.Volume(iscsiVolTag) 2316 c.Assert(err, jc.ErrorIsNil) 2317 2318 iscsiInfo, err := iscsiVolume.Info() 2319 c.Assert(err, jc.ErrorIsNil) 2320 c.Check(iscsiInfo, jc.DeepEquals, iscsiVolInfo) 2321 2322 attachment, err = newSb.VolumeAttachment(machineTag, iscsiVolTag) 2323 c.Assert(err, jc.ErrorIsNil) 2324 attInfo, err = attachment.Info() 2325 c.Assert(err, jc.ErrorIsNil) 2326 c.Check(attInfo, jc.DeepEquals, iscsiVolAttachmentInfo) 2327 2328 attachmentPlan, err := newSb.VolumeAttachmentPlan(machineTag, iscsiVolTag) 2329 c.Assert(err, gc.IsNil) 2330 c.Assert(attachmentPlan.Volume(), gc.Equals, iscsiVolTag) 2331 c.Assert(attachmentPlan.Machine(), gc.Equals, machineTag) 2332 2333 planInfo, err := attachmentPlan.PlanInfo() 2334 c.Assert(err, gc.IsNil) 2335 c.Assert(planInfo, jc.DeepEquals, attachmentPlanInfo) 2336 2337 volBlockInfo, err := attachmentPlan.BlockDeviceInfo() 2338 c.Assert(err, gc.IsNil) 2339 c.Assert(volBlockInfo, jc.DeepEquals, blockInfo) 2340 } 2341 2342 func (s *MigrationImportSuite) TestFilesystems(c *gc.C) { 2343 machine := s.Factory.MakeMachine(c, &factory.MachineParams{ 2344 Filesystems: []state.HostFilesystemParams{{ 2345 Filesystem: state.FilesystemParams{Size: 1234}, 2346 Attachment: state.FilesystemAttachmentParams{ 2347 Location: "location", 2348 ReadOnly: true}, 2349 }, { 2350 Filesystem: state.FilesystemParams{Size: 4000}, 2351 Attachment: state.FilesystemAttachmentParams{ 2352 ReadOnly: true}, 2353 }}, 2354 }) 2355 machineTag := machine.MachineTag() 2356 2357 // We know that the first filesystem is called "0/0" as it is the first 2358 // filesystem (filesystems use sequences), and it is bound to machine 0. 2359 fsTag := names.NewFilesystemTag("0/0") 2360 fsInfo := state.FilesystemInfo{ 2361 Size: 1500, 2362 Pool: "rootfs", 2363 FilesystemId: "filesystem id", 2364 } 2365 sb, err := state.NewStorageBackend(s.State) 2366 c.Assert(err, jc.ErrorIsNil) 2367 err = sb.SetFilesystemInfo(fsTag, fsInfo) 2368 c.Assert(err, jc.ErrorIsNil) 2369 fsAttachmentInfo := state.FilesystemAttachmentInfo{ 2370 MountPoint: "/mnt/foo", 2371 ReadOnly: true, 2372 } 2373 err = sb.SetFilesystemAttachmentInfo(machineTag, fsTag, fsAttachmentInfo) 2374 c.Assert(err, jc.ErrorIsNil) 2375 2376 _, newSt := s.importModel(c, s.State) 2377 newSb, err := state.NewStorageBackend(newSt) 2378 c.Assert(err, jc.ErrorIsNil) 2379 2380 filesystem, err := newSb.Filesystem(fsTag) 2381 c.Assert(err, jc.ErrorIsNil) 2382 2383 // TODO: check status 2384 // TODO: check storage instance 2385 info, err := filesystem.Info() 2386 c.Assert(err, jc.ErrorIsNil) 2387 c.Check(info, jc.DeepEquals, fsInfo) 2388 2389 attachment, err := newSb.FilesystemAttachment(machineTag, fsTag) 2390 c.Assert(err, jc.ErrorIsNil) 2391 attInfo, err := attachment.Info() 2392 c.Assert(err, jc.ErrorIsNil) 2393 c.Check(attInfo, jc.DeepEquals, fsAttachmentInfo) 2394 2395 fsTag = names.NewFilesystemTag("0/1") 2396 filesystem, err = newSb.Filesystem(fsTag) 2397 c.Assert(err, jc.ErrorIsNil) 2398 2399 params, needsProvisioning := filesystem.Params() 2400 c.Check(needsProvisioning, jc.IsTrue) 2401 c.Check(params.Pool, gc.Equals, "rootfs") 2402 c.Check(params.Size, gc.Equals, uint64(4000)) 2403 2404 attachment, err = newSb.FilesystemAttachment(machineTag, fsTag) 2405 c.Assert(err, jc.ErrorIsNil) 2406 attParams, needsProvisioning := attachment.Params() 2407 c.Check(needsProvisioning, jc.IsTrue) 2408 c.Check(attParams.ReadOnly, jc.IsTrue) 2409 } 2410 2411 func (s *MigrationImportSuite) TestStorage(c *gc.C) { 2412 app, u, storageTag := s.makeUnitWithStorage(c) 2413 sb, err := state.NewStorageBackend(s.State) 2414 c.Assert(err, jc.ErrorIsNil) 2415 original, err := sb.StorageInstance(storageTag) 2416 c.Assert(err, jc.ErrorIsNil) 2417 originalCount := state.StorageAttachmentCount(original) 2418 c.Assert(originalCount, gc.Equals, 1) 2419 originalAttachments, err := sb.StorageAttachments(storageTag) 2420 c.Assert(err, jc.ErrorIsNil) 2421 c.Assert(originalAttachments, gc.HasLen, 1) 2422 c.Assert(originalAttachments[0].Unit(), gc.Equals, u.UnitTag()) 2423 appName := app.Name() 2424 2425 _, newSt := s.importModel(c, s.State) 2426 2427 app, err = newSt.Application(appName) 2428 c.Assert(err, jc.ErrorIsNil) 2429 cons, err := app.StorageConstraints() 2430 c.Assert(err, jc.ErrorIsNil) 2431 c.Check(cons, jc.DeepEquals, map[string]state.StorageConstraints{ 2432 "data": {Pool: "modelscoped", Size: 0x400, Count: 1}, 2433 "allecto": {Pool: "loop", Size: 0x400}, 2434 }) 2435 2436 newSb, err := state.NewStorageBackend(newSt) 2437 c.Assert(err, jc.ErrorIsNil) 2438 2439 testInstance, err := newSb.StorageInstance(storageTag) 2440 c.Assert(err, jc.ErrorIsNil) 2441 2442 c.Check(testInstance.Tag(), gc.Equals, original.Tag()) 2443 c.Check(testInstance.Kind(), gc.Equals, original.Kind()) 2444 c.Check(testInstance.Life(), gc.Equals, original.Life()) 2445 c.Check(testInstance.StorageName(), gc.Equals, original.StorageName()) 2446 c.Check(testInstance.Pool(), gc.Equals, original.Pool()) 2447 c.Check(state.StorageAttachmentCount(testInstance), gc.Equals, originalCount) 2448 2449 attachments, err := newSb.StorageAttachments(storageTag) 2450 c.Assert(err, jc.ErrorIsNil) 2451 c.Assert(attachments, gc.HasLen, 1) 2452 c.Assert(attachments[0].Unit(), gc.Equals, u.UnitTag()) 2453 } 2454 2455 func (s *MigrationImportSuite) TestStorageDetached(c *gc.C) { 2456 _, u, storageTag := s.makeUnitWithStorage(c) 2457 err := u.Destroy() 2458 c.Assert(err, jc.ErrorIsNil) 2459 sb, err := state.NewStorageBackend(s.State) 2460 c.Assert(err, jc.ErrorIsNil) 2461 err = sb.DetachStorage(storageTag, u.UnitTag(), false, dontWait) 2462 c.Assert(err, jc.ErrorIsNil) 2463 err = u.EnsureDead() 2464 c.Assert(err, jc.ErrorIsNil) 2465 err = u.Remove() 2466 c.Assert(err, jc.ErrorIsNil) 2467 2468 s.importModel(c, s.State) 2469 } 2470 2471 func (s *MigrationImportSuite) TestStorageInstanceConstraints(c *gc.C) { 2472 _, _, storageTag := s.makeUnitWithStorage(c) 2473 _, newSt := s.importModel(c, s.State, func(desc map[string]interface{}) { 2474 storages := desc["storages"].(map[interface{}]interface{}) 2475 for _, item := range storages["storages"].([]interface{}) { 2476 testStorage := item.(map[interface{}]interface{}) 2477 cons := testStorage["constraints"].(map[interface{}]interface{}) 2478 cons["pool"] = "static" 2479 } 2480 }) 2481 newSb, err := state.NewStorageBackend(newSt) 2482 c.Assert(err, jc.ErrorIsNil) 2483 testInstance, err := newSb.StorageInstance(storageTag) 2484 c.Assert(err, jc.ErrorIsNil) 2485 c.Check(testInstance.Pool(), gc.Equals, "static") 2486 } 2487 2488 func (s *MigrationImportSuite) TestStorageInstanceConstraintsFallback(c *gc.C) { 2489 _, u, storageTag0 := s.makeUnitWithStorage(c) 2490 2491 sb, err := state.NewStorageBackend(s.State) 2492 c.Assert(err, jc.ErrorIsNil) 2493 _, err = sb.AddStorageForUnit(u.UnitTag(), "allecto", state.StorageConstraints{ 2494 Count: 3, 2495 Size: 1234, 2496 Pool: "modelscoped", 2497 }) 2498 c.Assert(err, jc.ErrorIsNil) 2499 storageTag1 := names.NewStorageTag("allecto/1") 2500 storageTag2 := names.NewStorageTag("allecto/2") 2501 2502 // We delete the storage instance constraints for each storage 2503 // instance. For data/0 and allecto/1 we also delete the volume, 2504 // and we delete the application storage constraints for "data". 2505 // 2506 // We expect: 2507 // - for data/0, to get the defaults (loop, 1G) 2508 // - for allecto/1, to get the application storage constraints 2509 // - for allecto/2, to get the volume pool/size 2510 2511 _, newSt := s.importModel(c, s.State, func(desc map[string]interface{}) { 2512 applications := desc["applications"].(map[interface{}]interface{}) 2513 volumes := desc["volumes"].(map[interface{}]interface{}) 2514 storages := desc["storages"].(map[interface{}]interface{}) 2515 storages["version"] = 2 2516 2517 app := applications["applications"].([]interface{})[0].(map[interface{}]interface{}) 2518 sc := app["storage-directives"].(map[interface{}]interface{}) 2519 delete(sc, "data") 2520 sc["allecto"].(map[interface{}]interface{})["pool"] = "modelscoped-block" 2521 2522 var keepVolumes []interface{} 2523 for _, item := range volumes["volumes"].([]interface{}) { 2524 volume := item.(map[interface{}]interface{}) 2525 switch volume["storage-id"] { 2526 case storageTag0.Id(), storageTag1.Id(): 2527 default: 2528 keepVolumes = append(keepVolumes, volume) 2529 } 2530 } 2531 volumes["volumes"] = keepVolumes 2532 2533 for _, item := range storages["storages"].([]interface{}) { 2534 testStorage := item.(map[interface{}]interface{}) 2535 delete(testStorage, "constraints") 2536 } 2537 }) 2538 2539 newSb, err := state.NewStorageBackend(newSt) 2540 c.Assert(err, jc.ErrorIsNil) 2541 2542 instance0, err := newSb.StorageInstance(storageTag0) 2543 c.Assert(err, jc.ErrorIsNil) 2544 c.Check(instance0.Pool(), gc.Equals, "loop") 2545 2546 instance1, err := newSb.StorageInstance(storageTag1) 2547 c.Assert(err, jc.ErrorIsNil) 2548 c.Check(instance1.Pool(), gc.Equals, "modelscoped-block") 2549 2550 instance2, err := newSb.StorageInstance(storageTag2) 2551 c.Assert(err, jc.ErrorIsNil) 2552 c.Check(instance2.Pool(), gc.Equals, "modelscoped") 2553 } 2554 2555 func (s *MigrationImportSuite) TestStoragePools(c *gc.C) { 2556 pm := poolmanager.New(state.NewStateSettings(s.State), provider.CommonStorageProviders()) 2557 _, err := pm.Create("test-pool", provider.LoopProviderType, map[string]interface{}{ 2558 "value": 42, 2559 }) 2560 c.Assert(err, jc.ErrorIsNil) 2561 2562 _, newSt := s.importModel(c, s.State) 2563 2564 pm = poolmanager.New(state.NewStateSettings(newSt), provider.CommonStorageProviders()) 2565 pools, err := pm.List() 2566 c.Assert(err, jc.ErrorIsNil) 2567 c.Assert(pools, gc.HasLen, 1) 2568 2569 pool := pools[0] 2570 c.Assert(pool.Name(), gc.Equals, "test-pool") 2571 c.Assert(pool.Provider(), gc.Equals, provider.LoopProviderType) 2572 c.Assert(pool.Attrs(), jc.DeepEquals, storage.Attrs{ 2573 "value": 42, 2574 }) 2575 } 2576 2577 func (s *MigrationImportSuite) TestPayloads(c *gc.C) { 2578 originalUnit := s.Factory.MakeUnit(c, nil) 2579 unitID := originalUnit.UnitTag().Id() 2580 up, err := s.State.UnitPayloads(originalUnit) 2581 c.Assert(err, jc.ErrorIsNil) 2582 original := payloads.Payload{ 2583 PayloadClass: charm.PayloadClass{ 2584 Name: "something", 2585 Type: "special", 2586 }, 2587 ID: "42", 2588 Status: "running", 2589 Labels: []string{"foo", "bar"}, 2590 } 2591 err = up.Track(original) 2592 c.Assert(err, jc.ErrorIsNil) 2593 2594 _, newSt := s.importModel(c, s.State) 2595 2596 unit, err := newSt.Unit(unitID) 2597 c.Assert(err, jc.ErrorIsNil) 2598 2599 up, err = newSt.UnitPayloads(unit) 2600 c.Assert(err, jc.ErrorIsNil) 2601 2602 result, err := up.List() 2603 c.Assert(err, jc.ErrorIsNil) 2604 c.Assert(result, gc.HasLen, 1) 2605 c.Assert(result[0].Payload, gc.NotNil) 2606 2607 testPayload := result[0].Payload 2608 2609 machineID, err := unit.AssignedMachineId() 2610 c.Check(err, jc.ErrorIsNil) 2611 c.Check(testPayload.Name, gc.Equals, original.Name) 2612 c.Check(testPayload.Type, gc.Equals, original.Type) 2613 c.Check(testPayload.ID, gc.Equals, original.ID) 2614 c.Check(testPayload.Status, gc.Equals, original.Status) 2615 c.Check(testPayload.Labels, jc.DeepEquals, original.Labels) 2616 c.Check(testPayload.Unit, gc.Equals, unitID) 2617 c.Check(testPayload.Machine, gc.Equals, machineID) 2618 } 2619 2620 func (s *MigrationImportSuite) TestRemoteApplications(c *gc.C) { 2621 remoteApp, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 2622 Name: "gravy-rainbow", 2623 URL: "me/model.rainbow", 2624 SourceModel: s.Model.ModelTag(), 2625 Token: "charisma", 2626 Endpoints: []charm.Relation{{ 2627 Interface: "mysql", 2628 Name: "db", 2629 Role: charm.RoleProvider, 2630 Scope: charm.ScopeGlobal, 2631 }, { 2632 Interface: "mysql-root", 2633 Name: "db-admin", 2634 Limit: 5, 2635 Role: charm.RoleProvider, 2636 Scope: charm.ScopeGlobal, 2637 }, { 2638 Interface: "logging", 2639 Name: "logging", 2640 Role: charm.RoleProvider, 2641 Scope: charm.ScopeGlobal, 2642 }}, 2643 Spaces: []*environs.ProviderSpaceInfo{{ 2644 SpaceInfo: network.SpaceInfo{ 2645 Name: "unicorns", 2646 ProviderId: "space-provider-id", 2647 Subnets: []network.SubnetInfo{{ 2648 CIDR: "10.0.1.0/24", 2649 ProviderId: "subnet-provider-id", 2650 AvailabilityZones: []string{"eu-west-1"}, 2651 }}, 2652 }, 2653 }}, 2654 }) 2655 c.Assert(err, jc.ErrorIsNil) 2656 err = remoteApp.SetStatus(status.StatusInfo{Status: status.Active}) 2657 c.Assert(err, jc.ErrorIsNil) 2658 2659 service := state.NewExternalControllers(s.State) 2660 _, err = service.Save(crossmodel.ControllerInfo{ 2661 ControllerTag: s.Model.ControllerTag(), 2662 Addrs: []string{"192.168.1.1:8080"}, 2663 Alias: "magic", 2664 CACert: "magic-ca-cert", 2665 }, s.Model.UUID()) 2666 c.Assert(err, jc.ErrorIsNil) 2667 2668 out, err := s.State.Export(map[string]string{}) 2669 c.Assert(err, jc.ErrorIsNil) 2670 2671 uuid := utils.MustNewUUID().String() 2672 in := newModel(out, uuid, "new") 2673 2674 _, newSt, err := s.Controller.Import(in) 2675 if err == nil { 2676 defer newSt.Close() 2677 } 2678 c.Assert(err, jc.ErrorIsNil) 2679 remoteApplications, err := newSt.AllRemoteApplications() 2680 c.Assert(err, jc.ErrorIsNil) 2681 c.Assert(remoteApplications, gc.HasLen, 1) 2682 2683 remoteApplication := remoteApplications[0] 2684 c.Assert(remoteApplication.Name(), gc.Equals, "gravy-rainbow") 2685 c.Assert(remoteApplication.ConsumeVersion(), gc.Equals, 1) 2686 2687 url, _ := remoteApplication.URL() 2688 c.Assert(url, gc.Equals, "me/model.rainbow") 2689 c.Assert(remoteApplication.SourceModel(), gc.Equals, s.Model.ModelTag()) 2690 2691 token, err := remoteApplication.Token() 2692 c.Assert(err, jc.ErrorIsNil) 2693 c.Assert(token, gc.Equals, "charisma") 2694 2695 s.assertRemoteApplicationEndpoints(c, remoteApp, remoteApplication) 2696 s.assertRemoteApplicationSpaces(c, remoteApp, remoteApplication) 2697 } 2698 2699 func (s *MigrationImportSuite) TestRemoteApplicationsConsumerProxy(c *gc.C) { 2700 remoteApp, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 2701 Name: "gravy-rainbow", 2702 URL: "me/model.rainbow", 2703 SourceModel: s.Model.ModelTag(), 2704 Token: "charisma", 2705 ConsumeVersion: 2, 2706 IsConsumerProxy: true, 2707 Endpoints: []charm.Relation{{ 2708 Interface: "mysql", 2709 Name: "db", 2710 Role: charm.RoleProvider, 2711 Scope: charm.ScopeGlobal, 2712 }, { 2713 Interface: "mysql-root", 2714 Name: "db-admin", 2715 Limit: 5, 2716 Role: charm.RoleProvider, 2717 Scope: charm.ScopeGlobal, 2718 }, { 2719 Interface: "logging", 2720 Name: "logging", 2721 Role: charm.RoleProvider, 2722 Scope: charm.ScopeGlobal, 2723 }}, 2724 Spaces: []*environs.ProviderSpaceInfo{{ 2725 SpaceInfo: network.SpaceInfo{ 2726 Name: "unicorns", 2727 ProviderId: "space-provider-id", 2728 Subnets: []network.SubnetInfo{{ 2729 CIDR: "10.0.1.0/24", 2730 ProviderId: "subnet-provider-id", 2731 AvailabilityZones: []string{"eu-west-1"}, 2732 }}, 2733 }, 2734 }}, 2735 }) 2736 c.Assert(err, jc.ErrorIsNil) 2737 2738 service := state.NewExternalControllers(s.State) 2739 _, err = service.Save(crossmodel.ControllerInfo{ 2740 ControllerTag: s.Model.ControllerTag(), 2741 Addrs: []string{"192.168.1.1:8080"}, 2742 Alias: "magic", 2743 CACert: "magic-ca-cert", 2744 }, s.Model.UUID()) 2745 c.Assert(err, jc.ErrorIsNil) 2746 2747 out, err := s.State.Export(map[string]string{}) 2748 c.Assert(err, jc.ErrorIsNil) 2749 2750 uuid := utils.MustNewUUID().String() 2751 in := newModel(out, uuid, "new") 2752 2753 _, newSt, err := s.Controller.Import(in) 2754 if err == nil { 2755 defer newSt.Close() 2756 } 2757 c.Assert(err, jc.ErrorIsNil) 2758 remoteApplications, err := newSt.AllRemoteApplications() 2759 c.Assert(err, jc.ErrorIsNil) 2760 c.Assert(remoteApplications, gc.HasLen, 1) 2761 2762 remoteApplication := remoteApplications[0] 2763 c.Assert(remoteApplication.Name(), gc.Equals, "gravy-rainbow") 2764 c.Assert(remoteApplication.ConsumeVersion(), gc.Equals, 2) 2765 2766 url, _ := remoteApplication.URL() 2767 c.Assert(url, gc.Equals, "me/model.rainbow") 2768 c.Assert(remoteApplication.SourceModel(), gc.Equals, s.Model.ModelTag()) 2769 2770 token, err := remoteApplication.Token() 2771 c.Assert(err, jc.ErrorIsNil) 2772 c.Assert(token, gc.Equals, "charisma") 2773 2774 s.assertRemoteApplicationEndpoints(c, remoteApp, remoteApplication) 2775 s.assertRemoteApplicationSpaces(c, remoteApp, remoteApplication) 2776 } 2777 2778 func (s *MigrationImportSuite) assertRemoteApplicationEndpoints(c *gc.C, expected, received *state.RemoteApplication) { 2779 receivedEndpoints, err := received.Endpoints() 2780 c.Assert(err, jc.ErrorIsNil) 2781 c.Assert(receivedEndpoints, gc.HasLen, 3) 2782 2783 expectedEndpoints, err := expected.Endpoints() 2784 c.Assert(err, jc.ErrorIsNil) 2785 c.Assert(expectedEndpoints, gc.HasLen, 3) 2786 2787 for k, expectedEndpoint := range expectedEndpoints { 2788 receivedEndpoint := receivedEndpoints[k] 2789 c.Assert(receivedEndpoint.Interface, gc.Equals, expectedEndpoint.Interface) 2790 c.Assert(receivedEndpoint.Name, gc.Equals, expectedEndpoint.Name) 2791 } 2792 } 2793 2794 func (s *MigrationImportSuite) assertRemoteApplicationSpaces(c *gc.C, expected, received *state.RemoteApplication) { 2795 receivedSpaces := received.Spaces() 2796 c.Assert(receivedSpaces, gc.HasLen, 1) 2797 2798 expectedSpaces := expected.Spaces() 2799 c.Assert(expectedSpaces, gc.HasLen, 1) 2800 for k, expectedSpace := range expectedSpaces { 2801 receivedSpace := receivedSpaces[k] 2802 c.Assert(receivedSpace.Name, gc.Equals, expectedSpace.Name) 2803 c.Assert(receivedSpace.ProviderId, gc.Equals, expectedSpace.ProviderId) 2804 2805 c.Assert(receivedSpace.Subnets, gc.HasLen, 1) 2806 receivedSubnet := receivedSpace.Subnets[0] 2807 2808 c.Assert(expectedSpace.Subnets, gc.HasLen, 1) 2809 expectedSubnet := expectedSpace.Subnets[0] 2810 2811 c.Assert(receivedSubnet.CIDR, gc.Equals, expectedSubnet.CIDR) 2812 c.Assert(receivedSubnet.ProviderId, gc.Equals, expectedSubnet.ProviderId) 2813 c.Assert(receivedSubnet.AvailabilityZones, gc.DeepEquals, expectedSubnet.AvailabilityZones) 2814 } 2815 } 2816 2817 func (s *MigrationImportSuite) TestApplicationsWithNilConfigValues(c *gc.C) { 2818 application := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 2819 CharmConfig: map[string]interface{}{ 2820 "foo": "bar", 2821 }, 2822 }) 2823 s.primeStatusHistory(c, application, status.Active, 5) 2824 // Since above factory method calls newly updated state.AddApplication(...) 2825 // which removes config settings with nil value before writing 2826 // application into database, 2827 // strip config setting values to nil directly to simulate 2828 // what could happen to some applications in 2.0 and 2.1. 2829 // For more context, see https://bugs.launchpad.net/juju/+bug/1667199 2830 settings := state.GetApplicationCharmConfig(s.State, application) 2831 settings.Set("foo", nil) 2832 _, err := settings.Write() 2833 c.Assert(err, jc.ErrorIsNil) 2834 2835 _, newSt := s.importModel(c, s.State) 2836 2837 importedApplications, err := newSt.AllApplications() 2838 c.Assert(err, jc.ErrorIsNil) 2839 c.Assert(importedApplications, gc.HasLen, 1) 2840 importedApplication := importedApplications[0] 2841 2842 // Ensure that during import application settings with nil config values 2843 // were stripped and not written into database. 2844 importedSettings := state.GetApplicationCharmConfig(newSt, importedApplication) 2845 _, importedFound := importedSettings.Get("foo") 2846 c.Assert(importedFound, jc.IsFalse) 2847 } 2848 2849 func (s *MigrationImportSuite) TestOneSubordinateTwoGuvnors(c *gc.C) { 2850 // Check that invalid relationscopes aren't created when importing 2851 // a subordinate related to 2 principals. 2852 wordpress := state.AddTestingApplication(c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress")) 2853 mysql := state.AddTestingApplication(c, s.State, "mysql", state.AddTestingCharm(c, s.State, "mysql")) 2854 wordpress0 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: wordpress}) 2855 mysql0 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: mysql}) 2856 2857 logging := s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging")) 2858 2859 addSubordinate := func(app *state.Application, unit *state.Unit) string { 2860 eps, err := s.State.InferEndpoints(app.Name(), logging.Name()) 2861 c.Assert(err, jc.ErrorIsNil) 2862 rel, err := s.State.AddRelation(eps...) 2863 c.Assert(err, jc.ErrorIsNil) 2864 pru, err := rel.Unit(unit) 2865 c.Assert(err, jc.ErrorIsNil) 2866 err = pru.EnterScope(nil) 2867 c.Assert(err, jc.ErrorIsNil) 2868 // Need to reload the doc to get the subordinates. 2869 err = unit.Refresh() 2870 c.Assert(err, jc.ErrorIsNil) 2871 subordinates := unit.SubordinateNames() 2872 c.Assert(subordinates, gc.HasLen, 1) 2873 loggingUnit, err := s.State.Unit(subordinates[0]) 2874 c.Assert(err, jc.ErrorIsNil) 2875 sub, err := rel.Unit(loggingUnit) 2876 c.Assert(err, jc.ErrorIsNil) 2877 err = sub.EnterScope(nil) 2878 c.Assert(err, jc.ErrorIsNil) 2879 return rel.String() 2880 } 2881 2882 logMysqlKey := addSubordinate(mysql, mysql0) 2883 logWpKey := addSubordinate(wordpress, wordpress0) 2884 2885 units, err := logging.AllUnits() 2886 c.Assert(err, jc.ErrorIsNil) 2887 c.Assert(units, gc.HasLen, 2) 2888 2889 for _, unit := range units { 2890 app, err := unit.Application() 2891 c.Assert(err, jc.ErrorIsNil) 2892 agentTools := version.Binary{ 2893 Number: jujuversion.Current, 2894 Arch: arch.HostArch(), 2895 Release: app.CharmOrigin().Platform.OS, 2896 } 2897 err = unit.SetAgentVersion(agentTools) 2898 c.Assert(err, jc.ErrorIsNil) 2899 } 2900 2901 _, newSt := s.importModel(c, s.State) 2902 2903 logMysqlRel, err := newSt.KeyRelation(logMysqlKey) 2904 c.Assert(err, jc.ErrorIsNil) 2905 logWpRel, err := newSt.KeyRelation(logWpKey) 2906 c.Assert(err, jc.ErrorIsNil) 2907 2908 mysqlLogUnit, err := newSt.Unit("logging/0") 2909 c.Assert(err, jc.ErrorIsNil) 2910 wpLogUnit, err := newSt.Unit("logging/1") 2911 c.Assert(err, jc.ErrorIsNil) 2912 2913 // Sanity checks 2914 name, ok := mysqlLogUnit.PrincipalName() 2915 c.Assert(ok, jc.IsTrue) 2916 c.Assert(name, gc.Equals, "mysql/0") 2917 2918 name, ok = wpLogUnit.PrincipalName() 2919 c.Assert(ok, jc.IsTrue) 2920 c.Assert(name, gc.Equals, "wordpress/0") 2921 2922 checkScope := func(unit *state.Unit, rel *state.Relation, expected bool) { 2923 ru, err := rel.Unit(unit) 2924 c.Assert(err, jc.ErrorIsNil) 2925 // Sanity check 2926 valid, err := ru.Valid() 2927 c.Assert(err, jc.ErrorIsNil) 2928 c.Check(valid, gc.Equals, expected) 2929 2930 inscope, err := ru.InScope() 2931 c.Assert(err, jc.ErrorIsNil) 2932 c.Check(inscope, gc.Equals, expected) 2933 } 2934 // The WP logging unit shouldn't be in scope for the mysql-logging 2935 // relation. 2936 checkScope(wpLogUnit, logMysqlRel, false) 2937 // Similarly, the mysql logging unit shouldn't be in scope for the 2938 // wp-logging relation. 2939 checkScope(mysqlLogUnit, logWpRel, false) 2940 2941 // But obviously the units should be in their relations. 2942 checkScope(mysqlLogUnit, logMysqlRel, true) 2943 checkScope(wpLogUnit, logWpRel, true) 2944 } 2945 2946 func (s *MigrationImportSuite) TestImportingModelWithBlankType(c *gc.C) { 2947 testModel, err := s.State.Export(map[string]string{}) 2948 c.Assert(err, jc.ErrorIsNil) 2949 2950 newConfig := testModel.Config() 2951 newConfig["uuid"] = "aabbccdd-1234-8765-abcd-0123456789ab" 2952 newConfig["name"] = "something-new" 2953 noTypeModel := description.NewModel(description.ModelArgs{ 2954 Type: "", 2955 Owner: testModel.Owner(), 2956 Config: newConfig, 2957 LatestToolsVersion: testModel.LatestToolsVersion(), 2958 EnvironVersion: testModel.EnvironVersion(), 2959 Blocks: testModel.Blocks(), 2960 Cloud: testModel.Cloud(), 2961 CloudRegion: testModel.CloudRegion(), 2962 }) 2963 imported, newSt, err := s.Controller.Import(noTypeModel) 2964 c.Assert(err, jc.ErrorIsNil) 2965 defer func() { _ = newSt.Close() }() 2966 2967 c.Assert(imported.Type(), gc.Equals, state.ModelTypeIAAS) 2968 } 2969 2970 func (s *MigrationImportSuite) TestImportingModelWithDefaultSeriesBefore2935(c *gc.C) { 2971 defaultBase, ok := s.testImportingModelWithDefaultSeries(c, version.MustParse("2.7.8")) 2972 c.Assert(ok, jc.IsFalse, gc.Commentf("value: %q", defaultBase)) 2973 } 2974 2975 func (s *MigrationImportSuite) TestImportingModelWithDefaultSeriesAfter2935(c *gc.C) { 2976 defaultBase, ok := s.testImportingModelWithDefaultSeries(c, version.MustParse("2.9.35")) 2977 c.Assert(ok, jc.IsTrue) 2978 c.Assert(defaultBase, gc.Equals, "ubuntu@22.04/stable") 2979 } 2980 2981 func (s *MigrationImportSuite) testImportingModelWithDefaultSeries(c *gc.C, toolsVer version.Number) (string, bool) { 2982 testModel, err := s.State.Export(map[string]string{}) 2983 c.Assert(err, jc.ErrorIsNil) 2984 2985 newConfig := testModel.Config() 2986 newConfig["uuid"] = "aabbccdd-1234-8765-abcd-0123456789ab" 2987 newConfig["name"] = "something-new" 2988 newConfig["default-series"] = "jammy" 2989 newConfig["agent-version"] = toolsVer.String() 2990 importModel := description.NewModel(description.ModelArgs{ 2991 Type: string(state.ModelTypeIAAS), 2992 Owner: testModel.Owner(), 2993 Config: newConfig, 2994 EnvironVersion: testModel.EnvironVersion(), 2995 Blocks: testModel.Blocks(), 2996 Cloud: testModel.Cloud(), 2997 CloudRegion: testModel.CloudRegion(), 2998 }) 2999 imported, newSt, err := s.Controller.Import(importModel) 3000 c.Assert(err, jc.ErrorIsNil) 3001 defer func() { _ = newSt.Close() }() 3002 3003 importedCfg, err := imported.Config() 3004 c.Assert(err, jc.ErrorIsNil) 3005 return importedCfg.DefaultBase() 3006 } 3007 3008 func (s *MigrationImportSuite) TestImportingRelationApplicationSettings(c *gc.C) { 3009 state.AddTestingApplication(c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress")) 3010 state.AddTestingApplication(c, s.State, "mysql", state.AddTestingCharm(c, s.State, "mysql")) 3011 eps, err := s.State.InferEndpoints("mysql", "wordpress") 3012 c.Assert(err, jc.ErrorIsNil) 3013 rel, err := s.State.AddRelation(eps...) 3014 c.Assert(err, jc.ErrorIsNil) 3015 3016 wordpressSettings := map[string]interface{}{ 3017 "venusian": "superbug", 3018 } 3019 err = rel.UpdateApplicationSettings("wordpress", &fakeToken{}, wordpressSettings) 3020 c.Assert(err, jc.ErrorIsNil) 3021 mysqlSettings := map[string]interface{}{ 3022 "planet b": "perihelion", 3023 } 3024 err = rel.UpdateApplicationSettings("mysql", &fakeToken{}, mysqlSettings) 3025 c.Assert(err, jc.ErrorIsNil) 3026 3027 _, newSt := s.importModel(c, s.State) 3028 3029 newWordpress, err := newSt.Application("wordpress") 3030 c.Assert(err, jc.ErrorIsNil) 3031 c.Assert(state.RelationCount(newWordpress), gc.Equals, 1) 3032 rels, err := newWordpress.Relations() 3033 c.Assert(err, jc.ErrorIsNil) 3034 c.Assert(rels, gc.HasLen, 1) 3035 3036 newRel := rels[0] 3037 3038 newWpSettings, err := newRel.ApplicationSettings("wordpress") 3039 c.Assert(err, jc.ErrorIsNil) 3040 c.Assert(newWpSettings, gc.DeepEquals, wordpressSettings) 3041 3042 newMysqlSettings, err := newRel.ApplicationSettings("mysql") 3043 c.Assert(err, jc.ErrorIsNil) 3044 c.Assert(newMysqlSettings, gc.DeepEquals, mysqlSettings) 3045 } 3046 3047 func (s *MigrationImportSuite) TestApplicationAddLatestCharmChannelTrack(c *gc.C) { 3048 st := s.State 3049 // Add a application with charm settings, app config, and leadership settings. 3050 f := factory.NewFactory(st, s.StatePool) 3051 3052 // Add a application with charm settings, app config, and leadership settings. 3053 testCharm := f.MakeCharmV2(c, &factory.CharmParams{ 3054 Name: "snappass-test", // it has resources 3055 }) 3056 c.Assert(testCharm.Meta().Resources, gc.HasLen, 3) 3057 origin := &state.CharmOrigin{ 3058 Source: "charm-hub", 3059 Type: "charm", 3060 Revision: &charm.MustParseURL(testCharm.URL()).Revision, 3061 Channel: &state.Channel{ 3062 Risk: "edge", 3063 }, 3064 ID: "charm-hub-id", 3065 Hash: "charmhub-hash", 3066 Platform: &state.Platform{ 3067 Architecture: charm.MustParseURL(testCharm.URL()).Architecture, 3068 OS: "ubuntu", 3069 Channel: "12.10/stable", 3070 }, 3071 } 3072 application := f.MakeApplication(c, &factory.ApplicationParams{ 3073 Charm: testCharm, 3074 CharmOrigin: origin, 3075 }) 3076 allApplications, err := s.State.AllApplications() 3077 c.Assert(err, jc.ErrorIsNil) 3078 c.Assert(allApplications, gc.HasLen, 1) 3079 3080 _, newSt := s.importModel(c, s.State) 3081 importedApp, err := newSt.Application(application.Name()) 3082 c.Assert(err, jc.ErrorIsNil) 3083 exportedOrigin := application.CharmOrigin() 3084 exportedOrigin.Channel.Track = "latest" 3085 c.Assert(importedApp.CharmOrigin(), gc.DeepEquals, exportedOrigin, gc.Commentf("obtained %s", pretty.Sprint(importedApp.CharmOrigin()))) 3086 } 3087 3088 func (s *MigrationImportSuite) TestApplicationFillInCharmOriginID(c *gc.C) { 3089 st := s.State 3090 // Add a application with charm settings, app config, and leadership settings. 3091 f := factory.NewFactory(st, s.StatePool) 3092 3093 // Add a application with charm settings, app config, and leadership settings. 3094 testCharm := f.MakeCharmV2(c, &factory.CharmParams{ 3095 Name: "snappass-test", // it has resources 3096 }) 3097 c.Assert(testCharm.Meta().Resources, gc.HasLen, 3) 3098 origin := &state.CharmOrigin{ 3099 Source: "charm-hub", 3100 Type: "charm", 3101 Revision: &charm.MustParseURL(testCharm.URL()).Revision, 3102 Channel: &state.Channel{ 3103 Risk: "edge", 3104 }, 3105 ID: "charm-hub-id", 3106 Hash: "charmhub-hash", 3107 Platform: &state.Platform{ 3108 Architecture: charm.MustParseURL(testCharm.URL()).Architecture, 3109 OS: "ubuntu", 3110 Channel: "12.10/stable", 3111 }, 3112 } 3113 appOne := f.MakeApplication(c, &factory.ApplicationParams{ 3114 Name: "one", 3115 Charm: testCharm, 3116 CharmOrigin: origin, 3117 }) 3118 originNoID := origin 3119 originNoID.ID = "" 3120 originNoID.Hash = "" 3121 appTwo := f.MakeApplication(c, &factory.ApplicationParams{ 3122 Name: "two", 3123 Charm: testCharm, 3124 CharmOrigin: origin, 3125 }) 3126 appThree := f.MakeApplication(c, &factory.ApplicationParams{ 3127 Name: "three", 3128 Charm: testCharm, 3129 CharmOrigin: origin, 3130 }) 3131 allApplications, err := s.State.AllApplications() 3132 c.Assert(err, jc.ErrorIsNil) 3133 c.Assert(allApplications, gc.HasLen, 3) 3134 3135 _, newSt := s.importModel(c, s.State) 3136 importedAppOne, err := newSt.Application(appOne.Name()) 3137 c.Assert(err, jc.ErrorIsNil) 3138 importedAppTwo, err := newSt.Application(appTwo.Name()) 3139 c.Assert(err, jc.ErrorIsNil) 3140 importedAppThree, err := newSt.Application(appThree.Name()) 3141 c.Assert(err, jc.ErrorIsNil) 3142 obtainedChOrigOne := importedAppOne.CharmOrigin() 3143 obtainedChOrigTwo := importedAppTwo.CharmOrigin() 3144 obtainedChOrigThree := importedAppThree.CharmOrigin() 3145 c.Assert(obtainedChOrigTwo.ID, gc.Equals, obtainedChOrigOne.ID) 3146 c.Assert(obtainedChOrigThree.ID, gc.Equals, obtainedChOrigOne.ID) 3147 } 3148 3149 func (s *MigrationImportSuite) TestSecrets(c *gc.C) { 3150 now := time.Now().UTC().Round(time.Second) 3151 next := now.Add(time.Minute).Round(time.Second).UTC() 3152 3153 backendStore := state.NewSecretBackends(s.State) 3154 backendID, err := backendStore.CreateSecretBackend(state.CreateSecretBackendParams{ 3155 Name: "myvault", 3156 BackendType: "vault", 3157 TokenRotateInterval: ptr(666 * time.Second), 3158 NextRotateTime: ptr(next), 3159 }) 3160 c.Assert(err, jc.ErrorIsNil) 3161 3162 store := state.NewSecrets(s.State) 3163 owner := s.Factory.MakeApplication(c, nil) 3164 uri := secrets.NewURI() 3165 expire := now.Add(2 * time.Hour).Round(time.Second).UTC() 3166 p := state.CreateSecretParams{ 3167 Version: 1, 3168 Owner: owner.Tag(), 3169 UpdateSecretParams: state.UpdateSecretParams{ 3170 LeaderToken: &fakeToken{}, 3171 RotatePolicy: ptr(secrets.RotateDaily), 3172 NextRotateTime: ptr(next), 3173 Description: ptr("my secret"), 3174 Label: ptr("foobar"), 3175 ExpireTime: ptr(expire), 3176 Params: nil, 3177 Data: map[string]string{"foo": "bar"}, 3178 }, 3179 } 3180 md, err := store.CreateSecret(uri, p) 3181 c.Assert(err, jc.ErrorIsNil) 3182 updateTime := time.Now().UTC().Round(time.Second) 3183 md, err = store.UpdateSecret(md.URI, state.UpdateSecretParams{ 3184 LeaderToken: &fakeToken{}, 3185 AutoPrune: ptr(true), 3186 ValueRef: &secrets.ValueRef{ 3187 BackendID: backendID, 3188 RevisionID: "rev-id", 3189 }, 3190 }) 3191 c.Assert(err, jc.ErrorIsNil) 3192 3193 err = s.State.GrantSecretAccess(uri, state.SecretAccessParams{ 3194 LeaderToken: &fakeToken{}, 3195 Scope: owner.Tag(), 3196 Subject: owner.Tag(), 3197 Role: secrets.RoleManage, 3198 }) 3199 c.Assert(err, jc.ErrorIsNil) 3200 3201 consumer := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 3202 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{ 3203 Name: "wordpress", 3204 }), 3205 }) 3206 err = s.State.SaveSecretConsumer(uri, consumer.Tag(), &secrets.SecretConsumerMetadata{ 3207 Label: "consumer label", 3208 CurrentRevision: 666, 3209 }) 3210 c.Assert(err, jc.ErrorIsNil) 3211 3212 _, err = s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 3213 Name: "remote-app", SourceModel: s.Model.ModelTag(), IsConsumerProxy: true}) 3214 c.Assert(err, jc.ErrorIsNil) 3215 remoteConsumer := names.NewApplicationTag("remote-app") 3216 err = s.State.SaveSecretRemoteConsumer(uri, remoteConsumer, &secrets.SecretConsumerMetadata{ 3217 CurrentRevision: 667, 3218 }) 3219 c.Assert(err, jc.ErrorIsNil) 3220 3221 backendRefCount, err := s.State.ReadBackendRefCount(backendID) 3222 c.Assert(err, jc.ErrorIsNil) 3223 c.Assert(backendRefCount, gc.Equals, 1) 3224 3225 err = s.Model.UpdateModelConfig(map[string]interface{}{config.SecretBackendKey: "myvault"}, nil) 3226 c.Assert(err, jc.ErrorIsNil) 3227 mCfg, err := s.Model.ModelConfig() 3228 c.Assert(err, jc.ErrorIsNil) 3229 c.Assert(mCfg.SecretBackend(), jc.DeepEquals, "myvault") 3230 3231 newModel, newSt := s.importModel(c, s.State) 3232 3233 mCfg, err = newModel.ModelConfig() 3234 c.Assert(err, jc.ErrorIsNil) 3235 c.Assert(mCfg.SecretBackend(), jc.DeepEquals, "myvault") 3236 3237 backendRefCount, err = s.State.ReadBackendRefCount(backendID) 3238 c.Assert(err, jc.ErrorIsNil) 3239 c.Assert(backendRefCount, gc.Equals, 2) 3240 3241 store = state.NewSecrets(newSt) 3242 all, err := store.ListSecrets(state.SecretsFilter{}) 3243 c.Assert(err, jc.ErrorIsNil) 3244 c.Assert(all, gc.HasLen, 1) 3245 c.Assert(all[0], jc.DeepEquals, md) 3246 3247 revs, err := store.ListSecretRevisions(md.URI) 3248 c.Assert(err, jc.ErrorIsNil) 3249 mc := jc.NewMultiChecker() 3250 mc.AddExpr(`_.CreateTime`, jc.Almost, jc.ExpectedValue) 3251 mc.AddExpr(`_.UpdateTime`, jc.Almost, jc.ExpectedValue) 3252 c.Assert(revs, mc, []*secrets.SecretRevisionMetadata{{ 3253 Revision: 1, 3254 ValueRef: nil, 3255 CreateTime: now, 3256 UpdateTime: updateTime, 3257 ExpireTime: &expire, 3258 }, { 3259 Revision: 2, 3260 ValueRef: &secrets.ValueRef{ 3261 BackendID: backendID, 3262 RevisionID: "rev-id", 3263 }, 3264 BackendName: ptr("myvault"), 3265 CreateTime: now, 3266 UpdateTime: now, 3267 }}) 3268 3269 access, err := newSt.SecretAccess(uri, owner.Tag()) 3270 c.Assert(err, jc.ErrorIsNil) 3271 c.Assert(access, gc.Equals, secrets.RoleManage) 3272 3273 info, err := newSt.GetSecretConsumer(uri, consumer.Tag()) 3274 c.Assert(err, jc.ErrorIsNil) 3275 c.Assert(info, jc.DeepEquals, &secrets.SecretConsumerMetadata{ 3276 Label: "consumer label", 3277 CurrentRevision: 666, 3278 LatestRevision: 2, 3279 }) 3280 3281 info, err = newSt.GetSecretRemoteConsumer(uri, remoteConsumer) 3282 c.Assert(err, jc.ErrorIsNil) 3283 c.Assert(info, jc.DeepEquals, &secrets.SecretConsumerMetadata{ 3284 CurrentRevision: 667, 3285 LatestRevision: 2, 3286 }) 3287 3288 backendRefCount, err = newSt.ReadBackendRefCount(backendID) 3289 c.Assert(err, jc.ErrorIsNil) 3290 c.Assert(backendRefCount, gc.Equals, 2) 3291 } 3292 3293 func (s *MigrationImportSuite) TestSecretsEnsureConsumerRevisionInfo(c *gc.C) { 3294 store := state.NewSecrets(s.State) 3295 owner := s.Factory.MakeApplication(c, nil) 3296 uri := secrets.NewURI() 3297 p := state.CreateSecretParams{ 3298 Version: 1, 3299 Owner: owner.Tag(), 3300 UpdateSecretParams: state.UpdateSecretParams{ 3301 LeaderToken: &fakeToken{}, 3302 RotatePolicy: ptr(secrets.RotateNever), 3303 Data: map[string]string{"foo": "bar"}, 3304 }, 3305 } 3306 md, err := store.CreateSecret(uri, p) 3307 c.Assert(err, jc.ErrorIsNil) 3308 3309 consumer := s.Factory.MakeApplication(c, &factory.ApplicationParams{ 3310 Charm: s.Factory.MakeCharm(c, &factory.CharmParams{ 3311 Name: "wordpress", 3312 }), 3313 }) 3314 err = s.State.SaveSecretConsumer(uri, consumer.Tag(), &secrets.SecretConsumerMetadata{ 3315 Label: "consumer label", 3316 CurrentRevision: 0, 3317 LatestRevision: 0, 3318 }) 3319 c.Assert(err, jc.ErrorIsNil) 3320 3321 _, newSt := s.importModel(c, s.State) 3322 3323 store = state.NewSecrets(newSt) 3324 all, err := store.ListSecrets(state.SecretsFilter{}) 3325 c.Assert(err, jc.ErrorIsNil) 3326 c.Assert(all, gc.HasLen, 1) 3327 c.Assert(all[0], jc.DeepEquals, md) 3328 3329 info, err := newSt.GetSecretConsumer(uri, consumer.Tag()) 3330 c.Assert(err, jc.ErrorIsNil) 3331 c.Assert(info, jc.DeepEquals, &secrets.SecretConsumerMetadata{ 3332 Label: "consumer label", 3333 CurrentRevision: 1, 3334 LatestRevision: 1, 3335 }) 3336 } 3337 3338 func (s *MigrationImportSuite) TestSecretsMissingBackend(c *gc.C) { 3339 store := state.NewSecrets(s.State) 3340 owner := s.Factory.MakeApplication(c, nil) 3341 uri := secrets.NewURI() 3342 3343 backendStore := state.NewSecretBackends(s.State) 3344 _, err := backendStore.CreateSecretBackend(state.CreateSecretBackendParams{ 3345 ID: "backend-id", 3346 Name: "foo", 3347 BackendType: "vault", 3348 }) 3349 c.Assert(err, jc.ErrorIsNil) 3350 3351 p := state.CreateSecretParams{ 3352 Version: 1, 3353 Owner: owner.Tag(), 3354 UpdateSecretParams: state.UpdateSecretParams{ 3355 LeaderToken: &fakeToken{}, 3356 ValueRef: &secrets.ValueRef{ 3357 BackendID: "backend-id", 3358 RevisionID: "rev-id", 3359 }, 3360 }, 3361 } 3362 _, err = store.CreateSecret(uri, p) 3363 c.Assert(err, jc.ErrorIsNil) 3364 3365 out, err := s.State.Export(map[string]string{}) 3366 c.Assert(err, jc.ErrorIsNil) 3367 3368 err = backendStore.DeleteSecretBackend("foo", true) 3369 c.Assert(err, jc.ErrorIsNil) 3370 3371 uuid := utils.MustNewUUID().String() 3372 in := newModel(out, uuid, "new") 3373 _, _, err = s.Controller.Import(in) 3374 c.Assert(err, gc.ErrorMatches, "secrets: target controller does not have all required secret backends set up") 3375 } 3376 3377 func (s *MigrationImportSuite) TestDefaultSecretBackend(c *gc.C) { 3378 testModel, err := s.State.Export(map[string]string{}) 3379 c.Assert(err, jc.ErrorIsNil) 3380 3381 newConfig := testModel.Config() 3382 newConfig["uuid"] = "aabbccdd-1234-8765-abcd-0123456789ab" 3383 newConfig["name"] = "something-new" 3384 delete(newConfig, "secret-backend") 3385 importModel := description.NewModel(description.ModelArgs{ 3386 Type: string(state.ModelTypeIAAS), 3387 Owner: testModel.Owner(), 3388 Config: newConfig, 3389 EnvironVersion: testModel.EnvironVersion(), 3390 Blocks: testModel.Blocks(), 3391 Cloud: testModel.Cloud(), 3392 CloudRegion: testModel.CloudRegion(), 3393 }) 3394 imported, newSt, err := s.Controller.Import(importModel) 3395 c.Assert(err, jc.ErrorIsNil) 3396 defer func() { _ = newSt.Close() }() 3397 3398 importedCfg, err := imported.Config() 3399 c.Assert(err, jc.ErrorIsNil) 3400 c.Assert(importedCfg.SecretBackend(), gc.Equals, "auto") 3401 } 3402 3403 func (s *MigrationImportSuite) TestApplicationWithProvisioningState(c *gc.C) { 3404 caasSt := s.Factory.MakeCAASModel(c, nil) 3405 s.AddCleanup(func(_ *gc.C) { caasSt.Close() }) 3406 3407 cons := constraints.MustParse("arch=amd64 mem=8G") 3408 platform := &state.Platform{ 3409 Architecture: arch.DefaultArchitecture, 3410 OS: "ubuntu", 3411 Channel: "20.04", 3412 } 3413 testCharm, application, _ := s.setupSourceApplications(c, caasSt, cons, platform, false) 3414 3415 err := application.SetScale(1, 0, true) 3416 c.Assert(err, jc.ErrorIsNil) 3417 err = application.SetProvisioningState(state.ApplicationProvisioningState{ 3418 Scaling: true, 3419 ScaleTarget: 1, 3420 }) 3421 c.Assert(err, jc.ErrorIsNil) 3422 3423 allApplications, err := caasSt.AllApplications() 3424 c.Assert(err, jc.ErrorIsNil) 3425 c.Assert(allApplications, gc.HasLen, 1) 3426 3427 _, newSt := s.importModel(c, caasSt) 3428 // Manually copy across the charm from the old model 3429 // as it's normally done later. 3430 f := factory.NewFactory(newSt, s.StatePool) 3431 f.MakeCharm(c, &factory.CharmParams{ 3432 Name: "starsay", // it has resources 3433 Series: "kubernetes", 3434 URL: testCharm.URL(), 3435 Revision: strconv.Itoa(testCharm.Revision()), 3436 }) 3437 importedApplication, err := newSt.Application(application.Name()) 3438 c.Assert(err, jc.ErrorIsNil) 3439 3440 c.Assert(importedApplication.ProvisioningState(), jc.DeepEquals, &state.ApplicationProvisioningState{ 3441 Scaling: true, 3442 ScaleTarget: 1, 3443 }) 3444 } 3445 3446 // newModel replaces the uuid and name of the config attributes so we 3447 // can use all the other data to validate imports. An owner and name of the 3448 // model are unique together in a controller. 3449 // Also, optionally overwrite the return value of certain methods 3450 func newModel(m description.Model, uuid, name string) *mockModel { 3451 return &mockModel{Model: m, uuid: uuid, name: name} 3452 } 3453 3454 type mockModel struct { 3455 description.Model 3456 uuid string 3457 name string 3458 fwRules []description.FirewallRule 3459 } 3460 3461 func (m *mockModel) Tag() names.ModelTag { 3462 return names.NewModelTag(m.uuid) 3463 } 3464 3465 func (m *mockModel) Config() map[string]interface{} { 3466 c := m.Model.Config() 3467 c["uuid"] = m.uuid 3468 c["name"] = m.name 3469 return c 3470 } 3471 3472 func (m *mockModel) FirewallRules() []description.FirewallRule { 3473 if m.fwRules == nil { 3474 return m.Model.FirewallRules() 3475 } 3476 return m.fwRules 3477 } 3478 3479 // swapModel will swap the order of the applications appearing in the 3480 // model. 3481 type swapModel struct { 3482 description.Model 3483 c *gc.C 3484 } 3485 3486 func (m swapModel) Applications() []description.Application { 3487 values := m.Model.Applications() 3488 m.c.Assert(len(values), gc.Equals, 2) 3489 values[0], values[1] = values[1], values[0] 3490 return values 3491 }