pkg/pluginregistry/pluginregistry.go (157 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. package pluginregistry import ( "fmt" "strings" "sync" "github.com/facebookincubator/contest/pkg/event" "github.com/facebookincubator/contest/pkg/job" "github.com/facebookincubator/contest/pkg/target" "github.com/facebookincubator/contest/pkg/test" "github.com/facebookincubator/contest/pkg/xcontext" ) // PluginRegistry manages all the plugins available in the system. It associates Plugin // identifiers (implemented as simple strings) with factory functions that create instances // of those plugins. A Plugin instance is owner by a single Job object. type PluginRegistry struct { lock sync.RWMutex Context xcontext.Context // TargetManagers collects a mapping of Plugin Name <-> TargetManager constructor TargetManagers map[string]target.TargetManagerFactory // TestFetchers collects a mapping of Plugin Name <-> TestFetchers constructor TestFetchers map[string]test.TestFetcherFactory // TestStep collects a mapping of Plugin Name <-> TestStep constructor TestSteps map[string]test.TestStepFactory // TestStepEvents collects a mapping between TestStep and list of event.Name // that the TestStep is allowed to emit at runtime TestStepsEvents map[string]map[event.Name]bool // Reporters collects a mapping of Plugin Name <-> Reporter constructor Reporters map[string]job.ReporterFactory } // NewPluginRegistry constructs a new empty plugin registry func NewPluginRegistry(ctx xcontext.Context) *PluginRegistry { pr := PluginRegistry{ Context: ctx, } pr.TargetManagers = make(map[string]target.TargetManagerFactory) pr.TestFetchers = make(map[string]test.TestFetcherFactory) pr.TestSteps = make(map[string]test.TestStepFactory) pr.TestStepsEvents = make(map[string]map[event.Name]bool) pr.Reporters = make(map[string]job.ReporterFactory) return &pr } // RegisterTargetManager register a factory for TargetManager plugins func (r *PluginRegistry) RegisterTargetManager(pluginName string, tmf target.TargetManagerFactory) error { pluginName = strings.ToLower(pluginName) r.lock.Lock() defer r.lock.Unlock() r.Context.Infof("Registering target manager %s", pluginName) if _, found := r.TargetManagers[pluginName]; found { return fmt.Errorf("TargetManager %s already registered", pluginName) } r.TargetManagers[pluginName] = tmf return nil } // RegisterTestFetcher registers a TestFetcher within the registry func (r *PluginRegistry) RegisterTestFetcher(pluginName string, tff test.TestFetcherFactory) error { pluginName = strings.ToLower(pluginName) r.lock.Lock() defer r.lock.Unlock() r.Context.Infof("Registering test fetcher %s", pluginName) if _, found := r.TestFetchers[pluginName]; found { return fmt.Errorf("TestFetcher %s already registered", pluginName) } r.TestFetchers[pluginName] = tff return nil } // RegisterTestStep registers a TestStep within the registry and the associated events func (r *PluginRegistry) RegisterTestStep(pluginName string, tsf test.TestStepFactory, stepEvents []event.Name) error { pluginName = strings.ToLower(pluginName) r.lock.Lock() defer r.lock.Unlock() r.Context.Infof("Registering test step %s", pluginName) if _, found := r.TestSteps[pluginName]; found { return fmt.Errorf("TestSteps %s already registered", pluginName) } r.TestSteps[pluginName] = tsf // Verify that all the events the test step is associated with validate correctly mapEvents := make(map[event.Name]bool) for _, event := range stepEvents { if err := event.Validate(); err != nil { return fmt.Errorf("could not register TestStep %s: %v", pluginName, err) } mapEvents[event] = true } r.TestStepsEvents[pluginName] = mapEvents return nil } // RegisterReporter registers a Reporter within the registry func (r *PluginRegistry) RegisterReporter(pluginName string, rf job.ReporterFactory) error { pluginName = strings.ToLower(pluginName) r.lock.Lock() defer r.lock.Unlock() r.Context.Infof("Registering reporter %s", pluginName) if _, found := r.Reporters[pluginName]; found { return fmt.Errorf("Reporter %s already registered", pluginName) } r.Reporters[pluginName] = rf return nil } // NewTargetManager returns a new instance of TargetManager from its // corresponding name func (r *PluginRegistry) NewTargetManager(pluginName string) (target.TargetManager, error) { pluginName = strings.ToLower(pluginName) var ( targetManagerFactory target.TargetManagerFactory found bool ) r.lock.RLock() targetManagerFactory, found = r.TargetManagers[pluginName] r.lock.RUnlock() if !found { return nil, fmt.Errorf("TargetManager %v is not registered", pluginName) } targetManager := targetManagerFactory() return targetManager, nil } // NewTestFetcher returns a new instance of TestFetcher from its // corresponding name func (r *PluginRegistry) NewTestFetcher(pluginName string) (test.TestFetcher, error) { pluginName = strings.ToLower(pluginName) var ( testFetcherFactory test.TestFetcherFactory found bool ) r.lock.RLock() testFetcherFactory, found = r.TestFetchers[pluginName] r.lock.RUnlock() if !found { return nil, fmt.Errorf("TestFetcher %v is not registered", pluginName) } testFetcher := testFetcherFactory() return testFetcher, nil } // NewTestStep returns a new instance of a TestStep from its corresponding name func (r *PluginRegistry) NewTestStep(pluginName string) (test.TestStep, error) { pluginName = strings.ToLower(pluginName) var ( testStepFactory test.TestStepFactory found bool ) r.lock.RLock() testStepFactory, found = r.TestSteps[pluginName] r.lock.RUnlock() if !found { return nil, fmt.Errorf("TestStep %s is not registered", pluginName) } testStep := testStepFactory() return testStep, nil } // NewTestStepEvents returns a map of events.EventName which can be emitted by the TestStep func (r *PluginRegistry) NewTestStepEvents(pluginName string) (map[event.Name]bool, error) { pluginName = strings.ToLower(pluginName) var ( testStepEvents map[event.Name]bool found bool ) r.lock.RLock() testStepEvents, found = r.TestStepsEvents[pluginName] r.lock.RUnlock() if !found { return nil, fmt.Errorf("TestStep %s does not have any event associated", pluginName) } return testStepEvents, nil } // NewReporter returns a new instance of a Reporter from its corresponding name func (r *PluginRegistry) NewReporter(pluginName string) (job.Reporter, error) { pluginName = strings.ToLower(pluginName) var ( reporterFactory job.ReporterFactory found bool ) r.lock.RLock() reporterFactory, found = r.Reporters[pluginName] r.lock.RUnlock() if !found { return nil, fmt.Errorf("Reporter %s is not registered", pluginName) } reporter := reporterFactory() return reporter, nil }