clever-tools 4.2.0 → 4.4.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/README.md +9 -9
- package/bin/clever.js +116 -14
- package/package.json +3 -2
- package/src/clever-client/drains.js +127 -0
- package/src/clever-client/k8s.js +96 -0
- package/src/clever-client/operators.js +16 -0
- package/src/commands/drain.js +118 -62
- package/src/commands/k8s.js +193 -0
- package/src/commands/otoroshi.js +12 -0
- package/src/experimental-features.js +27 -0
- package/src/lib/ascii.js +48 -0
- package/src/lib/k8s.js +126 -0
- package/src/lib/operator-commands.js +8 -0
- package/src/lib/prompts.js +2 -0
- package/src/models/drain.js +28 -132
- package/src/models/ids-resolver.js +11 -0
- package/src/models/ng.js +5 -15
- package/src/parsers.js +2 -1
package/src/models/drain.js
CHANGED
|
@@ -1,133 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
{
|
|
5
|
-
{
|
|
6
|
-
{
|
|
7
|
-
{
|
|
8
|
-
{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (keyExist(drainTargetConfig)) {
|
|
30
|
-
body.APIKey = drainTargetConfig.apiKey;
|
|
31
|
-
}
|
|
32
|
-
if (indexPrefixExist(drainTargetConfig)) {
|
|
33
|
-
body.indexPrefix = drainTargetConfig.indexPrefix;
|
|
34
|
-
}
|
|
35
|
-
if (structuredDataParametersExist(drainTargetConfig)) {
|
|
36
|
-
body.structuredDataParameters = drainTargetConfig.structuredDataParameters;
|
|
37
|
-
}
|
|
38
|
-
return body;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function authorizeDrainCreation(drainTargetType, drainTargetCredentials, drainTargetConfig) {
|
|
42
|
-
if (drainTypeExists(drainTargetType)) {
|
|
43
|
-
// retrieve field for drain type ('mandatory', 'optional', undefined)
|
|
44
|
-
const credStatus = fieldStatus(drainTargetType).credentials;
|
|
45
|
-
const keyStatus = fieldStatus(drainTargetType).apiKey;
|
|
46
|
-
const indexPrefixStatus = fieldStatus(drainTargetType).indexPrefix;
|
|
47
|
-
const structuredDataParametersStatus = fieldStatus(drainTargetType).structuredDataParameters;
|
|
48
|
-
|
|
49
|
-
if (credStatus === 'MANDATORY') {
|
|
50
|
-
return credentialsExist(drainTargetCredentials);
|
|
51
|
-
}
|
|
52
|
-
if (credStatus === 'OPTIONAL') {
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
if (!credStatus && !keyStatus) {
|
|
56
|
-
return credentialsEmpty(drainTargetCredentials);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (keyStatus === 'MANDATORY') {
|
|
60
|
-
return keyExist(drainTargetConfig);
|
|
61
|
-
}
|
|
62
|
-
if (keyStatus === 'OPTIONAL') {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
if (!keyStatus) {
|
|
66
|
-
return keyEmpty(drainTargetConfig);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (indexPrefixStatus === 'MANDATORY') {
|
|
70
|
-
return indexPrefixExist(drainTargetConfig);
|
|
71
|
-
}
|
|
72
|
-
if (indexPrefixStatus === 'OPTIONAL') {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
if (!indexPrefixStatus) {
|
|
76
|
-
return indexPrefixEmpty(drainTargetConfig);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (structuredDataParametersStatus === 'MANDATORY') {
|
|
80
|
-
return structuredDataParametersExist(drainTargetConfig);
|
|
81
|
-
}
|
|
82
|
-
if (structuredDataParametersStatus === 'OPTIONAL') {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
if (!structuredDataParametersStatus) {
|
|
86
|
-
return structuredDataParametersEmpty(drainTargetConfig);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function fieldStatus(drainTargetType) {
|
|
92
|
-
return DRAIN_TYPES.find(({ id }) => id === drainTargetType);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function drainTypeExists(drainTargetType) {
|
|
96
|
-
return DRAIN_TYPES.some(({ id }) => id === drainTargetType);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function credentialsExist({ username, password }) {
|
|
100
|
-
return username != null && password != null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function credentialsEmpty({ username, password }) {
|
|
104
|
-
return username == null && password == null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function indexPrefixExist({ indexPrefix }) {
|
|
108
|
-
return indexPrefix != null;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function indexPrefixEmpty({ indexPrefix }) {
|
|
112
|
-
return indexPrefix == null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function structuredDataParametersExist({ structuredDataParameters }) {
|
|
116
|
-
return structuredDataParameters != null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function structuredDataParametersEmpty({ structuredDataParameters }) {
|
|
120
|
-
return structuredDataParameters == null;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function keyExist({ apiKey }) {
|
|
124
|
-
return apiKey != null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function keyEmpty({ apiKey }) {
|
|
128
|
-
return apiKey == null;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export function listDrainTypes() {
|
|
132
|
-
return cliparse.autocomplete.words(DRAIN_TYPES.map((type) => type.id));
|
|
1
|
+
export const DRAIN_TYPES = {
|
|
2
|
+
DATADOG: { apiCode: 'DATADOG', cliCode: 'datadog', label: 'Datadog' },
|
|
3
|
+
ELASTICSEARCH: { apiCode: 'ELASTICSEARCH', cliCode: 'elasticsearch', label: 'Elasticsearch' },
|
|
4
|
+
NEWRELIC: { apiCode: 'NEWRELIC', cliCode: 'newrelic', label: 'New Relic' },
|
|
5
|
+
OVH_TCP: { apiCode: 'OVH_TCP', cliCode: 'ovh-tcp', label: 'OVH TCP' },
|
|
6
|
+
RAW_HTTP: { apiCode: 'RAW_HTTP', cliCode: 'raw-http', label: 'Raw HTTP' },
|
|
7
|
+
SYSLOG_TCP: { apiCode: 'SYSLOG_TCP', cliCode: 'syslog-tcp', label: 'Syslog TCP' },
|
|
8
|
+
SYSLOG_UDP: { apiCode: 'SYSLOG_UDP', cliCode: 'syslog-udp', label: 'Syslog UDP' },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const DRAIN_TYPE_CLI_CODES = Object.values(DRAIN_TYPES).map(({ cliCode }) => cliCode);
|
|
12
|
+
|
|
13
|
+
export function formatDrain(rawDrain) {
|
|
14
|
+
const drainType = DRAIN_TYPES[rawDrain.recipient.type];
|
|
15
|
+
const drainDetails = [
|
|
16
|
+
['ID', rawDrain.id],
|
|
17
|
+
['Status', rawDrain.status.status],
|
|
18
|
+
['Execution status', rawDrain.execution.status],
|
|
19
|
+
['URL', rawDrain.recipient.url],
|
|
20
|
+
['Type', drainType.label],
|
|
21
|
+
['Custom index', rawDrain.recipient.index],
|
|
22
|
+
['SD parameters', rawDrain.recipient.rfc5424StructuredDataParameters],
|
|
23
|
+
['Message output rate', Math.floor(rawDrain.backlog.msgRateOut * 60) + ' messages/minute'],
|
|
24
|
+
['Message throughput', Math.floor(rawDrain.backlog.msgRateOut) + ' bytes/second'],
|
|
25
|
+
['Backlog', rawDrain.backlog.msgBacklog + ' pending messages'],
|
|
26
|
+
['Last error', rawDrain.execution.lastError],
|
|
27
|
+
];
|
|
28
|
+
return Object.fromEntries(drainDetails.filter(([_name, value]) => value != null));
|
|
133
29
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getSummary } from '@clevercloud/client/esm/api/v2/user.js';
|
|
2
2
|
import { Logger } from '../logger.js';
|
|
3
|
+
import * as User from '../models/user.js';
|
|
3
4
|
import { loadIdsCache, writeIdsCache } from './configuration.js';
|
|
5
|
+
import * as Organisation from './organisation.js';
|
|
4
6
|
import { sendToApi } from './send-to-api.js';
|
|
5
7
|
|
|
6
8
|
/*
|
|
@@ -171,3 +173,12 @@ export async function findAddonsByAddonProvider(provider) {
|
|
|
171
173
|
|
|
172
174
|
return candidates;
|
|
173
175
|
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get the owner ID from an Organisation ID or name
|
|
179
|
+
* @param {object} orgIdOrName The Organisation ID or name
|
|
180
|
+
* @returns {Promise<string>} The owner ID
|
|
181
|
+
*/
|
|
182
|
+
export async function getOwnerIdFromOrgIdOrName(orgIdOrName) {
|
|
183
|
+
return orgIdOrName != null ? Organisation.getId(orgIdOrName) : User.getCurrentId();
|
|
184
|
+
}
|
package/src/models/ng.js
CHANGED
|
@@ -9,10 +9,9 @@ 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;
|
|
@@ -38,7 +37,7 @@ export const NG_MEMBER_PREFIXES = {
|
|
|
38
37
|
*/
|
|
39
38
|
export async function create(label, description, tags, membersIds, orgaIdOrName) {
|
|
40
39
|
const id = `ng_${crypto.randomUUID()}`;
|
|
41
|
-
const ownerId = await
|
|
40
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(orgaIdOrName);
|
|
42
41
|
|
|
43
42
|
if (membersIds?.length > 0) {
|
|
44
43
|
checkMembersToLink(membersIds);
|
|
@@ -126,7 +125,7 @@ export async function getPeerConfig(peerIdOrLabel, ngIdOrLabel, orgaIdOrName) {
|
|
|
126
125
|
* @returns {Promise<Array<Object>>} The Network Groups
|
|
127
126
|
*/
|
|
128
127
|
export async function getNG(networkGroupId, orgaIdOrName) {
|
|
129
|
-
const ownerId = await
|
|
128
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(orgaIdOrName);
|
|
130
129
|
|
|
131
130
|
Logger.info(`Get Network Group ${networkGroupId} for owner ${ownerId}`);
|
|
132
131
|
const result = await getNetworkGroup({ networkGroupId, ownerId }).then(sendToApi);
|
|
@@ -141,7 +140,7 @@ export async function getNG(networkGroupId, orgaIdOrName) {
|
|
|
141
140
|
* @returns {Promise<Array<Object>>} The Network Groups
|
|
142
141
|
*/
|
|
143
142
|
export async function getAllNGs(orgaIdOrName) {
|
|
144
|
-
const ownerId = await
|
|
143
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(orgaIdOrName);
|
|
145
144
|
|
|
146
145
|
Logger.info(`Listing Network Groups from owner ${ownerId}`);
|
|
147
146
|
const result = await listNetworkGroups({ ownerId }).then(sendToApi);
|
|
@@ -159,7 +158,7 @@ export async function getAllNGs(orgaIdOrName) {
|
|
|
159
158
|
* @returns {Promise<Object>} Found results
|
|
160
159
|
*/
|
|
161
160
|
export async function searchNgOrResource(idOrLabel, orgaIdOrName, type = 'all', exactMatch = true) {
|
|
162
|
-
const ownerId = await
|
|
161
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(orgaIdOrName);
|
|
163
162
|
|
|
164
163
|
// If idOrLabel is a string we use it, or we look through multiple keys
|
|
165
164
|
const query =
|
|
@@ -265,12 +264,3 @@ async function pollNetworkGroup(ownerId, ngId, { waitForMembers = null, waitForD
|
|
|
265
264
|
pollOnce();
|
|
266
265
|
});
|
|
267
266
|
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Get the owner ID from an Organisation ID or name
|
|
271
|
-
* @param {object} orgaIdOrName The Organisation ID or name
|
|
272
|
-
* @returns {Promise<string>} The owner ID
|
|
273
|
-
*/
|
|
274
|
-
async function getOwnerIdFromOrgaIdOrName(orgaIdOrName) {
|
|
275
|
-
return orgaIdOrName != null ? Organisation.getId(orgaIdOrName) : User.getCurrentId();
|
|
276
|
-
}
|
package/src/parsers.js
CHANGED
|
@@ -100,12 +100,13 @@ export function orgaIdOrName(string) {
|
|
|
100
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;
|
|
101
101
|
const operatorIdRegex =
|
|
102
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;
|
|
103
104
|
|
|
104
105
|
export function addonIdOrName(string) {
|
|
105
106
|
if (string.match(addonIdRegex)) {
|
|
106
107
|
return cliparse.parsers.success({ addon_id: string });
|
|
107
108
|
}
|
|
108
|
-
if (string.match(operatorIdRegex)) {
|
|
109
|
+
if (string.match(operatorIdRegex) || string.match(ulidRegex)) {
|
|
109
110
|
return cliparse.parsers.success({ operator_id: string });
|
|
110
111
|
}
|
|
111
112
|
return cliparse.parsers.success({ addon_name: string });
|