From cbfb30604b8c2781e564bb250dd58d08c7d57b3c Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Thu, 6 Feb 2020 12:32:49 -0600 Subject: [PATCH] feat(start): implement `agoric start testnet` Both docker install and `--sdk` install are supported. --- packages/agoric-cli/bin/agoric | 2 + packages/agoric-cli/lib/start.js | 127 ++++++++++++++++-- .../agoric-cli/template/.agservers/.gitignore | 1 + .../setup-solo/src/ag_setup_solo/main.py | 11 +- 4 files changed, 125 insertions(+), 16 deletions(-) diff --git a/packages/agoric-cli/bin/agoric b/packages/agoric-cli/bin/agoric index 04128f33688..80f59a67b95 100755 --- a/packages/agoric-cli/bin/agoric +++ b/packages/agoric-cli/bin/agoric @@ -6,6 +6,7 @@ const chalk = esmRequire('chalk').default; const WebSocket = esmRequire('ws'); const { spawn } = esmRequire('child_process'); const fs = esmRequire('fs').promises; +const os = esmRequire('os'); const main = esmRequire('../lib/main.js').default; const progname = path.basename(process.argv[1]); @@ -23,6 +24,7 @@ main(progname, rawArgs, { error, makeWebSocket, fs, + os, process, spawn, }).then( diff --git a/packages/agoric-cli/lib/start.js b/packages/agoric-cli/lib/start.js index 19d86d391c0..94cafab747d 100644 --- a/packages/agoric-cli/lib/start.js +++ b/packages/agoric-cli/lib/start.js @@ -2,10 +2,15 @@ import parseArgs from 'minimist'; import path from 'path'; import chalk from 'chalk'; -const FAKE_CHAIN_DELAY = 5; +const FAKE_CHAIN_DELAY = + process.env.FAKE_CHAIN_DELAY === undefined + ? 5 + : Number(process.env.FAKE_CHAIN_DELAY); +const PORT = process.env.PORT || 8000; +const HOST_PORT = process.env.HOST_PORT || PORT; export default async function startMain(progname, rawArgs, priv, opts) { - const { console, error, fs, spawn, process } = priv; + const { console, error, fs, spawn, os, process } = priv; const { reset, _: args } = parseArgs(rawArgs, { boolean: ['reset'], }); @@ -40,17 +45,22 @@ export default async function startMain(progname, rawArgs, priv, opts) { }; let agSolo; + let agSetupSolo; let agServer; if (opts.sdk) { agSolo = path.resolve(__dirname, '../../cosmic-swingset/bin/ag-solo'); + agSetupSolo = path.resolve(__dirname, '../../cosmic-swingset/setup-solo'); } else { - if (!(await exists('.agservers/node_modules'))) { - return error(`you must first run '${progname} install'`); - } agSolo = `${process.cwd()}/node_modules/@agoric/cosmic-swingset/bin/ag-solo`; } - async function startDev(profileName) { + async function startFakeChain(profileName) { + if (!opts.sdk) { + if (!(await exists('.agservers/node_modules'))) { + return error(`you must first run '${progname} install'`); + } + } + const fakeGCI = 'myFakeGCI'; if (!(await exists(agServer))) { console.log(chalk.yellow(`initializing ${profileName}`)); @@ -78,19 +88,112 @@ export default async function startMain(progname, rawArgs, priv, opts) { ); await linkHtml(profileName); - await pspawn(agSolo, ['start', '--role=two_client'], { + return pspawn(agSolo, ['start', '--role=two_client'], { stdio: 'inherit', cwd: agServer, }); } - async function startTestnet(profileName) { + async function startTestnetDocker(profileName, startArgs) { + const IMAGE = `agoric/cosmic-swingset-setup-solo`; + + if (startArgs[0] === '--pull') { + startArgs.shift(); + await pspawn('docker', ['pull', IMAGE], { + stdio: 'inherit', + }); + } + + const setupRun = (...bonusArgs) => + pspawn( + 'docker', + [ + 'run', + `-p127.0.0.1:${HOST_PORT}:${PORT}`, + `--volume=${process.cwd()}:/usr/src/dapp`, + `-eAG_SOLO_BASEDIR=/usr/src/dapp/.agservers/${profileName}`, + `--rm`, + `-it`, + IMAGE, + `--webport=${PORT}`, + `--webhost=0.0.0.0`, + ...bonusArgs, + ...startArgs, + ], + { + stdio: 'inherit', + }, + ); + + if (!(await exists(agServer))) { + const status = + (await setupRun('--no-restart')) || (await linkHtml(profileName)); + if (status) { + return status; + } + } + + return setupRun(); + } + + async function startTestnetSdk(profileName, startArgs) { + const virtEnv = path.resolve( + `.agservers/ve3-${os.platform()}-${os.arch()}`, + ); + if (!(await exists(`${virtEnv}/bin/pip`))) { + const status = await pspawn('python3', ['-mvenv', virtEnv], { + stdio: 'inherit', + cwd: agSetupSolo, + }); + if (status) { + return status; + } + } + + const pipRun = (...bonusArgs) => + pspawn(`${virtEnv}/bin/pip`, bonusArgs, { + stdio: 'inherit', + cwd: agSetupSolo, + }); + + if (!(await exists(`${virtEnv}/bin/wheel`))) { + const status = await pipRun('install', 'wheel'); + if (status) { + return status; + } + } + + if (!(await exists(`${virtEnv}/bin/ag-setup-solo`))) { + const status = await pipRun('install', `--editable`, '.'); + if (status) { + return status; + } + } + + const setupRun = (...bonusArgs) => + pspawn( + `${virtEnv}/bin/ag-setup-solo`, + [`--webport=${PORT}`, ...bonusArgs, ...startArgs], + { + stdio: 'inherit', + env: { ...process.env, AG_SOLO_BASEDIR: agServer }, + }, + ); + + if (!(await exists(agServer))) { + const status = + (await setupRun('--no-restart')) || (await linkHtml(profileName)); + if (status) { + return status; + } + } + return setupRun(); } const profiles = { - dev: startDev, - testnet: startTestnet, + dev: startFakeChain, + testnet: opts.sdk ? startTestnetSdk : startTestnetDocker, }; const profileName = args[0] || 'dev'; @@ -108,9 +211,9 @@ export default async function startMain(progname, rawArgs, priv, opts) { agServer = `.agservers/${profileName}`; if (reset) { console.log(chalk.green(`removing ${agServer}`)); - // FIXME: Use portable rimraf. + // rm is available on all the unix-likes, so use it for speed. await pspawn('rm', ['-rf', agServer], { stdio: 'inherit' }); } - await startFn(profileName, agServer); + return startFn(profileName, args[0] ? args.slice(1) : args); } diff --git a/packages/agoric-cli/template/.agservers/.gitignore b/packages/agoric-cli/template/.agservers/.gitignore index 55982bb1e97..e6050a73379 100644 --- a/packages/agoric-cli/template/.agservers/.gitignore +++ b/packages/agoric-cli/template/.agservers/.gitignore @@ -80,3 +80,4 @@ integration-test/transform-tests/output /dev /testnet /chain +/ve* diff --git a/packages/cosmic-swingset/setup-solo/src/ag_setup_solo/main.py b/packages/cosmic-swingset/setup-solo/src/ag_setup_solo/main.py index 55c7de24aa3..e7be9da4341 100644 --- a/packages/cosmic-swingset/setup-solo/src/ag_setup_solo/main.py +++ b/packages/cosmic-swingset/setup-solo/src/ag_setup_solo/main.py @@ -38,6 +38,7 @@ class Options(usage.Options): ] optFlags = [ ["destroy", None, "destroy all chain state"], + ["no-restart", None, "do not restart the solo vat"], ] def parseArgs(self, basedir=os.environ.get('AG_SOLO_BASEDIR', 'agoric')): self['basedir'] = os.environ['AG_SOLO_BASEDIR'] = basedir @@ -46,7 +47,9 @@ def parseArgs(self, basedir=os.environ.get('AG_SOLO_BASEDIR', 'agoric')): def setIngress(sm): print('Setting chain parameters with ' + AG_SOLO) subprocess.run([AG_SOLO, 'set-gci-ingress', '--chainID=%s' % sm['chainName'], sm['gci'], *sm['rpcAddrs']], check=True) -def restart(): +def restart(o): + if o['no-restart']: + return print('Restarting ' + AG_SOLO) os.execvp(AG_SOLO, [AG_SOLO, 'start', '--role=client']) @@ -95,7 +98,7 @@ def cleanup(): except: cleanup() raise - restart() + restart(o) def doInit(o): BASEDIR = o['basedir'] @@ -123,7 +126,7 @@ def resetNetconfig(o): for conn in conns: if 'GCI' in conn and conn['GCI'] == netconfig['gci']: print('Already have an entry for ' + conn['GCI'] + '; not replacing') - restart() + restart(o) sys.exit(1) return netconfig @@ -169,5 +172,5 @@ def main(): doInit(o) setIngress(netconfig) - restart() + restart(o) sys.exit(1)