Skip to content

Commit ce8bd1e

Browse files
author
Srinivasan Muralidharan
committed
FAB-1920 install a chaincode on local peer
https://jira.hyperledger.org/browse/FAB-1920 peer chaincode install -n <name> -p <path> -v <version> This command should be executed on the local peer as opposed to other commands such as "invoke", "query" which are sent to the remote peer. ie, assumes the chaincode is on the peer (like chaincode_example02). Contrast this with the old deploy that can be executed from any CLI/SDK outside of the peer. On successful execution the command will create a chaincode package on the file system of the peer prefixed by the "fileSystemPath" as defined in core.yaml. ------- NOTE ------ -The "package" will eventually have more contents such as policy and will be -signed etc, but for now is just the serialized chaincode deployment spec. This will be used in subsequent CRs to get the mechanics of a chaincode on multiple chains ironed out. ------- NOTE ------ Example ======= With default "fileSystemPath: /var/hyperledger/production", peer chaincode install -n mycc -p my/chain/code -v firstversion will create chaincode package in /var/hyperledger/production/chaincodes/mycc.firstversion using the chaincode defined in my/chain/code. Change-Id: I479834736d816ed522688dacae6e946340debf11 Signed-off-by: Srinivasan Muralidharan <[email protected]>
1 parent f7c19f8 commit ce8bd1e

File tree

6 files changed

+287
-12
lines changed

6 files changed

+287
-12
lines changed

core/chaincode/platforms/golang/hash.go

+15-8
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,17 @@ func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, erro
151151
return "", errors.New("Cannot collect files from empty chaincode path")
152152
}
153153

154-
input := spec.Input
155-
if input == nil || len(input.Args) == 0 {
156-
return "", errors.New("Cannot collect files from empty input")
154+
//install will not have inputs and we don't have to collect hash for it
155+
var inputbytes []byte
156+
157+
var err error
158+
if spec.Input == nil || len(spec.Input.Args) == 0 {
159+
logger.Debugf("not using input for hash computation for %v ", chaincodeID)
160+
} else {
161+
inputbytes, err = proto.Marshal(spec.Input)
162+
if err != nil {
163+
return "", fmt.Errorf("Error marshalling constructor: %s", err)
164+
}
157165
}
158166

159167
//code root will point to the directory where the code exists
@@ -170,7 +178,6 @@ func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, erro
170178

171179
path := chaincodeID.Path
172180

173-
var err error
174181
var actualcodepath string
175182
if strings.HasPrefix(path, "http://") {
176183
ishttp = true
@@ -193,11 +200,11 @@ func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, erro
193200
if err = ccutil.IsCodeExist(tmppath); err != nil {
194201
return "", fmt.Errorf("code does not exist %s", err)
195202
}
196-
inputbytes, err := proto.Marshal(input)
197-
if err != nil {
198-
return "", fmt.Errorf("Error marshalling constructor: %s", err)
203+
204+
hash := []byte{}
205+
if inputbytes != nil {
206+
hash = util.GenerateHashFromSignature(actualcodepath, inputbytes)
199207
}
200-
hash := util.GenerateHashFromSignature(actualcodepath, inputbytes)
201208

202209
hash, err = ccutil.HashFilesInDir(filepath.Join(codegopath, "src"), actualcodepath, hash, tw)
203210
if err != nil {

peer/chaincode/chaincode.go

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ func AddFlags(cmd *cobra.Command) {
4242
fmt.Sprintf("Path to %s", chainFuncName))
4343
flags.StringVarP(&chaincodeName, "name", "n", common.UndefinedParamValue,
4444
fmt.Sprint("Name of the chaincode returned by the deploy transaction"))
45+
flags.StringVarP(&chaincodeVersion, "version", "v", common.UndefinedParamValue,
46+
fmt.Sprint("Version of the chaincode specifid in deploy/upgrade transactions"))
4547
flags.StringVarP(&chaincodeUsr, "username", "u", common.UndefinedParamValue,
4648
fmt.Sprint("Username for chaincode operations when security is enabled"))
4749
flags.StringVarP(&customIDGenAlg, "tid", "t", common.UndefinedParamValue,
@@ -65,6 +67,7 @@ func Cmd(cf *ChaincodeCmdFactory) *cobra.Command {
6567
chaincodeCmd.AddCommand(queryCmd(cf))
6668
chaincodeCmd.AddCommand(upgradeCmd(cf))
6769
chaincodeCmd.AddCommand(packageCmd(cf))
70+
chaincodeCmd.AddCommand(installCmd(cf))
6871

6972
return chaincodeCmd
7073
}
@@ -80,6 +83,7 @@ var (
8083
chaincodeQueryHex bool
8184
customIDGenAlg string
8285
chainID string
86+
chaincodeVersion string
8387
policy string
8488
escc string
8589
vscc string

peer/chaincode/common.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,9 @@ func checkChaincodeCmdParams(cmd *cobra.Command) error {
197197
return fmt.Errorf("Non-empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
198198
}
199199
} else {
200-
return errors.New("Empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
200+
if cmd == nil || cmd != chaincodeInstallCmd {
201+
return errors.New("Empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
202+
}
201203
}
202204

203205
return nil

peer/chaincode/install.go

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
Copyright IBM Corp. 2016-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 chaincode
18+
19+
import (
20+
"fmt"
21+
"io/ioutil"
22+
"os"
23+
24+
"github.com/hyperledger/fabric/peer/common"
25+
26+
"github.com/golang/protobuf/proto"
27+
"github.com/spf13/cobra"
28+
"github.com/spf13/viper"
29+
)
30+
31+
var chaincodeInstallCmd *cobra.Command
32+
33+
// installCmd returns the cobra command for Chaincode Deploy
34+
func installCmd(cf *ChaincodeCmdFactory) *cobra.Command {
35+
chaincodeInstallCmd = &cobra.Command{
36+
Use: "install",
37+
Short: fmt.Sprintf("Package the specified chaincode into a deployment spec and save it on the peer's path."),
38+
Long: fmt.Sprintf(`Package the specified chaincode into a deployment spec and save it on the peer's path.`),
39+
ValidArgs: []string{"1"},
40+
RunE: func(cmd *cobra.Command, args []string) error {
41+
return chaincodeInstall(cmd, args, cf)
42+
},
43+
}
44+
45+
return chaincodeInstallCmd
46+
}
47+
48+
func createCCInstallPath(path string) (string, error) {
49+
if _, err := os.Stat(path); err != nil {
50+
return "", err
51+
}
52+
chaincodePath := path + "/chaincodes"
53+
if s, err := os.Stat(chaincodePath); err != nil {
54+
if os.IsNotExist(err) {
55+
if err := os.Mkdir(chaincodePath, 0755); err != nil {
56+
return "", err
57+
}
58+
return chaincodePath, nil
59+
}
60+
return "", err
61+
} else if !s.IsDir() {
62+
return "", fmt.Errorf("chaincode path exists but not a dir: %s", chaincodePath)
63+
}
64+
65+
return chaincodePath, nil
66+
}
67+
68+
func packageCC(chaincodeBin []byte) ([]byte, error) {
69+
//TODO create proper, secured package, for now return chaincode binary asis
70+
return chaincodeBin, nil
71+
}
72+
73+
func installCC(path string, bin []byte) error {
74+
if _, err := os.Stat(path); err == nil || !os.IsNotExist(err) {
75+
return fmt.Errorf("chaincode %s exists", path)
76+
}
77+
78+
if err := ioutil.WriteFile(path, bin, 0644); err != nil {
79+
logger.Errorf("Failed writing deployment spec to file [%s]: [%s]", path, err)
80+
return err
81+
}
82+
83+
return nil
84+
}
85+
86+
// chaincodeInstall deploys the chaincode. On success, the chaincode name
87+
// (hash) is printed to STDOUT for use by subsequent chaincode-related CLI
88+
// commands.
89+
func chaincodeInstall(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error {
90+
if chaincodePath == common.UndefinedParamValue || chaincodeVersion == common.UndefinedParamValue {
91+
return fmt.Errorf("Must supply value for %s path and version parameters.\n", chainFuncName)
92+
}
93+
94+
peerPath := viper.GetString("peer.fileSystemPath")
95+
if peerPath == "" {
96+
return fmt.Errorf("Peer's environment \"peer.fileSystemPath\" is not set")
97+
}
98+
99+
var ccpath string
100+
var err error
101+
102+
//create the chaincodes dir if necessary
103+
if ccpath, err = createCCInstallPath(peerPath); err != nil {
104+
return err
105+
}
106+
107+
//check if chaincode already exists
108+
fileToWrite := ccpath + "/" + chaincodeName + "." + chaincodeVersion
109+
if _, err := os.Stat(fileToWrite); err == nil || !os.IsNotExist(err) {
110+
return fmt.Errorf("chaincode %s exists", fileToWrite)
111+
}
112+
113+
spec, err := getChaincodeSpecification(cmd)
114+
if err != nil {
115+
return err
116+
}
117+
118+
cds, err := getChaincodeBytes(spec)
119+
if err != nil {
120+
return fmt.Errorf("Error getting chaincode code %s: %s", chainFuncName, err)
121+
}
122+
123+
cdsBytes, err := proto.Marshal(cds)
124+
if err != nil {
125+
return fmt.Errorf("Error marshalling chaincode deployment spec : %s", err)
126+
}
127+
128+
//TODO - packageCC is just a stub. It needs to be filled out with other items
129+
//such as serialized policy and has to be signed.
130+
pkgBytes, err := packageCC(cdsBytes)
131+
if err != nil {
132+
logger.Errorf("Failed creating package [%s]", err)
133+
return err
134+
}
135+
136+
err = installCC(fileToWrite, pkgBytes)
137+
if err != nil {
138+
logger.Errorf("Failed writing deployment spec to file [%s]: [%s]", fileToWrite, err)
139+
return err
140+
}
141+
142+
logger.Debugf("Installed chaincode (%s,%s) of size <%d>", chaincodeName, chaincodeVersion, len(cdsBytes))
143+
return err
144+
}

peer/chaincode/install_test.go

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
Copyright IBM Corp. 2016-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 chaincode
18+
19+
import (
20+
"os"
21+
"testing"
22+
23+
"github.com/hyperledger/fabric/peer/common"
24+
25+
"github.com/spf13/cobra"
26+
"github.com/spf13/viper"
27+
)
28+
29+
func initInstallTest(fsPath string, t *testing.T) *cobra.Command {
30+
viper.Set("peer.fileSystemPath", fsPath)
31+
finitInstallTest(fsPath)
32+
33+
//if mkdir fails everthing will fail... but it should not
34+
if err := os.Mkdir(fsPath, 0755); err != nil {
35+
t.Fatalf("could not create install env")
36+
}
37+
38+
InitMSP()
39+
40+
signer, err := common.GetDefaultSigner()
41+
if err != nil {
42+
t.Fatalf("Get default signer error: %v", err)
43+
}
44+
45+
mockCF := &ChaincodeCmdFactory{
46+
Signer: signer,
47+
}
48+
49+
cmd := installCmd(mockCF)
50+
AddFlags(cmd)
51+
52+
return cmd
53+
}
54+
55+
func finitInstallTest(fsPath string) {
56+
os.RemoveAll(fsPath)
57+
}
58+
59+
// TestInstallCmd tests generation of install command
60+
func TestInstallCmd(t *testing.T) {
61+
fsPath := "/tmp/installtest"
62+
63+
cmd := initInstallTest(fsPath, t)
64+
defer finitInstallTest(fsPath)
65+
66+
args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "testversion"}
67+
cmd.SetArgs(args)
68+
69+
if err := cmd.Execute(); err != nil {
70+
t.Fatalf("Error executing install command %s", err)
71+
}
72+
73+
if _, err := os.Stat(fsPath + "/chaincodes/example02.testversion"); err != nil {
74+
t.Fatalf("chaincode example02.testversion does not exist %s", err)
75+
}
76+
}
77+
78+
// TestNonExistentCC non existent chaincode should fail as expected
79+
func TestNonExistentCC(t *testing.T) {
80+
fsPath := "/tmp/installtest"
81+
82+
cmd := initInstallTest(fsPath, t)
83+
defer finitInstallTest(fsPath)
84+
85+
args := []string{"-n", "badexample02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/bad_example02", "-v", "testversion"}
86+
cmd.SetArgs(args)
87+
88+
if err := cmd.Execute(); err == nil {
89+
t.Fatalf("Expected error executing install command for bad chaincode")
90+
}
91+
92+
if _, err := os.Stat(fsPath + "/chaincodes/badexample02.testversion"); err == nil {
93+
t.Fatalf("chaincode example02.testversion should not exist")
94+
}
95+
}
96+
97+
// TestCCExists should fail second time
98+
func TestCCExists(t *testing.T) {
99+
fsPath := "/tmp/installtest"
100+
101+
cmd := initInstallTest(fsPath, t)
102+
defer finitInstallTest(fsPath)
103+
104+
args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "testversion"}
105+
cmd.SetArgs(args)
106+
107+
if err := cmd.Execute(); err != nil {
108+
t.Fatalf("Error executing install command %s", err)
109+
}
110+
111+
if _, err := os.Stat(fsPath + "/chaincodes/example02.testversion"); err != nil {
112+
t.Fatalf("chaincode example02.testversion does not exist %s", err)
113+
}
114+
115+
if err := cmd.Execute(); err == nil {
116+
t.Fatalf("Expected error reinstall but succeeded")
117+
}
118+
}

peer/chaincode/upgrade_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func TestUpgradeCmd(t *testing.T) {
7979
cmd := upgradeCmd(mockCF)
8080
AddFlags(cmd)
8181

82-
args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"}
82+
args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "anotherversion", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"}
8383
cmd.SetArgs(args)
8484

8585
if err := cmd.Execute(); err != nil {
@@ -112,7 +112,7 @@ func TestUpgradeCmdEndorseFail(t *testing.T) {
112112
cmd := upgradeCmd(mockCF)
113113
AddFlags(cmd)
114114

115-
args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"}
115+
args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "anotherversion", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"}
116116
cmd.SetArgs(args)
117117

118118
expectErrMsg := fmt.Sprintf("Could not assemble transaction, err Proposal response was not successful, error code %d, msg %s", errCode, errMsg)
@@ -152,7 +152,7 @@ func TestUpgradeCmdSendTXFail(t *testing.T) {
152152
cmd := upgradeCmd(mockCF)
153153
AddFlags(cmd)
154154

155-
args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"}
155+
args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "anotherversion", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"}
156156
cmd.SetArgs(args)
157157

158158
expectErrMsg := sendErr.Error()

0 commit comments

Comments
 (0)