github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/migration_export.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 "strings" 9 "time" 10 11 "github.com/juju/charm/v12" 12 "github.com/juju/collections/set" 13 "github.com/juju/description/v5" 14 "github.com/juju/errors" 15 "github.com/juju/featureflag" 16 "github.com/juju/loggo" 17 "github.com/juju/mgo/v3/bson" 18 "github.com/juju/names/v5" 19 20 "github.com/juju/juju/core/arch" 21 corecharm "github.com/juju/juju/core/charm" 22 "github.com/juju/juju/core/container" 23 "github.com/juju/juju/core/crossmodel" 24 "github.com/juju/juju/core/network" 25 "github.com/juju/juju/core/payloads" 26 "github.com/juju/juju/core/resources" 27 "github.com/juju/juju/core/secrets" 28 "github.com/juju/juju/feature" 29 secretsprovider "github.com/juju/juju/secrets/provider" 30 "github.com/juju/juju/state/migrations" 31 "github.com/juju/juju/storage/poolmanager" 32 ) 33 34 // The following exporter type is being refactored. This is to better model the 35 // dependencies for creating the exported yaml and to correctly provide us to 36 // unit tests at the right level of work. Rather than create integration tests 37 // at the "unit" level. 38 // 39 // All exporting migrations have been currently moved to `state/migrations`. 40 // Each provide their own type that allows them to execute a migration step 41 // before return if successful or not via an error. The step resembles the 42 // visitor pattern for good reason, as it allows us to safely model what is 43 // required at a type level and type safety level. Everything is typed all the 44 // way down. We can then create mocks for each one independently from other 45 // migration steps (see examples). 46 // 47 // As this is in its infancy, there are intermediary steps. Each export type 48 // creates its own StateExportMigration. In the future, there will be only 49 // one and each migration step will add itself to that and Run for completion. 50 // 51 // Whilst we're creating these steps, it is expected to create the unit tests 52 // and supplement all of these tests with existing tests, to ensure that no 53 // gaps are missing. In the future the integration tests should be replaced with 54 // the new shell tests to ensure a full end to end test is performed. 55 56 const maxStatusHistoryEntries = 20 57 58 // ExportConfig allows certain aspects of the model to be skipped 59 // during the export. The intent of this is to be able to get a partial 60 // export to support other API calls, like status. 61 type ExportConfig struct { 62 IgnoreIncompleteModel bool 63 SkipActions bool 64 SkipAnnotations bool 65 SkipCloudImageMetadata bool 66 SkipCredentials bool 67 SkipIPAddresses bool 68 SkipSettings bool 69 SkipSSHHostKeys bool 70 SkipStatusHistory bool 71 SkipLinkLayerDevices bool 72 SkipUnitAgentBinaries bool 73 SkipMachineAgentBinaries bool 74 SkipRelationData bool 75 SkipInstanceData bool 76 SkipApplicationOffers bool 77 SkipOfferConnections bool 78 SkipExternalControllers bool 79 SkipSecrets bool 80 } 81 82 // ExportPartial the current model for the State optionally skipping 83 // aspects as defined by the ExportConfig. 84 func (st *State) ExportPartial(cfg ExportConfig) (description.Model, error) { 85 return st.exportImpl(cfg, map[string]string{}) 86 } 87 88 // Export the current model for the State. 89 func (st *State) Export(leaders map[string]string) (description.Model, error) { 90 return st.exportImpl(ExportConfig{}, leaders) 91 } 92 93 func (st *State) exportImpl(cfg ExportConfig, leaders map[string]string) (description.Model, error) { 94 dbModel, err := st.Model() 95 if err != nil { 96 return nil, errors.Trace(err) 97 } 98 export := exporter{ 99 st: st, 100 cfg: cfg, 101 dbModel: dbModel, 102 logger: loggo.GetLogger("juju.state.export-model"), 103 } 104 if err := export.readAllStatuses(); err != nil { 105 return nil, errors.Annotate(err, "reading statuses") 106 } 107 if err := export.readAllStatusHistory(); err != nil { 108 return nil, errors.Trace(err) 109 } 110 if err := export.readAllSettings(); err != nil { 111 return nil, errors.Trace(err) 112 } 113 if err := export.readAllStorageConstraints(); err != nil { 114 return nil, errors.Trace(err) 115 } 116 if err := export.readAllAnnotations(); err != nil { 117 return nil, errors.Trace(err) 118 } 119 if err := export.readAllConstraints(); err != nil { 120 return nil, errors.Trace(err) 121 } 122 123 modelConfig, found := export.modelSettings[modelGlobalKey] 124 if !found && !cfg.SkipSettings { 125 return nil, errors.New("missing model config") 126 } 127 delete(export.modelSettings, modelGlobalKey) 128 129 blocks, err := export.readBlocks() 130 if err != nil { 131 return nil, errors.Trace(err) 132 } 133 134 args := description.ModelArgs{ 135 Type: string(dbModel.Type()), 136 Cloud: dbModel.CloudName(), 137 CloudRegion: dbModel.CloudRegion(), 138 Owner: dbModel.Owner(), 139 Config: modelConfig.Settings, 140 PasswordHash: dbModel.doc.PasswordHash, 141 LatestToolsVersion: dbModel.LatestToolsVersion(), 142 EnvironVersion: dbModel.EnvironVersion(), 143 Blocks: blocks, 144 } 145 if args.SecretBackendID, err = export.secretBackendID(); err != nil { 146 return nil, errors.Trace(err) 147 } 148 export.model = description.NewModel(args) 149 if credsTag, credsSet := dbModel.CloudCredentialTag(); credsSet && !cfg.SkipCredentials { 150 creds, err := st.CloudCredential(credsTag) 151 if err != nil { 152 return nil, errors.Trace(err) 153 } 154 export.model.SetCloudCredential(description.CloudCredentialArgs{ 155 Owner: credsTag.Owner(), 156 Cloud: credsTag.Cloud(), 157 Name: credsTag.Name(), 158 AuthType: creds.AuthType, 159 Attributes: creds.Attributes, 160 }) 161 } 162 modelKey := dbModel.globalKey() 163 export.model.SetAnnotations(export.getAnnotations(modelKey)) 164 if err := export.sequences(); err != nil { 165 return nil, errors.Trace(err) 166 } 167 constraintsArgs, err := export.constraintsArgs(modelKey) 168 if err != nil { 169 return nil, errors.Trace(err) 170 } 171 export.model.SetConstraints(constraintsArgs) 172 if err := export.modelStatus(); err != nil { 173 return nil, errors.Trace(err) 174 } 175 if err := export.modelUsers(); err != nil { 176 return nil, errors.Trace(err) 177 } 178 if err := export.machines(); err != nil { 179 return nil, errors.Trace(err) 180 } 181 if err := export.applications(leaders); err != nil { 182 return nil, errors.Trace(err) 183 } 184 if err := export.remoteApplications(); err != nil { 185 return nil, errors.Trace(err) 186 } 187 if err := export.relations(); err != nil { 188 return nil, errors.Trace(err) 189 } 190 if err := export.remoteEntities(); err != nil { 191 return nil, errors.Trace(err) 192 } 193 if err := export.offerConnections(); err != nil { 194 return nil, errors.Trace(err) 195 } 196 if err := export.relationNetworks(); err != nil { 197 return nil, errors.Trace(err) 198 } 199 if err := export.spaces(); err != nil { 200 return nil, errors.Trace(err) 201 } 202 if err := export.subnets(); err != nil { 203 return nil, errors.Trace(err) 204 } 205 if err := export.ipAddresses(); err != nil { 206 return nil, errors.Trace(err) 207 } 208 if err := export.linklayerdevices(); err != nil { 209 return nil, errors.Trace(err) 210 } 211 if err := export.sshHostKeys(); err != nil { 212 return nil, errors.Trace(err) 213 } 214 if err := export.actions(); err != nil { 215 return nil, errors.Trace(err) 216 } 217 if err := export.operations(); err != nil { 218 return nil, errors.Trace(err) 219 } 220 if err := export.cloudimagemetadata(); err != nil { 221 return nil, errors.Trace(err) 222 } 223 if err := export.storage(); err != nil { 224 return nil, errors.Trace(err) 225 } 226 if err := export.externalControllers(); err != nil { 227 return nil, errors.Trace(err) 228 } 229 if err := export.secrets(); err != nil { 230 return nil, errors.Trace(err) 231 } 232 if err := export.remoteSecrets(); err != nil { 233 return nil, errors.Trace(err) 234 } 235 236 // If we are doing a partial export, it doesn't really make sense 237 // to validate the model. 238 fullExport := ExportConfig{} 239 if cfg == fullExport { 240 if err := export.model.Validate(); err != nil { 241 return nil, errors.Trace(err) 242 } 243 } 244 245 export.model.SetSLA(dbModel.SLALevel(), dbModel.SLAOwner(), string(dbModel.SLACredential())) 246 export.model.SetMeterStatus(dbModel.MeterStatus().Code.String(), dbModel.MeterStatus().Info) 247 248 if featureflag.Enabled(feature.StrictMigration) { 249 if err := export.checkUnexportedValues(); err != nil { 250 return nil, errors.Trace(err) 251 } 252 } 253 254 return export.model, nil 255 } 256 257 // ExportStateMigration defines a migration for exporting various entities into 258 // a destination description model from the source state. 259 // It accumulates a series of migrations to run at a later time. 260 // Running the state migration visits all the migrations and exits upon seeing 261 // the first error from the migration. 262 type ExportStateMigration struct { 263 src *State 264 dst description.Model 265 exporter *exporter 266 migrations []func() error 267 } 268 269 // Add adds a migration to execute at a later time 270 // Return error from the addition will cause the Run to terminate early. 271 func (m *ExportStateMigration) Add(f func() error) { 272 m.migrations = append(m.migrations, f) 273 } 274 275 // Run executes all the migrations required to be run. 276 func (m *ExportStateMigration) Run() error { 277 for _, f := range m.migrations { 278 if err := f(); err != nil { 279 return errors.Trace(err) 280 } 281 } 282 return nil 283 } 284 285 type exporter struct { 286 cfg ExportConfig 287 st *State 288 dbModel *Model 289 model description.Model 290 logger loggo.Logger 291 292 annotations map[string]annotatorDoc 293 constraints map[string]bson.M 294 modelSettings map[string]settingsDoc 295 modelStorageConstraints map[string]storageConstraintsDoc 296 status map[string]bson.M 297 statusHistory map[string][]historicalStatusDoc 298 // Map of application name to units. Populated as part 299 // of the applications export. 300 units map[string][]*Unit 301 } 302 303 func (e *exporter) sequences() error { 304 sequences, err := e.st.Sequences() 305 if err != nil { 306 return errors.Trace(err) 307 } 308 309 for name, value := range sequences { 310 e.model.SetSequence(name, value) 311 } 312 return nil 313 } 314 315 func (e *exporter) readBlocks() (map[string]string, error) { 316 blocks, closer := e.st.db().GetCollection(blocksC) 317 defer closer() 318 319 var docs []blockDoc 320 if err := blocks.Find(nil).All(&docs); err != nil { 321 return nil, errors.Trace(err) 322 } 323 324 result := make(map[string]string) 325 for _, doc := range docs { 326 // We don't care about the id, uuid, or tag. 327 // The uuid and tag both refer to the model uuid, and the 328 // id is opaque - even though it is sequence generated. 329 result[doc.Type.MigrationValue()] = doc.Message 330 } 331 return result, nil 332 } 333 334 func (e *exporter) modelStatus() error { 335 statusArgs, err := e.statusArgs(modelGlobalKey) 336 if err != nil { 337 return errors.Annotatef(err, "status for model") 338 } 339 340 e.model.SetStatus(statusArgs) 341 e.model.SetStatusHistory(e.statusHistoryArgs(modelGlobalKey)) 342 return nil 343 } 344 345 func (e *exporter) modelUsers() error { 346 users, err := e.dbModel.Users() 347 if err != nil { 348 return errors.Trace(err) 349 } 350 lastConnections, err := e.readLastConnectionTimes() 351 if err != nil { 352 return errors.Trace(err) 353 } 354 for _, user := range users { 355 lastConn := lastConnections[strings.ToLower(user.UserName)] 356 arg := description.UserArgs{ 357 Name: user.UserTag, 358 DisplayName: user.DisplayName, 359 CreatedBy: user.CreatedBy, 360 DateCreated: user.DateCreated, 361 LastConnection: lastConn, 362 Access: string(user.Access), 363 } 364 e.model.AddUser(arg) 365 } 366 return nil 367 } 368 369 func (e *exporter) machines() error { 370 machines, err := e.st.AllMachines() 371 if err != nil { 372 return errors.Trace(err) 373 } 374 e.logger.Debugf("found %d machines", len(machines)) 375 376 instances, err := e.loadMachineInstanceData() 377 if err != nil { 378 return errors.Trace(err) 379 } 380 blockDevices, err := e.loadMachineBlockDevices() 381 if err != nil { 382 return errors.Trace(err) 383 } 384 openedPorts, err := e.loadOpenedPortRangesForMachine() 385 if err != nil { 386 return errors.Trace(err) 387 } 388 389 // We are iterating through a flat list of machines, but the migration 390 // model stores the nesting. The AllMachines method assures us that the 391 // machines are returned in an order so the parent will always before 392 // any children. 393 machineMap := make(map[string]description.Machine) 394 395 for _, machine := range machines { 396 e.logger.Debugf("export machine %s", machine.Id()) 397 398 var exParent description.Machine 399 if parentId := container.ParentId(machine.Id()); parentId != "" { 400 var found bool 401 exParent, found = machineMap[parentId] 402 if !found { 403 return errors.Errorf("machine %s missing parent", machine.Id()) 404 } 405 } 406 407 exMachine, err := e.newMachine(exParent, machine, instances, openedPorts, blockDevices) 408 if err != nil { 409 return errors.Trace(err) 410 } 411 machineMap[machine.Id()] = exMachine 412 } 413 414 return nil 415 } 416 417 func (e *exporter) loadOpenedPortRangesForMachine() (map[string]*machinePortRanges, error) { 418 mprs, err := getOpenedPortRangesForAllMachines(e.st) 419 if err != nil { 420 return nil, errors.Annotate(err, "opened port ranges") 421 } 422 423 openedPortsByMachine := make(map[string]*machinePortRanges) 424 for _, mpr := range mprs { 425 openedPortsByMachine[mpr.MachineID()] = mpr 426 } 427 428 e.logger.Debugf("found %d openedPorts docs", len(openedPortsByMachine)) 429 return openedPortsByMachine, nil 430 } 431 432 func (e *exporter) loadOpenedPortRangesForApplication() (map[string]*applicationPortRanges, error) { 433 mprs, err := getOpenedApplicationPortRangesForAllApplications(e.st) 434 if err != nil { 435 return nil, errors.Annotate(err, "opened port ranges") 436 } 437 438 openedPortsByApplication := make(map[string]*applicationPortRanges) 439 for _, mpr := range mprs { 440 openedPortsByApplication[mpr.ApplicationName()] = mpr 441 } 442 443 e.logger.Debugf("found %d openedPorts docs", len(openedPortsByApplication)) 444 return openedPortsByApplication, nil 445 } 446 447 func (e *exporter) loadMachineInstanceData() (map[string]instanceData, error) { 448 instanceDataCollection, closer := e.st.db().GetCollection(instanceDataC) 449 defer closer() 450 451 var instData []instanceData 452 instances := make(map[string]instanceData) 453 if err := instanceDataCollection.Find(nil).All(&instData); err != nil { 454 return nil, errors.Annotate(err, "instance data") 455 } 456 e.logger.Debugf("found %d instanceData", len(instData)) 457 for _, data := range instData { 458 instances[data.MachineId] = data 459 } 460 return instances, nil 461 } 462 463 func (e *exporter) loadMachineBlockDevices() (map[string][]BlockDeviceInfo, error) { 464 coll, closer := e.st.db().GetCollection(blockDevicesC) 465 defer closer() 466 467 var deviceData []blockDevicesDoc 468 result := make(map[string][]BlockDeviceInfo) 469 if err := coll.Find(nil).All(&deviceData); err != nil { 470 return nil, errors.Annotate(err, "block devices") 471 } 472 e.logger.Debugf("found %d block device records", len(deviceData)) 473 for _, data := range deviceData { 474 result[data.Machine] = data.BlockDevices 475 } 476 return result, nil 477 } 478 479 func (e *exporter) newMachine(exParent description.Machine, machine *Machine, instances map[string]instanceData, portsData map[string]*machinePortRanges, blockDevices map[string][]BlockDeviceInfo) (description.Machine, error) { 480 args := description.MachineArgs{ 481 Id: machine.MachineTag(), 482 Nonce: machine.doc.Nonce, 483 PasswordHash: machine.doc.PasswordHash, 484 Placement: machine.doc.Placement, 485 Base: machine.doc.Base.String(), 486 ContainerType: machine.doc.ContainerType, 487 } 488 489 if supported, ok := machine.SupportedContainers(); ok { 490 containers := make([]string, len(supported)) 491 for i, containerType := range supported { 492 containers[i] = string(containerType) 493 } 494 args.SupportedContainers = &containers 495 } 496 497 for _, job := range machine.Jobs() { 498 args.Jobs = append(args.Jobs, job.MigrationValue()) 499 } 500 501 // A null value means that we don't yet know which containers 502 // are supported. An empty slice means 'no containers are supported'. 503 var exMachine description.Machine 504 if exParent == nil { 505 exMachine = e.model.AddMachine(args) 506 } else { 507 exMachine = exParent.AddContainer(args) 508 } 509 exMachine.SetAddresses( 510 e.newAddressArgsSlice(machine.doc.MachineAddresses), 511 e.newAddressArgsSlice(machine.doc.Addresses)) 512 exMachine.SetPreferredAddresses( 513 e.newAddressArgs(machine.doc.PreferredPublicAddress), 514 e.newAddressArgs(machine.doc.PreferredPrivateAddress)) 515 516 // We fully expect the machine to have tools set, and that there is 517 // some instance data. 518 if !e.cfg.SkipInstanceData { 519 instData, found := instances[machine.doc.Id] 520 if !found && !e.cfg.IgnoreIncompleteModel { 521 return nil, errors.NotValidf("missing instance data for machine %s", machine.Id()) 522 } 523 if found { 524 exMachine.SetInstance(e.newCloudInstanceArgs(instData)) 525 instance := exMachine.Instance() 526 instanceKey := machine.globalInstanceKey() 527 statusArgs, err := e.statusArgs(instanceKey) 528 if err != nil { 529 return nil, errors.Annotatef(err, "status for machine instance %s", machine.Id()) 530 } 531 instance.SetStatus(statusArgs) 532 instance.SetStatusHistory(e.statusHistoryArgs(instanceKey)) 533 // Extract the modification status from the status dataset 534 modificationInstanceKey := machine.globalModificationKey() 535 modificationStatusArgs, err := e.statusArgs(modificationInstanceKey) 536 if err != nil { 537 return nil, errors.Annotatef(err, "modification status for machine instance %s", machine.Id()) 538 } 539 instance.SetModificationStatus(modificationStatusArgs) 540 } 541 } 542 543 // We don't rely on devices being there. If they aren't, we get an empty slice, 544 // which is fine to iterate over with range. 545 for _, device := range blockDevices[machine.doc.Id] { 546 exMachine.AddBlockDevice(description.BlockDeviceArgs{ 547 Name: device.DeviceName, 548 Links: device.DeviceLinks, 549 Label: device.Label, 550 UUID: device.UUID, 551 HardwareID: device.HardwareId, 552 WWN: device.WWN, 553 BusAddress: device.BusAddress, 554 Size: device.Size, 555 FilesystemType: device.FilesystemType, 556 InUse: device.InUse, 557 MountPoint: device.MountPoint, 558 }) 559 } 560 561 // Find the current machine status. 562 globalKey := machine.globalKey() 563 statusArgs, err := e.statusArgs(globalKey) 564 if err != nil { 565 return nil, errors.Annotatef(err, "status for machine %s", machine.Id()) 566 } 567 exMachine.SetStatus(statusArgs) 568 exMachine.SetStatusHistory(e.statusHistoryArgs(globalKey)) 569 570 if !e.cfg.SkipMachineAgentBinaries { 571 tools, err := machine.AgentTools() 572 if err != nil && !e.cfg.IgnoreIncompleteModel { 573 // This means the tools aren't set, but they should be. 574 return nil, errors.Trace(err) 575 } 576 if err == nil { 577 exMachine.SetTools(description.AgentToolsArgs{ 578 Version: tools.Version, 579 URL: tools.URL, 580 SHA256: tools.SHA256, 581 Size: tools.Size, 582 }) 583 } 584 } 585 586 for _, args := range e.openedPortRangesArgsForMachine(machine.Id(), portsData) { 587 exMachine.AddOpenedPortRange(args) 588 } 589 590 exMachine.SetAnnotations(e.getAnnotations(globalKey)) 591 592 constraintsArgs, err := e.constraintsArgs(globalKey) 593 if err != nil { 594 return nil, errors.Trace(err) 595 } 596 exMachine.SetConstraints(constraintsArgs) 597 598 return exMachine, nil 599 } 600 601 func (e *exporter) openedPortRangesArgsForMachine(machineID string, portsData map[string]*machinePortRanges) []description.OpenedPortRangeArgs { 602 if portsData[machineID] == nil { 603 return nil 604 } 605 606 var result []description.OpenedPortRangeArgs 607 for unitName, unitPorts := range portsData[machineID].ByUnit() { 608 for endpointName, portRanges := range unitPorts.ByEndpoint() { 609 for _, pr := range portRanges { 610 result = append(result, description.OpenedPortRangeArgs{ 611 UnitName: unitName, 612 EndpointName: endpointName, 613 FromPort: pr.FromPort, 614 ToPort: pr.ToPort, 615 Protocol: pr.Protocol, 616 }) 617 } 618 } 619 } 620 return result 621 } 622 623 func (e *exporter) newAddressArgsSlice(a []address) []description.AddressArgs { 624 result := make([]description.AddressArgs, len(a)) 625 for i, addr := range a { 626 result[i] = e.newAddressArgs(addr) 627 } 628 return result 629 } 630 631 func (e *exporter) newAddressArgs(a address) description.AddressArgs { 632 return description.AddressArgs{ 633 Value: a.Value, 634 Type: a.AddressType, 635 Scope: a.Scope, 636 Origin: a.Origin, 637 SpaceID: a.SpaceID, 638 } 639 } 640 641 func (e *exporter) newCloudInstanceArgs(data instanceData) description.CloudInstanceArgs { 642 inst := description.CloudInstanceArgs{ 643 InstanceId: string(data.InstanceId), 644 DisplayName: data.DisplayName, 645 } 646 if data.Arch != nil { 647 inst.Architecture = *data.Arch 648 } 649 if data.Mem != nil { 650 inst.Memory = *data.Mem 651 } 652 if data.RootDisk != nil { 653 inst.RootDisk = *data.RootDisk 654 } 655 if data.RootDiskSource != nil { 656 inst.RootDiskSource = *data.RootDiskSource 657 } 658 if data.CpuCores != nil { 659 inst.CpuCores = *data.CpuCores 660 } 661 if data.CpuPower != nil { 662 inst.CpuPower = *data.CpuPower 663 } 664 if data.Tags != nil { 665 inst.Tags = *data.Tags 666 } 667 if data.AvailZone != nil { 668 inst.AvailabilityZone = *data.AvailZone 669 } 670 if data.VirtType != nil { 671 inst.VirtType = *data.VirtType 672 } 673 if len(data.CharmProfiles) > 0 { 674 inst.CharmProfiles = data.CharmProfiles 675 } 676 return inst 677 } 678 679 func (e *exporter) applications(leaders map[string]string) error { 680 applications, err := e.st.AllApplications() 681 if err != nil { 682 return errors.Trace(err) 683 } 684 e.logger.Debugf("found %d applications", len(applications)) 685 686 e.units, err = e.readAllUnits() 687 if err != nil { 688 return errors.Trace(err) 689 } 690 691 meterStatus, err := e.readAllMeterStatus() 692 if err != nil { 693 return errors.Trace(err) 694 } 695 696 bindings, err := e.readAllEndpointBindings() 697 if err != nil { 698 return errors.Trace(err) 699 } 700 701 payloads, err := e.readAllPayloads() 702 if err != nil { 703 return errors.Trace(err) 704 } 705 706 podSpecs, err := e.readAllPodSpecs() 707 if err != nil { 708 return errors.Trace(err) 709 } 710 cloudServices, err := e.readAllCloudServices() 711 if err != nil { 712 return errors.Trace(err) 713 } 714 cloudContainers, err := e.readAllCloudContainers() 715 if err != nil { 716 return errors.Trace(err) 717 } 718 719 resourcesSt := e.st.Resources() 720 721 appOfferMap, err := e.groupOffersByApplicationName() 722 if err != nil { 723 return errors.Trace(err) 724 } 725 726 openedPorts, err := e.loadOpenedPortRangesForApplication() 727 if err != nil { 728 return errors.Trace(err) 729 } 730 731 for _, application := range applications { 732 applicationUnits := e.units[application.Name()] 733 resources, err := resourcesSt.ListResources(application.Name()) 734 if err != nil { 735 return errors.Trace(err) 736 } 737 appCtx := addApplicationContext{ 738 application: application, 739 units: applicationUnits, 740 meterStatus: meterStatus, 741 podSpecs: podSpecs, 742 cloudServices: cloudServices, 743 cloudContainers: cloudContainers, 744 payloads: payloads, 745 resources: resources, 746 endpoingBindings: bindings, 747 leader: leaders[application.Name()], 748 portsData: openedPorts, 749 } 750 751 if appOfferMap != nil { 752 appCtx.offers = appOfferMap[application.Name()] 753 } 754 755 if err := e.addApplication(appCtx); err != nil { 756 return errors.Trace(err) 757 } 758 759 } 760 return nil 761 } 762 763 func (e *exporter) readAllStorageConstraints() error { 764 coll, closer := e.st.db().GetCollection(storageConstraintsC) 765 defer closer() 766 767 storageConstraints := make(map[string]storageConstraintsDoc) 768 var doc storageConstraintsDoc 769 iter := coll.Find(nil).Iter() 770 defer func() { _ = iter.Close() }() 771 for iter.Next(&doc) { 772 storageConstraints[e.st.localID(doc.DocID)] = doc 773 } 774 if err := iter.Close(); err != nil { 775 return errors.Annotate(err, "failed to read storage constraints") 776 } 777 e.logger.Debugf("read %d storage constraint documents", len(storageConstraints)) 778 e.modelStorageConstraints = storageConstraints 779 return nil 780 } 781 782 func (e *exporter) storageConstraints(doc storageConstraintsDoc) map[string]description.StorageDirectiveArgs { 783 result := make(map[string]description.StorageDirectiveArgs) 784 for key, value := range doc.Constraints { 785 result[key] = description.StorageDirectiveArgs{ 786 Pool: value.Pool, 787 Size: value.Size, 788 Count: value.Count, 789 } 790 } 791 return result 792 } 793 794 func (e *exporter) readAllPayloads() (map[string][]payloads.FullPayloadInfo, error) { 795 result := make(map[string][]payloads.FullPayloadInfo) 796 all, err := ModelPayloads{db: e.st.database}.ListAll() 797 if err != nil { 798 return nil, errors.Trace(err) 799 } 800 for _, pl := range all { 801 result[pl.Unit] = append(result[pl.Unit], pl) 802 } 803 return result, nil 804 } 805 806 type addApplicationContext struct { 807 application *Application 808 units []*Unit 809 meterStatus map[string]*meterStatusDoc 810 leader string 811 payloads map[string][]payloads.FullPayloadInfo 812 resources resources.ApplicationResources 813 endpoingBindings map[string]bindingsMap 814 portsData map[string]*applicationPortRanges 815 816 // CAAS 817 podSpecs map[string]string 818 cloudServices map[string]*cloudServiceDoc 819 cloudContainers map[string]*cloudContainerDoc 820 821 // Offers 822 offers []*crossmodel.ApplicationOffer 823 } 824 825 func (e *exporter) addApplication(ctx addApplicationContext) error { 826 application := ctx.application 827 appName := application.Name() 828 globalKey := application.globalKey() 829 charmConfigKey := application.charmConfigKey() 830 appConfigKey := application.applicationConfigKey() 831 leadershipKey := leadershipSettingsKey(appName) 832 storageConstraintsKey := application.storageConstraintsKey() 833 834 var charmConfig map[string]interface{} 835 applicationCharmSettingsDoc, found := e.modelSettings[charmConfigKey] 836 if !found && !e.cfg.SkipSettings && !e.cfg.IgnoreIncompleteModel { 837 return errors.Errorf("missing charm settings for application %q", appName) 838 } 839 if found { 840 charmConfig = applicationCharmSettingsDoc.Settings 841 } 842 delete(e.modelSettings, charmConfigKey) 843 844 var applicationConfig map[string]interface{} 845 applicationConfigDoc, found := e.modelSettings[appConfigKey] 846 if !found && !e.cfg.SkipSettings && !e.cfg.IgnoreIncompleteModel { 847 return errors.Errorf("missing config for application %q", appName) 848 } 849 if found { 850 applicationConfig = applicationConfigDoc.Settings 851 } 852 delete(e.modelSettings, appConfigKey) 853 854 var leadershipSettings map[string]interface{} 855 leadershipSettingsDoc, found := e.modelSettings[leadershipKey] 856 if !found && !e.cfg.SkipSettings && !e.cfg.IgnoreIncompleteModel { 857 return errors.Errorf("missing leadership settings for application %q", appName) 858 } 859 if found { 860 leadershipSettings = leadershipSettingsDoc.Settings 861 } 862 delete(e.modelSettings, leadershipKey) 863 864 args := description.ApplicationArgs{ 865 Tag: application.ApplicationTag(), 866 Type: e.model.Type(), 867 Subordinate: application.doc.Subordinate, 868 CharmURL: *application.doc.CharmURL, 869 CharmModifiedVersion: application.doc.CharmModifiedVersion, 870 ForceCharm: application.doc.ForceCharm, 871 Exposed: application.doc.Exposed, 872 PasswordHash: application.doc.PasswordHash, 873 Placement: application.doc.Placement, 874 HasResources: application.doc.HasResources, 875 DesiredScale: application.doc.DesiredScale, 876 MinUnits: application.doc.MinUnits, 877 EndpointBindings: map[string]string(ctx.endpoingBindings[globalKey]), 878 ApplicationConfig: applicationConfig, 879 CharmConfig: charmConfig, 880 Leader: ctx.leader, 881 LeadershipSettings: leadershipSettings, 882 MetricsCredentials: application.doc.MetricCredentials, 883 PodSpec: ctx.podSpecs[application.globalKey()], 884 } 885 886 if cloudService, found := ctx.cloudServices[application.globalKey()]; found { 887 args.CloudService = e.cloudService(cloudService) 888 } 889 if constraints, found := e.modelStorageConstraints[storageConstraintsKey]; found { 890 args.StorageDirectives = e.storageConstraints(constraints) 891 } 892 893 if ps := application.ProvisioningState(); ps != nil { 894 args.ProvisioningState = &description.ProvisioningStateArgs{ 895 Scaling: ps.Scaling, 896 ScaleTarget: ps.ScaleTarget, 897 } 898 } 899 900 // Include exposed endpoint details 901 if len(application.doc.ExposedEndpoints) > 0 { 902 args.ExposedEndpoints = make(map[string]description.ExposedEndpointArgs) 903 for epName, details := range application.doc.ExposedEndpoints { 904 args.ExposedEndpoints[epName] = description.ExposedEndpointArgs{ 905 ExposeToSpaceIDs: details.ExposeToSpaceIDs, 906 ExposeToCIDRs: details.ExposeToCIDRs, 907 } 908 } 909 } 910 911 exApplication := e.model.AddApplication(args) 912 913 // Populate offer list 914 for _, offer := range ctx.offers { 915 endpoints := make(map[string]string, len(offer.Endpoints)) 916 for k, ep := range offer.Endpoints { 917 endpoints[k] = ep.Name 918 } 919 920 userMap, err := e.st.GetOfferUsers(offer.OfferUUID) 921 if err != nil { 922 return errors.Annotatef(err, "ACL for offer %s", offer.OfferName) 923 } 924 925 var acl map[string]string 926 if len(userMap) != 0 { 927 acl = make(map[string]string, len(userMap)) 928 for user, access := range userMap { 929 acl[user] = accessToString(access) 930 } 931 } 932 933 _ = exApplication.AddOffer(description.ApplicationOfferArgs{ 934 OfferUUID: offer.OfferUUID, 935 OfferName: offer.OfferName, 936 Endpoints: endpoints, 937 ACL: acl, 938 ApplicationName: offer.ApplicationName, 939 ApplicationDescription: offer.ApplicationDescription, 940 }) 941 } 942 943 // Find the current application status. 944 statusArgs, err := e.statusArgs(globalKey) 945 if err != nil { 946 return errors.Annotatef(err, "status for application %s", appName) 947 } 948 949 exApplication.SetStatus(statusArgs) 950 exApplication.SetStatusHistory(e.statusHistoryArgs(globalKey)) 951 exApplication.SetAnnotations(e.getAnnotations(globalKey)) 952 953 globalAppWorkloadKey := applicationGlobalOperatorKey(appName) 954 operatorStatusArgs, err := e.statusArgs(globalAppWorkloadKey) 955 if err != nil { 956 if !errors.IsNotFound(err) { 957 return errors.Annotatef(err, "application operator status for application %s", appName) 958 } 959 } 960 exApplication.SetOperatorStatus(operatorStatusArgs) 961 e.statusHistoryArgs(globalAppWorkloadKey) 962 963 constraintsArgs, err := e.constraintsArgs(globalKey) 964 if err != nil { 965 return errors.Trace(err) 966 } 967 exApplication.SetConstraints(constraintsArgs) 968 969 defaultArch := constraintsArgs.Architecture 970 if defaultArch == "" { 971 defaultArch = arch.DefaultArchitecture 972 } 973 charmOriginArgs, err := e.getCharmOrigin(application.doc, defaultArch) 974 if err != nil { 975 return errors.Annotatef(err, "charm origin") 976 } 977 exApplication.SetCharmOrigin(charmOriginArgs) 978 979 if err := e.setResources(exApplication, ctx.resources); err != nil { 980 return errors.Trace(err) 981 } 982 983 for _, args := range e.openedPortRangesArgsForApplication(appName, ctx.portsData) { 984 exApplication.AddOpenedPortRange(args) 985 } 986 987 // Set Tools for application - this is only for CAAS models. 988 isSidecar, err := ctx.application.IsSidecar() 989 if err != nil { 990 return errors.Trace(err) 991 } 992 993 for _, unit := range ctx.units { 994 agentKey := unit.globalAgentKey() 995 unitMeterStatus, found := ctx.meterStatus[agentKey] 996 if !found { 997 return errors.Errorf("missing meter status for unit %s", unit.Name()) 998 } 999 1000 workloadVersion, err := e.unitWorkloadVersion(unit) 1001 if err != nil { 1002 return errors.Trace(err) 1003 } 1004 args := description.UnitArgs{ 1005 Tag: unit.UnitTag(), 1006 Type: string(unit.modelType), 1007 Machine: names.NewMachineTag(unit.doc.MachineId), 1008 WorkloadVersion: workloadVersion, 1009 PasswordHash: unit.doc.PasswordHash, 1010 MeterStatusCode: unitMeterStatus.Code, 1011 MeterStatusInfo: unitMeterStatus.Info, 1012 } 1013 if principalName, isSubordinate := unit.PrincipalName(); isSubordinate { 1014 args.Principal = names.NewUnitTag(principalName) 1015 } 1016 if subs := unit.SubordinateNames(); len(subs) > 0 { 1017 for _, subName := range subs { 1018 args.Subordinates = append(args.Subordinates, names.NewUnitTag(subName)) 1019 } 1020 } 1021 if cloudContainer, found := ctx.cloudContainers[unit.globalKey()]; found { 1022 args.CloudContainer = e.cloudContainer(cloudContainer) 1023 } 1024 1025 // Export charm and agent state stored to the controller. 1026 unitState, err := unit.State() 1027 if err != nil { 1028 return errors.Trace(err) 1029 } 1030 if charmState, found := unitState.CharmState(); found { 1031 args.CharmState = charmState 1032 } 1033 if relationState, found := unitState.RelationState(); found { 1034 args.RelationState = relationState 1035 } 1036 if uniterState, found := unitState.UniterState(); found { 1037 args.UniterState = uniterState 1038 } 1039 if storageState, found := unitState.StorageState(); found { 1040 args.StorageState = storageState 1041 } 1042 if meterStatusState, found := unitState.MeterStatusState(); found { 1043 args.MeterStatusState = meterStatusState 1044 } 1045 exUnit := exApplication.AddUnit(args) 1046 1047 e.setUnitResources(exUnit, ctx.resources.UnitResources) 1048 1049 if err := e.setUnitPayloads(exUnit, ctx.payloads[unit.UnitTag().Id()]); err != nil { 1050 return errors.Trace(err) 1051 } 1052 1053 // workload uses globalKey, agent uses globalAgentKey, 1054 // workload version uses globalWorkloadVersionKey. 1055 globalKey := unit.globalKey() 1056 statusArgs, err := e.statusArgs(globalKey) 1057 if err != nil { 1058 return errors.Annotatef(err, "workload status for unit %s", unit.Name()) 1059 } 1060 exUnit.SetWorkloadStatus(statusArgs) 1061 exUnit.SetWorkloadStatusHistory(e.statusHistoryArgs(globalKey)) 1062 1063 statusArgs, err = e.statusArgs(agentKey) 1064 if err != nil { 1065 return errors.Annotatef(err, "agent status for unit %s", unit.Name()) 1066 } 1067 exUnit.SetAgentStatus(statusArgs) 1068 exUnit.SetAgentStatusHistory(e.statusHistoryArgs(agentKey)) 1069 1070 workloadVersionKey := unit.globalWorkloadVersionKey() 1071 exUnit.SetWorkloadVersionHistory(e.statusHistoryArgs(workloadVersionKey)) 1072 1073 if (e.dbModel.Type() != ModelTypeCAAS && !e.cfg.SkipUnitAgentBinaries) || isSidecar { 1074 tools, err := unit.AgentTools() 1075 if err != nil && !e.cfg.IgnoreIncompleteModel { 1076 // This means the tools aren't set, but they should be. 1077 return errors.Trace(err) 1078 } 1079 if err == nil { 1080 exUnit.SetTools(description.AgentToolsArgs{ 1081 Version: tools.Version, 1082 URL: tools.URL, 1083 SHA256: tools.SHA256, 1084 Size: tools.Size, 1085 }) 1086 } 1087 } 1088 if e.dbModel.Type() == ModelTypeCAAS { 1089 // TODO(caas) - Actually use the exported cloud container details and status history. 1090 // Currently these are only grabbed to make the MigrationExportSuite tests happy. 1091 globalCCKey := unit.globalCloudContainerKey() 1092 _, err = e.statusArgs(globalCCKey) 1093 if err != nil { 1094 if !errors.IsNotFound(err) { 1095 return errors.Annotatef(err, "cloud container workload status for unit %s", unit.Name()) 1096 } 1097 } 1098 e.statusHistoryArgs(globalCCKey) 1099 } 1100 exUnit.SetAnnotations(e.getAnnotations(globalKey)) 1101 1102 constraintsArgs, err := e.constraintsArgs(agentKey) 1103 if err != nil { 1104 return errors.Trace(err) 1105 } 1106 exUnit.SetConstraints(constraintsArgs) 1107 } 1108 1109 if e.dbModel.Type() == ModelTypeCAAS && !isSidecar { 1110 tools, err := ctx.application.AgentTools() 1111 if err != nil { 1112 // This means the tools aren't set, but they should be. 1113 return errors.Trace(err) 1114 } 1115 exApplication.SetTools(description.AgentToolsArgs{ 1116 Version: tools.Version, 1117 }) 1118 } 1119 1120 return nil 1121 } 1122 1123 func (e *exporter) openedPortRangesArgsForApplication(appName string, portsData map[string]*applicationPortRanges) []description.OpenedPortRangeArgs { 1124 if portsData[appName] == nil { 1125 return nil 1126 } 1127 1128 var result []description.OpenedPortRangeArgs 1129 for unitName, unitPorts := range portsData[appName].ByUnit() { 1130 for endpointName, portRanges := range unitPorts.ByEndpoint() { 1131 for _, pr := range portRanges { 1132 result = append(result, description.OpenedPortRangeArgs{ 1133 UnitName: unitName, 1134 EndpointName: endpointName, 1135 FromPort: pr.FromPort, 1136 ToPort: pr.ToPort, 1137 Protocol: pr.Protocol, 1138 }) 1139 } 1140 } 1141 } 1142 return result 1143 } 1144 1145 func (e *exporter) unitWorkloadVersion(unit *Unit) (string, error) { 1146 // Rather than call unit.WorkloadVersion(), which does a database 1147 // query, we go directly to the status value that is stored. 1148 key := unit.globalWorkloadVersionKey() 1149 info, err := e.statusArgs(key) 1150 if err != nil { 1151 return "", errors.Trace(err) 1152 } 1153 return info.Message, nil 1154 } 1155 1156 func (e *exporter) setResources(exApp description.Application, resources resources.ApplicationResources) error { 1157 if len(resources.Resources) != len(resources.CharmStoreResources) { 1158 return errors.New("number of resources don't match charm store resources") 1159 } 1160 1161 for i, resource := range resources.Resources { 1162 exResource := exApp.AddResource(description.ResourceArgs{ 1163 Name: resource.Name, 1164 }) 1165 exResource.SetApplicationRevision(description.ResourceRevisionArgs{ 1166 Revision: resource.Revision, 1167 Type: resource.Type.String(), 1168 Path: resource.Path, 1169 Description: resource.Description, 1170 Origin: resource.Origin.String(), 1171 FingerprintHex: resource.Fingerprint.Hex(), 1172 Size: resource.Size, 1173 Timestamp: resource.Timestamp, 1174 Username: resource.Username, 1175 }) 1176 csResource := resources.CharmStoreResources[i] 1177 exResource.SetCharmStoreRevision(description.ResourceRevisionArgs{ 1178 Revision: csResource.Revision, 1179 Type: csResource.Type.String(), 1180 Path: csResource.Path, 1181 Description: csResource.Description, 1182 Origin: csResource.Origin.String(), 1183 Size: csResource.Size, 1184 FingerprintHex: csResource.Fingerprint.Hex(), 1185 }) 1186 } 1187 1188 return nil 1189 } 1190 1191 func (e *exporter) setUnitResources(exUnit description.Unit, allResources []resources.UnitResources) { 1192 for _, res := range findUnitResources(exUnit.Name(), allResources) { 1193 exUnit.AddResource(description.UnitResourceArgs{ 1194 Name: res.Name, 1195 RevisionArgs: description.ResourceRevisionArgs{ 1196 Revision: res.Revision, 1197 Type: res.Type.String(), 1198 Path: res.Path, 1199 Description: res.Description, 1200 Origin: res.Origin.String(), 1201 FingerprintHex: res.Fingerprint.Hex(), 1202 Size: res.Size, 1203 Timestamp: res.Timestamp, 1204 Username: res.Username, 1205 }, 1206 }) 1207 } 1208 } 1209 1210 func findUnitResources(unitName string, allResources []resources.UnitResources) []resources.Resource { 1211 for _, unitResources := range allResources { 1212 if unitResources.Tag.Id() == unitName { 1213 return unitResources.Resources 1214 } 1215 } 1216 return nil 1217 } 1218 1219 func (e *exporter) setUnitPayloads(exUnit description.Unit, payloads []payloads.FullPayloadInfo) error { 1220 if len(payloads) == 0 { 1221 return nil 1222 } 1223 unitID := exUnit.Tag().Id() 1224 machineID := exUnit.Machine().Id() 1225 for _, payload := range payloads { 1226 if payload.Machine != machineID { 1227 return errors.NotValidf("payload for unit %q reports wrong machine %q (should be %q)", unitID, payload.Machine, machineID) 1228 } 1229 args := description.PayloadArgs{ 1230 Name: payload.Name, 1231 Type: payload.Type, 1232 RawID: payload.ID, 1233 State: payload.Status, 1234 Labels: payload.Labels, 1235 } 1236 exUnit.AddPayload(args) 1237 } 1238 return nil 1239 } 1240 1241 func (e *exporter) relations() error { 1242 rels, err := e.st.AllRelations() 1243 if err != nil { 1244 return errors.Trace(err) 1245 } 1246 e.logger.Debugf("read %d relations", len(rels)) 1247 1248 relationScopes := set.NewStrings() 1249 if !e.cfg.SkipRelationData { 1250 relationScopes, err = e.readAllRelationScopes() 1251 if err != nil { 1252 return errors.Trace(err) 1253 } 1254 } 1255 1256 for _, relation := range rels { 1257 exRelation := e.model.AddRelation(description.RelationArgs{ 1258 Id: relation.Id(), 1259 Key: relation.String(), 1260 }) 1261 globalKey := relation.globalScope() 1262 statusArgs, err := e.statusArgs(globalKey) 1263 if err == nil { 1264 exRelation.SetStatus(statusArgs) 1265 } else if !errors.IsNotFound(err) { 1266 return errors.Annotatef(err, "status for relation %v", relation.Id()) 1267 } 1268 1269 for _, ep := range relation.Endpoints() { 1270 if err := e.relationEndpoint(relation, exRelation, ep, relationScopes); err != nil { 1271 return errors.Trace(err) 1272 } 1273 } 1274 } 1275 return nil 1276 } 1277 1278 func (e *exporter) relationEndpoint( 1279 relation *Relation, 1280 exRelation description.Relation, 1281 ep Endpoint, 1282 relationScopes set.Strings, 1283 ) error { 1284 exEndPoint := exRelation.AddEndpoint(description.EndpointArgs{ 1285 ApplicationName: ep.ApplicationName, 1286 Name: ep.Name, 1287 Role: string(ep.Role), 1288 Interface: ep.Interface, 1289 Optional: ep.Optional, 1290 Limit: ep.Limit, 1291 Scope: string(ep.Scope), 1292 }) 1293 1294 key := relationApplicationSettingsKey(relation.Id(), ep.ApplicationName) 1295 appSettingsDoc, found := e.modelSettings[key] 1296 if !found && !e.cfg.SkipSettings && !e.cfg.SkipRelationData { 1297 return errors.Errorf("missing application settings for %q application %q", relation, ep.ApplicationName) 1298 } 1299 delete(e.modelSettings, key) 1300 exEndPoint.SetApplicationSettings(appSettingsDoc.Settings) 1301 1302 // We expect a relationScope and settings for each of 1303 // the units of the specified application. 1304 // We need to check both local and remote applications 1305 // in case we are dealing with a CMR. 1306 if units, ok := e.units[ep.ApplicationName]; ok { 1307 for _, unit := range units { 1308 ru, err := relation.Unit(unit) 1309 if err != nil { 1310 return errors.Trace(err) 1311 } 1312 1313 if err := e.relationUnit(exEndPoint, ru, unit.Name(), relationScopes); err != nil { 1314 return errors.Annotatef(err, "processing relation unit in %s", relation) 1315 } 1316 } 1317 } else { 1318 remotes, err := relation.AllRemoteUnits(ep.ApplicationName) 1319 if err != nil { 1320 if errors.Is(err, errors.NotFound) { 1321 // If there are no local or remote units for this application, 1322 // then there are none in scope. We are done. 1323 return nil 1324 } 1325 return errors.Annotatef(err, "retrieving remote units for %s", relation) 1326 } 1327 1328 for _, ru := range remotes { 1329 if err := e.relationUnit(exEndPoint, ru, ru.unitName, relationScopes); err != nil { 1330 return errors.Annotatef(err, "processing relation unit in %s", relation) 1331 } 1332 } 1333 } 1334 1335 return nil 1336 } 1337 1338 func (e *exporter) relationUnit( 1339 exEndPoint description.Endpoint, 1340 ru *RelationUnit, 1341 unitName string, 1342 relationScopes set.Strings, 1343 ) error { 1344 valid, err := ru.Valid() 1345 if err != nil { 1346 return errors.Trace(err) 1347 } 1348 if !valid { 1349 // It doesn't make sense for this application to have a 1350 // relations scope for this endpoint. For example the 1351 // situation where we have a subordinate charm related to 1352 // two different principals. 1353 return nil 1354 } 1355 1356 key := ru.key() 1357 if !e.cfg.SkipRelationData && !relationScopes.Contains(key) && !e.cfg.IgnoreIncompleteModel { 1358 return errors.Errorf("missing relation scope for %s", unitName) 1359 } 1360 settingsDoc, found := e.modelSettings[key] 1361 if !found && !e.cfg.SkipSettings && !e.cfg.SkipRelationData && !e.cfg.IgnoreIncompleteModel { 1362 return errors.Errorf("missing relation settings for %s", unitName) 1363 } 1364 delete(e.modelSettings, key) 1365 exEndPoint.SetUnitSettings(unitName, settingsDoc.Settings) 1366 1367 return nil 1368 } 1369 1370 func (e *exporter) remoteEntities() error { 1371 e.logger.Debugf("reading remote entities") 1372 migration := &ExportStateMigration{ 1373 src: e.st, 1374 dst: e.model, 1375 } 1376 migration.Add(func() error { 1377 m := migrations.ExportRemoteEntities{} 1378 return m.Execute(remoteEntitiesShim{ 1379 st: migration.src, 1380 }, migration.dst) 1381 }) 1382 return migration.Run() 1383 } 1384 1385 // offerConnectionsShim provides a way to model our dependencies by providing 1386 // a shim layer to manage the covariance of the state package to the migration 1387 // package. 1388 type offerConnectionsShim struct { 1389 st *State 1390 } 1391 1392 // AllOfferConnections returns all offer connections in the model. 1393 // The offer connection shim converts a state.OfferConnection to a 1394 // migrations.MigrationOfferConnection. 1395 func (s offerConnectionsShim) AllOfferConnections() ([]migrations.MigrationOfferConnection, error) { 1396 conns, err := s.st.AllOfferConnections() 1397 if err != nil { 1398 return nil, errors.Trace(err) 1399 } 1400 result := make([]migrations.MigrationOfferConnection, len(conns)) 1401 for k, v := range conns { 1402 result[k] = v 1403 } 1404 return result, nil 1405 } 1406 1407 func (e *exporter) offerConnections() error { 1408 if e.cfg.SkipOfferConnections { 1409 return nil 1410 } 1411 1412 e.logger.Debugf("reading offer connections") 1413 migration := &ExportStateMigration{ 1414 src: e.st, 1415 dst: e.model, 1416 } 1417 migration.Add(func() error { 1418 m := migrations.ExportOfferConnections{} 1419 return m.Execute(offerConnectionsShim{st: migration.src}, migration.dst) 1420 }) 1421 return migration.Run() 1422 } 1423 1424 // externalControllersShim is to handle the fact that go doesn't handle 1425 // covariance and the tight abstraction around the new migration export work 1426 // ensures that we handle our dependencies up front. 1427 type externalControllerShim struct { 1428 st *State 1429 } 1430 1431 // externalControllerInfoShim is used to align to an interface with in the 1432 // migrations package. 1433 type externalControllerInfoShim struct { 1434 info externalControllerDoc 1435 } 1436 1437 // ID holds the controller ID from the external controller 1438 func (e externalControllerInfoShim) ID() string { 1439 return e.info.Id 1440 } 1441 1442 // Alias holds an alias (human friendly) name for the controller. 1443 func (e externalControllerInfoShim) Alias() string { 1444 return e.info.Alias 1445 } 1446 1447 // Addrs holds the host:port values for the external 1448 // controller's API server. 1449 func (e externalControllerInfoShim) Addrs() []string { 1450 return e.info.Addrs 1451 } 1452 1453 // CACert holds the certificate to validate the external 1454 // controller's target API server's TLS certificate. 1455 func (e externalControllerInfoShim) CACert() string { 1456 return e.info.CACert 1457 } 1458 1459 // Models holds model UUIDs hosted on this controller. 1460 func (e externalControllerInfoShim) Models() []string { 1461 return e.info.Models 1462 } 1463 1464 func (s externalControllerShim) ControllerForModel(uuid string) (migrations.MigrationExternalController, error) { 1465 entity, err := s.st.ExternalControllerForModel(uuid) 1466 if err != nil { 1467 return nil, errors.Trace(err) 1468 } 1469 return externalControllerInfoShim{ 1470 info: entity.doc, 1471 }, nil 1472 } 1473 1474 // AllRemoteApplications returns all remote applications in the model. 1475 func (s externalControllerShim) AllRemoteApplications() ([]migrations.MigrationRemoteApplication, error) { 1476 remoteApps, err := s.st.AllRemoteApplications() 1477 if err != nil { 1478 return nil, errors.Trace(err) 1479 } 1480 result := make([]migrations.MigrationRemoteApplication, len(remoteApps)) 1481 for k, v := range remoteApps { 1482 result[k] = remoteApplicationShim{RemoteApplication: v} 1483 } 1484 return result, nil 1485 } 1486 1487 func (e *exporter) externalControllers() error { 1488 if e.cfg.SkipExternalControllers { 1489 return nil 1490 } 1491 e.logger.Debugf("reading external controllers") 1492 migration := &ExportStateMigration{ 1493 src: e.st, 1494 dst: e.model, 1495 } 1496 migration.Add(func() error { 1497 m := migrations.ExportExternalControllers{} 1498 return m.Execute(externalControllerShim{st: migration.src}, migration.dst) 1499 }) 1500 return migration.Run() 1501 } 1502 1503 // remoteEntitiesShim is to handle the fact that go doesn't handle covariance 1504 // and the tight abstraction around the new migration export work ensures that 1505 // we handle our dependencies up front. 1506 type remoteEntitiesShim struct { 1507 st *State 1508 } 1509 1510 // AllRemoteEntities returns all remote entities in the model. 1511 func (s remoteEntitiesShim) AllRemoteEntities() ([]migrations.MigrationRemoteEntity, error) { 1512 entities, err := s.st.AllRemoteEntities() 1513 if err != nil { 1514 return nil, errors.Trace(err) 1515 } 1516 result := make([]migrations.MigrationRemoteEntity, len(entities)) 1517 for k, v := range entities { 1518 result[k] = v 1519 } 1520 return result, nil 1521 } 1522 1523 func (e *exporter) relationNetworks() error { 1524 e.logger.Debugf("reading relation networks") 1525 migration := &ExportStateMigration{ 1526 src: e.st, 1527 dst: e.model, 1528 } 1529 migration.Add(func() error { 1530 m := migrations.ExportRelationNetworks{} 1531 return m.Execute(relationNetworksShim{st: migration.src}, migration.dst) 1532 }) 1533 return migration.Run() 1534 } 1535 1536 // relationNetworksShim is to handle the fact that go doesn't handle covariance 1537 // and the tight abstraction around the new migration export work ensures that 1538 // we handle our dependencies up front. 1539 type relationNetworksShim struct { 1540 st *State 1541 } 1542 1543 func (s relationNetworksShim) AllRelationNetworks() ([]migrations.MigrationRelationNetworks, error) { 1544 entities, err := NewRelationNetworks(s.st).AllRelationNetworks() 1545 if err != nil { 1546 return nil, errors.Trace(err) 1547 } 1548 result := make([]migrations.MigrationRelationNetworks, len(entities)) 1549 for k, v := range entities { 1550 result[k] = v 1551 } 1552 return result, nil 1553 } 1554 1555 func (e *exporter) spaces() error { 1556 spaces, err := e.st.AllSpaces() 1557 if err != nil { 1558 return errors.Trace(err) 1559 } 1560 e.logger.Debugf("read %d spaces", len(spaces)) 1561 1562 for _, space := range spaces { 1563 // We do not export the alpha space because it is created by default 1564 // with the new model. This is OK, because it is immutable. 1565 // Any subnets added to the space will still be exported. 1566 if space.Id() == network.AlphaSpaceId { 1567 continue 1568 } 1569 1570 e.model.AddSpace(description.SpaceArgs{ 1571 Id: space.Id(), 1572 Name: space.Name(), 1573 Public: space.IsPublic(), 1574 ProviderID: string(space.ProviderId()), 1575 }) 1576 } 1577 return nil 1578 } 1579 1580 func (e *exporter) linklayerdevices() error { 1581 if e.cfg.SkipLinkLayerDevices { 1582 return nil 1583 } 1584 linklayerdevices, err := e.st.AllLinkLayerDevices() 1585 if err != nil { 1586 return errors.Trace(err) 1587 } 1588 e.logger.Debugf("read %d ip devices", len(linklayerdevices)) 1589 for _, device := range linklayerdevices { 1590 e.model.AddLinkLayerDevice(description.LinkLayerDeviceArgs{ 1591 ProviderID: string(device.ProviderID()), 1592 MachineID: device.MachineID(), 1593 Name: device.Name(), 1594 MTU: device.MTU(), 1595 Type: string(device.Type()), 1596 MACAddress: device.MACAddress(), 1597 IsAutoStart: device.IsAutoStart(), 1598 IsUp: device.IsUp(), 1599 ParentName: device.ParentName(), 1600 VirtualPortType: string(device.VirtualPortType()), 1601 }) 1602 } 1603 return nil 1604 } 1605 1606 func (e *exporter) subnets() error { 1607 subnets, err := e.st.AllSubnets() 1608 if err != nil { 1609 return errors.Trace(err) 1610 } 1611 e.logger.Debugf("read %d subnets", len(subnets)) 1612 1613 for _, subnet := range subnets { 1614 args := description.SubnetArgs{ 1615 ID: subnet.ID(), 1616 CIDR: subnet.CIDR(), 1617 ProviderId: string(subnet.ProviderId()), 1618 ProviderNetworkId: string(subnet.ProviderNetworkId()), 1619 VLANTag: subnet.VLANTag(), 1620 SpaceID: subnet.SpaceID(), 1621 AvailabilityZones: subnet.AvailabilityZones(), 1622 FanLocalUnderlay: subnet.FanLocalUnderlay(), 1623 FanOverlay: subnet.FanOverlay(), 1624 IsPublic: subnet.IsPublic(), 1625 } 1626 e.model.AddSubnet(args) 1627 } 1628 return nil 1629 } 1630 1631 func (e *exporter) ipAddresses() error { 1632 if e.cfg.SkipIPAddresses { 1633 return nil 1634 } 1635 ipaddresses, err := e.st.AllIPAddresses() 1636 if err != nil { 1637 return errors.Trace(err) 1638 } 1639 e.logger.Debugf("read %d ip addresses", len(ipaddresses)) 1640 for _, addr := range ipaddresses { 1641 e.model.AddIPAddress(description.IPAddressArgs{ 1642 ProviderID: string(addr.ProviderID()), 1643 DeviceName: addr.DeviceName(), 1644 MachineID: addr.MachineID(), 1645 SubnetCIDR: addr.SubnetCIDR(), 1646 ConfigMethod: string(addr.ConfigMethod()), 1647 Value: addr.Value(), 1648 DNSServers: addr.DNSServers(), 1649 DNSSearchDomains: addr.DNSSearchDomains(), 1650 GatewayAddress: addr.GatewayAddress(), 1651 ProviderNetworkID: addr.ProviderNetworkID().String(), 1652 ProviderSubnetID: addr.ProviderSubnetID().String(), 1653 Origin: string(addr.Origin()), 1654 }) 1655 } 1656 return nil 1657 } 1658 1659 func (e *exporter) sshHostKeys() error { 1660 if e.cfg.SkipSSHHostKeys { 1661 return nil 1662 } 1663 machines, err := e.st.AllMachines() 1664 if err != nil { 1665 return errors.Trace(err) 1666 } 1667 for _, machine := range machines { 1668 keys, err := e.st.GetSSHHostKeys(machine.MachineTag()) 1669 if errors.IsNotFound(err) { 1670 continue 1671 } else if err != nil { 1672 return errors.Trace(err) 1673 } 1674 if len(keys) == 0 { 1675 continue 1676 } 1677 e.model.AddSSHHostKey(description.SSHHostKeyArgs{ 1678 MachineID: machine.Id(), 1679 Keys: keys, 1680 }) 1681 } 1682 return nil 1683 } 1684 1685 func (e *exporter) cloudimagemetadata() error { 1686 if e.cfg.SkipCloudImageMetadata { 1687 return nil 1688 } 1689 cloudimagemetadata, err := e.st.CloudImageMetadataStorage.AllCloudImageMetadata() 1690 if err != nil { 1691 return errors.Trace(err) 1692 } 1693 e.logger.Debugf("read %d cloudimagemetadata", len(cloudimagemetadata)) 1694 for _, metadata := range cloudimagemetadata { 1695 e.model.AddCloudImageMetadata(description.CloudImageMetadataArgs{ 1696 Stream: metadata.Stream, 1697 Region: metadata.Region, 1698 Version: metadata.Version, 1699 Arch: metadata.Arch, 1700 VirtType: metadata.VirtType, 1701 RootStorageType: metadata.RootStorageType, 1702 RootStorageSize: metadata.RootStorageSize, 1703 DateCreated: metadata.DateCreated, 1704 Source: metadata.Source, 1705 Priority: metadata.Priority, 1706 ImageId: metadata.ImageId, 1707 }) 1708 } 1709 return nil 1710 } 1711 1712 func (e *exporter) actions() error { 1713 if e.cfg.SkipActions { 1714 return nil 1715 } 1716 1717 m, err := e.st.Model() 1718 if err != nil { 1719 return errors.Trace(err) 1720 } 1721 1722 actions, err := m.AllActions() 1723 if err != nil { 1724 return errors.Trace(err) 1725 } 1726 e.logger.Debugf("read %d actions", len(actions)) 1727 for _, a := range actions { 1728 results, message := a.Results() 1729 arg := description.ActionArgs{ 1730 Receiver: a.Receiver(), 1731 Name: a.Name(), 1732 Operation: a.(*action).doc.Operation, 1733 Parameters: a.Parameters(), 1734 Enqueued: a.Enqueued(), 1735 Started: a.Started(), 1736 Completed: a.Completed(), 1737 Status: string(a.Status()), 1738 Results: results, 1739 Message: message, 1740 Id: a.Id(), 1741 Parallel: a.Parallel(), 1742 ExecutionGroup: a.ExecutionGroup(), 1743 } 1744 messages := a.Messages() 1745 arg.Messages = make([]description.ActionMessage, len(messages)) 1746 for i, m := range messages { 1747 arg.Messages[i] = m 1748 } 1749 e.model.AddAction(arg) 1750 } 1751 return nil 1752 } 1753 1754 func (e *exporter) operations() error { 1755 if e.cfg.SkipActions { 1756 return nil 1757 } 1758 1759 m, err := e.st.Model() 1760 if err != nil { 1761 return errors.Trace(err) 1762 } 1763 1764 operations, err := m.AllOperations() 1765 if err != nil { 1766 return errors.Trace(err) 1767 } 1768 e.logger.Debugf("read %d operations", len(operations)) 1769 for _, op := range operations { 1770 opDetails, ok := op.(*operation) 1771 if !ok { 1772 return errors.Errorf("operation must be of type operation") 1773 } 1774 arg := description.OperationArgs{ 1775 Summary: op.Summary(), 1776 Fail: op.Fail(), 1777 Enqueued: op.Enqueued(), 1778 Started: op.Started(), 1779 Completed: op.Completed(), 1780 Status: string(op.Status()), 1781 CompleteTaskCount: opDetails.doc.CompleteTaskCount, 1782 SpawnedTaskCount: opDetails.doc.SpawnedTaskCount, 1783 Id: op.Id(), 1784 } 1785 e.model.AddOperation(arg) 1786 } 1787 return nil 1788 } 1789 1790 func (e *exporter) secretBackendID() (string, error) { 1791 mCfg, err := e.dbModel.Config() 1792 if err != nil { 1793 return "", errors.Trace(err) 1794 } 1795 backendName := mCfg.SecretBackend() 1796 if backendName == "" || backendName == secretsprovider.Auto || backendName == secretsprovider.Internal { 1797 return "", nil 1798 } 1799 store := NewSecretBackends(e.st) 1800 backend, err := store.GetSecretBackend(backendName) 1801 if err != nil { 1802 return "", errors.Trace(err) 1803 } 1804 return backend.ID, nil 1805 } 1806 1807 func (e *exporter) secrets() error { 1808 if e.cfg.SkipSecrets { 1809 return nil 1810 } 1811 store := NewSecrets(e.st) 1812 1813 allSecrets, err := store.ListSecrets(SecretsFilter{}) 1814 if err != nil { 1815 return errors.Trace(err) 1816 } 1817 e.logger.Debugf("read %d secrets", len(allSecrets)) 1818 allRevisions, err := store.allSecretRevisions() 1819 if err != nil { 1820 return errors.Trace(err) 1821 } 1822 revisionArgsByID := make(map[string][]description.SecretRevisionArgs) 1823 for _, rev := range allRevisions { 1824 id, _ := splitSecretRevision(e.st.localID(rev.DocID)) 1825 revArg := description.SecretRevisionArgs{ 1826 Number: rev.Revision, 1827 Created: rev.CreateTime, 1828 Updated: rev.UpdateTime, 1829 ExpireTime: rev.ExpireTime, 1830 Obsolete: rev.Obsolete, 1831 PendingDelete: rev.PendingDelete, 1832 } 1833 if len(rev.Data) > 0 { 1834 revArg.Content = make(secrets.SecretData) 1835 for k, v := range rev.Data { 1836 revArg.Content[k] = fmt.Sprintf("%v", v) 1837 } 1838 } 1839 if rev.ValueRef != nil { 1840 revArg.ValueRef = &description.SecretValueRefArgs{ 1841 BackendID: rev.ValueRef.BackendID, 1842 RevisionID: rev.ValueRef.RevisionID, 1843 } 1844 } 1845 revisionArgsByID[id] = append(revisionArgsByID[id], revArg) 1846 } 1847 allPermissions, err := store.allSecretPermissions() 1848 if err != nil { 1849 return errors.Trace(err) 1850 } 1851 accessArgsByID := make(map[string]map[string]description.SecretAccessArgs) 1852 for _, perm := range allPermissions { 1853 id := strings.Split(e.st.localID(perm.DocID), "#")[0] 1854 accessArg := description.SecretAccessArgs{ 1855 Scope: perm.Scope, 1856 Role: perm.Role, 1857 } 1858 access, ok := accessArgsByID[id] 1859 if !ok { 1860 access = make(map[string]description.SecretAccessArgs) 1861 accessArgsByID[id] = access 1862 } 1863 access[perm.Subject] = accessArg 1864 } 1865 allConsumers, err := store.allLocalSecretConsumers() 1866 if err != nil { 1867 return errors.Trace(err) 1868 } 1869 consumersByID := make(map[string][]description.SecretConsumerArgs) 1870 for _, info := range allConsumers { 1871 consumer, err := names.ParseTag(info.ConsumerTag) 1872 if err != nil { 1873 return errors.Trace(err) 1874 } 1875 id := strings.Split(e.st.localID(info.DocID), "#")[0] 1876 consumerArg := description.SecretConsumerArgs{ 1877 Consumer: consumer, 1878 Label: info.Label, 1879 CurrentRevision: info.CurrentRevision, 1880 } 1881 consumersByID[id] = append(consumersByID[id], consumerArg) 1882 } 1883 1884 allRemoteConsumers, err := store.allSecretRemoteConsumers() 1885 if err != nil { 1886 return errors.Trace(err) 1887 } 1888 remoteConsumersByID := make(map[string][]description.SecretRemoteConsumerArgs) 1889 for _, info := range allRemoteConsumers { 1890 consumer, err := names.ParseTag(info.ConsumerTag) 1891 if err != nil { 1892 return errors.Trace(err) 1893 } 1894 id := strings.Split(e.st.localID(info.DocID), "#")[0] 1895 remoteConsumerArg := description.SecretRemoteConsumerArgs{ 1896 Consumer: consumer, 1897 CurrentRevision: info.CurrentRevision, 1898 } 1899 remoteConsumersByID[id] = append(remoteConsumersByID[id], remoteConsumerArg) 1900 } 1901 1902 for _, md := range allSecrets { 1903 owner, err := names.ParseTag(md.OwnerTag) 1904 if err != nil { 1905 return errors.Trace(err) 1906 } 1907 arg := description.SecretArgs{ 1908 ID: md.URI.ID, 1909 Version: md.Version, 1910 Description: md.Description, 1911 Label: md.Label, 1912 RotatePolicy: md.RotatePolicy.String(), 1913 AutoPrune: md.AutoPrune, 1914 Owner: owner, 1915 Created: md.CreateTime, 1916 Updated: md.UpdateTime, 1917 NextRotateTime: md.NextRotateTime, 1918 Revisions: revisionArgsByID[md.URI.ID], 1919 ACL: accessArgsByID[md.URI.ID], 1920 Consumers: consumersByID[md.URI.ID], 1921 RemoteConsumers: remoteConsumersByID[md.URI.ID], 1922 } 1923 e.model.AddSecret(arg) 1924 } 1925 return nil 1926 } 1927 1928 func (e *exporter) remoteSecrets() error { 1929 if e.cfg.SkipSecrets { 1930 return nil 1931 } 1932 store := NewSecrets(e.st) 1933 1934 allConsumers, err := store.allRemoteSecretConsumers() 1935 if err != nil { 1936 return errors.Trace(err) 1937 } 1938 e.logger.Debugf("read %d remote secret consumers", len(allConsumers)) 1939 for _, info := range allConsumers { 1940 consumer, err := names.ParseTag(info.ConsumerTag) 1941 if err != nil { 1942 return errors.Trace(err) 1943 } 1944 id := strings.Split(e.st.localID(info.DocID), "#")[0] 1945 uri, err := secrets.ParseURI(id) 1946 if err != nil { 1947 return errors.Trace(err) 1948 } 1949 arg := description.RemoteSecretArgs{ 1950 ID: uri.ID, 1951 SourceUUID: uri.SourceUUID, 1952 Consumer: consumer, 1953 Label: info.Label, 1954 CurrentRevision: info.CurrentRevision, 1955 LatestRevision: info.LatestRevision, 1956 } 1957 e.model.AddRemoteSecret(arg) 1958 } 1959 return nil 1960 } 1961 1962 func (e *exporter) readAllRelationScopes() (set.Strings, error) { 1963 relationScopes, closer := e.st.db().GetCollection(relationScopesC) 1964 defer closer() 1965 1966 var docs []relationScopeDoc 1967 err := relationScopes.Find(nil).All(&docs) 1968 if err != nil { 1969 return nil, errors.Annotate(err, "cannot get all relation scopes") 1970 } 1971 e.logger.Debugf("found %d relationScope docs", len(docs)) 1972 1973 result := set.NewStrings() 1974 for _, doc := range docs { 1975 result.Add(doc.Key) 1976 } 1977 return result, nil 1978 } 1979 1980 func (e *exporter) readAllUnits() (map[string][]*Unit, error) { 1981 unitsCollection, closer := e.st.db().GetCollection(unitsC) 1982 defer closer() 1983 1984 var docs []unitDoc 1985 err := unitsCollection.Find(nil).Sort("name").All(&docs) 1986 if err != nil { 1987 return nil, errors.Annotate(err, "cannot get all units") 1988 } 1989 e.logger.Debugf("found %d unit docs", len(docs)) 1990 result := make(map[string][]*Unit) 1991 for _, doc := range docs { 1992 units := result[doc.Application] 1993 result[doc.Application] = append(units, newUnit(e.st, e.dbModel.Type(), &doc)) 1994 } 1995 return result, nil 1996 } 1997 1998 func (e *exporter) readAllEndpointBindings() (map[string]bindingsMap, error) { 1999 bindings, closer := e.st.db().GetCollection(endpointBindingsC) 2000 defer closer() 2001 2002 var docs []endpointBindingsDoc 2003 err := bindings.Find(nil).All(&docs) 2004 if err != nil { 2005 return nil, errors.Annotate(err, "cannot get all application endpoint bindings") 2006 } 2007 e.logger.Debugf("found %d application endpoint binding docs", len(docs)) 2008 result := make(map[string]bindingsMap) 2009 for _, doc := range docs { 2010 result[e.st.localID(doc.DocID)] = doc.Bindings 2011 } 2012 return result, nil 2013 } 2014 2015 func (e *exporter) readAllMeterStatus() (map[string]*meterStatusDoc, error) { 2016 meterStatuses, closer := e.st.db().GetCollection(meterStatusC) 2017 defer closer() 2018 2019 var docs []meterStatusDoc 2020 err := meterStatuses.Find(nil).All(&docs) 2021 if err != nil { 2022 return nil, errors.Annotate(err, "cannot get all meter status docs") 2023 } 2024 e.logger.Debugf("found %d meter status docs", len(docs)) 2025 result := make(map[string]*meterStatusDoc) 2026 for _, v := range docs { 2027 doc := v 2028 result[e.st.localID(doc.DocID)] = &doc 2029 } 2030 return result, nil 2031 } 2032 2033 func (e *exporter) readAllPodSpecs() (map[string]string, error) { 2034 specs, closer := e.st.db().GetCollection(podSpecsC) 2035 defer closer() 2036 2037 var docs []containerSpecDoc 2038 err := specs.Find(nil).All(&docs) 2039 if err != nil { 2040 return nil, errors.Annotate(err, "cannot get all pod spec docs") 2041 } 2042 e.logger.Debugf("found %d pod spec docs", len(docs)) 2043 result := make(map[string]string) 2044 for _, doc := range docs { 2045 result[e.st.localID(doc.Id)] = doc.Spec 2046 } 2047 return result, nil 2048 } 2049 2050 func (e *exporter) readAllCloudServices() (map[string]*cloudServiceDoc, error) { 2051 cloudServices, closer := e.st.db().GetCollection(cloudServicesC) 2052 defer closer() 2053 2054 var docs []cloudServiceDoc 2055 err := cloudServices.Find(nil).All(&docs) 2056 if err != nil { 2057 return nil, errors.Annotate(err, "cannot get all cloud service docs") 2058 } 2059 e.logger.Debugf("found %d cloud service docs", len(docs)) 2060 result := make(map[string]*cloudServiceDoc) 2061 for _, v := range docs { 2062 doc := v 2063 result[e.st.localID(doc.DocID)] = &doc 2064 } 2065 return result, nil 2066 } 2067 2068 func (e *exporter) cloudService(doc *cloudServiceDoc) *description.CloudServiceArgs { 2069 return &description.CloudServiceArgs{ 2070 ProviderId: doc.ProviderId, 2071 Addresses: e.newAddressArgsSlice(doc.Addresses), 2072 } 2073 } 2074 2075 func (e *exporter) readAllCloudContainers() (map[string]*cloudContainerDoc, error) { 2076 cloudContainers, closer := e.st.db().GetCollection(cloudContainersC) 2077 defer closer() 2078 2079 var docs []cloudContainerDoc 2080 err := cloudContainers.Find(nil).All(&docs) 2081 if err != nil { 2082 return nil, errors.Annotate(err, "cannot get all cloud container docs") 2083 } 2084 e.logger.Debugf("found %d cloud container docs", len(docs)) 2085 result := make(map[string]*cloudContainerDoc) 2086 for _, v := range docs { 2087 doc := v 2088 result[e.st.localID(doc.Id)] = &doc 2089 } 2090 return result, nil 2091 } 2092 2093 func (e *exporter) cloudContainer(doc *cloudContainerDoc) *description.CloudContainerArgs { 2094 result := &description.CloudContainerArgs{ 2095 ProviderId: doc.ProviderId, 2096 Ports: doc.Ports, 2097 } 2098 if doc.Address != nil { 2099 result.Address = e.newAddressArgs(*doc.Address) 2100 } 2101 return result 2102 } 2103 2104 func (e *exporter) readLastConnectionTimes() (map[string]time.Time, error) { 2105 lastConnections, closer := e.st.db().GetCollection(modelUserLastConnectionC) 2106 defer closer() 2107 2108 var docs []modelUserLastConnectionDoc 2109 if err := lastConnections.Find(nil).All(&docs); err != nil { 2110 return nil, errors.Trace(err) 2111 } 2112 2113 result := make(map[string]time.Time) 2114 for _, doc := range docs { 2115 result[doc.UserName] = doc.LastConnection.UTC() 2116 } 2117 return result, nil 2118 } 2119 2120 func (e *exporter) readAllAnnotations() error { 2121 e.annotations = make(map[string]annotatorDoc) 2122 if e.cfg.SkipAnnotations { 2123 return nil 2124 } 2125 2126 annotations, closer := e.st.db().GetCollection(annotationsC) 2127 defer closer() 2128 2129 var docs []annotatorDoc 2130 if err := annotations.Find(nil).All(&docs); err != nil { 2131 return errors.Trace(err) 2132 } 2133 e.logger.Debugf("read %d annotations docs", len(docs)) 2134 2135 for _, doc := range docs { 2136 e.annotations[doc.GlobalKey] = doc 2137 } 2138 return nil 2139 } 2140 2141 func (e *exporter) readAllConstraints() error { 2142 constraintsCollection, closer := e.st.db().GetCollection(constraintsC) 2143 defer closer() 2144 2145 // Since the constraintsDoc doesn't include any global key or _id 2146 // fields, we can't just deserialize the entire collection into a slice 2147 // of docs, so we get them all out with bson maps. 2148 var docs []bson.M 2149 err := constraintsCollection.Find(nil).All(&docs) 2150 if err != nil { 2151 return errors.Annotate(err, "failed to read constraints collection") 2152 } 2153 2154 e.logger.Debugf("read %d constraints docs", len(docs)) 2155 e.constraints = make(map[string]bson.M) 2156 for _, doc := range docs { 2157 docId, ok := doc["_id"].(string) 2158 if !ok { 2159 return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"]) 2160 } 2161 id := e.st.localID(docId) 2162 e.constraints[id] = doc 2163 e.logger.Debugf("doc[%q] = %#v", id, doc) 2164 } 2165 return nil 2166 } 2167 2168 // getAnnotations doesn't really care if there are any there or not 2169 // for the key, but if they were there, they are removed so we can 2170 // check at the end of the export for anything we have forgotten. 2171 func (e *exporter) getAnnotations(key string) map[string]string { 2172 result, found := e.annotations[key] 2173 if found { 2174 delete(e.annotations, key) 2175 } 2176 return result.Annotations 2177 } 2178 2179 func (e *exporter) getCharmOrigin(doc applicationDoc, defaultArch string) (description.CharmOriginArgs, error) { 2180 // Everything should be migrated, but in the case that it's not, handle 2181 // that case. 2182 origin := doc.CharmOrigin 2183 2184 // If the channel is empty, then we fall back to the Revision. 2185 // Set default revision to -1. This is because a revision of 0 is 2186 // a valid revision for local charms which we need to be able to 2187 // from. On import, in the -1 case we grab the revision by parsing 2188 // the charm url. 2189 revision := -1 2190 if rev := origin.Revision; rev != nil { 2191 revision = *rev 2192 } 2193 2194 var channel charm.Channel 2195 if origin.Channel != nil { 2196 channel = charm.MakePermissiveChannel(origin.Channel.Track, origin.Channel.Risk, origin.Channel.Branch) 2197 } 2198 // Platform is now mandatory moving forward, so we need to ensure that 2199 // the architecture is set in the platform if it's not set. This 2200 // shouldn't happen that often, but handles clients sending bad requests 2201 // when deploying. 2202 pArch := origin.Platform.Architecture 2203 if pArch == "" { 2204 e.logger.Debugf("using default architecture (%q) for doc[%q]", defaultArch, doc.DocID) 2205 pArch = defaultArch 2206 } 2207 platform := corecharm.Platform{ 2208 Architecture: pArch, 2209 OS: origin.Platform.OS, 2210 Channel: origin.Platform.Channel, 2211 } 2212 2213 return description.CharmOriginArgs{ 2214 Source: origin.Source, 2215 ID: origin.ID, 2216 Hash: origin.Hash, 2217 Revision: revision, 2218 Channel: channel.String(), 2219 Platform: platform.String(), 2220 }, nil 2221 } 2222 2223 func (e *exporter) readAllSettings() error { 2224 e.modelSettings = make(map[string]settingsDoc) 2225 if e.cfg.SkipSettings { 2226 return nil 2227 } 2228 2229 settings, closer := e.st.db().GetCollection(settingsC) 2230 defer closer() 2231 2232 var docs []settingsDoc 2233 if err := settings.Find(nil).All(&docs); err != nil { 2234 return errors.Trace(err) 2235 } 2236 2237 for _, doc := range docs { 2238 key := e.st.localID(doc.DocID) 2239 e.modelSettings[key] = doc 2240 } 2241 return nil 2242 } 2243 2244 func (e *exporter) readAllStatuses() error { 2245 statuses, closer := e.st.db().GetCollection(statusesC) 2246 defer closer() 2247 2248 var docs []bson.M 2249 err := statuses.Find(nil).All(&docs) 2250 if err != nil { 2251 return errors.Annotate(err, "failed to read status collection") 2252 } 2253 2254 e.logger.Debugf("read %d status documents", len(docs)) 2255 e.status = make(map[string]bson.M) 2256 for _, doc := range docs { 2257 docId, ok := doc["_id"].(string) 2258 if !ok { 2259 return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"]) 2260 } 2261 id := e.st.localID(docId) 2262 e.status[id] = doc 2263 } 2264 2265 return nil 2266 } 2267 2268 func (e *exporter) readAllStatusHistory() error { 2269 statuses, closer := e.st.db().GetCollection(statusesHistoryC) 2270 defer closer() 2271 2272 count := 0 2273 e.statusHistory = make(map[string][]historicalStatusDoc) 2274 if e.cfg.SkipStatusHistory { 2275 return nil 2276 } 2277 var doc historicalStatusDoc 2278 // In tests, sorting by time can leave the results 2279 // underconstrained - include document id for deterministic 2280 // ordering in those cases. 2281 iter := statuses.Find(nil).Sort("-updated", "-_id").Iter() 2282 defer func() { _ = iter.Close() }() 2283 for iter.Next(&doc) { 2284 history := e.statusHistory[doc.GlobalKey] 2285 e.statusHistory[doc.GlobalKey] = append(history, doc) 2286 count++ 2287 } 2288 2289 if err := iter.Close(); err != nil { 2290 return errors.Annotate(err, "failed to read status history collection") 2291 } 2292 2293 e.logger.Debugf("read %d status history documents", count) 2294 2295 return nil 2296 } 2297 2298 func (e *exporter) statusArgs(globalKey string) (description.StatusArgs, error) { 2299 result := description.StatusArgs{} 2300 statusDoc, found := e.status[globalKey] 2301 if !found { 2302 return result, errors.NotFoundf("status data for %s", globalKey) 2303 } 2304 delete(e.status, globalKey) 2305 2306 status, ok := statusDoc["status"].(string) 2307 if !ok { 2308 return result, errors.Errorf("expected string for status, got %T", statusDoc["status"]) 2309 } 2310 info, ok := statusDoc["statusinfo"].(string) 2311 if !ok { 2312 return result, errors.Errorf("expected string for statusinfo, got %T", statusDoc["statusinfo"]) 2313 } 2314 // data is an embedded map and comes out as a bson.M 2315 // A bson.M is map[string]interface{}, so we can type cast it. 2316 data, ok := statusDoc["statusdata"].(bson.M) 2317 if !ok { 2318 return result, errors.Errorf("expected map for data, got %T", statusDoc["statusdata"]) 2319 } 2320 dataMap := map[string]interface{}(data) 2321 updated, ok := statusDoc["updated"].(int64) 2322 if !ok { 2323 return result, errors.Errorf("expected int64 for updated, got %T", statusDoc["updated"]) 2324 } 2325 2326 result.Value = status 2327 result.Message = info 2328 result.Data = dataMap 2329 result.Updated = time.Unix(0, updated) 2330 return result, nil 2331 } 2332 2333 func (e *exporter) statusHistoryArgs(globalKey string) []description.StatusArgs { 2334 history := e.statusHistory[globalKey] 2335 e.logger.Tracef("found %d status history docs for %s", len(history), globalKey) 2336 if len(history) > maxStatusHistoryEntries { 2337 history = history[:maxStatusHistoryEntries] 2338 } 2339 result := make([]description.StatusArgs, len(history)) 2340 for i, doc := range history { 2341 result[i] = description.StatusArgs{ 2342 Value: string(doc.Status), 2343 Message: doc.StatusInfo, 2344 Data: doc.StatusData, 2345 Updated: time.Unix(0, doc.Updated), 2346 } 2347 } 2348 delete(e.statusHistory, globalKey) 2349 return result 2350 } 2351 2352 func (e *exporter) constraintsArgs(globalKey string) (description.ConstraintsArgs, error) { 2353 doc, found := e.constraints[globalKey] 2354 if !found { 2355 // No constraints for this key. 2356 e.logger.Tracef("no constraints found for key %q", globalKey) 2357 return description.ConstraintsArgs{}, nil 2358 } 2359 // We capture any type error using a closure to avoid having to return 2360 // multiple values from the optional functions. This does mean that we will 2361 // only report on the last one, but that is fine as there shouldn't be any. 2362 var optionalErr error 2363 optionalString := func(name string) string { 2364 switch value := doc[name].(type) { 2365 case nil: 2366 case string: 2367 return value 2368 default: 2369 optionalErr = errors.Errorf("expected string for %s, got %T", name, value) 2370 } 2371 return "" 2372 } 2373 optionalInt := func(name string) uint64 { 2374 switch value := doc[name].(type) { 2375 case nil: 2376 case uint64: 2377 return value 2378 case int64: 2379 return uint64(value) 2380 default: 2381 optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value) 2382 } 2383 return 0 2384 } 2385 optionalStringSlice := func(name string) []string { 2386 switch value := doc[name].(type) { 2387 case nil: 2388 case []string: 2389 return value 2390 case []interface{}: 2391 var result []string 2392 for _, val := range value { 2393 sval, ok := val.(string) 2394 if !ok { 2395 optionalErr = errors.Errorf("expected string slice for %s, got %T value", name, val) 2396 return nil 2397 } 2398 result = append(result, sval) 2399 } 2400 return result 2401 default: 2402 optionalErr = errors.Errorf("expected []string for %s, got %T", name, value) 2403 } 2404 return nil 2405 } 2406 optionalBool := func(name string) bool { 2407 switch value := doc[name].(type) { 2408 case nil: 2409 case bool: 2410 return value 2411 default: 2412 optionalErr = errors.Errorf("expected bool for %s, got %T", name, value) 2413 } 2414 return false 2415 } 2416 result := description.ConstraintsArgs{ 2417 AllocatePublicIP: optionalBool("allocatepublicip"), 2418 Architecture: optionalString("arch"), 2419 Container: optionalString("container"), 2420 CpuCores: optionalInt("cpucores"), 2421 CpuPower: optionalInt("cpupower"), 2422 ImageID: optionalString("imageid"), 2423 InstanceType: optionalString("instancetype"), 2424 Memory: optionalInt("mem"), 2425 RootDisk: optionalInt("rootdisk"), 2426 RootDiskSource: optionalString("rootdisksource"), 2427 Spaces: optionalStringSlice("spaces"), 2428 Tags: optionalStringSlice("tags"), 2429 VirtType: optionalString("virttype"), 2430 Zones: optionalStringSlice("zones"), 2431 } 2432 if optionalErr != nil { 2433 return description.ConstraintsArgs{}, errors.Trace(optionalErr) 2434 } 2435 return result, nil 2436 } 2437 2438 func (e *exporter) checkUnexportedValues() error { 2439 if e.cfg.IgnoreIncompleteModel { 2440 return nil 2441 } 2442 2443 var missing []string 2444 2445 // As annotations are saved into the model, they are removed from the 2446 // exporter's map. If there are any left at the end, we are missing 2447 // things. 2448 for key, doc := range e.annotations { 2449 missing = append(missing, fmt.Sprintf("unexported annotations for %s, %s", doc.Tag, key)) 2450 } 2451 2452 for key := range e.modelSettings { 2453 missing = append(missing, fmt.Sprintf("unexported settings for %s", key)) 2454 } 2455 2456 for key := range e.status { 2457 if !e.cfg.SkipInstanceData && !strings.HasSuffix(key, "#instance") { 2458 missing = append(missing, fmt.Sprintf("unexported status for %s", key)) 2459 } 2460 } 2461 2462 for key := range e.statusHistory { 2463 if !e.cfg.SkipInstanceData && !(strings.HasSuffix(key, "#instance") || strings.HasSuffix(key, "#modification")) { 2464 missing = append(missing, fmt.Sprintf("unexported status history for %s", key)) 2465 } 2466 } 2467 2468 if len(missing) > 0 { 2469 content := strings.Join(missing, "\n ") 2470 return errors.Errorf("migration missed some docs:\n %s", content) 2471 } 2472 return nil 2473 } 2474 2475 func (e *exporter) remoteApplications() error { 2476 e.logger.Debugf("read remote applications") 2477 migration := &ExportStateMigration{ 2478 src: e.st, 2479 dst: e.model, 2480 exporter: e, 2481 } 2482 migration.Add(func() error { 2483 m := migrations.ExportRemoteApplications{} 2484 return m.Execute(remoteApplicationsShim{ 2485 st: migration.src, 2486 exporter: e, 2487 }, migration.dst) 2488 }) 2489 return migration.Run() 2490 } 2491 2492 // remoteApplicationsShim is to handle the fact that go doesn't handle covariance 2493 // and the tight abstraction around the new migration export work ensures that 2494 // we handle our dependencies up front. 2495 type remoteApplicationsShim struct { 2496 st *State 2497 exporter *exporter 2498 } 2499 2500 // AllRemoteApplications returns all remote applications in the model. 2501 func (s remoteApplicationsShim) AllRemoteApplications() ([]migrations.MigrationRemoteApplication, error) { 2502 remoteApps, err := s.st.AllRemoteApplications() 2503 if err != nil { 2504 return nil, errors.Trace(err) 2505 } 2506 result := make([]migrations.MigrationRemoteApplication, len(remoteApps)) 2507 for k, v := range remoteApps { 2508 result[k] = remoteApplicationShim{RemoteApplication: v} 2509 } 2510 return result, nil 2511 } 2512 2513 func (s remoteApplicationsShim) StatusArgs(key string) (description.StatusArgs, error) { 2514 return s.exporter.statusArgs(key) 2515 } 2516 2517 type remoteApplicationShim struct { 2518 *RemoteApplication 2519 } 2520 2521 func (s remoteApplicationShim) Endpoints() ([]migrations.MigrationRemoteEndpoint, error) { 2522 endpoints, err := s.RemoteApplication.Endpoints() 2523 if err != nil { 2524 return nil, errors.Trace(err) 2525 } 2526 result := make([]migrations.MigrationRemoteEndpoint, len(endpoints)) 2527 for k, v := range endpoints { 2528 result[k] = migrations.MigrationRemoteEndpoint{ 2529 Name: v.Name, 2530 Role: v.Role, 2531 Interface: v.Interface, 2532 } 2533 } 2534 return result, nil 2535 } 2536 2537 func (s remoteApplicationShim) Spaces() []migrations.MigrationRemoteSpace { 2538 spaces := s.RemoteApplication.Spaces() 2539 result := make([]migrations.MigrationRemoteSpace, len(spaces)) 2540 for k, v := range spaces { 2541 subnets := make([]migrations.MigrationRemoteSubnet, len(v.Subnets)) 2542 for k, v := range v.Subnets { 2543 subnets[k] = migrations.MigrationRemoteSubnet{ 2544 CIDR: v.CIDR, 2545 ProviderId: v.ProviderId, 2546 VLANTag: v.VLANTag, 2547 AvailabilityZones: v.AvailabilityZones, 2548 ProviderSpaceId: v.ProviderSpaceId, 2549 ProviderNetworkId: v.ProviderNetworkId, 2550 } 2551 } 2552 result[k] = migrations.MigrationRemoteSpace{ 2553 CloudType: v.CloudType, 2554 Name: v.Name, 2555 ProviderId: v.ProviderId, 2556 ProviderAttributes: v.ProviderAttributes, 2557 Subnets: subnets, 2558 } 2559 } 2560 return result 2561 } 2562 2563 func (s remoteApplicationShim) GlobalKey() string { 2564 return s.RemoteApplication.globalKey() 2565 } 2566 2567 // Macaroon returns the encoded macaroon JSON. 2568 func (s remoteApplicationShim) Macaroon() string { 2569 return s.RemoteApplication.doc.Macaroon 2570 } 2571 2572 func (e *exporter) storage() error { 2573 if err := e.volumes(); err != nil { 2574 return errors.Trace(err) 2575 } 2576 if err := e.filesystems(); err != nil { 2577 return errors.Trace(err) 2578 } 2579 if err := e.storageInstances(); err != nil { 2580 return errors.Trace(err) 2581 } 2582 if err := e.storagePools(); err != nil { 2583 return errors.Trace(err) 2584 } 2585 return nil 2586 } 2587 2588 func (e *exporter) volumes() error { 2589 coll, closer := e.st.db().GetCollection(volumesC) 2590 defer closer() 2591 2592 attachments, err := e.readVolumeAttachments() 2593 if err != nil { 2594 return errors.Trace(err) 2595 } 2596 2597 attachmentPlans, err := e.readVolumeAttachmentPlans() 2598 if err != nil { 2599 return errors.Trace(err) 2600 } 2601 2602 var doc volumeDoc 2603 iter := coll.Find(nil).Sort("_id").Iter() 2604 defer func() { _ = iter.Close() }() 2605 for iter.Next(&doc) { 2606 vol := &volume{e.st, doc} 2607 plan := attachmentPlans[doc.Name] 2608 if err := e.addVolume(vol, attachments[doc.Name], plan); err != nil { 2609 return errors.Trace(err) 2610 } 2611 } 2612 if err := iter.Close(); err != nil { 2613 return errors.Annotate(err, "failed to read volumes") 2614 } 2615 return nil 2616 } 2617 2618 func (e *exporter) addVolume(vol *volume, volAttachments []volumeAttachmentDoc, attachmentPlans []volumeAttachmentPlanDoc) error { 2619 args := description.VolumeArgs{ 2620 Tag: vol.VolumeTag(), 2621 } 2622 if tag, err := vol.StorageInstance(); err == nil { 2623 // only returns an error when no storage tag. 2624 args.Storage = tag 2625 } else { 2626 if !errors.IsNotAssigned(err) { 2627 // This is an unexpected error. 2628 return errors.Trace(err) 2629 } 2630 } 2631 logger.Debugf("addVolume: %#v", vol.doc) 2632 if info, err := vol.Info(); err == nil { 2633 logger.Debugf(" info %#v", info) 2634 args.Provisioned = true 2635 args.Size = info.Size 2636 args.Pool = info.Pool 2637 args.HardwareID = info.HardwareId 2638 args.WWN = info.WWN 2639 args.VolumeID = info.VolumeId 2640 args.Persistent = info.Persistent 2641 } else { 2642 params, _ := vol.Params() 2643 logger.Debugf(" params %#v", params) 2644 args.Size = params.Size 2645 args.Pool = params.Pool 2646 } 2647 2648 globalKey := vol.globalKey() 2649 statusArgs, err := e.statusArgs(globalKey) 2650 if err != nil { 2651 return errors.Annotatef(err, "status for volume %s", vol.doc.Name) 2652 } 2653 2654 exVolume := e.model.AddVolume(args) 2655 exVolume.SetStatus(statusArgs) 2656 exVolume.SetStatusHistory(e.statusHistoryArgs(globalKey)) 2657 if count := len(volAttachments); count != vol.doc.AttachmentCount { 2658 return errors.Errorf("volume attachment count mismatch, have %d, expected %d", 2659 count, vol.doc.AttachmentCount) 2660 } 2661 for _, doc := range volAttachments { 2662 va := volumeAttachment{doc} 2663 logger.Debugf(" attachment %#v", doc) 2664 args := description.VolumeAttachmentArgs{ 2665 Host: va.Host(), 2666 } 2667 if info, err := va.Info(); err == nil { 2668 logger.Debugf(" info %#v", info) 2669 args.Provisioned = true 2670 args.ReadOnly = info.ReadOnly 2671 args.DeviceName = info.DeviceName 2672 args.DeviceLink = info.DeviceLink 2673 args.BusAddress = info.BusAddress 2674 if info.PlanInfo != nil { 2675 args.DeviceType = string(info.PlanInfo.DeviceType) 2676 args.DeviceAttributes = info.PlanInfo.DeviceAttributes 2677 } 2678 } else { 2679 params, _ := va.Params() 2680 logger.Debugf(" params %#v", params) 2681 args.ReadOnly = params.ReadOnly 2682 } 2683 exVolume.AddAttachment(args) 2684 } 2685 2686 for _, doc := range attachmentPlans { 2687 va := volumeAttachmentPlan{doc} 2688 logger.Debugf(" attachment plan %#v", doc) 2689 args := description.VolumeAttachmentPlanArgs{ 2690 Machine: va.Machine(), 2691 } 2692 if info, err := va.PlanInfo(); err == nil { 2693 logger.Debugf(" plan info %#v", info) 2694 args.DeviceType = string(info.DeviceType) 2695 args.DeviceAttributes = info.DeviceAttributes 2696 } else if !errors.IsNotFound(err) { 2697 return errors.Trace(err) 2698 } 2699 if info, err := va.BlockDeviceInfo(); err == nil { 2700 logger.Debugf(" block device info %#v", info) 2701 args.DeviceName = info.DeviceName 2702 args.DeviceLinks = info.DeviceLinks 2703 args.Label = info.Label 2704 args.UUID = info.UUID 2705 args.HardwareId = info.HardwareId 2706 args.WWN = info.WWN 2707 args.BusAddress = info.BusAddress 2708 args.Size = info.Size 2709 args.FilesystemType = info.FilesystemType 2710 args.InUse = info.InUse 2711 args.MountPoint = info.MountPoint 2712 } else if !errors.IsNotFound(err) { 2713 return errors.Trace(err) 2714 } 2715 exVolume.AddAttachmentPlan(args) 2716 } 2717 return nil 2718 } 2719 2720 func (e *exporter) readVolumeAttachments() (map[string][]volumeAttachmentDoc, error) { 2721 coll, closer := e.st.db().GetCollection(volumeAttachmentsC) 2722 defer closer() 2723 2724 result := make(map[string][]volumeAttachmentDoc) 2725 var doc volumeAttachmentDoc 2726 var count int 2727 iter := coll.Find(nil).Iter() 2728 defer func() { _ = iter.Close() }() 2729 for iter.Next(&doc) { 2730 result[doc.Volume] = append(result[doc.Volume], doc) 2731 count++ 2732 } 2733 if err := iter.Close(); err != nil { 2734 return nil, errors.Annotate(err, "failed to read volumes attachments") 2735 } 2736 e.logger.Debugf("read %d volume attachment documents", count) 2737 return result, nil 2738 } 2739 2740 func (e *exporter) readVolumeAttachmentPlans() (map[string][]volumeAttachmentPlanDoc, error) { 2741 coll, closer := e.st.db().GetCollection(volumeAttachmentPlanC) 2742 defer closer() 2743 2744 result := make(map[string][]volumeAttachmentPlanDoc) 2745 var doc volumeAttachmentPlanDoc 2746 var count int 2747 iter := coll.Find(nil).Iter() 2748 defer func() { _ = iter.Close() }() 2749 for iter.Next(&doc) { 2750 result[doc.Volume] = append(result[doc.Volume], doc) 2751 count++ 2752 } 2753 if err := iter.Close(); err != nil { 2754 return nil, errors.Annotate(err, "failed to read volume attachment plans") 2755 } 2756 e.logger.Debugf("read %d volume attachment plan documents", count) 2757 return result, nil 2758 } 2759 2760 func (e *exporter) filesystems() error { 2761 coll, closer := e.st.db().GetCollection(filesystemsC) 2762 defer closer() 2763 2764 attachments, err := e.readFilesystemAttachments() 2765 if err != nil { 2766 return errors.Trace(err) 2767 } 2768 var doc filesystemDoc 2769 iter := coll.Find(nil).Sort("_id").Iter() 2770 defer func() { _ = iter.Close() }() 2771 for iter.Next(&doc) { 2772 fs := &filesystem{e.st, doc} 2773 if err := e.addFilesystem(fs, attachments[doc.FilesystemId]); err != nil { 2774 return errors.Trace(err) 2775 } 2776 } 2777 if err := iter.Close(); err != nil { 2778 return errors.Annotate(err, "failed to read filesystems") 2779 } 2780 return nil 2781 } 2782 2783 func (e *exporter) addFilesystem(fs *filesystem, fsAttachments []filesystemAttachmentDoc) error { 2784 // Here we don't care about the cases where the filesystem is not assigned to storage instances 2785 // nor no backing volues. In both those situations we have empty tags. 2786 storage, _ := fs.Storage() 2787 volume, _ := fs.Volume() 2788 args := description.FilesystemArgs{ 2789 Tag: fs.FilesystemTag(), 2790 Storage: storage, 2791 Volume: volume, 2792 } 2793 logger.Debugf("addFilesystem: %#v", fs.doc) 2794 if info, err := fs.Info(); err == nil { 2795 logger.Debugf(" info %#v", info) 2796 args.Provisioned = true 2797 args.Size = info.Size 2798 args.Pool = info.Pool 2799 args.FilesystemID = info.FilesystemId 2800 } else { 2801 params, _ := fs.Params() 2802 logger.Debugf(" params %#v", params) 2803 args.Size = params.Size 2804 args.Pool = params.Pool 2805 } 2806 2807 globalKey := fs.globalKey() 2808 statusArgs, err := e.statusArgs(globalKey) 2809 if err != nil { 2810 return errors.Annotatef(err, "status for filesystem %s", fs.doc.FilesystemId) 2811 } 2812 2813 exFilesystem := e.model.AddFilesystem(args) 2814 exFilesystem.SetStatus(statusArgs) 2815 exFilesystem.SetStatusHistory(e.statusHistoryArgs(globalKey)) 2816 if count := len(fsAttachments); count != fs.doc.AttachmentCount { 2817 return errors.Errorf("filesystem attachment count mismatch, have %d, expected %d", 2818 count, fs.doc.AttachmentCount) 2819 } 2820 for _, doc := range fsAttachments { 2821 va := filesystemAttachment{doc} 2822 logger.Debugf(" attachment %#v", doc) 2823 args := description.FilesystemAttachmentArgs{ 2824 Host: va.Host(), 2825 } 2826 if info, err := va.Info(); err == nil { 2827 logger.Debugf(" info %#v", info) 2828 args.Provisioned = true 2829 args.ReadOnly = info.ReadOnly 2830 args.MountPoint = info.MountPoint 2831 } else { 2832 params, _ := va.Params() 2833 logger.Debugf(" params %#v", params) 2834 args.ReadOnly = params.ReadOnly 2835 args.MountPoint = params.Location 2836 } 2837 exFilesystem.AddAttachment(args) 2838 } 2839 return nil 2840 } 2841 2842 func (e *exporter) readFilesystemAttachments() (map[string][]filesystemAttachmentDoc, error) { 2843 coll, closer := e.st.db().GetCollection(filesystemAttachmentsC) 2844 defer closer() 2845 2846 result := make(map[string][]filesystemAttachmentDoc) 2847 var doc filesystemAttachmentDoc 2848 var count int 2849 iter := coll.Find(nil).Iter() 2850 defer func() { _ = iter.Close() }() 2851 for iter.Next(&doc) { 2852 result[doc.Filesystem] = append(result[doc.Filesystem], doc) 2853 count++ 2854 } 2855 if err := iter.Close(); err != nil { 2856 return nil, errors.Annotate(err, "failed to read filesystem attachments") 2857 } 2858 e.logger.Debugf("read %d filesystem attachment documents", count) 2859 return result, nil 2860 } 2861 2862 func (e *exporter) storageInstances() error { 2863 sb, err := NewStorageBackend(e.st) 2864 if err != nil { 2865 return errors.Trace(err) 2866 } 2867 coll, closer := e.st.db().GetCollection(storageInstancesC) 2868 defer closer() 2869 2870 attachments, err := e.readStorageAttachments() 2871 if err != nil { 2872 return errors.Trace(err) 2873 } 2874 var doc storageInstanceDoc 2875 iter := coll.Find(nil).Sort("_id").Iter() 2876 defer func() { _ = iter.Close() }() 2877 for iter.Next(&doc) { 2878 instance := &storageInstance{sb, doc} 2879 if err := e.addStorage(instance, attachments[doc.Id]); err != nil { 2880 return errors.Trace(err) 2881 } 2882 } 2883 if err := iter.Close(); err != nil { 2884 return errors.Annotate(err, "failed to read storage instances") 2885 } 2886 return nil 2887 } 2888 2889 func (e *exporter) addStorage(instance *storageInstance, attachments []names.UnitTag) error { 2890 owner, ok := instance.Owner() 2891 if !ok { 2892 owner = nil 2893 } 2894 cons := description.StorageInstanceConstraints(instance.doc.Constraints) 2895 args := description.StorageArgs{ 2896 Tag: instance.StorageTag(), 2897 Kind: instance.Kind().String(), 2898 Owner: owner, 2899 Name: instance.StorageName(), 2900 Attachments: attachments, 2901 Constraints: &cons, 2902 } 2903 e.model.AddStorage(args) 2904 return nil 2905 } 2906 2907 func (e *exporter) readStorageAttachments() (map[string][]names.UnitTag, error) { 2908 coll, closer := e.st.db().GetCollection(storageAttachmentsC) 2909 defer closer() 2910 2911 result := make(map[string][]names.UnitTag) 2912 var doc storageAttachmentDoc 2913 var count int 2914 iter := coll.Find(nil).Iter() 2915 defer func() { _ = iter.Close() }() 2916 for iter.Next(&doc) { 2917 unit := names.NewUnitTag(doc.Unit) 2918 result[doc.StorageInstance] = append(result[doc.StorageInstance], unit) 2919 count++ 2920 } 2921 if err := iter.Close(); err != nil { 2922 return nil, errors.Annotate(err, "failed to read storage attachments") 2923 } 2924 e.logger.Debugf("read %d storage attachment documents", count) 2925 return result, nil 2926 } 2927 2928 func (e *exporter) storagePools() error { 2929 registry, err := e.st.storageProviderRegistry() 2930 if err != nil { 2931 return errors.Annotate(err, "getting provider registry") 2932 } 2933 pm := poolmanager.New(storagePoolSettingsManager{e: e}, registry) 2934 poolConfigs, err := pm.List() 2935 if err != nil { 2936 return errors.Annotate(err, "listing pools") 2937 } 2938 for _, cfg := range poolConfigs { 2939 e.model.AddStoragePool(description.StoragePoolArgs{ 2940 Name: cfg.Name(), 2941 Provider: string(cfg.Provider()), 2942 Attributes: cfg.Attrs(), 2943 }) 2944 } 2945 return nil 2946 } 2947 2948 func (e *exporter) groupOffersByApplicationName() (map[string][]*crossmodel.ApplicationOffer, error) { 2949 if e.cfg.SkipApplicationOffers { 2950 return nil, nil 2951 } 2952 2953 offerList, err := NewApplicationOffers(e.st).AllApplicationOffers() 2954 if err != nil { 2955 return nil, errors.Annotate(err, "listing offers") 2956 } 2957 2958 if len(offerList) == 0 { 2959 return nil, nil 2960 } 2961 2962 appMap := make(map[string][]*crossmodel.ApplicationOffer) 2963 for _, offer := range offerList { 2964 appMap[offer.ApplicationName] = append(appMap[offer.ApplicationName], offer) 2965 } 2966 return appMap, nil 2967 } 2968 2969 type storagePoolSettingsManager struct { 2970 poolmanager.SettingsManager 2971 e *exporter 2972 } 2973 2974 func (m storagePoolSettingsManager) ListSettings(keyPrefix string) (map[string]map[string]interface{}, error) { 2975 result := make(map[string]map[string]interface{}) 2976 for key, doc := range m.e.modelSettings { 2977 if strings.HasPrefix(key, keyPrefix) { 2978 result[key] = doc.Settings 2979 delete(m.e.modelSettings, key) 2980 } 2981 } 2982 return result, nil 2983 }