github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/state/backups/backups_linux.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build linux
     5  
     6  package backups
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/names"
    13  
    14  	"github.com/juju/juju/agent"
    15  	"github.com/juju/juju/juju/paths"
    16  	"github.com/juju/juju/network"
    17  	"github.com/juju/juju/state"
    18  )
    19  
    20  // Restore handles either returning or creating a state server to a backed up status:
    21  // * extracts the content of the given backup file and:
    22  // * runs mongorestore with the backed up mongo dump
    23  // * updates and writes configuration files
    24  // * updates existing db entries to make sure they hold no references to
    25  // old instances
    26  // * updates config in all agents.
    27  func (b *backups) Restore(backupId string, args RestoreArgs) error {
    28  	meta, backupReader, err := b.Get(backupId)
    29  	if err != nil {
    30  		return errors.Annotatef(err, "could not fetch backup %q", backupId)
    31  	}
    32  
    33  	defer backupReader.Close()
    34  
    35  	workspace, err := NewArchiveWorkspaceReader(backupReader)
    36  	if err != nil {
    37  		return errors.Annotate(err, "cannot unpack backup file")
    38  	}
    39  	defer workspace.Close()
    40  
    41  	// TODO(perrito666) Create a compatibility table of sorts.
    42  	version := meta.Origin.Version
    43  	backupMachine := names.NewMachineTag(meta.Origin.Machine)
    44  
    45  	// delete all the files to be replaced
    46  	if err := PrepareMachineForRestore(); err != nil {
    47  		return errors.Annotate(err, "cannot delete existing files")
    48  	}
    49  
    50  	if err := workspace.UnpackFilesBundle(filesystemRoot()); err != nil {
    51  		return errors.Annotate(err, "cannot obtain system files from backup")
    52  	}
    53  
    54  	if err := updateBackupMachineTag(backupMachine, args.NewInstTag); err != nil {
    55  		return errors.Annotate(err, "cannot update paths to reflect current machine id")
    56  	}
    57  
    58  	var agentConfig agent.ConfigSetterWriter
    59  	// The path for the config file might change if the tag changed
    60  	// and also the rest of the path, so we assume as little as possible.
    61  	datadir, err := paths.DataDir(args.NewInstSeries)
    62  	if err != nil {
    63  		return errors.Annotate(err, "cannot determine DataDir for the restored machine")
    64  	}
    65  	agentConfigFile := agent.ConfigPath(datadir, args.NewInstTag)
    66  	if agentConfig, err = agent.ReadConfig(agentConfigFile); err != nil {
    67  		return errors.Annotate(err, "cannot load agent config from disk")
    68  	}
    69  	ssi, ok := agentConfig.StateServingInfo()
    70  	if !ok {
    71  		return errors.Errorf("cannot determine state serving info")
    72  	}
    73  	// The machine tag might have changed, we update it.
    74  	agentConfig.SetValue("tag", args.NewInstTag.String())
    75  	apiHostPorts := [][]network.HostPort{
    76  		network.NewHostPorts(ssi.APIPort, args.PrivateAddress),
    77  	}
    78  	agentConfig.SetAPIHostPorts(apiHostPorts)
    79  	if err := agentConfig.Write(); err != nil {
    80  		return errors.Annotate(err, "cannot write new agent configuration")
    81  	}
    82  
    83  	// Restore mongodb from backup
    84  	if err := placeNewMongo(workspace.DBDumpDir, version); err != nil {
    85  		return errors.Annotate(err, "error restoring state from backup")
    86  	}
    87  
    88  	// Re-start replicaset with the new value for server address
    89  	dialInfo, err := newDialInfo(args.PrivateAddress, agentConfig)
    90  	if err != nil {
    91  		return errors.Annotate(err, "cannot produce dial information")
    92  	}
    93  
    94  	memberHostPort := fmt.Sprintf("%s:%d", args.PrivateAddress, ssi.StatePort)
    95  	err = resetReplicaSet(dialInfo, memberHostPort)
    96  	if err != nil {
    97  		return errors.Annotate(err, "cannot reset replicaSet")
    98  	}
    99  
   100  	err = updateMongoEntries(args.NewInstId, args.NewInstTag.Id(), backupMachine.Id(), dialInfo)
   101  	if err != nil {
   102  		return errors.Annotate(err, "cannot update mongo entries")
   103  	}
   104  
   105  	// From here we work with the restored state server
   106  	mgoInfo, ok := agentConfig.MongoInfo()
   107  	if !ok {
   108  		return errors.Errorf("cannot retrieve info to connect to mongo")
   109  	}
   110  
   111  	st, err := newStateConnection(agentConfig.Environment(), mgoInfo)
   112  	if err != nil {
   113  		return errors.Trace(err)
   114  	}
   115  	defer st.Close()
   116  
   117  	machine, err := st.Machine(args.NewInstTag.Id())
   118  	if err != nil {
   119  		return errors.Trace(err)
   120  	}
   121  
   122  	err = updateMachineAddresses(machine, args.PrivateAddress, args.PublicAddress)
   123  	if err != nil {
   124  		return errors.Annotate(err, "cannot update api server machine addresses")
   125  	}
   126  
   127  	// update all agents known to the new state server.
   128  	// TODO(perrito666): We should never stop process because of this.
   129  	// updateAllMachines will not return errors for individual
   130  	// agent update failures
   131  	machines, err := st.AllMachines()
   132  	if err != nil {
   133  		return errors.Trace(err)
   134  	}
   135  	if err = updateAllMachines(args.PrivateAddress, machines); err != nil {
   136  		return errors.Annotate(err, "cannot update agents")
   137  	}
   138  
   139  	info, err := st.RestoreInfoSetter()
   140  
   141  	if err != nil {
   142  		return errors.Trace(err)
   143  	}
   144  
   145  	// Mark restoreInfo as Finished so upon restart of the apiserver
   146  	// the client can reconnect and determine if we where succesful.
   147  	err = info.SetStatus(state.RestoreFinished)
   148  
   149  	return errors.Annotate(err, "failed to set status to finished")
   150  }