github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/migration_import.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 "encoding/hex" 8 "fmt" 9 "reflect" 10 "strconv" 11 "time" 12 13 "github.com/juju/charm/v12" 14 "github.com/juju/collections/set" 15 "github.com/juju/collections/transform" 16 "github.com/juju/description/v5" 17 "github.com/juju/errors" 18 "github.com/juju/loggo" 19 "github.com/juju/mgo/v3/bson" 20 "github.com/juju/mgo/v3/txn" 21 "github.com/juju/names/v5" 22 "github.com/juju/version/v2" 23 24 "github.com/juju/juju/cloud" 25 "github.com/juju/juju/controller" 26 corebase "github.com/juju/juju/core/base" 27 corecharm "github.com/juju/juju/core/charm" 28 "github.com/juju/juju/core/constraints" 29 "github.com/juju/juju/core/container" 30 "github.com/juju/juju/core/instance" 31 "github.com/juju/juju/core/network" 32 "github.com/juju/juju/core/payloads" 33 "github.com/juju/juju/core/permission" 34 "github.com/juju/juju/core/status" 35 "github.com/juju/juju/environs/config" 36 secretsprovider "github.com/juju/juju/secrets/provider" 37 "github.com/juju/juju/state/cloudimagemetadata" 38 "github.com/juju/juju/storage" 39 "github.com/juju/juju/storage/poolmanager" 40 "github.com/juju/juju/storage/provider" 41 "github.com/juju/juju/tools" 42 ) 43 44 // Import the database agnostic model representation into the database. 45 func (ctrl *Controller) Import(model description.Model) (_ *Model, _ *State, err error) { 46 st, err := ctrl.pool.SystemState() 47 if err != nil { 48 return nil, nil, errors.Trace(err) 49 } 50 modelUUID := model.Tag().Id() 51 logger := loggo.GetLogger("juju.state.import-model") 52 logger.Debugf("import starting for model %s", modelUUID) 53 54 // At this stage, attempting to import a model with the same 55 // UUID as an existing model will error. 56 if modelExists, err := st.ModelExists(modelUUID); err != nil { 57 return nil, nil, errors.Trace(err) 58 } else if modelExists { 59 // We have an existing matching model. 60 return nil, nil, errors.AlreadyExistsf("model %s", modelUUID) 61 } 62 63 // Unfortunately a version was released that exports v4 models 64 // with the Type field blank. Treat this as IAAS. 65 modelType := ModelTypeIAAS 66 if model.Type() != "" { 67 modelType, err = ParseModelType(model.Type()) 68 if err != nil { 69 return nil, nil, errors.Trace(err) 70 } 71 } 72 73 // Create the model. 74 cfg, err := modelConfig(model.Config()) 75 if err != nil { 76 return nil, nil, errors.Trace(err) 77 } 78 args := ModelArgs{ 79 Type: modelType, 80 CloudName: model.Cloud(), 81 CloudRegion: model.CloudRegion(), 82 Config: cfg, 83 Owner: model.Owner(), 84 MigrationMode: MigrationModeImporting, 85 EnvironVersion: model.EnvironVersion(), 86 PasswordHash: model.PasswordHash(), 87 StorageProviderRegistry: storage.StaticProviderRegistry{}, 88 } 89 if creds := model.CloudCredential(); creds != nil { 90 // Need to add credential or make sure an existing credential 91 // matches. 92 // TODO: there really should be a way to create a cloud credential 93 // tag in the names package from the cloud, owner and name. 94 credID := fmt.Sprintf("%s/%s/%s", creds.Cloud(), creds.Owner(), creds.Name()) 95 if !names.IsValidCloudCredential(credID) { 96 return nil, nil, errors.NotValidf("cloud credential ID %q", credID) 97 } 98 credTag := names.NewCloudCredentialTag(credID) 99 100 existingCreds, err := st.CloudCredential(credTag) 101 102 if errors.IsNotFound(err) { 103 credential := cloud.NewCredential( 104 cloud.AuthType(creds.AuthType()), 105 creds.Attributes()) 106 if err := st.UpdateCloudCredential(credTag, credential); err != nil { 107 return nil, nil, errors.Trace(err) 108 } 109 } else if err != nil { 110 return nil, nil, errors.Trace(err) 111 } else { 112 // ensure existing creds match 113 if existingCreds.AuthType != creds.AuthType() { 114 return nil, nil, errors.Errorf("credential auth type mismatch: %q != %q", existingCreds.AuthType, creds.AuthType()) 115 } 116 if !reflect.DeepEqual(existingCreds.Attributes, creds.Attributes()) { 117 return nil, nil, errors.Errorf("credential attribute mismatch: %v != %v", existingCreds.Attributes, creds.Attributes()) 118 } 119 if existingCreds.Revoked { 120 return nil, nil, errors.Errorf("credential %q is revoked", credID) 121 } 122 } 123 124 args.CloudCredential = credTag 125 } 126 dbModel, newSt, err := ctrl.NewModel(args) 127 if err != nil { 128 return nil, nil, errors.Trace(err) 129 } 130 logger.Debugf("model created %s/%s", dbModel.Owner().Id(), dbModel.Name()) 131 defer func() { 132 if err != nil { 133 newSt.Close() 134 } 135 }() 136 137 // We don't actually care what the old model status was, because we are 138 // going to set it to busy, with a message of migrating. 139 if err := dbModel.SetStatus(status.StatusInfo{ 140 Status: status.Busy, 141 Message: "importing", 142 }); err != nil { 143 return nil, nil, errors.Trace(err) 144 } 145 146 // I would have loved to use import, but that is a reserved word. 147 restore := importer{ 148 st: newSt, 149 dbModel: dbModel, 150 model: model, 151 logger: logger, 152 } 153 if err := restore.sequences(); err != nil { 154 return nil, nil, errors.Annotate(err, "sequences") 155 } 156 // We need to import the sequences first as we may add blocks 157 // in the modelExtras which will touch the block sequence. 158 if err := restore.modelExtras(); err != nil { 159 return nil, nil, errors.Annotate(err, "base model aspects") 160 } 161 if err := newSt.SetModelConstraints(restore.constraints(model.Constraints())); err != nil { 162 return nil, nil, errors.Annotate(err, "model constraints") 163 } 164 if err := restore.sshHostKeys(); err != nil { 165 return nil, nil, errors.Annotate(err, "sshHostKeys") 166 } 167 if err := restore.cloudimagemetadata(); err != nil { 168 return nil, nil, errors.Annotate(err, "cloudimagemetadata") 169 } 170 if err := restore.actions(); err != nil { 171 return nil, nil, errors.Annotate(err, "actions") 172 } 173 if err := restore.operations(); err != nil { 174 return nil, nil, errors.Annotate(err, "operations") 175 } 176 177 if err := restore.modelUsers(); err != nil { 178 return nil, nil, errors.Annotate(err, "modelUsers") 179 } 180 // Spaces are needed to migrate Subnets 181 if err := restore.spaces(); err != nil { 182 return nil, nil, errors.Annotate(err, "spaces") 183 } 184 // Subnets are needed to migrate machine portsDocs 185 if err := restore.subnets(); err != nil { 186 return nil, nil, errors.Annotate(err, "subnets") 187 } 188 if err := restore.machines(); err != nil { 189 return nil, nil, errors.Annotate(err, "machines") 190 } 191 if err := restore.applications(); err != nil { 192 return nil, nil, errors.Annotate(err, "applications") 193 } 194 if err := restore.remoteApplications(); err != nil { 195 return nil, nil, errors.Annotate(err, "remoteapplications") 196 } 197 if err := restore.firewallRules(); err != nil { 198 return nil, nil, errors.Annotate(err, "firewallrules") 199 } 200 if err := restore.relations(); err != nil { 201 return nil, nil, errors.Annotate(err, "relations") 202 } 203 if err := restore.remoteEntities(); err != nil { 204 return nil, nil, errors.Annotate(err, "remoteentitites") 205 } 206 if err := restore.externalControllers(); err != nil { 207 return nil, nil, errors.Annotate(err, "externalcontrollers") 208 } 209 if err := restore.relationNetworks(); err != nil { 210 return nil, nil, errors.Annotate(err, "relationnetworks") 211 } 212 if err := restore.linklayerdevices(); err != nil { 213 return nil, nil, errors.Annotate(err, "linklayerdevices") 214 } 215 if err := restore.ipAddresses(); err != nil { 216 return nil, nil, errors.Annotate(err, "ipAddresses") 217 } 218 if err := restore.storage(); err != nil { 219 return nil, nil, errors.Annotate(err, "storage") 220 } 221 if err := restore.secretBackend(); err != nil { 222 return nil, nil, errors.Annotate(err, "secret backend") 223 } 224 if err := restore.secrets(); err != nil { 225 return nil, nil, errors.Annotate(err, "secrets") 226 } 227 if err := restore.remoteSecrets(); err != nil { 228 return nil, nil, errors.Annotate(err, "remote secrets") 229 } 230 231 // NOTE: at the end of the import make sure that the mode of the model 232 // is set to "imported" not "active" (or whatever we call it). This way 233 // we don't start model workers for it before the migration process 234 // is complete. 235 236 // Update the sequences to match that the source. 237 238 if err := dbModel.SetSLA( 239 model.SLA().Level(), 240 model.SLA().Owner(), 241 []byte(model.SLA().Credentials()), 242 ); err != nil { 243 return nil, nil, errors.Trace(err) 244 } 245 246 if MeterStatusFromString(model.MeterStatus().Code()).String() != MeterNotAvailable.String() { 247 if err := dbModel.SetMeterStatus(model.MeterStatus().Code(), model.MeterStatus().Info()); err != nil { 248 return nil, nil, errors.Trace(err) 249 } 250 } 251 252 logger.Debugf("import success") 253 return dbModel, newSt, nil 254 } 255 256 // modelConfig creates a config for the model being imported. 257 func modelConfig(attrs map[string]interface{}) (*config.Config, error) { 258 // If the tools version is before 2.9.35, the default-series 259 // value is cleared. This matches an upgrade step for 2.9.35 260 // as well. Ensuring that the default-series value is set by 261 // the user rather than a hold over from an old juju set value. 262 // Related to using the default-series value in the same way 263 // as a series flag at deploy. 264 v, ok := attrs[config.AgentVersionKey].(string) 265 if !ok { 266 return nil, errors.New("model config missing agent-version") 267 } 268 toolsVersion, err := version.Parse(v) 269 if err != nil { 270 return nil, errors.Trace(err) 271 } 272 // Using MustParse as the value parsed will never change. 273 newer := version.MustParse("2.9.35") 274 if comp := toolsVersion.Compare(newer); comp < 0 { 275 attrs[config.DefaultBaseKey] = "" 276 delete(attrs, config.DefaultSeriesKey) 277 } 278 279 if v, ok := attrs[config.DefaultSeriesKey]; ok { 280 if v == "" { 281 attrs[config.DefaultBaseKey] = "" 282 } else { 283 s, err := corebase.GetBaseFromSeries(v.(string)) 284 if err != nil { 285 return nil, errors.Trace(err) 286 } 287 attrs[config.DefaultBaseKey] = s.String() 288 } 289 delete(attrs, config.DefaultSeriesKey) 290 } 291 292 // Ensure the expected default secret-backend value is set. 293 if v, ok := attrs[config.SecretBackendKey].(string); v == "" || !ok { 294 attrs[config.SecretBackendKey] = config.DefaultSecretBackend 295 } 296 297 return config.New(config.NoDefaults, attrs) 298 } 299 300 // ImportStateMigration defines a migration for importing various entities from 301 // a source description model to the destination state. 302 // It accumulates a series of migrations to Run at a later time. 303 // Running the state migration visits all the migrations and exits upon seeing 304 // the first error from the migration. 305 type ImportStateMigration struct { 306 src description.Model 307 dst Database 308 knownSecretBackends set.Strings 309 migrations []func() error 310 } 311 312 // Add adds a migration to execute at a later time 313 // Return error from the addition will cause the Run to terminate early. 314 func (m *ImportStateMigration) Add(f func() error) { 315 m.migrations = append(m.migrations, f) 316 } 317 318 // Run executes all the migrations required to be run. 319 func (m *ImportStateMigration) Run() error { 320 for _, f := range m.migrations { 321 if err := f(); err != nil { 322 return errors.Trace(err) 323 } 324 } 325 return nil 326 } 327 328 type importer struct { 329 st *State 330 dbModel *Model 331 model description.Model 332 logger loggo.Logger 333 // applicationUnits is populated at the end of loading the applications, and is a 334 // map of application name to the units of that application. 335 applicationUnits map[string]map[string]*Unit 336 charmOrigins map[string]*CharmOrigin 337 } 338 339 func (i *importer) modelExtras() error { 340 if latest := i.model.LatestToolsVersion(); latest.String() != version.Zero.String() { 341 if err := i.dbModel.UpdateLatestToolsVersion(latest); err != nil { 342 return errors.Trace(err) 343 } 344 } 345 346 if annotations := i.model.Annotations(); len(annotations) > 0 { 347 if err := i.dbModel.SetAnnotations(i.dbModel, annotations); err != nil { 348 return errors.Trace(err) 349 } 350 } 351 352 blockType := map[string]BlockType{ 353 "destroy-model": DestroyBlock, 354 "remove-object": RemoveBlock, 355 "all-changes": ChangeBlock, 356 } 357 358 for blockName, message := range i.model.Blocks() { 359 block, ok := blockType[blockName] 360 if !ok { 361 return errors.Errorf("unknown block type: %q", blockName) 362 } 363 // We should check that each switch block can be assigned. 364 err := i.st.SwitchBlockOn(block, message) 365 if err != nil { 366 return errors.Trace(err) 367 } 368 } 369 370 if err := i.importStatusHistory(modelGlobalKey, i.model.StatusHistory()); err != nil { 371 return errors.Trace(err) 372 } 373 return nil 374 } 375 376 func (i *importer) sequences() error { 377 sequenceValues := i.model.Sequences() 378 docs := make([]interface{}, 0, len(sequenceValues)) 379 for key, value := range sequenceValues { 380 // The sequences which track charm revisions aren't imported 381 // here because they get set when charm binaries are imported 382 // later. Importing them here means the wrong values get used. 383 if !isCharmRevSeqName(key) { 384 docs = append(docs, sequenceDoc{ 385 DocID: key, 386 Name: key, 387 Counter: value, 388 }) 389 } 390 } 391 392 // In reality, we will almost always have sequences to migrate. 393 // However, in tests, sometimes we don't. 394 if len(docs) == 0 { 395 return nil 396 } 397 398 sequences, closer := i.st.db().GetCollection(sequenceC) 399 defer closer() 400 401 if err := sequences.Writeable().Insert(docs...); err != nil { 402 return errors.Trace(err) 403 } 404 return nil 405 } 406 407 func (i *importer) modelUsers() error { 408 i.logger.Debugf("importing users") 409 410 // The user that was auto-added when we created the model will have 411 // the wrong DateCreated, so we remove it, and add in all the users we 412 // know about. It is also possible that the owner of the model no 413 // longer has access to the model due to changes over time. 414 if err := i.st.RemoveUserAccess(i.dbModel.Owner(), i.dbModel.ModelTag()); err != nil { 415 return errors.Trace(err) 416 } 417 418 users := i.model.Users() 419 modelUUID := i.dbModel.UUID() 420 var ops []txn.Op 421 for _, user := range users { 422 ops = append(ops, createModelUserOps( 423 modelUUID, 424 user.Name(), 425 user.CreatedBy(), 426 user.DisplayName(), 427 user.DateCreated(), 428 permission.Access(user.Access()))..., 429 ) 430 } 431 if err := i.st.db().RunTransaction(ops); err != nil { 432 return errors.Trace(err) 433 } 434 // Now set their last connection times. 435 for _, user := range users { 436 i.logger.Debugf("user %s", user.Name()) 437 lastConnection := user.LastConnection() 438 if lastConnection.IsZero() { 439 continue 440 } 441 err := i.dbModel.updateLastModelConnection(user.Name(), lastConnection) 442 if err != nil { 443 return errors.Trace(err) 444 } 445 } 446 return nil 447 } 448 449 func (i *importer) machines() error { 450 i.logger.Debugf("importing machines") 451 for _, m := range i.model.Machines() { 452 if err := i.machine(m); err != nil { 453 i.logger.Errorf("error importing machine: %s", err) 454 return errors.Annotate(err, m.Id()) 455 } 456 } 457 458 i.logger.Debugf("importing machines succeeded") 459 return nil 460 } 461 462 func (i *importer) machine(m description.Machine) error { 463 // Import this machine, then import its containers. 464 i.logger.Debugf("importing machine %s", m.Id()) 465 466 // 1. construct a machineDoc 467 mdoc, err := i.makeMachineDoc(m) 468 if err != nil { 469 return errors.Annotatef(err, "machine %s", m.Id()) 470 } 471 // 2. construct enough MachineTemplate to pass into 'insertNewMachineOps' 472 // - adds constraints doc 473 // - adds status doc 474 // - adds machine block devices doc 475 476 mStatus := m.Status() 477 if mStatus == nil { 478 return errors.NotValidf("missing status") 479 } 480 machineStatusDoc := statusDoc{ 481 ModelUUID: i.st.ModelUUID(), 482 Status: status.Status(mStatus.Value()), 483 StatusInfo: mStatus.Message(), 484 StatusData: mStatus.Data(), 485 Updated: mStatus.Updated().UnixNano(), 486 } 487 // A machine isn't valid if it doesn't have an instance. 488 instance := m.Instance() 489 instStatus := instance.Status() 490 instanceStatusDoc := statusDoc{ 491 ModelUUID: i.st.ModelUUID(), 492 Status: status.Status(instStatus.Value()), 493 StatusInfo: instStatus.Message(), 494 StatusData: instStatus.Data(), 495 Updated: instStatus.Updated().UnixNano(), 496 } 497 // importing without a modification-status shouldn't cause a panic, so we 498 // should check if it's nil or not. 499 var modificationStatusDoc statusDoc 500 if modStatus := instance.ModificationStatus(); modStatus != nil { 501 modificationStatusDoc = statusDoc{ 502 ModelUUID: i.st.ModelUUID(), 503 Status: status.Status(modStatus.Value()), 504 StatusInfo: modStatus.Message(), 505 StatusData: modStatus.Data(), 506 Updated: modStatus.Updated().UnixNano(), 507 } 508 } 509 cons := i.constraints(m.Constraints()) 510 prereqOps, machineOp := i.st.baseNewMachineOps( 511 mdoc, 512 machineStatusDoc, 513 instanceStatusDoc, 514 modificationStatusDoc, 515 cons, 516 ) 517 518 // 3. create op for adding in instance data 519 prereqOps = append(prereqOps, i.machineInstanceOp(mdoc, instance)) 520 521 if parentId := container.ParentId(mdoc.Id); parentId != "" { 522 prereqOps = append(prereqOps, 523 // Update containers record for host machine. 524 addChildToContainerRefOp(i.st, parentId, mdoc.Id), 525 ) 526 } 527 // insertNewContainerRefOp adds an empty doc into the containerRefsC 528 // collection for the machine being added. 529 prereqOps = append(prereqOps, insertNewContainerRefOp(i.st, mdoc.Id)) 530 531 // 4. gather prereqs and machine op, run ops. 532 ops := append(prereqOps, machineOp) 533 534 // 5. add any ops that we may need to add the opened ports information for the machine. 535 ops = append(ops, i.machinePortsOp(m)) 536 537 if err := i.st.db().RunTransaction(ops); err != nil { 538 return errors.Trace(err) 539 } 540 541 machine := newMachine(i.st, mdoc) 542 if annotations := m.Annotations(); len(annotations) > 0 { 543 if err := i.dbModel.SetAnnotations(machine, annotations); err != nil { 544 return errors.Trace(err) 545 } 546 } 547 if err := i.importStatusHistory(machine.globalKey(), m.StatusHistory()); err != nil { 548 return errors.Trace(err) 549 } 550 if err := i.importStatusHistory(machine.globalInstanceKey(), instance.StatusHistory()); err != nil { 551 return errors.Trace(err) 552 } 553 if err := i.importMachineBlockDevices(machine, m); err != nil { 554 return errors.Trace(err) 555 } 556 557 // Now that this machine exists in the database, process each of the 558 // containers in this machine. 559 for _, container := range m.Containers() { 560 if err := i.machine(container); err != nil { 561 return errors.Annotate(err, container.Id()) 562 } 563 } 564 return nil 565 } 566 567 func (i *importer) importMachineBlockDevices(machine *Machine, m description.Machine) error { 568 var devices []BlockDeviceInfo 569 for _, device := range m.BlockDevices() { 570 devices = append(devices, BlockDeviceInfo{ 571 DeviceName: device.Name(), 572 DeviceLinks: device.Links(), 573 Label: device.Label(), 574 UUID: device.UUID(), 575 HardwareId: device.HardwareID(), 576 WWN: device.WWN(), 577 BusAddress: device.BusAddress(), 578 Size: device.Size(), 579 FilesystemType: device.FilesystemType(), 580 InUse: device.InUse(), 581 MountPoint: device.MountPoint(), 582 }) 583 } 584 585 if err := machine.SetMachineBlockDevices(devices...); err != nil { 586 return errors.Trace(err) 587 } 588 return nil 589 } 590 591 func (i *importer) machinePortsOp(m description.Machine) txn.Op { 592 modelUUID := i.st.ModelUUID() 593 machineID := m.Id() 594 595 portRangeDoc := machinePortRangesDoc{ 596 DocID: i.st.docID(machineID), 597 MachineID: machineID, 598 ModelUUID: modelUUID, 599 UnitRanges: make(map[string]network.GroupedPortRanges), 600 } 601 602 for unitName, unitPorts := range m.OpenedPortRanges().ByUnit() { 603 portRangeDoc.UnitRanges[unitName] = make(network.GroupedPortRanges) 604 605 for endpointName, portRanges := range unitPorts.ByEndpoint() { 606 portRangeList := make([]network.PortRange, len(portRanges)) 607 for i, pr := range portRanges { 608 portRangeList[i] = network.PortRange{ 609 FromPort: pr.FromPort(), 610 ToPort: pr.ToPort(), 611 Protocol: pr.Protocol(), 612 } 613 } 614 615 portRangeDoc.UnitRanges[unitName][endpointName] = portRangeList 616 } 617 } 618 619 return txn.Op{ 620 C: openedPortsC, 621 Id: machineID, 622 Assert: txn.DocMissing, 623 Insert: portRangeDoc, 624 } 625 } 626 627 func (i *importer) applicationPortsOp(a description.Application) txn.Op { 628 modelUUID := i.st.ModelUUID() 629 appName := a.Name() 630 docID := i.st.docID(applicationGlobalKey(appName)) 631 632 portRangeDoc := newApplicationPortRangesDoc(docID, modelUUID, appName) 633 for unitName, unitPorts := range a.OpenedPortRanges().ByUnit() { 634 portRangeDoc.UnitRanges[unitName] = make(network.GroupedPortRanges) 635 636 for endpointName, portRanges := range unitPorts.ByEndpoint() { 637 portRangeList := transform.Slice(portRanges, func(pr description.UnitPortRange) network.PortRange { 638 return network.PortRange{ 639 FromPort: pr.FromPort(), 640 ToPort: pr.ToPort(), 641 Protocol: pr.Protocol(), 642 } 643 }) 644 portRangeDoc.UnitRanges[unitName][endpointName] = portRangeList 645 } 646 } 647 648 return txn.Op{ 649 C: openedPortsC, 650 Id: docID, 651 Assert: txn.DocMissing, 652 Insert: portRangeDoc, 653 } 654 } 655 656 func (i *importer) machineInstanceOp(mdoc *machineDoc, inst description.CloudInstance) txn.Op { 657 doc := &instanceData{ 658 DocID: mdoc.DocID, 659 MachineId: mdoc.Id, 660 InstanceId: instance.Id(inst.InstanceId()), 661 DisplayName: inst.DisplayName(), 662 ModelUUID: mdoc.ModelUUID, 663 } 664 665 if arch := inst.Architecture(); arch != "" { 666 doc.Arch = &arch 667 } 668 if mem := inst.Memory(); mem != 0 { 669 doc.Mem = &mem 670 } 671 if rootDisk := inst.RootDisk(); rootDisk != 0 { 672 doc.RootDisk = &rootDisk 673 } 674 if rootDiskSource := inst.RootDiskSource(); rootDiskSource != "" { 675 doc.RootDiskSource = &rootDiskSource 676 } 677 if cores := inst.CpuCores(); cores != 0 { 678 doc.CpuCores = &cores 679 } 680 if power := inst.CpuPower(); power != 0 { 681 doc.CpuPower = &power 682 } 683 if tags := inst.Tags(); len(tags) > 0 { 684 doc.Tags = &tags 685 } 686 if az := inst.AvailabilityZone(); az != "" { 687 doc.AvailZone = &az 688 } 689 if vt := inst.VirtType(); vt != "" { 690 doc.VirtType = &vt 691 } 692 if profiles := inst.CharmProfiles(); len(profiles) > 0 { 693 doc.CharmProfiles = profiles 694 } 695 696 return txn.Op{ 697 C: instanceDataC, 698 Id: mdoc.DocID, 699 Assert: txn.DocMissing, 700 Insert: doc, 701 } 702 } 703 704 func (i *importer) makeMachineDoc(m description.Machine) (*machineDoc, error) { 705 id := m.Id() 706 supported, supportedSet := m.SupportedContainers() 707 supportedContainers := make([]instance.ContainerType, len(supported)) 708 for j, c := range supported { 709 supportedContainers[j] = instance.ContainerType(c) 710 } 711 jobs, err := i.makeMachineJobs(m.Jobs()) 712 if err != nil { 713 return nil, errors.Trace(err) 714 } 715 716 agentTools, err := i.makeTools(m.Tools()) 717 if err != nil { 718 return nil, errors.Trace(err) 719 } 720 721 machineTag := m.Tag() 722 base, err := corebase.ParseBaseFromString(m.Base()) 723 if err != nil { 724 return nil, errors.Trace(err) 725 } 726 macBase := Base{OS: base.OS, Channel: base.Channel.String()} 727 return &machineDoc{ 728 DocID: i.st.docID(id), 729 Id: id, 730 ModelUUID: i.st.ModelUUID(), 731 Nonce: m.Nonce(), 732 Base: macBase.Normalise(), 733 ContainerType: m.ContainerType(), 734 Principals: nil, // Set during unit import. 735 Life: Alive, 736 Tools: agentTools, 737 Jobs: jobs, 738 PasswordHash: m.PasswordHash(), 739 Clean: !i.machineHasUnits(machineTag), 740 Volumes: i.machineVolumes(machineTag), 741 Filesystems: i.machineFilesystems(machineTag), 742 Addresses: i.makeAddresses(m.ProviderAddresses()), 743 MachineAddresses: i.makeAddresses(m.MachineAddresses()), 744 PreferredPrivateAddress: i.makeAddress(m.PreferredPrivateAddress()), 745 PreferredPublicAddress: i.makeAddress(m.PreferredPublicAddress()), 746 SupportedContainersKnown: supportedSet, 747 SupportedContainers: supportedContainers, 748 Placement: m.Placement(), 749 }, nil 750 } 751 752 func (i *importer) machineHasUnits(tag names.MachineTag) bool { 753 for _, app := range i.model.Applications() { 754 for _, unit := range app.Units() { 755 if unit.Machine() == tag { 756 return true 757 } 758 } 759 } 760 return false 761 } 762 763 func (i *importer) machineVolumes(tag names.MachineTag) []string { 764 var result []string 765 for _, volume := range i.model.Volumes() { 766 for _, attachment := range volume.Attachments() { 767 if attachment.Host() == tag { 768 result = append(result, volume.Tag().Id()) 769 } 770 } 771 } 772 return result 773 } 774 775 func (i *importer) machineFilesystems(tag names.MachineTag) []string { 776 var result []string 777 for _, filesystem := range i.model.Filesystems() { 778 for _, attachment := range filesystem.Attachments() { 779 if attachment.Host() == tag { 780 result = append(result, filesystem.Tag().Id()) 781 } 782 } 783 } 784 return result 785 } 786 787 func (i *importer) makeMachineJobs(jobs []string) ([]MachineJob, error) { 788 // At time of writing, there are three valid jobs. If any jobs gets 789 // deprecated or changed in the future, older models that specify those 790 // jobs need to be handled here. 791 result := make([]MachineJob, 0, len(jobs)) 792 for _, job := range jobs { 793 switch job { 794 case "host-units": 795 result = append(result, JobHostUnits) 796 case "api-server": 797 result = append(result, JobManageModel) 798 default: 799 return nil, errors.Errorf("unknown machine job: %q", job) 800 } 801 } 802 return result, nil 803 } 804 805 func (i *importer) makeTools(t description.AgentTools) (*tools.Tools, error) { 806 if t == nil { 807 return nil, nil 808 } 809 result := &tools.Tools{ 810 Version: t.Version(), 811 URL: t.URL(), 812 SHA256: t.SHA256(), 813 Size: t.Size(), 814 } 815 return result, nil 816 } 817 818 func (i *importer) makeAddress(addr description.Address) address { 819 if addr == nil { 820 return address{} 821 } 822 823 newAddr := address{ 824 Value: addr.Value(), 825 AddressType: addr.Type(), 826 Scope: addr.Scope(), 827 Origin: addr.Origin(), 828 SpaceID: addr.SpaceID(), 829 } 830 831 // Addresses are placed in the default space if no space ID is set. 832 if newAddr.SpaceID == "" { 833 newAddr.SpaceID = "0" 834 } 835 836 return newAddr 837 } 838 839 func (i *importer) makeAddresses(addrs []description.Address) []address { 840 result := make([]address, len(addrs)) 841 for j, addr := range addrs { 842 result[j] = i.makeAddress(addr) 843 } 844 return result 845 } 846 847 func (i *importer) applications() error { 848 i.logger.Debugf("importing applications") 849 850 ctrlCfg, err := i.st.ControllerConfig() 851 if err != nil { 852 return errors.Trace(err) 853 } 854 855 // Ensure we import principal applications first, so that 856 // subordinate units can refer to the principal ones. 857 var principals, subordinates []description.Application 858 for _, app := range i.model.Applications() { 859 if app.Subordinate() { 860 subordinates = append(subordinates, app) 861 } else { 862 principals = append(principals, app) 863 } 864 } 865 866 i.charmOrigins = make(map[string]*CharmOrigin, len(principals)+len(subordinates)) 867 868 for _, s := range append(principals, subordinates...) { 869 if err := i.application(s, ctrlCfg); err != nil { 870 i.logger.Errorf("error importing application %s: %s", s.Name(), err) 871 return errors.Annotate(err, s.Name()) 872 } 873 } 874 875 if err := i.loadUnits(); err != nil { 876 return errors.Annotate(err, "loading new units from db") 877 } 878 i.logger.Debugf("importing applications succeeded") 879 return nil 880 } 881 882 func (i *importer) loadUnits() error { 883 unitsCollection, closer := i.st.db().GetCollection(unitsC) 884 defer closer() 885 886 docs := []unitDoc{} 887 err := unitsCollection.Find(nil).All(&docs) 888 if err != nil { 889 return errors.Annotate(err, "cannot get all units") 890 } 891 892 result := make(map[string]map[string]*Unit) 893 for _, doc := range docs { 894 units, found := result[doc.Application] 895 if !found { 896 units = make(map[string]*Unit) 897 result[doc.Application] = units 898 } 899 units[doc.Name] = newUnit(i.st, i.dbModel.Type(), &doc) 900 } 901 i.applicationUnits = result 902 return nil 903 904 } 905 906 // makeStatusDoc assumes status is non-nil. 907 func (i *importer) makeStatusDoc(statusVal description.Status) statusDoc { 908 doc := statusDoc{ 909 Status: status.Status(statusVal.Value()), 910 StatusInfo: statusVal.Message(), 911 StatusData: statusVal.Data(), 912 Updated: statusVal.Updated().UnixNano(), 913 } 914 // Older versions of Juju would pass through NeverSet() on the status 915 // description for application statuses that hadn't been explicitly 916 // set by the lead unit. If that is the case, we make the status what 917 // the new code expects. 918 if statusVal.NeverSet() { 919 doc.Status = status.Unset 920 doc.StatusInfo = "" 921 doc.StatusData = nil 922 } 923 return doc 924 } 925 926 func (i *importer) application(a description.Application, ctrlCfg controller.Config) error { 927 // Import this application, then its units. 928 i.logger.Debugf("importing application %s", a.Name()) 929 930 // 1. construct an applicationDoc 931 appDoc, err := i.makeApplicationDoc(a) 932 if err != nil { 933 return errors.Trace(err) 934 } 935 app := newApplication(i.st, appDoc) 936 937 // 2. construct a statusDoc 938 status := a.Status() 939 if status == nil { 940 return errors.NotValidf("missing status") 941 } 942 appStatusDoc := i.makeStatusDoc(status) 943 944 // When creating the settings, we ignore nils. In other circumstances, nil 945 // means to delete the value (reset to default), so creating with nil should 946 // mean to use the default, i.e. don't set the value. 947 // There may have existed some applications with settings that contained 948 // nil values, see lp#1667199. When importing, we want these stripped. 949 removeNils(a.CharmConfig()) 950 removeNils(a.ApplicationConfig()) 951 952 var operatorStatusDoc *statusDoc 953 if i.dbModel.Type() == ModelTypeCAAS { 954 operatorStatus := i.makeStatusDoc(a.OperatorStatus()) 955 operatorStatusDoc = &operatorStatus 956 } 957 ops, err := addApplicationOps(i.st, app, addApplicationOpsArgs{ 958 applicationDoc: appDoc, 959 statusDoc: appStatusDoc, 960 constraints: i.constraints(a.Constraints()), 961 storage: i.storageConstraints(a.StorageDirectives()), 962 charmConfig: a.CharmConfig(), 963 applicationConfig: a.ApplicationConfig(), 964 leadershipSettings: a.LeadershipSettings(), 965 operatorStatus: operatorStatusDoc, 966 }) 967 if err != nil { 968 return errors.Trace(err) 969 } 970 971 bindings, err := i.parseBindings(a.EndpointBindings()) 972 if err != nil { 973 return errors.Trace(err) 974 } 975 976 ops = append(ops, txn.Op{ 977 C: endpointBindingsC, 978 Id: app.globalKey(), 979 Assert: txn.DocMissing, 980 Insert: endpointBindingsDoc{ 981 Bindings: bindings.Map(), 982 }, 983 }) 984 985 ops = append(ops, i.appResourceOps(a)...) 986 987 // add any ops that we may need to add the opened ports information for the application. 988 ops = append(ops, i.applicationPortsOp(a)) 989 990 if err := i.st.db().RunTransaction(ops); err != nil { 991 return errors.Trace(err) 992 } 993 994 if a.PodSpec() != "" { 995 cm, err := i.dbModel.CAASModel() 996 if err != nil { 997 return errors.NewNotSupported(err, "adding pod spec to IAAS model") 998 } 999 // We pass a nil token as there is no need to perform 1000 // leadership checks while migrating. 1001 spec := a.PodSpec() 1002 if err := cm.SetPodSpec(nil, a.Tag(), &spec); err != nil { 1003 return errors.Trace(err) 1004 } 1005 } 1006 // TODO(caas): Add raw k8s spec to juju/description for model migration! 1007 1008 if cs := a.CloudService(); cs != nil { 1009 app, err := i.st.Application(a.Name()) 1010 if err != nil { 1011 return errors.Trace(err) 1012 } 1013 addr := i.makeAddresses(cs.Addresses()) 1014 if err := app.UpdateCloudService(cs.ProviderId(), networkAddresses(addr)); err != nil { 1015 return errors.Trace(err) 1016 } 1017 } 1018 1019 if annotations := a.Annotations(); len(annotations) > 0 { 1020 if err := i.dbModel.SetAnnotations(app, annotations); err != nil { 1021 return errors.Trace(err) 1022 } 1023 } 1024 if err := i.importStatusHistory(app.globalKey(), a.StatusHistory()); err != nil { 1025 return errors.Trace(err) 1026 } 1027 1028 for _, unit := range a.Units() { 1029 if err := i.unit(a, unit, ctrlCfg); err != nil { 1030 return errors.Trace(err) 1031 } 1032 } 1033 1034 if err := i.applicationOffers(a); err != nil { 1035 i.logger.Errorf("error importing application %s: %s", app.Name(), err) 1036 return errors.Annotate(err, app.Name()) 1037 } 1038 1039 return nil 1040 } 1041 1042 func (i *importer) applicationOffers(app ApplicationDescription) error { 1043 i.logger.Debugf("importing application offer") 1044 migration := &ImportStateMigration{ 1045 src: i.model, 1046 dst: i.st.db(), 1047 } 1048 migration.Add(func() error { 1049 m := ImportApplicationOffer{} 1050 // The following shims compose a model and series of methods that should 1051 // be public, but are private (for unit/mock testing) and we encapsulate 1052 // that as one thing. 1053 return m.Execute(applicationDescriptionShim{ 1054 stateApplicationOfferDocumentFactoryShim{ 1055 stateModelNamspaceShim{ 1056 Model: migration.src, 1057 st: i.st, 1058 }, 1059 i, 1060 }, 1061 app, 1062 }, migration.dst) 1063 }) 1064 if err := migration.Run(); err != nil { 1065 return errors.Trace(err) 1066 } 1067 i.logger.Debugf("importing application offer succeeded") 1068 return nil 1069 } 1070 1071 // parseBindings converts a bindings map from a 2.6.x or 2.7+ migration export 1072 // into a Bindings object. 1073 // 1074 // When migrating from a 2.6.x controller, the bindings in the description 1075 // output are encoded as {endpoint name => space name} with "" representing 1076 // the "default" (now called alpha) space. The empty spaces must be remapped 1077 // to the correct default space name for the new controller. 1078 // 1079 // On the other hand, migration exports from 2.7+ are using space IDs instead 1080 // of space names as the map values and can safely be passed to the NewBindings 1081 // c-tor. 1082 func (i *importer) parseBindings(bindingsMap map[string]string) (*Bindings, error) { 1083 defaultMappingsAreIds := true 1084 for epName, spNameOrID := range bindingsMap { 1085 if spNameOrID == "" { 1086 defaultMappingsAreIds = false 1087 bindingsMap[epName] = network.AlphaSpaceName 1088 } 1089 } 1090 1091 // 2.6 controllers only populate the default space key if set to the 1092 // non-default space whereas 2.7 controllers always set it. 1093 // The application implementation in the description package has 1094 // `omitempty` for bindings, so we need to create it if nil. 1095 // There's an added complication. Coming from a 2.6 controller, the 1096 // mapping is endpoint -> spaceName. If the 2.6 controller has been 1097 // upgraded to 2.7.x (x < 7), the mapping is endpoint -> spaceID. 1098 // We need to ensure that a consistent mapping value is used. 1099 if bindingsMap == nil { 1100 bindingsMap = make(map[string]string, 1) 1101 } 1102 if _, exists := bindingsMap[defaultEndpointName]; !exists { 1103 if defaultMappingsAreIds { 1104 bindingsMap[defaultEndpointName] = network.AlphaSpaceId 1105 } else { 1106 bindingsMap[defaultEndpointName] = network.AlphaSpaceName 1107 } 1108 } 1109 1110 return NewBindings(i.st, bindingsMap) 1111 } 1112 1113 func (i *importer) appResourceOps(app description.Application) []txn.Op { 1114 // Add a placeholder record for each resource that is a placeholder. 1115 // Resources define placeholders as resources where the timestamp is Zero. 1116 var result []txn.Op 1117 appName := app.Name() 1118 1119 var makeResourceDoc = func(id, name string, rev description.ResourceRevision) resourceDoc { 1120 fingerprint, _ := hex.DecodeString(rev.FingerprintHex()) 1121 return resourceDoc{ 1122 ID: id, 1123 ApplicationID: appName, 1124 Name: name, 1125 Type: rev.Type(), 1126 Path: rev.Path(), 1127 Description: rev.Description(), 1128 Origin: rev.Origin(), 1129 Revision: rev.Revision(), 1130 Fingerprint: fingerprint, 1131 Size: rev.Size(), 1132 Username: rev.Username(), 1133 } 1134 } 1135 1136 for _, r := range app.Resources() { 1137 // I cannot for the life of me find the function where the underlying 1138 // resource id is defined to be the appname/resname but that is what 1139 // ends up in the DB. 1140 resName := r.Name() 1141 resID := appName + "/" + resName 1142 // Check both the app and charmstore 1143 if appRev := r.ApplicationRevision(); appRev.Timestamp().IsZero() { 1144 result = append(result, txn.Op{ 1145 C: resourcesC, 1146 Id: applicationResourceID(resID), 1147 Assert: txn.DocMissing, 1148 Insert: makeResourceDoc(resID, resName, appRev), 1149 }) 1150 } 1151 if storeRev := r.CharmStoreRevision(); storeRev.Timestamp().IsZero() { 1152 doc := makeResourceDoc(resID, resName, storeRev) 1153 doc.LastPolled = i.st.nowToTheSecond() 1154 result = append(result, txn.Op{ 1155 C: resourcesC, 1156 Id: charmStoreResourceID(resID), 1157 Assert: txn.DocMissing, 1158 Insert: doc, 1159 }) 1160 } 1161 } 1162 return result 1163 } 1164 1165 func (i *importer) storageConstraints(cons map[string]description.StorageDirective) map[string]StorageConstraints { 1166 if len(cons) == 0 { 1167 return nil 1168 } 1169 result := make(map[string]StorageConstraints) 1170 for key, value := range cons { 1171 result[key] = StorageConstraints{ 1172 Pool: value.Pool(), 1173 Size: value.Size(), 1174 Count: value.Count(), 1175 } 1176 } 1177 return result 1178 } 1179 1180 func (i *importer) unit(s description.Application, u description.Unit, ctrlCfg controller.Config) error { 1181 i.logger.Debugf("importing unit %s", u.Name()) 1182 1183 // 1. construct a unitDoc 1184 udoc, err := i.makeUnitDoc(s, u) 1185 if err != nil { 1186 return errors.Trace(err) 1187 } 1188 1189 // 2. construct a statusDoc for the workload status and agent status 1190 agentStatus := u.AgentStatus() 1191 if agentStatus == nil { 1192 return errors.NotValidf("missing agent status") 1193 } 1194 agentStatusDoc := i.makeStatusDoc(agentStatus) 1195 1196 workloadStatus := u.WorkloadStatus() 1197 if workloadStatus == nil { 1198 return errors.NotValidf("missing workload status") 1199 } 1200 workloadStatusDoc := i.makeStatusDoc(workloadStatus) 1201 1202 workloadVersion := u.WorkloadVersion() 1203 versionStatus := status.Active 1204 if workloadVersion == "" { 1205 versionStatus = status.Unknown 1206 } 1207 workloadVersionDoc := statusDoc{ 1208 Status: versionStatus, 1209 StatusInfo: workloadVersion, 1210 } 1211 1212 var cloudContainer *cloudContainerDoc 1213 if cc := u.CloudContainer(); cc != nil { 1214 cloudContainer = &cloudContainerDoc{ 1215 Id: unitGlobalKey(u.Name()), 1216 ProviderId: cc.ProviderId(), 1217 Ports: cc.Ports(), 1218 } 1219 if cc.Address() != nil { 1220 addr := i.makeAddress(cc.Address()) 1221 cloudContainer.Address = &addr 1222 } 1223 } 1224 1225 ops, err := addUnitOps(i.st, addUnitOpsArgs{ 1226 unitDoc: udoc, 1227 agentStatusDoc: agentStatusDoc, 1228 workloadStatusDoc: &workloadStatusDoc, 1229 workloadVersionDoc: &workloadVersionDoc, 1230 meterStatusDoc: &meterStatusDoc{ 1231 Code: u.MeterStatusCode(), 1232 Info: u.MeterStatusInfo(), 1233 }, 1234 containerDoc: cloudContainer, 1235 }) 1236 if err != nil { 1237 return errors.Trace(err) 1238 } 1239 1240 if i.dbModel.Type() == ModelTypeIAAS && u.Principal().Id() == "" { 1241 // If the unit is a principal, add it to its machine. 1242 ops = append(ops, txn.Op{ 1243 C: machinesC, 1244 Id: u.Machine().Id(), 1245 Assert: txn.DocExists, 1246 Update: bson.M{"$addToSet": bson.M{"principals": u.Name()}}, 1247 }) 1248 } 1249 1250 // We should only have constraints for principal agents. 1251 // We don't encode that business logic here, if there are constraints 1252 // in the imported model, we put them in the database. 1253 if cons := u.Constraints(); cons != nil { 1254 agentGlobalKey := unitAgentGlobalKey(u.Name()) 1255 ops = append(ops, createConstraintsOp(agentGlobalKey, i.constraints(cons))) 1256 } 1257 1258 if err := i.st.db().RunTransaction(ops); err != nil { 1259 i.logger.Debugf("failed ops: %#v", ops) 1260 return errors.Trace(err) 1261 } 1262 1263 model, err := i.st.Model() 1264 if err != nil { 1265 return errors.Trace(err) 1266 } 1267 1268 // The assertion logic in unit.SetState assumes that the DocID is 1269 // present. Since the txn for creating the unit doc has completed 1270 // without an error, we can safely populate the doc's model UUID and 1271 // DocID. 1272 udoc.ModelUUID = model.UUID() 1273 udoc.DocID = ensureModelUUID(udoc.ModelUUID, udoc.Name) 1274 1275 unit := newUnit(i.st, model.Type(), udoc) 1276 if annotations := u.Annotations(); len(annotations) > 0 { 1277 if err := i.dbModel.SetAnnotations(unit, annotations); err != nil { 1278 return errors.Trace(err) 1279 } 1280 } 1281 if err := i.importStatusHistory(unit.globalKey(), u.WorkloadStatusHistory()); err != nil { 1282 return errors.Trace(err) 1283 } 1284 if err := i.importStatusHistory(unit.globalAgentKey(), u.AgentStatusHistory()); err != nil { 1285 return errors.Trace(err) 1286 } 1287 if err := i.importStatusHistory(unit.globalWorkloadVersionKey(), u.WorkloadVersionHistory()); err != nil { 1288 return errors.Trace(err) 1289 } 1290 if err := i.importUnitState(unit, u, ctrlCfg); err != nil { 1291 return errors.Trace(err) 1292 } 1293 if i.dbModel.Type() == ModelTypeIAAS { 1294 if err := i.importUnitPayloads(unit, u.Payloads()); err != nil { 1295 return errors.Trace(err) 1296 } 1297 } 1298 return nil 1299 } 1300 1301 func (i *importer) importUnitState(unit *Unit, u description.Unit, ctrlCfg controller.Config) error { 1302 us := NewUnitState() 1303 1304 if charmState := u.CharmState(); len(charmState) != 0 { 1305 us.SetCharmState(charmState) 1306 } 1307 if relationState := u.RelationState(); len(relationState) != 0 { 1308 us.SetRelationState(relationState) 1309 } 1310 if uniterState := u.UniterState(); uniterState != "" { 1311 us.SetUniterState(uniterState) 1312 } 1313 if storageState := u.StorageState(); storageState != "" { 1314 us.SetStorageState(storageState) 1315 } 1316 if meterStatusState := u.MeterStatusState(); meterStatusState != "" { 1317 us.SetMeterStatusState(meterStatusState) 1318 } 1319 1320 // No state to persist. 1321 if !us.Modified() { 1322 return nil 1323 } 1324 1325 return unit.SetState(us, UnitStateSizeLimits{ 1326 MaxCharmStateSize: ctrlCfg.MaxCharmStateSize(), 1327 MaxAgentStateSize: ctrlCfg.MaxAgentStateSize(), 1328 }) 1329 } 1330 1331 func (i *importer) importUnitPayloads(unit *Unit, payloadInfo []description.Payload) error { 1332 up, err := i.st.UnitPayloads(unit) 1333 if err != nil { 1334 return errors.Trace(err) 1335 } 1336 1337 for _, p := range payloadInfo { 1338 if err := up.Track(payloads.Payload{ 1339 PayloadClass: charm.PayloadClass{ 1340 Name: p.Name(), 1341 Type: p.Type(), 1342 }, 1343 ID: p.RawID(), 1344 Status: p.State(), 1345 Labels: p.Labels(), 1346 }); err != nil { 1347 return errors.Trace(err) 1348 } 1349 } 1350 1351 return nil 1352 } 1353 1354 func (i *importer) makeApplicationDoc(a description.Application) (*applicationDoc, error) { 1355 units := a.Units() 1356 1357 origin, err := i.makeCharmOrigin(a) 1358 if err != nil { 1359 return nil, errors.Trace(err) 1360 } 1361 1362 var exposedEndpoints map[string]ExposedEndpoint 1363 if expEps := a.ExposedEndpoints(); len(expEps) > 0 { 1364 exposedEndpoints = make(map[string]ExposedEndpoint, len(expEps)) 1365 for epName, details := range expEps { 1366 exposedEndpoints[epName] = ExposedEndpoint{ 1367 ExposeToSpaceIDs: details.ExposeToSpaceIDs(), 1368 ExposeToCIDRs: details.ExposeToCIDRs(), 1369 } 1370 } 1371 } 1372 1373 agentTools, err := i.makeTools(a.Tools()) 1374 if err != nil { 1375 return nil, errors.Trace(err) 1376 } 1377 cURLStr := a.CharmURL() 1378 1379 appDoc := &applicationDoc{ 1380 Name: a.Name(), 1381 Subordinate: a.Subordinate(), 1382 CharmURL: &cURLStr, 1383 CharmModifiedVersion: a.CharmModifiedVersion(), 1384 CharmOrigin: *origin, 1385 ForceCharm: a.ForceCharm(), 1386 PasswordHash: a.PasswordHash(), 1387 Life: Alive, 1388 UnitCount: len(units), 1389 RelationCount: i.relationCount(a.Name()), 1390 Exposed: a.Exposed(), 1391 ExposedEndpoints: exposedEndpoints, 1392 MinUnits: a.MinUnits(), 1393 Tools: agentTools, 1394 MetricCredentials: a.MetricsCredentials(), 1395 DesiredScale: a.DesiredScale(), 1396 Placement: a.Placement(), 1397 HasResources: a.HasResources(), 1398 } 1399 1400 if ps := a.ProvisioningState(); ps != nil { 1401 appDoc.ProvisioningState = &ApplicationProvisioningState{ 1402 Scaling: ps.Scaling(), 1403 ScaleTarget: ps.ScaleTarget(), 1404 } 1405 } 1406 1407 return appDoc, nil 1408 } 1409 1410 // makeCharmOrigin returns the charm origin for an application 1411 // 1412 // Previous versions of the Juju server and clients have treated applications charm 1413 // origins very loosely, particularly during `refresh --switch`s. The server performed 1414 // no validation on origins received from the client, and client often mutated them 1415 // incorrectly. For instance, when switching from a ch charm to local, pylibjuju simply 1416 // sent back a copy of the ch charm origin, whereas the CLI only set the source to local. 1417 // Both resulted in incorrect/invalidate origins. 1418 // 1419 // Calculate the origin Source and Revision from the charm url. Ensure ID, Hash and Channel 1420 // are dropped from local charm. Keep ID, Hash and Channel (for ch charms) and Platform (always) 1421 // we get from the origin. We can trust these since supported clients cannot break these 1422 // 1423 // This was fixed in pylibjuju 3.2.3.0 and juju 3.3.0. As of writing, no versions of the 1424 // server validate new charm origins on calls to SetCharm. Ideally, the client shouldn't 1425 // handle charm origins at all, being an implementation detail. But this will probably have 1426 // to wait until the api re-write 1427 // 1428 // https://bugs.launchpad.net/juju/+bug/2039267 1429 // https://github.com/juju/python-libjuju/issues/962 1430 // 1431 // Due to LP:1986547: where the track is missing from the effective channel it implicitly 1432 // resolves to 'latest' if the charm does not have a default channel defined. So if the 1433 // received channel has no track, we can be confident it should be 'latest' 1434 // 1435 // TODO: Once we have confidence in charm origins, do not parse charm url and simplify 1436 // into a translation layer 1437 func (i *importer) makeCharmOrigin(a description.Application) (*CharmOrigin, error) { 1438 sourceOrigin := a.CharmOrigin() 1439 curl, err := charm.ParseURL(a.CharmURL()) 1440 if err != nil { 1441 return nil, errors.Trace(err) 1442 } 1443 1444 // Fix bad datasets from LP 1999060 during migration. 1445 // ID and Hash missing from N-1 of N applications' 1446 // charm origins when deployed using the same charm. 1447 if foundOrigin, ok := i.charmOrigins[curl.String()]; ok { 1448 return foundOrigin, nil 1449 } 1450 1451 var channel *Channel 1452 serialized := sourceOrigin.Channel() 1453 if serialized != "" && charm.CharmHub.Matches(curl.Schema) { 1454 c, err := charm.ParseChannelNormalize(serialized) 1455 if err != nil { 1456 return nil, errors.Trace(err) 1457 } 1458 track := c.Track 1459 if track == "" { 1460 track = "latest" 1461 } 1462 channel = &Channel{ 1463 Track: track, 1464 Risk: string(c.Risk), 1465 Branch: c.Branch, 1466 } 1467 } 1468 1469 p, err := corecharm.ParsePlatformNormalize(sourceOrigin.Platform()) 1470 if err != nil { 1471 return nil, errors.Trace(err) 1472 } 1473 platform := &Platform{ 1474 Architecture: p.Architecture, 1475 OS: p.OS, 1476 Channel: p.Channel, 1477 } 1478 1479 // We can hardcode type to charm as we never store bundles in state. 1480 var origin *CharmOrigin 1481 if charm.Local.Matches(curl.Schema) { 1482 origin = &CharmOrigin{ 1483 Source: corecharm.Local.String(), 1484 Type: "charm", 1485 Revision: &curl.Revision, 1486 Platform: platform, 1487 } 1488 } else if charm.CharmHub.Matches(curl.Schema) { 1489 origin = &CharmOrigin{ 1490 Source: corecharm.CharmHub.String(), 1491 Type: "charm", 1492 Revision: &curl.Revision, 1493 ID: sourceOrigin.ID(), 1494 Hash: sourceOrigin.Hash(), 1495 Channel: channel, 1496 Platform: platform, 1497 } 1498 } else { 1499 return nil, errors.Errorf("Unrecognised charm url schema %q", curl.Schema) 1500 } 1501 1502 if !reflect.DeepEqual(sourceOrigin, origin) { 1503 i.logger.Warningf("Source origin for application %q is invalid. Normalising", a.Name()) 1504 } 1505 1506 i.charmOrigins[curl.String()] = origin 1507 return origin, nil 1508 } 1509 1510 func (i *importer) relationCount(application string) int { 1511 count := 0 1512 1513 for _, rel := range i.model.Relations() { 1514 for _, ep := range rel.Endpoints() { 1515 if ep.ApplicationName() == application { 1516 count++ 1517 } 1518 } 1519 } 1520 1521 return count 1522 } 1523 1524 func (i *importer) getPrincipalMachineID(principal names.UnitTag) string { 1525 // We know this is a valid unit name, so we don't care about the error. 1526 appName, _ := names.UnitApplication(principal.Id()) 1527 for _, app := range i.model.Applications() { 1528 if app.Name() == appName { 1529 for _, unit := range app.Units() { 1530 if unit.Tag() == principal { 1531 return unit.Machine().Id() 1532 } 1533 } 1534 } 1535 } 1536 // We should never get here, but if we do, just return an empty 1537 // machine ID. 1538 i.logger.Warningf("unable to find principal %q", principal.Id()) 1539 return "" 1540 } 1541 1542 func (i *importer) makeUnitDoc(s description.Application, u description.Unit) (*unitDoc, error) { 1543 // NOTE: if we want to support units having different charms deployed 1544 // than the application recommends and migrate that, then we should serialize 1545 // the charm url for each unit rather than grabbing the applications charm url. 1546 // Currently the units charm url matching the application is a precondiation 1547 // to migration. 1548 charmURL := s.CharmURL() 1549 1550 var subordinates []string 1551 if subs := u.Subordinates(); len(subs) > 0 { 1552 for _, s := range subs { 1553 subordinates = append(subordinates, s.Id()) 1554 } 1555 } 1556 1557 machineID := u.Machine().Id() 1558 if s.Subordinate() && machineID == "" && i.dbModel.Type() != ModelTypeCAAS { 1559 // If we don't have a machine ID and we should, go get the 1560 // machine ID from the principal. 1561 machineID = i.getPrincipalMachineID(u.Principal()) 1562 } 1563 1564 agentTools, err := i.makeTools(u.Tools()) 1565 if err != nil { 1566 return nil, errors.Trace(err) 1567 } 1568 1569 p, err := corecharm.ParsePlatformNormalize(s.CharmOrigin().Platform()) 1570 if err != nil { 1571 return nil, errors.Trace(err) 1572 } 1573 base := Base{OS: p.OS, Channel: p.Channel}.Normalise() 1574 return &unitDoc{ 1575 Name: u.Name(), 1576 Application: s.Name(), 1577 Base: base, 1578 CharmURL: &charmURL, 1579 Principal: u.Principal().Id(), 1580 Subordinates: subordinates, 1581 StorageAttachmentCount: i.unitStorageAttachmentCount(u.Tag()), 1582 MachineId: machineID, 1583 Tools: agentTools, 1584 Life: Alive, 1585 PasswordHash: u.PasswordHash(), 1586 }, nil 1587 } 1588 1589 func (i *importer) unitStorageAttachmentCount(unit names.UnitTag) int { 1590 count := 0 1591 for _, storage := range i.model.Storages() { 1592 for _, tag := range storage.Attachments() { 1593 if tag == unit { 1594 count++ 1595 } 1596 } 1597 } 1598 return count 1599 } 1600 1601 func (i *importer) remoteApplications() error { 1602 i.logger.Debugf("importing remote applications") 1603 migration := &ImportStateMigration{ 1604 src: i.model, 1605 dst: i.st.db(), 1606 } 1607 migration.Add(func() error { 1608 m := ImportRemoteApplications{} 1609 return m.Execute(stateDocumentFactoryShim{ 1610 stateModelNamspaceShim{ 1611 Model: migration.src, 1612 st: i.st, 1613 }, 1614 i, 1615 }, migration.dst) 1616 }) 1617 if err := migration.Run(); err != nil { 1618 return errors.Trace(err) 1619 } 1620 i.logger.Debugf("importing remote applications succeeded") 1621 return nil 1622 } 1623 1624 func (i *importer) firewallRules() error { 1625 i.logger.Debugf("importing firewall rules") 1626 migration := &ImportStateMigration{ 1627 src: i.model, 1628 dst: i.st.db(), 1629 } 1630 migration.Add(func() error { 1631 m := ImportFirewallRules{} 1632 return m.Execute(stateModelNamspaceShim{ 1633 Model: migration.src, 1634 st: i.st, 1635 }, i.dbModel) 1636 }) 1637 if err := migration.Run(); err != nil { 1638 return errors.Trace(err) 1639 } 1640 i.logger.Debugf("importing firewall rules succeeded") 1641 return nil 1642 } 1643 1644 func (i *importer) makeRemoteApplicationDoc(app description.RemoteApplication) *remoteApplicationDoc { 1645 doc := &remoteApplicationDoc{ 1646 Name: app.Name(), 1647 URL: app.URL(), 1648 SourceModelUUID: app.SourceModelTag().Id(), 1649 IsConsumerProxy: app.IsConsumerProxy(), 1650 Bindings: app.Bindings(), 1651 Macaroon: app.Macaroon(), 1652 Version: app.ConsumeVersion(), 1653 } 1654 if !doc.IsConsumerProxy { 1655 doc.OfferUUID = app.OfferUUID() 1656 } 1657 descEndpoints := app.Endpoints() 1658 eps := make([]remoteEndpointDoc, len(descEndpoints)) 1659 for i, ep := range descEndpoints { 1660 eps[i] = remoteEndpointDoc{ 1661 Name: ep.Name(), 1662 Role: charm.RelationRole(ep.Role()), 1663 Interface: ep.Interface(), 1664 // TODO: Role, Scope 1665 } 1666 } 1667 doc.Endpoints = eps 1668 descSpaces := app.Spaces() 1669 spaces := make([]remoteSpaceDoc, len(descSpaces)) 1670 for i, space := range descSpaces { 1671 spaces[i] = remoteSpaceDoc{ 1672 CloudType: space.CloudType(), 1673 Name: space.Name(), 1674 ProviderId: space.ProviderId(), 1675 ProviderAttributes: space.ProviderAttributes(), 1676 } 1677 descSubnets := space.Subnets() 1678 subnets := make([]remoteSubnetDoc, len(descSubnets)) 1679 for i, subnet := range descSubnets { 1680 subnets[i] = remoteSubnetDoc{ 1681 CIDR: subnet.CIDR(), 1682 ProviderId: subnet.ProviderId(), 1683 VLANTag: subnet.VLANTag(), 1684 AvailabilityZones: subnet.AvailabilityZones(), 1685 ProviderSpaceId: subnet.ProviderSpaceId(), 1686 ProviderNetworkId: subnet.ProviderNetworkId(), 1687 } 1688 } 1689 spaces[i].Subnets = subnets 1690 } 1691 doc.Spaces = spaces 1692 return doc 1693 } 1694 1695 func (i *importer) relations() error { 1696 i.logger.Debugf("importing relations") 1697 for _, r := range i.model.Relations() { 1698 if err := i.relation(r); err != nil { 1699 i.logger.Errorf("error importing relation %s: %s", r.Key(), err) 1700 return errors.Annotate(err, r.Key()) 1701 } 1702 } 1703 1704 i.logger.Debugf("importing relations succeeded") 1705 return nil 1706 } 1707 1708 func (i *importer) relation(rel description.Relation) error { 1709 relationDoc := i.makeRelationDoc(rel) 1710 ops := []txn.Op{ 1711 { 1712 C: relationsC, 1713 Id: relationDoc.Key, 1714 Assert: txn.DocMissing, 1715 Insert: relationDoc, 1716 }, 1717 } 1718 1719 var relStatusDoc statusDoc 1720 relStatus := rel.Status() 1721 if relStatus != nil { 1722 relStatusDoc = i.makeStatusDoc(relStatus) 1723 } else { 1724 // Relations are marked as either 1725 // joining or joined, depending on 1726 // whether there are any units in scope. 1727 relStatusDoc = statusDoc{ 1728 Status: status.Joining, 1729 Updated: time.Now().UnixNano(), 1730 } 1731 if relationDoc.UnitCount > 0 { 1732 relStatusDoc.Status = status.Joined 1733 } 1734 } 1735 ops = append(ops, createStatusOp(i.st, relationGlobalScope(rel.Id()), relStatusDoc)) 1736 1737 dbRelation := newRelation(i.st, relationDoc) 1738 // Add an op that adds the relation scope document for each 1739 // unit of the application, and an op that adds the relation settings 1740 // for each unit. 1741 for _, endpoint := range rel.Endpoints() { 1742 appKey := relationApplicationSettingsKey(dbRelation.Id(), endpoint.ApplicationName()) 1743 appSettings := endpoint.ApplicationSettings() 1744 ops = append(ops, createSettingsOp(settingsC, appKey, appSettings)) 1745 1746 units := i.applicationUnits[endpoint.ApplicationName()] 1747 for unitName, settings := range endpoint.AllSettings() { 1748 var ru *RelationUnit 1749 var err error 1750 1751 if unit, ok := units[unitName]; ok { 1752 ru, err = dbRelation.Unit(unit) 1753 if err != nil { 1754 return errors.Trace(err) 1755 } 1756 } else { 1757 ru, err = dbRelation.RemoteUnit(unitName) 1758 if err != nil { 1759 if errors.Is(err, errors.NotFound) { 1760 // This mirrors the logic from export. 1761 // If there are no local or remote units in scope, 1762 // then we are done for this endpoint. 1763 continue 1764 } 1765 return errors.Trace(err) 1766 } 1767 } 1768 1769 ruKey := ru.key() 1770 ops = append(ops, txn.Op{ 1771 C: relationScopesC, 1772 Id: ruKey, 1773 Assert: txn.DocMissing, 1774 Insert: relationScopeDoc{ 1775 Key: ruKey, 1776 }, 1777 }, 1778 createSettingsOp(settingsC, ruKey, settings), 1779 ) 1780 } 1781 } 1782 1783 if err := i.st.db().RunTransaction(ops); err != nil { 1784 return errors.Trace(err) 1785 } 1786 1787 return nil 1788 } 1789 1790 func (i *importer) makeRelationDoc(rel description.Relation) *relationDoc { 1791 endpoints := rel.Endpoints() 1792 doc := &relationDoc{ 1793 Key: rel.Key(), 1794 Id: rel.Id(), 1795 Endpoints: make([]Endpoint, len(endpoints)), 1796 Life: Alive, 1797 } 1798 for i, ep := range endpoints { 1799 doc.Endpoints[i] = Endpoint{ 1800 ApplicationName: ep.ApplicationName(), 1801 Relation: charm.Relation{ 1802 Name: ep.Name(), 1803 Role: charm.RelationRole(ep.Role()), 1804 Interface: ep.Interface(), 1805 Optional: ep.Optional(), 1806 Limit: ep.Limit(), 1807 Scope: charm.RelationScope(ep.Scope()), 1808 }, 1809 } 1810 doc.UnitCount += ep.UnitCount() 1811 } 1812 return doc 1813 } 1814 1815 func (i *importer) remoteEntities() error { 1816 i.logger.Debugf("importing remote entities") 1817 migration := &ImportStateMigration{ 1818 src: i.model, 1819 dst: i.st.db(), 1820 } 1821 offerUUIDByName := make(map[string]string) 1822 for _, app := range i.model.Applications() { 1823 for _, offer := range app.Offers() { 1824 offerUUIDByName[offer.OfferName()] = offer.OfferUUID() 1825 } 1826 } 1827 migration.Add(func() error { 1828 m := ImportRemoteEntities{} 1829 return m.Execute(&applicationOffersStateShim{ 1830 offerUUIDByName: offerUUIDByName, 1831 stateModelNamspaceShim: stateModelNamspaceShim{ 1832 Model: migration.src, 1833 st: i.st, 1834 }}, migration.dst) 1835 }) 1836 if err := migration.Run(); err != nil { 1837 return errors.Trace(err) 1838 } 1839 i.logger.Debugf("importing remote entities succeeded") 1840 return nil 1841 } 1842 1843 func (i *importer) relationNetworks() error { 1844 i.logger.Debugf("importing relation networks") 1845 migration := &ImportStateMigration{ 1846 src: i.model, 1847 dst: i.st.db(), 1848 } 1849 migration.Add(func() error { 1850 m := ImportRelationNetworks{} 1851 return m.Execute(stateModelNamspaceShim{ 1852 Model: migration.src, 1853 st: i.st, 1854 }, migration.dst) 1855 }) 1856 if err := migration.Run(); err != nil { 1857 return errors.Trace(err) 1858 } 1859 i.logger.Debugf("importing relation networks succeeded") 1860 return nil 1861 } 1862 1863 func (i *importer) externalControllers() error { 1864 i.logger.Debugf("importing external controllers") 1865 migration := &ImportStateMigration{ 1866 src: i.model, 1867 dst: i.st.db(), 1868 } 1869 migration.Add(func() error { 1870 m := ImportExternalControllers{} 1871 return m.Execute(stateExternalControllerDocumentFactoryShim{ 1872 stateModelNamspaceShim{ 1873 Model: migration.src, 1874 st: i.st, 1875 }, 1876 i, 1877 }, migration.dst) 1878 }) 1879 if err := migration.Run(); err != nil { 1880 return errors.Trace(err) 1881 } 1882 i.logger.Debugf("importing external controllers succeeded") 1883 return nil 1884 } 1885 1886 // spaces imports spaces without subnets, which are added later. 1887 func (i *importer) spaces() error { 1888 i.logger.Debugf("importing spaces") 1889 for _, s := range i.model.Spaces() { 1890 // The default space should not have been exported, but be defensive. 1891 // Any subnets added to the space will be imported subsequently. 1892 if s.Name() == network.AlphaSpaceName { 1893 continue 1894 } 1895 1896 if s.Id() == "" { 1897 if _, err := i.st.AddSpace(s.Name(), network.Id(s.ProviderID()), nil, s.Public()); err != nil { 1898 i.logger.Errorf("error importing space %s: %s", s.Name(), err) 1899 return errors.Annotate(err, s.Name()) 1900 } 1901 continue 1902 } 1903 1904 ops := i.st.addSpaceTxnOps(s.Id(), s.Name(), network.Id(s.ProviderID()), s.Public()) 1905 if err := i.st.db().RunTransaction(ops); err != nil { 1906 i.logger.Errorf("error importing space %s: %s", s.Name(), err) 1907 return errors.Annotate(err, s.Name()) 1908 } 1909 } 1910 1911 i.logger.Debugf("importing spaces succeeded") 1912 return nil 1913 } 1914 1915 func (i *importer) linklayerdevices() error { 1916 i.logger.Debugf("importing linklayerdevices") 1917 for _, device := range i.model.LinkLayerDevices() { 1918 err := i.addLinkLayerDevice(device) 1919 if err != nil { 1920 i.logger.Errorf("error importing ip device %v: %s", device, err) 1921 return errors.Trace(err) 1922 } 1923 } 1924 i.logger.Debugf("importing linklayerdevices succeeded") 1925 return nil 1926 } 1927 1928 func (i *importer) addLinkLayerDevice(device description.LinkLayerDevice) error { 1929 providerID := device.ProviderID() 1930 modelUUID := i.st.ModelUUID() 1931 localID := linkLayerDeviceGlobalKey(device.MachineID(), device.Name()) 1932 linkLayerDeviceDocID := i.st.docID(localID) 1933 newDoc := &linkLayerDeviceDoc{ 1934 ModelUUID: modelUUID, 1935 DocID: linkLayerDeviceDocID, 1936 MachineID: device.MachineID(), 1937 ProviderID: providerID, 1938 Name: device.Name(), 1939 MTU: device.MTU(), 1940 Type: network.LinkLayerDeviceType(device.Type()), 1941 MACAddress: device.MACAddress(), 1942 IsAutoStart: device.IsAutoStart(), 1943 IsUp: device.IsUp(), 1944 ParentName: device.ParentName(), 1945 VirtualPortType: network.VirtualPortType(device.VirtualPortType()), 1946 } 1947 1948 ops := []txn.Op{{ 1949 C: linkLayerDevicesC, 1950 Id: newDoc.DocID, 1951 Insert: newDoc, 1952 }} 1953 if providerID != "" { 1954 id := network.Id(providerID) 1955 ops = append(ops, i.st.networkEntityGlobalKeyOp("linklayerdevice", id)) 1956 } 1957 if err := i.st.db().RunTransaction(ops); err != nil { 1958 return errors.Trace(err) 1959 } 1960 return nil 1961 } 1962 1963 func (i *importer) subnets() error { 1964 i.logger.Debugf("importing subnets") 1965 for _, subnet := range i.model.Subnets() { 1966 info := network.SubnetInfo{ 1967 CIDR: subnet.CIDR(), 1968 ProviderId: network.Id(subnet.ProviderId()), 1969 ProviderNetworkId: network.Id(subnet.ProviderNetworkId()), 1970 VLANTag: subnet.VLANTag(), 1971 AvailabilityZones: subnet.AvailabilityZones(), 1972 IsPublic: subnet.IsPublic(), 1973 SpaceID: subnet.SpaceID(), 1974 1975 // SpaceName will only be present when migrating from pre-2.7 1976 // models. We use it to look up a space ID. 1977 SpaceName: subnet.SpaceName(), 1978 } 1979 info.SetFan(subnet.FanLocalUnderlay(), subnet.FanOverlay()) 1980 1981 if info.SpaceID == "" && info.SpaceName != "" { 1982 space, err := i.st.SpaceByName(subnet.SpaceName()) 1983 if err != nil { 1984 return errors.Trace(err) 1985 } 1986 info.SpaceID = space.Id() 1987 } 1988 1989 snID := subnet.ID() 1990 if snID == "" { 1991 seq, err := sequence(i.st, "subnet") 1992 if err != nil { 1993 return errors.Trace(err) 1994 } 1995 snID = strconv.Itoa(seq) 1996 } 1997 err := i.addSubnet(snID, info) 1998 if err != nil { 1999 return errors.Trace(err) 2000 } 2001 } 2002 i.logger.Debugf("importing subnets succeeded") 2003 return nil 2004 } 2005 2006 func (i *importer) addSubnet(id string, args network.SubnetInfo) error { 2007 buildTxn := func(attempt int) ([]txn.Op, error) { 2008 subnetDoc, ops, err := i.st.addSubnetOps(id, args) 2009 if err != nil { 2010 return nil, errors.Trace(err) 2011 } 2012 subnet := &Subnet{st: i.st, doc: subnetDoc} 2013 if attempt != 0 { 2014 if _, err = i.st.Subnet(id); err == nil { 2015 return nil, errors.AlreadyExistsf("subnet %q", args.CIDR) 2016 } 2017 if err := subnet.Refresh(); err != nil { 2018 if errors.IsNotFound(err) { 2019 return nil, errors.Errorf("ProviderId %q not unique", args.ProviderId) 2020 } 2021 return nil, errors.Trace(err) 2022 } 2023 } 2024 return ops, nil 2025 } 2026 err := i.st.db().Run(buildTxn) 2027 if err != nil { 2028 return errors.Trace(err) 2029 } 2030 return nil 2031 } 2032 2033 func (i *importer) ipAddresses() error { 2034 i.logger.Debugf("importing IP addresses") 2035 for _, addr := range i.model.IPAddresses() { 2036 err := i.addIPAddress(addr) 2037 if err != nil { 2038 i.logger.Errorf("error importing IP address %v: %s", addr, err) 2039 return errors.Trace(err) 2040 } 2041 } 2042 i.logger.Debugf("importing IP addresses succeeded") 2043 return nil 2044 } 2045 2046 func (i *importer) addIPAddress(addr description.IPAddress) error { 2047 addressValue := addr.Value() 2048 subnetCIDR := addr.SubnetCIDR() 2049 2050 globalKey := ipAddressGlobalKey(addr.MachineID(), addr.DeviceName(), addressValue) 2051 ipAddressDocID := i.st.docID(globalKey) 2052 providerID := addr.ProviderID() 2053 2054 modelUUID := i.st.ModelUUID() 2055 2056 // Compatibility shim for deployments prior to 2.9.1. 2057 configType := addr.ConfigMethod() 2058 if configType == "dynamic" { 2059 configType = string(network.ConfigDHCP) 2060 } 2061 2062 newDoc := &ipAddressDoc{ 2063 DocID: ipAddressDocID, 2064 ModelUUID: modelUUID, 2065 ProviderID: providerID, 2066 DeviceName: addr.DeviceName(), 2067 MachineID: addr.MachineID(), 2068 SubnetCIDR: subnetCIDR, 2069 ConfigMethod: network.AddressConfigType(configType), 2070 Value: addressValue, 2071 DNSServers: addr.DNSServers(), 2072 DNSSearchDomains: addr.DNSSearchDomains(), 2073 GatewayAddress: addr.GatewayAddress(), 2074 IsDefaultGateway: addr.IsDefaultGateway(), 2075 ProviderNetworkID: addr.ProviderNetworkID(), 2076 ProviderSubnetID: addr.ProviderSubnetID(), 2077 Origin: network.Origin(addr.Origin()), 2078 IsShadow: addr.IsShadow(), 2079 IsSecondary: addr.IsSecondary(), 2080 } 2081 2082 ops := []txn.Op{{ 2083 C: ipAddressesC, 2084 Id: newDoc.DocID, 2085 Insert: newDoc, 2086 }} 2087 2088 if providerID != "" { 2089 id := network.Id(providerID) 2090 ops = append(ops, i.st.networkEntityGlobalKeyOp("address", id)) 2091 } 2092 if err := i.st.db().RunTransaction(ops); err != nil { 2093 return errors.Trace(err) 2094 } 2095 return nil 2096 } 2097 2098 func (i *importer) sshHostKeys() error { 2099 i.logger.Debugf("importing ssh host keys") 2100 for _, key := range i.model.SSHHostKeys() { 2101 name := names.NewMachineTag(key.MachineID()) 2102 err := i.st.SetSSHHostKeys(name, key.Keys()) 2103 if err != nil { 2104 i.logger.Errorf("error importing ssh host keys %v: %s", key, err) 2105 return errors.Trace(err) 2106 } 2107 } 2108 i.logger.Debugf("importing ssh host keys succeeded") 2109 return nil 2110 } 2111 2112 func (i *importer) cloudimagemetadata() error { 2113 i.logger.Debugf("importing cloudimagemetadata") 2114 images := i.model.CloudImageMetadata() 2115 var metadatas []cloudimagemetadata.Metadata 2116 for _, image := range images { 2117 // We only want to import custom (user defined metadata). 2118 // Everything else *now* expires after a set time anyway and 2119 // coming from Juju < 2.3.4 would result in non-expiring metadata. 2120 if image.Source() != "custom" { 2121 continue 2122 } 2123 var rootStoragePtr *uint64 2124 if rootStorageSize, ok := image.RootStorageSize(); ok { 2125 rootStoragePtr = &rootStorageSize 2126 } 2127 metadatas = append(metadatas, cloudimagemetadata.Metadata{ 2128 MetadataAttributes: cloudimagemetadata.MetadataAttributes{ 2129 Source: image.Source(), 2130 Stream: image.Stream(), 2131 Region: image.Region(), 2132 Version: image.Version(), 2133 Arch: image.Arch(), 2134 RootStorageType: image.RootStorageType(), 2135 RootStorageSize: rootStoragePtr, 2136 VirtType: image.VirtType(), 2137 }, 2138 Priority: image.Priority(), 2139 ImageId: image.ImageId(), 2140 DateCreated: image.DateCreated(), 2141 }) 2142 } 2143 err := i.st.CloudImageMetadataStorage.SaveMetadata(metadatas) 2144 if err != nil { 2145 i.logger.Errorf("error importing cloudimagemetadata %v: %s", images, err) 2146 return errors.Trace(err) 2147 } 2148 i.logger.Debugf("importing cloudimagemetadata succeeded") 2149 return nil 2150 } 2151 2152 func (i *importer) actions() error { 2153 i.logger.Debugf("importing actions") 2154 for _, action := range i.model.Actions() { 2155 err := i.addAction(action) 2156 if err != nil { 2157 i.logger.Errorf("error importing action %v: %s", action, err) 2158 return errors.Trace(err) 2159 } 2160 } 2161 i.logger.Debugf("importing actions succeeded") 2162 return nil 2163 } 2164 2165 func (i *importer) addAction(action description.Action) error { 2166 modelUUID := i.st.ModelUUID() 2167 newDoc := &actionDoc{ 2168 DocId: i.st.docID(action.Id()), 2169 ModelUUID: modelUUID, 2170 Receiver: action.Receiver(), 2171 Name: action.Name(), 2172 Operation: action.Operation(), 2173 Parameters: action.Parameters(), 2174 Enqueued: action.Enqueued(), 2175 Results: action.Results(), 2176 Message: action.Message(), 2177 Started: action.Started(), 2178 Completed: action.Completed(), 2179 Status: ActionStatus(action.Status()), 2180 Parallel: action.Parallel(), 2181 ExecutionGroup: action.ExecutionGroup(), 2182 } 2183 2184 ops := []txn.Op{{ 2185 C: actionsC, 2186 Id: newDoc.DocId, 2187 Insert: newDoc, 2188 }} 2189 2190 if activeStatus.Contains(string(newDoc.Status)) { 2191 prefix := ensureActionMarker(action.Receiver()) 2192 notificationDoc := &actionNotificationDoc{ 2193 DocId: i.st.docID(prefix + action.Id()), 2194 ModelUUID: modelUUID, 2195 Receiver: action.Receiver(), 2196 ActionID: action.Id(), 2197 } 2198 ops = append(ops, txn.Op{ 2199 C: actionNotificationsC, 2200 Id: notificationDoc.DocId, 2201 Insert: notificationDoc, 2202 }) 2203 } 2204 2205 if err := i.st.db().RunTransaction(ops); err != nil { 2206 return errors.Trace(err) 2207 } 2208 return nil 2209 } 2210 2211 // operations takes the imported operations data and writes it to 2212 // the new model. 2213 func (i *importer) operations() error { 2214 i.logger.Debugf("importing operations") 2215 for _, op := range i.model.Operations() { 2216 err := i.addOperation(op) 2217 if err != nil { 2218 i.logger.Errorf("error importing operation %v: %s", op, err) 2219 return errors.Trace(err) 2220 } 2221 } 2222 i.logger.Debugf("importing operations succeeded") 2223 return nil 2224 } 2225 2226 func (i *importer) addOperation(op description.Operation) error { 2227 modelUUID := i.st.ModelUUID() 2228 newDoc := &operationDoc{ 2229 DocId: i.st.docID(op.Id()), 2230 ModelUUID: modelUUID, 2231 Summary: op.Summary(), 2232 Fail: op.Fail(), 2233 Enqueued: op.Enqueued(), 2234 Started: op.Started(), 2235 Completed: op.Completed(), 2236 Status: ActionStatus(op.Status()), 2237 CompleteTaskCount: op.CompleteTaskCount(), 2238 SpawnedTaskCount: i.countActionTasksForOperation(op), 2239 } 2240 ops := []txn.Op{{ 2241 C: operationsC, 2242 Id: newDoc.DocId, 2243 Insert: newDoc, 2244 }} 2245 2246 if err := i.st.db().RunTransaction(ops); err != nil { 2247 return errors.Trace(err) 2248 } 2249 return nil 2250 } 2251 2252 func (i *importer) countActionTasksForOperation(op description.Operation) int { 2253 if op.SpawnedTaskCount() > 0 { 2254 return op.SpawnedTaskCount() 2255 } 2256 opID := op.Id() 2257 var count int 2258 for _, action := range i.model.Actions() { 2259 if action.Operation() == opID { 2260 count += 1 2261 } 2262 } 2263 return count 2264 } 2265 2266 func (i *importer) importStatusHistory(globalKey string, history []description.Status) error { 2267 docs := make([]interface{}, len(history)) 2268 for i, statusVal := range history { 2269 docs[i] = historicalStatusDoc{ 2270 GlobalKey: globalKey, 2271 Status: status.Status(statusVal.Value()), 2272 StatusInfo: statusVal.Message(), 2273 StatusData: statusVal.Data(), 2274 Updated: statusVal.Updated().UnixNano(), 2275 } 2276 } 2277 if len(docs) == 0 { 2278 return nil 2279 } 2280 2281 statusHistory, closer := i.st.db().GetCollection(statusesHistoryC) 2282 defer closer() 2283 2284 if err := statusHistory.Writeable().Insert(docs...); err != nil { 2285 return errors.Trace(err) 2286 } 2287 return nil 2288 } 2289 2290 func (i *importer) constraints(cons description.Constraints) constraints.Value { 2291 var result constraints.Value 2292 if cons == nil { 2293 return result 2294 } 2295 2296 if allocate := cons.AllocatePublicIP(); allocate { 2297 result.AllocatePublicIP = &allocate 2298 } 2299 if arch := cons.Architecture(); arch != "" { 2300 result.Arch = &arch 2301 } 2302 if container := instance.ContainerType(cons.Container()); container != "" { 2303 result.Container = &container 2304 } 2305 if cores := cons.CpuCores(); cores != 0 { 2306 result.CpuCores = &cores 2307 } 2308 if power := cons.CpuPower(); power != 0 { 2309 result.CpuPower = &power 2310 } 2311 if inst := cons.InstanceType(); inst != "" { 2312 result.InstanceType = &inst 2313 } 2314 if mem := cons.Memory(); mem != 0 { 2315 result.Mem = &mem 2316 } 2317 if disk := cons.RootDisk(); disk != 0 { 2318 result.RootDisk = &disk 2319 } 2320 if source := cons.RootDiskSource(); source != "" { 2321 result.RootDiskSource = &source 2322 } 2323 if spaces := cons.Spaces(); len(spaces) > 0 { 2324 result.Spaces = &spaces 2325 } 2326 if tags := cons.Tags(); len(tags) > 0 { 2327 result.Tags = &tags 2328 } 2329 if virt := cons.VirtType(); virt != "" { 2330 result.VirtType = &virt 2331 } 2332 if zones := cons.Zones(); len(zones) > 0 { 2333 result.Zones = &zones 2334 } 2335 return result 2336 } 2337 2338 func (i *importer) storage() error { 2339 if err := i.storagePools(); err != nil { 2340 return errors.Annotate(err, "storage pools") 2341 } 2342 if err := i.storageInstances(); err != nil { 2343 return errors.Annotate(err, "storage instances") 2344 } 2345 if err := i.volumes(); err != nil { 2346 return errors.Annotate(err, "volumes") 2347 } 2348 if err := i.filesystems(); err != nil { 2349 return errors.Annotate(err, "filesystems") 2350 } 2351 return nil 2352 } 2353 2354 func (i *importer) storageInstances() error { 2355 i.logger.Debugf("importing storage instances") 2356 for _, storage := range i.model.Storages() { 2357 err := i.addStorageInstance(storage) 2358 if err != nil { 2359 i.logger.Errorf("error importing storage %s: %s", storage.Tag(), err) 2360 return errors.Trace(err) 2361 } 2362 } 2363 i.logger.Debugf("importing storage instances succeeded") 2364 return nil 2365 } 2366 2367 func (i *importer) addStorageInstance(storage description.Storage) error { 2368 kind := parseStorageKind(storage.Kind()) 2369 if kind == StorageKindUnknown { 2370 return errors.Errorf("storage kind %q is unknown", storage.Kind()) 2371 } 2372 owner, err := storage.Owner() 2373 if err != nil { 2374 return errors.Annotate(err, "storage owner") 2375 } 2376 var storageOwner string 2377 if owner != nil { 2378 storageOwner = owner.String() 2379 } 2380 attachments := storage.Attachments() 2381 tag := storage.Tag() 2382 var ops []txn.Op 2383 for _, unit := range attachments { 2384 ops = append(ops, createStorageAttachmentOp(tag, unit)) 2385 } 2386 doc := &storageInstanceDoc{ 2387 Id: storage.Tag().Id(), 2388 Kind: kind, 2389 Owner: storageOwner, 2390 StorageName: storage.Name(), 2391 AttachmentCount: len(attachments), 2392 Constraints: i.storageInstanceConstraints(storage), 2393 } 2394 ops = append(ops, txn.Op{ 2395 C: storageInstancesC, 2396 Id: tag.Id(), 2397 Assert: txn.DocMissing, 2398 Insert: doc, 2399 }) 2400 2401 if owner != nil { 2402 refcounts, closer := i.st.db().GetCollection(refcountsC) 2403 defer closer() 2404 storageRefcountKey := entityStorageRefcountKey(owner, storage.Name()) 2405 incRefOp, err := nsRefcounts.CreateOrIncRefOp(refcounts, storageRefcountKey, 1) 2406 if err != nil { 2407 return errors.Trace(err) 2408 } 2409 ops = append(ops, incRefOp) 2410 } 2411 2412 if err := i.st.db().RunTransaction(ops); err != nil { 2413 return errors.Trace(err) 2414 } 2415 return nil 2416 } 2417 2418 func (i *importer) storageInstanceConstraints(storage description.Storage) storageInstanceConstraints { 2419 if cons, ok := storage.Constraints(); ok { 2420 return storageInstanceConstraints(cons) 2421 } 2422 // Older versions of Juju did not record storage constraints on the 2423 // storage instance, so we must do what we do during upgrade steps: 2424 // reconstitute the constraints from the corresponding volume or 2425 // filesystem, or else look in the owner's application storage 2426 // constraints, and if all else fails, apply the defaults. 2427 var cons storageInstanceConstraints 2428 var defaultPool string 2429 switch parseStorageKind(storage.Kind()) { 2430 case StorageKindBlock: 2431 defaultPool = string(provider.LoopProviderType) 2432 for _, volume := range i.model.Volumes() { 2433 if volume.Storage() == storage.Tag() { 2434 cons.Pool = volume.Pool() 2435 cons.Size = volume.Size() 2436 break 2437 } 2438 } 2439 case StorageKindFilesystem: 2440 defaultPool = string(provider.RootfsProviderType) 2441 for _, filesystem := range i.model.Filesystems() { 2442 if filesystem.Storage() == storage.Tag() { 2443 cons.Pool = filesystem.Pool() 2444 cons.Size = filesystem.Size() 2445 break 2446 } 2447 } 2448 } 2449 if cons.Pool == "" { 2450 cons.Pool = defaultPool 2451 cons.Size = 1024 2452 if owner, _ := storage.Owner(); owner != nil { 2453 var appName string 2454 switch owner := owner.(type) { 2455 case names.ApplicationTag: 2456 appName = owner.Id() 2457 case names.UnitTag: 2458 appName, _ = names.UnitApplication(owner.Id()) 2459 } 2460 for _, app := range i.model.Applications() { 2461 if app.Name() != appName { 2462 continue 2463 } 2464 storageName, _ := names.StorageName(storage.Tag().Id()) 2465 appStorageCons, ok := app.StorageDirectives()[storageName] 2466 if ok { 2467 cons.Pool = appStorageCons.Pool() 2468 cons.Size = appStorageCons.Size() 2469 } 2470 break 2471 } 2472 } 2473 logger.Warningf( 2474 "no volume or filesystem found, using application storage constraints for %s", 2475 names.ReadableString(storage.Tag()), 2476 ) 2477 } 2478 return cons 2479 } 2480 2481 func (i *importer) volumes() error { 2482 i.logger.Debugf("importing volumes") 2483 sb, err := NewStorageBackend(i.st) 2484 if err != nil { 2485 return errors.Trace(err) 2486 } 2487 for _, volume := range i.model.Volumes() { 2488 err := i.addVolume(volume, sb) 2489 if err != nil { 2490 i.logger.Errorf("error importing volume %s: %s", volume.Tag(), err) 2491 return errors.Trace(err) 2492 } 2493 } 2494 i.logger.Debugf("importing volumes succeeded") 2495 return nil 2496 } 2497 2498 func (i *importer) addVolume(volume description.Volume, sb *storageBackend) error { 2499 attachments := volume.Attachments() 2500 attachmentPlans := volume.AttachmentPlans() 2501 2502 tag := volume.Tag() 2503 var params *VolumeParams 2504 var info *VolumeInfo 2505 if volume.Provisioned() { 2506 info = &VolumeInfo{ 2507 HardwareId: volume.HardwareID(), 2508 WWN: volume.WWN(), 2509 Size: volume.Size(), 2510 Pool: volume.Pool(), 2511 VolumeId: volume.VolumeID(), 2512 Persistent: volume.Persistent(), 2513 } 2514 } else { 2515 params = &VolumeParams{ 2516 Size: volume.Size(), 2517 Pool: volume.Pool(), 2518 } 2519 } 2520 doc := volumeDoc{ 2521 Name: tag.Id(), 2522 StorageId: volume.Storage().Id(), 2523 // Life: ..., // TODO: import life, default is Alive 2524 Params: params, 2525 Info: info, 2526 AttachmentCount: len(attachments), 2527 } 2528 if detachable, err := isDetachableVolumePool(sb, volume.Pool()); err != nil { 2529 return errors.Trace(err) 2530 } else if !detachable && len(attachments) == 1 { 2531 doc.HostId = attachments[0].Host().Id() 2532 } 2533 status := i.makeStatusDoc(volume.Status()) 2534 ops := sb.newVolumeOps(doc, status) 2535 2536 for _, attachment := range attachments { 2537 ops = append(ops, i.addVolumeAttachmentOp(tag.Id(), attachment, attachment.VolumePlanInfo())) 2538 } 2539 2540 if len(attachmentPlans) > 0 { 2541 for _, val := range attachmentPlans { 2542 ops = append(ops, i.addVolumeAttachmentPlanOp(tag.Id(), val)) 2543 } 2544 } 2545 2546 if err := i.st.db().RunTransaction(ops); err != nil { 2547 return errors.Trace(err) 2548 } 2549 2550 if err := i.importStatusHistory(volumeGlobalKey(tag.Id()), volume.StatusHistory()); err != nil { 2551 return errors.Annotate(err, "status history") 2552 } 2553 return nil 2554 } 2555 2556 func (i *importer) addVolumeAttachmentPlanOp(volID string, volumePlan description.VolumeAttachmentPlan) txn.Op { 2557 descriptionPlanInfo := volumePlan.VolumePlanInfo() 2558 planInfo := &VolumeAttachmentPlanInfo{ 2559 DeviceType: storage.DeviceType(descriptionPlanInfo.DeviceType()), 2560 DeviceAttributes: descriptionPlanInfo.DeviceAttributes(), 2561 } 2562 2563 descriptionBlockInfo := volumePlan.BlockDevice() 2564 blockInfo := &BlockDeviceInfo{ 2565 DeviceName: descriptionBlockInfo.Name(), 2566 DeviceLinks: descriptionBlockInfo.Links(), 2567 Label: descriptionBlockInfo.Label(), 2568 UUID: descriptionBlockInfo.UUID(), 2569 HardwareId: descriptionBlockInfo.HardwareID(), 2570 WWN: descriptionBlockInfo.WWN(), 2571 BusAddress: descriptionBlockInfo.BusAddress(), 2572 Size: descriptionBlockInfo.Size(), 2573 FilesystemType: descriptionBlockInfo.FilesystemType(), 2574 InUse: descriptionBlockInfo.InUse(), 2575 MountPoint: descriptionBlockInfo.MountPoint(), 2576 } 2577 2578 machineId := volumePlan.Machine().Id() 2579 return txn.Op{ 2580 C: volumeAttachmentPlanC, 2581 Id: volumeAttachmentId(machineId, volID), 2582 Assert: txn.DocMissing, 2583 Insert: &volumeAttachmentPlanDoc{ 2584 Volume: volID, 2585 Machine: machineId, 2586 PlanInfo: planInfo, 2587 BlockDevice: blockInfo, 2588 }, 2589 } 2590 } 2591 2592 func (i *importer) addVolumeAttachmentOp(volID string, attachment description.VolumeAttachment, planInfo description.VolumePlanInfo) txn.Op { 2593 var info *VolumeAttachmentInfo 2594 var params *VolumeAttachmentParams 2595 2596 planInf := &VolumeAttachmentPlanInfo{} 2597 2598 deviceType := planInfo.DeviceType() 2599 deviceAttrs := planInfo.DeviceAttributes() 2600 if deviceType != "" || deviceAttrs != nil { 2601 if deviceType != "" { 2602 planInf.DeviceType = storage.DeviceType(deviceType) 2603 } 2604 if deviceAttrs != nil { 2605 planInf.DeviceAttributes = deviceAttrs 2606 } 2607 } else { 2608 planInf = nil 2609 } 2610 2611 if attachment.Provisioned() { 2612 info = &VolumeAttachmentInfo{ 2613 DeviceName: attachment.DeviceName(), 2614 DeviceLink: attachment.DeviceLink(), 2615 BusAddress: attachment.BusAddress(), 2616 ReadOnly: attachment.ReadOnly(), 2617 PlanInfo: planInf, 2618 } 2619 } else { 2620 params = &VolumeAttachmentParams{ 2621 ReadOnly: attachment.ReadOnly(), 2622 } 2623 } 2624 2625 hostId := attachment.Host().Id() 2626 return txn.Op{ 2627 C: volumeAttachmentsC, 2628 Id: volumeAttachmentId(hostId, volID), 2629 Assert: txn.DocMissing, 2630 Insert: &volumeAttachmentDoc{ 2631 Volume: volID, 2632 Host: hostId, 2633 Params: params, 2634 Info: info, 2635 }, 2636 } 2637 } 2638 2639 func (i *importer) filesystems() error { 2640 i.logger.Debugf("importing filesystems") 2641 sb, err := NewStorageBackend(i.st) 2642 if err != nil { 2643 return errors.Trace(err) 2644 } 2645 for _, fs := range i.model.Filesystems() { 2646 err := i.addFilesystem(fs, sb) 2647 if err != nil { 2648 i.logger.Errorf("error importing filesystem %s: %s", fs.Tag(), err) 2649 return errors.Trace(err) 2650 } 2651 } 2652 i.logger.Debugf("importing filesystems succeeded") 2653 return nil 2654 } 2655 2656 func (i *importer) addFilesystem(filesystem description.Filesystem, sb *storageBackend) error { 2657 2658 attachments := filesystem.Attachments() 2659 tag := filesystem.Tag() 2660 var params *FilesystemParams 2661 var info *FilesystemInfo 2662 if filesystem.Provisioned() { 2663 info = &FilesystemInfo{ 2664 Size: filesystem.Size(), 2665 Pool: filesystem.Pool(), 2666 FilesystemId: filesystem.FilesystemID(), 2667 } 2668 } else { 2669 params = &FilesystemParams{ 2670 Size: filesystem.Size(), 2671 Pool: filesystem.Pool(), 2672 } 2673 } 2674 doc := filesystemDoc{ 2675 FilesystemId: tag.Id(), 2676 StorageId: filesystem.Storage().Id(), 2677 VolumeId: filesystem.Volume().Id(), 2678 // Life: ..., // TODO: import life, default is Alive 2679 Params: params, 2680 Info: info, 2681 AttachmentCount: len(attachments), 2682 } 2683 if detachable, err := isDetachableFilesystemPool(sb, filesystem.Pool()); err != nil { 2684 return errors.Trace(err) 2685 } else if !detachable && len(attachments) == 1 { 2686 doc.HostId = attachments[0].Host().Id() 2687 } 2688 status := i.makeStatusDoc(filesystem.Status()) 2689 ops := sb.newFilesystemOps(doc, status) 2690 2691 for _, attachment := range attachments { 2692 ops = append(ops, i.addFilesystemAttachmentOp(tag.Id(), attachment)) 2693 } 2694 2695 if err := i.st.db().RunTransaction(ops); err != nil { 2696 return errors.Trace(err) 2697 } 2698 2699 if err := i.importStatusHistory(filesystemGlobalKey(tag.Id()), filesystem.StatusHistory()); err != nil { 2700 return errors.Annotate(err, "status history") 2701 } 2702 return nil 2703 } 2704 2705 func (i *importer) addFilesystemAttachmentOp(fsID string, attachment description.FilesystemAttachment) txn.Op { 2706 var info *FilesystemAttachmentInfo 2707 var params *FilesystemAttachmentParams 2708 if attachment.Provisioned() { 2709 info = &FilesystemAttachmentInfo{ 2710 MountPoint: attachment.MountPoint(), 2711 ReadOnly: attachment.ReadOnly(), 2712 } 2713 } else { 2714 params = &FilesystemAttachmentParams{ 2715 Location: attachment.MountPoint(), 2716 ReadOnly: attachment.ReadOnly(), 2717 } 2718 } 2719 2720 hostId := attachment.Host().Id() 2721 return txn.Op{ 2722 C: filesystemAttachmentsC, 2723 Id: filesystemAttachmentId(hostId, fsID), 2724 Assert: txn.DocMissing, 2725 Insert: &filesystemAttachmentDoc{ 2726 Filesystem: fsID, 2727 Host: hostId, 2728 // Life: ..., // TODO: import life, default is Alive 2729 Params: params, 2730 Info: info, 2731 }, 2732 } 2733 } 2734 2735 func (i *importer) storagePools() error { 2736 registry, err := i.st.storageProviderRegistry() 2737 if err != nil { 2738 return errors.Annotate(err, "getting provider registry") 2739 } 2740 pm := poolmanager.New(NewStateSettings(i.st), registry) 2741 2742 for _, pool := range i.model.StoragePools() { 2743 _, err := pm.Create(pool.Name(), storage.ProviderType(pool.Provider()), pool.Attributes()) 2744 if err != nil { 2745 return errors.Annotatef(err, "creating pool %q", pool.Name()) 2746 } 2747 } 2748 return nil 2749 } 2750 2751 func (i *importer) secretBackend() error { 2752 mCfg, err := i.dbModel.ModelConfig() 2753 if err != nil { 2754 return errors.Trace(err) 2755 } 2756 mSecretBackendName := mCfg.SecretBackend() 2757 if mSecretBackendName == "" || mSecretBackendName == secretsprovider.Auto || mSecretBackendName == secretsprovider.Internal { 2758 return nil 2759 } 2760 2761 backendID := i.model.SecretBackendID() 2762 if backendID == "" { 2763 // We reject if no backend ID is set, because we don't want to accidentally drain secrets to the wrong backend. 2764 // So we suggest to upgrade the source controller if no backend ID in the exported data(because the source model is too old). 2765 return errors.NotFoundf("secret backend config %q in model export", mSecretBackendName) 2766 } 2767 i.logger.Debugf("importing secret backend") 2768 backends := NewSecretBackends(i.st) 2769 mBackend, err := backends.GetSecretBackendByID(backendID) 2770 if err != nil { 2771 return errors.Annotatef(err, "cannot load secret backend %q", backendID) 2772 } 2773 err = i.dbModel.UpdateModelConfig(map[string]interface{}{config.SecretBackendKey: mBackend.Name}, nil) 2774 return errors.Trace(err) 2775 } 2776 2777 func (i *importer) secrets() error { 2778 i.logger.Debugf("importing secrets") 2779 backends := NewSecretBackends(i.st) 2780 allBackends, err := backends.ListSecretBackends() 2781 if err != nil { 2782 return errors.Annotate(err, "loading secret backends") 2783 } 2784 2785 knownBackends := set.NewStrings() 2786 for _, b := range allBackends { 2787 knownBackends.Add(b.ID) 2788 } 2789 2790 migration := &ImportStateMigration{ 2791 src: i.model, 2792 dst: i.st.db(), 2793 knownSecretBackends: knownBackends, 2794 } 2795 migration.Add(func() error { 2796 m := ImportSecrets{} 2797 return m.Execute(&secretStateShim{ 2798 stateModelNamspaceShim: stateModelNamspaceShim{ 2799 Model: migration.src, 2800 st: i.st, 2801 }, 2802 }, migration.dst, migration.knownSecretBackends) 2803 }) 2804 if err := migration.Run(); err != nil { 2805 return errors.Trace(err) 2806 } 2807 i.logger.Debugf("importing secrets succeeded") 2808 return nil 2809 } 2810 2811 func (i *importer) remoteSecrets() error { 2812 i.logger.Debugf("importing remote secret references") 2813 migration := &ImportStateMigration{ 2814 src: i.model, 2815 dst: i.st.db(), 2816 } 2817 migration.Add(func() error { 2818 m := ImportRemoteSecrets{} 2819 return m.Execute(&secretStateShim{ 2820 stateModelNamspaceShim: stateModelNamspaceShim{ 2821 Model: migration.src, 2822 st: i.st, 2823 }, 2824 }, migration.dst) 2825 }) 2826 if err := migration.Run(); err != nil { 2827 return errors.Trace(err) 2828 } 2829 i.logger.Debugf("importing remote secret references succeeded") 2830 return nil 2831 }