Skip to content

Commit cb6ac1d

Browse files
committed
FAB-15642 Consensus mig.: permissive maintenance filter
Currently the maintenance filter is not allowing config changes other than consensus type changes in maintenance mode. It seems generally useful that the network operator might want to for instance to modify batch parameters before switching over consensus type, to add new certificate authorities, etc. We therefore lift the restriction and let the admin do any kind of config change he is authorized to in maintenance mode. Two restrictions remain: 1. Entry to- and exit from- maintenance mode should not be accompanied by any config change; 2. ConsensusType.Type can change only in maintenance-mode. Change-Id: I82387946a5a86c4217cc98aeb7b5d266d93d6054 Signed-off-by: Yoav Tock <tock@il.ibm.com>
1 parent a941571 commit cb6ac1d

File tree

3 files changed

+109
-82
lines changed

3 files changed

+109
-82
lines changed

integration/raft/migration_test.go

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -419,115 +419,109 @@ var _ = Describe("Kafka2RaftMigration", func() {
419419
validateConsensusTypeValue(consensusTypeValue, "kafka", protosorderer.ConsensusType_STATE_MAINTENANCE)
420420

421421
//=== Step 7: ===
422-
By("7) Config update on system channel, changing both ConsensusType.Type and other value is forbidden")
423-
config, updatedConfig = prepareTransition(network, peer, orderer, syschannel,
424-
"kafka", protosorderer.ConsensusType_STATE_MAINTENANCE,
425-
"etcdraft", raftMetadata, protosorderer.ConsensusType_STATE_MAINTENANCE)
426-
updateConfigWithBatchTimeout(updatedConfig)
427-
updateOrdererConfigFailed(network, orderer, syschannel, config, updatedConfig, peer, orderer)
428-
429-
//=== Step 8: ===
430-
By("8) Config update on standard channel, changing both ConsensusType.Type and other value is forbidden")
431-
config, updatedConfig = prepareTransition(network, peer, orderer, channel1,
432-
"kafka", protosorderer.ConsensusType_STATE_MAINTENANCE,
433-
"etcdraft", raftMetadata, protosorderer.ConsensusType_STATE_MAINTENANCE)
434-
updateConfigWithBatchTimeout(updatedConfig)
435-
updateOrdererConfigFailed(network, orderer, channel1, config, updatedConfig, peer, orderer)
436-
437-
//=== Step 9: ===
438-
By("9) Config update on system channel, changing value other than ConsensusType.Type is forbidden")
439-
config = nwo.GetConfig(network, peer, orderer, syschannel)
440-
updatedConfig, consensusTypeValue, err = extractOrdererConsensusType(config)
441-
Expect(err).NotTo(HaveOccurred())
442-
validateConsensusTypeValue(consensusTypeValue, "kafka", protosorderer.ConsensusType_STATE_MAINTENANCE)
443-
updateConfigWithBatchTimeout(updatedConfig)
444-
updateOrdererConfigFailed(network, orderer, syschannel, config, updatedConfig, peer, orderer)
445-
446-
//=== Step 10: ===
447-
By("10) Config update on standard channel, changing value other than ConsensusType.Type is forbidden")
448-
config = nwo.GetConfig(network, peer, orderer, channel1)
449-
updatedConfig, consensusTypeValue, err = extractOrdererConsensusType(config)
450-
Expect(err).NotTo(HaveOccurred())
451-
validateConsensusTypeValue(consensusTypeValue, "kafka", protosorderer.ConsensusType_STATE_MAINTENANCE)
452-
updateConfigWithBatchTimeout(updatedConfig)
453-
updateOrdererConfigFailed(network, orderer, channel1, config, updatedConfig, peer, orderer)
454-
455-
//=== Step 11: ===
456-
By("11) Config update on system channel, change ConsensusType.Type to unsupported type, forbidden")
422+
By("7) Config update on system channel, change ConsensusType.Type to unsupported type, forbidden")
457423
assertTransitionFailed(network, peer, orderer, syschannel,
458424
"kafka", protosorderer.ConsensusType_STATE_MAINTENANCE,
459425
"solo", nil, protosorderer.ConsensusType_STATE_MAINTENANCE)
460426

461-
//=== Step 12: ===
462-
By("12) Config update on standard channel, change ConsensusType.Type to unsupported type, forbidden")
427+
//=== Step 8: ===
428+
By("8) Config update on standard channel, change ConsensusType.Type to unsupported type, forbidden")
463429
assertTransitionFailed(network, peer, orderer, channel1,
464430
"kafka", protosorderer.ConsensusType_STATE_MAINTENANCE,
465431
"hesse", nil, protosorderer.ConsensusType_STATE_MAINTENANCE)
466432

467-
//=== Step 13: ===
468-
By("13) Config update on system channel, change ConsensusType.Type and State, forbidden")
433+
//=== Step 9: ===
434+
By("9) Config update on system channel, change ConsensusType.Type and State, forbidden")
469435
assertTransitionFailed(network, peer, orderer, syschannel,
470436
"kafka", protosorderer.ConsensusType_STATE_MAINTENANCE,
471437
"etcdraft", raftMetadata, protosorderer.ConsensusType_STATE_NORMAL)
472438

473-
//=== Step 14: ===
474-
By("14) Config update on standard channel, change ConsensusType.Type and State, forbidden")
439+
//=== Step 10: ===
440+
By("10) Config update on standard channel, change ConsensusType.Type and State, forbidden")
475441
assertTransitionFailed(network, peer, orderer, channel1,
476442
"kafka", protosorderer.ConsensusType_STATE_MAINTENANCE,
477443
"etcdraft", raftMetadata, protosorderer.ConsensusType_STATE_NORMAL)
478444

479-
//=== Step 15: ===
480-
By("15) Config update on system channel, change ConsensusType.Type")
445+
//=== Step 11: ===
446+
By("11) Config update on system channel, changing both ConsensusType.Type and other value is permitted")
481447
config, updatedConfig = prepareTransition(network, peer, orderer, syschannel,
482448
"kafka", protosorderer.ConsensusType_STATE_MAINTENANCE,
483449
"etcdraft", raftMetadata, protosorderer.ConsensusType_STATE_MAINTENANCE)
450+
updateConfigWithBatchTimeout(updatedConfig)
484451
nwo.UpdateOrdererConfig(network, orderer, syschannel, config, updatedConfig, peer, orderer)
485452

486-
By("15) Verify: system channel config changed")
453+
By("11) Verify: system channel config changed")
487454
sysBlockNum := nwo.CurrentConfigBlockNumber(network, peer, orderer, syschannel)
488455
Expect(sysBlockNum).To(Equal(sysStartBlockNum + 1))
489456
config = nwo.GetConfig(network, peer, orderer, syschannel)
490457
updatedConfig, consensusTypeValue, err = extractOrdererConsensusType(config)
491458
Expect(err).NotTo(HaveOccurred())
492459
validateConsensusTypeValue(consensusTypeValue, "etcdraft", protosorderer.ConsensusType_STATE_MAINTENANCE)
493460

494-
//=== Step 16: ===
495-
By("16) Config update on standard channel, changing ConsensusType.Type")
461+
//=== Step 12: ===
462+
By("12) Config update on standard channel, changing both ConsensusType.Type and other value is permitted")
496463
config, updatedConfig = prepareTransition(network, peer, orderer, channel1,
497464
"kafka", protosorderer.ConsensusType_STATE_MAINTENANCE,
498465
"etcdraft", raftMetadata, protosorderer.ConsensusType_STATE_MAINTENANCE)
466+
updateConfigWithBatchTimeout(updatedConfig)
499467
nwo.UpdateOrdererConfig(network, orderer, channel1, config, updatedConfig, peer, orderer)
500468

501-
By("16) Verify: standard channel config changed")
469+
By("12) Verify: standard channel config changed")
502470
std1BlockNum := nwo.CurrentConfigBlockNumber(network, peer, orderer, channel1)
503471
Expect(std1BlockNum).To(Equal(std1StartBlockNum + 1))
504472
config = nwo.GetConfig(network, peer, orderer, channel1)
505473
updatedConfig, consensusTypeValue, err = extractOrdererConsensusType(config)
506474
Expect(err).NotTo(HaveOccurred())
507475
validateConsensusTypeValue(consensusTypeValue, "etcdraft", protosorderer.ConsensusType_STATE_MAINTENANCE)
508476

509-
//=== Step 17: ===
510-
By("17) Config update on system channel, change ConsensusType.Type back to kafka, forbidden")
477+
//=== Step 13: ===
478+
By("13) Config update on system channel, changing value other than ConsensusType.Type is permitted")
479+
config = nwo.GetConfig(network, peer, orderer, syschannel)
480+
updatedConfig, consensusTypeValue, err = extractOrdererConsensusType(config)
481+
Expect(err).NotTo(HaveOccurred())
482+
validateConsensusTypeValue(consensusTypeValue, "etcdraft", protosorderer.ConsensusType_STATE_MAINTENANCE)
483+
updateConfigWithBatchTimeout(updatedConfig)
484+
nwo.UpdateOrdererConfig(network, orderer, syschannel, config, updatedConfig, peer, orderer)
485+
486+
By("13) Verify: system channel config changed")
487+
sysBlockNum = nwo.CurrentConfigBlockNumber(network, peer, orderer, syschannel)
488+
Expect(sysBlockNum).To(Equal(sysStartBlockNum + 2))
489+
490+
//=== Step 14: ===
491+
By("14) Config update on standard channel, changing value other than ConsensusType.Type is permitted")
492+
config = nwo.GetConfig(network, peer, orderer, channel1)
493+
updatedConfig, consensusTypeValue, err = extractOrdererConsensusType(config)
494+
Expect(err).NotTo(HaveOccurred())
495+
validateConsensusTypeValue(consensusTypeValue, "etcdraft", protosorderer.ConsensusType_STATE_MAINTENANCE)
496+
updateConfigWithBatchTimeout(updatedConfig)
497+
nwo.UpdateOrdererConfig(network, orderer, channel1, config, updatedConfig, peer, orderer)
498+
499+
By("14) Verify: standard channel config changed")
500+
std1BlockNum = nwo.CurrentConfigBlockNumber(network, peer, orderer, channel1)
501+
Expect(std1BlockNum).To(Equal(std1StartBlockNum + 2))
502+
503+
//=== Step 15: ===
504+
By("15) Config update on system channel, change ConsensusType.Type back to kafka, forbidden")
511505
assertTransitionFailed(network, peer, orderer, syschannel,
512506
"etcdraft", protosorderer.ConsensusType_STATE_MAINTENANCE,
513507
"kafka", nil, protosorderer.ConsensusType_STATE_MAINTENANCE)
514508

515-
//=== Step 18: ===
516-
By("18) Config update on standard channel, changing ConsensusType.Type back to kafka, forbidden")
509+
//=== Step 16: ===
510+
By("16) Config update on standard channel, changing ConsensusType.Type back to kafka, forbidden")
517511
assertTransitionFailed(network, peer, orderer, channel1,
518512
"etcdraft", protosorderer.ConsensusType_STATE_MAINTENANCE,
519513
"kafka", nil, protosorderer.ConsensusType_STATE_MAINTENANCE)
520514

521-
//=== Step 19: ===
522-
By("19) Config update on system channel, changing both ConsensusType State & some other value is forbidden")
515+
//=== Step 17: ===
516+
By("17) Config update on system channel, changing both ConsensusType State & some other value is forbidden")
523517
config, updatedConfig = prepareTransition(network, peer, orderer, syschannel,
524518
"etcdraft", protosorderer.ConsensusType_STATE_MAINTENANCE,
525519
"etcdraft", raftMetadata, protosorderer.ConsensusType_STATE_NORMAL)
526520
updateConfigWithBatchTimeout(updatedConfig)
527521
updateOrdererConfigFailed(network, orderer, syschannel, config, updatedConfig, peer, orderer)
528522

529-
//=== Step 20: ===
530-
By("20) Config update on standard channel, both ConsensusType State & some other value is forbidden")
523+
//=== Step 18: ===
524+
By("18) Config update on standard channel, both ConsensusType State & some other value is forbidden")
531525
config, updatedConfig = prepareTransition(network, peer, orderer, channel1,
532526
"etcdraft", protosorderer.ConsensusType_STATE_MAINTENANCE,
533527
"etcdraft", raftMetadata, protosorderer.ConsensusType_STATE_NORMAL)
@@ -807,9 +801,9 @@ func updateConfigWithBatchTimeout(updatedConfig *common.Config) {
807801
Expect(err).NotTo(HaveOccurred())
808802
toDur, err := time.ParseDuration(batchTimeoutValue.Timeout)
809803
Expect(err).NotTo(HaveOccurred())
810-
toDur = toDur + time.Duration(1000000)
804+
toDur = toDur + time.Duration(100000000)
811805
batchTimeoutValue.Timeout = toDur.String()
812-
806+
By(fmt.Sprintf("Increasing BatchTimeout to %s", batchTimeoutValue.Timeout))
813807
updatedConfig.ChannelGroup.Groups["Orderer"].Values["BatchTimeout"] = &common.ConfigValue{
814808
ModPolicy: "Admins",
815809
Value: protoutil.MarshalOrPanic(batchTimeoutValue),

orderer/common/msgprocessor/maintenancefilter.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0
77
package msgprocessor
88

99
import (
10+
"bytes"
11+
1012
"github.com/golang/protobuf/proto"
1113
"github.com/hyperledger/fabric/common/channelconfig"
1214
"github.com/hyperledger/fabric/common/configtx"
@@ -90,13 +92,22 @@ func (mf *MaintenanceFilter) inspect(configEnvelope *cb.ConfigEnvelope, ordererC
9092
return nil
9193
}
9294

93-
if nextOrdererConfig.ConsensusState() != ordererConfig.ConsensusState() ||
94-
ordererConfig.ConsensusState() == orderer.ConsensusType_STATE_MAINTENANCE {
95+
// Entry to- and exit from- maintenance-mode should not be accompanied by any other change.
96+
if ordererConfig.ConsensusState() != nextOrdererConfig.ConsensusState() {
9597
if err1Change := mf.ensureConsensusTypeChangeOnly(configEnvelope); err1Change != nil {
9698
return err1Change
9799
}
100+
if ordererConfig.ConsensusType() != nextOrdererConfig.ConsensusType() {
101+
return errors.Errorf("attempted to change ConsensusType.Type from %s to %s, but ConsensusType.State is changing from %s to %s",
102+
ordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType(), ordererConfig.ConsensusState(), nextOrdererConfig.ConsensusState())
103+
}
104+
if !bytes.Equal(nextOrdererConfig.ConsensusMetadata(), ordererConfig.ConsensusMetadata()) {
105+
return errors.Errorf("attempted to change ConsensusType.Metadata, but ConsensusType.State is changing from %s to %s",
106+
ordererConfig.ConsensusState(), nextOrdererConfig.ConsensusState())
107+
}
98108
}
99109

110+
// ConsensusType.Type can only change in maintenance-mode, and only from kafka to raft (for now).
100111
if ordererConfig.ConsensusType() != nextOrdererConfig.ConsensusType() {
101112
if ordererConfig.ConsensusState() == orderer.ConsensusType_STATE_NORMAL {
102113
return errors.Errorf("attempted to change consensus type from %s to %s, but current config ConsensusType.State is not in maintenance mode",

orderer/common/msgprocessor/maintenancefilter_test.go

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,15 @@ import (
1010
"testing"
1111

1212
"github.com/hyperledger/fabric/common/capabilities"
13-
"github.com/hyperledger/fabric/internal/configtxlator/update"
14-
15-
"github.com/hyperledger/fabric/protos/orderer/etcdraft"
16-
1713
"github.com/hyperledger/fabric/common/channelconfig"
1814
mockconfig "github.com/hyperledger/fabric/common/mocks/config"
1915
"github.com/hyperledger/fabric/internal/configtxgen/configtxgentest"
2016
"github.com/hyperledger/fabric/internal/configtxgen/encoder"
2117
"github.com/hyperledger/fabric/internal/configtxgen/localconfig"
18+
"github.com/hyperledger/fabric/internal/configtxlator/update"
2219
"github.com/hyperledger/fabric/protos/common"
2320
"github.com/hyperledger/fabric/protos/orderer"
21+
"github.com/hyperledger/fabric/protos/orderer/etcdraft"
2422
"github.com/hyperledger/fabric/protoutil"
2523
"github.com/stretchr/testify/assert"
2624
"github.com/stretchr/testify/require"
@@ -180,7 +178,7 @@ func TestMaintenanceInspectEntry(t *testing.T) {
180178
configTx := makeConfigEnvelope(t, current, next)
181179
err := mf.Apply(configTx)
182180
assert.EqualError(t, err,
183-
"config transaction inspection failed: attempted to change consensus type from kafka to etcdraft, but current config ConsensusType.State is not in maintenance mode")
181+
"config transaction inspection failed: attempted to change ConsensusType.Type from kafka to etcdraft, but ConsensusType.State is changing from STATE_NORMAL to STATE_MAINTENANCE")
184182
})
185183

186184
t.Run("Bad: change consensus type not in maintenance", func(t *testing.T) {
@@ -233,7 +231,7 @@ func TestMaintenanceInspectChange(t *testing.T) {
233231
configTx := makeConfigEnvelope(t, current, next)
234232
err := mf.Apply(configTx)
235233
assert.EqualError(t, err,
236-
"config transaction inspection failed: attempted to change consensus type from kafka to etcdraft, but next config ConsensusType.State is not in maintenance mode")
234+
"config transaction inspection failed: attempted to change ConsensusType.Type from kafka to etcdraft, but ConsensusType.State is changing from STATE_MAINTENANCE to STATE_NORMAL")
237235
})
238236

239237
t.Run("Bad: etcdraft metadata", func(t *testing.T) {
@@ -272,11 +270,32 @@ func TestMaintenanceInspectExit(t *testing.T) {
272270
configTx := makeConfigEnvelope(t, current, next)
273271
err := mf.Apply(configTx)
274272
assert.EqualError(t, err,
275-
"config transaction inspection failed: attempted to change consensus type from etcdraft to kafka, but next config ConsensusType.State is not in maintenance mode")
273+
"config transaction inspection failed: attempted to change ConsensusType.Type from etcdraft to kafka, but ConsensusType.State is changing from STATE_MAINTENANCE to STATE_NORMAL")
274+
})
275+
276+
t.Run("Bad: exit with extra group", func(t *testing.T) {
277+
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validMetadata, state: orderer.ConsensusType_STATE_NORMAL}
278+
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 1)
279+
err := mf.Apply(configTx)
280+
assert.EqualError(t, err, "config transaction inspection failed: config update contains changes to more than one group")
281+
})
282+
283+
t.Run("Bad: exit with extra value", func(t *testing.T) {
284+
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validMetadata, state: orderer.ConsensusType_STATE_NORMAL}
285+
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 2)
286+
err := mf.Apply(configTx)
287+
assert.EqualError(t, err, "config transaction inspection failed: config update contains changes to values in group Channel")
288+
})
289+
290+
t.Run("Bad: exit with extra orderer value", func(t *testing.T) {
291+
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validMetadata, state: orderer.ConsensusType_STATE_NORMAL}
292+
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 3)
293+
err := mf.Apply(configTx)
294+
assert.EqualError(t, err, "config transaction inspection failed: config update contain more then just the ConsensusType value in the Orderer group")
276295
})
277296
}
278297

279-
func TestMaintenanceEnsureSingleExtra(t *testing.T) {
298+
func TestMaintenanceExtra(t *testing.T) {
280299
msActive := &mockSystemChannelFilterSupport{
281300
OrdererConfigVal: &mockconfig.Orderer{
282301
CapabilitiesVal: &mockconfig.OrdererCapabilities{ConsensusTypeMigrationVal: true},
@@ -286,31 +305,32 @@ func TestMaintenanceEnsureSingleExtra(t *testing.T) {
286305
}
287306
mf := NewMaintenanceFilter(msActive)
288307
require.NotNil(t, mf)
289-
current := consensusTypeInfo{ordererType: "kafka", metadata: []byte{}, state: orderer.ConsensusType_STATE_NORMAL}
308+
current := consensusTypeInfo{ordererType: "kafka", metadata: nil, state: orderer.ConsensusType_STATE_MAINTENANCE}
309+
validMetadata := protoutil.MarshalOrPanic(&etcdraft.ConfigMetadata{})
290310

291-
t.Run("Bad: with extra group", func(t *testing.T) {
292-
next := consensusTypeInfo{ordererType: "kafka", metadata: []byte{}, state: orderer.ConsensusType_STATE_MAINTENANCE}
311+
t.Run("Good: with extra group", func(t *testing.T) {
312+
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
293313
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 1)
294314
err := mf.Apply(configTx)
295-
assert.EqualError(t, err, "config transaction inspection failed: config update contains changes to more than one group")
315+
assert.NoError(t, err)
296316
})
297317

298-
t.Run("Bad: with extra value", func(t *testing.T) {
299-
next := consensusTypeInfo{ordererType: "kafka", metadata: []byte{}, state: orderer.ConsensusType_STATE_MAINTENANCE}
318+
t.Run("Good: with extra value", func(t *testing.T) {
319+
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
300320
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 2)
301321
err := mf.Apply(configTx)
302-
assert.EqualError(t, err, "config transaction inspection failed: config update contains changes to values in group Channel")
322+
assert.NoError(t, err)
303323
})
304324

305-
t.Run("Bad: with extra orderer value", func(t *testing.T) {
306-
next := consensusTypeInfo{ordererType: "kafka", metadata: []byte{}, state: orderer.ConsensusType_STATE_MAINTENANCE}
325+
t.Run("Good: with extra orderer value", func(t *testing.T) {
326+
next := consensusTypeInfo{ordererType: "etcdraft", metadata: validMetadata, state: orderer.ConsensusType_STATE_MAINTENANCE}
307327
configTx := makeConfigEnvelopeWithExtraStuff(t, current, next, 3)
308328
err := mf.Apply(configTx)
309-
assert.EqualError(t, err, "config transaction inspection failed: config update contain more then just the ConsensusType value in the Orderer group")
329+
assert.NoError(t, err)
310330
})
311331
}
312332

313-
func TestMaintenanceEnsureSingleMissing(t *testing.T) {
333+
func TestMaintenanceMissingConsensusType(t *testing.T) {
314334
msActive := &mockSystemChannelFilterSupport{
315335
OrdererConfigVal: &mockconfig.Orderer{
316336
CapabilitiesVal: &mockconfig.OrdererCapabilities{ConsensusTypeMigrationVal: true},
@@ -320,10 +340,12 @@ func TestMaintenanceEnsureSingleMissing(t *testing.T) {
320340
}
321341
mf := NewMaintenanceFilter(msActive)
322342
require.NotNil(t, mf)
323-
current := consensusTypeInfo{ordererType: "kafka", metadata: []byte{}, state: orderer.ConsensusType_STATE_MAINTENANCE}
324-
configTx := makeConfigEnvelopeWithExtraStuff(t, current, current, 1)
325-
err := mf.Apply(configTx)
326-
assert.EqualError(t, err, "config transaction inspection failed: update does not contain the Orderer group")
343+
current := consensusTypeInfo{ordererType: "kafka", metadata: nil, state: orderer.ConsensusType_STATE_MAINTENANCE}
344+
for i := 1; i < 4; i++ {
345+
configTx := makeConfigEnvelopeWithExtraStuff(t, current, current, i)
346+
err := mf.Apply(configTx)
347+
assert.NoError(t, err)
348+
}
327349
}
328350

329351
type consensusTypeInfo struct {

0 commit comments

Comments
 (0)