Skip to content

Commit 67ef0b4

Browse files
committed
Query installed +lifecycle chaincodes from CLI
This CR adds the functionality to query chaincodes that have been installed with +lifecycle. The name, version, and hash for each installed chaincode will be printed. FAB-11534 #done Change-Id: Ib2a7a36f70e04ed4513d3c9a15815d9ea47b6304 Signed-off-by: Will Lahti <wtlahti@us.ibm.com>
1 parent 2defa53 commit 67ef0b4

File tree

3 files changed

+180
-74
lines changed

3 files changed

+180
-74
lines changed

core/common/ccprovider/ccprovider.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,7 @@ func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
346346
ccpack, err := GetChaincodeFromFS(ccname, ccversion)
347347
if err != nil {
348348
// either chaincode on filesystem has been tampered with or
349-
// a non-chaincode file has been found in the chaincodes directory
350-
ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name())
349+
// +lifecycle chaincode files exist in the chaincodes directory.
351350
continue
352351
}
353352

peer/chaincode/list.go

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/golang/protobuf/proto"
1818
cb "github.com/hyperledger/fabric/protos/common"
1919
pb "github.com/hyperledger/fabric/protos/peer"
20+
lb "github.com/hyperledger/fabric/protos/peer/lifecycle"
2021
"github.com/hyperledger/fabric/protos/utils"
2122
"github.com/pkg/errors"
2223
"github.com/spf13/cobra"
@@ -26,7 +27,8 @@ var getInstalledChaincodes bool
2627
var getInstantiatedChaincodes bool
2728
var chaincodeListCmd *cobra.Command
2829

29-
// installCmd returns the cobra command for Chaincode Deploy
30+
// listCmd returns the cobra command for listing
31+
// the installed or instantiated chaincodes
3032
func listCmd(cf *ChaincodeCmdFactory) *cobra.Command {
3133
chaincodeListCmd = &cobra.Command{
3234
Use: "list",
@@ -44,6 +46,7 @@ func listCmd(cf *ChaincodeCmdFactory) *cobra.Command {
4446
"peerAddresses",
4547
"tlsRootCertFiles",
4648
"connectionProfile",
49+
"newLifecycle",
4750
}
4851
attachFlags(chaincodeListCmd, flagList)
4952

@@ -71,52 +74,85 @@ func getChaincodes(cmd *cobra.Command, cf *ChaincodeCmdFactory) error {
7174
}
7275

7376
var prop *pb.Proposal
74-
if getInstalledChaincodes && (!getInstantiatedChaincodes) {
75-
prop, _, err = utils.CreateGetInstalledChaincodesProposal(creator)
76-
} else if getInstantiatedChaincodes && (!getInstalledChaincodes) {
77+
78+
if (getInstalledChaincodes && getInstantiatedChaincodes) || (!getInstalledChaincodes && !getInstantiatedChaincodes) {
79+
return errors.New("must explicitly specify \"--installed\" or \"--instantiated\"")
80+
}
81+
82+
if getInstalledChaincodes {
83+
prop, err = getInstalledChaincodesProposal(newLifecycle, creator)
84+
}
85+
86+
if getInstantiatedChaincodes {
7787
prop, _, err = utils.CreateGetChaincodesProposal(channelID, creator)
78-
} else {
79-
return fmt.Errorf("Must explicitly specify \"--installed\" or \"--instantiated\"")
8088
}
8189

8290
if err != nil {
83-
return fmt.Errorf("Error creating proposal %s: %s", chainFuncName, err)
91+
return errors.WithMessage(err, "error creating proposal")
8492
}
8593

8694
var signedProp *pb.SignedProposal
8795
signedProp, err = utils.GetSignedProposal(prop, cf.Signer)
8896
if err != nil {
89-
return fmt.Errorf("Error creating signed proposal %s: %s", chainFuncName, err)
97+
return errors.WithMessage(err, "error creating signed proposal")
9098
}
9199

92100
// list is currently only supported for one peer
93101
proposalResponse, err := cf.EndorserClients[0].ProcessProposal(context.Background(), signedProp)
94102
if err != nil {
95-
return errors.Errorf("Error endorsing %s: %s", chainFuncName, err)
103+
return errors.WithMessage(err, "error endorsing proposal")
96104
}
97105

98106
if proposalResponse.Response == nil {
99-
return errors.Errorf("Proposal response had nil 'response'")
107+
return errors.Errorf("proposal response had nil response")
100108
}
101109

102110
if proposalResponse.Response.Status != int32(cb.Status_SUCCESS) {
103-
return errors.Errorf("Bad response: %d - %s", proposalResponse.Response.Status, proposalResponse.Response.Message)
111+
return errors.Errorf("bad response: %d - %s", proposalResponse.Response.Status, proposalResponse.Response.Message)
104112
}
105113

106-
cqr := &pb.ChaincodeQueryResponse{}
107-
err = proto.Unmarshal(proposalResponse.Response.Payload, cqr)
108-
if err != nil {
109-
return err
114+
return printResponse(getInstalledChaincodes, proposalResponse)
115+
}
116+
117+
// printResponse prints the information included in the response
118+
// from the server. If getInstalledChaincodes is set to false, the
119+
// proposal response will be interpreted as containing instantiated
120+
// chaincode information.
121+
func printResponse(getInstalledChaincodes bool, proposalResponse *pb.ProposalResponse) error {
122+
var qicr *lb.QueryInstalledChaincodesResult
123+
var cqr *pb.ChaincodeQueryResponse
124+
125+
if newLifecycle {
126+
qicr = &lb.QueryInstalledChaincodesResult{}
127+
err := proto.Unmarshal(proposalResponse.Response.Payload, qicr)
128+
if err != nil {
129+
return err
130+
}
131+
} else {
132+
cqr = &pb.ChaincodeQueryResponse{}
133+
err := proto.Unmarshal(proposalResponse.Response.Payload, cqr)
134+
if err != nil {
135+
return err
136+
}
110137
}
111138

112139
if getInstalledChaincodes {
113140
fmt.Println("Get installed chaincodes on peer:")
114141
} else {
115142
fmt.Printf("Get instantiated chaincodes on channel %s:\n", channelID)
116143
}
144+
145+
if qicr != nil {
146+
for _, chaincode := range qicr.InstalledChaincodes {
147+
fmt.Printf("Name: %s, Version: %s, Hash: %x\n", chaincode.Name, chaincode.Version, chaincode.Hash)
148+
}
149+
return nil
150+
}
151+
117152
for _, chaincode := range cqr.Chaincodes {
118153
fmt.Printf("%v\n", ccInfo{chaincode}.String())
119154
}
155+
120156
return nil
121157
}
122158

@@ -150,3 +186,35 @@ func (cci ccInfo) String() string {
150186
func isBytes(v reflect.Value) bool {
151187
return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8
152188
}
189+
190+
func getInstalledChaincodesProposal(newLifecycle bool, creator []byte) (*pb.Proposal, error) {
191+
if newLifecycle {
192+
return createNewLifecycleQueryInstalledChaincodeProposal(creator)
193+
}
194+
proposal, _, err := utils.CreateGetInstalledChaincodesProposal(creator)
195+
return proposal, err
196+
}
197+
198+
func createNewLifecycleQueryInstalledChaincodeProposal(creatorBytes []byte) (*pb.Proposal, error) {
199+
args := &lb.QueryInstalledChaincodesArgs{}
200+
201+
argsBytes, err := proto.Marshal(args)
202+
if err != nil {
203+
return nil, err
204+
}
205+
ccInput := &pb.ChaincodeInput{Args: [][]byte{[]byte("QueryInstalledChaincodes"), argsBytes}}
206+
207+
cis := &pb.ChaincodeInvocationSpec{
208+
ChaincodeSpec: &pb.ChaincodeSpec{
209+
ChaincodeId: &pb.ChaincodeID{Name: "+lifecycle"},
210+
Input: ccInput,
211+
},
212+
}
213+
214+
proposal, _, err := utils.CreateProposalFromCIS(cb.HeaderType_ENDORSER_TRANSACTION, "", cis, creatorBytes)
215+
if err != nil {
216+
return nil, errors.WithMessage(err, "error creating proposal for ChaincodeInvocationSpec")
217+
}
218+
219+
return proposal, nil
220+
}

peer/chaincode/list_test.go

Lines changed: 96 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/golang/protobuf/proto"
1515
"github.com/hyperledger/fabric/peer/common"
1616
pb "github.com/hyperledger/fabric/protos/peer"
17+
lb "github.com/hyperledger/fabric/protos/peer/lifecycle"
1718
"github.com/stretchr/testify/assert"
1819
)
1920

@@ -31,7 +32,7 @@ func TestChaincodeListCmd(t *testing.T) {
3132
}
3233
installedCqrBytes, err := proto.Marshal(installedCqr)
3334
if err != nil {
34-
t.Fatalf("Marshale error: %s", err)
35+
t.Fatalf("Marshal error: %s", err)
3536
}
3637

3738
mockResponse := &pb.ProposalResponse{
@@ -46,61 +47,99 @@ func TestChaincodeListCmd(t *testing.T) {
4647
BroadcastClient: mockBroadcastClient,
4748
}
4849

49-
// reset channelID, it might have been set by previous test
50-
channelID = ""
51-
52-
// Get installed chaincodes
53-
installedChaincodesCmd := listCmd(mockCF)
54-
55-
args := []string{"--installed"}
56-
installedChaincodesCmd.SetArgs(args)
57-
if err := installedChaincodesCmd.Execute(); err != nil {
58-
t.Errorf("Run chaincode list cmd to get installed chaincodes error:%v", err)
59-
}
60-
61-
resetFlags()
62-
63-
// Get instantiated chaincodes
64-
instantiatedChaincodesCmd := listCmd(mockCF)
65-
args = []string{"--instantiated"}
66-
instantiatedChaincodesCmd.SetArgs(args)
67-
err = instantiatedChaincodesCmd.Execute()
68-
assert.Error(t, err, "Run chaincode list cmd to get instantiated chaincodes should fail if invoked without -C flag")
69-
70-
args = []string{"--instantiated", "-C", "mychannel"}
71-
instantiatedChaincodesCmd.SetArgs(args)
72-
if err := instantiatedChaincodesCmd.Execute(); err != nil {
73-
t.Errorf("Run chaincode list cmd to get instantiated chaincodes error:%v", err)
74-
}
75-
76-
resetFlags()
77-
78-
// Wrong case: Set both "--installed" and "--instantiated"
79-
Cmd := listCmd(mockCF)
80-
args = []string{"--installed", "--instantiated"}
81-
Cmd.SetArgs(args)
82-
err = Cmd.Execute()
83-
assert.Error(t, err, "Run chaincode list cmd to get instantiated/installed chaincodes should fail if invoked without -C flag")
84-
85-
args = []string{"--installed", "--instantiated", "-C", "mychannel"}
86-
Cmd.SetArgs(args)
87-
expectErr := fmt.Errorf("Must explicitly specify \"--installed\" or \"--instantiated\"")
88-
if err := Cmd.Execute(); err == nil || err.Error() != expectErr.Error() {
89-
t.Errorf("Expect error: %s", expectErr)
90-
}
91-
92-
resetFlags()
93-
94-
// Wrong case: Miss "--intsalled" and "--instantiated"
95-
nilCmd := listCmd(mockCF)
96-
97-
args = []string{"-C", "mychannel"}
98-
nilCmd.SetArgs(args)
99-
100-
expectErr = fmt.Errorf("Must explicitly specify \"--installed\" or \"--instantiated\"")
101-
if err := nilCmd.Execute(); err == nil || err.Error() != expectErr.Error() {
102-
t.Errorf("Expect error: %s", expectErr)
103-
}
50+
cmd := listCmd(mockCF)
51+
52+
t.Run("get installed chaincodes - legacy lscc", func(t *testing.T) {
53+
resetFlags()
54+
55+
args := []string{"--installed"}
56+
cmd.SetArgs(args)
57+
if err := cmd.Execute(); err != nil {
58+
t.Errorf("Run chaincode list cmd to get installed chaincodes error:%v", err)
59+
}
60+
})
61+
62+
t.Run("get installed chaincodes - +lifecycle", func(t *testing.T) {
63+
resetFlags()
64+
queryInstalledChaincodeResult := &lb.QueryInstalledChaincodesResult{
65+
InstalledChaincodes: []*lb.QueryInstalledChaincodesResult_InstalledChaincode{
66+
{Name: "test1", Version: "v1.0", Hash: []byte("hash1")},
67+
{Name: "testcc2", Version: "v2.0", Hash: []byte("hash2")},
68+
},
69+
}
70+
qicrBytes, err := proto.Marshal(queryInstalledChaincodeResult)
71+
if err != nil {
72+
t.Fatalf("Marshal error: %s", err)
73+
}
74+
75+
mockResponse.Response = &pb.Response{Status: 200, Payload: qicrBytes}
76+
77+
args := []string{"--installed", "--newLifecycle"}
78+
cmd.SetArgs(args)
79+
if err := cmd.Execute(); err != nil {
80+
t.Errorf("Run chaincode list cmd to get installed chaincodes error:%v", err)
81+
}
82+
})
83+
84+
t.Run("get instantiated chaincodes - no channel", func(t *testing.T) {
85+
resetFlags()
86+
87+
args := []string{"--instantiated"}
88+
cmd.SetArgs(args)
89+
err = cmd.Execute()
90+
assert.Error(t, err, "Run chaincode list cmd to get instantiated chaincodes should fail if invoked without -C flag")
91+
})
92+
93+
t.Run("get instantiated chaincodes - no channel", func(t *testing.T) {
94+
resetFlags()
95+
96+
args := []string{"--instantiated"}
97+
cmd.SetArgs(args)
98+
err = cmd.Execute()
99+
assert.Error(t, err, "Run chaincode list cmd to get instantiated chaincodes should fail if invoked without -C flag")
100+
})
101+
102+
t.Run("get instantiated chaincodes - success", func(t *testing.T) {
103+
resetFlags()
104+
instantiatedChaincodesCmd := listCmd(mockCF)
105+
args := []string{"--instantiated", "-C", "mychannel"}
106+
instantiatedChaincodesCmd.SetArgs(args)
107+
if err := instantiatedChaincodesCmd.Execute(); err != nil {
108+
t.Errorf("Run chaincode list cmd to get instantiated chaincodes error:%v", err)
109+
}
110+
})
111+
112+
t.Run("both --installed and --instantiated set - no channel", func(t *testing.T) {
113+
resetFlags()
114+
115+
// Wrong case: Set both "--installed" and "--instantiated"
116+
cmd = listCmd(mockCF)
117+
args := []string{"--installed", "--instantiated"}
118+
cmd.SetArgs(args)
119+
err = cmd.Execute()
120+
assert.Error(t, err, "Run chaincode list cmd to get instantiated/installed chaincodes should fail if invoked without -C flag")
121+
})
122+
123+
t.Run("both --installed and --instantiated set - no channel", func(t *testing.T) {
124+
resetFlags()
125+
args := []string{"--installed", "--instantiated", "-C", "mychannel"}
126+
cmd.SetArgs(args)
127+
expectErr := fmt.Errorf("must explicitly specify \"--installed\" or \"--instantiated\"")
128+
if err := cmd.Execute(); err == nil || err.Error() != expectErr.Error() {
129+
t.Errorf("Expect error: %s", expectErr)
130+
}
131+
})
132+
133+
t.Run("neither --installed nor --instantiated set", func(t *testing.T) {
134+
resetFlags()
135+
args := []string{"-C", "mychannel"}
136+
cmd.SetArgs(args)
137+
138+
expectErr := fmt.Errorf("must explicitly specify \"--installed\" or \"--instantiated\"")
139+
if err := cmd.Execute(); err == nil || err.Error() != expectErr.Error() {
140+
t.Errorf("Expect error: %s", expectErr)
141+
}
142+
})
104143
}
105144

106145
func TestChaincodeListFailure(t *testing.T) {
@@ -132,7 +171,7 @@ func TestChaincodeListFailure(t *testing.T) {
132171
instantiatedChaincodesCmd.SetArgs(args)
133172
err = instantiatedChaincodesCmd.Execute()
134173
assert.Error(t, err)
135-
assert.Regexp(t, "Bad response: 500 - error message", err.Error())
174+
assert.Regexp(t, "bad response: 500 - error message", err.Error())
136175
}
137176

138177
func TestString(t *testing.T) {

0 commit comments

Comments
 (0)