Skip to content

Commit b219274

Browse files
committed
[FAB-9556] Peer CLI multi-endorse via connection prof
This CR extends peer CLI's support for obtaining multiple endorsements for an "invoke" call. The peers and the paths to the TLS root cert files for the peers can now be supplied by the --connectionprofile pflag, which if set will be used instead of the --peerAddresses and --tlsRootCertFiles pflags. Change-Id: I94d3511332cd7c0bb06de20d99ce5d355bc1829c Signed-off-by: Will Lahti <wtlahti@us.ibm.com>
1 parent 32d20ad commit b219274

14 files changed

+735
-0
lines changed

peer/chaincode/chaincode.go

+3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ var (
6565
collectionConfigBytes []byte
6666
peerAddresses []string
6767
tlsRootCertFiles []string
68+
connectionProfile string
6869
)
6970

7071
var chaincodeCmd = &cobra.Command{
@@ -116,6 +117,8 @@ func resetFlags() {
116117
fmt.Sprint("The addresses of the peers to connect to"))
117118
flags.StringArrayVarP(&tlsRootCertFiles, "tlsRootCertFiles", "", []string{common.UndefinedParamValue},
118119
fmt.Sprint("If TLS is enabled, the paths to the TLS root cert files of the peers to connect to. The order and number of certs specified should match the --peerAddresses flag"))
120+
flags.StringVarP(&connectionProfile, "connectionProfile", "", common.UndefinedParamValue,
121+
fmt.Sprint("Connection profile that provides the necessary connection information for the network. Note: currently only supported for providing peer connection information"))
119122
}
120123

121124
func attachFlags(cmd *cobra.Command, names []string) {

peer/chaincode/common.go

+21
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,27 @@ func checkChaincodeCmdParams(cmd *cobra.Command) error {
292292
}
293293

294294
func validatePeerConnectionParameters(cmdName string) error {
295+
if connectionProfile != common.UndefinedParamValue {
296+
networkConfig, err := common.GetConfig(connectionProfile)
297+
if err != nil {
298+
return err
299+
}
300+
if len(networkConfig.Channels[channelID].Peers) != 0 {
301+
peerAddresses = []string{}
302+
tlsRootCertFiles = []string{}
303+
for peer, peerChannelConfig := range networkConfig.Channels[channelID].Peers {
304+
if peerChannelConfig.EndorsingPeer {
305+
peerConfig, ok := networkConfig.Peers[peer]
306+
if !ok {
307+
return errors.Errorf("peer '%s' is defined in the channel config but doesn't have associated peer config", peer)
308+
}
309+
peerAddresses = append(peerAddresses, peerConfig.URL)
310+
tlsRootCertFiles = append(tlsRootCertFiles, peerConfig.TLSCACerts.Path)
311+
}
312+
}
313+
}
314+
}
315+
295316
// currently only support multiple peer addresses for invoke
296317
if cmdName != "invoke" && len(peerAddresses) > 1 {
297318
return errors.Errorf("'%s' command can only be executed against one peer. received %d", cmdName, len(peerAddresses))

peer/chaincode/common_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,29 @@ func TestValidatePeerConnectionParams(t *testing.T) {
274274
err = validatePeerConnectionParameters("invoke")
275275
assert.NoError(err)
276276

277+
// failure - connection profile doesn't exist
278+
resetFlags()
279+
connectionProfile = "blah"
280+
err = validatePeerConnectionParameters("invoke")
281+
assert.Error(err)
282+
assert.Contains(err.Error(), "error reading connection profile")
283+
284+
// failure - connection profile has peer defined in channel config but
285+
// not in peer config
286+
resetFlags()
287+
channelID = "mychannel"
288+
connectionProfile = "../common/testdata/connectionprofile-uneven.yaml"
289+
err = validatePeerConnectionParameters("invoke")
290+
assert.Error(err)
291+
assert.Contains(err.Error(), "defined in the channel config but doesn't have associated peer config")
292+
293+
// success - connection profile exists
294+
resetFlags()
295+
channelID = "mychannel"
296+
connectionProfile = "../common/testdata/connectionprofile.yaml"
297+
err = validatePeerConnectionParameters("invoke")
298+
assert.NoError(err)
299+
277300
// cleanup pflags and viper
278301
resetFlags()
279302
viper.Reset()

peer/chaincode/install.go

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func installCmd(cf *ChaincodeCmdFactory) *cobra.Command {
5252
"version",
5353
"peerAddresses",
5454
"tlsRootCertFiles",
55+
"connectionProfile",
5556
}
5657
attachFlags(chaincodeInstallCmd, flagList)
5758

peer/chaincode/instantiate.go

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func instantiateCmd(cf *ChaincodeCmdFactory) *cobra.Command {
4646
"collections-config",
4747
"peerAddresses",
4848
"tlsRootCertFiles",
49+
"connectionProfile",
4950
}
5051
attachFlags(chaincodeInstantiateCmd, flagList)
5152

peer/chaincode/invoke.go

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func invokeCmd(cf *ChaincodeCmdFactory) *cobra.Command {
3232
"channelID",
3333
"peerAddresses",
3434
"tlsRootCertFiles",
35+
"connectionProfile",
3536
}
3637
attachFlags(chaincodeInvokeCmd, flagList)
3738

peer/chaincode/list.go

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func listCmd(cf *ChaincodeCmdFactory) *cobra.Command {
4343
"instantiated",
4444
"peerAddresses",
4545
"tlsRootCertFiles",
46+
"connectionProfile",
4647
}
4748
attachFlags(chaincodeListCmd, flagList)
4849

peer/chaincode/query.go

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func queryCmd(cf *ChaincodeCmdFactory) *cobra.Command {
3333
"channelID",
3434
"peerAddresses",
3535
"tlsRootCertFiles",
36+
"connectionProfile",
3637
}
3738
attachFlags(chaincodeQueryCmd, flagList)
3839

peer/chaincode/upgrade.go

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func upgradeCmd(cf *ChaincodeCmdFactory) *cobra.Command {
4444
"vscc",
4545
"peerAddresses",
4646
"tlsRootCertFiles",
47+
"connectionProfile",
4748
}
4849
attachFlags(chaincodeUpgradeCmd, flagList)
4950

peer/common/networkconfig.go

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package common
8+
9+
import (
10+
"io/ioutil"
11+
12+
"github.com/pkg/errors"
13+
yaml "gopkg.in/yaml.v2"
14+
)
15+
16+
// NetworkConfig provides a static definition of a Hyperledger Fabric network
17+
type NetworkConfig struct {
18+
Name string `yaml:"name"`
19+
Xtype string `yaml:"x-type"`
20+
Description string `yaml:"description"`
21+
Version string `yaml:"version"`
22+
Channels map[string]ChannelNetworkConfig `yaml:"channels"`
23+
Organizations map[string]OrganizationConfig `yaml:"organizations"`
24+
Peers map[string]PeerConfig `yaml:"peers"`
25+
Client ClientConfig `yaml:"client"`
26+
Orderers map[string]OrdererConfig `yaml:"orderers"`
27+
CertificateAuthorities map[string]CAConfig `yaml:"certificateAuthorities"`
28+
}
29+
30+
// ClientConfig - not currently used by CLI
31+
type ClientConfig struct {
32+
Organization string `yaml:"organization"`
33+
Logging LoggingType `yaml:"logging"`
34+
CryptoConfig CCType `yaml:"cryptoconfig"`
35+
TLS TLSType `yaml:"tls"`
36+
CredentialStore CredentialStoreType `yaml:"credentialStore"`
37+
}
38+
39+
// LoggingType not currently used by CLI
40+
type LoggingType struct {
41+
Level string `yaml:"level"`
42+
}
43+
44+
// CCType - not currently used by CLI
45+
type CCType struct {
46+
Path string `yaml:"path"`
47+
}
48+
49+
// TLSType - not currently used by CLI
50+
type TLSType struct {
51+
Enabled bool `yaml:"enabled"`
52+
}
53+
54+
// CredentialStoreType - not currently used by CLI
55+
type CredentialStoreType struct {
56+
Path string `yaml:"path"`
57+
CryptoStore struct {
58+
Path string `yaml:"path"`
59+
}
60+
Wallet string `yaml:"wallet"`
61+
}
62+
63+
// ChannelNetworkConfig provides the definition of channels for the network
64+
type ChannelNetworkConfig struct {
65+
// Orderers list of ordering service nodes
66+
Orderers []string `yaml:"orderers"`
67+
// Peers a list of peer-channels that are part of this organization
68+
// to get the real Peer config object, use the Name field and fetch NetworkConfig.Peers[Name]
69+
Peers map[string]PeerChannelConfig `yaml:"peers"`
70+
// Chaincodes list of services
71+
Chaincodes []string `yaml:"chaincodes"`
72+
}
73+
74+
// PeerChannelConfig defines the peer capabilities
75+
type PeerChannelConfig struct {
76+
EndorsingPeer bool `yaml:"endorsingPeer"`
77+
ChaincodeQuery bool `yaml:"chaincodeQuery"`
78+
LedgerQuery bool `yaml:"ledgerQuery"`
79+
EventSource bool `yaml:"eventSource"`
80+
}
81+
82+
// OrganizationConfig provides the definition of an organization in the network
83+
// not currently used by CLI
84+
type OrganizationConfig struct {
85+
MspID string `yaml:"mspid"`
86+
Peers []string `yaml:"peers"`
87+
CryptoPath string `yaml:"cryptoPath"`
88+
CertificateAuthorities []string `yaml:"certificateAuthorities"`
89+
AdminPrivateKey TLSConfig `yaml:"adminPrivateKey"`
90+
SignedCert TLSConfig `yaml:"signedCert"`
91+
}
92+
93+
// OrdererConfig defines an orderer configuration
94+
// not currently used by CLI
95+
type OrdererConfig struct {
96+
URL string `yaml:"url"`
97+
GrpcOptions map[string]interface{} `yaml:"grpcOptions"`
98+
TLSCACerts TLSConfig `yaml:"tlsCACerts"`
99+
}
100+
101+
// PeerConfig defines a peer configuration
102+
type PeerConfig struct {
103+
URL string `yaml:"url"`
104+
EventURL string `yaml:"eventUrl"`
105+
GRPCOptions map[string]interface{} `yaml:"grpcOptions"`
106+
TLSCACerts TLSConfig `yaml:"tlsCACerts"`
107+
}
108+
109+
// CAConfig defines a CA configuration
110+
// not currently used by CLI
111+
type CAConfig struct {
112+
URL string `yaml:"url"`
113+
HTTPOptions map[string]interface{} `yaml:"httpOptions"`
114+
TLSCACerts MutualTLSConfig `yaml:"tlsCACerts"`
115+
Registrar EnrollCredentials `yaml:"registrar"`
116+
CaName string `yaml:"caName"`
117+
}
118+
119+
// EnrollCredentials holds credentials used for enrollment
120+
// not currently used by CLI
121+
type EnrollCredentials struct {
122+
EnrollID string `yaml:"enrollId"`
123+
EnrollSecret string `yaml:"enrollSecret"`
124+
}
125+
126+
// TLSConfig TLS configurations
127+
type TLSConfig struct {
128+
// the following two fields are interchangeable.
129+
// If Path is available, then it will be used to load the cert
130+
// if Pem is available, then it has the raw data of the cert it will be used as-is
131+
// Certificate root certificate path
132+
Path string `yaml:"path"`
133+
// Certificate actual content
134+
Pem string `yaml:"pem"`
135+
}
136+
137+
// MutualTLSConfig Mutual TLS configurations
138+
// not currently used by CLI
139+
type MutualTLSConfig struct {
140+
Pem []string `yaml:"pem"`
141+
142+
// Certfiles root certificates for TLS validation (Comma separated path list)
143+
Path string `yaml:"path"`
144+
145+
//Client TLS information
146+
Client TLSKeyPair `yaml:"client"`
147+
}
148+
149+
// TLSKeyPair contains the private key and certificate for TLS encryption
150+
// not currently used by CLI
151+
type TLSKeyPair struct {
152+
Key TLSConfig `yaml:"key"`
153+
Cert TLSConfig `yaml:"cert"`
154+
}
155+
156+
// GetConfig unmarshals the provided connection profile into a network
157+
// configuration struct
158+
func GetConfig(fileName string) (*NetworkConfig, error) {
159+
if fileName == "" {
160+
return nil, errors.New("filename cannot be empty")
161+
}
162+
163+
data, err := ioutil.ReadFile(fileName)
164+
if err != nil {
165+
return nil, errors.Wrap(err, "error reading connection profile")
166+
}
167+
168+
configData := string(data)
169+
config := &NetworkConfig{}
170+
err = yaml.Unmarshal([]byte(configData), &config)
171+
if err != nil {
172+
return nil, errors.Wrap(err, "error unmarshaling YAML")
173+
}
174+
175+
return config, nil
176+
}

peer/common/networkconfig_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package common_test
8+
9+
import (
10+
"testing"
11+
12+
"github.com/hyperledger/fabric/peer/common"
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestGetConfig(t *testing.T) {
17+
assert := assert.New(t)
18+
19+
// failure - empty file name
20+
networkConfig, err := common.GetConfig("")
21+
assert.Error(err)
22+
assert.Nil(networkConfig)
23+
24+
// failure - file doesn't exist
25+
networkConfig, err = common.GetConfig("fakefile.yaml")
26+
assert.Error(err)
27+
assert.Nil(networkConfig)
28+
29+
// failure - unexpected values for a few bools in the connection profile
30+
networkConfig, err = common.GetConfig("testdata/connectionprofile-bad.yaml")
31+
assert.Error(err, "error should have been nil")
32+
assert.Nil(networkConfig, "network config should be set")
33+
34+
// success
35+
networkConfig, err = common.GetConfig("testdata/connectionprofile.yaml")
36+
assert.NoError(err, "error should have been nil")
37+
assert.NotNil(networkConfig, "network config should be set")
38+
assert.Equal(networkConfig.Name, "connection-profile")
39+
40+
channelPeers := networkConfig.Channels["mychannel"].Peers
41+
assert.Equal(len(channelPeers), 2)
42+
for _, peer := range channelPeers {
43+
assert.True(peer.EndorsingPeer)
44+
}
45+
46+
peers := networkConfig.Peers
47+
assert.Equal(len(peers), 2)
48+
for _, peer := range peers {
49+
assert.NotEmpty(peer.TLSCACerts.Path)
50+
}
51+
}

0 commit comments

Comments
 (0)