github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/uniter/relation/relationer.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package relation 5 6 import ( 7 "fmt" 8 9 "gopkg.in/juju/charm.v6-unstable/hooks" 10 11 apiuniter "github.com/juju/juju/api/uniter" 12 "github.com/juju/juju/worker/uniter/hook" 13 "github.com/juju/juju/worker/uniter/runner/context" 14 ) 15 16 // Relationer manages a unit's presence in a relation. 17 type Relationer struct { 18 ru *apiuniter.RelationUnit 19 dir *StateDir 20 dying bool 21 } 22 23 // NewRelationer creates a new Relationer. The unit will not join the 24 // relation until explicitly requested. 25 func NewRelationer(ru *apiuniter.RelationUnit, dir *StateDir) *Relationer { 26 return &Relationer{ 27 ru: ru, 28 dir: dir, 29 } 30 } 31 32 // ContextInfo returns a represention of the Relationer's current state. 33 func (r *Relationer) ContextInfo() *context.RelationInfo { 34 members := r.dir.State().Members 35 memberNames := make([]string, 0, len(members)) 36 for memberName := range members { 37 memberNames = append(memberNames, memberName) 38 } 39 return &context.RelationInfo{r.ru, memberNames} 40 } 41 42 // IsImplicit returns whether the local relation endpoint is implicit. Implicit 43 // relations do not run hooks. 44 func (r *Relationer) IsImplicit() bool { 45 return r.ru.Endpoint().IsImplicit() 46 } 47 48 // Join initializes local state and causes the unit to enter its relation 49 // scope, allowing its counterpart units to detect its presence and settings 50 // changes. Local state directory is not created until needed. 51 func (r *Relationer) Join() error { 52 if r.dying { 53 panic("dying relationer must not join!") 54 } 55 // We need to make sure the state directory exists before we join the 56 // relation, lest a subsequent ReadAllStateDirs report local state that 57 // doesn't include relations recorded in remote state. 58 if err := r.dir.Ensure(); err != nil { 59 return err 60 } 61 // uniter.RelationUnit.EnterScope() sets the unit's private address 62 // internally automatically, so no need to set it here. 63 return r.ru.EnterScope() 64 } 65 66 // SetDying informs the relationer that the unit is departing the relation, 67 // and that the only hooks it should send henceforth are -departed hooks, 68 // until the relation is empty, followed by a -broken hook. 69 func (r *Relationer) SetDying() error { 70 if r.IsImplicit() { 71 r.dying = true 72 return r.die() 73 } 74 r.dying = true 75 return nil 76 } 77 78 // die is run when the relationer has no further responsibilities; it leaves 79 // relation scope, and removes the local relation state directory. 80 func (r *Relationer) die() error { 81 if err := r.ru.LeaveScope(); err != nil { 82 return err 83 } 84 return r.dir.Remove() 85 } 86 87 // PrepareHook checks that the relation is in a state such that it makes 88 // sense to execute the supplied hook, and ensures that the relation context 89 // contains the latest relation state as communicated in the hook.Info. It 90 // returns the name of the hook that must be run. 91 func (r *Relationer) PrepareHook(hi hook.Info) (hookName string, err error) { 92 if r.IsImplicit() { 93 panic("implicit relations must not run hooks") 94 } 95 if err = r.dir.State().Validate(hi); err != nil { 96 return 97 } 98 name := r.ru.Endpoint().Name 99 return fmt.Sprintf("%s-%s", name, hi.Kind), nil 100 } 101 102 // CommitHook persists the fact of the supplied hook's completion. 103 func (r *Relationer) CommitHook(hi hook.Info) error { 104 if r.IsImplicit() { 105 panic("implicit relations must not run hooks") 106 } 107 if hi.Kind == hooks.RelationBroken { 108 return r.die() 109 } 110 return r.dir.Write(hi) 111 }