UNPKG

portainer-backup

Version:

Utility for scripting or scheduling scheduled backups for Portainer

170 lines (151 loc) 6.14 kB
/** * ------------------------------------------------------------------- * ___ ___ ___ ___ ___ * / __| /_\ \ / /_\ / __| __| * \__ \/ _ \ V / _ \ (_ | _| * |___/_/_\_\_/_/_\_\___|___| ___ ___ ___ * / __|/ _ \| __|_ _\ \ / /_\ | _ \ __| * \__ \ (_) | _| | | \ \/\/ / _ \| / _| * |___/\___/|_| |_| \_/\_/_/ \_\_|_\___| * * ------------------------------------------------------------------- * COPYRIGHT SAVAGESOFTWARE,LLC, @ 2022, ALL RIGHTS RESERVED * https://github.com/SavageSoftware/portainer-backup * ------------------------------------------------------------------- */ // import libraries import axios from 'axios'; import fs from 'node:fs'; /** * This class is responsible for managing access * and communication with a Portainer server. */ export class Portainer { // minimum supported portainer server version static MIN_VERSION = "2.11.0"; // relative API URL paths static URL = { STATUS: "/api/status", BACKUP: "/api/backup", STACKS: "/api/stacks" } /** * Default Constructor * * @param {*} context */ constructor(context) { this.context = context; this.config = context.config; } /** * Get portainer status (version and instance id) from portainer server. * * @returns Promise with JSON object that contains "Version" and "InstanceID" elements. * exmaple: { * Version: '2.11.1', * InstanceID: '8d98af6e-8908-4d5c-80f0-11b8e6272219' * } */ status() { return new Promise((resolve, reject) => { const url = new URL(Portainer.URL.STATUS, this.config.portainer.baseUrl).toString(); // next, get Portainer status via status API (no access token required for this call) axios.get(url) // handle success (200<OK>) on status request .then((response) => { resolve(response.data); }) // handle error (!200) on status request .catch((err) => { reject(err); }); }); } /** * Download portainer data backup archive file from portainer server. * * @returns Promise with HTTP response stream data from backup file request. */ backup() { return new Promise((resolve, reject) => { const url = new URL(Portainer.URL.BACKUP, this.config.portainer.baseUrl).toString(); // include API TOKEN in request header; identify expected stream response const options = { headers: { 'X-API-Key': `${this.config.portainer.token}`}, responseType: 'stream', }; // create request payload; include optional backup protection password let payload = { password: (this.config.backup.password) ? this.config.backup.password : "" }; // execute API call to perform backup of portainer data // (this will generate a data stream which includes a TAR.GZ archive file) axios.post(url, payload, options) // handle success (200<OK>) on status request .then((response) => { return resolve(response); }) // handle error (!200) on status request .catch((err) => { return reject(err); }); }); } /** * Save stream data to backup file (async). * * @param {*} stream input stream of data to save to backup file * @param {*} file target file to save backup archive to. * @returns Promise with BACKUP FILE that was written to filesystem. */ saveBackup(stream, file) { return new Promise((resolve, reject) => { // write downlaoded backup file to file system const writer = fs.createWriteStream(file) // pipe file data from response stream to file writer stream.pipe(writer); // return promise on file completion or error writer.on('finish', ()=>{ resolve(file); }); writer.on('error', reject); }); } /** * Get stacks metadata/catalog from portainer server. * @returns Promise with stacks metadata/catalog obtained from portainer server. */ stacksMetadata() { return new Promise((resolve, reject) => { const url = new URL(Portainer.URL.STACKS, this.config.portainer.baseUrl).toString(); axios.get(url, {headers: { 'X-API-Key': `${this.config.portainer.token}`}}) // handle success (200<OK>) on status request .then((response) => { return resolve(response.data); }) // handle error (!200) on status request .catch((err) => { return reject(err); }); }); } /** * Get stack docker-compose file for the given stack ID from the portainer server. * * @param {*} stackId target stack to acquire * @returns docker-compose data for the requested stack ID. */ stackFile(stackId) { return new Promise((resolve, reject) => { const url = new URL(Portainer.URL.STACKS, this.config.portainer.baseUrl).toString(); axios.get(url + `/${stackId}/file`, {headers: { 'X-API-Key': `${this.config.portainer.token}`}}) // handle success (200<OK>) on status request .then((response) => { return resolve(response.data); }) // handle error (!200) on status request .catch((err) => { return reject(err); }); }); } }