github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "strconv" 8 "strings" 9 10 "github.com/juju/errors" 11 "gopkg.in/juju/names.v2" 12 "gopkg.in/juju/worker.v1" 13 "gopkg.in/juju/worker.v1/catacomb" 14 "gopkg.in/macaroon.v2-unstable" 15 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/core/watcher" 18 ) 19 20 type relationUnitsSettingsFunc func([]string) ([]params.SettingsResult, error) 21 22 // relationUnitsWorker uses instances of watcher.RelationUnitsWatcher to 23 // listen to changes to relation settings in a model, local or remote. 24 // Local changes are exported to the remote model. 25 // Remote changes are consumed by the local model. 26 type relationUnitsWorker struct { 27 catacomb catacomb.Catacomb 28 relationTag names.RelationTag 29 ruw watcher.RelationUnitsWatcher 30 changes chan<- params.RemoteRelationChangeEvent 31 32 applicationToken string 33 macaroon *macaroon.Macaroon 34 remoteRelationToken string 35 36 unitSettingsFunc relationUnitsSettingsFunc 37 } 38 39 func newRelationUnitsWorker( 40 relationTag names.RelationTag, 41 applicationToken string, 42 macaroon *macaroon.Macaroon, 43 remoteRelationToken string, 44 ruw watcher.RelationUnitsWatcher, 45 changes chan<- params.RemoteRelationChangeEvent, 46 unitSettingsFunc relationUnitsSettingsFunc, 47 ) (*relationUnitsWorker, error) { 48 w := &relationUnitsWorker{ 49 relationTag: relationTag, 50 applicationToken: applicationToken, 51 macaroon: macaroon, 52 remoteRelationToken: remoteRelationToken, 53 ruw: ruw, 54 changes: changes, 55 unitSettingsFunc: unitSettingsFunc, 56 } 57 err := catacomb.Invoke(catacomb.Plan{ 58 Site: &w.catacomb, 59 Work: w.loop, 60 Init: []worker.Worker{ruw}, 61 }) 62 return w, err 63 } 64 65 // Kill is defined on worker.Worker 66 func (w *relationUnitsWorker) Kill() { 67 w.catacomb.Kill(nil) 68 } 69 70 // Wait is defined on worker.Worker 71 func (w *relationUnitsWorker) Wait() error { 72 err := w.catacomb.Wait() 73 if err != nil { 74 logger.Errorf("error in relation units worker for %v: %v", w.relationTag.Id(), err) 75 } 76 return err 77 } 78 79 func (w *relationUnitsWorker) loop() error { 80 var ( 81 changes chan<- params.RemoteRelationChangeEvent 82 event params.RemoteRelationChangeEvent 83 ) 84 for { 85 select { 86 case <-w.catacomb.Dying(): 87 return w.catacomb.ErrDying() 88 case change, ok := <-w.ruw.Changes(): 89 if !ok { 90 // We are dying. 91 return w.catacomb.ErrDying() 92 } 93 logger.Debugf("relation units changed for %v: %#v", w.relationTag, change) 94 if evt, err := w.relationUnitsChangeEvent(change); err != nil { 95 return errors.Trace(err) 96 } else { 97 if evt == nil { 98 continue 99 } 100 event = *evt 101 changes = w.changes 102 } 103 case changes <- event: 104 changes = nil 105 } 106 } 107 } 108 109 func (w *relationUnitsWorker) relationUnitsChangeEvent( 110 change watcher.RelationUnitsChange, 111 ) (*params.RemoteRelationChangeEvent, error) { 112 logger.Debugf("update relation units for %v", w.relationTag) 113 if len(change.Changed)+len(change.Departed) == 0 { 114 return nil, nil 115 } 116 // Ensure all the changed units have been exported. 117 changedUnitNames := make([]string, 0, len(change.Changed)) 118 for name := range change.Changed { 119 changedUnitNames = append(changedUnitNames, name) 120 } 121 122 // unitNum parses a unit name and extracts the unit number. 123 unitNum := func(unitName string) (int, error) { 124 parts := strings.Split(unitName, "/") 125 if len(parts) < 2 { 126 return -1, errors.NotValidf("unit name %v", unitName) 127 } 128 return strconv.Atoi(parts[1]) 129 } 130 131 // Construct the event to send to the remote model. 132 event := ¶ms.RemoteRelationChangeEvent{ 133 RelationToken: w.remoteRelationToken, 134 ApplicationToken: w.applicationToken, 135 Macaroons: macaroon.Slice{w.macaroon}, 136 DepartedUnits: make([]int, len(change.Departed)), 137 } 138 for i, u := range change.Departed { 139 num, err := unitNum(u) 140 if err != nil { 141 return nil, errors.Trace(err) 142 } 143 event.DepartedUnits[i] = num 144 } 145 146 if len(changedUnitNames) > 0 { 147 // For changed units, we publish/consume the current settings values. 148 results, err := w.unitSettingsFunc(changedUnitNames) 149 if err != nil { 150 return nil, errors.Annotate(err, "fetching relation units settings") 151 } 152 for i, result := range results { 153 if result.Error != nil { 154 return nil, errors.Annotatef(result.Error, "fetching relation unit settings for %v", changedUnitNames[i]) 155 } 156 } 157 for i, result := range results { 158 num, err := unitNum(changedUnitNames[i]) 159 if err != nil { 160 return nil, errors.Trace(err) 161 } 162 change := params.RemoteRelationUnitChange{ 163 UnitId: num, 164 Settings: make(map[string]interface{}), 165 } 166 for k, v := range result.Settings { 167 change.Settings[k] = v 168 } 169 event.ChangedUnits = append(event.ChangedUnits, change) 170 } 171 } 172 return event, nil 173 }