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