"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const _ = require("lodash");
const command_base_exec_tenant_1 = require("../../command-base-exec-tenant");
const contracts_1 = require("../../apis/codiac-api/contracts");
const contracts_2 = require("../../apis/codiac-relay/contracts");
const cluster_def_1 = require("@codiac.io/codiac-domain/cluster-def");
const security_account_1 = require("@codiac.io/codiac-domain/security-account");
const codiac_ops_common_1 = require("@codiac.io/codiac-ops-common");
const cloud_provider_contextualizer_1 = require("../../uilogic/cloud-provider-contextualizer");
const terminal_utils_1 = require("../../ops/terminal-utils");
const local_command_runner_1 = require("../../ops/terminal-command-runner/local-command-runner");
const prettify_1 = require("../../prettify");
class ClusterBootstrap extends command_base_exec_tenant_1.CommandBaseExecTenant {
    constructor() {
        // static hidden = true;
        super(...arguments);
        this.adminOnly = true;
    }
    // ---------------------------------------------------------------------------
    // Lazy-loaded dependencies
    // ---------------------------------------------------------------------------
    get uiCloudPvd() {
        if (this._uiCloudPvd === undefined)
            this._uiCloudPvd = this.ioc.get(cloud_provider_contextualizer_1.CloudProviderContextualizer);
        return this._uiCloudPvd;
    }
    get termUtils() {
        if (this._termUtils === undefined)
            this._termUtils = this.ioc.get(terminal_utils_1.TerminalUtils);
        return this._termUtils;
    }
    get localCommandRunner() {
        if (this._localCommandRunner === undefined)
            this._localCommandRunner = this.ioc.get(local_command_runner_1.LocalCommandRunner);
        return this._localCommandRunner;
    }
    // ---------------------------------------------------------------------------
    // Bootstrap
    // ---------------------------------------------------------------------------
    bootstrap() {
        super.bootstrap();
        cloud_provider_contextualizer_1.CloudProviderContextualizer.bootstrap(this.ioc);
        this.ioc.bind(terminal_utils_1.TerminalUtils).toDynamicValue(() => new terminal_utils_1.TerminalUtils(this.logger));
        this.ioc.bind(local_command_runner_1.LocalCommandRunner).toDynamicValue(() => new local_command_runner_1.LocalCommandRunner(this.msgClient, this.cliEnviro, this.termUtils, this.logger));
    }
    // ---------------------------------------------------------------------------
    // Lifecycle methods
    // ---------------------------------------------------------------------------
    captureArgs() {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { args, flags } = yield this.parse(ClusterBootstrap);
            let partial = {
                name: args.name,
                provider: this.stringToEnum(contracts_2.CloudProviderEnum, flags.provider),
                providerSubscription: flags.providerSubscription,
                providerGroup: flags.resourceGroup,
                locationName: flags.location,
                nodeSpec: flags.nodeSpec,
                startingNodeCount: flags.nodeQty,
                k8sVersion: flags.k8sVersion,
                nodesMin: flags.nodesMin,
                nodesMax: flags.nodesMax,
                subnet: flags.subnet,
                networkPlugin: flags.networkPlugin,
            };
            if (args.name) {
                partial.identitySpec = {
                    type: cluster_def_1.ClusterDefConstants.DEFAULT_IDENTITY_TYPE,
                    name: cluster_def_1.ClusterDefConstants.buildDefaultIdentityName(args.name),
                };
            }
            if (flags.identityUserId && flags.identityUserName) {
                partial.identityInstance = new security_account_1.SecurityAccount(flags.identityUserId, flags.identityUserName, flags.identityUserName, flags.identityUserPassword);
            }
            const takeDefaults = (_a = flags['take-defaults']) !== null && _a !== void 0 ? _a : false;
            return [partial, takeDefaults];
        });
    }
    buildCandidate_silently(partial, rootMap) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            partial.privateOwner = this.ioc.get(codiac_ops_common_1.UserNameTenantCode).tenantCode;
            partial.identitySpec = { type: contracts_1.SecurityAccountTypeEnum.servicePrincipal, name: '' };
            partial.ingressDef = undefined;
            partial.nodeCount = undefined;
            if (!partial.name)
                throw new Error('Cluster name must be provided.');
            if (!partial.provider)
                throw new Error('Provider must be provided.');
            if (!partial.providerSubscription)
                throw new Error('Provider subscription must be provided.');
            if (!partial.locationName)
                throw new Error('Location must be provided.');
            if (!partial.nodeSpec)
                throw new Error('Node spec must be provided.');
            if (!partial.startingNodeCount)
                throw new Error('Node quantity must be provided.');
            if (!partial.providerGroup)
                throw new Error('Resource group must be provided.');
            if (!partial.k8sVersion)
                throw new Error('Kubernetes version must be provided.');
            return partial;
        });
    }
    buildCandidate_interactively(partial, takeDefaults, rootMap) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let confirmed = false;
            do {
                this._ui.userMessage(this._styler.bold('Bootstrap Cluster:'));
                const allPvds = this.cspUiFactory.getAllClusterProviders(this.ioc);
                partial.provider = yield this.uiCloudPvd.promptForCloudProvider(allPvds, partial.provider);
                const cspUser = yield this.uiCloudPvd.promptForCloudProviderCreds(this.ioc, partial.provider, undefined, undefined, undefined, partial.providerSubscription);
                if (partial.providerSubscription == undefined && cspUser.subscriptionId) {
                    partial.providerSubscription = cspUser.subscriptionId;
                }
                const regCfg = this.cspUiFactory.create(partial.provider, this.ioc);
                const def = yield regCfg.defineCluster(partial, undefined, cspUser.cspTenantId);
                partial = def.spec;
                if (typeof partial.startingNodeCount === 'string') {
                    partial.startingNodeCount = Number.parseInt(partial.startingNodeCount);
                }
                console.clear();
                this._ui.userMessage((0, prettify_1.prettify)(partial, [(_a = partial.identityInstance) === null || _a === void 0 ? void 0 : _a.password]));
                this._ui.userMessage('Codiac will: provision this cluster, install the Codiac agent, then install the default cluster stack.');
                confirmed = yield this._ui.promptConfirm('confirmBootstrap', `Continue? (hit 'N' to edit your input)`, false);
            } while (!confirmed);
            partial.privateOwner = this.ioc.get(codiac_ops_common_1.UserNameTenantCode).tenantCode;
            partial.identitySpec = { type: contracts_1.SecurityAccountTypeEnum.servicePrincipal, name: '' };
            return partial;
        });
    }
    buildCommandString(candidate, cmd) {
        cmd.push(candidate.name);
        cmd.push('--provider', candidate.provider);
        cmd.push('--providerSubscription', candidate.providerSubscription);
        cmd.push('--resourceGroup', candidate.providerGroup);
        cmd.push('--location', candidate.locationName);
        cmd.push('--nodeSpec', candidate.nodeSpec);
        cmd.push('--nodeQty', candidate.startingNodeCount.toString());
        if (candidate.nodesMin != undefined)
            cmd.push('--nodesMin', candidate.nodesMin.toString());
        if (candidate.nodesMax != undefined)
            cmd.push('--nodesMax', candidate.nodesMax.toString());
        if (candidate.k8sVersion)
            cmd.push('--k8sVersion', candidate.k8sVersion);
        if (candidate.networkPlugin)
            cmd.push('--networkPlugin', candidate.networkPlugin);
        if (candidate.subnet && !_.isEmpty(candidate.subnet)) {
            (typeof candidate.subnet === 'string' ? [candidate.subnet] : candidate.subnet)
                .forEach(s => cmd.push('--subnet', s));
        }
        if (candidate.identityInstance) {
            cmd.push('--identityUserName', candidate.identityInstance.name);
            cmd.push('--identityUserId', candidate.identityInstance.id);
            if (candidate.identityInstance.password)
                cmd.push('--identityUserPassword', candidate.identityInstance.password);
        }
        cmd.push('--silent');
        return cmd;
    }
    executeTask(candidate) {
        var _a, _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                if (candidate.provider === contracts_2.CloudProviderEnum.localMachine) {
                    yield this.msgClient.connect();
                    this.localCommandRunner.listen();
                }
                const result = yield this.watcher.triggerAndWatch((correlationId, socketId) => this.genRelayClient.Call.clusterCreateAsync({
                    spec: candidate,
                    socketId,
                    skipInit: false,
                    correlationId,
                }));
                if (result.error) {
                    this.logger.error(`Error bootstrapping cluster [${candidate.name}]: ${(_a = result.message) !== null && _a !== void 0 ? _a : ''}; ${result.error.message}`, result.error);
                    this._ui.userMessage(`${this._styler.colorCritical('ERROR!')} Error bootstrapping cluster [${candidate.name}]: ${(_b = result.message) !== null && _b !== void 0 ? _b : ''}; ${result.error.message}`);
                }
                else if (!result.success) {
                    this.logger.notice(`Cluster bootstrap failed. ${result.message}`);
                    this._ui.userMessage(`Cluster bootstrap failed. ${result.message}`);
                }
                else {
                    const msg = `${this._styler.colorGood('SUCCESS!')} Cluster [${this._styler.hilite(candidate.name)}] bootstrapped successfully.`;
                    this.logger.notice(msg, result);
                    this._ui.userMessage(msg);
                }
            }
            catch (err) {
                throw err;
            }
        });
    }
    stringToEnum(enumObj, str) {
        if (str === undefined)
            return undefined;
        return Object.values(enumObj).includes(str) ? str : undefined;
    }
}
exports.default = ClusterBootstrap;
ClusterBootstrap.description = 'Provisions a new cluster, installs the Codiac agent, and installs the default cluster stack in a single operation. ' +
    'This is the recommended path for standing up a new cluster. ' +
    'Use `cod cluster provision`, `cod cluster agent install`, and `cod cluster stack install` separately if you need control over individual steps.';
ClusterBootstrap.aliases = [];
ClusterBootstrap.examples = [
    {
        description: 'Bootstrap a cluster interactively.',
        command: '<%= config.bin %> <%= command.id %>',
    },
    {
        description: 'Bootstrap an Azure cluster non-interactively.',
        command: '<%= config.bin %> <%= command.id %> my-cluster --provider azure --providerSubscription <sub-id> --resourceGroup my-rg --location eastus --nodeSpec Standard_D4s_v3 --nodeQty 3 --k8sVersion 1.29.0 --silent',
    },
    {
        description: 'Renders the equivalent silent command and then executes.',
        command: '<%= config.bin %> <%= command.id %> my-cluster --echo',
    },
    {
        description: 'Renders the equivalent silent command without executing.',
        command: '<%= config.bin %> <%= command.id %> my-cluster --to-script',
    },
];
ClusterBootstrap.flags = {
    help: core_1.Flags.help({ char: 'h' }),
    provider: core_1.Flags.string({
        options: [contracts_2.CloudProviderEnum.azure, contracts_2.CloudProviderEnum.aws, contracts_2.CloudProviderEnum.localMachine],
        description: 'Cloud service provider on which to provision the cluster.',
    }),
    providerSubscription: core_1.Flags.string({
        char: 's',
        description: 'The cloud provider subscription Id (Azure) or account Id (AWS) under which to create the cluster.',
    }),
    resourceGroup: core_1.Flags.string({
        char: 'g',
        description: '(Azure only) The resource group to create for the cluster.',
    }),
    location: core_1.Flags.string({
        char: 'l',
        description: 'The short name of the regional data center in which the cluster is to reside.',
    }),
    nodeSpec: core_1.Flags.string({
        char: 'n',
        description: 'The VM size to use for cluster nodes.',
    }),
    nodeQty: core_1.Flags.integer({
        char: 'q',
        default: 2,
        description: 'Starting node count for the cluster.',
    }),
    k8sVersion: core_1.Flags.string({
        description: 'The version of Kubernetes to install on the cluster.',
    }),
    nodesMin: core_1.Flags.integer({
        description: 'Minimum number of nodes for autoscaling.',
    }),
    nodesMax: core_1.Flags.integer({
        description: 'Maximum number of nodes for autoscaling.',
    }),
    subnet: core_1.Flags.string({
        char: 'k',
        multiple: true,
        description: '(Optional) Resource Id of the subnet in which to create the cluster.',
    }),
    networkPlugin: core_1.Flags.string({
        description: '(Optional) Network plugin for the cluster.',
        options: ['kubenet', 'azure'],
    }),
    identityUserName: core_1.Flags.string({
        char: 'u',
        description: '(Optional) Name of the Service Principal or Managed Identity to execute as.',
    }),
    identityUserId: core_1.Flags.string({
        description: '(Optional) Id of the Service Principal or Managed Identity to execute as.',
    }),
    identityUserPassword: core_1.Flags.string({
        char: 'c',
        hidden: true,
        description: '(Optional) Password of the Service Principal or Managed Identity to execute as.',
    }),
    // Interaction mode flags
    silent: core_1.Flags.boolean({
        exclusive: ['echo', 'to-script'],
        description: 'Executes without any user interaction; fails on missing or invalid arguments.',
        relationships: [
            { type: 'all', flags: ['k8sVersion', 'provider', 'providerSubscription', 'location', 'nodeSpec', 'nodeQty', 'resourceGroup'] },
        ],
    }),
    echo: core_1.Flags.boolean({
        exclusive: ['to-script', 'silent'],
        description: 'Renders the equivalent non-interactive command for future use before executing.',
    }),
    'to-script': core_1.Flags.boolean({
        exclusive: ['echo', 'silent'],
        description: 'Renders the equivalent non-interactive command without executing it.',
    }),
    'take-defaults': core_1.Flags.boolean({
        exclusive: ['silent'],
        description: 'Prevents prompting for confirmation on parameters that were passed in or automatically set to default values.',
    }),
};
ClusterBootstrap.args = {
    name: core_1.Args.string({ description: 'Name of the cluster to provision.' }),
};
//# sourceMappingURL=bootstrap.js.map