Skip to content

Commit

Permalink
Merge pull request #734 from nklincoln/spawn-client-cli
Browse files Browse the repository at this point in the history
Provide cli command to spawn a distributed worker
  • Loading branch information
aklenik authored Feb 20, 2020
2 parents 159d98e + 6625b71 commit 9bf61aa
Show file tree
Hide file tree
Showing 36 changed files with 287 additions and 111 deletions.
2 changes: 1 addition & 1 deletion packages/caliper-burrow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
'use strict';

module.exports.AdminClient = require('./lib/burrow');
module.exports.ClientFactory = require('./lib/burrowClientFactory');
module.exports.WorkerFactory = require('./lib/burrowWorkerFactory');
File renamed without changes.
35 changes: 35 additions & 0 deletions packages/caliper-burrow/lib/burrowWorkerFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.
*/

'use strict';

const childProcess = require('child_process');
const path = require('path');

/**
* Class used to spawn burrow workers
*/
class BurrowWorkerFactory {

/**
* Spawn the worker
* @returns {Object} the child process
*/
async spawnWorker() {
const child = childProcess.fork(path.join(__dirname, './burrowWorker.js'), process.argv.slice(2), { env: process.env});
return child;
}
}

module.exports = BurrowWorkerFactory;
7 changes: 5 additions & 2 deletions packages/caliper-cli/caliper.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,27 @@
'use strict';

process.env.SUPPRESS_NO_CONFIG_WARNING = true;
const { CaliperUtils } = require('@hyperledger/caliper-core');
const Logger = CaliperUtils.getLogger('cli');
const yargs = require('yargs');
const version = 'v' + require('./package.json').version;

let results = yargs
.commandDir('./lib')
.help()
.example('caliper bind\ncaliper benchmark run\ncaliper zooclient start\ncaliper zooservice start ')
.example('caliper bind\ncaliper benchmark run\n caliper worker launch')
.demand(1)
.wrap(null)
.strict()
.epilogue('For more information on Hyperledger Caliper: https://hyperledger.github.io/caliper/')
.alias('v', 'version')
.version(version)
.describe('v', 'show version information')
.strict(false)
.argv;

results.thePromise.then( () => {
process.exit(0);
}).catch((error) => {
Logger.error(error);
process.exit(1);
});
5 changes: 2 additions & 3 deletions packages/caliper-cli/lib/benchmark/lib/runBenchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,10 @@ class RunBenchmark {
logger.info(`Set network configuration path: ${networkConfigPath}`);
logger.info(`Detected SUT type: ${blockchainType}`);

const {AdminClient, ClientFactory} = require(`@hyperledger/caliper-${blockchainType}`);
const {AdminClient, WorkerFactory} = require(`@hyperledger/caliper-${blockchainType}`);
const blockchainAdapter = new AdminClient(networkConfigPath, workspacePath);
const workerFactory = new ClientFactory();

const engine = new CaliperEngine(benchmarkConfig, networkConfig, blockchainAdapter, workerFactory);
const engine = new CaliperEngine(benchmarkConfig, networkConfig, blockchainAdapter, WorkerFactory);
const response = await engine.run();

if (response === 0) {
Expand Down
14 changes: 3 additions & 11 deletions packages/caliper-cli/lib/benchmark/runBenchmarkCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,12 @@
'use strict';

const RunBenchmark = require ('./lib/runBenchmark');
const Utils = require('../utility/utils');

// enforces singletons
const checkFn = (argv, options) => {

['caliper-benchconfig','caliper-networkconfig','caliper-workspace'].forEach((e)=>{
if (Array.isArray(argv[e])){
throw new Error(`Option ${e} can only be specified once`);
}
});

return true;
const uniqueArgs = ['caliper-benchconfig','caliper-networkconfig','caliper-workspace'];
return Utils.checkSingleton(argv, uniqueArgs);
};
module.exports._checkFn = checkFn;
module.exports.command = 'run [options]';
Expand All @@ -39,9 +34,6 @@ module.exports.builder = function (yargs){
});
yargs.usage('caliper benchmark run --caliper-workspace ~/myCaliperProject --caliper-benchconfig my-app-test-config.yaml --caliper-networkconfig my-sut-config.yaml');

// enforce the option after these options
yargs.requiresArg(['caliper-benchconfig','caliper-networkconfig','caliper-workspace']);

// enforce singletons
yargs.check(checkFn);

Expand Down
27 changes: 22 additions & 5 deletions packages/caliper-cli/lib/bind/bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
'use strict';

const { CaliperUtils, ConfigUtil } = require('@hyperledger/caliper-core');

const fs = require('fs');
const path = require('path');

const logger = CaliperUtils.getLogger('bind');
Expand All @@ -34,31 +36,46 @@ class Bind {
let sdk = ConfigUtil.get(ConfigUtil.keys.Bind.Sdk, undefined);
let cwd = ConfigUtil.get(ConfigUtil.keys.Bind.Cwd, undefined);
let userArgs = ConfigUtil.get(ConfigUtil.keys.Bind.Args, undefined);
let file = ConfigUtil.get(ConfigUtil.keys.Bind.File, undefined);

let bindOptions;
const defaultBindOpts = CaliperUtils.parseYaml(path.join(__dirname, './config.yaml'));

let bindOptions = CaliperUtils.parseYaml(path.join(__dirname, './config.yaml'));
if (file) {
// User has passed a configuration file to bind
if (!fs.existsSync(file)) {
let msg = `Passed bind file ${file} does not exist`;
logger.error(msg);
throw new Error(msg);
} else {
bindOptions = CaliperUtils.parseYaml(file);
}
} else {
bindOptions = defaultBindOpts;
}

let sutList = Object.keys(bindOptions.sut);
if (!sut) {
let msg = `SUT name is not specified. Available SUTs: ${sutList.join(' | ')}`;
let msg = `SUT name is not specified. Available SUTs: ${Object.keys(defaultBindOpts.sut).join(' | ')}`;
logger.error(msg);
throw new Error(msg);
}

if (!sutList.includes(sut)) {
let msg = `Unknown SUT name "${sut}". Available SUTs: ${sutList.join(' | ')}`;
let msg = `Unknown SUT name "${sut}". Available SUTs: ${Object.keys(defaultBindOpts.sut).join(' | ')}`;
logger.error(msg);
throw new Error(msg);
}

let sutSdkList = Object.keys(bindOptions.sut[sut]);
if (!sdk) {
let msg = `"${sut}" SDK version is not specified. Available versions: ${sutSdkList.join(' | ')}`;
let msg = `"${sut}" SDK version is not specified. Available versions: ${Object.keys(defaultBindOpts.sut[sut]).join(' | ')}`;
logger.error(msg);
throw new Error(msg);
}

if (!sutSdkList.includes(sdk)) {
let msg = `Unknown "${sut}" SDK version "${sdk}". Available versions: ${sutSdkList.join(' | ')}`;
let msg = `Unknown "${sut}" SDK version "${sdk}". Available versions: ${Object.keys(defaultBindOpts.sut[sut]).join(' | ')}`;
logger.error(msg);
throw new Error(msg);
}
Expand Down
22 changes: 9 additions & 13 deletions packages/caliper-cli/lib/bindCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,15 @@

'use strict';

const Bind = require ('./bind/bind.js');
const Bind = require('./bind/bind.js');
const Utils = require('./utility/utils');

// enforces singletons
const checkFn = (argv, options) => {

['caliper-bind-sut','caliper-bind-sdk', 'caliper-bind-args', 'caliper-bind-cwd'].forEach((e)=>{
if (Array.isArray(argv[e])){
throw new Error(`Option ${e} can only be specified once`);
}
});

return true;
const checkFn = (argv) => {
const uniqueArgs = ['caliper-bind-sut','caliper-bind-sdk', 'caliper-bind-args', 'caliper-bind-cwd', 'caliper-bind-file'];
return Utils.checkSingleton(argv, uniqueArgs);
};

module.exports._checkFn = checkFn;
module.exports.command = 'bind [options]';
module.exports.describe = 'Bind Caliper to a specific SUT and its SDK version';
Expand All @@ -35,11 +31,11 @@ module.exports.builder = function (yargs){
yargs.options({
'caliper-bind-sut' : {describe: 'The name of the platform to bind to', type: 'string' },
'caliper-bind-sdk' : {describe: 'Version of the platform SDK to bind to', type: 'string'},
'caliper-bind-cwd' : {describe: 'The working directory for performing the SDK install', type: 'string'},
'caliper-bind-args' : {describe: 'Additional arguments to pass to "npm install". Use the "=" notation when setting this parameter', type: 'string'}
'caliper-bind-cwd' : {describe: 'The working directory for performing the SDK install', type: 'string' },
'caliper-bind-args' : {describe: 'Additional arguments to pass to "npm install". Use the "=" notation when setting this parameter', type: 'string' },
'caliper-bind-file' : {describe: 'Yaml file to override default (supported) package versions when binding an SDK', type: 'string' }
});
yargs.usage('Usage:\n caliper bind --caliper-bind-sut fabric --caliper-bind-sdk 1.4.1 --caliper-bind-cwd ./ --caliper-bind-args="-g"');

// enforce the option after these options
yargs.requiresArg(['caliper-bind-sut','caliper-bind-sdk','caliper-bind-args', 'caliper-bind-cwd']);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,25 @@

'use strict';

const childProcess = require('child_process');
const path = require('path');

/**
* Class used to spawn FISCO BCOS client workers
* utility functions for CLI
*/
class FiscoBcosClientFactory {
class Utils {

/**
* Spawn the worker and perform required init
* @returns {Object} the child process
* Utility function to check for singleton values
* @param {string[]} passedArgs arguments passed by user
* @param {string[]} uniqueArgs arguments that must be unique
* @returns {boolean} boolean true if passes check
*/
spawnWorker() {
const child = childProcess.fork(path.join(__dirname, './fiscoBcosClientWorker.js'), process.argv.slice(2), { env: process.env });
return child;
static checkSingleton(passedArgs, uniqueArgs) {
uniqueArgs.forEach((e) => {
if (Array.isArray(passedArgs[e])) {
throw new Error(`Option [${e}] can only be specified once`);
}
});
return true;
}
}

module.exports = FiscoBcosClientFactory;
module.exports = Utils;
58 changes: 58 additions & 0 deletions packages/caliper-cli/lib/worker/lib/launch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.
*/

'use strict';

const { CaliperUtils, ConfigUtil } = require('@hyperledger/caliper-core');
const Bind = require('../../bind/bind');
const logger = CaliperUtils.getLogger('launch');

/**
* Caliper worker Launch command
* @private
*/
class Launch {

/**
* Command process for the Launch command.
* @param {string} argv Argument list from the caliper Launch command. Unused, relying on ConfigUtil instead.
*/
static async handler(argv) {
let sut = ConfigUtil.get(ConfigUtil.keys.Bind.Sut, undefined);
let sdk = ConfigUtil.get(ConfigUtil.keys.Bind.Sdk, undefined);

// only valid command if distributed workers
if (!ConfigUtil.get(ConfigUtil.keys.Worker.Remote, false)) {
const msg = 'Configuration definition indicates that worker is not remote: worker launch is invalid in this mode';
throw new Error(msg);
}

// not valid for process communications
if (ConfigUtil.get(ConfigUtil.keys.Worker.Communication.Method, 'process').localeCompare('process') === 0) {
const msg = 'Configuration definition indicates that worker is based on process communications: worker launch is invalid in this mode';
throw new Error(msg);
}

// bind first
logger.info(`Binding worker to SDK ${sdk}`);
await Bind.handler(argv);

// Launch target worker
logger.info(`Launching worker for sut ${sut}`);
const { WorkerFactory} = require(`@hyperledger/caliper-${sut}`);
WorkerFactory.spawnWorker();
}
}

module.exports = Launch;
48 changes: 48 additions & 0 deletions packages/caliper-cli/lib/worker/workerLaunchCommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.
*/

'use strict';

const Launch = require('./lib/launch.js');
const Utils = require('../utility/utils');

// enforces singletons
const checkFn = (argv) => {
const uniqueArgs = ['caliper-bind-sut','caliper-bind-sdk', 'caliper-bind-args', 'caliper-bind-cwd', 'caliper-bind-file'];
return Utils.checkSingleton(argv, uniqueArgs);
};

module.exports._checkFn = checkFn;
module.exports.command = 'launch [options]';
module.exports.describe = 'Launch a Caliper worker for a target SUT with a bound SDK version';
module.exports.builder = function (yargs){

yargs.options({
'caliper-bind-sut' : {describe: 'The name of the platform to bind to', type: 'string' },
'caliper-bind-sdk' : {describe: 'Version of the platform SDK to bind to', type: 'string'},
'caliper-bind-cwd' : {describe: 'The working directory for performing the SDK install', type: 'string' },
'caliper-bind-args' : {describe: 'Additional arguments to pass to "npm install". Use the "=" notation when setting this parameter', type: 'string' },
'caliper-bind-file' : {describe: 'Yaml file to override default (supported) package versions when binding an SDK', type: 'string' }
});
yargs.usage('Usage:\n caliper worker launch --caliper-bind-sut fabric --caliper-bind-sdk 1.4.1 --caliper-bind-cwd ./ --caliper-bind-args="-g"');

// enforce singletons
yargs.check(checkFn);

return yargs;
};

module.exports.handler = (argv) => {
return argv.thePromise = Launch.handler(argv);
};
23 changes: 23 additions & 0 deletions packages/caliper-cli/lib/workerCommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.
*/

'use strict';

module.exports.command = 'worker <subcommand>';
module.exports.describe = 'Caliper worker command';
module.exports.builder = function (yargs){

return yargs.demandCommand(1, 'Incorrect command. Please see the list of commands above, or enter "caliper worker --help".')
.commandDir('./worker');
};
2 changes: 1 addition & 1 deletion packages/caliper-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"@hyperledger/caliper-sawtooth": "0.3.0-unstable",
"@hyperledger/caliper-ethereum": "0.3.0-unstable",
"@hyperledger/caliper-fisco-bcos": "0.3.0-unstable",
"yargs": "10.0.3"
"yargs": "15.1.0"
},
"devDependencies": {
"chai": "^3.5.0",
Expand Down
Loading

0 comments on commit 9bf61aa

Please sign in to comment.