From 42397bf9e9030e7866643b44dbe7e4f648ba66fb Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Wed, 7 Dec 2022 21:05:41 -0500 Subject: [PATCH 01/11] Initial refactor Signed-off-by: Peter Broadhurst --- cmd/firefly.go | 25 +- cmd/firefly_test.go | 10 + internal/apiserver/route_spi_post_reset.go | 3 +- internal/coreconfig/coreconfig.go | 3 + internal/coremsgs/en_error_messages.go | 2 + internal/namespace/config.go | 10 +- internal/namespace/manager.go | 928 ++++--- internal/namespace/manager_test.go | 2713 ++++++++++---------- mocks/namespacemocks/manager.go | 13 +- 9 files changed, 1990 insertions(+), 1717 deletions(-) diff --git a/cmd/firefly.go b/cmd/firefly.go index ffe03f74d..bd33ae8bf 100644 --- a/cmd/firefly.go +++ b/cmd/firefly.go @@ -32,6 +32,7 @@ import ( "github.com/hyperledger/firefly-common/pkg/log" "github.com/hyperledger/firefly/internal/apiserver" "github.com/hyperledger/firefly/internal/coreconfig" + "github.com/hyperledger/firefly/internal/coremsgs" "github.com/hyperledger/firefly/internal/namespace" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -59,7 +60,7 @@ var showConfigCommand = &cobra.Command{ Short: "List out the configuration options", Run: func(cmd *cobra.Command, args []string) { // Initialize config of all plugins - resetConfig() + _ = resetConfig(true) getRootManager() _ = config.ReadConfig(configSuffix, cfgFile) @@ -81,9 +82,17 @@ func init() { rootCmd.AddCommand(showConfigCommand) } -func resetConfig() { +func resetConfig(startup bool) error { + if !startup && config.GetBool(coreconfig.ConfigAutoReload) { + // We do not allow these settings to be combined, because viper does not provide a way to + // stop the file listener on the old root Viper instance (before reset). So we would + // leak file listeners in the background. + // Note: This check is also in the API layer + return i18n.NewError(context.Background(), coremsgs.MsgDeprecatedResetWithAutoReload) + } coreconfig.Reset() apiserver.InitConfig() + return nil } func getRootManager() namespace.Manager { @@ -101,7 +110,7 @@ func Execute() error { func run() error { // Read the configuration - resetConfig() + _ = resetConfig(true) err := config.ReadConfig(configSuffix, cfgFile) // Setup logging after reading config (even if failed), to output header correctly @@ -142,14 +151,20 @@ func run() error { mgr.WaitStop() return nil case <-resetChan: + // This API that performs a full stop/restart reset, is deprecated + // in favor of selective reload of namespaces based on listening to changes + // in the configuration file. log.L(rootCtx).Infof("Restarting due to configuration change") cancelRunCtx() mgr.WaitStop() // Must wait for the server to close before we restart <-ffDone // Re-read the configuration - resetConfig() - if err := config.ReadConfig(configSuffix, cfgFile); err != nil { + err := resetConfig(false) + if err == nil { + err = config.ReadConfig(configSuffix, cfgFile) + } + if err != nil { return err } case err := <-errChan: diff --git a/cmd/firefly_test.go b/cmd/firefly_test.go index 43ad086b6..1213e208a 100644 --- a/cmd/firefly_test.go +++ b/cmd/firefly_test.go @@ -23,6 +23,8 @@ import ( "syscall" "testing" + "github.com/hyperledger/firefly-common/pkg/config" + "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/mocks/apiservermocks" "github.com/hyperledger/firefly/mocks/namespacemocks" "github.com/spf13/viper" @@ -169,3 +171,11 @@ func TestAPIServerError(t *testing.T) { err := <-errChan assert.EqualError(t, err, "pop") } + +func TestCannotResetWithConfigAutoReload(t *testing.T) { + err := resetConfig(true) + assert.NoError(t, err) + config.Set(coreconfig.ConfigAutoReload, true) + err = resetConfig(false) + assert.Regexp(t, "FF10433", err) +} diff --git a/internal/apiserver/route_spi_post_reset.go b/internal/apiserver/route_spi_post_reset.go index 6931cc043..cba784bf3 100644 --- a/internal/apiserver/route_spi_post_reset.go +++ b/internal/apiserver/route_spi_post_reset.go @@ -36,8 +36,7 @@ var spiPostReset = &ffapi.Route{ JSONOutputCodes: []int{http.StatusNoContent}, Extensions: &coreExtensions{ CoreJSONHandler: func(r *ffapi.APIRequest, cr *coreRequest) (output interface{}, err error) { - cr.mgr.Reset(cr.ctx) - return nil, nil + return nil, cr.mgr.Reset(cr.ctx) }, }, } diff --git a/internal/coreconfig/coreconfig.go b/internal/coreconfig/coreconfig.go index ba828acdd..8359056c6 100644 --- a/internal/coreconfig/coreconfig.go +++ b/internal/coreconfig/coreconfig.go @@ -114,6 +114,9 @@ var ( // BroadcastBatchTimeout is the timeout to wait for a batch to fill, before sending BroadcastBatchTimeout = ffc("broadcast.batch.timeout") + // ConfigAutoReload starts a filesystem listener against the config file, and if it changes analyzes the config file for changes that require individual namespaces to restart + ConfigAutoReload = ffc("config.autoReload") + // CacheEnabled determines whether cache will be enabled or not, default to true CacheEnabled = ffc("cache.enabled") diff --git a/internal/coremsgs/en_error_messages.go b/internal/coremsgs/en_error_messages.go index 065d1cac0..73aa3fc62 100644 --- a/internal/coremsgs/en_error_messages.go +++ b/internal/coremsgs/en_error_messages.go @@ -271,4 +271,6 @@ var ( MsgIdempotencyKeyDuplicateMessage = ffe("FF10430", "Idempotency key '%s' already used for message '%s'", 409) MsgIdempotencyKeyDuplicateTransaction = ffe("FF10431", "Idempotency key '%s' already used for transaction '%s'", 409) MsgNonIdempotencyKeyConflictTxInsert = ffe("FF10432", "Conflict on insert of transaction '%s'. No existing transaction matching idempotency key '%s' found", 409) + MsgDeprecatedResetWithAutoReload = ffe("FF10433", "The deprecated reset API cannot be used when dynamic config reload is enabled", 409) + MsgConfigArrayVsRawConfigMismatch = ffe("FF10434", "Error processing configuration - mismatch between raw and processed array lengths") ) diff --git a/internal/namespace/config.go b/internal/namespace/config.go index 98e5b9e77..e8bbcae26 100644 --- a/internal/namespace/config.go +++ b/internal/namespace/config.go @@ -29,8 +29,8 @@ const ( ) var ( - namespaceConfig = config.RootSection("namespaces") - namespacePredefined = namespaceConfig.SubArray(NamespacePredefined) + namespaceConfigSection = config.RootSection("namespaces") + namespacePredefined = namespaceConfigSection.SubArray(NamespacePredefined) ) func InitConfig(withDefaults bool) { @@ -54,8 +54,8 @@ func InitConfig(withDefaults bool) { contractConf.AddKnownKey(coreconfig.NamespaceMultipartyContractLocation) if withDefaults { - namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceName, "default") - namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceDescription, "Default predefined namespace") - namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceAssetKeyNormalization, "blockchain_plugin") + namespaceConfigSection.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceName, "default") + namespaceConfigSection.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceDescription, "Default predefined namespace") + namespaceConfigSection.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceAssetKeyNormalization, "blockchain_plugin") } } diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go index c1a5b3450..f6ba6447e 100644 --- a/internal/namespace/manager.go +++ b/internal/namespace/manager.go @@ -22,7 +22,9 @@ import ( "fmt" "strconv" "sync" + "time" + "github.com/fsnotify/fsnotify" "github.com/hyperledger/firefly-common/pkg/auth" "github.com/hyperledger/firefly-common/pkg/auth/authfactory" "github.com/hyperledger/firefly-common/pkg/config" @@ -52,6 +54,7 @@ import ( "github.com/hyperledger/firefly/pkg/identity" "github.com/hyperledger/firefly/pkg/sharedstorage" "github.com/hyperledger/firefly/pkg/tokens" + "github.com/spf13/viper" ) var ( @@ -75,7 +78,7 @@ type Manager interface { Init(ctx context.Context, cancelCtx context.CancelFunc, reset chan bool) error Start() error WaitStop() - Reset(ctx context.Context) + Reset(ctx context.Context) error Orchestrator(ns string) orchestrator.Orchestrator SPIEvents() spievents.Manager @@ -88,71 +91,60 @@ type Manager interface { type namespace struct { core.Namespace orchestrator orchestrator.Orchestrator + loadTime fftypes.FFTime config orchestrator.Config + configHash *fftypes.Bytes32 plugins []string + cancelCtx context.CancelFunc } type namespaceManager struct { - reset chan bool - nsMux sync.Mutex - namespaces map[string]*namespace - pluginNames map[string]bool - plugins struct { - blockchain map[string]blockchainPlugin - identity map[string]identityPlugin - database map[string]databasePlugin - sharedstorage map[string]sharedStoragePlugin - dataexchange map[string]dataExchangePlugin - tokens map[string]tokensPlugin - events map[string]eventsPlugin - auth map[string]authPlugin - } + reset chan bool + ctx context.Context + cancelCtx context.CancelFunc + nsMux sync.Mutex + namespaces map[string]*namespace + plugins map[string]*plugin metricsEnabled bool cacheManager cache.Manager metrics metrics.Manager adminEvents spievents.Manager utOrchestrator orchestrator.Orchestrator tokenBroadcastNames map[string]string + watchConfig func() // indirect from viper.WatchConfig for testing } -type blockchainPlugin struct { - config config.Section - plugin blockchain.Plugin -} - -type databasePlugin struct { - config config.Section - plugin database.Plugin -} - -type dataExchangePlugin struct { - config config.Section - plugin dataexchange.Plugin -} - -type sharedStoragePlugin struct { - config config.Section - plugin sharedstorage.Plugin -} - -type tokensPlugin struct { - config config.Section - plugin tokens.Plugin -} - -type identityPlugin struct { - config config.Section - plugin identity.Plugin -} - -type eventsPlugin struct { - config config.Section - plugin events.Plugin -} +type pluginCategory string + +const ( + pluginCategoryBlockchain pluginCategory = "blockchain" + pluginCategoryDatabase pluginCategory = "database" + pluginCategoryDataexchange pluginCategory = "dataexchange" + pluginCategorySharedstorage pluginCategory = "sharedstorage" + pluginCategoryTokens pluginCategory = "tokens" + pluginCategoryIdentity pluginCategory = "identity" + pluginCategoryEvents pluginCategory = "events" + pluginCategoryAuth pluginCategory = "auth" +) -type authPlugin struct { - config config.Section - plugin auth.Plugin +type plugin struct { + name string + category pluginCategory + pluginType string + ctx context.Context + cancelCtx context.CancelFunc + config config.Section + configHash *fftypes.Bytes32 + loadTime time.Time + + blockchain blockchain.Plugin + database database.Plugin + dataexchange dataexchange.Plugin + sharedstorage sharedstorage.Plugin + tokens tokens.Plugin + identity identity.Plugin + events events.Plugin + auth auth.Plugin } func stringSlicesEqual(a, b []string) bool { @@ -172,6 +164,7 @@ func NewNamespaceManager(withDefaults bool) Manager { namespaces: make(map[string]*namespace), metricsEnabled: config.GetBool(coreconfig.MetricsEnabled), tokenBroadcastNames: make(map[string]string), + watchConfig: viper.WatchConfig, } InitConfig(withDefaults) @@ -196,16 +189,188 @@ func NewNamespaceManager(withDefaults bool) Manager { return nm } +func (nm *namespaceManager) startConfigListener(ctx context.Context) { + go func() { + for { + log.L(ctx).Warnf("Starting configuration listener") + // Note there is no viper interface to make this end, so (apart from in unit tests) + // we never expect this to complete. + // To avoid this leak, we disable the use of the /spi/v1/reset API when config file + // listening is enabled. + viper.OnConfigChange(nm.newConfigChangeListener(ctx)) + nm.watchConfig() + select { + case <-ctx.Done(): + log.L(ctx).Debugf("Configuration listener ended") + return + default: + } + log.L(ctx).Warnf("Configuration listener ended (restarting)") + time.Sleep(5 * time.Second) + } + }() +} + +func (nm *namespaceManager) newConfigChangeListener(ctx context.Context) func(in fsnotify.Event) { + return func(in fsnotify.Event) { + nm.configFileChanged(ctx, in.Name) + } +} + +func (nm *namespaceManager) configFileChanged(ctx context.Context, filename string) { + log.L(ctx).Infof("Detected configuration file reload: '%s'", filename) + + // Get Viper to dump the whole new config, with everything resolved across env vars + // and the config file etc. + // We use this to detect if anything has changed. + rawConfig := viper.AllSettings() + + newPlugins, err := nm.loadPlugins(ctx, rawConfig) + if err != nil { + log.L(ctx).Errorf("Failed to initialize plugins after config reload: %s", err) + return + } + + allNewNamespaces, err := nm.loadNamespaces(ctx, rawConfig) + if err != nil { + log.L(ctx).Errorf("Failed to load namespaces after config reload: %s", err) + return + } + + // From this point we need to block any API calls resolving namespaces, until the reload is complete + nm.nsMux.Lock() + defer nm.nsMux.Unlock() + + // Stop all defunct namespaces + updatedNamespaces, err := nm.stopDefunctNamespaces(ctx, newPlugins, allNewNamespaces) + if err != nil { + log.L(ctx).Errorf("Failed to stop namespaces after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + + // Stop all defunct plugins - now the namespaces using them are all stopped + updatedPlugins, err := nm.stopDefunctPlugins(ctx, newPlugins) + if err != nil { + log.L(ctx).Errorf("Failed to stop namespaces after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + + // Update the new lists + nm.plugins = newPlugins + nm.namespaces = allNewNamespaces + + // Only initialize updated plugins + if err = nm.initPlugins(ctx, updatedPlugins); err != nil { + log.L(ctx).Errorf("Failed to initialize plugins after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + + // Only initialize the updated namespaces (which includes all that depend on above plugins) + if err = nm.initNamespaces(ctx, updatedNamespaces); err != nil { + log.L(ctx).Errorf("Failed to initialize namespaces after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + + // Now finally we can start all the new things + if err = nm.startNamespacesAndPlugins(updatedNamespaces, updatedPlugins); err != nil { + log.L(ctx).Errorf("Failed to initialize namespaces after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + +} + +func (nm *namespaceManager) stopDefunctNamespaces(ctx context.Context, newPlugins map[string]*plugin, newNamespaces map[string]*namespace) (map[string]*namespace, error) { + + // build a set of all the namespaces we've either added new, or have changed + updatedNamespaces := make(map[string]*namespace) + namespacesToStop := make(map[string]*namespace) + for nsName, newNS := range newNamespaces { + if existingNS := nm.namespaces[nsName]; existingNS != nil { + unchanged := existingNS.configHash.Equals(newNS.configHash) && + len(existingNS.plugins) == len(newNS.plugins) + for _, pluginName := range newNS.plugins { + existingPlugin := nm.plugins[pluginName] + newPlugin := newPlugins[pluginName] + unchanged = existingPlugin != nil && newPlugin != nil && + existingPlugin.configHash.Equals(newPlugin.configHash) + } + if unchanged { + log.L(ctx).Debugf("Namespace '%s' unchanged after config reload", nsName) + continue + } + // We need to stop the existing namespace + namespacesToStop[nsName] = existingNS + } + // This is either changed, or brand new - mark it in the map + updatedNamespaces[nsName] = newNS + } + + // Stop everything we need to stop + for nsName, existingNS := range nm.namespaces { + if namespacesToStop[nsName] != nil || newNamespaces[nsName] == nil { + log.L(ctx).Debugf("Stopping namespace '%s' after config reload. Loaded at %s", nsName, existingNS.loadTime) + nm.stopNamespace(ctx, existingNS) + } + } + + return updatedNamespaces, nil + +} + +func (nm *namespaceManager) stopDefunctPlugins(ctx context.Context, newPlugins map[string]*plugin) (map[string]*plugin, error) { + + // build a set of all the plugins we've either added new, or have changed + updatedPlugins := make(map[string]*plugin) + pluginsToStop := make(map[string]*plugin) + for pluginName, newPlugin := range newPlugins { + if existingPlugin := nm.plugins[pluginName]; existingPlugin != nil { + if existingPlugin.configHash.Equals(newPlugin.configHash) { + log.L(ctx).Debugf("Plugin '%s' unchanged after config reload", pluginName) + continue + } + // We need to stop the existing plugin + pluginsToStop[pluginName] = existingPlugin + } + // This is either changed, or brand new - mark it in the map + updatedPlugins[pluginName] = newPlugin + } + + // Stop everything we need to stop + for pluginName, existingPlugin := range nm.plugins { + if pluginsToStop[pluginName] != nil || newPlugins[pluginName] == nil { + log.L(ctx).Debugf("Stopping plugin '%s' after config reload. Loaded at %s", pluginName, existingPlugin.loadTime) + existingPlugin.cancelCtx() + } + } + + return updatedPlugins, nil +} + func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFunc, reset chan bool) (err error) { nm.reset = reset + nm.cancelCtx = cancelCtx - if err = nm.loadPlugins(ctx); err != nil { + initTimeRawConfig := viper.AllSettings() + nm.loadManagers(ctx) + if nm.plugins, err = nm.loadPlugins(ctx, initTimeRawConfig); err != nil { + return err + } + if nm.namespaces, err = nm.loadNamespaces(ctx, initTimeRawConfig); err != nil { return err } - if err = nm.initPlugins(ctx, cancelCtx); err != nil { + + // Initialize all the plugins on initial startup + if err = nm.initPlugins(ctx, nm.plugins); err != nil { return err } - if err = nm.loadNamespaces(ctx); err != nil { + + // Initialize all the namespaces on initial startup + if err = nm.initNamespaces(ctx, nm.namespaces); err != nil { return err } @@ -214,6 +379,14 @@ func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFu metrics.Registry() } + if config.GetBool(coreconfig.ConfigAutoReload) { + nm.startConfigListener(ctx) + } + + return nil +} + +func (nm *namespaceManager) initNamespaces(ctx context.Context, newNamespaces map[string]*namespace) error { // In network version 1, the blockchain plugin and multiparty contract were global and singular. // Therefore, if any namespace was EVER pointed at a V1 contract, that contract and that namespace's plugins // become the de facto configuration for ff_system as well. There can only be one V1 contract in the history @@ -222,7 +395,7 @@ func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFu var v1Namespace *namespace var v1Contract *core.MultipartyContract - for _, ns := range nm.namespaces { + for _, ns := range newNamespaces { if err := nm.initNamespace(ctx, ns); err != nil { return err } @@ -249,17 +422,20 @@ func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFu if v1Namespace != nil { systemNS := &namespace{ - Namespace: v1Namespace.Namespace, - config: v1Namespace.config, - plugins: v1Namespace.plugins, + Namespace: v1Namespace.Namespace, + config: v1Namespace.config, + plugins: v1Namespace.plugins, + configHash: v1Namespace.configHash, } systemNS.Name = core.LegacySystemNamespace systemNS.NetworkName = core.LegacySystemNamespace - nm.namespaces[core.LegacySystemNamespace] = systemNS - err = nm.initNamespace(ctx, systemNS) + if err := nm.initNamespace(ctx, systemNS); err != nil { + return err + } log.L(ctx).Infof("Initialized namespace '%s' as a copy of '%s'", core.LegacySystemNamespace, v1Namespace.Name) } - return err + + return nil } func (nm *namespaceManager) findV1Contract(ns *namespace) *core.MultipartyContract { @@ -275,6 +451,7 @@ func (nm *namespaceManager) findV1Contract(ns *namespace) *core.MultipartyContra } func (nm *namespaceManager) initNamespace(ctx context.Context, ns *namespace) (err error) { + var plugins *orchestrator.Plugins if ns.config.Multiparty.Enabled { plugins, err = nm.validateMultiPartyConfig(ctx, ns.Name, ns.plugins) @@ -315,36 +492,42 @@ func (nm *namespaceManager) initNamespace(ctx context.Context, ns *namespace) (e if err := or.Init(orCtx, orCancel); err != nil { return err } - go func() { - <-orCtx.Done() - nm.nsMux.Lock() - defer nm.nsMux.Unlock() - log.L(ctx).Infof("Terminated namespace '%s'", ns.Name) - delete(nm.namespaces, ns.Name) - }() return nil } +func (nm *namespaceManager) stopNamespace(ctx context.Context, ns *namespace) { + log.L(ctx).Infof("Requesting stop of namespace '%s'", ns.Name) + ns.cancelCtx() + ns.orchestrator.WaitStop() + log.L(ctx).Infof("Namespace '%s' stopped", ns.Name) +} + func (nm *namespaceManager) Start() error { + // On initial start, we need to start everything + return nm.startNamespacesAndPlugins(nm.namespaces, nm.plugins) +} + +func (nm *namespaceManager) startNamespacesAndPlugins(namespacesToStart map[string]*namespace, pluginsToStart map[string]*plugin) error { // Orchestrators must be started before plugins so as not to miss events - for _, ns := range nm.namespaces { + for _, ns := range namespacesToStart { if err := ns.orchestrator.Start(); err != nil { return err } } - for _, plugin := range nm.plugins.blockchain { - if err := plugin.plugin.Start(); err != nil { - return err - } - } - for _, plugin := range nm.plugins.dataexchange { - if err := plugin.plugin.Start(); err != nil { - return err - } - } - for _, plugin := range nm.plugins.tokens { - if err := plugin.plugin.Start(); err != nil { - return err + for _, plugin := range pluginsToStart { + switch plugin.category { + case pluginCategoryBlockchain: + if err := plugin.blockchain.Start(); err != nil { + return err + } + case pluginCategoryDataexchange: + if err := plugin.dataexchange.Start(); err != nil { + return err + } + case pluginCategoryTokens: + if err := plugin.tokens.Start(); err != nil { + return err + } } } return nil @@ -364,7 +547,14 @@ func (nm *namespaceManager) WaitStop() { nm.adminEvents.WaitStop() } -func (nm *namespaceManager) Reset(ctx context.Context) { +func (nm *namespaceManager) Reset(ctx context.Context) error { + if config.GetBool(coreconfig.ConfigAutoReload) { + // We do not allow these settings to be combined, because viper does not provide a way to + // stop the file listener on the old root Viper instance (before reset). So we would + // leak file listeners in the background. + return i18n.NewError(context.Background(), coremsgs.MsgDeprecatedResetWithAutoReload) + } + // Queue a restart of the root context to pick up a configuration change. // Caller is responsible for terminating the passed context to trigger the actual reset // (allows caller to cleanly finish processing the current request/event). @@ -372,10 +562,11 @@ func (nm *namespaceManager) Reset(ctx context.Context) { <-ctx.Done() nm.reset <- true }() + + return nil } -func (nm *namespaceManager) loadPlugins(ctx context.Context) (err error) { - nm.pluginNames = make(map[string]bool) +func (nm *namespaceManager) loadManagers(ctx context.Context) { if nm.metrics == nil { nm.metrics = metrics.NewMetricsManager(ctx) } @@ -384,100 +575,86 @@ func (nm *namespaceManager) loadPlugins(ctx context.Context) (err error) { nm.cacheManager = cache.NewCacheManager(ctx) } - if nm.plugins.database == nil { - nm.plugins.database, err = nm.getDatabasePlugins(ctx) - if err != nil { - return err - } - } - if nm.adminEvents == nil { nm.adminEvents = spievents.NewAdminEventManager(ctx) } +} - if nm.plugins.identity == nil { - nm.plugins.identity, err = nm.getIdentityPlugins(ctx) - if err != nil { - return err - } +func (nm *namespaceManager) loadPlugins(ctx context.Context, rawConfig fftypes.JSONObject) (newPlugins map[string]*plugin, err error) { + + newPlugins = make(map[string]*plugin) + + if err := nm.getDatabasePlugins(ctx, newPlugins, rawConfig); err != nil { + return nil, err } - if nm.plugins.blockchain == nil { - nm.plugins.blockchain, err = nm.getBlockchainPlugins(ctx) - if err != nil { - return err - } + if err := nm.getIdentityPlugins(ctx, newPlugins, rawConfig); err != nil { + return nil, err } - if nm.plugins.sharedstorage == nil { - nm.plugins.sharedstorage, err = nm.getSharedStoragePlugins(ctx) - if err != nil { - return err - } + if err := nm.getBlockchainPlugins(ctx, newPlugins, rawConfig); err != nil { + return nil, err } - if nm.plugins.dataexchange == nil { - nm.plugins.dataexchange, err = nm.getDataExchangePlugins(ctx) - if err != nil { - return err - } + if err := nm.getSharedStoragePlugins(ctx, newPlugins, rawConfig); err != nil { + return nil, err } - if nm.plugins.tokens == nil { - nm.plugins.tokens, err = nm.getTokensPlugins(ctx) - if err != nil { - return err - } + if err := nm.getDataExchangePlugins(ctx, newPlugins, rawConfig); err != nil { + return nil, err } - if nm.plugins.events == nil { - nm.plugins.events, err = nm.getEventPlugins(ctx) - if err != nil { - return err - } + if err := nm.getTokensPlugins(ctx, newPlugins, rawConfig); err != nil { + return nil, err } - if nm.plugins.auth == nil { - nm.plugins.auth, err = nm.getAuthPlugin(ctx) - if err != nil { - return err - } + if err := nm.getEventPlugins(ctx, newPlugins, rawConfig); err != nil { + return nil, err } - return nil + if err := nm.getAuthPlugin(ctx, newPlugins, rawConfig); err != nil { + return nil, err + } + + return newPlugins, nil +} + +func (nm *namespaceManager) configHash(rawConfigObject fftypes.JSONObject) *fftypes.Bytes32 { + b, _ := json.Marshal(rawConfigObject) + return fftypes.HashString(string(b)) } -func (nm *namespaceManager) getTokensPlugins(ctx context.Context) (plugins map[string]tokensPlugin, err error) { - plugins = make(map[string]tokensPlugin) +func (nm *namespaceManager) getTokensPlugins(ctx context.Context, plugins map[string]*plugin, rawConfig fftypes.JSONObject) (err error) { // Broadcast names must be unique broadcastNames := make(map[string]bool) tokensConfigArraySize := tokensConfig.ArraySize() + rawPluginTokensConfig := rawConfig.GetObject("plugins").GetObjectArray("tokens") + if len(rawPluginTokensConfig) != tokensConfigArraySize { + log.L(ctx).Errorf("Expected len(%d) for plugins.tokens: %s", tokensConfigArraySize, rawPluginTokensConfig) + return i18n.NewError(ctx, coremsgs.MsgConfigArrayVsRawConfigMismatch) + } for i := 0; i < tokensConfigArraySize; i++ { config := tokensConfig.ArrayEntry(i) - name, pluginType, err := nm.validatePluginConfig(ctx, config, "tokens") + configHash := nm.configHash(rawPluginTokensConfig[i]) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryTokens, config, configHash) if err != nil { - return nil, err + return err } broadcastName := config.GetString(coreconfig.PluginBroadcastName) // If there is no broadcast name, use the plugin name if broadcastName == "" { - broadcastName = name + broadcastName = pc.name } if _, exists := broadcastNames[broadcastName]; exists { - return nil, i18n.NewError(ctx, coremsgs.MsgDuplicatePluginBroadcastName, "tokens", broadcastName) + return i18n.NewError(ctx, coremsgs.MsgDuplicatePluginBroadcastName, pluginCategoryTokens, broadcastName) } broadcastNames[broadcastName] = true - nm.tokenBroadcastNames[name] = broadcastName + nm.tokenBroadcastNames[pc.name] = broadcastName - plugin, err := tifactory.GetPlugin(ctx, pluginType) + pc.tokens, err = tifactory.GetPlugin(ctx, pc.pluginType) if err != nil { - return nil, err - } - - plugins[name] = tokensPlugin{ - config: config.SubSection(pluginType), - plugin: plugin, + return err } } @@ -493,46 +670,47 @@ func (nm *namespaceManager) getTokensPlugins(ctx context.Context) (plugins map[s name := deprecatedConfig.GetString(coreconfig.PluginConfigName) pluginType := deprecatedConfig.GetString(tokens.TokensConfigPlugin) if name == "" || pluginType == "" { - return nil, i18n.NewError(ctx, coremsgs.MsgMissingTokensPluginConfig) + return i18n.NewError(ctx, coremsgs.MsgMissingTokensPluginConfig) } if err = fftypes.ValidateFFNameField(ctx, name, "name"); err != nil { - return nil, err + return err } nm.tokenBroadcastNames[name] = name - plugin, err := tifactory.GetPlugin(ctx, pluginType) + configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("tokens")) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryTokens, name, pluginType, deprecatedConfig, configHash) if err != nil { - return nil, err + return err } - plugins[name] = tokensPlugin{ - config: deprecatedConfig, - plugin: plugin, + pc.tokens, err = tifactory.GetPlugin(ctx, pluginType) + if err != nil { + return err } } } - return plugins, err + return nil } -func (nm *namespaceManager) getDatabasePlugins(ctx context.Context) (plugins map[string]databasePlugin, err error) { - plugins = make(map[string]databasePlugin) +func (nm *namespaceManager) getDatabasePlugins(ctx context.Context, plugins map[string]*plugin, rawConfig fftypes.JSONObject) (err error) { dbConfigArraySize := databaseConfig.ArraySize() + rawPluginDatabaseConfig := rawConfig.GetObject("plugins").GetObjectArray("database") + if len(rawPluginDatabaseConfig) != dbConfigArraySize { + log.L(ctx).Errorf("Expected len(%d) for plugins.database: %s", dbConfigArraySize, rawPluginDatabaseConfig) + return i18n.NewError(ctx, coremsgs.MsgConfigArrayVsRawConfigMismatch) + } for i := 0; i < dbConfigArraySize; i++ { config := databaseConfig.ArrayEntry(i) - name, pluginType, err := nm.validatePluginConfig(ctx, config, "database") + configHash := nm.configHash(rawPluginDatabaseConfig[i]) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryDatabase, config, configHash) if err != nil { - return nil, err + return err } - plugin, err := difactory.GetPlugin(ctx, pluginType) + pc.database, err = difactory.GetPlugin(ctx, pc.pluginType) if err != nil { - return nil, err - } - - plugins[name] = databasePlugin{ - config: config.SubSection(pluginType), - plugin: plugin, + return err } } @@ -540,60 +718,74 @@ func (nm *namespaceManager) getDatabasePlugins(ctx context.Context) (plugins map if len(plugins) == 0 { pluginType := deprecatedDatabaseConfig.GetString(coreconfig.PluginConfigType) if pluginType != "" { - plugin, err := difactory.GetPlugin(ctx, deprecatedDatabaseConfig.GetString(coreconfig.PluginConfigType)) + log.L(ctx).Warnf("Your database config uses a deprecated configuration structure - the database configuration has been moved under the 'plugins' section") + configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("database")) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryDatabase, "database_0", pluginType, deprecatedDatabaseConfig, configHash) if err != nil { - return nil, err + return err } - log.L(ctx).Warnf("Your database config uses a deprecated configuration structure - the database configuration has been moved under the 'plugins' section") - name := "database_0" - plugins[name] = databasePlugin{ - config: deprecatedDatabaseConfig.SubSection(pluginType), - plugin: plugin, + pc.database, err = difactory.GetPlugin(ctx, pluginType) + if err != nil { + return err } } } - return plugins, err + return nil } -func (nm *namespaceManager) validatePluginConfig(ctx context.Context, config config.Section, sectionName string) (name, pluginType string, err error) { - name = config.GetString(coreconfig.PluginConfigName) - pluginType = config.GetString(coreconfig.PluginConfigType) +func (nm *namespaceManager) validatePluginConfig(ctx context.Context, plugins map[string]*plugin, category pluginCategory, config config.Section, configHash *fftypes.Bytes32) (*plugin, error) { + name := config.GetString(coreconfig.PluginConfigName) + pluginType := config.GetString(coreconfig.PluginConfigType) if name == "" || pluginType == "" { - return "", "", i18n.NewError(ctx, coremsgs.MsgInvalidPluginConfiguration, sectionName) + return nil, i18n.NewError(ctx, coremsgs.MsgInvalidPluginConfiguration, category) } if err := fftypes.ValidateFFNameField(ctx, name, "name"); err != nil { - return "", "", err + return nil, err } - if _, ok := nm.pluginNames[name]; ok { - return "", "", i18n.NewError(ctx, coremsgs.MsgDuplicatePluginName, name) + return nm.newPluginCommon(ctx, plugins, category, name, pluginType, config, configHash) +} + +func (nm *namespaceManager) newPluginCommon(ctx context.Context, plugins map[string]*plugin, category pluginCategory, name, pluginType string, config config.Section, configHash *fftypes.Bytes32) (*plugin, error) { + if _, ok := plugins[name]; ok { + return nil, i18n.NewError(ctx, coremsgs.MsgDuplicatePluginName, name) } - nm.pluginNames[name] = true - return name, pluginType, nil + pc := &plugin{ + name: name, + category: category, + pluginType: pluginType, + config: config, + configHash: configHash, + loadTime: time.Now(), + } + plugins[name] = pc + // context is always inherited from namespaceManager BG context _not_ the context of the caller + pc.ctx, pc.cancelCtx = context.WithCancel(nm.ctx) + return pc, nil } -func (nm *namespaceManager) getDataExchangePlugins(ctx context.Context) (plugins map[string]dataExchangePlugin, err error) { - plugins = make(map[string]dataExchangePlugin) +func (nm *namespaceManager) getDataExchangePlugins(ctx context.Context, plugins map[string]*plugin, rawConfig fftypes.JSONObject) (err error) { dxConfigArraySize := dataexchangeConfig.ArraySize() + rawPluginDXConfig := rawConfig.GetObject("plugins").GetObjectArray("dataexchange") + if len(rawPluginDXConfig) != dxConfigArraySize { + log.L(ctx).Errorf("Expected len(%d) for plugins.dataexchange: %s", dxConfigArraySize, rawPluginDXConfig) + return i18n.NewError(ctx, coremsgs.MsgConfigArrayVsRawConfigMismatch) + } for i := 0; i < dxConfigArraySize; i++ { config := dataexchangeConfig.ArrayEntry(i) - name, pluginType, err := nm.validatePluginConfig(ctx, config, "dataexchange") + configHash := nm.configHash(rawPluginDXConfig[i]) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryDataexchange, config, configHash) if err != nil { - return nil, err + return err } - plugin, err := dxfactory.GetPlugin(ctx, pluginType) + pc.dataexchange, err = dxfactory.GetPlugin(ctx, pc.pluginType) if err != nil { - return nil, err - } - - plugins[name] = dataExchangePlugin{ - config: config.SubSection(pluginType), - plugin: plugin, + return err } } @@ -601,64 +793,64 @@ func (nm *namespaceManager) getDataExchangePlugins(ctx context.Context) (plugins if len(plugins) == 0 { pluginType := deprecatedDataexchangeConfig.GetString(coreconfig.PluginConfigType) if pluginType != "" { - plugin, err := dxfactory.GetPlugin(ctx, pluginType) + log.L(ctx).Warnf("Your data exchange config uses a deprecated configuration structure - the data exchange configuration has been moved under the 'plugins' section") + configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("dataexchange")) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryDataexchange, "dataexchange_0", pluginType, deprecatedDataexchangeConfig, configHash) if err != nil { - return nil, err + return err } - log.L(ctx).Warnf("Your data exchange config uses a deprecated configuration structure - the data exchange configuration has been moved under the 'plugins' section") - name := "dataexchange_0" - plugins[name] = dataExchangePlugin{ - config: deprecatedDataexchangeConfig.SubSection(pluginType), - plugin: plugin, + pc.dataexchange, err = dxfactory.GetPlugin(ctx, pluginType) + if err != nil { + return err } } } - return plugins, err + return nil } -func (nm *namespaceManager) getIdentityPlugins(ctx context.Context) (plugins map[string]identityPlugin, err error) { - plugins = make(map[string]identityPlugin) +func (nm *namespaceManager) getIdentityPlugins(ctx context.Context, plugins map[string]*plugin, rawConfig fftypes.JSONObject) (err error) { configSize := identityConfig.ArraySize() + rawPluginIdentityConfig := rawConfig.GetObject("plugins").GetObjectArray("identity") + if len(rawPluginIdentityConfig) != configSize { + log.L(ctx).Errorf("Expected len(%d) for plugins.identity: %s", configSize, rawPluginIdentityConfig) + return i18n.NewError(ctx, coremsgs.MsgConfigArrayVsRawConfigMismatch) + } for i := 0; i < configSize; i++ { config := identityConfig.ArrayEntry(i) - name, pluginType, err := nm.validatePluginConfig(ctx, config, "identity") + configHash := nm.configHash(rawPluginIdentityConfig[i]) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryIdentity, config, configHash) if err != nil { - return nil, err + return err } - plugin, err := iifactory.GetPlugin(ctx, pluginType) + pc.identity, err = iifactory.GetPlugin(ctx, pc.pluginType) if err != nil { - return nil, err - } - - plugins[name] = identityPlugin{ - config: config.SubSection(pluginType), - plugin: plugin, + return err } } - return plugins, err + return nil } -func (nm *namespaceManager) getBlockchainPlugins(ctx context.Context) (plugins map[string]blockchainPlugin, err error) { - plugins = make(map[string]blockchainPlugin) +func (nm *namespaceManager) getBlockchainPlugins(ctx context.Context, plugins map[string]*plugin, rawConfig fftypes.JSONObject) (err error) { blockchainConfigArraySize := blockchainConfig.ArraySize() + rawPluginBlockchainsConfig := rawConfig.GetObject("plugins").GetObjectArray("blockchain") + if len(rawPluginBlockchainsConfig) != blockchainConfigArraySize { + log.L(ctx).Errorf("Expected len(%d) for plugins.blockchain: %s", blockchainConfigArraySize, rawPluginBlockchainsConfig) + return i18n.NewError(ctx, coremsgs.MsgConfigArrayVsRawConfigMismatch) + } for i := 0; i < blockchainConfigArraySize; i++ { config := blockchainConfig.ArrayEntry(i) - name, pluginType, err := nm.validatePluginConfig(ctx, config, "blockchain") + configHash := nm.configHash(rawPluginBlockchainsConfig[i]) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryBlockchain, config, configHash) if err != nil { - return nil, err + return err } - plugin, err := bifactory.GetPlugin(ctx, pluginType) + pc.blockchain, err = bifactory.GetPlugin(ctx, pc.pluginType) if err != nil { - return nil, err - } - - plugins[name] = blockchainPlugin{ - config: config.SubSection(pluginType), - plugin: plugin, + return err } } @@ -666,40 +858,41 @@ func (nm *namespaceManager) getBlockchainPlugins(ctx context.Context) (plugins m if len(plugins) == 0 { pluginType := deprecatedBlockchainConfig.GetString(coreconfig.PluginConfigType) if pluginType != "" { - plugin, err := bifactory.GetPlugin(ctx, pluginType) + log.L(ctx).Warnf("Your blockchain config uses a deprecated configuration structure - the blockchain configuration has been moved under the 'plugins' section") + + configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("blockchain")) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryBlockchain, "blockchain_0", pluginType, deprecatedBlockchainConfig, configHash) if err != nil { - return nil, err + return err } - log.L(ctx).Warnf("Your blockchain config uses a deprecated configuration structure - the blockchain configuration has been moved under the 'plugins' section") - name := "blockchain_0" - plugins[name] = blockchainPlugin{ - config: deprecatedBlockchainConfig.SubSection(pluginType), - plugin: plugin, + pc.blockchain, err = bifactory.GetPlugin(ctx, pluginType) + if err != nil { + return err } } } - return plugins, err + return nil } -func (nm *namespaceManager) getSharedStoragePlugins(ctx context.Context) (plugins map[string]sharedStoragePlugin, err error) { - plugins = make(map[string]sharedStoragePlugin) +func (nm *namespaceManager) getSharedStoragePlugins(ctx context.Context, plugins map[string]*plugin, rawConfig fftypes.JSONObject) (err error) { configSize := sharedstorageConfig.ArraySize() + rawPluginSharedStorageConfig := rawConfig.GetObject("plugins").GetObjectArray("sharedstorage") + if len(rawPluginSharedStorageConfig) != configSize { + log.L(ctx).Errorf("Expected len(%d) for plugins.sharedstorage: %s", configSize, rawPluginSharedStorageConfig) + return i18n.NewError(ctx, coremsgs.MsgConfigArrayVsRawConfigMismatch) + } for i := 0; i < configSize; i++ { config := sharedstorageConfig.ArrayEntry(i) - name, pluginType, err := nm.validatePluginConfig(ctx, config, "sharedstorage") + configHash := nm.configHash(rawPluginSharedStorageConfig[i]) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategorySharedstorage, config, configHash) if err != nil { - return nil, err + return err } - plugin, err := ssfactory.GetPlugin(ctx, pluginType) + pc.sharedstorage, err = ssfactory.GetPlugin(ctx, pc.pluginType) if err != nil { - return nil, err - } - - plugins[name] = sharedStoragePlugin{ - config: config.SubSection(pluginType), - plugin: plugin, + return err } } @@ -707,66 +900,74 @@ func (nm *namespaceManager) getSharedStoragePlugins(ctx context.Context) (plugin if len(plugins) == 0 { pluginType := deprecatedSharedStorageConfig.GetString(coreconfig.PluginConfigType) if pluginType != "" { - plugin, err := ssfactory.GetPlugin(ctx, pluginType) + log.L(ctx).Warnf("Your shared storage config uses a deprecated configuration structure - the shared storage configuration has been moved under the 'plugins' section") + + configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("sharedstorage")) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategorySharedstorage, "sharedstorage_0", pluginType, deprecatedBlockchainConfig, configHash) if err != nil { - return nil, err + return err } - log.L(ctx).Warnf("Your shared storage config uses a deprecated configuration structure - the shared storage configuration has been moved under the 'plugins' section") - name := "sharedstorage_0" - plugins[name] = sharedStoragePlugin{ - config: deprecatedSharedStorageConfig.SubSection(pluginType), - plugin: plugin, + pc.sharedstorage, err = ssfactory.GetPlugin(ctx, pluginType) + if err != nil { + return err } } } - return plugins, err + return nil } -func (nm *namespaceManager) initPlugins(ctx context.Context, cancelCtx context.CancelFunc) (err error) { - for _, entry := range nm.plugins.database { - if err = entry.plugin.Init(ctx, entry.config); err != nil { - return err - } - entry.plugin.SetHandler(database.GlobalHandler, nm) - } - for _, entry := range nm.plugins.blockchain { - if err = entry.plugin.Init(ctx, cancelCtx, entry.config, nm.metrics, nm.cacheManager); err != nil { - return err - } - } - for _, entry := range nm.plugins.dataexchange { - if err = entry.plugin.Init(ctx, cancelCtx, entry.config); err != nil { - return err - } - } - for _, entry := range nm.plugins.sharedstorage { - if err = entry.plugin.Init(ctx, entry.config); err != nil { - return err - } - } - for name, entry := range nm.plugins.tokens { - if err = entry.plugin.Init(ctx, cancelCtx, name, entry.config); err != nil { - return err - } - } - for _, entry := range nm.plugins.events { - if err = entry.plugin.Init(ctx, entry.config); err != nil { - return err +func (nm *namespaceManager) initPlugins(ctx context.Context, pluginsToStart map[string]*plugin) (err error) { + for name, p := range nm.plugins { + if pluginsToStart[name] == nil { + continue } - } - for name, entry := range nm.plugins.auth { - if err = entry.plugin.Init(ctx, name, entry.config); err != nil { - return err + switch p.category { + case pluginCategoryDatabase: + if err = p.database.Init(p.ctx, p.config); err != nil { + return err + } + p.database.SetHandler(database.GlobalHandler, nm) + case pluginCategoryBlockchain: + if err = p.blockchain.Init(p.ctx, nm.cancelCtx /* allow plugin to stop whole process */, p.config, nm.metrics, nm.cacheManager); err != nil { + return err + } + case pluginCategoryDataexchange: + if err = p.dataexchange.Init(p.ctx, nm.cancelCtx /* allow plugin to stop whole process */, p.config); err != nil { + return err + } + case pluginCategorySharedstorage: + if err = p.sharedstorage.Init(p.ctx, p.config); err != nil { + return err + } + case pluginCategoryTokens: + if err = p.tokens.Init(p.ctx, nm.cancelCtx /* allow plugin to stop whole process */, name, p.config); err != nil { + return err + } + case pluginCategoryEvents: + if err = p.events.Init(p.ctx, p.config); err != nil { + return err + } + case pluginCategoryAuth: + if err = p.auth.Init(p.ctx, name, p.config); err != nil { + return err + } } } return nil } -func (nm *namespaceManager) loadNamespaces(ctx context.Context) (err error) { +func (nm *namespaceManager) loadNamespaces(ctx context.Context, rawConfig fftypes.JSONObject) (newNS map[string]*namespace, err error) { defaultName := config.GetString(coreconfig.NamespacesDefault) size := namespacePredefined.ArraySize() + rawPredefinedNSConfig := rawConfig.GetObject("namespaces").GetObjectArray("predefined") + if len(rawPredefinedNSConfig) != size { + log.L(ctx).Errorf("Expected len(%d) for namespaces.predefined: %s", size, rawPredefinedNSConfig) + return nil, i18n.NewError(ctx, coremsgs.MsgConfigArrayVsRawConfigMismatch) + } foundDefault := false + + newNS = make(map[string]*namespace) for i := 0; i < size; i++ { nsConfig := namespacePredefined.ArrayEntry(i) name := nsConfig.GetString(coreconfig.NamespaceName) @@ -774,22 +975,24 @@ func (nm *namespaceManager) loadNamespaces(ctx context.Context) (err error) { log.L(ctx).Warnf("Skipping unnamed entry at namespaces.predefined[%d]", i) continue } - if _, ok := nm.namespaces[name]; ok { + if _, ok := newNS[name]; ok { log.L(ctx).Warnf("Duplicate predefined namespace (ignored): %s", name) continue } foundDefault = foundDefault || name == defaultName - if nm.namespaces[name], err = nm.loadNamespace(ctx, name, i, nsConfig); err != nil { - return err + if newNS[name], err = nm.loadNamespace(ctx, name, i, nsConfig, rawPredefinedNSConfig[i]); err != nil { + return nil, err } } - if !foundDefault { - return i18n.NewError(ctx, coremsgs.MsgDefaultNamespaceNotFound, defaultName) + // We allow startup with zero namespaces defined, so that we can have a FF Core + // ready to accept config updates to add new namespaces. + if !foundDefault && size > 0 { + return nil, i18n.NewError(ctx, coremsgs.MsgDefaultNamespaceNotFound, defaultName) } - return err + return newNS, err } -func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, index int, conf config.Section) (*namespace, error) { +func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, index int, conf config.Section, rawNSConfig fftypes.JSONObject) (*namespace, error) { if err := fftypes.ValidateFFNameField(ctx, name, fmt.Sprintf("namespaces.predefined[%d].name", index)); err != nil { return nil, err } @@ -856,28 +1059,8 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde pluginsRaw := conf.Get(coreconfig.NamespacePlugins) plugins := conf.GetStringSlice(coreconfig.NamespacePlugins) if pluginsRaw == nil { - for plugin := range nm.plugins.blockchain { - plugins = append(plugins, plugin) - } - - for plugin := range nm.plugins.dataexchange { - plugins = append(plugins, plugin) - } - - for plugin := range nm.plugins.sharedstorage { - plugins = append(plugins, plugin) - } - - for plugin := range nm.plugins.database { - plugins = append(plugins, plugin) - } - - for plugin := range nm.plugins.identity { - plugins = append(plugins, plugin) - } - - for plugin := range nm.plugins.tokens { - plugins = append(plugins, plugin) + for pluginName := range nm.plugins { + plugins = append(plugins, pluginName) } } @@ -921,72 +1104,72 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde NetworkName: networkName, Description: conf.GetString(coreconfig.NamespaceDescription), }, - config: config, - plugins: plugins, + config: config, + configHash: nm.configHash(rawNSConfig), + plugins: plugins, }, nil } func (nm *namespaceManager) validatePlugins(ctx context.Context, name string, plugins []string) (*orchestrator.Plugins, error) { var result orchestrator.Plugins for _, pluginName := range plugins { - if instance, ok := nm.plugins.blockchain[pluginName]; ok { + p := nm.plugins[pluginName] + if p == nil { + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceUnknownPlugin, name, pluginName) + } + switch p.category { + case pluginCategoryBlockchain: if result.Blockchain.Plugin != nil { return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "blockchain") } result.Blockchain = orchestrator.BlockchainPlugin{ Name: pluginName, - Plugin: instance.plugin, + Plugin: p.blockchain, } - continue - } - if instance, ok := nm.plugins.dataexchange[pluginName]; ok { + case pluginCategoryDataexchange: if result.DataExchange.Plugin != nil { return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "dataexchange") } result.DataExchange = orchestrator.DataExchangePlugin{ Name: pluginName, - Plugin: instance.plugin, + Plugin: p.dataexchange, } - continue - } - if instance, ok := nm.plugins.sharedstorage[pluginName]; ok { + case pluginCategorySharedstorage: if result.SharedStorage.Plugin != nil { return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "sharedstorage") } result.SharedStorage = orchestrator.SharedStoragePlugin{ Name: pluginName, - Plugin: instance.plugin, + Plugin: p.sharedstorage, } - continue - } - if instance, ok := nm.plugins.database[pluginName]; ok { + case pluginCategoryDatabase: if result.Database.Plugin != nil { return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "database") } result.Database = orchestrator.DatabasePlugin{ Name: pluginName, - Plugin: instance.plugin, + Plugin: p.database, } - continue - } - if instance, ok := nm.plugins.tokens[pluginName]; ok { + case pluginCategoryTokens: result.Tokens = append(result.Tokens, orchestrator.TokensPlugin{ Name: pluginName, - Plugin: instance.plugin, + Plugin: p.tokens, }) - continue - } - if instance, ok := nm.plugins.identity[pluginName]; ok { + case pluginCategoryIdentity: + if result.Identity.Plugin != nil { + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "identity") + } result.Identity = orchestrator.IdentityPlugin{ Name: pluginName, - Plugin: instance.plugin, + Plugin: p.identity, + } + case pluginCategoryAuth: + if result.Auth.Plugin != nil { + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "auth") } - continue - } - if instance, ok := nm.plugins.auth[pluginName]; ok { result.Auth = orchestrator.AuthPlugin{ Name: pluginName, - Plugin: instance.plugin, + Plugin: p.auth, } continue } @@ -1010,9 +1193,11 @@ func (nm *namespaceManager) validateMultiPartyConfig(ctx context.Context, name s return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceWrongPluginsMultiparty, name) } - result.Events = make(map[string]events.Plugin, len(nm.plugins.events)) - for name, entry := range nm.plugins.events { - result.Events[name] = entry.plugin + result.Events = make(map[string]events.Plugin) + for name, p := range nm.plugins { + if p.category == pluginCategoryEvents { + result.Events[name] = p.events + } } return result, nil } @@ -1028,9 +1213,11 @@ func (nm *namespaceManager) validateNonMultipartyConfig(ctx context.Context, nam return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceNoDatabase, name) } - result.Events = make(map[string]events.Plugin, len(nm.plugins.events)) - for name, entry := range nm.plugins.events { - result.Events[name] = entry.plugin + result.Events = make(map[string]events.Plugin) + for name, p := range nm.plugins { + if p.category == pluginCategoryEvents { + result.Events[name] = p.events + } } return result, nil } @@ -1082,8 +1269,7 @@ func (nm *namespaceManager) ResolveOperationByNamespacedID(ctx context.Context, return or.Operations().ResolveOperationByID(ctx, u, op) } -func (nm *namespaceManager) getEventPlugins(ctx context.Context) (plugins map[string]eventsPlugin, err error) { - plugins = make(map[string]eventsPlugin) +func (nm *namespaceManager) getEventPlugins(ctx context.Context, plugins map[string]*plugin, rawConfig fftypes.JSONObject) (err error) { enabledTransports := config.GetStringSlice(coreconfig.EventTransportsEnabled) uniqueTransports := make(map[string]bool) for _, transport := range enabledTransports { @@ -1092,44 +1278,46 @@ func (nm *namespaceManager) getEventPlugins(ctx context.Context) (plugins map[st // Cannot disable the internal listener uniqueTransports[system.SystemEventsTransport] = true for transport := range uniqueTransports { - plugin, err := eifactory.GetPlugin(ctx, transport) + + eventsPlugin, err := eifactory.GetPlugin(ctx, transport) if err != nil { - return nil, err + return err } - - name := plugin.Name() + name := eventsPlugin.Name() section := config.RootSection("events").SubSection(name) - plugin.InitConfig(section) - plugins[name] = eventsPlugin{ - config: section, - plugin: plugin, + rawEventConfig := rawConfig.GetObject("events").GetObject(name) + + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryEvents, name, name /* name is category for events */, section, nm.configHash(rawEventConfig)) + if err != nil { + return err } + pc.events = eventsPlugin + pc.events.InitConfig(section) } - return plugins, err + return nil } -func (nm *namespaceManager) getAuthPlugin(ctx context.Context) (plugins map[string]authPlugin, err error) { - plugins = make(map[string]authPlugin) - +func (nm *namespaceManager) getAuthPlugin(ctx context.Context, plugins map[string]*plugin, rawConfig fftypes.JSONObject) (err error) { authConfigArraySize := authConfig.ArraySize() + rawPluginAuthConfig := rawConfig.GetObject("plugins").GetObjectArray("auth") + if len(rawPluginAuthConfig) != authConfigArraySize { + log.L(ctx).Errorf("Expected len(%d) for plugins.auth: %s", authConfigArraySize, rawPluginAuthConfig) + return i18n.NewError(ctx, coremsgs.MsgConfigArrayVsRawConfigMismatch) + } for i := 0; i < authConfigArraySize; i++ { config := authConfig.ArrayEntry(i) - name, pluginType, err := nm.validatePluginConfig(ctx, config, "auth") + configHash := nm.configHash(rawPluginAuthConfig[i]) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryAuth, config, configHash) if err != nil { - return nil, err + return err } - plugin, err := authfactory.GetPlugin(ctx, pluginType) + pc.auth, err = authfactory.GetPlugin(ctx, pc.pluginType) if err != nil { - return nil, err - } - - plugins[name] = authPlugin{ - config: config.SubSection(pluginType), - plugin: plugin, + return err } } - return plugins, err + return nil } func (nm *namespaceManager) Authorize(ctx context.Context, authReq *fftypes.AuthReq) error { diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go index 77828f058..24beeb27c 100644 --- a/internal/namespace/manager_test.go +++ b/internal/namespace/manager_test.go @@ -23,16 +23,9 @@ import ( "testing" "github.com/hyperledger/firefly-common/mocks/authmocks" - "github.com/hyperledger/firefly-common/pkg/auth/authfactory" - "github.com/hyperledger/firefly-common/pkg/config" "github.com/hyperledger/firefly-common/pkg/fftypes" - "github.com/hyperledger/firefly/internal/blockchain/bifactory" "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/internal/database/difactory" - "github.com/hyperledger/firefly/internal/dataexchange/dxfactory" - "github.com/hyperledger/firefly/internal/identity/iifactory" - "github.com/hyperledger/firefly/internal/sharedstorage/ssfactory" - "github.com/hyperledger/firefly/internal/tokens/tifactory" "github.com/hyperledger/firefly/mocks/blockchainmocks" "github.com/hyperledger/firefly/mocks/cachemocks" "github.com/hyperledger/firefly/mocks/databasemocks" @@ -40,14 +33,12 @@ import ( "github.com/hyperledger/firefly/mocks/eventsmocks" "github.com/hyperledger/firefly/mocks/identitymocks" "github.com/hyperledger/firefly/mocks/metricsmocks" - "github.com/hyperledger/firefly/mocks/operationmocks" "github.com/hyperledger/firefly/mocks/orchestratormocks" "github.com/hyperledger/firefly/mocks/sharedstoragemocks" "github.com/hyperledger/firefly/mocks/spieventsmocks" "github.com/hyperledger/firefly/mocks/tokenmocks" "github.com/hyperledger/firefly/pkg/core" "github.com/hyperledger/firefly/pkg/database" - "github.com/hyperledger/firefly/pkg/tokens" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -79,11 +70,11 @@ func (nm *testNamespaceManager) cleanup(t *testing.T) { nm.auth.AssertExpectations(t) } -func newTestNamespaceManager(resetConfig bool) *testNamespaceManager { +func newTestNamespaceManager(t *testing.T, resetConfig bool) (*testNamespaceManager, func()) { if resetConfig { coreconfig.Reset() InitConfig(true) - namespaceConfig.AddKnownKey("predefined.0.multiparty.enabled", true) + namespaceConfigSection.AddKnownKey("predefined.0.multiparty.enabled", true) } nm := &testNamespaceManager{ mmi: &metricsmocks.Manager{}, @@ -99,38 +90,80 @@ func newTestNamespaceManager(resetConfig bool) *testNamespaceManager { namespaceManager: namespaceManager{ reset: make(chan bool, 1), namespaces: make(map[string]*namespace), - pluginNames: make(map[string]bool), + plugins: make(map[string]*plugin), tokenBroadcastNames: make(map[string]string), }, } - nm.plugins.blockchain = map[string]blockchainPlugin{ - "ethereum": {plugin: nm.mbi}, + ctx, cancel := context.WithCancel(context.Background()) + nm.watchConfig = func() { + <-ctx.Done() } - nm.plugins.database = map[string]databasePlugin{ - "postgres": {plugin: nm.mdi}, - } - nm.plugins.dataexchange = map[string]dataExchangePlugin{ - "ffdx": {plugin: nm.mdx}, - } - nm.plugins.sharedstorage = map[string]sharedStoragePlugin{ - "ipfs": {plugin: nm.mps}, - } - nm.plugins.identity = map[string]identityPlugin{ - "tbd": {plugin: &identitymocks.Plugin{}}, - } - nm.plugins.tokens = map[string]tokensPlugin{ - "erc721": {plugin: nm.mti}, - "erc1155": {plugin: nm.mti}, - } - nm.plugins.events = map[string]eventsPlugin{ - "websockets": {plugin: nm.mev}, - } - nm.plugins.auth = map[string]authPlugin{ - "basicauth": {plugin: nm.auth}, + nm.plugins = map[string]*plugin{ + "ethereum": { + name: "ethereum", + category: pluginCategoryBlockchain, + pluginType: "ethereum", + blockchain: nm.mbi, + }, + "postgres": { + name: "postgres", + category: pluginCategoryDatabase, + pluginType: "postgres", + database: nm.mdi, + }, + "ffdx": { + name: "ffdx", + category: pluginCategoryDataexchange, + pluginType: "ffdx", + dataexchange: nm.mdx, + }, + "ipfs": { + name: "ipfs", + category: pluginCategorySharedstorage, + pluginType: "ipfs", + sharedstorage: nm.mps, + }, + "tbd": { + name: "tbd", + category: pluginCategorySharedstorage, + pluginType: "tbd", + identity: &identitymocks.Plugin{}, + }, + "erc721": { + name: "erc721", + category: pluginCategoryTokens, + pluginType: "erc721", + tokens: nm.mti, + }, + "erc1155": { + name: "erc1155", + category: pluginCategoryTokens, + pluginType: "erc1155", + tokens: nm.mti, + }, + "websockets": { + name: "websockets", + category: pluginCategoryEvents, + pluginType: "websockets", + events: nm.mev, + }, + "basicauth": { + name: "basicauth", + category: pluginCategoryAuth, + pluginType: "basicauth", + auth: nm.auth, + }, } nm.namespaceManager.metrics = nm.mmi nm.namespaceManager.adminEvents = nm.mae - return nm + return nm, func() { + a := recover() + if a != nil { + panic(a) + } + cancel() + nm.cleanup(t) + } } func TestNewNamespaceManager(t *testing.T) { @@ -139,8 +172,8 @@ func TestNewNamespaceManager(t *testing.T) { } func TestInit(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.metricsEnabled = true mo := &orchestratormocks.Orchestrator{} @@ -179,8 +212,8 @@ func TestInit(t *testing.T) { } func TestInitDatabaseFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.utOrchestrator = &orchestratormocks.Orchestrator{} @@ -192,8 +225,8 @@ func TestInitDatabaseFail(t *testing.T) { } func TestInitBlockchainFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.utOrchestrator = &orchestratormocks.Orchestrator{} @@ -207,8 +240,8 @@ func TestInitBlockchainFail(t *testing.T) { } func TestInitDataExchangeFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.utOrchestrator = &orchestratormocks.Orchestrator{} @@ -223,8 +256,8 @@ func TestInitDataExchangeFail(t *testing.T) { } func TestInitSharedStorageFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.utOrchestrator = &orchestratormocks.Orchestrator{} @@ -240,8 +273,8 @@ func TestInitSharedStorageFail(t *testing.T) { } func TestInitTokensFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.utOrchestrator = &orchestratormocks.Orchestrator{} @@ -258,8 +291,8 @@ func TestInitTokensFail(t *testing.T) { } func TestInitEventsFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.utOrchestrator = &orchestratormocks.Orchestrator{} @@ -278,8 +311,8 @@ func TestInitEventsFail(t *testing.T) { } func TestInitAuthFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.utOrchestrator = &orchestratormocks.Orchestrator{} @@ -299,8 +332,8 @@ func TestInitAuthFail(t *testing.T) { } func TestInitOrchestratorFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.mdi.On("Capabilities").Return(&database.Capabilities{ Concurrency: true, @@ -324,8 +357,8 @@ func TestInitOrchestratorFail(t *testing.T) { } func TestInitVersion1(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() mo := &orchestratormocks.Orchestrator{} mo.On("Init", mock.Anything, mock.Anything). @@ -367,8 +400,8 @@ func TestInitVersion1(t *testing.T) { } func TestInitFFSystemWithTerminatedV1Contract(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.metricsEnabled = true mo := &orchestratormocks.Orchestrator{} @@ -415,8 +448,8 @@ func TestInitFFSystemWithTerminatedV1Contract(t *testing.T) { } func TestLegacyNamespaceConflictingPlugins(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.metricsEnabled = true viper.SetConfigType("yaml") @@ -483,8 +516,8 @@ func TestLegacyNamespaceConflictingPlugins(t *testing.T) { } func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.metricsEnabled = true viper.SetConfigType("yaml") @@ -550,8 +583,8 @@ func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { } func TestLegacyNamespaceMatchingPlugins(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() nm.metricsEnabled = true viper.SetConfigType("yaml") @@ -618,8 +651,8 @@ func TestLegacyNamespaceMatchingPlugins(t *testing.T) { } func TestInitVersion1Fail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() mo := &orchestratormocks.Orchestrator{} mo.On("Init", mock.Anything, mock.Anything). @@ -657,8 +690,8 @@ func TestInitVersion1Fail(t *testing.T) { } func TestInitNamespaceQueryFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() ns := &namespace{ Namespace: core.Namespace{ @@ -674,8 +707,8 @@ func TestInitNamespaceQueryFail(t *testing.T) { } func TestInitNamespaceExistingUpsertFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() ns := &namespace{ Namespace: core.Namespace{ @@ -695,1319 +728,1333 @@ func TestInitNamespaceExistingUpsertFail(t *testing.T) { } func TestDeprecatedDatabasePlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) + nm, cleanup := newTestNamespaceManager(t, true) + defer cleanup() difactory.InitConfigDeprecated(deprecatedDatabaseConfig) deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "postgres") - plugins, err := nm.getDatabasePlugins(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} - -func TestDeprecatedDatabasePluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - difactory.InitConfigDeprecated(deprecatedDatabaseConfig) - deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "wrong") - _, err := nm.getDatabasePlugins(context.Background()) - assert.Regexp(t, "FF10122.*wrong", err) -} - -func TestDatabasePlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - difactory.InitConfig(databaseConfig) - config.Set("plugins.database", []fftypes.JSONObject{{}}) - databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres") - plugins, err := nm.getDatabasePlugins(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} - -func TestDatabasePluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - difactory.InitConfig(databaseConfig) - config.Set("plugins.database", []fftypes.JSONObject{{}}) - databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "unknown") - plugins, err := nm.getDatabasePlugins(context.Background()) - assert.Nil(t, plugins) - assert.Error(t, err) -} - -func TestDatabasePluginBadName(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.plugins.database = nil - difactory.InitConfig(databaseConfig) - config.Set("plugins.database", []fftypes.JSONObject{{}}) - databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong////") - databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres") - - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) - assert.Error(t, err) -} - -func TestIdentityPluginBadName(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - iifactory.InitConfig(identityConfig) - identityConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong//") - identityConfig.AddKnownKey(coreconfig.PluginConfigType, "tbd") - config.Set("plugins.identity", []fftypes.JSONObject{{}}) - _, err := nm.getIdentityPlugins(context.Background()) - assert.Regexp(t, "FF00140.*name", err) -} - -func TestIdentityPluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - iifactory.InitConfig(identityConfig) - identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - identityConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") - config.Set("plugins.identity", []fftypes.JSONObject{{}}) - _, err := nm.getIdentityPlugins(context.Background()) - assert.Regexp(t, "FF10212.*wrong", err) -} - -func TestIdentityPluginNoType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.plugins.identity = nil - iifactory.InitConfig(identityConfig) - identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - config.Set("plugins.identity", []fftypes.JSONObject{{}}) - - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10386.*type", err) -} - -func TestIdentityPlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - iifactory.InitConfig(identityConfig) - identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - identityConfig.AddKnownKey(coreconfig.PluginConfigType, "onchain") - config.Set("plugins.identity", []fftypes.JSONObject{{}}) - plugins, err := nm.getIdentityPlugins(context.Background()) - assert.NoError(t, err) - assert.Equal(t, 1, len(plugins)) -} - -func TestDeprecatedBlockchainPlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - bifactory.InitConfigDeprecated(deprecatedBlockchainConfig) - deprecatedBlockchainConfig.Set(coreconfig.PluginConfigType, "ethereum") - plugins, err := nm.getBlockchainPlugins(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} - -func TestDeprecatedBlockchainPluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - deprecatedBlockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") - _, err := nm.getBlockchainPlugins(context.Background()) - assert.Regexp(t, "FF10110.*wrong", err) -} - -func TestBlockchainPlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - bifactory.InitConfig(blockchainConfig) - config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) - blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "ethereum") - plugins, err := nm.getBlockchainPlugins(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} - -func TestBlockchainPluginNoType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - bifactory.InitConfig(blockchainConfig) - config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) - blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - _, err := nm.getBlockchainPlugins(context.Background()) - assert.Error(t, err) -} - -func TestBlockchainPluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.plugins.blockchain = nil - bifactory.InitConfig(blockchainConfig) - config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) - blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") - - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) - assert.Error(t, err) -} - -func TestDeprecatedSharedStoragePlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) - deprecatedSharedStorageConfig.Set(coreconfig.PluginConfigType, "ipfs") - plugins, err := nm.getSharedStoragePlugins(context.Background()) + plugins := make(map[string]*plugin) + err := nm.getDatabasePlugins(context.Background(), plugins, fftypes.JSONObject{}) assert.Equal(t, 1, len(plugins)) assert.NoError(t, err) } -func TestDeprecatedSharedStoragePluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) - deprecatedSharedStorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") - _, err := nm.getSharedStoragePlugins(context.Background()) - assert.Regexp(t, "FF10134.*wrong", err) -} - -func TestSharedStoragePlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - ssfactory.InitConfig(sharedstorageConfig) - config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) - sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "ipfs") - plugins, err := nm.getSharedStoragePlugins(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} - -func TestSharedStoragePluginNoType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - ssfactory.InitConfig(sharedstorageConfig) - config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) - sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - _, err := nm.getSharedStoragePlugins(context.Background()) - assert.Error(t, err) -} - -func TestSharedStoragePluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.plugins.sharedstorage = nil - ssfactory.InitConfig(sharedstorageConfig) - config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) - sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") - - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) - assert.Error(t, err) -} - -func TestDeprecatedDataExchangePlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) - deprecatedDataexchangeConfig.Set(coreconfig.PluginConfigType, "ffdx") - plugins, err := nm.getDataExchangePlugins(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} - -func TestDeprecatedDataExchangePluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) - deprecatedDataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") - _, err := nm.getDataExchangePlugins(context.Background()) - assert.Regexp(t, "FF10213.*wrong", err) -} - -func TestDataExchangePlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - dxfactory.InitConfig(dataexchangeConfig) - config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) - dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "ffdx") - plugins, err := nm.getDataExchangePlugins(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} - -func TestDataExchangePluginNoType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - dxfactory.InitConfig(dataexchangeConfig) - config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) - dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - _, err := nm.getDataExchangePlugins(context.Background()) - assert.Error(t, err) -} - -func TestDataExchangePluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.plugins.dataexchange = nil - dxfactory.InitConfig(dataexchangeConfig) - config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) - dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") - dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") - - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) - assert.Error(t, err) -} - -func TestDeprecatedTokensPlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - tifactory.InitConfigDeprecated(deprecatedTokensConfig) - config.Set("tokens", []fftypes.JSONObject{{}}) - deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test") - deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") - plugins, err := nm.getTokensPlugins(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} - -func TestDeprecatedTokensPluginNoName(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - tifactory.InitConfigDeprecated(deprecatedTokensConfig) - config.Set("tokens", []fftypes.JSONObject{{}}) - deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") - _, err := nm.getTokensPlugins(context.Background()) - assert.Regexp(t, "FF10273", err) -} - -func TestDeprecatedTokensPluginBadName(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - tifactory.InitConfigDeprecated(deprecatedTokensConfig) - config.Set("tokens", []fftypes.JSONObject{{}}) - deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "BAD!") - deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") - _, err := nm.getTokensPlugins(context.Background()) - assert.Regexp(t, "FF00140", err) -} - -func TestDeprecatedTokensPluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - tifactory.InitConfigDeprecated(deprecatedTokensConfig) - config.Set("tokens", []fftypes.JSONObject{{}}) - deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test") - deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "wrong") - _, err := nm.getTokensPlugins(context.Background()) - assert.Regexp(t, "FF10272.*wrong", err) -} - -func TestTokensPlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - tifactory.InitConfig(tokensConfig) - config.Set("plugins.tokens", []fftypes.JSONObject{{}}) - tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") - tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens") - plugins, err := nm.getTokensPlugins(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} - -func TestTokensPluginDuplicateBroadcastName(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - tifactory.InitConfig(tokensConfig) - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - plugins: - tokens: - - name: test1 - broadcastName: remote1 - type: fftokens - fftokens: - url: http://tokens:3000 - - name: test2 - broadcastName: remote1 - type: fftokens - fftokens: - url: http://tokens:3000 - `)) - assert.NoError(t, err) +// func TestDeprecatedDatabasePluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// difactory.InitConfigDeprecated(deprecatedDatabaseConfig) +// deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "wrong") +// _, err := nm.getDatabasePlugins(context.Background()) +// assert.Regexp(t, "FF10122.*wrong", err) +// } + +// func TestDatabasePlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// difactory.InitConfig(databaseConfig) +// config.Set("plugins.database", []fftypes.JSONObject{{}}) +// databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres") +// plugins, err := nm.getDatabasePlugins(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestDatabasePluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// difactory.InitConfig(databaseConfig) +// config.Set("plugins.database", []fftypes.JSONObject{{}}) +// databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "unknown") +// plugins, err := nm.getDatabasePlugins(context.Background()) +// assert.Nil(t, plugins) +// assert.Error(t, err) +// } + +// func TestDatabasePluginBadName(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.plugins.database = nil +// difactory.InitConfig(databaseConfig) +// config.Set("plugins.database", []fftypes.JSONObject{{}}) +// databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong////") +// databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres") + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err := nm.Init(ctx, cancelCtx, nm.reset) +// assert.Error(t, err) +// } + +// func TestIdentityPluginBadName(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// iifactory.InitConfig(identityConfig) +// identityConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong//") +// identityConfig.AddKnownKey(coreconfig.PluginConfigType, "tbd") +// config.Set("plugins.identity", []fftypes.JSONObject{{}}) +// _, err := nm.getIdentityPlugins(context.Background()) +// assert.Regexp(t, "FF00140.*name", err) +// } + +// func TestIdentityPluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// iifactory.InitConfig(identityConfig) +// identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// identityConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") +// config.Set("plugins.identity", []fftypes.JSONObject{{}}) +// _, err := nm.getIdentityPlugins(context.Background()) +// assert.Regexp(t, "FF10212.*wrong", err) +// } + +// func TestIdentityPluginNoType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.plugins.identity = nil +// iifactory.InitConfig(identityConfig) +// identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// config.Set("plugins.identity", []fftypes.JSONObject{{}}) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err := nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10386.*type", err) +// } + +// func TestIdentityPlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// iifactory.InitConfig(identityConfig) +// identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// identityConfig.AddKnownKey(coreconfig.PluginConfigType, "onchain") +// config.Set("plugins.identity", []fftypes.JSONObject{{}}) +// plugins, err := nm.getIdentityPlugins(context.Background()) +// assert.NoError(t, err) +// assert.Equal(t, 1, len(plugins)) +// } + +// func TestDeprecatedBlockchainPlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// bifactory.InitConfigDeprecated(deprecatedBlockchainConfig) +// deprecatedBlockchainConfig.Set(coreconfig.PluginConfigType, "ethereum") +// plugins, err := nm.getBlockchainPlugins(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestDeprecatedBlockchainPluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// deprecatedBlockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") +// _, err := nm.getBlockchainPlugins(context.Background()) +// assert.Regexp(t, "FF10110.*wrong", err) +// } + +// func TestBlockchainPlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// bifactory.InitConfig(blockchainConfig) +// config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) +// blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "ethereum") +// plugins, err := nm.getBlockchainPlugins(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestBlockchainPluginNoType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// bifactory.InitConfig(blockchainConfig) +// config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) +// blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// _, err := nm.getBlockchainPlugins(context.Background()) +// assert.Error(t, err) +// } + +// func TestBlockchainPluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.plugins.blockchain = nil +// bifactory.InitConfig(blockchainConfig) +// config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) +// blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err := nm.Init(ctx, cancelCtx, nm.reset) +// assert.Error(t, err) +// } + +// func TestDeprecatedSharedStoragePlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) +// deprecatedSharedStorageConfig.Set(coreconfig.PluginConfigType, "ipfs") +// plugins, err := nm.getSharedStoragePlugins(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestDeprecatedSharedStoragePluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) +// deprecatedSharedStorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") +// _, err := nm.getSharedStoragePlugins(context.Background()) +// assert.Regexp(t, "FF10134.*wrong", err) +// } + +// func TestSharedStoragePlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// ssfactory.InitConfig(sharedstorageConfig) +// config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) +// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "ipfs") +// plugins, err := nm.getSharedStoragePlugins(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestSharedStoragePluginNoType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// ssfactory.InitConfig(sharedstorageConfig) +// config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) +// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// _, err := nm.getSharedStoragePlugins(context.Background()) +// assert.Error(t, err) +// } + +// func TestSharedStoragePluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.plugins.sharedstorage = nil +// ssfactory.InitConfig(sharedstorageConfig) +// config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) +// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err := nm.Init(ctx, cancelCtx, nm.reset) +// assert.Error(t, err) +// } + +// func TestDeprecatedDataExchangePlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) +// deprecatedDataexchangeConfig.Set(coreconfig.PluginConfigType, "ffdx") +// plugins, err := nm.getDataExchangePlugins(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestDeprecatedDataExchangePluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) +// deprecatedDataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") +// _, err := nm.getDataExchangePlugins(context.Background()) +// assert.Regexp(t, "FF10213.*wrong", err) +// } + +// func TestDataExchangePlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// dxfactory.InitConfig(dataexchangeConfig) +// config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) +// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "ffdx") +// plugins, err := nm.getDataExchangePlugins(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestDataExchangePluginNoType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// dxfactory.InitConfig(dataexchangeConfig) +// config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) +// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// _, err := nm.getDataExchangePlugins(context.Background()) +// assert.Error(t, err) +// } + +// func TestDataExchangePluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.plugins.dataexchange = nil +// dxfactory.InitConfig(dataexchangeConfig) +// config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) +// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") +// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err := nm.Init(ctx, cancelCtx, nm.reset) +// assert.Error(t, err) +// } + +// func TestDeprecatedTokensPlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// tifactory.InitConfigDeprecated(deprecatedTokensConfig) +// config.Set("tokens", []fftypes.JSONObject{{}}) +// deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test") +// deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") +// plugins, err := nm.getTokensPlugins(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestDeprecatedTokensPluginNoName(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// tifactory.InitConfigDeprecated(deprecatedTokensConfig) +// config.Set("tokens", []fftypes.JSONObject{{}}) +// deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") +// _, err := nm.getTokensPlugins(context.Background()) +// assert.Regexp(t, "FF10273", err) +// } + +// func TestDeprecatedTokensPluginBadName(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// tifactory.InitConfigDeprecated(deprecatedTokensConfig) +// config.Set("tokens", []fftypes.JSONObject{{}}) +// deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "BAD!") +// deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") +// _, err := nm.getTokensPlugins(context.Background()) +// assert.Regexp(t, "FF00140", err) +// } + +// func TestDeprecatedTokensPluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// tifactory.InitConfigDeprecated(deprecatedTokensConfig) +// config.Set("tokens", []fftypes.JSONObject{{}}) +// deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test") +// deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "wrong") +// _, err := nm.getTokensPlugins(context.Background()) +// assert.Regexp(t, "FF10272.*wrong", err) +// } + +// func TestTokensPlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// tifactory.InitConfig(tokensConfig) +// config.Set("plugins.tokens", []fftypes.JSONObject{{}}) +// tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") +// tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens") +// plugins, err := nm.getTokensPlugins(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestTokensPluginDuplicateBroadcastName(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// tifactory.InitConfig(tokensConfig) +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// plugins: +// tokens: +// - name: test1 +// broadcastName: remote1 +// type: fftokens +// fftokens: +// url: http://tokens:3000 +// - name: test2 +// broadcastName: remote1 +// type: fftokens +// fftokens: +// url: http://tokens:3000 +// `)) +// assert.NoError(t, err) + +// plugins, err := nm.getTokensPlugins(context.Background()) +// assert.Nil(t, plugins) +// assert.Regexp(t, "FF10419", err) +// } + +// func TestMultipleTokensPluginsWithBroadcastName(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// tifactory.InitConfig(tokensConfig) +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// plugins: +// tokens: +// - name: test1 +// broadcastName: remote1 +// type: fftokens +// fftokens: +// url: http://tokens:3000 +// - name: test2 +// broadcastName: remote2 +// type: fftokens +// fftokens: +// url: http://tokens:3000 +// `)) +// assert.NoError(t, err) + +// plugins, err := nm.getTokensPlugins(context.Background()) +// assert.Equal(t, 2, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestTokensPluginNoType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// tifactory.InitConfig(tokensConfig) +// config.Set("plugins.tokens", []fftypes.JSONObject{{}}) +// tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") +// _, err := nm.getTokensPlugins(context.Background()) +// assert.Error(t, err) +// } + +// func TestTokensPluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.plugins.tokens = nil +// tifactory.InitConfig(tokensConfig) +// config.Set("plugins.tokens", []fftypes.JSONObject{{}}) +// tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") +// tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err := nm.Init(ctx, cancelCtx, nm.reset) +// assert.Error(t, err) +// } + +// func TestTokensPluginDuplicate(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.pluginNames["erc20_erc721"] = true +// tifactory.InitConfig(tokensConfig) +// config.Set("plugins.tokens", []fftypes.JSONObject{{}}) +// tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") +// tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens") +// _, err := nm.getTokensPlugins(context.Background()) +// assert.Regexp(t, "FF10395", err) +// } + +// func TestEventsPluginDefaults(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.plugins.events = nil +// plugins, err := nm.getEventPlugins(context.Background()) +// assert.Equal(t, 3, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestAuthPlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// authfactory.InitConfigArray(authConfig) +// config.Set("plugins.auth", []fftypes.JSONObject{{}}) +// authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") +// authConfig.AddKnownKey(coreconfig.PluginConfigType, "basic") +// plugins, err := nm.getAuthPlugin(context.Background()) +// assert.Equal(t, 1, len(plugins)) +// assert.NoError(t, err) +// } + +// func TestAuthPluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.plugins.auth = nil +// authfactory.InitConfigArray(authConfig) +// config.Set("plugins.auth", []fftypes.JSONObject{{}}) +// authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") +// authConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err := nm.Init(ctx, cancelCtx, nm.reset) +// assert.Error(t, err) +// } + +// func TestAuthPluginInvalid(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// authfactory.InitConfigArray(authConfig) +// config.Set("plugins.auth", []fftypes.JSONObject{{}}) +// authConfig.AddKnownKey(coreconfig.PluginConfigName, "bad name not allowed") +// authConfig.AddKnownKey(coreconfig.PluginConfigType, "basic") +// plugins, err := nm.getAuthPlugin(context.Background()) +// assert.Equal(t, 0, len(plugins)) +// assert.Error(t, err) +// } + +// func TestEventsPluginBadType(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// nm.plugins.events = nil +// config.Set(coreconfig.EventTransportsEnabled, []string{"!unknown!"}) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err := nm.Init(ctx, cancelCtx, nm.reset) +// assert.Error(t, err) +// } + +// func TestInitBadNamespace(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// nm.utOrchestrator = &orchestratormocks.Orchestrator{} + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: "!Badness" +// predefined: +// - name: "!Badness" +// `)) +// assert.NoError(t, err) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF00140", err) +// } + +// func TestLoadNamespacesReservedName(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ff_system +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.Regexp(t, "FF10388", err) +// } + +// func TestLoadNamespacesReservedNetworkName(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// multiparty: +// enabled: true +// networknamespace: ff_system +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.Regexp(t, "FF10388", err) +// } + +// func TestLoadNamespacesDuplicate(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [postgres] +// - name: ns1 +// plugins: [postgres] +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.NoError(t, err) +// assert.Len(t, nm.namespaces, 1) +// } + +// func TestLoadNamespacesNoName(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// predefined: +// - plugins: [postgres] +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.Regexp(t, "FF10166", err) +// } + +// func TestLoadNamespacesNoDefault(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns2 +// plugins: [postgres] +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.Regexp(t, "FF10166", err) +// } + +// func TestLoadNamespacesUseDefaults(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// multiparty: +// contract: +// - location: +// address: 0x1234 +// org: +// name: org1 +// node: +// name: node1 +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.NoError(t, err) +// assert.Len(t, nm.namespaces, 1) +// assert.Equal(t, "oldest", nm.namespaces["ns1"].config.Multiparty.Contracts[0].FirstEvent) +// } + +// func TestLoadNamespacesNonMultipartyNoDatabase(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [] +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10392", err) +// } + +// func TestLoadNamespacesMultipartyUnknownPlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [basicauth, bad] +// multiparty: +// enabled: true +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10390.*unknown", err) +// } + +// func TestLoadNamespacesMultipartyMultipleBlockchains(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [ethereum, ethereum] +// multiparty: +// enabled: true +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10394.*blockchain", err) +// } + +// func TestLoadNamespacesMultipartyMultipleDX(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [ffdx, ffdx] +// multiparty: +// enabled: true +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10394.*dataexchange", err) +// } + +// func TestLoadNamespacesMultipartyMultipleSS(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [ipfs, ipfs] +// multiparty: +// enabled: true +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10394.*sharedstorage", err) +// } + +// func TestLoadNamespacesMultipartyMultipleDB(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [postgres, postgres] +// multiparty: +// enabled: true +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10394.*database", err) +// } + +// func TestInitNamespacesMultipartyWithAuth(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [ethereum, postgres, ipfs, ffdx, basicauth] +// multiparty: +// enabled: true +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.NoError(t, err) +// } + +// func TestLoadNamespacesNonMultipartyWithAuth(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [ethereum, postgres, basicauth] +// multiparty: +// enabled: false +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.NoError(t, err) +// } + +// func TestLoadNamespacesMultipartyContract(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// multiparty: +// enabled: true +// contract: +// - location: +// address: 0x4ae50189462b0e5d52285f59929d037f790771a6 +// firstEvent: oldest +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.NoError(t, err) +// } + +// func TestLoadNamespacesMultipartyContractBadLocation(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// fail := make(chan string) +// namespaceConfig.AddKnownKey("predefined.0.multiparty.contract.0.location.address", fail) + +// err := nm.loadNamespaces(context.Background()) +// assert.Regexp(t, "json:", err) +// } + +// func TestLoadNamespacesNonMultipartyMultipleDB(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [postgres, postgres] +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10394.*database", err) +// } + +// func TestLoadNamespacesNonMultipartyMultipleBlockchains(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [ethereum, ethereum] +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10394.*blockchain", err) +// } + +// func TestLoadNamespacesMultipartyMissingPlugins(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [postgres] +// multiparty: +// enabled: true +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10391", err) +// } + +// func TestLoadNamespacesNonMultipartyUnknownPlugin(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [basicauth, erc721, bad] +// `)) +// assert.NoError(t, err) + +// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() +// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) +// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) +// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) +// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) +// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + +// ctx, cancelCtx := context.WithCancel(context.Background()) +// err = nm.Init(ctx, cancelCtx, nm.reset) +// assert.Regexp(t, "FF10390.*unknown", err) +// } + +// func TestLoadNamespacesNonMultipartyTokens(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [postgres, erc721] +// `)) +// assert.NoError(t, err) + +// err = nm.loadNamespaces(context.Background()) +// assert.NoError(t, err) +// } + +// func TestStart(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "ns": {orchestrator: mo}, +// } +// nm.plugins.blockchain = nil +// nm.plugins.dataexchange = nil +// nm.plugins.tokens = nil +// nm.metricsEnabled = true + +// mo.On("Start", mock.Anything).Return(nil) + +// err := nm.Start() +// assert.NoError(t, err) + +// mo.AssertExpectations(t) +// } + +// func TestStartBlockchainFail(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "ns": { +// orchestrator: mo, +// }, +// } + +// mo.On("Start", mock.Anything).Return(nil) +// nm.mbi.On("Start").Return(fmt.Errorf("pop")) + +// err := nm.Start() +// assert.EqualError(t, err, "pop") + +// mo.AssertExpectations(t) + +// } + +// func TestStartDataExchangeFail(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "ns": { +// orchestrator: mo, +// }, +// } +// nm.plugins.blockchain = nil + +// mo.On("Start", mock.Anything).Return(nil) +// nm.mdx.On("Start").Return(fmt.Errorf("pop")) + +// err := nm.Start() +// assert.EqualError(t, err, "pop") + +// mo.AssertExpectations(t) - plugins, err := nm.getTokensPlugins(context.Background()) - assert.Nil(t, plugins) - assert.Regexp(t, "FF10419", err) -} - -func TestMultipleTokensPluginsWithBroadcastName(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - tifactory.InitConfig(tokensConfig) - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - plugins: - tokens: - - name: test1 - broadcastName: remote1 - type: fftokens - fftokens: - url: http://tokens:3000 - - name: test2 - broadcastName: remote2 - type: fftokens - fftokens: - url: http://tokens:3000 - `)) - assert.NoError(t, err) - - plugins, err := nm.getTokensPlugins(context.Background()) - assert.Equal(t, 2, len(plugins)) - assert.NoError(t, err) -} +// } -func TestTokensPluginNoType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - tifactory.InitConfig(tokensConfig) - config.Set("plugins.tokens", []fftypes.JSONObject{{}}) - tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") - _, err := nm.getTokensPlugins(context.Background()) - assert.Error(t, err) -} +// func TestStartTokensFail(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() -func TestTokensPluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.plugins.tokens = nil - tifactory.InitConfig(tokensConfig) - config.Set("plugins.tokens", []fftypes.JSONObject{{}}) - tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") - tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "ns": { +// orchestrator: mo, +// }, +// } +// nm.plugins.blockchain = nil +// nm.plugins.dataexchange = nil - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) - assert.Error(t, err) -} +// mo.On("Start", mock.Anything).Return(nil) +// nm.mti.On("Start").Return(fmt.Errorf("pop")) -func TestTokensPluginDuplicate(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.pluginNames["erc20_erc721"] = true - tifactory.InitConfig(tokensConfig) - config.Set("plugins.tokens", []fftypes.JSONObject{{}}) - tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") - tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens") - _, err := nm.getTokensPlugins(context.Background()) - assert.Regexp(t, "FF10395", err) -} +// err := nm.Start() +// assert.EqualError(t, err, "pop") -func TestEventsPluginDefaults(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.plugins.events = nil - plugins, err := nm.getEventPlugins(context.Background()) - assert.Equal(t, 3, len(plugins)) - assert.NoError(t, err) -} +// } -func TestAuthPlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - authfactory.InitConfigArray(authConfig) - config.Set("plugins.auth", []fftypes.JSONObject{{}}) - authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") - authConfig.AddKnownKey(coreconfig.PluginConfigType, "basic") - plugins, err := nm.getAuthPlugin(context.Background()) - assert.Equal(t, 1, len(plugins)) - assert.NoError(t, err) -} +// func TestStartOrchestratorFail(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() -func TestAuthPluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.plugins.auth = nil - authfactory.InitConfigArray(authConfig) - config.Set("plugins.auth", []fftypes.JSONObject{{}}) - authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") - authConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "ns": {orchestrator: mo}, +// } - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) - assert.Error(t, err) -} +// mo.On("Start", mock.Anything).Return(fmt.Errorf("pop")) -func TestAuthPluginInvalid(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - authfactory.InitConfigArray(authConfig) - config.Set("plugins.auth", []fftypes.JSONObject{{}}) - authConfig.AddKnownKey(coreconfig.PluginConfigName, "bad name not allowed") - authConfig.AddKnownKey(coreconfig.PluginConfigType, "basic") - plugins, err := nm.getAuthPlugin(context.Background()) - assert.Equal(t, 0, len(plugins)) - assert.Error(t, err) -} +// err := nm.Start() +// assert.EqualError(t, err, "pop") -func TestEventsPluginBadType(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - nm.plugins.events = nil - config.Set(coreconfig.EventTransportsEnabled, []string{"!unknown!"}) +// mo.AssertExpectations(t) +// } - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) - assert.Error(t, err) -} +// func TestWaitStop(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() -func TestInitBadNamespace(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "ns": {orchestrator: mo}, +// } +// mae := nm.adminEvents.(*spieventsmocks.Manager) - nm.utOrchestrator = &orchestratormocks.Orchestrator{} +// mo.On("WaitStop").Return() +// mae.On("WaitStop").Return() - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// nm.WaitStop() - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: "!Badness" - predefined: - - name: "!Badness" - `)) - assert.NoError(t, err) +// mo.AssertExpectations(t) +// mae.AssertExpectations(t) +// } - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF00140", err) -} +// func TestReset(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() -func TestLoadNamespacesReservedName(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// childCtx, childCancel := context.WithCancel(context.Background()) +// err := nm.Reset(childCtx) +// assert.NoError(t, err) +// childCancel() - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ff_system - `)) - assert.NoError(t, err) +// assert.True(t, <-nm.namespaceManager.reset) +// } - err = nm.loadNamespaces(context.Background()) - assert.Regexp(t, "FF10388", err) -} +// func TestResetRejectIfConfigAutoReload(t *testing.T) { +// config.RootConfigReset() +// config.Set(coreconfig.ConfigAutoReload, true) -func TestLoadNamespacesReservedNetworkName(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// nm, cleanup := newTestNamespaceManager(t, false) +// defer cleanup() - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - multiparty: - enabled: true - networknamespace: ff_system - `)) - assert.NoError(t, err) +// err := nm.Reset(context.Background()) +// assert.Regexp(t, "FF10433", err) - err = nm.loadNamespaces(context.Background()) - assert.Regexp(t, "FF10388", err) -} +// } -func TestLoadNamespacesDuplicate(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// func TestLoadMetrics(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [postgres] - - name: ns1 - plugins: [postgres] - `)) - assert.NoError(t, err) +// nm.metrics = nil - err = nm.loadNamespaces(context.Background()) - assert.NoError(t, err) - assert.Len(t, nm.namespaces, 1) -} +// err := nm.loadPlugins(context.Background()) +// assert.NoError(t, err) +// } -func TestLoadNamespacesNoName(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// func TestLoadAdminEvents(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - predefined: - - plugins: [postgres] - `)) - assert.NoError(t, err) +// nm.adminEvents = nil - err = nm.loadNamespaces(context.Background()) - assert.Regexp(t, "FF10166", err) -} +// err := nm.loadPlugins(context.Background()) +// assert.NoError(t, err) +// assert.NotNil(t, nm.SPIEvents()) +// } -func TestLoadNamespacesNoDefault(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// func TestGetNamespaces(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns2 - plugins: [postgres] - `)) - assert.NoError(t, err) +// nm.namespaces = map[string]*namespace{ +// "default": {}, +// } - err = nm.loadNamespaces(context.Background()) - assert.Regexp(t, "FF10166", err) -} +// results, err := nm.GetNamespaces(context.Background()) +// assert.Nil(t, err) +// assert.Len(t, results, 1) +// } -func TestLoadNamespacesUseDefaults(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// func TestGetOperationByNamespacedID(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - multiparty: - contract: - - location: - address: 0x1234 - org: - name: org1 - node: - name: node1 - `)) - assert.NoError(t, err) +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "default": {orchestrator: mo}, +// } - err = nm.loadNamespaces(context.Background()) - assert.NoError(t, err) - assert.Len(t, nm.namespaces, 1) - assert.Equal(t, "oldest", nm.namespaces["ns1"].config.Multiparty.Contracts[0].FirstEvent) -} +// opID := fftypes.NewUUID() +// mo.On("GetOperationByID", context.Background(), opID.String()).Return(nil, nil) -func TestLoadNamespacesNonMultipartyNoDatabase(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// op, err := nm.GetOperationByNamespacedID(context.Background(), "default:"+opID.String()) +// assert.Nil(t, err) +// assert.Nil(t, op) - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [] - `)) - assert.NoError(t, err) +// mo.AssertExpectations(t) +// } - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// func TestGetOperationByNamespacedIDBadID(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10392", err) -} +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "default": {orchestrator: mo}, +// } -func TestLoadNamespacesMultipartyUnknownPlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// _, err := nm.GetOperationByNamespacedID(context.Background(), "default:bad") +// assert.Regexp(t, "FF00138", err) - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [basicauth, bad] - multiparty: - enabled: true - `)) - assert.NoError(t, err) +// mo.AssertExpectations(t) +// } - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// func TestGetOperationByNamespacedIDNoOrchestrator(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10390.*unknown", err) -} +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "default": {orchestrator: mo}, +// } + +// opID := fftypes.NewUUID() -func TestLoadNamespacesMultipartyMultipleBlockchains(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) +// _, err := nm.GetOperationByNamespacedID(context.Background(), "bad:"+opID.String()) +// assert.Regexp(t, "FF10109", err) - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [ethereum, ethereum] - multiparty: - enabled: true - `)) - assert.NoError(t, err) +// mo.AssertExpectations(t) +// } - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) +// func TestResolveOperationByNamespacedID(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// mo := &orchestratormocks.Orchestrator{} +// mom := &operationmocks.Manager{} +// nm.namespaces = map[string]*namespace{ +// "default": {orchestrator: mo}, +// } - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10394.*blockchain", err) -} - -func TestLoadNamespacesMultipartyMultipleDX(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [ffdx, ffdx] - multiparty: - enabled: true - `)) - assert.NoError(t, err) - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10394.*dataexchange", err) -} - -func TestLoadNamespacesMultipartyMultipleSS(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [ipfs, ipfs] - multiparty: - enabled: true - `)) - assert.NoError(t, err) - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10394.*sharedstorage", err) -} - -func TestLoadNamespacesMultipartyMultipleDB(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [postgres, postgres] - multiparty: - enabled: true - `)) - assert.NoError(t, err) - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10394.*database", err) -} - -func TestInitNamespacesMultipartyWithAuth(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [ethereum, postgres, ipfs, ffdx, basicauth] - multiparty: - enabled: true - `)) - assert.NoError(t, err) - - err = nm.loadNamespaces(context.Background()) - assert.NoError(t, err) -} - -func TestLoadNamespacesNonMultipartyWithAuth(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [ethereum, postgres, basicauth] - multiparty: - enabled: false - `)) - assert.NoError(t, err) - - err = nm.loadNamespaces(context.Background()) - assert.NoError(t, err) -} - -func TestLoadNamespacesMultipartyContract(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - multiparty: - enabled: true - contract: - - location: - address: 0x4ae50189462b0e5d52285f59929d037f790771a6 - firstEvent: oldest - `)) - assert.NoError(t, err) - - err = nm.loadNamespaces(context.Background()) - assert.NoError(t, err) -} - -func TestLoadNamespacesMultipartyContractBadLocation(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - fail := make(chan string) - namespaceConfig.AddKnownKey("predefined.0.multiparty.contract.0.location.address", fail) - - err := nm.loadNamespaces(context.Background()) - assert.Regexp(t, "json:", err) -} - -func TestLoadNamespacesNonMultipartyMultipleDB(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [postgres, postgres] - `)) - assert.NoError(t, err) - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10394.*database", err) -} - -func TestLoadNamespacesNonMultipartyMultipleBlockchains(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [ethereum, ethereum] - `)) - assert.NoError(t, err) - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10394.*blockchain", err) -} - -func TestLoadNamespacesMultipartyMissingPlugins(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [postgres] - multiparty: - enabled: true - `)) - assert.NoError(t, err) - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10391", err) -} - -func TestLoadNamespacesNonMultipartyUnknownPlugin(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [basicauth, erc721, bad] - `)) - assert.NoError(t, err) - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) - assert.Regexp(t, "FF10390.*unknown", err) -} - -func TestLoadNamespacesNonMultipartyTokens(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [postgres, erc721] - `)) - assert.NoError(t, err) - - err = nm.loadNamespaces(context.Background()) - assert.NoError(t, err) -} - -func TestStart(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "ns": {orchestrator: mo}, - } - nm.plugins.blockchain = nil - nm.plugins.dataexchange = nil - nm.plugins.tokens = nil - nm.metricsEnabled = true - - mo.On("Start", mock.Anything).Return(nil) - - err := nm.Start() - assert.NoError(t, err) - - mo.AssertExpectations(t) -} - -func TestStartBlockchainFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "ns": { - orchestrator: mo, - }, - } - - mo.On("Start", mock.Anything).Return(nil) - nm.mbi.On("Start").Return(fmt.Errorf("pop")) - - err := nm.Start() - assert.EqualError(t, err, "pop") - - mo.AssertExpectations(t) - -} - -func TestStartDataExchangeFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "ns": { - orchestrator: mo, - }, - } - nm.plugins.blockchain = nil - - mo.On("Start", mock.Anything).Return(nil) - nm.mdx.On("Start").Return(fmt.Errorf("pop")) - - err := nm.Start() - assert.EqualError(t, err, "pop") - - mo.AssertExpectations(t) - -} - -func TestStartTokensFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "ns": { - orchestrator: mo, - }, - } - nm.plugins.blockchain = nil - nm.plugins.dataexchange = nil - - mo.On("Start", mock.Anything).Return(nil) - nm.mti.On("Start").Return(fmt.Errorf("pop")) - - err := nm.Start() - assert.EqualError(t, err, "pop") - -} - -func TestStartOrchestratorFail(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "ns": {orchestrator: mo}, - } - - mo.On("Start", mock.Anything).Return(fmt.Errorf("pop")) - - err := nm.Start() - assert.EqualError(t, err, "pop") - - mo.AssertExpectations(t) -} - -func TestWaitStop(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "ns": {orchestrator: mo}, - } - mae := nm.adminEvents.(*spieventsmocks.Manager) - - mo.On("WaitStop").Return() - mae.On("WaitStop").Return() - - nm.WaitStop() - - mo.AssertExpectations(t) - mae.AssertExpectations(t) -} - -func TestReset(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - childCtx, childCancel := context.WithCancel(context.Background()) - nm.Reset(childCtx) - childCancel() - - assert.True(t, <-nm.namespaceManager.reset) -} - -func TestLoadMetrics(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - nm.metrics = nil - - err := nm.loadPlugins(context.Background()) - assert.NoError(t, err) -} - -func TestLoadAdminEvents(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - nm.adminEvents = nil - - err := nm.loadPlugins(context.Background()) - assert.NoError(t, err) - assert.NotNil(t, nm.SPIEvents()) -} - -func TestGetNamespaces(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - nm.namespaces = map[string]*namespace{ - "default": {}, - } - - results, err := nm.GetNamespaces(context.Background()) - assert.Nil(t, err) - assert.Len(t, results, 1) -} - -func TestGetOperationByNamespacedID(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "default": {orchestrator: mo}, - } - - opID := fftypes.NewUUID() - mo.On("GetOperationByID", context.Background(), opID.String()).Return(nil, nil) - - op, err := nm.GetOperationByNamespacedID(context.Background(), "default:"+opID.String()) - assert.Nil(t, err) - assert.Nil(t, op) - - mo.AssertExpectations(t) -} - -func TestGetOperationByNamespacedIDBadID(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "default": {orchestrator: mo}, - } - - _, err := nm.GetOperationByNamespacedID(context.Background(), "default:bad") - assert.Regexp(t, "FF00138", err) - - mo.AssertExpectations(t) -} - -func TestGetOperationByNamespacedIDNoOrchestrator(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "default": {orchestrator: mo}, - } - - opID := fftypes.NewUUID() - - _, err := nm.GetOperationByNamespacedID(context.Background(), "bad:"+opID.String()) - assert.Regexp(t, "FF10109", err) - - mo.AssertExpectations(t) -} - -func TestResolveOperationByNamespacedID(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - mom := &operationmocks.Manager{} - nm.namespaces = map[string]*namespace{ - "default": {orchestrator: mo}, - } - - opID := fftypes.NewUUID() - mo.On("Operations").Return(mom) - mom.On("ResolveOperationByID", context.Background(), opID, mock.Anything).Return(nil) - - err := nm.ResolveOperationByNamespacedID(context.Background(), "default:"+opID.String(), &core.OperationUpdateDTO{}) - assert.Nil(t, err) - - mo.AssertExpectations(t) - mom.AssertExpectations(t) -} - -func TestResolveOperationByNamespacedIDBadID(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "default": {orchestrator: mo}, - } - - err := nm.ResolveOperationByNamespacedID(context.Background(), "default:bad", &core.OperationUpdateDTO{}) - assert.Regexp(t, "FF00138", err) - - mo.AssertExpectations(t) -} - -func TestResolveOperationByNamespacedIDNoOrchestrator(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - mo := &orchestratormocks.Orchestrator{} - nm.namespaces = map[string]*namespace{ - "default": {orchestrator: mo}, - } - - opID := fftypes.NewUUID() - - err := nm.ResolveOperationByNamespacedID(context.Background(), "bad:"+opID.String(), &core.OperationUpdateDTO{}) - assert.Regexp(t, "FF10109", err) - - mo.AssertExpectations(t) -} - -func TestAuthorize(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - mo := &orchestratormocks.Orchestrator{} - mo.On("Authorize", mock.Anything, mock.Anything).Return(nil) - nm.namespaces["ns1"] = &namespace{ - orchestrator: mo, - } - nm.utOrchestrator = mo - err := nm.Authorize(context.Background(), &fftypes.AuthReq{ - Namespace: "ns1", - }) - assert.NoError(t, err) - - mo.AssertExpectations(t) -} - -func TestValidateNonMultipartyConfig(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - viper.SetConfigType("yaml") - err := viper.ReadConfig(strings.NewReader(` - namespaces: - default: ns1 - predefined: - - name: ns1 - plugins: [postgres, erc721] - `)) - assert.NoError(t, err) - - _, err = nm.validateNonMultipartyConfig(context.Background(), "ns1", []string{"postgres", "erc721"}) - assert.NoError(t, err) -} +// opID := fftypes.NewUUID() +// mo.On("Operations").Return(mom) +// mom.On("ResolveOperationByID", context.Background(), opID, mock.Anything).Return(nil) + +// err := nm.ResolveOperationByNamespacedID(context.Background(), "default:"+opID.String(), &core.OperationUpdateDTO{}) +// assert.Nil(t, err) + +// mo.AssertExpectations(t) +// mom.AssertExpectations(t) +// } + +// func TestResolveOperationByNamespacedIDBadID(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "default": {orchestrator: mo}, +// } + +// err := nm.ResolveOperationByNamespacedID(context.Background(), "default:bad", &core.OperationUpdateDTO{}) +// assert.Regexp(t, "FF00138", err) + +// mo.AssertExpectations(t) +// } + +// func TestResolveOperationByNamespacedIDNoOrchestrator(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// mo := &orchestratormocks.Orchestrator{} +// nm.namespaces = map[string]*namespace{ +// "default": {orchestrator: mo}, +// } + +// opID := fftypes.NewUUID() + +// err := nm.ResolveOperationByNamespacedID(context.Background(), "bad:"+opID.String(), &core.OperationUpdateDTO{}) +// assert.Regexp(t, "FF10109", err) + +// mo.AssertExpectations(t) +// } + +// func TestAuthorize(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() +// mo := &orchestratormocks.Orchestrator{} +// mo.On("Authorize", mock.Anything, mock.Anything).Return(nil) +// nm.namespaces["ns1"] = &namespace{ +// orchestrator: mo, +// } +// nm.utOrchestrator = mo +// err := nm.Authorize(context.Background(), &fftypes.AuthReq{ +// Namespace: "ns1", +// }) +// assert.NoError(t, err) + +// mo.AssertExpectations(t) +// } + +// func TestValidateNonMultipartyConfig(t *testing.T) { +// nm, cleanup := newTestNamespaceManager(t, true) +// defer cleanup() + +// viper.SetConfigType("yaml") +// err := viper.ReadConfig(strings.NewReader(` +// namespaces: +// default: ns1 +// predefined: +// - name: ns1 +// plugins: [postgres, erc721] +// `)) +// assert.NoError(t, err) + +// _, err = nm.validateNonMultipartyConfig(context.Background(), "ns1", []string{"postgres", "erc721"}) +// assert.NoError(t, err) +// } diff --git a/mocks/namespacemocks/manager.go b/mocks/namespacemocks/manager.go index 107c5782d..bc94d807e 100644 --- a/mocks/namespacemocks/manager.go +++ b/mocks/namespacemocks/manager.go @@ -111,8 +111,17 @@ func (_m *Manager) Orchestrator(ns string) orchestrator.Orchestrator { } // Reset provides a mock function with given fields: ctx -func (_m *Manager) Reset(ctx context.Context) { - _m.Called(ctx) +func (_m *Manager) Reset(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 } // ResolveOperationByNamespacedID provides a mock function with given fields: ctx, nsOpID, op From ed0f74ee0c156cde362d83b9afdc4c1b28ebefe4 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 8 Dec 2022 01:17:32 -0500 Subject: [PATCH 02/11] Original tests re-instated and passing with dynamic config reload Signed-off-by: Peter Broadhurst --- cmd/firefly.go | 2 +- docs/config_docs_generate_test.go | 2 +- docs/config_docs_test.go | 2 +- docs/reference/config.md | 6 + go.mod | 2 +- .../apiserver/route_spi_post_reset_test.go | 2 +- internal/coremsgs/en_config_descriptions.go | 2 + internal/namespace/config.go | 8 +- internal/namespace/manager.go | 210 +- internal/namespace/manager_test.go | 2851 ++++++++--------- 10 files changed, 1446 insertions(+), 1641 deletions(-) diff --git a/cmd/firefly.go b/cmd/firefly.go index bd33ae8bf..903ab81be 100644 --- a/cmd/firefly.go +++ b/cmd/firefly.go @@ -99,7 +99,7 @@ func getRootManager() namespace.Manager { if _utManager != nil { return _utManager } - return namespace.NewNamespaceManager(true) + return namespace.NewNamespaceManager() } // Execute is called by the main method of the package diff --git a/docs/config_docs_generate_test.go b/docs/config_docs_generate_test.go index f414a9179..fd1cb09c0 100644 --- a/docs/config_docs_generate_test.go +++ b/docs/config_docs_generate_test.go @@ -33,7 +33,7 @@ import ( func TestGenerateConfigDocs(t *testing.T) { // Initialize config of all plugins - namespace.NewNamespaceManager(false) + namespace.NewNamespaceManager() apiserver.InitConfig() f, err := os.Create(filepath.Join("reference", "config.md")) assert.NoError(t, err) diff --git a/docs/config_docs_test.go b/docs/config_docs_test.go index 398328033..f99539b42 100644 --- a/docs/config_docs_test.go +++ b/docs/config_docs_test.go @@ -34,7 +34,7 @@ import ( func TestConfigDocsUpToDate(t *testing.T) { // Initialize config of all plugins - namespace.NewNamespaceManager(false) + namespace.NewNamespaceManager() apiserver.InitConfig() generatedConfig, err := config.GenerateConfigMarkdown(context.Background(), configDocHeader, config.GetKnownKeys()) assert.NoError(t, err) diff --git a/docs/reference/config.md b/docs/reference/config.md index 8e3739bbb..150ca3c9b 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -355,6 +355,12 @@ nav_order: 2 |size|Max size of cached validators for data manager|[`BytesSize`](https://pkg.go.dev/github.com/docker/go-units#BytesSize)|`` |ttl|Time to live of cached validators for data manager|`string`|`` +## config + +|Key|Description|Type|Default Value| +|---|-----------|----|-------------| +|autoReload|Monitor the configuration file for changes, and automatically add/remove/reload namespaces and plugins|`boolean`|`` + ## cors |Key|Description|Type|Default Value| diff --git a/go.mod b/go.mod index 6cf739a49..0d0aec7d7 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/aidarkhanov/nanoid v1.0.8 github.com/blang/semver/v4 v4.0.0 github.com/docker/go-units v0.5.0 + github.com/fsnotify/fsnotify v1.6.0 github.com/getkin/kin-openapi v0.107.0 github.com/ghodss/yaml v1.0.0 github.com/go-resty/resty/v2 v2.7.0 @@ -38,7 +39,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/golang/protobuf v1.5.2 // indirect diff --git a/internal/apiserver/route_spi_post_reset_test.go b/internal/apiserver/route_spi_post_reset_test.go index 781a54e47..696654894 100644 --- a/internal/apiserver/route_spi_post_reset_test.go +++ b/internal/apiserver/route_spi_post_reset_test.go @@ -32,7 +32,7 @@ func TestAdminPostResetConfig(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mgr.On("Reset", mock.Anything).Return() + mgr.On("Reset", mock.Anything).Return(nil) r.ServeHTTP(res, req) assert.Equal(t, 204, res.Result().StatusCode) diff --git a/internal/coremsgs/en_config_descriptions.go b/internal/coremsgs/en_config_descriptions.go index 8f0927b64..b5a78466f 100644 --- a/internal/coremsgs/en_config_descriptions.go +++ b/internal/coremsgs/en_config_descriptions.go @@ -31,6 +31,8 @@ var ( ConfigGlobalMigrationsDirectory = ffc("config.global.migrations.directory", "The directory containing the numerically ordered migration DDL files to apply to the database", i18n.StringType) ConfigGlobalShutdownTimeout = ffc("config.global.shutdownTimeout", "The maximum amount of time to wait for any open HTTP requests to finish before shutting down the HTTP server", i18n.TimeDurationType) + ConfigConfigAutoReload = ffc("config.config.autoReload", "Monitor the configuration file for changes, and automatically add/remove/reload namespaces and plugins", i18n.BooleanType) + ConfigLegacyAdmin = ffc("config.admin.enabled", "Deprecated - use spi.enabled instead", i18n.BooleanType) ConfigSPIAddress = ffc("config.spi.address", "The IP address on which the admin HTTP API should listen", "IP Address "+i18n.StringType) ConfigSPIEnabled = ffc("config.spi.enabled", "Enables the admin HTTP API", i18n.BooleanType) diff --git a/internal/namespace/config.go b/internal/namespace/config.go index e8bbcae26..976c74fcc 100644 --- a/internal/namespace/config.go +++ b/internal/namespace/config.go @@ -33,7 +33,7 @@ var ( namespacePredefined = namespaceConfigSection.SubArray(NamespacePredefined) ) -func InitConfig(withDefaults bool) { +func InitConfig() { namespacePredefined.AddKnownKey(coreconfig.NamespaceName) namespacePredefined.AddKnownKey(coreconfig.NamespaceDescription) namespacePredefined.AddKnownKey(coreconfig.NamespacePlugins) @@ -52,10 +52,4 @@ func InitConfig(withDefaults bool) { contractConf := multipartyConf.SubArray(coreconfig.NamespaceMultipartyContract) contractConf.AddKnownKey(coreconfig.NamespaceMultipartyContractFirstEvent, string(core.SubOptsFirstEventOldest)) contractConf.AddKnownKey(coreconfig.NamespaceMultipartyContractLocation) - - if withDefaults { - namespaceConfigSection.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceName, "default") - namespaceConfigSection.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceDescription, "Default predefined namespace") - namespaceConfigSection.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceAssetKeyNormalization, "blockchain_plugin") - } } diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go index f6ba6447e..ea4d7325e 100644 --- a/internal/namespace/manager.go +++ b/internal/namespace/manager.go @@ -90,12 +90,14 @@ type Manager interface { type namespace struct { core.Namespace + ctx context.Context + cancelCtx context.CancelFunc orchestrator orchestrator.Orchestrator loadTime fftypes.FFTime config orchestrator.Config configHash *fftypes.Bytes32 - plugins []string - cancelCtx context.CancelFunc + pluginNames []string + plugins *orchestrator.Plugins } type namespaceManager struct { @@ -159,15 +161,20 @@ func stringSlicesEqual(a, b []string) bool { return true } -func NewNamespaceManager(withDefaults bool) Manager { +func NewNamespaceManager() Manager { nm := &namespaceManager{ namespaces: make(map[string]*namespace), metricsEnabled: config.GetBool(coreconfig.MetricsEnabled), tokenBroadcastNames: make(map[string]string), watchConfig: viper.WatchConfig, } + initAllConfig() + return nm +} - InitConfig(withDefaults) +func initAllConfig() { + + InitConfig() // Initialize the config on all the factories bifactory.InitConfigDeprecated(deprecatedBlockchainConfig) @@ -185,8 +192,6 @@ func NewNamespaceManager(withDefaults bool) Manager { // Events still live at the root of the config eifactory.InitConfig(config.RootSection("events")) - - return nm } func (nm *namespaceManager) startConfigListener(ctx context.Context) { @@ -217,13 +222,20 @@ func (nm *namespaceManager) newConfigChangeListener(ctx context.Context) func(in } } +func (nm *namespaceManager) dumpRootConfig() (jsonTree fftypes.JSONObject) { + viperTree := viper.AllSettings() + b, _ := json.Marshal(viperTree) + _ = json.Unmarshal(b, &jsonTree) + return +} + func (nm *namespaceManager) configFileChanged(ctx context.Context, filename string) { log.L(ctx).Infof("Detected configuration file reload: '%s'", filename) // Get Viper to dump the whole new config, with everything resolved across env vars // and the config file etc. // We use this to detect if anything has changed. - rawConfig := viper.AllSettings() + rawConfig := nm.dumpRootConfig() newPlugins, err := nm.loadPlugins(ctx, rawConfig) if err != nil { @@ -231,7 +243,7 @@ func (nm *namespaceManager) configFileChanged(ctx context.Context, filename stri return } - allNewNamespaces, err := nm.loadNamespaces(ctx, rawConfig) + allNewNamespaces, err := nm.loadNamespaces(ctx, rawConfig, newPlugins) if err != nil { log.L(ctx).Errorf("Failed to load namespaces after config reload: %s", err) return @@ -292,8 +304,8 @@ func (nm *namespaceManager) stopDefunctNamespaces(ctx context.Context, newPlugin for nsName, newNS := range newNamespaces { if existingNS := nm.namespaces[nsName]; existingNS != nil { unchanged := existingNS.configHash.Equals(newNS.configHash) && - len(existingNS.plugins) == len(newNS.plugins) - for _, pluginName := range newNS.plugins { + len(existingNS.pluginNames) == len(newNS.pluginNames) + for _, pluginName := range newNS.pluginNames { existingPlugin := nm.plugins[pluginName] newPlugin := newPlugins[pluginName] unchanged = existingPlugin != nil && newPlugin != nil && @@ -355,15 +367,19 @@ func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFu nm.reset = reset nm.cancelCtx = cancelCtx - initTimeRawConfig := viper.AllSettings() + initTimeRawConfig := nm.dumpRootConfig() nm.loadManagers(ctx) if nm.plugins, err = nm.loadPlugins(ctx, initTimeRawConfig); err != nil { return err } - if nm.namespaces, err = nm.loadNamespaces(ctx, initTimeRawConfig); err != nil { + if nm.namespaces, err = nm.loadNamespaces(ctx, initTimeRawConfig, nm.plugins); err != nil { return err } + return nm.initComponents(ctx) +} + +func (nm *namespaceManager) initComponents(ctx context.Context) (err error) { // Initialize all the plugins on initial startup if err = nm.initPlugins(ctx, nm.plugins); err != nil { return err @@ -411,7 +427,7 @@ func (nm *namespaceManager) initNamespaces(ctx context.Context, newNamespaces ma if v1Namespace == nil { v1Namespace = ns v1Contract = contract - } else if !stringSlicesEqual(v1Namespace.plugins, ns.plugins) || + } else if !stringSlicesEqual(v1Namespace.pluginNames, ns.pluginNames) || v1Contract.Location.String() != contract.Location.String() || v1Contract.FirstEvent != contract.FirstEvent { return i18n.NewError(ctx, coremsgs.MsgCannotInitLegacyNS, core.LegacySystemNamespace, v1Namespace.Name, ns.Name) @@ -422,13 +438,15 @@ func (nm *namespaceManager) initNamespaces(ctx context.Context, newNamespaces ma if v1Namespace != nil { systemNS := &namespace{ - Namespace: v1Namespace.Namespace, - config: v1Namespace.config, - plugins: v1Namespace.plugins, - configHash: v1Namespace.configHash, + Namespace: v1Namespace.Namespace, + config: v1Namespace.config, + pluginNames: v1Namespace.pluginNames, + plugins: v1Namespace.plugins, + configHash: v1Namespace.configHash, } systemNS.Name = core.LegacySystemNamespace systemNS.NetworkName = core.LegacySystemNamespace + newNamespaces[core.LegacySystemNamespace] = systemNS if err := nm.initNamespace(ctx, systemNS); err != nil { return err } @@ -452,17 +470,7 @@ func (nm *namespaceManager) findV1Contract(ns *namespace) *core.MultipartyContra func (nm *namespaceManager) initNamespace(ctx context.Context, ns *namespace) (err error) { - var plugins *orchestrator.Plugins - if ns.config.Multiparty.Enabled { - plugins, err = nm.validateMultiPartyConfig(ctx, ns.Name, ns.plugins) - } else { - plugins, err = nm.validateNonMultipartyConfig(ctx, ns.Name, ns.plugins) - } - if err != nil { - return err - } - - database := plugins.Database.Plugin + database := ns.plugins.Database.Plugin existing, err := database.GetNamespace(ctx, ns.Name) switch { case err != nil: @@ -485,21 +493,23 @@ func (nm *namespaceManager) initNamespace(ctx context.Context, ns *namespace) (e or := nm.utOrchestrator if or == nil { - or = orchestrator.NewOrchestrator(&ns.Namespace, ns.config, plugins, nm.metrics, nm.cacheManager) + or = orchestrator.NewOrchestrator(&ns.Namespace, ns.config, ns.plugins, nm.metrics, nm.cacheManager) } ns.orchestrator = or - orCtx, orCancel := context.WithCancel(ctx) - if err := or.Init(orCtx, orCancel); err != nil { + ns.ctx, ns.cancelCtx = context.WithCancel(ctx) + if err := or.Init(ns.ctx, ns.cancelCtx); err != nil { return err } return nil } func (nm *namespaceManager) stopNamespace(ctx context.Context, ns *namespace) { - log.L(ctx).Infof("Requesting stop of namespace '%s'", ns.Name) - ns.cancelCtx() - ns.orchestrator.WaitStop() - log.L(ctx).Infof("Namespace '%s' stopped", ns.Name) + if ns.cancelCtx != nil { + log.L(ctx).Infof("Requesting stop of namespace '%s'", ns.Name) + ns.cancelCtx() + ns.orchestrator.WaitStop() + log.L(ctx).Infof("Namespace '%s' stopped", ns.Name) + } } func (nm *namespaceManager) Start() error { @@ -542,7 +552,7 @@ func (nm *namespaceManager) WaitStop() { nm.nsMux.Unlock() for _, ns := range namespaces { - ns.orchestrator.WaitStop() + nm.stopNamespace(nm.ctx, ns) } nm.adminEvents.WaitStop() } @@ -957,7 +967,7 @@ func (nm *namespaceManager) initPlugins(ctx context.Context, pluginsToStart map[ return nil } -func (nm *namespaceManager) loadNamespaces(ctx context.Context, rawConfig fftypes.JSONObject) (newNS map[string]*namespace, err error) { +func (nm *namespaceManager) loadNamespaces(ctx context.Context, rawConfig fftypes.JSONObject, availablePlugins map[string]*plugin) (newNS map[string]*namespace, err error) { defaultName := config.GetString(coreconfig.NamespacesDefault) size := namespacePredefined.ArraySize() rawPredefinedNSConfig := rawConfig.GetObject("namespaces").GetObjectArray("predefined") @@ -980,7 +990,7 @@ func (nm *namespaceManager) loadNamespaces(ctx context.Context, rawConfig fftype continue } foundDefault = foundDefault || name == defaultName - if newNS[name], err = nm.loadNamespace(ctx, name, i, nsConfig, rawPredefinedNSConfig[i]); err != nil { + if newNS[name], err = nm.loadNamespace(ctx, name, i, nsConfig, rawPredefinedNSConfig[i], availablePlugins); err != nil { return nil, err } } @@ -992,7 +1002,7 @@ func (nm *namespaceManager) loadNamespaces(ctx context.Context, rawConfig fftype return newNS, err } -func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, index int, conf config.Section, rawNSConfig fftypes.JSONObject) (*namespace, error) { +func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, index int, conf config.Section, rawNSConfig fftypes.JSONObject, availablePlugins map[string]*plugin) (ns *namespace, err error) { if err := fftypes.ValidateFFNameField(ctx, name, fmt.Sprintf("namespaces.predefined[%d].name", index)); err != nil { return nil, err } @@ -1057,10 +1067,20 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde // If no plugins are listed under this namespace, use all defined plugins by default pluginsRaw := conf.Get(coreconfig.NamespacePlugins) - plugins := conf.GetStringSlice(coreconfig.NamespacePlugins) + pluginNames := conf.GetStringSlice(coreconfig.NamespacePlugins) if pluginsRaw == nil { for pluginName := range nm.plugins { - plugins = append(plugins, pluginName) + p := availablePlugins[pluginName] + switch p.category { + case pluginCategoryBlockchain, + pluginCategoryDatabase, + pluginCategoryDataexchange, + pluginCategoryIdentity, + pluginCategorySharedstorage, + pluginCategoryTokens, + pluginCategoryAuth: + pluginNames = append(pluginNames, pluginName) + } } } @@ -1076,12 +1096,7 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde for i := 0; i < contractConfArraySize; i++ { conf := contractsConf.ArrayEntry(i) - locationObject := conf.GetObject(coreconfig.NamespaceMultipartyContractLocation) - b, err := json.Marshal(locationObject) - if err != nil { - return nil, err - } - location := fftypes.JSONAnyPtrBytes(b) + location := fftypes.JSONAnyPtr(conf.GetObject(coreconfig.NamespaceMultipartyContractLocation).String()) contract := multiparty.Contract{ Location: location, FirstEvent: conf.GetString(coreconfig.NamespaceMultipartyContractFirstEvent), @@ -1098,29 +1113,51 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde config.Multiparty.Node.Description = nodeDesc } - return &namespace{ + ns = &namespace{ Namespace: core.Namespace{ Name: name, NetworkName: networkName, Description: conf.GetString(coreconfig.NamespaceDescription), }, - config: config, - configHash: nm.configHash(rawNSConfig), - plugins: plugins, - }, nil + config: config, + configHash: nm.configHash(rawNSConfig), + pluginNames: pluginNames, + } + + if ns.plugins, err = nm.validateNSPlugins(ctx, ns, availablePlugins); err != nil { + return nil, err + } + + if ns.config.Multiparty.Enabled { + err = nm.validateMultiPartyConfig(ctx, ns) + } else { + err = nm.validateNonMultipartyConfig(ctx, ns) + } + if err != nil { + return nil, err + } + + ns.plugins.Events = make(map[string]events.Plugin) + for name, p := range nm.plugins { + if p.category == pluginCategoryEvents { + ns.plugins.Events[name] = p.events + } + } + + return ns, nil } -func (nm *namespaceManager) validatePlugins(ctx context.Context, name string, plugins []string) (*orchestrator.Plugins, error) { +func (nm *namespaceManager) validateNSPlugins(ctx context.Context, ns *namespace, availablePlugins map[string]*plugin) (*orchestrator.Plugins, error) { var result orchestrator.Plugins - for _, pluginName := range plugins { - p := nm.plugins[pluginName] + for _, pluginName := range ns.pluginNames { + p := availablePlugins[pluginName] if p == nil { - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceUnknownPlugin, name, pluginName) + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceUnknownPlugin, ns.Name, pluginName) } switch p.category { case pluginCategoryBlockchain: if result.Blockchain.Plugin != nil { - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "blockchain") + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, ns.Name, "blockchain") } result.Blockchain = orchestrator.BlockchainPlugin{ Name: pluginName, @@ -1128,7 +1165,7 @@ func (nm *namespaceManager) validatePlugins(ctx context.Context, name string, pl } case pluginCategoryDataexchange: if result.DataExchange.Plugin != nil { - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "dataexchange") + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, ns.Name, "dataexchange") } result.DataExchange = orchestrator.DataExchangePlugin{ Name: pluginName, @@ -1136,7 +1173,7 @@ func (nm *namespaceManager) validatePlugins(ctx context.Context, name string, pl } case pluginCategorySharedstorage: if result.SharedStorage.Plugin != nil { - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "sharedstorage") + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, ns.Name, "sharedstorage") } result.SharedStorage = orchestrator.SharedStoragePlugin{ Name: pluginName, @@ -1144,7 +1181,7 @@ func (nm *namespaceManager) validatePlugins(ctx context.Context, name string, pl } case pluginCategoryDatabase: if result.Database.Plugin != nil { - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "database") + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, ns.Name, "database") } result.Database = orchestrator.DatabasePlugin{ Name: pluginName, @@ -1157,7 +1194,7 @@ func (nm *namespaceManager) validatePlugins(ctx context.Context, name string, pl }) case pluginCategoryIdentity: if result.Identity.Plugin != nil { - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "identity") + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, ns.Name, "identity") } result.Identity = orchestrator.IdentityPlugin{ Name: pluginName, @@ -1165,61 +1202,38 @@ func (nm *namespaceManager) validatePlugins(ctx context.Context, name string, pl } case pluginCategoryAuth: if result.Auth.Plugin != nil { - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, name, "auth") + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceMultiplePluginType, ns.Name, "auth") } result.Auth = orchestrator.AuthPlugin{ Name: pluginName, Plugin: p.auth, } - continue + default: + return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceUnknownPlugin, ns.Name, pluginName) } - - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceUnknownPlugin, name, pluginName) } return &result, nil } -func (nm *namespaceManager) validateMultiPartyConfig(ctx context.Context, name string, plugins []string) (*orchestrator.Plugins, error) { +func (nm *namespaceManager) validateMultiPartyConfig(ctx context.Context, ns *namespace) error { - result, err := nm.validatePlugins(ctx, name, plugins) - if err != nil { - return nil, err + if ns.plugins.Database.Plugin == nil || + ns.plugins.SharedStorage.Plugin == nil || + ns.plugins.DataExchange.Plugin == nil || + ns.plugins.Blockchain.Plugin == nil { + return i18n.NewError(ctx, coremsgs.MsgNamespaceWrongPluginsMultiparty, ns.Name) } - if result.Database.Plugin == nil || - result.SharedStorage.Plugin == nil || - result.DataExchange.Plugin == nil || - result.Blockchain.Plugin == nil { - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceWrongPluginsMultiparty, name) - } - - result.Events = make(map[string]events.Plugin) - for name, p := range nm.plugins { - if p.category == pluginCategoryEvents { - result.Events[name] = p.events - } - } - return result, nil + return nil } -func (nm *namespaceManager) validateNonMultipartyConfig(ctx context.Context, name string, plugins []string) (*orchestrator.Plugins, error) { +func (nm *namespaceManager) validateNonMultipartyConfig(ctx context.Context, ns *namespace) error { - result, err := nm.validatePlugins(ctx, name, plugins) - if err != nil { - return nil, err + if ns.plugins.Database.Plugin == nil { + return i18n.NewError(ctx, coremsgs.MsgNamespaceNoDatabase, ns.Name) } - if result.Database.Plugin == nil { - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceNoDatabase, name) - } - - result.Events = make(map[string]events.Plugin) - for name, p := range nm.plugins { - if p.category == pluginCategoryEvents { - result.Events[name] = p.events - } - } - return result, nil + return nil } func (nm *namespaceManager) SPIEvents() spievents.Manager { diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go index 24beeb27c..0384613a3 100644 --- a/internal/namespace/manager_test.go +++ b/internal/namespace/manager_test.go @@ -23,9 +23,17 @@ import ( "testing" "github.com/hyperledger/firefly-common/mocks/authmocks" + "github.com/hyperledger/firefly-common/pkg/auth/authfactory" + "github.com/hyperledger/firefly-common/pkg/config" "github.com/hyperledger/firefly-common/pkg/fftypes" + "github.com/hyperledger/firefly/internal/blockchain/bifactory" "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/internal/database/difactory" + "github.com/hyperledger/firefly/internal/dataexchange/dxfactory" + "github.com/hyperledger/firefly/internal/identity/iifactory" + "github.com/hyperledger/firefly/internal/orchestrator" + "github.com/hyperledger/firefly/internal/sharedstorage/ssfactory" + "github.com/hyperledger/firefly/internal/tokens/tifactory" "github.com/hyperledger/firefly/mocks/blockchainmocks" "github.com/hyperledger/firefly/mocks/cachemocks" "github.com/hyperledger/firefly/mocks/databasemocks" @@ -33,12 +41,14 @@ import ( "github.com/hyperledger/firefly/mocks/eventsmocks" "github.com/hyperledger/firefly/mocks/identitymocks" "github.com/hyperledger/firefly/mocks/metricsmocks" + "github.com/hyperledger/firefly/mocks/operationmocks" "github.com/hyperledger/firefly/mocks/orchestratormocks" "github.com/hyperledger/firefly/mocks/sharedstoragemocks" "github.com/hyperledger/firefly/mocks/spieventsmocks" "github.com/hyperledger/firefly/mocks/tokenmocks" "github.com/hyperledger/firefly/pkg/core" "github.com/hyperledger/firefly/pkg/database" + "github.com/hyperledger/firefly/pkg/tokens" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -46,16 +56,17 @@ import ( type testNamespaceManager struct { namespaceManager - mmi *metricsmocks.Manager - mae *spieventsmocks.Manager - mbi *blockchainmocks.Plugin - cmi *cachemocks.Manager - mdi *databasemocks.Plugin - mdx *dataexchangemocks.Plugin - mps *sharedstoragemocks.Plugin - mti *tokenmocks.Plugin - mev *eventsmocks.Plugin - auth *authmocks.Plugin + mmi *metricsmocks.Manager + mae *spieventsmocks.Manager + mbi *blockchainmocks.Plugin + cmi *cachemocks.Manager + mdi *databasemocks.Plugin + mdx *dataexchangemocks.Plugin + mps *sharedstoragemocks.Plugin + mti *tokenmocks.Plugin + mev *eventsmocks.Plugin + mai *authmocks.Plugin + mo *orchestratormocks.Orchestrator } func (nm *testNamespaceManager) cleanup(t *testing.T) { @@ -67,34 +78,36 @@ func (nm *testNamespaceManager) cleanup(t *testing.T) { nm.mdx.AssertExpectations(t) nm.mps.AssertExpectations(t) nm.mti.AssertExpectations(t) - nm.auth.AssertExpectations(t) + nm.mai.AssertExpectations(t) + nm.mo.AssertExpectations(t) } -func newTestNamespaceManager(t *testing.T, resetConfig bool) (*testNamespaceManager, func()) { - if resetConfig { - coreconfig.Reset() - InitConfig(true) - namespaceConfigSection.AddKnownKey("predefined.0.multiparty.enabled", true) - } +func newTestNamespaceManager(t *testing.T) (*testNamespaceManager, func()) { + coreconfig.Reset() + initAllConfig() + ctx, cancelCtx := context.WithCancel(context.Background()) nm := &testNamespaceManager{ - mmi: &metricsmocks.Manager{}, - mae: &spieventsmocks.Manager{}, - mbi: &blockchainmocks.Plugin{}, - cmi: &cachemocks.Manager{}, - mdi: &databasemocks.Plugin{}, - mdx: &dataexchangemocks.Plugin{}, - mps: &sharedstoragemocks.Plugin{}, - mti: &tokenmocks.Plugin{}, - mev: &eventsmocks.Plugin{}, - auth: &authmocks.Plugin{}, - namespaceManager: namespaceManager{ - reset: make(chan bool, 1), - namespaces: make(map[string]*namespace), - plugins: make(map[string]*plugin), - tokenBroadcastNames: make(map[string]string), - }, + mmi: &metricsmocks.Manager{}, + mae: &spieventsmocks.Manager{}, + mbi: &blockchainmocks.Plugin{}, + cmi: &cachemocks.Manager{}, + mdi: &databasemocks.Plugin{}, + mdx: &dataexchangemocks.Plugin{}, + mps: &sharedstoragemocks.Plugin{}, + mti: &tokenmocks.Plugin{}, + mev: &eventsmocks.Plugin{}, + mai: &authmocks.Plugin{}, + mo: &orchestratormocks.Orchestrator{}, + } + nm.namespaceManager = namespaceManager{ + ctx: ctx, + cancelCtx: cancelCtx, + reset: make(chan bool, 1), + namespaces: make(map[string]*namespace), + plugins: make(map[string]*plugin), + tokenBroadcastNames: make(map[string]string), + utOrchestrator: nm.mo, } - ctx, cancel := context.WithCancel(context.Background()) nm.watchConfig = func() { <-ctx.Done() } @@ -125,7 +138,7 @@ func newTestNamespaceManager(t *testing.T, resetConfig bool) (*testNamespaceMana }, "tbd": { name: "tbd", - category: pluginCategorySharedstorage, + category: pluginCategoryIdentity, pluginType: "tbd", identity: &identitymocks.Plugin{}, }, @@ -151,33 +164,95 @@ func newTestNamespaceManager(t *testing.T, resetConfig bool) (*testNamespaceMana name: "basicauth", category: pluginCategoryAuth, pluginType: "basicauth", - auth: nm.auth, + auth: nm.mai, + }, + } + nm.namespaces = map[string]*namespace{ + "default": { + Namespace: core.Namespace{ + Name: "default", + }, + ctx: nm.ctx, + cancelCtx: nm.cancelCtx, + orchestrator: nm.mo, + pluginNames: []string{ + "ethereum", + "postgres", + "ffdx", + "ipfs", + "tbd", + "erc721", + "erc1155", + "basicauth", + }, + plugins: &orchestrator.Plugins{ + Blockchain: orchestrator.BlockchainPlugin{ + Name: "ethereum", + Plugin: nm.plugins["ethereum"].blockchain, + }, + Database: orchestrator.DatabasePlugin{ + Name: "postgres", + Plugin: nm.plugins["postgres"].database, + }, + DataExchange: orchestrator.DataExchangePlugin{ + Name: "ffdx", + Plugin: nm.plugins["ffdx"].dataexchange, + }, + SharedStorage: orchestrator.SharedStoragePlugin{ + Name: "ipfs", + Plugin: nm.plugins["ipfs"].sharedstorage, + }, + Tokens: []orchestrator.TokensPlugin{ + { + Name: "erc721", + Plugin: nm.plugins["erc721"].tokens, + }, + { + Name: "erc1155", + Plugin: nm.plugins["erc1155"].tokens, + }, + }, + }, }, } nm.namespaceManager.metrics = nm.mmi nm.namespaceManager.adminEvents = nm.mae + return nm, func() { - a := recover() - if a != nil { + if a := recover(); a != nil { panic(a) } - cancel() + nm.cancelCtx() nm.cleanup(t) } } func TestNewNamespaceManager(t *testing.T) { - nm := NewNamespaceManager(true) + nm := NewNamespaceManager() assert.NotNil(t, nm) } -func TestInit(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) +func TestInitEmpty(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) defer cleanup() nm.metricsEnabled = true - mo := &orchestratormocks.Orchestrator{} - mo.On("Init", mock.Anything, mock.Anything). + err := nm.Init(nm.ctx, nm.cancelCtx, nm.reset) + assert.NoError(t, err) + + assert.Len(t, nm.plugins, 3) + assert.NotNil(t, nm.plugins["system"]) + assert.NotNil(t, nm.plugins["webhooks"]) + assert.NotNil(t, nm.plugins["websockets"]) + assert.Empty(t, nm.namespaces) +} + +func TestInitAllPlugins(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + nm.metricsEnabled = true + + nm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].Contracts.Active = &core.MultipartyContract{ Info: core.MultipartyContractInfo{ @@ -186,7 +261,6 @@ func TestInit(t *testing.T) { } }). Return(nil) - nm.utOrchestrator = mo nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() @@ -198,171 +272,134 @@ func TestInit(t *testing.T) { nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initComponents(nm.ctx) assert.NoError(t, err) - assert.Equal(t, mo, nm.Orchestrator("default")) + assert.Equal(t, nm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) assert.Nil(t, nm.Orchestrator(core.LegacySystemNamespace)) +} - mo.AssertExpectations(t) +func TestInitComponentsPluginsFail(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + + nm.namespaces = map[string]*namespace{} + nm.plugins = map[string]*plugin{ + "basicauth": nm.plugins["basicauth"], + } + err := nm.initComponents(context.Background()) + assert.Regexp(t, "pop", err) } func TestInitDatabaseFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - nm.utOrchestrator = &orchestratormocks.Orchestrator{} - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initPlugins(context.Background(), map[string]*plugin{ + "postgres": nm.plugins["postgres"], + }) assert.EqualError(t, err, "pop") } func TestInitBlockchainFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - nm.utOrchestrator = &orchestratormocks.Orchestrator{} - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(fmt.Errorf("pop")) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initPlugins(context.Background(), map[string]*plugin{ + "ethereum": nm.plugins["ethereum"], + }) assert.EqualError(t, err, "pop") } func TestInitDataExchangeFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - nm.utOrchestrator = &orchestratormocks.Orchestrator{} - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initPlugins(context.Background(), map[string]*plugin{ + "ffdx": nm.plugins["ffdx"], + }) assert.EqualError(t, err, "pop") } func TestInitSharedStorageFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - nm.utOrchestrator = &orchestratormocks.Orchestrator{} - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) nm.mps.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initPlugins(context.Background(), map[string]*plugin{ + "ipfs": nm.plugins["ipfs"], + }) assert.EqualError(t, err, "pop") } func TestInitTokensFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - nm.utOrchestrator = &orchestratormocks.Orchestrator{} - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) nm.mti.On("Init", mock.Anything, mock.Anything, mock.Anything, nil).Return(fmt.Errorf("pop")) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initPlugins(context.Background(), map[string]*plugin{ + "erc1155": nm.plugins["erc1155"], + }) assert.EqualError(t, err, "pop") } func TestInitEventsFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - nm.utOrchestrator = &orchestratormocks.Orchestrator{} - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) nm.mev.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initPlugins(context.Background(), map[string]*plugin{ + "websockets": nm.plugins["websockets"], + }) assert.EqualError(t, err, "pop") } func TestInitAuthFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - nm.utOrchestrator = &orchestratormocks.Orchestrator{} - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initPlugins(context.Background(), map[string]*plugin{ + "basicauth": nm.plugins["basicauth"], + }) assert.EqualError(t, err, "pop") } func TestInitOrchestratorFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - nm.mdi.On("Capabilities").Return(&database.Capabilities{ - Concurrency: true, - }) - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mbi.On("GetAndConvertDeprecatedContractConfig", mock.Anything).Return(nil, "", fmt.Errorf("pop")) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mo.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initNamespaces(nm.ctx, nm.namespaces) assert.Regexp(t, "pop", err) } func TestInitVersion1(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - mo := &orchestratormocks.Orchestrator{} - mo.On("Init", mock.Anything, mock.Anything). + nm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { + nm.namespaces["default"].config.Multiparty.Enabled = true nm.namespaces["default"].Contracts.Active = &core.MultipartyContract{ Location: fftypes.JSONAnyPtr("{}"), Info: core.MultipartyContractInfo{ @@ -370,43 +407,28 @@ func TestInitVersion1(t *testing.T) { }, } }). - Return(nil).Once() - mo.On("Init", mock.Anything, mock.Anything).Return(nil).Once() - nm.utOrchestrator = mo - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - + Return(nil).Twice() // legacy system namespace nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initNamespaces(nm.ctx, nm.namespaces) assert.NoError(t, err) - assert.Equal(t, mo, nm.Orchestrator("default")) + assert.Equal(t, nm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) assert.NotNil(t, nm.Orchestrator(core.LegacySystemNamespace)) - mo.AssertExpectations(t) } func TestInitFFSystemWithTerminatedV1Contract(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() nm.metricsEnabled = true - mo := &orchestratormocks.Orchestrator{} - mo.On("Init", mock.Anything, mock.Anything). + nm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { + nm.namespaces["default"].config.Multiparty.Enabled = true nm.namespaces["default"].Contracts = &core.MultipartyContracts{ Active: &core.MultipartyContract{ Info: core.MultipartyContractInfo{ @@ -421,34 +443,22 @@ func TestInitFFSystemWithTerminatedV1Contract(t *testing.T) { }}, } }). - Return(nil) - nm.utOrchestrator = mo + Return(nil).Twice() // legacy system namespace - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initNamespaces(nm.ctx, nm.namespaces) assert.NoError(t, err) - assert.Equal(t, mo, nm.Orchestrator("default")) + assert.Equal(t, nm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) - mo.AssertExpectations(t) } func TestLegacyNamespaceConflictingPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() nm.metricsEnabled = true @@ -469,8 +479,7 @@ func TestLegacyNamespaceConflictingPlugins(t *testing.T) { `)) assert.NoError(t, err) - mo := &orchestratormocks.Orchestrator{} - mo.On("Init", mock.Anything, mock.Anything). + nm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].Contracts = &core.MultipartyContracts{ Active: &core.MultipartyContract{ @@ -490,33 +499,25 @@ func TestLegacyNamespaceConflictingPlugins(t *testing.T) { } }). Return(nil) - nm.utOrchestrator = mo - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) + nm.namespaces, err = nm.loadNamespaces(nm.ctx, viper.AllSettings(), nm.plugins) + assert.Len(t, nm.namespaces, 2) + assert.NoError(t, err) + + err = nm.initNamespaces(nm.ctx, nm.namespaces) assert.Regexp(t, "FF10421", err) - assert.Equal(t, mo, nm.Orchestrator("default")) + assert.Equal(t, nm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) - mo.AssertExpectations(t) } func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() nm.metricsEnabled = true @@ -536,8 +537,7 @@ func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { `)) assert.NoError(t, err) - mo := &orchestratormocks.Orchestrator{} - mo.On("Init", mock.Anything, mock.Anything). + nm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].Contracts = &core.MultipartyContracts{ Active: &core.MultipartyContract{ @@ -557,33 +557,25 @@ func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { } }). Return(nil) - nm.utOrchestrator = mo - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) + nm.namespaces, err = nm.loadNamespaces(nm.ctx, viper.AllSettings(), nm.plugins) + assert.Len(t, nm.namespaces, 2) + assert.NoError(t, err) + + err = nm.initNamespaces(nm.ctx, nm.namespaces) assert.Regexp(t, "FF10421", err) - assert.Equal(t, mo, nm.Orchestrator("default")) + assert.Equal(t, nm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) - mo.AssertExpectations(t) } func TestLegacyNamespaceMatchingPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() nm.metricsEnabled = true @@ -603,8 +595,7 @@ func TestLegacyNamespaceMatchingPlugins(t *testing.T) { `)) assert.NoError(t, err) - mo := &orchestratormocks.Orchestrator{} - mo.On("Init", mock.Anything, mock.Anything). + nm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].Contracts = &core.MultipartyContracts{ Active: &core.MultipartyContract{ @@ -624,39 +615,31 @@ func TestLegacyNamespaceMatchingPlugins(t *testing.T) { } }). Return(nil) - nm.utOrchestrator = mo - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) + nm.namespaces, err = nm.loadNamespaces(nm.ctx, viper.AllSettings(), nm.plugins) + assert.Len(t, nm.namespaces, 2) + assert.NoError(t, err) + + err = nm.initNamespaces(nm.ctx, nm.namespaces) assert.NoError(t, err) - assert.Equal(t, mo, nm.Orchestrator("default")) + assert.Equal(t, nm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) - mo.AssertExpectations(t) } func TestInitVersion1Fail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - mo := &orchestratormocks.Orchestrator{} - mo.On("Init", mock.Anything, mock.Anything). + nm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { + nm.namespaces["default"].config.Multiparty.Enabled = true nm.namespaces["default"].Contracts.Active = &core.MultipartyContract{ Location: fftypes.JSONAnyPtr("{}"), Info: core.MultipartyContractInfo{ @@ -665,57 +648,31 @@ func TestInitVersion1Fail(t *testing.T) { } }). Return(nil).Once() - mo.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")).Once() - nm.utOrchestrator = mo - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + nm.mo.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")).Once() nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.initNamespaces(nm.ctx, nm.namespaces) assert.EqualError(t, err, "pop") - mo.AssertExpectations(t) } func TestInitNamespaceQueryFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - ns := &namespace{ - Namespace: core.Namespace{ - Name: "default", - }, - plugins: []string{"postgres"}, - } - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, fmt.Errorf("pop")) - err := nm.initNamespace(context.Background(), ns) + err := nm.initNamespace(context.Background(), nm.namespaces["default"]) assert.EqualError(t, err, "pop") } func TestInitNamespaceExistingUpsertFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() - ns := &namespace{ - Namespace: core.Namespace{ - Name: "default", - }, - plugins: []string{"postgres"}, - } existing := &core.Namespace{ NetworkName: "ns1", } @@ -723,1338 +680,1170 @@ func TestInitNamespaceExistingUpsertFail(t *testing.T) { nm.mdi.On("GetNamespace", mock.Anything, "default").Return(existing, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(fmt.Errorf("pop")) - err := nm.initNamespace(context.Background(), ns) + err := nm.initNamespace(context.Background(), nm.namespaces["default"]) assert.EqualError(t, err, "pop") } func TestDeprecatedDatabasePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, cleanup := newTestNamespaceManager(t) defer cleanup() difactory.InitConfigDeprecated(deprecatedDatabaseConfig) deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "postgres") plugins := make(map[string]*plugin) - err := nm.getDatabasePlugins(context.Background(), plugins, fftypes.JSONObject{}) + err := nm.getDatabasePlugins(context.Background(), plugins, nm.dumpRootConfig()) assert.Equal(t, 1, len(plugins)) assert.NoError(t, err) } -// func TestDeprecatedDatabasePluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// difactory.InitConfigDeprecated(deprecatedDatabaseConfig) -// deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "wrong") -// _, err := nm.getDatabasePlugins(context.Background()) -// assert.Regexp(t, "FF10122.*wrong", err) -// } - -// func TestDatabasePlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// difactory.InitConfig(databaseConfig) -// config.Set("plugins.database", []fftypes.JSONObject{{}}) -// databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres") -// plugins, err := nm.getDatabasePlugins(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestDatabasePluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// difactory.InitConfig(databaseConfig) -// config.Set("plugins.database", []fftypes.JSONObject{{}}) -// databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "unknown") -// plugins, err := nm.getDatabasePlugins(context.Background()) -// assert.Nil(t, plugins) -// assert.Error(t, err) -// } - -// func TestDatabasePluginBadName(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.plugins.database = nil -// difactory.InitConfig(databaseConfig) -// config.Set("plugins.database", []fftypes.JSONObject{{}}) -// databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong////") -// databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres") - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err := nm.Init(ctx, cancelCtx, nm.reset) -// assert.Error(t, err) -// } - -// func TestIdentityPluginBadName(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// iifactory.InitConfig(identityConfig) -// identityConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong//") -// identityConfig.AddKnownKey(coreconfig.PluginConfigType, "tbd") -// config.Set("plugins.identity", []fftypes.JSONObject{{}}) -// _, err := nm.getIdentityPlugins(context.Background()) -// assert.Regexp(t, "FF00140.*name", err) -// } - -// func TestIdentityPluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// iifactory.InitConfig(identityConfig) -// identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// identityConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") -// config.Set("plugins.identity", []fftypes.JSONObject{{}}) -// _, err := nm.getIdentityPlugins(context.Background()) -// assert.Regexp(t, "FF10212.*wrong", err) -// } - -// func TestIdentityPluginNoType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.plugins.identity = nil -// iifactory.InitConfig(identityConfig) -// identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// config.Set("plugins.identity", []fftypes.JSONObject{{}}) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err := nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10386.*type", err) -// } - -// func TestIdentityPlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// iifactory.InitConfig(identityConfig) -// identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// identityConfig.AddKnownKey(coreconfig.PluginConfigType, "onchain") -// config.Set("plugins.identity", []fftypes.JSONObject{{}}) -// plugins, err := nm.getIdentityPlugins(context.Background()) -// assert.NoError(t, err) -// assert.Equal(t, 1, len(plugins)) -// } - -// func TestDeprecatedBlockchainPlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// bifactory.InitConfigDeprecated(deprecatedBlockchainConfig) -// deprecatedBlockchainConfig.Set(coreconfig.PluginConfigType, "ethereum") -// plugins, err := nm.getBlockchainPlugins(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestDeprecatedBlockchainPluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// deprecatedBlockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") -// _, err := nm.getBlockchainPlugins(context.Background()) -// assert.Regexp(t, "FF10110.*wrong", err) -// } - -// func TestBlockchainPlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// bifactory.InitConfig(blockchainConfig) -// config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) -// blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "ethereum") -// plugins, err := nm.getBlockchainPlugins(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestBlockchainPluginNoType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// bifactory.InitConfig(blockchainConfig) -// config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) -// blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// _, err := nm.getBlockchainPlugins(context.Background()) -// assert.Error(t, err) -// } - -// func TestBlockchainPluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.plugins.blockchain = nil -// bifactory.InitConfig(blockchainConfig) -// config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) -// blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err := nm.Init(ctx, cancelCtx, nm.reset) -// assert.Error(t, err) -// } - -// func TestDeprecatedSharedStoragePlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) -// deprecatedSharedStorageConfig.Set(coreconfig.PluginConfigType, "ipfs") -// plugins, err := nm.getSharedStoragePlugins(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestDeprecatedSharedStoragePluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) -// deprecatedSharedStorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") -// _, err := nm.getSharedStoragePlugins(context.Background()) -// assert.Regexp(t, "FF10134.*wrong", err) -// } - -// func TestSharedStoragePlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// ssfactory.InitConfig(sharedstorageConfig) -// config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) -// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "ipfs") -// plugins, err := nm.getSharedStoragePlugins(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestSharedStoragePluginNoType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// ssfactory.InitConfig(sharedstorageConfig) -// config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) -// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// _, err := nm.getSharedStoragePlugins(context.Background()) -// assert.Error(t, err) -// } - -// func TestSharedStoragePluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.plugins.sharedstorage = nil -// ssfactory.InitConfig(sharedstorageConfig) -// config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) -// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err := nm.Init(ctx, cancelCtx, nm.reset) -// assert.Error(t, err) -// } - -// func TestDeprecatedDataExchangePlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) -// deprecatedDataexchangeConfig.Set(coreconfig.PluginConfigType, "ffdx") -// plugins, err := nm.getDataExchangePlugins(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestDeprecatedDataExchangePluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) -// deprecatedDataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") -// _, err := nm.getDataExchangePlugins(context.Background()) -// assert.Regexp(t, "FF10213.*wrong", err) -// } - -// func TestDataExchangePlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// dxfactory.InitConfig(dataexchangeConfig) -// config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) -// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "ffdx") -// plugins, err := nm.getDataExchangePlugins(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestDataExchangePluginNoType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// dxfactory.InitConfig(dataexchangeConfig) -// config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) -// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// _, err := nm.getDataExchangePlugins(context.Background()) -// assert.Error(t, err) -// } - -// func TestDataExchangePluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.plugins.dataexchange = nil -// dxfactory.InitConfig(dataexchangeConfig) -// config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) -// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") -// dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err := nm.Init(ctx, cancelCtx, nm.reset) -// assert.Error(t, err) -// } - -// func TestDeprecatedTokensPlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// tifactory.InitConfigDeprecated(deprecatedTokensConfig) -// config.Set("tokens", []fftypes.JSONObject{{}}) -// deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test") -// deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") -// plugins, err := nm.getTokensPlugins(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestDeprecatedTokensPluginNoName(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// tifactory.InitConfigDeprecated(deprecatedTokensConfig) -// config.Set("tokens", []fftypes.JSONObject{{}}) -// deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") -// _, err := nm.getTokensPlugins(context.Background()) -// assert.Regexp(t, "FF10273", err) -// } - -// func TestDeprecatedTokensPluginBadName(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// tifactory.InitConfigDeprecated(deprecatedTokensConfig) -// config.Set("tokens", []fftypes.JSONObject{{}}) -// deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "BAD!") -// deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") -// _, err := nm.getTokensPlugins(context.Background()) -// assert.Regexp(t, "FF00140", err) -// } - -// func TestDeprecatedTokensPluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// tifactory.InitConfigDeprecated(deprecatedTokensConfig) -// config.Set("tokens", []fftypes.JSONObject{{}}) -// deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test") -// deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "wrong") -// _, err := nm.getTokensPlugins(context.Background()) -// assert.Regexp(t, "FF10272.*wrong", err) -// } - -// func TestTokensPlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// tifactory.InitConfig(tokensConfig) -// config.Set("plugins.tokens", []fftypes.JSONObject{{}}) -// tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") -// tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens") -// plugins, err := nm.getTokensPlugins(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestTokensPluginDuplicateBroadcastName(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// tifactory.InitConfig(tokensConfig) -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// plugins: -// tokens: -// - name: test1 -// broadcastName: remote1 -// type: fftokens -// fftokens: -// url: http://tokens:3000 -// - name: test2 -// broadcastName: remote1 -// type: fftokens -// fftokens: -// url: http://tokens:3000 -// `)) -// assert.NoError(t, err) - -// plugins, err := nm.getTokensPlugins(context.Background()) -// assert.Nil(t, plugins) -// assert.Regexp(t, "FF10419", err) -// } - -// func TestMultipleTokensPluginsWithBroadcastName(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// tifactory.InitConfig(tokensConfig) -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// plugins: -// tokens: -// - name: test1 -// broadcastName: remote1 -// type: fftokens -// fftokens: -// url: http://tokens:3000 -// - name: test2 -// broadcastName: remote2 -// type: fftokens -// fftokens: -// url: http://tokens:3000 -// `)) -// assert.NoError(t, err) - -// plugins, err := nm.getTokensPlugins(context.Background()) -// assert.Equal(t, 2, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestTokensPluginNoType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// tifactory.InitConfig(tokensConfig) -// config.Set("plugins.tokens", []fftypes.JSONObject{{}}) -// tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") -// _, err := nm.getTokensPlugins(context.Background()) -// assert.Error(t, err) -// } - -// func TestTokensPluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.plugins.tokens = nil -// tifactory.InitConfig(tokensConfig) -// config.Set("plugins.tokens", []fftypes.JSONObject{{}}) -// tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") -// tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err := nm.Init(ctx, cancelCtx, nm.reset) -// assert.Error(t, err) -// } - -// func TestTokensPluginDuplicate(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.pluginNames["erc20_erc721"] = true -// tifactory.InitConfig(tokensConfig) -// config.Set("plugins.tokens", []fftypes.JSONObject{{}}) -// tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") -// tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens") -// _, err := nm.getTokensPlugins(context.Background()) -// assert.Regexp(t, "FF10395", err) -// } - -// func TestEventsPluginDefaults(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.plugins.events = nil -// plugins, err := nm.getEventPlugins(context.Background()) -// assert.Equal(t, 3, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestAuthPlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// authfactory.InitConfigArray(authConfig) -// config.Set("plugins.auth", []fftypes.JSONObject{{}}) -// authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") -// authConfig.AddKnownKey(coreconfig.PluginConfigType, "basic") -// plugins, err := nm.getAuthPlugin(context.Background()) -// assert.Equal(t, 1, len(plugins)) -// assert.NoError(t, err) -// } - -// func TestAuthPluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.plugins.auth = nil -// authfactory.InitConfigArray(authConfig) -// config.Set("plugins.auth", []fftypes.JSONObject{{}}) -// authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") -// authConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err := nm.Init(ctx, cancelCtx, nm.reset) -// assert.Error(t, err) -// } - -// func TestAuthPluginInvalid(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// authfactory.InitConfigArray(authConfig) -// config.Set("plugins.auth", []fftypes.JSONObject{{}}) -// authConfig.AddKnownKey(coreconfig.PluginConfigName, "bad name not allowed") -// authConfig.AddKnownKey(coreconfig.PluginConfigType, "basic") -// plugins, err := nm.getAuthPlugin(context.Background()) -// assert.Equal(t, 0, len(plugins)) -// assert.Error(t, err) -// } - -// func TestEventsPluginBadType(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// nm.plugins.events = nil -// config.Set(coreconfig.EventTransportsEnabled, []string{"!unknown!"}) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err := nm.Init(ctx, cancelCtx, nm.reset) -// assert.Error(t, err) -// } - -// func TestInitBadNamespace(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// nm.utOrchestrator = &orchestratormocks.Orchestrator{} - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: "!Badness" -// predefined: -// - name: "!Badness" -// `)) -// assert.NoError(t, err) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF00140", err) -// } - -// func TestLoadNamespacesReservedName(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ff_system -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.Regexp(t, "FF10388", err) -// } - -// func TestLoadNamespacesReservedNetworkName(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// multiparty: -// enabled: true -// networknamespace: ff_system -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.Regexp(t, "FF10388", err) -// } - -// func TestLoadNamespacesDuplicate(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [postgres] -// - name: ns1 -// plugins: [postgres] -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.NoError(t, err) -// assert.Len(t, nm.namespaces, 1) -// } - -// func TestLoadNamespacesNoName(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// predefined: -// - plugins: [postgres] -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.Regexp(t, "FF10166", err) -// } - -// func TestLoadNamespacesNoDefault(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns2 -// plugins: [postgres] -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.Regexp(t, "FF10166", err) -// } - -// func TestLoadNamespacesUseDefaults(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// multiparty: -// contract: -// - location: -// address: 0x1234 -// org: -// name: org1 -// node: -// name: node1 -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.NoError(t, err) -// assert.Len(t, nm.namespaces, 1) -// assert.Equal(t, "oldest", nm.namespaces["ns1"].config.Multiparty.Contracts[0].FirstEvent) -// } - -// func TestLoadNamespacesNonMultipartyNoDatabase(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [] -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10392", err) -// } - -// func TestLoadNamespacesMultipartyUnknownPlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [basicauth, bad] -// multiparty: -// enabled: true -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10390.*unknown", err) -// } - -// func TestLoadNamespacesMultipartyMultipleBlockchains(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [ethereum, ethereum] -// multiparty: -// enabled: true -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10394.*blockchain", err) -// } - -// func TestLoadNamespacesMultipartyMultipleDX(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [ffdx, ffdx] -// multiparty: -// enabled: true -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10394.*dataexchange", err) -// } - -// func TestLoadNamespacesMultipartyMultipleSS(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [ipfs, ipfs] -// multiparty: -// enabled: true -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10394.*sharedstorage", err) -// } - -// func TestLoadNamespacesMultipartyMultipleDB(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [postgres, postgres] -// multiparty: -// enabled: true -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10394.*database", err) -// } - -// func TestInitNamespacesMultipartyWithAuth(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [ethereum, postgres, ipfs, ffdx, basicauth] -// multiparty: -// enabled: true -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.NoError(t, err) -// } - -// func TestLoadNamespacesNonMultipartyWithAuth(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [ethereum, postgres, basicauth] -// multiparty: -// enabled: false -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.NoError(t, err) -// } - -// func TestLoadNamespacesMultipartyContract(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// multiparty: -// enabled: true -// contract: -// - location: -// address: 0x4ae50189462b0e5d52285f59929d037f790771a6 -// firstEvent: oldest -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.NoError(t, err) -// } - -// func TestLoadNamespacesMultipartyContractBadLocation(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// fail := make(chan string) -// namespaceConfig.AddKnownKey("predefined.0.multiparty.contract.0.location.address", fail) - -// err := nm.loadNamespaces(context.Background()) -// assert.Regexp(t, "json:", err) -// } - -// func TestLoadNamespacesNonMultipartyMultipleDB(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [postgres, postgres] -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10394.*database", err) -// } - -// func TestLoadNamespacesNonMultipartyMultipleBlockchains(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [ethereum, ethereum] -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10394.*blockchain", err) -// } - -// func TestLoadNamespacesMultipartyMissingPlugins(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [postgres] -// multiparty: -// enabled: true -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10391", err) -// } - -// func TestLoadNamespacesNonMultipartyUnknownPlugin(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [basicauth, erc721, bad] -// `)) -// assert.NoError(t, err) - -// nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() -// nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) -// nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) -// nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) -// nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) -// nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) -// nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - -// ctx, cancelCtx := context.WithCancel(context.Background()) -// err = nm.Init(ctx, cancelCtx, nm.reset) -// assert.Regexp(t, "FF10390.*unknown", err) -// } - -// func TestLoadNamespacesNonMultipartyTokens(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [postgres, erc721] -// `)) -// assert.NoError(t, err) - -// err = nm.loadNamespaces(context.Background()) -// assert.NoError(t, err) -// } - -// func TestStart(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "ns": {orchestrator: mo}, -// } -// nm.plugins.blockchain = nil -// nm.plugins.dataexchange = nil -// nm.plugins.tokens = nil -// nm.metricsEnabled = true - -// mo.On("Start", mock.Anything).Return(nil) - -// err := nm.Start() -// assert.NoError(t, err) - -// mo.AssertExpectations(t) -// } - -// func TestStartBlockchainFail(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "ns": { -// orchestrator: mo, -// }, -// } - -// mo.On("Start", mock.Anything).Return(nil) -// nm.mbi.On("Start").Return(fmt.Errorf("pop")) - -// err := nm.Start() -// assert.EqualError(t, err, "pop") - -// mo.AssertExpectations(t) - -// } - -// func TestStartDataExchangeFail(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "ns": { -// orchestrator: mo, -// }, -// } -// nm.plugins.blockchain = nil - -// mo.On("Start", mock.Anything).Return(nil) -// nm.mdx.On("Start").Return(fmt.Errorf("pop")) - -// err := nm.Start() -// assert.EqualError(t, err, "pop") - -// mo.AssertExpectations(t) +func TestDeprecatedDatabasePluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + difactory.InitConfigDeprecated(deprecatedDatabaseConfig) + deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "wrong") + err := nm.getDatabasePlugins(context.Background(), make(map[string]*plugin), nm.dumpRootConfig()) + assert.Regexp(t, "FF10122.*wrong", err) +} -// } +func TestDatabasePlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + difactory.InitConfig(databaseConfig) + config.Set("plugins.database", []fftypes.JSONObject{{}}) + databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres") + plugins := make(map[string]*plugin) + err := nm.getDatabasePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} -// func TestStartTokensFail(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() +func TestDatabasePluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + difactory.InitConfig(databaseConfig) + config.Set("plugins.database", []fftypes.JSONObject{{}}) + databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "unknown") + err := nm.getDatabasePlugins(context.Background(), make(map[string]*plugin), nm.dumpRootConfig()) + assert.Regexp(t, "FF10122.*unknown", err) +} -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "ns": { -// orchestrator: mo, -// }, -// } -// nm.plugins.blockchain = nil -// nm.plugins.dataexchange = nil +func TestDatabasePluginBadName(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + difactory.InitConfig(databaseConfig) + config.Set("plugins.database", []fftypes.JSONObject{{}}) + databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong////") + databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "postgres") + err := nm.getDatabasePlugins(context.Background(), make(map[string]*plugin), nm.dumpRootConfig()) + assert.Regexp(t, "FF00140", err) +} -// mo.On("Start", mock.Anything).Return(nil) -// nm.mti.On("Start").Return(fmt.Errorf("pop")) +func TestIdentityPluginBadName(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + iifactory.InitConfig(identityConfig) + identityConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong//") + identityConfig.AddKnownKey(coreconfig.PluginConfigType, "tbd") + config.Set("plugins.identity", []fftypes.JSONObject{{}}) + err := nm.getIdentityPlugins(context.Background(), make(map[string]*plugin), nm.dumpRootConfig()) + assert.Regexp(t, "FF00140.*name", err) +} -// err := nm.Start() -// assert.EqualError(t, err, "pop") +func TestIdentityPluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + iifactory.InitConfig(identityConfig) + identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + identityConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + config.Set("plugins.identity", []fftypes.JSONObject{{}}) + err := nm.getIdentityPlugins(context.Background(), make(map[string]*plugin), nm.dumpRootConfig()) + assert.Regexp(t, "FF10212.*wrong", err) +} -// } +func TestIdentityPluginNoType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + iifactory.InitConfig(identityConfig) + identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + config.Set("plugins.identity", []fftypes.JSONObject{{}}) -// func TestStartOrchestratorFail(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() + ctx, cancelCtx := context.WithCancel(context.Background()) + err := nm.Init(ctx, cancelCtx, nm.reset) + assert.Regexp(t, "FF10386.*type", err) +} -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "ns": {orchestrator: mo}, -// } +func TestIdentityPlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + iifactory.InitConfig(identityConfig) + identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + identityConfig.AddKnownKey(coreconfig.PluginConfigType, "onchain") + config.Set("plugins.identity", []fftypes.JSONObject{{}}) + plugins := make(map[string]*plugin) + err := nm.getIdentityPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.NoError(t, err) + assert.Equal(t, 1, len(plugins)) +} -// mo.On("Start", mock.Anything).Return(fmt.Errorf("pop")) +func TestDeprecatedBlockchainPlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + bifactory.InitConfigDeprecated(deprecatedBlockchainConfig) + deprecatedBlockchainConfig.Set(coreconfig.PluginConfigType, "ethereum") + plugins := make(map[string]*plugin) + err := nm.getBlockchainPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} -// err := nm.Start() -// assert.EqualError(t, err, "pop") +func TestDeprecatedBlockchainPluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + deprecatedBlockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + plugins := make(map[string]*plugin) + err := nm.getBlockchainPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10110.*wrong", err) +} -// mo.AssertExpectations(t) -// } +func TestBlockchainPlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + bifactory.InitConfig(blockchainConfig) + config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) + blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "ethereum") + plugins := make(map[string]*plugin) + err := nm.getBlockchainPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} -// func TestWaitStop(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() +func TestBlockchainPluginNoType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + bifactory.InitConfig(blockchainConfig) + config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) + blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + plugins := make(map[string]*plugin) + err := nm.getBlockchainPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10386", err) +} -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "ns": {orchestrator: mo}, -// } -// mae := nm.adminEvents.(*spieventsmocks.Manager) +func TestBlockchainPluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + bifactory.InitConfig(blockchainConfig) + config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) + blockchainConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") -// mo.On("WaitStop").Return() -// mae.On("WaitStop").Return() + plugins := make(map[string]*plugin) + err := nm.getBlockchainPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10110", err) +} -// nm.WaitStop() +func TestDeprecatedSharedStoragePlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) + deprecatedSharedStorageConfig.Set(coreconfig.PluginConfigType, "ipfs") + plugins := make(map[string]*plugin) + err := nm.getSharedStoragePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} -// mo.AssertExpectations(t) -// mae.AssertExpectations(t) -// } +func TestDeprecatedSharedStoragePluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) + deprecatedSharedStorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + plugins := make(map[string]*plugin) + err := nm.getSharedStoragePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10134.*wrong", err) +} -// func TestReset(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() +func TestSharedStoragePlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + ssfactory.InitConfig(sharedstorageConfig) + config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) + sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "ipfs") + plugins := make(map[string]*plugin) + err := nm.getSharedStoragePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} -// childCtx, childCancel := context.WithCancel(context.Background()) -// err := nm.Reset(childCtx) -// assert.NoError(t, err) -// childCancel() +func TestSharedStoragePluginNoType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + ssfactory.InitConfig(sharedstorageConfig) + config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) + sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + plugins := make(map[string]*plugin) + err := nm.getSharedStoragePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10386", err) +} -// assert.True(t, <-nm.namespaceManager.reset) -// } +func TestSharedStoragePluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + ssfactory.InitConfig(sharedstorageConfig) + config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) + sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") + + plugins := make(map[string]*plugin) + err := nm.getSharedStoragePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10134", err) +} + +func TestDeprecatedDataExchangePlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) + deprecatedDataexchangeConfig.Set(coreconfig.PluginConfigType, "ffdx") + plugins := make(map[string]*plugin) + err := nm.getDataExchangePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} + +func TestDeprecatedDataExchangePluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) + deprecatedDataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + plugins := make(map[string]*plugin) + err := nm.getDataExchangePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10213.*wrong", err) +} + +func TestDataExchangePlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + dxfactory.InitConfig(dataexchangeConfig) + config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) + dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "ffdx") + plugins := make(map[string]*plugin) + err := nm.getDataExchangePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} + +func TestDataExchangePluginNoType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + dxfactory.InitConfig(dataexchangeConfig) + config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) + dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + plugins := make(map[string]*plugin) + err := nm.getDataExchangePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10386", err) +} + +func TestDataExchangePluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + dxfactory.InitConfig(dataexchangeConfig) + config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) + dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") + dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") + + plugins := make(map[string]*plugin) + err := nm.getDataExchangePlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10213", err) +} + +func TestDeprecatedTokensPlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfigDeprecated(deprecatedTokensConfig) + config.Set("tokens", []fftypes.JSONObject{{}}) + deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test") + deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") + plugins := make(map[string]*plugin) + err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} + +func TestDeprecatedTokensPluginNoName(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfigDeprecated(deprecatedTokensConfig) + config.Set("tokens", []fftypes.JSONObject{{}}) + deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") + plugins := make(map[string]*plugin) + err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10273", err) +} + +func TestDeprecatedTokensPluginBadName(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfigDeprecated(deprecatedTokensConfig) + config.Set("tokens", []fftypes.JSONObject{{}}) + deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "BAD!") + deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "fftokens") + plugins := make(map[string]*plugin) + err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF00140", err) +} + +func TestDeprecatedTokensPluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfigDeprecated(deprecatedTokensConfig) + config.Set("tokens", []fftypes.JSONObject{{}}) + deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test") + deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "wrong") + plugins := make(map[string]*plugin) + err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10272.*wrong", err) +} + +func TestTokensPlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfig(tokensConfig) + config.Set("plugins.tokens", []fftypes.JSONObject{{}}) + tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") + tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens") + plugins := make(map[string]*plugin) + err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} + +func TestTokensPluginDuplicateBroadcastName(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfig(tokensConfig) + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + plugins: + tokens: + - name: test1 + broadcastName: remote1 + type: fftokens + fftokens: + url: http://tokens:3000 + - name: test2 + broadcastName: remote1 + type: fftokens + fftokens: + url: http://tokens:3000 + `)) + assert.NoError(t, err) + + plugins := make(map[string]*plugin) + err = nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10419", err) +} + +func TestMultipleTokensPluginsWithBroadcastName(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfig(tokensConfig) + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + plugins: + tokens: + - name: test1 + broadcastName: remote1 + type: fftokens + fftokens: + url: http://tokens:3000 + - name: test2 + broadcastName: remote2 + type: fftokens + fftokens: + url: http://tokens:3000 + `)) + assert.NoError(t, err) + + plugins := make(map[string]*plugin) + err = nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 2, len(plugins)) + assert.NoError(t, err) +} + +func TestTokensPluginNoType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfig(tokensConfig) + config.Set("plugins.tokens", []fftypes.JSONObject{{}}) + tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") + plugins := make(map[string]*plugin) + err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10386", err) +} + +func TestTokensPluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfig(tokensConfig) + config.Set("plugins.tokens", []fftypes.JSONObject{{}}) + tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") + tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + + plugins := make(map[string]*plugin) + err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10272", err) +} + +func TestTokensPluginDuplicate(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + tifactory.InitConfig(tokensConfig) + config.Set("plugins.tokens", []fftypes.JSONObject{{}}) + tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") + tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "fftokens") + plugins := make(map[string]*plugin) + plugins["erc20_erc721"] = &plugin{} + err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10395", err) +} + +func TestEventsPluginDefaults(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + plugins := make(map[string]*plugin) + err := nm.getEventPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 3, len(plugins)) + assert.NoError(t, err) +} + +func TestAuthPlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + authfactory.InitConfigArray(authConfig) + config.Set("plugins.auth", []fftypes.JSONObject{{}}) + authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") + authConfig.AddKnownKey(coreconfig.PluginConfigType, "basic") + plugins := make(map[string]*plugin) + err := nm.getAuthPlugin(context.Background(), plugins, nm.dumpRootConfig()) + assert.Equal(t, 1, len(plugins)) + assert.NoError(t, err) +} + +func TestAuthPluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + authfactory.InitConfigArray(authConfig) + config.Set("plugins.auth", []fftypes.JSONObject{{}}) + authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") + authConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + + plugins := make(map[string]*plugin) + err := nm.getAuthPlugin(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF00168", err) +} + +func TestAuthPluginInvalid(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + authfactory.InitConfigArray(authConfig) + config.Set("plugins.auth", []fftypes.JSONObject{{}}) + authConfig.AddKnownKey(coreconfig.PluginConfigName, "bad name not allowed") + authConfig.AddKnownKey(coreconfig.PluginConfigType, "basic") + plugins := make(map[string]*plugin) + err := nm.getAuthPlugin(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF00140", err) +} + +func TestEventsPluginBadType(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + config.Set(coreconfig.EventTransportsEnabled, []string{"!unknown!"}) + + plugins := make(map[string]*plugin) + err := nm.getEventPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10172", err) +} + +func TestInitBadNamespace(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: "!Badness" + predefined: + - name: "!Badness" + `)) + assert.NoError(t, err) + + ctx, cancelCtx := context.WithCancel(context.Background()) + err = nm.Init(ctx, cancelCtx, nm.reset) + assert.Regexp(t, "FF00140", err) +} + +func TestLoadNamespacesReservedName(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ff_system + `)) + assert.NoError(t, err) + + _, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10388", err) +} + +func TestLoadNamespacesReservedNetworkName(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + multiparty: + enabled: true + networknamespace: ff_system + `)) + assert.NoError(t, err) + + _, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10388", err) +} + +func TestLoadNamespacesDuplicate(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [postgres] + - name: ns1 + plugins: [postgres] + `)) + assert.NoError(t, err) + + newNS, err := nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.NoError(t, err) + assert.Len(t, newNS, 1) +} + +func TestLoadNamespacesNoName(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + predefined: + - plugins: [postgres] + `)) + assert.NoError(t, err) + + _, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10166", err) +} + +func TestLoadNamespacesNoDefault(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns2 + plugins: [postgres] + `)) + assert.NoError(t, err) + + _, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10166", err) +} + +func TestLoadNamespacesUseDefaults(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + multiparty: + contract: + - location: + address: 0x1234 + org: + name: org1 + node: + name: node1 + `)) + assert.NoError(t, err) + + newNS, err := nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.NoError(t, err) + assert.Len(t, newNS, 1) + assert.Equal(t, "oldest", newNS["ns1"].config.Multiparty.Contracts[0].FirstEvent) +} + +func TestLoadNamespacesNonMultipartyNoDatabase(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [] + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10392", err) + +} + +func TestLoadNamespacesMultipartyUnknownPlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [basicauth, bad] + multiparty: + enabled: true + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10390.*unknown", err) +} + +func TestLoadNamespacesMultipartyMultipleBlockchains(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [ethereum, ethereum] + multiparty: + enabled: true + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10394.*blockchain", err) +} + +func TestLoadNamespacesMultipartyMultipleDX(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [ffdx, ffdx] + multiparty: + enabled: true + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10394.*dataexchange", err) +} + +func TestLoadNamespacesMultipartyMultipleSS(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [ipfs, ipfs] + multiparty: + enabled: true + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10394.*sharedstorage", err) +} + +func TestLoadNamespacesMultipartyMultipleDB(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [postgres, postgres] + multiparty: + enabled: true + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10394.*database", err) +} + +func TestInitNamespacesMultipartyWithAuth(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [ethereum, postgres, ipfs, ffdx, basicauth] + multiparty: + enabled: true + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.NoError(t, err) +} + +func TestLoadNamespacesNonMultipartyWithAuth(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [ethereum, postgres, basicauth] + multiparty: + enabled: false + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.NoError(t, err) +} + +func TestLoadNamespacesMultipartyContract(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + multiparty: + enabled: true + contract: + - location: + address: 0x4ae50189462b0e5d52285f59929d037f790771a6 + firstEvent: oldest + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.NoError(t, err) +} + +func TestLoadNamespacesNonMultipartyMultipleDB(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [postgres, postgres] + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10394.*database", err) +} + +func TestLoadNamespacesNonMultipartyMultipleBlockchains(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [ethereum, ethereum] + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10394.*blockchain", err) +} + +func TestLoadNamespacesMultipartyMissingPlugins(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [postgres] + multiparty: + enabled: true + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10391", err) +} + +func TestLoadNamespacesNonMultipartyUnknownPlugin(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [basicauth, erc721, bad] + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10390.*unknown", err) +} + +func TestLoadNamespacesNonMultipartyTokens(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [postgres, erc721] + `)) + assert.NoError(t, err) -// func TestResetRejectIfConfigAutoReload(t *testing.T) { -// config.RootConfigReset() -// config.Set(coreconfig.ConfigAutoReload, true) + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.NoError(t, err) +} -// nm, cleanup := newTestNamespaceManager(t, false) -// defer cleanup() +func TestStart(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() -// err := nm.Reset(context.Background()) -// assert.Regexp(t, "FF10433", err) + nm.mbi.On("Start", mock.Anything).Return(nil) + nm.mdx.On("Start", mock.Anything).Return(nil) + nm.mti.On("Start", mock.Anything).Return(nil) + nm.mo.On("Start", mock.Anything).Return(nil) -// } + err := nm.Start() + assert.NoError(t, err) +} -// func TestLoadMetrics(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() +func TestStartBlockchainFail(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() -// nm.metrics = nil + nm.mo.On("Start", mock.Anything).Return(nil) + nm.mbi.On("Start").Return(fmt.Errorf("pop")) -// err := nm.loadPlugins(context.Background()) -// assert.NoError(t, err) -// } + err := nm.startNamespacesAndPlugins(nm.namespaces, map[string]*plugin{ + "ethereum": nm.plugins["ethereum"], + }) + assert.EqualError(t, err, "pop") -// func TestLoadAdminEvents(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() +} -// nm.adminEvents = nil +func TestStartDataExchangeFail(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() -// err := nm.loadPlugins(context.Background()) -// assert.NoError(t, err) -// assert.NotNil(t, nm.SPIEvents()) -// } + nm.mo.On("Start", mock.Anything).Return(nil) + nm.mdx.On("Start").Return(fmt.Errorf("pop")) -// func TestGetNamespaces(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() + err := nm.startNamespacesAndPlugins(nm.namespaces, map[string]*plugin{ + "ffdx": nm.plugins["ffdx"], + }) + assert.EqualError(t, err, "pop") -// nm.namespaces = map[string]*namespace{ -// "default": {}, -// } +} -// results, err := nm.GetNamespaces(context.Background()) -// assert.Nil(t, err) -// assert.Len(t, results, 1) -// } +func TestStartTokensFail(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() -// func TestGetOperationByNamespacedID(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() + nm.mo.On("Start", mock.Anything).Return(nil) + nm.mti.On("Start").Return(fmt.Errorf("pop")) -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "default": {orchestrator: mo}, -// } + err := nm.startNamespacesAndPlugins(nm.namespaces, map[string]*plugin{ + "erc1155": nm.plugins["erc1155"], + }) + assert.EqualError(t, err, "pop") -// opID := fftypes.NewUUID() -// mo.On("GetOperationByID", context.Background(), opID.String()).Return(nil, nil) +} -// op, err := nm.GetOperationByNamespacedID(context.Background(), "default:"+opID.String()) -// assert.Nil(t, err) -// assert.Nil(t, op) +func TestStartOrchestratorFail(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() -// mo.AssertExpectations(t) -// } + nm.mo.On("Start", mock.Anything).Return(fmt.Errorf("pop")) -// func TestGetOperationByNamespacedIDBadID(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() + err := nm.startNamespacesAndPlugins(nm.namespaces, map[string]*plugin{}) + assert.EqualError(t, err, "pop") +} -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "default": {orchestrator: mo}, -// } +func TestWaitStop(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() -// _, err := nm.GetOperationByNamespacedID(context.Background(), "default:bad") -// assert.Regexp(t, "FF00138", err) + nm.mo.On("WaitStop").Return() + nm.mae.On("WaitStop").Return() -// mo.AssertExpectations(t) -// } + nm.WaitStop() -// func TestGetOperationByNamespacedIDNoOrchestrator(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() + nm.mo.AssertExpectations(t) + nm.mae.AssertExpectations(t) +} -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "default": {orchestrator: mo}, -// } - -// opID := fftypes.NewUUID() +func TestReset(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + childCtx, childCancel := context.WithCancel(context.Background()) + err := nm.Reset(childCtx) + assert.NoError(t, err) + childCancel() + + assert.True(t, <-nm.namespaceManager.reset) +} + +func TestResetRejectIfConfigAutoReload(t *testing.T) { + config.RootConfigReset() + config.Set(coreconfig.ConfigAutoReload, true) + + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + config.Set(coreconfig.ConfigAutoReload, true) -// _, err := nm.GetOperationByNamespacedID(context.Background(), "bad:"+opID.String()) -// assert.Regexp(t, "FF10109", err) + err := nm.Reset(context.Background()) + assert.Regexp(t, "FF10433", err) -// mo.AssertExpectations(t) -// } +} + +func TestLoadMetrics(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + nm.metrics = nil + + nm.loadManagers(context.Background()) +} + +func TestLoadAdminEvents(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + nm.adminEvents = nil + + nm.loadManagers(context.Background()) + assert.NotNil(t, nm.SPIEvents()) +} + +func TestGetNamespaces(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + results, err := nm.GetNamespaces(context.Background()) + assert.Nil(t, err) + assert.Len(t, results, 1) +} + +func TestGetOperationByNamespacedID(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + mo := &orchestratormocks.Orchestrator{} + nm.namespaces = map[string]*namespace{ + "default": {orchestrator: mo}, + } + + opID := fftypes.NewUUID() + mo.On("GetOperationByID", context.Background(), opID.String()).Return(nil, nil) + + op, err := nm.GetOperationByNamespacedID(context.Background(), "default:"+opID.String()) + assert.Nil(t, err) + assert.Nil(t, op) + + mo.AssertExpectations(t) +} + +func TestGetOperationByNamespacedIDBadID(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + mo := &orchestratormocks.Orchestrator{} + nm.namespaces = map[string]*namespace{ + "default": {orchestrator: mo}, + } -// func TestResolveOperationByNamespacedID(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// mo := &orchestratormocks.Orchestrator{} -// mom := &operationmocks.Manager{} -// nm.namespaces = map[string]*namespace{ -// "default": {orchestrator: mo}, -// } + _, err := nm.GetOperationByNamespacedID(context.Background(), "default:bad") + assert.Regexp(t, "FF00138", err) -// opID := fftypes.NewUUID() -// mo.On("Operations").Return(mom) -// mom.On("ResolveOperationByID", context.Background(), opID, mock.Anything).Return(nil) - -// err := nm.ResolveOperationByNamespacedID(context.Background(), "default:"+opID.String(), &core.OperationUpdateDTO{}) -// assert.Nil(t, err) - -// mo.AssertExpectations(t) -// mom.AssertExpectations(t) -// } - -// func TestResolveOperationByNamespacedIDBadID(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "default": {orchestrator: mo}, -// } - -// err := nm.ResolveOperationByNamespacedID(context.Background(), "default:bad", &core.OperationUpdateDTO{}) -// assert.Regexp(t, "FF00138", err) - -// mo.AssertExpectations(t) -// } - -// func TestResolveOperationByNamespacedIDNoOrchestrator(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// mo := &orchestratormocks.Orchestrator{} -// nm.namespaces = map[string]*namespace{ -// "default": {orchestrator: mo}, -// } - -// opID := fftypes.NewUUID() - -// err := nm.ResolveOperationByNamespacedID(context.Background(), "bad:"+opID.String(), &core.OperationUpdateDTO{}) -// assert.Regexp(t, "FF10109", err) - -// mo.AssertExpectations(t) -// } - -// func TestAuthorize(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() -// mo := &orchestratormocks.Orchestrator{} -// mo.On("Authorize", mock.Anything, mock.Anything).Return(nil) -// nm.namespaces["ns1"] = &namespace{ -// orchestrator: mo, -// } -// nm.utOrchestrator = mo -// err := nm.Authorize(context.Background(), &fftypes.AuthReq{ -// Namespace: "ns1", -// }) -// assert.NoError(t, err) - -// mo.AssertExpectations(t) -// } - -// func TestValidateNonMultipartyConfig(t *testing.T) { -// nm, cleanup := newTestNamespaceManager(t, true) -// defer cleanup() - -// viper.SetConfigType("yaml") -// err := viper.ReadConfig(strings.NewReader(` -// namespaces: -// default: ns1 -// predefined: -// - name: ns1 -// plugins: [postgres, erc721] -// `)) -// assert.NoError(t, err) - -// _, err = nm.validateNonMultipartyConfig(context.Background(), "ns1", []string{"postgres", "erc721"}) -// assert.NoError(t, err) -// } + mo.AssertExpectations(t) +} + +func TestGetOperationByNamespacedIDNoOrchestrator(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + mo := &orchestratormocks.Orchestrator{} + nm.namespaces = map[string]*namespace{ + "default": {orchestrator: mo}, + } + + opID := fftypes.NewUUID() + + _, err := nm.GetOperationByNamespacedID(context.Background(), "bad:"+opID.String()) + assert.Regexp(t, "FF10109", err) + + mo.AssertExpectations(t) +} + +func TestResolveOperationByNamespacedID(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + mo := &orchestratormocks.Orchestrator{} + mom := &operationmocks.Manager{} + nm.namespaces = map[string]*namespace{ + "default": {orchestrator: mo}, + } + + opID := fftypes.NewUUID() + mo.On("Operations").Return(mom) + mom.On("ResolveOperationByID", context.Background(), opID, mock.Anything).Return(nil) + + err := nm.ResolveOperationByNamespacedID(context.Background(), "default:"+opID.String(), &core.OperationUpdateDTO{}) + assert.Nil(t, err) + + mo.AssertExpectations(t) + mom.AssertExpectations(t) +} + +func TestResolveOperationByNamespacedIDBadID(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + mo := &orchestratormocks.Orchestrator{} + nm.namespaces = map[string]*namespace{ + "default": {orchestrator: mo}, + } + + err := nm.ResolveOperationByNamespacedID(context.Background(), "default:bad", &core.OperationUpdateDTO{}) + assert.Regexp(t, "FF00138", err) + + mo.AssertExpectations(t) +} + +func TestResolveOperationByNamespacedIDNoOrchestrator(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + mo := &orchestratormocks.Orchestrator{} + nm.namespaces = map[string]*namespace{ + "default": {orchestrator: mo}, + } + + opID := fftypes.NewUUID() + + err := nm.ResolveOperationByNamespacedID(context.Background(), "bad:"+opID.String(), &core.OperationUpdateDTO{}) + assert.Regexp(t, "FF10109", err) + + mo.AssertExpectations(t) +} + +func TestAuthorize(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + mo := &orchestratormocks.Orchestrator{} + mo.On("Authorize", mock.Anything, mock.Anything).Return(nil) + nm.namespaces["ns1"] = &namespace{ + orchestrator: mo, + } + nm.utOrchestrator = mo + err := nm.Authorize(context.Background(), &fftypes.AuthReq{ + Namespace: "ns1", + }) + assert.NoError(t, err) + + mo.AssertExpectations(t) +} + +func TestValidateNonMultipartyConfig(t *testing.T) { + nm, cleanup := newTestNamespaceManager(t) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [postgres, erc721] + `)) + assert.NoError(t, err) + + _, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.NoError(t, err) +} From 5d3a3c30cc445e745fd0b241a6c652f35853d041 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Fri, 9 Dec 2022 00:37:58 -0500 Subject: [PATCH 03/11] First E2E reload test worked through with validation of correct reload Signed-off-by: Peter Broadhurst --- internal/blockchain/ethereum/ethereum.go | 4 +- internal/namespace/configreload.go | 236 +++++++++ internal/namespace/configreload_test.go | 390 ++++++++++++++ internal/namespace/manager.go | 299 +++-------- internal/namespace/manager_test.go | 618 +++++++++++++---------- 5 files changed, 1045 insertions(+), 502 deletions(-) create mode 100644 internal/namespace/configreload.go create mode 100644 internal/namespace/configreload_test.go diff --git a/internal/blockchain/ethereum/ethereum.go b/internal/blockchain/ethereum/ethereum.go index 62292c9f0..14f1741b9 100644 --- a/internal/blockchain/ethereum/ethereum.go +++ b/internal/blockchain/ethereum/ethereum.go @@ -146,13 +146,13 @@ func (e *Ethereum) Init(ctx context.Context, cancelCtx context.CancelFunc, conf } if ethconnectConf.GetString(ffresty.HTTPConfigURL) == "" { - return i18n.NewError(ctx, coremsgs.MsgMissingPluginConfig, "url", "blockchain.ethereum.ethconnect") + return i18n.NewError(ctx, coremsgs.MsgMissingPluginConfig, "url", ethconnectConf) } e.client = ffresty.New(e.ctx, ethconnectConf) e.topic = ethconnectConf.GetString(EthconnectConfigTopic) if e.topic == "" { - return i18n.NewError(ctx, coremsgs.MsgMissingPluginConfig, "topic", "blockchain.ethereum.ethconnect") + return i18n.NewError(ctx, coremsgs.MsgMissingPluginConfig, "topic", ethconnectConf) } e.prefixShort = ethconnectConf.GetString(EthconnectPrefixShort) e.prefixLong = ethconnectConf.GetString(EthconnectPrefixLong) diff --git a/internal/namespace/configreload.go b/internal/namespace/configreload.go new file mode 100644 index 000000000..71e5c3237 --- /dev/null +++ b/internal/namespace/configreload.go @@ -0,0 +1,236 @@ +// Copyright © 2022 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed 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 namespace + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/fsnotify/fsnotify" + "github.com/hyperledger/firefly-common/pkg/fftypes" + "github.com/hyperledger/firefly-common/pkg/log" + "github.com/hyperledger/firefly/internal/coreconfig" + "github.com/spf13/viper" +) + +func (nm *namespaceManager) dumpRootConfig() (jsonTree fftypes.JSONObject) { + viperTree := viper.AllSettings() + b, _ := json.Marshal(viperTree) + _ = json.Unmarshal(b, &jsonTree) + return +} + +func (nm *namespaceManager) startConfigListener(ctx context.Context) { + go func() { + for { + log.L(ctx).Warnf("Starting configuration listener") + // Note there is no viper interface to make this end, so (apart from in unit tests) + // we never expect this to complete. + // To avoid this leak, we disable the use of the /spi/v1/reset API when config file + // listening is enabled. + viper.OnConfigChange(nm.configFileChanged) + nm.watchConfig() + select { + case <-ctx.Done(): + log.L(ctx).Debugf("Configuration listener ended") + return + default: + } + log.L(ctx).Warnf("Configuration listener ended (restarting)") + time.Sleep(5 * time.Second) + } + }() +} + +func (nm *namespaceManager) configFileChanged(in fsnotify.Event) { + log.L(nm.ctx).Infof("Detected configuration file reload: '%s'", in.Name) + + // Because of the things we do to make defaults work with arrays, we have to reset + // the config when it changes and re-read it. + coreconfig.Reset() + initAllConfig() + err := viper.ReadInConfig() + if err != nil { + log.L(nm.ctx).Errorf("Failed to re-read configuration after config reload notification: %s", err) + nm.cancelCtx() // stop the world + return + } + + nm.configReloaded(nm.ctx) +} + +func (nm *namespaceManager) configReloaded(ctx context.Context) { + + // Get Viper to dump the whole new config, with everything resolved across env vars + // and the config file etc. + // We use this to detect if anything has changed. + rawConfig := nm.dumpRootConfig() + + // Build the new set of plugins from the config (including those that are unchanged) + allPluginsInNewConf, err := nm.loadPlugins(ctx, rawConfig) + if err != nil { + log.L(ctx).Errorf("Failed to initialize plugins after config reload: %s", err) + return + } + + // Analyze the new list to see which plugins need to be updated, + // so we load the namespaces against the correct list of plugins + availablePlugins, updatedPlugins, pluginsToStop, err := nm.analyzePluginChanges(ctx, allPluginsInNewConf) + if err != nil { + log.L(ctx).Errorf("Failed to stop namespaces after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + + // Build the new set of namespaces (including those that are unchanged) + allNewNamespaces, err := nm.loadNamespaces(ctx, rawConfig, availablePlugins) + if err != nil { + log.L(ctx).Errorf("Failed to load namespaces after config reload: %s", err) + return + } + + // From this point we need to block any API calls resolving namespaces, + // until the reload is complete + nm.nsMux.Lock() + defer nm.nsMux.Unlock() + + // Stop all defunct namespaces + availableNS, updatedNamespaces, err := nm.stopDefunctNamespaces(ctx, availablePlugins, allNewNamespaces) + if err != nil { + log.L(ctx).Errorf("Failed to stop namespaces after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + + // Stop all defunct plugins - now the namespaces using them are all stopped + nm.stopDefunctPlugins(ctx, pluginsToStop) + + // Update the new lists + nm.plugins = availablePlugins + nm.namespaces = availableNS + + // Only initialize updated plugins + if err = nm.initPlugins(ctx, updatedPlugins); err != nil { + log.L(ctx).Errorf("Failed to initialize plugins after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + + // Only initialize the updated namespaces (which includes all that depend on above plugins) + if err = nm.initNamespaces(ctx, updatedNamespaces); err != nil { + log.L(ctx).Errorf("Failed to initialize namespaces after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + + // Now finally we can start all the new things + if err = nm.startNamespacesAndPlugins(updatedNamespaces, updatedPlugins); err != nil { + log.L(ctx).Errorf("Failed to initialize namespaces after config reload: %s", err) + nm.cancelCtx() // stop the world + return + } + +} + +func (nm *namespaceManager) stopDefunctNamespaces(ctx context.Context, newPlugins map[string]*plugin, newNamespaces map[string]*namespace) (availableNamespaces, updatedNamespaces map[string]*namespace, err error) { + + // build a set of all the namespaces we've either added new, or have changed + updatedNamespaces = make(map[string]*namespace) + availableNamespaces = make(map[string]*namespace) + namespacesToStop := make(map[string]*namespace) + for nsName, newNS := range newNamespaces { + if existingNS := nm.namespaces[nsName]; existingNS != nil { + var changes []string + if !existingNS.configHash.Equals(newNS.configHash) { + changes = append(changes, "namespace_config") + } + if len(existingNS.pluginNames) != len(newNS.pluginNames) { + changes = append(changes, "plugin_count") + } + for _, pluginName := range newNS.pluginNames { + existingPlugin := nm.plugins[pluginName] + newPlugin := newPlugins[pluginName] + if existingPlugin == nil || newPlugin == nil || + !existingPlugin.configHash.Equals(newPlugin.configHash) { + changes = append(changes, fmt.Sprintf("plugin:%s", pluginName)) + } + } + if len(changes) == 0 { + log.L(ctx).Debugf("Namespace '%s' unchanged after config reload", nsName) + availableNamespaces[nsName] = existingNS + continue + } + // We need to stop the existing namespace + log.L(ctx).Infof("Namespace '%s' configuration changed: %v", nsName, changes) + namespacesToStop[nsName] = existingNS + } + // This is either changed, or brand new - mark it in the map + availableNamespaces[nsName] = newNS + updatedNamespaces[nsName] = newNS + } + + // Stop everything we need to stop + for nsName, existingNS := range nm.namespaces { + if namespacesToStop[nsName] != nil || newNamespaces[nsName] == nil { + log.L(ctx).Debugf("Stopping namespace '%s' after config reload. Loaded at %s", nsName, existingNS.loadTime) + nm.stopNamespace(ctx, existingNS) + } + } + + return availableNamespaces, updatedNamespaces, nil + +} + +func (nm *namespaceManager) analyzePluginChanges(ctx context.Context, newPlugins map[string]*plugin) (availablePlugins, updatedPlugins, pluginsToStop map[string]*plugin, err error) { + + // build a set of all the plugins we've either added new, or have changed + availablePlugins = make(map[string]*plugin) + updatedPlugins = make(map[string]*plugin) + pluginsToStop = make(map[string]*plugin) + for pluginName, newPlugin := range newPlugins { + if existingPlugin := nm.plugins[pluginName]; existingPlugin != nil { + if existingPlugin.configHash.Equals(newPlugin.configHash) { + log.L(ctx).Debugf("Plugin '%s' unchanged after config reload", pluginName) + availablePlugins[pluginName] = existingPlugin + continue + } + // We need to stop the existing plugin + pluginsToStop[pluginName] = existingPlugin + } + // This is either changed, or brand new - mark it in the map + updatedPlugins[pluginName] = newPlugin + availablePlugins[pluginName] = newPlugin + } + + // Look for everything that's deleted + for pluginName, existingPlugin := range nm.plugins { + if newPlugins[pluginName] == nil { + pluginsToStop[pluginName] = existingPlugin + } + } + + return +} + +func (nm *namespaceManager) stopDefunctPlugins(ctx context.Context, pluginsToStop map[string]*plugin) { + for pluginName, plugin := range pluginsToStop { + log.L(ctx).Debugf("Stopping plugin '%s' after config reload. Loaded at %s", pluginName, plugin.loadTime) + plugin.cancelCtx() + } +} diff --git a/internal/namespace/configreload_test.go b/internal/namespace/configreload_test.go new file mode 100644 index 000000000..389c1e0b2 --- /dev/null +++ b/internal/namespace/configreload_test.go @@ -0,0 +1,390 @@ +// Copyright © 2022 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed 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 namespace + +import ( + "context" + "strings" + "testing" + + "github.com/hyperledger/firefly-common/pkg/fftypes" + "github.com/hyperledger/firefly/internal/coreconfig" + "github.com/hyperledger/firefly/pkg/core" + "github.com/hyperledger/firefly/pkg/database" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +var exampleConfig1base = ` +--- +http: + address: 0.0.0.0 + port: 5000 + publicURL: https://myfirefly.example.com +log: + level: debug +metrics: + enabled: true + address: 127.0.0.1 + port: 6000 + path: /metrics +namespaces: + default: ns1 + predefined: + - defaultKey: 0xbEa50Ec98776beF144Fc63078e7b15291Ac64cfA + name: ns1 + plugins: + - sharedstorage-ns1 + - database0 + - blockchain-ns1 + - ff-dx + - erc1155-ns1 + - erc20_erc721-ns1 + - test_user_auth-ns1 + multiparty: + enabled: true + node: + name: node1 + org: + name: org1 + key: 0xbEa50Ec98776beF144Fc63078e7b15291Ac64cfA + contract: + - firstevent: "0" + location: + address: 0x7359d2ecc199C48369b390522c29b77A5Af30882 + - defaultKey: 0x630659A26fa005d50Fa9706D8a4e242fd4169A61 + name: ns2 + plugins: + - database0 + - blockchain-ns2 + - test_user_auth-ns2 + multiparty: {} +node: {} +plugins: + blockchain: + - ethereum: + ethconnect: + topic: "0" + url: https://ethconnect1.example.com:5000 + name: blockchain-ns1 + type: ethereum + - ethereum: + ethconnect: + topic: "0" + url: https://ethconnect2.example.com:5000 + name: blockchain-ns2 + type: ethereum + database: + - name: database0 + postgres: + url: postgres://postgrs.example.com:5432/firefly?sslmode=require + type: postgres + dataexchange: + - ffdx: + url: https://ffdx:3000 + initEnabled: true + name: ff-dx + type: ffdx + sharedstorage: + - ipfs: + api: + url: https://ipfs1.example.com + auth: + username: someuser + password: somepass + gateway: + url: https://ipfs1.example.com + auth: + username: someuser + password: somepass + name: sharedstorage-ns1 + type: ipfs + - ipfs: + api: + url: https://ipfs2.example.com + gateway: + url: https://ipfs2.example.com + name: sharedstorage-ns2 + type: ipfs + tokens: + - fftokens: + url: https://ff-tokens-ns1-erc1155:3000 + name: erc1155-ns1 + type: fftokens + - fftokens: + url: https://ff-tokens-ns1-erc20-erc721:3000 + name: erc20_erc721-ns1 + type: fftokens + - fftokens: + url: https://ff-tokens-ns2-erc20-erc721:3000 + name: erc20_erc721-ns2 + type: fftokens + auth: + - name: test_user_auth-ns1 + type: basic + basic: + passwordfile: /etc/firefly/test_users + - name: test_user_auth-ns2 + type: basic + basic: + passwordfile: /etc/firefly/test_users +ui: + path: ./frontend +` + +// here we deliberately make a bunch of ordering changes in fields, +// but the only actual difference is the creation of a third namespace, +// and some NEW plugins +var exampleConfig2extraNS = ` +--- +http: + address: 0.0.0.0 + port: 5000 + publicURL: https://myfirefly.example.com +ui: + path: ./frontend +namespaces: + default: ns1 + predefined: + - defaultKey: 0xbEa50Ec98776beF144Fc63078e7b15291Ac64cfA + name: ns1 + plugins: + - sharedstorage-ns1 + - database0 + - blockchain-ns1 + - ff-dx + - erc1155-ns1 + - erc20_erc721-ns1 + - test_user_auth-ns1 + multiparty: + enabled: true + node: + name: node1 + org: + name: org1 + key: 0xbEa50Ec98776beF144Fc63078e7b15291Ac64cfA + contract: + - firstevent: "0" + location: + address: 0x7359d2ecc199C48369b390522c29b77A5Af30882 + - defaultKey: 0x630659A26fa005d50Fa9706D8a4e242fd4169A61 + plugins: + - database0 + - blockchain-ns2 + - test_user_auth-ns2 + name: ns2 + multiparty: {} + - defaultKey: 0xF49C223038FA129c2Ba23D5c5f3Cdb50120F3EDe + name: ns3 + plugins: + - database0 + - blockchain-ns3 + - test_user_auth-ns3 + multiparty: {} +node: {} +log: + level: debug +metrics: + enabled: true + address: 127.0.0.1 + port: 6000 + path: /metrics +plugins: + blockchain: + - ethereum: + ethconnect: + topic: "0" + url: https://ethconnect1.example.com:5000 + name: blockchain-ns1 + type: ethereum + - ethereum: + ethconnect: + topic: "0" + url: https://ethconnect2.example.com:5000 + name: blockchain-ns2 + type: ethereum + - ethereum: + ethconnect: + topic: "0" + url: https://ethconnect3.example.com:5000 + name: blockchain-ns3 + type: ethereum + database: + - name: database0 + type: postgres + postgres: + url: postgres://postgrs.example.com:5432/firefly?sslmode=require + dataexchange: + - ffdx: + url: https://ffdx:3000 + initEnabled: true + name: ff-dx + type: ffdx + sharedstorage: + - ipfs: + gateway: + url: https://ipfs1.example.com + auth: + username: someuser + password: somepass + api: + url: https://ipfs1.example.com + auth: + username: someuser + password: somepass + type: ipfs + name: sharedstorage-ns1 + - ipfs: + api: + url: https://ipfs2.example.com + gateway: + url: https://ipfs2.example.com + name: sharedstorage-ns2 + type: ipfs + tokens: + - fftokens: + url: https://ff-tokens-ns1-erc1155:3000 + type: fftokens + name: erc1155-ns1 + - fftokens: + url: https://ff-tokens-ns1-erc20-erc721:3000 + type: fftokens + name: erc20_erc721-ns1 + - fftokens: + url: https://ff-tokens-ns2-erc20-erc721:3000 + type: fftokens + name: erc20_erc721-ns2 + auth: + - name: test_user_auth-ns1 + basic: + passwordfile: /etc/firefly/test_users + type: basic + - name: test_user_auth-ns2 + basic: + passwordfile: /etc/firefly/test_users + type: basic + - name: test_user_auth-ns3 + type: basic + basic: + passwordfile: /etc/firefly/test_users +` + +func mockInitConfig(nm *testNamespaceManager) { + nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() + nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) + nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mti[1].On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + nm.mei[0].On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mdi.On("GetNamespace", mock.Anything, "ns1").Return(nil, nil) + nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) + nm.mdi.On("GetNamespace", mock.Anything, "ns3").Return(nil, nil).Maybe() + nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + nm.mbi.On("Start").Return(nil) + nm.mdx.On("Start").Return(nil) + nm.mti[1].On("Start").Return(nil) + + nm.mo.On("Init", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + if nm.namespaces["ns1"] != nil && nm.namespaces["ns1"].Contracts != nil { + nm.namespaces["ns1"].Contracts.Active = &core.MultipartyContract{ + Info: core.MultipartyContractInfo{ + Version: 2, + }, + } + } + }). + Return(nil) + nm.mo.On("Start").Return(nil) +} + +func TestConfigReload1to2(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + nm, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(exampleConfig1base)) + assert.NoError(t, err) + + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + mockInitConfig(nm) + + err = nm.Init(ctx, cancelCtx, make(chan bool)) + assert.NoError(t, err) + + err = nm.Start() + assert.NoError(t, err) + + originalPlugins := nm.plugins + originalPluginHashes := make(map[string]*fftypes.Bytes32) + for k, v := range originalPlugins { + originalPluginHashes[k] = v.configHash + } + originaNS := nm.namespaces + originalNSHashes := make(map[string]*fftypes.Bytes32) + for k, v := range originaNS { + originalNSHashes[k] = v.configHash + } + + coreconfig.Reset() + initAllConfig() + viper.SetConfigType("yaml") + err = viper.ReadConfig(strings.NewReader(exampleConfig2extraNS)) + assert.NoError(t, err) + + // Drive the config reload + nm.configReloaded(nm.ctx) + + // Check that we didn't cancel the context + select { + case <-nm.ctx.Done(): + assert.Fail(t, "Error occurred in config reload") + default: + } + + // Check none of the plugins reloaded + for name := range originalPlugins { + assert.True(t, originalPlugins[name] == nm.plugins[name], name) + assert.Equal(t, originalPluginHashes[name], nm.plugins[name].configHash, name) + } + + // Check we have two more than before + assert.Len(t, nm.plugins, len(originalPlugins)+2) + assert.NotNil(t, nm.plugins["blockchain-ns3"]) + assert.NotNil(t, nm.plugins["test_user_auth-ns3"]) + + // Check none of the namespaces reloaded + for name := range originaNS { + assert.True(t, originaNS[name] == nm.namespaces[name], name) + assert.Equal(t, originalNSHashes[name], nm.namespaces[name].configHash, name) + } + + // Check we have one more than before + assert.Len(t, nm.namespaces, len(originaNS)+1) + assert.NotNil(t, nm.namespaces["ns3"]) + +} diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go index ea4d7325e..93f406296 100644 --- a/internal/namespace/manager.go +++ b/internal/namespace/manager.go @@ -18,13 +18,10 @@ package namespace import ( "context" - "encoding/json" "fmt" "strconv" "sync" - "time" - "github.com/fsnotify/fsnotify" "github.com/hyperledger/firefly-common/pkg/auth" "github.com/hyperledger/firefly-common/pkg/auth/authfactory" "github.com/hyperledger/firefly-common/pkg/config" @@ -65,6 +62,7 @@ var ( dataexchangeConfig = config.RootArray("plugins.dataexchange") identityConfig = config.RootArray("plugins.identity") authConfig = config.RootArray("plugins.auth") + eventsConfig = config.RootSection("events") // still at root // Deprecated configs deprecatedTokensConfig = config.RootArray("tokens") @@ -93,7 +91,7 @@ type namespace struct { ctx context.Context cancelCtx context.CancelFunc orchestrator orchestrator.Orchestrator - loadTime fftypes.FFTime + loadTime *fftypes.FFTime config orchestrator.Config configHash *fftypes.Bytes32 pluginNames []string @@ -111,9 +109,18 @@ type namespaceManager struct { cacheManager cache.Manager metrics metrics.Manager adminEvents spievents.Manager - utOrchestrator orchestrator.Orchestrator tokenBroadcastNames map[string]string watchConfig func() // indirect from viper.WatchConfig for testing + + orchestratorFactory func(ns *core.Namespace, config orchestrator.Config, plugins *orchestrator.Plugins, metrics metrics.Manager, cacheManager cache.Manager) orchestrator.Orchestrator + blockchainFactory func(ctx context.Context, pluginType string) (blockchain.Plugin, error) + databaseFactory func(ctx context.Context, pluginType string) (database.Plugin, error) + dataexchangeFactory func(ctx context.Context, pluginType string) (dataexchange.Plugin, error) + sharedstorageFactory func(ctx context.Context, pluginType string) (sharedstorage.Plugin, error) + tokensFactory func(ctx context.Context, pluginType string) (tokens.Plugin, error) + identityFactory func(ctx context.Context, pluginType string) (identity.Plugin, error) + eventsFactory func(ctx context.Context, pluginType string) (events.Plugin, error) + authFactory func(ctx context.Context, pluginType string) (auth.Plugin, error) } type pluginCategory string @@ -137,7 +144,7 @@ type plugin struct { cancelCtx context.CancelFunc config config.Section configHash *fftypes.Bytes32 - loadTime time.Time + loadTime *fftypes.FFTime blockchain blockchain.Plugin database database.Plugin @@ -167,6 +174,16 @@ func NewNamespaceManager() Manager { metricsEnabled: config.GetBool(coreconfig.MetricsEnabled), tokenBroadcastNames: make(map[string]string), watchConfig: viper.WatchConfig, + + orchestratorFactory: orchestrator.NewOrchestrator, + blockchainFactory: bifactory.GetPlugin, + databaseFactory: difactory.GetPlugin, + dataexchangeFactory: dxfactory.GetPlugin, + sharedstorageFactory: ssfactory.GetPlugin, + tokensFactory: tifactory.GetPlugin, + identityFactory: iifactory.GetPlugin, + eventsFactory: eifactory.GetPlugin, + authFactory: authfactory.GetPlugin, } initAllConfig() return nm @@ -189,182 +206,12 @@ func initAllConfig() { tifactory.InitConfigDeprecated(deprecatedTokensConfig) tifactory.InitConfig(tokensConfig) authfactory.InitConfigArray(authConfig) - - // Events still live at the root of the config - eifactory.InitConfig(config.RootSection("events")) -} - -func (nm *namespaceManager) startConfigListener(ctx context.Context) { - go func() { - for { - log.L(ctx).Warnf("Starting configuration listener") - // Note there is no viper interface to make this end, so (apart from in unit tests) - // we never expect this to complete. - // To avoid this leak, we disable the use of the /spi/v1/reset API when config file - // listening is enabled. - viper.OnConfigChange(nm.newConfigChangeListener(ctx)) - nm.watchConfig() - select { - case <-ctx.Done(): - log.L(ctx).Debugf("Configuration listener ended") - return - default: - } - log.L(ctx).Warnf("Configuration listener ended (restarting)") - time.Sleep(5 * time.Second) - } - }() -} - -func (nm *namespaceManager) newConfigChangeListener(ctx context.Context) func(in fsnotify.Event) { - return func(in fsnotify.Event) { - nm.configFileChanged(ctx, in.Name) - } -} - -func (nm *namespaceManager) dumpRootConfig() (jsonTree fftypes.JSONObject) { - viperTree := viper.AllSettings() - b, _ := json.Marshal(viperTree) - _ = json.Unmarshal(b, &jsonTree) - return -} - -func (nm *namespaceManager) configFileChanged(ctx context.Context, filename string) { - log.L(ctx).Infof("Detected configuration file reload: '%s'", filename) - - // Get Viper to dump the whole new config, with everything resolved across env vars - // and the config file etc. - // We use this to detect if anything has changed. - rawConfig := nm.dumpRootConfig() - - newPlugins, err := nm.loadPlugins(ctx, rawConfig) - if err != nil { - log.L(ctx).Errorf("Failed to initialize plugins after config reload: %s", err) - return - } - - allNewNamespaces, err := nm.loadNamespaces(ctx, rawConfig, newPlugins) - if err != nil { - log.L(ctx).Errorf("Failed to load namespaces after config reload: %s", err) - return - } - - // From this point we need to block any API calls resolving namespaces, until the reload is complete - nm.nsMux.Lock() - defer nm.nsMux.Unlock() - - // Stop all defunct namespaces - updatedNamespaces, err := nm.stopDefunctNamespaces(ctx, newPlugins, allNewNamespaces) - if err != nil { - log.L(ctx).Errorf("Failed to stop namespaces after config reload: %s", err) - nm.cancelCtx() // stop the world - return - } - - // Stop all defunct plugins - now the namespaces using them are all stopped - updatedPlugins, err := nm.stopDefunctPlugins(ctx, newPlugins) - if err != nil { - log.L(ctx).Errorf("Failed to stop namespaces after config reload: %s", err) - nm.cancelCtx() // stop the world - return - } - - // Update the new lists - nm.plugins = newPlugins - nm.namespaces = allNewNamespaces - - // Only initialize updated plugins - if err = nm.initPlugins(ctx, updatedPlugins); err != nil { - log.L(ctx).Errorf("Failed to initialize plugins after config reload: %s", err) - nm.cancelCtx() // stop the world - return - } - - // Only initialize the updated namespaces (which includes all that depend on above plugins) - if err = nm.initNamespaces(ctx, updatedNamespaces); err != nil { - log.L(ctx).Errorf("Failed to initialize namespaces after config reload: %s", err) - nm.cancelCtx() // stop the world - return - } - - // Now finally we can start all the new things - if err = nm.startNamespacesAndPlugins(updatedNamespaces, updatedPlugins); err != nil { - log.L(ctx).Errorf("Failed to initialize namespaces after config reload: %s", err) - nm.cancelCtx() // stop the world - return - } - -} - -func (nm *namespaceManager) stopDefunctNamespaces(ctx context.Context, newPlugins map[string]*plugin, newNamespaces map[string]*namespace) (map[string]*namespace, error) { - - // build a set of all the namespaces we've either added new, or have changed - updatedNamespaces := make(map[string]*namespace) - namespacesToStop := make(map[string]*namespace) - for nsName, newNS := range newNamespaces { - if existingNS := nm.namespaces[nsName]; existingNS != nil { - unchanged := existingNS.configHash.Equals(newNS.configHash) && - len(existingNS.pluginNames) == len(newNS.pluginNames) - for _, pluginName := range newNS.pluginNames { - existingPlugin := nm.plugins[pluginName] - newPlugin := newPlugins[pluginName] - unchanged = existingPlugin != nil && newPlugin != nil && - existingPlugin.configHash.Equals(newPlugin.configHash) - } - if unchanged { - log.L(ctx).Debugf("Namespace '%s' unchanged after config reload", nsName) - continue - } - // We need to stop the existing namespace - namespacesToStop[nsName] = existingNS - } - // This is either changed, or brand new - mark it in the map - updatedNamespaces[nsName] = newNS - } - - // Stop everything we need to stop - for nsName, existingNS := range nm.namespaces { - if namespacesToStop[nsName] != nil || newNamespaces[nsName] == nil { - log.L(ctx).Debugf("Stopping namespace '%s' after config reload. Loaded at %s", nsName, existingNS.loadTime) - nm.stopNamespace(ctx, existingNS) - } - } - - return updatedNamespaces, nil - -} - -func (nm *namespaceManager) stopDefunctPlugins(ctx context.Context, newPlugins map[string]*plugin) (map[string]*plugin, error) { - - // build a set of all the plugins we've either added new, or have changed - updatedPlugins := make(map[string]*plugin) - pluginsToStop := make(map[string]*plugin) - for pluginName, newPlugin := range newPlugins { - if existingPlugin := nm.plugins[pluginName]; existingPlugin != nil { - if existingPlugin.configHash.Equals(newPlugin.configHash) { - log.L(ctx).Debugf("Plugin '%s' unchanged after config reload", pluginName) - continue - } - // We need to stop the existing plugin - pluginsToStop[pluginName] = existingPlugin - } - // This is either changed, or brand new - mark it in the map - updatedPlugins[pluginName] = newPlugin - } - - // Stop everything we need to stop - for pluginName, existingPlugin := range nm.plugins { - if pluginsToStop[pluginName] != nil || newPlugins[pluginName] == nil { - log.L(ctx).Debugf("Stopping plugin '%s' after config reload. Loaded at %s", pluginName, existingPlugin.loadTime) - existingPlugin.cancelCtx() - } - } - - return updatedPlugins, nil + eifactory.InitConfig(eventsConfig) } func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFunc, reset chan bool) (err error) { nm.reset = reset + nm.ctx = ctx nm.cancelCtx = cancelCtx initTimeRawConfig := nm.dumpRootConfig() @@ -439,6 +286,7 @@ func (nm *namespaceManager) initNamespaces(ctx context.Context, newNamespaces ma if v1Namespace != nil { systemNS := &namespace{ Namespace: v1Namespace.Namespace, + loadTime: v1Namespace.loadTime, config: v1Namespace.config, pluginNames: v1Namespace.pluginNames, plugins: v1Namespace.plugins, @@ -491,13 +339,9 @@ func (nm *namespaceManager) initNamespace(ctx context.Context, ns *namespace) (e return err } - or := nm.utOrchestrator - if or == nil { - or = orchestrator.NewOrchestrator(&ns.Namespace, ns.config, ns.plugins, nm.metrics, nm.cacheManager) - } - ns.orchestrator = or + ns.orchestrator = nm.orchestratorFactory(&ns.Namespace, ns.config, ns.plugins, nm.metrics, nm.cacheManager) ns.ctx, ns.cancelCtx = context.WithCancel(ctx) - if err := or.Init(ns.ctx, ns.cancelCtx); err != nil { + if err := ns.orchestrator.Init(ns.ctx, ns.cancelCtx); err != nil { return err } return nil @@ -630,8 +474,7 @@ func (nm *namespaceManager) loadPlugins(ctx context.Context, rawConfig fftypes.J } func (nm *namespaceManager) configHash(rawConfigObject fftypes.JSONObject) *fftypes.Bytes32 { - b, _ := json.Marshal(rawConfigObject) - return fftypes.HashString(string(b)) + return fftypes.HashString(rawConfigObject.String()) } func (nm *namespaceManager) getTokensPlugins(ctx context.Context, plugins map[string]*plugin, rawConfig fftypes.JSONObject) (err error) { @@ -646,8 +489,7 @@ func (nm *namespaceManager) getTokensPlugins(ctx context.Context, plugins map[st } for i := 0; i < tokensConfigArraySize; i++ { config := tokensConfig.ArrayEntry(i) - configHash := nm.configHash(rawPluginTokensConfig[i]) - pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryTokens, config, configHash) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryTokens, config, rawPluginTokensConfig[i]) if err != nil { return err } @@ -662,7 +504,7 @@ func (nm *namespaceManager) getTokensPlugins(ctx context.Context, plugins map[st broadcastNames[broadcastName] = true nm.tokenBroadcastNames[pc.name] = broadcastName - pc.tokens, err = tifactory.GetPlugin(ctx, pc.pluginType) + pc.tokens, err = nm.tokensFactory(ctx, pc.pluginType) if err != nil { return err } @@ -687,13 +529,12 @@ func (nm *namespaceManager) getTokensPlugins(ctx context.Context, plugins map[st } nm.tokenBroadcastNames[name] = name - configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("tokens")) - pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryTokens, name, pluginType, deprecatedConfig, configHash) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryTokens, name, pluginType, deprecatedConfig, rawConfig.GetObject("plugins").GetObject("tokens")) if err != nil { return err } - pc.tokens, err = tifactory.GetPlugin(ctx, pluginType) + pc.tokens, err = nm.tokensFactory(ctx, pluginType) if err != nil { return err } @@ -712,13 +553,12 @@ func (nm *namespaceManager) getDatabasePlugins(ctx context.Context, plugins map[ } for i := 0; i < dbConfigArraySize; i++ { config := databaseConfig.ArrayEntry(i) - configHash := nm.configHash(rawPluginDatabaseConfig[i]) - pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryDatabase, config, configHash) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryDatabase, config, rawPluginDatabaseConfig[i]) if err != nil { return err } - pc.database, err = difactory.GetPlugin(ctx, pc.pluginType) + pc.database, err = nm.databaseFactory(ctx, pc.pluginType) if err != nil { return err } @@ -729,12 +569,11 @@ func (nm *namespaceManager) getDatabasePlugins(ctx context.Context, plugins map[ pluginType := deprecatedDatabaseConfig.GetString(coreconfig.PluginConfigType) if pluginType != "" { log.L(ctx).Warnf("Your database config uses a deprecated configuration structure - the database configuration has been moved under the 'plugins' section") - configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("database")) - pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryDatabase, "database_0", pluginType, deprecatedDatabaseConfig, configHash) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryDatabase, "database_0", pluginType, deprecatedDatabaseConfig, rawConfig.GetObject("plugins").GetObject("database")) if err != nil { return err } - pc.database, err = difactory.GetPlugin(ctx, pluginType) + pc.database, err = nm.databaseFactory(ctx, pluginType) if err != nil { return err } @@ -744,7 +583,7 @@ func (nm *namespaceManager) getDatabasePlugins(ctx context.Context, plugins map[ return nil } -func (nm *namespaceManager) validatePluginConfig(ctx context.Context, plugins map[string]*plugin, category pluginCategory, config config.Section, configHash *fftypes.Bytes32) (*plugin, error) { +func (nm *namespaceManager) validatePluginConfig(ctx context.Context, plugins map[string]*plugin, category pluginCategory, config config.Section, rawConfig fftypes.JSONObject) (*plugin, error) { name := config.GetString(coreconfig.PluginConfigName) pluginType := config.GetString(coreconfig.PluginConfigType) @@ -756,10 +595,10 @@ func (nm *namespaceManager) validatePluginConfig(ctx context.Context, plugins ma return nil, err } - return nm.newPluginCommon(ctx, plugins, category, name, pluginType, config, configHash) + return nm.newPluginCommon(ctx, plugins, category, name, pluginType, config, rawConfig) } -func (nm *namespaceManager) newPluginCommon(ctx context.Context, plugins map[string]*plugin, category pluginCategory, name, pluginType string, config config.Section, configHash *fftypes.Bytes32) (*plugin, error) { +func (nm *namespaceManager) newPluginCommon(ctx context.Context, plugins map[string]*plugin, category pluginCategory, name, pluginType string, config config.Section, rawConfig fftypes.JSONObject) (*plugin, error) { if _, ok := plugins[name]; ok { return nil, i18n.NewError(ctx, coremsgs.MsgDuplicatePluginName, name) } @@ -768,10 +607,11 @@ func (nm *namespaceManager) newPluginCommon(ctx context.Context, plugins map[str name: name, category: category, pluginType: pluginType, - config: config, - configHash: configHash, - loadTime: time.Now(), + config: config.SubSection(pluginType), + configHash: nm.configHash(rawConfig), + loadTime: fftypes.Now(), } + log.L(ctx).Tracef("Plugin %s config: %s", name, rawConfig.String()) plugins[name] = pc // context is always inherited from namespaceManager BG context _not_ the context of the caller pc.ctx, pc.cancelCtx = context.WithCancel(nm.ctx) @@ -787,13 +627,12 @@ func (nm *namespaceManager) getDataExchangePlugins(ctx context.Context, plugins } for i := 0; i < dxConfigArraySize; i++ { config := dataexchangeConfig.ArrayEntry(i) - configHash := nm.configHash(rawPluginDXConfig[i]) - pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryDataexchange, config, configHash) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryDataexchange, config, rawPluginDXConfig[i]) if err != nil { return err } - pc.dataexchange, err = dxfactory.GetPlugin(ctx, pc.pluginType) + pc.dataexchange, err = nm.dataexchangeFactory(ctx, pc.pluginType) if err != nil { return err } @@ -804,12 +643,11 @@ func (nm *namespaceManager) getDataExchangePlugins(ctx context.Context, plugins pluginType := deprecatedDataexchangeConfig.GetString(coreconfig.PluginConfigType) if pluginType != "" { log.L(ctx).Warnf("Your data exchange config uses a deprecated configuration structure - the data exchange configuration has been moved under the 'plugins' section") - configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("dataexchange")) - pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryDataexchange, "dataexchange_0", pluginType, deprecatedDataexchangeConfig, configHash) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryDataexchange, "dataexchange_0", pluginType, deprecatedDataexchangeConfig, rawConfig.GetObject("plugins").GetObject("dataexchange")) if err != nil { return err } - pc.dataexchange, err = dxfactory.GetPlugin(ctx, pluginType) + pc.dataexchange, err = nm.dataexchangeFactory(ctx, pluginType) if err != nil { return err } @@ -828,13 +666,12 @@ func (nm *namespaceManager) getIdentityPlugins(ctx context.Context, plugins map[ } for i := 0; i < configSize; i++ { config := identityConfig.ArrayEntry(i) - configHash := nm.configHash(rawPluginIdentityConfig[i]) - pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryIdentity, config, configHash) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryIdentity, config, rawPluginIdentityConfig[i]) if err != nil { return err } - pc.identity, err = iifactory.GetPlugin(ctx, pc.pluginType) + pc.identity, err = nm.identityFactory(ctx, pc.pluginType) if err != nil { return err } @@ -852,13 +689,12 @@ func (nm *namespaceManager) getBlockchainPlugins(ctx context.Context, plugins ma } for i := 0; i < blockchainConfigArraySize; i++ { config := blockchainConfig.ArrayEntry(i) - configHash := nm.configHash(rawPluginBlockchainsConfig[i]) - pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryBlockchain, config, configHash) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryBlockchain, config, rawPluginBlockchainsConfig[i]) if err != nil { return err } - pc.blockchain, err = bifactory.GetPlugin(ctx, pc.pluginType) + pc.blockchain, err = nm.blockchainFactory(ctx, pc.pluginType) if err != nil { return err } @@ -870,12 +706,11 @@ func (nm *namespaceManager) getBlockchainPlugins(ctx context.Context, plugins ma if pluginType != "" { log.L(ctx).Warnf("Your blockchain config uses a deprecated configuration structure - the blockchain configuration has been moved under the 'plugins' section") - configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("blockchain")) - pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryBlockchain, "blockchain_0", pluginType, deprecatedBlockchainConfig, configHash) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryBlockchain, "blockchain_0", pluginType, deprecatedBlockchainConfig, rawConfig.GetObject("plugins").GetObject("blockchain")) if err != nil { return err } - pc.blockchain, err = bifactory.GetPlugin(ctx, pluginType) + pc.blockchain, err = nm.blockchainFactory(ctx, pluginType) if err != nil { return err } @@ -894,13 +729,12 @@ func (nm *namespaceManager) getSharedStoragePlugins(ctx context.Context, plugins } for i := 0; i < configSize; i++ { config := sharedstorageConfig.ArrayEntry(i) - configHash := nm.configHash(rawPluginSharedStorageConfig[i]) - pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategorySharedstorage, config, configHash) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategorySharedstorage, config, rawPluginSharedStorageConfig[i]) if err != nil { return err } - pc.sharedstorage, err = ssfactory.GetPlugin(ctx, pc.pluginType) + pc.sharedstorage, err = nm.sharedstorageFactory(ctx, pc.pluginType) if err != nil { return err } @@ -912,12 +746,11 @@ func (nm *namespaceManager) getSharedStoragePlugins(ctx context.Context, plugins if pluginType != "" { log.L(ctx).Warnf("Your shared storage config uses a deprecated configuration structure - the shared storage configuration has been moved under the 'plugins' section") - configHash := nm.configHash(rawConfig.GetObject("plugins").GetObject("sharedstorage")) - pc, err := nm.newPluginCommon(ctx, plugins, pluginCategorySharedstorage, "sharedstorage_0", pluginType, deprecatedBlockchainConfig, configHash) + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategorySharedstorage, "sharedstorage_0", pluginType, deprecatedSharedStorageConfig, rawConfig.GetObject("plugins").GetObject("sharedstorage")) if err != nil { return err } - pc.sharedstorage, err = ssfactory.GetPlugin(ctx, pluginType) + pc.sharedstorage, err = nm.sharedstorageFactory(ctx, pluginType) if err != nil { return err } @@ -1119,10 +952,12 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde NetworkName: networkName, Description: conf.GetString(coreconfig.NamespaceDescription), }, + loadTime: fftypes.Now(), config: config, configHash: nm.configHash(rawNSConfig), pluginNames: pluginNames, } + log.L(ctx).Tracef("Namespace %s config: %s", name, rawNSConfig.String()) if ns.plugins, err = nm.validateNSPlugins(ctx, ns, availablePlugins); err != nil { return nil, err @@ -1293,20 +1128,21 @@ func (nm *namespaceManager) getEventPlugins(ctx context.Context, plugins map[str uniqueTransports[system.SystemEventsTransport] = true for transport := range uniqueTransports { - eventsPlugin, err := eifactory.GetPlugin(ctx, transport) + eventsPlugin, err := nm.eventsFactory(ctx, transport) if err != nil { return err } name := eventsPlugin.Name() - section := config.RootSection("events").SubSection(name) rawEventConfig := rawConfig.GetObject("events").GetObject(name) - pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryEvents, name, name /* name is category for events */, section, nm.configHash(rawEventConfig)) + eventsSection := config.RootSection("events") + pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryEvents, + name, name, /* name is category for events */ + eventsSection, rawEventConfig) if err != nil { return err } pc.events = eventsPlugin - pc.events.InitConfig(section) } return nil } @@ -1320,13 +1156,12 @@ func (nm *namespaceManager) getAuthPlugin(ctx context.Context, plugins map[strin } for i := 0; i < authConfigArraySize; i++ { config := authConfig.ArrayEntry(i) - configHash := nm.configHash(rawPluginAuthConfig[i]) - pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryAuth, config, configHash) + pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryAuth, config, rawPluginAuthConfig[i]) if err != nil { return err } - pc.auth, err = authfactory.GetPlugin(ctx, pc.pluginType) + pc.auth, err = nm.authFactory(ctx, pc.pluginType) if err != nil { return err } diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go index 0384613a3..fe19f3404 100644 --- a/internal/namespace/manager_test.go +++ b/internal/namespace/manager_test.go @@ -23,14 +23,17 @@ import ( "testing" "github.com/hyperledger/firefly-common/mocks/authmocks" + "github.com/hyperledger/firefly-common/pkg/auth" "github.com/hyperledger/firefly-common/pkg/auth/authfactory" "github.com/hyperledger/firefly-common/pkg/config" "github.com/hyperledger/firefly-common/pkg/fftypes" "github.com/hyperledger/firefly/internal/blockchain/bifactory" + "github.com/hyperledger/firefly/internal/cache" "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/internal/database/difactory" "github.com/hyperledger/firefly/internal/dataexchange/dxfactory" "github.com/hyperledger/firefly/internal/identity/iifactory" + "github.com/hyperledger/firefly/internal/metrics" "github.com/hyperledger/firefly/internal/orchestrator" "github.com/hyperledger/firefly/internal/sharedstorage/ssfactory" "github.com/hyperledger/firefly/internal/tokens/tifactory" @@ -46,14 +49,67 @@ import ( "github.com/hyperledger/firefly/mocks/sharedstoragemocks" "github.com/hyperledger/firefly/mocks/spieventsmocks" "github.com/hyperledger/firefly/mocks/tokenmocks" + "github.com/hyperledger/firefly/pkg/blockchain" "github.com/hyperledger/firefly/pkg/core" "github.com/hyperledger/firefly/pkg/database" + "github.com/hyperledger/firefly/pkg/dataexchange" + "github.com/hyperledger/firefly/pkg/events" + "github.com/hyperledger/firefly/pkg/identity" + "github.com/hyperledger/firefly/pkg/sharedstorage" "github.com/hyperledger/firefly/pkg/tokens" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) +var testBaseConfig = ` +--- +namespaces: + predefined: + - defaultKey: 0xbEa50Ec98776beF144Fc63078e7b15291Ac64cfA + name: default + plugins: + - ethereum + - postgres + - ffdx + - ipfs + - erc721 + - erc1155 + - basicauth + multiparty: + enabled: true + node: + name: node1 + org: + name: org1 + contract: + - firstevent: "0" + location: + address: 0x7359d2ecc199C48369b390522c29b77A5Af30882 +plugins: + blockchain: + - name: ethereum + type: ethereum + database: + - name: postgres + type: postgres + dataexchange: + - name: ffdx + type: ffdx + sharedstorage: + - ipfs: + name: ipfs + type: ipfs + tokens: + - name: erc721 + type: type1 + - name: erc1155 + type: type2 + auth: + - name: basicauth + type: basicauth +` + type testNamespaceManager struct { namespaceManager mmi *metricsmocks.Manager @@ -63,9 +119,10 @@ type testNamespaceManager struct { mdi *databasemocks.Plugin mdx *dataexchangemocks.Plugin mps *sharedstoragemocks.Plugin - mti *tokenmocks.Plugin - mev *eventsmocks.Plugin + mti []*tokenmocks.Plugin + mei []*eventsmocks.Plugin mai *authmocks.Plugin + mii *identitymocks.Plugin mo *orchestratormocks.Orchestrator } @@ -77,12 +134,22 @@ func (nm *testNamespaceManager) cleanup(t *testing.T) { nm.mdi.AssertExpectations(t) nm.mdx.AssertExpectations(t) nm.mps.AssertExpectations(t) - nm.mti.AssertExpectations(t) + nm.mti[0].AssertExpectations(t) + nm.mti[1].AssertExpectations(t) nm.mai.AssertExpectations(t) + nm.mii.AssertExpectations(t) + nm.mei[0].AssertExpectations(t) + nm.mei[1].AssertExpectations(t) + nm.mei[2].AssertExpectations(t) nm.mo.AssertExpectations(t) } -func newTestNamespaceManager(t *testing.T) (*testNamespaceManager, func()) { +func factoryMocks(m *mock.Mock, name string) { + m.On("Name").Return(name).Maybe() + m.On("InitConfig", mock.Anything).Maybe() +} + +func newTestNamespaceManager(t *testing.T, initConfig bool) (*testNamespaceManager, func()) { coreconfig.Reset() initAllConfig() ctx, cancelCtx := context.WithCancel(context.Background()) @@ -94,11 +161,22 @@ func newTestNamespaceManager(t *testing.T) (*testNamespaceManager, func()) { mdi: &databasemocks.Plugin{}, mdx: &dataexchangemocks.Plugin{}, mps: &sharedstoragemocks.Plugin{}, - mti: &tokenmocks.Plugin{}, - mev: &eventsmocks.Plugin{}, + mti: []*tokenmocks.Plugin{{}, {}}, + mei: []*eventsmocks.Plugin{{}, {}, {}}, mai: &authmocks.Plugin{}, + mii: &identitymocks.Plugin{}, mo: &orchestratormocks.Orchestrator{}, } + factoryMocks(&nm.mbi.Mock, "ethereum") + factoryMocks(&nm.mdi.Mock, "postgres") + factoryMocks(&nm.mdx.Mock, "ffdx") + factoryMocks(&nm.mps.Mock, "ipfs") + factoryMocks(&nm.mti[0].Mock, "erc721") + factoryMocks(&nm.mti[1].Mock, "erc1155") + factoryMocks(&nm.mei[0].Mock, "system") + factoryMocks(&nm.mei[1].Mock, "websockets") + factoryMocks(&nm.mei[2].Mock, "webhooks") + factoryMocks(&nm.mai.Mock, "basicauth") nm.namespaceManager = namespaceManager{ ctx: ctx, cancelCtx: cancelCtx, @@ -106,118 +184,86 @@ func newTestNamespaceManager(t *testing.T) (*testNamespaceManager, func()) { namespaces: make(map[string]*namespace), plugins: make(map[string]*plugin), tokenBroadcastNames: make(map[string]string), - utOrchestrator: nm.mo, - } - nm.watchConfig = func() { - <-ctx.Done() - } - nm.plugins = map[string]*plugin{ - "ethereum": { - name: "ethereum", - category: pluginCategoryBlockchain, - pluginType: "ethereum", - blockchain: nm.mbi, + orchestratorFactory: func(ns *core.Namespace, config orchestrator.Config, plugins *orchestrator.Plugins, metrics metrics.Manager, cacheManager cache.Manager) orchestrator.Orchestrator { + return nm.mo }, - "postgres": { - name: "postgres", - category: pluginCategoryDatabase, - pluginType: "postgres", - database: nm.mdi, + blockchainFactory: func(ctx context.Context, pluginType string) (blockchain.Plugin, error) { + return nm.mbi, nil }, - "ffdx": { - name: "ffdx", - category: pluginCategoryDataexchange, - pluginType: "ffdx", - dataexchange: nm.mdx, + databaseFactory: func(ctx context.Context, pluginType string) (database.Plugin, error) { + return nm.mdi, nil }, - "ipfs": { - name: "ipfs", - category: pluginCategorySharedstorage, - pluginType: "ipfs", - sharedstorage: nm.mps, + dataexchangeFactory: func(ctx context.Context, pluginType string) (dataexchange.Plugin, error) { + return nm.mdx, nil }, - "tbd": { - name: "tbd", - category: pluginCategoryIdentity, - pluginType: "tbd", - identity: &identitymocks.Plugin{}, + sharedstorageFactory: func(ctx context.Context, pluginType string) (sharedstorage.Plugin, error) { + return nm.mps, nil }, - "erc721": { - name: "erc721", - category: pluginCategoryTokens, - pluginType: "erc721", - tokens: nm.mti, + tokensFactory: func(ctx context.Context, pluginType string) (tokens.Plugin, error) { + if pluginType == "type1" { + return nm.mti[0], nil + } + return nm.mti[1], nil }, - "erc1155": { - name: "erc1155", - category: pluginCategoryTokens, - pluginType: "erc1155", - tokens: nm.mti, + identityFactory: func(ctx context.Context, pluginType string) (identity.Plugin, error) { + return nm.mii, nil }, - "websockets": { - name: "websockets", - category: pluginCategoryEvents, - pluginType: "websockets", - events: nm.mev, + eventsFactory: func(ctx context.Context, pluginType string) (events.Plugin, error) { + switch pluginType { + case "system": + return nm.mei[0], nil + case "websockets": + return nm.mei[1], nil + case "webhooks": + return nm.mei[2], nil + default: + panic(fmt.Errorf("Add plugin type %s to test", pluginType)) + } }, - "basicauth": { - name: "basicauth", - category: pluginCategoryAuth, - pluginType: "basicauth", - auth: nm.mai, + authFactory: func(ctx context.Context, pluginType string) (auth.Plugin, error) { + return nm.mai, nil }, } - nm.namespaces = map[string]*namespace{ - "default": { - Namespace: core.Namespace{ - Name: "default", - }, - ctx: nm.ctx, - cancelCtx: nm.cancelCtx, - orchestrator: nm.mo, - pluginNames: []string{ - "ethereum", - "postgres", - "ffdx", - "ipfs", - "tbd", - "erc721", - "erc1155", - "basicauth", - }, - plugins: &orchestrator.Plugins{ - Blockchain: orchestrator.BlockchainPlugin{ - Name: "ethereum", - Plugin: nm.plugins["ethereum"].blockchain, - }, - Database: orchestrator.DatabasePlugin{ - Name: "postgres", - Plugin: nm.plugins["postgres"].database, - }, - DataExchange: orchestrator.DataExchangePlugin{ - Name: "ffdx", - Plugin: nm.plugins["ffdx"].dataexchange, - }, - SharedStorage: orchestrator.SharedStoragePlugin{ - Name: "ipfs", - Plugin: nm.plugins["ipfs"].sharedstorage, - }, - Tokens: []orchestrator.TokensPlugin{ - { - Name: "erc721", - Plugin: nm.plugins["erc721"].tokens, - }, - { - Name: "erc1155", - Plugin: nm.plugins["erc1155"].tokens, - }, - }, - }, - }, + nm.watchConfig = func() { + <-ctx.Done() } nm.namespaceManager.metrics = nm.mmi nm.namespaceManager.adminEvents = nm.mae + if initConfig { + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(testBaseConfig)) + assert.NoError(t, err) + + nm.mo.On("Init", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + nm.namespaces["default"].Contracts.Active = &core.MultipartyContract{ + Info: core.MultipartyContractInfo{ + Version: 2, + }, + } + }). + Return(nil). + Once() + + nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil).Once() + nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return().Once() + nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil).Once() + nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil).Once() + nm.mti[0].On("Init", mock.Anything, mock.Anything, "erc721", mock.Anything).Return(nil).Once() + nm.mti[1].On("Init", mock.Anything, mock.Anything, "erc1155", mock.Anything).Return(nil).Once() + nm.mei[0].On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil).Once() + nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil).Once() + nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + + err = nm.Init(nm.ctx, nm.cancelCtx, nm.reset) + assert.NoError(t, err) + } + return nm, func() { if a := recover(); a != nil { panic(a) @@ -233,57 +279,28 @@ func TestNewNamespaceManager(t *testing.T) { } func TestInitEmpty(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() nm.metricsEnabled = true + nm.mei[0].On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) + nm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) + err := nm.Init(nm.ctx, nm.cancelCtx, nm.reset) assert.NoError(t, err) - assert.Len(t, nm.plugins, 3) - assert.NotNil(t, nm.plugins["system"]) - assert.NotNil(t, nm.plugins["webhooks"]) - assert.NotNil(t, nm.plugins["websockets"]) + assert.Len(t, nm.plugins, 3) // events assert.Empty(t, nm.namespaces) } func TestInitAllPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + _, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.metricsEnabled = true - - nm.mo.On("Init", mock.Anything, mock.Anything). - Run(func(args mock.Arguments) { - nm.namespaces["default"].Contracts.Active = &core.MultipartyContract{ - Info: core.MultipartyContractInfo{ - Version: 2, - }, - } - }). - Return(nil) - - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc721", nil).Return(nil) - nm.mti.On("Init", mock.Anything, mock.Anything, "erc1155", nil).Return(nil) - nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - - err := nm.initComponents(nm.ctx) - assert.NoError(t, err) - - assert.Equal(t, nm.mo, nm.Orchestrator("default")) - assert.Nil(t, nm.Orchestrator("unknown")) - assert.Nil(t, nm.Orchestrator(core.LegacySystemNamespace)) } func TestInitComponentsPluginsFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) @@ -297,7 +314,7 @@ func TestInitComponentsPluginsFail(t *testing.T) { } func TestInitDatabaseFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mdi.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) @@ -309,7 +326,7 @@ func TestInitDatabaseFail(t *testing.T) { } func TestInitBlockchainFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(fmt.Errorf("pop")) @@ -321,7 +338,7 @@ func TestInitBlockchainFail(t *testing.T) { } func TestInitDataExchangeFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) @@ -333,7 +350,7 @@ func TestInitDataExchangeFail(t *testing.T) { } func TestInitSharedStorageFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mps.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) @@ -345,31 +362,31 @@ func TestInitSharedStorageFail(t *testing.T) { } func TestInitTokensFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mti.On("Init", mock.Anything, mock.Anything, mock.Anything, nil).Return(fmt.Errorf("pop")) + nm.mti[0].On("Init", mock.Anything, mock.Anything, "erc721", mock.Anything).Return(fmt.Errorf("pop")) err := nm.initPlugins(context.Background(), map[string]*plugin{ - "erc1155": nm.plugins["erc1155"], + "erc721": nm.plugins["erc721"], }) assert.EqualError(t, err, "pop") } func TestInitEventsFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() - nm.mev.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + for _, mei := range nm.mei { + mei.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")).Maybe() + } - err := nm.initPlugins(context.Background(), map[string]*plugin{ - "websockets": nm.plugins["websockets"], - }) + err := nm.Init(nm.ctx, nm.cancelCtx, nm.reset) assert.EqualError(t, err, "pop") } func TestInitAuthFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) @@ -381,7 +398,7 @@ func TestInitAuthFail(t *testing.T) { } func TestInitOrchestratorFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mo.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) @@ -394,7 +411,7 @@ func TestInitOrchestratorFail(t *testing.T) { } func TestInitVersion1(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mo.On("Init", mock.Anything, mock.Anything). @@ -422,7 +439,7 @@ func TestInitVersion1(t *testing.T) { } func TestInitFFSystemWithTerminatedV1Contract(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metricsEnabled = true @@ -458,10 +475,11 @@ func TestInitFFSystemWithTerminatedV1Contract(t *testing.T) { } func TestLegacyNamespaceConflictingPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metricsEnabled = true + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -504,7 +522,7 @@ func TestLegacyNamespaceConflictingPlugins(t *testing.T) { nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.namespaces, err = nm.loadNamespaces(nm.ctx, viper.AllSettings(), nm.plugins) + nm.namespaces, err = nm.loadNamespaces(nm.ctx, nm.dumpRootConfig(), nm.plugins) assert.Len(t, nm.namespaces, 2) assert.NoError(t, err) @@ -517,10 +535,11 @@ func TestLegacyNamespaceConflictingPlugins(t *testing.T) { } func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metricsEnabled = true + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -562,7 +581,7 @@ func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.namespaces, err = nm.loadNamespaces(nm.ctx, viper.AllSettings(), nm.plugins) + nm.namespaces, err = nm.loadNamespaces(nm.ctx, nm.dumpRootConfig(), nm.plugins) assert.Len(t, nm.namespaces, 2) assert.NoError(t, err) @@ -575,10 +594,11 @@ func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { } func TestLegacyNamespaceMatchingPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metricsEnabled = true + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -621,7 +641,7 @@ func TestLegacyNamespaceMatchingPlugins(t *testing.T) { nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.namespaces, err = nm.loadNamespaces(nm.ctx, viper.AllSettings(), nm.plugins) + nm.namespaces, err = nm.loadNamespaces(nm.ctx, nm.dumpRootConfig(), nm.plugins) assert.Len(t, nm.namespaces, 2) assert.NoError(t, err) @@ -634,7 +654,7 @@ func TestLegacyNamespaceMatchingPlugins(t *testing.T) { } func TestInitVersion1Fail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mo.On("Init", mock.Anything, mock.Anything). @@ -660,7 +680,7 @@ func TestInitVersion1Fail(t *testing.T) { } func TestInitNamespaceQueryFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, fmt.Errorf("pop")) @@ -670,7 +690,7 @@ func TestInitNamespaceQueryFail(t *testing.T) { } func TestInitNamespaceExistingUpsertFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() existing := &core.Namespace{ @@ -685,7 +705,7 @@ func TestInitNamespaceExistingUpsertFail(t *testing.T) { } func TestDeprecatedDatabasePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfigDeprecated(deprecatedDatabaseConfig) deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "postgres") @@ -696,16 +716,19 @@ func TestDeprecatedDatabasePlugin(t *testing.T) { } func TestDeprecatedDatabasePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfigDeprecated(deprecatedDatabaseConfig) deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "wrong") + nm.databaseFactory = func(ctx context.Context, pluginType string) (database.Plugin, error) { + return nil, fmt.Errorf("pop") + } err := nm.getDatabasePlugins(context.Background(), make(map[string]*plugin), nm.dumpRootConfig()) - assert.Regexp(t, "FF10122.*wrong", err) + assert.Regexp(t, "pop", err) } func TestDatabasePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfig(databaseConfig) config.Set("plugins.database", []fftypes.JSONObject{{}}) @@ -718,18 +741,21 @@ func TestDatabasePlugin(t *testing.T) { } func TestDatabasePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfig(databaseConfig) config.Set("plugins.database", []fftypes.JSONObject{{}}) databaseConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") databaseConfig.AddKnownKey(coreconfig.PluginConfigType, "unknown") + nm.databaseFactory = func(ctx context.Context, pluginType string) (database.Plugin, error) { + return nil, fmt.Errorf("pop") + } err := nm.getDatabasePlugins(context.Background(), make(map[string]*plugin), nm.dumpRootConfig()) - assert.Regexp(t, "FF10122.*unknown", err) + assert.Regexp(t, "pop", err) } func TestDatabasePluginBadName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfig(databaseConfig) config.Set("plugins.database", []fftypes.JSONObject{{}}) @@ -740,7 +766,7 @@ func TestDatabasePluginBadName(t *testing.T) { } func TestIdentityPluginBadName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() iifactory.InitConfig(identityConfig) identityConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong//") @@ -751,18 +777,21 @@ func TestIdentityPluginBadName(t *testing.T) { } func TestIdentityPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() iifactory.InitConfig(identityConfig) identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") identityConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") config.Set("plugins.identity", []fftypes.JSONObject{{}}) + nm.identityFactory = func(ctx context.Context, pluginType string) (identity.Plugin, error) { + return nil, fmt.Errorf("pop") + } err := nm.getIdentityPlugins(context.Background(), make(map[string]*plugin), nm.dumpRootConfig()) - assert.Regexp(t, "FF10212.*wrong", err) + assert.Regexp(t, "pop", err) } func TestIdentityPluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() iifactory.InitConfig(identityConfig) identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") @@ -774,7 +803,7 @@ func TestIdentityPluginNoType(t *testing.T) { } func TestIdentityPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() iifactory.InitConfig(identityConfig) identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") @@ -787,7 +816,7 @@ func TestIdentityPlugin(t *testing.T) { } func TestDeprecatedBlockchainPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() bifactory.InitConfigDeprecated(deprecatedBlockchainConfig) deprecatedBlockchainConfig.Set(coreconfig.PluginConfigType, "ethereum") @@ -798,16 +827,19 @@ func TestDeprecatedBlockchainPlugin(t *testing.T) { } func TestDeprecatedBlockchainPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() deprecatedBlockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") plugins := make(map[string]*plugin) + nm.blockchainFactory = func(ctx context.Context, pluginType string) (blockchain.Plugin, error) { + return nil, fmt.Errorf("pop") + } err := nm.getBlockchainPlugins(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF10110.*wrong", err) + assert.Regexp(t, "pop", err) } func TestBlockchainPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() bifactory.InitConfig(blockchainConfig) config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) @@ -820,7 +852,7 @@ func TestBlockchainPlugin(t *testing.T) { } func TestBlockchainPluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() bifactory.InitConfig(blockchainConfig) config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) @@ -831,7 +863,7 @@ func TestBlockchainPluginNoType(t *testing.T) { } func TestBlockchainPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() bifactory.InitConfig(blockchainConfig) config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) @@ -839,12 +871,15 @@ func TestBlockchainPluginBadType(t *testing.T) { blockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") plugins := make(map[string]*plugin) + nm.blockchainFactory = func(ctx context.Context, pluginType string) (blockchain.Plugin, error) { + return nil, fmt.Errorf("pop") + } err := nm.getBlockchainPlugins(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF10110", err) + assert.Regexp(t, "pop", err) } func TestDeprecatedSharedStoragePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) deprecatedSharedStorageConfig.Set(coreconfig.PluginConfigType, "ipfs") @@ -855,17 +890,20 @@ func TestDeprecatedSharedStoragePlugin(t *testing.T) { } func TestDeprecatedSharedStoragePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) deprecatedSharedStorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + nm.sharedstorageFactory = func(ctx context.Context, pluginType string) (sharedstorage.Plugin, error) { + return nil, fmt.Errorf("pop") + } plugins := make(map[string]*plugin) err := nm.getSharedStoragePlugins(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF10134.*wrong", err) + assert.Regexp(t, "pop", err) } func TestSharedStoragePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfig(sharedstorageConfig) config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) @@ -878,7 +916,7 @@ func TestSharedStoragePlugin(t *testing.T) { } func TestSharedStoragePluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfig(sharedstorageConfig) config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) @@ -889,20 +927,23 @@ func TestSharedStoragePluginNoType(t *testing.T) { } func TestSharedStoragePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfig(sharedstorageConfig) config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") sharedstorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") + nm.sharedstorageFactory = func(ctx context.Context, pluginType string) (sharedstorage.Plugin, error) { + return nil, fmt.Errorf("pop") + } plugins := make(map[string]*plugin) err := nm.getSharedStoragePlugins(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF10134", err) + assert.Regexp(t, "pop", err) } func TestDeprecatedDataExchangePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) deprecatedDataexchangeConfig.Set(coreconfig.PluginConfigType, "ffdx") @@ -913,17 +954,20 @@ func TestDeprecatedDataExchangePlugin(t *testing.T) { } func TestDeprecatedDataExchangePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) deprecatedDataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + nm.dataexchangeFactory = func(ctx context.Context, pluginType string) (dataexchange.Plugin, error) { + return nil, fmt.Errorf("pop") + } plugins := make(map[string]*plugin) err := nm.getDataExchangePlugins(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF10213.*wrong", err) + assert.Regexp(t, "pop", err) } func TestDataExchangePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfig(dataexchangeConfig) config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) @@ -936,7 +980,7 @@ func TestDataExchangePlugin(t *testing.T) { } func TestDataExchangePluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfig(dataexchangeConfig) config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) @@ -947,20 +991,23 @@ func TestDataExchangePluginNoType(t *testing.T) { } func TestDataExchangePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfig(dataexchangeConfig) config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") dataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong//") + nm.dataexchangeFactory = func(ctx context.Context, pluginType string) (dataexchange.Plugin, error) { + return nil, fmt.Errorf("pop") + } plugins := make(map[string]*plugin) err := nm.getDataExchangePlugins(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF10213", err) + assert.Regexp(t, "pop", err) } func TestDeprecatedTokensPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfigDeprecated(deprecatedTokensConfig) config.Set("tokens", []fftypes.JSONObject{{}}) @@ -973,7 +1020,7 @@ func TestDeprecatedTokensPlugin(t *testing.T) { } func TestDeprecatedTokensPluginNoName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfigDeprecated(deprecatedTokensConfig) config.Set("tokens", []fftypes.JSONObject{{}}) @@ -984,7 +1031,7 @@ func TestDeprecatedTokensPluginNoName(t *testing.T) { } func TestDeprecatedTokensPluginBadName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfigDeprecated(deprecatedTokensConfig) config.Set("tokens", []fftypes.JSONObject{{}}) @@ -996,19 +1043,22 @@ func TestDeprecatedTokensPluginBadName(t *testing.T) { } func TestDeprecatedTokensPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfigDeprecated(deprecatedTokensConfig) config.Set("tokens", []fftypes.JSONObject{{}}) deprecatedTokensConfig.AddKnownKey(coreconfig.PluginConfigName, "test") deprecatedTokensConfig.AddKnownKey(tokens.TokensConfigPlugin, "wrong") + nm.tokensFactory = func(ctx context.Context, pluginType string) (tokens.Plugin, error) { + return nil, fmt.Errorf("pop") + } plugins := make(map[string]*plugin) err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF10272.*wrong", err) + assert.Regexp(t, "pop", err) } func TestTokensPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfig(tokensConfig) config.Set("plugins.tokens", []fftypes.JSONObject{{}}) @@ -1021,8 +1071,10 @@ func TestTokensPlugin(t *testing.T) { } func TestTokensPluginDuplicateBroadcastName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + + coreconfig.Reset() tifactory.InitConfig(tokensConfig) viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` @@ -1047,8 +1099,10 @@ func TestTokensPluginDuplicateBroadcastName(t *testing.T) { } func TestMultipleTokensPluginsWithBroadcastName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + + coreconfig.Reset() tifactory.InitConfig(tokensConfig) viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` @@ -1074,7 +1128,7 @@ func TestMultipleTokensPluginsWithBroadcastName(t *testing.T) { } func TestTokensPluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfig(tokensConfig) config.Set("plugins.tokens", []fftypes.JSONObject{{}}) @@ -1085,20 +1139,23 @@ func TestTokensPluginNoType(t *testing.T) { } func TestTokensPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfig(tokensConfig) config.Set("plugins.tokens", []fftypes.JSONObject{{}}) tokensConfig.AddKnownKey(coreconfig.PluginConfigName, "erc20_erc721") tokensConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + nm.tokensFactory = func(ctx context.Context, pluginType string) (tokens.Plugin, error) { + return nil, fmt.Errorf("pop") + } plugins := make(map[string]*plugin) err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF10272", err) + assert.Regexp(t, "pop", err) } func TestTokensPluginDuplicate(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfig(tokensConfig) config.Set("plugins.tokens", []fftypes.JSONObject{{}}) @@ -1111,7 +1168,7 @@ func TestTokensPluginDuplicate(t *testing.T) { } func TestEventsPluginDefaults(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() plugins := make(map[string]*plugin) err := nm.getEventPlugins(context.Background(), plugins, nm.dumpRootConfig()) @@ -1120,7 +1177,7 @@ func TestEventsPluginDefaults(t *testing.T) { } func TestAuthPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() authfactory.InitConfigArray(authConfig) config.Set("plugins.auth", []fftypes.JSONObject{{}}) @@ -1133,20 +1190,23 @@ func TestAuthPlugin(t *testing.T) { } func TestAuthPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() authfactory.InitConfigArray(authConfig) config.Set("plugins.auth", []fftypes.JSONObject{{}}) authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") authConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") + nm.authFactory = func(ctx context.Context, pluginType string) (auth.Plugin, error) { + return nil, fmt.Errorf("pop") + } plugins := make(map[string]*plugin) err := nm.getAuthPlugin(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF00168", err) + assert.Regexp(t, "pop", err) } func TestAuthPluginInvalid(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() authfactory.InitConfigArray(authConfig) config.Set("plugins.auth", []fftypes.JSONObject{{}}) @@ -1158,19 +1218,23 @@ func TestAuthPluginInvalid(t *testing.T) { } func TestEventsPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, false) defer cleanup() config.Set(coreconfig.EventTransportsEnabled, []string{"!unknown!"}) + nm.eventsFactory = func(ctx context.Context, pluginType string) (events.Plugin, error) { + return nil, fmt.Errorf("pop") + } plugins := make(map[string]*plugin) err := nm.getEventPlugins(context.Background(), plugins, nm.dumpRootConfig()) - assert.Regexp(t, "FF10172", err) + assert.Regexp(t, "pop", err) } func TestInitBadNamespace(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1186,9 +1250,10 @@ func TestInitBadNamespace(t *testing.T) { } func TestLoadNamespacesReservedName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1203,9 +1268,10 @@ func TestLoadNamespacesReservedName(t *testing.T) { } func TestLoadNamespacesReservedNetworkName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1223,9 +1289,10 @@ func TestLoadNamespacesReservedNetworkName(t *testing.T) { } func TestLoadNamespacesDuplicate(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1244,9 +1311,10 @@ func TestLoadNamespacesDuplicate(t *testing.T) { } func TestLoadNamespacesNoName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1260,9 +1328,10 @@ func TestLoadNamespacesNoName(t *testing.T) { } func TestLoadNamespacesNoDefault(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1278,9 +1347,10 @@ func TestLoadNamespacesNoDefault(t *testing.T) { } func TestLoadNamespacesUseDefaults(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1305,9 +1375,10 @@ func TestLoadNamespacesUseDefaults(t *testing.T) { } func TestLoadNamespacesNonMultipartyNoDatabase(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1324,9 +1395,10 @@ func TestLoadNamespacesNonMultipartyNoDatabase(t *testing.T) { } func TestLoadNamespacesMultipartyUnknownPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1344,9 +1416,10 @@ func TestLoadNamespacesMultipartyUnknownPlugin(t *testing.T) { } func TestLoadNamespacesMultipartyMultipleBlockchains(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1364,9 +1437,10 @@ func TestLoadNamespacesMultipartyMultipleBlockchains(t *testing.T) { } func TestLoadNamespacesMultipartyMultipleDX(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1384,9 +1458,10 @@ func TestLoadNamespacesMultipartyMultipleDX(t *testing.T) { } func TestLoadNamespacesMultipartyMultipleSS(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1404,9 +1479,10 @@ func TestLoadNamespacesMultipartyMultipleSS(t *testing.T) { } func TestLoadNamespacesMultipartyMultipleDB(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1424,9 +1500,10 @@ func TestLoadNamespacesMultipartyMultipleDB(t *testing.T) { } func TestInitNamespacesMultipartyWithAuth(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1444,9 +1521,10 @@ func TestInitNamespacesMultipartyWithAuth(t *testing.T) { } func TestLoadNamespacesNonMultipartyWithAuth(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1464,9 +1542,10 @@ func TestLoadNamespacesNonMultipartyWithAuth(t *testing.T) { } func TestLoadNamespacesMultipartyContract(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1487,9 +1566,10 @@ func TestLoadNamespacesMultipartyContract(t *testing.T) { } func TestLoadNamespacesNonMultipartyMultipleDB(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1505,9 +1585,10 @@ func TestLoadNamespacesNonMultipartyMultipleDB(t *testing.T) { } func TestLoadNamespacesNonMultipartyMultipleBlockchains(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1523,9 +1604,10 @@ func TestLoadNamespacesNonMultipartyMultipleBlockchains(t *testing.T) { } func TestLoadNamespacesMultipartyMissingPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1543,9 +1625,10 @@ func TestLoadNamespacesMultipartyMissingPlugins(t *testing.T) { } func TestLoadNamespacesNonMultipartyUnknownPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1561,9 +1644,10 @@ func TestLoadNamespacesNonMultipartyUnknownPlugin(t *testing.T) { } func TestLoadNamespacesNonMultipartyTokens(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: @@ -1579,12 +1663,13 @@ func TestLoadNamespacesNonMultipartyTokens(t *testing.T) { } func TestStart(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mbi.On("Start", mock.Anything).Return(nil) nm.mdx.On("Start", mock.Anything).Return(nil) - nm.mti.On("Start", mock.Anything).Return(nil) + nm.mti[0].On("Start", mock.Anything).Return(nil) + nm.mti[1].On("Start", mock.Anything).Return(nil) nm.mo.On("Start", mock.Anything).Return(nil) err := nm.Start() @@ -1592,7 +1677,7 @@ func TestStart(t *testing.T) { } func TestStartBlockchainFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mo.On("Start", mock.Anything).Return(nil) @@ -1606,7 +1691,7 @@ func TestStartBlockchainFail(t *testing.T) { } func TestStartDataExchangeFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mo.On("Start", mock.Anything).Return(nil) @@ -1620,21 +1705,21 @@ func TestStartDataExchangeFail(t *testing.T) { } func TestStartTokensFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mo.On("Start", mock.Anything).Return(nil) - nm.mti.On("Start").Return(fmt.Errorf("pop")) + nm.mti[0].On("Start").Return(fmt.Errorf("pop")) err := nm.startNamespacesAndPlugins(nm.namespaces, map[string]*plugin{ - "erc1155": nm.plugins["erc1155"], + "erc721": nm.plugins["erc721"], }) assert.EqualError(t, err, "pop") } func TestStartOrchestratorFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mo.On("Start", mock.Anything).Return(fmt.Errorf("pop")) @@ -1644,7 +1729,7 @@ func TestStartOrchestratorFail(t *testing.T) { } func TestWaitStop(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.mo.On("WaitStop").Return() @@ -1657,7 +1742,7 @@ func TestWaitStop(t *testing.T) { } func TestReset(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() childCtx, childCancel := context.WithCancel(context.Background()) @@ -1669,10 +1754,10 @@ func TestReset(t *testing.T) { } func TestResetRejectIfConfigAutoReload(t *testing.T) { - config.RootConfigReset() + coreconfig.Reset() config.Set(coreconfig.ConfigAutoReload, true) - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() config.Set(coreconfig.ConfigAutoReload, true) @@ -1682,7 +1767,7 @@ func TestResetRejectIfConfigAutoReload(t *testing.T) { } func TestLoadMetrics(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metrics = nil @@ -1691,7 +1776,7 @@ func TestLoadMetrics(t *testing.T) { } func TestLoadAdminEvents(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.adminEvents = nil @@ -1701,7 +1786,7 @@ func TestLoadAdminEvents(t *testing.T) { } func TestGetNamespaces(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() results, err := nm.GetNamespaces(context.Background()) @@ -1710,7 +1795,7 @@ func TestGetNamespaces(t *testing.T) { } func TestGetOperationByNamespacedID(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1729,7 +1814,7 @@ func TestGetOperationByNamespacedID(t *testing.T) { } func TestGetOperationByNamespacedIDBadID(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1744,7 +1829,7 @@ func TestGetOperationByNamespacedIDBadID(t *testing.T) { } func TestGetOperationByNamespacedIDNoOrchestrator(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1761,7 +1846,7 @@ func TestGetOperationByNamespacedIDNoOrchestrator(t *testing.T) { } func TestResolveOperationByNamespacedID(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1782,7 +1867,7 @@ func TestResolveOperationByNamespacedID(t *testing.T) { } func TestResolveOperationByNamespacedIDBadID(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1797,7 +1882,7 @@ func TestResolveOperationByNamespacedIDBadID(t *testing.T) { } func TestResolveOperationByNamespacedIDNoOrchestrator(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1814,26 +1899,23 @@ func TestResolveOperationByNamespacedIDNoOrchestrator(t *testing.T) { } func TestAuthorize(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - mo := &orchestratormocks.Orchestrator{} - mo.On("Authorize", mock.Anything, mock.Anything).Return(nil) + nm.mo.On("Authorize", mock.Anything, mock.Anything).Return(nil) nm.namespaces["ns1"] = &namespace{ - orchestrator: mo, + orchestrator: nm.mo, } - nm.utOrchestrator = mo err := nm.Authorize(context.Background(), &fftypes.AuthReq{ Namespace: "ns1", }) assert.NoError(t, err) - - mo.AssertExpectations(t) } func TestValidateNonMultipartyConfig(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t) + nm, cleanup := newTestNamespaceManager(t, true) defer cleanup() + coreconfig.Reset() viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(` namespaces: From 753dbac53f18dc5d74a0197051a262bb59e83aba Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Fri, 9 Dec 2022 00:56:51 -0500 Subject: [PATCH 04/11] Work through the config reset to re-instate the API config Signed-off-by: Peter Broadhurst --- cmd/firefly.go | 25 ++++--------- cmd/firefly_test.go | 24 ++++--------- docs/config_docs_generate_test.go | 2 +- docs/config_docs_test.go | 2 +- internal/namespace/config.go | 38 ++++++++++++++++++++ internal/namespace/configreload.go | 6 ++-- internal/namespace/configreload_test.go | 4 +-- internal/namespace/manager.go | 47 +++---------------------- internal/namespace/manager_test.go | 13 +++---- mocks/namespacemocks/manager.go | 10 +++--- 10 files changed, 76 insertions(+), 95 deletions(-) diff --git a/cmd/firefly.go b/cmd/firefly.go index 903ab81be..8fcbbe8d1 100644 --- a/cmd/firefly.go +++ b/cmd/firefly.go @@ -32,7 +32,6 @@ import ( "github.com/hyperledger/firefly-common/pkg/log" "github.com/hyperledger/firefly/internal/apiserver" "github.com/hyperledger/firefly/internal/coreconfig" - "github.com/hyperledger/firefly/internal/coremsgs" "github.com/hyperledger/firefly/internal/namespace" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -60,7 +59,7 @@ var showConfigCommand = &cobra.Command{ Short: "List out the configuration options", Run: func(cmd *cobra.Command, args []string) { // Initialize config of all plugins - _ = resetConfig(true) + resetConfig() getRootManager() _ = config.ReadConfig(configSuffix, cfgFile) @@ -82,17 +81,10 @@ func init() { rootCmd.AddCommand(showConfigCommand) } -func resetConfig(startup bool) error { - if !startup && config.GetBool(coreconfig.ConfigAutoReload) { - // We do not allow these settings to be combined, because viper does not provide a way to - // stop the file listener on the old root Viper instance (before reset). So we would - // leak file listeners in the background. - // Note: This check is also in the API layer - return i18n.NewError(context.Background(), coremsgs.MsgDeprecatedResetWithAutoReload) - } +func resetConfig() { coreconfig.Reset() + namespace.InitConfig() apiserver.InitConfig() - return nil } func getRootManager() namespace.Manager { @@ -110,7 +102,7 @@ func Execute() error { func run() error { // Read the configuration - _ = resetConfig(true) + resetConfig() err := config.ReadConfig(configSuffix, cfgFile) // Setup logging after reading config (even if failed), to output header correctly @@ -160,11 +152,8 @@ func run() error { // Must wait for the server to close before we restart <-ffDone // Re-read the configuration - err := resetConfig(false) - if err == nil { - err = config.ReadConfig(configSuffix, cfgFile) - } - if err != nil { + resetConfig() + if err = config.ReadConfig(configSuffix, cfgFile); err != nil { return err } case err := <-errChan: @@ -201,7 +190,7 @@ func startFirefly(ctx context.Context, cancelCtx context.CancelFunc, mgr namespa close(ffDone) }() - if err = mgr.Init(ctx, cancelCtx, resetChan); err != nil { + if err = mgr.Init(ctx, cancelCtx, resetChan, resetConfig); err != nil { errChan <- err return } diff --git a/cmd/firefly_test.go b/cmd/firefly_test.go index 1213e208a..e0aa3e953 100644 --- a/cmd/firefly_test.go +++ b/cmd/firefly_test.go @@ -23,8 +23,6 @@ import ( "syscall" "testing" - "github.com/hyperledger/firefly-common/pkg/config" - "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/mocks/apiservermocks" "github.com/hyperledger/firefly/mocks/namespacemocks" "github.com/spf13/viper" @@ -58,7 +56,7 @@ func TestShowConfig(t *testing.T) { func TestExecEngineInitFail(t *testing.T) { o := &namespacemocks.Manager{} - o.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("splutter")) + o.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("splutter")) _utManager = o defer func() { _utManager = nil }() os.Chdir(configDir) @@ -68,7 +66,7 @@ func TestExecEngineInitFail(t *testing.T) { func TestExecEngineStartFail(t *testing.T) { o := &namespacemocks.Manager{} - o.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + o.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) o.On("Start").Return(fmt.Errorf("bang")) _utManager = o defer func() { _utManager = nil }() @@ -79,7 +77,7 @@ func TestExecEngineStartFail(t *testing.T) { func TestExecOkExitSIGINT(t *testing.T) { o := &namespacemocks.Manager{} - o.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + o.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) o.On("Start").Return(nil) o.On("WaitStop").Return() _utManager = o @@ -95,7 +93,7 @@ func TestExecOkExitSIGINT(t *testing.T) { func TestExecOkCancel(t *testing.T) { o := &namespacemocks.Manager{} - init := o.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + init := o.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) init.RunFn = func(a mock.Arguments) { cancelCtx := a[1].(context.CancelFunc) cancelCtx() @@ -113,7 +111,7 @@ func TestExecOkCancel(t *testing.T) { func TestExecOkRestartThenExit(t *testing.T) { o := &namespacemocks.Manager{} initCount := 0 - init := o.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + init := o.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) init.RunFn = func(a mock.Arguments) { resetChan := a[2].(chan bool) initCount++ @@ -139,7 +137,7 @@ func TestExecOkRestartConfigProblem(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(tmpDir) var orContext context.Context - init := o.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + init := o.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) init.RunFn = func(a mock.Arguments) { orContext = a[0].(context.Context) resetChan := a[2].(chan bool) @@ -160,7 +158,7 @@ func TestExecOkRestartConfigProblem(t *testing.T) { func TestAPIServerError(t *testing.T) { o := &namespacemocks.Manager{} - o.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + o.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) o.On("Start").Return(nil) as := &apiservermocks.Server{} as.On("Serve", mock.Anything, o).Return(fmt.Errorf("pop")) @@ -171,11 +169,3 @@ func TestAPIServerError(t *testing.T) { err := <-errChan assert.EqualError(t, err, "pop") } - -func TestCannotResetWithConfigAutoReload(t *testing.T) { - err := resetConfig(true) - assert.NoError(t, err) - config.Set(coreconfig.ConfigAutoReload, true) - err = resetConfig(false) - assert.Regexp(t, "FF10433", err) -} diff --git a/docs/config_docs_generate_test.go b/docs/config_docs_generate_test.go index fd1cb09c0..c9855b995 100644 --- a/docs/config_docs_generate_test.go +++ b/docs/config_docs_generate_test.go @@ -33,7 +33,7 @@ import ( func TestGenerateConfigDocs(t *testing.T) { // Initialize config of all plugins - namespace.NewNamespaceManager() + namespace.InitConfig() apiserver.InitConfig() f, err := os.Create(filepath.Join("reference", "config.md")) assert.NoError(t, err) diff --git a/docs/config_docs_test.go b/docs/config_docs_test.go index f99539b42..c63edcbea 100644 --- a/docs/config_docs_test.go +++ b/docs/config_docs_test.go @@ -34,7 +34,7 @@ import ( func TestConfigDocsUpToDate(t *testing.T) { // Initialize config of all plugins - namespace.NewNamespaceManager() + namespace.InitConfig() apiserver.InitConfig() generatedConfig, err := config.GenerateConfigMarkdown(context.Background(), configDocHeader, config.GetKnownKeys()) assert.NoError(t, err) diff --git a/internal/namespace/config.go b/internal/namespace/config.go index 976c74fcc..fc4380cdc 100644 --- a/internal/namespace/config.go +++ b/internal/namespace/config.go @@ -17,8 +17,16 @@ package namespace import ( + "github.com/hyperledger/firefly-common/pkg/auth/authfactory" "github.com/hyperledger/firefly-common/pkg/config" + "github.com/hyperledger/firefly/internal/blockchain/bifactory" "github.com/hyperledger/firefly/internal/coreconfig" + "github.com/hyperledger/firefly/internal/database/difactory" + "github.com/hyperledger/firefly/internal/dataexchange/dxfactory" + "github.com/hyperledger/firefly/internal/events/eifactory" + "github.com/hyperledger/firefly/internal/identity/iifactory" + "github.com/hyperledger/firefly/internal/sharedstorage/ssfactory" + "github.com/hyperledger/firefly/internal/tokens/tifactory" "github.com/hyperledger/firefly/pkg/core" ) @@ -31,6 +39,22 @@ const ( var ( namespaceConfigSection = config.RootSection("namespaces") namespacePredefined = namespaceConfigSection.SubArray(NamespacePredefined) + + blockchainConfig = config.RootArray("plugins.blockchain") + tokensConfig = config.RootArray("plugins.tokens") + databaseConfig = config.RootArray("plugins.database") + sharedstorageConfig = config.RootArray("plugins.sharedstorage") + dataexchangeConfig = config.RootArray("plugins.dataexchange") + identityConfig = config.RootArray("plugins.identity") + authConfig = config.RootArray("plugins.auth") + eventsConfig = config.RootSection("events") // still at root + + // Deprecated configs + deprecatedTokensConfig = config.RootArray("tokens") + deprecatedBlockchainConfig = config.RootSection("blockchain") + deprecatedDatabaseConfig = config.RootSection("database") + deprecatedSharedStorageConfig = config.RootSection("sharedstorage") + deprecatedDataexchangeConfig = config.RootSection("dataexchange") ) func InitConfig() { @@ -52,4 +76,18 @@ func InitConfig() { contractConf := multipartyConf.SubArray(coreconfig.NamespaceMultipartyContract) contractConf.AddKnownKey(coreconfig.NamespaceMultipartyContractFirstEvent, string(core.SubOptsFirstEventOldest)) contractConf.AddKnownKey(coreconfig.NamespaceMultipartyContractLocation) + + bifactory.InitConfigDeprecated(deprecatedBlockchainConfig) + bifactory.InitConfig(blockchainConfig) + difactory.InitConfigDeprecated(deprecatedDatabaseConfig) + difactory.InitConfig(databaseConfig) + ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) + ssfactory.InitConfig(sharedstorageConfig) + dxfactory.InitConfig(dataexchangeConfig) + dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) + iifactory.InitConfig(identityConfig) + tifactory.InitConfigDeprecated(deprecatedTokensConfig) + tifactory.InitConfig(tokensConfig) + authfactory.InitConfigArray(authConfig) + eifactory.InitConfig(eventsConfig) } diff --git a/internal/namespace/configreload.go b/internal/namespace/configreload.go index 71e5c3237..31cbdcbef 100644 --- a/internal/namespace/configreload.go +++ b/internal/namespace/configreload.go @@ -25,7 +25,6 @@ import ( "github.com/fsnotify/fsnotify" "github.com/hyperledger/firefly-common/pkg/fftypes" "github.com/hyperledger/firefly-common/pkg/log" - "github.com/hyperledger/firefly/internal/coreconfig" "github.com/spf13/viper" ) @@ -63,8 +62,9 @@ func (nm *namespaceManager) configFileChanged(in fsnotify.Event) { // Because of the things we do to make defaults work with arrays, we have to reset // the config when it changes and re-read it. - coreconfig.Reset() - initAllConfig() + // We are passed this by our parent, as the config initialization of defaults and sections + // might include others than under the namespaces tree (API Server etc. etc.) + nm.resetConfig() err := viper.ReadInConfig() if err != nil { log.L(nm.ctx).Errorf("Failed to re-read configuration after config reload notification: %s", err) diff --git a/internal/namespace/configreload_test.go b/internal/namespace/configreload_test.go index 389c1e0b2..76caf8eca 100644 --- a/internal/namespace/configreload_test.go +++ b/internal/namespace/configreload_test.go @@ -333,7 +333,7 @@ func TestConfigReload1to2(t *testing.T) { mockInitConfig(nm) - err = nm.Init(ctx, cancelCtx, make(chan bool)) + err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) assert.NoError(t, err) err = nm.Start() @@ -351,7 +351,7 @@ func TestConfigReload1to2(t *testing.T) { } coreconfig.Reset() - initAllConfig() + InitConfig() viper.SetConfigType("yaml") err = viper.ReadConfig(strings.NewReader(exampleConfig2extraNS)) assert.NoError(t, err) diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go index 93f406296..90e766b6a 100644 --- a/internal/namespace/manager.go +++ b/internal/namespace/manager.go @@ -54,26 +54,8 @@ import ( "github.com/spf13/viper" ) -var ( - blockchainConfig = config.RootArray("plugins.blockchain") - tokensConfig = config.RootArray("plugins.tokens") - databaseConfig = config.RootArray("plugins.database") - sharedstorageConfig = config.RootArray("plugins.sharedstorage") - dataexchangeConfig = config.RootArray("plugins.dataexchange") - identityConfig = config.RootArray("plugins.identity") - authConfig = config.RootArray("plugins.auth") - eventsConfig = config.RootSection("events") // still at root - - // Deprecated configs - deprecatedTokensConfig = config.RootArray("tokens") - deprecatedBlockchainConfig = config.RootSection("blockchain") - deprecatedDatabaseConfig = config.RootSection("database") - deprecatedSharedStorageConfig = config.RootSection("sharedstorage") - deprecatedDataexchangeConfig = config.RootSection("dataexchange") -) - type Manager interface { - Init(ctx context.Context, cancelCtx context.CancelFunc, reset chan bool) error + Init(ctx context.Context, cancelCtx context.CancelFunc, reset chan bool, resetConfig func()) error Start() error WaitStop() Reset(ctx context.Context) error @@ -100,6 +82,7 @@ type namespace struct { type namespaceManager struct { reset chan bool + resetConfig func() ctx context.Context cancelCtx context.CancelFunc nsMux sync.Mutex @@ -185,32 +168,12 @@ func NewNamespaceManager() Manager { eventsFactory: eifactory.GetPlugin, authFactory: authfactory.GetPlugin, } - initAllConfig() return nm } -func initAllConfig() { - - InitConfig() - - // Initialize the config on all the factories - bifactory.InitConfigDeprecated(deprecatedBlockchainConfig) - bifactory.InitConfig(blockchainConfig) - difactory.InitConfigDeprecated(deprecatedDatabaseConfig) - difactory.InitConfig(databaseConfig) - ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) - ssfactory.InitConfig(sharedstorageConfig) - dxfactory.InitConfig(dataexchangeConfig) - dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) - iifactory.InitConfig(identityConfig) - tifactory.InitConfigDeprecated(deprecatedTokensConfig) - tifactory.InitConfig(tokensConfig) - authfactory.InitConfigArray(authConfig) - eifactory.InitConfig(eventsConfig) -} - -func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFunc, reset chan bool) (err error) { - nm.reset = reset +func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFunc, reset chan bool, resetConfig func()) (err error) { + nm.reset = reset // channel to ask our parent to reload us + nm.resetConfig = resetConfig // function to cause our parent to call InitConfig on all components, including us nm.ctx = ctx nm.cancelCtx = cancelCtx diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go index fe19f3404..5d58863eb 100644 --- a/internal/namespace/manager_test.go +++ b/internal/namespace/manager_test.go @@ -151,7 +151,7 @@ func factoryMocks(m *mock.Mock, name string) { func newTestNamespaceManager(t *testing.T, initConfig bool) (*testNamespaceManager, func()) { coreconfig.Reset() - initAllConfig() + InitConfig() ctx, cancelCtx := context.WithCancel(context.Background()) nm := &testNamespaceManager{ mmi: &metricsmocks.Manager{}, @@ -181,6 +181,7 @@ func newTestNamespaceManager(t *testing.T, initConfig bool) (*testNamespaceManag ctx: ctx, cancelCtx: cancelCtx, reset: make(chan bool, 1), + resetConfig: InitConfig, namespaces: make(map[string]*namespace), plugins: make(map[string]*plugin), tokenBroadcastNames: make(map[string]string), @@ -260,7 +261,7 @@ func newTestNamespaceManager(t *testing.T, initConfig bool) (*testNamespaceManag nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil).Once() nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() - err = nm.Init(nm.ctx, nm.cancelCtx, nm.reset) + err = nm.Init(nm.ctx, nm.cancelCtx, nm.reset, nm.resetConfig) assert.NoError(t, err) } @@ -287,7 +288,7 @@ func TestInitEmpty(t *testing.T) { nm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) nm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) - err := nm.Init(nm.ctx, nm.cancelCtx, nm.reset) + err := nm.Init(nm.ctx, nm.cancelCtx, nm.reset, nm.resetConfig) assert.NoError(t, err) assert.Len(t, nm.plugins, 3) // events @@ -381,7 +382,7 @@ func TestInitEventsFail(t *testing.T) { mei.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")).Maybe() } - err := nm.Init(nm.ctx, nm.cancelCtx, nm.reset) + err := nm.Init(nm.ctx, nm.cancelCtx, nm.reset, nm.resetConfig) assert.EqualError(t, err, "pop") } @@ -798,7 +799,7 @@ func TestIdentityPluginNoType(t *testing.T) { config.Set("plugins.identity", []fftypes.JSONObject{{}}) ctx, cancelCtx := context.WithCancel(context.Background()) - err := nm.Init(ctx, cancelCtx, nm.reset) + err := nm.Init(ctx, cancelCtx, nm.reset, nm.resetConfig) assert.Regexp(t, "FF10386.*type", err) } @@ -1245,7 +1246,7 @@ func TestInitBadNamespace(t *testing.T) { assert.NoError(t, err) ctx, cancelCtx := context.WithCancel(context.Background()) - err = nm.Init(ctx, cancelCtx, nm.reset) + err = nm.Init(ctx, cancelCtx, nm.reset, nm.resetConfig) assert.Regexp(t, "FF00140", err) } diff --git a/mocks/namespacemocks/manager.go b/mocks/namespacemocks/manager.go index bc94d807e..24463c59e 100644 --- a/mocks/namespacemocks/manager.go +++ b/mocks/namespacemocks/manager.go @@ -80,13 +80,13 @@ func (_m *Manager) GetOperationByNamespacedID(ctx context.Context, nsOpID string return r0, r1 } -// Init provides a mock function with given fields: ctx, cancelCtx, reset -func (_m *Manager) Init(ctx context.Context, cancelCtx context.CancelFunc, reset chan bool) error { - ret := _m.Called(ctx, cancelCtx, reset) +// Init provides a mock function with given fields: ctx, cancelCtx, reset, resetConfig +func (_m *Manager) Init(ctx context.Context, cancelCtx context.CancelFunc, reset chan bool, resetConfig func()) error { + ret := _m.Called(ctx, cancelCtx, reset, resetConfig) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, context.CancelFunc, chan bool) error); ok { - r0 = rf(ctx, cancelCtx, reset) + if rf, ok := ret.Get(0).(func(context.Context, context.CancelFunc, chan bool, func()) error); ok { + r0 = rf(ctx, cancelCtx, reset, resetConfig) } else { r0 = ret.Error(0) } From a0bb61de1a3148db8e9ddb6923c74b6637621785 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Fri, 9 Dec 2022 15:05:32 -0500 Subject: [PATCH 05/11] Pull in new config file listener from common and test it Signed-off-by: Peter Broadhurst --- go.mod | 2 +- go.sum | 4 + internal/namespace/configreload.go | 53 +- internal/namespace/configreload_test.go | 290 ++++++++++- internal/namespace/manager.go | 56 +- internal/namespace/manager_test.go | 662 ++++++++++++++---------- 6 files changed, 692 insertions(+), 375 deletions(-) diff --git a/go.mod b/go.mod index 0d0aec7d7..5994356d5 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/golang-migrate/migrate/v4 v4.15.2 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 - github.com/hyperledger/firefly-common v1.1.5 + github.com/hyperledger/firefly-common v1.1.6-0.20221209193217-440fcceceaae github.com/hyperledger/firefly-signer v1.1.2 github.com/jarcoal/httpmock v1.2.0 github.com/karlseguin/ccache v2.0.3+incompatible diff --git a/go.sum b/go.sum index abfd45b25..f5ece10f7 100644 --- a/go.sum +++ b/go.sum @@ -677,6 +677,10 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hyperledger/firefly-common v1.1.5 h1:yVOsUqZQoV/iCc/PxYFGdq/438JDdR/TdlCpMOv3XvM= github.com/hyperledger/firefly-common v1.1.5/go.mod h1:3ubN46/dB+xurCPvdfqMKjB/CJU3I/DsfOoS7dY2SyQ= +github.com/hyperledger/firefly-common v1.1.6-0.20221209164516-9f409ba6efc6 h1:eD99qjoPenw8ChoQoWOeUqjMw8kQq7zdIRyU4JOcYMA= +github.com/hyperledger/firefly-common v1.1.6-0.20221209164516-9f409ba6efc6/go.mod h1:3ubN46/dB+xurCPvdfqMKjB/CJU3I/DsfOoS7dY2SyQ= +github.com/hyperledger/firefly-common v1.1.6-0.20221209193217-440fcceceaae h1:AV1aN15uKAOhLQGB5WnHce5xqkjz29tnFKC7hl/7NTo= +github.com/hyperledger/firefly-common v1.1.6-0.20221209193217-440fcceceaae/go.mod h1:3ubN46/dB+xurCPvdfqMKjB/CJU3I/DsfOoS7dY2SyQ= github.com/hyperledger/firefly-signer v1.1.2 h1:QuS3M5w9px3BnPa4jIWMDg+z2ySK76MoO5Egh0G+tFg= github.com/hyperledger/firefly-signer v1.1.2/go.mod h1:4h2MN910A2knrWGYCT+aWjBDlhptgQn/9WcT1N/Ct8s= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= diff --git a/internal/namespace/configreload.go b/internal/namespace/configreload.go index 31cbdcbef..d046c5a68 100644 --- a/internal/namespace/configreload.go +++ b/internal/namespace/configreload.go @@ -20,11 +20,11 @@ import ( "context" "encoding/json" "fmt" - "time" - "github.com/fsnotify/fsnotify" + "github.com/hyperledger/firefly-common/pkg/config" "github.com/hyperledger/firefly-common/pkg/fftypes" "github.com/hyperledger/firefly-common/pkg/log" + "github.com/hyperledger/firefly/internal/coreconfig" "github.com/spf13/viper" ) @@ -35,30 +35,15 @@ func (nm *namespaceManager) dumpRootConfig() (jsonTree fftypes.JSONObject) { return } -func (nm *namespaceManager) startConfigListener(ctx context.Context) { - go func() { - for { - log.L(ctx).Warnf("Starting configuration listener") - // Note there is no viper interface to make this end, so (apart from in unit tests) - // we never expect this to complete. - // To avoid this leak, we disable the use of the /spi/v1/reset API when config file - // listening is enabled. - viper.OnConfigChange(nm.configFileChanged) - nm.watchConfig() - select { - case <-ctx.Done(): - log.L(ctx).Debugf("Configuration listener ended") - return - default: - } - log.L(ctx).Warnf("Configuration listener ended (restarting)") - time.Sleep(5 * time.Second) - } - }() +func (nm *namespaceManager) startConfigListener() error { + if config.GetBool(coreconfig.ConfigAutoReload) { + return config.WatchConfig(nm.ctx, nm.configFileChanged, nil) + } + return nil } -func (nm *namespaceManager) configFileChanged(in fsnotify.Event) { - log.L(nm.ctx).Infof("Detected configuration file reload: '%s'", in.Name) +func (nm *namespaceManager) configFileChanged() { + log.L(nm.ctx).Infof("Detected configuration file reload") // Because of the things we do to make defaults work with arrays, we have to reset // the config when it changes and re-read it. @@ -91,12 +76,7 @@ func (nm *namespaceManager) configReloaded(ctx context.Context) { // Analyze the new list to see which plugins need to be updated, // so we load the namespaces against the correct list of plugins - availablePlugins, updatedPlugins, pluginsToStop, err := nm.analyzePluginChanges(ctx, allPluginsInNewConf) - if err != nil { - log.L(ctx).Errorf("Failed to stop namespaces after config reload: %s", err) - nm.cancelCtx() // stop the world - return - } + availablePlugins, updatedPlugins, pluginsToStop := nm.analyzePluginChanges(ctx, allPluginsInNewConf) // Build the new set of namespaces (including those that are unchanged) allNewNamespaces, err := nm.loadNamespaces(ctx, rawConfig, availablePlugins) @@ -111,12 +91,7 @@ func (nm *namespaceManager) configReloaded(ctx context.Context) { defer nm.nsMux.Unlock() // Stop all defunct namespaces - availableNS, updatedNamespaces, err := nm.stopDefunctNamespaces(ctx, availablePlugins, allNewNamespaces) - if err != nil { - log.L(ctx).Errorf("Failed to stop namespaces after config reload: %s", err) - nm.cancelCtx() // stop the world - return - } + availableNS, updatedNamespaces := nm.stopDefunctNamespaces(ctx, availablePlugins, allNewNamespaces) // Stop all defunct plugins - now the namespaces using them are all stopped nm.stopDefunctPlugins(ctx, pluginsToStop) @@ -148,7 +123,7 @@ func (nm *namespaceManager) configReloaded(ctx context.Context) { } -func (nm *namespaceManager) stopDefunctNamespaces(ctx context.Context, newPlugins map[string]*plugin, newNamespaces map[string]*namespace) (availableNamespaces, updatedNamespaces map[string]*namespace, err error) { +func (nm *namespaceManager) stopDefunctNamespaces(ctx context.Context, newPlugins map[string]*plugin, newNamespaces map[string]*namespace) (availableNamespaces, updatedNamespaces map[string]*namespace) { // build a set of all the namespaces we've either added new, or have changed updatedNamespaces = make(map[string]*namespace) @@ -193,11 +168,11 @@ func (nm *namespaceManager) stopDefunctNamespaces(ctx context.Context, newPlugin } } - return availableNamespaces, updatedNamespaces, nil + return availableNamespaces, updatedNamespaces } -func (nm *namespaceManager) analyzePluginChanges(ctx context.Context, newPlugins map[string]*plugin) (availablePlugins, updatedPlugins, pluginsToStop map[string]*plugin, err error) { +func (nm *namespaceManager) analyzePluginChanges(ctx context.Context, newPlugins map[string]*plugin) (availablePlugins, updatedPlugins, pluginsToStop map[string]*plugin) { // build a set of all the plugins we've either added new, or have changed availablePlugins = make(map[string]*plugin) diff --git a/internal/namespace/configreload_test.go b/internal/namespace/configreload_test.go index 76caf8eca..d9eff7853 100644 --- a/internal/namespace/configreload_test.go +++ b/internal/namespace/configreload_test.go @@ -18,9 +18,13 @@ package namespace import ( "context" + "fmt" + "io/ioutil" "strings" "testing" + "time" + "github.com/hyperledger/firefly-common/pkg/config" "github.com/hyperledger/firefly-common/pkg/fftypes" "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/pkg/core" @@ -285,29 +289,29 @@ plugins: passwordfile: /etc/firefly/test_users ` -func mockInitConfig(nm *testNamespaceManager) { - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil) - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mti[1].On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mei[0].On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("GetNamespace", mock.Anything, "ns1").Return(nil, nil) - nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) - nm.mdi.On("GetNamespace", mock.Anything, "ns3").Return(nil, nil).Maybe() - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) - nm.mbi.On("Start").Return(nil) - nm.mdx.On("Start").Return(nil) - nm.mti[1].On("Start").Return(nil) - - nm.mo.On("Init", mock.Anything, mock.Anything). +func mockInitConfig(nmm *nmMocks) { + nmm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() + nmm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + nmm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + nmm.mps.On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mti[1].On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + nmm.mei[0].On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mdi.On("GetNamespace", mock.Anything, "ns1").Return(nil, nil) + nmm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) + nmm.mdi.On("GetNamespace", mock.Anything, "ns3").Return(nil, nil).Maybe() + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nmm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil) + nmm.mbi.On("Start").Return(nil) + nmm.mdx.On("Start").Return(nil) + nmm.mti[1].On("Start").Return(nil) + + nmm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { - if nm.namespaces["ns1"] != nil && nm.namespaces["ns1"].Contracts != nil { - nm.namespaces["ns1"].Contracts.Active = &core.MultipartyContract{ + if nmm.nm != nil && nmm.nm.namespaces["ns1"] != nil && nmm.nm.namespaces["ns1"].Contracts != nil { + nmm.nm.namespaces["ns1"].Contracts.Active = &core.MultipartyContract{ Info: core.MultipartyContractInfo{ Version: 2, }, @@ -315,13 +319,51 @@ func mockInitConfig(nm *testNamespaceManager) { } }). Return(nil) - nm.mo.On("Start").Return(nil) + nmm.mo.On("Start").Return(nil) + nmm.mo.On("WaitStop").Return(nil).Maybe() +} + +func TestConfigListenerE2E(t *testing.T) { + + testDir := t.TempDir() + configFilename := fmt.Sprintf("%s/config.firefly.yaml", testDir) + err := ioutil.WriteFile(configFilename, []byte(exampleConfig1base), 0664) + assert.NoError(t, err) + + coreconfig.Reset() + InitConfig() + err = config.ReadConfig("firefly", configFilename) + assert.NoError(t, err) + config.Set(coreconfig.ConfigAutoReload, true) + + nm := NewNamespaceManager().(*namespaceManager) + nmm := mockPluginFactories(nm) + mockInitConfig(nmm) + + ctx, cancelCtx := context.WithCancel(context.Background()) + err = nm.Init(ctx, cancelCtx, make(chan bool), func() { coreconfig.Reset() }) + assert.NoError(t, err) + defer func() { + cancelCtx() + nm.WaitStop() + }() + + err = nm.Start() + assert.NoError(t, err) + + err = ioutil.WriteFile(configFilename, []byte(exampleConfig2extraNS), 0664) + assert.NoError(t, err) + + for nm.namespaces["ns3"] == nil { + time.Sleep(10 * time.Millisecond) + } + } func TestConfigReload1to2(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - nm, cleanup := newTestNamespaceManager(t, false) + nm, mmn, cleanup := newTestNamespaceManager(t, false) defer cleanup() viper.SetConfigType("yaml") @@ -331,7 +373,7 @@ func TestConfigReload1to2(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() - mockInitConfig(nm) + mockInitConfig(mmn) err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) assert.NoError(t, err) @@ -388,3 +430,201 @@ func TestConfigReload1to2(t *testing.T) { assert.NotNil(t, nm.namespaces["ns3"]) } + +func TestConfigReloadBadNewConfigPlugins(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + nm, mmn, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(exampleConfig1base)) + assert.NoError(t, err) + + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + mockInitConfig(mmn) + + err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) + assert.NoError(t, err) + + originalPlugins := nm.plugins + originaNS := nm.namespaces + + err = nm.Start() + assert.NoError(t, err) + + coreconfig.Reset() + InitConfig() + viper.SetConfigType("yaml") + err = viper.ReadConfig(strings.NewReader(` +plugins: + database: [{"type": "invalid"}] +`)) + assert.NoError(t, err) + + // Drive the config reload + nm.configReloaded(nm.ctx) + + // Check that we didn't cancel the context + select { + case <-nm.ctx.Done(): + assert.Fail(t, "Config parse failure should not have crashed the system") + default: + } + + // Check we didn't lose our plugins + assert.Len(t, nm.plugins, len(originalPlugins)) + assert.Len(t, nm.namespaces, len(originaNS)) + +} + +func TestConfigReloadBadNSMissingRequiredPlugins(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + nm, mmn, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(exampleConfig1base)) + assert.NoError(t, err) + + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + mockInitConfig(mmn) + + err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) + assert.NoError(t, err) + + originalPlugins := nm.plugins + originaNS := nm.namespaces + + err = nm.Start() + assert.NoError(t, err) + + coreconfig.Reset() + InitConfig() + viper.SetConfigType("yaml") + err = viper.ReadConfig(strings.NewReader(` +namespaces: + predefined: + - defaultKey: 0xbEa50Ec98776beF144Fc63078e7b15291Ac64cfA + name: ns1 + plugins: + - sharedstorage-ns1 + - database0 + - blockchain-ns1 + - ff-dx + - erc1155-ns1 + - erc20_erc721-ns1 + - test_user_auth-ns1 +`)) + assert.NoError(t, err) + + // Drive the config reload + nm.configReloaded(nm.ctx) + + // Check that we didn't cancel the context + select { + case <-nm.ctx.Done(): + assert.Fail(t, "Config parse failure should not have crashed the system") + default: + } + + // Check we didn't lose our plugins + assert.Len(t, nm.plugins, len(originalPlugins)) + assert.Len(t, nm.namespaces, len(originaNS)) + +} + +func TestConfigDownToNothingOk(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + nm, mmn, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(exampleConfig1base)) + assert.NoError(t, err) + + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + mockInitConfig(mmn) + + err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) + assert.NoError(t, err) + + err = nm.Start() + assert.NoError(t, err) + + coreconfig.Reset() + InitConfig() + viper.SetConfigType("yaml") + // Nothing - no plugins, no namespaces + assert.NoError(t, err) + + mmn.mo.On("WaitStop").Return(nil) + + // Drive the config reload + nm.configReloaded(nm.ctx) + + // Check that we didn't cancel the context + select { + case <-nm.ctx.Done(): + assert.Fail(t, "Should have been happy destroying everything") + default: + } + + // Check we didn't lose our plugins + assert.Len(t, nm.plugins, len(mmn.mei)) // Just the events plugins + assert.Empty(t, nm.namespaces, 0) + +} + +func TestConfigStartPluginsFails(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + nm, mmn, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(exampleConfig1base)) + assert.NoError(t, err) + + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + mockInitConfig(mmn) + + err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) + assert.NoError(t, err) + + err = nm.Start() + assert.NoError(t, err) + + coreconfig.Reset() + InitConfig() + viper.SetConfigType("yaml") + // Nothing - no plugins, no namespaces + assert.NoError(t, err) + + mmn.mo.On("WaitStop").Return(nil) + + // Drive the config reload + nm.configReloaded(nm.ctx) + + // Check that we didn't cancel the context + select { + case <-nm.ctx.Done(): + assert.Fail(t, "Should have been happy destroying everything") + default: + } + + // Check we didn't lose our plugins + assert.Len(t, nm.plugins, len(mmn.mei)) // Just the events plugins + assert.Empty(t, nm.namespaces, 0) + +} diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go index 90e766b6a..f4dd8709a 100644 --- a/internal/namespace/manager.go +++ b/internal/namespace/manager.go @@ -205,8 +205,8 @@ func (nm *namespaceManager) initComponents(ctx context.Context) (err error) { metrics.Registry() } - if config.GetBool(coreconfig.ConfigAutoReload) { - nm.startConfigListener(ctx) + if err := nm.startConfigListener(); err != nil { + return err } return nil @@ -493,11 +493,9 @@ func (nm *namespaceManager) getTokensPlugins(ctx context.Context, plugins map[st nm.tokenBroadcastNames[name] = name pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryTokens, name, pluginType, deprecatedConfig, rawConfig.GetObject("plugins").GetObject("tokens")) - if err != nil { - return err + if err == nil { + pc.tokens, err = nm.tokensFactory(ctx, pluginType) } - - pc.tokens, err = nm.tokensFactory(ctx, pluginType) if err != nil { return err } @@ -533,10 +531,9 @@ func (nm *namespaceManager) getDatabasePlugins(ctx context.Context, plugins map[ if pluginType != "" { log.L(ctx).Warnf("Your database config uses a deprecated configuration structure - the database configuration has been moved under the 'plugins' section") pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryDatabase, "database_0", pluginType, deprecatedDatabaseConfig, rawConfig.GetObject("plugins").GetObject("database")) - if err != nil { - return err + if err == nil { + pc.database, err = nm.databaseFactory(ctx, pluginType) } - pc.database, err = nm.databaseFactory(ctx, pluginType) if err != nil { return err } @@ -591,11 +588,9 @@ func (nm *namespaceManager) getDataExchangePlugins(ctx context.Context, plugins for i := 0; i < dxConfigArraySize; i++ { config := dataexchangeConfig.ArrayEntry(i) pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryDataexchange, config, rawPluginDXConfig[i]) - if err != nil { - return err + if err == nil { + pc.dataexchange, err = nm.dataexchangeFactory(ctx, pc.pluginType) } - - pc.dataexchange, err = nm.dataexchangeFactory(ctx, pc.pluginType) if err != nil { return err } @@ -607,10 +602,9 @@ func (nm *namespaceManager) getDataExchangePlugins(ctx context.Context, plugins if pluginType != "" { log.L(ctx).Warnf("Your data exchange config uses a deprecated configuration structure - the data exchange configuration has been moved under the 'plugins' section") pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryDataexchange, "dataexchange_0", pluginType, deprecatedDataexchangeConfig, rawConfig.GetObject("plugins").GetObject("dataexchange")) - if err != nil { - return err + if err == nil { + pc.dataexchange, err = nm.dataexchangeFactory(ctx, pluginType) } - pc.dataexchange, err = nm.dataexchangeFactory(ctx, pluginType) if err != nil { return err } @@ -630,11 +624,9 @@ func (nm *namespaceManager) getIdentityPlugins(ctx context.Context, plugins map[ for i := 0; i < configSize; i++ { config := identityConfig.ArrayEntry(i) pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryIdentity, config, rawPluginIdentityConfig[i]) - if err != nil { - return err + if err == nil { + pc.identity, err = nm.identityFactory(ctx, pc.pluginType) } - - pc.identity, err = nm.identityFactory(ctx, pc.pluginType) if err != nil { return err } @@ -653,11 +645,9 @@ func (nm *namespaceManager) getBlockchainPlugins(ctx context.Context, plugins ma for i := 0; i < blockchainConfigArraySize; i++ { config := blockchainConfig.ArrayEntry(i) pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategoryBlockchain, config, rawPluginBlockchainsConfig[i]) - if err != nil { - return err + if err == nil { + pc.blockchain, err = nm.blockchainFactory(ctx, pc.pluginType) } - - pc.blockchain, err = nm.blockchainFactory(ctx, pc.pluginType) if err != nil { return err } @@ -670,10 +660,9 @@ func (nm *namespaceManager) getBlockchainPlugins(ctx context.Context, plugins ma log.L(ctx).Warnf("Your blockchain config uses a deprecated configuration structure - the blockchain configuration has been moved under the 'plugins' section") pc, err := nm.newPluginCommon(ctx, plugins, pluginCategoryBlockchain, "blockchain_0", pluginType, deprecatedBlockchainConfig, rawConfig.GetObject("plugins").GetObject("blockchain")) - if err != nil { - return err + if err == nil { + pc.blockchain, err = nm.blockchainFactory(ctx, pluginType) } - pc.blockchain, err = nm.blockchainFactory(ctx, pluginType) if err != nil { return err } @@ -693,11 +682,9 @@ func (nm *namespaceManager) getSharedStoragePlugins(ctx context.Context, plugins for i := 0; i < configSize; i++ { config := sharedstorageConfig.ArrayEntry(i) pc, err := nm.validatePluginConfig(ctx, plugins, pluginCategorySharedstorage, config, rawPluginSharedStorageConfig[i]) - if err != nil { - return err + if err == nil { + pc.sharedstorage, err = nm.sharedstorageFactory(ctx, pc.pluginType) } - - pc.sharedstorage, err = nm.sharedstorageFactory(ctx, pc.pluginType) if err != nil { return err } @@ -710,10 +697,9 @@ func (nm *namespaceManager) getSharedStoragePlugins(ctx context.Context, plugins log.L(ctx).Warnf("Your shared storage config uses a deprecated configuration structure - the shared storage configuration has been moved under the 'plugins' section") pc, err := nm.newPluginCommon(ctx, plugins, pluginCategorySharedstorage, "sharedstorage_0", pluginType, deprecatedSharedStorageConfig, rawConfig.GetObject("plugins").GetObject("sharedstorage")) - if err != nil { - return err + if err == nil { + pc.sharedstorage, err = nm.sharedstorageFactory(ctx, pluginType) } - pc.sharedstorage, err = nm.sharedstorageFactory(ctx, pluginType) if err != nil { return err } @@ -1006,8 +992,6 @@ func (nm *namespaceManager) validateNSPlugins(ctx context.Context, ns *namespace Name: pluginName, Plugin: p.auth, } - default: - return nil, i18n.NewError(ctx, coremsgs.MsgNamespaceUnknownPlugin, ns.Name, pluginName) } } return &result, nil diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go index 5d58863eb..3e4690e9d 100644 --- a/internal/namespace/manager_test.go +++ b/internal/namespace/manager_test.go @@ -19,6 +19,7 @@ package namespace import ( "context" "fmt" + "os" "strings" "testing" @@ -32,6 +33,7 @@ import ( "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/internal/database/difactory" "github.com/hyperledger/firefly/internal/dataexchange/dxfactory" + "github.com/hyperledger/firefly/internal/events/eifactory" "github.com/hyperledger/firefly/internal/identity/iifactory" "github.com/hyperledger/firefly/internal/metrics" "github.com/hyperledger/firefly/internal/orchestrator" @@ -107,11 +109,14 @@ plugins: type: type2 auth: - name: basicauth - type: basicauth + type: basicauth + identity: + - name: tbd + type: tbd ` -type testNamespaceManager struct { - namespaceManager +type nmMocks struct { + nm *namespaceManager mmi *metricsmocks.Manager mae *spieventsmocks.Manager mbi *blockchainmocks.Plugin @@ -126,22 +131,22 @@ type testNamespaceManager struct { mo *orchestratormocks.Orchestrator } -func (nm *testNamespaceManager) cleanup(t *testing.T) { - nm.mmi.AssertExpectations(t) - nm.mae.AssertExpectations(t) - nm.mbi.AssertExpectations(t) - nm.cmi.AssertExpectations(t) - nm.mdi.AssertExpectations(t) - nm.mdx.AssertExpectations(t) - nm.mps.AssertExpectations(t) - nm.mti[0].AssertExpectations(t) - nm.mti[1].AssertExpectations(t) - nm.mai.AssertExpectations(t) - nm.mii.AssertExpectations(t) - nm.mei[0].AssertExpectations(t) - nm.mei[1].AssertExpectations(t) - nm.mei[2].AssertExpectations(t) - nm.mo.AssertExpectations(t) +func (nmm *nmMocks) cleanup(t *testing.T) { + nmm.mmi.AssertExpectations(t) + nmm.mae.AssertExpectations(t) + nmm.mbi.AssertExpectations(t) + nmm.cmi.AssertExpectations(t) + nmm.mdi.AssertExpectations(t) + nmm.mdx.AssertExpectations(t) + nmm.mps.AssertExpectations(t) + nmm.mti[0].AssertExpectations(t) + nmm.mti[1].AssertExpectations(t) + nmm.mai.AssertExpectations(t) + nmm.mii.AssertExpectations(t) + nmm.mei[0].AssertExpectations(t) + nmm.mei[1].AssertExpectations(t) + nmm.mei[2].AssertExpectations(t) + nmm.mo.AssertExpectations(t) } func factoryMocks(m *mock.Mock, name string) { @@ -149,11 +154,9 @@ func factoryMocks(m *mock.Mock, name string) { m.On("InitConfig", mock.Anything).Maybe() } -func newTestNamespaceManager(t *testing.T, initConfig bool) (*testNamespaceManager, func()) { - coreconfig.Reset() - InitConfig() - ctx, cancelCtx := context.WithCancel(context.Background()) - nm := &testNamespaceManager{ +func mockPluginFactories(inm Manager) (nmm *nmMocks) { + nm := inm.(*namespaceManager) + nmm = &nmMocks{ mmi: &metricsmocks.Manager{}, mae: &spieventsmocks.Manager{}, mbi: &blockchainmocks.Plugin{}, @@ -167,17 +170,66 @@ func newTestNamespaceManager(t *testing.T, initConfig bool) (*testNamespaceManag mii: &identitymocks.Plugin{}, mo: &orchestratormocks.Orchestrator{}, } - factoryMocks(&nm.mbi.Mock, "ethereum") - factoryMocks(&nm.mdi.Mock, "postgres") - factoryMocks(&nm.mdx.Mock, "ffdx") - factoryMocks(&nm.mps.Mock, "ipfs") - factoryMocks(&nm.mti[0].Mock, "erc721") - factoryMocks(&nm.mti[1].Mock, "erc1155") - factoryMocks(&nm.mei[0].Mock, "system") - factoryMocks(&nm.mei[1].Mock, "websockets") - factoryMocks(&nm.mei[2].Mock, "webhooks") - factoryMocks(&nm.mai.Mock, "basicauth") - nm.namespaceManager = namespaceManager{ + factoryMocks(&nmm.mbi.Mock, "ethereum") + factoryMocks(&nmm.mdi.Mock, "postgres") + factoryMocks(&nmm.mdx.Mock, "ffdx") + factoryMocks(&nmm.mps.Mock, "ipfs") + factoryMocks(&nmm.mti[0].Mock, "erc721") + factoryMocks(&nmm.mti[1].Mock, "erc1155") + factoryMocks(&nmm.mei[0].Mock, "system") + factoryMocks(&nmm.mei[1].Mock, "websockets") + factoryMocks(&nmm.mei[2].Mock, "webhooks") + factoryMocks(&nmm.mai.Mock, "basicauth") + + nm.orchestratorFactory = func(ns *core.Namespace, config orchestrator.Config, plugins *orchestrator.Plugins, metrics metrics.Manager, cacheManager cache.Manager) orchestrator.Orchestrator { + return nmm.mo + } + nm.blockchainFactory = func(ctx context.Context, pluginType string) (blockchain.Plugin, error) { + return nmm.mbi, nil + } + nm.databaseFactory = func(ctx context.Context, pluginType string) (database.Plugin, error) { + return nmm.mdi, nil + } + nm.dataexchangeFactory = func(ctx context.Context, pluginType string) (dataexchange.Plugin, error) { + return nmm.mdx, nil + } + nm.sharedstorageFactory = func(ctx context.Context, pluginType string) (sharedstorage.Plugin, error) { + return nmm.mps, nil + } + nm.tokensFactory = func(ctx context.Context, pluginType string) (tokens.Plugin, error) { + if pluginType == "type1" { + return nmm.mti[0], nil + } + return nmm.mti[1], nil + } + nm.identityFactory = func(ctx context.Context, pluginType string) (identity.Plugin, error) { + return nmm.mii, nil + } + nm.eventsFactory = func(ctx context.Context, pluginType string) (events.Plugin, error) { + switch pluginType { + case "system": + return nmm.mei[0], nil + case "websockets": + return nmm.mei[1], nil + case "webhooks": + return nmm.mei[2], nil + default: + panic(fmt.Errorf("Add plugin type %s to test", pluginType)) + } + } + nm.authFactory = func(ctx context.Context, pluginType string) (auth.Plugin, error) { + return nmm.mai, nil + } + + nmm.nm = nm + return nmm +} + +func newTestNamespaceManager(t *testing.T, initConfig bool) (*namespaceManager, *nmMocks, func()) { + coreconfig.Reset() + InitConfig() + ctx, cancelCtx := context.WithCancel(context.Background()) + nm := &namespaceManager{ ctx: ctx, cancelCtx: cancelCtx, reset: make(chan bool, 1), @@ -185,58 +237,20 @@ func newTestNamespaceManager(t *testing.T, initConfig bool) (*testNamespaceManag namespaces: make(map[string]*namespace), plugins: make(map[string]*plugin), tokenBroadcastNames: make(map[string]string), - orchestratorFactory: func(ns *core.Namespace, config orchestrator.Config, plugins *orchestrator.Plugins, metrics metrics.Manager, cacheManager cache.Manager) orchestrator.Orchestrator { - return nm.mo - }, - blockchainFactory: func(ctx context.Context, pluginType string) (blockchain.Plugin, error) { - return nm.mbi, nil - }, - databaseFactory: func(ctx context.Context, pluginType string) (database.Plugin, error) { - return nm.mdi, nil - }, - dataexchangeFactory: func(ctx context.Context, pluginType string) (dataexchange.Plugin, error) { - return nm.mdx, nil - }, - sharedstorageFactory: func(ctx context.Context, pluginType string) (sharedstorage.Plugin, error) { - return nm.mps, nil - }, - tokensFactory: func(ctx context.Context, pluginType string) (tokens.Plugin, error) { - if pluginType == "type1" { - return nm.mti[0], nil - } - return nm.mti[1], nil - }, - identityFactory: func(ctx context.Context, pluginType string) (identity.Plugin, error) { - return nm.mii, nil - }, - eventsFactory: func(ctx context.Context, pluginType string) (events.Plugin, error) { - switch pluginType { - case "system": - return nm.mei[0], nil - case "websockets": - return nm.mei[1], nil - case "webhooks": - return nm.mei[2], nil - default: - panic(fmt.Errorf("Add plugin type %s to test", pluginType)) - } - }, - authFactory: func(ctx context.Context, pluginType string) (auth.Plugin, error) { - return nm.mai, nil - }, } - nm.watchConfig = func() { + nmm := mockPluginFactories(nm) + nmm.nm.watchConfig = func() { <-ctx.Done() } - nm.namespaceManager.metrics = nm.mmi - nm.namespaceManager.adminEvents = nm.mae + nmm.nm.metrics = nmm.mmi + nmm.nm.adminEvents = nmm.mae if initConfig { viper.SetConfigType("yaml") err := viper.ReadConfig(strings.NewReader(testBaseConfig)) assert.NoError(t, err) - nm.mo.On("Init", mock.Anything, mock.Anything). + nmm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].Contracts.Active = &core.MultipartyContract{ Info: core.MultipartyContractInfo{ @@ -247,30 +261,30 @@ func newTestNamespaceManager(t *testing.T, initConfig bool) (*testNamespaceManag Return(nil). Once() - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil).Once() - nm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return().Once() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(nil).Once() - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() - nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil).Once() - nm.mti[0].On("Init", mock.Anything, mock.Anything, "erc721", mock.Anything).Return(nil).Once() - nm.mti[1].On("Init", mock.Anything, mock.Anything, "erc1155", mock.Anything).Return(nil).Once() - nm.mei[0].On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil).Once() - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil).Once() - nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() - - err = nm.Init(nm.ctx, nm.cancelCtx, nm.reset, nm.resetConfig) + nmm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil).Once() + nmm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return().Once() + nmm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nmm.mmi, mock.Anything).Return(nil).Once() + nmm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + nmm.mps.On("Init", mock.Anything, mock.Anything).Return(nil).Once() + nmm.mti[0].On("Init", mock.Anything, mock.Anything, "erc721", mock.Anything).Return(nil).Once() + nmm.mti[1].On("Init", mock.Anything, mock.Anything, "erc1155", mock.Anything).Return(nil).Once() + nmm.mei[0].On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil).Once() + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil).Once() + nmm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + + err = nmm.nm.Init(nmm.nm.ctx, nmm.nm.cancelCtx, nmm.nm.reset, nmm.nm.resetConfig) assert.NoError(t, err) } - return nm, func() { + return nm, nmm, func() { if a := recover(); a != nil { panic(a) } - nm.cancelCtx() - nm.cleanup(t) + nmm.nm.cancelCtx() + nmm.cleanup(t) } } @@ -280,13 +294,13 @@ func TestNewNamespaceManager(t *testing.T) { } func TestInitEmpty(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, nmm, cleanup := newTestNamespaceManager(t, false) defer cleanup() nm.metricsEnabled = true - nm.mei[0].On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) - nm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mei[0].On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mei[1].On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mei[2].On("Init", mock.Anything, mock.Anything).Return(nil) err := nm.Init(nm.ctx, nm.cancelCtx, nm.reset, nm.resetConfig) assert.NoError(t, err) @@ -296,15 +310,15 @@ func TestInitEmpty(t *testing.T) { } func TestInitAllPlugins(t *testing.T) { - _, cleanup := newTestNamespaceManager(t, true) + _, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() } func TestInitComponentsPluginsFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nmm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) nm.namespaces = map[string]*namespace{} nm.plugins = map[string]*plugin{ @@ -315,10 +329,10 @@ func TestInitComponentsPluginsFail(t *testing.T) { } func TestInitDatabaseFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mdi.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nmm.mdi.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) err := nm.initPlugins(context.Background(), map[string]*plugin{ "postgres": nm.plugins["postgres"], @@ -327,10 +341,10 @@ func TestInitDatabaseFail(t *testing.T) { } func TestInitBlockchainFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nm.mmi, mock.Anything).Return(fmt.Errorf("pop")) + nmm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nmm.mmi, mock.Anything).Return(fmt.Errorf("pop")) err := nm.initPlugins(context.Background(), map[string]*plugin{ "ethereum": nm.plugins["ethereum"], @@ -339,10 +353,10 @@ func TestInitBlockchainFail(t *testing.T) { } func TestInitDataExchangeFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nmm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) err := nm.initPlugins(context.Background(), map[string]*plugin{ "ffdx": nm.plugins["ffdx"], @@ -351,10 +365,10 @@ func TestInitDataExchangeFail(t *testing.T) { } func TestInitSharedStorageFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mps.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nmm.mps.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) err := nm.initPlugins(context.Background(), map[string]*plugin{ "ipfs": nm.plugins["ipfs"], @@ -363,10 +377,10 @@ func TestInitSharedStorageFail(t *testing.T) { } func TestInitTokensFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mti[0].On("Init", mock.Anything, mock.Anything, "erc721", mock.Anything).Return(fmt.Errorf("pop")) + nmm.mti[0].On("Init", mock.Anything, mock.Anything, "erc721", mock.Anything).Return(fmt.Errorf("pop")) err := nm.initPlugins(context.Background(), map[string]*plugin{ "erc721": nm.plugins["erc721"], @@ -375,10 +389,10 @@ func TestInitTokensFail(t *testing.T) { } func TestInitEventsFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, nmm, cleanup := newTestNamespaceManager(t, false) defer cleanup() - for _, mei := range nm.mei { + for _, mei := range nmm.mei { mei.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")).Maybe() } @@ -387,10 +401,10 @@ func TestInitEventsFail(t *testing.T) { } func TestInitAuthFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nmm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) err := nm.initPlugins(context.Background(), map[string]*plugin{ "basicauth": nm.plugins["basicauth"], @@ -399,23 +413,41 @@ func TestInitAuthFail(t *testing.T) { } func TestInitOrchestratorFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mo.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nm.plugins = make(map[string]*plugin) + nmm.mo.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) - err := nm.initNamespaces(nm.ctx, nm.namespaces) + err := nm.initComponents(nm.ctx) assert.Regexp(t, "pop", err) } +func TestInitConfigListenerFail(t *testing.T) { + nm, _, cleanup := newTestNamespaceManager(t, true) + defer cleanup() + + nm.metricsEnabled = false + nm.plugins = make(map[string]*plugin) + nm.namespaces = make(map[string]*namespace) + + badDir := t.TempDir() + os.Remove(badDir) + viper.SetConfigFile(fmt.Sprintf("%s/problem", badDir)) + config.Set(coreconfig.ConfigAutoReload, true) + + err := nm.initComponents(nm.ctx) + assert.Regexp(t, "FF00194", err) +} + func TestInitVersion1(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mo.On("Init", mock.Anything, mock.Anything). + nmm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].config.Multiparty.Enabled = true nm.namespaces["default"].Contracts.Active = &core.MultipartyContract{ @@ -426,25 +458,25 @@ func TestInitVersion1(t *testing.T) { } }). Return(nil).Twice() // legacy system namespace - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) - nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) + nmm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) err := nm.initNamespaces(nm.ctx, nm.namespaces) assert.NoError(t, err) - assert.Equal(t, nm.mo, nm.Orchestrator("default")) + assert.Equal(t, nmm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) assert.NotNil(t, nm.Orchestrator(core.LegacySystemNamespace)) } func TestInitFFSystemWithTerminatedV1Contract(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metricsEnabled = true - nm.mo.On("Init", mock.Anything, mock.Anything). + nmm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].config.Multiparty.Enabled = true nm.namespaces["default"].Contracts = &core.MultipartyContracts{ @@ -463,20 +495,20 @@ func TestInitFFSystemWithTerminatedV1Contract(t *testing.T) { }). Return(nil).Twice() // legacy system namespace - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) - nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) + nmm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) err := nm.initNamespaces(nm.ctx, nm.namespaces) assert.NoError(t, err) - assert.Equal(t, nm.mo, nm.Orchestrator("default")) + assert.Equal(t, nmm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) } func TestLegacyNamespaceConflictingPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metricsEnabled = true @@ -498,7 +530,7 @@ func TestLegacyNamespaceConflictingPlugins(t *testing.T) { `)) assert.NoError(t, err) - nm.mo.On("Init", mock.Anything, mock.Anything). + nmm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].Contracts = &core.MultipartyContracts{ Active: &core.MultipartyContract{ @@ -519,9 +551,9 @@ func TestLegacyNamespaceConflictingPlugins(t *testing.T) { }). Return(nil) - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) - nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) + nmm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) nm.namespaces, err = nm.loadNamespaces(nm.ctx, nm.dumpRootConfig(), nm.plugins) assert.Len(t, nm.namespaces, 2) @@ -530,13 +562,13 @@ func TestLegacyNamespaceConflictingPlugins(t *testing.T) { err = nm.initNamespaces(nm.ctx, nm.namespaces) assert.Regexp(t, "FF10421", err) - assert.Equal(t, nm.mo, nm.Orchestrator("default")) + assert.Equal(t, nmm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) } func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metricsEnabled = true @@ -557,7 +589,7 @@ func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { `)) assert.NoError(t, err) - nm.mo.On("Init", mock.Anything, mock.Anything). + nmm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].Contracts = &core.MultipartyContracts{ Active: &core.MultipartyContract{ @@ -578,9 +610,9 @@ func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { }). Return(nil) - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) - nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) + nmm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) nm.namespaces, err = nm.loadNamespaces(nm.ctx, nm.dumpRootConfig(), nm.plugins) assert.Len(t, nm.namespaces, 2) @@ -589,13 +621,13 @@ func TestLegacyNamespaceConflictingPluginsTooManyPlugins(t *testing.T) { err = nm.initNamespaces(nm.ctx, nm.namespaces) assert.Regexp(t, "FF10421", err) - assert.Equal(t, nm.mo, nm.Orchestrator("default")) + assert.Equal(t, nmm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) } func TestLegacyNamespaceMatchingPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metricsEnabled = true @@ -616,7 +648,7 @@ func TestLegacyNamespaceMatchingPlugins(t *testing.T) { `)) assert.NoError(t, err) - nm.mo.On("Init", mock.Anything, mock.Anything). + nmm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].Contracts = &core.MultipartyContracts{ Active: &core.MultipartyContract{ @@ -637,10 +669,10 @@ func TestLegacyNamespaceMatchingPlugins(t *testing.T) { }). Return(nil) - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) - nm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) - nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) + nmm.mdi.On("GetNamespace", mock.Anything, "ns2").Return(nil, nil) + nmm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) nm.namespaces, err = nm.loadNamespaces(nm.ctx, nm.dumpRootConfig(), nm.plugins) assert.Len(t, nm.namespaces, 2) @@ -649,16 +681,16 @@ func TestLegacyNamespaceMatchingPlugins(t *testing.T) { err = nm.initNamespaces(nm.ctx, nm.namespaces) assert.NoError(t, err) - assert.Equal(t, nm.mo, nm.Orchestrator("default")) + assert.Equal(t, nmm.mo, nm.Orchestrator("default")) assert.Nil(t, nm.Orchestrator("unknown")) } func TestInitVersion1Fail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mo.On("Init", mock.Anything, mock.Anything). + nmm.mo.On("Init", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { nm.namespaces["default"].config.Multiparty.Enabled = true nm.namespaces["default"].Contracts.Active = &core.MultipartyContract{ @@ -669,11 +701,11 @@ func TestInitVersion1Fail(t *testing.T) { } }). Return(nil).Once() - nm.mo.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")).Once() + nmm.mo.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")).Once() - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) - nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) + nmm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil) + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) err := nm.initNamespaces(nm.ctx, nm.namespaces) assert.EqualError(t, err, "pop") @@ -681,32 +713,32 @@ func TestInitVersion1Fail(t *testing.T) { } func TestInitNamespaceQueryFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, fmt.Errorf("pop")) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, fmt.Errorf("pop")) err := nm.initNamespace(context.Background(), nm.namespaces["default"]) assert.EqualError(t, err, "pop") } func TestInitNamespaceExistingUpsertFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() existing := &core.Namespace{ NetworkName: "ns1", } - nm.mdi.On("GetNamespace", mock.Anything, "default").Return(existing, nil) - nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(fmt.Errorf("pop")) + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(existing, nil) + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(fmt.Errorf("pop")) err := nm.initNamespace(context.Background(), nm.namespaces["default"]) assert.EqualError(t, err, "pop") } func TestDeprecatedDatabasePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfigDeprecated(deprecatedDatabaseConfig) deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "postgres") @@ -717,7 +749,7 @@ func TestDeprecatedDatabasePlugin(t *testing.T) { } func TestDeprecatedDatabasePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfigDeprecated(deprecatedDatabaseConfig) deprecatedDatabaseConfig.Set(coreconfig.PluginConfigType, "wrong") @@ -729,7 +761,7 @@ func TestDeprecatedDatabasePluginBadType(t *testing.T) { } func TestDatabasePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfig(databaseConfig) config.Set("plugins.database", []fftypes.JSONObject{{}}) @@ -742,7 +774,7 @@ func TestDatabasePlugin(t *testing.T) { } func TestDatabasePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfig(databaseConfig) config.Set("plugins.database", []fftypes.JSONObject{{}}) @@ -756,7 +788,7 @@ func TestDatabasePluginBadType(t *testing.T) { } func TestDatabasePluginBadName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() difactory.InitConfig(databaseConfig) config.Set("plugins.database", []fftypes.JSONObject{{}}) @@ -767,7 +799,7 @@ func TestDatabasePluginBadName(t *testing.T) { } func TestIdentityPluginBadName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() iifactory.InitConfig(identityConfig) identityConfig.AddKnownKey(coreconfig.PluginConfigName, "wrong//") @@ -778,7 +810,7 @@ func TestIdentityPluginBadName(t *testing.T) { } func TestIdentityPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() iifactory.InitConfig(identityConfig) identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") @@ -792,7 +824,7 @@ func TestIdentityPluginBadType(t *testing.T) { } func TestIdentityPluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() iifactory.InitConfig(identityConfig) identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") @@ -804,7 +836,7 @@ func TestIdentityPluginNoType(t *testing.T) { } func TestIdentityPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() iifactory.InitConfig(identityConfig) identityConfig.AddKnownKey(coreconfig.PluginConfigName, "flapflip") @@ -817,7 +849,7 @@ func TestIdentityPlugin(t *testing.T) { } func TestDeprecatedBlockchainPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() bifactory.InitConfigDeprecated(deprecatedBlockchainConfig) deprecatedBlockchainConfig.Set(coreconfig.PluginConfigType, "ethereum") @@ -828,19 +860,18 @@ func TestDeprecatedBlockchainPlugin(t *testing.T) { } func TestDeprecatedBlockchainPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() deprecatedBlockchainConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") - plugins := make(map[string]*plugin) nm.blockchainFactory = func(ctx context.Context, pluginType string) (blockchain.Plugin, error) { return nil, fmt.Errorf("pop") } - err := nm.getBlockchainPlugins(context.Background(), plugins, nm.dumpRootConfig()) + _, err := nm.loadPlugins(context.Background(), nm.dumpRootConfig()) assert.Regexp(t, "pop", err) } func TestBlockchainPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() bifactory.InitConfig(blockchainConfig) config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) @@ -853,7 +884,7 @@ func TestBlockchainPlugin(t *testing.T) { } func TestBlockchainPluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() bifactory.InitConfig(blockchainConfig) config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) @@ -864,7 +895,7 @@ func TestBlockchainPluginNoType(t *testing.T) { } func TestBlockchainPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() bifactory.InitConfig(blockchainConfig) config.Set("plugins.blockchain", []fftypes.JSONObject{{}}) @@ -880,7 +911,7 @@ func TestBlockchainPluginBadType(t *testing.T) { } func TestDeprecatedSharedStoragePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) deprecatedSharedStorageConfig.Set(coreconfig.PluginConfigType, "ipfs") @@ -891,20 +922,19 @@ func TestDeprecatedSharedStoragePlugin(t *testing.T) { } func TestDeprecatedSharedStoragePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfigDeprecated(deprecatedSharedStorageConfig) deprecatedSharedStorageConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") nm.sharedstorageFactory = func(ctx context.Context, pluginType string) (sharedstorage.Plugin, error) { return nil, fmt.Errorf("pop") } - plugins := make(map[string]*plugin) - err := nm.getSharedStoragePlugins(context.Background(), plugins, nm.dumpRootConfig()) + _, err := nm.loadPlugins(context.Background(), nm.dumpRootConfig()) assert.Regexp(t, "pop", err) } func TestSharedStoragePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfig(sharedstorageConfig) config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) @@ -917,7 +947,7 @@ func TestSharedStoragePlugin(t *testing.T) { } func TestSharedStoragePluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfig(sharedstorageConfig) config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) @@ -928,7 +958,7 @@ func TestSharedStoragePluginNoType(t *testing.T) { } func TestSharedStoragePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() ssfactory.InitConfig(sharedstorageConfig) config.Set("plugins.sharedstorage", []fftypes.JSONObject{{}}) @@ -944,7 +974,7 @@ func TestSharedStoragePluginBadType(t *testing.T) { } func TestDeprecatedDataExchangePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) deprecatedDataexchangeConfig.Set(coreconfig.PluginConfigType, "ffdx") @@ -955,20 +985,19 @@ func TestDeprecatedDataExchangePlugin(t *testing.T) { } func TestDeprecatedDataExchangePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfigDeprecated(deprecatedDataexchangeConfig) deprecatedDataexchangeConfig.AddKnownKey(coreconfig.PluginConfigType, "wrong") nm.dataexchangeFactory = func(ctx context.Context, pluginType string) (dataexchange.Plugin, error) { return nil, fmt.Errorf("pop") } - plugins := make(map[string]*plugin) - err := nm.getDataExchangePlugins(context.Background(), plugins, nm.dumpRootConfig()) + _, err := nm.loadPlugins(context.Background(), nm.dumpRootConfig()) assert.Regexp(t, "pop", err) } func TestDataExchangePlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfig(dataexchangeConfig) config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) @@ -981,7 +1010,7 @@ func TestDataExchangePlugin(t *testing.T) { } func TestDataExchangePluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfig(dataexchangeConfig) config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) @@ -992,7 +1021,7 @@ func TestDataExchangePluginNoType(t *testing.T) { } func TestDataExchangePluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() dxfactory.InitConfig(dataexchangeConfig) config.Set("plugins.dataexchange", []fftypes.JSONObject{{}}) @@ -1008,7 +1037,7 @@ func TestDataExchangePluginBadType(t *testing.T) { } func TestDeprecatedTokensPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfigDeprecated(deprecatedTokensConfig) config.Set("tokens", []fftypes.JSONObject{{}}) @@ -1021,7 +1050,7 @@ func TestDeprecatedTokensPlugin(t *testing.T) { } func TestDeprecatedTokensPluginNoName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfigDeprecated(deprecatedTokensConfig) config.Set("tokens", []fftypes.JSONObject{{}}) @@ -1032,7 +1061,7 @@ func TestDeprecatedTokensPluginNoName(t *testing.T) { } func TestDeprecatedTokensPluginBadName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfigDeprecated(deprecatedTokensConfig) config.Set("tokens", []fftypes.JSONObject{{}}) @@ -1044,7 +1073,7 @@ func TestDeprecatedTokensPluginBadName(t *testing.T) { } func TestDeprecatedTokensPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfigDeprecated(deprecatedTokensConfig) config.Set("tokens", []fftypes.JSONObject{{}}) @@ -1053,13 +1082,12 @@ func TestDeprecatedTokensPluginBadType(t *testing.T) { nm.tokensFactory = func(ctx context.Context, pluginType string) (tokens.Plugin, error) { return nil, fmt.Errorf("pop") } - plugins := make(map[string]*plugin) - err := nm.getTokensPlugins(context.Background(), plugins, nm.dumpRootConfig()) + _, err := nm.loadPlugins(context.Background(), nm.dumpRootConfig()) assert.Regexp(t, "pop", err) } func TestTokensPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfig(tokensConfig) config.Set("plugins.tokens", []fftypes.JSONObject{{}}) @@ -1072,7 +1100,7 @@ func TestTokensPlugin(t *testing.T) { } func TestTokensPluginDuplicateBroadcastName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1100,7 +1128,7 @@ func TestTokensPluginDuplicateBroadcastName(t *testing.T) { } func TestMultipleTokensPluginsWithBroadcastName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1129,7 +1157,7 @@ func TestMultipleTokensPluginsWithBroadcastName(t *testing.T) { } func TestTokensPluginNoType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfig(tokensConfig) config.Set("plugins.tokens", []fftypes.JSONObject{{}}) @@ -1140,7 +1168,7 @@ func TestTokensPluginNoType(t *testing.T) { } func TestTokensPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfig(tokensConfig) config.Set("plugins.tokens", []fftypes.JSONObject{{}}) @@ -1156,7 +1184,7 @@ func TestTokensPluginBadType(t *testing.T) { } func TestTokensPluginDuplicate(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() tifactory.InitConfig(tokensConfig) config.Set("plugins.tokens", []fftypes.JSONObject{{}}) @@ -1169,7 +1197,7 @@ func TestTokensPluginDuplicate(t *testing.T) { } func TestEventsPluginDefaults(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() plugins := make(map[string]*plugin) err := nm.getEventPlugins(context.Background(), plugins, nm.dumpRootConfig()) @@ -1177,8 +1205,20 @@ func TestEventsPluginDefaults(t *testing.T) { assert.NoError(t, err) } +func TestEventsPluginDuplicate(t *testing.T) { + nm, _, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + eifactory.InitConfig(eventsConfig) + eventsConfig.AddKnownKey(coreconfig.PluginConfigName, "websockets") + eventsConfig.AddKnownKey(coreconfig.PluginConfigType, "websockets") + plugins := make(map[string]*plugin) + plugins["websockets"] = &plugin{} + err := nm.getEventPlugins(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10395", err) +} + func TestAuthPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() authfactory.InitConfigArray(authConfig) config.Set("plugins.auth", []fftypes.JSONObject{{}}) @@ -1191,7 +1231,7 @@ func TestAuthPlugin(t *testing.T) { } func TestAuthPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() authfactory.InitConfigArray(authConfig) config.Set("plugins.auth", []fftypes.JSONObject{{}}) @@ -1201,13 +1241,12 @@ func TestAuthPluginBadType(t *testing.T) { nm.authFactory = func(ctx context.Context, pluginType string) (auth.Plugin, error) { return nil, fmt.Errorf("pop") } - plugins := make(map[string]*plugin) - err := nm.getAuthPlugin(context.Background(), plugins, nm.dumpRootConfig()) + _, err := nm.loadPlugins(context.Background(), nm.dumpRootConfig()) assert.Regexp(t, "pop", err) } func TestAuthPluginInvalid(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() authfactory.InitConfigArray(authConfig) config.Set("plugins.auth", []fftypes.JSONObject{{}}) @@ -1218,21 +1257,54 @@ func TestAuthPluginInvalid(t *testing.T) { assert.Regexp(t, "FF00140", err) } +func TestAuthPluginDuplicate(t *testing.T) { + nm, _, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + authfactory.InitConfigArray(authConfig) + config.Set("plugins.auth", []fftypes.JSONObject{{}}) + authConfig.AddKnownKey(coreconfig.PluginConfigName, "basicauth") + authConfig.AddKnownKey(coreconfig.PluginConfigType, "basicauth") + plugins := make(map[string]*plugin) + plugins["basicauth"] = &plugin{} + err := nm.getAuthPlugin(context.Background(), plugins, nm.dumpRootConfig()) + assert.Regexp(t, "FF10395", err) +} + +func TestRawConfigCorrelation(t *testing.T) { + nm, _, cleanup := newTestNamespaceManager(t, true) + defer cleanup() + + _, err := nm.loadNamespaces(nm.ctx, fftypes.JSONObject{}, nm.plugins) + err = nm.getDatabasePlugins(nm.ctx, nm.plugins, fftypes.JSONObject{}) + assert.Regexp(t, "FF10434", err) + err = nm.getBlockchainPlugins(nm.ctx, nm.plugins, fftypes.JSONObject{}) + assert.Regexp(t, "FF10434", err) + err = nm.getDataExchangePlugins(nm.ctx, nm.plugins, fftypes.JSONObject{}) + assert.Regexp(t, "FF10434", err) + err = nm.getSharedStoragePlugins(nm.ctx, nm.plugins, fftypes.JSONObject{}) + assert.Regexp(t, "FF10434", err) + err = nm.getTokensPlugins(nm.ctx, nm.plugins, fftypes.JSONObject{}) + assert.Regexp(t, "FF10434", err) + err = nm.getIdentityPlugins(nm.ctx, nm.plugins, fftypes.JSONObject{}) + assert.Regexp(t, "FF10434", err) + err = nm.getAuthPlugin(nm.ctx, nm.plugins, fftypes.JSONObject{}) + assert.Regexp(t, "FF10434", err) +} + func TestEventsPluginBadType(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, false) + nm, _, cleanup := newTestNamespaceManager(t, false) defer cleanup() config.Set(coreconfig.EventTransportsEnabled, []string{"!unknown!"}) nm.eventsFactory = func(ctx context.Context, pluginType string) (events.Plugin, error) { return nil, fmt.Errorf("pop") } - plugins := make(map[string]*plugin) - err := nm.getEventPlugins(context.Background(), plugins, nm.dumpRootConfig()) + _, err := nm.loadPlugins(context.Background(), nm.dumpRootConfig()) assert.Regexp(t, "pop", err) } func TestInitBadNamespace(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1251,7 +1323,7 @@ func TestInitBadNamespace(t *testing.T) { } func TestLoadNamespacesReservedName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1269,7 +1341,7 @@ func TestLoadNamespacesReservedName(t *testing.T) { } func TestLoadNamespacesReservedNetworkName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1290,7 +1362,7 @@ func TestLoadNamespacesReservedNetworkName(t *testing.T) { } func TestLoadNamespacesDuplicate(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1312,7 +1384,7 @@ func TestLoadNamespacesDuplicate(t *testing.T) { } func TestLoadNamespacesNoName(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1329,7 +1401,7 @@ func TestLoadNamespacesNoName(t *testing.T) { } func TestLoadNamespacesNoDefault(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1348,7 +1420,7 @@ func TestLoadNamespacesNoDefault(t *testing.T) { } func TestLoadNamespacesUseDefaults(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1376,7 +1448,7 @@ func TestLoadNamespacesUseDefaults(t *testing.T) { } func TestLoadNamespacesNonMultipartyNoDatabase(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1396,7 +1468,7 @@ func TestLoadNamespacesNonMultipartyNoDatabase(t *testing.T) { } func TestLoadNamespacesMultipartyUnknownPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1417,7 +1489,7 @@ func TestLoadNamespacesMultipartyUnknownPlugin(t *testing.T) { } func TestLoadNamespacesMultipartyMultipleBlockchains(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1438,7 +1510,7 @@ func TestLoadNamespacesMultipartyMultipleBlockchains(t *testing.T) { } func TestLoadNamespacesMultipartyMultipleDX(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1459,7 +1531,7 @@ func TestLoadNamespacesMultipartyMultipleDX(t *testing.T) { } func TestLoadNamespacesMultipartyMultipleSS(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1480,7 +1552,7 @@ func TestLoadNamespacesMultipartyMultipleSS(t *testing.T) { } func TestLoadNamespacesMultipartyMultipleDB(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1500,8 +1572,50 @@ func TestLoadNamespacesMultipartyMultipleDB(t *testing.T) { assert.Regexp(t, "FF10394.*database", err) } +func TestLoadNamespacesMultipartyMultipleAuths(t *testing.T) { + nm, _, cleanup := newTestNamespaceManager(t, true) + defer cleanup() + + coreconfig.Reset() + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [basicauth, basicauth] + multiparty: + enabled: true + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10394.*auth", err) +} + +func TestLoadNamespacesMultipartyMultipleIdentity(t *testing.T) { + nm, _, cleanup := newTestNamespaceManager(t, true) + defer cleanup() + + coreconfig.Reset() + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(` + namespaces: + default: ns1 + predefined: + - name: ns1 + plugins: [tbd, tbd] + multiparty: + enabled: true + `)) + assert.NoError(t, err) + + nm.namespaces, err = nm.loadNamespaces(context.Background(), nm.dumpRootConfig(), nm.plugins) + assert.Regexp(t, "FF10394.*identity", err) +} + func TestInitNamespacesMultipartyWithAuth(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1522,7 +1636,7 @@ func TestInitNamespacesMultipartyWithAuth(t *testing.T) { } func TestLoadNamespacesNonMultipartyWithAuth(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1543,7 +1657,7 @@ func TestLoadNamespacesNonMultipartyWithAuth(t *testing.T) { } func TestLoadNamespacesMultipartyContract(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1567,7 +1681,7 @@ func TestLoadNamespacesMultipartyContract(t *testing.T) { } func TestLoadNamespacesNonMultipartyMultipleDB(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1586,7 +1700,7 @@ func TestLoadNamespacesNonMultipartyMultipleDB(t *testing.T) { } func TestLoadNamespacesNonMultipartyMultipleBlockchains(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1605,7 +1719,7 @@ func TestLoadNamespacesNonMultipartyMultipleBlockchains(t *testing.T) { } func TestLoadNamespacesMultipartyMissingPlugins(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1626,7 +1740,7 @@ func TestLoadNamespacesMultipartyMissingPlugins(t *testing.T) { } func TestLoadNamespacesNonMultipartyUnknownPlugin(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1645,7 +1759,7 @@ func TestLoadNamespacesNonMultipartyUnknownPlugin(t *testing.T) { } func TestLoadNamespacesNonMultipartyTokens(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() @@ -1664,25 +1778,25 @@ func TestLoadNamespacesNonMultipartyTokens(t *testing.T) { } func TestStart(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mbi.On("Start", mock.Anything).Return(nil) - nm.mdx.On("Start", mock.Anything).Return(nil) - nm.mti[0].On("Start", mock.Anything).Return(nil) - nm.mti[1].On("Start", mock.Anything).Return(nil) - nm.mo.On("Start", mock.Anything).Return(nil) + nmm.mbi.On("Start", mock.Anything).Return(nil) + nmm.mdx.On("Start", mock.Anything).Return(nil) + nmm.mti[0].On("Start", mock.Anything).Return(nil) + nmm.mti[1].On("Start", mock.Anything).Return(nil) + nmm.mo.On("Start", mock.Anything).Return(nil) err := nm.Start() assert.NoError(t, err) } func TestStartBlockchainFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mo.On("Start", mock.Anything).Return(nil) - nm.mbi.On("Start").Return(fmt.Errorf("pop")) + nmm.mo.On("Start", mock.Anything).Return(nil) + nmm.mbi.On("Start").Return(fmt.Errorf("pop")) err := nm.startNamespacesAndPlugins(nm.namespaces, map[string]*plugin{ "ethereum": nm.plugins["ethereum"], @@ -1692,11 +1806,11 @@ func TestStartBlockchainFail(t *testing.T) { } func TestStartDataExchangeFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mo.On("Start", mock.Anything).Return(nil) - nm.mdx.On("Start").Return(fmt.Errorf("pop")) + nmm.mo.On("Start", mock.Anything).Return(nil) + nmm.mdx.On("Start").Return(fmt.Errorf("pop")) err := nm.startNamespacesAndPlugins(nm.namespaces, map[string]*plugin{ "ffdx": nm.plugins["ffdx"], @@ -1706,11 +1820,11 @@ func TestStartDataExchangeFail(t *testing.T) { } func TestStartTokensFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mo.On("Start", mock.Anything).Return(nil) - nm.mti[0].On("Start").Return(fmt.Errorf("pop")) + nmm.mo.On("Start", mock.Anything).Return(nil) + nmm.mti[0].On("Start").Return(fmt.Errorf("pop")) err := nm.startNamespacesAndPlugins(nm.namespaces, map[string]*plugin{ "erc721": nm.plugins["erc721"], @@ -1720,30 +1834,30 @@ func TestStartTokensFail(t *testing.T) { } func TestStartOrchestratorFail(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mo.On("Start", mock.Anything).Return(fmt.Errorf("pop")) + nmm.mo.On("Start", mock.Anything).Return(fmt.Errorf("pop")) err := nm.startNamespacesAndPlugins(nm.namespaces, map[string]*plugin{}) assert.EqualError(t, err, "pop") } func TestWaitStop(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mo.On("WaitStop").Return() - nm.mae.On("WaitStop").Return() + nmm.mo.On("WaitStop").Return() + nmm.mae.On("WaitStop").Return() nm.WaitStop() - nm.mo.AssertExpectations(t) - nm.mae.AssertExpectations(t) + nmm.mo.AssertExpectations(t) + nmm.mae.AssertExpectations(t) } func TestReset(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() childCtx, childCancel := context.WithCancel(context.Background()) @@ -1751,14 +1865,14 @@ func TestReset(t *testing.T) { assert.NoError(t, err) childCancel() - assert.True(t, <-nm.namespaceManager.reset) + assert.True(t, <-nm.reset) } func TestResetRejectIfConfigAutoReload(t *testing.T) { coreconfig.Reset() config.Set(coreconfig.ConfigAutoReload, true) - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() config.Set(coreconfig.ConfigAutoReload, true) @@ -1768,7 +1882,7 @@ func TestResetRejectIfConfigAutoReload(t *testing.T) { } func TestLoadMetrics(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.metrics = nil @@ -1777,7 +1891,7 @@ func TestLoadMetrics(t *testing.T) { } func TestLoadAdminEvents(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() nm.adminEvents = nil @@ -1787,7 +1901,7 @@ func TestLoadAdminEvents(t *testing.T) { } func TestGetNamespaces(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() results, err := nm.GetNamespaces(context.Background()) @@ -1796,7 +1910,7 @@ func TestGetNamespaces(t *testing.T) { } func TestGetOperationByNamespacedID(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1815,7 +1929,7 @@ func TestGetOperationByNamespacedID(t *testing.T) { } func TestGetOperationByNamespacedIDBadID(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1830,7 +1944,7 @@ func TestGetOperationByNamespacedIDBadID(t *testing.T) { } func TestGetOperationByNamespacedIDNoOrchestrator(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1847,7 +1961,7 @@ func TestGetOperationByNamespacedIDNoOrchestrator(t *testing.T) { } func TestResolveOperationByNamespacedID(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1868,7 +1982,7 @@ func TestResolveOperationByNamespacedID(t *testing.T) { } func TestResolveOperationByNamespacedIDBadID(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1883,7 +1997,7 @@ func TestResolveOperationByNamespacedIDBadID(t *testing.T) { } func TestResolveOperationByNamespacedIDNoOrchestrator(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() mo := &orchestratormocks.Orchestrator{} @@ -1900,11 +2014,11 @@ func TestResolveOperationByNamespacedIDNoOrchestrator(t *testing.T) { } func TestAuthorize(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, nmm, cleanup := newTestNamespaceManager(t, true) defer cleanup() - nm.mo.On("Authorize", mock.Anything, mock.Anything).Return(nil) + nmm.mo.On("Authorize", mock.Anything, mock.Anything).Return(nil) nm.namespaces["ns1"] = &namespace{ - orchestrator: nm.mo, + orchestrator: nmm.mo, } err := nm.Authorize(context.Background(), &fftypes.AuthReq{ Namespace: "ns1", @@ -1913,7 +2027,7 @@ func TestAuthorize(t *testing.T) { } func TestValidateNonMultipartyConfig(t *testing.T) { - nm, cleanup := newTestNamespaceManager(t, true) + nm, _, cleanup := newTestNamespaceManager(t, true) defer cleanup() coreconfig.Reset() From 498647ba95d325bea6291880a0a58b229b5b9c15 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Fri, 9 Dec 2022 15:55:43 -0500 Subject: [PATCH 06/11] Worked through unit tests for dynamic config reload Signed-off-by: Peter Broadhurst --- go.mod | 4 +- go.sum | 8 +- internal/namespace/configreload.go | 5 +- internal/namespace/configreload_test.go | 387 +++++++++++++++++++++++- 4 files changed, 378 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 5994356d5..b805a6734 100644 --- a/go.mod +++ b/go.mod @@ -8,14 +8,13 @@ require ( github.com/aidarkhanov/nanoid v1.0.8 github.com/blang/semver/v4 v4.0.0 github.com/docker/go-units v0.5.0 - github.com/fsnotify/fsnotify v1.6.0 github.com/getkin/kin-openapi v0.107.0 github.com/ghodss/yaml v1.0.0 github.com/go-resty/resty/v2 v2.7.0 github.com/golang-migrate/migrate/v4 v4.15.2 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 - github.com/hyperledger/firefly-common v1.1.6-0.20221209193217-440fcceceaae + github.com/hyperledger/firefly-common v1.1.6-0.20221209201448-45af5a8b2529 github.com/hyperledger/firefly-signer v1.1.2 github.com/jarcoal/httpmock v1.2.0 github.com/karlseguin/ccache v2.0.3+incompatible @@ -39,6 +38,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/golang/protobuf v1.5.2 // indirect diff --git a/go.sum b/go.sum index f5ece10f7..0244a094a 100644 --- a/go.sum +++ b/go.sum @@ -675,12 +675,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hyperledger/firefly-common v1.1.5 h1:yVOsUqZQoV/iCc/PxYFGdq/438JDdR/TdlCpMOv3XvM= -github.com/hyperledger/firefly-common v1.1.5/go.mod h1:3ubN46/dB+xurCPvdfqMKjB/CJU3I/DsfOoS7dY2SyQ= -github.com/hyperledger/firefly-common v1.1.6-0.20221209164516-9f409ba6efc6 h1:eD99qjoPenw8ChoQoWOeUqjMw8kQq7zdIRyU4JOcYMA= -github.com/hyperledger/firefly-common v1.1.6-0.20221209164516-9f409ba6efc6/go.mod h1:3ubN46/dB+xurCPvdfqMKjB/CJU3I/DsfOoS7dY2SyQ= -github.com/hyperledger/firefly-common v1.1.6-0.20221209193217-440fcceceaae h1:AV1aN15uKAOhLQGB5WnHce5xqkjz29tnFKC7hl/7NTo= -github.com/hyperledger/firefly-common v1.1.6-0.20221209193217-440fcceceaae/go.mod h1:3ubN46/dB+xurCPvdfqMKjB/CJU3I/DsfOoS7dY2SyQ= +github.com/hyperledger/firefly-common v1.1.6-0.20221209201448-45af5a8b2529 h1:IungMJGghkaiNULQc/OpfVMyYQ0n+u/bKtJBjfUwE7c= +github.com/hyperledger/firefly-common v1.1.6-0.20221209201448-45af5a8b2529/go.mod h1:aqaq2ZUzSlkwVC/TF+pwUkHxGDLYSE2DzdozZ1MIMeM= github.com/hyperledger/firefly-signer v1.1.2 h1:QuS3M5w9px3BnPa4jIWMDg+z2ySK76MoO5Egh0G+tFg= github.com/hyperledger/firefly-signer v1.1.2/go.mod h1:4h2MN910A2knrWGYCT+aWjBDlhptgQn/9WcT1N/Ct8s= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= diff --git a/internal/namespace/configreload.go b/internal/namespace/configreload.go index d046c5a68..84005b7b2 100644 --- a/internal/namespace/configreload.go +++ b/internal/namespace/configreload.go @@ -133,10 +133,7 @@ func (nm *namespaceManager) stopDefunctNamespaces(ctx context.Context, newPlugin if existingNS := nm.namespaces[nsName]; existingNS != nil { var changes []string if !existingNS.configHash.Equals(newNS.configHash) { - changes = append(changes, "namespace_config") - } - if len(existingNS.pluginNames) != len(newNS.pluginNames) { - changes = append(changes, "plugin_count") + changes = append(changes, "namespace_config") // Encompasses the list of plugins } for _, pluginName := range newNS.pluginNames { existingPlugin := nm.plugins[pluginName] diff --git a/internal/namespace/configreload_test.go b/internal/namespace/configreload_test.go index d9eff7853..e63217297 100644 --- a/internal/namespace/configreload_test.go +++ b/internal/namespace/configreload_test.go @@ -289,6 +289,123 @@ plugins: passwordfile: /etc/firefly/test_users ` +var exampleConfig3NSchanges = ` +--- +http: + address: 0.0.0.0 + port: 5000 + publicURL: https://myfirefly.example.com +log: + level: debug +metrics: + enabled: true + address: 127.0.0.1 + port: 6000 + path: /metrics +namespaces: + default: ns1 + predefined: + - defaultKey: 0x763617D0e180F4909D796F8c46b6b2d17c61b5f2 # new default key + name: ns1 + plugins: + - sharedstorage-ns1 + - database0 + - blockchain-ns1 + - ff-dx + - erc1155-ns1 + - erc20_erc721-ns1 + - test_user_auth-ns1 + multiparty: + enabled: true + node: + name: node1 + org: + name: org1 + key: 0xbEa50Ec98776beF144Fc63078e7b15291Ac64cfA + contract: + - firstevent: "0" + location: + address: 0x7359d2ecc199C48369b390522c29b77A5Af30882 + - defaultKey: 0x630659A26fa005d50Fa9706D8a4e242fd4169A61 + name: ns2 + plugins: + - database0 + - blockchain-ns2 + - test_user_auth-ns2 # config changed + multiparty: {} +node: {} +plugins: + blockchain: + - ethereum: + ethconnect: + topic: "0" + url: https://ethconnect1.example.com:5000 + name: blockchain-ns1 + type: ethereum + - ethereum: + ethconnect: + topic: "0" + url: https://ethconnect2.example.com:5000 + name: blockchain-ns2 + type: ethereum + database: + - name: database0 + postgres: + url: postgres://postgrs.example.com:5432/firefly?sslmode=require + type: postgres + dataexchange: + - ffdx: + url: https://ffdx:3000 + initEnabled: true + name: ff-dx + type: ffdx + sharedstorage: + - ipfs: + api: + url: https://ipfs1.example.com + auth: + username: someuser + password: somepass + gateway: + url: https://ipfs1.example.com + auth: + username: someuser + password: somepass + name: sharedstorage-ns1 + type: ipfs + - ipfs: + api: + url: https://ipfs2.example.com + gateway: + url: https://ipfs2.example.com + name: sharedstorage-ns2 + type: ipfs + tokens: + - fftokens: + url: https://ff-tokens-ns1-erc1155:3000 + name: erc1155-ns1 + type: fftokens + - fftokens: + url: https://ff-tokens-ns1-erc20-erc721:3000 + name: erc20_erc721-ns1 + type: fftokens + - fftokens: + url: https://ff-tokens-ns2-erc20-erc721:3000 + name: erc20_erc721-ns2 + type: fftokens + auth: + - name: test_user_auth-ns1 + type: basic + basic: + passwordfile: /etc/firefly/test_users + - name: test_user_auth-ns2 + type: basic + basic: + passwordfile: /etc/firefly/test_users_new +ui: + path: ./frontend +` + func mockInitConfig(nmm *nmMocks) { nmm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) nmm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() @@ -360,10 +477,40 @@ func TestConfigListenerE2E(t *testing.T) { } +func TestConfigListenerUnreadableYAML(t *testing.T) { + + testDir := t.TempDir() + configFilename := fmt.Sprintf("%s/config.firefly.yaml", testDir) + viper.SetConfigFile(configFilename) + + coreconfig.Reset() + InitConfig() + config.Set(coreconfig.ConfigAutoReload, true) + + nm := NewNamespaceManager().(*namespaceManager) + nmm := mockPluginFactories(nm) + mockInitConfig(nmm) + + ctx, cancelCtx := context.WithCancel(context.Background()) + err := nm.Init(ctx, cancelCtx, make(chan bool), func() { coreconfig.Reset() }) + assert.NoError(t, err) + + err = nm.Start() + assert.NoError(t, err) + + err = ioutil.WriteFile(configFilename, []byte(`--\n: ! YAML !!!: !`), 0664) + assert.NoError(t, err) + + // Should stop itself + <-nm.ctx.Done() + nm.WaitStop() + +} + func TestConfigReload1to2(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - nm, mmn, cleanup := newTestNamespaceManager(t, false) + nm, nmm, cleanup := newTestNamespaceManager(t, false) defer cleanup() viper.SetConfigType("yaml") @@ -373,7 +520,7 @@ func TestConfigReload1to2(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() - mockInitConfig(mmn) + mockInitConfig(nmm) err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) assert.NoError(t, err) @@ -431,10 +578,76 @@ func TestConfigReload1to2(t *testing.T) { } +func TestConfigReload1to3(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + nm, nmm, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + + viper.SetConfigType("yaml") + err := viper.ReadConfig(strings.NewReader(exampleConfig1base)) + assert.NoError(t, err) + + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + mockInitConfig(nmm) + + err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) + assert.NoError(t, err) + + err = nm.Start() + assert.NoError(t, err) + + originalPlugins := nm.plugins + originalPluginHashes := make(map[string]*fftypes.Bytes32) + for k, v := range originalPlugins { + originalPluginHashes[k] = v.configHash + } + originaNS := nm.namespaces + originalNSHashes := make(map[string]*fftypes.Bytes32) + for k, v := range originaNS { + originalNSHashes[k] = v.configHash + } + + coreconfig.Reset() + InitConfig() + viper.SetConfigType("yaml") + err = viper.ReadConfig(strings.NewReader(exampleConfig3NSchanges)) + assert.NoError(t, err) + + // Drive the config reload + nm.configReloaded(nm.ctx) + + // Check that we didn't cancel the context + select { + case <-nm.ctx.Done(): + assert.Fail(t, "Error occurred in config reload") + default: + } + + // Check none of the plugins reloaded + for name := range originalPlugins { + if name != "test_user_auth-ns2" { + assert.True(t, originalPlugins[name] == nm.plugins[name], name) + assert.Equal(t, originalPluginHashes[name], nm.plugins[name].configHash, name) + } else { + assert.False(t, originalPlugins[name] == nm.plugins[name], name) + assert.NotEqual(t, originalPluginHashes[name], nm.plugins[name].configHash, name) + } + } + + // Check both namespaces reloaded + assert.Len(t, nm.namespaces, len(originaNS)) + assert.False(t, originaNS["ns1"] == nm.namespaces["ns1"]) + assert.NotEqual(t, originalNSHashes["ns1"], nm.namespaces["ns1"].configHash) + +} + func TestConfigReloadBadNewConfigPlugins(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - nm, mmn, cleanup := newTestNamespaceManager(t, false) + nm, nmm, cleanup := newTestNamespaceManager(t, false) defer cleanup() viper.SetConfigType("yaml") @@ -444,7 +657,7 @@ func TestConfigReloadBadNewConfigPlugins(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() - mockInitConfig(mmn) + mockInitConfig(nmm) err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) assert.NoError(t, err) @@ -483,7 +696,7 @@ plugins: func TestConfigReloadBadNSMissingRequiredPlugins(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - nm, mmn, cleanup := newTestNamespaceManager(t, false) + nm, nmm, cleanup := newTestNamespaceManager(t, false) defer cleanup() viper.SetConfigType("yaml") @@ -493,7 +706,7 @@ func TestConfigReloadBadNSMissingRequiredPlugins(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() - mockInitConfig(mmn) + mockInitConfig(nmm) err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) assert.NoError(t, err) @@ -542,7 +755,7 @@ namespaces: func TestConfigDownToNothingOk(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - nm, mmn, cleanup := newTestNamespaceManager(t, false) + nm, nmm, cleanup := newTestNamespaceManager(t, false) defer cleanup() viper.SetConfigType("yaml") @@ -552,7 +765,7 @@ func TestConfigDownToNothingOk(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() - mockInitConfig(mmn) + mockInitConfig(nmm) err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) assert.NoError(t, err) @@ -566,7 +779,7 @@ func TestConfigDownToNothingOk(t *testing.T) { // Nothing - no plugins, no namespaces assert.NoError(t, err) - mmn.mo.On("WaitStop").Return(nil) + nmm.mo.On("WaitStop").Return(nil) // Drive the config reload nm.configReloaded(nm.ctx) @@ -579,7 +792,7 @@ func TestConfigDownToNothingOk(t *testing.T) { } // Check we didn't lose our plugins - assert.Len(t, nm.plugins, len(mmn.mei)) // Just the events plugins + assert.Len(t, nm.plugins, len(nmm.mei)) // Just the events plugins assert.Empty(t, nm.namespaces, 0) } @@ -587,7 +800,7 @@ func TestConfigDownToNothingOk(t *testing.T) { func TestConfigStartPluginsFails(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - nm, mmn, cleanup := newTestNamespaceManager(t, false) + nm, nmm, cleanup := newTestNamespaceManager(t, false) defer cleanup() viper.SetConfigType("yaml") @@ -597,7 +810,7 @@ func TestConfigStartPluginsFails(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() - mockInitConfig(mmn) + mockInitConfig(nmm) err = nm.Init(ctx, cancelCtx, make(chan bool), func() {}) assert.NoError(t, err) @@ -611,7 +824,7 @@ func TestConfigStartPluginsFails(t *testing.T) { // Nothing - no plugins, no namespaces assert.NoError(t, err) - mmn.mo.On("WaitStop").Return(nil) + nmm.mo.On("WaitStop").Return(nil) // Drive the config reload nm.configReloaded(nm.ctx) @@ -624,7 +837,153 @@ func TestConfigStartPluginsFails(t *testing.T) { } // Check we didn't lose our plugins - assert.Len(t, nm.plugins, len(mmn.mei)) // Just the events plugins + assert.Len(t, nm.plugins, len(nmm.mei)) // Just the events plugins assert.Empty(t, nm.namespaces, 0) } + +func TestConfigReloadInitPluginsFailOnReload(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + nm, nmm, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + + // Start with empty config + for _, mei := range nmm.mei { + mei.On("Init", mock.Anything, mock.Anything).Return(nil).Maybe() + } + + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + err := nm.Init(ctx, cancelCtx, make(chan bool), func() {}) + assert.NoError(t, err) + + err = nm.Start() + assert.NoError(t, err) + + coreconfig.Reset() + InitConfig() + viper.SetConfigType("yaml") + err = viper.ReadConfig(strings.NewReader(` +plugins: + database: + - name: "badness" + type: "postgres" +`)) + assert.NoError(t, err) + + // Drive the config reload + nmm.mdi.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nm.configReloaded(nm.ctx) + + // Should terminate + <-nm.ctx.Done() + nmm.mae.On("WaitStop").Return(nil).Maybe() + nmm.mo.On("WaitStop").Return(nil).Maybe() + nm.WaitStop() + +} + +func TestConfigReloadInitNamespacesFailOnReload(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + nm, nmm, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + + // Start with empty config + for _, mei := range nmm.mei { + mei.On("Init", mock.Anything, mock.Anything).Return(nil).Maybe() + } + + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + err := nm.Init(ctx, cancelCtx, make(chan bool), func() {}) + assert.NoError(t, err) + + err = nm.Start() + assert.NoError(t, err) + + coreconfig.Reset() + InitConfig() + viper.SetConfigType("yaml") + err = viper.ReadConfig(strings.NewReader(` +plugins: + database: + - name: "postgres" + type: "postgres" +namespaces: + predefined: + - name: default + plugins: + - postgres + `)) + assert.NoError(t, err) + + // Drive the config reload + nmm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, fmt.Errorf("pop")) + nm.configReloaded(nm.ctx) + + // Should terminate + <-nm.ctx.Done() + nmm.mae.On("WaitStop").Return(nil).Maybe() + nmm.mo.On("WaitStop").Return(nil).Maybe() + nm.WaitStop() + +} + +func TestConfigReloadInitNamespacesFailOnStart(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + nm, nmm, cleanup := newTestNamespaceManager(t, false) + defer cleanup() + + // Start with empty config + for _, mei := range nmm.mei { + mei.On("Init", mock.Anything, mock.Anything).Return(nil).Maybe() + } + + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + err := nm.Init(ctx, cancelCtx, make(chan bool), func() {}) + assert.NoError(t, err) + + err = nm.Start() + assert.NoError(t, err) + + coreconfig.Reset() + InitConfig() + viper.SetConfigType("yaml") + err = viper.ReadConfig(strings.NewReader(` +plugins: + database: + - name: "postgres" + type: "postgres" +namespaces: + predefined: + - name: default + plugins: + - postgres + `)) + assert.NoError(t, err) + + // Drive the config reload + nmm.mdi.On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mdi.On("SetHandler", database.GlobalHandler, mock.Anything).Return() + nmm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil) + nmm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil) + nmm.mo.On("Init", mock.Anything, mock.Anything).Return(nil) + nmm.mo.On("Start", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + nm.configReloaded(nm.ctx) + + // Should terminate + <-nm.ctx.Done() + nmm.mae.On("WaitStop").Return(nil).Maybe() + nmm.mo.On("WaitStop").Return(nil).Maybe() + nm.WaitStop() + +} From 50e452a7f697781aed4e7d9bf2d2fb8b8bf35b5a Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Fri, 9 Dec 2022 16:16:33 -0500 Subject: [PATCH 07/11] Ensure metrics initialized before namespaces Signed-off-by: Peter Broadhurst --- internal/namespace/manager.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go index f4dd8709a..250df81b3 100644 --- a/internal/namespace/manager.go +++ b/internal/namespace/manager.go @@ -190,6 +190,11 @@ func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFu } func (nm *namespaceManager) initComponents(ctx context.Context) (err error) { + if nm.metricsEnabled { + // Ensure metrics are registered, before initializing the namespaces + metrics.Registry() + } + // Initialize all the plugins on initial startup if err = nm.initPlugins(ctx, nm.plugins); err != nil { return err @@ -200,11 +205,6 @@ func (nm *namespaceManager) initComponents(ctx context.Context) (err error) { return err } - if nm.metricsEnabled { - // Ensure metrics are registered - metrics.Registry() - } - if err := nm.startConfigListener(); err != nil { return err } From 1ffb555be43a788d40c624c29d421a6f0f931bc4 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Fri, 9 Dec 2022 16:29:35 -0500 Subject: [PATCH 08/11] Fix lint Signed-off-by: Peter Broadhurst --- internal/namespace/configreload.go | 2 +- internal/namespace/manager.go | 4 ++-- internal/namespace/manager_test.go | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/namespace/configreload.go b/internal/namespace/configreload.go index 84005b7b2..f397f9935 100644 --- a/internal/namespace/configreload.go +++ b/internal/namespace/configreload.go @@ -101,7 +101,7 @@ func (nm *namespaceManager) configReloaded(ctx context.Context) { nm.namespaces = availableNS // Only initialize updated plugins - if err = nm.initPlugins(ctx, updatedPlugins); err != nil { + if err = nm.initPlugins(updatedPlugins); err != nil { log.L(ctx).Errorf("Failed to initialize plugins after config reload: %s", err) nm.cancelCtx() // stop the world return diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go index 250df81b3..98dd4c642 100644 --- a/internal/namespace/manager.go +++ b/internal/namespace/manager.go @@ -196,7 +196,7 @@ func (nm *namespaceManager) initComponents(ctx context.Context) (err error) { } // Initialize all the plugins on initial startup - if err = nm.initPlugins(ctx, nm.plugins); err != nil { + if err = nm.initPlugins(nm.plugins); err != nil { return err } @@ -709,7 +709,7 @@ func (nm *namespaceManager) getSharedStoragePlugins(ctx context.Context, plugins return nil } -func (nm *namespaceManager) initPlugins(ctx context.Context, pluginsToStart map[string]*plugin) (err error) { +func (nm *namespaceManager) initPlugins(pluginsToStart map[string]*plugin) (err error) { for name, p := range nm.plugins { if pluginsToStart[name] == nil { continue diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go index 3e4690e9d..07adda68f 100644 --- a/internal/namespace/manager_test.go +++ b/internal/namespace/manager_test.go @@ -334,7 +334,7 @@ func TestInitDatabaseFail(t *testing.T) { nmm.mdi.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - err := nm.initPlugins(context.Background(), map[string]*plugin{ + err := nm.initPlugins(map[string]*plugin{ "postgres": nm.plugins["postgres"], }) assert.EqualError(t, err, "pop") @@ -346,7 +346,7 @@ func TestInitBlockchainFail(t *testing.T) { nmm.mbi.On("Init", mock.Anything, mock.Anything, mock.Anything, nmm.mmi, mock.Anything).Return(fmt.Errorf("pop")) - err := nm.initPlugins(context.Background(), map[string]*plugin{ + err := nm.initPlugins(map[string]*plugin{ "ethereum": nm.plugins["ethereum"], }) assert.EqualError(t, err, "pop") @@ -358,7 +358,7 @@ func TestInitDataExchangeFail(t *testing.T) { nmm.mdx.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - err := nm.initPlugins(context.Background(), map[string]*plugin{ + err := nm.initPlugins(map[string]*plugin{ "ffdx": nm.plugins["ffdx"], }) assert.EqualError(t, err, "pop") @@ -370,7 +370,7 @@ func TestInitSharedStorageFail(t *testing.T) { nmm.mps.On("Init", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - err := nm.initPlugins(context.Background(), map[string]*plugin{ + err := nm.initPlugins(map[string]*plugin{ "ipfs": nm.plugins["ipfs"], }) assert.EqualError(t, err, "pop") @@ -382,7 +382,7 @@ func TestInitTokensFail(t *testing.T) { nmm.mti[0].On("Init", mock.Anything, mock.Anything, "erc721", mock.Anything).Return(fmt.Errorf("pop")) - err := nm.initPlugins(context.Background(), map[string]*plugin{ + err := nm.initPlugins(map[string]*plugin{ "erc721": nm.plugins["erc721"], }) assert.EqualError(t, err, "pop") @@ -406,7 +406,7 @@ func TestInitAuthFail(t *testing.T) { nmm.mai.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - err := nm.initPlugins(context.Background(), map[string]*plugin{ + err := nm.initPlugins(map[string]*plugin{ "basicauth": nm.plugins["basicauth"], }) assert.EqualError(t, err, "pop") From 0214e15cc00bca88e88e9dae6006c3dcc9e24910 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 12 Dec 2022 08:29:05 -0500 Subject: [PATCH 09/11] Pull in updated fsnotify logic from common Signed-off-by: Peter Broadhurst --- go.mod | 2 +- go.sum | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b805a6734..47b0d7a49 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/golang-migrate/migrate/v4 v4.15.2 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 - github.com/hyperledger/firefly-common v1.1.6-0.20221209201448-45af5a8b2529 + github.com/hyperledger/firefly-common v1.1.6-0.20221212133115-cadb1f33bc0d github.com/hyperledger/firefly-signer v1.1.2 github.com/jarcoal/httpmock v1.2.0 github.com/karlseguin/ccache v2.0.3+incompatible diff --git a/go.sum b/go.sum index 0244a094a..20cbb7fb6 100644 --- a/go.sum +++ b/go.sum @@ -675,8 +675,10 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hyperledger/firefly-common v1.1.6-0.20221209201448-45af5a8b2529 h1:IungMJGghkaiNULQc/OpfVMyYQ0n+u/bKtJBjfUwE7c= -github.com/hyperledger/firefly-common v1.1.6-0.20221209201448-45af5a8b2529/go.mod h1:aqaq2ZUzSlkwVC/TF+pwUkHxGDLYSE2DzdozZ1MIMeM= +github.com/hyperledger/firefly-common v1.1.6-0.20221212132657-860de3c9a0de h1:qOAWrhp52lxcMqwcg/n0HVqancWmRd9i2WA3H4rPLxg= +github.com/hyperledger/firefly-common v1.1.6-0.20221212132657-860de3c9a0de/go.mod h1:aqaq2ZUzSlkwVC/TF+pwUkHxGDLYSE2DzdozZ1MIMeM= +github.com/hyperledger/firefly-common v1.1.6-0.20221212133115-cadb1f33bc0d h1:LbBrkxUJZjpNDz5HWxGZGVpn8JDs3FHu1rdpRhSK4D0= +github.com/hyperledger/firefly-common v1.1.6-0.20221212133115-cadb1f33bc0d/go.mod h1:aqaq2ZUzSlkwVC/TF+pwUkHxGDLYSE2DzdozZ1MIMeM= github.com/hyperledger/firefly-signer v1.1.2 h1:QuS3M5w9px3BnPa4jIWMDg+z2ySK76MoO5Egh0G+tFg= github.com/hyperledger/firefly-signer v1.1.2/go.mod h1:4h2MN910A2knrWGYCT+aWjBDlhptgQn/9WcT1N/Ct8s= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= From fde6af554863b9c9908766bf59d89dc650002817 Mon Sep 17 00:00:00 2001 From: Nicko Guyer Date: Fri, 13 Jan 2023 10:46:45 -0500 Subject: [PATCH 10/11] Update date in header Signed-off-by: Nicko Guyer --- internal/coreconfig/coreconfig.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/coreconfig/coreconfig.go b/internal/coreconfig/coreconfig.go index 15d5234fa..c6c3dcd56 100644 --- a/internal/coreconfig/coreconfig.go +++ b/internal/coreconfig/coreconfig.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Kaleido, Inc. +// Copyright © 2023 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // From f23122eaefd50cb3e7c78c5f0ef16c409c9983c7 Mon Sep 17 00:00:00 2001 From: Nicko Guyer Date: Fri, 13 Jan 2023 11:10:40 -0500 Subject: [PATCH 11/11] Update date in header Signed-off-by: Nicko Guyer --- internal/coremsgs/en_config_descriptions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/coremsgs/en_config_descriptions.go b/internal/coremsgs/en_config_descriptions.go index 9abc8c17a..80876e288 100644 --- a/internal/coremsgs/en_config_descriptions.go +++ b/internal/coremsgs/en_config_descriptions.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Kaleido, Inc. +// Copyright © 2023 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 //