From 9bae2501bc54e5f8c3f618baae922c4cb80add22 Mon Sep 17 00:00:00 2001 From: Bob Stasyszyn Date: Sun, 27 May 2018 18:05:36 -0400 Subject: [PATCH] [FAB-10453] Discovery, Selection moved to chprovider Discovery and Selection services were moved to the ChannelService. The discovery provider is now just a "local" discovery provider and the selection provider was removed. Change-Id: I13fbdcaba8b9f8ac736f7676f0a06137bcbfe920 Signed-off-by: Bob Stasyszyn --- pkg/client/channel/chclient.go | 14 +- pkg/client/channel/chclient_test.go | 65 +----- pkg/client/channel/invoke/signature_test.go | 14 +- pkg/client/channel/invoke/txnhandler_test.go | 33 +-- .../common/discovery/discoveryfilter_test.go | 12 +- .../discovery/dynamicdiscovery/chservice.go | 66 +++--- .../dynamicdiscovery/chservice_test.go | 30 +-- .../dynamicdiscovery/localprovider.go | 48 ++++ ...provider_test.go => localprovider_test.go} | 23 +- .../dynamicdiscovery/localservice.go | 15 +- .../dynamicdiscovery/localservice_test.go | 17 +- .../common/discovery/dynamicdiscovery/opts.go | 57 +++++ .../discovery/dynamicdiscovery/provider.go | 107 --------- .../discovery/dynamicdiscovery/service.go | 22 +- .../staticdiscovery/localprovider.go | 67 ++++++ .../discovery/staticdiscovery/localservice.go | 16 +- .../discovery/staticdiscovery/provider.go | 85 ------- .../discovery/staticdiscovery/service.go | 38 ++- .../staticdiscovery/staticdiscovery_test.go | 43 +--- pkg/client/common/mocks/mockdiscovery.go | 15 +- pkg/client/common/mocks/mockselection.go | 23 +- .../dynamicselection/ccpolicyprovider.go | 38 +-- .../dynamicselection/ccpolicyprovider_test.go | 113 ++------- .../dynamicselection/dynamicselection.go | 141 ++++-------- .../dynamicselection/dynamicselection_test.go | 217 +++++------------- .../staticselection/staticselection.go | 29 +-- .../staticselection/staticselection_test.go | 32 +-- pkg/client/ledger/ledger.go | 7 +- pkg/client/ledger/ledger_test.go | 21 +- pkg/client/resmgmt/resmgmt.go | 10 +- pkg/client/resmgmt/resmgmt_test.go | 35 --- pkg/common/providers/context/context.go | 2 - pkg/common/providers/fab/context.go | 2 + pkg/common/providers/fab/provider.go | 14 +- .../test/mockcontext/mockcontext.gen.go | 48 ---- .../providers/test/mockfab/mockfab.gen.go | 24 -- pkg/context/context.go | 75 +----- pkg/core/config/testdata/template/config.yaml | 1 + pkg/fab/chconfig/chconfig.go | 5 +- pkg/fab/endpointconfig.go | 11 +- pkg/fab/endpointconfig_test.go | 13 ++ pkg/fab/events/client/client_test.go | 85 +++---- .../events/client/dispatcher/dispatcher.go | 12 +- .../client/dispatcher/dispatcher_test.go | 14 +- pkg/fab/events/client/mocks/mockdiscovery.go | 22 +- pkg/fab/events/deliverclient/deliverclient.go | 30 +-- .../deliverclient/deliverclient_test.go | 8 +- .../deliverclient/dispatcher/dispatcher.go | 4 +- .../dispatcher/dispatcher_test.go | 16 +- pkg/fab/events/endpoint/endpoint_test.go | 28 +-- pkg/fab/events/endpoint/endpointdiscovery.go | 74 +++--- .../eventhubclient/dispatcher/dispatcher.go | 4 +- .../dispatcher/dispatcher_test.go | 20 +- .../events/eventhubclient/eventhubclient.go | 34 +-- .../eventhubclient/eventhubclient_test.go | 9 +- pkg/fab/mocks/mockchannel.go | 33 +-- pkg/fab/mocks/mockchprovider.go | 32 ++- pkg/fab/mocks/mockcontext.go | 37 --- pkg/fab/mocks/mockdiscovery.go | 19 +- pkg/fab/mocks/mockselection.go | 26 +-- pkg/fabsdk/api/factory.go | 2 - pkg/fabsdk/fabsdk.go | 38 +-- pkg/fabsdk/fabsdk_test.go | 2 - pkg/fabsdk/factory/defsvc/svcfactory.go | 16 +- pkg/fabsdk/factory/defsvc/svcfactory_test.go | 29 +-- pkg/fabsdk/provider/chpvdr/cachekey.go | 90 +++++--- pkg/fabsdk/provider/chpvdr/chprovider.go | 199 +++++++++++----- pkg/fabsdk/provider/chpvdr/chprovider_test.go | 27 ++- pkg/fabsdk/test/mocksdkapi/mocksdkapi.gen.go | 26 --- test/fixtures/config/config_test.yaml | 44 +++- .../endpointconfig_override_test.go | 6 + .../orgs/multiple_orgs_minconfig_test.go | 128 +++++++++-- test/integration/orgs/multiple_orgs_test.go | 117 ++++++---- test/integration/revoked/revoked_peer_test.go | 27 ++- test/integration/sdk/ledger_queries_test.go | 13 +- test/integration/sdk/sdk_dyndiscovery_test.go | 77 ++++++- test/integration/sdk/sdk_provider_test.go | 99 ++++++-- 77 files changed, 1334 insertions(+), 1761 deletions(-) create mode 100644 pkg/client/common/discovery/dynamicdiscovery/localprovider.go rename pkg/client/common/discovery/dynamicdiscovery/{provider_test.go => localprovider_test.go} (72%) create mode 100755 pkg/client/common/discovery/dynamicdiscovery/opts.go delete mode 100644 pkg/client/common/discovery/dynamicdiscovery/provider.go create mode 100644 pkg/client/common/discovery/staticdiscovery/localprovider.go delete mode 100644 pkg/client/common/discovery/staticdiscovery/provider.go diff --git a/pkg/client/channel/chclient.go b/pkg/client/channel/chclient.go index 38c8cd71a0..f21437f004 100644 --- a/pkg/client/channel/chclient.go +++ b/pkg/client/channel/chclient.go @@ -223,6 +223,16 @@ func (cc *Client) prepareHandlerContexts(reqCtx reqContext.Context, request Requ return nil, nil, errors.WithMessage(err, "failed to create transactor") } + selection, err := cc.context.ChannelService().Selection() + if err != nil { + return nil, nil, errors.WithMessage(err, "failed to create selection service") + } + + discovery, err := cc.context.ChannelService().Discovery() + if err != nil { + return nil, nil, errors.WithMessage(err, "failed to create discovery service") + } + peerFilter := func(peer fab.Peer) bool { if !cc.greylist.Accept(peer) { return false @@ -234,8 +244,8 @@ func (cc *Client) prepareHandlerContexts(reqCtx reqContext.Context, request Requ } clientContext := &invoke.ClientContext{ - Selection: cc.context.SelectionService(), - Discovery: cc.context.DiscoveryService(), + Selection: selection, + Discovery: discovery, Membership: cc.membership, Transactor: transactor, EventService: cc.eventService, diff --git a/pkg/client/channel/chclient_test.go b/pkg/client/channel/chclient_test.go index 0392f0fbfa..c3dd36d01d 100644 --- a/pkg/client/channel/chclient_test.go +++ b/pkg/client/channel/chclient_test.go @@ -453,32 +453,20 @@ func TestMultiErrorPropogation(t *testing.T) { assert.Equal(t, "Multiple errors occurred: \nTest Error\nTest Error", statusError.Message, "Expected multi error message") } -type serviceInit interface { - Initialize(context context.Channel) error -} - func TestDiscoveryGreylist(t *testing.T) { testPeer1 := fcmocks.NewMockPeer("Peer1", "http://peer1.com") testPeer1.Error = status.New(status.EndorserClientStatus, status.ConnectionFailed.ToInt32(), "test", []interface{}{testPeer1.URL()}) - selectionProvider, err := staticselection.New(fcmocks.NewMockEndpointConfig()) - assert.Nil(t, err, "Got error %s", err) - - selectionService, err := selectionProvider.CreateSelectionService("mychannel") - assert.Nil(t, err, "Got error %s", err) + discoveryService := txnmocks.NewMockDiscoveryService(nil, testPeer1) - discoveryService, err := setupTestDiscovery(nil, []fab.Peer{testPeer1}) + selectionService, err := staticselection.NewService(discoveryService) assert.Nil(t, err, "Got error %s", err) fabCtx := setupCustomTestContext(t, selectionService, discoveryService, nil) ctx := createChannelContext(fabCtx, channelID) - channelCtx, err := ctx() - assert.Nil(t, err, "Got error %s", err) - selectionService.(serviceInit).Initialize(channelCtx) - chClient, err := New(ctx) assert.Nil(t, err, "Got error %s", err) @@ -552,40 +540,15 @@ func setupCustomTestContext(t *testing.T, selectionService fab.SelectionService, mockChService := testChannelSvc.(*fcmocks.MockChannelService) mockChService.SetTransactor(&transactor) - - //Modify for custom mockcore to test scenarios - selectionProvider := ctx.MockProviderContext.SelectionProvider() - selectionProvider.(*fcmocks.MockSelectionProvider).SetCustomSelectionService(selectionService) + mockChService.SetDiscovery(discoveryService) + mockChService.SetSelection(selectionService) channelProvider := ctx.MockProviderContext.ChannelProvider() channelProvider.(*fcmocks.MockChannelProvider).SetCustomChannelService(testChannelSvc) - discoveryProvider := ctx.MockProviderContext.DiscoveryProvider() - discoveryProvider.(*fcmocks.MockStaticDiscoveryProvider).SetCustomDiscoveryService(discoveryService) - return createClientContext(ctx) } -func setupTestDiscovery(discErr error, peers []fab.Peer) (fab.DiscoveryService, error) { - - mockDiscovery, err := txnmocks.NewMockDiscoveryProvider(discErr, peers) - if err != nil { - return nil, errors.WithMessage(err, "NewMockDiscoveryProvider failed") - } - - return mockDiscovery.CreateDiscoveryService("mychannel") -} - -func setupTestSelection(discErr error, peers []fab.Peer) (*txnmocks.MockSelectionService, error) { - - mockSelection, err := txnmocks.NewMockSelectionProvider(discErr, peers) - if err != nil { - return nil, errors.WithMessage(err, "NewMockSelectinProvider failed") - } - - return mockSelection.CreateSelectionService("mychannel") -} - func setupChannelClient(peers []fab.Peer, t *testing.T) *Client { return setupChannelClientWithError(nil, nil, peers, t) @@ -593,17 +556,7 @@ func setupChannelClient(peers []fab.Peer, t *testing.T) *Client { func setupChannelClientWithError(discErr error, selectionErr error, peers []fab.Peer, t *testing.T) *Client { - discoveryService, err := setupTestDiscovery(discErr, nil) - if err != nil { - t.Fatalf("Failed to setup discovery service: %s", err) - } - - selectionService, err := setupTestSelection(selectionErr, peers) - if err != nil { - t.Fatalf("Failed to setup discovery service: %s", err) - } - - fabCtx := setupCustomTestContext(t, selectionService, discoveryService, nil) + fabCtx := setupCustomTestContext(t, txnmocks.NewMockSelectionService(selectionErr, peers...), txnmocks.NewMockDiscoveryService(discErr), nil) ctx := createChannelContext(fabCtx, channelID) @@ -618,13 +571,7 @@ func setupChannelClientWithError(discErr error, selectionErr error, peers []fab. func setupChannelClientWithNodes(peers []fab.Peer, orderers []fab.Orderer, t *testing.T) *Client { - discoveryService, err := setupTestDiscovery(nil, nil) - assert.Nil(t, err, "Failed to setup discovery service") - - selectionService, err := setupTestSelection(nil, peers) - assert.Nil(t, err, "Failed to setup discovery service") - - fabCtx := setupCustomTestContext(t, selectionService, discoveryService, orderers) + fabCtx := setupCustomTestContext(t, txnmocks.NewMockSelectionService(nil, peers...), txnmocks.NewMockDiscoveryService(nil), orderers) ctx := createChannelContext(fabCtx, channelID) diff --git a/pkg/client/channel/invoke/signature_test.go b/pkg/client/channel/invoke/signature_test.go index 13f0cb3eb1..56c72239c3 100644 --- a/pkg/client/channel/invoke/signature_test.go +++ b/pkg/client/channel/invoke/signature_test.go @@ -74,16 +74,6 @@ func setupContextForSignatureValidation(verifyErr, validateErr error, peers []fa membership.ValidateErr = validateErr membership.VerifyErr = verifyErr - discoveryService, err := setupTestDiscovery(nil, nil) - if err != nil { - t.Fatalf("Failed to setup discovery service: %s", err) - } - - selectionService, err := setupTestSelection(nil, peers) - if err != nil { - t.Fatalf("Failed to setup discovery service: %s", err) - } - transactor := txnmocks.MockTransactor{ Ctx: ctx, ChannelID: "", @@ -91,8 +81,8 @@ func setupContextForSignatureValidation(verifyErr, validateErr error, peers []fa return &ClientContext{ Membership: membership, - Discovery: discoveryService, - Selection: selectionService, + Discovery: fcmocks.NewMockDiscoveryService(nil), + Selection: fcmocks.NewMockSelectionService(nil, peers...), Transactor: &transactor, } diff --git a/pkg/client/channel/invoke/txnhandler_test.go b/pkg/client/channel/invoke/txnhandler_test.go index a549925e68..a6d5763426 100644 --- a/pkg/client/channel/invoke/txnhandler_test.go +++ b/pkg/client/channel/invoke/txnhandler_test.go @@ -290,16 +290,6 @@ func prepareRequestContext(request Request, opts Opts, t *testing.T) *RequestCon func setupChannelClientContext(discErr error, selectionErr error, peers []fab.Peer, t *testing.T) *ClientContext { membership := fcmocks.NewMockMembership() - discoveryService, err := setupTestDiscovery(discErr, nil) - if err != nil { - t.Fatalf("Failed to setup discovery service: %s", err) - } - - selectionService, err := setupTestSelection(selectionErr, peers) - if err != nil { - t.Fatalf("Failed to setup discovery service: %s", err) - } - ctx := setupTestContext() orderer := fcmocks.NewMockOrderer("", nil) transactor := txnmocks.MockTransactor{ @@ -310,8 +300,8 @@ func setupChannelClientContext(discErr error, selectionErr error, peers []fab.Pe return &ClientContext{ Membership: membership, - Discovery: discoveryService, - Selection: selectionService, + Discovery: txnmocks.NewMockDiscoveryService(discErr), + Selection: txnmocks.NewMockSelectionService(selectionErr, peers...), Transactor: &transactor, } @@ -322,22 +312,3 @@ func setupTestContext() context.Client { ctx := fcmocks.NewMockContext(user) return ctx } - -func setupTestDiscovery(discErr error, peers []fab.Peer) (fab.DiscoveryService, error) { - - mockDiscovery, err := txnmocks.NewMockDiscoveryProvider(discErr, peers) - if err != nil { - return nil, errors.WithMessage(err, "NewMockDiscoveryProvider failed") - } - return mockDiscovery.CreateDiscoveryService("mychannel") -} - -func setupTestSelection(discErr error, peers []fab.Peer) (*txnmocks.MockSelectionService, error) { - - mockSelection, err := txnmocks.NewMockSelectionProvider(discErr, peers) - if err != nil { - return nil, errors.WithMessage(err, "NewMockSelectinProvider failed") - } - - return mockSelection.CreateSelectionService("mychannel") -} diff --git a/pkg/client/common/discovery/discoveryfilter_test.go b/pkg/client/common/discovery/discoveryfilter_test.go index a3debf2121..2f1f4e3e56 100644 --- a/pkg/client/common/discovery/discoveryfilter_test.go +++ b/pkg/client/common/discovery/discoveryfilter_test.go @@ -39,22 +39,16 @@ func TestDiscoveryFilter(t *testing.T) { t.Fatalf(err.Error()) } - discoveryProvider, err := staticdiscovery.New(config1) - if err != nil { - t.Fatalf("Failed to setup discovery provider: %s", err) - } - discoveryProvider.Initialize(mocks.NewMockContext(mockmsp.NewMockSigningIdentity("user1", "Org1MSP"))) - - discoveryService, err := discoveryProvider.CreateDiscoveryService("mychannel") + discoveryService, err := staticdiscovery.NewService(config1, mocks.NewMockContext(mockmsp.NewMockSigningIdentity("user1", "Org1MSP")).InfraProvider(), "mychannel") if err != nil { t.Fatalf("Failed to setup discovery service: %s", err) } discoveryFilter := &mockFilter{called: false} - discoveryService = NewDiscoveryFilterService(discoveryService, discoveryFilter) + filteredService := NewDiscoveryFilterService(discoveryService, discoveryFilter) - peers, err := discoveryService.GetPeers() + peers, err := filteredService.GetPeers() if err != nil { t.Fatalf("Failed to get peers from discovery service: %s", err) } diff --git a/pkg/client/common/discovery/dynamicdiscovery/chservice.go b/pkg/client/common/discovery/dynamicdiscovery/chservice.go index 542fe1b122..12743b104a 100644 --- a/pkg/client/common/discovery/dynamicdiscovery/chservice.go +++ b/pkg/client/common/discovery/dynamicdiscovery/chservice.go @@ -8,6 +8,7 @@ package dynamicdiscovery import ( discclient "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/discovery/client" + coptions "github.com/hyperledger/fabric-sdk-go/pkg/common/options" contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" reqContext "github.com/hyperledger/fabric-sdk-go/pkg/context" @@ -15,51 +16,51 @@ import ( "github.com/pkg/errors" ) -// channelService implements a dynamic Discovery Service that queries +// ChannelService implements a dynamic Discovery Service that queries // Fabric's Discovery service for information about the peers that // are currently joined to the given channel. -type channelService struct { +type ChannelService struct { *service + channelID string } -// newChannelService creates a Discovery Service to query the list of member peers on a given channel. -func newChannelService(options options) *channelService { - logger.Debugf("Creating new dynamic discovery service with cache refresh interval %s", options.refreshInterval) - - s := &channelService{} - s.service = newService(s.queryPeers, options) - return s -} - -// Initialize initializes the service with channel context -func (s *channelService) Initialize(ctx contextAPI.Channel) error { - return s.service.Initialize(ctx) +// NewChannelService creates a Discovery Service to query the list of member peers on a given channel. +func NewChannelService(ctx contextAPI.Client, channelID string, opts ...coptions.Opt) (*ChannelService, error) { + logger.Debugf("Creating new dynamic discovery service") + s := &ChannelService{ + channelID: channelID, + } + s.service = newService(ctx.EndpointConfig(), s.queryPeers, opts...) + err := s.service.initialize(ctx) + if err != nil { + return nil, err + } + return s, nil } -func (s *channelService) channelContext() contextAPI.Channel { - return s.context().(contextAPI.Channel) +// Close releases resources +func (s *ChannelService) Close() { + logger.Debugf("Closing discovery service for channel [%s]", s.channelID) + s.service.Close() } -func (s *channelService) queryPeers() ([]fab.Peer, error) { - logger.Debugf("Refreshing peers of channel [%s] from discovery service...", s.channelContext().ChannelID()) +func (s *ChannelService) queryPeers() ([]fab.Peer, error) { + logger.Debugf("Refreshing peers of channel [%s] from discovery service...", s.channelID) - channelContext := s.channelContext() - if channelContext == nil { - return nil, errors.Errorf("the service has not been initialized") - } + ctx := s.context() - targets, err := s.getTargets(channelContext) + targets, err := s.getTargets(ctx) if err != nil { return nil, err } if len(targets) == 0 { - return nil, errors.Errorf("no peers configured for channel [%s]", channelContext.ChannelID()) + return nil, errors.Errorf("no peers configured for channel [%s]", s.channelID) } - reqCtx, cancel := reqContext.NewRequest(channelContext, reqContext.WithTimeout(s.responseTimeout)) + reqCtx, cancel := reqContext.NewRequest(ctx, reqContext.WithTimeout(s.responseTimeout)) defer cancel() - req := discclient.NewRequest().OfChannel(channelContext.ChannelID()).AddPeersQuery() + req := discclient.NewRequest().OfChannel(s.channelID).AddPeersQuery() responses, err := s.discoveryClient().Send(reqCtx, req, targets...) if err != nil { if len(responses) == 0 { @@ -67,15 +68,15 @@ func (s *channelService) queryPeers() ([]fab.Peer, error) { } logger.Warnf("Received %d response(s) and one or more errors from discovery client: %s", len(responses), err) } - return s.evaluate(channelContext, responses) + return s.evaluate(ctx, responses) } -func (s *channelService) getTargets(ctx contextAPI.Channel) ([]fab.PeerConfig, error) { +func (s *ChannelService) getTargets(ctx contextAPI.Client) ([]fab.PeerConfig, error) { // TODO: The number of peers to query should be retrieved from the channel policy. // This will done in a future patch. - chpeers, ok := ctx.EndpointConfig().ChannelPeers(ctx.ChannelID()) + chpeers, ok := ctx.EndpointConfig().ChannelPeers(s.channelID) if !ok { - return nil, errors.Errorf("failed to get peer configs for channel [%s]", ctx.ChannelID()) + return nil, errors.Errorf("failed to get peer configs for channel [%s]", s.channelID) } targets := make([]fab.PeerConfig, len(chpeers)) for i := 0; i < len(targets); i++ { @@ -85,19 +86,18 @@ func (s *channelService) getTargets(ctx contextAPI.Channel) ([]fab.PeerConfig, e } // evaluate validates the responses and returns the peers -func (s *channelService) evaluate(ctx contextAPI.Channel, responses []fabdiscovery.Response) ([]fab.Peer, error) { +func (s *ChannelService) evaluate(ctx contextAPI.Client, responses []fabdiscovery.Response) ([]fab.Peer, error) { if len(responses) == 0 { return nil, errors.New("no successful response received from any peer") } // TODO: In a future patch: // - validate the signatures in the responses - // - ensure N responses match according to the policy // For now just pick the first successful response var lastErr error for _, response := range responses { - endpoints, err := response.ForChannel(ctx.ChannelID()).Peers() + endpoints, err := response.ForChannel(s.channelID).Peers() if err != nil { lastErr = errors.Wrapf(err, "error getting peers from discovery response") logger.Warn(lastErr.Error()) diff --git a/pkg/client/common/discovery/dynamicdiscovery/chservice_test.go b/pkg/client/common/discovery/dynamicdiscovery/chservice_test.go index 7354498f7d..7058ed16b8 100644 --- a/pkg/client/common/discovery/dynamicdiscovery/chservice_test.go +++ b/pkg/client/common/discovery/dynamicdiscovery/chservice_test.go @@ -17,6 +17,7 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" mspmocks "github.com/hyperledger/fabric-sdk-go/pkg/msp/test/mockmsp" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -59,22 +60,15 @@ func TestDiscoveryService(t *testing.T) { return discClient, nil } - membershipService := newChannelService( - options{ - refreshInterval: 500 * time.Millisecond, - responseTimeout: 2 * time.Second, - }, + service, err := NewChannelService( + ctx, ch, + WithRefreshInterval(500*time.Millisecond), + WithResponseTimeout(2*time.Second), ) - defer membershipService.Close() - - chCtx := mocks.NewMockChannelContext(ctx, ch) - err := membershipService.Initialize(chCtx) - assert.NoError(t, err) - // Initialize again should produce no error - err = membershipService.Initialize(chCtx) - assert.NoError(t, err) + require.NoError(t, err) + defer service.Close() - peers, err := membershipService.GetPeers() + peers, err := service.GetPeers() assert.NoError(t, err) assert.Equal(t, 0, len(peers)) @@ -92,9 +86,9 @@ func TestDiscoveryService(t *testing.T) { time.Sleep(1 * time.Second) - peers, err = membershipService.GetPeers() + peers, err = service.GetPeers() assert.NoError(t, err) - assert.Equal(t, 1, len(peers)) + assert.Equalf(t, 1, len(peers), "Expected 1 peer") discClient.SetResponses( &dyndiscmocks.MockDiscoverEndpointResponse{ @@ -115,7 +109,7 @@ func TestDiscoveryService(t *testing.T) { time.Sleep(1 * time.Second) - peers, err = membershipService.GetPeers() + peers, err = service.GetPeers() assert.NoError(t, err) - assert.Equal(t, 2, len(peers)) + assert.Equalf(t, 2, len(peers), "Expected 2 peers") } diff --git a/pkg/client/common/discovery/dynamicdiscovery/localprovider.go b/pkg/client/common/discovery/dynamicdiscovery/localprovider.go new file mode 100644 index 0000000000..416ae8cbf0 --- /dev/null +++ b/pkg/client/common/discovery/dynamicdiscovery/localprovider.go @@ -0,0 +1,48 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package dynamicdiscovery + +import ( + "github.com/hyperledger/fabric-sdk-go/pkg/common/logging" + coptions "github.com/hyperledger/fabric-sdk-go/pkg/common/options" + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" + "github.com/hyperledger/fabric-sdk-go/pkg/util/concurrent/lazycache" + "github.com/pkg/errors" +) + +var logger = logging.NewLogger("fabsdk/client") + +// LocalProvider implements a local Dynamic Discovery LocalProvider that queries +// Fabric's Discovery service for information about the peers that +// are in the local MSP. +type LocalProvider struct { + cache *lazycache.Cache +} + +// NewLocalProvider creates a new local dynamic discovery provider +func NewLocalProvider(config fab.EndpointConfig, opts ...coptions.Opt) *LocalProvider { + return &LocalProvider{ + cache: lazycache.New("Local_Discovery_Service_Cache", func(key lazycache.Key) (interface{}, error) { + return newLocalService(config, key.String(), opts...), nil + }), + } +} + +// CreateLocalDiscoveryService returns a local discovery service +func (p *LocalProvider) CreateLocalDiscoveryService(mspID string) (fab.DiscoveryService, error) { + ref, err := p.cache.Get(lazycache.NewStringKey(mspID)) + if err != nil { + return nil, errors.WithMessage(err, "failed to get local discovery service from cache") + } + return ref.(fab.DiscoveryService), nil +} + +// Close will close the cache and all services contained by the cache. +func (p *LocalProvider) Close() { + logger.Debugf("Closing local provider cache") + p.cache.Close() +} diff --git a/pkg/client/common/discovery/dynamicdiscovery/provider_test.go b/pkg/client/common/discovery/dynamicdiscovery/localprovider_test.go similarity index 72% rename from pkg/client/common/discovery/dynamicdiscovery/provider_test.go rename to pkg/client/common/discovery/dynamicdiscovery/localprovider_test.go index cb930df56c..54c63e4990 100644 --- a/pkg/client/common/discovery/dynamicdiscovery/provider_test.go +++ b/pkg/client/common/discovery/dynamicdiscovery/localprovider_test.go @@ -17,8 +17,7 @@ import ( ) const ( - ch = "orgchannel" - ch2 = "channel2" + ch = "orgchannel" mspID1 = "Org1MSP" mspID2 = "Org2MSP" @@ -26,7 +25,7 @@ const ( peer1MSP1 = "peer1.org1.com:9999" ) -func TestDiscoveryProvider(t *testing.T) { +func TestLocalProvider(t *testing.T) { ctx := mocks.NewMockContext(mspmocks.NewMockSigningIdentity("test", mspID1)) config := &config{ EndpointConfig: mocks.NewMockEndpointConfig(), @@ -42,25 +41,9 @@ func TestDiscoveryProvider(t *testing.T) { } ctx.SetEndpointConfig(config) - p := New(config, WithRefreshInterval(30*time.Second), WithResponseTimeout(10*time.Second)) + p := NewLocalProvider(config, WithRefreshInterval(30*time.Second), WithResponseTimeout(10*time.Second)) defer p.Close() - service1, err := p.CreateDiscoveryService(ch) - assert.NoError(t, err) - - chCtx := mocks.NewMockChannelContext(ctx, ch) - - err = service1.(*channelService).Initialize(chCtx) - assert.NoError(t, err) - - service2, err := p.CreateDiscoveryService(ch) - assert.NoError(t, err) - assert.Equal(t, service1, service2) - - service2, err = p.CreateDiscoveryService(ch2) - assert.NoError(t, err) - assert.NotEqual(t, service1, service2) - localService1, err := p.CreateLocalDiscoveryService(mspID1) assert.NoError(t, err) diff --git a/pkg/client/common/discovery/dynamicdiscovery/localservice.go b/pkg/client/common/discovery/dynamicdiscovery/localservice.go index 0a6335fda0..3911e2130e 100644 --- a/pkg/client/common/discovery/dynamicdiscovery/localservice.go +++ b/pkg/client/common/discovery/dynamicdiscovery/localservice.go @@ -8,6 +8,7 @@ package dynamicdiscovery import ( discclient "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/discovery/client" + coptions "github.com/hyperledger/fabric-sdk-go/pkg/common/options" contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" reqContext "github.com/hyperledger/fabric-sdk-go/pkg/context" @@ -22,11 +23,11 @@ type LocalService struct { } // newLocalService creates a Local Discovery Service to query the list of member peers in the local MSP. -func newLocalService(mspID string, options options) *LocalService { - logger.Debugf("Creating new dynamic discovery service with cache refresh interval %s", options.refreshInterval) +func newLocalService(config fab.EndpointConfig, mspID string, opts ...coptions.Opt) *LocalService { + logger.Debugf("Creating new local discovery service") s := &LocalService{mspID: mspID} - s.service = newService(s.queryPeers, options) + s.service = newService(config, s.queryPeers, opts...) return s } @@ -35,7 +36,13 @@ func (s *LocalService) Initialize(ctx contextAPI.Local) error { if ctx.Identifier().MSPID != s.mspID { return errors.Errorf("expecting context for MSP [%s] but got [%s]", s.mspID, ctx.Identifier().MSPID) } - return s.service.Initialize(ctx) + return s.service.initialize(ctx) +} + +// Close releases resources +func (s *LocalService) Close() { + logger.Debugf("Closing local discovery service for MSP [%s]", s.mspID) + s.service.Close() } func (s *LocalService) localContext() contextAPI.Local { diff --git a/pkg/client/common/discovery/dynamicdiscovery/localservice_test.go b/pkg/client/common/discovery/dynamicdiscovery/localservice_test.go index cc1d52fccc..9157c927c1 100644 --- a/pkg/client/common/discovery/dynamicdiscovery/localservice_test.go +++ b/pkg/client/common/discovery/dynamicdiscovery/localservice_test.go @@ -50,19 +50,14 @@ func TestLocalDiscoveryService(t *testing.T) { } // Test initialize with invalid MSP ID - service := newLocalService( - mspID2, - options{}, - ) + service := newLocalService(config, mspID2) err := service.Initialize(localCtx) assert.Error(t, err) service = newLocalService( - mspID1, - options{ - refreshInterval: 500 * time.Millisecond, - responseTimeout: 2 * time.Second, - }, + config, mspID1, + WithRefreshInterval(500*time.Millisecond), + WithResponseTimeout(2*time.Second), ) defer service.Close() @@ -92,7 +87,7 @@ func TestLocalDiscoveryService(t *testing.T) { peers, err = service.GetPeers() assert.NoError(t, err) - assert.Equal(t, 1, len(peers)) + assert.Equal(t, 1, len(peers), "Expecting 1 peer") discClient.SetResponses( &dyndiscmocks.MockDiscoverEndpointResponse{ @@ -120,7 +115,7 @@ func TestLocalDiscoveryService(t *testing.T) { peers, err = service.GetPeers() assert.NoError(t, err) - assert.Equal(t, 2, len(peers)) + assert.Equal(t, 2, len(peers), "Expecting 2 peers") for _, p := range peers { assert.Equalf(t, mspID1, p.MSPID(), "Expecting peer to be in MSP [%s]", mspID1) diff --git a/pkg/client/common/discovery/dynamicdiscovery/opts.go b/pkg/client/common/discovery/dynamicdiscovery/opts.go new file mode 100755 index 0000000000..5957c254af --- /dev/null +++ b/pkg/client/common/discovery/dynamicdiscovery/opts.go @@ -0,0 +1,57 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package dynamicdiscovery + +import ( + "time" + + coptions "github.com/hyperledger/fabric-sdk-go/pkg/common/options" +) + +type options struct { + refreshInterval time.Duration + responseTimeout time.Duration +} + +// WithRefreshInterval sets the interval in which the +// peer cache is refreshed +func WithRefreshInterval(value time.Duration) coptions.Opt { + return func(p coptions.Params) { + logger.Debugf("Checking refreshIntervalSetter") + if setter, ok := p.(refreshIntervalSetter); ok { + setter.SetRefreshInterval(value) + } + } +} + +// WithResponseTimeout sets the Discover service response timeout +func WithResponseTimeout(value time.Duration) coptions.Opt { + return func(p coptions.Params) { + logger.Debugf("Checking responseTimeoutSetter") + if setter, ok := p.(responseTimeoutSetter); ok { + setter.SetResponseTimeout(value) + } + } +} + +type refreshIntervalSetter interface { + SetRefreshInterval(value time.Duration) +} + +type responseTimeoutSetter interface { + SetResponseTimeout(value time.Duration) +} + +func (o *options) SetRefreshInterval(value time.Duration) { + logger.Debugf("RefreshInterval: %s", value) + o.refreshInterval = value +} + +func (o *options) SetResponseTimeout(value time.Duration) { + logger.Debugf("ResponseTimeout: %s", value) + o.responseTimeout = value +} diff --git a/pkg/client/common/discovery/dynamicdiscovery/provider.go b/pkg/client/common/discovery/dynamicdiscovery/provider.go deleted file mode 100644 index 4e6373818e..0000000000 --- a/pkg/client/common/discovery/dynamicdiscovery/provider.go +++ /dev/null @@ -1,107 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package dynamicdiscovery - -import ( - "time" - - "github.com/hyperledger/fabric-sdk-go/pkg/common/logging" - "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" - "github.com/hyperledger/fabric-sdk-go/pkg/util/concurrent/lazycache" - "github.com/pkg/errors" -) - -var logger = logging.NewLogger("fabsdk/client") - -// Provider implements a dynamic Discovery Provider that queries -// Fabric's Discovery service for information about the peers that -// are currently joined to the given channel. -type Provider struct { - cache *lazycache.Cache -} - -// Opt is a provider option -type Opt func(o *options) - -// WithRefreshInterval sets the interval in which the -// peer cache is refreshed -func WithRefreshInterval(value time.Duration) Opt { - return func(o *options) { - o.refreshInterval = value - } -} - -// WithResponseTimeout sets the Discover service response timeout -func WithResponseTimeout(value time.Duration) Opt { - return func(o *options) { - o.responseTimeout = value - } -} - -type options struct { - refreshInterval time.Duration - responseTimeout time.Duration -} - -// New creates a new dynamic discovery provider -func New(config fab.EndpointConfig, opts ...Opt) *Provider { - options := options{} - for _, opt := range opts { - opt(&options) - } - - if options.refreshInterval == 0 { - options.refreshInterval = config.Timeout(fab.DiscoveryServiceRefresh) - } - if options.responseTimeout == 0 { - options.responseTimeout = config.Timeout(fab.DiscoveryResponse) - } - - return &Provider{ - cache: lazycache.New("Dynamic_Discovery_Service_Cache", func(key lazycache.Key) (interface{}, error) { - if mk, ok := key.(*mspKey); ok { - return newLocalService(mk.mspID, options), nil - } - return newChannelService(options), nil - }), - } -} - -// CreateDiscoveryService returns a discovery service for the given channel -func (p *Provider) CreateDiscoveryService(channelID string) (fab.DiscoveryService, error) { - ref, err := p.cache.Get(lazycache.NewStringKey(channelID)) - if err != nil { - return nil, errors.WithMessage(err, "failed to get discovery service from cache") - } - return ref.(fab.DiscoveryService), nil -} - -// CreateLocalDiscoveryService returns a local discovery service -func (p *Provider) CreateLocalDiscoveryService(mspID string) (fab.DiscoveryService, error) { - ref, err := p.cache.Get(newMSPKey(mspID)) - if err != nil { - return nil, errors.WithMessage(err, "failed to get local discovery service from cache") - } - return ref.(fab.DiscoveryService), nil -} - -// Close will close the cache and all services contained by the cache. -func (p *Provider) Close() { - p.cache.Close() -} - -type mspKey struct { - mspID string -} - -func newMSPKey(mspID string) *mspKey { - return &mspKey{mspID: mspID} -} - -func (k *mspKey) String() string { - return k.mspID -} diff --git a/pkg/client/common/discovery/dynamicdiscovery/service.go b/pkg/client/common/discovery/dynamicdiscovery/service.go index 468d55903e..3ea0229a16 100644 --- a/pkg/client/common/discovery/dynamicdiscovery/service.go +++ b/pkg/client/common/discovery/dynamicdiscovery/service.go @@ -12,6 +12,7 @@ import ( "time" discclient "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/discovery/client" + coptions "github.com/hyperledger/fabric-sdk-go/pkg/common/options" contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" fabdiscovery "github.com/hyperledger/fabric-sdk-go/pkg/fab/discovery" @@ -41,8 +42,21 @@ type service struct { type queryPeers func() ([]fab.Peer, error) -func newService(query queryPeers, options options) *service { - logger.Debugf("Creating new dynamic discovery service with cache refresh interval %s", options.refreshInterval) +func newService(config fab.EndpointConfig, query queryPeers, opts ...coptions.Opt) *service { + options := options{} + coptions.Apply(&options, opts) + + if options.refreshInterval == 0 { + options.refreshInterval = config.Timeout(fab.DiscoveryServiceRefresh) + } + + if options.responseTimeout == 0 { + options.responseTimeout = config.Timeout(fab.DiscoveryResponse) + } + + logger.Debugf("Cache refresh interval: %s", options.refreshInterval) + logger.Debugf("Deliver service response timeout: %s", options.responseTimeout) + return &service{ responseTimeout: options.responseTimeout, peersRef: lazyref.New( @@ -54,8 +68,8 @@ func newService(query queryPeers, options options) *service { } } -// Initialize initializes the service with local context -func (s *service) Initialize(ctx contextAPI.Client) error { +// initialize initializes the service with client context +func (s *service) initialize(ctx contextAPI.Client) error { s.lock.Lock() defer s.lock.Unlock() diff --git a/pkg/client/common/discovery/staticdiscovery/localprovider.go b/pkg/client/common/discovery/staticdiscovery/localprovider.go new file mode 100644 index 0000000000..c487c1d1fe --- /dev/null +++ b/pkg/client/common/discovery/staticdiscovery/localprovider.go @@ -0,0 +1,67 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package staticdiscovery + +import ( + "github.com/hyperledger/fabric-sdk-go/pkg/common/logging" + contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" + + "github.com/pkg/errors" +) + +var logger = logging.NewLogger("fabsdk/client") + +type peerCreator interface { + CreatePeerFromConfig(peerCfg *fab.NetworkPeer) (fab.Peer, error) +} + +/** + * Discovery Provider is used to discover peers on the network + */ + +// LocalProvider implements discovery provider +type LocalProvider struct { + config fab.EndpointConfig + fabPvdr peerCreator +} + +// NewLocalProvider returns discovery provider +func NewLocalProvider(config fab.EndpointConfig) (*LocalProvider, error) { + return &LocalProvider{config: config}, nil +} + +// Initialize initializes the DiscoveryProvider +func (dp *LocalProvider) Initialize(fabPvdr contextAPI.Providers) error { + dp.fabPvdr = fabPvdr.InfraProvider() + return nil +} + +// CreateLocalDiscoveryService return a local discovery service +func (dp *LocalProvider) CreateLocalDiscoveryService(mspID string) (fab.DiscoveryService, error) { + peers := []fab.Peer{} + + netPeers, ok := dp.config.NetworkPeers() + if !ok { + return nil, errors.New("unable to read configuration for network peers") + } + + logger.Debugf("Found %d peers", len(netPeers)) + + for _, p := range netPeers { + newPeer, err := dp.fabPvdr.CreatePeerFromConfig(&p) + if err != nil { + return nil, errors.WithMessage(err, "NewPeerFromConfig failed") + } + if newPeer.MSPID() == mspID { + logger.Debugf("Adding local peer [%s] for MSP [%s]", newPeer.URL(), mspID) + peers = append(peers, newPeer) + } + } + + return &localDiscoveryService{config: dp.config, peers: peers}, nil +} diff --git a/pkg/client/common/discovery/staticdiscovery/localservice.go b/pkg/client/common/discovery/staticdiscovery/localservice.go index 23acf9df4a..2d836e1e40 100644 --- a/pkg/client/common/discovery/staticdiscovery/localservice.go +++ b/pkg/client/common/discovery/staticdiscovery/localservice.go @@ -7,29 +7,15 @@ SPDX-License-Identifier: Apache-2.0 package staticdiscovery import ( - contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" ) type localDiscoveryService struct { config fab.EndpointConfig peers []fab.Peer - mspID string -} - -// Initialize initializes the service with local context -func (ds *localDiscoveryService) Initialize(ctx contextAPI.Local) error { - ds.mspID = ctx.Identifier().MSPID - return nil } // GetPeers is used to get local peers func (ds *localDiscoveryService) GetPeers() ([]fab.Peer, error) { - var peers []fab.Peer - for _, p := range ds.peers { - if p.MSPID() == ds.mspID { - peers = append(peers, p) - } - } - return peers, nil + return ds.peers, nil } diff --git a/pkg/client/common/discovery/staticdiscovery/provider.go b/pkg/client/common/discovery/staticdiscovery/provider.go deleted file mode 100644 index c311b5aad8..0000000000 --- a/pkg/client/common/discovery/staticdiscovery/provider.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package staticdiscovery - -import ( - contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" - "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" - - "github.com/pkg/errors" -) - -type peerCreator interface { - CreatePeerFromConfig(peerCfg *fab.NetworkPeer) (fab.Peer, error) -} - -/** - * Discovery Provider is used to discover peers on the network - */ - -// DiscoveryProvider implements discovery provider -type DiscoveryProvider struct { - config fab.EndpointConfig - fabPvdr peerCreator -} - -// New returns discovery provider -func New(config fab.EndpointConfig) (*DiscoveryProvider, error) { - return &DiscoveryProvider{config: config}, nil -} - -// Initialize initializes the DiscoveryProvider -func (dp *DiscoveryProvider) Initialize(fabPvdr contextAPI.Providers) error { - dp.fabPvdr = fabPvdr.InfraProvider() - return nil -} - -// CreateDiscoveryService return discovery service for specific channel -func (dp *DiscoveryProvider) CreateDiscoveryService(channelID string) (fab.DiscoveryService, error) { - if channelID == "" { - return nil, errors.New("channel ID must be provided") - } - - // Use configured channel peers - chPeers, ok := dp.config.ChannelPeers(channelID) - if !ok { - return nil, errors.New("unable to read configuration for channel peers") - } - - peers := []fab.Peer{} - for _, p := range chPeers { - newPeer, err := dp.fabPvdr.CreatePeerFromConfig(&p.NetworkPeer) - if err != nil || newPeer == nil { - return nil, errors.WithMessage(err, "NewPeer failed") - } - - peers = append(peers, newPeer) - } - - return &discoveryService{config: dp.config, peers: peers}, nil -} - -// CreateLocalDiscoveryService return a local discovery service -func (dp *DiscoveryProvider) CreateLocalDiscoveryService(mspID string) (fab.DiscoveryService, error) { - peers := []fab.Peer{} - - netPeers, ok := dp.config.NetworkPeers() - if !ok { - return nil, errors.New("unable to read configuration for network peers") - } - - for _, p := range netPeers { - newPeer, err := dp.fabPvdr.CreatePeerFromConfig(&p) - if err != nil { - return nil, errors.WithMessage(err, "NewPeerFromConfig failed") - } - - peers = append(peers, newPeer) - } - - return &localDiscoveryService{config: dp.config, peers: peers}, nil -} diff --git a/pkg/client/common/discovery/staticdiscovery/service.go b/pkg/client/common/discovery/staticdiscovery/service.go index fa680291f9..2f69882473 100644 --- a/pkg/client/common/discovery/staticdiscovery/service.go +++ b/pkg/client/common/discovery/staticdiscovery/service.go @@ -8,16 +8,42 @@ package staticdiscovery import ( "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" + "github.com/pkg/errors" ) -// discoveryService implements discovery service -type discoveryService struct { - config fab.EndpointConfig - peers []fab.Peer +// DiscoveryService implements a static discovery service +type DiscoveryService struct { + peers []fab.Peer } -// GetPeers is used to get peers -func (ds *discoveryService) GetPeers() ([]fab.Peer, error) { +// NewService creates a static discovery service +func NewService(config fab.EndpointConfig, peerCreator peerCreator, channelID string) (*DiscoveryService, error) { + if channelID == "" { + return nil, errors.New("channel ID must be provided") + } + + // Use configured channel peers + chPeers, ok := config.ChannelPeers(channelID) + if !ok { + return nil, errors.New("unable to read configuration for channel peers") + } + + peers := []fab.Peer{} + for _, p := range chPeers { + newPeer, err := peerCreator.CreatePeerFromConfig(&p.NetworkPeer) + if err != nil || newPeer == nil { + return nil, errors.WithMessage(err, "NewPeer failed") + } + + peers = append(peers, newPeer) + } + return &DiscoveryService{ + peers: peers, + }, nil +} + +// GetPeers is used to get peers +func (ds *DiscoveryService) GetPeers() ([]fab.Peer, error) { return ds.peers, nil } diff --git a/pkg/client/common/discovery/staticdiscovery/staticdiscovery_test.go b/pkg/client/common/discovery/staticdiscovery/staticdiscovery_test.go index 73a660cf80..7260b06da3 100644 --- a/pkg/client/common/discovery/staticdiscovery/staticdiscovery_test.go +++ b/pkg/client/common/discovery/staticdiscovery/staticdiscovery_test.go @@ -18,23 +18,8 @@ import ( func TestStaticDiscovery(t *testing.T) { - configBackend, err := config.FromFile("../../../../../test/fixtures/config/config_test.yaml")() - if err != nil { - t.Fatalf(err.Error()) - } - - config1, err := fabImpl.ConfigFromBackend(configBackend...) - if err != nil { - t.Fatalf(err.Error()) - } - - discoveryProvider, err := New(config1) - if err != nil { - t.Fatalf("Failed to setup discovery provider: %s", err) - } - discoveryProvider.Initialize(mocks.NewMockContext(mockmsp.NewMockSigningIdentity("user1", "Org1MSP"))) - - discoveryService, err := discoveryProvider.CreateDiscoveryService("mychannel") + ctx := mocks.NewMockContext(mockmsp.NewMockSigningIdentity("user1", "Org1MSP")) + discoveryService, err := NewService(ctx.EndpointConfig(), ctx.InfraProvider(), "mychannel") if err != nil { t.Fatalf("Failed to setup discovery service: %s", err) } @@ -53,20 +38,9 @@ func TestStaticDiscovery(t *testing.T) { } func TestStaticDiscoveryWhenChannelIsEmpty(t *testing.T) { - configBackend, err := config.FromFile("../../../../../test/fixtures/config/config_test.yaml")() - if err != nil { - t.Fatalf(err.Error()) - } - config1, err := fabImpl.ConfigFromBackend(configBackend...) - if err != nil { - t.Fatalf(err.Error()) - } - - discoveryProvider, _ := New(config1) - discoveryProvider.Initialize(mocks.NewMockContext(mockmsp.NewMockSigningIdentity("user1", "Org1MSP"))) - - _, err = discoveryProvider.CreateDiscoveryService("") + ctx := mocks.NewMockContext(mockmsp.NewMockSigningIdentity("user1", "Org1MSP")) + _, err := NewService(ctx.EndpointConfig(), ctx.InfraProvider(), "") assert.Error(t, err, "expecting error when channel ID is empty") } @@ -77,7 +51,7 @@ func TestStaticLocalDiscovery(t *testing.T) { config1, err := fabImpl.ConfigFromBackend(configBackend...) assert.NoError(t, err) - discoveryProvider, err := New(config1) + discoveryProvider, err := NewLocalProvider(config1) assert.NoError(t, err) clientCtx := mocks.NewMockContext(mockmsp.NewMockSigningIdentity("user1", "Org1MSP")) @@ -86,12 +60,7 @@ func TestStaticLocalDiscovery(t *testing.T) { discoveryService, err := discoveryProvider.CreateLocalDiscoveryService(clientCtx.Identifier().MSPID) assert.NoError(t, err) - localCtx := mocks.NewMockLocalContext(clientCtx, discoveryProvider) - - err = discoveryService.(*localDiscoveryService).Initialize(localCtx) - assert.NoError(t, err) - peers, err := discoveryService.GetPeers() assert.NoError(t, err) - assert.Equal(t, 1, len(peers)) + assert.Equal(t, 2, len(peers)) } diff --git a/pkg/client/common/mocks/mockdiscovery.go b/pkg/client/common/mocks/mockdiscovery.go index 604b189b72..b58bc092e8 100644 --- a/pkg/client/common/mocks/mockdiscovery.go +++ b/pkg/client/common/mocks/mockdiscovery.go @@ -7,8 +7,6 @@ SPDX-License-Identifier: Apache-2.0 package mocks import ( - "github.com/pkg/errors" - "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" ) @@ -34,19 +32,16 @@ func NewMockDiscoveryProvider(err error, peers []fab.Peer) (*MockStaticDiscovery return &MockStaticDiscoveryProvider{Error: err, Peers: peers}, nil } -// CreateDiscoveryService return discovery service for specific channel -func (dp *MockStaticDiscoveryProvider) CreateDiscoveryService(channelID string) (fab.DiscoveryService, error) { - if channelID == "error" { - return nil, errors.New("Generate error when creating new discovery service") - } - return &MockStaticDiscoveryService{Error: dp.Error, Peers: dp.Peers}, nil -} - // CreateLocalDiscoveryService return discovery service for specific channel func (dp *MockStaticDiscoveryProvider) CreateLocalDiscoveryService(mspID string) (fab.DiscoveryService, error) { return &MockStaticDiscoveryService{Error: dp.Error, Peers: dp.Peers}, nil } +// NewMockDiscoveryService returns mock discovery service +func NewMockDiscoveryService(err error, peers ...fab.Peer) *MockStaticDiscoveryService { + return &MockStaticDiscoveryService{Error: err, Peers: peers} +} + // GetPeers is used to discover eligible peers for chaincode func (ds *MockStaticDiscoveryService) GetPeers() ([]fab.Peer, error) { diff --git a/pkg/client/common/mocks/mockselection.go b/pkg/client/common/mocks/mockselection.go index e617d08556..2339da23d5 100644 --- a/pkg/client/common/mocks/mockselection.go +++ b/pkg/client/common/mocks/mockselection.go @@ -14,12 +14,6 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" ) -// MockSelectionProvider implements mock selection provider -type MockSelectionProvider struct { - Error error - Peers []fab.Peer -} - // MockSelectionService implements mock selection service type MockSelectionService struct { Error error @@ -27,14 +21,9 @@ type MockSelectionService struct { ChannelContext context.Channel } -// NewMockSelectionProvider returns mock selection provider -func NewMockSelectionProvider(err error, peers []fab.Peer) (*MockSelectionProvider, error) { - return &MockSelectionProvider{Error: err, Peers: peers}, nil -} - -// CreateSelectionService returns mock selection service -func (dp *MockSelectionProvider) CreateSelectionService(channelID string) (*MockSelectionService, error) { - return &MockSelectionService{Error: dp.Error, Peers: dp.Peers}, nil +// NewMockSelectionService returns mock selection service +func NewMockSelectionService(err error, peers ...fab.Peer) *MockSelectionService { + return &MockSelectionService{Error: err, Peers: peers} } // GetEndorsersForChaincode mockcore retrieving endorsing peers @@ -49,7 +38,11 @@ func (ds *MockSelectionService) GetEndorsersForChaincode(chaincodeIDs []string, var peers []fab.Peer if ds.ChannelContext != nil { var err error - peers, err = ds.ChannelContext.DiscoveryService().GetPeers() + discovery, err := ds.ChannelContext.ChannelService().Discovery() + if err != nil { + return nil, err + } + peers, err = discovery.GetPeers() if err != nil { return nil, err } diff --git a/pkg/client/common/selection/dynamicselection/ccpolicyprovider.go b/pkg/client/common/selection/dynamicselection/ccpolicyprovider.go index 1906712956..680c3fb5f6 100644 --- a/pkg/client/common/selection/dynamicselection/ccpolicyprovider.go +++ b/pkg/client/common/selection/dynamicselection/ccpolicyprovider.go @@ -20,9 +20,7 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/common/logging" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" - "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp" contextImpl "github.com/hyperledger/fabric-sdk-go/pkg/context" - "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/api" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/core/common/ccprovider" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common" ) @@ -36,57 +34,33 @@ const ( ccDataProviderfunction = "getccdata" ) -type peerCreator interface { - CreatePeerFromConfig(peerCfg *fab.NetworkPeer) (fab.Peer, error) -} - // CCPolicyProvider retrieves policy for the given chaincode ID type CCPolicyProvider interface { GetChaincodePolicy(chaincodeID string) (*common.SignaturePolicyEnvelope, error) } // NewCCPolicyProvider creates new chaincode policy data provider -func newCCPolicyProvider(providers api.Providers, channelID string, username string, orgName string) (CCPolicyProvider, error) { - if providers == nil || channelID == "" || username == "" || orgName == "" { - return nil, errors.New("Must provide providers, channel ID, user name and organisation for cc policy provider") - } - - //Get identity - mgr, ok := providers.IdentityManager(orgName) - if !ok { - return nil, errors.New("invalid options to create identity, invalid org name") - } - - identity, err := mgr.GetSigningIdentity(username) - if err != nil { - return nil, errors.WithMessage(err, "unable to create identity for ccl policy provider") - } - - discovery, err := providers.DiscoveryProvider().CreateDiscoveryService(channelID) - if err != nil { - return nil, errors.WithMessage(err, "unable to create discovery service for ccl policy provider") +func newCCPolicyProvider(ctx context.Client, discovery fab.DiscoveryService, channelID string) (CCPolicyProvider, error) { + if channelID == "" { + return nil, errors.New("Must provide channel ID for cc policy provider") } cpp := ccPolicyProvider{ - providers: providers, + context: ctx, channelID: channelID, - identity: identity, discovery: discovery, ccDataMap: make(map[string]*ccprovider.ChaincodeData), - provider: providers.InfraProvider(), } return &cpp, nil } type ccPolicyProvider struct { - providers context.Providers + context context.Client channelID string - identity msp.SigningIdentity discovery fab.DiscoveryService ccDataMap map[string]*ccprovider.ChaincodeData // TODO: Add expiry and configurable timeout for map entries mutex sync.RWMutex - provider peerCreator } func (dp *ccPolicyProvider) GetChaincodePolicy(chaincodeID string) (*common.SignaturePolicyEnvelope, error) { @@ -212,7 +186,7 @@ func (dp *ccPolicyProvider) getChannelContext() context.ChannelProvider { return func() (context.Channel, error) { //Get Client Context clientProvider := func() (context.Client, error) { - return &contextImpl.Client{Providers: dp.providers, SigningIdentity: dp.identity}, nil + return dp.context, nil } return contextImpl.NewChannel(clientProvider, dp.channelID) diff --git a/pkg/client/common/selection/dynamicselection/ccpolicyprovider_test.go b/pkg/client/common/selection/dynamicselection/ccpolicyprovider_test.go index 2d428f0fee..f84ac275ec 100644 --- a/pkg/client/common/selection/dynamicselection/ccpolicyprovider_test.go +++ b/pkg/client/common/selection/dynamicselection/ccpolicyprovider_test.go @@ -7,112 +7,45 @@ SPDX-License-Identifier: Apache-2.0 package dynamicselection import ( - "strings" "testing" - "github.com/hyperledger/fabric-sdk-go/pkg/core/config" - "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" + "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" + mspmocks "github.com/hyperledger/fabric-sdk-go/pkg/msp/test/mockmsp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func TestCCPolicyProvider(t *testing.T) { - // Create SDK setup for channel client with dynamic selection - sdk, err := fabsdk.New(config.FromFile("../../../../../test/fixtures/config/config_test.yaml")) - if err != nil { - t.Fatalf("Failed to create new SDK: %s", err) - } - defer sdk.Close() - - clientContext := sdk.Context(fabsdk.WithUser("User1"), fabsdk.WithOrg("Org1")) +var ( + peer1 = mocks.NewMockPeer("p1", "peer1.example.com:9999") + peer2 = mocks.NewMockPeer("p2", "peer2.example.com:9999") +) - context, err := clientContext() - if err != nil { - t.Fatal("Failed to create context") - } +func TestCCPolicyProvider(t *testing.T) { + context := mocks.NewMockContext( + mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), + ) // All good - ccPolicyProvider, err := newCCPolicyProvider(context, "mychannel", "User1", "Org1") - if err != nil { - t.Fatalf("Failed to setup cc policy provider: %s", err) - } + ccPolicyProvider, err := newCCPolicyProvider(context, mocks.NewMockDiscoveryService(nil, peer1, peer2), "mychannel") + require.NoErrorf(t, err, "Failed to setup cc policy provider") + require.NotNilf(t, ccPolicyProvider, "Policy provider is nil") // Empty chaincode ID _, err = ccPolicyProvider.GetChaincodePolicy("") - if err == nil { - t.Fatalf("Should have failed to retrieve chaincode policy for empty chaincode id") - } + assert.Errorf(t, err, "Should have failed to retrieve chaincode policy for empty chaincode id") // Non-existent chaincode ID _, err = ccPolicyProvider.GetChaincodePolicy("abc") - if err == nil { - t.Fatalf("Should have failed to retrieve non-existent cc policy") - } - + assert.Errorf(t, err, "Should have failed to retrieve non-existent cc policy") } func TestCCPolicyProviderNegative(t *testing.T) { - // Create SDK setup for channel client with dynamic selection - sdk, err := fabsdk.New(config.FromFile("../../../../../test/fixtures/config/config_test.yaml")) - if err != nil { - t.Fatalf("Failed to create new SDK: %s", err) - } - defer sdk.Close() - - clientContext := sdk.Context(fabsdk.WithUser("User1"), fabsdk.WithOrg("Org1")) - - context, err := clientContext() - if err != nil { - t.Fatal("Failed to create context") - } - // Nil sdk - _, err = newCCPolicyProvider(nil, "mychannel", "User1", "Org1") - if err == nil { - t.Fatalf("Should have failed for nil sdk") - } + context := mocks.NewMockContext( + mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), + ) // Invalid channelID - _, err = newCCPolicyProvider(context, "", "User1", "Org1") - if err == nil { - t.Fatalf("Should have failed for empty channel") - } - - // Empty user name - _, err = newCCPolicyProvider(context, "mychannel", "", "Prg1") - if err == nil { - t.Fatalf("Should have failed for empty user name") - } - - // Empty org name - _, err = newCCPolicyProvider(context, "mychannel", "User1", "") - if err == nil { - t.Fatalf("Should have failed for nil sdk") - } - -} - -func TestBadClient(t *testing.T) { - // Create SDK setup for channel client with dynamic selection - sdk, err := fabsdk.New(config.FromFile("../../../../../test/fixtures/config/config_test.yaml")) - if err != nil { - t.Fatalf("Failed to create new SDK: %s", err) - } - defer sdk.Close() - - clientContext := sdk.Context(fabsdk.WithUser("User1"), fabsdk.WithOrg("Org1")) - - context, err := clientContext() - if err != nil { - t.Fatal("Failed to create context") - } - - // Non-existent user - _, err = newCCPolicyProvider(context, "mychannel", "Invalid", "Org1") - if !strings.Contains(err.Error(), "user not found") { - t.Fatalf("Should have failed for invalid user name: %v", err) - } - - // Invalid org - _, err = newCCPolicyProvider(context, "mychannel", "User1", "Invalid") - if !strings.Contains(err.Error(), "invalid org name") { - t.Fatalf("Should have failed for invalid org name") - } + ccPolicyProvider, err := newCCPolicyProvider(context, mocks.NewMockDiscoveryService(nil, peer1, peer2), "") + require.Errorf(t, err, "Expected error for invalid channel ID") + require.Nilf(t, ccPolicyProvider, "Expected policy provider to be nil") } diff --git a/pkg/client/common/selection/dynamicselection/dynamicselection.go b/pkg/client/common/selection/dynamicselection/dynamicselection.go index a6efbc2607..e60e88fd64 100644 --- a/pkg/client/common/selection/dynamicselection/dynamicselection.go +++ b/pkg/client/common/selection/dynamicselection/dynamicselection.go @@ -8,139 +8,83 @@ package dynamicselection import ( "fmt" - "sync" "time" "github.com/hyperledger/fabric-sdk-go/pkg/util/concurrent/lazycache" "github.com/hyperledger/fabric-sdk-go/pkg/util/concurrent/lazyref" copts "github.com/hyperledger/fabric-sdk-go/pkg/common/options" - contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" "github.com/pkg/errors" "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/dynamicselection/pgresolver" "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/options" - "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/api" ) const defaultCacheTimeout = 30 * time.Minute -// ChannelUser contains user(identity) info to be used for specific channel -type ChannelUser struct { - ChannelID string - Username string - OrgName string -} - -// SelectionProvider implements selection provider -// TODO: refactor users into client contexts -type SelectionProvider struct { - config fab.EndpointConfig - users []ChannelUser - lbp pgresolver.LoadBalancePolicy - providers api.Providers - cacheTimeout time.Duration - refs []*selectionService - refLock sync.RWMutex -} - // Opt applies a selection provider option -type Opt func(*SelectionProvider) +type Opt func(*SelectionService) // WithLoadBalancePolicy sets the load-balance policy func WithLoadBalancePolicy(lbp pgresolver.LoadBalancePolicy) Opt { - return func(p *SelectionProvider) { - p.lbp = lbp + return func(s *SelectionService) { + s.pgLBP = lbp } } // WithCacheTimeout sets the expiration timeout of the cache func WithCacheTimeout(timeout time.Duration) Opt { - return func(p *SelectionProvider) { - p.cacheTimeout = timeout + return func(s *SelectionService) { + s.cacheTimeout = timeout } } -// New returns dynamic selection provider -func New(config fab.EndpointConfig, users []ChannelUser, opts ...Opt) (*SelectionProvider, error) { - p := &SelectionProvider{ - config: config, - users: users, - lbp: pgresolver.NewRandomLBP(), - cacheTimeout: defaultCacheTimeout, - } - - for _, opt := range opts { - opt(p) - } - - return p, nil -} - -type selectionService struct { +// SelectionService chooses endorsing peers for a given set of chaincodes using their chaincode policy +type SelectionService struct { channelID string pgResolvers *lazycache.Cache pgLBP pgresolver.LoadBalancePolicy ccPolicyProvider CCPolicyProvider discoveryService fab.DiscoveryService + cacheTimeout time.Duration } -// Initialize allow for initializing providers -func (p *SelectionProvider) Initialize(providers contextAPI.Providers) error { - p.providers = providers - return nil -} - -// CreateSelectionService creates a selection service -func (p *SelectionProvider) CreateSelectionService(channelID string) (fab.SelectionService, error) { - if channelID == "" { - return nil, errors.New("Must provide channel ID") - } - - var channelUser *ChannelUser - for _, p := range p.users { - if p.ChannelID == channelID { - channelUser = &p - break - } - } +type policyProviderFactory func() (CCPolicyProvider, error) - if channelUser == nil { - return nil, errors.New("Must provide user for channel") - } +// NewService creates a new dynamic selection service +func NewService(context context.Client, channelID string, discovery fab.DiscoveryService, opts ...Opt) (*SelectionService, error) { + return newService(context, channelID, discovery, + func() (CCPolicyProvider, error) { + return newCCPolicyProvider(context, discovery, channelID) + }, opts...) +} - ccPolicyProvider, err := newCCPolicyProvider(p.providers, channelID, channelUser.Username, channelUser.OrgName) +func newService(context context.Client, channelID string, discovery fab.DiscoveryService, factory policyProviderFactory, opts ...Opt) (*SelectionService, error) { + ccPolicyProvider, err := factory() if err != nil { return nil, errors.WithMessage(err, "Failed to create cc policy provider") } - svc, err := newSelectionService(channelID, p.lbp, ccPolicyProvider, p.cacheTimeout) - if err != nil { - return nil, err - } - p.refLock.Lock() - p.refs = append(p.refs, svc) - p.refLock.Unlock() - - return svc, nil -} + service := &SelectionService{ + channelID: channelID, + discoveryService: discovery, + ccPolicyProvider: ccPolicyProvider, + cacheTimeout: defaultCacheTimeout, + pgLBP: pgresolver.NewRandomLBP(), + } -// Close the selection services created by this provider -func (p *SelectionProvider) Close() { - p.refLock.Lock() - defer p.refLock.Unlock() + for _, opt := range opts { + opt(service) + } - for _, ref := range p.refs { - ref.Close() + if service.cacheTimeout == 0 { + service.cacheTimeout = context.EndpointConfig().Timeout(fab.SelectionServiceRefresh) } -} -func newSelectionService(channelID string, lbp pgresolver.LoadBalancePolicy, ccPolicyProvider CCPolicyProvider, cacheTimeout time.Duration) (*selectionService, error) { - service := &selectionService{ - channelID: channelID, - pgLBP: lbp, - ccPolicyProvider: ccPolicyProvider, + if service.pgLBP == nil { + service.pgLBP = pgresolver.NewRandomLBP() } service.pgResolvers = lazycache.New( @@ -150,7 +94,7 @@ func newSelectionService(channelID string, lbp pgresolver.LoadBalancePolicy, ccP func() (interface{}, error) { return service.createPGResolver(key.(*resolverKey)) }, - lazyref.WithAbsoluteExpiration(cacheTimeout), + lazyref.WithAbsoluteExpiration(service.cacheTimeout), ), nil }, ) @@ -158,12 +102,8 @@ func newSelectionService(channelID string, lbp pgresolver.LoadBalancePolicy, ccP return service, nil } -func (s *selectionService) Initialize(context contextAPI.Channel) error { - s.discoveryService = context.DiscoveryService() - return nil -} - -func (s *selectionService) GetEndorsersForChaincode(chaincodeIDs []string, opts ...copts.Opt) ([]fab.Peer, error) { +// GetEndorsersForChaincode returns the endorsing peers for the given chaincodes +func (s *SelectionService) GetEndorsersForChaincode(chaincodeIDs []string, opts ...copts.Opt) ([]fab.Peer, error) { if len(chaincodeIDs) == 0 { return nil, errors.New("no chaincode IDs provided") } @@ -199,11 +139,12 @@ func (s *selectionService) GetEndorsersForChaincode(chaincodeIDs []string, opts return peerGroup.Peers(), nil } -func (s *selectionService) Close() { +// Close closes all resources associated with the service +func (s *SelectionService) Close() { s.pgResolvers.Close() } -func (s *selectionService) getPeerGroupResolver(chaincodeIDs []string) (pgresolver.PeerGroupResolver, error) { +func (s *SelectionService) getPeerGroupResolver(chaincodeIDs []string) (pgresolver.PeerGroupResolver, error) { value, err := s.pgResolvers.Get(newResolverKey(s.channelID, chaincodeIDs...)) if err != nil { return nil, err @@ -216,7 +157,7 @@ func (s *selectionService) getPeerGroupResolver(chaincodeIDs []string) (pgresolv return resolver.(pgresolver.PeerGroupResolver), nil } -func (s *selectionService) createPGResolver(key *resolverKey) (pgresolver.PeerGroupResolver, error) { +func (s *SelectionService) createPGResolver(key *resolverKey) (pgresolver.PeerGroupResolver, error) { // Retrieve the signature policies for all of the chaincodes var policyGroups []pgresolver.GroupRetriever for _, ccID := range key.chaincodeIDs { @@ -248,7 +189,7 @@ func (s *selectionService) createPGResolver(key *resolverKey) (pgresolver.PeerGr return resolver, nil } -func (s *selectionService) getPolicyGroupForCC(channelID string, ccID string) (pgresolver.GroupRetriever, error) { +func (s *SelectionService) getPolicyGroupForCC(channelID string, ccID string) (pgresolver.GroupRetriever, error) { sigPolicyEnv, err := s.ccPolicyProvider.GetChaincodePolicy(ccID) if err != nil { return nil, errors.WithMessage(err, fmt.Sprintf("error querying chaincode [%s] on channel [%s]", ccID, channelID)) diff --git a/pkg/client/common/selection/dynamicselection/dynamicselection_test.go b/pkg/client/common/selection/dynamicselection/dynamicselection_test.go index 48dc35acf0..476d612403 100644 --- a/pkg/client/common/selection/dynamicselection/dynamicselection_test.go +++ b/pkg/client/common/selection/dynamicselection/dynamicselection_test.go @@ -7,18 +7,17 @@ SPDX-License-Identifier: Apache-2.0 package dynamicselection import ( - "reflect" "testing" "time" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/dynamicselection/pgresolver" + "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/options" "github.com/hyperledger/fabric-sdk-go/pkg/common/logging" + coptions "github.com/hyperledger/fabric-sdk-go/pkg/common/options" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" - "github.com/hyperledger/fabric-sdk-go/pkg/core/config" "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" - "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" - "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/factory/defsvc" + mspmocks "github.com/hyperledger/fabric-sdk-go/pkg/msp/test/mockmsp" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/core/common/ccprovider" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common" ) @@ -80,7 +79,46 @@ func TestGetEndorsersForChaincodeOneCC(t *testing.T) { // Org1 pg(p1), pg(p2), } - verify(t, service, expected, channel1, cc1) + verify(t, service, expected, channel1, nil, cc1) +} + +func TestGetEndorsersWithPeerFilter(t *testing.T) { + + channelPeers := []fab.Peer{p1, p2, p3, p4, p5, p6, p7, p8} + + service, err := newMockSelectionService( + newMockCCDataProvider(channel1). + add(cc1, getPolicy1()), + pgresolver.NewRoundRobinLBP(), + newMockDiscoveryService(channelPeers...), + ) + if err != nil { + t.Fatalf("got error creating selection service: %s", err) + } + + // Channel1(Policy(cc1)) = Org1 + expected := []pgresolver.PeerGroup{ + // Org1 + pg(p1), + } + opts := []coptions.Opt{ + options.WithPeerFilter(func(peer fab.Peer) bool { + return peer.URL() == p1.URL() + }), + } + verify(t, service, expected, channel1, opts, cc1) + + // Channel1(Policy(cc1)) = Org1 + expected = []pgresolver.PeerGroup{ + // Org1 + pg(p2), + } + opts = []coptions.Opt{ + options.WithPeerFilter(func(peer fab.Peer) bool { + return peer.URL() == p2.URL() + }), + } + verify(t, service, expected, channel1, opts, cc1) } func TestGetEndorsersForChaincodeTwoCCs(t *testing.T) { @@ -109,7 +147,7 @@ func TestGetEndorsersForChaincodeTwoCCs(t *testing.T) { pg(p1, p5, p8), pg(p1, p5, p9), pg(p1, p5, p10), pg(p1, p6, p8), pg(p1, p6, p9), pg(p1, p6, p10), pg(p1, p7, p8), pg(p1, p7, p9), pg(p1, p7, p10), pg(p2, p5, p8), pg(p2, p5, p9), pg(p2, p5, p10), pg(p2, p6, p8), pg(p2, p6, p9), pg(p2, p6, p10), pg(p2, p7, p8), pg(p2, p7, p9), pg(p2, p7, p10), } - verify(t, service, expected, channel1, cc1, cc2) + verify(t, service, expected, channel1, nil, cc1, cc2) } func TestGetEndorsersForChaincodeTwoCCsTwoChannels(t *testing.T) { @@ -139,7 +177,7 @@ func TestGetEndorsersForChaincodeTwoCCsTwoChannels(t *testing.T) { pg(p2, p5, p8), pg(p2, p5, p9), pg(p2, p5, p10), pg(p2, p6, p8), pg(p2, p6, p9), pg(p2, p6, p10), pg(p2, p7, p8), pg(p2, p7, p9), pg(p2, p7, p10), } - verify(t, service, expected, channel1, cc1, cc2) + verify(t, service, expected, channel1, nil, cc1, cc2) channel2Peers := []fab.Peer{p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12} service, err = newMockSelectionService( @@ -169,10 +207,10 @@ func TestGetEndorsersForChaincodeTwoCCsTwoChannels(t *testing.T) { pg(p12, p5, p8), pg(p12, p5, p9), pg(p12, p5, p10), pg(p12, p6, p8), pg(p12, p6, p9), pg(p12, p6, p10), pg(p12, p7, p8), pg(p12, p7, p9), pg(p12, p7, p10), } - verify(t, service, expected, channel2, cc1, cc2) + verify(t, service, expected, channel2, nil, cc1, cc2) } -func verify(t *testing.T, service fab.SelectionService, expectedPeerGroups []pgresolver.PeerGroup, channelID string, chaincodeIDs ...string) { +func verify(t *testing.T, service fab.SelectionService, expectedPeerGroups []pgresolver.PeerGroup, channelID string, getEndorsersOpts []coptions.Opt, chaincodeIDs ...string) { // Set the log level to WARNING since the following spits out too much info in DEBUG module := "pg-resolver" level := logging.GetLevel(module) @@ -180,7 +218,7 @@ func verify(t *testing.T, service fab.SelectionService, expectedPeerGroups []pgr defer logging.SetLevel(module, level) for i := 0; i < len(expectedPeerGroups); i++ { - peers, err := service.GetEndorsersForChaincode(chaincodeIDs) + peers, err := service.GetEndorsersForChaincode(chaincodeIDs, getEndorsersOpts...) if err != nil { t.Fatalf("error getting endorsers: %s", err) } @@ -233,7 +271,16 @@ func peer(name string, mspID string) fab.Peer { } func newMockSelectionService(ccPolicyProvider CCPolicyProvider, lbp pgresolver.LoadBalancePolicy, discoveryService fab.DiscoveryService) (fab.SelectionService, error) { - service, err := newSelectionService("", lbp, ccPolicyProvider, 5*time.Second) + context := mocks.NewMockContext( + mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), + ) + service, err := newService(context, "testchannel", discoveryService, + func() (CCPolicyProvider, error) { + return ccPolicyProvider, nil + }, + WithCacheTimeout(5*time.Second), + WithLoadBalancePolicy(lbp), + ) if err != nil { return nil, err } @@ -332,154 +379,6 @@ func toString(peers []fab.Peer) string { return str } -func TestDynamicSelection(t *testing.T) { - - // Create SDK setup for channel client with dynamic selection - sdk, err := fabsdk.New(config.FromFile("../../../../../test/fixtures/config/config_test.yaml")) - if err != nil { - t.Fatalf("Failed to create new SDK: %s", err) - } - defer sdk.Close() - - clientContext := sdk.Context(fabsdk.WithUser("User1"), fabsdk.WithOrg("Org1")) - ctx, err := clientContext() - if err != nil { - t.Fatalf("Failed to to get client context: %s", err) - } - - mychannelUser := ChannelUser{ChannelID: "mychannel", Username: "User1", OrgName: "Org1"} - - selectionProvider, err := New(ctx.EndpointConfig(), []ChannelUser{mychannelUser}) - if err != nil { - t.Fatalf("Failed to setup selection provider: %s", err) - } - - selectionProvider.providers = ctx - _, err = selectionProvider.CreateSelectionService("") - if err == nil { - t.Fatalf("Should have failed for empty channel name") - } - - selectionProvider.providers = nil - _, err = selectionProvider.CreateSelectionService("mychannel") - if err == nil { - t.Fatalf("Should have failed since sdk not provided") - } - - selectionProvider.providers = ctx - testLBPolicy(t, selectionProvider) - testCustomLBPolicy(t, ctx.EndpointConfig(), mychannelUser) -} - -func testLBPolicy(t *testing.T, selectionProvider *SelectionProvider) { - factory := DynamicSelectionProviderFactory{ - selectionProvider: selectionProvider, - } - - // Create SDK setup for channel client with dynamic selection - // This step is performed during the test to allow normal SDK-based initialized of the selection provider - sdk, err := fabsdk.New( - config.FromFile("../../../../../test/fixtures/config/config_test.yaml"), - fabsdk.WithServicePkg(&factory)) - if err != nil { - t.Fatalf("Failed to create new SDK: %s", err) - } - defer sdk.Close() - - selectionService, err := selectionProvider.CreateSelectionService("mychannel") - if err != nil { - t.Fatalf("Failed to create new selection service for channel: %s", err) - } - - if selectionProvider.lbp == nil { - t.Fatalf("Default load balancing policy is nil") - } - - if got, want := reflect.TypeOf(selectionProvider.lbp), reflect.TypeOf(pgresolver.NewRandomLBP()); got != want { - t.Fatalf("Default load balancing policy is wrong type. Want %v, Got %v", want, got) - } - - _, err = selectionService.GetEndorsersForChaincode(nil) - if err == nil { - t.Fatalf("Should have failed for no chaincode IDs provided") - } - - _, err = selectionService.GetEndorsersForChaincode([]string{""}) - if err == nil { - t.Fatalf("Should have failed since no channel peers are provided") - } - - _, err = selectionService.GetEndorsersForChaincode([]string{""}) - if err == nil { - t.Fatalf("Should have failed since empty cc ID provided") - } - - _, err = selectionService.GetEndorsersForChaincode([]string{"abc"}) - if err == nil { - t.Fatalf("Should have failed for non-existent cc ID") - } - -} - -func testCustomLBPolicy(t *testing.T, c fab.EndpointConfig, mychannelUser ChannelUser) { - - // Test custom load balancer - selectionProvider, err := New(c, []ChannelUser{mychannelUser}, WithLoadBalancePolicy(newCustomLBP())) - if err != nil { - t.Fatalf("Failed to setup selection provider: %s", err) - } - - factory := DynamicSelectionProviderFactory{ - selectionProvider: selectionProvider, - } - - // Create SDK setup for channel client with dynamic selection - // This step is performed during the test to allow normal SDK-based initialized of the selection provider - sdk, err := fabsdk.New( - config.FromFile("../../../../../test/fixtures/config/config_test.yaml"), - fabsdk.WithServicePkg(&factory)) - if err != nil { - t.Fatalf("Failed to create new SDK: %s", err) - } - defer sdk.Close() - - if selectionProvider.lbp == nil { - t.Fatalf("Failed to set load balancing policy") - } - - // Check correct load balancer policy - if got, want := reflect.TypeOf(selectionProvider.lbp), reflect.TypeOf(&customLBP{}); got != want { - t.Fatalf("Failed to set load balancing policy. Want %v, Got %v", want, got) - } - -} - -// DynamicSelectionProviderFactory is configured with dynamic (endorser) selection provider -type DynamicSelectionProviderFactory struct { - defsvc.ProviderFactory - selectionProvider fab.SelectionProvider -} - -// CreateSelectionProvider returns a new implementation of dynamic selection provider -func (f *DynamicSelectionProviderFactory) CreateSelectionProvider(config fab.EndpointConfig) (fab.SelectionProvider, error) { - return f.selectionProvider, nil -} - -type customLBP struct { -} - -// newCustomLBP returns a test load-balance policy -func newCustomLBP() pgresolver.LoadBalancePolicy { - return &customLBP{} -} - -func (lbp *customLBP) Choose(peerGroups []pgresolver.PeerGroup) pgresolver.PeerGroup { - if len(peerGroups) == 0 { - return pgresolver.NewPeerGroup() - } - return peerGroups[0] -} - type mockDiscoveryService struct { peers []fab.Peer } diff --git a/pkg/client/common/selection/staticselection/staticselection.go b/pkg/client/common/selection/staticselection/staticselection.go index af36a77e6c..2593e3703f 100644 --- a/pkg/client/common/selection/staticselection/staticselection.go +++ b/pkg/client/common/selection/staticselection/staticselection.go @@ -10,7 +10,6 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/options" "github.com/hyperledger/fabric-sdk-go/pkg/common/logging" copts "github.com/hyperledger/fabric-sdk-go/pkg/common/options" - contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" ) @@ -18,32 +17,18 @@ const loggerModule = "fabsdk/client" var logger = logging.NewLogger(loggerModule) -// SelectionProvider implements selection provider -type SelectionProvider struct { - config fab.EndpointConfig -} - -// New returns static selection provider -func New(config fab.EndpointConfig) (*SelectionProvider, error) { - return &SelectionProvider{config: config}, nil -} - -// selectionService implements static selection service -type selectionService struct { +// SelectionService implements static selection service +type SelectionService struct { discoveryService fab.DiscoveryService } -// CreateSelectionService creates a static selection service -func (p *SelectionProvider) CreateSelectionService(channelID string) (fab.SelectionService, error) { - return &selectionService{}, nil -} - -func (s *selectionService) Initialize(context contextAPI.Channel) error { - s.discoveryService = context.DiscoveryService() - return nil +// NewService creates a static selection service +func NewService(discovery fab.DiscoveryService) (fab.SelectionService, error) { + return &SelectionService{discoveryService: discovery}, nil } -func (s *selectionService) GetEndorsersForChaincode(chaincodeIDs []string, opts ...copts.Opt) ([]fab.Peer, error) { +// GetEndorsersForChaincode returns a set of endorsing peers +func (s *SelectionService) GetEndorsersForChaincode(chaincodeIDs []string, opts ...copts.Opt) ([]fab.Peer, error) { params := options.NewParams(opts) channelPeers, err := s.discoveryService.GetPeers() diff --git a/pkg/client/common/selection/staticselection/staticselection_test.go b/pkg/client/common/selection/staticselection/staticselection_test.go index 7ec23dbee9..85d045b30c 100644 --- a/pkg/client/common/selection/staticselection/staticselection_test.go +++ b/pkg/client/common/selection/staticselection/staticselection_test.go @@ -12,48 +12,18 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/options" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" - "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" - "github.com/hyperledger/fabric-sdk-go/pkg/core/config" - fabImpl "github.com/hyperledger/fabric-sdk-go/pkg/fab" fabmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" - mspmocks "github.com/hyperledger/fabric-sdk-go/pkg/msp/test/mockmsp" ) -type serviceInit interface { - Initialize(context context.Channel) error -} - func TestStaticSelection(t *testing.T) { - - configBackend, err := config.FromFile("../../../../../test/fixtures/config/config_test.yaml")() - if err != nil { - t.Fatalf(err.Error()) - } - - config, err := fabImpl.ConfigFromBackend(configBackend...) - if err != nil { - t.Fatalf(err.Error()) - } - peer1 := fabmocks.NewMockPeer("p1", "localhost:7051") peer2 := fabmocks.NewMockPeer("p2", "localhost:8051") - selectionProvider, err := New(config) + selectionService, err := NewService(fabmocks.NewMockDiscoveryService(nil, peer1, peer2)) if err != nil { t.Fatalf("Failed to setup selection provider: %s", err) } - selectionService, err := selectionProvider.CreateSelectionService("") - if err != nil { - t.Fatalf("Failed to setup selection service: %s", err) - } - - ctx := fabmocks.NewMockContext(mspmocks.NewMockSigningIdentity("User1", "")) - chctx := fabmocks.NewMockChannelContext(ctx, "testchannel") - chctx.Discovery = fabmocks.NewMockDiscoveryService(nil, []fab.Peer{peer1, peer2}) - - selectionService.(serviceInit).Initialize(chctx) - peers, err := selectionService.GetEndorsersForChaincode(nil) if err != nil { t.Fatalf("Failed to get endorsers: %s", err) diff --git a/pkg/client/ledger/ledger.go b/pkg/client/ledger/ledger.go index 32ed66b4d3..e66d8d7338 100644 --- a/pkg/client/ledger/ledger.go +++ b/pkg/client/ledger/ledger.go @@ -83,8 +83,13 @@ func New(channelProvider context.ChannelProvider, opts ...ClientOption) (*Client ledgerFilter := filter.NewEndpointFilter(channelContext, filter.LedgerQuery) + discoveryService, err := channelContext.ChannelService().Discovery() + if err != nil { + return nil, err + } + // Apply filter to discovery service - discovery := discovery.NewDiscoveryFilterService(channelContext.DiscoveryService(), ledgerFilter) + discovery := discovery.NewDiscoveryFilterService(discoveryService, ledgerFilter) ledgerClient := Client{ ctx: channelContext, diff --git a/pkg/client/ledger/ledger_test.go b/pkg/client/ledger/ledger_test.go index 9ec2214ab1..0505c2a95b 100644 --- a/pkg/client/ledger/ledger_test.go +++ b/pkg/client/ledger/ledger_test.go @@ -308,26 +308,14 @@ func setupCustomTestContext(t *testing.T, discoveryService fab.DiscoveryService, testChannelSvc, err := setupTestChannelService(ctx, orderers) assert.Nil(t, err, "Got error %s", err) testChannelSvc.(*fcmocks.MockChannelService).SetTransactor(&transactor) + testChannelSvc.(*fcmocks.MockChannelService).SetDiscovery(discoveryService) channelProvider := ctx.MockProviderContext.ChannelProvider() channelProvider.(*fcmocks.MockChannelProvider).SetCustomChannelService(testChannelSvc) - discoveryProvider := ctx.MockProviderContext.DiscoveryProvider() - discoveryProvider.(*fcmocks.MockStaticDiscoveryProvider).SetCustomDiscoveryService(discoveryService) - return createClientContext(ctx) } -func setupTestDiscovery(discErr error, peers []fab.Peer) (fab.DiscoveryService, error) { - - mockDiscovery, err := txnmocks.NewMockDiscoveryProvider(discErr, peers) - if err != nil { - return nil, errors.WithMessage(err, "NewMockDiscoveryProvider failed") - } - - return mockDiscovery.CreateDiscoveryService(channelID) -} - func setupLedgerClient(peers []fab.Peer, t *testing.T) *Client { return setupLedgerClientWithError(nil, nil, peers, t) @@ -335,12 +323,7 @@ func setupLedgerClient(peers []fab.Peer, t *testing.T) *Client { func setupLedgerClientWithError(discErr error, verifyErr error, peers []fab.Peer, t *testing.T) *Client { - discoveryService, err := setupTestDiscovery(discErr, peers) - if err != nil { - t.Fatalf("Failed to setup discovery service: %s", err) - } - - fabCtx := setupCustomTestContext(t, discoveryService, nil) + fabCtx := setupCustomTestContext(t, txnmocks.NewMockDiscoveryService(discErr, peers...), nil) ctx := createChannelContext(fabCtx, channelID) diff --git a/pkg/client/resmgmt/resmgmt.go b/pkg/client/resmgmt/resmgmt.go index 8cee0e7082..006827e834 100644 --- a/pkg/client/resmgmt/resmgmt.go +++ b/pkg/client/resmgmt/resmgmt.go @@ -568,7 +568,10 @@ func (rc *Client) QueryInstantiatedChaincodes(channelID string, options ...Reque target = opts.Targets[0] } else { // discover peers on this channel - discovery := chCtx.DiscoveryService() + discovery, err := chCtx.ChannelService().Discovery() + if err != nil { + return nil, errors.WithMessage(err, "failed to get discovery service") + } // default filter will be applied (if any) targets, err2 := rc.getDefaultTargets(discovery) if err2 != nil { @@ -650,7 +653,10 @@ func (rc *Client) getCCProposalTargets(channelID string, req InstantiateCCReques } // per channel discovery service - discovery := chCtx.DiscoveryService() + discovery, err := chCtx.ChannelService().Discovery() + if err != nil { + return nil, errors.WithMessage(err, "failed to get discovery service") + } //Default targets when targets are not provided in options if len(opts.Targets) == 0 { diff --git a/pkg/client/resmgmt/resmgmt_test.go b/pkg/client/resmgmt/resmgmt_test.go index 6cbfc4f86c..4d1f9c94b4 100644 --- a/pkg/client/resmgmt/resmgmt_test.go +++ b/pkg/client/resmgmt/resmgmt_test.go @@ -819,22 +819,6 @@ func TestInstantiateCCWithOpts(t *testing.T) { } } -func TestInstantiateCCDiscoveryError(t *testing.T) { - - // Setup test client and config - ctx := setupTestContext("test", "Org1MSP") - rc := setupResMgmtClient(t, ctx) - - ccPolicy := cauthdsl.SignedByMspMember("Org1MSP") - req := InstantiateCCRequest{Name: "name", Version: "version", Path: "path", Policy: ccPolicy} - - // Test InstantiateCCWithOpts create new discovery service per channel error - _, err := rc.InstantiateCC("error", req) - if err == nil || !strings.Contains(err.Error(), "failed to get discovery service") { - t.Fatalf("Should have failed to instantiate cc with opts with get discovery service error: %s", err) - } -} - func TestUpgradeCCRequiredParameters(t *testing.T) { rc := setupDefaultResMgmtClient(t) @@ -927,25 +911,6 @@ func TestUpgradeCCWithOpts(t *testing.T) { } } -func TestUpgradeCCDiscoveryError(t *testing.T) { - - // Setup test client and config - ctx := setupTestContext("test", "Org1MSP") - - // Create resource management client with discovery service that will generate an error - rc := setupResMgmtClient(t, ctx) - - // Test UpgradeCC discovery service error - ccPolicy := cauthdsl.SignedByMspMember("Org1MSP") - req := UpgradeCCRequest{Name: "name", Version: "version", Path: "path", Policy: ccPolicy} - - // Test error while creating discovery service for channel "error" - _, err := rc.UpgradeCC("error", req) - if err == nil { - t.Fatalf("Should have failed to upgrade cc with discovery error") - } -} - func TestCCProposal(t *testing.T) { ctx := setupTestContext("Admin", "Org1MSP") diff --git a/pkg/common/providers/context/context.go b/pkg/common/providers/context/context.go index 5e32e1a501..08da9cc899 100644 --- a/pkg/common/providers/context/context.go +++ b/pkg/common/providers/context/context.go @@ -31,8 +31,6 @@ type Local interface { // Channel supplies the configuration for channel context client type Channel interface { Client - DiscoveryService() fab.DiscoveryService - SelectionService() fab.SelectionService ChannelService() fab.ChannelService ChannelID() string } diff --git a/pkg/common/providers/fab/context.go b/pkg/common/providers/fab/context.go index 17ac682f98..6390af2681 100644 --- a/pkg/common/providers/fab/context.go +++ b/pkg/common/providers/fab/context.go @@ -19,6 +19,8 @@ type ChannelService interface { Membership() (ChannelMembership, error) ChannelConfig() (ChannelCfg, error) Transactor(reqCtx reqContext.Context) (Transactor, error) + Discovery() (DiscoveryService, error) + Selection() (SelectionService, error) } // Transactor supplies methods for sending transaction proposals and transactions. diff --git a/pkg/common/providers/fab/provider.go b/pkg/common/providers/fab/provider.go index 8644e61b35..07a5906e27 100644 --- a/pkg/common/providers/fab/provider.go +++ b/pkg/common/providers/fab/provider.go @@ -36,11 +36,6 @@ type InfraProvider interface { Close() } -// SelectionProvider is used to select peers for endorsement -type SelectionProvider interface { - CreateSelectionService(channelID string) (SelectionService, error) -} - // SelectionService selects peers for endorsement and commit events type SelectionService interface { // GetEndorsersForChaincode returns a set of peers that should satisfy the endorsement @@ -50,11 +45,6 @@ type SelectionService interface { GetEndorsersForChaincode(chaincodeIDs []string, opts ...options.Opt) ([]Peer, error) } -// DiscoveryProvider is used to discover peers on the network -type DiscoveryProvider interface { - CreateDiscoveryService(channelID string) (DiscoveryService, error) -} - // DiscoveryService is used to discover eligible peers on specific channel type DiscoveryService interface { GetPeers() ([]Peer, error) @@ -135,6 +125,8 @@ const ( DiscoveryResponse // DiscoveryServiceRefresh discovery service refresh interval DiscoveryServiceRefresh + // SelectionServiceRefresh selection service refresh interval + SelectionServiceRefresh ) // EventServiceType specifies the type of event service to use @@ -151,9 +143,7 @@ const ( // Providers represents the SDK configured service providers context. type Providers interface { - DiscoveryProvider() DiscoveryProvider LocalDiscoveryProvider() LocalDiscoveryProvider - SelectionProvider() SelectionProvider ChannelProvider() ChannelProvider InfraProvider() InfraProvider EndpointConfig() EndpointConfig diff --git a/pkg/common/providers/test/mockcontext/mockcontext.gen.go b/pkg/common/providers/test/mockcontext/mockcontext.gen.go index d7263c0790..2a6d9f7c72 100644 --- a/pkg/common/providers/test/mockcontext/mockcontext.gen.go +++ b/pkg/common/providers/test/mockcontext/mockcontext.gen.go @@ -60,18 +60,6 @@ func (mr *MockProvidersMockRecorder) CryptoSuite() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CryptoSuite", reflect.TypeOf((*MockProviders)(nil).CryptoSuite)) } -// DiscoveryProvider mocks base method -func (m *MockProviders) DiscoveryProvider() fab.DiscoveryProvider { - ret := m.ctrl.Call(m, "DiscoveryProvider") - ret0, _ := ret[0].(fab.DiscoveryProvider) - return ret0 -} - -// DiscoveryProvider indicates an expected call of DiscoveryProvider -func (mr *MockProvidersMockRecorder) DiscoveryProvider() *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoveryProvider", reflect.TypeOf((*MockProviders)(nil).DiscoveryProvider)) -} - // EndpointConfig mocks base method func (m *MockProviders) EndpointConfig() fab.EndpointConfig { ret := m.ctrl.Call(m, "EndpointConfig") @@ -133,18 +121,6 @@ func (mr *MockProvidersMockRecorder) LocalDiscoveryProvider() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalDiscoveryProvider", reflect.TypeOf((*MockProviders)(nil).LocalDiscoveryProvider)) } -// SelectionProvider mocks base method -func (m *MockProviders) SelectionProvider() fab.SelectionProvider { - ret := m.ctrl.Call(m, "SelectionProvider") - ret0, _ := ret[0].(fab.SelectionProvider) - return ret0 -} - -// SelectionProvider indicates an expected call of SelectionProvider -func (mr *MockProvidersMockRecorder) SelectionProvider() *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectionProvider", reflect.TypeOf((*MockProviders)(nil).SelectionProvider)) -} - // SigningManager mocks base method func (m *MockProviders) SigningManager() core.SigningManager { ret := m.ctrl.Call(m, "SigningManager") @@ -216,18 +192,6 @@ func (mr *MockClientMockRecorder) CryptoSuite() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CryptoSuite", reflect.TypeOf((*MockClient)(nil).CryptoSuite)) } -// DiscoveryProvider mocks base method -func (m *MockClient) DiscoveryProvider() fab.DiscoveryProvider { - ret := m.ctrl.Call(m, "DiscoveryProvider") - ret0, _ := ret[0].(fab.DiscoveryProvider) - return ret0 -} - -// DiscoveryProvider indicates an expected call of DiscoveryProvider -func (mr *MockClientMockRecorder) DiscoveryProvider() *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoveryProvider", reflect.TypeOf((*MockClient)(nil).DiscoveryProvider)) -} - // EndpointConfig mocks base method func (m *MockClient) EndpointConfig() fab.EndpointConfig { ret := m.ctrl.Call(m, "EndpointConfig") @@ -337,18 +301,6 @@ func (mr *MockClientMockRecorder) PublicVersion() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublicVersion", reflect.TypeOf((*MockClient)(nil).PublicVersion)) } -// SelectionProvider mocks base method -func (m *MockClient) SelectionProvider() fab.SelectionProvider { - ret := m.ctrl.Call(m, "SelectionProvider") - ret0, _ := ret[0].(fab.SelectionProvider) - return ret0 -} - -// SelectionProvider indicates an expected call of SelectionProvider -func (mr *MockClientMockRecorder) SelectionProvider() *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectionProvider", reflect.TypeOf((*MockClient)(nil).SelectionProvider)) -} - // Serialize mocks base method func (m *MockClient) Serialize() ([]byte, error) { ret := m.ctrl.Call(m, "Serialize") diff --git a/pkg/common/providers/test/mockfab/mockfab.gen.go b/pkg/common/providers/test/mockfab/mockfab.gen.go index 91be39bd7c..f0f38ee1b4 100644 --- a/pkg/common/providers/test/mockfab/mockfab.gen.go +++ b/pkg/common/providers/test/mockfab/mockfab.gen.go @@ -292,18 +292,6 @@ func (mr *MockProvidersMockRecorder) ChannelProvider() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChannelProvider", reflect.TypeOf((*MockProviders)(nil).ChannelProvider)) } -// DiscoveryProvider mocks base method -func (m *MockProviders) DiscoveryProvider() fab.DiscoveryProvider { - ret := m.ctrl.Call(m, "DiscoveryProvider") - ret0, _ := ret[0].(fab.DiscoveryProvider) - return ret0 -} - -// DiscoveryProvider indicates an expected call of DiscoveryProvider -func (mr *MockProvidersMockRecorder) DiscoveryProvider() *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoveryProvider", reflect.TypeOf((*MockProviders)(nil).DiscoveryProvider)) -} - // EndpointConfig mocks base method func (m *MockProviders) EndpointConfig() fab.EndpointConfig { ret := m.ctrl.Call(m, "EndpointConfig") @@ -339,15 +327,3 @@ func (m *MockProviders) LocalDiscoveryProvider() fab.LocalDiscoveryProvider { func (mr *MockProvidersMockRecorder) LocalDiscoveryProvider() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalDiscoveryProvider", reflect.TypeOf((*MockProviders)(nil).LocalDiscoveryProvider)) } - -// SelectionProvider mocks base method -func (m *MockProviders) SelectionProvider() fab.SelectionProvider { - ret := m.ctrl.Call(m, "SelectionProvider") - ret0, _ := ret[0].(fab.SelectionProvider) - return ret0 -} - -// SelectionProvider indicates an expected call of SelectionProvider -func (mr *MockProvidersMockRecorder) SelectionProvider() *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectionProvider", reflect.TypeOf((*MockProviders)(nil).SelectionProvider)) -} diff --git a/pkg/context/context.go b/pkg/context/context.go index 2ca97db485..c6e2e743a7 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -41,8 +41,6 @@ func (c *Local) LocalDiscoveryService() fab.DiscoveryService { //Channel supplies the configuration for channel context client type Channel struct { context.Client - discovery fab.DiscoveryService - selection fab.SelectionService channelService fab.ChannelService channelID string } @@ -52,16 +50,6 @@ func (c *Channel) Providers() context.Client { return c } -//DiscoveryService returns core discovery service -func (c *Channel) DiscoveryService() fab.DiscoveryService { - return c.discovery -} - -//SelectionService returns selection service -func (c *Channel) SelectionService() fab.SelectionService { - return c.selection -} - //ChannelService returns channel service func (c *Channel) ChannelService() fab.ChannelService { return c.channelService @@ -79,9 +67,7 @@ type Provider struct { identityConfig msp.IdentityConfig userStore msp.UserStore cryptoSuite core.CryptoSuite - discoveryProvider fab.DiscoveryProvider localDiscoveryProvider fab.LocalDiscoveryProvider - selectionProvider fab.SelectionProvider signingManager core.SigningManager idMgmtProvider msp.IdentityManagerProvider infraProvider fab.InfraProvider @@ -113,21 +99,11 @@ func (c *Provider) IdentityConfig() msp.IdentityConfig { return c.identityConfig } -// DiscoveryProvider returns discovery provider -func (c *Provider) DiscoveryProvider() fab.DiscoveryProvider { - return c.discoveryProvider -} - // LocalDiscoveryProvider returns the local discovery provider func (c *Provider) LocalDiscoveryProvider() fab.LocalDiscoveryProvider { return c.localDiscoveryProvider } -// SelectionProvider returns selection provider -func (c *Provider) SelectionProvider() fab.SelectionProvider { - return c.selectionProvider -} - // ChannelProvider provides channel services. func (c *Provider) ChannelProvider() fab.ChannelProvider { return c.channelProvider @@ -181,13 +157,6 @@ func WithCryptoSuite(cryptoSuite core.CryptoSuite) SDKContextParams { } } -//WithDiscoveryProvider sets discoveryProvider to Context Provider -func WithDiscoveryProvider(discoveryProvider fab.DiscoveryProvider) SDKContextParams { - return func(ctx *Provider) { - ctx.discoveryProvider = discoveryProvider - } -} - //WithLocalDiscoveryProvider sets the local discovery provider func WithLocalDiscoveryProvider(discoveryProvider fab.LocalDiscoveryProvider) SDKContextParams { return func(ctx *Provider) { @@ -195,13 +164,6 @@ func WithLocalDiscoveryProvider(discoveryProvider fab.LocalDiscoveryProvider) SD } } -//WithSelectionProvider sets selectionProvider to Context Provider -func WithSelectionProvider(selectionProvider fab.SelectionProvider) SDKContextParams { - return func(ctx *Provider) { - ctx.selectionProvider = selectionProvider - } -} - //WithSigningManager sets signingManager to Context Provider func WithSigningManager(signingManager core.SigningManager) SDKContextParams { return func(ctx *Provider) { @@ -292,50 +254,17 @@ func NewChannel(clientProvider context.ClientProvider, channelID string) (*Chann return nil, errors.WithMessage(err, "failed to get channel service to create channel client") } - discoveryService, err := client.DiscoveryProvider().CreateDiscoveryService(channelID) - if err != nil { - return nil, errors.WithMessage(err, "failed to get discovery service to create channel client") - } - - selectionService, err := client.SelectionProvider().CreateSelectionService(channelID) - if err != nil { - return nil, errors.WithMessage(err, "failed to get selection service to create channel client") - } - channel := &Channel{ Client: client, - selection: selectionService, - discovery: discoveryService, channelService: channelService, channelID: channelID, } - err = initialize(channel, channelService, discoveryService, selectionService) - if err != nil { - return nil, err - } - return channel, nil -} - -func initialize(channel *Channel, channelService fab.ChannelService, discoveryService fab.DiscoveryService, selectionService fab.SelectionService) error { - //initialize if pi, ok := channelService.(serviceInit); ok { if err := pi.Initialize(channel); err != nil { - return err - } - } - - if pi, ok := discoveryService.(serviceInit); ok { - if err := pi.Initialize(channel); err != nil { - return err - } - } - - if pi, ok := selectionService.(serviceInit); ok { - if err := pi.Initialize(channel); err != nil { - return err + return nil, err } } - return nil + return channel, nil } type reqContextKey string diff --git a/pkg/core/config/testdata/template/config.yaml b/pkg/core/config/testdata/template/config.yaml index 7f0b105616..619b6c38c6 100755 --- a/pkg/core/config/testdata/template/config.yaml +++ b/pkg/core/config/testdata/template/config.yaml @@ -75,6 +75,7 @@ client: # channelConfig: 30m # channelMembership: 30s # discovery: 10s +# selection: 10m # Needed to load users crypto keys and certs. cryptoconfig: diff --git a/pkg/fab/chconfig/chconfig.go b/pkg/fab/chconfig/chconfig.go index 12ec38b04d..a7befd9ba2 100644 --- a/pkg/fab/chconfig/chconfig.go +++ b/pkg/fab/chconfig/chconfig.go @@ -204,7 +204,10 @@ func (c *ChannelConfig) calculateTargetsFromConfig(ctx context.Client) ([]fab.Pr return nil, errors.WithMessage(err, "NewPeer failed") } - targets = append(targets, newPeer) + // Pick peers in the same MSP as the context since only they can query system chaincode + if newPeer.MSPID() == ctx.Identifier().MSPID { + targets = append(targets, newPeer) + } } targets = randomMaxTargets(targets, c.opts.MaxTargets) diff --git a/pkg/fab/endpointconfig.go b/pkg/fab/endpointconfig.go index ec13a03181..5157a41702 100644 --- a/pkg/fab/endpointconfig.go +++ b/pkg/fab/endpointconfig.go @@ -50,7 +50,8 @@ const ( defaultEventServiceIdleInterval = time.Minute * 2 defaultChannelConfigRefreshInterval = time.Second * 90 defaultChannelMemshpRefreshInterval = time.Second * 60 - defaultDiscoveryRefreshInterval = time.Second * 10 + defaultDiscoveryRefreshInterval = time.Second * 5 + defaultSelectionRefreshInterval = time.Minute * 10 defaultCacheSweepInterval = time.Second * 15 ) @@ -583,6 +584,11 @@ func (c *EndpointConfig) getTimeout(tType fab.TimeoutType) time.Duration { //nol if timeout == 0 { timeout = defaultDiscoveryRefreshInterval } + case fab.SelectionServiceRefresh: + timeout = c.backend.GetDuration("client.global.cache.selection") + if timeout == 0 { + timeout = defaultSelectionRefreshInterval + } case fab.CacheSweepInterval: // EXPERIMENTAL - do we need this to be configurable? timeout = c.backend.GetDuration("client.cache.interval.sweep") @@ -802,9 +808,12 @@ func (c *EndpointConfig) tryMatchingPeerConfig(networkConfig *fab.NetworkConfig, //loop over peerentityMatchers to find the matching peer for _, k := range keys { v := c.peerMatchers[k] + logger.Debugf("Trying to match peer [%s] with matcher [%s]", peerName, v.String()) if v.MatchString(peerName) { + logger.Debugf("Peer [%s] matched using matcher [%s]", peerName, v.String()) return c.matchPeer(networkConfig, peerName, k, v) } + logger.Debugf("Peer [%s] did not match using matcher [%s]", peerName, v.String()) } return nil diff --git a/pkg/fab/endpointconfig_test.go b/pkg/fab/endpointconfig_test.go index e388549393..356d0b5cb9 100644 --- a/pkg/fab/endpointconfig_test.go +++ b/pkg/fab/endpointconfig_test.go @@ -157,6 +157,7 @@ func TestTimeouts(t *testing.T) { customBackend.KeyValueMap["client.global.cache.channelConfig"] = "3m" customBackend.KeyValueMap["client.global.cache.channelMembership"] = "4m" customBackend.KeyValueMap["client.global.cache.discovery"] = "15s" + customBackend.KeyValueMap["client.global.cache.selection"] = "15m" endpointConfig, err := ConfigFromBackend(customBackend) if err != nil { @@ -210,6 +211,8 @@ func checkTimeouts(endpointConfig fab.EndpointConfig, t *testing.T, errStr strin assert.Equal(t, time.Minute*4, t1, "ChannelMembershipRefresh") t1 = endpointConfig.Timeout(fab.DiscoveryServiceRefresh) assert.Equal(t, time.Second*15, t1, "DiscoveryServiceRefresh") + t1 = endpointConfig.Timeout(fab.SelectionServiceRefresh) + assert.Equal(t, time.Minute*15, t1, "SelectionServiceRefresh") t1 = endpointConfig.Timeout(fab.DiscoveryConnection) assert.Equal(t, time.Second*20, t1, "DiscoveryConnection") t1 = endpointConfig.Timeout(fab.DiscoveryResponse) @@ -232,6 +235,8 @@ func TestDefaultTimeouts(t *testing.T) { customBackend.KeyValueMap["client.global.cache.eventServiceIdle"] = "" customBackend.KeyValueMap["client.global.cache.channelConfig"] = "" customBackend.KeyValueMap["client.global.cache.channelMembership"] = "" + customBackend.KeyValueMap["client.global.cache.discovery"] = "" + customBackend.KeyValueMap["client.global.cache.selection"] = "" endpointConfig, err := ConfigFromBackend(customBackend) if err != nil { @@ -263,6 +268,14 @@ func TestDefaultTimeouts(t *testing.T) { if t1 != defaultOrdererConnectionTimeout { t.Fatalf(errStr, "OrdererConnection", t1) } + t1 = endpointConfig.Timeout(fab.DiscoveryServiceRefresh) + if t1 != defaultDiscoveryRefreshInterval { + t.Fatalf(errStr, "DiscoveryRefreshInterval", t1) + } + t1 = endpointConfig.Timeout(fab.SelectionServiceRefresh) + if t1 != defaultSelectionRefreshInterval { + t.Fatalf(errStr, "SelectionRefreshInterval", t1) + } checkDefaultTimeout(endpointConfig, t, errStr) } diff --git a/pkg/fab/events/client/client_test.go b/pkg/fab/events/client/client_test.go index 1ceada18f8..16ae477bcd 100755 --- a/pkg/fab/events/client/client_test.go +++ b/pkg/fab/events/client/client_test.go @@ -51,11 +51,11 @@ func TestConnect(t *testing.T) { ) eventClient, _, err := newClientWithMockConnAndOpts( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg("mychannel"), + clientmocks.NewDiscoveryService(peer1, peer2), connectionProvider, filteredClientProvider, []options.Opt{}, ) if err != nil { @@ -85,11 +85,11 @@ func TestConnect(t *testing.T) { func TestFailConnect(t *testing.T) { eventClient, _, err := newClientWithMockConnAndOpts( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg("mychannel"), + clientmocks.NewDiscoveryService(peer1, peer2), mockconn.NewProviderFactory().Provider( mockconn.NewMockConnection( mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.FilteredBlockEventFactory, sourceURL)), @@ -107,11 +107,11 @@ func TestFailConnect(t *testing.T) { func TestCallsOnClosedClient(t *testing.T) { eventClient, _, err := newClientWithMockConn( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg("mychannel"), + clientmocks.NewDiscoveryService(peer1, peer2), filteredClientProvider, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.FilteredBlockEventFactory, sourceURL)), ) @@ -149,11 +149,11 @@ func TestCallsOnClosedClient(t *testing.T) { func TestCloseIfIdle(t *testing.T) { channelID := "mychannel" eventClient, _, err := newClientWithMockConn( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), clientProvider, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.BlockEventFactory, sourceURL)), ) @@ -183,11 +183,11 @@ func TestCloseIfIdle(t *testing.T) { func TestInvalidUnregister(t *testing.T) { channelID := "mychannel" eventClient, _, err := newClientWithMockConn( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), filteredClientProvider, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.FilteredBlockEventFactory, sourceURL)), ) @@ -206,11 +206,11 @@ func TestInvalidUnregister(t *testing.T) { func TestUnauthorizedBlockEvents(t *testing.T) { channelID := "mychannel" eventClient, _, err := newClientWithMockConn( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), filteredClientProvider, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.FilteredBlockEventFactory, sourceURL)), ) @@ -230,11 +230,11 @@ func TestUnauthorizedBlockEvents(t *testing.T) { func TestBlockEvents(t *testing.T) { channelID := "mychannel" eventClient, conn, err := newClientWithMockConn( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), clientProvider, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.BlockEventFactory, sourceURL)), ) @@ -291,11 +291,11 @@ func checkBlockEvent(t *testing.T, channelID string, conn mockconn.Connection, e func TestFilteredBlockEvents(t *testing.T) { channelID := "mychannel" eventClient, conn, err := newClientWithMockConn( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), filteredClientProvider, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.FilteredBlockEventFactory, sourceURL)), ) @@ -370,11 +370,11 @@ func checkFbEvent(t *testing.T, fbevent *fab.FilteredBlockEvent, channelID strin func TestBlockAndFilteredBlockEvents(t *testing.T) { channelID := "mychannel" eventClient, conn, err := newClientWithMockConn( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), clientProvider, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.BlockEventFactory, sourceURL)), ) @@ -451,11 +451,11 @@ func checkBlockAndFilteredBlockEvents(t *testing.T, channelID string, fbeventch func TestTxStatusEvents(t *testing.T) { channelID := "mychannel" eventClient, conn, err := newClientWithMockConn( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), filteredClientProvider, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.FilteredBlockEventFactory, sourceURL)), ) @@ -538,11 +538,11 @@ func checkTxStatusEvents(t *testing.T, eventch1 <-chan *fab.TxStatusEvent, event func TestCCEvents(t *testing.T) { channelID := "mychannel" eventClient, conn, err := newClientWithMockConn( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), filteredClientProvider, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.FilteredBlockEventFactory, sourceURL)), ) @@ -739,11 +739,11 @@ func TestConcurrentEvents(t *testing.T) { ccFilter := "event.*" eventClient, conn, err := newClientWithMockConnAndOpts( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg("mychannel"), + clientmocks.NewDiscoveryService(peer1, peer2), nil, clientProvider, []options.Opt{ esdispatcher.WithEventConsumerBufferSize(uint(numEvents) * 4), @@ -1019,11 +1019,11 @@ func testConnect(t *testing.T, maxConnectAttempts uint, expectedOutcome mockconn cp := mockconn.NewProviderFactory() eventClient, _, err := newClientWithMockConnAndOpts( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg("mychannel"), + clientmocks.NewDiscoveryService(peer1, peer2), cp.FlakeyProvider(connAttemptResult, mockconn.WithLedger(servicemocks.NewMockLedger(servicemocks.BlockEventFactory, sourceURL))), clientProvider, []options.Opt{ @@ -1056,11 +1056,11 @@ func testReconnect(t *testing.T, reconnect bool, maxReconnectAttempts uint, expe ledger := servicemocks.NewMockLedger(servicemocks.BlockEventFactory, sourceURL) eventClient, _, err := newClientWithMockConnAndOpts( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg("mychannel"), + clientmocks.NewDiscoveryService(peer1, peer2), cp.FlakeyProvider(connAttemptResult, mockconn.WithLedger(ledger)), clientProvider, []options.Opt{ @@ -1113,11 +1113,11 @@ func testReconnectRegistration(t *testing.T, expectedBlockEvents mockconn.NumBlo cp := mockconn.NewProviderFactory() eventClient, _, err := newClientWithMockConnAndOpts( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg("mychannel"), + clientmocks.NewDiscoveryService(peer1, peer2), cp.FlakeyProvider(connectResults, mockconn.WithLedger(ledger)), clientProvider, []options.Opt{ @@ -1267,11 +1267,11 @@ func listenEvents(blockch <-chan *fab.BlockEvent, ccch <-chan *fab.CCEvent, wait } } -type ClientProvider func(context context.Client, chConfig fab.ChannelCfg, connectionProvider api.ConnectionProvider, opts []options.Opt) (*Client, error) +type ClientProvider func(context context.Client, chConfig fab.ChannelCfg, discoveryService fab.DiscoveryService, connectionProvider api.ConnectionProvider, opts []options.Opt) (*Client, error) -var clientProvider = func(context context.Client, chConfig fab.ChannelCfg, connectionProvider api.ConnectionProvider, opts []options.Opt) (*Client, error) { +var clientProvider = func(context context.Client, chConfig fab.ChannelCfg, discoveryService fab.DiscoveryService, connectionProvider api.ConnectionProvider, opts []options.Opt) (*Client, error) { opts = append(opts, WithBlockEvents()) - return newClient(context, chConfig, connectionProvider, opts, + return newClient(context, chConfig, discoveryService, connectionProvider, opts, func() error { fmt.Printf("AfterConnect called") return nil @@ -1282,17 +1282,17 @@ var clientProvider = func(context context.Client, chConfig fab.ChannelCfg, conne }) } -var failAfterConnectClientProvider = func(context context.Client, chConfig fab.ChannelCfg, connectionProvider api.ConnectionProvider, opts []options.Opt) (*Client, error) { +var failAfterConnectClientProvider = func(context context.Client, chConfig fab.ChannelCfg, discoveryService fab.DiscoveryService, connectionProvider api.ConnectionProvider, opts []options.Opt) (*Client, error) { opts = append(opts, WithBlockEvents()) - return newClient(context, chConfig, connectionProvider, opts, + return newClient(context, chConfig, discoveryService, connectionProvider, opts, func() error { return errors.New("simulated failure after connect") }, nil) } -var filteredClientProvider = func(context context.Client, chConfig fab.ChannelCfg, connectionProvider api.ConnectionProvider, opts []options.Opt) (*Client, error) { - return newClient(context, chConfig, connectionProvider, opts, +var filteredClientProvider = func(context context.Client, chConfig fab.ChannelCfg, discoveryService fab.DiscoveryService, connectionProvider api.ConnectionProvider, opts []options.Opt) (*Client, error) { + return newClient(context, chConfig, discoveryService, connectionProvider, opts, func() error { fmt.Printf("AfterConnect called") return nil @@ -1303,10 +1303,11 @@ var filteredClientProvider = func(context context.Client, chConfig fab.ChannelCf }) } -func newClient(context context.Client, chConfig fab.ChannelCfg, connectionProvider api.ConnectionProvider, opts []options.Opt, afterConnect handler, beforeReconnect handler) (*Client, error) { +func newClient(context context.Client, chConfig fab.ChannelCfg, discoveryService fab.DiscoveryService, connectionProvider api.ConnectionProvider, opts []options.Opt, afterConnect handler, beforeReconnect handler) (*Client, error) { client := New( dispatcher.New( context, chConfig, + discoveryService, connectionProvider, opts..., ), @@ -1319,19 +1320,19 @@ func newClient(context context.Client, chConfig fab.ChannelCfg, connectionProvid return client, err } -func newClientWithMockConn(context context.Client, chConfig fab.ChannelCfg, clientProvider ClientProvider, connOpts ...mockconn.Opt) (*Client, mockconn.Connection, error) { +func newClientWithMockConn(context context.Client, chConfig fab.ChannelCfg, discovery fab.DiscoveryService, clientProvider ClientProvider, connOpts ...mockconn.Opt) (*Client, mockconn.Connection, error) { conn := mockconn.NewMockConnection(connOpts...) - client, _, err := newClientWithMockConnAndOpts(context, chConfig, mockconn.NewProviderFactory().Provider(conn), clientProvider, []options.Opt{}) + client, _, err := newClientWithMockConnAndOpts(context, chConfig, discovery, mockconn.NewProviderFactory().Provider(conn), clientProvider, []options.Opt{}) return client, conn, err } -func newClientWithMockConnAndOpts(context context.Client, chConfig fab.ChannelCfg, connectionProvider api.ConnectionProvider, clientProvider ClientProvider, opts []options.Opt, connOpts ...mockconn.Opt) (*Client, mockconn.Connection, error) { +func newClientWithMockConnAndOpts(context context.Client, chConfig fab.ChannelCfg, discovery fab.DiscoveryService, connectionProvider api.ConnectionProvider, clientProvider ClientProvider, opts []options.Opt, connOpts ...mockconn.Opt) (*Client, mockconn.Connection, error) { var conn mockconn.Connection if connectionProvider == nil { conn = mockconn.NewMockConnection(connOpts...) connectionProvider = mockconn.NewProviderFactory().Provider(conn) } - client, err := clientProvider(context, chConfig, connectionProvider, opts) + client, err := clientProvider(context, chConfig, discovery, connectionProvider, opts) return client, conn, err } diff --git a/pkg/fab/events/client/dispatcher/dispatcher.go b/pkg/fab/events/client/dispatcher/dispatcher.go index fd7b5e1db7..2243e30fa8 100755 --- a/pkg/fab/events/client/dispatcher/dispatcher.go +++ b/pkg/fab/events/client/dispatcher/dispatcher.go @@ -33,10 +33,11 @@ type Dispatcher struct { connection api.Connection connectionRegistration *ConnectionReg connectionProvider api.ConnectionProvider + discoveryService fab.DiscoveryService } // New creates a new dispatcher -func New(context context.Client, chConfig fab.ChannelCfg, connectionProvider api.ConnectionProvider, opts ...options.Opt) *Dispatcher { +func New(context context.Client, chConfig fab.ChannelCfg, discoveryService fab.DiscoveryService, connectionProvider api.ConnectionProvider, opts ...options.Opt) *Dispatcher { params := defaultParams() options.Apply(params, opts) @@ -45,6 +46,7 @@ func New(context context.Client, chConfig fab.ChannelCfg, connectionProvider api params: *params, context: context, chConfig: chConfig, + discoveryService: discoveryService, connectionProvider: connectionProvider, } } @@ -95,13 +97,7 @@ func (ed *Dispatcher) HandleConnectEvent(e esdispatcher.Event) { return } - discoveryService, err := ed.context.DiscoveryProvider().CreateDiscoveryService(ed.chConfig.ID()) - if err != nil { - evt.ErrCh <- nil - return - } - - peers, err := discoveryService.GetPeers() + peers, err := ed.discoveryService.GetPeers() if err != nil { evt.ErrCh <- err return diff --git a/pkg/fab/events/client/dispatcher/dispatcher_test.go b/pkg/fab/events/client/dispatcher/dispatcher_test.go index 4de982e41f..879292e648 100755 --- a/pkg/fab/events/client/dispatcher/dispatcher_test.go +++ b/pkg/fab/events/client/dispatcher/dispatcher_test.go @@ -32,11 +32,11 @@ func TestConnect(t *testing.T) { channelID := "testchannel" dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), clientmocks.NewProviderFactory().Provider( clientmocks.NewMockConnection( clientmocks.WithLedger( @@ -111,11 +111,11 @@ func TestConnectNoPeers(t *testing.T) { channelID := "testchannel" dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(), // Add no peers to discovery service ), - fabmocks.NewMockChannelCfg(channelID), + fabmocks.NewMockChannelCfg(channelID), // Add no peers to discovery service + clientmocks.NewDiscoveryService(), clientmocks.NewProviderFactory().Provider( clientmocks.NewMockConnection( clientmocks.WithLedger( @@ -154,11 +154,11 @@ func TestConnectionEvent(t *testing.T) { channelID := "testchannel" dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), clientmocks.NewProviderFactory().Provider( clientmocks.NewMockConnection( clientmocks.WithLedger( diff --git a/pkg/fab/events/client/mocks/mockdiscovery.go b/pkg/fab/events/client/mocks/mockdiscovery.go index e4aa20e71f..fa3347bbe7 100644 --- a/pkg/fab/events/client/mocks/mockdiscovery.go +++ b/pkg/fab/events/client/mocks/mockdiscovery.go @@ -10,30 +10,18 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" ) -// MockDiscoveryProvider mockcore out the discovery provider -type MockDiscoveryProvider struct { +// MockDiscoveryService is a mock discovery service used for event endpoint discovery +type MockDiscoveryService struct { peers []fab.Peer } -// NewDiscoveryProvider returns a new MockDiscoveryProvider -func NewDiscoveryProvider(peers ...fab.Peer) fab.DiscoveryProvider { - return &MockDiscoveryProvider{ +// NewDiscoveryService returns a new MockDiscoveryService +func NewDiscoveryService(peers ...fab.Peer) fab.DiscoveryService { + return &MockDiscoveryService{ peers: peers, } } -// CreateDiscoveryService returns a new MockDiscoveryService -func (p *MockDiscoveryProvider) CreateDiscoveryService(channelID string) (fab.DiscoveryService, error) { - return &MockDiscoveryService{ - peers: p.peers, - }, nil -} - -// MockDiscoveryService is a mock discovery service used for event endpoint discovery -type MockDiscoveryService struct { - peers []fab.Peer -} - // GetPeers returns a list of discovered peers func (s *MockDiscoveryService) GetPeers() ([]fab.Peer, error) { return s.peers, nil diff --git a/pkg/fab/events/deliverclient/deliverclient.go b/pkg/fab/events/deliverclient/deliverclient.go index 558d61b585..e4058db751 100755 --- a/pkg/fab/events/deliverclient/deliverclient.go +++ b/pkg/fab/events/deliverclient/deliverclient.go @@ -51,17 +51,20 @@ type Client struct { } // New returns a new deliver event client -func New(context fabcontext.Client, chConfig fab.ChannelCfg, opts ...options.Opt) (*Client, error) { +func New(context fabcontext.Client, chConfig fab.ChannelCfg, discoveryService fab.DiscoveryService, opts ...options.Opt) (*Client, error) { params := defaultParams() options.Apply(params, opts) - // Use a context that returns a custom Discovery Provider which - // produces event endpoints containing additional GRPC options. - deliverCtx := newDeliverContext(context) + // Use a custom Discovery Service which wraps the given discovery service + // and produces event endpoints containing additional GRPC options. + discoveryWrapper, err := endpoint.NewEndpointDiscoveryWrapper(context, chConfig.ID(), discoveryService) + if err != nil { + return nil, err + } client := &Client{ Client: *client.New( - dispatcher.New(deliverCtx, chConfig, params.connProvider, opts...), + dispatcher.New(context, chConfig, discoveryWrapper, params.connProvider, opts...), opts..., ), params: *params, @@ -135,20 +138,3 @@ func (c *Client) seekInfo() (*ab.SeekInfo, error) { return nil, errors.Errorf("unsupported seek type:[%s]", c.seekType) } } - -// deliverContext overrides the DiscoveryProvider -type deliverContext struct { - fabcontext.Client -} - -func newDeliverContext(ctx fabcontext.Client) fabcontext.Client { - return &deliverContext{ - Client: ctx, - } -} - -// DiscoveryProvider returns a custom discovery provider which produces -// event endpoints with additional GRPC options -func (ctx *deliverContext) DiscoveryProvider() fab.DiscoveryProvider { - return endpoint.NewDiscoveryProvider(ctx.Client) -} diff --git a/pkg/fab/events/deliverclient/deliverclient_test.go b/pkg/fab/events/deliverclient/deliverclient_test.go index 145c5c45b8..1709a5823e 100755 --- a/pkg/fab/events/deliverclient/deliverclient_test.go +++ b/pkg/fab/events/deliverclient/deliverclient_test.go @@ -44,6 +44,7 @@ func TestOptionsInNewClient(t *testing.T) { client, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), client.WithBlockEvents(), ) if err != nil { @@ -57,6 +58,7 @@ func TestClientConnect(t *testing.T) { eventClient, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), client.WithBlockEvents(), withConnectionProvider( clientmocks.NewProviderFactory().Provider( @@ -185,6 +187,7 @@ func testConnect(t *testing.T, maxConnectAttempts uint, expectedOutcome clientmo eventClient, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), client.WithBlockEvents(), withConnectionProvider( cp.FlakeyProvider( @@ -225,6 +228,7 @@ func testReconnect(t *testing.T, reconnect bool, maxReconnectAttempts uint, expe eventClient, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), client.WithBlockEvents(), withConnectionProvider( cp.FlakeyProvider( @@ -295,6 +299,7 @@ func testReconnectRegistration(t *testing.T, connectResults clientmocks.ConnectA eventClient, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), client.WithBlockEvents(), withConnectionProvider( cp.FlakeyProvider( @@ -450,9 +455,8 @@ func newMockConfig() *mockConfig { } func newMockContext() *fabmocks.MockContext { - ctx := fabmocks.NewMockContextWithCustomDiscovery( + ctx := fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "test1"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ) ctx.SetEndpointConfig(newMockConfig()) return ctx diff --git a/pkg/fab/events/deliverclient/dispatcher/dispatcher.go b/pkg/fab/events/deliverclient/dispatcher/dispatcher.go index 46875b3946..b5240f663d 100755 --- a/pkg/fab/events/deliverclient/dispatcher/dispatcher.go +++ b/pkg/fab/events/deliverclient/dispatcher/dispatcher.go @@ -37,9 +37,9 @@ type Dispatcher struct { } // New returns a new deliver dispatcher -func New(context fabcontext.Client, chConfig fab.ChannelCfg, connectionProvider api.ConnectionProvider, opts ...options.Opt) *Dispatcher { +func New(context fabcontext.Client, chConfig fab.ChannelCfg, discoveryService fab.DiscoveryService, connectionProvider api.ConnectionProvider, opts ...options.Opt) *Dispatcher { return &Dispatcher{ - Dispatcher: *clientdisp.New(context, chConfig, connectionProvider, opts...), + Dispatcher: *clientdisp.New(context, chConfig, discoveryService, connectionProvider, opts...), } } diff --git a/pkg/fab/events/deliverclient/dispatcher/dispatcher_test.go b/pkg/fab/events/deliverclient/dispatcher/dispatcher_test.go index c605bb962d..39d9cd06fb 100755 --- a/pkg/fab/events/deliverclient/dispatcher/dispatcher_test.go +++ b/pkg/fab/events/deliverclient/dispatcher/dispatcher_test.go @@ -34,11 +34,11 @@ func TestSeek(t *testing.T) { channelID := "testchannel" dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), clientmocks.NewProviderFactory().Provider( delivermocks.NewConnection( clientmocks.WithLedger(servicemocks.NewMockLedger(delivermocks.BlockEventFactory, sourceURL)), @@ -95,11 +95,11 @@ func TestUnauthorized(t *testing.T) { channelID := "testchannel" dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), clientmocks.NewProviderFactory().Provider( delivermocks.NewConnection( clientmocks.WithResults( @@ -162,11 +162,11 @@ func TestBlockEvents(t *testing.T) { ledger := servicemocks.NewMockLedger(delivermocks.BlockEventFactory, sourceURL) dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), clientmocks.NewProviderFactory().Provider( delivermocks.NewConnection( clientmocks.WithLedger(ledger), @@ -236,11 +236,11 @@ func TestFilteredBlockEvents(t *testing.T) { ledger := servicemocks.NewMockLedger(delivermocks.FilteredBlockEventFactory, sourceURL) dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), clientmocks.NewProviderFactory().Provider( delivermocks.NewConnection( clientmocks.WithLedger(ledger), diff --git a/pkg/fab/events/endpoint/endpoint_test.go b/pkg/fab/events/endpoint/endpoint_test.go index 103089b8ef..fae96f25de 100644 --- a/pkg/fab/events/endpoint/endpoint_test.go +++ b/pkg/fab/events/endpoint/endpoint_test.go @@ -12,8 +12,10 @@ import ( "time" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" + clientmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/events/client/mocks" fabmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" mspmocks "github.com/hyperledger/fabric-sdk-go/pkg/msp/test/mockmsp" + "github.com/stretchr/testify/assert" ) const ( @@ -69,12 +71,9 @@ func TestDiscoveryProvider(t *testing.T) { expectedNumPeers := len(peers) - discoveryProvider := NewDiscoveryProvider(ctx) + discoveryService, err := NewEndpointDiscoveryWrapper(ctx, "testchannel", clientmocks.NewDiscoveryService(peers...)) + assert.NoErrorf(t, err, "error creating discovery wrapper") - discoveryService, err := discoveryProvider.CreateDiscoveryService("testchannel") - if err != nil { - t.Fatalf("error creating discovery service: %s", err) - } peers, err = discoveryService.GetPeers() if err != nil { t.Fatalf("error getting peers: %s", err) @@ -89,12 +88,9 @@ func TestDiscoveryProviderWithTargetFilter(t *testing.T) { expectedNumPeers := len(peers) - 1 - discoveryProvider := NewDiscoveryProvider(ctx, WithTargetFilter(newMockFilter(p3))) + discoveryService, err := NewEndpointDiscoveryWrapper(ctx, "testchannel", clientmocks.NewDiscoveryService(peers...), WithTargetFilter(newMockFilter(p3))) + assert.NoErrorf(t, err, "error creating discovery wrapper") - discoveryService, err := discoveryProvider.CreateDiscoveryService("testchannel") - if err != nil { - t.Fatalf("error creating discovery service: %s", err) - } peers, err = discoveryService.GetPeers() if err != nil { t.Fatalf("error getting peers: %s", err) @@ -114,12 +110,9 @@ func TestDiscoveryProviderWithEventSource(t *testing.T) { expectedNumPeers := len(peers) - 1 - discoveryProvider := NewDiscoveryProvider(ctx) + discoveryService, err := NewEndpointDiscoveryWrapper(ctx, "testchannel", clientmocks.NewDiscoveryService(peers...)) + assert.NoErrorf(t, err, "error creating discovery wrapper") - discoveryService, err := discoveryProvider.CreateDiscoveryService("testchannel") - if err != nil { - t.Fatalf("error creating discovery service: %s", err) - } peers, err = discoveryService.GetPeers() if err != nil { t.Fatalf("error getting peers: %s", err) @@ -147,11 +140,8 @@ func (c *mockConfig) ChannelPeers(name string) ([]fab.ChannelPeer, bool) { } func newMockContext() *fabmocks.MockContext { - discoveryProvider := fabmocks.NewMockDiscoveryProvider(nil, peers) - - ctx := fabmocks.NewMockContextWithCustomDiscovery( + ctx := fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - discoveryProvider, ) ctx.SetEndpointConfig(newMockConfig()) return ctx diff --git a/pkg/fab/events/endpoint/endpointdiscovery.go b/pkg/fab/events/endpoint/endpointdiscovery.go index 389a4baf43..b89fbcbd4b 100644 --- a/pkg/fab/events/endpoint/endpointdiscovery.go +++ b/pkg/fab/events/endpoint/endpointdiscovery.go @@ -16,71 +16,53 @@ import ( var logger = logging.NewLogger("fabsdk/fab") -// DiscoveryProvider is a wrapper around a discovery provider that -// converts each peer into an EventEndpoint. The EventEndpoint -// provides additional connection options. -type DiscoveryProvider struct { - fab.DiscoveryProvider - ctx context.Client - filter fab.TargetFilter +// DiscoveryWrapper wraps a target discovery service and and adds endpoint data to each +// of the discovered peers. +type DiscoveryWrapper struct { + fab.DiscoveryService + ctx context.Client + chPeers []fab.ChannelPeer + filter fab.TargetFilter } // Opt is a discoveryProvider option -type Opt func(p *DiscoveryProvider) +type Opt func(p *DiscoveryWrapper) // WithTargetFilter applies the target filter to the discovery provider func WithTargetFilter(filter fab.TargetFilter) Opt { - return func(p *DiscoveryProvider) { + return func(p *DiscoveryWrapper) { p.filter = filter } } -// NewDiscoveryProvider returns a new event endpoint discovery provider -func NewDiscoveryProvider(ctx context.Client, opts ...Opt) *DiscoveryProvider { - p := &DiscoveryProvider{ - DiscoveryProvider: ctx.DiscoveryProvider(), - ctx: ctx, - } - - for _, opt := range opts { - opt(p) +// NewEndpointDiscoveryWrapper returns a new event endpoint discovery service +// that wraps a given target discovery service and adds endpoint data to each +// of the discovered peers. +func NewEndpointDiscoveryWrapper(ctx context.Client, channelID string, target fab.DiscoveryService, opts ...Opt) (*DiscoveryWrapper, error) { + chpeers, ok := ctx.EndpointConfig().ChannelPeers(channelID) + if !ok { + return nil, errors.Errorf("unable to get channel peers for channel [%s]", channelID) } - return p -} - -// CreateDiscoveryService creates a new DiscoveryService for the given channel -func (p *DiscoveryProvider) CreateDiscoveryService(channelID string) (fab.DiscoveryService, error) { - target, err := p.DiscoveryProvider.CreateDiscoveryService(channelID) - if err != nil { - return nil, err + s := &DiscoveryWrapper{ + DiscoveryService: target, + chPeers: chpeers, + ctx: ctx, } - if p.filter != nil { - target = discovery.NewDiscoveryFilterService(target, p.filter) + for _, opt := range opts { + opt(s) } - chpeers, ok := p.ctx.EndpointConfig().ChannelPeers(channelID) - if !ok { - return nil, errors.Errorf("unable to get channel peers for channel [%s]", channelID) + if s.filter != nil { + s.DiscoveryService = discovery.NewDiscoveryFilterService(target, s.filter) } - return &discoveryService{ - DiscoveryService: target, - ctx: p.ctx, - channelID: channelID, - chPeers: chpeers, - }, nil -} - -type discoveryService struct { - fab.DiscoveryService - ctx context.Client - channelID string - chPeers []fab.ChannelPeer + return s, nil } -func (s *discoveryService) GetPeers() ([]fab.Peer, error) { +// GetPeers returns the discovered peers +func (s *DiscoveryWrapper) GetPeers() ([]fab.Peer, error) { var eventEndpoints []fab.Peer peers, err := s.DiscoveryService.GetPeers() @@ -122,7 +104,7 @@ func (s *discoveryService) GetPeers() ([]fab.Peer, error) { return eventEndpoints, nil } -func (s *discoveryService) getChannelPeer(url string) *fab.ChannelPeer { +func (s *DiscoveryWrapper) getChannelPeer(url string) *fab.ChannelPeer { for _, chpeer := range s.chPeers { if chpeer.URL == url { return &chpeer diff --git a/pkg/fab/events/eventhubclient/dispatcher/dispatcher.go b/pkg/fab/events/eventhubclient/dispatcher/dispatcher.go index 5576c909db..a30a87ae09 100755 --- a/pkg/fab/events/eventhubclient/dispatcher/dispatcher.go +++ b/pkg/fab/events/eventhubclient/dispatcher/dispatcher.go @@ -36,9 +36,9 @@ type Dispatcher struct { } // New creates a new event hub dispatcher -func New(context fabcontext.Client, chConfig fab.ChannelCfg, connectionProvider api.ConnectionProvider, opts ...options.Opt) *Dispatcher { +func New(context fabcontext.Client, chConfig fab.ChannelCfg, discovery fab.DiscoveryService, connectionProvider api.ConnectionProvider, opts ...options.Opt) *Dispatcher { return &Dispatcher{ - Dispatcher: *clientdisp.New(context, chConfig, connectionProvider, opts...), + Dispatcher: *clientdisp.New(context, chConfig, discovery, connectionProvider, opts...), } } diff --git a/pkg/fab/events/eventhubclient/dispatcher/dispatcher_test.go b/pkg/fab/events/eventhubclient/dispatcher/dispatcher_test.go index 506058f014..3b1c626e35 100755 --- a/pkg/fab/events/eventhubclient/dispatcher/dispatcher_test.go +++ b/pkg/fab/events/eventhubclient/dispatcher/dispatcher_test.go @@ -37,11 +37,11 @@ var ( func TestRegisterInterests(t *testing.T) { channelID := "testchannel" dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(endpoint1, endpoint2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(endpoint1, endpoint2), clientmocks.NewProviderFactory().Provider( ehmocks.NewConnection( clientmocks.WithLedger(servicemocks.NewMockLedger(ehmocks.BlockEventFactory, sourceURL)), @@ -219,11 +219,11 @@ func connectToDispatcher(dispatcherEventch chan<- interface{}, t *testing.T) (ch func newDispatcher(channelID string) *Dispatcher { return New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(endpoint1, endpoint2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(endpoint1, endpoint2), clientmocks.NewProviderFactory().Provider( ehmocks.NewConnection( clientmocks.WithLedger(servicemocks.NewMockLedger(ehmocks.BlockEventFactory, sourceURL)), @@ -239,11 +239,11 @@ func newDispatcher(channelID string) *Dispatcher { func TestTimedOutRegister(t *testing.T) { channelID := "testchannel" dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(endpoint1, endpoint2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(endpoint1, endpoint2), clientmocks.NewProviderFactory().Provider( ehmocks.NewConnection( clientmocks.WithResults( @@ -299,11 +299,11 @@ func TestBlockEvents(t *testing.T) { channelID := "testchannel" ledger := servicemocks.NewMockLedger(ehmocks.BlockEventFactory, sourceURL) dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(endpoint1, endpoint2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(endpoint1, endpoint2), clientmocks.NewProviderFactory().Provider( ehmocks.NewConnection( clientmocks.WithLedger(ledger), @@ -378,11 +378,11 @@ func TestFilteredBlockEvents(t *testing.T) { channelID := "testchannel" ledger := servicemocks.NewMockLedger(ehmocks.FilteredBlockEventFactory, sourceURL) dispatcher := New( - fabmocks.NewMockContextWithCustomDiscovery( + fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(endpoint1, endpoint2), ), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(endpoint1, endpoint2), clientmocks.NewProviderFactory().Provider( ehmocks.NewConnection( clientmocks.WithLedger(ledger), diff --git a/pkg/fab/events/eventhubclient/eventhubclient.go b/pkg/fab/events/eventhubclient/eventhubclient.go index 9decf00140..b59b15a842 100755 --- a/pkg/fab/events/eventhubclient/eventhubclient.go +++ b/pkg/fab/events/eventhubclient/eventhubclient.go @@ -40,7 +40,7 @@ type Client struct { } // New returns a new event hub client -func New(context context.Client, chConfig fab.ChannelCfg, opts ...options.Opt) (*Client, error) { +func New(context context.Client, chConfig fab.ChannelCfg, discovery fab.DiscoveryService, opts ...options.Opt) (*Client, error) { params := defaultParams() // FIXME: Temporarily set the default to block events since Fabric 1.0 does @@ -49,14 +49,19 @@ func New(context context.Client, chConfig fab.ChannelCfg, opts ...options.Opt) ( options.Apply(params, opts) - // Use a context that returns a custom Discovery Provider which - // produces event endpoints containing the event URL and + // Use a custom Discovery Service which wraps the given discovery service + // and produces event endpoints containing the event URL and // additional GRPC options. - ehCtx := newEventHubContext(context) + discoveryWrapper, err := endpoint.NewEndpointDiscoveryWrapper( + context, chConfig.ID(), discovery, + endpoint.WithTargetFilter(newMSPFilter(context.Identifier().MSPID))) + if err != nil { + return nil, err + } client := &Client{ Client: *client.New( - dispatcher.New(ehCtx, chConfig, params.connProvider, opts...), + dispatcher.New(context, chConfig, discoveryWrapper, params.connProvider, opts...), opts..., ), params: *params, @@ -95,25 +100,6 @@ func (c *Client) registerInterests() error { return nil } -// ehContext overrides the DiscoveryProvider by returning -// the event hub discovery provider -type ehContext struct { - context.Client -} - -func newEventHubContext(ctx context.Client) context.Client { - return &ehContext{ - Client: ctx, - } -} - -// DiscoveryProvider returns a custom discovery provider for the event hub -// that provides additional connection options and filters by -// the current MSP ID -func (ctx *ehContext) DiscoveryProvider() fab.DiscoveryProvider { - return endpoint.NewDiscoveryProvider(ctx.Client, endpoint.WithTargetFilter(newMSPFilter(ctx.Identifier().MSPID))) -} - type mspFilter struct { mspID string } diff --git a/pkg/fab/events/eventhubclient/eventhubclient_test.go b/pkg/fab/events/eventhubclient/eventhubclient_test.go index 5ec00a36ac..3e5253cfee 100755 --- a/pkg/fab/events/eventhubclient/eventhubclient_test.go +++ b/pkg/fab/events/eventhubclient/eventhubclient_test.go @@ -47,6 +47,7 @@ func TestOptionsInNewClient(t *testing.T) { client, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), client.WithBlockEvents(), ) if err != nil { @@ -60,6 +61,7 @@ func TestClientConnect(t *testing.T) { eventClient, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), withConnectionProvider( clientmocks.NewProviderFactory().Provider( ehclientmocks.NewConnection( @@ -92,6 +94,7 @@ func TestTimeoutClientConnect(t *testing.T) { eventClient, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), withConnectionProvider( clientmocks.NewProviderFactory().Provider( ehclientmocks.NewConnection( @@ -205,6 +208,7 @@ func testConnect(t *testing.T, maxConnectAttempts uint, expectedOutcome clientmo eventClient, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), client.WithBlockEvents(), withConnectionProvider( clientmocks.NewProviderFactory().FlakeyProvider( @@ -245,6 +249,7 @@ func testReconnect(t *testing.T, reconnect bool, maxReconnectAttempts uint, expe eventClient, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), client.WithBlockEvents(), withConnectionProvider( cp.FlakeyProvider( @@ -295,6 +300,7 @@ func newEventClient(t *testing.T, channelID string, connectResults clientmocks.C eventClient, err := New( newMockContext(), fabmocks.NewMockChannelCfg(channelID), + clientmocks.NewDiscoveryService(peer1, peer2), client.WithBlockEvents(), withConnectionProvider( cp.FlakeyProvider( @@ -486,9 +492,8 @@ func (c *mockEventURLConfig) PeerConfigByURL(url string) (*fab.PeerConfig, error } func newMockContext() *fabmocks.MockContext { - ctx := fabmocks.NewMockContextWithCustomDiscovery( + ctx := fabmocks.NewMockContext( mspmocks.NewMockSigningIdentity("user1", "Org1MSP"), - clientmocks.NewDiscoveryProvider(peer1, peer2), ) config := newMockEventURLConfig() config.setURL(peer1.URL(), eventURL1) diff --git a/pkg/fab/mocks/mockchannel.go b/pkg/fab/mocks/mockchannel.go index 0749e0ec6c..b3c39bd7c5 100644 --- a/pkg/fab/mocks/mockchannel.go +++ b/pkg/fab/mocks/mockchannel.go @@ -18,8 +18,6 @@ import ( //Channel supplies the configuration for channel context client type Channel struct { context.Client - discovery fab.DiscoveryService - selection fab.SelectionService channelService fab.ChannelService channelID string } @@ -29,16 +27,6 @@ func (c *Channel) Providers() context.Client { return c } -//DiscoveryService returns discovery service -func (c *Channel) DiscoveryService() fab.DiscoveryService { - return c.discovery -} - -//SelectionService returns selection service -func (c *Channel) SelectionService() fab.SelectionService { - return c.selection -} - //ChannelService returns channel service func (c *Channel) ChannelService() fab.ChannelService { return c.channelService @@ -72,28 +60,13 @@ func NewMockChannel(channelID string) (*Channel, error) { return nil, errors.WithMessage(err, "failed to create mock channel service") } - peers := []fab.Peer{NewMockPeer("Peer1", "example.com")} - // Set up mock discovery service - mockDiscovery := NewMockDiscoveryProvider(nil, peers) - discoveryService, err := mockDiscovery.CreateDiscoveryService(channelID) - if err != nil { - return nil, errors.WithMessage(err, "failed to create discovery service") - } + peer := NewMockPeer("Peer1", "example.com") - // Set up mock selection service - mockSelection, err := NewMockSelectionProvider(nil, peers) - if err != nil { - return nil, errors.WithMessage(err, "NewMockSelectinProvider failed") - } - selectionService, err := mockSelection.CreateSelectionService("mychannel") - if err != nil { - return nil, errors.WithMessage(err, "failed to create selection service") - } + channelService.(*MockChannelService).SetDiscovery(NewMockDiscoveryService(nil, peer)) + channelService.(*MockChannelService).SetSelection(NewMockSelectionService(nil, peer)) channel := &Channel{ Client: ctx, - selection: selectionService, - discovery: discoveryService, channelService: channelService, channelID: channelID, } diff --git a/pkg/fab/mocks/mockchprovider.go b/pkg/fab/mocks/mockchprovider.go index 080a50b40e..f41c337c0d 100644 --- a/pkg/fab/mocks/mockchprovider.go +++ b/pkg/fab/mocks/mockchprovider.go @@ -17,7 +17,6 @@ import ( // MockChannelProvider holds a mock channel provider. type MockChannelProvider struct { ctx core.Providers - transactor fab.Transactor customChannelService fab.ChannelService } @@ -27,6 +26,8 @@ type MockChannelService struct { channelID string transactor fab.Transactor mockOrderers []string + discovery fab.DiscoveryService + selection fab.SelectionService } // NewMockChannelProvider returns a mock ChannelProvider @@ -38,11 +39,6 @@ func NewMockChannelProvider(ctx core.Providers) (*MockChannelProvider, error) { return &cp, nil } -// SetTransactor sets the default transactor for all mock channel services -func (cp *MockChannelProvider) SetTransactor(transactor fab.Transactor) { - cp.transactor = transactor -} - // ChannelService returns a mock ChannelService func (cp *MockChannelProvider) ChannelService(ctx fab.ClientContext, channelID string) (fab.ChannelService, error) { @@ -53,7 +49,9 @@ func (cp *MockChannelProvider) ChannelService(ctx fab.ClientContext, channelID s cs := MockChannelService{ provider: cp, channelID: channelID, - transactor: cp.transactor, + transactor: &MockTransactor{}, + discovery: NewMockDiscoveryService(nil), + selection: NewMockSelectionService(nil), } return &cs, nil } @@ -100,3 +98,23 @@ func (cs *MockChannelService) Membership() (fab.ChannelMembership, error) { func (cs *MockChannelService) ChannelConfig() (fab.ChannelCfg, error) { return &MockChannelCfg{MockID: cs.channelID, MockOrderers: cs.mockOrderers}, nil } + +// Discovery returns a mock DiscoveryService +func (cs *MockChannelService) Discovery() (fab.DiscoveryService, error) { + return cs.discovery, nil +} + +// SetDiscovery sets the mock DiscoveryService +func (cs *MockChannelService) SetDiscovery(discovery fab.DiscoveryService) { + cs.discovery = discovery +} + +// Selection returns a mock SelectionService +func (cs *MockChannelService) Selection() (fab.SelectionService, error) { + return cs.selection, nil +} + +// SetSelection sets the mock SelectionService +func (cs *MockChannelService) SetSelection(selection fab.SelectionService) { + cs.selection = selection +} diff --git a/pkg/fab/mocks/mockcontext.go b/pkg/fab/mocks/mockcontext.go index 46ae54a3b7..754655e37a 100644 --- a/pkg/fab/mocks/mockcontext.go +++ b/pkg/fab/mocks/mockcontext.go @@ -36,9 +36,7 @@ type MockProviderContext struct { identityManager map[string]msp.IdentityManager privateKey core.Key identity msp.SigningIdentity - discoveryProvider fab.DiscoveryProvider localDiscoveryProvider fab.LocalDiscoveryProvider - selectionProvider fab.SelectionProvider infraProvider fab.InfraProvider channelProvider fab.ChannelProvider } @@ -95,8 +93,6 @@ func NewMockProviderContext(userOpts ...ProviderOption) *MockProviderContext { cryptoSuite: &MockCryptoSuite{}, userStore: &mspmocks.MockUserStore{}, identityManager: im, - discoveryProvider: &MockStaticDiscoveryProvider{}, - selectionProvider: &MockSelectionProvider{}, infraProvider: &MockInfraProvider{}, channelProvider: &MockChannelProvider{}, identity: users.identity, @@ -179,21 +175,11 @@ func (pc *MockProviderContext) Sign(msg []byte) ([]byte, error) { return nil, nil } -//DiscoveryProvider returns discovery provider -func (pc *MockProviderContext) DiscoveryProvider() fab.DiscoveryProvider { - return pc.discoveryProvider -} - //LocalDiscoveryProvider returns a local discovery provider func (pc *MockProviderContext) LocalDiscoveryProvider() fab.LocalDiscoveryProvider { return pc.localDiscoveryProvider } -//SelectionProvider returns selection provider -func (pc *MockProviderContext) SelectionProvider() fab.SelectionProvider { - return pc.selectionProvider -} - //ChannelProvider returns channel provider func (pc *MockProviderContext) ChannelProvider() fab.ChannelProvider { return pc.channelProvider @@ -282,23 +268,10 @@ func (m MockContext) PrivateKey() core.Key { return m.SigningIdentity.PrivateKey() } -// NewMockContextWithCustomDiscovery creates a MockContext consisting of defaults and an identity -func NewMockContextWithCustomDiscovery(ic msp.SigningIdentity, discPvdr fab.DiscoveryProvider) *MockContext { - mockCtx := NewMockProviderContext(WithProviderUser(ic.Identifier().ID, ic.Identifier().MSPID)) - mockCtx.discoveryProvider = discPvdr - ctx := MockContext{ - MockProviderContext: mockCtx, - SigningIdentity: ic, - } - return &ctx -} - // MockChannelContext holds the client context plus channel-specific entities type MockChannelContext struct { *MockContext channelID string - Discovery fab.DiscoveryService - Selection fab.SelectionService Channel fab.ChannelService } @@ -310,16 +283,6 @@ func NewMockChannelContext(context *MockContext, channelID string) *MockChannelC } } -// DiscoveryService returns a discovery service -func (c *MockChannelContext) DiscoveryService() fab.DiscoveryService { - return c.Discovery -} - -// SelectionService returns the selection service -func (c *MockChannelContext) SelectionService() fab.SelectionService { - return c.Selection -} - // ChannelService returns the ChannelService func (c *MockChannelContext) ChannelService() fab.ChannelService { return c.Channel diff --git a/pkg/fab/mocks/mockdiscovery.go b/pkg/fab/mocks/mockdiscovery.go index 15fdc6690d..e40379d8ef 100644 --- a/pkg/fab/mocks/mockdiscovery.go +++ b/pkg/fab/mocks/mockdiscovery.go @@ -7,8 +7,6 @@ SPDX-License-Identifier: Apache-2.0 package mocks import ( - "github.com/pkg/errors" - "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" ) @@ -34,19 +32,6 @@ func NewMockDiscoveryProvider(err error, peers []fab.Peer) *MockStaticDiscoveryP return &MockStaticDiscoveryProvider{Error: err, Peers: peers} } -// CreateDiscoveryService return discovery service for specific channel -func (dp *MockStaticDiscoveryProvider) CreateDiscoveryService(channelID string) (fab.DiscoveryService, error) { - - if dp.customDiscoveryService != nil { - return dp.customDiscoveryService, nil - } - - if channelID == "error" { - return nil, errors.New("Generate error when creating new discovery service") - } - return NewMockDiscoveryService(dp.Error, dp.Peers), nil -} - // CreateLocalDiscoveryService return local discovery service func (dp *MockStaticDiscoveryProvider) CreateLocalDiscoveryService(mspID string) (fab.DiscoveryService, error) { @@ -54,7 +39,7 @@ func (dp *MockStaticDiscoveryProvider) CreateLocalDiscoveryService(mspID string) return dp.customDiscoveryService, nil } - return NewMockDiscoveryService(dp.Error, dp.Peers), nil + return NewMockDiscoveryService(dp.Error, dp.Peers...), nil } //SetCustomDiscoveryService sets custom discoveryService @@ -63,7 +48,7 @@ func (dp *MockStaticDiscoveryProvider) SetCustomDiscoveryService(customDiscovery } //NewMockDiscoveryService returns a new MockStaticDiscoveryService -func NewMockDiscoveryService(err error, peers []fab.Peer) *MockStaticDiscoveryService { +func NewMockDiscoveryService(err error, peers ...fab.Peer) *MockStaticDiscoveryService { return &MockStaticDiscoveryService{Error: err, Peers: peers} } diff --git a/pkg/fab/mocks/mockselection.go b/pkg/fab/mocks/mockselection.go index f904e6189d..847e1b542f 100644 --- a/pkg/fab/mocks/mockselection.go +++ b/pkg/fab/mocks/mockselection.go @@ -11,35 +11,15 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" ) -// MockSelectionProvider implements mock selection provider -type MockSelectionProvider struct { - Error error - Peers []fab.Peer - customSelectionService fab.SelectionService -} - // MockSelectionService implements mock selection service type MockSelectionService struct { Error error Peers []fab.Peer } -// NewMockSelectionProvider returns mock selection provider -func NewMockSelectionProvider(err error, peers []fab.Peer) (*MockSelectionProvider, error) { - return &MockSelectionProvider{Error: err, Peers: peers}, nil -} - -// CreateSelectionService returns mock selection service -func (dp *MockSelectionProvider) CreateSelectionService(channelID string) (fab.SelectionService, error) { - if dp.customSelectionService != nil { - return dp.customSelectionService, nil - } - return &MockSelectionService{Error: dp.Error, Peers: dp.Peers}, nil -} - -// SetCustomSelectionService sets custom selection service unit-test purposes -func (dp *MockSelectionProvider) SetCustomSelectionService(customSelectionService fab.SelectionService) { - dp.customSelectionService = customSelectionService +// NewMockSelectionService returns mock selection service +func NewMockSelectionService(err error, peers ...fab.Peer) *MockSelectionService { + return &MockSelectionService{Error: err, Peers: peers} } // GetEndorsersForChaincode mockcore retrieving endorsing peers diff --git a/pkg/fabsdk/api/factory.go b/pkg/fabsdk/api/factory.go index aa7bf486fc..6c01a37b4a 100644 --- a/pkg/fabsdk/api/factory.go +++ b/pkg/fabsdk/api/factory.go @@ -34,8 +34,6 @@ type MSPProviderFactory interface { // ServiceProviderFactory allows overriding default service providers (such as peer discovery) type ServiceProviderFactory interface { - CreateDiscoveryProvider(config fab.EndpointConfig) (fab.DiscoveryProvider, error) CreateLocalDiscoveryProvider(config fab.EndpointConfig) (fab.LocalDiscoveryProvider, error) - CreateSelectionProvider(config fab.EndpointConfig) (fab.SelectionProvider, error) CreateChannelProvider(config fab.EndpointConfig) (fab.ChannelProvider, error) } diff --git a/pkg/fabsdk/fabsdk.go b/pkg/fabsdk/fabsdk.go index d186e4b0d7..7bc7b90607 100644 --- a/pkg/fabsdk/fabsdk.go +++ b/pkg/fabsdk/fabsdk.go @@ -249,24 +249,12 @@ func initSDK(sdk *FabricSDK, configProvider core.ConfigProvider, opts []Option) return errors.WithMessage(err, "failed to create infra provider") } - // Initialize discovery provider - discoveryProvider, err := sdk.opts.Service.CreateDiscoveryProvider(cfg.endpointConfig) - if err != nil { - return errors.WithMessage(err, "failed to create discovery provider") - } - // Initialize local discovery provider localDiscoveryProvider, err := sdk.opts.Service.CreateLocalDiscoveryProvider(cfg.endpointConfig) if err != nil { return errors.WithMessage(err, "failed to create local discovery provider") } - // Initialize selection provider (for selecting endorsing peers) - selectionProvider, err := sdk.opts.Service.CreateSelectionProvider(cfg.endpointConfig) - if err != nil { - return errors.WithMessage(err, "failed to create selection provider") - } - channelProvider, err := sdk.opts.Service.CreateChannelProvider(cfg.endpointConfig) if err != nil { return errors.WithMessage(err, "failed to create channel provider") @@ -279,9 +267,7 @@ func initSDK(sdk *FabricSDK, configProvider core.ConfigProvider, opts []Option) context.WithCryptoSuite(cryptoSuite), context.WithSigningManager(signingManager), context.WithUserStore(userStore), - context.WithDiscoveryProvider(discoveryProvider), context.WithLocalDiscoveryProvider(localDiscoveryProvider), - context.WithSelectionProvider(selectionProvider), context.WithIdentityManagerProvider(identityManagerProvider), context.WithInfraProvider(infraProvider), context.WithChannelProvider(channelProvider)) @@ -294,13 +280,6 @@ func initSDK(sdk *FabricSDK, configProvider core.ConfigProvider, opts []Option) } } - if pi, ok := discoveryProvider.(providerInit); ok { - err = pi.Initialize(sdk.provider) - if err != nil { - return errors.WithMessage(err, "failed to initialize discovery provider") - } - } - if pi, ok := localDiscoveryProvider.(providerInit); ok { err = pi.Initialize(sdk.provider) if err != nil { @@ -308,13 +287,6 @@ func initSDK(sdk *FabricSDK, configProvider core.ConfigProvider, opts []Option) } } - if pi, ok := selectionProvider.(providerInit); ok { - err = pi.Initialize(sdk.provider) - if err != nil { - return errors.WithMessage(err, "failed to initialize selection provider") - } - } - if pi, ok := channelProvider.(providerInit); ok { err = pi.Initialize(sdk.provider) if err != nil { @@ -327,15 +299,17 @@ func initSDK(sdk *FabricSDK, configProvider core.ConfigProvider, opts []Option) // Close frees up caches and connections being maintained by the SDK func (sdk *FabricSDK) Close() { - if pvdr, ok := sdk.provider.DiscoveryProvider().(closeable); ok { - pvdr.Close() - } + logger.Debugf("Closing SDK... checking if local discovery provider is closable...") if pvdr, ok := sdk.provider.LocalDiscoveryProvider().(closeable); ok { + logger.Debugf("... closing local discovery provider") pvdr.Close() } - if pvdr, ok := sdk.provider.SelectionProvider().(closeable); ok { + logger.Debugf("... checking if channel provider is closable...") + if pvdr, ok := sdk.provider.ChannelProvider().(closeable); ok { + logger.Debugf("... closing channel provider") pvdr.Close() } + logger.Debugf("... closing infra provider") sdk.provider.InfraProvider().Close() } diff --git a/pkg/fabsdk/fabsdk_test.go b/pkg/fabsdk/fabsdk_test.go index 08dcbdbcd6..086a64d4fd 100644 --- a/pkg/fabsdk/fabsdk_test.go +++ b/pkg/fabsdk/fabsdk_test.go @@ -126,9 +126,7 @@ func TestWithServicePkg(t *testing.T) { defer mockCtrl.Finish() factory := mockapisdk.NewMockServiceProviderFactory(mockCtrl) - factory.EXPECT().CreateDiscoveryProvider(gomock.Any()).Return(nil, nil) factory.EXPECT().CreateLocalDiscoveryProvider(gomock.Any()).Return(nil, nil) - factory.EXPECT().CreateSelectionProvider(gomock.Any()).Return(nil, nil) factory.EXPECT().CreateChannelProvider(gomock.Any()).Return(nil, nil) _, err = New(c, WithServicePkg(factory)) diff --git a/pkg/fabsdk/factory/defsvc/svcfactory.go b/pkg/fabsdk/factory/defsvc/svcfactory.go index 050adc02aa..389b79310d 100644 --- a/pkg/fabsdk/factory/defsvc/svcfactory.go +++ b/pkg/fabsdk/factory/defsvc/svcfactory.go @@ -10,7 +10,6 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" discovery "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/staticdiscovery" - selection "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/staticselection" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/provider/chpvdr" ) @@ -23,22 +22,13 @@ func NewProviderFactory() *ProviderFactory { return &f } -// CreateDiscoveryProvider returns a new default implementation of discovery provider -func (f *ProviderFactory) CreateDiscoveryProvider(config fab.EndpointConfig) (fab.DiscoveryProvider, error) { - return discovery.New(config) -} - -// CreateLocalDiscoveryProvider returns a new default implementation of the local discovery provider +// CreateLocalDiscoveryProvider returns a static local discovery provider. This should be changed +// to use the dynamic provider when Fabric 1.1 is no longer supported func (f *ProviderFactory) CreateLocalDiscoveryProvider(config fab.EndpointConfig) (fab.LocalDiscoveryProvider, error) { - return discovery.New(config) + return discovery.NewLocalProvider(config) } // CreateChannelProvider returns a new default implementation of channel provider func (f *ProviderFactory) CreateChannelProvider(config fab.EndpointConfig) (fab.ChannelProvider, error) { return chpvdr.New(config) } - -// CreateSelectionProvider returns a new default implementation of selection service -func (f *ProviderFactory) CreateSelectionProvider(config fab.EndpointConfig) (fab.SelectionProvider, error) { - return selection.New(config) -} diff --git a/pkg/fabsdk/factory/defsvc/svcfactory_test.go b/pkg/fabsdk/factory/defsvc/svcfactory_test.go index f6855b7f5b..fb5e6ffac9 100644 --- a/pkg/fabsdk/factory/defsvc/svcfactory_test.go +++ b/pkg/fabsdk/factory/defsvc/svcfactory_test.go @@ -9,39 +9,22 @@ package defsvc import ( "testing" - discovery "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/staticdiscovery" - selection "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/staticselection" + "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/staticdiscovery" "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" ) -func TestCreateDiscoveryProvider(t *testing.T) { - - factory := NewProviderFactory() - config := mocks.NewMockEndpointConfig() - - dp, err := factory.CreateDiscoveryProvider(config) - if err != nil { - t.Fatalf("Unexpected error creating discovery provider %v", err) - } - - _, ok := dp.(*discovery.DiscoveryProvider) - if !ok { - t.Fatalf("Unexpected discovery provider created") - } -} - -func TestCreateSelectionProvider(t *testing.T) { +func TestCreateLocalDiscoveryProvider(t *testing.T) { factory := NewProviderFactory() config := mocks.NewMockEndpointConfig() - dp, err := factory.CreateSelectionProvider(config) + dp, err := factory.CreateLocalDiscoveryProvider(config) if err != nil { - t.Fatalf("Unexpected error creating discovery provider %v", err) + t.Fatalf("Unexpected error creating local discovery provider %v", err) } - _, ok := dp.(*selection.SelectionProvider) + _, ok := dp.(*staticdiscovery.LocalProvider) if !ok { - t.Fatalf("Unexpected selection provider created") + t.Fatalf("Unexpected local discovery provider created") } } diff --git a/pkg/fabsdk/provider/chpvdr/cachekey.go b/pkg/fabsdk/provider/chpvdr/cachekey.go index 0f86b92df1..a42059c500 100644 --- a/pkg/fabsdk/provider/chpvdr/cachekey.go +++ b/pkg/fabsdk/provider/chpvdr/cachekey.go @@ -14,34 +14,47 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" ) -// CacheKey holds a key for the provider cache -type CacheKey struct { - key string - context fab.ClientContext - chConfig fab.ChannelCfg - opts []options.Opt +// cacheKey holds a key for the provider cache +type cacheKey struct { + key string + context fab.ClientContext + channelConfig fab.ChannelCfg } -type params struct { - permitBlockEvents bool -} +// newCacheKey returns a new cacheKey +func newCacheKey(ctx fab.ClientContext, chConfig fab.ChannelCfg, opts ...options.Opt) (*cacheKey, error) { + identity, err := ctx.Serialize() + if err != nil { + return nil, err + } -func defaultParams() *params { - return ¶ms{} + params := defaultParams() + options.Apply(params, opts) + + h := sha256.New() + h.Write(append(identity, []byte(params.getOptKey())...)) // nolint + hash := h.Sum([]byte(chConfig.ID())) + + return &cacheKey{ + key: string(hash), + context: ctx, + channelConfig: chConfig, + }, nil } -func (p *params) PermitBlockEvents() { - p.permitBlockEvents = true +// String returns the key as a string +func (k *cacheKey) String() string { + return k.key } -func (p *params) getOptKey() string { - // Construct opts portion - optKey := "blockEvents:" + strconv.FormatBool(p.permitBlockEvents) - return optKey +// cacheKey holds a key for the provider cache +type eventCacheKey struct { + cacheKey + opts []options.Opt } -// NewCacheKey returns a new CacheKey -func NewCacheKey(ctx fab.ClientContext, chConfig fab.ChannelCfg, opts ...options.Opt) (*CacheKey, error) { +// newEventCacheKey returns a new eventCacheKey +func newEventCacheKey(ctx fab.ClientContext, chConfig fab.ChannelCfg, opts ...options.Opt) (*eventCacheKey, error) { identity, err := ctx.Serialize() if err != nil { return nil, err @@ -54,30 +67,35 @@ func NewCacheKey(ctx fab.ClientContext, chConfig fab.ChannelCfg, opts ...options h.Write(append(identity, []byte(params.getOptKey())...)) // nolint hash := h.Sum([]byte(chConfig.ID())) - return &CacheKey{ - key: string(hash), - context: ctx, - chConfig: chConfig, - opts: opts, + return &eventCacheKey{ + cacheKey: cacheKey{ + key: string(hash), + context: ctx, + channelConfig: chConfig, + }, + opts: opts, }, nil } -// String returns the key as a string -func (k *CacheKey) String() string { - return k.key +// Opts returns the options to use for creating events service +func (k *eventCacheKey) Opts() []options.Opt { + return k.opts } -// Context returns the Context -func (k *CacheKey) Context() fab.ClientContext { - return k.context +type params struct { + permitBlockEvents bool } -// ChannelConfig returns the channel configuration -func (k *CacheKey) ChannelConfig() fab.ChannelCfg { - return k.chConfig +func defaultParams() *params { + return ¶ms{} } -// Opts returns the options to use for creating events service -func (k *CacheKey) Opts() []options.Opt { - return k.opts +func (p *params) PermitBlockEvents() { + p.permitBlockEvents = true +} + +func (p *params) getOptKey() string { + // Construct opts portion + optKey := "blockEvents:" + strconv.FormatBool(p.permitBlockEvents) + return optKey } diff --git a/pkg/fabsdk/provider/chpvdr/chprovider.go b/pkg/fabsdk/provider/chpvdr/chprovider.go index e93189e1a0..3ed441ca5c 100644 --- a/pkg/fabsdk/provider/chpvdr/chprovider.go +++ b/pkg/fabsdk/provider/chpvdr/chprovider.go @@ -9,6 +9,9 @@ package chpvdr import ( reqContext "context" + "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/staticselection" + + "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/staticdiscovery" "github.com/hyperledger/fabric-sdk-go/pkg/common/logging" "github.com/hyperledger/fabric-sdk-go/pkg/common/options" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" @@ -34,10 +37,12 @@ type cache interface { // TODO: add listener for channel config changes. Upon channel config change, // underlying channel services need to recreate their channel clients. type ChannelProvider struct { - providerContext context.Providers - eventServiceCache cache - chCfgCache cache - membershipCache cache + providerContext context.Providers + eventServiceCache cache + discoveryServiceCache cache + selectionServiceCache cache + chCfgCache cache + membershipCache cache } // New creates a ChannelProvider based on a context @@ -46,24 +51,40 @@ func New(config fab.EndpointConfig) (*ChannelProvider, error) { chConfigRefresh := config.Timeout(fab.ChannelConfigRefresh) membershipRefresh := config.Timeout(fab.ChannelMembershipRefresh) - eventServiceCache := lazycache.New( + cp := ChannelProvider{ + chCfgCache: chconfig.NewRefCache(chConfigRefresh), + membershipCache: membership.NewRefCache(membershipRefresh), + } + + cp.discoveryServiceCache = lazycache.New( + "Discovery_Service_Cache", + func(key lazycache.Key) (interface{}, error) { + ck := key.(*cacheKey) + return cp.createDiscoveryService(ck.context, ck.channelConfig) + }, + ) + + cp.selectionServiceCache = lazycache.New( + "Selection_Service_Cache", + func(key lazycache.Key) (interface{}, error) { + ck := key.(*cacheKey) + return cp.createSelectionService(ck.context, ck.channelConfig) + }, + ) + + cp.eventServiceCache = lazycache.New( "Event_Service_Cache", func(key lazycache.Key) (interface{}, error) { - ck := key.(cacheKey) + ck := key.(*eventCacheKey) return NewEventClientRef( eventIdleTime, func() (fab.EventClient, error) { - return getEventClient(ck.Context(), ck.ChannelConfig(), ck.Opts()...) + return cp.createEventClient(ck.context, ck.channelConfig, ck.opts...) }, ), nil }, ) - cp := ChannelProvider{ - eventServiceCache: eventServiceCache, - chCfgCache: chconfig.NewRefCache(chConfigRefresh), - membershipCache: membership.NewRefCache(membershipRefresh), - } return &cp, nil } @@ -83,6 +104,12 @@ func (cp *ChannelProvider) Close() { logger.Debug("Closing channel configuration cache...") cp.chCfgCache.Close() + + logger.Debug("Closing selection service cache...") + cp.selectionServiceCache.Close() + + logger.Debug("Closing discovery service cache...") + cp.discoveryServiceCache.Close() } // ChannelService creates a ChannelService for an identity @@ -96,6 +123,99 @@ func (cp *ChannelProvider) ChannelService(ctx fab.ClientContext, channelID strin return &cs, nil } +func (cp *ChannelProvider) createEventClient(ctx context.Client, chConfig fab.ChannelCfg, opts ...options.Opt) (fab.EventClient, error) { + useDeliver, err := useDeliverEvents(ctx, chConfig) + if err != nil { + return nil, err + } + + discovery, err := cp.getDiscoveryService(ctx, chConfig.ID()) + if err != nil { + return nil, errors.WithMessage(err, "could not get discovery service") + } + + if useDeliver { + logger.Debugf("Using deliver events for channel [%s]", chConfig.ID()) + return deliverclient.New(ctx, chConfig, discovery, opts...) + } + + logger.Debugf("Using event hub events for channel [%s]", chConfig.ID()) + return eventhubclient.New(ctx, chConfig, discovery, opts...) +} + +func (cp *ChannelProvider) createDiscoveryService(ctx context.Client, chConfig fab.ChannelCfg) (fab.DiscoveryService, error) { + return staticdiscovery.NewService(ctx.EndpointConfig(), ctx.InfraProvider(), chConfig.ID()) +} + +func (cp *ChannelProvider) getDiscoveryService(context fab.ClientContext, channelID string) (fab.DiscoveryService, error) { + chnlCfg, err := cp.channelConfig(context, channelID) + if err != nil { + return nil, err + } + key, err := newCacheKey(context, chnlCfg) + if err != nil { + return nil, err + } + discoveryService, err := cp.discoveryServiceCache.Get(key) + if err != nil { + return nil, err + } + return discoveryService.(fab.DiscoveryService), nil +} + +func (cp *ChannelProvider) createSelectionService(ctx context.Client, chConfig fab.ChannelCfg) (fab.SelectionService, error) { + discovery, err := cp.getDiscoveryService(ctx, chConfig.ID()) + if err != nil { + return nil, err + } + return staticselection.NewService(discovery) +} + +func (cp *ChannelProvider) getSelectionService(context fab.ClientContext, channelID string) (fab.SelectionService, error) { + chnlCfg, err := cp.channelConfig(context, channelID) + if err != nil { + return nil, err + } + key, err := newCacheKey(context, chnlCfg) + if err != nil { + return nil, err + } + selectionService, err := cp.selectionServiceCache.Get(key) + if err != nil { + return nil, err + } + return selectionService.(fab.SelectionService), nil +} + +func (cp *ChannelProvider) channelConfig(context fab.ClientContext, channelID string) (fab.ChannelCfg, error) { + if channelID == "" { + // System channel + return chconfig.NewChannelCfg(""), nil + } + chCfgRef, err := cp.loadChannelCfgRef(context, channelID) + if err != nil { + return nil, err + } + chCfg, err := chCfgRef.Get() + if err != nil { + return nil, errors.WithMessage(err, "could not get chConfig cache reference") + } + return chCfg.(fab.ChannelCfg), nil +} + +func (cp *ChannelProvider) loadChannelCfgRef(context fab.ClientContext, channelID string) (*chconfig.Ref, error) { + key, err := chconfig.NewCacheKey(context, func(string) (fab.ChannelConfig, error) { return chconfig.New(channelID) }, channelID) + if err != nil { + return nil, err + } + c, err := cp.chCfgCache.Get(key) + if err != nil { + return nil, err + } + + return c.(*chconfig.Ref), nil +} + // ChannelService provides Channel clients and maintains contexts for them. // the identity context is used type ChannelService struct { @@ -115,7 +235,7 @@ func (cs *ChannelService) EventService(opts ...options.Opt) (fab.EventService, e if err != nil { return nil, err } - key, err := NewCacheKey(cs.context, chnlCfg, opts...) + key, err := newEventCacheKey(cs.context, chnlCfg, opts...) if err != nil { return nil, err } @@ -149,19 +269,7 @@ func (cs *ChannelService) Membership() (fab.ChannelMembership, error) { // ChannelConfig returns the channel config for this channel func (cs *ChannelService) ChannelConfig() (fab.ChannelCfg, error) { - if cs.channelID == "" { - // System channel - return chconfig.NewChannelCfg(""), nil - } - chCfgRef, err := cs.loadChannelCfgRef() - if err != nil { - return nil, err - } - chCfg, err := chCfgRef.Get() - if err != nil { - return nil, errors.WithMessage(err, "could not get chConfig cache reference") - } - return chCfg.(fab.ChannelCfg), nil + return cs.provider.channelConfig(cs.context, cs.channelID) } // Transactor returns the transactor @@ -173,32 +281,18 @@ func (cs *ChannelService) Transactor(reqCtx reqContext.Context) (fab.Transactor, return channelImpl.NewTransactor(reqCtx, cfg) } -func (cs *ChannelService) loadChannelCfgRef() (*chconfig.Ref, error) { - key, err := chconfig.NewCacheKey(cs.context, func(string) (fab.ChannelConfig, error) { return cs.Config() }, cs.channelID) - if err != nil { - return nil, err - } - c, err := cs.provider.chCfgCache.Get(key) - if err != nil { - return nil, err - } - - return c.(*chconfig.Ref), nil +// Discovery returns a DiscoveryService for the given channel +func (cs *ChannelService) Discovery() (fab.DiscoveryService, error) { + return cs.provider.getDiscoveryService(cs.context, cs.channelID) } -func getEventClient(ctx context.Client, chConfig fab.ChannelCfg, opts ...options.Opt) (fab.EventClient, error) { - useDeliver, err := useDeliverEvents(ctx, chConfig) - if err != nil { - return nil, err - } - - if useDeliver { - logger.Debugf("Using deliver events for channel [%s]", chConfig.ID()) - return deliverclient.New(ctx, chConfig, opts...) - } +// Selection returns a SelectionService for the given channel +func (cs *ChannelService) Selection() (fab.SelectionService, error) { + return cs.provider.getSelectionService(cs.context, cs.channelID) +} - logger.Debugf("Using event hub events for channel [%s]", chConfig.ID()) - return eventhubclient.New(ctx, chConfig, opts...) +func (cs *ChannelService) loadChannelCfgRef() (*chconfig.Ref, error) { + return cs.provider.loadChannelCfgRef(cs.context, cs.channelID) } func useDeliverEvents(ctx context.Client, chConfig fab.ChannelCfg) (bool, error) { @@ -214,10 +308,3 @@ func useDeliverEvents(ctx context.Client, chConfig fab.ChannelCfg) (bool, error) return false, errors.Errorf("unsupported event service type: %d", ctx.EndpointConfig().EventServiceType()) } } - -type cacheKey interface { - lazycache.Key - Context() fab.ClientContext - ChannelConfig() fab.ChannelCfg - Opts() []options.Opt -} diff --git a/pkg/fabsdk/provider/chpvdr/chprovider_test.go b/pkg/fabsdk/provider/chpvdr/chprovider_test.go index d71d45399f..7ca08b8e50 100644 --- a/pkg/fabsdk/provider/chpvdr/chprovider_test.go +++ b/pkg/fabsdk/provider/chpvdr/chprovider_test.go @@ -11,6 +11,9 @@ package chpvdr import ( "testing" + "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/staticdiscovery" + "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/staticselection" + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp" @@ -18,6 +21,7 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" mspmocks "github.com/hyperledger/fabric-sdk-go/pkg/msp/test/mockmsp" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type mockClientContext struct { @@ -47,13 +51,13 @@ func TestBasicValidChannel(t *testing.T) { mockChConfigCache.Put(chconfig.NewChannelCfg("mychannel")) cp.chCfgCache = mockChConfigCache - channelService, err := cp.ChannelService(clientCtx, "mychannel") + // System channel + channelService, err := cp.ChannelService(clientCtx, "") if err != nil { t.Fatalf("Unexpected error creating Channel Service: %v", err) } - // System channel - channelService, err = cp.ChannelService(clientCtx, "") + channelService, err = cp.ChannelService(clientCtx, "mychannel") if err != nil { t.Fatalf("Unexpected error creating Channel Service: %v", err) } @@ -69,6 +73,23 @@ func TestBasicValidChannel(t *testing.T) { channelConfig, err := channelService.ChannelConfig() assert.Nil(t, err) assert.NotNil(t, channelConfig) + assert.NotEmptyf(t, channelConfig.ID(), "Got empty channel ID from channel config") + + eventService, err := channelService.EventService() + require.NoError(t, err) + require.NotNil(t, eventService) + + discovery, err := channelService.Discovery() + require.NoError(t, err) + require.NotNil(t, discovery) + _, ok := discovery.(*staticdiscovery.DiscoveryService) + assert.Truef(t, ok, "Expecting discovery to be Static") + + selection, err := channelService.Selection() + require.NoError(t, err) + require.NotNil(t, selection) + _, ok = selection.(*staticselection.SelectionService) + assert.Truef(t, ok, "Expecting selection to be Static") } func TestResolveEventServiceType(t *testing.T) { diff --git a/pkg/fabsdk/test/mocksdkapi/mocksdkapi.gen.go b/pkg/fabsdk/test/mocksdkapi/mocksdkapi.gen.go index 513c712071..12033e9b3d 100644 --- a/pkg/fabsdk/test/mocksdkapi/mocksdkapi.gen.go +++ b/pkg/fabsdk/test/mocksdkapi/mocksdkapi.gen.go @@ -160,19 +160,6 @@ func (mr *MockServiceProviderFactoryMockRecorder) CreateChannelProvider(arg0 int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateChannelProvider", reflect.TypeOf((*MockServiceProviderFactory)(nil).CreateChannelProvider), arg0) } -// CreateDiscoveryProvider mocks base method -func (m *MockServiceProviderFactory) CreateDiscoveryProvider(arg0 fab.EndpointConfig) (fab.DiscoveryProvider, error) { - ret := m.ctrl.Call(m, "CreateDiscoveryProvider", arg0) - ret0, _ := ret[0].(fab.DiscoveryProvider) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateDiscoveryProvider indicates an expected call of CreateDiscoveryProvider -func (mr *MockServiceProviderFactoryMockRecorder) CreateDiscoveryProvider(arg0 interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDiscoveryProvider", reflect.TypeOf((*MockServiceProviderFactory)(nil).CreateDiscoveryProvider), arg0) -} - // CreateLocalDiscoveryProvider mocks base method func (m *MockServiceProviderFactory) CreateLocalDiscoveryProvider(arg0 fab.EndpointConfig) (fab.LocalDiscoveryProvider, error) { ret := m.ctrl.Call(m, "CreateLocalDiscoveryProvider", arg0) @@ -185,16 +172,3 @@ func (m *MockServiceProviderFactory) CreateLocalDiscoveryProvider(arg0 fab.Endpo func (mr *MockServiceProviderFactoryMockRecorder) CreateLocalDiscoveryProvider(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocalDiscoveryProvider", reflect.TypeOf((*MockServiceProviderFactory)(nil).CreateLocalDiscoveryProvider), arg0) } - -// CreateSelectionProvider mocks base method -func (m *MockServiceProviderFactory) CreateSelectionProvider(arg0 fab.EndpointConfig) (fab.SelectionProvider, error) { - ret := m.ctrl.Call(m, "CreateSelectionProvider", arg0) - ret0, _ := ret[0].(fab.SelectionProvider) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateSelectionProvider indicates an expected call of CreateSelectionProvider -func (mr *MockServiceProviderFactoryMockRecorder) CreateSelectionProvider(arg0 interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSelectionProvider", reflect.TypeOf((*MockServiceProviderFactory)(nil).CreateSelectionProvider), arg0) -} diff --git a/test/fixtures/config/config_test.yaml b/test/fixtures/config/config_test.yaml index 8443e0e14b..97ff7d48d3 100755 --- a/test/fixtures/config/config_test.yaml +++ b/test/fixtures/config/config_test.yaml @@ -64,6 +64,8 @@ client: # eventServiceIdle: 2m # channelConfig: 30m # channelMembership: 30s +# discovery: 10s +# selection: 10m # Root of the MSP directories with keys and certs. cryptoconfig: @@ -174,6 +176,12 @@ channels: ledgerQuery: true eventSource: true + peer1.org1.example.com: + endorsingPeer: true + chaincodeQuery: true + ledgerQuery: true + eventSource: true + peer0.org2.example.com: endorsingPeer: true chaincodeQuery: true @@ -212,6 +220,7 @@ organizations: peers: - peer0.org1.example.com + - peer1.org1.example.com # [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based # network. Typically certificates provisioning is done in a separate process outside of the @@ -301,6 +310,28 @@ peers: # Certificate location absolute path path: ${GOPATH}/src/github.com/hyperledger/fabric-sdk-go/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem + peer1.org1.example.com: + # this URL is used to send endorsement and query requests + url: peer1.org1.example.com:7151 + # eventUrl is only needed when using eventhub (default is delivery service) + eventUrl: peer1.org1.example.com:7153 + + grpcOptions: + ssl-target-name-override: peer1.org1.example.com + # These parameters should be set in coordination with the keepalive policy on the server, + # as incompatible settings can result in closing of connection. + # When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled + keep-alive-time: 0s + keep-alive-timeout: 20s + keep-alive-permit: false + fail-fast: false + # allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs + allow-insecure: false + + tlsCACerts: + # Certificate location absolute path + path: ${GOPATH}/src/github.com/hyperledger/fabric-sdk-go/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem + peer0.org2.example.com: url: peer0.org2.example.com:8051 # eventUrl is only needed when using eventhub (default is delivery service) @@ -375,7 +406,18 @@ certificateAuthorities: # SubstitutionExp for the fields eventUrl and gprcOptions.ssl-target-name-override respectively # In any case mappedHost's config will be used, so mapped host cannot be empty, if entityMatchers are used #entityMatchers: -# peer: +entityMatchers: + peer: + - pattern: (\w+).org1.example.com:(\d+) + urlSubstitutionExp: $1.org1.example.com:$2 + sslTargetOverrideUrlSubstitutionExp: $1.org1.example.com + mappedHost: peer0.org1.example.com + + - pattern: (\w+).org2.example.com:(\d+) + urlSubstitutionExp: $1.org2.example.com:$2 + sslTargetOverrideUrlSubstitutionExp: $1.org2.example.com + mappedHost: peer0.org2.example.com + # - pattern: (\w+).org1.example.(\w+) # urlSubstitutionExp: peer0.org1.example.com:7051 # eventUrlSubstitutionExp: peer0.org1.example.com:7053 diff --git a/test/integration/e2e/configless/endpointconfig_override_test.go b/test/integration/e2e/configless/endpointconfig_override_test.go index c409823378..fb66dcb838 100644 --- a/test/integration/e2e/configless/endpointconfig_override_test.go +++ b/test/integration/e2e/configless/endpointconfig_override_test.go @@ -273,6 +273,7 @@ var defaultTypes = map[fab.TimeoutType]time.Duration{ fab.ChannelConfigRefresh: time.Minute * 90, fab.ChannelMembershipRefresh: time.Second * 60, fab.DiscoveryServiceRefresh: time.Second * 10, + fab.SelectionServiceRefresh: time.Minute * 15, // EXPERIMENTAL - do we need this to be configurable? fab.CacheSweepInterval: time.Second * 15, } @@ -475,6 +476,11 @@ func (m *examplePeerConfig) PeerConfig(nameOrURL string) (*fab.PeerConfig, bool) return &pcfg, true } + i := strings.Index(nameOrURL, ":") + if i > 0 { + return m.PeerConfig(nameOrURL[0:i]) + } + return nil, false } diff --git a/test/integration/orgs/multiple_orgs_minconfig_test.go b/test/integration/orgs/multiple_orgs_minconfig_test.go index 405d05057d..d69f8ce8a1 100644 --- a/test/integration/orgs/multiple_orgs_minconfig_test.go +++ b/test/integration/orgs/multiple_orgs_minconfig_test.go @@ -13,10 +13,12 @@ import ( "time" "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/dynamicdiscovery" + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" "github.com/hyperledger/fabric-sdk-go/pkg/core/config" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/factory/defsvc" + "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/provider/chpvdr" "github.com/hyperledger/fabric-sdk-go/test/integration" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -48,15 +50,13 @@ func TestOrgsEndToEndWithBootstrapConfigs(t *testing.T) { org1AdminClientContext: sdk.Context(fabsdk.WithUser(org1AdminUser), fabsdk.WithOrg(org1)), org2AdminClientContext: sdk.Context(fabsdk.WithUser(org2AdminUser), fabsdk.WithOrg(org2)), ccName: bootStrapCC, + ccVersion: "0", } // create channel and join orderer/orgs peers to it if was not done already setupClientContextsAndChannel(t, sdk, &mc) - // wait some time to allow the gossip to propagate the peers discovery - time.Sleep(10 * time.Second) - - testDynamicDiscovery(t, sdk) + testDynamicDiscovery(t, sdk, &mc) // now run the same test as multiple_orgs_test.go to make sure it works with bootstrap config.. @@ -68,30 +68,46 @@ func TestOrgsEndToEndWithBootstrapConfigs(t *testing.T) { verifyWithOrg1(t, sdk, expectedValue, mc.ccName) } -func testDynamicDiscovery(t *testing.T, sdk *fabsdk.FabricSDK) { +func testDynamicDiscovery(t *testing.T, sdk *fabsdk.FabricSDK, mc *multiorgContext) { + peersList := discoverLocalPeers(t, sdk, mc.org1AdminClientContext, 2) + assert.Equal(t, 2, len(peersList), "Expected exactly 2 peers as per %s's channel and %s's org configs", channelID, org1) + peersList = discoverLocalPeers(t, sdk, mc.org2AdminClientContext, 1) + assert.Equal(t, 1, len(peersList), "Expected exactly 1 peer as per %s's channel and %s's org configs", channelID, org2) + // example discovering the peers from the bootstap peer // there should be three peers returned from discovery: // 1 org1 anchor peer (peer0.org1.example.com) // 1 discovered peer (not in config: peer1.org1.example.com) // 1 org2 anchor peer (peer0.org2.example.com) - peersList := discoverPeers(t, sdk) + peersList = discoverPeers(t, sdk) assert.Equal(t, 3, len(peersList), "Expected exactly 3 peers as per %s's channel and %s's org configs", channelID, org2) } func discoverPeers(t *testing.T, sdk *fabsdk.FabricSDK) []fab.Peer { - // any user from the network can access the discovery service, user org2User is selected for the test. - chProvider := sdk.ChannelContext(channelID, fabsdk.WithUser(org2User), fabsdk.WithOrg(org2)) + // any user from the network can access the discovery service, user org1User is selected for the test. + chProvider := sdk.ChannelContext(channelID, fabsdk.WithUser(org1User), fabsdk.WithOrg(org1)) chCtx, err := chProvider() require.NoError(t, err, "Error creating channel context") chCtx.ChannelService() - peers, err := chCtx.DiscoveryService().GetPeers() - require.NoErrorf(t, err, "Error getting peers for channel [%s]", channelID) - require.NotEmptyf(t, peers, "No peers were found for channel [%s]", channelID) - - t.Logf("Peers of channel [%s]:", channelID) - for i, p := range peers { - t.Logf("%d- [%s] - MSP [%s]", i, p.URL(), p.MSPID()) + discovery, err := chCtx.ChannelService().Discovery() + require.NoErrorf(t, err, "Error getting discovery service for channel [%s]", channelID) + + var peers []fab.Peer + for i := 0; i < 10; i++ { + peers, err = discovery.GetPeers() + require.NoErrorf(t, err, "Error getting peers for channel [%s]", channelID) + + t.Logf("Peers of channel [%s]:", channelID) + for i, p := range peers { + t.Logf("%d- [%s] - MSP [%s]", i, p.URL(), p.MSPID()) + } + if len(peers) >= 3 { + break + } + + // wait some time to allow the gossip to propagate the peers discovery + time.Sleep(3 * time.Second) } return peers } @@ -101,12 +117,82 @@ type DynamicDiscoveryProviderFactory struct { defsvc.ProviderFactory } -// CreateDiscoveryProvider returns a new dynamic discovery provider -func (f *DynamicDiscoveryProviderFactory) CreateDiscoveryProvider(config fab.EndpointConfig) (fab.DiscoveryProvider, error) { - return dynamicdiscovery.New(config), nil -} - // CreateLocalDiscoveryProvider returns a new local dynamic discovery provider func (f *DynamicDiscoveryProviderFactory) CreateLocalDiscoveryProvider(config fab.EndpointConfig) (fab.LocalDiscoveryProvider, error) { - return dynamicdiscovery.New(config), nil + return dynamicdiscovery.NewLocalProvider(config), nil +} + +// CreateChannelProvider returns a new default implementation of channel provider +func (f *DynamicDiscoveryProviderFactory) CreateChannelProvider(config fab.EndpointConfig) (fab.ChannelProvider, error) { + chProvider, err := chpvdr.New(config) + if err != nil { + return nil, err + } + return &channelProvider{ + ChannelProvider: chProvider, + services: make(map[string]*dynamicdiscovery.ChannelService), + }, nil +} + +type channelProvider struct { + fab.ChannelProvider + services map[string]*dynamicdiscovery.ChannelService +} + +type initializer interface { + Initialize(providers context.Providers) error +} + +// Initialize sets the provider context +func (cp *channelProvider) Initialize(providers context.Providers) error { + init, ok := cp.ChannelProvider.(initializer) + if ok { + init.Initialize(providers) + } + return nil +} + +type channelService struct { + fab.ChannelService + discovery fab.DiscoveryService +} + +type closable interface { + Close() +} + +// Close frees resources and caches. +func (cp *channelProvider) Close() { + if c, ok := cp.ChannelProvider.(closable); ok { + c.Close() + } + for _, discovery := range cp.services { + discovery.Close() + } +} + +// ChannelService creates a ChannelService for an identity +func (cp *channelProvider) ChannelService(ctx fab.ClientContext, channelID string) (fab.ChannelService, error) { + chService, err := cp.ChannelProvider.ChannelService(ctx, channelID) + if err != nil { + return nil, err + } + + discovery, ok := cp.services[channelID] + if !ok { + discovery, err = dynamicdiscovery.NewChannelService(ctx, channelID) + if err != nil { + return nil, err + } + cp.services[channelID] = discovery + } + + return &channelService{ + ChannelService: chService, + discovery: discovery, + }, nil +} + +func (cs *channelService) Discovery() (fab.DiscoveryService, error) { + return cs.discovery, nil } diff --git a/test/integration/orgs/multiple_orgs_test.go b/test/integration/orgs/multiple_orgs_test.go index 02dfa65d00..bb1d71bdc8 100644 --- a/test/integration/orgs/multiple_orgs_test.go +++ b/test/integration/orgs/multiple_orgs_test.go @@ -23,25 +23,22 @@ import ( packager "github.com/hyperledger/fabric-sdk-go/pkg/fab/ccpackager/gopackager" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" "github.com/pkg/errors" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/hyperledger/fabric-sdk-go/pkg/client/ledger" "github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" - "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/factory/defsvc" "github.com/hyperledger/fabric-sdk-go/test/integration" "github.com/hyperledger/fabric-sdk-go/test/metadata" - selection "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/dynamicselection" - "os" "github.com/hyperledger/fabric-sdk-go/pkg/client/channel" mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp" "github.com/hyperledger/fabric-sdk-go/pkg/fab/resource" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/cauthdsl" - "github.com/stretchr/testify/assert" ) const ( @@ -81,6 +78,7 @@ type multiorgContext struct { org1ResMgmt *resmgmt.Client org2ResMgmt *resmgmt.Client ccName string + ccVersion string } func TestMain(m *testing.M) { @@ -140,8 +138,14 @@ func TestOrgsEndToEnd(t *testing.T) { org1AdminClientContext: sdk.Context(fabsdk.WithUser(org1AdminUser), fabsdk.WithOrg(org1)), org2AdminClientContext: sdk.Context(fabsdk.WithUser(org2AdminUser), fabsdk.WithOrg(org2)), ccName: exampleCC, // basic multi orgs test uses exampleCC for testing + ccVersion: "0", } + peersList := discoverLocalPeers(t, sdk, mc.org1AdminClientContext, 2) + assert.Equal(t, 2, len(peersList), "Expected exactly 2 peers for MSP [%s]", org1) + peersList = discoverLocalPeers(t, sdk, mc.org2AdminClientContext, 1) + assert.Equal(t, 1, len(peersList), "Expected exactly 1 peer for MSP [%s]", org2) + expectedValue := testWithOrg1(t, sdk, &mc) expectedValue = testWithOrg2(t, expectedValue, mc.ccName) verifyWithOrg1(t, sdk, expectedValue, mc.ccName) @@ -185,6 +189,33 @@ func setupClientContextsAndChannel(t *testing.T, sdk *fabsdk.FabricSDK, mc *mult } } +func discoverLocalPeers(t *testing.T, sdk *fabsdk.FabricSDK, ctxProvider contextAPI.ClientProvider, expected int) []fab.Peer { + ctx, err := ctxProvider() + require.NoError(t, err, "Error creating context") + + discoveryProvider := ctx.LocalDiscoveryProvider() + discovery, err := discoveryProvider.CreateLocalDiscoveryService(ctx.Identifier().MSPID) + require.NoErrorf(t, err, "Error creating local discovery service") + + var peers []fab.Peer + for i := 0; i < 10; i++ { + peers, err = discovery.GetPeers() + require.NoErrorf(t, err, "Error getting peers for MSP [%s]", ctx.Identifier().MSPID) + + t.Logf("Peers for MSP [%s]:", ctx.Identifier().MSPID) + for i, p := range peers { + t.Logf("%d- [%s]", i, p.URL()) + } + if len(peers) >= expected { + break + } + + // wait some time to allow the gossip to propagate the peers discovery + time.Sleep(3 * time.Second) + } + return peers +} + func testWithOrg1(t *testing.T, sdk *fabsdk.FabricSDK, mc *multiorgContext) int { org1AdminChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(org1AdminUser), fabsdk.WithOrg(org1)) @@ -199,7 +230,7 @@ func testWithOrg1(t *testing.T, sdk *fabsdk.FabricSDK, mc *multiorgContext) int } // Create chaincode package for example cc - createCC(t, mc.org1ResMgmt, mc.org2ResMgmt, ccPkg, mc.ccName) + createCC(t, mc.org1ResMgmt, mc.org2ResMgmt, ccPkg, mc.ccName, mc.ccVersion) chClientOrg1User, chClientOrg2User := connectUserToOrgChannel(org1ChannelClientContext, t, org2ChannelClientContext) @@ -218,7 +249,7 @@ func testWithOrg1(t *testing.T, sdk *fabsdk.FabricSDK, mc *multiorgContext) int // Ledger client will verify blockchain info ledgerInfoBefore := getBlockchainInfo(ledgerClient, t) - // Org2 user moves funds on org2 peer + // Org2 user moves funds transactionID := moveFunds(chClientOrg2User, t, mc.ccName) // Assert that funds have changed value on org1 peer @@ -228,7 +259,7 @@ func testWithOrg1(t *testing.T, sdk *fabsdk.FabricSDK, mc *multiorgContext) int checkLedgerInfo(ledgerClient, t, ledgerInfoBefore, transactionID) // Start chaincode upgrade process (install and instantiate new version of exampleCC) - upgradeCC(ccPkg, mc.org1ResMgmt, t, mc.org2ResMgmt, mc.ccName) + upgradeCC(ccPkg, mc.org1ResMgmt, t, mc.org2ResMgmt, mc.ccName, "1") // Org2 user moves funds on org2 peer (cc policy fails since both Org1 and Org2 peers should participate) testCCPolicy(chClientOrg2User, t, mc.ccName) @@ -322,15 +353,14 @@ func testCCPolicy(chClientOrg2User *channel.Client, t *testing.T, ccName string) t.Fatalf("Should have failed to move funds due to cc policy") } // Org2 user moves funds (cc policy ok since we have provided peers for both Orgs) - _, err = chClientOrg2User.Execute(channel.Request{ChaincodeID: ccName, Fcn: "invoke", Args: integration.ExampleCCTxArgs()}, channel.WithTargets(orgTestPeer0, orgTestPeer1), - channel.WithRetry(retry.DefaultChannelOpts)) + _, err = chClientOrg2User.Execute(channel.Request{ChaincodeID: ccName, Fcn: "invoke", Args: integration.ExampleCCTxArgs()}, channel.WithRetry(retry.DefaultChannelOpts)) if err != nil { t.Fatalf("Failed to move funds: %s", err) } } -func upgradeCC(ccPkg *resource.CCPackage, org1ResMgmt *resmgmt.Client, t *testing.T, org2ResMgmt *resmgmt.Client, ccName string) { - installCCReq := resmgmt.InstallCCRequest{Name: ccName, Path: "github.com/example_cc", Version: "1", Package: ccPkg} +func upgradeCC(ccPkg *resource.CCPackage, org1ResMgmt *resmgmt.Client, t *testing.T, org2ResMgmt *resmgmt.Client, ccName, ccVersion string) { + installCCReq := resmgmt.InstallCCRequest{Name: ccName, Path: "github.com/example_cc", Version: ccVersion, Package: ccPkg} // Install example cc version '1' to Org1 peers _, err := org1ResMgmt.InstallCC(installCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts)) require.Nil(t, err, "error should be nil for InstallCC version '1' or Org1 peers") @@ -344,14 +374,13 @@ func upgradeCC(ccPkg *resource.CCPackage, org1ResMgmt *resmgmt.Client, t *testin require.Nil(t, err, "error should be nil for getting cc policy with both orgs to approve") // Org1 resource manager will instantiate 'example_cc' version 1 on 'orgchannel' - upgradeResp, err := org1ResMgmt.UpgradeCC(channelID, resmgmt.UpgradeCCRequest{Name: ccName, Path: "github.com/example_cc", Version: "1", Args: integration.ExampleCCUpgradeArgs(), Policy: org1Andorg2Policy}) + upgradeResp, err := org1ResMgmt.UpgradeCC(channelID, resmgmt.UpgradeCCRequest{Name: ccName, Path: "github.com/example_cc", Version: ccVersion, Args: integration.ExampleCCUpgradeArgs(), Policy: org1Andorg2Policy}) require.Nil(t, err, "error should be nil for UpgradeCC version '1' on 'orgchannel'") require.NotEmpty(t, upgradeResp, "transaction response should be populated") } func moveFunds(chClientOrgUser *channel.Client, t *testing.T, ccName string) fab.TransactionID { - response, err := chClientOrgUser.Execute(channel.Request{ChaincodeID: ccName, Fcn: "invoke", Args: integration.ExampleCCTxArgs()}, channel.WithTargets(orgTestPeer1), - channel.WithRetry(retry.DefaultChannelOpts)) + response, err := chClientOrgUser.Execute(channel.Request{ChaincodeID: ccName, Fcn: "invoke", Args: integration.ExampleCCTxArgs()}, channel.WithRetry(retry.DefaultChannelOpts)) if err != nil { t.Fatalf("Failed to move funds: %s", err) } @@ -414,7 +443,7 @@ func verifyErrorFromCC(chClientOrg1User *channel.Client, t *testing.T, ccName st t.Logf("verifyErrorFromCC status.FromError s: %s, ok: %t", s, ok) require.True(t, ok, "expected status error") - require.Equal(t, s.Code, int32(status.MultipleErrors)) + require.Equal(t, int32(status.MultipleErrors), s.Code) for _, err := range err.(multi.Errors) { s, ok := status.FromError(err) @@ -424,8 +453,8 @@ func verifyErrorFromCC(chClientOrg1User *channel.Client, t *testing.T, ccName st } } -func createCC(t *testing.T, org1ResMgmt *resmgmt.Client, org2ResMgmt *resmgmt.Client, ccPkg *resource.CCPackage, ccName string) { - installCCReq := resmgmt.InstallCCRequest{Name: ccName, Path: "github.com/example_cc", Version: "0", Package: ccPkg} +func createCC(t *testing.T, org1ResMgmt *resmgmt.Client, org2ResMgmt *resmgmt.Client, ccPkg *resource.CCPackage, ccName, ccVersion string) { + installCCReq := resmgmt.InstallCCRequest{Name: ccName, Path: "github.com/example_cc", Version: ccVersion, Package: ccPkg} // Install example cc to Org1 peers _, err := org1ResMgmt.InstallCC(installCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts)) @@ -435,38 +464,46 @@ func createCC(t *testing.T, org1ResMgmt *resmgmt.Client, org2ResMgmt *resmgmt.Cl _, err = org2ResMgmt.InstallCC(installCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts)) require.NoError(t, err, "InstallCC for Org2 failed") - instantiateCC(t, org1ResMgmt, []string{"peer0.org1.example.com"}, ccName) - // instantiateCC above will propagate the call to the other peers through gossip, so no need to call it on the other peers. - - // Verify that example CC is instantiated on Org1 peer - chaincodeQueryResponse, err := org1ResMgmt.QueryInstantiatedChaincodes(channelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts)) - require.NoError(t, err, "QueryInstantiatedChaincodes return error") + instantiateCC(t, org1ResMgmt, ccName, ccVersion) found := false - for _, chaincode := range chaincodeQueryResponse.Chaincodes { - if chaincode.Name == ccName { - found = true + for i := 0; i < 5; i++ { + // Verify that example CC is instantiated on Org1 peer + chaincodeQueryResponse, err := org1ResMgmt.QueryInstantiatedChaincodes(channelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts)) + require.NoError(t, err, "QueryInstantiatedChaincodes return error") + + t.Logf("Found %d instantiated chaincodes:", len(chaincodeQueryResponse.Chaincodes)) + for _, chaincode := range chaincodeQueryResponse.Chaincodes { + t.Logf("Found instantiated chaincode Name: [%s], Version: [%s], Path: [%s]", chaincode.Name, chaincode.Version, chaincode.Path) + if chaincode.Name == ccName { + found = true + break + } } + if found { + break + } + time.Sleep(5 * time.Second) } require.True(t, found, "QueryInstantiatedChaincodes failed to find instantiated '%s' chaincode", ccName) - } -func instantiateCC(t *testing.T, resMgmt *resmgmt.Client, targets []string, ccName string) { +func instantiateCC(t *testing.T, resMgmt *resmgmt.Client, ccName, ccVersion string) { // Set up chaincode policy to 'any of two msps' - ccPolicy := cauthdsl.SignedByAnyMember([]string{"Org1MSP", "Org2MSP"}) + ccPolicy, err := cauthdsl.FromString("AND ('Org1MSP.member','Org2MSP.member')") + require.NoErrorf(t, err, "Error creating CC policy") + // Org1 resource manager will instantiate 'example_cc' on 'orgchannel' instantiateResp, err := resMgmt.InstantiateCC( channelID, resmgmt.InstantiateCCRequest{ Name: ccName, Path: "github.com/example_cc", - Version: "0", + Version: ccVersion, Args: integration.ExampleCCInitArgs(), Policy: ccPolicy, }, resmgmt.WithRetry(retry.DefaultResMgmtOpts), - resmgmt.WithTargetEndpoints(targets...), ) require.Nil(t, err, "error should be nil for instantiateCC") @@ -474,13 +511,8 @@ func instantiateCC(t *testing.T, resMgmt *resmgmt.Client, targets []string, ccNa } func testWithOrg2(t *testing.T, expectedValue int, ccName string) int { - // Specify user that will be used by dynamic selection service (to retrieve chanincode policy information) - // This user has to have privileges to query lscc for chaincode data - mychannelUser := selection.ChannelUser{ChannelID: channelID, Username: "User1", OrgName: "Org1"} - // Create SDK setup for channel client with dynamic selection - sdk, err := fabsdk.New(integration.ConfigBackend, - fabsdk.WithServicePkg(&DynamicSelectionProviderFactory{ChannelUsers: []selection.ChannelUser{mychannelUser}})) + sdk, err := fabsdk.New(integration.ConfigBackend) if err != nil { t.Fatalf("Failed to create new SDK: %s", err) } @@ -568,14 +600,3 @@ func loadOrgPeers(t *testing.T, ctxProvider contextAPI.ClientProvider) { t.Fatal(err) } } - -// DynamicSelectionProviderFactory is configured with dynamic (endorser) selection provider -type DynamicSelectionProviderFactory struct { - defsvc.ProviderFactory - ChannelUsers []selection.ChannelUser -} - -// CreateSelectionProvider returns a new implementation of dynamic selection provider -func (f *DynamicSelectionProviderFactory) CreateSelectionProvider(config fab.EndpointConfig) (fab.SelectionProvider, error) { - return selection.New(config, f.ChannelUsers) -} diff --git a/test/integration/revoked/revoked_peer_test.go b/test/integration/revoked/revoked_peer_test.go index bf6ecb6930..6da6185651 100644 --- a/test/integration/revoked/revoked_peer_test.go +++ b/test/integration/revoked/revoked_peer_test.go @@ -8,9 +8,11 @@ package revoked import ( "path" + "strings" "testing" "github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry" + "github.com/hyperledger/fabric-sdk-go/pkg/common/errors/status" contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp" @@ -198,14 +200,25 @@ func createCC(t *testing.T, org1ResMgmt *resmgmt.Client, org2ResMgmt *resmgmt.Cl if err != nil { t.Fatal(err) } - // Set up chaincode policy to 'any of two msps' - ccPolicy := cauthdsl.SignedByAnyMember([]string{"Org1MSP", "Org2MSP"}) + // Set up chaincode policy to 'two-of-two msps' + ccPolicy, err := cauthdsl.FromString("AND ('Org1MSP.member','Org2MSP.member')") + require.NoErrorf(t, err, "Error creating cc policy with both orgs to approve") // Org1 resource manager will instantiate 'example_cc' on 'orgchannel' - resp, err := org1ResMgmt.InstantiateCC("orgchannel", - resmgmt.InstantiateCCRequest{Name: "exampleCC", Path: "github.com/example_cc", Version: "0", Args: integration.ExampleCCInitArgs(), Policy: ccPolicy}, - resmgmt.WithTargetEndpoints("peer0.org1.example.com")) - require.Nil(t, err, "error should be nil") - require.NotEmpty(t, resp, "transaction response should be populated") + _, err = org1ResMgmt.InstantiateCC( + "orgchannel", + resmgmt.InstantiateCCRequest{ + Name: "exampleCC", + Path: "github.com/example_cc", + Version: "0", + Args: integration.ExampleCCInitArgs(), + Policy: ccPolicy, + }, + ) + require.Errorf(t, err, "Expecting error instantiating CC on peer with revoked certificate") + stat, ok := status.FromError(err) + require.Truef(t, ok, "Expecting error to be a status error") + require.Equalf(t, stat.Code, int32(status.SignatureVerificationFailed), "Expecting signature verification error due to revoked cert") + require.Truef(t, strings.Contains(err.Error(), "the creator certificate is not valid"), "Expecting error message to contain 'the creator certificate is not valid'") } func createChannel(org1AdminUser msp.SigningIdentity, org2AdminUser msp.SigningIdentity, chMgmtClient *resmgmt.Client, t *testing.T) { diff --git a/test/integration/sdk/ledger_queries_test.go b/test/integration/sdk/ledger_queries_test.go index 5394075ef1..d6e0e69025 100644 --- a/test/integration/sdk/ledger_queries_test.go +++ b/test/integration/sdk/ledger_queries_test.go @@ -17,6 +17,7 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/core/config" "github.com/hyperledger/fabric-sdk-go/pkg/fab" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" + "github.com/stretchr/testify/require" ) func TestLedgerClientQueries(t *testing.T) { @@ -49,13 +50,13 @@ func TestLedgerClientQueries(t *testing.T) { t.Fatalf("failed to get endpoint config, error: %v", err) } - expectedPeerConfig, ok := endpointConfig.PeerConfig("peer0.org1.example.com") - if !ok { - t.Fatalf("Unable to fetch Peer config for %s", "peer0.org1.example.com") - } + expectedPeerConfig1, ok := endpointConfig.PeerConfig("peer0.org1.example.com") + require.Truef(t, ok, "Unable to fetch Peer config for %s", "peer0.org1.example.com") + expectedPeerConfig2, ok := endpointConfig.PeerConfig("peer1.org1.example.com") + require.Truef(t, ok, "Unable to fetch Peer config for %s", "peer1.org1.example.com") - if !strings.Contains(ledgerInfo.Endorser, expectedPeerConfig.URL) { - t.Fatalf("Expecting %s, got %s", expectedPeerConfig.URL, ledgerInfo.Endorser) + if !strings.Contains(ledgerInfo.Endorser, expectedPeerConfig1.URL) && !strings.Contains(ledgerInfo.Endorser, expectedPeerConfig2.URL) { + t.Fatalf("Expecting %s or %s, got %s", expectedPeerConfig1.URL, expectedPeerConfig2.URL, ledgerInfo.Endorser) } // Same query with target diff --git a/test/integration/sdk/sdk_dyndiscovery_test.go b/test/integration/sdk/sdk_dyndiscovery_test.go index faac75e8c1..5c092b0e63 100644 --- a/test/integration/sdk/sdk_dyndiscovery_test.go +++ b/test/integration/sdk/sdk_dyndiscovery_test.go @@ -11,13 +11,16 @@ package sdk import ( "testing" + "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/dynamicdiscovery" + + "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/provider/chpvdr" + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" "github.com/stretchr/testify/require" contextImpl "github.com/hyperledger/fabric-sdk-go/pkg/context" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/factory/defsvc" - "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/dynamicdiscovery" "github.com/hyperledger/fabric-sdk-go/pkg/core/config" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" ) @@ -27,7 +30,7 @@ func TestDynamicDiscovery(t *testing.T) { // Create SDK setup for channel client with dynamic selection sdk, err := fabsdk.New(config.FromFile("../../fixtures/config/config_test.yaml"), - fabsdk.WithServicePkg(&DynamicDiscoveryProviderFactory{})) + fabsdk.WithServicePkg(&dynamicDiscoveryProviderFactory{})) require.NoError(t, err, "Failed to create new SDK") defer sdk.Close() @@ -38,7 +41,10 @@ func TestDynamicDiscovery(t *testing.T) { chCtx, err := chProvider() require.NoError(t, err, "Error creating channel context") - peers, err := chCtx.DiscoveryService().GetPeers() + discoveryService, err := chCtx.ChannelService().Discovery() + require.NoError(t, err, "Error creating discovery service") + + peers, err := discoveryService.GetPeers() require.NoErrorf(t, err, "Error getting peers for channel [%s]", testSetup.ChannelID) require.NotEmptyf(t, peers, "No peers were found for channel [%s]", testSetup.ChannelID) @@ -53,7 +59,7 @@ func TestDynamicLocalDiscovery(t *testing.T) { // Create SDK setup for channel client with dynamic selection sdk, err := fabsdk.New(config.FromFile("../../fixtures/config/config_test.yaml"), - fabsdk.WithServicePkg(&DynamicDiscoveryProviderFactory{})) + fabsdk.WithServicePkg(&dynamicDiscoveryProviderFactory{})) require.NoError(t, err, "Failed to create new SDK") defer sdk.Close() @@ -77,17 +83,64 @@ func TestDynamicLocalDiscovery(t *testing.T) { } } -// DynamicDiscoveryProviderFactory is configured with dynamic (endorser) selection provider -type DynamicDiscoveryProviderFactory struct { +type dynamicDiscoveryProviderFactory struct { defsvc.ProviderFactory } -// CreateDiscoveryProvider returns a new dynamic discovery provider -func (f *DynamicDiscoveryProviderFactory) CreateDiscoveryProvider(config fab.EndpointConfig) (fab.DiscoveryProvider, error) { - return dynamicdiscovery.New(config), nil +type channelProvider struct { + fab.ChannelProvider + services map[string]*dynamicdiscovery.ChannelService +} + +type channelService struct { + fab.ChannelService + discovery fab.DiscoveryService +} + +// CreateChannelProvider returns a new default implementation of channel provider +func (f *dynamicDiscoveryProviderFactory) CreateChannelProvider(config fab.EndpointConfig) (fab.ChannelProvider, error) { + chProvider, err := chpvdr.New(config) + if err != nil { + return nil, err + } + return &channelProvider{ + ChannelProvider: chProvider, + services: make(map[string]*dynamicdiscovery.ChannelService), + }, nil +} + +// Close frees resources and caches. +func (cp *channelProvider) Close() { + if c, ok := cp.ChannelProvider.(closable); ok { + c.Close() + } + for _, discovery := range cp.services { + discovery.Close() + } +} + +// ChannelService creates a ChannelService for an identity +func (cp *channelProvider) ChannelService(ctx fab.ClientContext, channelID string) (fab.ChannelService, error) { + chService, err := cp.ChannelProvider.ChannelService(ctx, channelID) + if err != nil { + return nil, err + } + + discovery, ok := cp.services[channelID] + if !ok { + discovery, err = dynamicdiscovery.NewChannelService(ctx, channelID) + if err != nil { + return nil, err + } + cp.services[channelID] = discovery + } + + return &channelService{ + ChannelService: chService, + discovery: discovery, + }, nil } -// CreateLocalDiscoveryProvider returns a new local dynamic discovery provider -func (f *DynamicDiscoveryProviderFactory) CreateLocalDiscoveryProvider(config fab.EndpointConfig) (fab.LocalDiscoveryProvider, error) { - return dynamicdiscovery.New(config), nil +func (cs *channelService) Discovery() (fab.DiscoveryService, error) { + return cs.discovery, nil } diff --git a/test/integration/sdk/sdk_provider_test.go b/test/integration/sdk/sdk_provider_test.go index fb0349ce7e..1127eb0f99 100644 --- a/test/integration/sdk/sdk_provider_test.go +++ b/test/integration/sdk/sdk_provider_test.go @@ -13,12 +13,13 @@ import ( "github.com/hyperledger/fabric-sdk-go/test/integration" "github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry" + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" - "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/factory/defsvc" + "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk/provider/chpvdr" "github.com/hyperledger/fabric-sdk-go/pkg/client/channel" - selection "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/dynamicselection" + "github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/dynamicselection" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" "github.com/stretchr/testify/require" ) @@ -28,20 +29,9 @@ func TestDynamicSelection(t *testing.T) { // Using shared SDK instance to increase test speed. testSetup := mainTestSetup - //testSetup := integration.BaseSetupImpl{ - // ConfigFile: "../" + integration.ConfigTestFile, - // ChannelID: "mychannel", - // OrgID: org1Name, - // ChannelConfig: path.Join("../../", metadata.ChannelConfigPath, "mychannel.tx"), - //} - - // Specify user that will be used by dynamic selection service (to retrieve chanincode policy information) - // This user has to have privileges to query lscc for chaincode data - mychannelUser := selection.ChannelUser{ChannelID: testSetup.ChannelID, Username: "User1", OrgName: "Org1"} - // Create SDK setup for channel client with dynamic selection sdk, err := fabsdk.New(integration.ConfigBackend, - fabsdk.WithServicePkg(&DynamicSelectionProviderFactory{ChannelUsers: []selection.ChannelUser{mychannelUser}})) + fabsdk.WithServicePkg(&DynamicSelectionProviderFactory{})) if err != nil { t.Fatalf("Failed to create new SDK: %s", err) @@ -96,10 +86,83 @@ func TestDynamicSelection(t *testing.T) { // DynamicSelectionProviderFactory is configured with dynamic (endorser) selection provider type DynamicSelectionProviderFactory struct { defsvc.ProviderFactory - ChannelUsers []selection.ChannelUser } -// CreateSelectionProvider returns a new implementation of dynamic selection provider -func (f *DynamicSelectionProviderFactory) CreateSelectionProvider(config fab.EndpointConfig) (fab.SelectionProvider, error) { - return selection.New(config, f.ChannelUsers) +// CreateChannelProvider returns a new default implementation of channel provider +func (f *DynamicSelectionProviderFactory) CreateChannelProvider(config fab.EndpointConfig) (fab.ChannelProvider, error) { + chProvider, err := chpvdr.New(config) + if err != nil { + return nil, err + } + return &dynamicSelectionChannelProvider{ + ChannelProvider: chProvider, + services: make(map[string]*dynamicselection.SelectionService), + }, nil +} + +type dynamicSelectionChannelProvider struct { + fab.ChannelProvider + services map[string]*dynamicselection.SelectionService +} + +type initializer interface { + Initialize(providers context.Providers) error +} + +// Initialize sets the provider context +func (cp *dynamicSelectionChannelProvider) Initialize(providers context.Providers) error { + if init, ok := cp.ChannelProvider.(initializer); ok { + init.Initialize(providers) + } + return nil +} + +type closable interface { + Close() +} + +// Close frees resources and caches. +func (cp *dynamicSelectionChannelProvider) Close() { + if c, ok := cp.ChannelProvider.(closable); ok { + c.Close() + } + + for _, service := range cp.services { + service.Close() + } +} + +// ChannelService creates a ChannelService +func (cp *dynamicSelectionChannelProvider) ChannelService(ctx fab.ClientContext, channelID string) (fab.ChannelService, error) { + chService, err := cp.ChannelProvider.ChannelService(ctx, channelID) + if err != nil { + return nil, err + } + + selection, ok := cp.services[channelID] + if !ok { + discovery, err := chService.Discovery() + if err != nil { + return nil, err + } + selection, err := dynamicselection.NewService(ctx, channelID, discovery) + if err != nil { + return nil, err + } + cp.services[channelID] = selection + } + + return &dynamicSelectionChannelService{ + ChannelService: chService, + selection: selection, + }, nil +} + +type dynamicSelectionChannelService struct { + fab.ChannelService + selection fab.SelectionService +} + +func (cs *dynamicSelectionChannelService) Selection() (fab.SelectionService, error) { + return cs.selection, nil }