Spawn and Status commands with refactoring. (#21)

This commit is contained in:
Ravin Perera
2023-05-03 15:25:18 +05:30
committed by GitHub
parent 2f43dd0970
commit 857d15d2be
7 changed files with 143 additions and 56 deletions

View File

@@ -14,12 +14,12 @@ user_port_begin=$HP_USER_PORT_BEGIN
peer_port_begin=$HP_PEER_PORT_BEGIN
if [ "$command" = "create" ] || [ "$command" = "bindmesh" ] || [ "$command" = "destroy" ] || \
[ "$command" = "start" ] || [ "$command" = "stop" ] || \
[ "$command" = "logs" ] || [ "$command" = "sync" ] ; then
[ "$command" = "start" ] || [ "$command" = "stop" ] || [ "$command" = "spawn" ] || \
[ "$command" = "logs" ] || [ "$command" = "sync" ] || [ "$command" = "status" ] ; then
echo "sub-command: $command"
else
echo "Invalid sub-command."
echo "Expected: create | destroy | start | stop | logs | sync"
echo "Expected: create | destroy | start | stop | spawn | logs | sync | status"
exit 1
fi
@@ -159,7 +159,7 @@ function bind_mesh {
local contract_dir=$(contract_dir_mount_path $node)
local cfg_file=$contract_dir/cfg/hp.cfg
local peers=$(joinarr all_peers $instance_count $i)
jq ".contract.unl=$unl | .mesh.known_peers=$peers" $cfg_file > $cfg_file.tmp && mv $cfg_file.tmp $cfg_file
jq ".contract.unl=$unl | .mesh.known_peers=$peers | .contract.consensus.mode=\"public\"" $cfg_file > $cfg_file.tmp && mv $cfg_file.tmp $cfg_file
done
}
@@ -202,6 +202,47 @@ function destroy_cluster {
exists "network" $network && docker network rm $network
}
function spawn_node {
ensure_cluser_exists
local old_count=$(get_container_count)
local new_id=$((old_count+1))
create_instance $new_id
local tmp_dir=$(mktemp -d /tmp/hpdevkit.spawn.XXXXXX)
local node1_cfg=$(contract_dir_mount_path 1)/cfg/hp.cfg
local new_cfg=$(contract_dir_mount_path $new_id)/cfg/hp.cfg
# Override new node's contract id and unl from the information from node1.
# Copy node1 cfg to temp file and remove the information we don't need.
jq 'del(.contract,.node.public_key,.node.private_key)' $node1_cfg > $tmp_dir/a.json
jq '{contract:{id:.contract.id,execute:.contract.execute,unl:.contract.unl,log}}' $node1_cfg > $tmp_dir/b.json
jq -s '.[0] * .[1]' $tmp_dir/a.json $tmp_dir/b.json > $tmp_dir/from-node1.json
# Copy new cfg to temp file and remove the information we don't need.
jq 'del(.contract.unl,.contract.id)' $new_cfg > $tmp_dir/from-newnode.json
jq -s '.[0] * .[1]' $tmp_dir/from-node1.json $tmp_dir/from-newnode.json > $tmp_dir/merged.json
# Inject the list of all known peers.
local all_peers
for ((i=1; i<=$old_count; i++));
do
# Assign peer ports in incrementing order.
let peer_port=$(($peer_port_begin + $i - 1))
all_peers[i]="node$i:${peer_port}"
done
local peers_json=$(joinarr all_peers $old_count -1)
jq ".mesh.known_peers=$peers_json" $tmp_dir/merged.json > $new_cfg
# Cleanup temp dir.
rm -r $tmp_dir
# Start the new node.
change_cluster_status start $new_id
}
function change_cluster_status {
ensure_cluser_exists
@@ -220,7 +261,9 @@ function change_cluster_status {
function attach_logs {
! cluster_exists && echo "Cluster '$cluster' does not exist." && exit 1
local container_name="${container_prefix}_$1"
local node=$1
[ "$1" -eq "999999" ] && node=$(get_container_count)
local container_name="${container_prefix}_$node"
docker logs -f --tail=5 $container_name
}
@@ -250,6 +293,9 @@ function sync_contract_bundle {
sync_instance $i &
done
wait
# Cleanup the original contract bundle dir which is used to sync from.
rm -r $bundle_mount/*
}
function validate_port_begin {
@@ -257,6 +303,10 @@ function validate_port_begin {
! validate_port $peer_port_begin && echo "Invalid peer port begin." && exit 1
}
function show_status {
docker ps -a --format "table {{.Names}}\\t{{.State}}" | grep $container_prefix | sort
}
if [ $command = "create" ]; then
! validate_node_num_arg $cluster_size && echo "Invalid cluster size." && exit 1
validate_port_begin
@@ -270,9 +320,13 @@ elif [ $command = "start" ]; then
change_cluster_status start $2
elif [ $command = "stop" ]; then
change_cluster_status stop $2
elif [ $command = "spawn" ]; then
spawn_node
elif [ $command = "logs" ]; then
! validate_node_num_arg $2 && echo "Usage: logs <node id>" && exit 1
attach_logs $2
elif [ $command = "sync" ]; then
sync_contract_bundle
elif [ $command = "status" ]; then
show_status
fi

View File

@@ -1,52 +1,62 @@
#! /usr/bin/env node
const { program } = require('commander');
const { version, codeGen, deploy, clean, logs, start, stop, update, uninstall } = require('./lib/command-handler');
const commands = require('./lib/commands');
program
.command('version')
.description('hpdevkit version')
.action(version);
.description('Display the hpdevkit version.')
.action(commands.version);
program
.command('gen <platform> <app-type> <project-name>')
.description('hpdevkit gen <platform> <app-type> <project-name>')
.action(codeGen);
.description('Generate HotPocket application development projects.')
.action(commands.codeGen);
program
.command('deploy <contract-path>')
.description('hpdevkit deploy <contract-path>')
.action(deploy);
.description('Deploy the specified directory to a HotPocket cluster.')
.action(commands.deploy);
program
.command('clean')
.description('hpdevkit clean')
.action(clean);
.description('Destroy the HotPocket cluster.')
.action(commands.clean);
program
.command('logs <node-number>')
.description('hpdevkit logs <node-number>')
.action(logs);
.description('Display logs of the specified node.')
.action(commands.logs);
program
.command('start <node-number>')
.description('hpdevkit start <node-number>')
.action(start);
.command('start [node-number]')
.description('Start the specified node. Starts all nodes if unspecified.')
.action(commands.start);
program
.command('stop <node-number>')
.description('hpdevkit stop <node-number>')
.action(stop);
.command('stop [node-number]')
.description('Stop the specified node. Stops all nodes if unspecified.')
.action(commands.stop);
program
.command('spawn')
.description('Create a fresh node which connects to the existing cluster.')
.action(commands.spawn);
program
.command('status')
.description('Display status of running nodes.')
.action(commands.status);
program
.command('update')
.description('hpdevkit update')
.action(update);
.description('Update hpdevkit.')
.action(commands.update);
program
.command('uninstall')
.description('uninstall')
.action(uninstall);
.description('Uninstall hpdevkit.')
.action(commands.uninstall);
try {
program.parse();

View File

@@ -4,12 +4,12 @@ const { exec } = require('./child-proc');
const {
CONSTANTS,
initializeDeploymentCluster,
runOnContainer,
executeOnContainer,
runOnNewContainer,
executeOnManagementContainer,
teardownDeploymentCluster,
isExists,
updateDockerImages
} = require('./common');
} = require('./docker-helpers');
const { success, error, info, warn } = require('./logger');
function version() {
@@ -37,7 +37,7 @@ function codeGen(platform, apptype, projName) {
}
try {
runOnContainer(CONSTANTS.codegenContainerName, null, null, null, null, `${platform} ${apptype} ${projName}`, 'codegen');
runOnNewContainer(CONSTANTS.codegenContainerName, null, null, null, null, `${platform} ${apptype} ${projName}`, 'codegen');
exec(`docker cp ${CONSTANTS.codegenContainerName}:${CONSTANTS.codegenOutputDir} ./${projName}`);
success(`Project '${projName}' created.`);
}
@@ -60,18 +60,24 @@ function deploy(contractPath) {
`rm -rf ${CONSTANTS.bundleMount}` :
`mkdir -p ${CONSTANTS.bundleMount} && rm -rf ${CONSTANTS.bundleMount}/* ${CONSTANTS.bundleMount}/.??*`;
executeOnContainer(CONSTANTS.deploymentContainerName, prepareBundleDir);
exec(`docker cp ${contractPath} "${CONSTANTS.deploymentContainerName}:${CONSTANTS.bundleMount}"`);
executeOnManagementContainer(prepareBundleDir);
exec(`docker cp ${contractPath} "${CONSTANTS.managementContainerName}:${CONSTANTS.bundleMount}"`);
// Sync contract bundle to all instance directories in the cluster.
executeOnContainer(CONSTANTS.deploymentContainerName, 'cluster stop ; cluster sync ; cluster start');
executeOnManagementContainer('cluster stop ; cluster sync ; cluster start');
if (appenv.defaultNode > 0) {
info(`Streaming logs of node ${appenv.defaultNode}:`);
executeOnContainer(CONSTANTS.deploymentContainerName, `cluster logs ${appenv.defaultNode}`);
executeOnManagementContainer(`cluster logs ${appenv.defaultNode}`);
}
}
function spawn() {
info(`command: spawn (cluster: ${appenv.cluster})`);
executeOnManagementContainer('cluster spawn && cluster logs 999999');
}
function clean() {
info(`command: clean (cluster: ${appenv.cluster})`);
@@ -81,19 +87,25 @@ function clean() {
function logs(nodeNumber) {
info(`command: logs (cluster: ${appenv.cluster})`);
runOnContainer(null, null, true, true, null, `logs ${nodeNumber}`, 'cluster');
executeOnManagementContainer(`cluster logs ${nodeNumber}`);
}
function start(nodeNumber) {
info(`command: start (cluster: ${appenv.cluster})`);
runOnContainer(null, null, true, true, null, `start ${nodeNumber}`, 'cluster');
executeOnManagementContainer(`cluster start ${nodeNumber}`);
}
function stop(nodeNumber) {
info(`command: stop (cluster: ${appenv.cluster})`);
runOnContainer(null, null, true, true, null, `stop ${nodeNumber}`, 'cluster');
executeOnManagementContainer(`cluster stop ${nodeNumber}`);
}
function status() {
info(`command: status (cluster: ${appenv.cluster})`);
executeOnManagementContainer(`cluster status`);
}
function update() {
@@ -124,7 +136,7 @@ function uninstall() {
exec(`npm -g uninstall ${CONSTANTS.npmPackageName}`, true);
// Remove deployment cluster if exist.
if (isExists(CONSTANTS.deploymentContainerName)) {
if (isExists(CONSTANTS.managementContainerName)) {
info('\nCleaning the deployed contracts...');
teardownDeploymentCluster();
}
@@ -151,6 +163,8 @@ module.exports = {
logs,
start,
stop,
spawn,
status,
update,
uninstall
};

View File

@@ -3,7 +3,6 @@ const { exec } = require("./child-proc");
const { log, info } = require("./logger");
const GLOBAL_PREFIX = "hpdevkit";
const VERSION = "0.1.0";
const CONSTANTS = {
npmPackageName: `hpdevkit`,
@@ -12,7 +11,7 @@ const CONSTANTS = {
network: `${GLOBAL_PREFIX}_${appenv.cluster}_net`,
containerPrefix: `${GLOBAL_PREFIX}_${appenv.cluster}_node`,
bundleMount: `${GLOBAL_PREFIX}_vol/contract_bundle`,
deploymentContainerName: `${GLOBAL_PREFIX}_${appenv.cluster}_deploymgr`,
managementContainerName: `${GLOBAL_PREFIX}_${appenv.cluster}_deploymgr`,
confOverrideFile: "hp.cfg.override",
codegenOutputDir: "/codegen-output",
codegenContainerName: `${GLOBAL_PREFIX}_codegen`,
@@ -20,7 +19,7 @@ const CONSTANTS = {
prerequisiteInstaller: "install.sh"
};
function runOnContainer(name, detached, autoRemove, mountStock, mountVolume, entryCmd, entryPoint, interactive = true, restart = null) {
function runOnNewContainer(name, detached, autoRemove, mountSock, mountVolume, entryCmd, entryPoint, interactive = true, restart = null) {
command = `docker run`;
if (interactive)
@@ -35,7 +34,7 @@ function runOnContainer(name, detached, autoRemove, mountStock, mountVolume, ent
if (autoRemove)
command += " --rm";
if (mountStock)
if (mountSock)
command += " --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock";
if (mountVolume)
@@ -71,6 +70,15 @@ function executeOnContainer(name, cmd) {
exec(`docker exec ${name} /bin/bash -c "${cmd}"`, true);
}
function executeOnManagementContainer(cmd) {
if (!isExists(CONSTANTS.managementContainerName)) {
info(`cluster '${appenv.cluster}' not found.`)
return;
}
executeOnContainer(CONSTANTS.managementContainerName, cmd)
}
function isExists(name, type = null) {
try {
const res = exec(`docker ${type === 'image' ? 'image ' : ''}inspect ${name}`);
@@ -85,26 +93,26 @@ function isExists(name, type = null) {
}
function initializeDeploymentCluster() {
if (!isExists(CONSTANTS.deploymentContainerName)) {
if (!isExists(CONSTANTS.managementContainerName)) {
log("\nInitializing deployment cluster");
// Stop cluster if running. Create cluster if not exists.
runOnContainer(CONSTANTS.deploymentContainerName, null, true, true, null, 'cluster stop ; cluster create', null);
runOnNewContainer(CONSTANTS.managementContainerName, null, true, true, null, 'cluster stop ; cluster create', null);
// Spin up management container.
runOnContainer(CONSTANTS.deploymentContainerName, true, false, true, true, null, null, true, 'unless-stopped');
runOnNewContainer(CONSTANTS.managementContainerName, true, false, true, true, null, null, true, 'unless-stopped');
// Bind the instance mesh network config together.
executeOnContainer(CONSTANTS.deploymentContainerName, 'cluster bindmesh');
executeOnContainer(CONSTANTS.managementContainerName, 'cluster bindmesh');
}
}
function teardownDeploymentCluster() {
if (isExists(CONSTANTS.deploymentContainerName)) {
exec(`docker stop ${CONSTANTS.deploymentContainerName}`);
exec(`docker rm ${CONSTANTS.deploymentContainerName}`);
if (isExists(CONSTANTS.managementContainerName)) {
exec(`docker stop ${CONSTANTS.managementContainerName}`);
exec(`docker rm ${CONSTANTS.managementContainerName}`);
}
runOnContainer(null, null, true, true, null, "cluster stop ; cluster destroy", null, false);
runOnNewContainer(null, null, true, true, null, "cluster stop ; cluster destroy", null, false);
}
function updateDockerImages() {
@@ -112,15 +120,16 @@ function updateDockerImages() {
exec(`docker pull ${appenv.instanceImage}`, true);
// Clear if there's already deployed cluster since they are outdated now.
if (isExists(CONSTANTS.deploymentContainerName)) {
if (isExists(CONSTANTS.managementContainerName)) {
info('\nCleaning the deployed contracts...');
teardownDeploymentCluster();
}
}
module.exports = {
runOnContainer,
runOnNewContainer,
executeOnContainer,
executeOnManagementContainer,
isExists,
initializeDeploymentCluster,
teardownDeploymentCluster,

4
npm/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "hpdevkit",
"version": "0.5.6",
"version": "0.5.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "hpdevkit",
"version": "0.5.6",
"version": "0.5.7",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "hpdevkit",
"version": "0.5.6",
"version": "0.5.7",
"description": "Developer toolkit for HotPocket smart contract development",
"scripts": {
"install": "node scripts/install.js"

View File

@@ -1,3 +1,3 @@
const { updateDockerImages } = require("../lib/common");
const { updateDockerImages } = require("../lib/docker-helpers");
updateDockerImages();