Skip to content

Commit 5c3e6dc

Browse files
committed
[FAB-1883] Add CLI support to load anchor peers
This commit adds a flag (-a) that loads anchor peers from files. The files can be created easily using any text editor and they are in the following format: host port pem certificate The parameter of the anchor peer is a comma separated file list. Signed-off-by: Yacov Manevich <[email protected]> Change-Id: I59cda7b09251b1ef6e7475d70e975e3369915eff
1 parent 377e440 commit 5c3e6dc

File tree

11 files changed

+416
-21
lines changed

11 files changed

+416
-21
lines changed

common/configtx/test/helper.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/hyperledger/fabric/protos/utils"
2727

2828
"github.com/golang/protobuf/proto"
29+
"github.com/hyperledger/fabric/protos/peer"
2930
)
3031

3132
const (
@@ -62,7 +63,8 @@ func init() {
6263
}
6364

6465
template = configtx.NewSimpleTemplate(templateProto.Items...)
65-
gossTemplate := configtx.NewSimpleTemplate(utils.EncodeAnchorPeers())
66+
anchorPeers := []*peer.AnchorPeer{{Host: "fakehost", Port: 2000, Cert: []byte{}}}
67+
gossTemplate := configtx.NewSimpleTemplate(utils.EncodeAnchorPeers(anchorPeers))
6668
genesisFactory = genesis.NewFactoryImpl(configtx.NewCompositeTemplate(MSPTemplate{}, template, gossTemplate))
6769
}
6870

peer/channel/channel.go

+42-6
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ var (
3939
genesisBlockPath string
4040

4141
// create related variables
42-
chainID string
42+
chainID string
43+
anchorPeerList string
4344
)
4445

4546
// Cmd returns the cobra command for Node
@@ -58,12 +59,13 @@ func Cmd(cf *ChannelCmdFactory) *cobra.Command {
5859
return channelCmd
5960
}
6061

61-
//AddFlags adds flags for create and join
62+
// AddFlags adds flags for create and join
6263
func AddFlags(cmd *cobra.Command) {
6364
flags := cmd.PersistentFlags()
6465

6566
flags.StringVarP(&genesisBlockPath, "blockpath", "b", common.UndefinedParamValue, "Path to file containing genesis block")
6667
flags.StringVarP(&chainID, "chain", "c", "mychain", "In case of a newChain command, the chain ID to create.")
68+
flags.StringVarP(&anchorPeerList, "anchors", "a", "", anchorPeerUsage)
6769
}
6870

6971
var channelCmd = &cobra.Command{
@@ -74,10 +76,11 @@ var channelCmd = &cobra.Command{
7476

7577
// ChannelCmdFactory holds the clients used by ChannelCmdFactory
7678
type ChannelCmdFactory struct {
77-
EndorserClient pb.EndorserClient
78-
Signer msp.SigningIdentity
79-
BroadcastClient common.BroadcastClient
80-
DeliverClient deliverClientIntf
79+
EndorserClient pb.EndorserClient
80+
Signer msp.SigningIdentity
81+
BroadcastClient common.BroadcastClient
82+
DeliverClient deliverClientIntf
83+
AnchorPeerParser *common.AnchorPeerParser
8184
}
8285

8386
// InitCmdFactory init the ChannelCmdFactor with default clients
@@ -116,7 +119,40 @@ func InitCmdFactory(isOrdererRequired bool) (*ChannelCmdFactory, error) {
116119
}
117120

118121
cmdFact.DeliverClient = newDeliverClient(client, chainID)
122+
cmdFact.AnchorPeerParser = common.GetAnchorPeersParser(anchorPeerList)
119123
}
120124

121125
return cmdFact, nil
122126
}
127+
128+
const anchorPeerUsage = `In case of a newChain command, the list of anchor peer files, separated by commas.
129+
The files should be in the following format:
130+
anchorPeerHost
131+
anchorPeerPort
132+
PEM encoded certificate.
133+
134+
In example:
135+
1.2.3.4
136+
7051
137+
-----BEGIN CERTIFICATE-----
138+
MIIDXTCCAkWgAwIBAgIJALRf63iSHa0BMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
139+
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
140+
aWRnaXRzIFB0eSBMdGQwHhcNMTcwMTI2MjMyMzM1WhcNMTgwMTI2MjMyMzM1WjBF
141+
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
142+
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
143+
CgKCAQEAzbph0SEHYb/tvNYATWfpl7oAFpw3Tcn2s0icJaScqs2RodjosIOBK6AB
144+
N6fkgGDHwYhYbMNfJzUYSYgXD4MPjDxzPw+/Hz02bjuxFB8pQnmln6b6pVHz79vL
145+
i3UQ8eaCe3zswpX0JJTlOs5wdJGOySNRNatbVKl9HDNWcNl6Ec5MrlK3/v6OGF03
146+
0ak7QYDNjyHaz3rMaOzJumRJeOxtjUO/+TbjN+bkcXSgQH9LjoeaZdkV/QWrCA1I
147+
qGowBOxYcyiX56bKKFvCZ76ZYA55d3HyI/H7S258CTdE6WUTDXNqmXnX5WbBuUiK
148+
dypI+KmGlzrRETahrJSJKdlxxtpPVwIDAQABo1AwTjAdBgNVHQ4EFgQUnK6ITmnz
149+
hfNKFr+57Bcayzio47EwHwYDVR0jBBgwFoAUnK6ITmnzhfNKFr+57Bcayzio47Ew
150+
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAvYFu4xQDE11C8wdK/5LE
151+
G61E9yjsDjFlhzgsG8+TqWI6LjHzm3hSNj7VMI7f0ckydxxOSQqKEkkQaL5GNS3B
152+
JOwsGtPjgQ2Sxx2KrEyaNozxznm1qZflQCis95NVvjHeiybbLfjQRVKde0+7kSKc
153+
cqBBE+IwxNofNyevlRyCBNsH6v2DLJoiFwvE5PqY6XvAcC17va/TKS16TVCqpxX0
154+
OrngleEKom1hiU1MzGZ29/nGpwP/oD8Lf+BqxipLf3BdiDR2+n5dbrV/ul1VczwQ
155+
F2ht++pZbdiqmv7CRAfvkSzrkwIeL+XfVR6ncFf4Nf92u6DJDnTzc/0K3pLaE+bo
156+
JQ==
157+
-----END CERTIFICATE-----
158+
`

peer/channel/create.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
configtxtest "github.com/hyperledger/fabric/common/configtx/test"
2626
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
2727
"github.com/hyperledger/fabric/orderer/common/bootstrap/provisional"
28+
"github.com/hyperledger/fabric/peer/common"
2829
cb "github.com/hyperledger/fabric/protos/common"
2930
"github.com/hyperledger/fabric/protos/utils"
3031
"github.com/spf13/cobra"
@@ -44,11 +45,17 @@ func createCmd(cf *ChannelCmdFactory) *cobra.Command {
4445
}
4546

4647
func sendCreateChainTransaction(cf *ChannelCmdFactory) error {
48+
if cf.AnchorPeerParser == nil {
49+
cf.AnchorPeerParser = common.GetDefaultAnchorPeerParser()
50+
}
51+
anchorPeers, err := cf.AnchorPeerParser.Parse()
52+
if err != nil {
53+
return err
54+
}
4755
//TODO this is a temporary hack until `orderer.template` is supplied from the CLI
4856
oTemplate := configtxtest.GetOrdererTemplate()
4957
mspTemplate := configtx.NewSimpleTemplate(utils.EncodeMSPUnsigned(chainID))
50-
gossTemplate := configtx.NewSimpleTemplate(utils.EncodeAnchorPeers())
51-
58+
gossTemplate := configtx.NewSimpleTemplate(utils.EncodeAnchorPeers(anchorPeers))
5259
chCrtTemp := configtx.NewCompositeTemplate(oTemplate, mspTemplate, gossTemplate)
5360

5461
signer, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()

peer/channel/create_test.go

+77-8
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,40 @@ func TestCreateChain(t *testing.T) {
8787

8888
mockBroadcastClient := common.GetMockBroadcastClient(nil)
8989

90+
mockCF := &ChannelCmdFactory{
91+
BroadcastClient: mockBroadcastClient,
92+
Signer: signer,
93+
DeliverClient: &mockDeliverClient{},
94+
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersOrg1.txt"),
95+
}
96+
97+
cmd := createCmd(mockCF)
98+
99+
AddFlags(cmd)
100+
101+
args := []string{"-c", mockchain, "-a", "../common/testdata/anchorPeersOrg1.txt"}
102+
cmd.SetArgs(args)
103+
104+
if err := cmd.Execute(); err != nil {
105+
t.Fail()
106+
t.Errorf("expected join command to succeed")
107+
}
108+
}
109+
110+
func TestCreateChainWithDefaultAnchorPeers(t *testing.T) {
111+
InitMSP()
112+
113+
mockchain := "mockchain"
114+
115+
defer os.Remove(mockchain + ".block")
116+
117+
signer, err := common.GetDefaultSigner()
118+
if err != nil {
119+
t.Fatalf("Get default signer error: %v", err)
120+
}
121+
122+
mockBroadcastClient := common.GetMockBroadcastClient(nil)
123+
90124
mockCF := &ChannelCmdFactory{
91125
BroadcastClient: mockBroadcastClient,
92126
Signer: signer,
@@ -106,6 +140,39 @@ func TestCreateChain(t *testing.T) {
106140
}
107141
}
108142

143+
func TestCreateChainInvalidAnchorPeers(t *testing.T) {
144+
InitMSP()
145+
146+
mockchain := "mockchain"
147+
148+
defer os.Remove(mockchain + ".block")
149+
150+
signer, err := common.GetDefaultSigner()
151+
if err != nil {
152+
t.Fatalf("Get default signer error: %v", err)
153+
}
154+
155+
mockBroadcastClient := common.GetMockBroadcastClient(nil)
156+
157+
mockCF := &ChannelCmdFactory{
158+
BroadcastClient: mockBroadcastClient,
159+
Signer: signer,
160+
DeliverClient: &mockDeliverClient{},
161+
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersBadPEM.txt"),
162+
}
163+
164+
cmd := createCmd(mockCF)
165+
166+
AddFlags(cmd)
167+
168+
args := []string{"-c", mockchain, "-a", "../common/testdata/anchorPeersBadPEM.txt"}
169+
cmd.SetArgs(args)
170+
171+
if err := cmd.Execute(); err == nil {
172+
t.Errorf("expected create chain to fail because of invalid anchor peer file")
173+
}
174+
}
175+
109176
func TestCreateChainBCFail(t *testing.T) {
110177
InitMSP()
111178

@@ -122,16 +189,17 @@ func TestCreateChainBCFail(t *testing.T) {
122189
mockBroadcastClient := common.GetMockBroadcastClient(sendErr)
123190

124191
mockCF := &ChannelCmdFactory{
125-
BroadcastClient: mockBroadcastClient,
126-
Signer: signer,
127-
DeliverClient: &mockDeliverClient{},
192+
BroadcastClient: mockBroadcastClient,
193+
Signer: signer,
194+
DeliverClient: &mockDeliverClient{},
195+
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersOrg1.txt"),
128196
}
129197

130198
cmd := createCmd(mockCF)
131199

132200
AddFlags(cmd)
133201

134-
args := []string{"-c", mockchain}
202+
args := []string{"-c", mockchain, "-a", "../common/testdata/anchorPeersOrg1.txt"}
135203
cmd.SetArgs(args)
136204

137205
expectedErrMsg := sendErr.Error()
@@ -161,16 +229,17 @@ func TestCreateChainDeliverFail(t *testing.T) {
161229
recvErr := fmt.Errorf("deliver create tx failed")
162230

163231
mockCF := &ChannelCmdFactory{
164-
BroadcastClient: mockBroadcastClient,
165-
Signer: signer,
166-
DeliverClient: &mockDeliverClient{recvErr},
232+
BroadcastClient: mockBroadcastClient,
233+
Signer: signer,
234+
DeliverClient: &mockDeliverClient{recvErr},
235+
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersOrg1.txt"),
167236
}
168237

169238
cmd := createCmd(mockCF)
170239

171240
AddFlags(cmd)
172241

173-
args := []string{"-c", mockchain}
242+
args := []string{"-c", mockchain, "-a", "../common/testdata/anchorPeersOrg1.txt"}
174243
cmd.SetArgs(args)
175244

176245
expectedErrMsg := recvErr.Error()

peer/common/anchors.go

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
Copyright IBM Corp. 2017 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+
*/
16+
17+
package common
18+
19+
import (
20+
"bufio"
21+
"crypto/x509"
22+
"encoding/pem"
23+
"fmt"
24+
"os"
25+
"strconv"
26+
"strings"
27+
28+
"github.com/hyperledger/fabric/protos/peer"
29+
)
30+
31+
type AnchorPeerParser struct {
32+
anchorPeerParam string
33+
defaultAnchorPeers []*peer.AnchorPeer
34+
}
35+
36+
func (app *AnchorPeerParser) Parse() ([]*peer.AnchorPeer, error) {
37+
if app.defaultAnchorPeers != nil {
38+
return app.defaultAnchorPeers, nil
39+
}
40+
return loadAnchorPeers(app.anchorPeerParam)
41+
}
42+
43+
func GetDefaultAnchorPeerParser() *AnchorPeerParser {
44+
anchorPeerFile := "/tmp/anchorPeer.txt"
45+
f, err := os.Create(anchorPeerFile)
46+
if err != nil {
47+
panic(err)
48+
}
49+
defer f.Close()
50+
defer os.Remove(anchorPeerFile)
51+
f.Write([]byte(defaultAnchorPeerFile))
52+
f.Sync()
53+
aps, err := loadAnchorPeers(anchorPeerFile)
54+
if err != nil {
55+
panic(err)
56+
}
57+
return &AnchorPeerParser{defaultAnchorPeers: aps}
58+
}
59+
60+
func loadAnchorPeers(anchorPeerParam string) ([]*peer.AnchorPeer, error) {
61+
anchorPeerFileList := strings.Split(anchorPeerParam, ",")
62+
for _, f := range anchorPeerFileList {
63+
if _, err := os.Stat(f); os.IsNotExist(err) {
64+
return nil, fmt.Errorf("File %s doesn't exist", f)
65+
}
66+
}
67+
68+
var anchorPeers []*peer.AnchorPeer
69+
70+
for _, f := range anchorPeerFileList {
71+
if ap, err := anchorPeerFromFile(f); err != nil {
72+
return nil, err
73+
} else {
74+
anchorPeers = append(anchorPeers, ap)
75+
}
76+
}
77+
78+
return anchorPeers, nil
79+
}
80+
81+
func anchorPeerFromFile(filename string) (*peer.AnchorPeer, error) {
82+
f, err := os.Open(filename)
83+
if err != nil {
84+
return nil, err
85+
}
86+
defer f.Close()
87+
sc := bufio.NewScanner(f)
88+
invalidFormatErr := fmt.Errorf("%s has invalid format", filename)
89+
if !sc.Scan() {
90+
return nil, invalidFormatErr
91+
}
92+
hostname := sc.Text()
93+
if !sc.Scan() || len(hostname) == 0 {
94+
return nil, invalidFormatErr
95+
}
96+
port, err := strconv.ParseInt(sc.Text(), 10, 64)
97+
if err != nil {
98+
return nil, fmt.Errorf("Second line must be a number: %d", port)
99+
}
100+
101+
var rawPEM []byte
102+
for sc.Scan() {
103+
line := sc.Text()
104+
line = line + "\n"
105+
rawPEM = append(rawPEM, []byte(line)...)
106+
}
107+
block, _ := pem.Decode(rawPEM)
108+
if block == nil {
109+
return nil, fmt.Errorf("Anchor peer certificate is not a valid PEM certificate")
110+
}
111+
_, err = x509.ParseCertificate(block.Bytes)
112+
if err != nil {
113+
return nil, fmt.Errorf("Anchor peer certificate is not a valid PEM certificate: %v", err)
114+
}
115+
ap := &peer.AnchorPeer{
116+
Host: hostname,
117+
Port: int32(port),
118+
Cert: block.Bytes,
119+
}
120+
return ap, nil
121+
}
122+
123+
const defaultAnchorPeerFile = `anchorpeer
124+
7051
125+
-----BEGIN CERTIFICATE-----
126+
MIIDZzCCAk+gAwIBAgIJAKSntkwsYDPhMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNV
127+
BAYTAklMMQ8wDQYDVQQIDAZJc3JhZWwxDjAMBgNVBAcMBUhhaWZhMQwwCgYDVQQK
128+
DANJQk0xDDAKBgNVBAsMA0lCTTAeFw0xNzAxMjcwMzUzMjdaFw0xODAxMjcwMzUz
129+
MjdaMEoxCzAJBgNVBAYTAklMMQ8wDQYDVQQIDAZJc3JhZWwxDjAMBgNVBAcMBUhh
130+
aWZhMQwwCgYDVQQKDANJQk0xDDAKBgNVBAsMA0lCTTCCASIwDQYJKoZIhvcNAQEB
131+
BQADggEPADCCAQoCggEBAL0KUiuuZFfs+BN7+FnDKoeiVGCkayQVrPrdeO8Mwu/n
132+
928T9lhmBI6wFnmkdeEjYTi1M5dks8hEal2AP8ykREc+LTmMH5JAJ8kktnoNQteO
133+
rdFqnBpzA0IdiDnaLLLU3QD22VT47TPxWnfqZ3Z+fEJkmxc+tNmJJ5/0eCxXC4v4
134+
875wQZP8CEeI1EpkljL6AILLNCUN4qpug2R2CCBRvGaqA81TM8NKxvWgN90iSAiv
135+
vrQIc3/aelIpaJN457JEqLWgAcWw982rFUn5+D3u63pUq99lWH16VU4vRdUFzqi1
136+
E3mBbGNTcNBzrBYswj5KhMFHLBpzIwQQX+Tvjh70cwkCAwEAAaNQME4wHQYDVR0O
137+
BBYEFNHpTtXPDggAIavkdxLh+ttFH+HCMB8GA1UdIwQYMBaAFNHpTtXPDggAIavk
138+
dxLh+ttFH+HCMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADuYrb0h
139+
eXepdpkUSZ6t5mk6R4vyaGDQAHCUltL5Q2qhL8f5sWxMBqke/JhbB02+pQCsvj8P
140+
SIVSXuCXgFbzP0O3gNWqGhGn9atgN/j81hGyXtpAl5U5hyqcaFATX++Rdv58TKty
141+
WnjzYUtnrG2W6c5uK/XPmoUHoNHxkgj1HrlmuahdrxzFXkdcND7UIfW8U2K0Cz4V
142+
gJyAC5yIOs+kakE2gwjJI8SqREgegfO1JIbBfnUCkDJj1TLu2eUkBgnVLeJrcbXq
143+
AbiMV4MFfj5KFA51Tp8QltKbsPPm1Vx3+CRVWNnMgqVWygIQF+8h4H/CcETU4XCV
144+
4LqJvYfKwy27YUA=
145+
-----END CERTIFICATE-----`

0 commit comments

Comments
 (0)