@ps-aux/nodebup 0.9.1 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
@@ -26,7 +26,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
26
26
|
const parseConnectionUrl = url => {
|
27
27
|
var _url$match;
|
28
28
|
|
29
|
-
|
29
|
+
// TODO what if there are query params in the end?
|
30
|
+
const regex = /postgres:\/\/(?<username>.*):(?<password>.*)@(?<host>.*):(?<port>\d*)(\/(?<database>.*))?$/;
|
30
31
|
const match = (_url$match = url.match(regex)) === null || _url$match === void 0 ? void 0 : _url$match.groups;
|
31
32
|
if (!match || !match.username || !match.password || !match.host || !match.port) throw new Error(`The Postgres connection URL does not match required regex: ${regex.toString()}`);
|
32
33
|
return {
|
@@ -59,7 +60,7 @@ let PgBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.m
|
|
59
60
|
|
60
61
|
_defineProperty(this, "outFileName", 'backup.sql');
|
61
62
|
|
62
|
-
_defineProperty(this, "backup", ({
|
63
|
+
_defineProperty(this, "backup", async ({
|
63
64
|
pgUrl,
|
64
65
|
pgVersion
|
65
66
|
}) => {
|
@@ -69,16 +70,20 @@ let PgBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.m
|
|
69
70
|
this.log.info(`Backing up Postgres database, version=${version}`);
|
70
71
|
const connParams = parseConnectionUrl(pgUrl);
|
71
72
|
const pass = connParams.password;
|
72
|
-
this.fs.inTmpDir('pg-backup', dir => {
|
73
|
+
await this.fs.inTmpDir('pg-backup', async dir => {
|
73
74
|
const outputDir = _Path.AbsPath.from(dir);
|
74
75
|
|
75
76
|
this.log.info('Dumping database to a file');
|
76
77
|
const bashCmds = [`echo "*:*:*:*:${pass}" > ~/.pgpass`, `chmod 400 ~/.pgpass`, `pg_dumpall -d ${pgUrl}`]; // Don't forget that this itself might be run in docker
|
77
78
|
// therefore volume mounts are not usable (will apply to the location at host)
|
78
79
|
|
79
|
-
const b = this.sh.execAndReturnBuffer(`docker run --network host ` + `postgres:${version} ` + `bash -c '${bashCmds.join(' && ')}'`);
|
80
80
|
const dumpOut = outputDir.resolve(this.outFileName);
|
81
|
-
this.fs.
|
81
|
+
const f = this.fs.writeStream(dumpOut);
|
82
|
+
await this.sh.asyncExec(`docker run --rm --network host ` + `postgres:${version} ` + `bash -c '${bashCmds.join(' && ')}'`, // f.write
|
83
|
+
data => {
|
84
|
+
f.write(data);
|
85
|
+
});
|
86
|
+
f.end();
|
82
87
|
this.log.info('Compressing');
|
83
88
|
const zipOutputName = `pg-backup-${this.now()}.zip`;
|
84
89
|
this.zip.zipDir(outputDir, outputDir.resolve(zipOutputName));
|
@@ -88,7 +93,7 @@ let PgBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.m
|
|
88
93
|
});
|
89
94
|
});
|
90
95
|
|
91
|
-
_defineProperty(this, "restore", ({
|
96
|
+
_defineProperty(this, "restore", async ({
|
92
97
|
pgUrl,
|
93
98
|
pgVersion
|
94
99
|
}) => {
|
@@ -97,7 +102,7 @@ let PgBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.m
|
|
97
102
|
|
98
103
|
parseConnectionUrl(pgUrl);
|
99
104
|
this.log.info(`Restoring Postgres database, version=${version}`);
|
100
|
-
this.fs.inTmpDir('pg-restore', dirStr => {
|
105
|
+
await this.fs.inTmpDir('pg-restore', dirStr => {
|
101
106
|
const dir = _Path.AbsPath.from(dirStr);
|
102
107
|
|
103
108
|
storage.restore(dir); // TODO check if the dir contains the with the expected name
|
package/lib/fs/Fs.js
CHANGED
@@ -37,6 +37,11 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
|
|
37
37
|
_fs.default.writeFileSync(path.str(), content);
|
38
38
|
});
|
39
39
|
|
40
|
+
_defineProperty(this, "writeStream", path => {
|
41
|
+
this.log.debug('Writing into file', path);
|
42
|
+
return _fs.default.createWriteStream(path.str());
|
43
|
+
});
|
44
|
+
|
40
45
|
_defineProperty(this, "isFile", path => !this.isDir(path));
|
41
46
|
|
42
47
|
_defineProperty(this, "ensureIsFile", p => {
|
@@ -59,11 +64,11 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
|
|
59
64
|
return _fs.default.readdirSync(path.str()).map(f => path.resolve(f));
|
60
65
|
});
|
61
66
|
|
62
|
-
_defineProperty(this, "inTmpDir", (name, withDir) => {
|
67
|
+
_defineProperty(this, "inTmpDir", async (name, withDir) => {
|
63
68
|
const dir = this.mkTmpDir(name);
|
64
69
|
|
65
70
|
try {
|
66
|
-
withDir(dir);
|
71
|
+
await withDir(dir);
|
67
72
|
} finally {
|
68
73
|
this.rmDir(_Path.AbsPath.from(dir));
|
69
74
|
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
var _Fs = require("./Fs");
|
4
|
+
|
5
|
+
var _Path = require("./path/Path");
|
6
|
+
|
7
|
+
var _AppLogger = require("../log/AppLogger");
|
8
|
+
|
9
|
+
it.skip('withTmpDir', async () => {
|
10
|
+
const fs = new _Fs.Fs(_Path.AbsPath.from(__dirname), new _AppLogger.AppLogger());
|
11
|
+
await fs.inTmpDir('foo', async () => {
|
12
|
+
console.log('before');
|
13
|
+
const wait = new Promise((res, rej) => {
|
14
|
+
setTimeout(res, 1000);
|
15
|
+
});
|
16
|
+
await wait;
|
17
|
+
console.log('after');
|
18
|
+
});
|
19
|
+
console.log('done');
|
20
|
+
});
|
package/lib/tools/shell/Shell.js
CHANGED
@@ -11,13 +11,16 @@ var _inversify = require("inversify");
|
|
11
11
|
|
12
12
|
var _AppLogger = require("../../log/AppLogger");
|
13
13
|
|
14
|
+
var _child_process = require("child_process");
|
15
|
+
|
14
16
|
var _dec, _dec2, _dec3, _class;
|
15
17
|
|
16
18
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
17
19
|
|
18
20
|
// TODO copy pasted from my other project
|
21
|
+
// TODO don't log the full command as it contains secrets
|
19
22
|
let Shell = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger]), _dec(_class = _dec2(_class = _dec3(_class = class Shell {
|
20
|
-
constructor(log) {
|
23
|
+
constructor(log = (0, _AppLogger.logger)('shell')) {
|
21
24
|
this.log = log;
|
22
25
|
|
23
26
|
_defineProperty(this, "exec", (cmd, opts = {}) => {
|
@@ -38,19 +41,31 @@ let Shell = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("desi
|
|
38
41
|
});
|
39
42
|
|
40
43
|
_defineProperty(this, "execAndReturnString", (cmd, ops = {}) => {
|
41
|
-
this.log.debug(cmd, '[stdout consumed]')
|
44
|
+
// this.log.debug(cmd, '[stdout consumed]')
|
42
45
|
return (0, _shellCmd.shellCmd)(cmd, {
|
43
46
|
returnStdout: 'string',
|
44
47
|
...ops
|
45
48
|
});
|
46
49
|
});
|
47
50
|
|
48
|
-
_defineProperty(this, "
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
_defineProperty(this, "asyncExec", (cmd, onStdout, ops = {}) => {
|
52
|
+
let onDone;
|
53
|
+
let onError;
|
54
|
+
const p = new Promise((res, rej) => {
|
55
|
+
onDone = res;
|
56
|
+
onError = rej;
|
57
|
+
});
|
58
|
+
const r = (0, _child_process.spawn)(cmd, {
|
59
|
+
shell: true,
|
60
|
+
cwd: ops.cwd,
|
61
|
+
env: ops.env
|
62
|
+
});
|
63
|
+
r.stdout.on('data', onStdout);
|
64
|
+
r.stderr.on('data', data => console.error(data.toString()));
|
65
|
+
r.on('exit', status => {
|
66
|
+
if (status === 0) onDone();else onError(new Error('Exited with non zero return code: ' + status));
|
53
67
|
});
|
68
|
+
return p;
|
54
69
|
});
|
55
70
|
}
|
56
71
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
var _Shell = require("./Shell");
|
4
|
+
|
5
|
+
const bigStdOutCme = () => {
|
6
|
+
const times = 5_00_000;
|
7
|
+
return `bash -c 'for i in {1..${times}}; do echo "Looong data"; done; echo Done'`;
|
8
|
+
}; // Not to be run on CI
|
9
|
+
|
10
|
+
|
11
|
+
it.skip('execAndReturnStream', async () => {
|
12
|
+
const sh = new _Shell.Shell(); // The output is too big for the exec buffer
|
13
|
+
|
14
|
+
expect(() => sh.execAndReturnString(bigStdOutCme())).toThrow('spawnSync /bin/sh ENOBUFS'); //
|
15
|
+
|
16
|
+
let out = '';
|
17
|
+
await sh.asyncExec(bigStdOutCme(), data => {
|
18
|
+
out = data.toString().trim();
|
19
|
+
});
|
20
|
+
expect(out).toEndWith('Done');
|
21
|
+
}, 5000_000);
|