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  }