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  }