Reviewed-on: https://git.van-hemmen.com///GuillaumeHemmen/docker-volume-manager/pulls/1 Co-authored-by: Guillaume "B.B." Van Hemmen <GuillaumeHemmen@noreply.git.van-hemmen.com> Co-committed-by: Guillaume "B.B." Van Hemmen <GuillaumeHemmen@noreply.git.van-hemmen.com>
This commit is contained in:
parent
a27fca4dea
commit
4430f6f844
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 = {
|
const command: GluegunCommand = {
|
||||||
name: 'docker-volume-manager',
|
name: 'docker-volume-manager',
|
||||||
run: async (toolbox) => {
|
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'
|
import { GluegunToolbox } from 'gluegun'
|
||||||
|
|
||||||
|
|
||||||
|
// Example command, to be rmeoved
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'generate',
|
name: 'generate',
|
||||||
alias: ['g'],
|
alias: ['g'],
|
||||||
|
|
Loading…
Reference in a new issue