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 }