Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit d38bc9c

Browse files
committed
[FAB-9238] PeerChannelConfig loading logic refactoring
- To set default = 'true' for PeerChannelConfig items, Instead of manual lookup of keys in endpointconfig, a unmarshal hook function is used. All manual key build and lookup logic in endpoint config has been removed. - revoke integration test to use config backends Change-Id: I465bdc37116d2505c7cecc976a3c5745d8289741 Signed-off-by: Sudesh Shetty <sudesh.shetty@securekey.com>
1 parent 041e8e1 commit d38bc9c

File tree

6 files changed

+207
-507
lines changed

6 files changed

+207
-507
lines changed

pkg/core/config/lookup/lookup.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ func New(coreBackend core.ConfigBackend) *ConfigLookup {
1919
return &ConfigLookup{backend: coreBackend}
2020
}
2121

22+
//unmarshalOpts opts for unmarshal key function
23+
type unmarshalOpts struct {
24+
hookFunc mapstructure.DecodeHookFunc
25+
}
26+
27+
// UnmarshalOption describes a functional parameter unmarshaling
28+
type UnmarshalOption func(o *unmarshalOpts)
29+
30+
// WithUnmarshalHookFunction provides an option to pass Custom Decode Hook Func
31+
// for unmarshaling
32+
func WithUnmarshalHookFunction(hookFunction mapstructure.DecodeHookFunc) UnmarshalOption {
33+
return func(o *unmarshalOpts) {
34+
o.hookFunc = hookFunction
35+
}
36+
}
37+
2238
//ConfigLookup is wrapper for core.ConfigBackend which performs key lookup and unmarshalling
2339
type ConfigLookup struct {
2440
backend core.ConfigBackend
@@ -66,19 +82,35 @@ func (c *ConfigLookup) GetDuration(key string) time.Duration {
6682
}
6783

6884
//UnmarshalKey unmarshals value for given key to rawval type
69-
func (c *ConfigLookup) UnmarshalKey(key string, rawVal interface{}) error {
85+
func (c *ConfigLookup) UnmarshalKey(key string, rawVal interface{}, opts ...UnmarshalOption) error {
7086
value, ok := c.backend.Lookup(key)
7187
if !ok {
7288
return nil
7389
}
7490

91+
//mandatory hook func
92+
hookFn := mapstructure.StringToTimeDurationHookFunc()
93+
94+
//check for opts
95+
unmarshalOpts := unmarshalOpts{}
96+
for _, param := range opts {
97+
param(&unmarshalOpts)
98+
}
99+
100+
//compose multiple hook funcs to one if found in opts
101+
if unmarshalOpts.hookFunc != nil {
102+
hookFn = mapstructure.ComposeDecodeHookFunc(hookFn, unmarshalOpts.hookFunc)
103+
}
104+
105+
//build decoder
75106
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
76-
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
107+
DecodeHook: hookFn,
77108
Result: rawVal,
78109
})
79110
if err != nil {
80111
return err
81112
}
82113

114+
//decode
83115
return decoder.Decode(value)
84116
}

pkg/core/config/lookup/lookup_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ import (
1919

2020
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
2121
"github.com/hyperledger/fabric-sdk-go/pkg/core/mocks"
22+
"github.com/mitchellh/mapstructure"
2223
"github.com/spf13/viper"
2324
"github.com/stretchr/testify/assert"
2425
)
2526

2627
var sampleConfigFile = "../testdata/config_test.yaml"
2728

29+
const orgChannelID = "orgchannel"
30+
2831
var backend *mocks.MockConfigBackend
2932

3033
func TestMain(m *testing.M) {
@@ -244,6 +247,39 @@ func setupCustomBackend() {
244247
backend = &mocks.MockConfigBackend{KeyValueMap: backendMap}
245248
}
246249

250+
func TestUnmarshalWithHookFunc(t *testing.T) {
251+
testLookup := New(backend)
252+
tamperPeerChannelConfig(backend)
253+
//output struct
254+
networkConfig := fab.NetworkConfig{}
255+
testLookup.UnmarshalKey("channels", &networkConfig.Channels, WithUnmarshalHookFunction(setTrueDefaultForPeerChannelConfig()))
256+
257+
//Test if mandatory hook func is working as expected
258+
assert.True(t, len(networkConfig.Channels) == 3)
259+
assert.True(t, len(networkConfig.Channels["mychannel"].Peers) == 1)
260+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.MinResponses == 1)
261+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.MaxTargets == 1)
262+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.RetryOpts.MaxBackoff.String() == (5*time.Second).String())
263+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.RetryOpts.InitialBackoff.String() == (500*time.Millisecond).String())
264+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.RetryOpts.BackoffFactor == 2.0)
265+
266+
//Test if custom hook func is working
267+
assert.True(t, len(networkConfig.Channels[orgChannelID].Peers) == 2)
268+
//test orgchannel peer1 (EndorsingPeer should be true as set, remaining should be default = true)
269+
orgChannelPeer1 := networkConfig.Channels[orgChannelID].Peers["peer0.org1.example.com"]
270+
assert.True(t, orgChannelPeer1.EndorsingPeer)
271+
assert.True(t, orgChannelPeer1.LedgerQuery)
272+
assert.True(t, orgChannelPeer1.EventSource)
273+
assert.True(t, orgChannelPeer1.ChaincodeQuery)
274+
275+
//test orgchannel peer1 (EndorsingPeer should be false as set, remaining should be default = true)
276+
orgChannelPeer2 := networkConfig.Channels[orgChannelID].Peers["peer0.org2.example.com"]
277+
assert.False(t, orgChannelPeer2.EndorsingPeer)
278+
assert.True(t, orgChannelPeer2.LedgerQuery)
279+
assert.True(t, orgChannelPeer2.EventSource)
280+
assert.True(t, orgChannelPeer2.ChaincodeQuery)
281+
}
282+
247283
func newViper() *viper.Viper {
248284
myViper := viper.New()
249285
replacer := strings.NewReplacer(".", "_")
@@ -255,3 +291,45 @@ func newViper() *viper.Viper {
255291
}
256292
return myViper
257293
}
294+
295+
func tamperPeerChannelConfig(backend *mocks.MockConfigBackend) {
296+
channelsMap := backend.KeyValueMap["channels"]
297+
orgChannel := map[string]interface{}{
298+
"orderers": []string{"orderer.example.com"},
299+
"peers": map[string]interface{}{
300+
"peer0.org1.example.com": map[string]interface{}{"endorsingpeer": true},
301+
"peer0.org2.example.com": map[string]interface{}{"endorsingpeer": false},
302+
},
303+
}
304+
(channelsMap.(map[string]interface{}))[orgChannelID] = orgChannel
305+
}
306+
307+
func setTrueDefaultForPeerChannelConfig() mapstructure.DecodeHookFunc {
308+
return func(
309+
f reflect.Type,
310+
t reflect.Type,
311+
data interface{}) (interface{}, error) {
312+
313+
//If target is of type 'fab.PeerChannelConfig', then only hook should work
314+
if t == reflect.TypeOf(fab.PeerChannelConfig{}) {
315+
dataMap, ok := data.(map[string]interface{})
316+
if ok {
317+
setDefault(dataMap, "endorsingpeer", true)
318+
setDefault(dataMap, "chaincodequery", true)
319+
setDefault(dataMap, "ledgerquery", true)
320+
setDefault(dataMap, "eventsource", true)
321+
322+
return dataMap, nil
323+
}
324+
}
325+
return data, nil
326+
}
327+
}
328+
329+
//setDefault sets default value provided to map if given key not found
330+
func setDefault(dataMap map[string]interface{}, key string, defaultVal bool) {
331+
_, ok := dataMap[key]
332+
if !ok {
333+
dataMap[key] = true
334+
}
335+
}

pkg/fab/endpointconfig.go

Lines changed: 37 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"crypto/tls"
1111
"crypto/x509"
1212
"io/ioutil"
13+
"reflect"
1314
"regexp"
1415
"sort"
1516
"strconv"
@@ -27,6 +28,7 @@ import (
2728
"github.com/hyperledger/fabric-sdk-go/pkg/core/config/lookup"
2829
"github.com/hyperledger/fabric-sdk-go/pkg/core/cryptosuite"
2930
"github.com/hyperledger/fabric-sdk-go/pkg/util/pathvar"
31+
"github.com/mitchellh/mapstructure"
3032
"github.com/pkg/errors"
3133
)
3234

@@ -401,13 +403,12 @@ func (c *EndpointConfig) ChannelPeers(name string) ([]fab.ChannelPeer, error) {
401403
// viper lowercases all key maps
402404
chConfig, ok := netConfig.Channels[strings.ToLower(name)]
403405
if !ok {
404-
matchingChannel, mappedChannel, matchErr := c.tryMatchingChannelConfig(name)
406+
matchingChannel, _, matchErr := c.tryMatchingChannelConfig(name)
405407
if matchErr != nil {
406408
return peers, nil
407409
}
408410

409411
// reset 'name' with the mappedChannel as it's referenced further below
410-
name = mappedChannel
411412
chConfig = *matchingChannel
412413
}
413414

@@ -433,21 +434,6 @@ func (c *EndpointConfig) ChannelPeers(name string) ([]fab.ChannelPeer, error) {
433434
p.TLSCACerts.Path = pathvar.Subst(p.TLSCACerts.Path)
434435
}
435436

436-
// Assemble channel peer key
437-
chPeerKey := "channels." + name + ".peers." + peerName
438-
439-
// Default value for endorsing peer key is true
440-
setEndorsingPeer(chPeerKey, c, &chPeerConfig)
441-
442-
// Default value for chaincode query key is true
443-
setChaincodeQuery(chPeerKey, c, &chPeerConfig)
444-
445-
// Default value for ledger query key is true
446-
setLedgerQuery(chPeerKey, c, &chPeerConfig)
447-
448-
// Default value for event source key is true
449-
setEventSource(chPeerKey, c, &chPeerConfig)
450-
451437
mspID, err := c.PeerMSPID(peerName)
452438
if err != nil {
453439
return nil, errors.Errorf("failed to retrieve msp id for peer %s", peerName)
@@ -464,38 +450,6 @@ func (c *EndpointConfig) ChannelPeers(name string) ([]fab.ChannelPeer, error) {
464450

465451
}
466452

467-
func setEndorsingPeer(chPeerKey string, c *EndpointConfig, chPeerConfig *fab.PeerChannelConfig) {
468-
endorsingPeerKey := strings.ToLower(chPeerKey + ".endorsingPeer")
469-
_, ok := c.backend.Lookup(endorsingPeerKey)
470-
if !ok {
471-
chPeerConfig.EndorsingPeer = true
472-
}
473-
}
474-
475-
func setEventSource(chPeerKey string, c *EndpointConfig, chPeerConfig *fab.PeerChannelConfig) {
476-
eventSourceKey := strings.ToLower(chPeerKey + ".eventSource")
477-
_, ok := c.backend.Lookup(eventSourceKey)
478-
if !ok {
479-
chPeerConfig.EventSource = true
480-
}
481-
}
482-
483-
func setLedgerQuery(chPeerKey string, c *EndpointConfig, chPeerConfig *fab.PeerChannelConfig) {
484-
ledgerQueryKey := strings.ToLower(chPeerKey + ".ledgerQuery")
485-
_, ok := c.backend.Lookup(ledgerQueryKey)
486-
if !ok {
487-
chPeerConfig.LedgerQuery = true
488-
}
489-
}
490-
491-
func setChaincodeQuery(chPeerKey string, c *EndpointConfig, chPeerConfig *fab.PeerChannelConfig) {
492-
ccQueryKey := strings.ToLower(chPeerKey + ".chaincodeQuery")
493-
_, ok := c.backend.Lookup(ccQueryKey)
494-
if !ok {
495-
chPeerConfig.ChaincodeQuery = true
496-
}
497-
}
498-
499453
// ChannelOrderers returns a list of channel orderers
500454
func (c *EndpointConfig) ChannelOrderers(name string) ([]fab.OrdererConfig, error) {
501455
orderers := []fab.OrdererConfig{}
@@ -717,7 +671,7 @@ func (c *EndpointConfig) cacheNetworkConfiguration() error {
717671
return errors.WithMessage(err, "failed to parse 'client' config item to networkConfig.Client type")
718672
}
719673

720-
err = c.backend.UnmarshalKey("channels", &networkConfig.Channels)
674+
err = c.backend.UnmarshalKey("channels", &networkConfig.Channels, lookup.WithUnmarshalHookFunction(peerChannelConfigHookFunc()))
721675
logger.Debugf("channels are: %+v", networkConfig.Channels)
722676
if err != nil {
723677
return errors.WithMessage(err, "failed to parse 'channels' config item to networkConfig.Channels type")
@@ -1216,3 +1170,36 @@ func loadByteKeyOrCertFromFile(c *msp.ClientConfig, isKey bool) ([]byte, error)
12161170
}
12171171
return bts, nil
12181172
}
1173+
1174+
//peerChannelConfigHookFunc returns hook function for unmarshalling 'fab.PeerChannelConfig'
1175+
// Rule : default set to 'true' if not provided in config
1176+
func peerChannelConfigHookFunc() mapstructure.DecodeHookFunc {
1177+
return func(
1178+
f reflect.Type,
1179+
t reflect.Type,
1180+
data interface{}) (interface{}, error) {
1181+
1182+
//If target is of type 'fab.PeerChannelConfig', then only hook should work
1183+
if t == reflect.TypeOf(fab.PeerChannelConfig{}) {
1184+
dataMap, ok := data.(map[string]interface{})
1185+
if ok {
1186+
setDefault(dataMap, "endorsingpeer", true)
1187+
setDefault(dataMap, "chaincodequery", true)
1188+
setDefault(dataMap, "ledgerquery", true)
1189+
setDefault(dataMap, "eventsource", true)
1190+
1191+
return dataMap, nil
1192+
}
1193+
}
1194+
1195+
return data, nil
1196+
}
1197+
}
1198+
1199+
//setDefault sets default value provided to map if given key not found
1200+
func setDefault(dataMap map[string]interface{}, key string, defaultVal bool) {
1201+
_, ok := dataMap[key]
1202+
if !ok {
1203+
dataMap[key] = true
1204+
}
1205+
}

pkg/fab/endpointconfig_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const (
4040
configPemTestFilePath = "../core/config/testdata/config_test_pem.yaml"
4141
configEmbeddedUsersTestFilePath = "../core/config/testdata/config_test_embedded_pems.yaml"
4242
configType = "yaml"
43+
orgChannelID = "orgchannel"
4344
)
4445

4546
var configBackend core.ConfigBackend
@@ -1256,3 +1257,59 @@ func TestTLSClientCertsNoCerts(t *testing.T) {
12561257
t.Fatalf("Actual cert is not equal to empty cert")
12571258
}
12581259
}
1260+
1261+
func TestPeerChannelConfig(t *testing.T) {
1262+
//get custom backend and tamper orgchannel values for test
1263+
backend := getCustomBackend()
1264+
tamperPeerChannelConfig(backend)
1265+
1266+
//get endpoint config
1267+
config, err := ConfigFromBackend(backend)
1268+
if err != nil {
1269+
t.Fatal(err)
1270+
}
1271+
1272+
//get network config
1273+
networkConfig, err := config.NetworkConfig()
1274+
if err != nil {
1275+
t.Fatal(err)
1276+
}
1277+
1278+
//Test if channels config are working as expected, with time values parsed properly
1279+
assert.True(t, len(networkConfig.Channels) == 3)
1280+
assert.True(t, len(networkConfig.Channels["mychannel"].Peers) == 1)
1281+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.MinResponses == 1)
1282+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.MaxTargets == 1)
1283+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.RetryOpts.MaxBackoff.String() == (5*time.Second).String())
1284+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.RetryOpts.InitialBackoff.String() == (500*time.Millisecond).String())
1285+
assert.True(t, networkConfig.Channels["mychannel"].Policies.QueryChannelConfig.RetryOpts.BackoffFactor == 2.0)
1286+
1287+
//Test if custom hook for (default=true) func is working
1288+
assert.True(t, len(networkConfig.Channels[orgChannelID].Peers) == 2)
1289+
//test orgchannel peer1 (EndorsingPeer should be true as set, remaining should be default = true)
1290+
orgChannelPeer1 := networkConfig.Channels[orgChannelID].Peers["peer0.org1.example.com"]
1291+
assert.True(t, orgChannelPeer1.EndorsingPeer)
1292+
assert.True(t, orgChannelPeer1.LedgerQuery)
1293+
assert.True(t, orgChannelPeer1.EventSource)
1294+
assert.True(t, orgChannelPeer1.ChaincodeQuery)
1295+
1296+
//test orgchannel peer1 (EndorsingPeer should be false as set, remaining should be default = true)
1297+
orgChannelPeer2 := networkConfig.Channels[orgChannelID].Peers["peer0.org2.example.com"]
1298+
assert.False(t, orgChannelPeer2.EndorsingPeer)
1299+
assert.True(t, orgChannelPeer2.LedgerQuery)
1300+
assert.True(t, orgChannelPeer2.EventSource)
1301+
assert.True(t, orgChannelPeer2.ChaincodeQuery)
1302+
1303+
}
1304+
1305+
func tamperPeerChannelConfig(backend *mocks.MockConfigBackend) {
1306+
channelsMap := backend.KeyValueMap["channels"]
1307+
orgChannel := map[string]interface{}{
1308+
"orderers": []string{"orderer.example.com"},
1309+
"peers": map[string]interface{}{
1310+
"peer0.org1.example.com": map[string]interface{}{"endorsingpeer": true},
1311+
"peer0.org2.example.com": map[string]interface{}{"endorsingpeer": false},
1312+
},
1313+
}
1314+
(channelsMap.(map[string]interface{}))[orgChannelID] = orgChannel
1315+
}

0 commit comments

Comments
 (0)