Skip to content

Commit bccbd4d

Browse files
tock-ibmyacovm
authored andcommitted
FAB-13264 consensus migration: kafka2raft green path #1
This is the first of four (1/4) sub-tasks that focus on the "green" path of consensus-type migration from Kafka to Raft.  By "green" we mean that there are no failures or aborts along the way. The flow of the green path and the changes made in these 4 tasks are described below. The 4 sub-tasks are staged in a way that minimizes dependencies between them. In this sub-task we introduce changes to the  orderer/common/bootstrap package (see details below). In essence, Just before the last config block of the system channel (COMMIT) is written to the ledger, the bootstrap file (a.k.a "genesis.block", do not confuse with the first block of the ledger) is swapped with the last block of the system channel. This sub-task extends package orderer/common/bootstrap to support this functionality. See respective JIRA item for further details. The "green" path for migration is the following: 1. Start with a Kafka-based ordering service 2. Send a config update tx (START-TX) on the system channel that: - Has ConsensusType.MigrationState=START - This will disable the creation of new channels - This will disable the processing of normal (standard channel) transactions 3. Wait until the START-TX is committed and get the block height H of that tx 4. Send a config update tx (CONTEXT-TX) on each of the standard channels that: - Has ConsensusType.MigrationState=CONTEXT - Has ConsensusType.MigrationContext=H - Has ConsensusType.Type="etcdraft" - Has ConsensusType.Metadata=<a marshaled etcdraft metadata: Consenters, Options, etc> 5. Send a config update tx (COMMIT-TX) on the system channel that: - Has ConsensusType.MigrationState=COMMIT - Has ConsensusType.MigrationContext=H - Has ConsensusType.Type="etcdraft" - Has ConsensusType.Metadata=<a marshaled etcdraft metadata: Consenters, Options, etc> - The metadata should be the same as for the standard channels, with the same precautions. - If committed successfully, no further configuration will be possible 6. Restart each orderer - The orderer will bootstrap into an etcdraft mode - Each channel will form a cluster - Normal transactions can resume now 7. In order to configure the channels (system or standard), make sure that the first config update tx (on any given channel) after migration has: - Has ConsensusType.MigrationState=NONE - Has ConsensusType.MigrationS=NONE - In addition to other changes to the channel's config. Change-Id: Iccd146bb7260bafa4e4d8c4ee457d2ac19f5a642 Signed-off-by: Yoav Tock <tock@il.ibm.com>
1 parent 8fed497 commit bccbd4d

File tree

3 files changed

+346
-101
lines changed

3 files changed

+346
-101
lines changed

orderer/common/bootstrap/bootstrap.go

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,5 @@
1-
/*
2-
Copyright IBM Corp. 2016 All Rights Reserved.
3-
4-
Licensed under the Apache License, Version 2.0 (the "License");
5-
you may not use this file except in compliance with the License.
6-
You may obtain a copy of the License at
7-
8-
http://www.apache.org/licenses/LICENSE-2.0
9-
10-
Unless required by applicable law or agreed to in writing, software
11-
distributed under the License is distributed on an "AS IS" BASIS,
12-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
See the License for the specific language governing permissions and
14-
limitations under the License.
15-
*/
1+
// Copyright IBM Corp. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
163

174
package bootstrap
185

@@ -26,3 +13,26 @@ type Helper interface {
2613
// the ledger (be it reading from the filesystem, generating it, etc.)
2714
GenesisBlock() *ab.Block
2815
}
16+
17+
// Replacer provides the ability to to replace the current genesis block used
18+
// for bootstrapping with the supplied block. It is used during consensus-type
19+
// migration in order to replace the original genesis block used for
20+
// bootstrapping with the latest config block of the system channel, which
21+
// contains the new consensus-type. This will ensure the instantiation of the
22+
// correct consenter type when the server restarts.
23+
type Replacer interface {
24+
// ReplaceGenesisBlockFile should first copy the current file to a backup
25+
// file: <genesis-file-name> => <genesis-file-name>.bak
26+
// and then overwrite the original file with the content of the given block.
27+
// If something goes wrong during migration, the original file could be
28+
// restored from the backup.
29+
// An error is returned if the operation was not completed successfully.
30+
ReplaceGenesisBlockFile(block *ab.Block) error
31+
32+
// CheckReadWrite checks whether the current file is readable and writable,
33+
// because if it is not, there is no point in attempting to replace. This
34+
// check is performed at the beginning of the consensus-type migration
35+
// process.
36+
// An error is returned if the file is not readable and writable.
37+
CheckReadWrite() error
38+
}
Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,121 @@
1-
/*
2-
Copyright IBM Corp. 2016 All Rights Reserved.
3-
4-
Licensed under the Apache License, Version 2.0 (the "License");
5-
you may not use this file except in compliance with the License.
6-
You may obtain a copy of the License at
7-
8-
http://www.apache.org/licenses/LICENSE-2.0
9-
10-
Unless required by applicable law or agreed to in writing, software
11-
distributed under the License is distributed on an "AS IS" BASIS,
12-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
See the License for the specific language governing permissions and
14-
limitations under the License.
15-
*/
1+
// Copyright IBM Corp. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
163

174
package file
185

196
import (
20-
"fmt"
7+
"io"
218
"io/ioutil"
9+
"os"
2210

2311
"github.com/golang/protobuf/proto"
2412
"github.com/hyperledger/fabric/orderer/common/bootstrap"
2513
cb "github.com/hyperledger/fabric/protos/common"
14+
"github.com/pkg/errors"
2615
)
2716

2817
type fileBootstrapper struct {
2918
GenesisBlockFile string
3019
}
3120

32-
// New returns a new static bootstrap helper
21+
// New returns a new static bootstrap helper.
3322
func New(fileName string) bootstrap.Helper {
3423
return &fileBootstrapper{
3524
GenesisBlockFile: fileName,
3625
}
3726
}
3827

39-
// GenesisBlock returns the genesis block to be used for bootstrapping
28+
// NewReplacer returns a new bootstrap replacer.
29+
func NewReplacer(fileName string) bootstrap.Replacer {
30+
return &fileBootstrapper{
31+
GenesisBlockFile: fileName,
32+
}
33+
}
34+
35+
// GenesisBlock returns the genesis block to be used for bootstrapping.
4036
func (b *fileBootstrapper) GenesisBlock() *cb.Block {
4137
bootstrapFile, fileErr := ioutil.ReadFile(b.GenesisBlockFile)
4238
if fileErr != nil {
43-
panic(fmt.Errorf("Unable to bootstrap orderer. Error reading genesis block file: %v", fileErr))
39+
panic(errors.Errorf("unable to bootstrap orderer. Error reading genesis block file: %v", fileErr))
4440
}
4541
genesisBlock := &cb.Block{}
4642
unmarshallErr := proto.Unmarshal(bootstrapFile, genesisBlock)
4743
if unmarshallErr != nil {
48-
panic(fmt.Errorf("Unable to bootstrap orderer. Error unmarshalling genesis block: %v", unmarshallErr))
44+
panic(errors.Errorf("unable to bootstrap orderer. Error unmarshalling genesis block: %v", unmarshallErr))
4945

5046
}
5147
return genesisBlock
5248
} // GenesisBlock
49+
50+
// ReplaceGenesisBlockFile creates a backup of the genesis block file, and then replaces
51+
// it with the content of the given block.
52+
// This is used during consensus-type migration in order to generate a bootstrap file that
53+
// specifies the new consensus-type.
54+
func (b *fileBootstrapper) ReplaceGenesisBlockFile(block *cb.Block) error {
55+
buff, marshalErr := proto.Marshal(block)
56+
if marshalErr != nil {
57+
return errors.Wrap(marshalErr, "could not marshal block into a []byte")
58+
}
59+
60+
genFileStat, statErr := os.Stat(b.GenesisBlockFile)
61+
if statErr != nil {
62+
return errors.Wrapf(statErr, "could not get the os.Stat of the genesis block file: %s", b.GenesisBlockFile)
63+
}
64+
65+
if !genFileStat.Mode().IsRegular() {
66+
return errors.Errorf("genesis block file: %s, is not a regular file", b.GenesisBlockFile)
67+
}
68+
69+
backupFile := b.GenesisBlockFile + ".bak"
70+
if err := backupGenesisFile(b.GenesisBlockFile, backupFile); err != nil {
71+
return errors.Wrapf(err, "could not copy genesis block file (%s) into backup file: %s",
72+
b.GenesisBlockFile, backupFile)
73+
}
74+
75+
if err := ioutil.WriteFile(b.GenesisBlockFile, buff, genFileStat.Mode()); err != nil {
76+
return errors.Wrapf(err, "could not write new genesis block into file: %s; use backup if necessary: %s",
77+
b.GenesisBlockFile, backupFile)
78+
}
79+
80+
return nil
81+
}
82+
83+
func backupGenesisFile(src, dst string) error {
84+
source, err := os.Open(src)
85+
if err != nil {
86+
return err
87+
}
88+
defer source.Close()
89+
90+
destination, err := os.Create(dst)
91+
if err != nil {
92+
return err
93+
}
94+
defer destination.Close()
95+
96+
_, err = io.Copy(destination, source)
97+
return err
98+
}
99+
100+
func (b *fileBootstrapper) CheckReadWrite() error {
101+
genFileStat, statErr := os.Stat(b.GenesisBlockFile)
102+
if statErr != nil {
103+
return errors.Wrapf(statErr, "could not get the os.Stat of the genesis block file: %s", b.GenesisBlockFile)
104+
}
105+
106+
if !genFileStat.Mode().IsRegular() {
107+
return errors.Errorf("genesis block file: %s, is not a regular file", b.GenesisBlockFile)
108+
}
109+
110+
genFile, openErr := os.OpenFile(b.GenesisBlockFile, os.O_RDWR, genFileStat.Mode().Perm())
111+
if openErr != nil {
112+
if os.IsPermission(openErr) {
113+
return errors.Wrapf(openErr, "genesis block file: %s, cannot be opened for read-write, check permissions", b.GenesisBlockFile)
114+
} else {
115+
return errors.Wrapf(openErr, "genesis block file: %s, cannot be opened for read-write", b.GenesisBlockFile)
116+
}
117+
}
118+
genFile.Close()
119+
120+
return nil
121+
}

0 commit comments

Comments
 (0)