@ps-aux/nodebup 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bup/aggregate/AggregateBackupController.js +9 -9
- package/lib/bup/pg/PgBackupController.js +87 -25
- package/lib/cli/app.js +81 -50
- package/lib/config/Config.js +6 -5
- package/lib/config/expandAndCreatePath.js +2 -2
- package/lib/ctx/Context.js +10 -10
- package/lib/fs/Fs.js +25 -5
- package/lib/fs/fssync/FsSyncer.js +1 -1
- package/lib/storage/rclone/RcloneClient.spec.js +6 -5
- package/lib/storage/restic/ResticClient.js +9 -3
- package/lib/storage/restic/ResticClient.spec.js +4 -3
- package/lib/storage/restic/ResticController.js +12 -2
- package/lib/tools/shell/Shell.js +3 -3
- package/lib/tools/shell/Shell.test.js +7 -2
- package/lib/tools/ssh/SshKeyManager.js +4 -4
- package/package.json +6 -4
@@ -5,14 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
});
|
6
6
|
exports.AggregateBackupController = void 0;
|
7
7
|
|
8
|
-
var _FsSyncer = require("../../fs/fssync/FsSyncer");
|
9
|
-
|
10
|
-
var _Gpg = require("../../tools/gpg/Gpg");
|
11
|
-
|
12
|
-
var _Fs = require("../../fs/Fs");
|
13
|
-
|
14
|
-
var _SshKeyManager = require("../../tools/ssh/SshKeyManager");
|
15
|
-
|
16
8
|
var _inversify = require("inversify");
|
17
9
|
|
18
10
|
var _ContextSymbols = require("../../ctx/ContextSymbols");
|
@@ -25,6 +17,14 @@ var _BackupStorageProvider = require("../../storage/BackupStorageProvider");
|
|
25
17
|
|
26
18
|
var _logging = require("../../log/logging");
|
27
19
|
|
20
|
+
var _SshKeyManager = require("../../tools/ssh/SshKeyManager");
|
21
|
+
|
22
|
+
var _Gpg = require("../../tools/gpg/Gpg");
|
23
|
+
|
24
|
+
var _Fs = require("../../fs/Fs");
|
25
|
+
|
26
|
+
var _FsSyncer = require("../../fs/fssync/FsSyncer");
|
27
|
+
|
28
28
|
var _dec, _dec2, _dec3, _dec4, _class;
|
29
29
|
|
30
30
|
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; }
|
@@ -53,7 +53,7 @@ let AggregateBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = fu
|
|
53
53
|
} = aggr.sources;
|
54
54
|
|
55
55
|
try {
|
56
|
-
this.fs.ensureDir(aggrDir);
|
56
|
+
await this.fs.ensureDir(aggrDir);
|
57
57
|
file.forEach(f => this.runFileTask(f, aggrDir));
|
58
58
|
sshKey.forEach(k => this.runSshKeyTask(k, aggrDir));
|
59
59
|
gpgKey.forEach(k => this.runGpgKeyTask(k, aggrDir));
|
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
});
|
6
6
|
exports.PgBackupController = void 0;
|
7
7
|
|
8
|
-
var _Fs = require("../../fs/Fs");
|
9
|
-
|
10
8
|
var _inversify = require("inversify");
|
11
9
|
|
12
10
|
var _Shell = require("../../tools/shell/Shell");
|
@@ -19,6 +17,8 @@ var _Zipper = require("../../tools/compression/Zipper");
|
|
19
17
|
|
20
18
|
var _logging = require("../../log/logging");
|
21
19
|
|
20
|
+
var _Fs = require("../../fs/Fs");
|
21
|
+
|
22
22
|
var _dec, _dec2, _dec3, _class, _class2, _temp;
|
23
23
|
|
24
24
|
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; }
|
@@ -59,61 +59,123 @@ let PgBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.m
|
|
59
59
|
|
60
60
|
_defineProperty(this, "getVersion", version => version || PgBackupController.defaultPgVersion);
|
61
61
|
|
62
|
-
_defineProperty(this, "outFileName", 'backup.sql');
|
63
|
-
|
64
62
|
_defineProperty(this, "backup", async ({
|
65
63
|
pgUrl,
|
66
|
-
|
64
|
+
pgBinDir,
|
65
|
+
backupName
|
67
66
|
}) => {
|
68
67
|
const storage = this.storageBackendProvider.provide();
|
69
|
-
const
|
68
|
+
const connParams = parseConnectionUrl(pgUrl); // TODO validate url
|
70
69
|
|
71
|
-
this.log.info(`Backing up Postgres database
|
72
|
-
const connParams = parseConnectionUrl(pgUrl);
|
73
|
-
const pass = connParams.password; // if (3 > 2)
|
74
|
-
// return
|
70
|
+
this.log.info(`Backing up Postgres database @${connParams.host}`);
|
75
71
|
|
76
|
-
|
77
|
-
const outputDir = _Path.AbsPath.from(dir);
|
72
|
+
const dir = _Path.AbsPath.from(`/var/backup/bup/${backupName}/postgres`); // const dir = AbsPath.from('/var/foo')
|
78
73
|
|
79
|
-
|
80
|
-
|
74
|
+
|
75
|
+
await this.fs.inNewDir(dir, async () => {
|
76
|
+
// const outputDir = AbsPath.from(dir)
|
77
|
+
this.log.info('Processing Postgres backup'); // Don't forget that this itself might be run in docker
|
81
78
|
// therefore volume mounts are not usable (will apply to the location at host)
|
82
79
|
|
83
|
-
const
|
84
|
-
this.log.info(`Dumping
|
85
|
-
|
80
|
+
const file = PgBackupController.dumpFileName;
|
81
|
+
this.log.info(`Dumping data into ${file}`);
|
82
|
+
let cmd = `pg_dumpall -d ${pgUrl} `;
|
83
|
+
|
84
|
+
if (pgBinDir) {
|
85
|
+
cmd = this.addPgBinDir(cmd, pgBinDir);
|
86
|
+
}
|
87
|
+
|
88
|
+
await this.sh.asyncExec(`${cmd} > ${file}`, {
|
89
|
+
env: {
|
90
|
+
PGPASSWORD: connParams.password
|
91
|
+
},
|
92
|
+
cwd: dir.str()
|
93
|
+
}, {
|
94
|
+
stdout: o => this.log.trace(o),
|
95
|
+
stderr: o => this.log.error(o)
|
96
|
+
});
|
97
|
+
this.log.info(`Compressing ${file}`);
|
98
|
+
await this.sh.asyncExec(`gzip -v ${file}`, {
|
99
|
+
cwd: dir.str()
|
100
|
+
}, {
|
101
|
+
stdout: o => this.log.trace('gzip: ' + o),
|
102
|
+
stderr: o => this.log.info('gzip: ' + o)
|
103
|
+
});
|
86
104
|
this.log.info('Uploading');
|
87
|
-
storage.store(
|
105
|
+
storage.store(dir);
|
88
106
|
});
|
89
107
|
});
|
90
108
|
|
109
|
+
_defineProperty(this, "dumpFromDockerCmd", (pass, pgUrl, version) => {
|
110
|
+
const bashCmds = [`echo "*:*:*:*:${pass}" > ~/.pgpass`, `chmod 400 ~/.pgpass`, `pg_dumpall -d ${pgUrl}`]; // Restore docker command
|
111
|
+
// this.sh.exec(
|
112
|
+
// `gzip --decompress --stdout ${zipFile.str()} | docker run --network host -i ` +
|
113
|
+
// `-e PGPASSWORD=${con.password} ` +
|
114
|
+
// `postgres:${version} ` +
|
115
|
+
// `psql ${connectionArgs} -v ON_ERROR_STOP=0`
|
116
|
+
// )
|
117
|
+
// TODO consider pulling the docker images first so the Docke daremon info messages about container pulling are not logged as errors
|
118
|
+
|
119
|
+
return `docker run --rm --network host postgres:${version} ` + `bash -c '${bashCmds.join(' && ')}'`;
|
120
|
+
});
|
121
|
+
|
91
122
|
_defineProperty(this, "restore", async ({
|
92
123
|
pgUrl,
|
93
|
-
|
124
|
+
pgBinDir
|
94
125
|
}) => {
|
95
|
-
const version = this.getVersion(pgVersion)
|
126
|
+
// const version = this.getVersion(pgVersion)
|
96
127
|
const storage = this.storageBackendProvider.provide(); // To validate
|
97
128
|
|
98
129
|
const con = parseConnectionUrl(pgUrl);
|
99
|
-
this.log.info(`Restoring Postgres database
|
130
|
+
this.log.info(`Restoring Postgres database`);
|
100
131
|
await this.fs.inTmpDir('pg-restore', async dirStr => {
|
101
132
|
const dir = _Path.AbsPath.from(dirStr);
|
102
133
|
|
103
134
|
storage.restore(dir); // TODO check if the dir contains the with the expected name
|
104
135
|
// TODO add e2e test for docker
|
105
136
|
|
106
|
-
|
107
|
-
|
137
|
+
this.log.info('Backup dir restored');
|
138
|
+
const file = PgBackupController.dumpFileName;
|
139
|
+
const zipFile = file + '.gz';
|
140
|
+
const zipPath = this.fs.listFiles(dir).find(n => n.basename() === zipFile);
|
141
|
+
if (!zipPath) throw new Error(`Expected to find file ${zipFile} in the restore data`);
|
142
|
+
this.log.info(`Decompressing ${zipFile}`);
|
143
|
+
await this.sh.asyncExec(`gzip --decompress -v ${zipFile}`, {
|
144
|
+
cwd: dir.str()
|
145
|
+
}, {
|
146
|
+
stdout: o => this.log.trace('gzip: ' + o),
|
147
|
+
stderr: o => this.log.info('gzip: ' + o)
|
148
|
+
}); // Database is set to 'postgres' so that also users which don't have db created can use db-less URL
|
108
149
|
// Restoring user needs to have the admin access anyway
|
109
150
|
|
110
151
|
const connectionArgs = `-h ${con.host} -p ${con.port} -U ${con.username} -d postgres`; // Don't forget that this itself might be run in docker
|
111
152
|
// therefore volume mounts are not usable (will apply to the location at host)
|
112
153
|
|
113
|
-
|
154
|
+
let cmd = `psql ${connectionArgs} -v ON_ERROR_STOP=0 < ${file}`;
|
155
|
+
|
156
|
+
if (pgBinDir) {
|
157
|
+
cmd = this.addPgBinDir(cmd, pgBinDir);
|
158
|
+
}
|
159
|
+
|
160
|
+
await this.sh.asyncExec(cmd, {
|
161
|
+
cwd: dir.str(),
|
162
|
+
env: {
|
163
|
+
PGPASSWORD: con.password
|
164
|
+
}
|
165
|
+
}, {
|
166
|
+
stdout: o => this.log.trace(o),
|
167
|
+
stderr: o => this.log.error(o)
|
168
|
+
});
|
169
|
+
this.log.info(`Data successfully inserted into database @${con.host}`);
|
114
170
|
});
|
115
171
|
});
|
172
|
+
|
173
|
+
_defineProperty(this, "addPgBinDir", (cmd, pgBinDir) => {
|
174
|
+
this.log.info(`Using PG bin dir ${pgBinDir}`);
|
175
|
+
this.fs.ensureIsDir(_Path.AbsPath.from(pgBinDir));
|
176
|
+
return pgBinDir + '/' + cmd;
|
177
|
+
});
|
116
178
|
}
|
117
179
|
|
118
|
-
}, _defineProperty(_class2, "defaultPgVersion", '14.2'), _temp)) || _class) || _class) || _class);
|
180
|
+
}, _defineProperty(_class2, "defaultPgVersion", '14.2'), _defineProperty(_class2, "dumpFileName", 'pg-dump.sql'), _temp)) || _class) || _class) || _class);
|
119
181
|
exports.PgBackupController = PgBackupController;
|
package/lib/cli/app.js
CHANGED
@@ -53,55 +53,63 @@ const restoreOptions = [backupTagOption, {
|
|
53
53
|
convertCase: true
|
54
54
|
}];
|
55
55
|
|
56
|
-
const createApp = () =>
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
})
|
98
|
-
|
99
|
-
|
100
|
-
}
|
56
|
+
const createApp = () => {
|
57
|
+
const p = _nclif.CliApp.of({
|
58
|
+
binName: 'bup',
|
59
|
+
description: 'Data backup utility'
|
60
|
+
}, {
|
61
|
+
commands: {
|
62
|
+
dir: (0, _nclif.cmdGroup)({
|
63
|
+
options: singleStorageOptions,
|
64
|
+
commands: {
|
65
|
+
backup: (0, _nclif.cmd)({
|
66
|
+
description: 'Backup a dir to the given storage',
|
67
|
+
options: backupOptions,
|
68
|
+
positionals: [{
|
69
|
+
name: 'path',
|
70
|
+
required: true
|
71
|
+
}],
|
72
|
+
run: (cmd, c) => c.get(_DirBackupController.DirBackupController).backup(cmd)
|
73
|
+
}),
|
74
|
+
restore: (0, _nclif.cmd)({
|
75
|
+
description: 'Restore the storage to the given dir',
|
76
|
+
options: restoreOptions,
|
77
|
+
positionals: [{
|
78
|
+
name: 'path',
|
79
|
+
required: true
|
80
|
+
}],
|
81
|
+
run: (cmd, c) => c.get(_DirBackupController.DirBackupController).restore(cmd)
|
82
|
+
})
|
83
|
+
}
|
84
|
+
}),
|
85
|
+
aggr: (0, _nclif.cmdGroup)({
|
86
|
+
commands: {
|
87
|
+
backup: (0, _nclif.cmd)({
|
88
|
+
description: 'Run backup',
|
89
|
+
options: [storageNameOpt],
|
90
|
+
positionals: [{
|
91
|
+
name: 'aggregateName',
|
92
|
+
required: true
|
93
|
+
}],
|
94
|
+
run: (a, c) => c.get(_AggregateBackupController.AggregateBackupController).backup(a.aggregateName)
|
95
|
+
})
|
96
|
+
}
|
97
|
+
}),
|
98
|
+
restic,
|
99
|
+
pg
|
100
|
+
}
|
101
|
+
}).envConfig('NODEBUP').addObjectConfig(pwd => (0, _Config.readConfig)(pwd)).context(({
|
102
|
+
config,
|
103
|
+
inputs
|
104
|
+
}) => (0, _Context.createContext)(config, inputs));
|
105
|
+
|
106
|
+
return p;
|
107
|
+
};
|
101
108
|
|
102
109
|
exports.createApp = createApp;
|
103
110
|
const restic = (0, _nclif.cmdGroup)({
|
104
111
|
options: singleStorageOptions,
|
112
|
+
description: 'Restic commands',
|
105
113
|
commands: {
|
106
114
|
'init-repo': (0, _nclif.cmd)({
|
107
115
|
run: (_, c) => c.get(_ResticController.ResticController).initRepo()
|
@@ -109,6 +117,17 @@ const restic = (0, _nclif.cmdGroup)({
|
|
109
117
|
snapshots: (0, _nclif.cmd)({
|
110
118
|
run: (_, c, p) => c.get(_ResticController.ResticController).listSnapshots(p.stdout)
|
111
119
|
}),
|
120
|
+
env: (0, _nclif.cmd)({
|
121
|
+
run: ({
|
122
|
+
export: doExport
|
123
|
+
}, c) => c.get(_ResticController.ResticController).printEnv(doExport),
|
124
|
+
options: [{
|
125
|
+
name: 'export',
|
126
|
+
type: 'boolean',
|
127
|
+
description: 'Add "export" statement to each env variable inoutput',
|
128
|
+
convertCase: true
|
129
|
+
}]
|
130
|
+
}),
|
112
131
|
cmd: (0, _nclif.cmd)({
|
113
132
|
positionals: [{
|
114
133
|
name: 'cmd',
|
@@ -121,20 +140,32 @@ const restic = (0, _nclif.cmdGroup)({
|
|
121
140
|
}
|
122
141
|
});
|
123
142
|
const pg = (0, _nclif.cmdGroup)({
|
143
|
+
description: 'Postgres backup commands',
|
124
144
|
options: [...singleStorageOptions, {
|
125
145
|
name: 'pg-url',
|
146
|
+
description: 'Postgres URL',
|
126
147
|
convertCase: true,
|
127
148
|
fromConfig: 'pg.url',
|
128
149
|
required: true
|
129
150
|
}, {
|
130
|
-
name: 'pg-
|
151
|
+
name: 'pg-bin-dir',
|
152
|
+
description: 'A directory with Postgres binaries (if the ones on the path should not be used)',
|
131
153
|
convertCase: true,
|
132
|
-
fromConfig: 'pg.
|
133
|
-
|
134
|
-
|
154
|
+
fromConfig: 'pg.bin-dir'
|
155
|
+
} // {
|
156
|
+
// name: 'pg-version',
|
157
|
+
// convertCase: true,
|
158
|
+
// fromConfig: 'pg.version',
|
159
|
+
// description: `Postgres version - default is ${PgBackupController.defaultPgVersion}`
|
160
|
+
// }
|
161
|
+
],
|
135
162
|
commands: {
|
136
163
|
backup: (0, _nclif.cmd)({
|
137
|
-
options: backupOptions,
|
164
|
+
options: [...backupOptions, {
|
165
|
+
name: 'backup-name',
|
166
|
+
convertCase: true,
|
167
|
+
required: true
|
168
|
+
}],
|
138
169
|
run: (inp, c) => c.get(_PgBackupController.PgBackupController).backup(inp)
|
139
170
|
}),
|
140
171
|
restore: (0, _nclif.cmd)({
|
package/lib/config/Config.js
CHANGED
@@ -5,16 +5,16 @@ Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
});
|
6
6
|
exports.readConfig = void 0;
|
7
7
|
|
8
|
+
var _expandAndCreatePath = require("./expandAndCreatePath");
|
9
|
+
|
10
|
+
var _Fs = require("../fs/Fs");
|
11
|
+
|
8
12
|
var _Path = require("../fs/path/Path");
|
9
13
|
|
10
14
|
var _readYaml = require("../fs/readYaml");
|
11
15
|
|
12
16
|
var _validateConfigAgainstSchema = require("./validateConfigAgainstSchema");
|
13
17
|
|
14
|
-
var _expandAndCreatePath = require("./expandAndCreatePath");
|
15
|
-
|
16
|
-
var _Fs = require("../fs/Fs");
|
17
|
-
|
18
18
|
const expandPaths = (cfg, exp) => {
|
19
19
|
cfg.aggregates.forEach(a => {
|
20
20
|
a.sources.file.forEach(f => {
|
@@ -37,7 +37,8 @@ const readFromFile = cwd => {
|
|
37
37
|
const cfg = fs.exists(cfgPath) ? (0, _readYaml.readYaml)(cfgPath.str()) : {
|
38
38
|
aggregates: [],
|
39
39
|
storage: []
|
40
|
-
};
|
40
|
+
}; // eslint-disable-next-line no-undef
|
41
|
+
|
41
42
|
(0, _validateConfigAgainstSchema.validateConfigAgainstSchema)(cfg);
|
42
43
|
return cfg;
|
43
44
|
};
|
@@ -5,12 +5,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
});
|
6
6
|
exports.expandStrPath = exports.expandAndCreatePath = void 0;
|
7
7
|
|
8
|
-
var _Path = require("../fs/path/Path");
|
9
|
-
|
10
8
|
var _os = _interopRequireDefault(require("os"));
|
11
9
|
|
12
10
|
var _path = _interopRequireDefault(require("path"));
|
13
11
|
|
12
|
+
var _Path = require("../fs/path/Path");
|
13
|
+
|
14
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
15
15
|
|
16
16
|
const expandStrPath = (path, pwd) => {
|
package/lib/ctx/Context.js
CHANGED
@@ -5,16 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
});
|
6
6
|
exports.createContext = void 0;
|
7
7
|
|
8
|
-
var _Shell = require("../tools/shell/Shell");
|
9
|
-
|
10
|
-
var _FsSyncer = require("../fs/fssync/FsSyncer");
|
11
|
-
|
12
|
-
var _Gpg = require("../tools/gpg/Gpg");
|
13
|
-
|
14
|
-
var _Fs = require("../fs/Fs");
|
15
|
-
|
16
|
-
var _SshKeyManager = require("../tools/ssh/SshKeyManager");
|
17
|
-
|
18
8
|
var _inversify = require("inversify");
|
19
9
|
|
20
10
|
var _ContextSymbols = require("./ContextSymbols");
|
@@ -39,6 +29,16 @@ var _PgBackupController = require("../bup/pg/PgBackupController");
|
|
39
29
|
|
40
30
|
var _Zipper = require("../tools/compression/Zipper");
|
41
31
|
|
32
|
+
var _FsSyncer = require("../fs/fssync/FsSyncer");
|
33
|
+
|
34
|
+
var _Gpg = require("../tools/gpg/Gpg");
|
35
|
+
|
36
|
+
var _Shell = require("../tools/shell/Shell");
|
37
|
+
|
38
|
+
var _Fs = require("../fs/Fs");
|
39
|
+
|
40
|
+
var _SshKeyManager = require("../tools/ssh/SshKeyManager");
|
41
|
+
|
42
42
|
const createContext = (cfg, inp) => {
|
43
43
|
const c = new _inversify.Container();
|
44
44
|
c.bind(_ContextSymbols.AppConfig_).toConstantValue(cfg);
|
package/lib/fs/Fs.js
CHANGED
@@ -7,9 +7,9 @@ exports.Fs = void 0;
|
|
7
7
|
|
8
8
|
var _fs = _interopRequireDefault(require("fs"));
|
9
9
|
|
10
|
-
var
|
10
|
+
var _promises = _interopRequireDefault(require("fs/promises"));
|
11
11
|
|
12
|
-
var
|
12
|
+
var _path = _interopRequireDefault(require("path"));
|
13
13
|
|
14
14
|
var _inversify = require("inversify");
|
15
15
|
|
@@ -19,6 +19,8 @@ var _os = _interopRequireDefault(require("os"));
|
|
19
19
|
|
20
20
|
var _logging = require("../log/logging");
|
21
21
|
|
22
|
+
var _Path = require("./path/Path");
|
23
|
+
|
22
24
|
var _dec, _dec2, _dec3, _class;
|
23
25
|
|
24
26
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
@@ -73,10 +75,23 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
|
|
73
75
|
try {
|
74
76
|
await withDir(dir);
|
75
77
|
} finally {
|
78
|
+
// TODO handle if not exists?
|
76
79
|
this.rmDir(_Path.AbsPath.from(dir));
|
77
80
|
}
|
78
81
|
});
|
79
82
|
|
83
|
+
_defineProperty(this, "inNewDir", async (path, withDir) => {
|
84
|
+
let created = false;
|
85
|
+
|
86
|
+
try {
|
87
|
+
await this.ensureDir(path, true);
|
88
|
+
created = true;
|
89
|
+
await withDir();
|
90
|
+
} finally {
|
91
|
+
if (created) this.rmDir(path);
|
92
|
+
}
|
93
|
+
});
|
94
|
+
|
80
95
|
_defineProperty(this, "mkTmpDir", name => {
|
81
96
|
const dir = _fs.default.mkdtempSync(_path.default.resolve(_os.default.tmpdir(), name));
|
82
97
|
|
@@ -97,7 +112,7 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
|
|
97
112
|
return JSON.parse(file);
|
98
113
|
});
|
99
114
|
|
100
|
-
_defineProperty(this, "ensureDir", path => {
|
115
|
+
_defineProperty(this, "ensureDir", async (path, mustBeEmpty = false) => {
|
101
116
|
if (!this.exists(path)) {
|
102
117
|
this.log.trace('Dir %s does not exist. Wil be created', path.str());
|
103
118
|
|
@@ -106,13 +121,18 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
|
|
106
121
|
});
|
107
122
|
} else {
|
108
123
|
if (this.isFile(path)) throw new Error(`${path.str()} exists and is not a directory`);
|
124
|
+
|
125
|
+
if (mustBeEmpty) {
|
126
|
+
const content = await _promises.default.readdir(path.str());
|
127
|
+
if (content.length) throw new Error(`${path.str()} exists but is not empty`);
|
128
|
+
}
|
109
129
|
}
|
110
130
|
});
|
111
131
|
|
112
|
-
_defineProperty(this, "ensureParentDir", path => {
|
132
|
+
_defineProperty(this, "ensureParentDir", async path => {
|
113
133
|
const parent = path.parent();
|
114
134
|
if (this.exists(parent)) return;
|
115
|
-
this.ensureDir(parent);
|
135
|
+
await this.ensureDir(parent);
|
116
136
|
});
|
117
137
|
|
118
138
|
_defineProperty(this, "exists", path => _fs.default.existsSync(path.str()));
|
@@ -34,7 +34,7 @@ let FsSyncer = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("d
|
|
34
34
|
if (this.fs.isFile(from)) this.syncFile(from, to);else this.syncDirs(from, to);
|
35
35
|
});
|
36
36
|
|
37
|
-
_defineProperty(this, "syncDirs", (from, to) => {
|
37
|
+
_defineProperty(this, "syncDirs", async (from, to) => {
|
38
38
|
this.fs.ensureIsDir(from);
|
39
39
|
this.log.info('Syncing dir', from.str(), '==>', to.str());
|
40
40
|
const fromPath = toUnixDirString(from);
|
@@ -15,11 +15,12 @@ var _b2TestConfig = require("../../../test/b2TestConfig");
|
|
15
15
|
var _testHelper = require("../../../test/testHelper");
|
16
16
|
|
17
17
|
describe('RcloneClient', () => {
|
18
|
-
|
18
|
+
// TODO clean up dir locations more
|
19
|
+
const testData = (0, _test.testdataDirBuilder)('rclone');
|
19
20
|
describe('local', () => {
|
20
21
|
const backupDir = (0, _test.testDir)('backup/1');
|
21
|
-
const restoreDir =
|
22
|
-
const storeDir = (
|
22
|
+
const restoreDir = testData('local');
|
23
|
+
const storeDir = testData('store');
|
23
24
|
const cfg = {
|
24
25
|
type: 'local',
|
25
26
|
location: (0, _test.testdataDir)(storeDir),
|
@@ -68,11 +69,11 @@ describe('RcloneClient', () => {
|
|
68
69
|
password: 'foo123'
|
69
70
|
}
|
70
71
|
}, new _Shell.Shell());
|
71
|
-
const restoreDir =
|
72
|
+
const restoreDir = testData('b2');
|
72
73
|
afterAll(() => {
|
73
74
|
(0, _testHelper.cleanDir)(restoreDir);
|
74
75
|
});
|
75
|
-
it('push
|
76
|
+
it('push and restore data', () => {
|
76
77
|
sut.backup(_Path.AbsPath.from(backupDir));
|
77
78
|
sut.restore(_Path.AbsPath.from(restoreDir));
|
78
79
|
const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
|
@@ -30,10 +30,13 @@ class ResticClient {
|
|
30
30
|
|
31
31
|
_defineProperty(this, "log", (0, _logging.classObjLog)(this));
|
32
32
|
|
33
|
-
_defineProperty(this, "prepareRepo", () => {
|
33
|
+
_defineProperty(this, "prepareRepo", async () => {
|
34
34
|
this.log.info('Preparing a restic repo at %s', this.url);
|
35
|
-
this.shell.
|
35
|
+
await this.shell.asyncExec(`restic init`, {
|
36
36
|
env: this.env()
|
37
|
+
}, {
|
38
|
+
stdout: o => this.log.trace(o),
|
39
|
+
stderr: o => this.log.error(o)
|
37
40
|
});
|
38
41
|
});
|
39
42
|
|
@@ -77,14 +80,17 @@ class ResticClient {
|
|
77
80
|
});
|
78
81
|
|
79
82
|
_defineProperty(this, "snapshots", () => {
|
80
|
-
this.log.debug(
|
83
|
+
this.log.debug(`Listing snapshots of repo=${this.url}`);
|
81
84
|
const out = this.shell.execAndReturnString(`restic snapshots --json`, {
|
82
85
|
env: this.env()
|
83
86
|
});
|
84
87
|
const res = JSON.parse(out);
|
85
88
|
return res.map(r => ({
|
86
89
|
id: r.id,
|
90
|
+
short_id: r.short_id,
|
87
91
|
time: new Date(r.time),
|
92
|
+
paths: r.paths,
|
93
|
+
hostname: r.hostname,
|
88
94
|
tags: r.tags
|
89
95
|
}));
|
90
96
|
});
|
@@ -30,8 +30,9 @@ describe('ResticClient', () => {
|
|
30
30
|
(0, _testHelper.cleanDir)(repoDir);
|
31
31
|
(0, _testHelper.cleanDir)(restoreDir);
|
32
32
|
});
|
33
|
-
it('create repo', () => {
|
34
|
-
sut.prepareRepo();
|
33
|
+
it('create repo', async () => {
|
34
|
+
await sut.prepareRepo();
|
35
|
+
console.log('repo created');
|
35
36
|
});
|
36
37
|
it('push data & forget', () => {
|
37
38
|
const from = _Path.AbsPath.from(backupDir);
|
@@ -76,7 +77,7 @@ describe('ResticClient', () => {
|
|
76
77
|
afterAll(() => {
|
77
78
|
(0, _testHelper.cleanDir)(restoreDir);
|
78
79
|
});
|
79
|
-
it('push
|
80
|
+
it('push and restore data', () => {
|
80
81
|
// sut.prepareRepo()
|
81
82
|
sut.backup(_Path.AbsPath.from(backupDir), _Path.RelativePath.from('.'));
|
82
83
|
sut.restore(_Path.AbsPath.from(restoreDir));
|
@@ -39,10 +39,11 @@ let ResticController = (_dec = (0, _inversify.injectable)(), _dec2 = function (t
|
|
39
39
|
return this.restFact.createClient(props);
|
40
40
|
});
|
41
41
|
|
42
|
-
_defineProperty(this, "initRepo", () => {
|
42
|
+
_defineProperty(this, "initRepo", async () => {
|
43
43
|
const props = this.storageConfigProvider.provide();
|
44
44
|
this.log.info(`Initializing Restic repo ${props.repo}`);
|
45
|
-
this.client().prepareRepo();
|
45
|
+
await this.client().prepareRepo();
|
46
|
+
this.log.info('Repo initialized');
|
46
47
|
});
|
47
48
|
|
48
49
|
_defineProperty(this, "listSnapshots", print => {
|
@@ -54,6 +55,15 @@ let ResticController = (_dec = (0, _inversify.injectable)(), _dec2 = function (t
|
|
54
55
|
const res = this.client().runCmd(cmd);
|
55
56
|
print(res.toString());
|
56
57
|
});
|
58
|
+
|
59
|
+
_defineProperty(this, "printEnv", (doExport = false) => {
|
60
|
+
const env = this.client().env();
|
61
|
+
const out = Object.entries(env).map(([k, v]) => `${k}=${v}`).map(line => {
|
62
|
+
if (!doExport) return line;
|
63
|
+
return `export ${line}`;
|
64
|
+
}).join('\n');
|
65
|
+
console.log(out);
|
66
|
+
});
|
57
67
|
}
|
58
68
|
|
59
69
|
}) || _class) || _class) || _class) || _class);
|
package/lib/tools/shell/Shell.js
CHANGED
@@ -48,7 +48,7 @@ let Shell = (_dec = (0, _inversify.injectable)(), _dec(_class = class Shell {
|
|
48
48
|
});
|
49
49
|
});
|
50
50
|
|
51
|
-
_defineProperty(this, "asyncExec", (cmd,
|
51
|
+
_defineProperty(this, "asyncExec", (cmd, ops = {}, on) => {
|
52
52
|
let onDone;
|
53
53
|
let onError;
|
54
54
|
const p = new Promise((res, rej) => {
|
@@ -60,8 +60,8 @@ let Shell = (_dec = (0, _inversify.injectable)(), _dec(_class = class Shell {
|
|
60
60
|
cwd: ops.cwd,
|
61
61
|
env: ops.env
|
62
62
|
});
|
63
|
-
r.stdout.on('data',
|
64
|
-
r.stderr.on('data',
|
63
|
+
r.stdout.on('data', o => on.stdout(o.toString()));
|
64
|
+
r.stderr.on('data', o => on.stderr(o.toString()));
|
65
65
|
r.on('exit', status => {
|
66
66
|
if (status === 0) onDone();else onError(new Error('Exited with non zero return code: ' + status));
|
67
67
|
});
|
@@ -14,8 +14,13 @@ it.skip('execAndReturnStream', async () => {
|
|
14
14
|
expect(() => sh.execAndReturnString(bigStdOutCme())).toThrow('spawnSync /bin/sh ENOBUFS'); //
|
15
15
|
|
16
16
|
let out = '';
|
17
|
-
await sh.asyncExec(bigStdOutCme(),
|
18
|
-
|
17
|
+
await sh.asyncExec(bigStdOutCme(), {}, {
|
18
|
+
stdout: data => {
|
19
|
+
out = data.toString().trim();
|
20
|
+
},
|
21
|
+
stderr: data => {
|
22
|
+
console.error(data);
|
23
|
+
}
|
19
24
|
});
|
20
25
|
expect(out).toEndWith('Done');
|
21
26
|
}, 5000_000);
|
@@ -5,14 +5,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
});
|
6
6
|
exports.SshKeyManager = void 0;
|
7
7
|
|
8
|
-
var _Fs = require("../../fs/Fs");
|
9
|
-
|
10
|
-
var _Path = require("../../fs/path/Path");
|
11
|
-
|
12
8
|
var _os = _interopRequireDefault(require("os"));
|
13
9
|
|
14
10
|
var _inversify = require("inversify");
|
15
11
|
|
12
|
+
var _Fs = require("../../fs/Fs");
|
13
|
+
|
14
|
+
var _Path = require("../../fs/path/Path");
|
15
|
+
|
16
16
|
var _dec, _dec2, _dec3, _class;
|
17
17
|
|
18
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
package/package.json
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ps-aux/nodebup",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.12.0",
|
4
4
|
"description": "",
|
5
5
|
"module": "lib/index.js",
|
6
6
|
"main": "lib/index.js",
|
7
7
|
"scripts": {
|
8
|
+
"run": "ts-node ./src/cli/bin.ts",
|
8
9
|
"build": "rm -rf build && babel --extensions '.ts,.js,.md' src -d lib src",
|
9
10
|
"publish:local": "npm run build && chmod +x lib/cli/bin.js && npm link",
|
10
11
|
"pub": "npm publish --access public",
|
11
|
-
"test": "jest",
|
12
|
-
"test:ci": "jest --group=-no-ci",
|
12
|
+
"test": "jest --runInBand",
|
13
|
+
"test:ci": "jest --group=-no-ci --runInBand",
|
13
14
|
"tc": "tsc --noEmit",
|
14
15
|
"format": "prettier \"**/*.{js,ts,tsx}\" --write",
|
15
16
|
"build-n-run": "npm run build && ",
|
@@ -68,6 +69,7 @@
|
|
68
69
|
"pg": "^8.7.3",
|
69
70
|
"prettier": "^2.5.1",
|
70
71
|
"ts-jest": "^27.1.2",
|
72
|
+
"ts-node": "^10.9.2",
|
71
73
|
"typescript": "^4.5.4"
|
72
74
|
},
|
73
75
|
"lint-staged": {
|
@@ -78,7 +80,7 @@
|
|
78
80
|
},
|
79
81
|
"dependencies": {
|
80
82
|
"@hapi/joi": "^17.1.1",
|
81
|
-
"@ps-aux/nclif": "^0.0
|
83
|
+
"@ps-aux/nclif": "^0.9.0-alpha5",
|
82
84
|
"@types/hapi__joi": "^17.1.8",
|
83
85
|
"axios": "^0.24.0",
|
84
86
|
"handlebars": "^4.7.7",
|