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 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.27.1 darwin-arm64 node-v18.17.1
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
- body: {
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.27.1",
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": "^14.14.41",
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",