Skip to content

Commit

Permalink
[FABG-946] Honor Excluded Orderer in SDK Config (#43)
Browse files Browse the repository at this point in the history
a new return value in OrdererConfig() is added
	it's a new flag that states if the orderer is being
	ignored by the config's EntityMatchers

Change-Id: I1cb77f854656c97a201a1c8a0600c1fa3d04ecb4
Signed-off-by: Baha Shaaban <baha.shaaban@securekey.com>
  • Loading branch information
baha-ai committed Feb 7, 2020
1 parent b5424d1 commit 92bda8e
Show file tree
Hide file tree
Showing 13 changed files with 310 additions and 64 deletions.
6 changes: 5 additions & 1 deletion pkg/client/resmgmt/opts.go
Expand Up @@ -89,7 +89,11 @@ func WithOrdererEndpoint(key string) RequestOption {

return func(ctx context.Client, opts *requestOptions) error {

ordererCfg, found := ctx.EndpointConfig().OrdererConfig(key)
ordererCfg, found, ignoreOrderer := ctx.EndpointConfig().OrdererConfig(key)
if ignoreOrderer {
return errors.Errorf("orderer url : %s is explicitly ignored by EntityMatchers config - can't add orderer", key)
}

if !found {
return errors.Errorf("orderer not found for url : %s", key)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/common/providers/fab/provider.go
Expand Up @@ -93,7 +93,7 @@ type CommManager interface {
type EndpointConfig interface {
Timeout(TimeoutType) time.Duration
OrderersConfig() []OrdererConfig
OrdererConfig(nameOrURL string) (*OrdererConfig, bool)
OrdererConfig(nameOrURL string) (*OrdererConfig, bool, bool)
PeersConfig(org string) ([]PeerConfig, bool)
PeerConfig(nameOrURL string) (*PeerConfig, bool)
NetworkConfig() *NetworkConfig
Expand Down
5 changes: 3 additions & 2 deletions pkg/common/providers/test/mockfab/mockfab.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 13 additions & 5 deletions pkg/fab/channel/transactor.go
Expand Up @@ -60,12 +60,14 @@ func orderersFromChannelCfg(ctx context.Client, cfg fab.ChannelCfg) ([]fab.Order
return nil, err
}
if len(orderers) > 0 {

logger.Debugf("there [%v] orderer(s) in SDK config returning these and skipping channelCfg", len(orderers))
return orderers, nil
}

ordererDict := orderersByTarget(ctx)

logger.Debugf("there are no 'channel orderer(s)' in SDK configs. Got [%v] 'orderer(s)' configs from SDK and try to lookup additional ones in channelCfg", len(ordererDict))

// Add orderer if specified in channel config
for _, target := range cfg.Orderers() {

Expand All @@ -76,14 +78,19 @@ func orderersFromChannelCfg(ctx context.Client, cfg fab.ChannelCfg) ([]fab.Order
if !ok {
logger.Debugf("Failed to get channel Cfg orderer [%s] from ordererDict, now trying orderer Matchers in Entity Matchers", target)
// Try to find a match from entityMatchers config
matchingOrdererConfig, found := ctx.EndpointConfig().OrdererConfig(strings.ToLower(target))
matchingOrdererConfig, found, ignore := ctx.EndpointConfig().OrdererConfig(strings.ToLower(target))
if ignore {
logger.Debugf("orderer [%s] is ignored and will not be added", target)
continue
}

if found {
logger.Debugf("Found matching ordererConfig from entity Matchers for channel Cfg Orderer [%s]", target)
oCfg = *matchingOrdererConfig
ok = true
}

}

//create orderer using channel config block orderer address
if !ok {
logger.Debugf("Unable to find matching ordererConfig from entity Matchers for channel Cfg Orderer [%s]", target)
Expand Down Expand Up @@ -112,8 +119,8 @@ func orderersFromChannel(ctx context.Client, channelID string) ([]fab.Orderer, e
orderers := []fab.Orderer{}
for _, chOrderer := range chNetworkConfig.Orderers {

ordererConfig, found := ctx.EndpointConfig().OrdererConfig(chOrderer)
if !found {
ordererConfig, found, ignoreOrderer := ctx.EndpointConfig().OrdererConfig(chOrderer)
if !found || ignoreOrderer {
//continue if given channel orderer not found in endpoint config
continue
}
Expand All @@ -135,6 +142,7 @@ func orderersByTarget(ctx context.Client) map[string]fab.OrdererConfig {
for _, oc := range orderersConfig {
address := endpoint.ToAddress(oc.URL)
ordererDict[address] = oc
logger.Debugf("ordererConfig from SDK to be added: %s", oc.URL)
}
return ordererDict
}
Expand Down
163 changes: 163 additions & 0 deletions pkg/fab/channel/transactor_test.go
Expand Up @@ -11,11 +11,13 @@ import (
"testing"
"time"

"github.com/golang/mock/gomock"
"github.com/hyperledger/fabric-sdk-go/test/metadata"
"github.com/stretchr/testify/assert"

"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/core"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/test/mockfab"
"github.com/hyperledger/fabric-sdk-go/pkg/context"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config/lookup"
Expand Down Expand Up @@ -203,16 +205,177 @@ func TestOrderersURLOverride(t *testing.T) {
user := mspmocks.NewMockSigningIdentity("test", "test")
ctx := mocks.NewMockContext(user)
ctx.SetEndpointConfig(endpointCfg)
// create a mock channel config for mychannel with this orderer created above
chConfig := mocks.NewMockChannelCfg("mychannel")
chConfig.MockOrderers = []string{"example.com"}

// now test orderersFromChannelCfg with above channel config (chConfig) and sdk config passed in as ctx
o, err := orderersFromChannelCfg(ctx, chConfig)
assert.Nil(t, err)
assert.NotEmpty(t, o)
assert.Equal(t, 1, len(o), "expected one orderer from response orderers list")
assert.Equal(t, sampleOrdererURL, o[0].URL(), "orderer URL override from endpointconfig channels is not working as expected")
}

func TestExcludedOrdrerer(t *testing.T) {
sampleOrdererURL := "orderer.example.com.sample.url:100090"

//Create endpoint mockEndpoingCfg
configPath := filepath.Join(metadata.GetProjectPath(), "pkg", "core", "config", "testdata", "config_test.yaml")
configBackends, err := config.FromFile(configPath)()
if err != nil {
t.Fatal("failed to get mockEndpoingCfg backends")
}

networkConfig := endpointConfigEntity{}
err = lookup.New(configBackends...).UnmarshalKey("orderers", &networkConfig.Orderers)
if err != nil {
t.Fatal("failed to unmarshal orderer")
}

orderer := networkConfig.Orderers["orderer.example.com"]
orderer.URL = sampleOrdererURL
networkConfig.Orderers["orderer.example.com"] = orderer

// create a mock channel mockEndpoingCfg for mychannel with this orderer created above
chConfig := mocks.NewMockChannelCfg("mychannel")
chConfig.MockOrderers = []string{"example.com"}

backendMap := make(map[string]interface{})
backendMap["orderers"] = networkConfig.Orderers

backends := append([]core.ConfigBackend{}, &mocksConfig.MockConfigBackend{KeyValueMap: backendMap})
backends = append(backends, configBackends...)

// now try to add a second orderer to the configs
// 1. update channel mockEndpoingCfg with this new orderer
chConfig.MockOrderers = append(chConfig.MockOrderers, "example2.com")
// 2. update sdk configs as well
sampleOrderer2URL := "orderer.example2.com:9999"
networkConfig.Orderers["orderer.example2.com"] = fabImpl.OrdererConfig{
URL: sampleOrderer2URL,
TLSCACerts: networkConfig.Orderers["orderer.example.com"].TLSCACerts, // for testing only, adding dummy cert
GRPCOptions: networkConfig.Orderers["orderer.example.com"].GRPCOptions, // for testing only, adding dummy cert
}
backendMap["orderers"] = networkConfig.Orderers

backendMap["channels"] = map[string]interface{}{
"mychannel": map[string]interface{}{
"orderers": []string{"orderer.example.com", "orderer.example2.com"},
},
}

backends = append([]core.ConfigBackend{}, &mocksConfig.MockConfigBackend{KeyValueMap: backendMap})
backends = append(backends, configBackends...)
endpointCfg, err := fabImpl.ConfigFromBackend(backends...)
if err != nil {
t.Fatal("failed to get endpoint mockEndpoingCfg", err)
}

user := mspmocks.NewMockSigningIdentity("test", "test")
ctx := mocks.NewMockContext(user)
ctx.SetEndpointConfig(endpointCfg)
// 3. now test orderersFromChannelCfg with updated chConfig and sdk mockEndpoingCfg (ctx)
o, err := orderersFromChannelCfg(ctx, chConfig)
assert.Nil(t, err)
assert.NotEmpty(t, o)
assert.Equal(t, 2, len(o), "expected 2 orderers from response orderers list")
assert.Equal(t, sampleOrdererURL, o[0].URL(),
"orderer URL override from endpointconfig channels is not working as expected")
assert.Equal(t, sampleOrderer2URL, o[1].URL(),
"orderer URL override from endpointconfig channels is not working as expected")

sampleOrdererExcludedURL := "orderer.excluded.example3.com:8888"
// finally add a blacklisted orderer and ensure it's not returned in the configs

// first make sure it's added in the channel config
chConfig.MockOrderers = append(chConfig.MockOrderers, "example3.com")

// and add it's added in the networkConfig of the SDK
networkConfig.Orderers[sampleOrdererExcludedURL] = fabImpl.OrdererConfig{
URL: sampleOrdererExcludedURL,
TLSCACerts: networkConfig.Orderers["orderer.example.com"].TLSCACerts, // for testing only, adding dummy cert
GRPCOptions: networkConfig.Orderers["orderer.example.com"].GRPCOptions, // for testing only, adding dummy cert
}

// create mock EncdpointConfig to control returned values
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
mockEndpoingCfg := mockfab.NewMockEndpointConfig(mockCtrl)

var orderersCfgs []fab.OrdererConfig
for _, v := range networkConfig.Orderers {
orderersCfgs = append(orderersCfgs, fab.OrdererConfig{
URL: v.URL,
GRPCOptions: v.GRPCOptions,
})
}

mockEndpoingCfg.EXPECT().ChannelConfig("mychannel").Return(&fab.ChannelEndpointConfig{
Orderers: []string{}}) // empty channel.Orderers SDK config, to force fetching from orderers SDK config
mockEndpoingCfg.EXPECT().OrdererConfig("example.com").Return(&orderersCfgs[0], true, false)
mockEndpoingCfg.EXPECT().OrdererConfig("example2.com").Return(&orderersCfgs[1], true, false)
mockEndpoingCfg.EXPECT().OrdererConfig("example3.com").Return(nil, false, true) // true means ignored
mockEndpoingCfg.EXPECT().OrderersConfig().Return(orderersCfgs)

ctx.SetEndpointConfig(mockEndpoingCfg)

// example3.com is marked as ignored in mockEndpointCfg above (this is equivalent to field: ignoreEndpoint:true in
// EntityMatchers) in SDK configs while it is added in the channel chConfig
o, err = orderersFromChannelCfg(ctx, chConfig)
assert.Nil(t, err)
assert.NotEmpty(t, o)
assert.Equal(t, 2, len(o), "expected 2 orderers from response orderers list")

// now try with example2.com not found, to be populated from chConfig
mockEndpoingCfg.EXPECT().ChannelConfig("mychannel").Return(&fab.ChannelEndpointConfig{
Orderers: []string{}}) // empty channel.Orderers SDK config, to force fetching from orderers SDK config
mockEndpoingCfg.EXPECT().OrdererConfig("example.com").Return(&orderersCfgs[0], true, false)
mockEndpoingCfg.EXPECT().OrdererConfig("example2.com").Return(nil, false, false)
mockEndpoingCfg.EXPECT().OrdererConfig("example3.com").Return(nil, false, true) // true means ignored
mockEndpoingCfg.EXPECT().OrderersConfig().Return(orderersCfgs)

ctx.SetEndpointConfig(mockEndpoingCfg)

o, err = orderersFromChannelCfg(ctx, chConfig)
assert.Nil(t, err)
assert.NotEmpty(t, o)
assert.Equal(t, 2, len(o), "expected 2 orderers from response orderers list")


// now retry the same previous two tests with channelConfig returning list of orderers
mockEndpoingCfg.EXPECT().ChannelConfig("mychannel").Return(&fab.ChannelEndpointConfig{
Orderers: chConfig.MockOrderers}) // read orderers from channel.Orderers SDK config
mockEndpoingCfg.EXPECT().OrdererConfig("example.com").Return(&orderersCfgs[0], true, false)
mockEndpoingCfg.EXPECT().OrdererConfig("example2.com").Return(&orderersCfgs[1], true, false)
mockEndpoingCfg.EXPECT().OrdererConfig("example3.com").Return(nil, false, true) // true means ignored

ctx.SetEndpointConfig(mockEndpoingCfg)

// example3.com is marked as ignored in mockEndpointCfg above (this is equivalent to field: ignoreEndpoint:true in
// EntityMatchers) in SDK configs while it is added in the channel chConfig
o, err = orderersFromChannelCfg(ctx, chConfig)
assert.Nil(t, err)
assert.NotEmpty(t, o)
assert.Equal(t, 2, len(o), "expected 2 orderers from response orderers list")

// now try with example2.com not found, to be populated from chConfig
mockEndpoingCfg.EXPECT().ChannelConfig("mychannel").Return(&fab.ChannelEndpointConfig{
Orderers: chConfig.MockOrderers}) // read orderers from channel.Orderers SDK config
mockEndpoingCfg.EXPECT().OrdererConfig("example.com").Return(&orderersCfgs[0], true, false) // found
mockEndpoingCfg.EXPECT().OrdererConfig("example2.com").Return(nil, false, false) // not found
mockEndpoingCfg.EXPECT().OrdererConfig("example3.com").Return(nil, false, true) // excluded

ctx.SetEndpointConfig(mockEndpoingCfg)

o, err = orderersFromChannelCfg(ctx, chConfig)
assert.Nil(t, err)
assert.NotEmpty(t, o)
assert.Equal(t, 1, len(o),
"expected 1 orderer from response orderers list since 1 orderer is not found " +
"and another is excluded")
}

//endpointConfigEntity contains endpoint config elements needed by endpointconfig
type endpointConfigEntity struct {
Channels map[string]fab.ChannelEndpointConfig
Expand Down
34 changes: 20 additions & 14 deletions pkg/fab/endpointconfig.go
Expand Up @@ -193,7 +193,7 @@ func (c *EndpointConfig) OrderersConfig() []fab.OrdererConfig {
}

// OrdererConfig returns the requested orderer
func (c *EndpointConfig) OrdererConfig(nameOrURL string) (*fab.OrdererConfig, bool) {
func (c *EndpointConfig) OrdererConfig(nameOrURL string) (*fab.OrdererConfig, bool, bool) {
return c.tryMatchingOrdererConfig(nameOrURL, true)
}

Expand Down Expand Up @@ -1282,8 +1282,8 @@ func (c *EndpointConfig) loadOrdererConfigs() error {
ordererConfigs := []fab.OrdererConfig{}
for name := range c.networkConfig.Orderers {

matchedOrderer, ok := c.tryMatchingOrdererConfig(name, false)
if !ok {
matchedOrderer, ok, ignoreOrderer := c.tryMatchingOrdererConfig(name, false)
if !ok || ignoreOrderer {
continue
}

Expand Down Expand Up @@ -1345,7 +1345,11 @@ func (c *EndpointConfig) loadChannelOrderers() error {
orderers := []fab.OrdererConfig{}
for _, ordererName := range channelConfig.Orderers {

orderer, ok := c.tryMatchingOrdererConfig(strings.ToLower(ordererName), false)
orderer, ok, ignoreOrderer := c.tryMatchingOrdererConfig(strings.ToLower(ordererName), false)
if ignoreOrderer {
continue
}

if !ok {
return errors.Errorf("Could not find Orderer Config for channel orderer [%s]", ordererName)
}
Expand Down Expand Up @@ -1539,7 +1543,7 @@ func (c *EndpointConfig) getMappedPeer(host string) *fab.PeerConfig {
return &mappedConfig
}

func (c *EndpointConfig) tryMatchingOrdererConfig(ordererSearchKey string, searchByURL bool) (*fab.OrdererConfig, bool) {
func (c *EndpointConfig) tryMatchingOrdererConfig(ordererSearchKey string, searchByURL bool) (*fab.OrdererConfig, bool, bool) {

//loop over orderer entity matchers to find the matching orderer
for _, matcher := range c.ordererMatchers {
Expand All @@ -1552,7 +1556,7 @@ func (c *EndpointConfig) tryMatchingOrdererConfig(ordererSearchKey string, searc
//direct lookup if orderer matchers are not configured or no matchers matched
orderer, ok := c.networkConfig.Orderers[strings.ToLower(ordererSearchKey)]
if ok {
return &orderer, true
return &orderer, true, false
}

if searchByURL {
Expand All @@ -1563,7 +1567,7 @@ func (c *EndpointConfig) tryMatchingOrdererConfig(ordererSearchKey string, searc
URL: ordererCfg.URL,
GRPCOptions: ordererCfg.GRPCOptions,
TLSCACert: ordererCfg.TLSCACert,
}, true
}, true, false
}
}
}
Expand All @@ -1574,17 +1578,19 @@ func (c *EndpointConfig) tryMatchingOrdererConfig(ordererSearchKey string, searc
URL: ordererSearchKey,
GRPCOptions: c.defaultOrdererConfig.GRPCOptions,
TLSCACert: c.defaultOrdererConfig.TLSCACert,
}, true
}, true, false
}

return nil, false
return nil, false, false
}

func (c *EndpointConfig) matchOrderer(ordererSearchKey string, matcher matcherEntry) (*fab.OrdererConfig, bool) {
func (c *EndpointConfig) matchOrderer(ordererSearchKey string, matcher matcherEntry) (*fab.OrdererConfig, bool, bool) {

if matcher.matchConfig.IgnoreEndpoint {
logger.Debugf("Ignoring orderer `%s` since entity matcher IgnoreEndpoint flag is on", ordererSearchKey)
return nil, false
logger.Debugf(" Ignoring orderer `%s` since entity matcher IgnoreEndpoint flag is on", ordererSearchKey)
// IgnoreEndpoint must force ignoring this matching orderer (weather found or not) and must be explicitly
// mentioned. The third argument is explicitly used for this, all other cases will return false.
return nil, false, true
}

mappedHost := c.regexMatchAndReplace(matcher.regex, ordererSearchKey, matcher.matchConfig.MappedHost)
Expand All @@ -1593,7 +1599,7 @@ func (c *EndpointConfig) matchOrderer(ordererSearchKey string, matcher matcherEn
matchedOrderer := c.getMappedOrderer(mappedHost)
if matchedOrderer == nil {
logger.Debugf("Could not find mapped host [%s] for orderer [%s]", matcher.matchConfig.MappedHost, ordererSearchKey)
return nil, false
return nil, false, false
}

//URLSubstitutionExp if found use from entity matcher otherwise use from mapped host
Expand All @@ -1611,7 +1617,7 @@ func (c *EndpointConfig) matchOrderer(ordererSearchKey string, matcher matcherEn
matchedOrderer.URL = c.getDefaultMatchingURL(ordererSearchKey)
}

return matchedOrderer, true
return matchedOrderer, true, false
}

func (c *EndpointConfig) getMappedOrderer(host string) *fab.OrdererConfig {
Expand Down

0 comments on commit 92bda8e

Please sign in to comment.