import {GluegunCommand, GluegunFilesystem} from 'gluegun' import * as process from "process"; import {Toolbox} from "gluegun/build/types/domain/toolbox"; const checkForDocker = async (toolbox: Toolbox) => { const {system, print} = toolbox; print.divider() print.info('Checking if the system has docker...'); const spinner = print.spin('running docker --version'); try { await system.run('docker --version'); spinner.succeed('Docker is installed on your system!'); print.info('Checking if current user can run docker...'); spinner.start('running docker ps'); try { await system.run('docker ps'); spinner.succeed('You can run docker!'); } catch (dockerSudoError) { spinner.fail('You are not allowed to run docker, please try again with an allowed user (did you forgot to run the command with sudo?).') process.exit(2) } } catch (dockerVersionError) { spinner.fail('Docker is not installed on your system!') process.exit(1) } } const isValidPath = (path: string, filesystem: GluegunFilesystem) => filesystem.exists(path) && path !== '' && path !== '/' const getAndValidateBackupFolder = async (toolbox: Toolbox): Promise<string> => { const {prompt, print, filesystem} = toolbox; print.divider(); let backupPath: string = '' do { const {pathResponse} = await prompt.ask({ type: 'input', name: 'pathResponse', message: 'Please, enter the absolute path to the folder containing the backups' }) backupPath = pathResponse if (!isValidPath(backupPath, filesystem)) { print.error(`${backupPath} is not a valid path! Please try again.`) print.newline() } } while (!isValidPath(backupPath, filesystem)) return backupPath; } const selectAction = async (toolbox: Toolbox): Promise<string> => { const {prompt, print} = toolbox; print.divider(); const {action} = await prompt.ask({ type: 'select', name: 'action', message: 'Select an action:', choices: ['Backup volume', 'Restore volumes', 'exit'], }); return action; } const listAndSelectDockerVolumes = async (toolbox: Toolbox): Promise<string[]> => { const {system, prompt, print} = toolbox; print.divider(); const spinner = print.spin('running docker volume ls'); try { const volumes = await system.run('docker volume ls --format "{{.Name}}"'); spinner.succeed('Volumes listed!'); const volumeList = volumes.split('\n').filter((volume: string) => volume !== ''); const {selectedVolumes} = await prompt.ask({ type: 'multiselect', name: 'selectedVolumes', message: 'Select the volumes you want to backup:', choices: volumeList, }) as { selectedVolumes: string[] }; return selectedVolumes; } catch (dockerVolumeError) { spinner.fail('Failed to list docker volumes!') process.exit(3) } } const runVolumeBackup = async (toolbox: Toolbox, workingDir: string, volumes: string[]) => { const {print, system} = toolbox; print.divider(); print.info('starting backup process'); print.newline(); const spinner = print.spin(); for (const volume of volumes) { try { spinner.start(`backing up volume ${volume}...`) await system.run(`docker run --rm -v ${volume}:/volume -v ${workingDir}:/tmp alpine sh -c "cd /volume && tar -c -p -f /tmp/${volume}.tar ."`); spinner.succeed(`volume ${volume} backed up!`) } catch (error) { spinner.fail(`volume ${volume} backup failed!`) print.error(error); print.newline(); } } } const command: GluegunCommand = { name: 'docker-volume-manager', run: async (toolbox) => { const {print} = toolbox print.info('####################################') print.info('# Welcome to docker-volume-manager #') print.info('####################################') // check if docker is present on your system await checkForDocker(toolbox); // select action const choice = await selectAction(toolbox); if (choice === 'Backup volume') { // select the backup location folder // @ts-ignore const workingFolder = await getAndValidateBackupFolder(toolbox); // list and select the volumes const volumes = await listAndSelectDockerVolumes(toolbox); // run the backup await runVolumeBackup(toolbox, workingFolder, volumes); print.success('Your volume have been backed up!') } else if (choice === 'Restore volumes') { // select the backup location folder // list and select the archives to restore // run the restore print.success('your backup have been restored!'); } else if (choice === 'exit') { process.exit(0); } // should never happen process.exit(999); }, } module.exports = command