Skip to content

Commit

Permalink
FAB-1920 install a chaincode on local peer
Browse files Browse the repository at this point in the history
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 <muralisr@us.ibm.com>
  • Loading branch information
Srinivasan Muralidharan committed Feb 8, 2017
1 parent f7c19f8 commit ce8bd1e
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 12 deletions.
23 changes: 15 additions & 8 deletions core/chaincode/platforms/golang/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,17 @@ func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, erro
return "", errors.New("Cannot collect files from empty chaincode path")
}

input := spec.Input
if input == nil || len(input.Args) == 0 {
return "", errors.New("Cannot collect files from empty input")
//install will not have inputs and we don't have to collect hash for it
var inputbytes []byte

var err error
if spec.Input == nil || len(spec.Input.Args) == 0 {
logger.Debugf("not using input for hash computation for %v ", chaincodeID)
} else {
inputbytes, err = proto.Marshal(spec.Input)
if err != nil {
return "", fmt.Errorf("Error marshalling constructor: %s", err)
}
}

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

path := chaincodeID.Path

var err error
var actualcodepath string
if strings.HasPrefix(path, "http://") {
ishttp = true
Expand All @@ -193,11 +200,11 @@ func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, erro
if err = ccutil.IsCodeExist(tmppath); err != nil {
return "", fmt.Errorf("code does not exist %s", err)
}
inputbytes, err := proto.Marshal(input)
if err != nil {
return "", fmt.Errorf("Error marshalling constructor: %s", err)

hash := []byte{}
if inputbytes != nil {
hash = util.GenerateHashFromSignature(actualcodepath, inputbytes)
}
hash := util.GenerateHashFromSignature(actualcodepath, inputbytes)

hash, err = ccutil.HashFilesInDir(filepath.Join(codegopath, "src"), actualcodepath, hash, tw)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions peer/chaincode/chaincode.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func AddFlags(cmd *cobra.Command) {
fmt.Sprintf("Path to %s", chainFuncName))
flags.StringVarP(&chaincodeName, "name", "n", common.UndefinedParamValue,
fmt.Sprint("Name of the chaincode returned by the deploy transaction"))
flags.StringVarP(&chaincodeVersion, "version", "v", common.UndefinedParamValue,
fmt.Sprint("Version of the chaincode specifid in deploy/upgrade transactions"))
flags.StringVarP(&chaincodeUsr, "username", "u", common.UndefinedParamValue,
fmt.Sprint("Username for chaincode operations when security is enabled"))
flags.StringVarP(&customIDGenAlg, "tid", "t", common.UndefinedParamValue,
Expand All @@ -65,6 +67,7 @@ func Cmd(cf *ChaincodeCmdFactory) *cobra.Command {
chaincodeCmd.AddCommand(queryCmd(cf))
chaincodeCmd.AddCommand(upgradeCmd(cf))
chaincodeCmd.AddCommand(packageCmd(cf))
chaincodeCmd.AddCommand(installCmd(cf))

return chaincodeCmd
}
Expand All @@ -80,6 +83,7 @@ var (
chaincodeQueryHex bool
customIDGenAlg string
chainID string
chaincodeVersion string
policy string
escc string
vscc string
Expand Down
4 changes: 3 additions & 1 deletion peer/chaincode/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ func checkChaincodeCmdParams(cmd *cobra.Command) error {
return fmt.Errorf("Non-empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
}
} else {
return errors.New("Empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
if cmd == nil || cmd != chaincodeInstallCmd {
return errors.New("Empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
}
}

return nil
Expand Down
144 changes: 144 additions & 0 deletions peer/chaincode/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
Copyright IBM Corp. 2016-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 chaincode

import (
"fmt"
"io/ioutil"
"os"

"github.com/hyperledger/fabric/peer/common"

"github.com/golang/protobuf/proto"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var chaincodeInstallCmd *cobra.Command

// installCmd returns the cobra command for Chaincode Deploy
func installCmd(cf *ChaincodeCmdFactory) *cobra.Command {
chaincodeInstallCmd = &cobra.Command{
Use: "install",
Short: fmt.Sprintf("Package the specified chaincode into a deployment spec and save it on the peer's path."),
Long: fmt.Sprintf(`Package the specified chaincode into a deployment spec and save it on the peer's path.`),
ValidArgs: []string{"1"},
RunE: func(cmd *cobra.Command, args []string) error {
return chaincodeInstall(cmd, args, cf)
},
}

return chaincodeInstallCmd
}

func createCCInstallPath(path string) (string, error) {
if _, err := os.Stat(path); err != nil {
return "", err
}
chaincodePath := path + "/chaincodes"
if s, err := os.Stat(chaincodePath); err != nil {
if os.IsNotExist(err) {
if err := os.Mkdir(chaincodePath, 0755); err != nil {
return "", err
}
return chaincodePath, nil
}
return "", err
} else if !s.IsDir() {
return "", fmt.Errorf("chaincode path exists but not a dir: %s", chaincodePath)
}

return chaincodePath, nil
}

func packageCC(chaincodeBin []byte) ([]byte, error) {
//TODO create proper, secured package, for now return chaincode binary asis
return chaincodeBin, nil
}

func installCC(path string, bin []byte) error {
if _, err := os.Stat(path); err == nil || !os.IsNotExist(err) {
return fmt.Errorf("chaincode %s exists", path)
}

if err := ioutil.WriteFile(path, bin, 0644); err != nil {
logger.Errorf("Failed writing deployment spec to file [%s]: [%s]", path, err)
return err
}

return nil
}

// chaincodeInstall deploys the chaincode. On success, the chaincode name
// (hash) is printed to STDOUT for use by subsequent chaincode-related CLI
// commands.
func chaincodeInstall(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error {
if chaincodePath == common.UndefinedParamValue || chaincodeVersion == common.UndefinedParamValue {
return fmt.Errorf("Must supply value for %s path and version parameters.\n", chainFuncName)
}

peerPath := viper.GetString("peer.fileSystemPath")
if peerPath == "" {
return fmt.Errorf("Peer's environment \"peer.fileSystemPath\" is not set")
}

var ccpath string
var err error

//create the chaincodes dir if necessary
if ccpath, err = createCCInstallPath(peerPath); err != nil {
return err
}

//check if chaincode already exists
fileToWrite := ccpath + "/" + chaincodeName + "." + chaincodeVersion
if _, err := os.Stat(fileToWrite); err == nil || !os.IsNotExist(err) {
return fmt.Errorf("chaincode %s exists", fileToWrite)
}

spec, err := getChaincodeSpecification(cmd)
if err != nil {
return err
}

cds, err := getChaincodeBytes(spec)
if err != nil {
return fmt.Errorf("Error getting chaincode code %s: %s", chainFuncName, err)
}

cdsBytes, err := proto.Marshal(cds)
if err != nil {
return fmt.Errorf("Error marshalling chaincode deployment spec : %s", err)
}

//TODO - packageCC is just a stub. It needs to be filled out with other items
//such as serialized policy and has to be signed.
pkgBytes, err := packageCC(cdsBytes)
if err != nil {
logger.Errorf("Failed creating package [%s]", err)
return err
}

err = installCC(fileToWrite, pkgBytes)
if err != nil {
logger.Errorf("Failed writing deployment spec to file [%s]: [%s]", fileToWrite, err)
return err
}

logger.Debugf("Installed chaincode (%s,%s) of size <%d>", chaincodeName, chaincodeVersion, len(cdsBytes))
return err
}
118 changes: 118 additions & 0 deletions peer/chaincode/install_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
Copyright IBM Corp. 2016-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 chaincode

import (
"os"
"testing"

"github.com/hyperledger/fabric/peer/common"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func initInstallTest(fsPath string, t *testing.T) *cobra.Command {
viper.Set("peer.fileSystemPath", fsPath)
finitInstallTest(fsPath)

//if mkdir fails everthing will fail... but it should not
if err := os.Mkdir(fsPath, 0755); err != nil {
t.Fatalf("could not create install env")
}

InitMSP()

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

mockCF := &ChaincodeCmdFactory{
Signer: signer,
}

cmd := installCmd(mockCF)
AddFlags(cmd)

return cmd
}

func finitInstallTest(fsPath string) {
os.RemoveAll(fsPath)
}

// TestInstallCmd tests generation of install command
func TestInstallCmd(t *testing.T) {
fsPath := "/tmp/installtest"

cmd := initInstallTest(fsPath, t)
defer finitInstallTest(fsPath)

args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "testversion"}
cmd.SetArgs(args)

if err := cmd.Execute(); err != nil {
t.Fatalf("Error executing install command %s", err)
}

if _, err := os.Stat(fsPath + "/chaincodes/example02.testversion"); err != nil {
t.Fatalf("chaincode example02.testversion does not exist %s", err)
}
}

// TestNonExistentCC non existent chaincode should fail as expected
func TestNonExistentCC(t *testing.T) {
fsPath := "/tmp/installtest"

cmd := initInstallTest(fsPath, t)
defer finitInstallTest(fsPath)

args := []string{"-n", "badexample02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/bad_example02", "-v", "testversion"}
cmd.SetArgs(args)

if err := cmd.Execute(); err == nil {
t.Fatalf("Expected error executing install command for bad chaincode")
}

if _, err := os.Stat(fsPath + "/chaincodes/badexample02.testversion"); err == nil {
t.Fatalf("chaincode example02.testversion should not exist")
}
}

// TestCCExists should fail second time
func TestCCExists(t *testing.T) {
fsPath := "/tmp/installtest"

cmd := initInstallTest(fsPath, t)
defer finitInstallTest(fsPath)

args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "testversion"}
cmd.SetArgs(args)

if err := cmd.Execute(); err != nil {
t.Fatalf("Error executing install command %s", err)
}

if _, err := os.Stat(fsPath + "/chaincodes/example02.testversion"); err != nil {
t.Fatalf("chaincode example02.testversion does not exist %s", err)
}

if err := cmd.Execute(); err == nil {
t.Fatalf("Expected error reinstall but succeeded")
}
}
6 changes: 3 additions & 3 deletions peer/chaincode/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestUpgradeCmd(t *testing.T) {
cmd := upgradeCmd(mockCF)
AddFlags(cmd)

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

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

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

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

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

expectErrMsg := sendErr.Error()
Expand Down

0 comments on commit ce8bd1e

Please sign in to comment.