Skip to content

Commit

Permalink
[FAB-8345] Package chaincode metadata descriptors
Browse files Browse the repository at this point in the history
Adds an optional metadataPath to the chaincode
package function for Golang and Node.js chaincode.

Change-Id: I90e72a79e9f5511a042ae281cb91820e0b965ed8
Signed-off-by: Gari Singh <[email protected]>
  • Loading branch information
mastersingh24 committed Feb 26, 2018
1 parent d044103 commit 1a37371
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 37 deletions.
18 changes: 12 additions & 6 deletions fabric-client/lib/Packager.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ var logger = utils.getLogger('packager');
/**
* Utility function to package a chaincode. The contents will be returned as a byte array.
*
* @param {Object} chaincodePath required - String of the path to location of
* @param {string} chaincodePath required - String of the path to location of
* the source code of the chaincode
* @param {Object} chaincodeType optional - String of the type of chaincode
* @param {string} [chaincodeType] String of the type of chaincode
* ['golang', 'node', 'car', 'java'] (default 'golang')
* @param {boolean} devmode optional - True if using dev mode
* @param {boolean} [devmode] Set to true to use chaincode development mode
* @param {string} [metadataPath] The path to the top-level directory containing metadata descriptors
* @returns {Promise} A promise for the data as a byte array
*/
module.exports.package = function(chaincodePath, chaincodeType, devmode) {
module.exports.package = function(chaincodePath, chaincodeType, devmode, metadataPath) {
logger.debug('packager: chaincodePath: %s, chaincodeType: %s, devmode: %s',chaincodePath,chaincodeType,devmode);
return new Promise(function(resolve, reject) {
if (devmode) {
Expand All @@ -57,9 +58,14 @@ module.exports.package = function(chaincodePath, chaincodeType, devmode) {
handler = new Node();
break;
default:
handler = new Golang();
let keep = [
'.go',
'.c',
'.h'
];
handler = new Golang(keep);
}

return resolve(handler.package(chaincodePath));
return resolve(handler.package(chaincodePath, metadataPath));
});
};
52 changes: 51 additions & 1 deletion fabric-client/lib/packager/BasePackager.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,18 @@
'use strict';

var fs = require('fs-extra');
var klaw = require('klaw');
var tar = require('tar-stream');
var path = require('path');
var zlib = require('zlib');

var BasePackager = class {

/**
* Constructor
*
* @param {*} [keep] Array of valid source file extensions
*/
constructor (keep) {
if (this.constructor === BasePackager) {
// BasePackager can not be constructed.
Expand All @@ -37,8 +44,9 @@ var BasePackager = class {
* included in an archive file.
*
* @param chaincodePath
* @param metadataPath
*/
package (chaincodePath) {
package (chaincodePath, metadataPath) {
throw new TypeError('Please implement method package from child class');
}

Expand All @@ -52,6 +60,48 @@ var BasePackager = class {
throw new Error('abstract function called');
}

/**
* Find the metadata descriptor files.
*
* @param filePath The top-level directory containing the metadata descriptors.
* Only files with a ".json" extension will be included in the results.
* @returns {Promise}
*/
findMetadataDescriptors (filePath) {
return new Promise((resolve, reject) => {
var descriptors = [];
klaw(filePath).on('data', (entry) => {
if (entry.stats.isFile() && this.isMetadata(entry.path)) {

var desc = {
name: path.join('META-INF', path.relative(filePath, entry.path).split('\\').join('/')), // for windows style paths
fqp: entry.path
};
descriptors.push(desc);
}
})
.on('error', (error, item) => {
logger.error(`error while packaging ${item.path}`);
reject(error);
})
.on('end', () => {
resolve(descriptors);
});
});
}

/**
* Predicate function for determining whether a given path should be
* considered a valid metadata descriptor based entirely on the
* file extension.
* @param filePath The top-level directory containing the metadata descriptors.
* @returns {boolean} Returns true for valid metadata descriptors.
*/
isMetadata (filePath) {
var extensions = ['.json'];
return (extensions.indexOf(path.extname(filePath)) != -1);
}

/**
* Predicate function for determining whether a given path should be
* considered valid source code, based entirely on the extension. It is
Expand Down
34 changes: 15 additions & 19 deletions fabric-client/lib/packager/Golang.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,16 @@ var BasePackager = require('./BasePackager');

var logger = utils.getLogger('packager/Golang.js');

// A list of file extensions that should be packaged into the .tar.gz.
// Files with all other file extenstions will be excluded to minimize the size
// of the install payload.
var keep = [
'.go',
'.c',
'.h'
];

class GolangPackager extends BasePackager {

constructor () {
super(keep);
}

/**
* All of the files in the directory of the environment variable
* GOPATH joined to the request.chaincodePath will be included
* in an archive file.
* @param chaincodePath
* Package chaincode source and metadata for deployment.
* @param {string} chaincodePath The Go package name. The GOPATH environment variable must be set
* and the package must be located under GOPATH/src.
* @param {string} [metadataPath] The path to the top-level directory containing metadata descriptors.
* @returns {Promise.<TResult>}
*/
package (chaincodePath) {
package (chaincodePath, metadataPath) {
logger.info('packaging GOLANG from %s', chaincodePath);

// Determine the user's $GOPATH
Expand All @@ -63,7 +50,16 @@ class GolangPackager extends BasePackager {

var buffer = new sbuf.WritableStreamBuffer();

return this.findSource(goPath, projDir).then((descriptors) => {
return this.findSource(goPath, projDir).then((srcDescriptors) => {
if (metadataPath){
return super.findMetadataDescriptors(metadataPath)
.then((metaDescriptors) => {
return srcDescriptors.concat(metaDescriptors);
});
} else {
return srcDescriptors;
}
}).then((descriptors) => {
return super.generateTarGz(descriptors, buffer);
}).then(() => {
return buffer.getContents();
Expand Down
25 changes: 15 additions & 10 deletions fabric-client/lib/packager/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,14 @@ let BasePackager = require('./BasePackager');

class NodePackager extends BasePackager {

constructor () {
super([]);
}

/**
* All of the files in the directory of request.chaincodePath will be
* included in an archive file.
* @param chaincodePath
* Package chaincode source and metadata for deployment.
* @param {string} chaincodePath The path to the top-level directory containing the source code
* and package.json.
* @param {string} [metadataPath] The path to the top-level directory containing metadata descriptors
* @returns {Promise.<TResult>}
*/
package (chaincodePath) {
package (chaincodePath, metadataPath) {
logger.info('packaging Node from %s', chaincodePath);

// Compose the path to the chaincode project directory
Expand All @@ -47,8 +44,16 @@ class NodePackager extends BasePackager {
// will need to assemble sources from multiple packages

let buffer = new sbuf.WritableStreamBuffer();

return this.findSource(projDir).then((descriptors) => {
return this.findSource(projDir).then((srcDescriptors) => {
if (metadataPath){
return super.findMetadataDescriptors(metadataPath)
.then((metaDescriptors) => {
return srcDescriptors.concat(metaDescriptors);
});
} else {
return srcDescriptors;
}
}).then((descriptors) => {
return super.generateTarGz(descriptors, buffer);
}).then(() => {
return buffer.getContents();
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/metadata/statedb/couchdb/indexes/index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
not json
71 changes: 71 additions & 0 deletions test/unit/packager.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,51 @@ const fs = require('fs-extra');
const targz = require('targz');

const Packager = require('fabric-client/lib/Packager.js');
var Node = require('fabric-client/lib/packager/Node.js');
var Golang = require('fabric-client/lib/packager/Golang.js');



test('\n\n** BasePackager tests **\n\n', function(t) {
var keep = [
'.keep',
'.keep2'
];
// test with concrete implementations
var node = new Node(keep);
t.equal(node.isSource('path/src.keep'), true, 'Node.isSource() should return true for valid extension \".keep\"');
t.equal(node.isSource('path/src.keep2'), true, 'Node.isSource() should return true for valid extension \".keep2\"');
t.equal(node.isSource('path/src.keep3'), false, 'Node.isSource() should return false for invalid extension \".keep3\"');
t.equal(node.isMetadata('path/metadata.json'), true, 'Node.isMetadata() should return true for valid extension \".json\"');
t.equal(node.isMetadata('path/metadata.notjson'), false, 'Node.isMetadata() should return false for invalid extension \".notjson\"');
node.findMetadataDescriptors(testutil.METADATA_PATH)
.then((descriptors) => {
let expected = 'META-INF/statedb/couchdb/indexes/index.json';
t.equal(descriptors.length, 1, 'Expected Node.findMetadataDescriptors() to return one valid descriptor');
t.equal(descriptors[0].name, expected, 'Node.findMetadataDescriptors() should return valid descriptor name');
}).catch((err) => {
t.fail('Node.findMetadataDescriptors() failed with unexpected error');
t.comment(err.stack ? err.stack : err);
});

var golang = new Golang(keep);
t.equal(golang.isSource('path/src.keep'), true, 'Golang.isSource() should return true for valid extension \".keep\"');
t.equal(golang.isSource('path/src.keep2'), true, 'Golang.isSource() should return true for valid extension \".keep2\"');
t.equal(golang.isSource('path/src.keep3'), false, 'Golang.isSource() should return false for invalid extension \".keep3\"');
t.equal(golang.isMetadata('path/metadata.json'), true, 'Golang.isMetadata() should return true for valid extension \".json\"');
t.equal(golang.isMetadata('path/metadata.notjson'), false, 'Golang.isMetadata() should return false for invalid extension \".notjson\"');
golang.findMetadataDescriptors(testutil.METADATA_PATH)
.then((descriptors) => {
let expected = 'META-INF/statedb/couchdb/indexes/index.json';
t.equal(descriptors.length, 1, 'Expected Golang.findMetadataDescriptors() to return one valid descriptor');
t.equal(descriptors[0].name, expected, 'Golang.findMetadataDescriptors() should return valid descriptor name');
}).catch((err) => {
t.fail('Golang.findMetadataDescriptors() failed with unexpected error');
t.comment(err.stack ? err.stack : err);
});

t.end();
});

test('\n\n** Golang Packager tests **\n\n', function(t) {
Packager.package('blah','',true)
Expand Down Expand Up @@ -70,6 +115,24 @@ test('\n\n** Golang Packager tests **\n\n', function(t) {

t.end();
});
return Packager.package(testutil.CHAINCODE_PATH,'', false, testutil.METADATA_PATH);
}).then((data) => {
let tmpFile = path.join(testutil.getTempDir(), 'test-deploy-copy.tar.gz');
let destDir = path.join(testutil.getTempDir(), 'test-deploy-copy-tar-gz');
fs.writeFileSync(tmpFile, data);
fs.removeSync(destDir);
targz.decompress({
src: tmpFile,
dest: destDir
}, (err) => {
if (err) {
t.fail('Failed to extract generated chaincode package. ' + err);
let checkPath = path.join(destDir, 'META-INF', 'statedb', 'couchdb', 'indexes', 'index.json');
t.equal(fs.existsSync(checkPath), true,
'The tar.gz file produced by Packager.package() has the "META-INF/statedb/couchdb/indexes/index.json" file');
}
t.end();
});
}).catch((err) => {
t.fail('Caught error in Package.package tests');
t.comment(err.stack ? err.stack : err);
Expand Down Expand Up @@ -149,6 +212,14 @@ test('\n\n** Node.js Packager tests **\n\n', function(t) {
checkPath = path.join(targzDir, 'src', 'node_modules');
t.equal(fs.existsSync(checkPath), true, 'The tar.gz file produced by Packager.package() has the "node_modules" folder');
});
}).then(()=>{
return Packager.package(destDir, 'node', false, testutil.METADATA_PATH);
}).then((data) => {
return check(data, () => {
let checkPath = path.join(targzDir, 'META-INF', 'statedb', 'couchdb', 'indexes', 'index.json');
t.equal(fs.existsSync(checkPath), true,
'The tar.gz file produced by Packager.package() has the "META-INF/statedb/couchdb/indexes/index.json" file');
});
}).then(() => {
t.end();
}).catch((err) => {
Expand Down
2 changes: 1 addition & 1 deletion test/unit/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ module.exports.END2END = {
module.exports.NODE_CHAINCODE_PATH = path.resolve(__dirname, '../fixtures/src/node_cc/example_cc');
module.exports.NODE_CHAINCODE_UPGRADE_PATH = path.resolve(__dirname, '../fixtures/src/node_cc/example_cc1');
module.exports.NODE_CHAINCODE_UPGRADE_PATH_V2 = path.resolve(__dirname, '../fixtures/src/node_cc/example_cc2');

module.exports.METADATA_PATH = path.resolve(__dirname, '../fixtures/metadata');

module.exports.NODE_END2END = {
channel: 'mychannel',
Expand Down

0 comments on commit 1a37371

Please sign in to comment.