github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/jujud/util/util.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package util 5 6 import ( 7 "fmt" 8 "io" 9 "strconv" 10 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/utils/series" 14 15 "github.com/juju/juju/agent" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/juju/paths" 18 "github.com/juju/juju/mongo" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/worker" 21 "github.com/juju/juju/worker/upgrader" 22 ) 23 24 var ( 25 logger = loggo.GetLogger("juju.cmd.jujud.util") 26 DataDir = paths.MustSucceed(paths.DataDir(series.HostSeries())) 27 EnsureMongoServer = mongo.EnsureServer 28 ) 29 30 // RequiredError is useful when complaining about missing command-line options. 31 func RequiredError(name string) error { 32 return fmt.Errorf("--%s option must be set", name) 33 } 34 35 // IsFatal determines if an error is fatal to the process. 36 func IsFatal(err error) bool { 37 err = errors.Cause(err) 38 switch err { 39 case worker.ErrTerminateAgent, worker.ErrRebootMachine, worker.ErrShutdownMachine: 40 return true 41 } 42 43 if isUpgraded(err) { 44 return true 45 } 46 _, ok := err.(*FatalError) 47 return ok 48 } 49 50 func isUpgraded(err error) bool { 51 _, ok := err.(*upgrader.UpgradeReadyError) 52 return ok 53 } 54 55 // FatalError is an error type designated for fatal errors. 56 type FatalError struct { 57 Err string 58 } 59 60 // Error returns an error string. 61 func (e *FatalError) Error() string { 62 return e.Err 63 } 64 65 func importance(err error) int { 66 err = errors.Cause(err) 67 switch { 68 case err == nil: 69 return 0 70 default: 71 return 1 72 case isUpgraded(err): 73 return 2 74 case err == worker.ErrRebootMachine: 75 return 3 76 case err == worker.ErrShutdownMachine: 77 return 3 78 case err == worker.ErrTerminateAgent: 79 return 4 80 } 81 } 82 83 // MoreImportant returns whether err0 is more important than err1 - 84 // that is, whether we should act on err0 in preference to err1. 85 func MoreImportant(err0, err1 error) bool { 86 return importance(err0) > importance(err1) 87 } 88 89 // MoreImportantError returns the most important error 90 func MoreImportantError(err0, err1 error) error { 91 if importance(err0) > importance(err1) { 92 return err0 93 } 94 return err1 95 } 96 97 // AgentDone processes the error returned by an exiting agent. 98 func AgentDone(logger loggo.Logger, err error) error { 99 err = errors.Cause(err) 100 switch err { 101 case worker.ErrTerminateAgent, worker.ErrRebootMachine, worker.ErrShutdownMachine: 102 // These errors are swallowed here because we want to exit 103 // the agent process without error, to avoid the init system 104 // restarting us. 105 err = nil 106 } 107 if ug, ok := err.(*upgrader.UpgradeReadyError); ok { 108 if err := ug.ChangeAgentTools(); err != nil { 109 // Return and let the init system deal with the restart. 110 err = errors.Annotate(err, "cannot change agent tools") 111 logger.Infof(err.Error()) 112 return err 113 } 114 } 115 return err 116 } 117 118 // Pinger provides a type that knows how to ping. 119 type Pinger interface { 120 121 // Ping pings something. 122 Ping() error 123 } 124 125 // ConnectionIsFatal returns a function suitable for passing as the 126 // isFatal argument to worker.NewRunner, that diagnoses an error as 127 // fatal if the connection has failed or if the error is otherwise 128 // fatal. 129 func ConnectionIsFatal(logger loggo.Logger, conns ...Pinger) func(err error) bool { 130 return func(err error) bool { 131 if IsFatal(err) { 132 return true 133 } 134 for _, conn := range conns { 135 if ConnectionIsDead(logger, conn) { 136 return true 137 } 138 } 139 return false 140 } 141 } 142 143 // ConnectionIsDead returns true if the given pinger fails to ping. 144 var ConnectionIsDead = func(logger loggo.Logger, conn Pinger) bool { 145 if err := conn.Ping(); err != nil { 146 logger.Infof("error pinging %T: %v", conn, err) 147 return true 148 } 149 return false 150 } 151 152 // NewEnsureServerParams creates an EnsureServerParams from an agent 153 // configuration. 154 func NewEnsureServerParams(agentConfig agent.Config) (mongo.EnsureServerParams, error) { 155 // If oplog size is specified in the agent configuration, use that. 156 // Otherwise leave the default zero value to indicate to EnsureServer 157 // that it should calculate the size. 158 var oplogSize int 159 if oplogSizeString := agentConfig.Value(agent.MongoOplogSize); oplogSizeString != "" { 160 var err error 161 if oplogSize, err = strconv.Atoi(oplogSizeString); err != nil { 162 return mongo.EnsureServerParams{}, fmt.Errorf("invalid oplog size: %q", oplogSizeString) 163 } 164 } 165 166 // If numa ctl preference is specified in the agent configuration, use that. 167 // Otherwise leave the default false value to indicate to EnsureServer 168 // that numactl should not be used. 169 var numaCtlPolicy bool 170 if numaCtlString := agentConfig.Value(agent.NUMACtlPreference); numaCtlString != "" { 171 var err error 172 if numaCtlPolicy, err = strconv.ParseBool(numaCtlString); err != nil { 173 return mongo.EnsureServerParams{}, fmt.Errorf("invalid numactl preference: %q", numaCtlString) 174 } 175 } 176 177 si, ok := agentConfig.StateServingInfo() 178 if !ok { 179 return mongo.EnsureServerParams{}, fmt.Errorf("agent config has no state serving info") 180 } 181 182 params := mongo.EnsureServerParams{ 183 APIPort: si.APIPort, 184 StatePort: si.StatePort, 185 Cert: si.Cert, 186 PrivateKey: si.PrivateKey, 187 CAPrivateKey: si.CAPrivateKey, 188 SharedSecret: si.SharedSecret, 189 SystemIdentity: si.SystemIdentity, 190 191 DataDir: agentConfig.DataDir(), 192 OplogSize: oplogSize, 193 SetNUMAControlPolicy: numaCtlPolicy, 194 } 195 return params, nil 196 } 197 198 // NewCloseWorker returns a task that wraps the given task, 199 // closing the given closer when it finishes. 200 func NewCloseWorker(logger loggo.Logger, worker worker.Worker, closer io.Closer) worker.Worker { 201 return &CloseWorker{ 202 worker: worker, 203 closer: closer, 204 logger: logger, 205 } 206 } 207 208 // CloseWorker is a worker which closes the given closer when finished 209 // with a task. 210 type CloseWorker struct { 211 worker worker.Worker 212 closer io.Closer 213 logger loggo.Logger 214 } 215 216 func (c *CloseWorker) Kill() { 217 c.worker.Kill() 218 } 219 220 func (c *CloseWorker) Wait() error { 221 err := c.worker.Wait() 222 if err := c.closer.Close(); err != nil { 223 c.logger.Errorf("closeWorker: close error: %v", err) 224 } 225 return err 226 } 227 228 // ParamsStateServingInfoToStateStateServingInfo converts a 229 // params.StateServingInfo to a state.StateServingInfo. 230 func ParamsStateServingInfoToStateStateServingInfo(i params.StateServingInfo) state.StateServingInfo { 231 return state.StateServingInfo{ 232 APIPort: i.APIPort, 233 StatePort: i.StatePort, 234 Cert: i.Cert, 235 PrivateKey: i.PrivateKey, 236 CAPrivateKey: i.CAPrivateKey, 237 SharedSecret: i.SharedSecret, 238 SystemIdentity: i.SystemIdentity, 239 } 240 }