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  }