Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Commit

Permalink
Generate file with all necessary commands to execute
Browse files Browse the repository at this point in the history
  • Loading branch information
nagdahimanshu committed Oct 19, 2023
1 parent d16f8f8 commit 2a98940
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 102 deletions.
102 changes: 63 additions & 39 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { captureForgingStatusAtSnapshotHeight } from './events';
import {
copyGenesisBlock,
createGenesisBlock,
getGenesisBlockCreateCommand,
writeGenesisAssets,
writeGenesisBlock,
} from './utils/genesis_block';
Expand All @@ -56,9 +57,9 @@ import { installLiskCore, startLiskCore, isLiskCoreV3Running } from './utils/nod
import { resolveAbsolutePath, verifyOutputPath } from './utils/path';
import { execAsync } from './utils/process';
import { CustomError } from './utils/exception';
import { getCommandsToExecPostMigration, writeCommandsToExecute } from './utils/commands';

let configCoreV4: PartialApplicationConfig;

class LiskMigrator extends Command {
public static description = 'Migrate Lisk Core to latest version';

Expand Down Expand Up @@ -113,48 +114,48 @@ class LiskMigrator extends Command {
};

public async run(): Promise<void> {
try {
const { flags } = this.parse(LiskMigrator);
const liskCoreV3DataPath = resolveAbsolutePath(
flags['lisk-core-v3-data-path'] ?? DEFAULT_LISK_CORE_PATH,
const { flags } = this.parse(LiskMigrator);
const liskCoreV3DataPath = resolveAbsolutePath(
flags['lisk-core-v3-data-path'] ?? DEFAULT_LISK_CORE_PATH,
);
const outputPath = flags.output ?? join(__dirname, '..', 'output');
const snapshotHeight = flags['snapshot-height'];
const customConfigPath = flags.config;
const autoMigrateUserConfig = flags['auto-migrate-config'] ?? false;
const autoStartLiskCoreV4 = flags['auto-start-lisk-core-v4'];
const pageSize = Number(flags['page-size']);

verifyOutputPath(outputPath);

const client = await getAPIClient(liskCoreV3DataPath);
const nodeInfo = (await client.node.getNodeInfo()) as NodeInfo;
const { version: appVersion, networkIdentifier } = nodeInfo;

cli.action.start('Verifying if backup height from node config matches snapshot height');
if (snapshotHeight !== nodeInfo.backup.height) {
this.error(
`Lisk Core v3 backup height (${nodeInfo.backup.height}) does not match the expected snapshot height (${snapshotHeight}).`,
);
const outputPath = flags.output ?? join(__dirname, '..', 'output');
const snapshotHeight = flags['snapshot-height'];
const customConfigPath = flags.config;
const autoMigrateUserConfig = flags['auto-migrate-config'] ?? false;
const autoStartLiskCoreV4 = flags['auto-start-lisk-core-v4'];
const pageSize = Number(flags['page-size']);

verifyOutputPath(outputPath);

const client = await getAPIClient(liskCoreV3DataPath);
const nodeInfo = (await client.node.getNodeInfo()) as NodeInfo;
const { version: appVersion, networkIdentifier } = nodeInfo;

cli.action.start('Verifying if backup height from node config matches snapshot height');
if (snapshotHeight !== nodeInfo.backup.height) {
this.error(
`Lisk Core v3 backup height (${nodeInfo.backup.height}) does not match the expected snapshot height (${snapshotHeight}).`,
);
}
cli.action.stop('Snapshot height matches backup height');

cli.action.start(
`Verifying snapshot height to be multiples of round length i.e ${ROUND_LENGTH}`,
}
cli.action.stop('Snapshot height matches backup height');

cli.action.start(
`Verifying snapshot height to be multiples of round length i.e ${ROUND_LENGTH}`,
);
if (snapshotHeight % ROUND_LENGTH !== 0) {
this.error(
`Invalid snapshot height provided: ${snapshotHeight}. It must be an exact multiple of round length (${ROUND_LENGTH}).`,
);
if (snapshotHeight % ROUND_LENGTH !== 0) {
this.error(
`Invalid snapshot height provided: ${snapshotHeight}. It must be an exact multiple of round length (${ROUND_LENGTH}).`,
);
}
cli.action.stop('Snapshot height is valid');
}
cli.action.stop('Snapshot height is valid');

const networkConstant = NETWORK_CONSTANT[networkIdentifier] as NetworkConfigLocal;
const outputDir = flags.output ? outputPath : `${outputPath}/${networkIdentifier}`;
const networkConstant = NETWORK_CONSTANT[networkIdentifier] as NetworkConfigLocal;
const outputDir = flags.output ? outputPath : `${outputPath}/${networkIdentifier}`;

// Ensure the output directory is present
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
// Ensure the output directory is present
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });

try {
// Asynchronously capture the node's Forging Status information at the snapshot height
// This information is necessary for the node operators to enable generator post-migration without getting PoM'd
captureForgingStatusAtSnapshotHeight(this, client, snapshotHeight, outputDir);
Expand Down Expand Up @@ -363,10 +364,33 @@ class LiskMigrator extends Command {
this.log('Please copy genesis block to the Lisk Core V4 network directory.');
}
} catch (error) {
this.error(error as string);
const commandsToExecute: string[] = [];
const code = Number(`${(error as { message: string; code: number }).code}`);

if (code === ERROR_CODES.GENESIS_BLOCK_CREATE) {
const genesisBlockCreateCommand = getGenesisBlockCreateCommand();
commandsToExecute.push(genesisBlockCreateCommand);
}
if (code === ERROR_CODES.LISK_CORE_START) {
const liskCoreStartCommand = `lisk core start--network ${networkConstant.name}`;
commandsToExecute.push(liskCoreStartCommand);
}
await writeCommandsToExecute(
[...commandsToExecute, ...(await getCommandsToExecPostMigration(outputDir))],
outputDir,
);
this.error(`${(error as Error).message}`);
}

this.log('Successfully finished migration. Exiting!!!');
this.log(
`Creating file with the commands to execute post migration: ${outputDir}/commandsToExecute.txt`,
);
await writeCommandsToExecute(await getCommandsToExecPostMigration(outputDir), outputDir);
this.log(
`Successfully created file with the commands to execute post migration: ${outputDir}/commandsToExecute.txt`,
);

process.exit(0);
}
}
Expand Down
38 changes: 20 additions & 18 deletions src/utils/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import { resolve } from 'path';

import { read, write } from './fs';
import { read, write, exists } from './fs';

export const getCommandsToExecute = async (outputDir: string) => {
export const getCommandsToExecPostMigration = async (outputDir: string) => {
const commandsToExecute = [];

commandsToExecute.push(
Expand All @@ -25,22 +25,24 @@ export const getCommandsToExecute = async (outputDir: string) => {
commandsToExecute.push('lisk-core keys:import --file-path config/keys.json');

const forgingStatusJsonFilepath = resolve(outputDir, 'forgingStatus.json');
const forgingStatusString = (await read(forgingStatusJsonFilepath)) as string;
const forgingStatusJson = JSON.parse(forgingStatusString);

if (forgingStatusJson.length) {
for (const forgingStatus of forgingStatusJson) {
commandsToExecute.push(
`lisk-core endpoint:invoke random_setHashOnion '{"address":"${forgingStatus.lskAddress}"}'`,
);

commandsToExecute.push(
`lisk-core endpoint:invoke generator_setStatus '{"address":"${forgingStatus.lskAddress}", "height": ${forgingStatus.height}, "maxHeightGenerated": ${forgingStatus.maxHeightPreviouslyForged}, "maxHeightPrevoted": ${forgingStatus.maxHeightPrevoted} }' --pretty`,
);

commandsToExecute.push(
`lisk-core generator:enable ${forgingStatus.lskAddress} --use-status-value`,
);
if (await exists(forgingStatusJsonFilepath)) {
const forgingStatusString = (await read(forgingStatusJsonFilepath)) as string;
const forgingStatusJson = JSON.parse(forgingStatusString);

if (forgingStatusJson.length) {
for (const forgingStatus of forgingStatusJson) {
commandsToExecute.push(
`lisk-core endpoint:invoke random_setHashOnion '{"address":"${forgingStatus.lskAddress}"}'`,
);

commandsToExecute.push(
`lisk-core endpoint:invoke generator_setStatus '{"address":"${forgingStatus.lskAddress}", "height": ${forgingStatus.height}, "maxHeightGenerated": ${forgingStatus.maxHeightPreviouslyForged}, "maxHeightPrevoted": ${forgingStatus.maxHeightPrevoted} }' --pretty`,
);

commandsToExecute.push(
`lisk-core generator:enable ${forgingStatus.lskAddress} --use-status-value`,
);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/utils/genesis_block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { CustomError } from './exception';

let genesisBlockCreateCommand: string;

export const getGenesisBlockCreateCommand = () => genesisBlockCreateCommand;

export const createChecksum = async (filePath: string): Promise<string> => {
const fileStream = fs.createReadStream(filePath);
const dataHash = crypto.createHash('sha256');
Expand Down
87 changes: 49 additions & 38 deletions src/utils/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ import { isPortAvailable } from './network';
import { resolveAbsolutePath } from './path';
import { Port } from '../types';
import { getAPIClient } from '../client';
import { DEFAULT_PORT_P2P, DEFAULT_PORT_RPC, LEGACY_DB_PATH, SNAPSHOT_DIR } from '../constants';
import {
DEFAULT_PORT_P2P,
DEFAULT_PORT_RPC,
ERROR_CODES,
LEGACY_DB_PATH,
SNAPSHOT_DIR,
} from '../constants';
import { CustomError } from './exception';

const INSTALL_LISK_CORE_COMMAND = 'npm i -g lisk-core@^4.0.0-rc.1';
const INSTALL_PM2_COMMAND = 'npm i -g pm2';
Expand Down Expand Up @@ -175,50 +182,54 @@ export const startLiskCore = async (
network: string,
outputDir: string,
): Promise<void | Error> => {
const networkPort = (_config?.network?.port as Port) ?? DEFAULT_PORT_P2P;
if (!(await isPortAvailable(networkPort))) {
throw new Error(`Port ${networkPort} is not available for P2P communication.`);
}

const rpcPort = (_config?.network?.port as Port) ?? DEFAULT_PORT_RPC;
if (!(await isPortAvailable(rpcPort))) {
throw new Error(`Port ${rpcPort} is not available to start the RPC server.`);
}
try {
const networkPort = (_config?.network?.port as Port) ?? DEFAULT_PORT_P2P;
if (!(await isPortAvailable(networkPort))) {
throw new Error(`Port ${networkPort} is not available for P2P communication.`);
}

await backupDefaultDirectoryIfExists(_this, liskCoreV3DataPath);
await copyLegacyDB(_this);
const rpcPort = (_config?.network?.port as Port) ?? DEFAULT_PORT_RPC;
if (!(await isPortAvailable(rpcPort))) {
throw new Error(`Port ${rpcPort} is not available to start the RPC server.`);
}

const configPath = await getFinalConfigPath(outputDir, network);
const liskCoreStartCommand = await resolveLiskCoreStartCommand(_this, network, configPath);
await backupDefaultDirectoryIfExists(_this, liskCoreV3DataPath);
await copyLegacyDB(_this);

const pm2Config = {
name: 'lisk-core-v4',
script: liskCoreStartCommand,
};
const configPath = await getFinalConfigPath(outputDir, network);
const liskCoreStartCommand = await resolveLiskCoreStartCommand(_this, network, configPath);

const isUserConfirmed = await cli.confirm(
`Start Lisk Core with the following pm2 configuration? [yes/no]\n${JSON.stringify(
pm2Config,
null,
'\t',
)}`,
);
const pm2Config = {
name: 'lisk-core-v4',
script: liskCoreStartCommand,
};

if (!isUserConfirmed) {
_this.error(
'User did not confirm to start Lisk Core v4 with the customized PM2 config. Skipping the Lisk Core v4 auto-start process. Please start the node manually.',
const isUserConfirmed = await cli.confirm(
`Start Lisk Core with the following pm2 configuration? [yes/no]\n${JSON.stringify(
pm2Config,
null,
'\t',
)}`,
);
}

_this.log('Installing PM2...');
await installPM2();
_this.log('Finished installing PM2.');
if (!isUserConfirmed) {
_this.error(
'User did not confirm to start Lisk Core v4 with the customized PM2 config. Skipping the Lisk Core v4 auto-start process. Please start the node manually.',
);
}

const pm2FilePath = path.resolve(outputDir, PM2_FILE_NAME);
_this.log(`Creating PM2 config at ${pm2FilePath}`);
fs.writeFileSync(pm2FilePath, JSON.stringify(pm2Config, null, '\t'));
_this.log(`Successfully created the PM2 config at ${pm2FilePath}`);
_this.log('Installing PM2...');
await installPM2();
_this.log('Finished installing PM2.');

const PM2_COMMAND_START = `pm2 start ${pm2FilePath}`;
_this.log(await execAsync(PM2_COMMAND_START));
const pm2FilePath = path.resolve(outputDir, PM2_FILE_NAME);
_this.log(`Creating PM2 config at ${pm2FilePath}`);
fs.writeFileSync(pm2FilePath, JSON.stringify(pm2Config, null, '\t'));
_this.log(`Successfully created the PM2 config at ${pm2FilePath}`);

const PM2_COMMAND_START = `pm2 start ${pm2FilePath}`;
_this.log(await execAsync(PM2_COMMAND_START));
} catch (error) {
throw new CustomError(`${(error as Error).message}`, ERROR_CODES.LISK_CORE_START);
}
};
13 changes: 6 additions & 7 deletions test/unit/utils/commands.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
*/
import * as fs from 'fs-extra';
import { join } from 'path';
import { getCommandsToExecute, writeCommandsToExecute } from '../../../src/utils/commands';
import {
getCommandsToExecPostMigration,
writeCommandsToExecute,
} from '../../../src/utils/commands';
import { exists } from '../../../src/utils/fs';

const outputDir = join(__dirname, '../../..', 'test/unit/fixtures');
Expand All @@ -22,14 +25,10 @@ afterAll(() => {
fs.removeSync(join(outputDir, 'commandsToExecute.txt'));
});

describe('Test getCommandsToExecute method', () => {
describe('Test getCommandsToExecPostMigration method', () => {
it('should create commandsToExecute text file', async () => {
const commandsToExecute = await getCommandsToExecute(outputDir);
const commandsToExecute = await getCommandsToExecPostMigration(outputDir);
await writeCommandsToExecute(commandsToExecute, outputDir);
expect(await exists(`${outputDir}/commandsToExecute.txt`)).toBe(true);
});

it('should throw error when outputDir is invalid', async () => {
await expect(getCommandsToExecute('invalidPath')).rejects.toThrow();
});
});

0 comments on commit 2a98940

Please sign in to comment.