github.com/cs3org/reva/v2@v2.27.7/pkg/mentix/mentix.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package mentix 20 21 import ( 22 "fmt" 23 "net/http" 24 "strings" 25 "time" 26 27 "github.com/rs/zerolog" 28 29 "github.com/cs3org/reva/v2/pkg/appctx" 30 "github.com/cs3org/reva/v2/pkg/mentix/accservice" 31 "github.com/cs3org/reva/v2/pkg/mentix/config" 32 "github.com/cs3org/reva/v2/pkg/mentix/connectors" 33 "github.com/cs3org/reva/v2/pkg/mentix/entity" 34 "github.com/cs3org/reva/v2/pkg/mentix/exchangers" 35 "github.com/cs3org/reva/v2/pkg/mentix/exchangers/exporters" 36 "github.com/cs3org/reva/v2/pkg/mentix/exchangers/importers" 37 "github.com/cs3org/reva/v2/pkg/mentix/meshdata" 38 ) 39 40 // Mentix represents the main Mentix service object. 41 type Mentix struct { 42 conf *config.Configuration 43 log *zerolog.Logger 44 45 connectors *connectors.Collection 46 importers *importers.Collection 47 exporters *exporters.Collection 48 49 meshDataSet meshdata.Map 50 51 updateInterval time.Duration 52 } 53 54 const ( 55 runLoopSleeptime = time.Millisecond * 1000 56 ) 57 58 func (mntx *Mentix) initialize(conf *config.Configuration, log *zerolog.Logger) error { 59 if conf == nil { 60 return fmt.Errorf("no configuration provided") 61 } 62 mntx.conf = conf 63 64 if log == nil { 65 return fmt.Errorf("no logger provided") 66 } 67 mntx.log = log 68 69 // Initialize the connectors that will be used to gather the mesh data 70 if err := mntx.initConnectors(); err != nil { 71 return fmt.Errorf("unable to initialize connector: %v", err) 72 } 73 74 // Initialize the exchangers 75 if err := mntx.initExchangers(); err != nil { 76 return fmt.Errorf("unable to initialize exchangers: %v", err) 77 } 78 79 // Get the update interval 80 duration, err := time.ParseDuration(mntx.conf.UpdateInterval) 81 if err != nil { 82 // If the duration can't be parsed, default to one hour 83 duration = time.Hour 84 } 85 mntx.updateInterval = duration 86 87 // Create empty mesh data set 88 mntx.meshDataSet = make(meshdata.Map) 89 90 // Log some infos 91 connectorNames := entity.GetNames(mntx.connectors) 92 importerNames := entity.GetNames(mntx.importers) 93 exporterNames := entity.GetNames(mntx.exporters) 94 log.Info().Msgf("mentix started with connectors: %v; importers: %v; exporters: %v; update interval: %v", 95 strings.Join(connectorNames, ", "), 96 strings.Join(importerNames, ", "), 97 strings.Join(exporterNames, ", "), 98 duration, 99 ) 100 101 return nil 102 } 103 104 func (mntx *Mentix) initConnectors() error { 105 // Use all connectors exposed by the connectors package 106 conns, err := connectors.AvailableConnectors(mntx.conf) 107 if err != nil { 108 return fmt.Errorf("unable to get registered conns: %v", err) 109 } 110 mntx.connectors = conns 111 112 if err := mntx.connectors.ActivateAll(mntx.conf, mntx.log); err != nil { 113 return fmt.Errorf("unable to activate connectors: %v", err) 114 } 115 116 return nil 117 } 118 119 func (mntx *Mentix) initExchangers() error { 120 // Use all importers exposed by the importers package 121 imps, err := importers.AvailableImporters(mntx.conf) 122 if err != nil { 123 return fmt.Errorf("unable to get registered importers: %v", err) 124 } 125 mntx.importers = imps 126 127 if err := mntx.importers.ActivateAll(mntx.conf, mntx.log); err != nil { 128 return fmt.Errorf("unable to activate importers: %v", err) 129 } 130 131 // Use all exporters exposed by the exporters package 132 exps, err := exporters.AvailableExporters(mntx.conf) 133 if err != nil { 134 return fmt.Errorf("unable to get registered exporters: %v", err) 135 } 136 mntx.exporters = exps 137 138 if err := mntx.exporters.ActivateAll(mntx.conf, mntx.log); err != nil { 139 return fmt.Errorf("unable to activate exporters: %v", err) 140 } 141 142 return nil 143 } 144 145 func (mntx *Mentix) startExchangers() error { 146 // Start all importers 147 if err := mntx.importers.StartAll(); err != nil { 148 return fmt.Errorf("unable to start importers: %v", err) 149 } 150 151 // Start all exporters 152 if err := mntx.exporters.StartAll(); err != nil { 153 return fmt.Errorf("unable to start exporters: %v", err) 154 } 155 156 return nil 157 } 158 159 func (mntx *Mentix) stopExchangers() { 160 mntx.exporters.StopAll() 161 mntx.importers.StopAll() 162 } 163 164 func (mntx *Mentix) destroy() { 165 mntx.stopExchangers() 166 } 167 168 // Run starts the Mentix service that will periodically pull the configured data source and publish this data 169 // through the enabled exporters. 170 func (mntx *Mentix) Run(stopSignal <-chan struct{}) error { 171 defer mntx.destroy() 172 173 // Start all im- & exporters; they will be stopped in mntx.destroy 174 if err := mntx.startExchangers(); err != nil { 175 return fmt.Errorf("unable to start exchangers: %v", err) 176 } 177 178 updateTimestamp := time.Time{} 179 loop: 180 for { 181 if stopSignal != nil { 182 // Poll the stopSignal channel; if a signal was received, break the loop, terminating Mentix gracefully 183 select { 184 case <-stopSignal: 185 break loop 186 187 default: 188 } 189 } 190 191 // Perform all regular actions 192 mntx.tick(&updateTimestamp) 193 194 time.Sleep(runLoopSleeptime) 195 } 196 197 return nil 198 } 199 200 func (mntx *Mentix) tick(updateTimestamp *time.Time) { 201 // Let all importers do their work first 202 meshDataUpdated, err := mntx.processImporters() 203 if err != nil { 204 mntx.log.Err(err).Msgf("an error occurred while processing the importers: %v", err) 205 } 206 207 // If mesh data has been imported or enough time has passed, update the stored mesh data and all exporters 208 if meshDataUpdated || time.Since(*updateTimestamp) >= mntx.updateInterval { 209 // Retrieve and update the mesh data; if the importers modified any data, these changes will 210 // be reflected automatically here 211 if meshDataSet, err := mntx.retrieveMeshDataSet(); err == nil { 212 if err := mntx.applyMeshDataSet(meshDataSet); err != nil { 213 mntx.log.Err(err).Msg("failed to apply mesh data") 214 } 215 } else { 216 mntx.log.Err(err).Msg("failed to retrieve mesh data") 217 } 218 219 *updateTimestamp = time.Now() 220 } 221 } 222 223 func (mntx *Mentix) processImporters() (bool, error) { 224 meshDataUpdated := false 225 226 for _, importer := range mntx.importers.Importers { 227 updated, err := importer.Process(mntx.connectors) 228 if err != nil { 229 return false, fmt.Errorf("unable to process importer '%v': %v", importer.GetName(), err) 230 } 231 meshDataUpdated = meshDataUpdated || updated 232 233 if updated { 234 mntx.log.Debug().Msgf("mesh data imported from '%v'", importer.GetName()) 235 } 236 } 237 238 return meshDataUpdated, nil 239 } 240 241 func (mntx *Mentix) retrieveMeshDataSet() (meshdata.Map, error) { 242 meshDataSet := make(meshdata.Map) 243 244 for _, connector := range mntx.connectors.Connectors { 245 meshData, err := connector.RetrieveMeshData() 246 if err == nil { 247 meshDataSet[connector.GetID()] = meshData 248 } else { 249 mntx.log.Err(err).Msgf("retrieving mesh data from connector '%v' failed", connector.GetName()) 250 } 251 } 252 253 return meshDataSet, nil 254 } 255 256 func (mntx *Mentix) applyMeshDataSet(meshDataSet meshdata.Map) error { 257 // Check if mesh data from any connector has changed 258 meshDataChanged := false 259 for connectorID, meshData := range meshDataSet { 260 if !meshData.Compare(mntx.meshDataSet[connectorID]) { 261 meshDataChanged = true 262 break 263 } 264 } 265 266 if meshDataChanged { 267 mntx.log.Debug().Msg("mesh data changed, applying") 268 269 mntx.meshDataSet = meshDataSet 270 271 exchangers := make([]exchangers.Exchanger, 0, len(mntx.exporters.Exporters)+len(mntx.importers.Importers)) 272 exchangers = append(exchangers, mntx.exporters.Exchangers()...) 273 exchangers = append(exchangers, mntx.importers.Exchangers()...) 274 275 for _, exchanger := range exchangers { 276 if err := exchanger.Update(mntx.meshDataSet); err != nil { 277 return fmt.Errorf("unable to update mesh data on exchanger '%v': %v", exchanger.GetName(), err) 278 } 279 } 280 } 281 282 return nil 283 } 284 285 // GetRequestImporters returns all exporters that can handle HTTP requests. 286 func (mntx *Mentix) GetRequestImporters() []exchangers.RequestExchanger { 287 return mntx.importers.GetRequestImporters() 288 } 289 290 // GetRequestExporters returns all exporters that can handle HTTP requests. 291 func (mntx *Mentix) GetRequestExporters() []exchangers.RequestExchanger { 292 return mntx.exporters.GetRequestExporters() 293 } 294 295 // RequestHandler handles any incoming HTTP requests by asking each RequestExchanger whether it wants to 296 // handle the request (usually based on the relative URL path). 297 func (mntx *Mentix) RequestHandler(w http.ResponseWriter, r *http.Request) { 298 defer r.Body.Close() 299 300 log := appctx.GetLogger(r.Context()) 301 302 switch r.Method { 303 case http.MethodGet: 304 mntx.handleRequest(mntx.GetRequestExporters(), w, r, log) 305 306 case http.MethodPost: 307 mntx.handleRequest(mntx.GetRequestImporters(), w, r, log) 308 309 default: 310 log.Err(fmt.Errorf("unsupported method")).Msg("error handling incoming request") 311 } 312 } 313 314 func (mntx *Mentix) handleRequest(exchangers []exchangers.RequestExchanger, w http.ResponseWriter, r *http.Request, log *zerolog.Logger) { 315 // Ask each RequestExchanger if it wants to handle the request 316 for _, exchanger := range exchangers { 317 if exchanger.WantsRequest(r) { 318 exchanger.HandleRequest(w, r, mntx.conf, log) 319 } 320 } 321 } 322 323 // New creates a new Mentix service instance. 324 func New(conf *config.Configuration, log *zerolog.Logger) (*Mentix, error) { 325 // Configure the accounts service upfront 326 if err := accservice.InitAccountsService(conf); err != nil { 327 return nil, fmt.Errorf("unable to initialize the accounts service: %v", err) 328 } 329 330 mntx := new(Mentix) 331 if err := mntx.initialize(conf, log); err != nil { 332 return nil, fmt.Errorf("unable to initialize Mentix: %v", err) 333 } 334 return mntx, nil 335 }