github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/uniter/subordinaterelationwatcher.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter 5 6 import ( 7 "github.com/juju/collections/set" 8 "github.com/juju/errors" 9 "gopkg.in/juju/charm.v6" 10 "gopkg.in/juju/worker.v1/catacomb" 11 12 "github.com/juju/juju/state" 13 ) 14 15 type subRelationsWatcher struct { 16 catacomb catacomb.Catacomb 17 backend *state.State 18 app *state.Application 19 principalName string 20 21 // Maps relation keys to whether that relation should be 22 // included. Needed particularly for when the relation goes away. 23 relations map[string]bool 24 out chan []string 25 } 26 27 // newSubordinateRelationsWatcher creates a watcher that will notify 28 // about relation lifecycle events for subordinateApp, but filtered to 29 // be relevant to a unit deployed to a container with the 30 // principalName app. Global relations will be included, but only 31 // container-scoped relations for the principal application will be 32 // emitted - other container-scoped relations will be filtered out. 33 func newSubordinateRelationsWatcher(backend *state.State, subordinateApp *state.Application, principalName string) ( 34 state.StringsWatcher, error, 35 ) { 36 w := &subRelationsWatcher{ 37 backend: backend, 38 app: subordinateApp, 39 principalName: principalName, 40 relations: make(map[string]bool), 41 out: make(chan []string), 42 } 43 err := catacomb.Invoke(catacomb.Plan{ 44 Site: &w.catacomb, 45 Work: w.loop, 46 }) 47 return w, errors.Trace(err) 48 } 49 50 func (w *subRelationsWatcher) loop() error { 51 defer close(w.out) 52 relationsw := w.app.WatchRelations() 53 if err := w.catacomb.Add(relationsw); err != nil { 54 return errors.Trace(err) 55 } 56 var ( 57 sentInitial bool 58 out chan []string 59 60 currentRelations = set.NewStrings() 61 ) 62 for { 63 select { 64 case <-w.catacomb.Dying(): 65 return w.catacomb.ErrDying() 66 case out <- currentRelations.Values(): 67 sentInitial = true 68 currentRelations = set.NewStrings() 69 out = nil 70 case newRelations, ok := <-relationsw.Changes(): 71 if !ok { 72 return w.catacomb.ErrDying() 73 } 74 for _, relation := range newRelations { 75 if currentRelations.Contains(relation) { 76 continue 77 } 78 shouldSend, err := w.shouldSend(relation) 79 if err != nil { 80 return errors.Trace(err) 81 } 82 if shouldSend { 83 currentRelations.Add(relation) 84 } 85 } 86 if !sentInitial || currentRelations.Size() > 0 { 87 out = w.out 88 } 89 } 90 } 91 } 92 93 func (w *subRelationsWatcher) shouldSend(key string) (bool, error) { 94 if shouldSend, found := w.relations[key]; found { 95 return shouldSend, nil 96 } 97 result, err := w.shouldSendCheck(key) 98 if err == nil { 99 w.relations[key] = result 100 } 101 return result, errors.Trace(err) 102 } 103 104 func (w *subRelationsWatcher) shouldSendCheck(key string) (bool, error) { 105 rel, err := w.backend.KeyRelation(key) 106 if errors.IsNotFound(err) { 107 // We never saw it, and it's already gone away, so we can drop it. 108 logger.Debugf("couldn't find unknown relation %q", key) 109 return false, nil 110 } else if err != nil { 111 return false, errors.Trace(err) 112 } 113 114 thisEnd, err := rel.Endpoint(w.app.Name()) 115 if err != nil { 116 return false, errors.Trace(err) 117 } 118 if thisEnd.Scope == charm.ScopeGlobal { 119 return true, nil 120 } 121 122 // Only allow container relations if the other end is our 123 // principal or the other end is a subordinate. 124 otherEnds, err := rel.RelatedEndpoints(w.app.Name()) 125 if err != nil { 126 return false, errors.Trace(err) 127 } 128 for _, otherEnd := range otherEnds { 129 if otherEnd.ApplicationName == w.principalName { 130 return true, nil 131 } 132 otherApp, err := w.backend.Application(otherEnd.ApplicationName) 133 if err != nil { 134 return false, errors.Trace(err) 135 } 136 if !otherApp.IsPrincipal() { 137 return true, nil 138 } 139 } 140 return false, nil 141 } 142 143 // Changes implements watcher.StringsWatcher. 144 func (w *subRelationsWatcher) Changes() <-chan []string { 145 return w.out 146 } 147 148 // Err implements watcher.StringsWatcher. 149 func (w *subRelationsWatcher) Err() error { 150 return w.catacomb.Err() 151 } 152 153 // Kill implements watcher.StringsWatcher. 154 func (w *subRelationsWatcher) Kill() { 155 w.catacomb.Kill(nil) 156 } 157 158 // Stop implements watcher.StringsWatcher. 159 func (w *subRelationsWatcher) Stop() error { 160 w.Kill() 161 return w.Wait() 162 } 163 164 // Wait implements watcher.StringsWatcher. 165 func (w *subRelationsWatcher) Wait() error { 166 return w.catacomb.Wait() 167 }