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