From 21fd747e37d20e2427ad6e86b6d5d15435fbc660 Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Mon, 4 Mar 2024 14:30:16 -0800 Subject: [PATCH] refactor(connector-fabric): deployContractGoSourceV1 uses Fabric v2.5.6 The deployContractGoSourceV1() method now assumes that the underlying test ledger is Fabric 2.5 (current LTS). This will allow us to upgrade the contracts that are being used by the Supply chain app to Fabric 2.x from Fabric 1.x which will then implicitly fix a large number of other issues at the same time. This change is part of laying the foundation for that follow-up work. Primary changes: ----------------- 1. Added a new, standalone utility function to deploy go source contracts with the name of `deployContractGoSourceImplFabricV256()`. 2. The code of this function was derived from the original Fabric v1 compatible deployContractGoSourceV1 method of the Fabric connector. 3. 2 organizations are supported for deployment via the endpoint. 4. The endpoint is only used by the supply chain app example at the moment and there is no test coverage of it due to dependencies that will be resolved in a follow-up pull request that is coming soon. Secondary changes: 1. Also extracted the SSH execution function from the fabric connector into a standalone function that can be used without having to have a Fabric connector instance created first. 2. Also extracted/abstracted some logic into a utility function for similar reasons that is used to replace logging configuration environment variables in shell commands that we use to perform contract deployment onto the Fabric test ledgers. Depends on #3054 Signed-off-by: Peter Somogyvari --- .../find-and-replace-fabric-logging-spec.ts | 25 ++ .../src/main/typescript/common/ssh-exec.ts | 30 ++ ...y-contract-go-source-impl-fabric-v2-5-6.ts | 355 ++++++++++++++++++ .../plugin-ledger-connector-fabric.ts | 229 +---------- 4 files changed, 424 insertions(+), 215 deletions(-) create mode 100644 packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/common/find-and-replace-fabric-logging-spec.ts create mode 100644 packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/common/ssh-exec.ts create mode 100644 packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/deploy-contract-go-source/deploy-contract-go-source-impl-fabric-v2-5-6.ts diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/common/find-and-replace-fabric-logging-spec.ts b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/common/find-and-replace-fabric-logging-spec.ts new file mode 100644 index 0000000000..9e903aa4ea --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/common/find-and-replace-fabric-logging-spec.ts @@ -0,0 +1,25 @@ +import { Checks } from "@hyperledger/cactus-common"; + +const PATTERN_FABRIC_CORE_LOGGING_LEVEL = new RegExp( + `\\s+(-e|--env)\\s+CORE_LOGGING_LEVEL='?"?\\w+'?"?\\s+`, + "gmi", +); + +const PATTERN_FABRIC_LOGGING_SPEC = new RegExp( + `FABRIC_LOGGING_SPEC=('?"?\\w+'?"?)`, + "gmi", +); + +export function findAndReplaceFabricLoggingSpec( + input: string, + newLogLevel: string, +): string { + Checks.nonBlankString(input, `findAndReplaceFabricLoggingSpec() arg1`); + Checks.nonBlankString(newLogLevel, `findAndReplaceFabricLoggingSpec() arg2`); + return input + .replace(PATTERN_FABRIC_CORE_LOGGING_LEVEL, " ") + .replace( + PATTERN_FABRIC_LOGGING_SPEC, + " FABRIC_LOGGING_SPEC= ".concat(newLogLevel), + ); +} diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/common/ssh-exec.ts b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/common/ssh-exec.ts new file mode 100644 index 0000000000..b61a4a20de --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/common/ssh-exec.ts @@ -0,0 +1,30 @@ +import { + NodeSSH, + SSHExecCommandOptions, + SSHExecCommandResponse, +} from "node-ssh"; +import { RuntimeError } from "run-time-error-cjs"; + +import { Logger } from "@hyperledger/cactus-common"; + +import { isSshExecOk } from "./is-ssh-exec-ok"; + +export async function sshExec( + ctx: { readonly log: Logger }, + cmd: string, + label: string, + ssh: NodeSSH, + sshCmdOptions: SSHExecCommandOptions, +): Promise { + ctx.log.debug(`${label} CMD: ${cmd}`); + const cmdRes = await ssh.execCommand(cmd, sshCmdOptions); + ctx.log.debug(`${label} CMD Response .code: %o`, cmdRes.code); + ctx.log.debug(`${label} CMD Response .signal: %o`, cmdRes.signal); + ctx.log.debug(`${label} CMD Response .stderr: %s`, cmdRes.stderr); + ctx.log.debug(`${label} CMD Response .stdout: %s`, cmdRes.stdout); + + if (!isSshExecOk(cmdRes)) { + throw new RuntimeError(`Expected ${label} cmdRes.code as null or 0`); + } + return cmdRes; +} diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/deploy-contract-go-source/deploy-contract-go-source-impl-fabric-v2-5-6.ts b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/deploy-contract-go-source/deploy-contract-go-source-impl-fabric-v2-5-6.ts new file mode 100644 index 0000000000..d3c64b85fb --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/deploy-contract-go-source/deploy-contract-go-source-impl-fabric-v2-5-6.ts @@ -0,0 +1,355 @@ +import path from "path"; +import temp from "temp"; +import fs from "fs/promises"; + +import { + NodeSSH, + SSHExecCommandOptions, + SSHExecCommandResponse, +} from "node-ssh"; + +import { Checks, Logger } from "@hyperledger/cactus-common"; +import { FABRIC_25_LTS_FABRIC_SAMPLES__ORDERER_TLS_ROOTCERT_FILE_ORG_1 } from "@hyperledger/cactus-test-tooling"; + +import { + DeployContractGoSourceV1Request, + DeployContractGoSourceV1Response, +} from "../generated/openapi/typescript-axios/api"; +import { IPluginLedgerConnectorFabricOptions } from "../plugin-ledger-connector-fabric"; +import { sshExec } from "../common/ssh-exec"; +import { findAndReplaceFabricLoggingSpec } from "../common/find-and-replace-fabric-logging-spec"; +import { IQueryInstalledResponse } from "../peer/i-query-installed-response"; +import { isSshExecOk } from "../common/is-ssh-exec-ok"; +import { IQueryCommittedResponse } from "../peer/i-query-committed-response"; + +/** + * Constant value holding the default $GOPATH in the Fabric CLI container as + * observed on fabric deployments that are produced by the official examples + * found in the https://github.com/hyperledger/fabric-samples repository. + */ +export const K_DEFAULT_CLI_CONTAINER_GO_PATH = "/opt/gopath/"; + +export interface IDeployContractGoSourceImplFabricV256Context { + readonly log: Logger; + readonly className: string; + readonly dockerBinary: string; + readonly opts: IPluginLedgerConnectorFabricOptions; +} + +/** + * @param req The object containing all the necessary metadata and parameters + * in order to have the contract deployed. + */ +export async function deployContractGoSourceImplFabricV256( + ctx: IDeployContractGoSourceImplFabricV256Context, + req: DeployContractGoSourceV1Request, +): Promise { + const { log, className, dockerBinary } = ctx; + const fnTag = `${className}#deployContractGoSourceImplFabricV256()`; + + const cliContainerGoPath = + ctx.opts.cliContainerGoPath || K_DEFAULT_CLI_CONTAINER_GO_PATH; + + const ssh = new NodeSSH(); + await ssh.connect(ctx.opts.sshConfig); + log.debug(`SSH connection OK`); + + try { + log.debug(`${fnTag} Deploying .go source: ${req.goSource.filename}`); + + Checks.truthy(req.goSource, `${fnTag}:req.goSource`); + + temp.track(); + const tmpDirPrefix = `hyperledger-cacti-${className}`; + const tmpDirPath = temp.mkdirSync(tmpDirPrefix); + + // The module name of the chain-code, for example this will extract + // ccName to be "hello-world" from a filename of "hello-world.go" + const inferredModuleName = path.basename(req.goSource.filename, ".go"); + log.debug(`Inferred module name: ${inferredModuleName}`); + const ccName = req.moduleName || inferredModuleName; + log.debug(`Determined ChainCode name: ${ccName}`); + + const remoteDirPath = path.join(cliContainerGoPath, "src/", ccName); + log.debug(`Remote dir path on CLI container: ${remoteDirPath}`); + + const localFilePath = path.join(tmpDirPath, req.goSource.filename); + await fs.writeFile(localFilePath, req.goSource.body, "base64"); + + const remoteFilePath = path.join(remoteDirPath, req.goSource.filename); + + log.debug(`SCP from/to %o => %o`, localFilePath, remoteFilePath); + await ssh.putFile(localFilePath, remoteFilePath); + log.debug(`SCP OK %o`, remoteFilePath); + + const sshOpts: SSHExecCommandOptions = { + execOptions: { + pty: true, + env: { + // just in case go modules would be otherwise disabled + GO111MODULE: "on", + FABRIC_LOGGING_SPEC: "DEBUG", + }, + }, + cwd: remoteDirPath, + }; + + const dockerCliExecEnv = Object.entries(ctx.opts.cliContainerEnv) + .map(([key, value]) => `--env ${key}=${value}`) + .join(" "); + + const dockerBuildCmd = + `${dockerBinary} exec ` + + dockerCliExecEnv + + ` --workdir=${remoteDirPath}` + + ` cli `; + + await sshExec( + ctx, + `${dockerBinary} exec cli mkdir -p ${remoteDirPath}/`, + "Create ChainCode project (go module) directory", + ssh, + sshOpts, + ); + + await sshExec( + ctx, + `${dockerBinary} exec cli go version`, + "Print go version", + ssh, + sshOpts, + ); + + const copyToCliCmd = `${dockerBinary} cp ${remoteFilePath} cli:${remoteFilePath}`; + log.debug(`Copy to CLI Container CMD: ${copyToCliCmd}`); + const copyToCliRes = await ssh.execCommand(copyToCliCmd, sshOpts); + log.debug(`Copy to CLI Container CMD Response: %o`, copyToCliRes); + Checks.truthy(copyToCliRes.code === 0, `copyToCliRes.code === 0`); + + { + const goModInitCmd = `${dockerBuildCmd} go mod init ${ccName}`; + log.debug(`go mod init CMD: ${goModInitCmd}`); + const goModInitRes = await ssh.execCommand(goModInitCmd, sshOpts); + log.debug(`go mod init CMD Response: %o`, goModInitRes); + Checks.truthy(goModInitRes.code === 0, `goModInitRes.code === 0`); + } + + const pinnedDeps = req.pinnedDeps || []; + for (const dep of pinnedDeps) { + const goGetCmd = `${dockerBuildCmd} go get ${dep}`; + log.debug(`go get CMD: ${goGetCmd}`); + const goGetRes = await ssh.execCommand(goGetCmd, sshOpts); + log.debug(`go get CMD Response: %o`, goGetRes); + Checks.truthy(goGetRes.code === 0, `goGetRes.code === 0`); + } + + { + const goModTidyCmd = `${dockerBuildCmd} go mod tidy`; + log.debug(`go mod tidy CMD: ${goModTidyCmd}`); + const goModTidyRes = await ssh.execCommand(goModTidyCmd, sshOpts); + log.debug(`go mod tidy CMD Response: %o`, goModTidyRes); + Checks.truthy(goModTidyRes.code === 0, `goModTidyRes.code === 0`); + } + + { + const goVendorCmd = `${dockerBuildCmd} go mod vendor`; + log.debug(`go mod vendor CMD: ${goVendorCmd}`); + const goVendorRes = await ssh.execCommand(goVendorCmd, sshOpts); + log.debug(`go mod vendor CMD Response: %o`, goVendorRes); + Checks.truthy(goVendorRes.code === 0, `goVendorRes.code === 0`); + } + + { + const goBuildCmd = `${dockerBuildCmd} go build`; + log.debug(`go build CMD: ${goBuildCmd}`); + const goBuildRes = await ssh.execCommand(goBuildCmd, sshOpts); + log.debug(`go build CMD Response: %o`, goBuildRes); + Checks.truthy(goBuildRes.code === 0, `goBuildRes.code === 0`); + } + + let success = true; + + const installationCommandResponses: SSHExecCommandResponse[] = []; + const ccSequence = 1; + const orderer = "orderer.example.com:7050"; + const ordererTLSHostnameOverride = "orderer.example.com"; + + const ccPkgCmd = + `${dockerBuildCmd} peer lifecycle chaincode package ${ccName}.tar.gz ` + + ` --path ${remoteDirPath} ` + + ` --label ${ccName} ` + + ` --lang golang`; + + const ccPkgLabel = `packaging chain code`; + const ccPkgRes = await sshExec(ctx, ccPkgCmd, ccPkgLabel, ssh, sshOpts); + Checks.truthy(ccPkgRes.code === 0, `ccPkgRes.code === 0`); + + for (const org of req.targetOrganizations) { + const dockerExecEnv = Object.entries(org) + .map(([key, val]) => `--env ${key}=${val}`) + .join(" "); + + const dockerExecCmd = + `${dockerBinary} exec ` + + dockerExecEnv + + ` --env GO111MODULE=on` + + ` --workdir=${remoteDirPath}` + + ` cli `; + + const ccInstallLbl = `Install ChainCode in ${org.CORE_PEER_LOCALMSPID}`; + const ccInstallCmd = `${dockerExecCmd} peer lifecycle chaincode install ${ccName}.tar.gz `; + + const anInstallCmdRes = await sshExec( + ctx, + ccInstallCmd, + ccInstallLbl, + ssh, + sshOpts, + ); + + installationCommandResponses.push(anInstallCmdRes); + + // const ctorArgsJson = JSON.stringify(req.constructorArgs || {}); + + // Need to make sure that the logging is turned off otherwise it + // mangles the JSON syntax and makes the output invalid... + const dockerExecCmdInfoLog = findAndReplaceFabricLoggingSpec( + dockerExecCmd, + "ERROR", + ); + + const instantiationCommandResponses = []; + const cmdQueryInstalled = `${dockerExecCmdInfoLog} peer lifecycle chaincode queryinstalled --output json`; + const lblQueryInstalled = `query installed contracts`; + const resQueryInstalled = await sshExec( + ctx, + cmdQueryInstalled, + lblQueryInstalled, + ssh, + sshOpts, + ); + + log.debug("Queries installed contracts OK."); + Checks.truthy(resQueryInstalled.stdout.includes(ccName)); + log.debug("Validated that contract is in fact installed OK."); + + const json = resQueryInstalled.stdout; + const qir = JSON.parse(json) as IQueryInstalledResponse; + const icc = qir.installed_chaincodes.find( + (chainCode) => chainCode.label === ccName, + ); + + ctx.log.debug(`Parsed list of installed contracts: %o`, qir); + + Checks.truthy(icc, "No installed chaincode with label: %o", ccName); + + if (!icc?.package_id) { + throw new Error(`${fnTag}: package ID falsy. Something's wrong.`); + } + const packageId = icc?.package_id; + ctx.log.debug(`Found package ID: ${packageId}`); + + const instantiateCmd = + ` ${dockerExecCmd} peer lifecycle chaincode approveformyorg ` + + `--orderer ${orderer} ` + + `--ordererTLSHostnameOverride ${ordererTLSHostnameOverride} ` + + `--tls ` + + `--cafile ${FABRIC_25_LTS_FABRIC_SAMPLES__ORDERER_TLS_ROOTCERT_FILE_ORG_1} ` + + `--channelID ${req.channelId} ` + + `--name ${ccName} ` + + `--version ${req.chainCodeVersion} ` + + `--package-id ${packageId} ` + + `--sequence ${ccSequence} ` + + ``; + + const cmdLabel = `approveformyorg ChainCode in ${org.CORE_PEER_LOCALMSPID}`; + log.debug(`ApproveForMyOrg CMD: %o`, instantiateCmd); + + const instantiationCmdRes = await sshExec( + ctx, + instantiateCmd, + cmdLabel, + ssh, + sshOpts, + ); + Checks.truthy(instantiationCmdRes.code === 0, `res.code === 0`); + instantiationCommandResponses.push(instantiationCmdRes); + + log.debug(`ApproveForMyOrg CMD Response:%o`, instantiationCmdRes); + success = success && isSshExecOk(instantiationCmdRes); + } + + const commitCmd = + `${dockerBuildCmd} peer lifecycle chaincode commit ` + + ` --name ${ccName} ` + + ` --version ${req.chainCodeVersion} ` + + ` --channelID ${req.channelId} ` + + ` --tls ` + + ` --orderer ${orderer} ` + + ` --ordererTLSHostnameOverride ${ordererTLSHostnameOverride} ` + + ` --cafile ${FABRIC_25_LTS_FABRIC_SAMPLES__ORDERER_TLS_ROOTCERT_FILE_ORG_1} ` + + ` --peerAddresses ${req.targetOrganizations[0].CORE_PEER_ADDRESS} ` + + ` --tlsRootCertFiles ${req.targetOrganizations[0].CORE_PEER_TLS_ROOTCERT_FILE}` + + ` --peerAddresses ${req.targetOrganizations[1].CORE_PEER_ADDRESS} ` + + ` --tlsRootCertFiles ${req.targetOrganizations[1].CORE_PEER_TLS_ROOTCERT_FILE}` + + ` --sequence=${ccSequence} `; + + const lblCcCommit = "peer lifecycle chaincode commit"; + + const resCommit = await sshExec(ctx, commitCmd, lblCcCommit, ssh, sshOpts); + + success = success && isSshExecOk(resCommit); + + // Need to make sure that the logging is turned off otherwise it + // mangles the JSON syntax and makes the output invalid... + const dockerBuildCmdInfoLog = findAndReplaceFabricLoggingSpec( + dockerBuildCmd, + "ERROR", + ); + + const cmdQueryCommitted2 = `${dockerBuildCmdInfoLog} peer lifecycle chaincode querycommitted --channelID=${req.channelId} --output json`; + const lblQueryCommitted2 = `peer lifecycle chaincode querycommitted --channelID=${req.channelId}`; + + log.debug(`${lblQueryCommitted2} CMD Response:%o`, cmdQueryCommitted2); + + const resQueryCommitted2 = await sshExec( + ctx, + cmdQueryCommitted2, + lblQueryCommitted2, + ssh, + sshOpts, + ); + + Checks.truthy( + resQueryCommitted2.stdout.includes(ccName), + "stdout has contract name", + ); + const committedCCsJson = resQueryCommitted2.stdout; + const qcr2 = JSON.parse(committedCCsJson) as IQueryCommittedResponse; + const ccd2 = qcr2.chaincode_definitions.find( + (ccd) => ccd.name === ccName && ccd.version === req.chainCodeVersion, + ); + + ctx.log.debug(`Parsed list of installed contracts: %o`, qcr2); + + Checks.truthy(ccd2, "No installed chaincode with label: %o", ccName); + + log.debug(`EXIT doDeploy()`); + const res: DeployContractGoSourceV1Response = { + success, + installationCommandResponses, + instantiationCommandResponse: installationCommandResponses[0], + }; + + return res; + } catch (ex) { + ctx.log.debug(`${fnTag} crashed. Re-throwing...`, ex); + throw ex; + } finally { + try { + ssh.dispose(); + } finally { + temp.cleanup(); + } + } +} diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts index 4dac5a77bf..6302c3ad85 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts @@ -141,6 +141,8 @@ import { GetBlockEndpointV1 } from "./get-block/get-block-endpoint-v1"; import { querySystemChainCode } from "./common/query-system-chain-code"; import { isSshExecOk } from "./common/is-ssh-exec-ok"; import { asBuffer, assertFabricFunctionIsAvailable } from "./common/utils"; +import { findAndReplaceFabricLoggingSpec } from "./common/find-and-replace-fabric-logging-spec"; +import { deployContractGoSourceImplFabricV256 } from "./deploy-contract-go-source/deploy-contract-go-source-impl-fabric-v2-5-6"; const { loadFromConfig } = require("fabric-network/lib/impl/ccp/networkconfig"); assertFabricFunctionIsAvailable(loadFromConfig, "loadFromConfig"); @@ -392,7 +394,7 @@ export class PluginLedgerConnectorFabric } temp.track(); - const tmpDirPrefix = `hyperledger-cactus-${this.className}`; + const tmpDirPrefix = `hyperledger-cacti-${this.className}`; const tmpDirPath = temp.mkdirSync(tmpDirPrefix); const remoteDirPath = path.join(this.cliContainerGoPath, "src/", ccLabel); @@ -429,31 +431,12 @@ export class PluginLedgerConnectorFabric ` --workdir=${remoteDirPath}` + ` cli `; - const r1 = new RegExp( - `\\s+(-e|--env)\\s+CORE_LOGGING_LEVEL='?"?\\w+'?"?\\s+`, - "gmi", - ); - const r2 = new RegExp(`FABRIC_LOGGING_SPEC=('?"?\\w+'?"?)`, "gmi"); - // Need to make sure that the logging is turned off otherwise it // mangles the JSON syntax and makes the output invalid... - const dockerBuildCmdInfoLog = dockerBuildCmd - .replace(r1, " ") - .replace(r2, " FABRIC_LOGGING_SPEC=ERROR "); - - // await this.sshExec( - // `${dockerBinary} exec cli mkdir -p ${remoteDirPath}/`, - // "Create ChainCode project (go module) directory", - // ssh, - // sshCmdOptions, - // ); - - // await this.sshExec( - // `${dockerBinary} exec cli go version`, - // "Print go version", - // ssh, - // sshCmdOptions, - // ); + const dockerBuildCmdInfoLog = findAndReplaceFabricLoggingSpec( + dockerBuildCmd, + "ERROR", + ); for (const sourceFile of sourceFiles) { const { filename, filepath, body } = sourceFile; @@ -469,13 +452,6 @@ export class PluginLedgerConnectorFabric log.debug(`SCP OK %o`, remoteDirPath); if (ccLang === ChainCodeProgrammingLanguage.Golang) { - // const cliRemoteDirPath = path.join(remoteDirPath, "../"); - // const copyToCliCmd = `${dockerBinary} cp ${remoteDirPath} cli:${cliRemoteDirPath}`; - // log.debug(`Copy to CLI Container CMD: ${copyToCliCmd}`); - // const copyToCliRes = await ssh.execCommand(copyToCliCmd, sshCmdOptions); - // log.debug(`Copy to CLI Container CMD Response: %o`, copyToCliRes); - // Checks.truthy(copyToCliRes.code === null, `copyToCliRes.code === null`); - { const label = "docker copy go code to cli container"; const cliRemoteDirPath = path.join(remoteDirPath, "../"); @@ -670,191 +646,14 @@ export class PluginLedgerConnectorFabric public async deployContractGoSourceV1( req: DeployContractGoSourceV1Request, ): Promise { - const fnTag = `${this.className}#deployContract()`; const { log } = this; - - const ssh = new NodeSSH(); - await ssh.connect(this.opts.sshConfig); - log.debug(`SSH connection OK`); - - try { - log.debug(`${fnTag} Deploying .go source: ${req.goSource.filename}`); - - Checks.truthy(req.goSource, `${fnTag}:req.goSource`); - - temp.track(); - const tmpDirPrefix = `hyperledger-cactus-${this.className}`; - const tmpDirPath = temp.mkdirSync(tmpDirPrefix); - - // The module name of the chain-code, for example this will extract - // ccName to be "hello-world" from a filename of "hello-world.go" - const inferredModuleName = path.basename(req.goSource.filename, ".go"); - log.debug(`Inferred module name: ${inferredModuleName}`); - const ccName = req.moduleName || inferredModuleName; - log.debug(`Determined ChainCode name: ${ccName}`); - - const remoteDirPath = path.join(this.cliContainerGoPath, "src/", ccName); - log.debug(`Remote dir path on CLI container: ${remoteDirPath}`); - - const localFilePath = path.join(tmpDirPath, req.goSource.filename); - fs.writeFileSync(localFilePath, req.goSource.body, "base64"); - - const remoteFilePath = path.join(remoteDirPath, req.goSource.filename); - - log.debug(`SCP from/to %o => %o`, localFilePath, remoteFilePath); - await ssh.putFile(localFilePath, remoteFilePath); - log.debug(`SCP OK %o`, remoteFilePath); - - const sshCmdOptions: SSHExecCommandOptions = { - execOptions: { - pty: true, - env: { - // just in case go modules would be otherwise disabled - GO111MODULE: "on", - FABRIC_LOGGING_SPEC: "DEBUG", - }, - }, - cwd: remoteDirPath, - }; - - const dockerExecEnv = Object.entries(this.opts.cliContainerEnv) - .map(([key, value]) => `--env ${key}=${value}`) - .join(" "); - - const { dockerBinary } = this; - const dockerBuildCmd = - `${dockerBinary} exec ` + - dockerExecEnv + - ` --env GO111MODULE=on` + - ` --workdir=${remoteDirPath}` + - ` cli `; - - await this.sshExec( - `${dockerBinary} exec cli mkdir -p ${remoteDirPath}/`, - "Create ChainCode project (go module) directory", - ssh, - sshCmdOptions, - ); - - await this.sshExec( - `${dockerBinary} exec cli go version`, - "Print go version", - ssh, - sshCmdOptions, - ); - - const copyToCliCmd = `${dockerBinary} cp ${remoteFilePath} cli:${remoteFilePath}`; - log.debug(`Copy to CLI Container CMD: ${copyToCliCmd}`); - const copyToCliRes = await ssh.execCommand(copyToCliCmd, sshCmdOptions); - log.debug(`Copy to CLI Container CMD Response: %o`, copyToCliRes); - Checks.truthy(copyToCliRes.code === 0, `copyToCliRes.code === 0`); - - { - const goModInitCmd = `${dockerBuildCmd} go mod init ${ccName}`; - log.debug(`go mod init CMD: ${goModInitCmd}`); - const goModInitRes = await ssh.execCommand(goModInitCmd, sshCmdOptions); - log.debug(`go mod init CMD Response: %o`, goModInitRes); - Checks.truthy(goModInitRes.code === 0, `goModInitRes.code === 0`); - } - - const pinnedDeps = req.pinnedDeps || []; - for (const dep of pinnedDeps) { - const goGetCmd = `${dockerBuildCmd} go get ${dep}`; - log.debug(`go get CMD: ${goGetCmd}`); - const goGetRes = await ssh.execCommand(goGetCmd, sshCmdOptions); - log.debug(`go get CMD Response: %o`, goGetRes); - Checks.truthy(goGetRes.code === 0, `goGetRes.code === 0`); - } - - { - const goModTidyCmd = `${dockerBuildCmd} go mod tidy`; - log.debug(`go mod tidy CMD: ${goModTidyCmd}`); - const goModTidyRes = await ssh.execCommand(goModTidyCmd, sshCmdOptions); - log.debug(`go mod tidy CMD Response: %o`, goModTidyRes); - Checks.truthy(goModTidyRes.code === 0, `goModTidyRes.code === 0`); - } - - { - const goVendorCmd = `${dockerBuildCmd} go mod vendor`; - log.debug(`go mod vendor CMD: ${goVendorCmd}`); - const goVendorRes = await ssh.execCommand(goVendorCmd, sshCmdOptions); - log.debug(`go mod vendor CMD Response: %o`, goVendorRes); - Checks.truthy(goVendorRes.code === 0, `goVendorRes.code === 0`); - } - - { - const goBuildCmd = `${dockerBuildCmd} go build`; - log.debug(`go build CMD: ${goBuildCmd}`); - const goBuildRes = await ssh.execCommand(goBuildCmd, sshCmdOptions); - log.debug(`go build CMD Response: %o`, goBuildRes); - Checks.truthy(goBuildRes.code === 0, `goBuildRes.code === 0`); - } - - const installationCommandResponses: SSHExecCommandResponse[] = []; - // https://github.com/hyperledger/fabric-samples/blob/release-1.4/fabcar/startFabric.sh - for (const org of req.targetOrganizations) { - const env = - ` --env CORE_PEER_LOCALMSPID=${org.CORE_PEER_LOCALMSPID}` + - ` --env CORE_PEER_ADDRESS=${org.CORE_PEER_ADDRESS}` + - ` --env CORE_PEER_MSPCONFIGPATH=${org.CORE_PEER_MSPCONFIGPATH}` + - ` --env CORE_PEER_TLS_ROOTCERT_FILE=${org.CORE_PEER_TLS_ROOTCERT_FILE}`; - - const anInstallationCommandResponse = await this.sshExec( - dockerBinary + - ` exec ${env} cli peer chaincode install` + - ` --name ${ccName} ` + - ` --path ${ccName} ` + - ` --version ${req.chainCodeVersion} ` + - ` --lang golang`, - `Install ChainCode in ${org.CORE_PEER_LOCALMSPID}`, - ssh, - sshCmdOptions, - ); - - installationCommandResponses.push(anInstallationCommandResponse); - } - - let success = true; - - const ctorArgsJson = JSON.stringify(req.constructorArgs || {}); - const ordererCaFile = - "/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt"; - - const instantiateCmd = - `${dockerBuildCmd} peer chaincode instantiate ` + - ` --name ${ccName} ` + - ` --version ${req.chainCodeVersion} ` + - ` --ctor '${ctorArgsJson}' ` + - ` --channelID ${req.channelId} ` + - ` --peerAddresses ${req.targetPeerAddresses[0]} ` + - ` --lang golang ` + - ` --tlsRootCertFiles ${req.tlsRootCertFiles}` + - ` --policy "${req.policyDslSource}"` + - ` --tls --cafile ${ordererCaFile}`; - - log.debug(`Instantiate CMD: %o`, instantiateCmd); - const instantiationCommandResponse = await ssh.execCommand( - instantiateCmd, - sshCmdOptions, - ); - - log.debug(`Instantiate CMD Response:%o`, instantiationCommandResponse); - success = success && isSshExecOk(instantiationCommandResponse); - - log.debug(`EXIT doDeploy()`); - const res: DeployContractGoSourceV1Response = { - success, - installationCommandResponses, - instantiationCommandResponse, - }; - return res; - } finally { - try { - ssh.dispose(); - } finally { - temp.cleanup(); - } - } + const ctx = { + log, + opts: this.opts, + dockerBinary: this.dockerBinary, + className: this.className, + }; + return deployContractGoSourceImplFabricV256(ctx, req); } /**