Skip to content

Commit 3b9cc55

Browse files
author
Jason Yellick
committed
[FAB-2262] Split configtx manager
https://jira.hyperledger.org/browse/FAB-2262 The configtx manager.go file has gotten large and unweildly. As preparation for supporting partial updates, and to facilitate the development of tests, this CR simply splits the manager.go file into manager.go, update.go, and config.go. This CR contains no functional changes. Change-Id: I21db6dee4ed5cf8819050ee8c04cbe123b76040c Signed-off-by: Jason Yellick <[email protected]>
1 parent 9381acb commit 3b9cc55

File tree

3 files changed

+275
-228
lines changed

3 files changed

+275
-228
lines changed

common/configtx/config.go

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
Copyright IBM Corp. 2016-2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package configtx
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/hyperledger/fabric/common/configtx/api"
23+
cb "github.com/hyperledger/fabric/protos/common"
24+
)
25+
26+
type configResult struct {
27+
handler api.Transactional
28+
subResults []*configResult
29+
}
30+
31+
func (cr *configResult) commit() {
32+
for _, subResult := range cr.subResults {
33+
subResult.commit()
34+
}
35+
cr.handler.CommitConfig()
36+
}
37+
38+
func (cr *configResult) rollback() {
39+
for _, subResult := range cr.subResults {
40+
subResult.rollback()
41+
}
42+
cr.handler.RollbackConfig()
43+
}
44+
45+
// proposeGroup proposes a group configuration with a given handler
46+
// it will in turn recursively call itself until all groups have been exhausted
47+
// at each call, it returns the handler that was passed in, plus any handlers returned
48+
// by recursive calls into proposeGroup
49+
func (cm *configManager) proposeGroup(name string, group *cb.ConfigGroup, handler api.Handler) (*configResult, error) {
50+
subGroups := make([]string, len(group.Groups))
51+
i := 0
52+
for subGroup := range group.Groups {
53+
subGroups[i] = subGroup
54+
i++
55+
}
56+
57+
logger.Debugf("Beginning new config for channel %s and group %s", cm.chainID, name)
58+
subHandlers, err := handler.BeginConfig(subGroups)
59+
if err != nil {
60+
return nil, err
61+
}
62+
63+
if len(subHandlers) != len(subGroups) {
64+
return nil, fmt.Errorf("Programming error, did not return as many handlers as groups %d vs %d", len(subHandlers), len(subGroups))
65+
}
66+
67+
result := &configResult{
68+
handler: handler,
69+
subResults: make([]*configResult, 0, len(subGroups)),
70+
}
71+
72+
for i, subGroup := range subGroups {
73+
subResult, err := cm.proposeGroup(name+"/"+subGroup, group.Groups[subGroup], subHandlers[i])
74+
if err != nil {
75+
result.rollback()
76+
return nil, err
77+
}
78+
result.subResults = append(result.subResults, subResult)
79+
}
80+
81+
for key, value := range group.Values {
82+
if err := handler.ProposeConfig(key, value); err != nil {
83+
result.rollback()
84+
return nil, err
85+
}
86+
}
87+
88+
return result, nil
89+
}
90+
91+
func (cm *configManager) proposePolicies(rootGroup *cb.ConfigGroup) (*configResult, error) {
92+
cm.initializer.PolicyHandler().BeginConfig(nil) // XXX temporary workaround until policy manager is adapted with sub-policies
93+
94+
for key, policy := range rootGroup.Policies {
95+
logger.Debugf("Proposing policy: %s", key)
96+
if err := cm.initializer.PolicyHandler().ProposePolicy(key, []string{RootGroupKey}, policy); err != nil {
97+
cm.initializer.PolicyHandler().RollbackConfig()
98+
return nil, err
99+
}
100+
}
101+
102+
return &configResult{handler: cm.initializer.PolicyHandler()}, nil
103+
}
104+
105+
func (cm *configManager) processConfig(channelGroup *cb.ConfigGroup) (*configResult, error) {
106+
helperGroup := cb.NewConfigGroup()
107+
helperGroup.Groups[RootGroupKey] = channelGroup
108+
groupResult, err := cm.proposeGroup("", helperGroup, cm.initializer)
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
policyResult, err := cm.proposePolicies(channelGroup)
114+
if err != nil {
115+
groupResult.rollback()
116+
return nil, err
117+
}
118+
policyResult.subResults = []*configResult{groupResult}
119+
120+
return policyResult, nil
121+
}

common/configtx/manager.go

-228
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ import (
2121
"regexp"
2222

2323
"github.com/hyperledger/fabric/common/configtx/api"
24-
"github.com/hyperledger/fabric/common/policies"
2524
cb "github.com/hyperledger/fabric/protos/common"
26-
"github.com/hyperledger/fabric/protos/utils"
2725

2826
logging "github.com/op/go-logging"
2927
)
@@ -43,25 +41,6 @@ var (
4341
// NewConfigItemPolicyKey is the ID of the policy used when no other policy can be resolved, for instance when attempting to create a new config item
4442
const NewConfigItemPolicyKey = "NewConfigItemPolicy"
4543

46-
type configResult struct {
47-
handler api.Transactional
48-
subResults []*configResult
49-
}
50-
51-
func (cr *configResult) commit() {
52-
for _, subResult := range cr.subResults {
53-
subResult.commit()
54-
}
55-
cr.handler.CommitConfig()
56-
}
57-
58-
func (cr *configResult) rollback() {
59-
for _, subResult := range cr.subResults {
60-
subResult.rollback()
61-
}
62-
cr.handler.RollbackConfig()
63-
}
64-
6544
type configManager struct {
6645
api.Resources
6746
sequence uint64
@@ -166,213 +145,6 @@ func (cm *configManager) commitCallbacks() {
166145
}
167146
}
168147

169-
// proposeGroup proposes a group configuration with a given handler
170-
// it will in turn recursively call itself until all groups have been exhausted
171-
// at each call, it returns the handler that was passed in, plus any handlers returned
172-
// by recursive calls into proposeGroup
173-
func (cm *configManager) proposeGroup(name string, group *cb.ConfigGroup, handler api.Handler) (*configResult, error) {
174-
subGroups := make([]string, len(group.Groups))
175-
i := 0
176-
for subGroup := range group.Groups {
177-
subGroups[i] = subGroup
178-
i++
179-
}
180-
181-
logger.Debugf("Beginning new config for channel %s and group %s", cm.chainID, name)
182-
subHandlers, err := handler.BeginConfig(subGroups)
183-
if err != nil {
184-
return nil, err
185-
}
186-
187-
if len(subHandlers) != len(subGroups) {
188-
return nil, fmt.Errorf("Programming error, did not return as many handlers as groups %d vs %d", len(subHandlers), len(subGroups))
189-
}
190-
191-
result := &configResult{
192-
handler: handler,
193-
subResults: make([]*configResult, 0, len(subGroups)),
194-
}
195-
196-
for i, subGroup := range subGroups {
197-
subResult, err := cm.proposeGroup(name+"/"+subGroup, group.Groups[subGroup], subHandlers[i])
198-
if err != nil {
199-
result.rollback()
200-
return nil, err
201-
}
202-
result.subResults = append(result.subResults, subResult)
203-
}
204-
205-
for key, value := range group.Values {
206-
if err := handler.ProposeConfig(key, value); err != nil {
207-
result.rollback()
208-
return nil, err
209-
}
210-
}
211-
212-
return result, nil
213-
}
214-
215-
func (cm *configManager) proposePolicies(rootGroup *cb.ConfigGroup) (*configResult, error) {
216-
cm.initializer.PolicyHandler().BeginConfig(nil) // XXX temporary workaround until policy manager is adapted with sub-policies
217-
218-
for key, policy := range rootGroup.Policies {
219-
logger.Debugf("Proposing policy: %s", key)
220-
if err := cm.initializer.PolicyHandler().ProposePolicy(key, []string{RootGroupKey}, policy); err != nil {
221-
cm.initializer.PolicyHandler().RollbackConfig()
222-
return nil, err
223-
}
224-
}
225-
226-
return &configResult{handler: cm.initializer.PolicyHandler()}, nil
227-
}
228-
229-
// authorizeUpdate validates that all modified config has the corresponding modification policies satisfied by the signature set
230-
// it returns a map of the modified config
231-
func (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelope) (map[string]comparable, error) {
232-
if configUpdateEnv == nil {
233-
return nil, fmt.Errorf("Cannot process nil ConfigUpdateEnvelope")
234-
}
235-
236-
config, err := UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
237-
if err != nil {
238-
return nil, err
239-
}
240-
241-
if config.Header == nil {
242-
return nil, fmt.Errorf("Must have header set")
243-
}
244-
245-
seq := computeSequence(config.WriteSet)
246-
if err != nil {
247-
return nil, err
248-
}
249-
250-
signedData, err := configUpdateEnv.AsSignedData()
251-
if err != nil {
252-
return nil, err
253-
}
254-
255-
// Verify config is a sequential update to prevent exhausting sequence numbers
256-
if seq != cm.sequence+1 {
257-
return nil, fmt.Errorf("Config sequence number jumped from %d to %d", cm.sequence, seq)
258-
}
259-
260-
// Verify config is intended for this globally unique chain ID
261-
if config.Header.ChannelId != cm.chainID {
262-
return nil, fmt.Errorf("Config is for the wrong chain, expected %s, got %s", cm.chainID, config.Header.ChannelId)
263-
}
264-
265-
configMap, err := mapConfig(config.WriteSet)
266-
if err != nil {
267-
return nil, err
268-
}
269-
for key, value := range configMap {
270-
logger.Debugf("Processing key %s with value %v", key, value)
271-
if key == "[Groups] /Channel" {
272-
// XXX temporary hack to prevent group evaluation for modification
273-
continue
274-
}
275-
276-
// Ensure the config sequence numbers are correct to prevent replay attacks
277-
var isModified bool
278-
279-
oldValue, ok := cm.config[key]
280-
if ok {
281-
isModified = !value.equals(oldValue)
282-
} else {
283-
if value.version() != seq {
284-
return nil, fmt.Errorf("Key %v was new, but had an older Sequence %d set", key, value.version())
285-
}
286-
isModified = true
287-
}
288-
289-
// If a config item was modified, its Version must be set correctly, and it must satisfy the modification policy
290-
if isModified {
291-
logger.Debugf("Proposed config item %s on channel %s has been modified", key, cm.chainID)
292-
293-
if value.version() != seq {
294-
return nil, fmt.Errorf("Key %s was modified, but its Version %d does not equal current configtx Sequence %d", key, value.version(), seq)
295-
}
296-
297-
// Get the modification policy for this config item if one was previously specified
298-
// or accept it if it is new, as the group policy will be evaluated for its inclusion
299-
var policy policies.Policy
300-
if ok {
301-
policy, _ = cm.PolicyManager().GetPolicy(oldValue.modPolicy())
302-
// Ensure the policy is satisfied
303-
if err = policy.Evaluate(signedData); err != nil {
304-
return nil, err
305-
}
306-
}
307-
308-
}
309-
}
310-
311-
// Ensure that any config items which used to exist still exist, to prevent implicit deletion
312-
for key, _ := range cm.config {
313-
_, ok := configMap[key]
314-
if !ok {
315-
return nil, fmt.Errorf("Missing key %v in new config", key)
316-
}
317-
318-
}
319-
320-
return cm.computeUpdateResult(configMap), nil
321-
}
322-
323-
// computeUpdateResult takes a configMap generated by an update and produces a new configMap overlaying it onto the old config
324-
func (cm *configManager) computeUpdateResult(updatedConfig map[string]comparable) map[string]comparable {
325-
newConfigMap := make(map[string]comparable)
326-
for key, value := range cm.config {
327-
newConfigMap[key] = value
328-
}
329-
330-
for key, value := range updatedConfig {
331-
newConfigMap[key] = value
332-
}
333-
return newConfigMap
334-
}
335-
336-
func (cm *configManager) processConfig(channelGroup *cb.ConfigGroup) (*configResult, error) {
337-
helperGroup := cb.NewConfigGroup()
338-
helperGroup.Groups[RootGroupKey] = channelGroup
339-
groupResult, err := cm.proposeGroup("", helperGroup, cm.initializer)
340-
if err != nil {
341-
return nil, err
342-
}
343-
344-
policyResult, err := cm.proposePolicies(channelGroup)
345-
if err != nil {
346-
groupResult.rollback()
347-
return nil, err
348-
}
349-
policyResult.subResults = []*configResult{groupResult}
350-
351-
return policyResult, nil
352-
}
353-
354-
func envelopeToConfigUpdate(configtx *cb.Envelope) (*cb.ConfigUpdateEnvelope, error) {
355-
payload, err := utils.UnmarshalPayload(configtx.Payload)
356-
if err != nil {
357-
return nil, err
358-
}
359-
360-
if payload.Header == nil || payload.Header.ChannelHeader == nil {
361-
return nil, fmt.Errorf("Envelope must have ChannelHeader")
362-
}
363-
364-
if payload.Header == nil || payload.Header.ChannelHeader.Type != int32(cb.HeaderType_CONFIG_UPDATE) {
365-
return nil, fmt.Errorf("Not a tx of type CONFIG_UPDATE")
366-
}
367-
368-
configUpdateEnv, err := UnmarshalConfigUpdateEnvelope(payload.Data)
369-
if err != nil {
370-
return nil, fmt.Errorf("Error unmarshaling ConfigUpdateEnvelope: %s", err)
371-
}
372-
373-
return configUpdateEnv, nil
374-
}
375-
376148
// Validate attempts to validate a new configtx against the current config state
377149
func (cm *configManager) Validate(configtx *cb.Envelope) error {
378150
configUpdateEnv, err := envelopeToConfigUpdate(configtx)

0 commit comments

Comments
 (0)