github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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 16 "github.com/juju/juju/agent" 17 apirsyslog "github.com/juju/juju/api/rsyslog" 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/version" 23 "github.com/juju/juju/worker" 24 "github.com/juju/juju/worker/rsyslog" 25 "github.com/juju/juju/worker/upgrader" 26 ) 27 28 var ( 29 logger = loggo.GetLogger("juju.cmd.jujud.util") 30 DataDir = paths.MustSucceed(paths.DataDir(version.Current.Series)) 31 EnsureMongoServer = mongo.EnsureServer 32 ) 33 34 // requiredError is useful when complaining about missing command-line options. 35 func RequiredError(name string) error { 36 return fmt.Errorf("--%s option must be set", name) 37 } 38 39 // IsFatal determines if an error is fatal to the process. 40 func IsFatal(err error) bool { 41 err = errors.Cause(err) 42 switch err { 43 case worker.ErrTerminateAgent, worker.ErrRebootMachine, worker.ErrShutdownMachine: 44 return true 45 } 46 47 if isUpgraded(err) { 48 return true 49 } 50 _, ok := err.(*FatalError) 51 return ok 52 } 53 54 func isUpgraded(err error) bool { 55 _, ok := err.(*upgrader.UpgradeReadyError) 56 return ok 57 } 58 59 // FatalError is an error type designated for fatal errors. 60 type FatalError struct { 61 Err string 62 } 63 64 // Error returns an error string. 65 func (e *FatalError) Error() string { 66 return e.Err 67 } 68 69 func importance(err error) int { 70 err = errors.Cause(err) 71 switch { 72 case err == nil: 73 return 0 74 default: 75 return 1 76 case isUpgraded(err): 77 return 2 78 case err == worker.ErrRebootMachine: 79 return 3 80 case err == worker.ErrShutdownMachine: 81 return 3 82 case err == worker.ErrTerminateAgent: 83 return 4 84 } 85 } 86 87 // MoreImportant returns whether err0 is more important than err1 - 88 // that is, whether we should act on err0 in preference to err1. 89 func MoreImportant(err0, err1 error) bool { 90 return importance(err0) > importance(err1) 91 } 92 93 // MoreImportantError returns the most important error 94 func MoreImportantError(err0, err1 error) error { 95 if importance(err0) > importance(err1) { 96 return err0 97 } 98 return err1 99 } 100 101 // AgentDone processes the error returned by 102 // an exiting agent. 103 func AgentDone(logger loggo.Logger, err error) error { 104 err = errors.Cause(err) 105 switch err { 106 case worker.ErrTerminateAgent, worker.ErrRebootMachine, worker.ErrShutdownMachine: 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 Namespace: agentConfig.Value(agent.Namespace), 195 OplogSize: oplogSize, 196 SetNumaControlPolicy: numaCtlPolicy, 197 } 198 return params, nil 199 } 200 201 // NewCloseWorker returns a task that wraps the given task, 202 // closing the given closer when it finishes. 203 func NewCloseWorker(logger loggo.Logger, worker worker.Worker, closer io.Closer) worker.Worker { 204 return &CloseWorker{ 205 worker: worker, 206 closer: closer, 207 logger: logger, 208 } 209 } 210 211 // CloseWorker is a worker which closes the given closer when finished 212 // with a task. 213 type CloseWorker struct { 214 worker worker.Worker 215 closer io.Closer 216 logger loggo.Logger 217 } 218 219 func (c *CloseWorker) Kill() { 220 c.worker.Kill() 221 } 222 223 func (c *CloseWorker) Wait() error { 224 err := c.worker.Wait() 225 if err := c.closer.Close(); err != nil { 226 c.logger.Errorf("closeWorker: close error: %v", err) 227 } 228 return err 229 } 230 231 // HookExecutionLock returns an *fslock.Lock suitable for use as a 232 // unit hook execution lock. Other workers may also use this lock if 233 // they require isolation from hook execution. 234 func HookExecutionLock(dataDir string) (*fslock.Lock, error) { 235 lockDir := filepath.Join(dataDir, "locks") 236 return fslock.NewLock(lockDir, "uniter-hook-execution") 237 } 238 239 // NewRsyslogConfigWorker creates and returns a new 240 // RsyslogConfigWorker based on the specified configuration 241 // parameters. 242 var NewRsyslogConfigWorker = func(st *apirsyslog.State, agentConfig agent.Config, mode rsyslog.RsyslogMode) (worker.Worker, error) { 243 tag := agentConfig.Tag() 244 namespace := agentConfig.Value(agent.Namespace) 245 addrs, err := agentConfig.APIAddresses() 246 if err != nil { 247 return nil, err 248 } 249 return rsyslog.NewRsyslogConfigWorker(st, mode, tag, namespace, addrs, agent.DefaultConfDir) 250 } 251 252 // ParamsStateServingInfoToStateStateServingInfo converts a 253 // params.StateServingInfo to a state.StateServingInfo. 254 func ParamsStateServingInfoToStateStateServingInfo(i params.StateServingInfo) state.StateServingInfo { 255 return state.StateServingInfo{ 256 APIPort: i.APIPort, 257 StatePort: i.StatePort, 258 Cert: i.Cert, 259 PrivateKey: i.PrivateKey, 260 CAPrivateKey: i.CAPrivateKey, 261 SharedSecret: i.SharedSecret, 262 SystemIdentity: i.SystemIdentity, 263 } 264 }