kourou 0.27.1 → 0.28.1
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/README.md +39 -1
- package/lib/commands/es/snapshot/create.js +5 -1
- package/lib/commands/es/snapshot/restore.d.ts +16 -0
- package/lib/commands/es/snapshot/restore.js +40 -0
- package/lib/commands/paas/elasticsearch/dump.d.ts +35 -0
- package/lib/commands/paas/elasticsearch/dump.js +172 -0
- package/lib/commands/paas/snapshots/restore.js +1 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ $ npm install -g kourou
|
|
|
27
27
|
$ kourou COMMAND
|
|
28
28
|
running command...
|
|
29
29
|
$ kourou (-v|--version|version)
|
|
30
|
-
kourou/0.
|
|
30
|
+
kourou/0.28.1 darwin-arm64 node-v20.10.0
|
|
31
31
|
$ kourou --help [COMMAND]
|
|
32
32
|
USAGE
|
|
33
33
|
$ kourou COMMAND
|
|
@@ -149,6 +149,7 @@ All other arguments and options will be passed as-is to the `sdk:query` method.
|
|
|
149
149
|
* [`kourou es:snapshot:create REPOSITORY NAME`](#kourou-essnapshotcreate-repository-name)
|
|
150
150
|
* [`kourou es:snapshot:create-repository REPOSITORY LOCATION`](#kourou-essnapshotcreate-repository-repository-location)
|
|
151
151
|
* [`kourou es:snapshot:list REPOSITORY`](#kourou-essnapshotlist-repository)
|
|
152
|
+
* [`kourou es:snapshot:restore REPOSITORY NAME`](#kourou-essnapshotrestore-repository-name)
|
|
152
153
|
* [`kourou file:decrypt FILE`](#kourou-filedecrypt-file)
|
|
153
154
|
* [`kourou file:encrypt FILE`](#kourou-fileencrypt-file)
|
|
154
155
|
* [`kourou file:test FILE`](#kourou-filetest-file)
|
|
@@ -161,6 +162,7 @@ All other arguments and options will be passed as-is to the `sdk:query` method.
|
|
|
161
162
|
* [`kourou instance:logs`](#kourou-instancelogs)
|
|
162
163
|
* [`kourou instance:spawn`](#kourou-instancespawn)
|
|
163
164
|
* [`kourou paas:deploy ENVIRONMENT APPLICATIONID IMAGE`](#kourou-paasdeploy-environment-applicationid-image)
|
|
165
|
+
* [`kourou paas:elasticsearch:dump ENVIRONMENT APPLICATIONID DUMPDIRECTORY`](#kourou-paaselasticsearchdump-environment-applicationid-dumpdirectory)
|
|
164
166
|
* [`kourou paas:init PROJECT`](#kourou-paasinit-project)
|
|
165
167
|
* [`kourou paas:login`](#kourou-paaslogin)
|
|
166
168
|
* [`kourou paas:logs ENVIRONMENT APPLICATION`](#kourou-paaslogs-environment-application)
|
|
@@ -794,6 +796,23 @@ OPTIONS
|
|
|
794
796
|
|
|
795
797
|
_See code: [lib/commands/es/snapshot/list.js](lib/commands/es/snapshot/list.js)_
|
|
796
798
|
|
|
799
|
+
## `kourou es:snapshot:restore REPOSITORY NAME`
|
|
800
|
+
|
|
801
|
+
Restore a snapshot repository inside an ES instance
|
|
802
|
+
|
|
803
|
+
```
|
|
804
|
+
USAGE
|
|
805
|
+
$ kourou es:snapshot:restore REPOSITORY NAME
|
|
806
|
+
|
|
807
|
+
ARGUMENTS
|
|
808
|
+
REPOSITORY ES repository name
|
|
809
|
+
NAME ES snapshot name
|
|
810
|
+
|
|
811
|
+
OPTIONS
|
|
812
|
+
-n, --node=node [default: http://localhost:9200] Elasticsearch server URL
|
|
813
|
+
--help show CLI help
|
|
814
|
+
```
|
|
815
|
+
|
|
797
816
|
## `kourou file:decrypt FILE`
|
|
798
817
|
|
|
799
818
|
Decrypts an encrypted file.
|
|
@@ -1064,6 +1083,25 @@ OPTIONS
|
|
|
1064
1083
|
|
|
1065
1084
|
_See code: [lib/commands/paas/deploy.js](lib/commands/paas/deploy.js)_
|
|
1066
1085
|
|
|
1086
|
+
## `kourou paas:elasticsearch:dump ENVIRONMENT APPLICATIONID DUMPDIRECTORY`
|
|
1087
|
+
|
|
1088
|
+
Dump data from the Elasticsearch of a PaaS application
|
|
1089
|
+
|
|
1090
|
+
```
|
|
1091
|
+
USAGE
|
|
1092
|
+
$ kourou paas:elasticsearch:dump ENVIRONMENT APPLICATIONID DUMPDIRECTORY
|
|
1093
|
+
|
|
1094
|
+
ARGUMENTS
|
|
1095
|
+
ENVIRONMENT Project environment name
|
|
1096
|
+
APPLICATIONID Application Identifier
|
|
1097
|
+
DUMPDIRECTORY Directory where to store dump files
|
|
1098
|
+
|
|
1099
|
+
OPTIONS
|
|
1100
|
+
--batch-size=batch-size [default: 2000] Maximum batch size
|
|
1101
|
+
--help show CLI help
|
|
1102
|
+
--project=project Current PaaS project
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1067
1105
|
## `kourou paas:init PROJECT`
|
|
1068
1106
|
|
|
1069
1107
|
Initialize a PaaS project in current directory
|
|
@@ -9,7 +9,11 @@ class EsSnapshotsCreate extends common_1.Kommand {
|
|
|
9
9
|
const esRequest = {
|
|
10
10
|
repository: this.args.repository,
|
|
11
11
|
snapshot: this.args.name,
|
|
12
|
-
body: {
|
|
12
|
+
body: {
|
|
13
|
+
indices: "*",
|
|
14
|
+
include_global_state: false,
|
|
15
|
+
partial: false,
|
|
16
|
+
},
|
|
13
17
|
};
|
|
14
18
|
const response = await esClient.snapshot.create(esRequest);
|
|
15
19
|
this.logOk(`Success ${JSON.stringify(response.body)}`);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { flags } from "@oclif/command";
|
|
2
|
+
import { Kommand } from "../../../common";
|
|
3
|
+
export default class EsSnapshotsRestore extends Kommand {
|
|
4
|
+
static initSdk: boolean;
|
|
5
|
+
static description: string;
|
|
6
|
+
static flags: {
|
|
7
|
+
node: flags.IOptionFlag<string>;
|
|
8
|
+
help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
|
|
9
|
+
};
|
|
10
|
+
static args: {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
required: boolean;
|
|
14
|
+
}[];
|
|
15
|
+
runSafe(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("@oclif/command");
|
|
4
|
+
const elasticsearch_1 = require("@elastic/elasticsearch");
|
|
5
|
+
const common_1 = require("../../../common");
|
|
6
|
+
class EsSnapshotsRestore extends common_1.Kommand {
|
|
7
|
+
async runSafe() {
|
|
8
|
+
const esClient = new elasticsearch_1.Client({ node: this.flags.node });
|
|
9
|
+
await esClient.indices.close({
|
|
10
|
+
index: "*",
|
|
11
|
+
expand_wildcards: "all",
|
|
12
|
+
});
|
|
13
|
+
const esRequest = {
|
|
14
|
+
repository: this.args.repository,
|
|
15
|
+
snapshot: this.args.name,
|
|
16
|
+
body: {
|
|
17
|
+
feature_states: ["none"],
|
|
18
|
+
include_global_state: false,
|
|
19
|
+
indices: "*",
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const response = await esClient.snapshot.restore(esRequest);
|
|
23
|
+
this.logOk(`Success ${JSON.stringify(response.body)}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.default = EsSnapshotsRestore;
|
|
27
|
+
EsSnapshotsRestore.initSdk = false;
|
|
28
|
+
EsSnapshotsRestore.description = "Restore a snapshot repository inside an ES instance";
|
|
29
|
+
EsSnapshotsRestore.flags = {
|
|
30
|
+
node: command_1.flags.string({
|
|
31
|
+
char: "n",
|
|
32
|
+
description: "Elasticsearch server URL",
|
|
33
|
+
default: "http://localhost:9200",
|
|
34
|
+
}),
|
|
35
|
+
help: command_1.flags.help(),
|
|
36
|
+
};
|
|
37
|
+
EsSnapshotsRestore.args = [
|
|
38
|
+
{ name: "repository", description: "ES repository name", required: true },
|
|
39
|
+
{ name: "name", description: "ES snapshot name", required: true },
|
|
40
|
+
];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { flags } from "@oclif/command";
|
|
2
|
+
import { PaasKommand } from "../../../support/PaasKommand";
|
|
3
|
+
declare class PaasEsDump extends PaasKommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
|
|
7
|
+
project: flags.IOptionFlag<string | undefined>;
|
|
8
|
+
"batch-size": import("@oclif/parser/lib/flags").IOptionFlag<number>;
|
|
9
|
+
};
|
|
10
|
+
static args: {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
required: boolean;
|
|
14
|
+
}[];
|
|
15
|
+
runSafe(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* @description Get all indexes from the Elasticsearch of the PaaS application.
|
|
18
|
+
* @returns The indexes.
|
|
19
|
+
*/
|
|
20
|
+
private getAllIndexes;
|
|
21
|
+
/**
|
|
22
|
+
* @description Dump documents from the Elasticsearch of the PaaS application.
|
|
23
|
+
* @param pitId ID of the PIT opened on Elasticsearch.
|
|
24
|
+
* @param searchAfter Cursor for dumping documents after a certain one.
|
|
25
|
+
* @returns The dumped documents.
|
|
26
|
+
*/
|
|
27
|
+
private dumpDocuments;
|
|
28
|
+
private dumpAllDocuments;
|
|
29
|
+
/**
|
|
30
|
+
* @description Finish the document dumping session.
|
|
31
|
+
* @param pitId ID of the PIT opened on Elasticsearch.
|
|
32
|
+
*/
|
|
33
|
+
private finishDump;
|
|
34
|
+
}
|
|
35
|
+
export default PaasEsDump;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const path_1 = (0, tslib_1.__importDefault)(require("path"));
|
|
5
|
+
const promises_1 = (0, tslib_1.__importDefault)(require("node:fs/promises"));
|
|
6
|
+
const ndjson_1 = (0, tslib_1.__importDefault)(require("ndjson"));
|
|
7
|
+
const command_1 = require("@oclif/command");
|
|
8
|
+
const PaasKommand_1 = require("../../../support/PaasKommand");
|
|
9
|
+
class PaasEsDump extends PaasKommand_1.PaasKommand {
|
|
10
|
+
async runSafe() {
|
|
11
|
+
// Check that the batch size is positive
|
|
12
|
+
if (this.flags["batch-size"] <= 0) {
|
|
13
|
+
this.logKo(`The batch size must be greater than zero. (Specified batch size: ${this.flags["batch-size"]})`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
// Log in to the PaaS
|
|
17
|
+
const apiKey = await this.getCredentials();
|
|
18
|
+
await this.initPaasClient({ apiKey });
|
|
19
|
+
const user = await this.paas.auth.getCurrentUser();
|
|
20
|
+
this.logInfo(`Logged as "${user._id}" for project "${this.flags.project || this.getProject()}"`);
|
|
21
|
+
// Create the dump directory
|
|
22
|
+
await promises_1.default.mkdir(this.args.dumpDirectory, { recursive: true });
|
|
23
|
+
// Dump the indexes
|
|
24
|
+
this.logInfo("Dumping Elasticsearch indexes...");
|
|
25
|
+
const indexesResult = await this.getAllIndexes();
|
|
26
|
+
await promises_1.default.writeFile(path_1.default.join(this.args.dumpDirectory, "indexes.json"), JSON.stringify(indexesResult));
|
|
27
|
+
this.logOk("Elasticsearch indexes dumped!");
|
|
28
|
+
// Dump all the documents
|
|
29
|
+
this.logInfo("Dumping Elasticsearch documents...");
|
|
30
|
+
await this.dumpAllDocuments();
|
|
31
|
+
this.logOk("Elasticsearch documents dumped!");
|
|
32
|
+
this.logOk(`The dumped files are available under "${path_1.default.resolve(this.args.dumpDirectory)}"`);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @description Get all indexes from the Elasticsearch of the PaaS application.
|
|
36
|
+
* @returns The indexes.
|
|
37
|
+
*/
|
|
38
|
+
async getAllIndexes() {
|
|
39
|
+
const { result } = await this.paas.query({
|
|
40
|
+
controller: "application/storage",
|
|
41
|
+
action: "getIndexes",
|
|
42
|
+
environmentId: this.args.environment,
|
|
43
|
+
projectId: this.flags.project || this.getProject(),
|
|
44
|
+
applicationId: this.args.applicationId,
|
|
45
|
+
body: {},
|
|
46
|
+
});
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @description Dump documents from the Elasticsearch of the PaaS application.
|
|
51
|
+
* @param pitId ID of the PIT opened on Elasticsearch.
|
|
52
|
+
* @param searchAfter Cursor for dumping documents after a certain one.
|
|
53
|
+
* @returns The dumped documents.
|
|
54
|
+
*/
|
|
55
|
+
async dumpDocuments(pitId, searchAfter) {
|
|
56
|
+
const { result } = await this.paas.query({
|
|
57
|
+
controller: "application/storage",
|
|
58
|
+
action: "dumpDocuments",
|
|
59
|
+
environmentId: this.args.environment,
|
|
60
|
+
projectId: this.flags.project || this.getProject(),
|
|
61
|
+
applicationId: this.args.applicationId,
|
|
62
|
+
body: {
|
|
63
|
+
pitId,
|
|
64
|
+
searchAfter: JSON.stringify(searchAfter),
|
|
65
|
+
size: this.flags["batch-size"],
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
async dumpAllDocuments() {
|
|
71
|
+
// Prepare dumping all documents
|
|
72
|
+
let pitId = "";
|
|
73
|
+
let searchAfter = [];
|
|
74
|
+
let dumpedDocuments = 0;
|
|
75
|
+
let totalDocuments = 0;
|
|
76
|
+
const fd = await promises_1.default.open(path_1.default.join(this.args.dumpDirectory, "documents.jsonl"), "w");
|
|
77
|
+
const writeStream = fd.createWriteStream();
|
|
78
|
+
const ndjsonStream = ndjson_1.default.stringify();
|
|
79
|
+
writeStream.on("error", (error) => {
|
|
80
|
+
throw error;
|
|
81
|
+
});
|
|
82
|
+
ndjsonStream.on("data", (line) => {
|
|
83
|
+
writeStream.write(line);
|
|
84
|
+
});
|
|
85
|
+
const teardown = async () => {
|
|
86
|
+
// Finish the dump session if a PIT ID is set
|
|
87
|
+
if (pitId.length > 0) {
|
|
88
|
+
await this.finishDump(pitId);
|
|
89
|
+
}
|
|
90
|
+
// Close the open streams/file
|
|
91
|
+
writeStream.close();
|
|
92
|
+
await fd.close();
|
|
93
|
+
};
|
|
94
|
+
try {
|
|
95
|
+
// Dump the first batch
|
|
96
|
+
let result = await this.dumpDocuments(pitId, searchAfter);
|
|
97
|
+
let hits = result.hits.hits;
|
|
98
|
+
while (hits.length > 0) {
|
|
99
|
+
// Update the PIT ID and the cursor for the next dump
|
|
100
|
+
pitId = result.pit_id;
|
|
101
|
+
searchAfter = hits[hits.length - 1].sort;
|
|
102
|
+
// Save the documents
|
|
103
|
+
for (let i = 0; i < hits.length; ++i) {
|
|
104
|
+
ndjsonStream.write(hits[i]);
|
|
105
|
+
}
|
|
106
|
+
dumpedDocuments += hits.length;
|
|
107
|
+
totalDocuments = result.hits.total.value;
|
|
108
|
+
this.logInfo(`Dumping Elasticsearch documents: ${Math.floor(dumpedDocuments / totalDocuments * 100)}% (${dumpedDocuments}/${totalDocuments})`);
|
|
109
|
+
// Dump the next batch
|
|
110
|
+
result = await this.dumpDocuments(pitId, searchAfter);
|
|
111
|
+
hits = result.hits.hits;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
teardown();
|
|
116
|
+
this.logKo(`Error while dumping the documents: ${error}`);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
// Finish the dump
|
|
120
|
+
teardown();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* @description Finish the document dumping session.
|
|
124
|
+
* @param pitId ID of the PIT opened on Elasticsearch.
|
|
125
|
+
*/
|
|
126
|
+
async finishDump(pitId) {
|
|
127
|
+
try {
|
|
128
|
+
await this.paas.query({
|
|
129
|
+
controller: "application/storage",
|
|
130
|
+
action: "finishDumpDocuments",
|
|
131
|
+
environmentId: this.args.environment,
|
|
132
|
+
projectId: this.flags.project || this.getProject(),
|
|
133
|
+
applicationId: this.args.applicationId,
|
|
134
|
+
body: {
|
|
135
|
+
pitId,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
this.logInfo(`Unable to cleanly finish the dump session: ${error}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
PaasEsDump.description = "Dump data from the Elasticsearch of a PaaS application";
|
|
145
|
+
PaasEsDump.flags = {
|
|
146
|
+
help: command_1.flags.help(),
|
|
147
|
+
project: command_1.flags.string({
|
|
148
|
+
description: "Current PaaS project",
|
|
149
|
+
}),
|
|
150
|
+
"batch-size": command_1.flags.integer({
|
|
151
|
+
description: "Maximum batch size",
|
|
152
|
+
default: 2000,
|
|
153
|
+
}),
|
|
154
|
+
};
|
|
155
|
+
PaasEsDump.args = [
|
|
156
|
+
{
|
|
157
|
+
name: "environment",
|
|
158
|
+
description: "Project environment name",
|
|
159
|
+
required: true,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "applicationId",
|
|
163
|
+
description: "Application Identifier",
|
|
164
|
+
required: true,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: "dumpDirectory",
|
|
168
|
+
description: "Directory where to store dump files",
|
|
169
|
+
required: true,
|
|
170
|
+
}
|
|
171
|
+
];
|
|
172
|
+
exports.default = PaasEsDump;
|
|
@@ -14,10 +14,7 @@ class PaasSnapshotsRestore extends PaasKommand_1.PaasKommand {
|
|
|
14
14
|
environmentId: this.args.environment,
|
|
15
15
|
projectId: this.flags.project || this.getProject(),
|
|
16
16
|
applicationId: this.args.applicationId,
|
|
17
|
-
|
|
18
|
-
repository: "automated",
|
|
19
|
-
snapshot: this.args.snapshotId,
|
|
20
|
-
},
|
|
17
|
+
snapshotId: this.args.snapshotId
|
|
21
18
|
});
|
|
22
19
|
this.logInfo("Ok");
|
|
23
20
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kourou",
|
|
3
3
|
"description": "The CLI that helps you manage your Kuzzle instances",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.28.1",
|
|
5
5
|
"author": "The Kuzzle Team <support@kuzzle.io>",
|
|
6
6
|
"bin": {
|
|
7
7
|
"kourou": "./bin/run"
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@types/listr": "^0.14.2",
|
|
57
57
|
"@types/mocha": "^8.2.2",
|
|
58
58
|
"@types/ndjson": "^2.0.0",
|
|
59
|
-
"@types/node": "^
|
|
59
|
+
"@types/node": "^18.19.0",
|
|
60
60
|
"@types/node-emoji": "^1.8.1",
|
|
61
61
|
"@types/node-fetch": "^2.6.1",
|
|
62
62
|
"@types/tar": "^6.1.3",
|