// MongoVersion implements Config. func (c *configInternal) MongoVersion() mongo.Version { v, err := mongo.NewVersion(c.mongoVersion) if err != nil { return mongo.Mongo24 } return v }
// Create is the API method that requests juju to create a new backup // of its state. It returns the metadata for that backup. func (a *API) Create(args params.BackupsCreateArgs) (p params.BackupsMetadataResult, err error) { backupsMethods, closer := newBackups(a.backend) defer closer.Close() session := a.backend.MongoSession().Copy() defer session.Close() // Don't go if HA isn't ready. err = waitUntilReady(session, 60) if err != nil { return p, errors.Annotatef(err, "HA not ready; try again later") } mgoInfo := a.backend.MongoConnectionInfo() v, err := a.backend.MongoVersion() if err != nil { return p, errors.Annotatef(err, "discovering mongo version") } mongoVersion, err := mongo.NewVersion(v) if err != nil { return p, errors.Trace(err) } dbInfo, err := backups.NewDBInfo(mgoInfo, session, mongoVersion) if err != nil { return p, errors.Trace(err) } mSeries, err := a.backend.MachineSeries(a.machineID) if err != nil { return p, errors.Trace(err) } meta, err := backups.NewMetadataState(a.backend, a.machineID, mSeries) if err != nil { return p, errors.Trace(err) } meta.Notes = args.Notes err = backupsMethods.Create(meta, a.paths, dbInfo) if err != nil { return p, errors.Trace(err) } return ResultFromMetadata(meta), nil }
// Restore implements the server side of Backups.Restore. func (a *API) Restore(p params.RestoreArgs) error { logger.Infof("Starting server side restore") // Get hold of a backup file Reader backup, closer := newBackups(a.backend) defer closer.Close() // Obtain the address of current machine, where we will be performing restore. machine, err := a.backend.Machine(a.machineID) if err != nil { return errors.Trace(err) } addr, err := machine.PrivateAddress() if err != nil { return errors.Annotatef(err, "error fetching internal address for machine %q", machine) } publicAddress, err := machine.PublicAddress() if err != nil { return errors.Annotatef(err, "error fetching public address for machine %q", machine) } info := a.backend.RestoreInfo() // Signal to current state and api server that restore will begin err = info.SetStatus(state.RestoreInProgress) if err != nil { return errors.Annotatef(err, "cannot set the server to %q mode", state.RestoreInProgress) } // Any abnormal termination of this function will mark restore as failed, // succesful termination will call Exit and never run this. defer info.SetStatus(state.RestoreFailed) instanceId, err := machine.InstanceId() if err != nil { return errors.Annotate(err, "cannot obtain instance id for machine to be restored") } logger.Infof("beginning server side restore of backup %q", p.BackupId) // Restore restoreArgs := backups.RestoreArgs{ PrivateAddress: addr.Value, PublicAddress: publicAddress.Value, NewInstId: instanceId, NewInstTag: machine.Tag(), NewInstSeries: machine.Series(), } session := a.backend.MongoSession().Copy() defer session.Close() // Don't go if HA isn't ready. err = waitUntilReady(session, 60) if err != nil { return errors.Annotatef(err, "HA not ready; try again later") } mgoInfo := a.backend.MongoConnectionInfo() logger.Debugf("mongo info from state %+v", mgoInfo) v, err := a.backend.MongoVersion() if err != nil { return errors.Annotatef(err, "discovering mongo version") } mongoVersion, err := mongo.NewVersion(v) if err != nil { return errors.Trace(err) } dbInfo, err := backups.NewDBInfo(mgoInfo, session, mongoVersion) if err != nil { return errors.Trace(err) } oldTagString, err := backup.Restore(p.BackupId, dbInfo, restoreArgs) if err != nil { return errors.Annotate(err, "restore failed") } // A backup can be made of any component of an ha array. // The files in a backup don't contain purely relativized paths. // If the backup is made of the bootstrap node (machine 0) the // recently created machine will have the same paths and therefore // the startup scripts will fit the new juju. If the backup belongs // to a different machine, we need to create a new set of startup // scripts and exit with 0 (so that the current script does not try // to restart the old juju, which will no longer be there). if oldTagString != nil && oldTagString != bootstrapNode { srvName := fmt.Sprintf("jujud-%s", oldTagString) srv, err := service.DiscoverService(srvName, common.Conf{}) if err != nil { return errors.Annotatef(err, "cannot find %q service", srvName) } if err := srv.Start(); err != nil { return errors.Annotatef(err, "cannot start %q service", srvName) } // We dont want machine-0 to restart since the new one has a different tag. // We started the new one above. os.Exit(0) } // After restoring, the api server needs a forced restart, tomb will not work // this is because we change all of juju configuration files and mongo too. // Exiting with 0 would prevent upstart to respawn the process // NOTE(fwereade): the apiserver needs to be restarted, yes, but // this approach is completely broken. The only place it's ever // ok to use os.Exit is in a main() func that's *so* simple as to // be reasonably left untested. // // And passing os.Exit in wouldn't make this any better either, // just using it subverts the expectations of *everything* else // running in the process. os.Exit(1) return nil }