diff --git a/common/configtx/handlers/application/sharedconfig.go b/common/configtx/handlers/application/sharedconfig.go index c9004afc473..797649e88a3 100644 --- a/common/configtx/handlers/application/sharedconfig.go +++ b/common/configtx/handlers/application/sharedconfig.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/hyperledger/fabric/common/configtx/api" + "github.com/hyperledger/fabric/common/configtx/handlers" "github.com/hyperledger/fabric/common/configtx/handlers/msp" cb "github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric/protos/peer" @@ -65,6 +66,7 @@ var logger = logging.MustGetLogger("peer/sharedconfig") type sharedConfig struct { anchorPeers []*pb.AnchorPeer + orgs map[string]*handlers.OrgConfig } // SharedConfigImpl is an implementation of Manager and configtx.ConfigHandler @@ -95,7 +97,9 @@ func (di *SharedConfigImpl) BeginConfig() { if di.pendingConfig != nil { logger.Panicf("Programming error, cannot call begin in the middle of a proposal") } - di.pendingConfig = &sharedConfig{} + di.pendingConfig = &sharedConfig{ + orgs: make(map[string]*handlers.OrgConfig), + } } // RollbackConfig is used to abandon a new config proposal @@ -142,5 +146,10 @@ func (pm *SharedConfigImpl) Handler(path []string) (api.Handler, error) { return nil, fmt.Errorf("Application group allows only one further level of nesting") } - return pm.mspConfig.Handler(path[1:]) + 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 } diff --git a/common/configtx/handlers/orderer/sharedconfig.go b/common/configtx/handlers/orderer/sharedconfig.go index a831c9b2a5b..4e7f17261c5 100644 --- a/common/configtx/handlers/orderer/sharedconfig.go +++ b/common/configtx/handlers/orderer/sharedconfig.go @@ -24,6 +24,7 @@ import ( "time" "github.com/hyperledger/fabric/common/configtx/api" + "github.com/hyperledger/fabric/common/configtx/handlers" "github.com/hyperledger/fabric/common/configtx/handlers/msp" cb "github.com/hyperledger/fabric/protos/common" ab "github.com/hyperledger/fabric/protos/orderer" @@ -98,6 +99,7 @@ type ordererConfig struct { kafkaBrokers []string ingressPolicyNames []string egressPolicyNames []string + orgs map[string]*handlers.OrgConfig } // ManagerImpl is an implementation of configtxapi.OrdererConfig and configtxapi.Handler @@ -160,7 +162,9 @@ func (pm *ManagerImpl) BeginConfig() { if pm.pendingConfig != nil { logger.Fatalf("Programming error, cannot call begin in the middle of a proposal") } - pm.pendingConfig = &ordererConfig{} + pm.pendingConfig = &ordererConfig{ + orgs: make(map[string]*handlers.OrgConfig), + } } // RollbackConfig is used to abandon a new config proposal @@ -281,7 +285,12 @@ func (pm *ManagerImpl) Handler(path []string) (api.Handler, error) { return nil, fmt.Errorf("Orderer group allows only one further level of nesting") } - return pm.mspConfig.Handler(path[1:]) + 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. diff --git a/common/configtx/handlers/organization.go b/common/configtx/handlers/organization.go new file mode 100644 index 00000000000..93b113ecbfe --- /dev/null +++ b/common/configtx/handlers/organization.go @@ -0,0 +1,105 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package handlers + +import ( + "fmt" + + "github.com/hyperledger/fabric/common/configtx/api" + mspconfig "github.com/hyperledger/fabric/common/configtx/handlers/msp" + "github.com/hyperledger/fabric/msp" + cb "github.com/hyperledger/fabric/protos/common" + + "github.com/op/go-logging" +) + +// Org config keys +const ( + // MSPKey is value key for marshaled *mspconfig.MSPConfig + MSPKey = "MSP" +) + +var logger = logging.MustGetLogger("common/configtx/handlers") + +type orgConfig struct { + msp msp.MSP +} + +// SharedConfigImpl is an implementation of Manager and configtx.ConfigHandler +// In general, it should only be referenced as an Impl for the configtx.Manager +type OrgConfig struct { + id string + pendingConfig *orgConfig + config *orgConfig + + mspConfig *mspconfig.MSPConfigHandler +} + +// NewSharedConfigImpl creates a new SharedConfigImpl with the given CryptoHelper +func NewOrgConfig(id string, mspConfig *mspconfig.MSPConfigHandler) *OrgConfig { + return &OrgConfig{ + id: id, + config: &orgConfig{}, + mspConfig: mspConfig, + } +} + +// BeginConfig is used to start a new config proposal +func (oc *OrgConfig) BeginConfig() { + logger.Debugf("Beginning a possible new org config") + if oc.pendingConfig != nil { + logger.Panicf("Programming error, cannot call begin in the middle of a proposal") + } + oc.pendingConfig = &orgConfig{} +} + +// RollbackConfig is used to abandon a new config proposal +func (oc *OrgConfig) RollbackConfig() { + logger.Debugf("Rolling back proposed org config") + oc.pendingConfig = nil +} + +// CommitConfig is used to commit a new config proposal +func (oc *OrgConfig) CommitConfig() { + logger.Debugf("Committing new org config") + if oc.pendingConfig == nil { + logger.Panicf("Programming error, cannot call commit without an existing proposal") + } + oc.config = oc.pendingConfig + oc.pendingConfig = nil +} + +// ProposeConfig is used to add new config to the config proposal +func (oc *OrgConfig) ProposeConfig(key string, configValue *cb.ConfigValue) error { + switch key { + case MSPKey: + logger.Debugf("Initializing org MSP for id %s", oc.id) + return oc.mspConfig.ProposeConfig(key, configValue) + default: + logger.Warningf("Uknown org config item with key %s", key) + } + return nil +} + +// Handler returns the associated api.Handler for the given path +func (oc *OrgConfig) Handler(path []string) (api.Handler, error) { + if len(path) == 0 { + return oc, nil + } + + return nil, fmt.Errorf("Organizations do not further nesting") +} diff --git a/common/configtx/handlers/organization_test.go b/common/configtx/handlers/organization_test.go new file mode 100644 index 00000000000..eabe174a6e2 --- /dev/null +++ b/common/configtx/handlers/organization_test.go @@ -0,0 +1,65 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package handlers + +import ( + "testing" + + configtxapi "github.com/hyperledger/fabric/common/configtx/api" + + logging "github.com/op/go-logging" +) + +func init() { + logging.SetLevel(logging.DEBUG, "") +} + +func TestInterface(t *testing.T) { + _ = configtxapi.SubInitializer(NewOrgConfig("id", nil)) +} + +func TestDoubleBegin(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Fatalf("Should have panicked on multiple begin configs") + } + }() + + m := NewOrgConfig("id", nil) + m.BeginConfig() + m.BeginConfig() +} + +func TestCommitWithoutBegin(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Fatalf("Should have panicked on multiple begin configs") + } + }() + + m := NewOrgConfig("id", nil) + m.CommitConfig() +} + +func TestRollback(t *testing.T) { + m := NewOrgConfig("id", nil) + m.pendingConfig = &orgConfig{} + m.RollbackConfig() + if m.pendingConfig != nil { + t.Fatalf("Should have cleared pending config on rollback") + } +} diff --git a/common/configtx/test/helper.go b/common/configtx/test/helper.go index 033f974de79..e64b9a2bd1f 100644 --- a/common/configtx/test/helper.go +++ b/common/configtx/test/helper.go @@ -23,6 +23,7 @@ import ( "github.com/hyperledger/fabric/common/configtx" configtxapplication "github.com/hyperledger/fabric/common/configtx/handlers/application" configtxmsp "github.com/hyperledger/fabric/common/configtx/handlers/msp" + configtxorderer "github.com/hyperledger/fabric/common/configtx/handlers/orderer" genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" "github.com/hyperledger/fabric/common/configtx/tool/provisional" "github.com/hyperledger/fabric/common/genesis" @@ -86,11 +87,13 @@ func OrdererTemplate() configtx.Template { // MSPTemplate returns the test MSP template func MSPTemplate() configtx.Template { - mspConf, err := msp.GetLocalMspConfig(sampleMSPPath, "SAMPLE") + const sampleID = "SAMPLE" + mspConf, err := msp.GetLocalMspConfig(sampleMSPPath, sampleID) if err != nil { logger.Panicf("Could not load sample MSP config: %s", err) } - return configtx.NewSimpleTemplate(configtxmsp.TemplateGroupMSP([]string{configtxapplication.GroupKey}, mspConf)) + return configtx.NewSimpleTemplate(configtxmsp.TemplateGroupMSP([]string{configtxapplication.GroupKey, sampleID}, mspConf), + configtxmsp.TemplateGroupMSP([]string{configtxorderer.GroupKey, sampleID}, mspConf)) } // ApplicationTemplate returns the test application template diff --git a/orderer/sample_clients/broadcast_config/newchain.go b/orderer/sample_clients/broadcast_config/newchain.go index 381aa7fe302..9bebcabbc3a 100644 --- a/orderer/sample_clients/broadcast_config/newchain.go +++ b/orderer/sample_clients/broadcast_config/newchain.go @@ -18,15 +18,17 @@ package main import ( "github.com/hyperledger/fabric/common/configtx" - "github.com/hyperledger/fabric/common/configtx/tool/provisional" + configtxtest "github.com/hyperledger/fabric/common/configtx/test" + //"github.com/hyperledger/fabric/common/configtx/tool/provisional" "github.com/hyperledger/fabric/msp" cb "github.com/hyperledger/fabric/protos/common" ) func newChainRequest(consensusType, creationPolicy, newChannelId string) *cb.Envelope { - genConf.Orderer.OrdererType = consensusType - generator := provisional.New(genConf) - channelTemplate := generator.ChannelTemplate() + //genConf.Orderer.OrdererType = consensusType + //generator := provisional.New(genConf) + //channelTemplate := generator.ChannelTemplate() + channelTemplate := configtxtest.CompositeTemplate() signer, err := msp.NewNoopMsp().GetDefaultSigningIdentity() if err != nil {