github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/orchestrator/orchestrator.go (about)

     1  // Copyright © 2021 Kaleido, Inc.
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package orchestrator
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/kaleido-io/firefly/internal/batch"
    24  	"github.com/kaleido-io/firefly/internal/blockchain/bifactory"
    25  	"github.com/kaleido-io/firefly/internal/broadcast"
    26  	"github.com/kaleido-io/firefly/internal/config"
    27  	"github.com/kaleido-io/firefly/internal/data"
    28  	"github.com/kaleido-io/firefly/internal/database/difactory"
    29  	"github.com/kaleido-io/firefly/internal/dataexchange/dxfactory"
    30  	"github.com/kaleido-io/firefly/internal/events"
    31  	"github.com/kaleido-io/firefly/internal/i18n"
    32  	"github.com/kaleido-io/firefly/internal/identity/iifactory"
    33  	"github.com/kaleido-io/firefly/internal/log"
    34  	"github.com/kaleido-io/firefly/internal/networkmap"
    35  	"github.com/kaleido-io/firefly/internal/privatemessaging"
    36  	"github.com/kaleido-io/firefly/internal/publicstorage/psfactory"
    37  	"github.com/kaleido-io/firefly/pkg/blockchain"
    38  	"github.com/kaleido-io/firefly/pkg/database"
    39  	"github.com/kaleido-io/firefly/pkg/dataexchange"
    40  	"github.com/kaleido-io/firefly/pkg/fftypes"
    41  	"github.com/kaleido-io/firefly/pkg/identity"
    42  	"github.com/kaleido-io/firefly/pkg/publicstorage"
    43  )
    44  
    45  var (
    46  	blockchainConfig    = config.NewPluginConfig("blockchain")
    47  	databaseConfig      = config.NewPluginConfig("database")
    48  	identityConfig      = config.NewPluginConfig("identity")
    49  	publicstorageConfig = config.NewPluginConfig("publicstorage")
    50  	dataexchangeConfig  = config.NewPluginConfig("dataexchange")
    51  )
    52  
    53  // Orchestrator is the main interface behind the API, implementing the actions
    54  type Orchestrator interface {
    55  	Init(ctx context.Context) error
    56  	Start() error
    57  	WaitStop() // The close itself is performed by canceling the context
    58  	Broadcast() broadcast.Manager
    59  	PrivateMessaging() privatemessaging.Manager
    60  	Events() events.EventManager
    61  	NetworkMap() networkmap.Manager
    62  	Data() data.Manager
    63  
    64  	// Subscription management
    65  	GetSubscriptions(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Subscription, error)
    66  	GetSubscriptionByID(ctx context.Context, ns, id string) (*fftypes.Subscription, error)
    67  	CreateSubscription(ctx context.Context, ns string, subDef *fftypes.Subscription) (*fftypes.Subscription, error)
    68  	DeleteSubscription(ctx context.Context, ns, id string) error
    69  
    70  	// Data Query
    71  	GetNamespace(ctx context.Context, ns string) (*fftypes.Namespace, error)
    72  	GetNamespaces(ctx context.Context, filter database.AndFilter) ([]*fftypes.Namespace, error)
    73  	GetTransactionByID(ctx context.Context, ns, id string) (*fftypes.Transaction, error)
    74  	GetTransactionOperations(ctx context.Context, ns, id string) ([]*fftypes.Operation, error)
    75  	GetTransactions(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Transaction, error)
    76  	GetMessageByID(ctx context.Context, ns, id string, withValues bool) (*fftypes.MessageInput, error)
    77  	GetMessages(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Message, error)
    78  	GetMessageTransaction(ctx context.Context, ns, id string) (*fftypes.Transaction, error)
    79  	GetMessageOperations(ctx context.Context, ns, id string) ([]*fftypes.Operation, error)
    80  	GetMessageEvents(ctx context.Context, ns, id string, filter database.AndFilter) ([]*fftypes.Event, error)
    81  	GetMessageData(ctx context.Context, ns, id string) ([]*fftypes.Data, error)
    82  	GetMessagesForData(ctx context.Context, ns, dataID string, filter database.AndFilter) ([]*fftypes.Message, error)
    83  	GetBatchByID(ctx context.Context, ns, id string) (*fftypes.Batch, error)
    84  	GetBatches(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Batch, error)
    85  	GetDataByID(ctx context.Context, ns, id string) (*fftypes.Data, error)
    86  	GetData(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Data, error)
    87  	GetDatatypeByID(ctx context.Context, ns, id string) (*fftypes.Datatype, error)
    88  	GetDatatypes(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Datatype, error)
    89  	GetOperationByID(ctx context.Context, ns, id string) (*fftypes.Operation, error)
    90  	GetOperations(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Operation, error)
    91  	GetEventByID(ctx context.Context, ns, id string) (*fftypes.Event, error)
    92  	GetEvents(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Event, error)
    93  }
    94  
    95  type orchestrator struct {
    96  	ctx           context.Context
    97  	started       bool
    98  	database      database.Plugin
    99  	blockchain    blockchain.Plugin
   100  	identity      identity.Plugin
   101  	publicstorage publicstorage.Plugin
   102  	dataexchange  dataexchange.Plugin
   103  	events        events.EventManager
   104  	networkmap    networkmap.Manager
   105  	batch         batch.Manager
   106  	broadcast     broadcast.Manager
   107  	messaging     privatemessaging.Manager
   108  	data          data.Manager
   109  	bc            boundCallbacks
   110  }
   111  
   112  func NewOrchestrator() Orchestrator {
   113  	or := &orchestrator{}
   114  
   115  	// Initialize the config on all the factories
   116  	bifactory.InitPrefix(blockchainConfig)
   117  	difactory.InitPrefix(databaseConfig)
   118  	psfactory.InitPrefix(publicstorageConfig)
   119  	dxfactory.InitPrefix(dataexchangeConfig)
   120  
   121  	return or
   122  }
   123  
   124  func (or *orchestrator) Init(ctx context.Context) (err error) {
   125  	or.ctx = ctx
   126  	err = or.initPlugins(ctx)
   127  	if err == nil {
   128  		err = or.initComponents(ctx)
   129  	}
   130  	if err == nil {
   131  		err = or.initNamespaces(ctx)
   132  	}
   133  	// Bind together the blockchain interface callbacks, with the events manager
   134  	or.bc.bi = or.blockchain
   135  	or.bc.ei = or.events
   136  	or.bc.dx = or.dataexchange
   137  	return err
   138  }
   139  
   140  func (or *orchestrator) Start() error {
   141  	err := or.blockchain.Start()
   142  	if err == nil {
   143  		err = or.batch.Start()
   144  	}
   145  	if err == nil {
   146  		err = or.events.Start()
   147  	}
   148  	if err == nil {
   149  		err = or.broadcast.Start()
   150  	}
   151  	if err == nil {
   152  		err = or.messaging.Start()
   153  	}
   154  	or.started = true
   155  	return err
   156  }
   157  
   158  func (or *orchestrator) WaitStop() {
   159  	if !or.started {
   160  		return
   161  	}
   162  	if or.batch != nil {
   163  		or.batch.WaitStop()
   164  		or.batch = nil
   165  	}
   166  	if or.broadcast != nil {
   167  		or.broadcast.WaitStop()
   168  		or.broadcast = nil
   169  	}
   170  	or.started = false
   171  }
   172  
   173  func (or *orchestrator) Broadcast() broadcast.Manager {
   174  	return or.broadcast
   175  }
   176  
   177  func (or *orchestrator) PrivateMessaging() privatemessaging.Manager {
   178  	return or.messaging
   179  }
   180  
   181  func (or *orchestrator) Events() events.EventManager {
   182  	return or.events
   183  }
   184  
   185  func (or *orchestrator) NetworkMap() networkmap.Manager {
   186  	return or.networkmap
   187  }
   188  
   189  func (or *orchestrator) Data() data.Manager {
   190  	return or.data
   191  }
   192  
   193  func (or *orchestrator) initPlugins(ctx context.Context) (err error) {
   194  
   195  	if or.database == nil {
   196  		diType := config.GetString(config.DatabaseType)
   197  		if or.database, err = difactory.GetPlugin(ctx, diType); err != nil {
   198  			return err
   199  		}
   200  	}
   201  	if err = or.database.Init(ctx, databaseConfig.SubPrefix(or.database.Name()), or); err != nil {
   202  		return err
   203  	}
   204  
   205  	if or.identity == nil {
   206  		iiType := config.GetString(config.IdentityType)
   207  		if or.identity, err = iifactory.GetPlugin(ctx, iiType); err != nil {
   208  			return err
   209  		}
   210  	}
   211  	if err = or.identity.Init(ctx, identityConfig.SubPrefix(or.identity.Name()), or); err != nil {
   212  		return err
   213  	}
   214  
   215  	if or.blockchain == nil {
   216  		biType := config.GetString(config.BlockchainType)
   217  		if or.blockchain, err = bifactory.GetPlugin(ctx, biType); err != nil {
   218  			return err
   219  		}
   220  	}
   221  	if err = or.blockchain.Init(ctx, blockchainConfig.SubPrefix(or.blockchain.Name()), &or.bc); err != nil {
   222  		return err
   223  	}
   224  
   225  	if or.publicstorage == nil {
   226  		psType := config.GetString(config.PublicStorageType)
   227  		if or.publicstorage, err = psfactory.GetPlugin(ctx, psType); err != nil {
   228  			return err
   229  		}
   230  	}
   231  	if err = or.publicstorage.Init(ctx, publicstorageConfig.SubPrefix(or.publicstorage.Name()), or); err != nil {
   232  		return err
   233  	}
   234  
   235  	if or.dataexchange == nil {
   236  		dxType := config.GetString(config.DataexchangeType)
   237  		if or.dataexchange, err = dxfactory.GetPlugin(ctx, dxType); err != nil {
   238  			return err
   239  		}
   240  	}
   241  	return or.dataexchange.Init(ctx, dataexchangeConfig.SubPrefix(or.dataexchange.Name()), &or.bc)
   242  }
   243  
   244  func (or *orchestrator) initComponents(ctx context.Context) (err error) {
   245  
   246  	if or.data == nil {
   247  		or.data, err = data.NewDataManager(ctx, or.database, or.dataexchange)
   248  		if err != nil {
   249  			return err
   250  		}
   251  	}
   252  
   253  	if or.batch == nil {
   254  		or.batch, err = batch.NewBatchManager(ctx, or.database, or.data)
   255  		if err != nil {
   256  			return err
   257  		}
   258  	}
   259  
   260  	if or.broadcast == nil {
   261  		if or.broadcast, err = broadcast.NewBroadcastManager(ctx, or.database, or.identity, or.data, or.blockchain, or.dataexchange, or.publicstorage, or.batch); err != nil {
   262  			return err
   263  		}
   264  	}
   265  
   266  	if or.messaging == nil {
   267  		if or.messaging, err = privatemessaging.NewPrivateMessaging(ctx, or.database, or.identity, or.dataexchange, or.blockchain, or.batch, or.data); err != nil {
   268  			return err
   269  		}
   270  	}
   271  
   272  	if or.events == nil {
   273  		or.events, err = events.NewEventManager(ctx, or.publicstorage, or.database, or.identity, or.broadcast, or.messaging, or.data)
   274  		if err != nil {
   275  			return err
   276  		}
   277  	}
   278  
   279  	if or.networkmap == nil {
   280  		or.networkmap, err = networkmap.NewNetworkMap(ctx, or.database, or.broadcast, or.dataexchange, or.identity)
   281  		if err != nil {
   282  			return err
   283  		}
   284  	}
   285  
   286  	return nil
   287  }
   288  
   289  func (or *orchestrator) getPrefdefinedNamespaces(ctx context.Context) ([]*fftypes.Namespace, error) {
   290  	defaultNS := config.GetString(config.NamespacesDefault)
   291  	predefined := config.GetObjectArray(config.NamespacesPredefined)
   292  	namespaces := []*fftypes.Namespace{
   293  		{
   294  			Name:        fftypes.SystemNamespace,
   295  			Type:        fftypes.NamespaceTypeSystem,
   296  			Description: i18n.Expand(ctx, i18n.MsgSystemNSDescription),
   297  		},
   298  	}
   299  	foundDefault := false
   300  	for i, nsObject := range predefined {
   301  		name := nsObject.GetString("name")
   302  		err := fftypes.ValidateFFNameField(ctx, name, fmt.Sprintf("namespaces.predefined[%d].name", i))
   303  		if err != nil {
   304  			return nil, err
   305  		}
   306  		foundDefault = foundDefault || name == defaultNS
   307  		description := nsObject.GetString("description")
   308  		dup := false
   309  		for _, existing := range namespaces {
   310  			if existing.Name == name {
   311  				log.L(ctx).Warnf("Duplicate predefined namespace (ignored): %s", name)
   312  				dup = true
   313  			}
   314  		}
   315  		if !dup {
   316  			namespaces = append(namespaces, &fftypes.Namespace{
   317  				Type:        fftypes.NamespaceTypeLocal,
   318  				Name:        name,
   319  				Description: description,
   320  			})
   321  		}
   322  	}
   323  	if !foundDefault {
   324  		return nil, i18n.NewError(ctx, i18n.MsgDefaultNamespaceNotFound, defaultNS)
   325  	}
   326  	return namespaces, nil
   327  }
   328  
   329  func (or *orchestrator) initNamespaces(ctx context.Context) error {
   330  	predefined, err := or.getPrefdefinedNamespaces(ctx)
   331  	if err != nil {
   332  		return err
   333  	}
   334  	for _, newNS := range predefined {
   335  		ns, err := or.database.GetNamespace(ctx, newNS.Name)
   336  		if err != nil {
   337  			return err
   338  		}
   339  		var updated bool
   340  		if ns == nil {
   341  			updated = true
   342  			newNS.ID = fftypes.NewUUID()
   343  			newNS.Created = fftypes.Now()
   344  		} else {
   345  			// Only update if the description has changed, and the one in our DB is locally defined
   346  			updated = ns.Description != newNS.Description && ns.Type == fftypes.NamespaceTypeLocal
   347  		}
   348  		if updated {
   349  			if err := or.database.UpsertNamespace(ctx, newNS, true); err != nil {
   350  				return err
   351  			}
   352  		}
   353  	}
   354  	return nil
   355  }