#0 - Implement backup #1
3 changed files with 167 additions and 3 deletions
11
.editorconfig
Normal file
11
.editorconfig
Normal file
|
@ -0,0 +1,11 @@
|
|||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
max_line_length = 80
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
|
@ -1,11 +1,162 @@
|
|||
import { GluegunCommand } from 'gluegun'
|
||||
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
|
||||
const {print} = toolbox
|
||||
|
||||
print.info('Welcome to your CLI')
|
||||
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);
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { GluegunToolbox } from 'gluegun'
|
||||
|
||||
|
||||
// Example command, to be rmeoved
|
||||
module.exports = {
|
||||
name: 'generate',
|
||||
alias: ['g'],
|
||||
|
|
Loading…
Reference in a new issue