Skip to content

Commit

Permalink
[FAB-2574] Config parsing outside configtx.Manager
Browse files Browse the repository at this point in the history
https://jira.hyperledger.org/browse/FAB-2574

The configtx.Manager is currently the only place which parses a
configuration transaction (appropriately unmarshaling all of the
config values).

In order to facilitate the inspection of configuration
transactions outside of the context of a full running system, this
CR splits the configuration parsing from the configtx.Manager (although
the configtx.Manager is the only consumer of this code).

Change-Id: I91d7fb880892f4e88cf50bc3ba930f67204dcc52
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Mar 7, 2017
1 parent cf29ef3 commit 093394b
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 58 deletions.
12 changes: 9 additions & 3 deletions common/configtx/api/api.go
Expand Up @@ -84,14 +84,20 @@ type PolicyHandler interface {
ProposePolicy(tx interface{}, key string, path []string, policy *cb.ConfigPolicy) error
}

// Initializer is used as indirection between Manager and Handler to allow
// for single Handlers to handle multiple paths
type Initializer interface {
// Proposer contains the references necesssary to appropriately unmarshal
// a cb.ConfigGroup
type Proposer interface {
// ValueProposer return the root value proposer
ValueProposer() config.ValueProposer

// PolicyProposer return the root policy proposer
PolicyProposer() policies.Proposer
}

// Initializer is used as indirection between Manager and Handler to allow
// for single Handlers to handle multiple paths
type Initializer interface {
Proposer

Resources
}
114 changes: 59 additions & 55 deletions common/configtx/config.go
Expand Up @@ -27,23 +27,14 @@ import (
"github.com/golang/protobuf/proto"
)

type configGroupWrapper struct {
*cb.ConfigGroup
deserializedValues map[string]proto.Message
}

func newConfigGroupWrapper(group *cb.ConfigGroup) *configGroupWrapper {
return &configGroupWrapper{
ConfigGroup: group,
deserializedValues: make(map[string]proto.Message),
}
}

type configResult struct {
tx interface{}
handler api.Transactional
policyHandler api.Transactional
subResults []*configResult
tx interface{}
groupName string
group *cb.ConfigGroup
valueHandler config.ValueProposer
policyHandler policies.Proposer
subResults []*configResult
deserializedValues map[string]proto.Message
}

func (cr *configResult) preCommit() error {
Expand All @@ -53,100 +44,113 @@ func (cr *configResult) preCommit() error {
return err
}
}
return cr.handler.PreCommit(cr.tx)
return cr.valueHandler.PreCommit(cr.tx)
}

func (cr *configResult) commit() {
for _, subResult := range cr.subResults {
subResult.commit()
}
cr.handler.CommitProposals(cr.tx)
cr.valueHandler.CommitProposals(cr.tx)
cr.policyHandler.CommitProposals(cr.tx)
}

func (cr *configResult) rollback() {
for _, subResult := range cr.subResults {
subResult.rollback()
}
cr.handler.RollbackProposals(cr.tx)
cr.valueHandler.RollbackProposals(cr.tx)
cr.policyHandler.RollbackProposals(cr.tx)
}

// proposeGroup proposes a group configuration with a given handler
// it will in turn recursively call itself until all groups have been exhausted
// at each call, it returns the handler that was passed in, plus any handlers returned
// by recursive calls into proposeGroup
func (cm *configManager) proposeGroup(tx interface{}, name string, group *configGroupWrapper, handler config.ValueProposer, policyHandler policies.Proposer) (*configResult, error) {
subGroups := make([]string, len(group.Groups))
// at each call, it updates the configResult to contain references to the handlers
// which have been invoked so that calling result.commit() or result.rollback() will
// appropriately cleanup
func proposeGroup(result *configResult) error {
subGroups := make([]string, len(result.group.Groups))
i := 0
for subGroup := range group.Groups {
for subGroup := range result.group.Groups {
subGroups[i] = subGroup
i++
}

logger.Debugf("Beginning new config for channel %s and group %s", cm.current.channelID, name)
valueDeserializer, subHandlers, err := handler.BeginValueProposals(tx, subGroups)
valueDeserializer, subValueHandlers, err := result.valueHandler.BeginValueProposals(result.tx, subGroups)
if err != nil {
return nil, err
return err
}

subPolicyHandlers, err := policyHandler.BeginPolicyProposals(tx, subGroups)
subPolicyHandlers, err := result.policyHandler.BeginPolicyProposals(result.tx, subGroups)
if err != nil {
return nil, err
}

if len(subHandlers) != len(subGroups) || len(subPolicyHandlers) != len(subGroups) {
return nil, fmt.Errorf("Programming error, did not return as many handlers as groups %d vs %d vs %d", len(subHandlers), len(subGroups), len(subPolicyHandlers))
return err
}

result := &configResult{
tx: tx,
handler: handler,
policyHandler: policyHandler,
subResults: make([]*configResult, 0, len(subGroups)),
if len(subValueHandlers) != len(subGroups) || len(subPolicyHandlers) != len(subGroups) {
return fmt.Errorf("Programming error, did not return as many handlers as groups %d vs %d vs %d", len(subValueHandlers), len(subGroups), len(subPolicyHandlers))
}

for i, subGroup := range subGroups {
subResult, err := cm.proposeGroup(tx, name+"/"+subGroup, newConfigGroupWrapper(group.Groups[subGroup]), subHandlers[i], subPolicyHandlers[i])
for key, value := range result.group.Values {
msg, err := valueDeserializer.Deserialize(key, value.Value)
if err != nil {
result.rollback()
return nil, err
return err
}
result.subResults = append(result.subResults, subResult)
result.deserializedValues[key] = msg
}

for key, value := range group.Values {
msg, err := valueDeserializer.Deserialize(key, value.Value)
if err != nil {
for key, policy := range result.group.Policies {
if err := result.policyHandler.ProposePolicy(result.tx, key, policy); err != nil {
result.rollback()
return nil, err
return err
}
group.deserializedValues[key] = msg
}

for key, policy := range group.Policies {
if err := policyHandler.ProposePolicy(tx, key, policy); err != nil {
result.subResults = make([]*configResult, 0, len(subGroups))

for i, subGroup := range subGroups {
result.subResults = append(result.subResults, &configResult{
tx: result.tx,
groupName: result.groupName + "/" + subGroup,
group: result.group.Groups[subGroup],
valueHandler: subValueHandlers[i],
policyHandler: subPolicyHandlers[i],
deserializedValues: make(map[string]proto.Message),
})

if err := proposeGroup(result.subResults[i]); err != nil {
result.rollback()
return nil, err
return err
}
}

err = result.preCommit()
if err != nil {
result.rollback()
return nil, err
return err
}

return result, nil
return nil
}

func (cm *configManager) processConfig(channelGroup *cb.ConfigGroup) (*configResult, error) {
func processConfig(channelGroup *cb.ConfigGroup, proposer api.Proposer) (*configResult, error) {
helperGroup := cb.NewConfigGroup()
helperGroup.Groups[RootGroupKey] = channelGroup
groupResult, err := cm.proposeGroup(channelGroup, "", newConfigGroupWrapper(helperGroup), cm.initializer.ValueProposer(), cm.initializer.PolicyProposer())

configResult := &configResult{
group: helperGroup,
valueHandler: proposer.ValueProposer(),
policyHandler: proposer.PolicyProposer(),
}
err := proposeGroup(configResult)
if err != nil {
return nil, err
}

return groupResult, nil
return configResult, nil
}

func (cm *configManager) processConfig(channelGroup *cb.ConfigGroup) (*configResult, error) {
logger.Debugf("Beginning new config for channel %s", cm.current.channelID)
return processConfig(channelGroup, cm.initializer)
}

0 comments on commit 093394b

Please sign in to comment.