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  }