github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/apiserver/backups/restore.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package backups 5 6 import ( 7 "fmt" 8 "os" 9 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/service" 15 "github.com/juju/juju/service/common" 16 "github.com/juju/juju/state" 17 "github.com/juju/juju/state/backups" 18 ) 19 20 var bootstrapNode = names.NewMachineTag("0") 21 22 // Restore implements the server side of Backups.Restore. 23 func (a *API) Restore(p params.RestoreArgs) error { 24 25 // Get hold of a backup file Reader 26 backup, closer := newBackups(a.st) 27 defer closer.Close() 28 29 // Obtain the address of current machine, where we will be performing restore. 30 machine, err := a.st.Machine(a.machineID) 31 if err != nil { 32 return errors.Trace(err) 33 } 34 35 addr, err := machine.PrivateAddress() 36 if err != nil { 37 return errors.Annotatef(err, "error fetching internal address for machine %q", machine) 38 39 } 40 41 publicAddress, err := machine.PublicAddress() 42 if err != nil { 43 return errors.Annotatef(err, "error fetching public address for machine %q", machine) 44 45 } 46 47 info := a.st.RestoreInfo() 48 // Signal to current state and api server that restore will begin 49 err = info.SetStatus(state.RestoreInProgress) 50 if err != nil { 51 return errors.Annotatef(err, "cannot set the server to %q mode", state.RestoreInProgress) 52 } 53 // Any abnormal termination of this function will mark restore as failed, 54 // succesful termination will call Exit and never run this. 55 defer info.SetStatus(state.RestoreFailed) 56 57 instanceId, err := machine.InstanceId() 58 if err != nil { 59 return errors.Annotate(err, "cannot obtain instance id for machine to be restored") 60 } 61 62 logger.Infof("beginning server side restore of backup %q", p.BackupId) 63 // Restore 64 restoreArgs := backups.RestoreArgs{ 65 PrivateAddress: addr.Value, 66 PublicAddress: publicAddress.Value, 67 NewInstId: instanceId, 68 NewInstTag: machine.Tag(), 69 NewInstSeries: machine.Series(), 70 } 71 72 oldTagString, err := backup.Restore(p.BackupId, restoreArgs) 73 if err != nil { 74 return errors.Annotate(err, "restore failed") 75 } 76 77 // A backup can be made of any component of an ha array. 78 // The files in a backup dont contain purely relativized paths. 79 // If the backup is made of the bootstrap node (machine 0) the 80 // recently created machine will have the same paths and therefore 81 // the startup scripts will fit the new juju. If the backup belongs 82 // to a different machine, we need to create a new set of startup 83 // scripts and exit with 0 (so that the current script does not try 84 // to restart the old juju, which will no longer be there). 85 if oldTagString != nil && oldTagString != bootstrapNode { 86 srvName := fmt.Sprintf("jujud-%s", oldTagString) 87 srv, err := service.DiscoverService(srvName, common.Conf{}) 88 if err != nil { 89 return errors.Annotatef(err, "cannot find %q service", srvName) 90 } 91 if err := srv.Start(); err != nil { 92 return errors.Annotatef(err, "cannot start %q service", srvName) 93 } 94 // We dont want machine-0 to restart since the new one has a different tag. 95 // We started the new one above. 96 os.Exit(0) 97 } 98 99 // After restoring, the api server needs a forced restart, tomb will not work 100 // this is because we change all of juju configuration files and mongo too. 101 // Exiting with 0 would prevent upstart to respawn the process 102 103 // NOTE(fwereade): the apiserver needs to be restarted, yes, but 104 // this approach is completely broken. The only place it's ever 105 // ok to use os.Exit is in a main() func that's *so* simple as to 106 // be reasonably left untested. 107 // 108 // And passing os.Exit in wouldn't make this any better either, 109 // just using it subverts the expectations of *everything* else 110 // running in the process. 111 os.Exit(1) 112 return nil 113 } 114 115 // PrepareRestore implements the server side of Backups.PrepareRestore. 116 func (a *API) PrepareRestore() error { 117 info := a.st.RestoreInfo() 118 logger.Infof("entering restore preparation mode") 119 return info.SetStatus(state.RestorePending) 120 } 121 122 // FinishRestore implements the server side of Backups.FinishRestore. 123 func (a *API) FinishRestore() error { 124 info := a.st.RestoreInfo() 125 currentStatus, err := info.Status() 126 if err != nil { 127 return errors.Trace(err) 128 } 129 if currentStatus != state.RestoreFinished { 130 if err := info.SetStatus(state.RestoreFailed); err != nil { 131 return errors.Trace(err) 132 } 133 return errors.Errorf("Restore did not finish succesfuly") 134 } 135 logger.Infof("Succesfully restored") 136 return info.SetStatus(state.RestoreChecked) 137 }