clever-tools 4.1.0 → 4.3.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/bin/clever.js +106 -20
- package/package.json +1 -1
- package/src/clever-client/k8s.js +96 -0
- package/src/commands/addon.js +119 -146
- package/src/commands/diag.js +33 -4
- package/src/commands/k8s.js +193 -0
- package/src/commands/ng.js +1 -1
- package/src/experimental-features.js +32 -5
- package/src/lib/ascii.js +48 -0
- package/src/lib/k8s.js +126 -0
- package/src/lib/prompts.js +2 -0
- package/src/models/ids-resolver.js +11 -0
- package/src/models/log-v4.js +1 -1
- package/src/models/ng-resources.js +16 -28
- package/src/models/ng.js +19 -30
- package/src/parsers.js +5 -3
package/src/models/ng.js
CHANGED
|
@@ -9,17 +9,20 @@ import crypto from 'node:crypto';
|
|
|
9
9
|
import { searchNetworkGroupOrResource } from '../clever-client/ng.js';
|
|
10
10
|
import { styleText } from '../lib/style-text.js';
|
|
11
11
|
import { Logger } from '../logger.js';
|
|
12
|
+
import { getOwnerIdFromOrgIdOrName } from './ids-resolver.js';
|
|
12
13
|
import { checkMembersToLink } from './ng-resources.js';
|
|
13
|
-
import * as Organisation from './organisation.js';
|
|
14
14
|
import { sendToApi } from './send-to-api.js';
|
|
15
|
-
import * as User from './user.js';
|
|
16
15
|
|
|
17
16
|
export const POLLING_TIMEOUT_MS = 30_000;
|
|
18
17
|
export const POLLING_INTERVAL_MS = 1000;
|
|
19
18
|
export const DOMAIN = 'cc-ng.cloud';
|
|
20
|
-
const
|
|
19
|
+
export const NG_MEMBER_PREFIXES = {
|
|
21
20
|
app_: 'APPLICATION',
|
|
22
|
-
|
|
21
|
+
elasticsearch_: 'ADDON',
|
|
22
|
+
mongodb_: 'ADDON',
|
|
23
|
+
mysql_: 'ADDON',
|
|
24
|
+
postgresql_: 'ADDON',
|
|
25
|
+
redis_: 'ADDON',
|
|
23
26
|
external_: 'EXTERNAL',
|
|
24
27
|
};
|
|
25
28
|
|
|
@@ -34,10 +37,10 @@ const TYPE_PREFIXES = {
|
|
|
34
37
|
*/
|
|
35
38
|
export async function create(label, description, tags, membersIds, orgaIdOrName) {
|
|
36
39
|
const id = `ng_${crypto.randomUUID()}`;
|
|
37
|
-
const ownerId = await
|
|
40
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(orgaIdOrName);
|
|
38
41
|
|
|
39
42
|
if (membersIds?.length > 0) {
|
|
40
|
-
|
|
43
|
+
checkMembersToLink(membersIds);
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
const members = constructMembers(id, membersIds || []);
|
|
@@ -72,11 +75,11 @@ export async function destroy(ngIdOrLabel, orgaIdOrName) {
|
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
/**
|
|
75
|
-
* Get the
|
|
78
|
+
* Get the WireGuard configuration of a Network Group peer
|
|
76
79
|
* @param {object} peerIdOrLabel The Peer ID or Label
|
|
77
80
|
* @param {object} ngIdOrLabel The Network Group ID or Label
|
|
78
81
|
* @param {object} orgaIdOrName The owner ID or name
|
|
79
|
-
* @returns {Promise<Object>} The Peer
|
|
82
|
+
* @returns {Promise<Object>} The Peer WireGuard configuration
|
|
80
83
|
* @throws {Error} If the Peer is not found
|
|
81
84
|
* @throws {Error} If the Network Group is not found
|
|
82
85
|
* @throws {Error} If the Peer is not in the Network Group
|
|
@@ -116,13 +119,13 @@ export async function getPeerConfig(peerIdOrLabel, ngIdOrLabel, orgaIdOrName) {
|
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
/**
|
|
119
|
-
* Get a Network
|
|
122
|
+
* Get a Network Group from an owner with members and peers
|
|
120
123
|
* @param {string} networkGroupId The Network Group ID
|
|
121
124
|
* @param {string} orgaIdOrName The owner ID or name
|
|
122
125
|
* @returns {Promise<Array<Object>>} The Network Groups
|
|
123
126
|
*/
|
|
124
127
|
export async function getNG(networkGroupId, orgaIdOrName) {
|
|
125
|
-
const ownerId = await
|
|
128
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(orgaIdOrName);
|
|
126
129
|
|
|
127
130
|
Logger.info(`Get Network Group ${networkGroupId} for owner ${ownerId}`);
|
|
128
131
|
const result = await getNetworkGroup({ networkGroupId, ownerId }).then(sendToApi);
|
|
@@ -137,7 +140,7 @@ export async function getNG(networkGroupId, orgaIdOrName) {
|
|
|
137
140
|
* @returns {Promise<Array<Object>>} The Network Groups
|
|
138
141
|
*/
|
|
139
142
|
export async function getAllNGs(orgaIdOrName) {
|
|
140
|
-
const ownerId = await
|
|
143
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(orgaIdOrName);
|
|
141
144
|
|
|
142
145
|
Logger.info(`Listing Network Groups from owner ${ownerId}`);
|
|
143
146
|
const result = await listNetworkGroups({ ownerId }).then(sendToApi);
|
|
@@ -155,7 +158,7 @@ export async function getAllNGs(orgaIdOrName) {
|
|
|
155
158
|
* @returns {Promise<Object>} Found results
|
|
156
159
|
*/
|
|
157
160
|
export async function searchNgOrResource(idOrLabel, orgaIdOrName, type = 'all', exactMatch = true) {
|
|
158
|
-
const ownerId = await
|
|
161
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(orgaIdOrName);
|
|
159
162
|
|
|
160
163
|
// If idOrLabel is a string we use it, or we look through multiple keys
|
|
161
164
|
const query =
|
|
@@ -187,7 +190,7 @@ export async function searchNgOrResource(idOrLabel, orgaIdOrName, type = 'all',
|
|
|
187
190
|
|
|
188
191
|
if (filtered.length > 1 && type !== 'all') {
|
|
189
192
|
throw new Error(`Multiple resources found for ${styleText('red', query)}, use ID instead:
|
|
190
|
-
${filtered.map((f) => ` • ${f.id} ${styleText('grey', `(${f.label} - ${f.type})`)}`).join('\n')}`);
|
|
193
|
+
${filtered.map((f) => ` • ${f.id} ${styleText('grey', `(${f.domainName || f.label} - ${f.type})`)}`).join('\n')}`);
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
// Deduplicate results
|
|
@@ -203,14 +206,9 @@ ${filtered.map((f) => ` • ${f.id} ${styleText('grey', `(${f.label} - ${f.type}
|
|
|
203
206
|
export function constructMembers(ngId, membersIds) {
|
|
204
207
|
return membersIds.map((id) => {
|
|
205
208
|
const domainName = `${id}.m.${ngId}.${DOMAIN}`;
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
id,
|
|
210
|
-
domainName,
|
|
211
|
-
// Get kind from prefix match in id (app_*, addon_*, external_*) or default to 'APPLICATION'
|
|
212
|
-
kind: prefixToType[Object.keys(prefixToType).find((p) => id.startsWith(p))] ?? TYPE_PREFIXES.app_,
|
|
213
|
-
};
|
|
209
|
+
const prefix = Object.keys(NG_MEMBER_PREFIXES).find((p) => id.startsWith(p));
|
|
210
|
+
const kind = NG_MEMBER_PREFIXES[prefix];
|
|
211
|
+
return { id, domainName, kind };
|
|
214
212
|
});
|
|
215
213
|
}
|
|
216
214
|
|
|
@@ -266,12 +264,3 @@ async function pollNetworkGroup(ownerId, ngId, { waitForMembers = null, waitForD
|
|
|
266
264
|
pollOnce();
|
|
267
265
|
});
|
|
268
266
|
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get the owner ID from an Organisation ID or name
|
|
272
|
-
* @param {object} orgaIdOrName The Organisation ID or name
|
|
273
|
-
* @returns {Promise<string>} The owner ID
|
|
274
|
-
*/
|
|
275
|
-
async function getOwnerIdFromOrgaIdOrName(orgaIdOrName) {
|
|
276
|
-
return orgaIdOrName != null ? Organisation.getId(orgaIdOrName) : User.getCurrentId();
|
|
277
|
-
}
|
package/src/parsers.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import cliparse from 'cliparse';
|
|
2
2
|
import ISO8601 from 'iso8601-duration';
|
|
3
3
|
import * as Application from './models/application.js';
|
|
4
|
+
import { NG_MEMBER_PREFIXES } from './models/ng.js';
|
|
4
5
|
|
|
5
6
|
const addonOptionsRegex = /^[\w-]+=.+$/;
|
|
6
7
|
|
|
@@ -99,12 +100,13 @@ export function orgaIdOrName(string) {
|
|
|
99
100
|
const addonIdRegex = /^addon_[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
100
101
|
const operatorIdRegex =
|
|
101
102
|
/^(keycloak|otoroshi|matomo|metabase)_[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
103
|
+
const ulidRegex = /^(kubernetes)_[0-9A-HJ-NP-TV-Z]{26}$/i;
|
|
102
104
|
|
|
103
105
|
export function addonIdOrName(string) {
|
|
104
106
|
if (string.match(addonIdRegex)) {
|
|
105
107
|
return cliparse.parsers.success({ addon_id: string });
|
|
106
108
|
}
|
|
107
|
-
if (string.match(operatorIdRegex)) {
|
|
109
|
+
if (string.match(operatorIdRegex) || string.match(ulidRegex)) {
|
|
108
110
|
return cliparse.parsers.success({ operator_id: string });
|
|
109
111
|
}
|
|
110
112
|
return cliparse.parsers.success({ addon_name: string });
|
|
@@ -225,12 +227,12 @@ function parseSimpleDuration(durationStr) {
|
|
|
225
227
|
}
|
|
226
228
|
}
|
|
227
229
|
|
|
228
|
-
// Network
|
|
230
|
+
// Network Groups parsers
|
|
229
231
|
export function ngResourceType(string) {
|
|
230
232
|
if (string.startsWith('ng_')) {
|
|
231
233
|
return cliparse.parsers.success({ ngId: string });
|
|
232
234
|
}
|
|
233
|
-
if (
|
|
235
|
+
if (Object.keys(NG_MEMBER_PREFIXES).some((prefix) => string.startsWith(prefix))) {
|
|
234
236
|
return cliparse.parsers.success({ memberId: string });
|
|
235
237
|
}
|
|
236
238
|
return cliparse.parsers.success({ ngResourceLabel: string });
|