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 }