UNPKG

@containernerds/incus-client

Version:

A Node.js client for automating Incus (LXD) servers.

176 lines (160 loc) 5.83 kB
import axios, { AxiosInstance } from 'axios'; import https from 'https'; import fs from 'fs'; import { InstanceSource, InstanceSpec, defaultInstanceSource, InstanceRunningState } from '../types'; import { asyncPoll } from '../utils'; export const createInstanceSource = (overrides: InstanceSource): InstanceSource => { return { ...defaultInstanceSource, ...overrides, }; }; export const createInstanceSpec = (overrides: InstanceSpec): InstanceSpec => { return { architecture: 'x86_64', config: {}, devices: {}, ephemeral: false, instance_type: 't1.micro', profiles: ['default'], restore: '', source: createInstanceSource({ alias: 'ubuntu/22.04' }), start: true, stateful: false, type: 'container', ...overrides, }; }; export class InstancesAPI { private client: AxiosInstance; private defaultInstanceSource: InstanceSource; private project: string; constructor(baseURL: string, httpsAgent: https.Agent, project: string) { this.client = axios.create({ baseURL, httpsAgent}); this.defaultInstanceSource = { allow_inconsistent: false, instance_only: false, live: false, mode: 'pull', protocol: 'simplestreams', server: 'https://images.linuxcontainers.org', type: 'image', }; this.project = project; }; async listInstances() { const response = await this.client.get(`/1.0/instances?project=${this.project}`); return response.data; }; async createInstance(instanceSpec: InstanceSpec) { let instanceSource: InstanceSource = { ...this.defaultInstanceSource, }; const response = await this.client.post(`/1.0/instances?project=${this.project}`, { ...this.defaultInstanceSource, ...instanceSpec }); return response.data; }; // TODO: NEED TO TEST THIS, NOT SURE IF IT WORKS async createInstanceFromBackup(backupFile: string) { const backup = fs.readFileSync(backupFile); const response = await this.client.post(`/1.0/instances?project=${this.project}`, { backup, }); return response.data; }; /** * Gets a specific instance (basic struct). * * @param {string} instanceName * @returns {Promise<any>} A promise that resolves with the response data. * @throws {Error} If the request fails. */ async getInstance(instanceName: string) { const response = await this.client.get(`/1.0/instances/${instanceName}?project=${this.project}`); return response.data; }; /** * Deletes an instance by its name. * This also deletes anything owned by the instance such as snapshots and backups. * * @param {string} instanceName - The name of the instance to delete. * @returns {Promise<any>} A promise that resolves with the response data. * @throws {Error} If the request fails. */ async deleteInstance(instanceName: string, force: boolean = false): Promise<any> { if (force) { await this.changeInstanceState(instanceName, InstanceRunningState.STOP, force); }; const response = await this.client.delete(`/1.0/instances/${instanceName}?project=${this.project}`); return response.data; }; /** * Gets the state of an instance. * * @param {string} instanceName - The name of the instance to get the state of. * @returns {Promise<any>} A promise that resolves with the response data. * @throws {Error} If the request fails. */ async getInstanceState(instanceName: string) { const response = await this.client.get(`/1.0/instances/${instanceName}/state?project=${this.project}`); return response.data; }; /** * Changes the state of an instance. * * @param {string} instanceName - The name of the instance to change the state of. * @param {string} state - The state to change the instance to. * @param {boolean} force - Whether to force the state change. * @param {boolean} stateful - Whether to perform a stateful change. * @param {number} timeout - The time to wait for the state change to complete. * @returns {Promise<any>} A promise that resolves with the response data. * @throws {Error} If the request fails. */ async changeInstanceState( instanceName: string, state: string, force: boolean = false, stateful: boolean = false, timeout: number = 30 ): Promise<any> { const response = await this.client.put(`/1.0/instances/${instanceName}/state?project=${this.project}`, { action: state, force, stateful, timeout, }); // stop:Stopped // start:Running // restart:Running // TODO support freeze, unfreeze, migrate, snapshot, restore, delete const linked: any = { 'stop': 'Stopped', 'start': 'Running', 'restart': 'Running', } if (response.status === 202) { const pollForStatus = await asyncPoll(() => { const res = this.getInstanceState(instanceName).then((data) => { if (data.metadata.status === linked[state]) { return true; } else { return false; }; }).catch((error) => { return false; }); return res; }, 5, 5000); return pollForStatus; } else { return response.data; } }; };