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