170 lines
4.6 KiB
JavaScript
170 lines
4.6 KiB
JavaScript
#!/usr/bin/env node
|
|
const { randomUUID } = require('crypto');
|
|
const { registerNode, randomTokenHex } = require('./register-node');
|
|
|
|
function getArgs() {
|
|
return process.argv.slice(2);
|
|
}
|
|
|
|
function getFlagValue(args, flag, fallback) {
|
|
const index = args.indexOf(flag);
|
|
|
|
if (index === -1 || index + 1 >= args.length) {
|
|
return fallback;
|
|
}
|
|
|
|
return args[index + 1];
|
|
}
|
|
|
|
function hasFlag(args, flag) {
|
|
return args.includes(flag);
|
|
}
|
|
|
|
function jsonOut(value) {
|
|
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
}
|
|
|
|
async function fetchEnvelope(url, options) {
|
|
const response = await fetch(url, options);
|
|
const payload = await response.json();
|
|
|
|
if (!response.ok || payload.error) {
|
|
const error = new Error(payload && payload.error ? payload.error.message : `Request failed: ${response.status}`);
|
|
error.payload = payload;
|
|
throw error;
|
|
}
|
|
|
|
return payload;
|
|
}
|
|
|
|
function buildContextHeaders() {
|
|
return {
|
|
'x-request-id': randomUUID(),
|
|
'x-correlation-id': randomUUID(),
|
|
};
|
|
}
|
|
|
|
function usage() {
|
|
process.stdout.write([
|
|
'Skipper CLI',
|
|
'',
|
|
'Commands:',
|
|
' skipper health --url <base_url>',
|
|
' skipper deploy apply --url <base_url> --admin-token <token> --tenant-id <tenant_id> [--idempotency-key <key>]',
|
|
' skipper node register --node-id <id> [--role <role>] [--region <region>] [--token <token>]',
|
|
' skipper node show --url <base_url> --admin-token <token> --node-id <id>',
|
|
'',
|
|
].join('\n'));
|
|
}
|
|
|
|
async function cmdHealth(args) {
|
|
const baseUrl = getFlagValue(args, '--url', process.env.SKIPPER_URL || 'http://127.0.0.1:3000');
|
|
const payload = await fetchEnvelope(`${baseUrl.replace(/\/$/, '')}/v1/health`, {
|
|
headers: buildContextHeaders(),
|
|
});
|
|
jsonOut(payload);
|
|
}
|
|
|
|
async function cmdDeployApply(args) {
|
|
const baseUrl = getFlagValue(args, '--url', process.env.SKIPPER_URL || 'http://127.0.0.1:3000');
|
|
const adminToken = getFlagValue(args, '--admin-token', process.env.ADMIN_TOKEN);
|
|
const tenantId = getFlagValue(args, '--tenant-id', process.env.TENANT_ID);
|
|
const idempotencyKey = getFlagValue(args, '--idempotency-key', process.env.IDEMPOTENCY_KEY || randomUUID());
|
|
|
|
if (!adminToken) {
|
|
throw new Error('Missing admin token. Use --admin-token or ADMIN_TOKEN.');
|
|
}
|
|
|
|
if (!tenantId) {
|
|
throw new Error('Missing tenant id. Use --tenant-id or TENANT_ID.');
|
|
}
|
|
|
|
const payload = await fetchEnvelope(`${baseUrl.replace(/\/$/, '')}/v1/deployments/${tenantId}/apply`, {
|
|
method: 'POST',
|
|
headers: {
|
|
...buildContextHeaders(),
|
|
'x-admin-token': adminToken,
|
|
'x-idempotency-key': idempotencyKey,
|
|
},
|
|
});
|
|
|
|
jsonOut(payload);
|
|
}
|
|
|
|
async function cmdNodeRegister(args) {
|
|
const nodeId = getFlagValue(args, '--node-id', process.env.NODE_ID);
|
|
const role = getFlagValue(args, '--role', process.env.NODE_ROLE || 'worker');
|
|
const region = getFlagValue(args, '--region', process.env.NODE_REGION || 'default');
|
|
const token = getFlagValue(args, '--token', process.env.NODE_TOKEN || randomTokenHex(32));
|
|
|
|
if (!nodeId) {
|
|
throw new Error('Missing node id. Use --node-id or NODE_ID.');
|
|
}
|
|
|
|
jsonOut(await registerNode({ nodeId, role, region, token }));
|
|
}
|
|
|
|
async function cmdNodeShow(args) {
|
|
const baseUrl = getFlagValue(args, '--url', process.env.SKIPPER_URL || 'http://127.0.0.1:3000');
|
|
const adminToken = getFlagValue(args, '--admin-token', process.env.ADMIN_TOKEN);
|
|
const nodeId = getFlagValue(args, '--node-id', process.env.NODE_ID);
|
|
|
|
if (!adminToken) {
|
|
throw new Error('Missing admin token. Use --admin-token or ADMIN_TOKEN.');
|
|
}
|
|
|
|
if (!nodeId) {
|
|
throw new Error('Missing node id. Use --node-id or NODE_ID.');
|
|
}
|
|
|
|
const payload = await fetchEnvelope(`${baseUrl.replace(/\/$/, '')}/v1/resources/node/${nodeId}`, {
|
|
headers: {
|
|
...buildContextHeaders(),
|
|
'x-admin-token': adminToken,
|
|
},
|
|
});
|
|
|
|
jsonOut(payload);
|
|
}
|
|
|
|
async function main() {
|
|
const args = getArgs();
|
|
|
|
if (args.length === 0 || hasFlag(args, '--help') || hasFlag(args, '-h')) {
|
|
usage();
|
|
return;
|
|
}
|
|
|
|
const [group, command, subcommand] = args;
|
|
|
|
if (group === 'health') {
|
|
await cmdHealth(args.slice(1));
|
|
return;
|
|
}
|
|
|
|
if (group === 'deploy' && command === 'apply') {
|
|
await cmdDeployApply(args.slice(2));
|
|
return;
|
|
}
|
|
|
|
if (group === 'node' && command === 'register') {
|
|
await cmdNodeRegister(args.slice(2));
|
|
return;
|
|
}
|
|
|
|
if (group === 'node' && command === 'show') {
|
|
await cmdNodeShow(args.slice(2));
|
|
return;
|
|
}
|
|
|
|
throw new Error(`Unknown command: ${args.join(' ')}`);
|
|
}
|
|
|
|
main().catch((error) => {
|
|
process.stderr.write(`${error.message}\n`);
|
|
if (error.payload) {
|
|
process.stderr.write(`${JSON.stringify(error.payload, null, 2)}\n`);
|
|
}
|
|
process.exit(1);
|
|
});
|