add sqlite3 addon take 2

- there is no container id during the addon lifecycle
- sqlite3 requires the localstorage addon to be inited. so this has to
  become like the ftp option
- remove all that child_process streaming stuff. too complicated
This commit is contained in:
Girish Ramakrishnan
2024-11-20 23:26:43 +05:30
parent f8cd0b5f52
commit 4316d3eade
3 changed files with 58 additions and 112 deletions
+53 -107
View File
@@ -42,7 +42,6 @@ const addonConfigs = require('./addonconfigs.js'),
assert = require('assert'),
blobs = require('./blobs.js'),
BoxError = require('./boxerror.js'),
child_process = require('child_process'),
constants = require('./constants.js'),
crypto = require('crypto'),
dashboard = require('./dashboard.js'),
@@ -106,8 +105,8 @@ const ADDONS = {
localstorage: {
setup: setupLocalStorage,
teardown: teardownLocalStorage,
backup: NOOP, // no backup because it's already inside app data
restore: NOOP,
backup: backupLocalStorage, // no backup because it's already inside app data
restore: restoreLocalStorage,
getDynamicEnvironment: NOOP,
clear: clearLocalStorage,
},
@@ -206,15 +205,7 @@ const ADDONS = {
restore: setupOidc,
getDynamicEnvironment: getDynamicEnvironmentOidc,
clear: NOOP,
},
sqlite: {
setup: setupSqlite,
teardown: NOOP,
backup: backupSqlite,
restore: restoreSqlite,
getDynamicEnvironment: NOOP,
clear: clearSqlite,
},
}
};
// services are actual containers that are running. addons are the concepts requested by app
@@ -912,6 +903,56 @@ async function teardownLocalStorage(app, options) {
if (error) throw new BoxError(BoxError.FS_ERROR, error);
}
async function backupSqlite(app, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
debug('Backing up sqlite');
const volumeDataDir = await apps.getStorageDir(app);
const cmd = `sqlite3 ${options.path} ".dump"`;
const runCmd = `docker run --rm --name=sqlite-${app.id} \
--net cloudron \
-v ${volumeDataDir}:/app/data \
--label isCloudronManaged=true \
--read-only -v /tmp -v /run ${infra.images.base} ${cmd} > ${dumpPath('sqlite', app.id)}`;
await shell.bash(runCmd, { encoding: 'utf8' });
}
async function backupLocalStorage(app, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
if (options.sqlite) await backupSqlite(app, options.sqlite);
}
async function restoreSqlite(app, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
debug('Restoring sqlite');
const volumeDataDir = await apps.getStorageDir(app);
const cmd = `sqlite3 ${options.path}`;
const runCmd = `docker run --rm --name=sqlite-${app.id} \
--net cloudron \
-v ${volumeDataDir}:/app/data \
--label isCloudronManaged=true \
--read-only -v /tmp -v /run ${infra.images.base} ${cmd} < ${dumpPath('sqlite', app.id)}`;
await shell.bash(runCmd, { encoding: 'utf8' });
}
async function restoreLocalStorage(app, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
if (options.sqlite) await restoreSqlite(app, options.sqlite);
}
async function setupTurn(app, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
@@ -1688,101 +1729,6 @@ async function restartMongodb() {
return await docker.restartContainer('mongodb');
}
async function setupSqlite(app, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
debug(`Setting up sqlite`);
await shell.spawn('docker', ['exec', '-t', app.containerId, 'sqlite3', options.path], {});
}
async function pipeProcessToFile(program, args, filename) {
assert.strictEqual(typeof program, 'string');
assert(Array.isArray(args));
assert.strictEqual(typeof filename, 'string');
return new Promise((resolve, reject) => {
const writeStream = fs.createWriteStream(filename);
const cp = child_process.spawn(program, args);
pipeline(cp.stdout, writeStream, (error) => {
if (error) return reject(new BoxError(BoxError.ADDONS_ERROR, `Error piping ${program} ${JSON.stringify(args)} to ${filename}: ${error.message}`));
resolve();
});
cp.on('close', function (code, signal) { // always called. after 'exit' or 'error'
if (code === 0) return; // program exited, we still have to wait for write stream to finish
const e = new BoxError(BoxError.SHELL_ERROR, `${program} exited with code ${code} signal ${signal}`);
e.code = code;
e.signal = signal;
debug(`${program} ${args.join(' ').replace(/\n/g, '\\n')} errored`, e);
reject(e);
});
cp.on('error', function (error) { // when the command itself could not be started
debug(`${program} ${args.join(' ').replace(/\n/g, '\\n')} errored`, error);
});
});
}
async function backupSqlite(app, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
debug('Backing up sqlite');
await pipeProcessToFile('docker', ['exec', '-t', app.containerId, 'sqlite3', options.path, '.dump'], dumpPath('sqlite', app.id));
}
async function pipeFileToProcess(filename, program, args) {
assert.strictEqual(typeof url, 'string');
assert.strictEqual(typeof program, 'string');
assert(Array.isArray(args));
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(filename);
const cp = child_process.spawn(program, args);
pipeline(readStream, cp.stdin, function (error) {
if (error) return reject(new BoxError(BoxError.ADDONS_ERROR, `Error piping ${filename} to ${program} ${JSON.stringify(args)}: ${error.message}`));
resolve();
});
cp.on('close', function (code, signal) { // always called. after 'exit' or 'error'
if (code === 0) return; // program exited, we still have to wait for write stream to finish
const e = new BoxError(BoxError.SHELL_ERROR, `${program} exited with code ${code} signal ${signal}`);
e.code = code;
e.signal = signal;
debug(`${program} ${args.join(' ').replace(/\n/g, '\\n')} errored`, e);
reject(e);
});
cp.on('error', function (error) { // when the command itself could not be started
debug(`${program} ${args.join(' ').replace(/\n/g, '\\n')} errored`, error);
});
});
}
async function restoreSqlite(app, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
debug('Restoring sqlite');
await pipeFileToProcess(dumpPath, 'docker', ['exec', '-t', app.containerId, 'sqlite3', dumpPath('sqlite', app.id)]);
}
async function clearSqlite(app, options) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
debug(`Clearing sqlite`);
await shell.spawn('docker', ['exec', '-t', app.containerId, 'rm', '-f', options.path], {});
await shell.spawn('docker', ['exec', '-t', app.containerId, 'sqlite3', options.path], {});
}
async function startGraphite(existingInfra) {
assert.strictEqual(typeof existingInfra, 'object');