Skip to content

Commit

Permalink
Merge "[FAB-1883] Add CLI support to load anchor peers"
Browse files Browse the repository at this point in the history
  • Loading branch information
christo4ferris authored and Gerrit Code Review committed Jan 27, 2017
2 parents 44afe49 + 5c3e6dc commit cf28448
Show file tree
Hide file tree
Showing 11 changed files with 416 additions and 21 deletions.
4 changes: 3 additions & 1 deletion common/configtx/test/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/hyperledger/fabric/protos/utils"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/protos/peer"
)

const (
Expand Down Expand Up @@ -62,7 +63,8 @@ func init() {
}

template = configtx.NewSimpleTemplate(templateProto.Items...)
gossTemplate := configtx.NewSimpleTemplate(utils.EncodeAnchorPeers())
anchorPeers := []*peer.AnchorPeer{{Host: "fakehost", Port: 2000, Cert: []byte{}}}
gossTemplate := configtx.NewSimpleTemplate(utils.EncodeAnchorPeers(anchorPeers))
genesisFactory = genesis.NewFactoryImpl(configtx.NewCompositeTemplate(MSPTemplate{}, template, gossTemplate))
}

Expand Down
48 changes: 42 additions & 6 deletions peer/channel/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ var (
genesisBlockPath string

// create related variables
chainID string
chainID string
anchorPeerList string
)

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

//AddFlags adds flags for create and join
// AddFlags adds flags for create and join
func AddFlags(cmd *cobra.Command) {
flags := cmd.PersistentFlags()

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

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

// ChannelCmdFactory holds the clients used by ChannelCmdFactory
type ChannelCmdFactory struct {
EndorserClient pb.EndorserClient
Signer msp.SigningIdentity
BroadcastClient common.BroadcastClient
DeliverClient deliverClientIntf
EndorserClient pb.EndorserClient
Signer msp.SigningIdentity
BroadcastClient common.BroadcastClient
DeliverClient deliverClientIntf
AnchorPeerParser *common.AnchorPeerParser
}

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

cmdFact.DeliverClient = newDeliverClient(client, chainID)
cmdFact.AnchorPeerParser = common.GetAnchorPeersParser(anchorPeerList)
}

return cmdFact, nil
}

const anchorPeerUsage = `In case of a newChain command, the list of anchor peer files, separated by commas.
The files should be in the following format:
anchorPeerHost
anchorPeerPort
PEM encoded certificate.
In example:
1.2.3.4
7051
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJALRf63iSHa0BMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTcwMTI2MjMyMzM1WhcNMTgwMTI2MjMyMzM1WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAzbph0SEHYb/tvNYATWfpl7oAFpw3Tcn2s0icJaScqs2RodjosIOBK6AB
N6fkgGDHwYhYbMNfJzUYSYgXD4MPjDxzPw+/Hz02bjuxFB8pQnmln6b6pVHz79vL
i3UQ8eaCe3zswpX0JJTlOs5wdJGOySNRNatbVKl9HDNWcNl6Ec5MrlK3/v6OGF03
0ak7QYDNjyHaz3rMaOzJumRJeOxtjUO/+TbjN+bkcXSgQH9LjoeaZdkV/QWrCA1I
qGowBOxYcyiX56bKKFvCZ76ZYA55d3HyI/H7S258CTdE6WUTDXNqmXnX5WbBuUiK
dypI+KmGlzrRETahrJSJKdlxxtpPVwIDAQABo1AwTjAdBgNVHQ4EFgQUnK6ITmnz
hfNKFr+57Bcayzio47EwHwYDVR0jBBgwFoAUnK6ITmnzhfNKFr+57Bcayzio47Ew
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAvYFu4xQDE11C8wdK/5LE
G61E9yjsDjFlhzgsG8+TqWI6LjHzm3hSNj7VMI7f0ckydxxOSQqKEkkQaL5GNS3B
JOwsGtPjgQ2Sxx2KrEyaNozxznm1qZflQCis95NVvjHeiybbLfjQRVKde0+7kSKc
cqBBE+IwxNofNyevlRyCBNsH6v2DLJoiFwvE5PqY6XvAcC17va/TKS16TVCqpxX0
OrngleEKom1hiU1MzGZ29/nGpwP/oD8Lf+BqxipLf3BdiDR2+n5dbrV/ul1VczwQ
F2ht++pZbdiqmv7CRAfvkSzrkwIeL+XfVR6ncFf4Nf92u6DJDnTzc/0K3pLaE+bo
JQ==
-----END CERTIFICATE-----
`
11 changes: 9 additions & 2 deletions peer/channel/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
configtxtest "github.com/hyperledger/fabric/common/configtx/test"
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
"github.com/hyperledger/fabric/orderer/common/bootstrap/provisional"
"github.com/hyperledger/fabric/peer/common"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"
"github.com/spf13/cobra"
Expand All @@ -44,11 +45,17 @@ func createCmd(cf *ChannelCmdFactory) *cobra.Command {
}

func sendCreateChainTransaction(cf *ChannelCmdFactory) error {
if cf.AnchorPeerParser == nil {
cf.AnchorPeerParser = common.GetDefaultAnchorPeerParser()
}
anchorPeers, err := cf.AnchorPeerParser.Parse()
if err != nil {
return err
}
//TODO this is a temporary hack until `orderer.template` is supplied from the CLI
oTemplate := configtxtest.GetOrdererTemplate()
mspTemplate := configtx.NewSimpleTemplate(utils.EncodeMSPUnsigned(chainID))
gossTemplate := configtx.NewSimpleTemplate(utils.EncodeAnchorPeers())

gossTemplate := configtx.NewSimpleTemplate(utils.EncodeAnchorPeers(anchorPeers))
chCrtTemp := configtx.NewCompositeTemplate(oTemplate, mspTemplate, gossTemplate)

signer, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
Expand Down
85 changes: 77 additions & 8 deletions peer/channel/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,40 @@ func TestCreateChain(t *testing.T) {

mockBroadcastClient := common.GetMockBroadcastClient(nil)

mockCF := &ChannelCmdFactory{
BroadcastClient: mockBroadcastClient,
Signer: signer,
DeliverClient: &mockDeliverClient{},
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersOrg1.txt"),
}

cmd := createCmd(mockCF)

AddFlags(cmd)

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

if err := cmd.Execute(); err != nil {
t.Fail()
t.Errorf("expected join command to succeed")
}
}

func TestCreateChainWithDefaultAnchorPeers(t *testing.T) {
InitMSP()

mockchain := "mockchain"

defer os.Remove(mockchain + ".block")

signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}

mockBroadcastClient := common.GetMockBroadcastClient(nil)

mockCF := &ChannelCmdFactory{
BroadcastClient: mockBroadcastClient,
Signer: signer,
Expand All @@ -106,6 +140,39 @@ func TestCreateChain(t *testing.T) {
}
}

func TestCreateChainInvalidAnchorPeers(t *testing.T) {
InitMSP()

mockchain := "mockchain"

defer os.Remove(mockchain + ".block")

signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}

mockBroadcastClient := common.GetMockBroadcastClient(nil)

mockCF := &ChannelCmdFactory{
BroadcastClient: mockBroadcastClient,
Signer: signer,
DeliverClient: &mockDeliverClient{},
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersBadPEM.txt"),
}

cmd := createCmd(mockCF)

AddFlags(cmd)

args := []string{"-c", mockchain, "-a", "../common/testdata/anchorPeersBadPEM.txt"}
cmd.SetArgs(args)

if err := cmd.Execute(); err == nil {
t.Errorf("expected create chain to fail because of invalid anchor peer file")
}
}

func TestCreateChainBCFail(t *testing.T) {
InitMSP()

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

mockCF := &ChannelCmdFactory{
BroadcastClient: mockBroadcastClient,
Signer: signer,
DeliverClient: &mockDeliverClient{},
BroadcastClient: mockBroadcastClient,
Signer: signer,
DeliverClient: &mockDeliverClient{},
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersOrg1.txt"),
}

cmd := createCmd(mockCF)

AddFlags(cmd)

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

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

mockCF := &ChannelCmdFactory{
BroadcastClient: mockBroadcastClient,
Signer: signer,
DeliverClient: &mockDeliverClient{recvErr},
BroadcastClient: mockBroadcastClient,
Signer: signer,
DeliverClient: &mockDeliverClient{recvErr},
AnchorPeerParser: common.GetAnchorPeersParser("../common/testdata/anchorPeersOrg1.txt"),
}

cmd := createCmd(mockCF)

AddFlags(cmd)

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

expectedErrMsg := recvErr.Error()
Expand Down
145 changes: 145 additions & 0 deletions peer/common/anchors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
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 common

import (
"bufio"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"strconv"
"strings"

"github.com/hyperledger/fabric/protos/peer"
)

type AnchorPeerParser struct {
anchorPeerParam string
defaultAnchorPeers []*peer.AnchorPeer
}

func (app *AnchorPeerParser) Parse() ([]*peer.AnchorPeer, error) {
if app.defaultAnchorPeers != nil {
return app.defaultAnchorPeers, nil
}
return loadAnchorPeers(app.anchorPeerParam)
}

func GetDefaultAnchorPeerParser() *AnchorPeerParser {
anchorPeerFile := "/tmp/anchorPeer.txt"
f, err := os.Create(anchorPeerFile)
if err != nil {
panic(err)
}
defer f.Close()
defer os.Remove(anchorPeerFile)
f.Write([]byte(defaultAnchorPeerFile))
f.Sync()
aps, err := loadAnchorPeers(anchorPeerFile)
if err != nil {
panic(err)
}
return &AnchorPeerParser{defaultAnchorPeers: aps}
}

func loadAnchorPeers(anchorPeerParam string) ([]*peer.AnchorPeer, error) {
anchorPeerFileList := strings.Split(anchorPeerParam, ",")
for _, f := range anchorPeerFileList {
if _, err := os.Stat(f); os.IsNotExist(err) {
return nil, fmt.Errorf("File %s doesn't exist", f)
}
}

var anchorPeers []*peer.AnchorPeer

for _, f := range anchorPeerFileList {
if ap, err := anchorPeerFromFile(f); err != nil {
return nil, err
} else {
anchorPeers = append(anchorPeers, ap)
}
}

return anchorPeers, nil
}

func anchorPeerFromFile(filename string) (*peer.AnchorPeer, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
sc := bufio.NewScanner(f)
invalidFormatErr := fmt.Errorf("%s has invalid format", filename)
if !sc.Scan() {
return nil, invalidFormatErr
}
hostname := sc.Text()
if !sc.Scan() || len(hostname) == 0 {
return nil, invalidFormatErr
}
port, err := strconv.ParseInt(sc.Text(), 10, 64)
if err != nil {
return nil, fmt.Errorf("Second line must be a number: %d", port)
}

var rawPEM []byte
for sc.Scan() {
line := sc.Text()
line = line + "\n"
rawPEM = append(rawPEM, []byte(line)...)
}
block, _ := pem.Decode(rawPEM)
if block == nil {
return nil, fmt.Errorf("Anchor peer certificate is not a valid PEM certificate")
}
_, err = x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("Anchor peer certificate is not a valid PEM certificate: %v", err)
}
ap := &peer.AnchorPeer{
Host: hostname,
Port: int32(port),
Cert: block.Bytes,
}
return ap, nil
}

const defaultAnchorPeerFile = `anchorpeer
7051
-----BEGIN CERTIFICATE-----
MIIDZzCCAk+gAwIBAgIJAKSntkwsYDPhMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNV
BAYTAklMMQ8wDQYDVQQIDAZJc3JhZWwxDjAMBgNVBAcMBUhhaWZhMQwwCgYDVQQK
DANJQk0xDDAKBgNVBAsMA0lCTTAeFw0xNzAxMjcwMzUzMjdaFw0xODAxMjcwMzUz
MjdaMEoxCzAJBgNVBAYTAklMMQ8wDQYDVQQIDAZJc3JhZWwxDjAMBgNVBAcMBUhh
aWZhMQwwCgYDVQQKDANJQk0xDDAKBgNVBAsMA0lCTTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAL0KUiuuZFfs+BN7+FnDKoeiVGCkayQVrPrdeO8Mwu/n
928T9lhmBI6wFnmkdeEjYTi1M5dks8hEal2AP8ykREc+LTmMH5JAJ8kktnoNQteO
rdFqnBpzA0IdiDnaLLLU3QD22VT47TPxWnfqZ3Z+fEJkmxc+tNmJJ5/0eCxXC4v4
875wQZP8CEeI1EpkljL6AILLNCUN4qpug2R2CCBRvGaqA81TM8NKxvWgN90iSAiv
vrQIc3/aelIpaJN457JEqLWgAcWw982rFUn5+D3u63pUq99lWH16VU4vRdUFzqi1
E3mBbGNTcNBzrBYswj5KhMFHLBpzIwQQX+Tvjh70cwkCAwEAAaNQME4wHQYDVR0O
BBYEFNHpTtXPDggAIavkdxLh+ttFH+HCMB8GA1UdIwQYMBaAFNHpTtXPDggAIavk
dxLh+ttFH+HCMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADuYrb0h
eXepdpkUSZ6t5mk6R4vyaGDQAHCUltL5Q2qhL8f5sWxMBqke/JhbB02+pQCsvj8P
SIVSXuCXgFbzP0O3gNWqGhGn9atgN/j81hGyXtpAl5U5hyqcaFATX++Rdv58TKty
WnjzYUtnrG2W6c5uK/XPmoUHoNHxkgj1HrlmuahdrxzFXkdcND7UIfW8U2K0Cz4V
gJyAC5yIOs+kakE2gwjJI8SqREgegfO1JIbBfnUCkDJj1TLu2eUkBgnVLeJrcbXq
AbiMV4MFfj5KFA51Tp8QltKbsPPm1Vx3+CRVWNnMgqVWygIQF+8h4H/CcETU4XCV
4LqJvYfKwy27YUA=
-----END CERTIFICATE-----`
Loading

0 comments on commit cf28448

Please sign in to comment.