github.com/Unheilbar/quorum@v1.0.0/plugin/service.go (about)

     1  package plugin
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sync"
     7  	"sync/atomic"
     8  	"unsafe"
     9  
    10  	"github.com/ethereum/go-ethereum/accounts/pluggable"
    11  	"github.com/ethereum/go-ethereum/log"
    12  	"github.com/ethereum/go-ethereum/rpc"
    13  )
    14  
    15  type PluginManagerInterface interface {
    16  	APIs() []rpc.API
    17  	Start() (err error)
    18  	Stop() error
    19  	IsEnabled(name PluginInterfaceName) bool
    20  	PluginsInfo() interface{}
    21  	Reload(name PluginInterfaceName) (bool, error)
    22  	GetPluginTemplate(name PluginInterfaceName, v managedPlugin) error
    23  }
    24  
    25  //go:generate mockgen -source=service.go -destination plugin_manager_mockery.go -package plugin
    26  var _ PluginManagerInterface = &PluginManager{}
    27  var _ PluginManagerInterface = &MockPluginManagerInterface{}
    28  
    29  // this implements geth service
    30  type PluginManager struct {
    31  	nodeName           string // geth node name
    32  	pluginBaseDir      string // base directory for all the plugins
    33  	verifier           Verifier
    34  	centralClient      *CentralClient
    35  	downloader         *Downloader
    36  	settings           *Settings
    37  	mux                sync.Mutex                            // control concurrent access to plugins cache
    38  	plugins            map[PluginInterfaceName]managedPlugin // lazy load the actual plugin templates
    39  	initializedPlugins map[PluginInterfaceName]managedPlugin // prepopulate during initialization of plugin manager, needed for starting/stopping/getting info
    40  	pluginsStarted     *int32
    41  }
    42  
    43  // this is called after PluginManager service has been successfully started
    44  // See node/node.go#Start()
    45  func (s *PluginManager) APIs() []rpc.API {
    46  	return append([]rpc.API{
    47  		{
    48  			Namespace: "admin",
    49  			Service:   NewPluginManagerAPI(s),
    50  			Version:   "1.0",
    51  			Public:    false,
    52  		},
    53  	}, s.delegateAPIs()...)
    54  }
    55  
    56  func (s *PluginManager) Start() (err error) {
    57  	initializedPluginsCount := len(s.initializedPlugins)
    58  	if initializedPluginsCount == 0 {
    59  		log.Info("No plugins to initialise")
    60  		return
    61  	}
    62  	if atomic.LoadInt32(s.pluginsStarted) != 0 {
    63  		log.Info("Plugins already started")
    64  		return
    65  	}
    66  	log.Info("Starting all plugins", "count", initializedPluginsCount)
    67  	startedPlugins := make([]managedPlugin, 0, initializedPluginsCount)
    68  	for _, p := range s.initializedPlugins {
    69  		if err = p.Start(); err != nil {
    70  			break
    71  		} else {
    72  			startedPlugins = append(startedPlugins, p)
    73  		}
    74  	}
    75  	if err != nil {
    76  		for _, p := range startedPlugins {
    77  			_ = p.Stop()
    78  		}
    79  	} else {
    80  		atomic.StoreInt32(s.pluginsStarted, 1)
    81  	}
    82  	return
    83  }
    84  
    85  func (s *PluginManager) getPlugin(name PluginInterfaceName) (managedPlugin, bool) {
    86  	s.mux.Lock()
    87  	defer s.mux.Unlock()
    88  	p, ok := s.plugins[name] // check if it's been used before
    89  	if !ok {
    90  		p, ok = s.initializedPlugins[name] // check if it's been initialized before
    91  	}
    92  	return p, ok
    93  }
    94  
    95  // Check if a plugin is enabled/setup
    96  func (s *PluginManager) IsEnabled(name PluginInterfaceName) bool {
    97  	if s == nil {
    98  		return false
    99  	}
   100  	_, ok := s.initializedPlugins[name]
   101  	return ok
   102  }
   103  
   104  // store the plugin instance to the value of the pointer v and cache it
   105  // this function makes sure v value will never be nil
   106  func (s *PluginManager) GetPluginTemplate(name PluginInterfaceName, v managedPlugin) error {
   107  	rv := reflect.ValueOf(v)
   108  	if rv.Kind() != reflect.Ptr || rv.IsNil() {
   109  		return fmt.Errorf("invalid argument value, expected a pointer but got %s", reflect.TypeOf(v))
   110  	}
   111  	recoverToErrorFunc := func(f func()) (err error) {
   112  		defer func() {
   113  			if r := recover(); r != nil {
   114  				err = fmt.Errorf("%s", r)
   115  			}
   116  		}()
   117  		f()
   118  		return
   119  	}
   120  	if p, ok := s.plugins[name]; ok {
   121  		return recoverToErrorFunc(func() {
   122  			cachedValue := reflect.ValueOf(p)
   123  			rv.Elem().Set(cachedValue.Elem())
   124  		})
   125  	}
   126  	base, ok := s.initializedPlugins[name]
   127  	if !ok {
   128  		return fmt.Errorf("plugin: [%s] is not found", name)
   129  	}
   130  	if err := recoverToErrorFunc(func() {
   131  		basePluginValue := reflect.ValueOf(base)
   132  		// the first field in the plugin template object is the basePlugin
   133  		// it indicates that the plugin template "extends" basePlugin
   134  		basePluginField := rv.Elem().FieldByName("basePlugin")
   135  		if !basePluginField.IsValid() || basePluginField.Type() != basePluginPointerType {
   136  			panic("plugin template must extend *basePlugin")
   137  		}
   138  		// need to have write access to the unexported field in the target object
   139  		basePluginField = reflect.NewAt(basePluginField.Type(), unsafe.Pointer(basePluginField.UnsafeAddr())).Elem()
   140  		basePluginField.Set(basePluginValue)
   141  	}); err != nil {
   142  		return err
   143  	}
   144  	s.mux.Lock()
   145  	defer s.mux.Unlock()
   146  	s.plugins[name] = v
   147  	return nil
   148  }
   149  
   150  func (s *PluginManager) Stop() error {
   151  	initializedPluginsCount := len(s.initializedPlugins)
   152  	log.Info("Stopping all plugins", "count", initializedPluginsCount)
   153  	allErrors := make([]error, 0)
   154  	for _, p := range s.initializedPlugins {
   155  		if err := p.Stop(); err != nil {
   156  			allErrors = append(allErrors, err)
   157  		}
   158  	}
   159  	log.Info("All plugins stopped", "errors", allErrors)
   160  	if initializedPluginsCount > 0 {
   161  		atomic.StoreInt32(s.pluginsStarted, 0)
   162  	}
   163  	if len(allErrors) == 0 {
   164  		return nil
   165  	}
   166  	return fmt.Errorf("%s", allErrors)
   167  }
   168  
   169  // Provide details of current plugins being used
   170  func (s *PluginManager) PluginsInfo() interface{} {
   171  	info := make(map[PluginInterfaceName]interface{})
   172  	if len(s.initializedPlugins) == 0 {
   173  		return info
   174  	}
   175  	info["baseDir"] = s.pluginBaseDir
   176  	for _, p := range s.initializedPlugins {
   177  		k, v := p.Info()
   178  		info[k] = v
   179  	}
   180  	return info
   181  }
   182  
   183  // AddAccountPluginToBackend adds the account plugin to the provided account backend
   184  func (s *PluginManager) AddAccountPluginToBackend(b *pluggable.Backend) error {
   185  	v := new(ReloadableAccountServiceFactory)
   186  	if err := s.GetPluginTemplate(AccountPluginInterfaceName, v); err != nil {
   187  		return err
   188  	}
   189  	service, err := v.Create()
   190  	if err != nil {
   191  		return err
   192  	}
   193  	if err := b.SetPluginService(service); err != nil {
   194  		return err
   195  	}
   196  	return nil
   197  }
   198  
   199  func (s *PluginManager) Reload(name PluginInterfaceName) (bool, error) {
   200  	p, ok := s.getPlugin(name)
   201  	if !ok {
   202  		return false, fmt.Errorf("no such plugin provider: %s", name)
   203  	}
   204  	_ = p.Stop()
   205  	if err := p.Start(); err != nil {
   206  		return false, err
   207  	}
   208  	return true, nil
   209  }
   210  
   211  // this is to configure delegate APIs call to the plugins
   212  func (s *PluginManager) delegateAPIs() []rpc.API {
   213  	apis := make([]rpc.API, 0)
   214  	for _, p := range s.initializedPlugins {
   215  		interfaceName, _ := p.Info()
   216  		if pluginProvider, ok := pluginProviders[interfaceName]; ok {
   217  			if pluginProvider.apiProviderFunc != nil {
   218  				namespace := fmt.Sprintf("plugin@%s", interfaceName)
   219  				log.Debug("adding RPC API delegate for plugin", "provider", interfaceName, "namespace", namespace)
   220  				if delegates, err := pluginProvider.apiProviderFunc(namespace, s); err != nil {
   221  					log.Error("unable to delegate RPC API calls to plugin", "provider", interfaceName, "error", err)
   222  				} else {
   223  					apis = append(apis, delegates...)
   224  				}
   225  			}
   226  		}
   227  	}
   228  	return apis
   229  }
   230  
   231  func NewPluginManager(nodeName string, settings *Settings, skipVerify bool, localVerify bool, publicKey string) (*PluginManager, error) {
   232  	pm := &PluginManager{
   233  		nodeName:           nodeName,
   234  		pluginBaseDir:      settings.BaseDir.String(),
   235  		centralClient:      NewPluginCentralClient(settings.CentralConfig),
   236  		plugins:            make(map[PluginInterfaceName]managedPlugin),
   237  		initializedPlugins: make(map[PluginInterfaceName]managedPlugin),
   238  		settings:           settings,
   239  		pluginsStarted:     new(int32),
   240  	}
   241  	pm.downloader = NewDownloader(pm)
   242  	if skipVerify {
   243  		log.Warn("plugin: ignore integrity verification")
   244  		pm.verifier = NewNonVerifier()
   245  	} else {
   246  		var err error
   247  		if pm.verifier, err = NewVerifier(pm, localVerify, publicKey); err != nil {
   248  			return nil, err
   249  		}
   250  	}
   251  	for pluginName, pluginDefinition := range settings.Providers {
   252  		log.Debug("Preparing plugin", "provider", pluginName, "name", pluginDefinition.Name, "version", pluginDefinition.Version)
   253  		pluginProvider, ok := pluginProviders[pluginName]
   254  		if !ok {
   255  			return nil, fmt.Errorf("plugin: [%s] is not supported", pluginName)
   256  		}
   257  		base, err := newBasePlugin(pm, pluginName, pluginDefinition, pluginProvider.pluginSet)
   258  		if err != nil {
   259  			return nil, fmt.Errorf("plugin [%s] %s", pluginName, err.Error())
   260  		}
   261  		pm.initializedPlugins[pluginName] = base
   262  	}
   263  	log.Debug("Created plugin manager", "PluginsInfo()", pm.PluginsInfo())
   264  	return pm, nil
   265  }
   266  
   267  func NewEmptyPluginManager() *PluginManager {
   268  	return &PluginManager{
   269  		plugins: make(map[PluginInterfaceName]managedPlugin),
   270  	}
   271  }