@controlplane/cli
Version: 
Control Plane Corporation CLI
208 lines • 8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentCmd = void 0;
const os = require("os");
const path = require("path");
const options_1 = require("./options");
const generic_1 = require("./generic");
const resolver_1 = require("./resolver");
const version_1 = require("../util/version");
const command_1 = require("../cli/command");
const agentDefaults_1 = require("./agentDefaults");
const agentManifest_1 = require("./agentManifest");
const logger_1 = require("../util/logger");
const functions_1 = require("../util/functions");
const io_1 = require("../util/io");
const child_process_1 = require("child_process");
class AgentCmd extends command_1.Command {
    constructor() {
        super(...arguments);
        this.command = 'agent';
        this.describe = 'Manage agents';
    }
    builder(yargs) {
        const resolver = (0, resolver_1.kindResolver)('agent');
        const opts = [options_1.withOrgOptions, options_1.withStandardOptions];
        const commandName = 'agent';
        const commandNamePlural = 'agents';
        const commandNameA = 'an agent';
        return (yargs
            .demandCommand()
            .version(false)
            .help()
            // generic
            .command(new generic_1.Get(commandNamePlural, resolver, ...opts).toYargs())
            .command(new generic_1.Edit(commandName, resolver, ...opts).toYargs())
            .command(new generic_1.Query(commandNamePlural, resolver, undefined, ...opts).toYargs())
            .command(new generic_1.Delete(commandNamePlural, resolver, ...opts).toYargs())
            .command(new generic_1.Eventlog(commandName, resolver, ...opts).toYargs())
            .command(new generic_1.Tag(commandNamePlural, resolver, ...opts).toYargs())
            .command(new generic_1.Patch(commandName, resolver, ...opts).toYargs())
            .command(new generic_1.ListPermissions(commandNameA, resolver, ...opts).toYargs())
            .command(new generic_1.ViewAccessReport(commandName, resolver, ...opts).toYargs())
            .command(new generic_1.Update(commandName, resolver, [
            {
                path: 'description',
            },
            {
                path: 'tags.<key>',
            },
        ], ...opts).toYargs())
            // specific
            .command(new Create(resolver).toYargs())
            .command(new Up().toYargs())
            .command(new agentManifest_1.Manifest().toYargs())
            .command(new Info(resolver).toYargs()));
    }
    handle() { }
}
exports.AgentCmd = AgentCmd;
class Create extends command_1.Command {
    constructor(resolve) {
        super();
        this.resolve = resolve;
        this.command = 'create';
        this.describe = 'Create a new agent';
    }
    withCreateOptions(yargs) {
        return yargs.options({
            name: {
                describe: 'Name of the agent',
                requiresArg: true,
                demandOption: true,
            },
            description: {
                alias: 'desc',
                requiresArg: true,
                describe: 'Optional description, defaults to the name if not set',
            },
        });
    }
    builder(yargs) {
        return (0, functions_1.pipe)(
        //
        this.withCreateOptions, generic_1.withTagOptions, options_1.withOrgOptions, options_1.withStandardOptions)(yargs);
    }
    async handle(args) {
        const body = {
            name: args.name,
            description: args.description,
            tags: (0, generic_1.fromTagOptions)(args),
        };
        const agentsHome = this.resolve.homeLink(this.session.context);
        // must not use create() as it follows the `location` link
        const data = await this.client.post(agentsHome, body);
        this.session.outFormat(data.status.bootstrapConfig);
    }
}
class Up extends command_1.Command {
    constructor() {
        super();
        this.command = 'up';
        this.describe = 'Run an agent within a local Docker instance';
    }
    withUpOptions(yargs) {
        return yargs.options({
            'bootstrap-file': {
                describe: 'Path to the bootstrap config file',
                requiresArg: true,
                demandOption: true,
            },
            background: {
                alias: 'b',
                boolean: true,
                describe: 'If set, run the agent as a background process',
            },
            image: {
                requiresArg: true,
                describe: 'Advanced use: Use a different agent Docker image',
            },
            net: {
                requiresArg: true,
                describe: 'Docker network to use',
                default: 'bridge',
            },
        });
    }
    builder(yargs) {
        return (0, functions_1.pipe)(this.withUpOptions)(yargs);
    }
    async createVol(agentId) {
        const volName = `agent-${agentId}`;
        try {
            await (0, child_process_1.spawn)('docker', ['volume', 'create', volName]);
        }
        catch (e) {
            // ignore error, volume already exists
        }
        return volName;
    }
    async handle(args) {
        const bootstrapFile = path.resolve(args.bootstrapFile);
        const bootstrapConfig = JSON.parse(await (0, io_1.readTextFile)(bootstrapFile));
        const info = {
            cloudProvider: 'docker',
            image: args.image || agentDefaults_1.DEFAULT_IMAGE,
            cliVersion: version_1.About.build,
            session: this.session.id,
            username: os.userInfo().username,
            hostname: os.hostname(),
        };
        const volName = await this.createVol(bootstrapConfig.agentId);
        const dockerArgs = ['run'];
        dockerArgs.push('--init');
        dockerArgs.push('--cap-add');
        dockerArgs.push('CAP_NET_ADMIN');
        dockerArgs.push('--name');
        dockerArgs.push(`agent-${bootstrapConfig.agentId}`);
        dockerArgs.push('--hostname');
        dockerArgs.push(`agent-${bootstrapConfig.agentId}`);
        dockerArgs.push('--net');
        dockerArgs.push(args.net);
        dockerArgs.push('--mount');
        dockerArgs.push(`source=${volName},target=/etc/controlplane-keys`);
        dockerArgs.push('--volume');
        dockerArgs.push(`${bootstrapFile}:/etc/controlplane/bootstrap.json:ro`);
        dockerArgs.push('--env');
        dockerArgs.push(`BOOTSTRAP_PATH=/etc/controlplane/bootstrap.json`);
        dockerArgs.push('--env');
        dockerArgs.push(`WG_PRIVATE_KEY_PATH=/etc/controlplane-keys/agent.private`);
        dockerArgs.push('--env');
        dockerArgs.push(`WG_PUBLIC_KEY_PATH=/etc/controlplane-keys/agent.public`);
        dockerArgs.push('--env');
        dockerArgs.push(`ENV_INFO=${JSON.stringify(info)}`);
        dockerArgs.push('--entrypoint');
        dockerArgs.push(`/docker-entrypoint.sh`);
        if (args.background) {
            dockerArgs.push('-td');
        }
        else {
            dockerArgs.push('--env');
            dockerArgs.push(`PRETTY_LOGS=1`);
            dockerArgs.push('-ti');
            dockerArgs.push('--rm');
        }
        dockerArgs.push(args.image || agentDefaults_1.DEFAULT_IMAGE);
        logger_1.logger.debug('Invoking docker as ' + dockerArgs);
        await (0, child_process_1.spawn)('docker', dockerArgs, {
            stdio: 'inherit',
        });
    }
}
class Info extends command_1.Command {
    constructor(resolve) {
        super();
        this.resolve = resolve;
        this.command = 'info <ref>';
        this.describe = 'Get info about an agent';
    }
    builder(yargs) {
        return (0, functions_1.pipe)(generic_1.withSingleRef, options_1.withOrgOptions, options_1.withStandardOptions)(yargs);
    }
    async handle(args) {
        const link = this.resolve.resourceLink(args.ref, this.session.context);
        const body = await this.client.get(link + '/-info');
        this.session.outFormat(body);
    }
}
//# sourceMappingURL=agent.js.map