youtubebeat/vendor/github.com/elastic/beats/libbeat/feature/registry.go

202 lines
4.7 KiB
Go

// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package feature
import (
"fmt"
"reflect"
"strings"
"sync"
"github.com/elastic/beats/libbeat/logp"
)
type mapper map[string]map[string]Featurable
// Registry implements a global FeatureRegistry for any kind of feature in beats.
// feature are grouped by namespace, a namespace is a kind of plugin like outputs, inputs, or queue.
// The feature name must be unique.
type Registry struct {
sync.RWMutex
namespaces mapper
log *logp.Logger
}
// NewRegistry returns a new registry.
func NewRegistry() *Registry {
return &Registry{
namespaces: make(mapper),
log: logp.NewLogger("registry"),
}
}
// Register registers a new feature into a specific namespace, namespace are lazy created.
// Feature name must be unique.
func (r *Registry) Register(feature Featurable) error {
r.Lock()
defer r.Unlock()
ns := normalize(feature.Namespace())
name := normalize(feature.Name())
if feature.Factory() == nil {
return fmt.Errorf("feature '%s' cannot be registered with a nil factory", name)
}
// Lazy create namespaces
_, found := r.namespaces[ns]
if !found {
r.namespaces[ns] = make(map[string]Featurable)
}
f, found := r.namespaces[ns][name]
if found {
if featuresEqual(feature, f) {
// Allow both old style and new style of plugin to work together.
r.log.Debugw(
"ignoring, feature '%s' is already registered in the namespace '%s'",
name,
ns,
)
return nil
}
return fmt.Errorf(
"could not register new feature '%s' in namespace '%s', feature name must be unique",
name,
ns,
)
}
r.log.Debugw(
"registering new feature",
"namespace",
ns,
"name",
name,
)
r.namespaces[ns][name] = feature
return nil
}
// Unregister removes a feature from the registry.
func (r *Registry) Unregister(namespace, name string) error {
r.Lock()
defer r.Unlock()
ns := normalize(namespace)
v, found := r.namespaces[ns]
if !found {
return fmt.Errorf("unknown namespace named '%s'", ns)
}
_, found = v[name]
if !found {
return fmt.Errorf("unknown feature '%s' in namespace '%s'", name, ns)
}
delete(r.namespaces[ns], name)
return nil
}
// Lookup searches for a Feature by the namespace-name pair.
func (r *Registry) Lookup(namespace, name string) (Featurable, error) {
r.RLock()
defer r.RUnlock()
ns := normalize(namespace)
n := normalize(name)
v, found := r.namespaces[ns]
if !found {
return nil, fmt.Errorf("unknown namespace named '%s'", ns)
}
m, found := v[n]
if !found {
return nil, fmt.Errorf("unknown feature '%s' in namespace '%s'", n, ns)
}
return m, nil
}
// LookupAll returns all the features for a specific namespace.
func (r *Registry) LookupAll(namespace string) ([]Featurable, error) {
r.RLock()
defer r.RUnlock()
ns := normalize(namespace)
v, found := r.namespaces[ns]
if !found {
return nil, fmt.Errorf("unknown namespace named '%s'", ns)
}
list := make([]Featurable, len(v))
c := 0
for _, feature := range v {
list[c] = feature
c++
}
return list, nil
}
// Overwrite allow to replace an existing feature with a new implementation.
func (r *Registry) Overwrite(feature Featurable) error {
_, err := r.Lookup(feature.Namespace(), feature.Name())
if err == nil {
err := r.Unregister(feature.Namespace(), feature.Name())
if err != nil {
return err
}
}
return r.Register(feature)
}
// Size returns the number of registered features in the registry.
func (r *Registry) Size() int {
r.RLock()
defer r.RUnlock()
c := 0
for _, namespace := range r.namespaces {
c += len(namespace)
}
return c
}
func featuresEqual(f1, f2 Featurable) bool {
// There is no safe way to compare function in go,
// but since the function pointers are global it should be stable.
if f1.Name() == f2.Name() &&
f1.Namespace() == f2.Namespace() &&
reflect.ValueOf(f1.Factory()).Pointer() == reflect.ValueOf(f2.Factory()).Pointer() {
return true
}
return false
}
func normalize(s string) string {
return strings.ToLower(s)
}