github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/remoterelations/relationunitsworker.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package remoterelations 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery" 11 "github.com/juju/errors" 12 "github.com/juju/names/v5" 13 "github.com/juju/worker/v3" 14 "github.com/juju/worker/v3/catacomb" 15 "gopkg.in/macaroon.v2" 16 17 "github.com/juju/juju/api/watcher" 18 "github.com/juju/juju/rpc/params" 19 ) 20 21 // RelationUnitChangeEvent encapsulates a remote relation event, 22 // adding the tag of the relation which changed. 23 type RelationUnitChangeEvent struct { 24 Tag names.RelationTag 25 params.RemoteRelationChangeEvent 26 } 27 28 // relationUnitsWorker uses instances of watcher.RelationUnitsWatcher to 29 // listen to changes to relation settings in a model, local or remote. 30 // Local changes are exported to the remote model. 31 // Remote changes are consumed by the local model. 32 type relationUnitsWorker struct { 33 catacomb catacomb.Catacomb 34 35 mu sync.Mutex 36 37 // mostRecentChange is stored here for the engine report. 38 mostRecentChange RelationUnitChangeEvent 39 changeSince time.Time 40 41 relationTag names.RelationTag 42 rrw watcher.RemoteRelationWatcher 43 changes chan<- RelationUnitChangeEvent 44 macaroon *macaroon.Macaroon 45 mode string // mode is local or remote. 46 47 logger Logger 48 } 49 50 func newRelationUnitsWorker( 51 relationTag names.RelationTag, 52 macaroon *macaroon.Macaroon, 53 rrw watcher.RemoteRelationWatcher, 54 changes chan<- RelationUnitChangeEvent, 55 logger Logger, 56 mode string, 57 ) (*relationUnitsWorker, error) { 58 w := &relationUnitsWorker{ 59 relationTag: relationTag, 60 macaroon: macaroon, 61 rrw: rrw, 62 changes: changes, 63 logger: logger, 64 mode: mode, 65 } 66 err := catacomb.Invoke(catacomb.Plan{ 67 Site: &w.catacomb, 68 Work: w.loop, 69 Init: []worker.Worker{rrw}, 70 }) 71 return w, err 72 } 73 74 // Kill is defined on worker.Worker 75 func (w *relationUnitsWorker) Kill() { 76 w.catacomb.Kill(nil) 77 } 78 79 // Wait is defined on worker.Worker 80 func (w *relationUnitsWorker) Wait() error { 81 err := w.catacomb.Wait() 82 if errors.IsNotFound(err) || params.IsCodeNotFound(err) { 83 err = nil 84 } 85 if err != nil { 86 w.logger.Errorf("error in relation units worker for %v: %v", w.relationTag.Id(), err) 87 } 88 return err 89 } 90 91 func (w *relationUnitsWorker) loop() error { 92 for { 93 select { 94 case <-w.catacomb.Dying(): 95 return w.catacomb.ErrDying() 96 case change, ok := <-w.rrw.Changes(): 97 if !ok { 98 // We are dying. 99 return w.catacomb.ErrDying() 100 } 101 w.logger.Debugf("%v relation units changed for %v: %#v", w.mode, w.relationTag, &change) 102 if isEmpty(change) { 103 continue 104 } 105 106 // Add macaroon in case this event is sent to a remote 107 // facade. 108 109 // TODO(babbageclunk): move this so it happens just before 110 // the event is published to the remote facade. 111 change.Macaroons = macaroon.Slice{w.macaroon} 112 change.BakeryVersion = bakery.LatestVersion 113 114 w.mu.Lock() 115 w.mostRecentChange = RelationUnitChangeEvent{ 116 Tag: w.relationTag, 117 RemoteRelationChangeEvent: change, 118 } 119 w.changeSince = time.Now() 120 event := w.mostRecentChange 121 w.mu.Unlock() 122 // Send in lockstep so we don't drop events (otherwise 123 // we'd need to merge them - not too hard in this 124 // case but probably not needed). 125 select { 126 case <-w.catacomb.Dying(): 127 return w.catacomb.ErrDying() 128 case w.changes <- event: 129 } 130 } 131 } 132 } 133 134 func isEmpty(change params.RemoteRelationChangeEvent) bool { 135 return len(change.ChangedUnits)+len(change.DepartedUnits) == 0 && change.ApplicationSettings == nil 136 } 137 138 // Report provides information for the engine report. 139 func (w *relationUnitsWorker) Report() map[string]interface{} { 140 result := make(map[string]interface{}) 141 w.mu.Lock() 142 defer w.mu.Unlock() 143 144 if w.mostRecentChange.Tag.Id() != "" { 145 var changed []int 146 for _, c := range w.mostRecentChange.ChangedUnits { 147 changed = append(changed, c.UnitId) 148 } 149 result["departed"] = w.mostRecentChange.DepartedUnits 150 result["changed"] = changed 151 result["since"] = w.changeSince.Format(time.RFC1123Z) 152 } 153 154 return result 155 }