github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/worker/uniter/modes.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter 5 6 import ( 7 stderrors "errors" 8 "fmt" 9 10 "launchpad.net/tomb" 11 12 "launchpad.net/juju-core/charm" 13 "launchpad.net/juju-core/charm/hooks" 14 "launchpad.net/juju-core/environs" 15 "launchpad.net/juju-core/state/api/params" 16 "launchpad.net/juju-core/state/watcher" 17 "launchpad.net/juju-core/worker" 18 ucharm "launchpad.net/juju-core/worker/uniter/charm" 19 "launchpad.net/juju-core/worker/uniter/hook" 20 ) 21 22 // Mode defines the signature of the functions that implement the possible 23 // states of a running Uniter. 24 type Mode func(u *Uniter) (Mode, error) 25 26 // ModeInit is the initial Uniter mode. 27 func ModeInit(u *Uniter) (next Mode, err error) { 28 defer modeContext("ModeInit", &err)() 29 logger.Infof("updating unit addresses") 30 // TODO(dimitern): We might be able to drop all this address stuff 31 // entirely once we have machine addresses. 32 providerType, err := u.st.ProviderType() 33 if err != nil { 34 return nil, err 35 } 36 provider, err := environs.Provider(providerType) 37 if err != nil { 38 return nil, err 39 } 40 if private, err := provider.PrivateAddress(); err != nil { 41 logger.Errorf("cannot get unit's private address: %v", err) 42 return nil, err 43 } else if err = u.unit.SetPrivateAddress(private); err != nil { 44 logger.Errorf("cannot set unit's private address: %v", err) 45 return nil, err 46 } 47 if public, err := provider.PublicAddress(); err != nil { 48 logger.Errorf("cannot get unit's public address: %v", err) 49 return nil, err 50 } else if err = u.unit.SetPublicAddress(public); err != nil { 51 logger.Errorf("cannot set unit's public address: %v", err) 52 return nil, err 53 } 54 logger.Infof("reconciling relation state") 55 if err := u.restoreRelations(); err != nil { 56 return nil, err 57 } 58 return ModeContinue, nil 59 } 60 61 // ModeContinue determines what action to take based on persistent uniter state. 62 func ModeContinue(u *Uniter) (next Mode, err error) { 63 defer modeContext("ModeContinue", &err)() 64 65 // If we haven't yet loaded state, do so. 66 if u.s == nil { 67 logger.Infof("loading uniter state") 68 if u.s, err = u.sf.Read(); err == ErrNoStateFile { 69 // When no state exists, start from scratch. 70 logger.Infof("charm is not deployed") 71 curl, _, err := u.service.CharmURL() 72 if err != nil { 73 return nil, err 74 } 75 return ModeInstalling(curl), nil 76 } else if err != nil { 77 return nil, err 78 } 79 } 80 81 // Filter out states not related to charm deployment. 82 switch u.s.Op { 83 case Continue: 84 logger.Infof("continuing after %q hook", u.s.Hook.Kind) 85 switch u.s.Hook.Kind { 86 case hooks.Stop: 87 return ModeTerminating, nil 88 case hooks.UpgradeCharm: 89 return ModeConfigChanged, nil 90 case hooks.ConfigChanged: 91 if !u.s.Started { 92 return ModeStarting, nil 93 } 94 } 95 if !u.ranConfigChanged { 96 return ModeConfigChanged, nil 97 } 98 return ModeAbide, nil 99 case RunHook: 100 if u.s.OpStep == Queued { 101 logger.Infof("found queued %q hook", u.s.Hook.Kind) 102 if err = u.runHook(*u.s.Hook); err != nil && err != errHookFailed { 103 return nil, err 104 } 105 return ModeContinue, nil 106 } 107 if u.s.OpStep == Done { 108 logger.Infof("found uncommitted %q hook", u.s.Hook.Kind) 109 if err = u.commitHook(*u.s.Hook); err != nil { 110 return nil, err 111 } 112 return ModeContinue, nil 113 } 114 logger.Infof("awaiting error resolution for %q hook", u.s.Hook.Kind) 115 return ModeHookError, nil 116 } 117 118 // Resume interrupted deployment operations. 119 curl := u.s.CharmURL 120 if u.s.Op == Install { 121 logger.Infof("resuming charm install") 122 return ModeInstalling(curl), nil 123 } else if u.s.Op == Upgrade { 124 logger.Infof("resuming charm upgrade") 125 return ModeUpgrading(curl), nil 126 } 127 panic(fmt.Errorf("unhandled uniter operation %q", u.s.Op)) 128 } 129 130 // ModeInstalling is responsible for the initial charm deployment. 131 func ModeInstalling(curl *charm.URL) Mode { 132 name := fmt.Sprintf("ModeInstalling %s", curl) 133 return func(u *Uniter) (next Mode, err error) { 134 defer modeContext(name, &err)() 135 if err = u.deploy(curl, Install); err != nil { 136 return nil, err 137 } 138 return ModeContinue, nil 139 } 140 } 141 142 // ModeUpgrading is responsible for upgrading the charm. 143 func ModeUpgrading(curl *charm.URL) Mode { 144 name := fmt.Sprintf("ModeUpgrading %s", curl) 145 return func(u *Uniter) (next Mode, err error) { 146 defer modeContext(name, &err)() 147 if err = u.deploy(curl, Upgrade); err == ucharm.ErrConflict { 148 return ModeConflicted(curl), nil 149 } else if err != nil { 150 return nil, err 151 } 152 return ModeContinue, nil 153 } 154 } 155 156 // ModeConfigChanged runs the "config-changed" hook. 157 func ModeConfigChanged(u *Uniter) (next Mode, err error) { 158 defer modeContext("ModeConfigChanged", &err)() 159 if !u.s.Started { 160 if err = u.unit.SetStatus(params.StatusInstalled, "", nil); err != nil { 161 return nil, err 162 } 163 } 164 u.f.DiscardConfigEvent() 165 if err := u.runHook(hook.Info{Kind: hooks.ConfigChanged}); err == errHookFailed { 166 return ModeHookError, nil 167 } else if err != nil { 168 return nil, err 169 } 170 return ModeContinue, nil 171 } 172 173 // ModeStarting runs the "start" hook. 174 func ModeStarting(u *Uniter) (next Mode, err error) { 175 defer modeContext("ModeStarting", &err)() 176 if err := u.runHook(hook.Info{Kind: hooks.Start}); err == errHookFailed { 177 return ModeHookError, nil 178 } else if err != nil { 179 return nil, err 180 } 181 return ModeContinue, nil 182 } 183 184 // ModeStopping runs the "stop" hook. 185 func ModeStopping(u *Uniter) (next Mode, err error) { 186 defer modeContext("ModeStopping", &err)() 187 if err := u.runHook(hook.Info{Kind: hooks.Stop}); err == errHookFailed { 188 return ModeHookError, nil 189 } else if err != nil { 190 return nil, err 191 } 192 return ModeContinue, nil 193 } 194 195 // ModeTerminating marks the unit dead and returns ErrTerminateAgent. 196 func ModeTerminating(u *Uniter) (next Mode, err error) { 197 defer modeContext("ModeTerminating", &err)() 198 if err = u.unit.SetStatus(params.StatusStopped, "", nil); err != nil { 199 return nil, err 200 } 201 w, err := u.unit.Watch() 202 if err != nil { 203 return nil, err 204 } 205 defer watcher.Stop(w, &u.tomb) 206 for { 207 select { 208 case <-u.tomb.Dying(): 209 return nil, tomb.ErrDying 210 case _, ok := <-w.Changes(): 211 if !ok { 212 return nil, watcher.MustErr(w) 213 } 214 if err := u.unit.Refresh(); err != nil { 215 return nil, err 216 } 217 if hasSubs, err := u.unit.HasSubordinates(); err != nil { 218 return nil, err 219 } else if hasSubs { 220 continue 221 } 222 // The unit is known to be Dying; so if it didn't have subordinates 223 // just above, it can't acquire new ones before this call. 224 if err := u.unit.EnsureDead(); err != nil { 225 return nil, err 226 } 227 return nil, worker.ErrTerminateAgent 228 } 229 } 230 } 231 232 // ModeAbide is the Uniter's usual steady state. It watches for and responds to: 233 // * service configuration changes 234 // * charm upgrade requests 235 // * relation changes 236 // * unit death 237 func ModeAbide(u *Uniter) (next Mode, err error) { 238 defer modeContext("ModeAbide", &err)() 239 if u.s.Op != Continue { 240 return nil, fmt.Errorf("insane uniter state: %#v", u.s) 241 } 242 if err = u.unit.SetStatus(params.StatusStarted, "", nil); err != nil { 243 return nil, err 244 } 245 u.f.WantUpgradeEvent(false) 246 for _, r := range u.relationers { 247 r.StartHooks() 248 } 249 defer func() { 250 for _, r := range u.relationers { 251 if e := r.StopHooks(); e != nil && err == nil { 252 err = e 253 } 254 } 255 }() 256 select { 257 case <-u.f.UnitDying(): 258 return modeAbideDyingLoop(u) 259 default: 260 } 261 return modeAbideAliveLoop(u) 262 } 263 264 // modeAbideAliveLoop handles all state changes for ModeAbide when the unit 265 // is in an Alive state. 266 func modeAbideAliveLoop(u *Uniter) (Mode, error) { 267 for { 268 hi := hook.Info{} 269 select { 270 case <-u.tomb.Dying(): 271 return nil, tomb.ErrDying 272 case <-u.f.UnitDying(): 273 return modeAbideDyingLoop(u) 274 case <-u.f.ConfigEvents(): 275 hi = hook.Info{Kind: hooks.ConfigChanged} 276 case hi = <-u.relationHooks: 277 case ids := <-u.f.RelationsEvents(): 278 added, err := u.updateRelations(ids) 279 if err != nil { 280 return nil, err 281 } 282 for _, r := range added { 283 r.StartHooks() 284 } 285 continue 286 case curl := <-u.f.UpgradeEvents(): 287 return ModeUpgrading(curl), nil 288 } 289 if err := u.runHook(hi); err == errHookFailed { 290 return ModeHookError, nil 291 } else if err != nil { 292 return nil, err 293 } 294 } 295 } 296 297 // modeAbideDyingLoop handles the proper termination of all relations in 298 // response to a Dying unit. 299 func modeAbideDyingLoop(u *Uniter) (next Mode, err error) { 300 if err := u.unit.Refresh(); err != nil { 301 return nil, err 302 } 303 if err = u.unit.DestroyAllSubordinates(); err != nil { 304 return nil, err 305 } 306 for id, r := range u.relationers { 307 if err := r.SetDying(); err != nil { 308 return nil, err 309 } else if r.IsImplicit() { 310 delete(u.relationers, id) 311 } 312 } 313 for { 314 if len(u.relationers) == 0 { 315 return ModeStopping, nil 316 } 317 hi := hook.Info{} 318 select { 319 case <-u.tomb.Dying(): 320 return nil, tomb.ErrDying 321 case <-u.f.ConfigEvents(): 322 hi = hook.Info{Kind: hooks.ConfigChanged} 323 case hi = <-u.relationHooks: 324 } 325 if err = u.runHook(hi); err == errHookFailed { 326 return ModeHookError, nil 327 } else if err != nil { 328 return nil, err 329 } 330 } 331 } 332 333 // ModeHookError is responsible for watching and responding to: 334 // * user resolution of hook errors 335 // * charm upgrade requests 336 func ModeHookError(u *Uniter) (next Mode, err error) { 337 defer modeContext("ModeHookError", &err)() 338 if u.s.Op != RunHook || u.s.OpStep != Pending { 339 return nil, fmt.Errorf("insane uniter state: %#v", u.s) 340 } 341 msg := fmt.Sprintf("hook failed: %q", u.currentHookName()) 342 // Create error information for status. 343 data := params.StatusData{"hook": u.currentHookName()} 344 if u.s.Hook.Kind.IsRelation() { 345 data["relation-id"] = u.s.Hook.RelationId 346 if u.s.Hook.RemoteUnit != "" { 347 data["remote-unit"] = u.s.Hook.RemoteUnit 348 } 349 } 350 if err = u.unit.SetStatus(params.StatusError, msg, data); err != nil { 351 return nil, err 352 } 353 u.f.WantResolvedEvent() 354 u.f.WantUpgradeEvent(true) 355 for { 356 select { 357 case <-u.tomb.Dying(): 358 return nil, tomb.ErrDying 359 case rm := <-u.f.ResolvedEvents(): 360 switch rm { 361 case params.ResolvedRetryHooks: 362 err = u.runHook(*u.s.Hook) 363 case params.ResolvedNoHooks: 364 err = u.commitHook(*u.s.Hook) 365 default: 366 return nil, fmt.Errorf("unknown resolved mode %q", rm) 367 } 368 if e := u.f.ClearResolved(); e != nil { 369 return nil, e 370 } 371 if err == errHookFailed { 372 continue 373 } else if err != nil { 374 return nil, err 375 } 376 return ModeContinue, nil 377 case curl := <-u.f.UpgradeEvents(): 378 return ModeUpgrading(curl), nil 379 } 380 } 381 } 382 383 // ModeConflicted is responsible for watching and responding to: 384 // * user resolution of charm upgrade conflicts 385 // * forced charm upgrade requests 386 func ModeConflicted(curl *charm.URL) Mode { 387 return func(u *Uniter) (next Mode, err error) { 388 defer modeContext("ModeConflicted", &err)() 389 // TODO(mue) Add helpful data here too in later CL. 390 if err = u.unit.SetStatus(params.StatusError, "upgrade failed", nil); err != nil { 391 return nil, err 392 } 393 u.f.WantResolvedEvent() 394 u.f.WantUpgradeEvent(true) 395 select { 396 case <-u.tomb.Dying(): 397 return nil, tomb.ErrDying 398 case <-u.f.ResolvedEvents(): 399 err = u.deployer.NotifyResolved() 400 if e := u.f.ClearResolved(); e != nil { 401 return nil, e 402 } 403 if err != nil { 404 return nil, err 405 } 406 case curl = <-u.f.UpgradeEvents(): 407 if err := u.deployer.NotifyRevert(); err != nil { 408 return nil, err 409 } 410 } 411 return ModeUpgrading(curl), nil 412 } 413 } 414 415 // modeContext returns a function that implements logging and common error 416 // manipulation for Mode funcs. 417 func modeContext(name string, err *error) func() { 418 logger.Infof("%s starting", name) 419 return func() { 420 logger.Debugf("%s exiting", name) 421 switch *err { 422 case nil, tomb.ErrDying, worker.ErrTerminateAgent: 423 default: 424 *err = stderrors.New(name + ": " + (*err).Error()) 425 } 426 } 427 }