#0000 - Add docker-volume-manager command and .editorconfig

Introduces `docker-volume-manager` with functionalities to backup and restore Docker volumes. Also adds an .editorconfig file to ensure consistent coding styles across the project.
This commit is contained in:
Guillaume "B.B." Van Hemmen 2024-08-08 06:51:27 +02:00
parent a27fca4dea
commit fd7c4e87e7
Signed by: GuillaumeHemmen
GPG key ID: 7921E64524F7EB35
3 changed files with 171 additions and 3 deletions

11
.editorconfig Normal file
View 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

View file

@ -1,11 +1,166 @@
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}...`)
// improvement: display progress
// see: https://superuser.com/questions/168749/is-there-a-way-to-see-any-tar-progress-per-file
await system.run(`docker run --rm -v ${volume}:/volume -v ${workingDir}:/tmp alpine sh -c "cd /volume && tar -z -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
// @ts-ignore
const workingFolder = await getAndValidateBackupFolder(toolbox);
// 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);
},
}

View file

@ -1,5 +1,7 @@
import { GluegunToolbox } from 'gluegun'
// Example command, to be rmeoved
module.exports = {
name: 'generate',
alias: ['g'],