clever-tools 3.10.1 → 3.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/README.md +1 -1
- package/bin/clever.js +206 -5
- package/package.json +5 -3
- package/src/clever-client/auth-bridge.js +61 -0
- package/src/clever-client/ng.js +18 -0
- package/src/commands/addon.js +38 -19
- package/src/commands/config.js +3 -3
- package/src/commands/console.js +3 -4
- package/src/commands/curl.js +17 -17
- package/src/commands/database.js +9 -9
- package/src/commands/domain.js +13 -16
- package/src/commands/env.js +15 -8
- package/src/commands/features.js +93 -0
- package/src/commands/kv.js +93 -0
- package/src/commands/link.js +4 -2
- package/src/commands/ng.js +202 -0
- package/src/commands/profile.js +16 -7
- package/src/commands/published-config.js +7 -7
- package/src/commands/stop.js +2 -2
- package/src/commands/tcp-redirs.js +5 -5
- package/src/commands/tokens.js +137 -0
- package/src/experimental-features.js +61 -0
- package/src/lib/ng-print.js +195 -0
- package/src/logger.js +3 -0
- package/src/models/activity.js +2 -3
- package/src/models/addon.js +4 -4
- package/src/models/application.js +26 -16
- package/src/models/configuration.js +51 -15
- package/src/models/ids-resolver.js +38 -1
- package/src/models/namespaces.js +3 -2
- package/src/models/ng-resources.js +270 -0
- package/src/models/ng.js +276 -0
- package/src/models/send-to-api.js +19 -8
- package/src/parsers.js +57 -3
- package/src/prompt-password.js +10 -0
package/src/commands/database.js
CHANGED
|
@@ -2,8 +2,8 @@ import { sendToApi } from '../models/send-to-api.js';
|
|
|
2
2
|
import { getBackups } from '@clevercloud/client/esm/api/v2/backups.js';
|
|
3
3
|
import { Logger } from '../logger.js';
|
|
4
4
|
import { formatTable as initFormatTable } from '../format-table.js';
|
|
5
|
-
import superagent from 'superagent';
|
|
6
5
|
import fs from 'node:fs';
|
|
6
|
+
import { Writable } from 'node:stream';
|
|
7
7
|
import { findOwnerId } from '../models/addon.js';
|
|
8
8
|
import { resolveRealId, resolveAddonId } from '../models/ids-resolver.js';
|
|
9
9
|
|
|
@@ -80,14 +80,14 @@ export async function downloadBackups (params) {
|
|
|
80
80
|
throw new Error('no backup with this ID');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (output) {
|
|
88
|
-
fs.writeFileSync(output, res.body);
|
|
89
|
-
return;
|
|
83
|
+
const response = await globalThis.fetch(backup.download_url);
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
throw new Error('Failed to download backup');
|
|
90
86
|
}
|
|
91
87
|
|
|
92
|
-
|
|
88
|
+
await response
|
|
89
|
+
.body
|
|
90
|
+
.pipeTo(
|
|
91
|
+
Writable.toWeb(output ? fs.createWriteStream(output) : process.stdout),
|
|
92
|
+
);
|
|
93
93
|
}
|
package/src/commands/domain.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
removeDomain,
|
|
10
10
|
unmarkFavouriteDomain,
|
|
11
11
|
} from '@clevercloud/client/esm/api/v2/application.js';
|
|
12
|
-
import { getSummary } from '@clevercloud/client/
|
|
12
|
+
import { getSummary } from '@clevercloud/client/esm/api/v2/user.js';
|
|
13
13
|
import { sendToApi } from '../models/send-to-api.js';
|
|
14
14
|
import colors from 'colors/safe.js';
|
|
15
15
|
import { parse as parseDomain } from 'tldts';
|
|
@@ -17,6 +17,7 @@ import { diagDomainConfig } from '@clevercloud/client/esm/utils/diag-domain-conf
|
|
|
17
17
|
import { sortDomains } from '@clevercloud/client/esm/utils/domains.js';
|
|
18
18
|
import { DnsResolver } from '../models/node-dns-resolver.js';
|
|
19
19
|
import _ from 'lodash';
|
|
20
|
+
import { getDefaultLoadBalancersDnsInfo } from '@clevercloud/client/esm/api/v4/load-balancers.js';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* @typedef {import('@clevercloud/client/esm/utils/diag-domain-config.types.js').DomainInfo} DomainInfo
|
|
@@ -110,7 +111,7 @@ export async function diagApplication (params) {
|
|
|
110
111
|
let hasError = false;
|
|
111
112
|
|
|
112
113
|
const app = await getApp({ id: ownerId, appId }).then(sendToApi);
|
|
113
|
-
const expectedDnsForPublicLoadBalancer = await getDefaultLoadBalancersDnsInfo({
|
|
114
|
+
const expectedDnsForPublicLoadBalancer = await getDefaultLoadBalancersDnsInfo({ ownerId, appId }).then(sendToApi);
|
|
114
115
|
|
|
115
116
|
const loadBalancerDnsConfig = {
|
|
116
117
|
aRecords: expectedDnsForPublicLoadBalancer[0].dns.a,
|
|
@@ -181,7 +182,16 @@ export async function overview (params) {
|
|
|
181
182
|
|
|
182
183
|
const applicationsWithDomains = await Promise.all(
|
|
183
184
|
applications.map(async (app) => {
|
|
184
|
-
const domains = await getAllDomains({ id: app.ownerId, appId: app.id })
|
|
185
|
+
const domains = await getAllDomains({ id: app.ownerId, appId: app.id })
|
|
186
|
+
.then(sendToApi)
|
|
187
|
+
.catch((error) => {
|
|
188
|
+
// if the user cannot access application domains, we simply act as if there were no domains
|
|
189
|
+
if (error?.response?.status === 403) {
|
|
190
|
+
Logger.printErrorLine(`You cannot list domains for application ${app.name} (${app.id})`);
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
throw error;
|
|
194
|
+
});
|
|
185
195
|
return { app, domains };
|
|
186
196
|
}),
|
|
187
197
|
);
|
|
@@ -351,19 +361,6 @@ function reportDomainDiagnostics ({ hostname, pathPrefix, resolvedDnsConfig, dia
|
|
|
351
361
|
}
|
|
352
362
|
}
|
|
353
363
|
|
|
354
|
-
// TODO: put in clever client
|
|
355
|
-
function getDefaultLoadBalancersDnsInfo (params) {
|
|
356
|
-
return Promise.resolve({
|
|
357
|
-
method: 'get',
|
|
358
|
-
url: `/v4/load-balancers/organisations/${params.id}/applications/${params.appId}/load-balancers/default`,
|
|
359
|
-
headers: {
|
|
360
|
-
Accept: 'application/json',
|
|
361
|
-
},
|
|
362
|
-
// no query params
|
|
363
|
-
// no body
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
|
|
367
364
|
/**
|
|
368
365
|
* Parses domain information from vhosts array
|
|
369
366
|
*
|
package/src/commands/env.js
CHANGED
|
@@ -4,16 +4,23 @@ import colors from 'colors/safe.js';
|
|
|
4
4
|
import * as variables from '../models/variables.js';
|
|
5
5
|
import { sendToApi } from '../models/send-to-api.js';
|
|
6
6
|
import { toNameEqualsValueString, validateName } from '@clevercloud/client/esm/utils/env-vars.js';
|
|
7
|
-
import
|
|
7
|
+
import {
|
|
8
|
+
getAllEnvVars,
|
|
9
|
+
getAllEnvVarsForAddons,
|
|
10
|
+
getAllEnvVarsForDependencies,
|
|
11
|
+
updateEnvVar,
|
|
12
|
+
updateAllEnvVars,
|
|
13
|
+
removeEnvVar,
|
|
14
|
+
} from '@clevercloud/client/esm/api/v2/application.js';
|
|
8
15
|
|
|
9
16
|
export async function list (params) {
|
|
10
17
|
const { alias, app: appIdOrName, 'add-export': addExportsOption, format } = params.options;
|
|
11
18
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
12
19
|
|
|
13
20
|
const [envFromApp, envFromAddons, envFromDeps] = await Promise.all([
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
getAllEnvVars({ id: ownerId, appId }).then(sendToApi),
|
|
22
|
+
getAllEnvVarsForAddons({ id: ownerId, appId }).then(sendToApi),
|
|
23
|
+
getAllEnvVarsForDependencies({ id: ownerId, appId }).then(sendToApi),
|
|
17
24
|
]);
|
|
18
25
|
|
|
19
26
|
switch (format) {
|
|
@@ -69,7 +76,7 @@ export async function set (params) {
|
|
|
69
76
|
|
|
70
77
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
71
78
|
|
|
72
|
-
await
|
|
79
|
+
await updateEnvVar({ id: ownerId, appId, envName }, { value }).then(sendToApi);
|
|
73
80
|
|
|
74
81
|
Logger.println('Your environment variable has been successfully saved');
|
|
75
82
|
};
|
|
@@ -79,7 +86,7 @@ export async function rm (params) {
|
|
|
79
86
|
const { alias, app: appIdOrName } = params.options;
|
|
80
87
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
81
88
|
|
|
82
|
-
await
|
|
89
|
+
await removeEnvVar({ id: ownerId, appId, envName }).then(sendToApi);
|
|
83
90
|
|
|
84
91
|
Logger.println('Your environment variable has been successfully removed');
|
|
85
92
|
};
|
|
@@ -90,7 +97,7 @@ export async function importEnv (params) {
|
|
|
90
97
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
91
98
|
|
|
92
99
|
const envVars = await variables.readVariablesFromStdin(format);
|
|
93
|
-
await
|
|
100
|
+
await updateAllEnvVars({ id: ownerId, appId }, envVars).then(sendToApi);
|
|
94
101
|
|
|
95
102
|
Logger.println('Environment variables have been set');
|
|
96
103
|
};
|
|
@@ -110,7 +117,7 @@ export async function importVarsFromLocalEnv (params) {
|
|
|
110
117
|
|
|
111
118
|
for (const envName of envNames) {
|
|
112
119
|
const value = process.env[envName] || '';
|
|
113
|
-
await
|
|
120
|
+
await updateEnvVar({ id: ownerId, appId, envName }, { value }).then(sendToApi);
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
Logger.println('Your environment variables have been successfully saved');
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { getFeatures, setFeature } from '../models/configuration.js';
|
|
2
|
+
import { EXPERIMENTAL_FEATURES } from '../experimental-features.js';
|
|
3
|
+
import { formatTable as initFormatTable } from '../format-table.js';
|
|
4
|
+
import { Logger } from '../logger.js';
|
|
5
|
+
|
|
6
|
+
const formatTable = initFormatTable();
|
|
7
|
+
|
|
8
|
+
export async function list (params) {
|
|
9
|
+
const { format } = params.options;
|
|
10
|
+
|
|
11
|
+
const featuresConf = await getFeatures();
|
|
12
|
+
// Add status from configuration file and remove instructions
|
|
13
|
+
const features = Object.entries(EXPERIMENTAL_FEATURES).map(([id, feature]) => {
|
|
14
|
+
const enabled = featuresConf[id] === true;
|
|
15
|
+
return {
|
|
16
|
+
id,
|
|
17
|
+
status: feature.status,
|
|
18
|
+
description: feature.description,
|
|
19
|
+
enabled,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// For each feature, print the object with the id, status, description and enabled
|
|
24
|
+
switch (format) {
|
|
25
|
+
case 'json': {
|
|
26
|
+
Logger.printJson(features);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
case 'human':
|
|
30
|
+
default: {
|
|
31
|
+
const headers = ['ID', 'STATUS', 'DESCRIPTION', 'ENABLED'];
|
|
32
|
+
|
|
33
|
+
Logger.println(formatTable([
|
|
34
|
+
headers,
|
|
35
|
+
...features.map((feature) => [
|
|
36
|
+
feature.id,
|
|
37
|
+
feature.status,
|
|
38
|
+
feature.description,
|
|
39
|
+
feature.enabled,
|
|
40
|
+
]),
|
|
41
|
+
]));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function info (params) {
|
|
47
|
+
const { feature } = params.namedArgs;
|
|
48
|
+
const availableFeatures = Object.keys(EXPERIMENTAL_FEATURES);
|
|
49
|
+
|
|
50
|
+
if (!availableFeatures.includes(feature)) {
|
|
51
|
+
throw new Error(`Unavailable feature: ${feature}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Logger.println(EXPERIMENTAL_FEATURES[feature].instructions);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function enable (params) {
|
|
58
|
+
const { features } = params.namedArgs;
|
|
59
|
+
const availableFeatures = Object.keys(EXPERIMENTAL_FEATURES);
|
|
60
|
+
|
|
61
|
+
const unknownFeatures = features.filter((feature) => !availableFeatures.includes(feature));
|
|
62
|
+
if (unknownFeatures.length > 0) {
|
|
63
|
+
throw new Error(`Unavailable feature(s): ${unknownFeatures.join(', ')}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const featureName of features) {
|
|
67
|
+
await setFeature(featureName, true);
|
|
68
|
+
Logger.println(`Experimental feature '${featureName}' enabled`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (features.length === 1) {
|
|
72
|
+
Logger.println(EXPERIMENTAL_FEATURES[features[0]].instructions);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
Logger.println();
|
|
76
|
+
Logger.println("To learn more about these experimental features, use 'clever features info FEATURE_NAME'");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function disable (params) {
|
|
81
|
+
const { features } = params.namedArgs;
|
|
82
|
+
const availableFeatures = Object.keys(EXPERIMENTAL_FEATURES);
|
|
83
|
+
|
|
84
|
+
const unknownFeatures = features.filter((feature) => !availableFeatures.includes(feature));
|
|
85
|
+
if (unknownFeatures.length > 0) {
|
|
86
|
+
throw new Error(`Unavailable feature(s): ${unknownFeatures.join(', ')}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const featureName of features) {
|
|
90
|
+
await setFeature(featureName, false);
|
|
91
|
+
Logger.println(`Experimental feature '${featureName}' disabled`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
import colors from 'colors/safe.js';
|
|
3
|
+
import { Logger } from '../logger.js';
|
|
4
|
+
import { sendToApi } from '../models/send-to-api.js';
|
|
5
|
+
import { getAllEnvVars } from '@clevercloud/client/esm/api/v2/addon.js';
|
|
6
|
+
import { findAddonsByNameOrId } from '../models/ids-resolver.js';
|
|
7
|
+
|
|
8
|
+
const URL_ENV_KEY = 'REDIS_URL';
|
|
9
|
+
const MAX_RETRIES_PER_REQUEST = 1;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Send a raw command to a compatible KV database
|
|
13
|
+
* @param {Object} params
|
|
14
|
+
* @param {Array<string>} params.args
|
|
15
|
+
* @param {Object} params.options
|
|
16
|
+
* @param {string} params.options.format
|
|
17
|
+
* @returns {Promise<void>}
|
|
18
|
+
*/
|
|
19
|
+
export async function sendRawCommand (params) {
|
|
20
|
+
const [addonIdOrRealIdOrName] = params.args;
|
|
21
|
+
const { org, format } = params.options;
|
|
22
|
+
|
|
23
|
+
const addons = await findAddonsByNameOrId(addonIdOrRealIdOrName, org);
|
|
24
|
+
|
|
25
|
+
if (addons.length === 0) {
|
|
26
|
+
throw new Error(`Add-on ${addonIdOrRealIdOrName} not found`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (addons.length > 1) {
|
|
30
|
+
const formattedAddons = addons
|
|
31
|
+
.map(({ addonId, ownerId }) => `\n${colors.grey(`- ${addonId} (${ownerId})`)}`)
|
|
32
|
+
.join('');
|
|
33
|
+
throw new Error(`Several add-ons found for '${addonIdOrRealIdOrName}', use ID instead:${formattedAddons}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { addonId, ownerId } = addons[0];
|
|
37
|
+
|
|
38
|
+
const url = await getAddonUrl(ownerId, addonId);
|
|
39
|
+
|
|
40
|
+
Logger.debug(`Extracted command: ${params.args.join(' ')}`);
|
|
41
|
+
const command = params.args.slice(1);
|
|
42
|
+
|
|
43
|
+
const result = await sendCommand(url, command);
|
|
44
|
+
|
|
45
|
+
switch (format) {
|
|
46
|
+
case 'json': {
|
|
47
|
+
Logger.printJson(result);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case 'human':
|
|
51
|
+
default: {
|
|
52
|
+
Logger.println(result);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get the URL of the compatible KV database
|
|
59
|
+
* @param {string} ownerId
|
|
60
|
+
* @param {string} addonId
|
|
61
|
+
* @returns {Promise<string>} the URL of the compatible KV database
|
|
62
|
+
*/
|
|
63
|
+
async function getAddonUrl (ownerId, addonId) {
|
|
64
|
+
|
|
65
|
+
const envVars = await getAllEnvVars({ id: ownerId, addonId }).then(sendToApi);
|
|
66
|
+
const redisUrl = envVars.find((env) => env.name === URL_ENV_KEY)?.value;
|
|
67
|
+
|
|
68
|
+
if (!redisUrl) {
|
|
69
|
+
throw new Error(`Environment variable ${colors.red(URL_ENV_KEY)} not found, is it a Materia KV or Redis® add-on?`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return redisUrl;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Send a command to a compatible KV database
|
|
77
|
+
* @param {string} url
|
|
78
|
+
* @param {Array<string>} command
|
|
79
|
+
* @returns {Promise<string>} the command result
|
|
80
|
+
*/
|
|
81
|
+
async function sendCommand (url, command) {
|
|
82
|
+
Logger.debug(`Sending command '${command.join(' ')}' to ${url}`);
|
|
83
|
+
const client = new Redis(url, { maxRetriesPerRequest: MAX_RETRIES_PER_REQUEST });
|
|
84
|
+
try {
|
|
85
|
+
const result = await client.call(...command);
|
|
86
|
+
Logger.debug(`Command result: ${result}`);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
await client.disconnect();
|
|
91
|
+
Logger.debug('Disconnected from server');
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/commands/link.js
CHANGED
|
@@ -7,9 +7,11 @@ export async function link (params) {
|
|
|
7
7
|
|
|
8
8
|
if (app.app_id != null && orgaIdOrName != null) {
|
|
9
9
|
Logger.warn('You\'ve specified a unique application ID, organisation option will be ignored');
|
|
10
|
+
await Application.linkRepo(app, null, alias);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
await Application.linkRepo(app, orgaIdOrName, alias);
|
|
10
14
|
}
|
|
11
|
-
|
|
12
|
-
await Application.linkRepo(app, orgaIdOrName, alias);
|
|
13
15
|
|
|
14
16
|
Logger.println('Your application has been successfully linked!');
|
|
15
17
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import colors from 'colors/safe.js';
|
|
2
|
+
import * as networkGroup from '../models/ng.js';
|
|
3
|
+
import * as networkGroupResources from '../models/ng-resources.js';
|
|
4
|
+
|
|
5
|
+
import { Logger } from '../logger.js';
|
|
6
|
+
import { printResults } from '../lib/ng-print.js';
|
|
7
|
+
|
|
8
|
+
/** Create a Network Group
|
|
9
|
+
* @param {Object} params
|
|
10
|
+
* @param {Array<Object>} params.args
|
|
11
|
+
* @param {Object} params.args[0] Network Group label
|
|
12
|
+
* @param {string} params.options.description Network Group description
|
|
13
|
+
* @param {Array<string>} params.options.link Array of member IDs or labels to link to the Network Group
|
|
14
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
15
|
+
* @param {string} params.options.tags Comma-separated list of tags
|
|
16
|
+
*/
|
|
17
|
+
export async function createNg (params) {
|
|
18
|
+
const [ngLabel] = params.args;
|
|
19
|
+
const label = ngLabel.ngResourceLabel;
|
|
20
|
+
const { description, link: membersIds, org, tags } = params.options;
|
|
21
|
+
|
|
22
|
+
await networkGroup.create(label, description, tags, membersIds, org);
|
|
23
|
+
|
|
24
|
+
const successMessage = `Network Group ${colors.green(label)} successfully created`;
|
|
25
|
+
if (membersIds == null) {
|
|
26
|
+
Logger.printSuccess(`${successMessage}!`);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
Logger.printSuccess(`${successMessage} with member(s):`);
|
|
30
|
+
Logger.println(membersIds.map((id) => colors.grey(` - ${id}`)).join('\n'));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Delete a Network Group
|
|
35
|
+
* @param {Object} params
|
|
36
|
+
* @param {Object} params.args[0] Network Group ID or label
|
|
37
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
38
|
+
*/
|
|
39
|
+
export async function deleteNg (params) {
|
|
40
|
+
const [ngIdOrLabel] = params.args;
|
|
41
|
+
const { org } = params.options;
|
|
42
|
+
|
|
43
|
+
await networkGroup.destroy(ngIdOrLabel, org);
|
|
44
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
45
|
+
Logger.printSuccess(`Network Group ${colors.green(ngText)} successfully deleted!`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Create an external peer in a Network Group
|
|
49
|
+
* @param {Object} params
|
|
50
|
+
* @param {Object} params.args[0] External peer ID or label
|
|
51
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
52
|
+
* @param {string} params.args[2] Wireguard public key
|
|
53
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
54
|
+
*/
|
|
55
|
+
export async function createExternalPeer (params) {
|
|
56
|
+
const [peerIdOrLabel, ngIdOrLabel, publicKey] = params.args;
|
|
57
|
+
const { org } = params.options;
|
|
58
|
+
|
|
59
|
+
await networkGroupResources.createExternalPeerWithParent(ngIdOrLabel, peerIdOrLabel.ngResourceLabel, publicKey, org);
|
|
60
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
61
|
+
Logger.printSuccess(`External peer ${colors.green(peerIdOrLabel.ngResourceLabel)} successfully created in Network Group ${colors.green(ngText)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Delete an external peer from a Network Group
|
|
65
|
+
* @param {Object} params
|
|
66
|
+
* @param {Object} params.args[0] External peer ID or label
|
|
67
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
68
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
69
|
+
*/
|
|
70
|
+
export async function deleteExternalPeer (params) {
|
|
71
|
+
const [peerIdOrLabel, ngIdOrLabel] = params.args;
|
|
72
|
+
const { org } = params.options;
|
|
73
|
+
|
|
74
|
+
const peerText = peerIdOrLabel.ngResourceLabel ?? peerIdOrLabel.memberId;
|
|
75
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
76
|
+
await networkGroupResources.deleteExternalPeerWithParent(ngIdOrLabel, peerText, org);
|
|
77
|
+
Logger.printSuccess(`External peer ${colors.green(peerText)} successfully deleted from Network Group ${colors.green(ngText)}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Link a member to a Network Group
|
|
81
|
+
* @param {Object} params
|
|
82
|
+
* @param {string} params.args[0] Member ID
|
|
83
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
84
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
85
|
+
*/
|
|
86
|
+
export async function linkToNg (params) {
|
|
87
|
+
const [resourceId, ngIdOrLabel] = params.args;
|
|
88
|
+
const { org } = params.options;
|
|
89
|
+
|
|
90
|
+
await networkGroupResources.linkMember(ngIdOrLabel, resourceId.memberId, org);
|
|
91
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
92
|
+
Logger.printSuccess(`Member ${colors.green(resourceId.memberId)} successfully linked to Network Group ${colors.green(ngText)}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Unlink a member from a Network Group
|
|
96
|
+
* @param {Object} params
|
|
97
|
+
* @param {string} params.args[0] Member ID
|
|
98
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
99
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
100
|
+
*/
|
|
101
|
+
export async function unlinkFromNg (params) {
|
|
102
|
+
const [resourceId, ngIdOrLabel] = params.args;
|
|
103
|
+
const { org } = params.options;
|
|
104
|
+
|
|
105
|
+
await networkGroupResources.unlinkMember(ngIdOrLabel, resourceId.memberId, org);
|
|
106
|
+
const ngText = ngIdOrLabel.ngResourceLabel ?? ngIdOrLabel.ngId;
|
|
107
|
+
Logger.printSuccess(`Member ${colors.green(resourceId.memberId)} successfully unlinked from Network Group ${colors.green(ngText)}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Print the configuration of a Network Group's peer
|
|
111
|
+
* @param {Object} params
|
|
112
|
+
* @param {Object} params.args[0] Peer ID or label
|
|
113
|
+
* @param {Object} params.args[1] Network Group ID or label
|
|
114
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
115
|
+
* @param {string} params.options.format Output format
|
|
116
|
+
*/
|
|
117
|
+
export async function getPeerConfig (params) {
|
|
118
|
+
const [peerIdOrLabel, ngIdOrLabel] = params.args;
|
|
119
|
+
const { org, format } = params.options;
|
|
120
|
+
|
|
121
|
+
const config = await networkGroup.getPeerConfig(peerIdOrLabel, ngIdOrLabel, org);
|
|
122
|
+
|
|
123
|
+
switch (format) {
|
|
124
|
+
case 'json': {
|
|
125
|
+
Logger.printJson(config);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case 'human':
|
|
129
|
+
default: {
|
|
130
|
+
const decodedConfiguration = Buffer.from(config.configuration, 'base64').toString('utf8');
|
|
131
|
+
Logger.println(decodedConfiguration);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** List Network Groups, their members and peers
|
|
137
|
+
* @param {Object} params
|
|
138
|
+
* @param {Object} params.options.orgaIdOrName Organisation ID or name
|
|
139
|
+
* @param {string} params.options.format Output format
|
|
140
|
+
*/
|
|
141
|
+
export async function listNg (params) {
|
|
142
|
+
const { org, format } = params.options;
|
|
143
|
+
|
|
144
|
+
const ngs = await networkGroup.getAllNGs(org);
|
|
145
|
+
|
|
146
|
+
switch (format) {
|
|
147
|
+
case 'json': {
|
|
148
|
+
Logger.printJson(ngs);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
case 'human':
|
|
152
|
+
default: {
|
|
153
|
+
if (!ngs.length) {
|
|
154
|
+
Logger.println(`ℹ️ No Network Group found, create one with ${colors.blue('clever ng create')} command`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const ngList = ngs.map(({
|
|
158
|
+
id,
|
|
159
|
+
label,
|
|
160
|
+
networkIp,
|
|
161
|
+
members,
|
|
162
|
+
peers,
|
|
163
|
+
}) => ({
|
|
164
|
+
ID: id,
|
|
165
|
+
Label: label,
|
|
166
|
+
'Network CIDR': networkIp,
|
|
167
|
+
Members: Object.keys(members).length,
|
|
168
|
+
Peers: Object.keys(peers).length,
|
|
169
|
+
}));
|
|
170
|
+
|
|
171
|
+
console.table(ngList);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Show information about a Network Group, a member or a peer
|
|
177
|
+
* @param {Object} params
|
|
178
|
+
* @param {Object} params.args[0] ID or label of the Network Group, a member or a peer
|
|
179
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
180
|
+
* @param {string} params.options.format Output format
|
|
181
|
+
*/
|
|
182
|
+
export async function get (params) {
|
|
183
|
+
const [idOrLabel] = params.args;
|
|
184
|
+
const { org, format } = params.options;
|
|
185
|
+
const type = params.options.type ?? 'single';
|
|
186
|
+
|
|
187
|
+
await printResults(idOrLabel, org, format, 'get', type);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Show information about a Network Group, a member or a peer
|
|
191
|
+
* @param {Object} params
|
|
192
|
+
* @param {Object} params.args[0] ID or label of the Network Group, a member or a peer
|
|
193
|
+
* @param {Object} params.options.org Organisation ID or name
|
|
194
|
+
* @param {string} params.options.format Output format
|
|
195
|
+
*/
|
|
196
|
+
export async function search (params) {
|
|
197
|
+
const [idOrLabel] = params.args;
|
|
198
|
+
const { org, format } = params.options;
|
|
199
|
+
const type = params.options.type;
|
|
200
|
+
|
|
201
|
+
await printResults(idOrLabel, org, format, 'search', type);
|
|
202
|
+
}
|
package/src/commands/profile.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import colors from 'colors/safe.js';
|
|
2
|
-
|
|
2
|
+
import openPage from 'open';
|
|
3
3
|
import { Logger } from '../logger.js';
|
|
4
4
|
import * as User from '../models/user.js';
|
|
5
|
+
import dedent from 'dedent';
|
|
5
6
|
|
|
6
7
|
export async function profile (params) {
|
|
7
8
|
const { format } = params.options;
|
|
@@ -37,12 +38,20 @@ export async function profile (params) {
|
|
|
37
38
|
}
|
|
38
39
|
case 'human':
|
|
39
40
|
default: {
|
|
40
|
-
Logger.println(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
Logger.println(dedent`
|
|
42
|
+
You're currently logged in as:
|
|
43
|
+
User id ${formattedUser.id}
|
|
44
|
+
Name ${formattedUser.name ?? colors.red.bold('[not specified]')}
|
|
45
|
+
Email ${formattedUser.email}
|
|
46
|
+
Token expiration ${tokenExpiration}
|
|
47
|
+
Two factor auth ${formattedUser.has2FA ? 'yes' : 'no'}
|
|
48
|
+
`);
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
};
|
|
52
|
+
|
|
53
|
+
export async function openProfile () {
|
|
54
|
+
const URL = 'https://console.clever-cloud.com/users/me/information';
|
|
55
|
+
Logger.debug('Opening the profile page in your browser');
|
|
56
|
+
await openPage(URL, { wait: false });
|
|
57
|
+
}
|
|
@@ -3,13 +3,13 @@ import { Logger } from '../logger.js';
|
|
|
3
3
|
import * as variables from '../models/variables.js';
|
|
4
4
|
import { sendToApi } from '../models/send-to-api.js';
|
|
5
5
|
import { toNameEqualsValueString, validateName } from '@clevercloud/client/esm/utils/env-vars.js';
|
|
6
|
-
import
|
|
6
|
+
import { getAllExposedEnvVars, updateAllExposedEnvVars } from '@clevercloud/client/esm/api/v2/application.js';
|
|
7
7
|
|
|
8
8
|
export async function list (params) {
|
|
9
9
|
const { alias, app: appIdOrName, format } = params.options;
|
|
10
10
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
11
11
|
|
|
12
|
-
const publishedConfigs = await
|
|
12
|
+
const publishedConfigs = await getAllExposedEnvVars({ id: ownerId, appId }).then(sendToApi);
|
|
13
13
|
const pairs = Object.entries(publishedConfigs).map(([name, value]) => ({ name, value }));
|
|
14
14
|
|
|
15
15
|
switch (format) {
|
|
@@ -39,9 +39,9 @@ export async function set (params) {
|
|
|
39
39
|
|
|
40
40
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
41
41
|
|
|
42
|
-
const publishedConfigs = await
|
|
42
|
+
const publishedConfigs = await getAllExposedEnvVars({ id: ownerId, appId }).then(sendToApi);
|
|
43
43
|
publishedConfigs[varName] = varValue;
|
|
44
|
-
await
|
|
44
|
+
await updateAllExposedEnvVars({ id: ownerId, appId }, publishedConfigs).then(sendToApi);
|
|
45
45
|
|
|
46
46
|
Logger.println('Your published config item has been successfully saved');
|
|
47
47
|
};
|
|
@@ -51,9 +51,9 @@ export async function rm (params) {
|
|
|
51
51
|
const { alias, app: appIdOrName } = params.options;
|
|
52
52
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
53
53
|
|
|
54
|
-
const publishedConfigs = await
|
|
54
|
+
const publishedConfigs = await getAllExposedEnvVars({ id: ownerId, appId }).then(sendToApi);
|
|
55
55
|
delete publishedConfigs[varName];
|
|
56
|
-
await
|
|
56
|
+
await updateAllExposedEnvVars({ id: ownerId, appId }, publishedConfigs).then(sendToApi);
|
|
57
57
|
|
|
58
58
|
Logger.println('Your published config item has been successfully removed');
|
|
59
59
|
};
|
|
@@ -64,7 +64,7 @@ export async function importEnv (params) {
|
|
|
64
64
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
65
65
|
|
|
66
66
|
const publishedConfigs = await variables.readVariablesFromStdin(format);
|
|
67
|
-
await
|
|
67
|
+
await updateAllExposedEnvVars({ id: ownerId, appId }, publishedConfigs).then(sendToApi);
|
|
68
68
|
|
|
69
69
|
Logger.println('Your published configs have been set');
|
|
70
70
|
};
|
package/src/commands/stop.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as Application from '../models/application.js';
|
|
2
|
-
import * as application from '@clevercloud/client/esm/api/v2/application.js';
|
|
3
2
|
import { Logger } from '../logger.js';
|
|
4
3
|
import { sendToApi } from '../models/send-to-api.js';
|
|
4
|
+
import { undeploy as stopApplication } from '@clevercloud/client/esm/api/v2/application.js';
|
|
5
5
|
|
|
6
6
|
export async function stop (params) {
|
|
7
7
|
const { alias, app: appIdOrName } = params.options;
|
|
8
8
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
9
9
|
|
|
10
|
-
await
|
|
10
|
+
await stopApplication({ id: ownerId, appId }).then(sendToApi);
|
|
11
11
|
Logger.println('App successfully stopped!');
|
|
12
12
|
}
|