// Deploy deploys the supplied chaincode image to the validators through a transaction func (d *Devops) Deploy(ctx context.Context, spec *pb.ChaincodeSpec) (*pb.ChaincodeDeploymentSpec, error) { // get the deployment spec chaincodeDeploymentSpec, err := d.getChaincodeBytes(ctx, spec) if err != nil { devopsLogger.Error(fmt.Sprintf("Error deploying chaincode spec: %v\n\n error: %s", spec, err)) return nil, err } // Now create the Transactions message and send to Peer. transID := chaincodeDeploymentSpec.ChaincodeSpec.ChaincodeID.Name var tx *pb.Transaction var sec crypto.Client if viper.GetBool("security.enabled") { if devopsLogger.IsEnabledFor(logging.DEBUG) { devopsLogger.Debug("Initializing secure devops using context %s", spec.SecureContext) } sec, err = crypto.InitClient(spec.SecureContext, nil) defer crypto.CloseClient(sec) // remove the security context since we are no longer need it down stream spec.SecureContext = "" if nil != err { return nil, err } if devopsLogger.IsEnabledFor(logging.DEBUG) { devopsLogger.Debug("Creating secure transaction %s", transID) } tx, err = sec.NewChaincodeDeployTransaction(chaincodeDeploymentSpec, transID) if nil != err { return nil, err } } else { if devopsLogger.IsEnabledFor(logging.DEBUG) { devopsLogger.Debug("Creating deployment transaction (%s)", transID) } tx, err = pb.NewChaincodeDeployTransaction(chaincodeDeploymentSpec, transID) if err != nil { return nil, fmt.Errorf("Error deploying chaincode: %s ", err) } } if devopsLogger.IsEnabledFor(logging.DEBUG) { devopsLogger.Debug("Sending deploy transaction (%s) to validator", tx.Uuid) } resp := d.coord.ExecuteTransaction(tx) if resp.Status == pb.Response_FAILURE { err = fmt.Errorf(string(resp.Msg)) } return chaincodeDeploymentSpec, err }
// Deploy first builds the chaincode package and subsequently deploys it to the // blockchain. func (s *ServerOpenchainREST) Deploy(rw web.ResponseWriter, req *web.Request) { restLogger.Info("REST deploying chaincode...") // Decode the incoming JSON payload var spec pb.ChaincodeSpec err := jsonpb.Unmarshal(req.Body, &spec) // Check for proper JSON syntax if err != nil { // Unmarshall returns a " character around unrecognized fields in the case // of a schema validation failure. These must be replaced with a ' character. // Otherwise, the returned JSON is invalid. errVal := strings.Replace(err.Error(), "\"", "'", -1) // Client must supply payload if err == io.EOF { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"Payload must contain a ChaincodeSpec.\"}") restLogger.Error("{\"Error\": \"Payload must contain a ChaincodeSpec.\"}") } else { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"%s\"}", errVal) restLogger.Error(fmt.Sprintf("{\"Error\": \"%s\"}", errVal)) } return } // Check that the ChaincodeID is not nil. if spec.ChaincodeID == nil { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"Payload must contain a ChaincodeID.\"}") restLogger.Error("{\"Error\": \"Payload must contain a ChaincodeID.\"}") return } // If the peer is running in development mode, confirm that the Chaincode name // is not left blank. If the peer is running in production mode, confirm that // the Chaincode path is not left blank. This is necessary as in development // mode, the chaincode is identified by name not by path during the deploy // process. if viper.GetString("chaincode.mode") == chaincode.DevModeUserRunsChaincode { // Check that the Chaincode name is not blank. if spec.ChaincodeID.Name == "" { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"Chaincode name may not be blank in development mode.\"}") restLogger.Error("{\"Error\": \"Chaincode name may not be blank in development mode.\"}") return } } else { // Check that the Chaincode path is not left blank. if spec.ChaincodeID.Path == "" { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"Chaincode path may not be blank.\"}") restLogger.Error("{\"Error\": \"Chaincode path may not be blank.\"}") return } } // If security is enabled, add client login token if viper.GetBool("security.enabled") { chaincodeUsr := spec.SecureContext if chaincodeUsr == "" { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"Must supply username for chaincode when security is enabled.\"}") restLogger.Error("{\"Error\": \"Must supply username for chaincode when security is enabled.\"}") return } // Retrieve the REST data storage path // Returns /var/openchain/production/client/ localStore := getRESTFilePath() // Check if the user is logged in before sending transaction if _, err := os.Stat(localStore + "loginToken_" + chaincodeUsr); err == nil { restLogger.Info("Local user '%s' is already logged in. Retrieving login token.\n", chaincodeUsr) // Read in the login token token, err := ioutil.ReadFile(localStore + "loginToken_" + chaincodeUsr) if err != nil { rw.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(rw, "{\"Error\": \"Fatal error -- %s\"}", err) panic(fmt.Errorf("Fatal error when reading client login token: %s\n", err)) } // Add the login token to the chaincodeSpec spec.SecureContext = string(token) // If privacy is enabled, mark chaincode as confidential if viper.GetBool("security.privacy") { spec.ConfidentialityLevel = pb.ConfidentialityLevel_CONFIDENTIAL } } else { // Check if the token is not there and fail if os.IsNotExist(err) { rw.WriteHeader(http.StatusUnauthorized) fmt.Fprintf(rw, "{\"Error\": \"User not logged in. Use the '/registrar' endpoint to obtain a security token.\"}") restLogger.Error("{\"Error\": \"User not logged in. Use the '/registrar' endpoint to obtain a security token.\"}") return } // Unexpected error rw.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(rw, "{\"Error\": \"Fatal error -- %s\"}", err) panic(fmt.Errorf("Fatal error when checking for client login token: %s\n", err)) } } // Deploy the ChaincodeSpec chaincodeDeploymentSpec, err := s.devops.Deploy(context.Background(), &spec) if err != nil { // Replace " characters with ' errVal := strings.Replace(err.Error(), "\"", "'", -1) rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"%s\"}", errVal) restLogger.Error(fmt.Sprintf("{\"Error\": \"Deploying Chaincode -- %s\"}", errVal)) return } // Clients will need the chaincode name in order to invoke or query it chainID := chaincodeDeploymentSpec.ChaincodeSpec.ChaincodeID.Name rw.WriteHeader(http.StatusOK) fmt.Fprintf(rw, "{\"OK\": \"Successfully deployed chainCode.\",\"message\":\""+chainID+"\"}") restLogger.Info("Successfuly deployed chainCode: " + chainID + ".\n") }