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  }