Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
peterpeterparker committed Nov 29, 2024
2 parents af05738 + d9e8870 commit 60e190f
Show file tree
Hide file tree
Showing 10 changed files with 512 additions and 3 deletions.
55 changes: 55 additions & 0 deletions src/api/ic.api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import {ICManagementCanister} from '@dfinity/ic-management';
import type {
list_canister_snapshots_result,
snapshot_id
} from '@dfinity/ic-management/dist/candid/ic-management';
import type {Principal} from '@dfinity/principal';
import {initAgent} from './agent.api';

Expand All @@ -21,3 +25,54 @@ export const canisterStart = async ({canisterId}: {canisterId: Principal}): Prom

await startCanister(canisterId);
};

export const takeCanisterSnapshot = async (params: {
canisterId: Principal;
snapshotId?: snapshot_id;
}): Promise<void> => {
const agent = await initAgent();

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

await takeCanisterSnapshot(params);
};

export const listCanisterSnapshots = async (params: {
canisterId: Principal;
}): Promise<list_canister_snapshots_result> => {
const agent = await initAgent();

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

return await listCanisterSnapshots(params);
};

export const loadCanisterSnapshot = async (params: {
canisterId: Principal;
snapshotId: snapshot_id;
}): Promise<void> => {
const agent = await initAgent();

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

await loadCanisterSnapshot(params);
};

export const deleteCanisterSnapshot = async (params: {
canisterId: Principal;
snapshotId: snapshot_id;
}): Promise<void> => {
const agent = await initAgent();

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

await deleteCanisterSnapshot(params);
};
85 changes: 85 additions & 0 deletions src/commands/backup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {nextArg} from '@junobuild/cli-tools';
import {red} from 'kleur';
import {logHelpBackup} from '../help/backup.help';
import {logHelpDev} from '../help/dev.help';
import {
createSnapshotMissionControl,
deleteSnapshotMissionControl,
restoreSnapshotMissionControl
} from '../services/backup/backup.mission-control.services';
import {
createSnapshotOrbiter,
deleteSnapshotOrbiter,
restoreSnapshotOrbiter
} from '../services/backup/backup.orbiter.services';
import {
createSnapshotSatellite,
deleteSnapshotSatellite,
restoreSnapshotSatellite
} from '../services/backup/backup.satellite.services';

export const backup = async (args?: string[]) => {
const [subCommand] = args ?? [];

switch (subCommand) {
case 'create':
await executeBackupFn({
args,
satelliteFn: createSnapshotSatellite,
missionControlFn: createSnapshotMissionControl,
orbiterFn: createSnapshotOrbiter
});
break;
case 'restore':
await executeBackupFn({
args,
satelliteFn: restoreSnapshotSatellite,
missionControlFn: restoreSnapshotMissionControl,
orbiterFn: restoreSnapshotOrbiter
});
break;
case 'delete':
await executeBackupFn({
args,
satelliteFn: deleteSnapshotSatellite,
missionControlFn: deleteSnapshotMissionControl,
orbiterFn: deleteSnapshotOrbiter
});
break;
default:
console.log(`${red('Unknown subcommand.')}`);
logHelpDev();
}
};

const executeBackupFn = async ({
args,
satelliteFn,
missionControlFn,
orbiterFn
}: {
args?: string[];
satelliteFn: (params: {args?: string[]}) => Promise<void>;
missionControlFn: () => Promise<void>;
orbiterFn: () => Promise<void>;
}) => {
const target = nextArg({args, option: '-t'}) ?? nextArg({args, option: '--target'});

switch (target) {
case 's':
case 'satellite':
await satelliteFn({args});
break;
case 'm':
case 'mission-control':
await missionControlFn();
break;
case 'o':
case 'orbiter':
await orbiterFn();
break;
default:
console.log(`${red('Unknown target.')}`);
logHelpBackup(args);
}
};
40 changes: 40 additions & 0 deletions src/help/backup.help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {cyan, green, magenta, yellow} from 'kleur';
import {helpMode, helpOutput} from './common.help';
import {TITLE} from './help';
import {TARGET_OPTION_NOTE, targetOption} from './target.help';

export const BACKUP_DESCRIPTION = 'Handle backup-related tasks.';

const usage = `Usage: ${green('juno')} ${cyan('dev')} ${magenta('<subcommand>')} ${yellow('[options]')}
Subcommands:
${magenta('create')} Create a backup of your current state.
${magenta('restore')} Restore a previously created backup.
${magenta('delete')} Delete an existing backup
Options:
${targetOption('backup')}
${helpMode}
${yellow('-h, --help')} Output usage information.
Notes:
${TARGET_OPTION_NOTE}`;

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

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

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

export const targetOption = (action: 'upgrade' | 'backup'): string =>
`${yellow('-t, --target')} Which module type should be ${action === 'backup' ? 'backed up' : 'upgraded'}? Valid targets are ${magenta('satellite')}, ${magenta('mission-control')} or ${magenta('orbiter')}.`;

export const TARGET_OPTION_NOTE = `- Targets can be shortened to ${magenta('s')} for satellite, ${magenta('m')} for mission-control and ${magenta('o')} for orbiter.`;
7 changes: 4 additions & 3 deletions src/help/upgrade.help.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {cyan, green, magenta, yellow} from 'kleur';
import {cyan, green, yellow} from 'kleur';
import {helpMode, helpOutput} from './common.help';
import {TITLE} from './help';
import {TARGET_OPTION_NOTE, targetOption} from './target.help';

export const UPGRADE_DESCRIPTION = 'Upgrade a module to a new version.';

const usage = `Usage: ${green('juno')} ${cyan('upgrade')} ${yellow('[options]')}
Options:
${yellow('-t, --target')} Which module type should be upgraded? Valid targets are ${magenta('satellite')}, ${magenta('mission-control')} or ${magenta('orbiter')}.
${targetOption('upgrade')}
${yellow('-s, --src')} An optional local gzipped WASM file for the upgrade. By default, the CDN will be used.
${yellow('-r, --reset')} Reset to the initial state.
${yellow('-cc, --clear-chunks')} Clear any previously uploaded WASM chunks (applies if the WASM size is greater than 2MB).
Expand All @@ -18,7 +19,7 @@ Options:
Notes:
- Resetting a mission control is not possible.
- Targets can be shortened to ${magenta('s')} for satellite, ${magenta('m')} for mission-control and ${magenta('o')} for orbiter.`;
${TARGET_OPTION_NOTE}`;

const doc = `${UPGRADE_DESCRIPTION}
Expand Down
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {hasArgs} from '@junobuild/cli-tools';
import {red} from 'kleur';
import {login, logout} from './commands/auth';
import {backup} from './commands/backup';
import {clear} from './commands/clear';
import {config} from './commands/config';
import {deploy} from './commands/deploy';
Expand All @@ -12,6 +13,7 @@ import {upgrade} from './commands/upgrade';
import {use} from './commands/use';
import {version as versionCommand} from './commands/version';
import {whoami} from './commands/whoami';
import {logHelpBackup} from './help/backup.help';
import {logHelpClear} from './help/clear.help';
import {logHelpConfig} from './help/config.help';
import {logHelpDeploy} from './help/deploy.help';
Expand Down Expand Up @@ -81,6 +83,9 @@ export const run = async () => {
case 'dev':
logHelpDev(args);
break;
case 'backup':
logHelpBackup(args);
break;
case 'init':
logHelpInit(args);
break;
Expand Down Expand Up @@ -149,6 +154,9 @@ export const run = async () => {
case 'dev':
await dev(args);
break;
case 'backup':
await backup(args);
break;
case 'help':
console.log(help);
break;
Expand Down
46 changes: 46 additions & 0 deletions src/services/backup/backup.mission-control.services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {isNullish} from '@junobuild/utils';
import {red} from 'kleur';
import {getCliMissionControl} from '../../configs/cli.config';
import type {AssetKey} from '../../types/asset-key';
import {createSnapshot, deleteSnapshot, restoreSnapshot} from './backup.services';

export const createSnapshotMissionControl = async () => {
await executeBackupFn({
fn: createSnapshot
});
};

export const restoreSnapshotMissionControl = async () => {
await executeBackupFn({
fn: restoreSnapshot
});
};

export const deleteSnapshotMissionControl = async () => {
await executeBackupFn({
fn: deleteSnapshot
});
};

const executeBackupFn = async ({
fn
}: {
fn: (params: {canisterId: string; segment: AssetKey}) => Promise<void>;
}) => {
const missionControl = await getCliMissionControl();

// TODO: this can be a common assertion
if (isNullish(missionControl)) {
console.log(
`${red(
'No mission control found.'
)} Ignore this error if your controller does not control your mission control.`
);
return;
}

await fn({
canisterId: missionControl,
segment: 'mission_control'
});
};
40 changes: 40 additions & 0 deletions src/services/backup/backup.orbiter.services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {getCliOrbiters} from '../../configs/cli.config';
import type {AssetKey} from '../../types/asset-key';
import {createSnapshot, deleteSnapshot, restoreSnapshot} from './backup.services';

export const createSnapshotOrbiter = async () => {
await executeBackupFn({
fn: createSnapshot
});
};

export const restoreSnapshotOrbiter = async () => {
await executeBackupFn({
fn: restoreSnapshot
});
};

export const deleteSnapshotOrbiter = async () => {
await executeBackupFn({
fn: deleteSnapshot
});
};

const executeBackupFn = async ({
fn
}: {
fn: (params: {canisterId: string; segment: AssetKey}) => Promise<void>;
}) => {
const authOrbiters = await getCliOrbiters();

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

for (const orbiter of authOrbiters) {
await fn({
canisterId: orbiter.p,
segment: 'orbiter'
});
}
};
Loading

0 comments on commit 60e190f

Please sign in to comment.