Skip to content

Commit

Permalink
feat: stop and start commands (#154)
Browse files Browse the repository at this point in the history
* feat: stop and start commands

* feat: implement start and stop
  • Loading branch information
peterpeterparker authored Nov 25, 2024
1 parent 9c3b7fb commit 931e34a
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/api/ic.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {ICManagementCanister} from '@dfinity/ic-management';
import type {Principal} from '@dfinity/principal';
import {initAgent} from '../utils/actor.utils';

export const canisterStop = async ({canisterId}: {canisterId: Principal}): Promise<void> => {
const agent = await initAgent();

const {stopCanister} = ICManagementCanister.create({
agent
});

await stopCanister(canisterId);
};

export const canisterStart = async ({canisterId}: {canisterId: Principal}): Promise<void> => {
const agent = await initAgent();

const {startCanister} = ICManagementCanister.create({
agent
});

await startCanister(canisterId);
};
38 changes: 38 additions & 0 deletions src/commands/start-stop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {nextArg} from '@junobuild/cli-tools';
import {red} from 'kleur';
import {logHelpStart} from '../help/start.help';
import {logHelpStop} from '../help/stop.help';
import {
startStopMissionControl,
startStopOrbiter,
startStopSatellite
} from '../services/start-stop.services';
import type {StartStopAction} from '../types/start-stop';

export const startStop = async ({args, action}: {args?: string[]; action: StartStopAction}) => {
const target = nextArg({args, option: '-t'}) ?? nextArg({args, option: '--target'});

switch (target) {
case 's':
case 'satellite':
await startStopSatellite({args, action});
break;
case 'm':
case 'mission-control':
await startStopMissionControl({args, action});
break;
case 'o':
case 'orbiter':
await startStopOrbiter({args, action});
break;
default:
console.log(`${red('Unknown target.')}`);

if (action === 'stop') {
logHelpStop(args);
return;
}

logHelpStart(args);
}
};
34 changes: 34 additions & 0 deletions src/help/start.help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {cyan, green, magenta, yellow} from 'kleur';
import {helpMode, helpOutput} from './common.help';
import {TITLE} from './help';

export const START_DESCRIPTION = 'Start a module.';

const usage = `Usage: ${green('juno')} ${cyan('start')} ${yellow('[options]')}
Options:
${yellow('-t, --target')} Which module type should be started? Valid targets are ${magenta('satellite')}, ${magenta('mission-control')} or ${magenta('orbiter')}.
${helpMode}
${yellow('-h, --help')} Output usage information.
Notes:
- Targets can be shortened to ${magenta('s')} for satellite, ${magenta('m')} for mission-control and ${magenta('o')} for orbiter.`;

const doc = `${START_DESCRIPTION}
\`\`\`bash
${usage}
\`\`\`
`;

const help = `${TITLE}
${START_DESCRIPTION}
${usage}
`;

export const logHelpStart = (args?: string[]) => {
console.log(helpOutput(args) === 'doc' ? doc : help);
};
34 changes: 34 additions & 0 deletions src/help/stop.help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {cyan, green, magenta, yellow} from 'kleur';
import {helpMode, helpOutput} from './common.help';
import {TITLE} from './help';

export const STOP_DESCRIPTION = 'Stop a module.';

const usage = `Usage: ${green('juno')} ${cyan('stop')} ${yellow('[options]')}
Options:
${yellow('-t, --target')} Which module type should be stopped? Valid targets are ${magenta('satellite')}, ${magenta('mission-control')} or ${magenta('orbiter')}.
${helpMode}
${yellow('-h, --help')} Output usage information.
Notes:
- Targets can be shortened to ${magenta('s')} for satellite, ${magenta('m')} for mission-control and ${magenta('o')} for orbiter.`;

const doc = `${STOP_DESCRIPTION}
\`\`\`bash
${usage}
\`\`\`
`;

const help = `${TITLE}
${STOP_DESCRIPTION}
${usage}
`;

export const logHelpStop = (args?: string[]) => {
console.log(helpOutput(args) === 'doc' ? doc : help);
};
15 changes: 15 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {deploy} from './commands/deploy';
import {dev} from './commands/dev';
import {init} from './commands/init';
import {open} from './commands/open';
import {startStop} from './commands/start-stop';
import {upgrade} from './commands/upgrade';
import {use} from './commands/use';
import {version as versionCommand} from './commands/version';
Expand All @@ -20,6 +21,8 @@ import {logHelpInit} from './help/init.help';
import {logHelpLogin} from './help/login.help';
import {logHelpLogout} from './help/logout.help';
import {logHelpOpen} from './help/open.help';
import {logHelpStart} from './help/start.help';
import {logHelpStop} from './help/stop.help';
import {logHelpUpgrade} from './help/upgrade.help';
import {logHelpUse} from './help/use.help';
import {logHelpVersion} from './help/version.help';
Expand Down Expand Up @@ -90,6 +93,12 @@ export const run = async () => {
case 'whoami':
logHelpWhoAmI(args);
break;
case 'stop':
logHelpStop(args);
break;
case 'start':
logHelpStart(args);
break;
default:
console.log(`${red('Unknown command.')}`);
console.log(help);
Expand Down Expand Up @@ -131,6 +140,12 @@ export const run = async () => {
case 'use':
await use(args);
break;
case 'stop':
await startStop({args, action: 'stop'});
break;
case 'start':
await startStop({args, action: 'start'});
break;
case 'dev':
await dev(args);
break;
Expand Down
106 changes: 106 additions & 0 deletions src/services/start-stop.services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {Principal} from '@dfinity/principal';
import {assertNonNullish, isNullish} from '@junobuild/utils';
import {cyan, red} from 'kleur';
import ora from 'ora';
import {canisterStart, canisterStop} from '../api/ic.api';
import {getCliMissionControl, getCliOrbiters} from '../configs/cli.config';
import {junoConfigExist, readJunoConfig} from '../configs/juno.config';
import type {AssetKey} from '../types/asset-key';
import type {StartStopAction} from '../types/start-stop';
import {configEnv} from '../utils/config.utils';
import {consoleNoConfigFound} from '../utils/msg.utils';
import {satelliteParameters} from '../utils/satellite.utils';

export const startStopMissionControl = async ({
action
}: {
args?: string[];
action: StartStopAction;
}) => {
const missionControl = await getCliMissionControl();

if (isNullish(missionControl)) {
console.log(`${red(`A mission control must be set in your configuration.`)}`);
process.exit(1);
}

await startStop({
action,
segment: 'mission_control',
canisterId: missionControl
});
};

export const startStopOrbiter = async ({action}: {args?: string[]; action: StartStopAction}) => {
const authOrbiters = await getCliOrbiters();

if (authOrbiters === undefined || authOrbiters.length === 0) {
return;
}

if (authOrbiters.length > 0) {
console.log(`${red(`The CLI supports only one orbiter per project. Reach out to Juno.`)}`);
process.exit(1);
}

const [orbiter] = authOrbiters;

await startStop({
action,
segment: 'orbiter',
canisterId: orbiter.p
});
};

export const startStopSatellite = async ({
args,
action
}: {
args?: string[];
action: StartStopAction;
}) => {
if (!(await junoConfigExist())) {
consoleNoConfigFound();
return;
}

const env = configEnv(args);
const {satellite: satelliteConfig} = await readJunoConfig(env);

const satellite = await satelliteParameters({satellite: satelliteConfig, env});
const {satelliteId} = satellite;

// TS guard. satelliteParameters exit if satelliteId is undefined.
assertNonNullish(satelliteId);

await startStop({
action,
segment: 'satellite',
canisterId: satelliteId
});
};

const startStop = async ({
action,
segment,
canisterId
}: {
action: StartStopAction;
canisterId: string;
segment: AssetKey;
}) => {
const spinner = ora(`${action === 'stop' ? 'Stopping' : 'Starting'} satellite...`).start();

try {
const fn = action === 'stop' ? canisterStop : canisterStart;
await fn({canisterId: Principal.fromText(canisterId)});
} finally {
spinner.stop();
}

const capitalize = (str: string): string => str[0].toUpperCase() + str.slice(1);

console.log(
`${capitalize(segment)} ${canisterId} ${cyan(action === 'stop' ? 'stopped' : 'started')}.`
);
};
1 change: 1 addition & 0 deletions src/types/start-stop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type StartStopAction = 'start' | 'stop';

0 comments on commit 931e34a

Please sign in to comment.