Skip to content

Commit

Permalink
[FAB-2261] Make Handler creation transactional
Browse files Browse the repository at this point in the history
https://jira.hyperledger.org/browse/FAB-2261

At the organization level, handlers were being instaniated dynamically,
but, the handlers were only being instructed to begin transactions
statically.  This caused a crash when a dynamically created org handler
would be told to process a config item before begin the config
transaction.

This CR reworks the handler instantiation to be done at the beginning of
a transaction.

Change-Id: Ifbd2f9adf5809cb9fca3108573f105db4b0d790a
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Feb 16, 2017
1 parent 7559dd9 commit 3a61f6b
Show file tree
Hide file tree
Showing 18 changed files with 264 additions and 216 deletions.
2 changes: 1 addition & 1 deletion common/cauthdsl/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func makePolicySource(policyResult bool) *cb.Policy {
}

func addPolicy(manager *policies.ManagerImpl, id string, policy *cb.Policy) {
manager.BeginConfig()
manager.BeginConfig(nil)
err := manager.ProposePolicy(id, []string{}, &cb.ConfigPolicy{
Policy: policy,
})
Expand Down
14 changes: 4 additions & 10 deletions common/configtx/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ type OrdererConfig interface {
// Handler provides a hook which allows other pieces of code to participate in config proposals
// TODO, this should probably be renamed to ValueHandler
type Handler interface {
Transactional

// ProposeConfig called when config is added to a proposal
ProposeConfig(key string, configValue *cb.ConfigValue) error
}
Expand Down Expand Up @@ -129,7 +131,7 @@ type Resources interface {
// Transactional is an interface which allows for an update to be proposed and rolled back
type Transactional interface {
// BeginConfig called when a config proposal is begun
BeginConfig()
BeginConfig(groups []string) ([]Handler, error)

// RollbackConfig called when a config proposal is abandoned
RollbackConfig()
Expand All @@ -138,14 +140,6 @@ type Transactional interface {
CommitConfig()
}

// SubInitializer is used downstream from initializer
type SubInitializer interface {
Transactional

// Handler returns the associated value handler for a given config path
Handler(path []string) (Handler, error)
}

// PolicyHandler is used for config updates to policy
type PolicyHandler interface {
Transactional
Expand All @@ -156,7 +150,7 @@ type PolicyHandler interface {
// Initializer is used as indirection between Manager and Handler to allow
// for single Handlers to handle multiple paths
type Initializer interface {
SubInitializer
Handler

Resources

Expand Down
9 changes: 8 additions & 1 deletion common/configtx/handlers/application/organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package application
import (
"fmt"

"github.com/hyperledger/fabric/common/configtx/api"
"github.com/hyperledger/fabric/common/configtx/handlers"
mspconfig "github.com/hyperledger/fabric/common/configtx/handlers/msp"
cb "github.com/hyperledger/fabric/protos/common"
Expand Down Expand Up @@ -62,18 +63,23 @@ func (oc *ApplicationOrgConfig) AnchorPeers() []*pb.AnchorPeer {
}

// BeginConfig is used to start a new config proposal
func (oc *ApplicationOrgConfig) BeginConfig() {
func (oc *ApplicationOrgConfig) BeginConfig(groups []string) ([]api.Handler, error) {
logger.Debugf("Beginning a possible new org config")
if len(groups) != 0 {
return nil, fmt.Errorf("ApplicationGroup does not support subgroups")
}
if oc.pendingConfig != nil {
logger.Panicf("Programming error, cannot call begin in the middle of a proposal")
}
oc.pendingConfig = &applicationOrgConfig{}
return oc.OrgConfig.BeginConfig(groups)
}

// RollbackConfig is used to abandon a new config proposal
func (oc *ApplicationOrgConfig) RollbackConfig() {
logger.Debugf("Rolling back proposed org config")
oc.pendingConfig = nil
oc.OrgConfig.RollbackConfig()
}

// CommitConfig is used to commit a new config proposal
Expand All @@ -84,6 +90,7 @@ func (oc *ApplicationOrgConfig) CommitConfig() {
}
oc.config = oc.pendingConfig
oc.pendingConfig = nil
oc.OrgConfig.CommitConfig()
}

// ProposeConfig is used to add new config to the config proposal
Expand Down
8 changes: 4 additions & 4 deletions common/configtx/handlers/application/organization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ func groupToKeyValue(configGroup *cb.ConfigGroup) (string, *cb.ConfigValue) {
}

func TestApplicationOrgInterface(t *testing.T) {
_ = configtxapi.SubInitializer(NewApplicationOrgConfig("id", nil))
_ = configtxapi.Handler(NewApplicationOrgConfig("id", nil))
}

func TestApplicationOrgDoubleBegin(t *testing.T) {
m := NewApplicationOrgConfig("id", nil)
m.BeginConfig()
assert.Panics(t, m.BeginConfig, "Two begins back to back should have caused a panic")
m.BeginConfig(nil)
assert.Panics(t, func() { m.BeginConfig(nil) }, "Two begins back to back should have caused a panic")
}

func TestApplicationOrgCommitWithoutBegin(t *testing.T) {
Expand All @@ -76,7 +76,7 @@ func TestApplicationOrgAnchorPeers(t *testing.T) {
invalidMessage := makeInvalidConfigValue()
validMessage := TemplateAnchorPeers("id", endVal)
m := NewApplicationOrgConfig("id", nil)
m.BeginConfig()
m.BeginConfig(nil)

assert.Error(t, m.ProposeConfig(AnchorPeersKey, invalidMessage), "Should have failed on invalid message")
assert.NoError(t, m.ProposeConfig(groupToKeyValue(validMessage)), "Should not have failed on invalid message")
Expand Down
32 changes: 11 additions & 21 deletions common/configtx/handlers/application/sharedconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ limitations under the License.
package application

import (
"fmt"

"github.com/hyperledger/fabric/common/configtx/api"
"github.com/hyperledger/fabric/common/configtx/handlers"
"github.com/hyperledger/fabric/common/configtx/handlers/msp"
Expand Down Expand Up @@ -77,14 +75,24 @@ func NewSharedConfigImpl(mspConfig *msp.MSPConfigHandler) *SharedConfigImpl {
}

// BeginConfig is used to start a new config proposal
func (di *SharedConfigImpl) BeginConfig() {
func (di *SharedConfigImpl) BeginConfig(groups []string) ([]api.Handler, error) {
logger.Debugf("Beginning a possible new peer shared config")
if di.pendingConfig != nil {
logger.Panicf("Programming error, cannot call begin in the middle of a proposal")
}
di.pendingConfig = &sharedConfig{
orgs: make(map[string]api.ApplicationOrgConfig),
}
orgHandlers := make([]api.Handler, len(groups))
for i, group := range groups {
org, ok := di.pendingConfig.orgs[group]
if !ok {
org = NewApplicationOrgConfig(group, di.mspConfig)
di.pendingConfig.orgs[group] = org
}
orgHandlers[i] = org.(*ApplicationOrgConfig)
}
return orgHandlers, nil
}

// RollbackConfig is used to abandon a new config proposal
Expand Down Expand Up @@ -113,21 +121,3 @@ func (di *SharedConfigImpl) ProposeConfig(key string, configValue *cb.ConfigValu
func (di *SharedConfigImpl) Organizations() map[string]api.ApplicationOrgConfig {
return di.config.orgs
}

// Handler returns the associated api.Handler for the given path
func (pm *SharedConfigImpl) Handler(path []string) (api.Handler, error) {
if len(path) == 0 {
return pm, nil
}

if len(path) > 1 {
return nil, fmt.Errorf("Application group allows only one further level of nesting")
}

org, ok := pm.pendingConfig.orgs[path[0]]
if !ok {
org = NewApplicationOrgConfig(path[0], pm.mspConfig)
pm.pendingConfig.orgs[path[0]] = org
}
return org.(*ApplicationOrgConfig), nil
}
4 changes: 2 additions & 2 deletions common/configtx/handlers/application/sharedconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ func TestApplicationDoubleBegin(t *testing.T) {
}()

m := NewSharedConfigImpl(nil)
m.BeginConfig()
m.BeginConfig()
m.BeginConfig(nil)
m.BeginConfig(nil)
}

func TestApplicationCommitWithoutBegin(t *testing.T) {
Expand Down
39 changes: 16 additions & 23 deletions common/configtx/handlers/channel/sharedconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,26 @@ func (pm *SharedConfigImpl) OrdererAddresses() []string {
}

// BeginConfig is used to start a new config proposal
func (pm *SharedConfigImpl) BeginConfig() {
func (pm *SharedConfigImpl) BeginConfig(groups []string) ([]api.Handler, error) {
handlers := make([]api.Handler, len(groups))

for i, group := range groups {
switch group {
case application.GroupKey:
handlers[i] = pm.applicationConfig
case orderer.GroupKey:
handlers[i] = pm.ordererConfig
default:
return nil, fmt.Errorf("Disallowed channel group: %s", group)
}
}

if pm.pendingConfig != nil {
logger.Panicf("Programming error, cannot call begin in the middle of a proposal")
}

pm.pendingConfig = &chainConfig{}
return handlers, nil
}

// RollbackConfig is used to abandon a new config proposal
Expand Down Expand Up @@ -163,25 +178,3 @@ func (pm *SharedConfigImpl) ProposeConfig(key string, configValue *cb.ConfigValu
}
return nil
}

// Handler returns the associated api.Handler for the given path
func (pm *SharedConfigImpl) Handler(path []string) (api.Handler, error) {
if len(path) == 0 {
return pm, nil
}

var initializer api.SubInitializer

switch path[0] {
case application.GroupKey:
initializer = pm.applicationConfig
case orderer.GroupKey:
initializer = pm.ordererConfig
default:
return nil, fmt.Errorf("Disallowed channel group: %s", path[0])
}

return initializer.Handler(path[1:])

return nil, fmt.Errorf("Unallowed group")
}
10 changes: 5 additions & 5 deletions common/configtx/handlers/channel/sharedconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ func TestDoubleBegin(t *testing.T) {
}()

m := NewSharedConfigImpl(nil, nil)
m.BeginConfig()
m.BeginConfig()
m.BeginConfig(nil)
m.BeginConfig(nil)
}

func TestCommitWithoutBegin(t *testing.T) {
Expand Down Expand Up @@ -85,7 +85,7 @@ func TestHashingAlgorithm(t *testing.T) {
validAlgorithm := DefaultHashingAlgorithm()

m := NewSharedConfigImpl(nil, nil)
m.BeginConfig()
m.BeginConfig(nil)

err := m.ProposeConfig(HashingAlgorithmKey, invalidMessage)
if err == nil {
Expand Down Expand Up @@ -115,7 +115,7 @@ func TestBlockDataHashingStructure(t *testing.T) {
validWidth := DefaultBlockDataHashingStructure()

m := NewSharedConfigImpl(nil, nil)
m.BeginConfig()
m.BeginConfig(nil)

err := m.ProposeConfig(BlockDataHashingStructureKey, invalidMessage)
if err == nil {
Expand Down Expand Up @@ -143,7 +143,7 @@ func TestOrdererAddresses(t *testing.T) {
invalidMessage := makeInvalidConfigValue()
validMessage := DefaultOrdererAddresses()
m := NewSharedConfigImpl(nil, nil)
m.BeginConfig()
m.BeginConfig(nil)

err := m.ProposeConfig(OrdererAddressesKey, invalidMessage)
if err == nil {
Expand Down
10 changes: 0 additions & 10 deletions common/configtx/handlers/msp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"reflect"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/configtx/api"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protos/common"
mspprotos "github.com/hyperledger/fabric/protos/msp"
Expand Down Expand Up @@ -83,12 +82,3 @@ func (bh *MSPConfigHandler) ProposeConfig(key string, configValue *common.Config
bh.proposedMgr = msp.NewMSPManager()
return bh.proposedMgr.Setup(bh.config)
}

// Handler returns the associated api.Handler for the given path
func (bh *MSPConfigHandler) Handler(path []string) (api.Handler, error) {
if len(path) > 0 {
return nil, fmt.Errorf("MSP handler does not support nested groups")
}

return bh, nil
}
34 changes: 13 additions & 21 deletions common/configtx/handlers/orderer/sharedconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,24 @@ func (pm *ManagerImpl) EgressPolicyNames() []string {
}

// BeginConfig is used to start a new config proposal
func (pm *ManagerImpl) BeginConfig() {
logger.Debugf("Beginning possible new orderer config")
func (pm *ManagerImpl) BeginConfig(groups []string) ([]api.Handler, error) {
logger.Debugf("Beginning a possible new orderer shared config")
if pm.pendingConfig != nil {
logger.Fatalf("Programming error, cannot call begin in the middle of a proposal")
logger.Panicf("Programming error, cannot call begin in the middle of a proposal")
}
pm.pendingConfig = &ordererConfig{
orgs: make(map[string]*handlers.OrgConfig),
}
orgHandlers := make([]api.Handler, len(groups))
for i, group := range groups {
org, ok := pm.pendingConfig.orgs[group]
if !ok {
org = handlers.NewOrgConfig(group, pm.mspConfig)
pm.pendingConfig.orgs[group] = org
}
orgHandlers[i] = org
}
return orgHandlers, nil
}

// RollbackConfig is used to abandon a new config proposal
Expand Down Expand Up @@ -275,24 +285,6 @@ func (pm *ManagerImpl) ProposeConfig(key string, configValue *cb.ConfigValue) er
return nil
}

// Handler returns the associated api.Handler for the given path
func (pm *ManagerImpl) Handler(path []string) (api.Handler, error) {
if len(path) == 0 {
return pm, nil
}

if len(path) > 1 {
return nil, fmt.Errorf("Orderer group allows only one further level of nesting")
}

org, ok := pm.pendingConfig.orgs[path[0]]
if !ok {
org = handlers.NewOrgConfig(path[0], pm.mspConfig)
pm.pendingConfig.orgs[path[0]] = org
}
return org, nil
}

// This does just a barebones sanity check.
func brokerEntrySeemsValid(broker string) bool {
if !strings.Contains(broker, ":") {
Expand Down
Loading

0 comments on commit 3a61f6b

Please sign in to comment.