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