@liara/cli
Version:
The command line interface for Liara
185 lines (184 loc) • 6.91 kB
JavaScript
import ora from 'ora';
import inquirer from 'inquirer';
import { Flags } from '@oclif/core';
import Command from '../../base.js';
import { IAAS_API_URL } from '../../constants.js';
import { checkVMNameRegexPattern, } from '../../utils/name-regex.js';
import { createDebugLogger } from '../../utils/output.js';
class VmCreate extends Command {
constructor() {
super(...arguments);
this.SSHConfirmationCount = 0;
}
async setGotConfig(config) {
await super.setGotConfig(config);
this.got = this.got.extend({
prefixUrl: IAAS_API_URL,
});
}
async run() {
const { flags } = await this.parse(VmCreate);
const debug = createDebugLogger(flags.debug);
this.spinner = ora();
await this.setGotConfig(flags);
try {
const oss = await this.getOperatingSystems();
const vmName = await this.promptVMName();
const osName = await this.promptOperatingSystems(oss);
const osVersion = await this.promptOperatingSystemVersions(osName, oss);
const plan = await this.promptPlan();
const SSHKeys = await this.promptSSHKey();
const newSSH = SSHKeys.length > 0
? {
config: {
SSHKeys: SSHKeys,
},
}
: null;
const newVM = {
OS: `${osName}-${osVersion}`,
name: vmName,
plan,
...newSSH,
};
await this.got.post('vm', {
json: newVM,
});
if (flags.detach) {
this.spinner.succeed(`vm "${vmName}" creation request submitted successfully in detach mode. Use "liara vm info" to view connection details.`);
return;
}
this.spinner.start(`creating vm "${vmName}"...`);
const intervalID = setInterval(async () => {
const vmState = await this.getVmState(vmName);
if (vmState === 'CREATED') {
this.spinner.succeed(`vm "${vmName}" is created successfully. Use "liara vm info" to view connection details.`);
clearInterval(intervalID);
}
}, 2000);
}
catch (error) {
debug(error.message);
if (error.response && error.response.data) {
debug(JSON.stringify(error.response.data));
}
if (error.response && error.response.statusCode === 409) {
this.error(`A vm with this name already exists. Please choose a unique name.`);
}
throw error;
}
}
async getOperatingSystems() {
this.spinner.start('Loading...');
try {
const oss = await this.got('oss').json();
this.spinner.stop();
return oss;
}
catch (error) {
this.spinner.stop();
throw error;
}
}
async promptOperatingSystems(oss) {
const { os } = (await inquirer.prompt({
name: 'os',
type: 'list',
message: 'Select an OS:',
choices: [...Object.keys(oss).map((platform) => platform)],
}));
return os;
}
async promptOperatingSystemVersions(osName, oss) {
const { osVersion } = (await inquirer.prompt({
name: 'osVersion',
type: 'list',
message: `Select the ${osName[0].toUpperCase() + osName.slice(1)} version:`,
choices: [...oss[osName].map((version) => version)],
}));
return osVersion;
}
async promptVMName() {
const { vmName } = (await inquirer.prompt({
message: 'Enter vm name: ',
type: 'input',
name: 'vmName',
validate: (input) => input.length > 3 && input.length < 20,
}));
if (!checkVMNameRegexPattern(vmName)) {
this.error('Invalid vm name. It must start with a lowercase letter, contain only lowercase letters, numbers, or hyphens, and be between 4 and 19 characters long.');
}
return vmName;
}
async promptPlan() {
this.spinner.start('Loading...');
try {
const { plans } = await this.got('plans').json();
this.spinner.stop();
const { plan } = (await inquirer.prompt({
name: 'plan',
type: 'list',
message: 'Please select a plan:',
choices: Object.keys(plans).map((planKey) => {
const planDetails = plans[planKey];
const { RAM, CPU, volume, monthlyPrice } = planDetails;
const ram = `RAM: ${RAM.amount}${' '.repeat(4 - RAM.amount.toString().length)} GB`;
const cpu = `CPU: ${CPU.amount}${' '.repeat(4 - CPU.amount.toString().length)} Core${CPU.amount > 1 ? 's' : ' '}`;
const disk = `Disk: ${volume}${' '.repeat(4 - volume.toString().length)} GB`;
const price = `Price: ${monthlyPrice.toLocaleString()}${' '.repeat(10 - monthlyPrice.toLocaleString().length)} Tomans/Month`;
return {
value: planKey,
name: `${ram.padEnd(8)} | ${cpu.padEnd(10)} | ${disk.padEnd(8)} | ${price}`,
};
}),
}));
return plan;
}
catch (error) {
this.spinner.stop();
throw error;
}
}
async promptSSHKey() {
const SSHKeys = [];
while (await this.SSHKeyConfirmation()) {
const { SSHKey } = (await inquirer.prompt({
message: 'Enter SSH Key: ',
name: 'SSHKey',
type: 'input',
validate: (input) => input.length !== 0,
}));
SSHKeys.push(SSHKey);
}
return SSHKeys;
}
async SSHKeyConfirmation() {
const { shouldContinue } = (await inquirer.prompt({
message: `${this.SSHConfirmationCount == 0 ? 'Add SSH Key?' : 'Add another SSH Key?'} (Default: No)`,
type: 'confirm',
default: false,
name: 'shouldContinue',
}));
if (shouldContinue)
this.SSHConfirmationCount++;
return shouldContinue;
}
async getVmState(vmName) {
const { vms } = await this.got('vm').json();
const vm = vms.filter((vm) => vm.name === vmName)[0];
return vm.state;
}
}
VmCreate.flags = {
...Command.flags,
vm: Flags.string({
char: 'v',
description: 'vm name',
}),
detach: Flags.boolean({
char: 'd',
description: 'run command in detach mode',
}),
};
VmCreate.description = 'create a vm';
export default VmCreate;