github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"net"
    10  	"strconv"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/names"
    14  	"github.com/juju/utils/shell"
    15  
    16  	"github.com/juju/juju/agent"
    17  	"github.com/juju/juju/juju/paths"
    18  	"github.com/juju/juju/mongo"
    19  	"github.com/juju/juju/network"
    20  	"github.com/juju/juju/service"
    21  	"github.com/juju/juju/state"
    22  )
    23  
    24  func ensureMongoService(agentConfig agent.Config) error {
    25  	var oplogSize int
    26  	if oplogSizeString := agentConfig.Value(agent.MongoOplogSize); oplogSizeString != "" {
    27  		var err error
    28  		if oplogSize, err = strconv.Atoi(oplogSizeString); err != nil {
    29  			return errors.Annotatef(err, "invalid oplog size: %q", oplogSizeString)
    30  		}
    31  	}
    32  
    33  	var numaCtlPolicy bool
    34  	if numaCtlString := agentConfig.Value(agent.NumaCtlPreference); numaCtlString != "" {
    35  		var err error
    36  		if numaCtlPolicy, err = strconv.ParseBool(numaCtlString); err != nil {
    37  			return errors.Annotatef(err, "invalid numactl preference: %q", numaCtlString)
    38  		}
    39  	}
    40  
    41  	si, ok := agentConfig.StateServingInfo()
    42  	if !ok {
    43  		return errors.Errorf("agent config has no state serving info")
    44  	}
    45  
    46  	err := mongo.EnsureServiceInstalled(agentConfig.DataDir(),
    47  		si.StatePort,
    48  		oplogSize,
    49  		numaCtlPolicy,
    50  		agentConfig.MongoVersion(),
    51  		true,
    52  	)
    53  	return errors.Annotate(err, "cannot ensure that mongo service start/stop scripts are in place")
    54  }
    55  
    56  // Restore handles either returning or creating a controller to a backed up status:
    57  // * extracts the content of the given backup file and:
    58  // * runs mongorestore with the backed up mongo dump
    59  // * updates and writes configuration files
    60  // * updates existing db entries to make sure they hold no references to
    61  // old instances
    62  // * updates config in all agents.
    63  func (b *backups) Restore(backupId string, args RestoreArgs) (names.Tag, error) {
    64  	meta, backupReader, err := b.Get(backupId)
    65  	if err != nil {
    66  		return nil, errors.Annotatef(err, "could not fetch backup %q", backupId)
    67  	}
    68  
    69  	defer backupReader.Close()
    70  
    71  	workspace, err := NewArchiveWorkspaceReader(backupReader)
    72  	if err != nil {
    73  		return nil, errors.Annotate(err, "cannot unpack backup file")
    74  	}
    75  	defer workspace.Close()
    76  
    77  	// TODO(perrito666) Create a compatibility table of sorts.
    78  	version := meta.Origin.Version
    79  	backupMachine := names.NewMachineTag(meta.Origin.Machine)
    80  
    81  	if err := mongo.StopService(); err != nil {
    82  		return nil, errors.Annotate(err, "cannot stop mongo to replace files")
    83  	}
    84  
    85  	// delete all the files to be replaced
    86  	if err := PrepareMachineForRestore(); err != nil {
    87  		return nil, errors.Annotate(err, "cannot delete existing files")
    88  	}
    89  	logger.Infof("deleted old files to place new")
    90  
    91  	if err := workspace.UnpackFilesBundle(filesystemRoot()); err != nil {
    92  		return nil, errors.Annotate(err, "cannot obtain system files from backup")
    93  	}
    94  	logger.Infof("placed new files")
    95  
    96  	var agentConfig agent.ConfigSetterWriter
    97  	// The path for the config file might change if the tag changed
    98  	// and also the rest of the path, so we assume as little as possible.
    99  	datadir, err := paths.DataDir(args.NewInstSeries)
   100  	if err != nil {
   101  		return nil, errors.Annotate(err, "cannot determine DataDir for the restored machine")
   102  	}
   103  	agentConfigFile := agent.ConfigPath(datadir, backupMachine)
   104  	if agentConfig, err = agent.ReadConfig(agentConfigFile); err != nil {
   105  		return nil, errors.Annotate(err, "cannot load agent config from disk")
   106  	}
   107  	ssi, ok := agentConfig.StateServingInfo()
   108  	if !ok {
   109  		return nil, errors.Errorf("cannot determine state serving info")
   110  	}
   111  	APIHostPorts := network.NewHostPorts(ssi.APIPort, args.PrivateAddress, args.PublicAddress)
   112  	agentConfig.SetAPIHostPorts([][]network.HostPort{APIHostPorts})
   113  	if err := agentConfig.Write(); err != nil {
   114  		return nil, errors.Annotate(err, "cannot write new agent configuration")
   115  	}
   116  	logger.Infof("wrote new agent config")
   117  
   118  	if backupMachine.Id() != "0" {
   119  		logger.Infof("extra work needed backup belongs to %q machine", backupMachine.String())
   120  		serviceName := "jujud-" + agentConfig.Tag().String()
   121  		aInfo := service.NewMachineAgentInfo(
   122  			agentConfig.Tag().Id(),
   123  			dataDir,
   124  			paths.MustSucceed(paths.LogDir(args.NewInstSeries)),
   125  		)
   126  
   127  		// TODO(perrito666) renderer should have a RendererForSeries, for the moment
   128  		// restore only works on linuxes.
   129  		renderer, _ := shell.NewRenderer("bash")
   130  		serviceAgentConf := service.AgentConf(aInfo, renderer)
   131  		svc, err := service.NewService(serviceName, serviceAgentConf, args.NewInstSeries)
   132  		if err != nil {
   133  			return nil, errors.Annotate(err, "cannot generate service for the restored agent.")
   134  		}
   135  		if err := svc.Install(); err != nil {
   136  			return nil, errors.Annotate(err, "cannot install service for the restored agent.")
   137  		}
   138  		logger.Infof("new machine service")
   139  	}
   140  
   141  	logger.Infof("mongo service will be reinstalled to ensure its presence")
   142  	if err := ensureMongoService(agentConfig); err != nil {
   143  		return nil, errors.Annotate(err, "failed to reinstall service for juju-db")
   144  	}
   145  
   146  	logger.Infof("new mongo will be restored")
   147  	// Restore mongodb from backup
   148  	if err := placeNewMongoService(workspace.DBDumpDir, version); err != nil {
   149  		return nil, errors.Annotate(err, "error restoring state from backup")
   150  	}
   151  
   152  	// Re-start replicaset with the new value for server address
   153  	dialInfo, err := newDialInfo(args.PrivateAddress, agentConfig)
   154  	if err != nil {
   155  		return nil, errors.Annotate(err, "cannot produce dial information")
   156  	}
   157  
   158  	logger.Infof("restarting replicaset")
   159  	memberHostPort := net.JoinHostPort(args.PrivateAddress, strconv.Itoa(ssi.StatePort))
   160  	err = resetReplicaSet(dialInfo, memberHostPort)
   161  	if err != nil {
   162  		return nil, errors.Annotate(err, "cannot reset replicaSet")
   163  	}
   164  
   165  	err = updateMongoEntries(args.NewInstId, args.NewInstTag.Id(), backupMachine.Id(), dialInfo)
   166  	if err != nil {
   167  		return nil, errors.Annotate(err, "cannot update mongo entries")
   168  	}
   169  
   170  	// From here we work with the restored controller
   171  	mgoInfo, ok := agentConfig.MongoInfo()
   172  	if !ok {
   173  		return nil, errors.Errorf("cannot retrieve info to connect to mongo")
   174  	}
   175  
   176  	st, err := newStateConnection(agentConfig.Model(), mgoInfo)
   177  	if err != nil {
   178  		return nil, errors.Trace(err)
   179  	}
   180  	defer st.Close()
   181  
   182  	machine, err := st.Machine(backupMachine.Id())
   183  	if err != nil {
   184  		return nil, errors.Trace(err)
   185  	}
   186  
   187  	err = updateMachineAddresses(machine, args.PrivateAddress, args.PublicAddress)
   188  	if err != nil {
   189  		return nil, errors.Annotate(err, "cannot update api server machine addresses")
   190  	}
   191  
   192  	// update all agents known to the new controller.
   193  	// TODO(perrito666): We should never stop process because of this.
   194  	// updateAllMachines will not return errors for individual
   195  	// agent update failures
   196  	machines, err := st.AllMachines()
   197  	if err != nil {
   198  		return nil, errors.Trace(err)
   199  	}
   200  	if err = updateAllMachines(args.PrivateAddress, machines); err != nil {
   201  		return nil, errors.Annotate(err, "cannot update agents")
   202  	}
   203  
   204  	info, err := st.RestoreInfoSetter()
   205  	if err != nil {
   206  		return nil, errors.Trace(err)
   207  	}
   208  
   209  	// Mark restoreInfo as Finished so upon restart of the apiserver
   210  	// the client can reconnect and determine if we where succesful.
   211  	err = info.SetStatus(state.RestoreFinished)
   212  
   213  	return backupMachine, errors.Annotate(err, "failed to set status to finished")
   214  }