Skip to content

Commit

Permalink
[FAB-5944] Restore disabled config update checks
Browse files Browse the repository at this point in the history
In order to refactor the channel config to be immutable, checks which
depended upon the previous state had to be removed (in particular,
checks for the consensus type changing and for the org's MSPID changing
were temporarily removed).

This CR adds those checks back in a step which allows the previous
config to validate the new one via 'ValidateNew'.

Change-Id: Ic6c51f4b09f5aaaa8d176d850728287a1381b5e4
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Aug 30, 2017
1 parent 0cd1626 commit 10d340c
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 17 deletions.
6 changes: 6 additions & 0 deletions common/channelconfig/api.go
Expand Up @@ -64,6 +64,9 @@ type Consortiums interface {
type Consortium interface {
// ChannelCreationPolicy returns the policy to check when instantiating a channel for this consortium
ChannelCreationPolicy() *cb.Policy

// Organizations returns the organizations for this consortium
Organizations() map[string]Org
}

// Orderer stores the common shared orderer config
Expand Down Expand Up @@ -116,4 +119,7 @@ type Resources interface {

// MSPManager returns the msp.MSPManager for the chain
MSPManager() msp.MSPManager

// ValidateNew should return an error if a new set of configuration resources is incompatible with the current one
ValidateNew(resources Resources) error
}
71 changes: 71 additions & 0 deletions common/channelconfig/bundle.go
Expand Up @@ -80,6 +80,77 @@ func (b *Bundle) ConfigtxManager() configtxapi.Manager {
return b.configtxManager
}

// ValidateNew checks if a new bundle's contained configuration is valid to be derived from the current bundle.
// This allows checks of the nature "Make sure that the consensus type did not change." which is otherwise
func (b *Bundle) ValidateNew(nb Resources) error {
if oc, ok := b.OrdererConfig(); ok {
noc, ok := nb.OrdererConfig()
if !ok {
return fmt.Errorf("Current config has orderer section, but new config does not")
}

if oc.ConsensusType() != noc.ConsensusType() {
return fmt.Errorf("Attempted to change consensus type from %s to %s", oc.ConsensusType(), noc.ConsensusType())
}

for orgName, org := range oc.Organizations() {
norg, ok := noc.Organizations()[orgName]
if !ok {
continue
}
mspID := org.MSPID()
if mspID != norg.MSPID() {
return fmt.Errorf("Orderer org %s attempted to change MSP ID from %s to %s", orgName, mspID, norg.MSPID())
}
}
}

if ac, ok := b.ApplicationConfig(); ok {
nac, ok := nb.ApplicationConfig()
if !ok {
return fmt.Errorf("Current config has consortiums section, but new config does not")
}

for orgName, org := range ac.Organizations() {
norg, ok := nac.Organizations()[orgName]
if !ok {
continue
}
mspID := org.MSPID()
if mspID != norg.MSPID() {
return fmt.Errorf("Application org %s attempted to change MSP ID from %s to %s", orgName, mspID, norg.MSPID())
}
}
}

if cc, ok := b.ConsortiumsConfig(); ok {
ncc, ok := nb.ConsortiumsConfig()
if !ok {
return fmt.Errorf("Current config has consortiums section, but new config does not")
}

for consortiumName, consortium := range cc.Consortiums() {
nconsortium, ok := ncc.Consortiums()[consortiumName]
if !ok {
continue
}

for orgName, org := range consortium.Organizations() {
norg, ok := nconsortium.Organizations()[orgName]
if !ok {
continue
}
mspID := org.MSPID()
if mspID != norg.MSPID() {
return fmt.Errorf("Consortium %s org %s attempted to change MSP ID from %s to %s", consortiumName, orgName, mspID, norg.MSPID())
}
}
}
}

return nil
}

type simpleProposer struct {
policyManager policies.Manager
}
Expand Down
202 changes: 202 additions & 0 deletions common/channelconfig/bundle_test.go
@@ -0,0 +1,202 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package channelconfig

import (
"testing"

ab "github.com/hyperledger/fabric/protos/orderer"

"github.com/stretchr/testify/assert"
)

func TestValidateNew(t *testing.T) {
t.Run("DisappearingOrdererConfig", func(t *testing.T) {
cb := &Bundle{
channelConfig: &ChannelConfig{
ordererConfig: &OrdererConfig{},
},
}

nb := &Bundle{
channelConfig: &ChannelConfig{},
}

err := cb.ValidateNew(nb)
assert.Error(t, err)
assert.Regexp(t, "Current config has orderer section, but new config does not", err.Error())
})

t.Run("DisappearingApplicationConfig", func(t *testing.T) {
cb := &Bundle{
channelConfig: &ChannelConfig{
appConfig: &ApplicationConfig{},
},
}

nb := &Bundle{
channelConfig: &ChannelConfig{},
}

err := cb.ValidateNew(nb)
assert.Error(t, err)
assert.Regexp(t, "Current config has consortiums section, but new config does not", err.Error())
})

t.Run("DisappearingConsortiumsConfig", func(t *testing.T) {
cb := &Bundle{
channelConfig: &ChannelConfig{
consortiumsConfig: &ConsortiumsConfig{},
},
}

nb := &Bundle{
channelConfig: &ChannelConfig{},
}

err := cb.ValidateNew(nb)
assert.Error(t, err)
assert.Regexp(t, "Current config has consortiums section, but new config does not", err.Error())
})

t.Run("ConsensusTypeChange", func(t *testing.T) {
cb := &Bundle{
channelConfig: &ChannelConfig{
ordererConfig: &OrdererConfig{
protos: &OrdererProtos{
ConsensusType: &ab.ConsensusType{
Type: "type1",
},
},
},
},
}

nb := &Bundle{
channelConfig: &ChannelConfig{
ordererConfig: &OrdererConfig{
protos: &OrdererProtos{
ConsensusType: &ab.ConsensusType{
Type: "type2",
},
},
},
},
}

err := cb.ValidateNew(nb)
assert.Error(t, err)
assert.Regexp(t, "Attempted to change consensus type from", err.Error())
})

t.Run("OrdererOrgMSPIDChange", func(t *testing.T) {
cb := &Bundle{
channelConfig: &ChannelConfig{
ordererConfig: &OrdererConfig{
protos: &OrdererProtos{
ConsensusType: &ab.ConsensusType{
Type: "type1",
},
},
orgs: map[string]Org{
"org1": &OrganizationConfig{mspID: "org1msp"},
"org2": &OrganizationConfig{mspID: "org2msp"},
"org3": &OrganizationConfig{mspID: "org3msp"},
},
},
},
}

nb := &Bundle{
channelConfig: &ChannelConfig{
ordererConfig: &OrdererConfig{
protos: &OrdererProtos{
ConsensusType: &ab.ConsensusType{
Type: "type1",
},
},
orgs: map[string]Org{
"org1": &OrganizationConfig{mspID: "org1msp"},
"org3": &OrganizationConfig{mspID: "org2msp"},
},
},
},
}

err := cb.ValidateNew(nb)
assert.Error(t, err)
assert.Regexp(t, "Orderer org org3 attempted to change MSP ID from", err.Error())
})

t.Run("ApplicationOrgMSPIDChange", func(t *testing.T) {
cb := &Bundle{
channelConfig: &ChannelConfig{
appConfig: &ApplicationConfig{
applicationOrgs: map[string]ApplicationOrg{
"org1": &ApplicationOrgConfig{OrganizationConfig: &OrganizationConfig{mspID: "org1msp"}},
"org2": &ApplicationOrgConfig{OrganizationConfig: &OrganizationConfig{mspID: "org2msp"}},
"org3": &ApplicationOrgConfig{OrganizationConfig: &OrganizationConfig{mspID: "org3msp"}},
},
},
},
}

nb := &Bundle{
channelConfig: &ChannelConfig{
appConfig: &ApplicationConfig{
applicationOrgs: map[string]ApplicationOrg{
"org1": &ApplicationOrgConfig{OrganizationConfig: &OrganizationConfig{mspID: "org1msp"}},
"org3": &ApplicationOrgConfig{OrganizationConfig: &OrganizationConfig{mspID: "org2msp"}},
},
},
},
}

err := cb.ValidateNew(nb)
assert.Error(t, err)
assert.Regexp(t, "Application org org3 attempted to change MSP ID from", err.Error())
})

t.Run("ConsortiumOrgMSPIDChange", func(t *testing.T) {
cb := &Bundle{
channelConfig: &ChannelConfig{
consortiumsConfig: &ConsortiumsConfig{
consortiums: map[string]Consortium{
"consortium1": &ConsortiumConfig{
orgs: map[string]Org{
"org1": &OrganizationConfig{mspID: "org1msp"},
"org2": &OrganizationConfig{mspID: "org2msp"},
"org3": &OrganizationConfig{mspID: "org3msp"},
},
},
"consortium2": &ConsortiumConfig{},
"consortium3": &ConsortiumConfig{},
},
},
},
}

nb := &Bundle{
channelConfig: &ChannelConfig{
consortiumsConfig: &ConsortiumsConfig{
consortiums: map[string]Consortium{
"consortium1": &ConsortiumConfig{
orgs: map[string]Org{
"org1": &OrganizationConfig{mspID: "org1msp"},
"org3": &OrganizationConfig{mspID: "org2msp"},
},
},
},
},
},
}

err := cb.ValidateNew(nb)
assert.Error(t, err)
assert.Regexp(t, "Consortium consortium1 org org3 attempted to change MSP ID from", err.Error())
})
}
5 changes: 5 additions & 0 deletions common/channelconfig/bundlesource.go
Expand Up @@ -93,3 +93,8 @@ func (bs *BundleSource) ApplicationConfig() (Application, bool) {
func (bs *BundleSource) ConfigtxManager() configtxapi.Manager {
return bs.StableBundle().ConfigtxManager()
}

// ValidateNew passes through to the current bundle
func (bs *BundleSource) ValidateNew(resources Resources) error {
return bs.StableBundle().ValidateNew(resources)
}
6 changes: 3 additions & 3 deletions common/channelconfig/consortium.go
Expand Up @@ -20,14 +20,14 @@ type ConsortiumProtos struct {
// ConsortiumConfig holds the consoritums configuration information
type ConsortiumConfig struct {
protos *ConsortiumProtos
orgs map[string]*OrganizationConfig
orgs map[string]Org
}

// NewConsortiumConfig creates a new instance of the consoritums config
func NewConsortiumConfig(consortiumGroup *cb.ConfigGroup, mspConfig *MSPConfigHandler) (*ConsortiumConfig, error) {
cc := &ConsortiumConfig{
protos: &ConsortiumProtos{},
orgs: make(map[string]*OrganizationConfig),
orgs: make(map[string]Org),
}

if err := DeserializeProtoValuesFromGroup(consortiumGroup, cc.protos); err != nil {
Expand All @@ -45,7 +45,7 @@ func NewConsortiumConfig(consortiumGroup *cb.ConfigGroup, mspConfig *MSPConfigHa
}

// Organizations returns the set of organizations in the consortium
func (cc *ConsortiumConfig) Organizations() map[string]*OrganizationConfig {
func (cc *ConsortiumConfig) Organizations() map[string]Org {
return cc.orgs
}

Expand Down
6 changes: 0 additions & 6 deletions common/channelconfig/orderer.go
Expand Up @@ -116,7 +116,6 @@ func (oc *OrdererConfig) Organizations() map[string]Org {

func (oc *OrdererConfig) Validate() error {
for _, validator := range []func() error{
oc.validateConsensusType,
oc.validateBatchSize,
oc.validateBatchTimeout,
oc.validateKafkaBrokers,
Expand All @@ -129,11 +128,6 @@ func (oc *OrdererConfig) Validate() error {
return nil
}

func (oc *OrdererConfig) validateConsensusType() error {
// XXX TODO add check to prevent changing consensus type back
return nil
}

func (oc *OrdererConfig) validateBatchSize() error {
if oc.protos.BatchSize.MaxMessageCount == 0 {
return fmt.Errorf("Attempted to set the batch size max message count to an invalid value: 0")
Expand Down
5 changes: 0 additions & 5 deletions common/channelconfig/orderer_test.go
Expand Up @@ -19,11 +19,6 @@ func init() {
logging.SetLevel(logging.DEBUG, "")
}

func TestConsensusType(t *testing.T) {
oc := &OrdererConfig{protos: &OrdererProtos{ConsensusType: &ab.ConsensusType{Type: "foo"}}}
assert.NoError(t, oc.validateConsensusType(), "Should have validly set new consensus type")
}

func TestBatchSize(t *testing.T) {
validMaxMessageCount := uint32(10)
validAbsoluteMaxBytes := uint32(1000)
Expand Down
2 changes: 0 additions & 2 deletions common/channelconfig/organization.go
Expand Up @@ -84,7 +84,5 @@ func (oc *OrganizationConfig) validateMSP() error {
return fmt.Errorf("MSP for org %s has empty MSP ID", oc.name)
}

// XXX TODO Add back a check for MSP ID modification

return nil
}
8 changes: 8 additions & 0 deletions common/mocks/config/resources.go
Expand Up @@ -34,6 +34,9 @@ type Resources struct {

// MSPManagerVal is returned as the result of MSPManager()
MSPManagerVal msp.MSPManager

// ValidateNewErr is returned as the result of ValidateNew
ValidateNewErr error
}

// ConfigtxMangaer returns ConfigtxManagerVal
Expand Down Expand Up @@ -69,3 +72,8 @@ func (r *Resources) ConsortiumsConfig() (channelconfig.Consortiums, bool) {
func (r *Resources) MSPManager() msp.MSPManager {
return r.MSPManagerVal
}

// ValidateNew returns ValidateNewErr
func (r *Resources) ValidateNew(res channelconfig.Resources) error {
return r.ValidateNewErr
}

0 comments on commit 10d340c

Please sign in to comment.