github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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, err := a.st.RestoreInfoSetter() 48 if err != nil { 49 return errors.Trace(err) 50 } 51 // Signal to current state and api server that restore will begin 52 err = info.SetStatus(state.RestoreInProgress) 53 if err != nil { 54 return errors.Annotatef(err, "cannot set the server to %q mode", state.RestoreInProgress) 55 } 56 // Any abnormal termination of this function will mark restore as failed, 57 // succesful termination will call Exit and never run this. 58 defer info.SetStatus(state.RestoreFailed) 59 60 instanceId, err := machine.InstanceId() 61 if err != nil { 62 return errors.Annotate(err, "cannot obtain instance id for machine to be restored") 63 } 64 65 logger.Infof("beginning server side restore of backup %q", p.BackupId) 66 // Restore 67 restoreArgs := backups.RestoreArgs{ 68 PrivateAddress: addr.Value, 69 PublicAddress: publicAddress.Value, 70 NewInstId: instanceId, 71 NewInstTag: machine.Tag(), 72 NewInstSeries: machine.Series(), 73 } 74 75 oldTagString, err := backup.Restore(p.BackupId, restoreArgs) 76 if err != nil { 77 return errors.Annotate(err, "restore failed") 78 } 79 80 // A backup can be made of any component of an ha array. 81 // The files in a backup dont contain purely relativized paths. 82 // If the backup is made of the bootstrap node (machine 0) the 83 // recently created machine will have the same paths and therefore 84 // the startup scripts will fit the new juju. If the backup belongs 85 // to a different machine, we need to create a new set of startup 86 // scripts and exit with 0 (so that the current script does not try 87 // to restart the old juju, which will no longer be there). 88 if oldTagString != nil && oldTagString != bootstrapNode { 89 srvName := fmt.Sprintf("jujud-%s", oldTagString) 90 srv, err := service.DiscoverService(srvName, common.Conf{}) 91 if err != nil { 92 return errors.Annotatef(err, "cannot find %q service", srvName) 93 } 94 if err := srv.Start(); err != nil { 95 return errors.Annotatef(err, "cannot start %q service", srvName) 96 } 97 // We dont want machine-0 to restart since the new one has a different tag. 98 // We started the new one above. 99 os.Exit(0) 100 } 101 102 // After restoring, the api server needs a forced restart, tomb will not work 103 // this is because we change all of juju configuration files and mongo too. 104 // Exiting with 0 would prevent upstart to respawn the process 105 os.Exit(1) 106 return nil 107 } 108 109 // PrepareRestore implements the server side of Backups.PrepareRestore. 110 func (a *API) PrepareRestore() error { 111 info, err := a.st.RestoreInfoSetter() 112 if err != nil { 113 return errors.Trace(err) 114 } 115 logger.Infof("entering restore preparation mode") 116 return info.SetStatus(state.RestorePending) 117 } 118 119 // FinishRestore implements the server side of Backups.FinishRestore. 120 func (a *API) FinishRestore() error { 121 info, err := a.st.RestoreInfoSetter() 122 if err != nil { 123 return errors.Trace(err) 124 } 125 currentStatus := info.Status() 126 if currentStatus != state.RestoreFinished { 127 info.SetStatus(state.RestoreFailed) 128 return errors.Errorf("Restore did not finish succesfuly") 129 } 130 logger.Infof("Succesfully restored") 131 return info.SetStatus(state.RestoreChecked) 132 }