github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/version" 12 "gopkg.in/juju/charm.v6-unstable" 13 "gopkg.in/mgo.v2/bson" 14 "gopkg.in/mgo.v2/txn" 15 16 "github.com/juju/juju/constraints" 17 "github.com/juju/juju/core/description" 18 "github.com/juju/juju/environs/config" 19 "github.com/juju/juju/instance" 20 "github.com/juju/juju/status" 21 "github.com/juju/juju/tools" 22 ) 23 24 // When we import a new model, we need to give the leaders some time to 25 // settle. We don't want to have leader switches just because we migrated an 26 // environment, so this time needs to be long enough to make sure we cover 27 // the time taken to migration a reasonable sized environment. We don't yet 28 // know how long this is going to be, but we need something. 29 var initialLeaderClaimTime = time.Minute 30 31 // Import the database agnostic model representation into the database. 32 func (st *State) Import(model description.Model) (_ *Model, _ *State, err error) { 33 logger := loggo.GetLogger("juju.state.import-model") 34 logger.Debugf("import starting for model %s", model.Tag().Id()) 35 // At this stage, attempting to import a model with the same 36 // UUID as an existing model will error. 37 tag := model.Tag() 38 _, err = st.GetModel(tag) 39 if err == nil { 40 // We have an existing matching model. 41 return nil, nil, errors.AlreadyExistsf("model with UUID %s", tag.Id()) 42 } else if !errors.IsNotFound(err) { 43 return nil, nil, errors.Trace(err) 44 } 45 46 // Create the model. 47 cfg, err := config.New(config.NoDefaults, model.Config()) 48 if err != nil { 49 return nil, nil, errors.Trace(err) 50 } 51 dbModel, newSt, err := st.NewModel(ModelArgs{ 52 Config: cfg, 53 Owner: model.Owner(), 54 MigrationMode: MigrationModeImporting, 55 }) 56 if err != nil { 57 return nil, nil, errors.Trace(err) 58 } 59 logger.Debugf("model created %s/%s", dbModel.Owner().Canonical(), dbModel.Name()) 60 defer func() { 61 if err != nil { 62 newSt.Close() 63 } 64 }() 65 66 // I would have loved to use import, but that is a reserved word. 67 restore := importer{ 68 st: newSt, 69 dbModel: dbModel, 70 model: model, 71 logger: logger, 72 } 73 if err := restore.sequences(); err != nil { 74 return nil, nil, errors.Annotate(err, "sequences") 75 } 76 // We need to import the sequences first as we may add blocks 77 // in the modelExtras which will touch the block sequence. 78 if err := restore.modelExtras(); err != nil { 79 return nil, nil, errors.Annotate(err, "base model aspects") 80 } 81 if err := newSt.SetModelConstraints(restore.constraints(model.Constraints())); err != nil { 82 return nil, nil, errors.Annotate(err, "model constraints") 83 } 84 85 if err := restore.modelUsers(); err != nil { 86 return nil, nil, errors.Annotate(err, "modelUsers") 87 } 88 if err := restore.machines(); err != nil { 89 return nil, nil, errors.Annotate(err, "machines") 90 } 91 if err := restore.services(); err != nil { 92 return nil, nil, errors.Annotate(err, "services") 93 } 94 if err := restore.relations(); err != nil { 95 return nil, nil, errors.Annotate(err, "relations") 96 } 97 98 // NOTE: at the end of the import make sure that the mode of the model 99 // is set to "imported" not "active" (or whatever we call it). This way 100 // we don't start model workers for it before the migration process 101 // is complete. 102 103 // Update the sequences to match that the source. 104 105 logger.Debugf("import success") 106 return dbModel, newSt, nil 107 } 108 109 type importer struct { 110 st *State 111 dbModel *Model 112 model description.Model 113 logger loggo.Logger 114 // serviceUnits is populated at the end of loading the services, and is a 115 // map of service name to units of that service. 116 serviceUnits map[string][]*Unit 117 } 118 119 func (i *importer) modelExtras() error { 120 if latest := i.model.LatestToolsVersion(); latest != version.Zero { 121 if err := i.dbModel.UpdateLatestToolsVersion(latest); err != nil { 122 return errors.Trace(err) 123 } 124 } 125 126 if annotations := i.model.Annotations(); len(annotations) > 0 { 127 if err := i.st.SetAnnotations(i.dbModel, annotations); err != nil { 128 return errors.Trace(err) 129 } 130 } 131 132 blockType := map[string]BlockType{ 133 "destroy-model": DestroyBlock, 134 "remove-object": RemoveBlock, 135 "all-changes": ChangeBlock, 136 } 137 138 for blockName, message := range i.model.Blocks() { 139 block, ok := blockType[blockName] 140 if !ok { 141 return errors.Errorf("unknown block type: %q", blockName) 142 } 143 i.st.SwitchBlockOn(block, message) 144 } 145 return nil 146 } 147 148 func (i *importer) sequences() error { 149 sequenceValues := i.model.Sequences() 150 docs := make([]interface{}, 0, len(sequenceValues)) 151 for key, value := range sequenceValues { 152 docs = append(docs, sequenceDoc{ 153 DocID: key, 154 Name: key, 155 Counter: value, 156 }) 157 } 158 159 // In reality, we will almost always have sequences to migrate. 160 // However, in tests, sometimes we don't. 161 if len(docs) == 0 { 162 return nil 163 } 164 165 sequences, closer := i.st.getCollection(sequenceC) 166 defer closer() 167 168 if err := sequences.Writeable().Insert(docs...); err != nil { 169 return errors.Trace(err) 170 } 171 return nil 172 } 173 174 func (i *importer) modelUsers() error { 175 i.logger.Debugf("importing users") 176 177 // The user that was auto-added when we created the model will have 178 // the wrong DateCreated, so we remove it, and add in all the users we 179 // know about. It is also possible that the owner of the model no 180 // longer has access to the model due to changes over time. 181 if err := i.st.RemoveModelUser(i.dbModel.Owner()); err != nil { 182 return errors.Trace(err) 183 } 184 185 users := i.model.Users() 186 modelUUID := i.dbModel.UUID() 187 var ops []txn.Op 188 for _, user := range users { 189 access := ModelAdminAccess 190 if user.ReadOnly() { 191 access = ModelReadAccess 192 } 193 ops = append(ops, createModelUserOp( 194 modelUUID, 195 user.Name(), 196 user.CreatedBy(), 197 user.DisplayName(), 198 user.DateCreated(), 199 access)) 200 } 201 if err := i.st.runTransaction(ops); err != nil { 202 return errors.Trace(err) 203 } 204 // Now set their last connection times. 205 for _, user := range users { 206 i.logger.Debugf("user %s", user.Name()) 207 lastConnection := user.LastConnection() 208 if lastConnection.IsZero() { 209 continue 210 } 211 envUser, err := i.st.ModelUser(user.Name()) 212 if err != nil { 213 return errors.Trace(err) 214 } 215 err = envUser.updateLastConnection(lastConnection) 216 if err != nil { 217 return errors.Trace(err) 218 } 219 } 220 return nil 221 } 222 223 func (i *importer) machines() error { 224 i.logger.Debugf("importing machines") 225 for _, m := range i.model.Machines() { 226 if err := i.machine(m); err != nil { 227 i.logger.Errorf("error importing machine: %s", err) 228 return errors.Annotate(err, m.Id()) 229 } 230 } 231 232 i.logger.Debugf("importing machines succeeded") 233 return nil 234 } 235 236 func (i *importer) machine(m description.Machine) error { 237 // Import this machine, then import its containers. 238 i.logger.Debugf("importing machine %s", m.Id()) 239 240 // 1. construct a machineDoc 241 mdoc, err := i.makeMachineDoc(m) 242 if err != nil { 243 return errors.Annotatef(err, "machine %s", m.Id()) 244 } 245 // 2. construct enough MachineTemplate to pass into 'insertNewMachineOps' 246 // - adds constraints doc 247 // - adds status doc 248 // - adds machine block devices doc 249 250 // TODO: consider filesystems and volumes 251 mStatus := m.Status() 252 if mStatus == nil { 253 return errors.NotValidf("missing status") 254 } 255 machineStatusDoc := statusDoc{ 256 ModelUUID: i.st.ModelUUID(), 257 Status: status.Status(mStatus.Value()), 258 StatusInfo: mStatus.Message(), 259 StatusData: mStatus.Data(), 260 Updated: mStatus.Updated().UnixNano(), 261 } 262 // XXX(mjs) - this needs to be included in the serialized model 263 // (a card exists for the work). Fake it for now. 264 instanceStatusDoc := statusDoc{ 265 ModelUUID: i.st.ModelUUID(), 266 Status: status.StatusStarted, 267 } 268 cons := i.constraints(m.Constraints()) 269 prereqOps, machineOp := i.st.baseNewMachineOps( 270 mdoc, 271 machineStatusDoc, 272 instanceStatusDoc, 273 cons, 274 ) 275 276 // 3. create op for adding in instance data 277 if instance := m.Instance(); instance != nil { 278 prereqOps = append(prereqOps, i.machineInstanceOp(mdoc, instance)) 279 } 280 281 if parentId := ParentId(mdoc.Id); parentId != "" { 282 prereqOps = append(prereqOps, 283 // Update containers record for host machine. 284 i.st.addChildToContainerRefOp(parentId, mdoc.Id), 285 ) 286 } 287 // insertNewContainerRefOp adds an empty doc into the containerRefsC 288 // collection for the machine being added. 289 prereqOps = append(prereqOps, i.st.insertNewContainerRefOp(mdoc.Id)) 290 291 // 4. gather prereqs and machine op, run ops. 292 ops := append(prereqOps, machineOp) 293 294 // 5. add any ops that we may need to add the opened ports information. 295 ops = append(ops, i.machinePortsOps(m)...) 296 297 if err := i.st.runTransaction(ops); err != nil { 298 return errors.Trace(err) 299 } 300 301 machine := newMachine(i.st, mdoc) 302 if annotations := m.Annotations(); len(annotations) > 0 { 303 if err := i.st.SetAnnotations(machine, annotations); err != nil { 304 return errors.Trace(err) 305 } 306 } 307 if err := i.importStatusHistory(machine.globalKey(), m.StatusHistory()); err != nil { 308 return errors.Trace(err) 309 } 310 311 // Now that this machine exists in the database, process each of the 312 // containers in this machine. 313 for _, container := range m.Containers() { 314 if err := i.machine(container); err != nil { 315 return errors.Annotate(err, container.Id()) 316 } 317 } 318 return nil 319 } 320 321 func (i *importer) machinePortsOps(m description.Machine) []txn.Op { 322 var result []txn.Op 323 machineID := m.Id() 324 325 for _, ports := range m.OpenedPorts() { 326 subnetID := ports.SubnetID() 327 doc := &portsDoc{ 328 MachineID: machineID, 329 SubnetID: subnetID, 330 } 331 for _, opened := range ports.OpenPorts() { 332 doc.Ports = append(doc.Ports, PortRange{ 333 UnitName: opened.UnitName(), 334 FromPort: opened.FromPort(), 335 ToPort: opened.ToPort(), 336 Protocol: opened.Protocol(), 337 }) 338 } 339 result = append(result, txn.Op{ 340 C: openedPortsC, 341 Id: portsGlobalKey(machineID, subnetID), 342 Assert: txn.DocMissing, 343 Insert: doc, 344 }) 345 } 346 347 return result 348 } 349 350 func (i *importer) machineInstanceOp(mdoc *machineDoc, inst description.CloudInstance) txn.Op { 351 doc := &instanceData{ 352 DocID: mdoc.DocID, 353 MachineId: mdoc.Id, 354 InstanceId: instance.Id(inst.InstanceId()), 355 ModelUUID: mdoc.ModelUUID, 356 } 357 358 if arch := inst.Architecture(); arch != "" { 359 doc.Arch = &arch 360 } 361 if mem := inst.Memory(); mem != 0 { 362 doc.Mem = &mem 363 } 364 if rootDisk := inst.RootDisk(); rootDisk != 0 { 365 doc.RootDisk = &rootDisk 366 } 367 if cores := inst.CpuCores(); cores != 0 { 368 doc.CpuCores = &cores 369 } 370 if power := inst.CpuPower(); power != 0 { 371 doc.CpuPower = &power 372 } 373 if tags := inst.Tags(); len(tags) > 0 { 374 doc.Tags = &tags 375 } 376 if az := inst.AvailabilityZone(); az != "" { 377 doc.AvailZone = &az 378 } 379 380 return txn.Op{ 381 C: instanceDataC, 382 Id: mdoc.DocID, 383 Assert: txn.DocMissing, 384 Insert: doc, 385 } 386 } 387 388 func (i *importer) makeMachineDoc(m description.Machine) (*machineDoc, error) { 389 id := m.Id() 390 supported, supportedSet := m.SupportedContainers() 391 supportedContainers := make([]instance.ContainerType, len(supported)) 392 for j, c := range supported { 393 supportedContainers[j] = instance.ContainerType(c) 394 } 395 jobs, err := i.makeMachineJobs(m.Jobs()) 396 if err != nil { 397 return nil, errors.Trace(err) 398 } 399 return &machineDoc{ 400 DocID: i.st.docID(id), 401 Id: id, 402 ModelUUID: i.st.ModelUUID(), 403 Nonce: m.Nonce(), 404 Series: m.Series(), 405 ContainerType: m.ContainerType(), 406 Principals: nil, // Set during unit import. 407 Life: Alive, 408 Tools: i.makeTools(m.Tools()), 409 Jobs: jobs, 410 NoVote: true, // State servers can't be migrated yet. 411 HasVote: false, // State servers can't be migrated yet. 412 PasswordHash: m.PasswordHash(), 413 Clean: true, // check this later 414 Addresses: i.makeAddresses(m.ProviderAddresses()), 415 MachineAddresses: i.makeAddresses(m.MachineAddresses()), 416 PreferredPrivateAddress: i.makeAddress(m.PreferredPrivateAddress()), 417 PreferredPublicAddress: i.makeAddress(m.PreferredPublicAddress()), 418 SupportedContainersKnown: supportedSet, 419 SupportedContainers: supportedContainers, 420 Placement: m.Placement(), 421 }, nil 422 } 423 424 func (i *importer) makeMachineJobs(jobs []string) ([]MachineJob, error) { 425 // At time of writing, there are three valid jobs. If any jobs gets 426 // deprecated or changed in the future, older models that specify those 427 // jobs need to be handled here. 428 result := make([]MachineJob, 0, len(jobs)) 429 for _, job := range jobs { 430 switch job { 431 case "host-units": 432 result = append(result, JobHostUnits) 433 case "api-server": 434 result = append(result, JobManageModel) 435 case "manage-networking": 436 result = append(result, JobManageNetworking) 437 default: 438 return nil, errors.Errorf("unknown machine job: %q", job) 439 } 440 } 441 return result, nil 442 } 443 444 func (i *importer) makeTools(t description.AgentTools) *tools.Tools { 445 if t == nil { 446 return nil 447 } 448 return &tools.Tools{ 449 Version: t.Version(), 450 URL: t.URL(), 451 SHA256: t.SHA256(), 452 Size: t.Size(), 453 } 454 } 455 456 func (i *importer) makeAddress(addr description.Address) address { 457 if addr == nil { 458 return address{} 459 } 460 return address{ 461 Value: addr.Value(), 462 AddressType: addr.Type(), 463 Scope: addr.Scope(), 464 Origin: addr.Origin(), 465 } 466 } 467 468 func (i *importer) makeAddresses(addrs []description.Address) []address { 469 result := make([]address, len(addrs)) 470 for j, addr := range addrs { 471 result[j] = i.makeAddress(addr) 472 } 473 return result 474 } 475 476 func (i *importer) services() error { 477 i.logger.Debugf("importing services") 478 for _, s := range i.model.Services() { 479 if err := i.service(s); err != nil { 480 i.logger.Errorf("error importing service %s: %s", s.Name(), err) 481 return errors.Annotate(err, s.Name()) 482 } 483 } 484 485 if err := i.loadUnits(); err != nil { 486 return errors.Annotate(err, "loading new units from db") 487 } 488 i.logger.Debugf("importing services succeeded") 489 return nil 490 } 491 492 func (i *importer) loadUnits() error { 493 unitsCollection, closer := i.st.getCollection(unitsC) 494 defer closer() 495 496 docs := []unitDoc{} 497 err := unitsCollection.Find(nil).All(&docs) 498 if err != nil { 499 return errors.Annotate(err, "cannot get all units") 500 } 501 502 result := make(map[string][]*Unit) 503 for _, doc := range docs { 504 units := result[doc.Service] 505 result[doc.Service] = append(units, newUnit(i.st, &doc)) 506 } 507 i.serviceUnits = result 508 return nil 509 510 } 511 512 // makeStatusDoc assumes status is non-nil. 513 func (i *importer) makeStatusDoc(statusVal description.Status) statusDoc { 514 return statusDoc{ 515 Status: status.Status(statusVal.Value()), 516 StatusInfo: statusVal.Message(), 517 StatusData: statusVal.Data(), 518 Updated: statusVal.Updated().UnixNano(), 519 } 520 } 521 522 func (i *importer) service(s description.Service) error { 523 // Import this service, then soon, its units. 524 i.logger.Debugf("importing service %s", s.Name()) 525 526 // 1. construct a serviceDoc 527 sdoc, err := i.makeServiceDoc(s) 528 if err != nil { 529 return errors.Trace(err) 530 } 531 532 // 2. construct a statusDoc 533 status := s.Status() 534 if status == nil { 535 return errors.NotValidf("missing status") 536 } 537 statusDoc := i.makeStatusDoc(status) 538 // TODO: update never set malarky... maybe... 539 540 ops := addServiceOps(i.st, addServiceOpsArgs{ 541 serviceDoc: sdoc, 542 statusDoc: statusDoc, 543 constraints: i.constraints(s.Constraints()), 544 // storage TODO, 545 settings: s.Settings(), 546 settingsRefCount: s.SettingsRefCount(), 547 leadershipSettings: s.LeadershipSettings(), 548 }) 549 550 if err := i.st.runTransaction(ops); err != nil { 551 return errors.Trace(err) 552 } 553 554 svc := newService(i.st, sdoc) 555 if annotations := s.Annotations(); len(annotations) > 0 { 556 if err := i.st.SetAnnotations(svc, annotations); err != nil { 557 return errors.Trace(err) 558 } 559 } 560 if err := i.importStatusHistory(svc.globalKey(), s.StatusHistory()); err != nil { 561 return errors.Trace(err) 562 } 563 564 for _, unit := range s.Units() { 565 if err := i.unit(s, unit); err != nil { 566 return errors.Trace(err) 567 } 568 } 569 570 if s.Leader() != "" { 571 if err := i.st.LeadershipClaimer().ClaimLeadership( 572 s.Name(), 573 s.Leader(), 574 initialLeaderClaimTime); err != nil { 575 return errors.Trace(err) 576 } 577 } 578 579 return nil 580 } 581 582 func (i *importer) unit(s description.Service, u description.Unit) error { 583 i.logger.Debugf("importing unit %s", u.Name()) 584 585 // 1. construct a unitDoc 586 udoc, err := i.makeUnitDoc(s, u) 587 if err != nil { 588 return errors.Trace(err) 589 } 590 591 // 2. construct a statusDoc for the workload status and agent status 592 agentStatus := u.AgentStatus() 593 if agentStatus == nil { 594 return errors.NotValidf("missing agent status") 595 } 596 agentStatusDoc := i.makeStatusDoc(agentStatus) 597 598 workloadStatus := u.WorkloadStatus() 599 if workloadStatus == nil { 600 return errors.NotValidf("missing workload status") 601 } 602 workloadStatusDoc := i.makeStatusDoc(workloadStatus) 603 604 ops := addUnitOps(i.st, addUnitOpsArgs{ 605 unitDoc: udoc, 606 agentStatusDoc: agentStatusDoc, 607 workloadStatusDoc: workloadStatusDoc, 608 meterStatusDoc: &meterStatusDoc{ 609 Code: u.MeterStatusCode(), 610 Info: u.MeterStatusInfo(), 611 }, 612 }) 613 614 // If the unit is a principal, add it to its machine. 615 if u.Principal().Id() == "" { 616 ops = append(ops, txn.Op{ 617 C: machinesC, 618 Id: u.Machine().Id(), 619 Assert: txn.DocExists, 620 Update: bson.M{"$addToSet": bson.M{"principals": u.Name()}}, 621 }) 622 } 623 624 // We should only have constraints for principal agents. 625 // We don't encode that business logic here, if there are constraints 626 // in the imported model, we put them in the database. 627 if cons := u.Constraints(); cons != nil { 628 agentGlobalKey := unitAgentGlobalKey(u.Name()) 629 ops = append(ops, createConstraintsOp(i.st, agentGlobalKey, i.constraints(cons))) 630 } 631 632 if err := i.st.runTransaction(ops); err != nil { 633 return errors.Trace(err) 634 } 635 636 unit := newUnit(i.st, udoc) 637 if annotations := u.Annotations(); len(annotations) > 0 { 638 if err := i.st.SetAnnotations(unit, annotations); err != nil { 639 return errors.Trace(err) 640 } 641 } 642 if err := i.importStatusHistory(unit.globalKey(), u.WorkloadStatusHistory()); err != nil { 643 return errors.Trace(err) 644 } 645 if err := i.importStatusHistory(unit.globalAgentKey(), u.AgentStatusHistory()); err != nil { 646 return errors.Trace(err) 647 } 648 649 return nil 650 } 651 652 func (i *importer) makeServiceDoc(s description.Service) (*serviceDoc, error) { 653 charmUrl, err := charm.ParseURL(s.CharmURL()) 654 if err != nil { 655 return nil, errors.Trace(err) 656 } 657 658 return &serviceDoc{ 659 Name: s.Name(), 660 Series: s.Series(), 661 Subordinate: s.Subordinate(), 662 CharmURL: charmUrl, 663 Channel: s.Channel(), 664 CharmModifiedVersion: s.CharmModifiedVersion(), 665 ForceCharm: s.ForceCharm(), 666 Life: Alive, 667 UnitCount: len(s.Units()), 668 RelationCount: i.relationCount(s.Name()), 669 Exposed: s.Exposed(), 670 MinUnits: s.MinUnits(), 671 MetricCredentials: s.MetricsCredentials(), 672 }, nil 673 } 674 675 func (i *importer) relationCount(service string) int { 676 count := 0 677 678 for _, rel := range i.model.Relations() { 679 for _, ep := range rel.Endpoints() { 680 if ep.ServiceName() == service { 681 count++ 682 } 683 } 684 } 685 686 return count 687 } 688 689 func (i *importer) makeUnitDoc(s description.Service, u description.Unit) (*unitDoc, error) { 690 // NOTE: if we want to support units having different charms deployed 691 // than the service recomments and migrate that, then we should serialize 692 // the charm url for each unit rather than grabbing the services charm url. 693 // Currently the units charm url matching the service is a precondiation 694 // to migration. 695 charmUrl, err := charm.ParseURL(s.CharmURL()) 696 if err != nil { 697 return nil, errors.Trace(err) 698 } 699 700 var subordinates []string 701 if subs := u.Subordinates(); len(subs) > 0 { 702 for _, s := range subs { 703 subordinates = append(subordinates, s.Id()) 704 } 705 } 706 707 return &unitDoc{ 708 Name: u.Name(), 709 Service: s.Name(), 710 Series: s.Series(), 711 CharmURL: charmUrl, 712 Principal: u.Principal().Id(), 713 Subordinates: subordinates, 714 // StorageAttachmentCount int `bson:"storageattachmentcount"` 715 MachineId: u.Machine().Id(), 716 Tools: i.makeTools(u.Tools()), 717 Life: Alive, 718 PasswordHash: u.PasswordHash(), 719 }, nil 720 } 721 722 func (i *importer) relations() error { 723 i.logger.Debugf("importing relations") 724 for _, r := range i.model.Relations() { 725 if err := i.relation(r); err != nil { 726 i.logger.Errorf("error importing relation %s: %s", r.Key(), err) 727 return errors.Annotate(err, r.Key()) 728 } 729 } 730 731 i.logger.Debugf("importing relations succeeded") 732 return nil 733 } 734 735 func (i *importer) relation(rel description.Relation) error { 736 relationDoc := i.makeRelationDoc(rel) 737 ops := []txn.Op{ 738 { 739 C: relationsC, 740 Id: relationDoc.Key, 741 Assert: txn.DocMissing, 742 Insert: relationDoc, 743 }, 744 } 745 746 dbRelation := newRelation(i.st, relationDoc) 747 // Add an op that adds the relation scope document for each 748 // unit of the service, and an op that adds the relation settings 749 // for each unit. 750 for _, endpoint := range rel.Endpoints() { 751 units := i.serviceUnits[endpoint.ServiceName()] 752 for _, unit := range units { 753 ru, err := dbRelation.Unit(unit) 754 if err != nil { 755 return errors.Trace(err) 756 } 757 ruKey := ru.key() 758 ops = append(ops, txn.Op{ 759 C: relationScopesC, 760 Id: ruKey, 761 Assert: txn.DocMissing, 762 Insert: relationScopeDoc{ 763 Key: ruKey, 764 }, 765 }, 766 createSettingsOp(ruKey, endpoint.Settings(unit.Name())), 767 ) 768 } 769 } 770 771 if err := i.st.runTransaction(ops); err != nil { 772 return errors.Trace(err) 773 } 774 775 return nil 776 } 777 778 func (i *importer) makeRelationDoc(rel description.Relation) *relationDoc { 779 endpoints := rel.Endpoints() 780 doc := &relationDoc{ 781 Key: rel.Key(), 782 Id: rel.Id(), 783 Endpoints: make([]Endpoint, len(endpoints)), 784 Life: Alive, 785 } 786 for i, ep := range endpoints { 787 doc.Endpoints[i] = Endpoint{ 788 ServiceName: ep.ServiceName(), 789 Relation: charm.Relation{ 790 Name: ep.Name(), 791 Role: charm.RelationRole(ep.Role()), 792 Interface: ep.Interface(), 793 Optional: ep.Optional(), 794 Limit: ep.Limit(), 795 Scope: charm.RelationScope(ep.Scope()), 796 }, 797 } 798 doc.UnitCount += ep.UnitCount() 799 } 800 return doc 801 } 802 803 func (i *importer) importStatusHistory(globalKey string, history []description.Status) error { 804 docs := make([]interface{}, len(history)) 805 for i, statusVal := range history { 806 docs[i] = historicalStatusDoc{ 807 GlobalKey: globalKey, 808 Status: status.Status(statusVal.Value()), 809 StatusInfo: statusVal.Message(), 810 StatusData: statusVal.Data(), 811 Updated: statusVal.Updated().UnixNano(), 812 } 813 } 814 815 statusHistory, closer := i.st.getCollection(statusesHistoryC) 816 defer closer() 817 818 if err := statusHistory.Writeable().Insert(docs...); err != nil { 819 return errors.Trace(err) 820 } 821 return nil 822 } 823 824 func (i *importer) constraints(cons description.Constraints) constraints.Value { 825 var result constraints.Value 826 if cons == nil { 827 return result 828 } 829 830 if arch := cons.Architecture(); arch != "" { 831 result.Arch = &arch 832 } 833 if container := instance.ContainerType(cons.Container()); container != "" { 834 result.Container = &container 835 } 836 if cores := cons.CpuCores(); cores != 0 { 837 result.CpuCores = &cores 838 } 839 if power := cons.CpuPower(); power != 0 { 840 result.CpuPower = &power 841 } 842 if inst := cons.InstanceType(); inst != "" { 843 result.InstanceType = &inst 844 } 845 if mem := cons.Memory(); mem != 0 { 846 result.Mem = &mem 847 } 848 if disk := cons.RootDisk(); disk != 0 { 849 result.RootDisk = &disk 850 } 851 if spaces := cons.Spaces(); len(spaces) > 0 { 852 result.Spaces = &spaces 853 } 854 if tags := cons.Tags(); len(tags) > 0 { 855 result.Tags = &tags 856 } 857 return result 858 }