launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/jujud/agent.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "fmt" 8 "io" 9 "time" 10 11 "launchpad.net/gnuflag" 12 13 "launchpad.net/juju-core/agent" 14 "launchpad.net/juju-core/cmd" 15 "launchpad.net/juju-core/errors" 16 "launchpad.net/juju-core/state" 17 "launchpad.net/juju-core/state/api" 18 apiagent "launchpad.net/juju-core/state/api/agent" 19 apideployer "launchpad.net/juju-core/state/api/deployer" 20 "launchpad.net/juju-core/state/api/params" 21 "launchpad.net/juju-core/version" 22 "launchpad.net/juju-core/worker" 23 "launchpad.net/juju-core/worker/deployer" 24 "launchpad.net/juju-core/worker/upgrader" 25 ) 26 27 // requiredError is useful when complaining about missing command-line options. 28 func requiredError(name string) error { 29 return fmt.Errorf("--%s option must be set", name) 30 } 31 32 // AgentConf handles command-line flags shared by all agents. 33 type AgentConf struct { 34 dataDir string 35 config agent.Config 36 } 37 38 // addFlags injects common agent flags into f. 39 func (c *AgentConf) addFlags(f *gnuflag.FlagSet) { 40 f.StringVar(&c.dataDir, "data-dir", "/var/lib/juju", "directory for juju data") 41 } 42 43 func (c *AgentConf) checkArgs(args []string) error { 44 if c.dataDir == "" { 45 return requiredError("data-dir") 46 } 47 return cmd.CheckEmpty(args) 48 } 49 50 func (c *AgentConf) read(tag string) (err error) { 51 c.config, err = agent.ReadConf(c.dataDir, tag) 52 return 53 } 54 55 func importance(err error) int { 56 switch { 57 case err == nil: 58 return 0 59 default: 60 return 1 61 case isUpgraded(err): 62 return 2 63 case err == worker.ErrTerminateAgent: 64 return 3 65 } 66 } 67 68 // moreImportant returns whether err0 is 69 // more important than err1 - that is, whether 70 // we should act on err0 in preference to err1. 71 func moreImportant(err0, err1 error) bool { 72 return importance(err0) > importance(err1) 73 } 74 75 func isUpgraded(err error) bool { 76 _, ok := err.(*upgrader.UpgradeReadyError) 77 return ok 78 } 79 80 type Agent interface { 81 Entity(st *state.State) (AgentState, error) 82 Tag() string 83 } 84 85 // The AgentState interface is implemented by state types 86 // that represent running agents. 87 type AgentState interface { 88 // SetAgentVersion sets the tools version that the agent is 89 // currently running. 90 SetAgentVersion(v version.Binary) error 91 Tag() string 92 SetMongoPassword(password string) error 93 Life() state.Life 94 } 95 96 type fatalError struct { 97 Err string 98 } 99 100 func (e *fatalError) Error() string { 101 return e.Err 102 } 103 104 func isFatal(err error) bool { 105 if err == worker.ErrTerminateAgent { 106 return true 107 } 108 if isUpgraded(err) { 109 return true 110 } 111 _, ok := err.(*fatalError) 112 return ok 113 } 114 115 type pinger interface { 116 Ping() error 117 } 118 119 // connectionIsFatal returns a function suitable for passing 120 // as the isFatal argument to worker.NewRunner, 121 // that diagnoses an error as fatal if the connection 122 // has failed or if the error is otherwise fatal. 123 func connectionIsFatal(conn pinger) func(err error) bool { 124 return func(err error) bool { 125 if isFatal(err) { 126 return true 127 } 128 if err := conn.Ping(); err != nil { 129 logger.Infof("error pinging %T: %v", conn, err) 130 return true 131 } 132 return false 133 } 134 } 135 136 // isleep waits for the given duration or until it receives a value on 137 // stop. It returns whether the full duration was slept without being 138 // stopped. 139 func isleep(d time.Duration, stop <-chan struct{}) bool { 140 select { 141 case <-stop: 142 return false 143 case <-time.After(d): 144 } 145 return true 146 } 147 148 func openState(agentConfig agent.Config, a Agent) (*state.State, AgentState, error) { 149 st, err := agentConfig.OpenState() 150 if err != nil { 151 return nil, nil, err 152 } 153 entity, err := a.Entity(st) 154 if errors.IsNotFoundError(err) || err == nil && entity.Life() == state.Dead { 155 err = worker.ErrTerminateAgent 156 } 157 if err != nil { 158 st.Close() 159 return nil, nil, err 160 } 161 return st, entity, nil 162 } 163 164 type apiOpener interface { 165 OpenAPI(api.DialOpts) (*api.State, string, error) 166 } 167 168 func openAPIState(agentConfig apiOpener, a Agent) (*api.State, *apiagent.Entity, error) { 169 // We let the API dial fail immediately because the 170 // runner's loop outside the caller of openAPIState will 171 // keep on retrying. If we block for ages here, 172 // then the worker that's calling this cannot 173 // be interrupted. 174 st, newPassword, err := agentConfig.OpenAPI(api.DialOpts{}) 175 if err != nil { 176 if params.IsCodeNotProvisioned(err) { 177 err = worker.ErrTerminateAgent 178 } 179 if params.IsCodeUnauthorized(err) { 180 err = worker.ErrTerminateAgent 181 } 182 return nil, nil, err 183 } 184 entity, err := st.Agent().Entity(a.Tag()) 185 unauthorized := params.IsCodeUnauthorized(err) 186 dead := err == nil && entity.Life() == params.Dead 187 if unauthorized || dead { 188 err = worker.ErrTerminateAgent 189 } 190 if err != nil { 191 st.Close() 192 return nil, nil, err 193 } 194 if newPassword != "" { 195 if err := entity.SetPassword(newPassword); err != nil { 196 return nil, nil, err 197 } 198 } 199 return st, entity, nil 200 } 201 202 // agentDone processes the error returned by 203 // an exiting agent. 204 func agentDone(err error) error { 205 if err == worker.ErrTerminateAgent { 206 err = nil 207 } 208 if ug, ok := err.(*upgrader.UpgradeReadyError); ok { 209 if err := ug.ChangeAgentTools(); err != nil { 210 // Return and let upstart deal with the restart. 211 return err 212 } 213 } 214 return err 215 } 216 217 type closeWorker struct { 218 worker worker.Worker 219 closer io.Closer 220 } 221 222 // newCloseWorker returns a task that wraps the given task, 223 // closing the given closer when it finishes. 224 func newCloseWorker(worker worker.Worker, closer io.Closer) worker.Worker { 225 return &closeWorker{ 226 worker: worker, 227 closer: closer, 228 } 229 } 230 231 func (c *closeWorker) Kill() { 232 c.worker.Kill() 233 } 234 235 func (c *closeWorker) Wait() error { 236 err := c.worker.Wait() 237 if err := c.closer.Close(); err != nil { 238 logger.Errorf("closeWorker: close error: %v", err) 239 } 240 return err 241 } 242 243 // newDeployContext gives the tests the opportunity to create a deployer.Context 244 // that can be used for testing so as to avoid (1) deploying units to the system 245 // running the tests and (2) get access to the *State used internally, so that 246 // tests can be run without waiting for the 5s watcher refresh time to which we would 247 // otherwise be restricted. 248 var newDeployContext = func(st *apideployer.State, agentConfig agent.Config) deployer.Context { 249 return deployer.NewSimpleContext(agentConfig, st) 250 }