Skip to content

Commit 1874d35

Browse files
author
Jason Yellick
committed
[FAB-6832] Add peer resource config bundlesource
This CR mimics the channel config bundle, and adds a peer resource config bundle source which provides access to an underlying set of channel config bundle and peer resource config bundle. The underlying data structures are updateable via an atomic pointer assignment, and stable views of the current configuration (totaling both the channel configuration and the peer resource configuration) may be obtained. Change-Id: I84646c526b256aa140d23f4d7982c91dd67b0aa3 Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
1 parent ecabe49 commit 1874d35

File tree

7 files changed

+260
-52
lines changed

7 files changed

+260
-52
lines changed

common/resourcesconfig/bundle.go

Lines changed: 87 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import (
1010
"fmt"
1111

1212
"github.com/hyperledger/fabric/common/cauthdsl"
13+
"github.com/hyperledger/fabric/common/channelconfig"
1314
"github.com/hyperledger/fabric/common/configtx"
1415
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
1516
"github.com/hyperledger/fabric/common/flogging"
1617
"github.com/hyperledger/fabric/common/policies"
17-
"github.com/hyperledger/fabric/msp"
1818
cb "github.com/hyperledger/fabric/protos/common"
1919
"github.com/hyperledger/fabric/protos/utils"
20+
21+
"github.com/pkg/errors"
2022
)
2123

2224
// RootGroupKey is the namespace in the config tree for this set of config
@@ -26,29 +28,18 @@ var logger = flogging.MustGetLogger("common/config/resource")
2628

2729
// Bundle stores an immutable group of resources configuration
2830
type Bundle struct {
29-
rg *resourceGroup
30-
cm configtxapi.Manager
31-
pm policies.Manager
32-
rpm policies.Manager
31+
channelID string
32+
resConf *cb.Config
33+
chanConf channelconfig.Resources
34+
rg *resourceGroup
35+
cm configtxapi.Manager
36+
pm *policyRouter
3337
}
3438

35-
// New creates a new resources config bundle
36-
// TODO, change interface to take config and not an envelope
39+
// NewBundleFromEnvelope creates a new resources config bundle.
40+
// TODO, this method should probably be removed, as the resourcesconfig is never in an Envelope naturally.
3741
// TODO, add an atomic BundleSource
38-
func New(envConfig *cb.Envelope, mspManager msp.MSPManager, channelPolicyManager policies.Manager) (*Bundle, error) {
39-
policyProviderMap := make(map[int32]policies.Provider)
40-
for pType := range cb.Policy_PolicyType_name {
41-
rtype := cb.Policy_PolicyType(pType)
42-
switch rtype {
43-
case cb.Policy_UNKNOWN:
44-
// Do not register a handler
45-
case cb.Policy_SIGNATURE:
46-
policyProviderMap[pType] = cauthdsl.NewPolicyProvider(mspManager)
47-
case cb.Policy_MSP:
48-
// Add hook for MSP Handler here
49-
}
50-
}
51-
42+
func NewBundleFromEnvelope(envConfig *cb.Envelope, chanConf channelconfig.Resources) (*Bundle, error) {
5243
payload, err := utils.UnmarshalPayload(envConfig.Payload)
5344
if err != nil {
5445
return nil, err
@@ -63,31 +54,28 @@ func New(envConfig *cb.Envelope, mspManager msp.MSPManager, channelPolicyManager
6354
return nil, fmt.Errorf("config is nil")
6455
}
6556

66-
resourcesPolicyManager, err := policies.NewManagerImpl(RootGroupKey, policyProviderMap, configEnvelope.Config.ChannelGroup)
57+
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
6758
if err != nil {
68-
return nil, err
59+
return nil, errors.Wrap(err, "failed to unmarshal channel header")
6960
}
7061

71-
resourceGroup, err := newResourceGroup(configEnvelope.Config.ChannelGroup)
62+
return NewBundle(chdr.ChannelId, configEnvelope.Config, chanConf)
63+
}
64+
65+
// NewBundle creates a resources config bundle which implements the Resources interface.
66+
func NewBundle(channelID string, config *cb.Config, chanConf channelconfig.Resources) (*Bundle, error) {
67+
resourceGroup, err := newResourceGroup(config.ChannelGroup)
7268
if err != nil {
7369
return nil, err
7470
}
7571

7672
b := &Bundle{
77-
rpm: resourcesPolicyManager,
78-
rg: resourceGroup,
79-
pm: &policyRouter{
80-
channelPolicyManager: channelPolicyManager,
81-
resourcesPolicyManager: resourcesPolicyManager,
82-
},
83-
}
84-
85-
b.cm, err = configtx.NewManagerImpl(envConfig, b)
86-
if err != nil {
87-
return nil, err
73+
channelID: channelID,
74+
rg: resourceGroup,
75+
resConf: config,
8876
}
8977

90-
return b, nil
78+
return b.NewFromChannelConfig(chanConf)
9179
}
9280

9381
// RootGroupKey returns the name of the key for the root group (the namespace for this config).
@@ -114,3 +102,66 @@ func (b *Bundle) APIPolicyMapper() PolicyMapper {
114102
func (b *Bundle) ChaincodeRegistry() ChaincodeRegistry {
115103
return b.rg.chaincodesGroup
116104
}
105+
106+
// ChannelConfig returns the channel config which this resources config depends on.
107+
// Note, consumers of the resourcesconfig should almost never refer to the PolicyManager
108+
// within the channel config, and should instead refer to the PolicyManager exposed by
109+
// this Bundle.
110+
func (b *Bundle) ChannelConfig() channelconfig.Resources {
111+
return b.chanConf
112+
}
113+
114+
// ValidateNew is currently a no-op. The idea is that there may be additional checking which needs to be done
115+
// between an old resource configuration and a new one. In the channel resource configuration case, we add some
116+
// checks to make sure that the MSP IDs have not changed, and that the consensus type has not changed. There is
117+
// currently no such check required in the peer resources, but, in the interest of following the pattern established
118+
// by the channel configuration, it is included here.
119+
func (b *Bundle) ValidateNew(resources Resources) error {
120+
return nil
121+
}
122+
123+
// NewFromChannelConfig builds a new Bundle, based on this Bundle, but with a different underlying
124+
// channel config. This is usually invoked when a channel config update is processed.
125+
func (b *Bundle) NewFromChannelConfig(chanConf channelconfig.Resources) (*Bundle, error) {
126+
policyProviderMap := make(map[int32]policies.Provider)
127+
for pType := range cb.Policy_PolicyType_name {
128+
rtype := cb.Policy_PolicyType(pType)
129+
switch rtype {
130+
case cb.Policy_UNKNOWN:
131+
// Do not register a handler
132+
case cb.Policy_SIGNATURE:
133+
policyProviderMap[pType] = cauthdsl.NewPolicyProvider(chanConf.MSPManager())
134+
case cb.Policy_MSP:
135+
// Add hook for MSP Handler here
136+
}
137+
}
138+
139+
resourcesPolicyManager, err := policies.NewManagerImpl(RootGroupKey, policyProviderMap, b.resConf.ChannelGroup)
140+
141+
if err != nil {
142+
return nil, err
143+
}
144+
145+
result := &Bundle{
146+
channelID: b.channelID,
147+
chanConf: chanConf,
148+
pm: &policyRouter{
149+
channelPolicyManager: chanConf.PolicyManager(),
150+
resourcesPolicyManager: resourcesPolicyManager,
151+
},
152+
rg: b.rg,
153+
resConf: b.resConf,
154+
}
155+
156+
env, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, b.channelID, nil, &cb.ConfigEnvelope{Config: b.resConf}, 0, 0)
157+
if err != nil {
158+
return nil, errors.Wrap(err, "creating envelope for configtx manager failed")
159+
}
160+
161+
result.cm, err = configtx.NewManagerImpl(env, b)
162+
if err != nil {
163+
return nil, err
164+
}
165+
166+
return result, nil
167+
}

common/resourcesconfig/bundle_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package resourcesconfig
99
import (
1010
"testing"
1111

12+
mockchannelconfig "github.com/hyperledger/fabric/common/mocks/config"
1213
cb "github.com/hyperledger/fabric/protos/common"
1314
"github.com/hyperledger/fabric/protos/utils"
1415

@@ -32,15 +33,17 @@ func TestBundle(t *testing.T) {
3233
}, 0, 0)
3334
assert.NoError(t, err)
3435

35-
b, err := New(env, nil, nil)
36+
b, err := NewBundleFromEnvelope(env, &mockchannelconfig.Resources{})
3637
assert.NoError(t, err)
3738
assert.NotNil(t, b)
3839

3940
assert.Equal(t, b.RootGroupKey(), RootGroupKey)
4041
assert.NotNil(t, b.ConfigtxManager())
4142
assert.NotNil(t, b.PolicyManager())
4243
assert.NotNil(t, b.APIPolicyMapper())
44+
assert.NotNil(t, b.ChannelConfig())
4345
assert.NotNil(t, b.ChaincodeRegistry())
46+
assert.Nil(t, b.ValidateNew(nil))
4447
}
4548

4649
func TestBundleFailure(t *testing.T) {
@@ -55,7 +58,7 @@ func TestBundleFailure(t *testing.T) {
5558
}, 0, 0)
5659
assert.NoError(t, err)
5760

58-
b, err := New(env, nil, nil)
61+
b, err := NewBundleFromEnvelope(env, &mockchannelconfig.Resources{})
5962
assert.Error(t, err)
6063
assert.Nil(t, b)
6164
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package resourcesconfig
8+
9+
import (
10+
"sync/atomic"
11+
12+
"github.com/hyperledger/fabric/common/channelconfig"
13+
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
14+
"github.com/hyperledger/fabric/common/policies"
15+
)
16+
17+
// BundleSource stores a reference to the current peer resource configuration bundle
18+
// It also provides a method to update this bundle. The assorted methods of BundleSource
19+
// largely pass through to the underlying bundle, but do so through an atomic pointer
20+
// so that cross go-routine reads are not vulnerable to out-of-order execution memory
21+
// type bugs.
22+
type BundleSource struct {
23+
bundle atomic.Value
24+
callbacks []func(*Bundle)
25+
}
26+
27+
// NewBundleSource creates a new BundleSource with an initial Bundle value
28+
// The callbacks will be invoked whenever the Update method is called for the
29+
// BundleSource. Note, these callbacks are called immediately before this function
30+
// returns.
31+
func NewBundleSource(bundle *Bundle, callbacks ...func(*Bundle)) *BundleSource {
32+
bs := &BundleSource{
33+
callbacks: callbacks,
34+
}
35+
bs.Update(bundle)
36+
return bs
37+
}
38+
39+
// Update sets a new bundle as the bundle source and calls any registered callbacks
40+
func (bs *BundleSource) Update(newBundle *Bundle) {
41+
bs.bundle.Store(newBundle)
42+
for _, callback := range bs.callbacks {
43+
callback(newBundle)
44+
}
45+
}
46+
47+
// StableBundle returns a pointer to a stable Bundle.
48+
// It is stable because calls to its assorted methods will always return the same
49+
// result, as the underlying data structures are immutable. For instance, calling
50+
// BundleSource.PolicyManager() and BundleSource.APIPolicyMapper() to get first the
51+
// the policy manager and then references into that policy manager could result in a
52+
// bug because an update might replace the underlying Bundle in between. Therefore,
53+
// for operations which require consistency between the Bundle calls, the caller
54+
// should first retrieve a StableBundle, then operate on it.
55+
func (bs *BundleSource) StableBundle() *Bundle {
56+
return bs.bundle.Load().(*Bundle)
57+
}
58+
59+
// ConfigtxManager returns a reference to a configtx.Manager which can process updates to this config.
60+
func (bs *BundleSource) ConfigtxManager() configtxapi.Manager {
61+
return bs.StableBundle().ConfigtxManager()
62+
}
63+
64+
// PolicyManager returns a policy manager which can resolve names both in the /Channel and /Resources namespaces.
65+
func (bs *BundleSource) PolicyManager() policies.Manager {
66+
return bs.StableBundle().PolicyManager()
67+
}
68+
69+
// APIPolicyMapper returns a way to map API names to policies governing their invocation.
70+
func (bs *BundleSource) APIPolicyMapper() PolicyMapper {
71+
return bs.StableBundle().APIPolicyMapper()
72+
}
73+
74+
// ChaincodeRegistery returns a way to query for chaincodes defined in this channel.
75+
func (bs *BundleSource) ChaincodeRegistry() ChaincodeRegistry {
76+
return bs.StableBundle().ChaincodeRegistry()
77+
}
78+
79+
// ChannelConfig returns the channel config which this resources config depends on.
80+
// Note, consumers of the resourcesconfig should almost never refer to the PolicyManager
81+
// within the channel config, and should instead refer to the PolicyManager exposed by
82+
// this Bundle.
83+
func (bs *BundleSource) ChannelConfig() channelconfig.Resources {
84+
return bs.StableBundle().ChannelConfig()
85+
}
86+
87+
// ValidateNew passes through to the current bundle
88+
func (bs *BundleSource) ValidateNew(resources Resources) error {
89+
return bs.StableBundle().ValidateNew(resources)
90+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package resourcesconfig
8+
9+
import (
10+
"testing"
11+
12+
mockchannelconfig "github.com/hyperledger/fabric/common/mocks/config"
13+
cb "github.com/hyperledger/fabric/protos/common"
14+
"github.com/hyperledger/fabric/protos/utils"
15+
16+
"github.com/stretchr/testify/assert"
17+
)
18+
19+
func TestBundleSource(t *testing.T) {
20+
env, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, "foo", nil, &cb.ConfigEnvelope{
21+
Config: &cb.Config{
22+
ChannelGroup: sampleResourceGroup,
23+
},
24+
}, 0, 0)
25+
assert.NoError(t, err)
26+
27+
b, err := NewBundleFromEnvelope(env, &mockchannelconfig.Resources{})
28+
assert.NoError(t, err)
29+
assert.NotNil(t, b)
30+
31+
calledBack := false
32+
var calledBackWith *Bundle
33+
34+
bs := NewBundleSource(b, func(ib *Bundle) {
35+
calledBack = true
36+
calledBackWith = ib
37+
})
38+
39+
assert.True(t, calledBack)
40+
assert.Equal(t, b, calledBackWith)
41+
42+
assert.Equal(t, b.ConfigtxManager(), bs.ConfigtxManager())
43+
assert.Equal(t, b.PolicyManager(), bs.PolicyManager())
44+
assert.Equal(t, b.APIPolicyMapper(), bs.APIPolicyMapper())
45+
assert.Equal(t, b.ChannelConfig(), bs.ChannelConfig())
46+
assert.Equal(t, b.ChaincodeRegistry(), bs.ChaincodeRegistry())
47+
assert.Equal(t, b.ValidateNew(nil), bs.ValidateNew(nil))
48+
49+
calledBack = false
50+
nb := &Bundle{}
51+
bs.Update(nb)
52+
assert.True(t, calledBack)
53+
assert.Equal(t, nb, calledBackWith)
54+
}

common/resourcesconfig/resourcesconfig.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0
77
package resourcesconfig
88

99
import (
10+
"github.com/hyperledger/fabric/common/channelconfig"
1011
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
1112
"github.com/hyperledger/fabric/common/policies"
1213
)
@@ -53,11 +54,16 @@ type Resources interface {
5354
ConfigtxManager() configtxapi.Manager
5455

5556
// PolicyManager returns a policy manager which can resolve names both in the /Channel and /Resources namespaces.
57+
// Note, the result of this method is almost definitely the one you want. Calling ChannelConfig().PolicyManager()
58+
// will return a policy manager which can only resolve policies in the /Channel namespace.
5659
PolicyManager() policies.Manager
5760

5861
// APIPolicyMapper returns a way to map API names to policies governing their invocation.
5962
APIPolicyMapper() PolicyMapper
6063

6164
// ChaincodeRegistery returns a way to query for chaincodes defined in this channel.
6265
ChaincodeRegistry() ChaincodeRegistry
66+
67+
// ChannelConfig returns the channelconfig.Resources which this config depends on.
68+
ChannelConfig() channelconfig.Resources
6369
}

core/peer/peer.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,17 @@ func GetLedger(cid string) ledger.PeerLedger {
411411
return nil
412412
}
413413

414+
// GetChannelConfig returns the channel configuration of the chain with channel ID. Note that this
415+
// call returns nil if chain cid has not been created.
416+
func GetChannelConfig(cid string) channelconfig.Resources {
417+
chains.RLock()
418+
defer chains.RUnlock()
419+
if c, ok := chains.list[cid]; ok {
420+
return c.cs
421+
}
422+
return nil
423+
}
424+
414425
// GetPolicyManager returns the policy manager of the chain with chain ID. Note that this
415426
// call returns nil if chain cid has not been created.
416427
func GetPolicyManager(cid string) policies.Manager {

0 commit comments

Comments
 (0)