Skip to content

Commit

Permalink
[FAB-2319] Implement hierarchical policies storage
Browse files Browse the repository at this point in the history
https://jira.hyperledger.org/browse/FAB-2319

This CR finally removes the hack of only handling policies stored at the
root channel level and creates a policy manager at each level of the
config.  It also rolls up the policy managers so that policy managers at
a higher level may reference policies at a lower level.

Change-Id: I18f616ecfc3929f03defdbe02f8a253d0c6b2f69
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Feb 17, 2017
1 parent fee7c6c commit a971b0f
Show file tree
Hide file tree
Showing 14 changed files with 453 additions and 183 deletions.
12 changes: 6 additions & 6 deletions common/cauthdsl/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ func makePolicySource(policyResult bool) *cb.Policy {
}
}

func addPolicy(manager *policies.ManagerImpl, id string, policy *cb.Policy) {
manager.BeginConfig(nil)
err := manager.ProposePolicy(id, []string{}, &cb.ConfigPolicy{
func addPolicy(manager policies.Proposer, id string, policy *cb.Policy) {
manager.BeginPolicyProposals(nil)
err := manager.ProposePolicy(id, &cb.ConfigPolicy{
Policy: policy,
})
if err != nil {
Expand All @@ -75,7 +75,7 @@ func providerMap() map[int32]policies.Provider {

func TestAccept(t *testing.T) {
policyID := "policyID"
m := policies.NewManagerImpl(providerMap())
m := policies.NewManagerImpl("test", providerMap())
addPolicy(m, policyID, acceptAllPolicy)
policy, ok := m.GetPolicy(policyID)
if !ok {
Expand All @@ -89,7 +89,7 @@ func TestAccept(t *testing.T) {

func TestReject(t *testing.T) {
policyID := "policyID"
m := policies.NewManagerImpl(providerMap())
m := policies.NewManagerImpl("test", providerMap())
addPolicy(m, policyID, rejectAllPolicy)
policy, ok := m.GetPolicy(policyID)
if !ok {
Expand All @@ -102,7 +102,7 @@ func TestReject(t *testing.T) {
}

func TestRejectOnUnknown(t *testing.T) {
m := policies.NewManagerImpl(providerMap())
m := policies.NewManagerImpl("test", providerMap())
policy, ok := m.GetPolicy("FakePolicyID")
if ok {
t.Error("Should not have found policy which was never added, but did")
Expand Down
13 changes: 4 additions & 9 deletions common/configtx/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,11 @@ type PolicyHandler interface {
// Initializer is used as indirection between Manager and Handler to allow
// for single Handlers to handle multiple paths
type Initializer interface {
// ProposeValue is used for propsing group values
ProposeValue(key string, configValue *cb.ConfigValue) error
// ValueProposer return the root value proposer
ValueProposer() configvalues.ValueProposer

// BeginValueProposals is called when a config proposal is begun
BeginValueProposals(groups []string) ([]configvalues.ValueProposer, error)

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

Resources

// PolicyProposer returns the PolicyHandler to handle updates to policy
PolicyHandler() PolicyHandler
}
52 changes: 24 additions & 28 deletions common/configtx/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,37 @@ import (

"github.com/hyperledger/fabric/common/configtx/api"
configvaluesapi "github.com/hyperledger/fabric/common/configvalues/api"
"github.com/hyperledger/fabric/common/policies"
cb "github.com/hyperledger/fabric/protos/common"
)

type configResult struct {
handler api.Transactional
subResults []*configResult
handler api.Transactional
policyHandler api.Transactional
subResults []*configResult
}

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

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

// 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(name string, group *cb.ConfigGroup, handler configvaluesapi.ValueProposer) (*configResult, error) {
func (cm *configManager) proposeGroup(name string, group *cb.ConfigGroup, handler configvaluesapi.ValueProposer, policyHandler policies.Proposer) (*configResult, error) {
subGroups := make([]string, len(group.Groups))
i := 0
for subGroup := range group.Groups {
Expand All @@ -61,17 +65,23 @@ func (cm *configManager) proposeGroup(name string, group *cb.ConfigGroup, handle
return nil, err
}

if len(subHandlers) != len(subGroups) {
return nil, fmt.Errorf("Programming error, did not return as many handlers as groups %d vs %d", len(subHandlers), len(subGroups))
subPolicyHandlers, err := policyHandler.BeginPolicyProposals(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))
}

result := &configResult{
handler: handler,
subResults: make([]*configResult, 0, len(subGroups)),
handler: handler,
policyHandler: policyHandler,
subResults: make([]*configResult, 0, len(subGroups)),
}

for i, subGroup := range subGroups {
subResult, err := cm.proposeGroup(name+"/"+subGroup, group.Groups[subGroup], subHandlers[i])
subResult, err := cm.proposeGroup(name+"/"+subGroup, group.Groups[subGroup], subHandlers[i], subPolicyHandlers[i])
if err != nil {
result.rollback()
return nil, err
Expand All @@ -86,37 +96,23 @@ func (cm *configManager) proposeGroup(name string, group *cb.ConfigGroup, handle
}
}

return result, nil
}

func (cm *configManager) proposePolicies(rootGroup *cb.ConfigGroup) (*configResult, error) {
cm.initializer.PolicyHandler().BeginConfig(nil) // XXX temporary workaround until policy manager is adapted with sub-policies

for key, policy := range rootGroup.Policies {
logger.Debugf("Proposing policy: %s", key)
if err := cm.initializer.PolicyHandler().ProposePolicy(key, []string{RootGroupKey}, policy); err != nil {
cm.initializer.PolicyHandler().RollbackProposals()
for key, policy := range group.Policies {
if err := policyHandler.ProposePolicy(key, policy); err != nil {
result.rollback()
return nil, err
}
}

return &configResult{handler: cm.initializer.PolicyHandler()}, nil
return result, nil
}

func (cm *configManager) processConfig(channelGroup *cb.ConfigGroup) (*configResult, error) {
helperGroup := cb.NewConfigGroup()
helperGroup.Groups[RootGroupKey] = channelGroup
groupResult, err := cm.proposeGroup("", helperGroup, cm.initializer)
if err != nil {
return nil, err
}

policyResult, err := cm.proposePolicies(channelGroup)
groupResult, err := cm.proposeGroup("", helperGroup, cm.initializer.ValueProposer(), cm.initializer.PolicyProposer())
if err != nil {
groupResult.rollback()
return nil, err
}
policyResult.subResults = []*configResult{groupResult}

return policyResult, nil
return groupResult, nil
}
86 changes: 54 additions & 32 deletions common/configtx/initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,70 +84,92 @@ func newResources() *resources {
applicationConfig := configtxapplication.NewSharedConfigImpl(mspConfigHandler)

return &resources{
policyManager: policies.NewManagerImpl(policyProviderMap),
policyManager: policies.NewManagerImpl(RootGroupKey, policyProviderMap),
channelConfig: configtxchannel.NewSharedConfigImpl(ordererConfig, applicationConfig),
ordererConfig: ordererConfig,
applicationConfig: applicationConfig,
mspConfigHandler: mspConfigHandler,
}

}

type initializer struct {
*resources
is map[string]api.Initializer
type valueProposerRoot struct {
channelConfig *configtxchannel.SharedConfigImpl
mspConfigHandler *configtxmsp.MSPConfigHandler
}

// NewInitializer creates a chain initializer for the basic set of common chain resources
func NewInitializer() api.Initializer {
return &initializer{
resources: newResources(),
}
type policyProposerRoot struct {
policyManager policies.Proposer
}

// BeginValueProposals is used to start a new config proposal
func (i *initializer) BeginValueProposals(groups []string) ([]configvaluesapi.ValueProposer, error) {
func (v *valueProposerRoot) BeginValueProposals(groups []string) ([]configvaluesapi.ValueProposer, error) {
if len(groups) != 1 {
logger.Panicf("Initializer only supports having one root group")
}
i.mspConfigHandler.BeginConfig()
return []configvaluesapi.ValueProposer{i.channelConfig}, nil
logger.Debugf("Calling begin for MSP manager")
v.mspConfigHandler.BeginConfig()
return []configvaluesapi.ValueProposer{v.channelConfig}, nil
}

// RollbackConfig is used to abandon a new config proposal
func (i *initializer) RollbackProposals() {
func (i *valueProposerRoot) RollbackProposals() {
logger.Debugf("Calling rollback for MSP manager")
i.mspConfigHandler.RollbackProposals()
}

// CommitConfig is used to commit a new config proposal
func (i *initializer) CommitProposals() {
func (i *valueProposerRoot) CommitProposals() {
logger.Debugf("Calling commit for MSP manager")
i.mspConfigHandler.CommitProposals()
}

type importHack struct {
*policies.ManagerImpl
func (i *valueProposerRoot) ProposeValue(key string, value *cb.ConfigValue) error {
return fmt.Errorf("Programming error, this should never be invoked")
}

func (ih importHack) BeginConfig(groups []string) ([]api.PolicyHandler, error) {
policyManagers, err := ih.ManagerImpl.BeginConfig(groups)
if err != nil {
return nil, err
}
handlers := make([]api.PolicyHandler, len(policyManagers))
for i, policyManager := range policyManagers {
handlers[i] = &importHack{ManagerImpl: policyManager}
// BeginPolicyProposals is used to start a new config proposal
func (p *policyProposerRoot) BeginPolicyProposals(groups []string) ([]policies.Proposer, error) {
if len(groups) != 1 {
logger.Panicf("Initializer only supports having one root group")
}
return handlers, err
return []policies.Proposer{p.policyManager}, nil
}

func (ih importHack) ProposeConfig(key string, value *cb.ConfigValue) error {
return fmt.Errorf("Temporary hack")
func (i *policyProposerRoot) ProposePolicy(key string, policy *cb.ConfigPolicy) error {
return fmt.Errorf("Programming error, this should never be invoked")
}

func (i *initializer) PolicyHandler() api.PolicyHandler {
return importHack{ManagerImpl: i.policyManager}
// RollbackConfig is used to abandon a new config proposal
func (i *policyProposerRoot) RollbackProposals() {}

// CommitConfig is used to commit a new config proposal
func (i *policyProposerRoot) CommitProposals() {}

type initializer struct {
*resources
vpr *valueProposerRoot
ppr *policyProposerRoot
}

func (i *initializer) ProposeValue(key string, value *cb.ConfigValue) error {
return fmt.Errorf("Programming error, this should never be invoked")
// NewInitializer creates a chain initializer for the basic set of common chain resources
func NewInitializer() api.Initializer {
resources := newResources()
return &initializer{
resources: resources,
vpr: &valueProposerRoot{
channelConfig: resources.channelConfig,
mspConfigHandler: resources.mspConfigHandler,
},
ppr: &policyProposerRoot{
policyManager: resources.policyManager,
},
}
}

func (i *initializer) PolicyProposer() policies.Proposer {
return i.ppr
}

func (i *initializer) ValueProposer() configvaluesapi.ValueProposer {
return i.vpr
}
16 changes: 5 additions & 11 deletions common/configtx/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,11 @@ func defaultInitializer() *mockconfigtx.Initializer {
Policy: &mockpolicies.Policy{},
},
},
PolicyHandlerVal: &mockconfigtx.PolicyHandler{
Handler: mockconfigtx.Handler{
Transactional: mockconfigtx.Transactional{
HandlerVal: &mockconfigtx.Handler{},
},
},
PolicyProposerVal: &mockconfigtx.PolicyProposer{
Transactional: mockconfigtx.Transactional{},
},
Handler: mockconfigtx.Handler{
Transactional: mockconfigtx.Transactional{
HandlerVal: &mockconfigtx.Handler{},
},
ValueProposerVal: &mockconfigtx.ValueProposer{
Transactional: mockconfigtx.Transactional{},
},
}
}
Expand Down Expand Up @@ -412,7 +406,7 @@ func TestInvalidProposal(t *testing.T) {
t.Fatalf("Error constructing config manager: %s", err)
}

initializer.HandlerVal = &mockconfigtx.Handler{ErrorForProposeConfig: fmt.Errorf("err")}
initializer.ValueProposerVal = &mockconfigtx.ValueProposer{ErrorForProposeConfig: fmt.Errorf("err")}

newConfig := makeConfigUpdateEnvelope(defaultChain, makeConfigPair("foo", "foo", 1, []byte("foo")))

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit a971b0f

Please sign in to comment.