clever-tools 3.13.1 → 4.0.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 +130 -37
- package/bin/clever.js +1427 -718
- package/package.json +62 -77
- package/src/clever-client/auth-bridge.js +9 -12
- package/src/clever-client/ng.js +1 -1
- package/src/clever-client/operators.js +7 -7
- package/src/command-options.js +6 -6
- package/src/command-promise-handler.js +6 -6
- package/src/commands/accesslogs.js +38 -29
- package/src/commands/activity.js +62 -40
- package/src/commands/addon.js +65 -69
- package/src/commands/applications.js +33 -31
- package/src/commands/cancel-deploy.js +7 -7
- package/src/commands/config.js +9 -7
- package/src/commands/console.js +3 -3
- package/src/commands/create.js +33 -32
- package/src/commands/curl.js +14 -36
- package/src/commands/database.js +10 -30
- package/src/commands/delete.js +8 -8
- package/src/commands/deploy.js +60 -46
- package/src/commands/diag.js +27 -25
- package/src/commands/domain.js +89 -91
- package/src/commands/drain.js +26 -14
- package/src/commands/emails.js +24 -28
- package/src/commands/env.js +18 -18
- package/src/commands/features.js +12 -18
- package/src/commands/keycloak.js +11 -11
- package/src/commands/kv.js +13 -13
- package/src/commands/link.js +9 -8
- package/src/commands/login.js +19 -27
- package/src/commands/logout.js +1 -1
- package/src/commands/logs.js +20 -12
- package/src/commands/makeDefault.js +5 -5
- package/src/commands/matomo.js +7 -7
- package/src/commands/metabase.js +9 -9
- package/src/commands/ng.js +32 -32
- package/src/commands/notify-email.js +11 -15
- package/src/commands/open.js +1 -1
- package/src/commands/otoroshi.js +11 -11
- package/src/commands/profile.js +8 -8
- package/src/commands/published-config.js +11 -11
- package/src/commands/restart.js +15 -8
- package/src/commands/scale.js +25 -16
- package/src/commands/service.js +28 -11
- package/src/commands/ssh-keys.js +25 -29
- package/src/commands/ssh.js +1 -2
- package/src/commands/status.js +31 -22
- package/src/commands/stop.js +3 -3
- package/src/commands/tcp-redirs.js +27 -26
- package/src/commands/tokens.js +44 -43
- package/src/commands/unlink.js +7 -5
- package/src/commands/version.js +2 -4
- package/src/commands/webhooks.js +8 -10
- package/src/experimental-features.js +7 -5
- package/src/format-table.js +46 -15
- package/src/initial-setup.js +2 -32
- package/src/initial-update-notifier.js +18 -0
- package/src/lib/has-param.js +16 -0
- package/src/lib/ng-print.js +21 -24
- package/src/lib/operator-commands.js +58 -53
- package/src/lib/prompts.js +5 -5
- package/src/lib/slugify.js +1 -1
- package/src/logger.js +26 -21
- package/src/models/activity.js +3 -3
- package/src/models/addon.js +54 -56
- package/src/models/app_configuration.js +34 -32
- package/src/models/application.js +90 -77
- package/src/models/application_configuration.js +19 -27
- package/src/models/configuration.js +30 -32
- package/src/models/deployments.js +9 -19
- package/src/models/domain.js +10 -9
- package/src/models/drain.js +16 -15
- package/src/models/exit-strategy-option.js +15 -5
- package/src/models/fs-utils.js +4 -3
- package/src/models/git.js +26 -32
- package/src/models/ids-resolver.js +37 -41
- package/src/models/isomorphic-http-with-agent.js +3 -3
- package/src/models/json-array.js +5 -6
- package/src/models/log-v4.js +44 -52
- package/src/models/log.js +40 -45
- package/src/models/namespaces.js +4 -4
- package/src/models/ng-resources.js +49 -39
- package/src/models/ng.js +41 -40
- package/src/models/node-dns-resolver.js +3 -3
- package/src/models/notification.js +7 -10
- package/src/models/operator.js +8 -10
- package/src/models/organisation.js +5 -8
- package/src/models/send-to-api.js +18 -11
- package/src/models/user.js +6 -5
- package/src/models/utils.js +5 -8
- package/src/models/variables.js +18 -20
- package/src/parsers.js +34 -34
- package/src/load-package-json.cjs +0 -5
package/src/commands/scale.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import * as Application from '../models/application.js';
|
|
2
1
|
import { Logger } from '../logger.js';
|
|
2
|
+
import * as Application from '../models/application.js';
|
|
3
3
|
|
|
4
|
-
function validateOptions
|
|
5
|
-
|
|
4
|
+
function validateOptions(options) {
|
|
6
5
|
let { flavor, 'min-flavor': minFlavor, 'max-flavor': maxFlavor } = options;
|
|
7
|
-
let {
|
|
6
|
+
let {
|
|
7
|
+
instances,
|
|
8
|
+
'min-instances': minInstances,
|
|
9
|
+
'max-instances': maxInstances,
|
|
10
|
+
'build-flavor': buildFlavor,
|
|
11
|
+
} = options;
|
|
8
12
|
|
|
9
13
|
if ([flavor, minFlavor, maxFlavor, instances, minInstances, maxInstances, buildFlavor].every((v) => v == null)) {
|
|
10
14
|
throw new Error('You should provide at least 1 option');
|
|
@@ -12,7 +16,7 @@ function validateOptions (options) {
|
|
|
12
16
|
|
|
13
17
|
if (flavor != null) {
|
|
14
18
|
if (minFlavor != null || maxFlavor != null) {
|
|
15
|
-
throw new Error(
|
|
19
|
+
throw new Error("You can't use --flavor and --min-flavor or --max-flavor at the same time");
|
|
16
20
|
}
|
|
17
21
|
minFlavor = flavor;
|
|
18
22
|
maxFlavor = flavor;
|
|
@@ -20,38 +24,43 @@ function validateOptions (options) {
|
|
|
20
24
|
|
|
21
25
|
if (instances != null) {
|
|
22
26
|
if (minInstances != null || maxInstances != null) {
|
|
23
|
-
throw new Error(
|
|
27
|
+
throw new Error("You can't use --instances and --min-instances or --max-instances at the same time");
|
|
24
28
|
}
|
|
25
29
|
minInstances = instances;
|
|
26
30
|
maxInstances = instances;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
if (minInstances != null && maxInstances != null && minInstances > maxInstances) {
|
|
30
|
-
throw new Error(
|
|
34
|
+
throw new Error("min-instances can't be greater than max-instances");
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
if (minFlavor != null && maxFlavor != null) {
|
|
34
38
|
const minFlavorIndex = Application.listAvailableFlavors().indexOf(minFlavor);
|
|
35
39
|
const maxFlavorIndex = Application.listAvailableFlavors().indexOf(maxFlavor);
|
|
36
40
|
if (minFlavorIndex > maxFlavorIndex) {
|
|
37
|
-
throw new Error(
|
|
41
|
+
throw new Error("min-flavor can't be a greater flavor than max-flavor");
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
return { minFlavor, maxFlavor, minInstances, maxInstances, buildFlavor };
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
export async function scale
|
|
48
|
+
export async function scale(params) {
|
|
45
49
|
const { alias, app: appIdOrName } = params.options;
|
|
46
50
|
const { minFlavor, maxFlavor, minInstances, maxInstances, buildFlavor } = validateOptions(params.options);
|
|
47
51
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
48
52
|
|
|
49
|
-
await Application.setScalability(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
await Application.setScalability(
|
|
54
|
+
appId,
|
|
55
|
+
ownerId,
|
|
56
|
+
{
|
|
57
|
+
minFlavor,
|
|
58
|
+
maxFlavor,
|
|
59
|
+
minInstances,
|
|
60
|
+
maxInstances,
|
|
61
|
+
},
|
|
62
|
+
buildFlavor,
|
|
63
|
+
);
|
|
55
64
|
|
|
56
65
|
Logger.println('App rescaled successfully');
|
|
57
|
-
}
|
|
66
|
+
}
|
package/src/commands/service.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
import { Logger } from '../logger.js';
|
|
1
3
|
import * as Addon from '../models/addon.js';
|
|
2
4
|
import * as Application from '../models/application.js';
|
|
3
|
-
import { Logger } from '../logger.js';
|
|
4
5
|
|
|
5
|
-
export async function list
|
|
6
|
-
const {
|
|
6
|
+
export async function list(params) {
|
|
7
|
+
const {
|
|
8
|
+
alias,
|
|
9
|
+
app: appIdOrName,
|
|
10
|
+
'show-all': showAll,
|
|
11
|
+
'only-apps': onlyApps,
|
|
12
|
+
'only-addons': onlyAddons,
|
|
13
|
+
format,
|
|
14
|
+
} = params.options;
|
|
7
15
|
if (onlyApps && onlyAddons) {
|
|
8
16
|
throw new Error('--only-apps and --only-addons are mutually exclusive');
|
|
9
17
|
}
|
|
@@ -38,20 +46,29 @@ export async function list (params) {
|
|
|
38
46
|
}
|
|
39
47
|
case 'human':
|
|
40
48
|
default: {
|
|
41
|
-
|
|
49
|
+
const { applications = [], addons = [] } = formattedServices;
|
|
50
|
+
|
|
51
|
+
if (applications.length === 0 && addons.length === 0) {
|
|
52
|
+
Logger.printInfo(
|
|
53
|
+
`No linked services found, use ${styleText('blue', 'clever service link-app')} or ${styleText('blue', 'clever service link-addon')} to link services to an application`,
|
|
54
|
+
);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (applications.length > 0) {
|
|
42
59
|
Logger.println('Applications:');
|
|
43
|
-
|
|
60
|
+
applications.forEach(({ isLinked, name }) => Logger.println(`${isLinked ? '*' : ' '} ${name}`));
|
|
44
61
|
}
|
|
45
62
|
|
|
46
|
-
if (
|
|
63
|
+
if (addons.length > 0) {
|
|
47
64
|
Logger.println('Addons:');
|
|
48
|
-
|
|
65
|
+
addons.forEach(({ isLinked, name, realId }) => Logger.println(`${isLinked ? '*' : ' '} ${name} (${realId})`));
|
|
49
66
|
}
|
|
50
67
|
}
|
|
51
68
|
}
|
|
52
69
|
}
|
|
53
70
|
|
|
54
|
-
export async function linkApp
|
|
71
|
+
export async function linkApp(params) {
|
|
55
72
|
const { alias, app: appIdOrName } = params.options;
|
|
56
73
|
const [dependency] = params.args;
|
|
57
74
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
@@ -60,7 +77,7 @@ export async function linkApp (params) {
|
|
|
60
77
|
Logger.println(`App ${dependency.app_id || dependency.app_name} successfully linked`);
|
|
61
78
|
}
|
|
62
79
|
|
|
63
|
-
export async function unlinkApp
|
|
80
|
+
export async function unlinkApp(params) {
|
|
64
81
|
const { alias, app: appIdOrName } = params.options;
|
|
65
82
|
const [dependency] = params.args;
|
|
66
83
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
@@ -69,7 +86,7 @@ export async function unlinkApp (params) {
|
|
|
69
86
|
Logger.println(`App ${dependency.app_id || dependency.app_name} successfully unlinked`);
|
|
70
87
|
}
|
|
71
88
|
|
|
72
|
-
export async function linkAddon
|
|
89
|
+
export async function linkAddon(params) {
|
|
73
90
|
const { alias, app: appIdOrName } = params.options;
|
|
74
91
|
const [addon] = params.args;
|
|
75
92
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
@@ -78,7 +95,7 @@ export async function linkAddon (params) {
|
|
|
78
95
|
Logger.println(`Addon ${addon.addon_id || addon.addon_name} successfully linked`);
|
|
79
96
|
}
|
|
80
97
|
|
|
81
|
-
export async function unlinkAddon
|
|
98
|
+
export async function unlinkAddon(params) {
|
|
82
99
|
const { alias, app: appIdOrName } = params.options;
|
|
83
100
|
const [addon] = params.args;
|
|
84
101
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
package/src/commands/ssh-keys.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { confirm } from '../lib/prompts.js';
|
|
2
|
-
import colors from 'colors/safe.js';
|
|
3
|
-
import { Logger } from '../logger.js';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import { sendToApi } from '../models/send-to-api.js';
|
|
6
|
-
import { openBrowser } from '../models/utils.js';
|
|
7
|
-
import dedent from 'dedent';
|
|
8
1
|
import {
|
|
9
2
|
todo_addSshKey as addSshKey,
|
|
10
3
|
todo_getSshKeys as getSshKeys,
|
|
11
4
|
todo_removeSshKey as removeSshKey,
|
|
12
5
|
} from '@clevercloud/client/esm/api/v2/user.js';
|
|
6
|
+
import dedent from 'dedent';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import { styleText } from 'node:util';
|
|
9
|
+
import { confirm } from '../lib/prompts.js';
|
|
10
|
+
import { Logger } from '../logger.js';
|
|
11
|
+
import { sendToApi } from '../models/send-to-api.js';
|
|
12
|
+
import { openBrowser } from '../models/utils.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* List SSH keys of the current user
|
|
16
16
|
* @param {object} params The command parameters
|
|
17
17
|
* @param {string} params.options.format The output format
|
|
18
18
|
*/
|
|
19
|
-
export async function list
|
|
19
|
+
export async function list(params) {
|
|
20
20
|
const { format } = params.options;
|
|
21
21
|
|
|
22
22
|
const keys = await getUserSshKeys();
|
|
@@ -30,23 +30,23 @@ export async function list (params) {
|
|
|
30
30
|
default: {
|
|
31
31
|
if (keys.length === 0) {
|
|
32
32
|
Logger.println(dedent`
|
|
33
|
-
${
|
|
33
|
+
${styleText('blue', '🔐 No SSH keys')}
|
|
34
34
|
|
|
35
35
|
To list the SSH keys on your local system, use the following command:
|
|
36
|
-
${
|
|
36
|
+
${styleText('grey', 'ssh-add -l -E sha256')}
|
|
37
37
|
|
|
38
38
|
To create a new key pair, use the following command:
|
|
39
|
-
${
|
|
39
|
+
${styleText('grey', 'ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_clever -C "An optional comment"')}
|
|
40
40
|
|
|
41
41
|
Then add the public key to your Clever Cloud account:
|
|
42
|
-
${
|
|
42
|
+
${styleText('grey', 'clever ssh-keys add myNewKey ~/.ssh/id_ed25519_clever.pub')}
|
|
43
43
|
`);
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
Logger.println(`🔐 ${keys.length} SSH key(s):`);
|
|
48
48
|
keys.forEach((key) => {
|
|
49
|
-
Logger.println(` • ${
|
|
49
|
+
Logger.println(` • ${styleText('blue', key.name)}`, styleText('grey', `(${key.fingerprint})`));
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -57,7 +57,7 @@ export async function list (params) {
|
|
|
57
57
|
* @param {object} params The command parameters
|
|
58
58
|
* @param {Array<string>} params.args
|
|
59
59
|
*/
|
|
60
|
-
export async function add
|
|
60
|
+
export async function add(params) {
|
|
61
61
|
const [keyName, filePath] = params.args;
|
|
62
62
|
|
|
63
63
|
if (!fs.existsSync(filePath)) {
|
|
@@ -69,11 +69,10 @@ export async function add (params) {
|
|
|
69
69
|
|
|
70
70
|
try {
|
|
71
71
|
await addSshKey({ key: encodeURIComponent(keyName) }, JSON.stringify(pubKeyContent)).then(sendToApi);
|
|
72
|
-
}
|
|
73
|
-
catch (e) {
|
|
72
|
+
} catch (e) {
|
|
74
73
|
console.log(e?.responseBody?.id);
|
|
75
74
|
if (e?.responseBody?.id === 505) {
|
|
76
|
-
throw new Error(
|
|
75
|
+
throw new Error("This SSH key is not valid, please make sure you're pointing to the public key file");
|
|
77
76
|
}
|
|
78
77
|
}
|
|
79
78
|
|
|
@@ -85,13 +84,13 @@ export async function add (params) {
|
|
|
85
84
|
* @param {object} params The command parameters
|
|
86
85
|
* @param {Array<string>} params.args
|
|
87
86
|
*/
|
|
88
|
-
export async function remove
|
|
87
|
+
export async function remove(params) {
|
|
89
88
|
const [keyName] = params.args;
|
|
90
89
|
|
|
91
90
|
const keys = await getUserSshKeys();
|
|
92
91
|
|
|
93
92
|
if (keys.find((key) => key.name === keyName) == null) {
|
|
94
|
-
throw new Error(`SSH key ${
|
|
93
|
+
throw new Error(`SSH key ${styleText('red', keyName)} not found`);
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
const keyNameEncoded = encodeURIComponent(keyName);
|
|
@@ -106,12 +105,9 @@ export async function remove (params) {
|
|
|
106
105
|
* @param {object} params.options The command options
|
|
107
106
|
* @param {boolean} params.options.yes The user confirmation
|
|
108
107
|
*/
|
|
109
|
-
export async function removeAll
|
|
108
|
+
export async function removeAll(params) {
|
|
110
109
|
if (!params.options.yes) {
|
|
111
|
-
await confirm(
|
|
112
|
-
'Are you sure you want to remove all your SSH keys?',
|
|
113
|
-
'No SSH keys removed',
|
|
114
|
-
);
|
|
110
|
+
await confirm('Are you sure you want to remove all your SSH keys?', 'No SSH keys removed');
|
|
115
111
|
}
|
|
116
112
|
|
|
117
113
|
const keys = await getUserSshKeys();
|
|
@@ -124,7 +120,8 @@ export async function removeAll (params) {
|
|
|
124
120
|
const results = await Promise.all(
|
|
125
121
|
keys.map((key) => {
|
|
126
122
|
const keyNameEncoded = encodeURIComponent(key.name);
|
|
127
|
-
return removeSshKey({ key: keyNameEncoded })
|
|
123
|
+
return removeSshKey({ key: keyNameEncoded })
|
|
124
|
+
.then(sendToApi)
|
|
128
125
|
.then(() => [true, key.name])
|
|
129
126
|
.catch(() => [false, key.name]);
|
|
130
127
|
}),
|
|
@@ -132,8 +129,7 @@ export async function removeAll (params) {
|
|
|
132
129
|
|
|
133
130
|
if (results.every(([isRemoved]) => isRemoved)) {
|
|
134
131
|
Logger.printSuccess('All SSH keys were removed successfully');
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
132
|
+
} else {
|
|
137
133
|
const keyNamesWithErrors = results
|
|
138
134
|
.filter(([isRemoved]) => !isRemoved)
|
|
139
135
|
.map(([_, keyName]) => keyName)
|
|
@@ -146,14 +142,14 @@ export async function removeAll (params) {
|
|
|
146
142
|
* Open the SSH keys management page of the Console in your browser
|
|
147
143
|
* @returns {Promise<void>} A promise that resolves when the page is opened
|
|
148
144
|
*/
|
|
149
|
-
export function openConsole
|
|
145
|
+
export function openConsole() {
|
|
150
146
|
return openBrowser('/users/me/ssh-keys', 'Opening the SSH keys management page of the Console in your browser');
|
|
151
147
|
}
|
|
152
148
|
|
|
153
149
|
/**
|
|
154
150
|
* @return {Promise<Array<{ name: string, key: string, fingerprint: string }>>}
|
|
155
151
|
*/
|
|
156
|
-
async function getUserSshKeys
|
|
152
|
+
async function getUserSshKeys() {
|
|
157
153
|
const rawKeys = await getSshKeys().then(sendToApi);
|
|
158
154
|
return rawKeys.sort((a, b) => a.name.localeCompare(b.name));
|
|
159
155
|
}
|
package/src/commands/ssh.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
|
|
3
2
|
import * as Application from '../models/application.js';
|
|
4
3
|
import { conf } from '../models/configuration.js';
|
|
5
4
|
|
|
6
|
-
export async function ssh
|
|
5
|
+
export async function ssh(params) {
|
|
7
6
|
const { alias, app: appIdOrName, 'identity-file': identityFile } = params.options;
|
|
8
7
|
|
|
9
8
|
const { appId } = await Application.resolveId(appIdOrName, alias);
|
package/src/commands/status.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
+
import { getAllInstances, get as getApplication } from '@clevercloud/client/esm/api/v2/application.js';
|
|
1
2
|
import _ from 'lodash';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import * as Application from '../models/application.js';
|
|
3
|
+
import { styleText } from 'node:util';
|
|
5
4
|
import { Logger } from '../logger.js';
|
|
6
|
-
|
|
7
|
-
import { get as getApplication, getAllInstances } from '@clevercloud/client/esm/api/v2/application.js';
|
|
5
|
+
import * as Application from '../models/application.js';
|
|
8
6
|
import { sendToApi } from '../models/send-to-api.js';
|
|
9
7
|
|
|
10
|
-
export async function status
|
|
8
|
+
export async function status(params) {
|
|
11
9
|
const { alias, app: appIdOrName, format } = params.options;
|
|
12
10
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
13
11
|
|
|
@@ -23,30 +21,37 @@ export async function status (params) {
|
|
|
23
21
|
}
|
|
24
22
|
case 'human':
|
|
25
23
|
default: {
|
|
26
|
-
const statusMessage =
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
const statusMessage =
|
|
25
|
+
status.status === 'running'
|
|
26
|
+
? `${styleText(['bold', 'green'], 'running')} ${displayInstances(status.instances, status.commit)}`
|
|
27
|
+
: styleText(['bold', 'red'], 'stopped');
|
|
29
28
|
|
|
30
29
|
Logger.println(`${status.name}: ${statusMessage}`);
|
|
31
|
-
Logger.println(`Executed as: ${
|
|
30
|
+
Logger.println(`Executed as: ${styleText('bold', status.lifetime)}`);
|
|
32
31
|
if (status.deploymentInProgress) {
|
|
33
|
-
Logger.println(
|
|
32
|
+
Logger.println(
|
|
33
|
+
`Deployment in progress ${displayInstances(status.deploymentInProgress.instances, status.deploymentInProgress.commit)}`,
|
|
34
|
+
);
|
|
34
35
|
}
|
|
35
36
|
Logger.println();
|
|
36
37
|
Logger.println('Scalability:');
|
|
37
|
-
Logger.println(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Logger.println(`
|
|
38
|
+
Logger.println(
|
|
39
|
+
` Auto scalability: ${status.scalability.enabled ? styleText('green', 'enabled') : styleText('red', 'disabled')}`,
|
|
40
|
+
);
|
|
41
|
+
Logger.println(` Scalers: ${styleText('bold', formatScalability(status.scalability.horizontal))}`);
|
|
42
|
+
Logger.println(` Sizes: ${styleText('bold', formatScalability(status.scalability.vertical))}`);
|
|
43
|
+
Logger.println(
|
|
44
|
+
` Dedicated build: ${status.separateBuild ? styleText('bold', status.buildFlavor) : styleText('red', 'disabled')}`,
|
|
45
|
+
);
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
function displayInstances
|
|
50
|
+
function displayInstances(instances, commit) {
|
|
46
51
|
return `(${instances.map((instance) => `${instance.count}*${instance.flavor}`)}, Commit: ${commit || 'N/A'})`;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
|
-
function computeStatus
|
|
54
|
+
function computeStatus(instances, app) {
|
|
50
55
|
const upInstances = _.filter(instances, ({ state }) => state === 'UP');
|
|
51
56
|
const isUp = !_.isEmpty(upInstances);
|
|
52
57
|
const upCommit = _(upInstances).map('commit').head();
|
|
@@ -57,8 +62,7 @@ function computeStatus (instances, app) {
|
|
|
57
62
|
|
|
58
63
|
const { minFlavor, maxFlavor, minInstances, maxInstances } = app.instance;
|
|
59
64
|
|
|
60
|
-
const scalabilityEnabled =
|
|
61
|
-
|| (minInstances !== maxInstances);
|
|
65
|
+
const scalabilityEnabled = minFlavor.name !== maxFlavor.name || minInstances !== maxInstances;
|
|
62
66
|
|
|
63
67
|
const status = {
|
|
64
68
|
id: app.id,
|
|
@@ -86,11 +90,16 @@ function computeStatus (instances, app) {
|
|
|
86
90
|
return status;
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
/**
|
|
94
|
+
* @param {number} min
|
|
95
|
+
* @param {number} max
|
|
96
|
+
* @returns {string}
|
|
97
|
+
*/
|
|
98
|
+
function formatScalability({ min, max }) {
|
|
99
|
+
return min === max ? min.toString() : `${min} to ${max}`;
|
|
91
100
|
}
|
|
92
101
|
|
|
93
|
-
function groupInstances
|
|
102
|
+
function groupInstances(instances) {
|
|
94
103
|
return _(instances)
|
|
95
104
|
.groupBy((i) => i.flavor.name)
|
|
96
105
|
.map((instances, flavorName) => ({
|
package/src/commands/stop.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { undeploy as stopApplication } from '@clevercloud/client/esm/api/v2/application.js';
|
|
2
2
|
import { Logger } from '../logger.js';
|
|
3
|
+
import * as Application from '../models/application.js';
|
|
3
4
|
import { sendToApi } from '../models/send-to-api.js';
|
|
4
|
-
import { undeploy as stopApplication } from '@clevercloud/client/esm/api/v2/application.js';
|
|
5
5
|
|
|
6
|
-
export async function stop
|
|
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
|
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import * as Namespaces from '../models/namespaces.js';
|
|
4
|
-
import { sendToApi } from '../models/send-to-api.js';
|
|
1
|
+
import { addTcpRedir, getTcpRedirs, removeTcpRedir } from '@clevercloud/client/esm/api/v2/application.js';
|
|
2
|
+
import { styleText } from 'node:util';
|
|
5
3
|
import { confirm } from '../lib/prompts.js';
|
|
6
4
|
import { Logger } from '../logger.js';
|
|
7
5
|
import * as Application from '../models/application.js';
|
|
8
|
-
import
|
|
6
|
+
import * as Namespaces from '../models/namespaces.js';
|
|
7
|
+
import { sendToApi } from '../models/send-to-api.js';
|
|
9
8
|
|
|
10
|
-
export async function listNamespaces
|
|
9
|
+
export async function listNamespaces(params) {
|
|
11
10
|
const { alias, app: appIdOrName, format } = params.options;
|
|
12
11
|
const { ownerId } = await Application.resolveId(appIdOrName, alias);
|
|
13
12
|
|
|
@@ -40,7 +39,7 @@ export async function listNamespaces (params) {
|
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
export async function list
|
|
42
|
+
export async function list(params) {
|
|
44
43
|
const { alias, app: appIdOrName, format } = params.options;
|
|
45
44
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
46
45
|
|
|
@@ -55,8 +54,7 @@ export async function list (params) {
|
|
|
55
54
|
default: {
|
|
56
55
|
if (redirs.length === 0) {
|
|
57
56
|
Logger.println('No active TCP redirection for this application');
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
57
|
+
} else {
|
|
60
58
|
Logger.println('Enabled TCP redirections:');
|
|
61
59
|
for (const { namespace, port } of redirs) {
|
|
62
60
|
Logger.println(port + ' on ' + namespace);
|
|
@@ -66,11 +64,13 @@ export async function list (params) {
|
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
|
|
69
|
-
async function acceptPayment
|
|
67
|
+
async function acceptPayment(result, skipConfirmation) {
|
|
70
68
|
if (!skipConfirmation) {
|
|
71
|
-
result.lines.forEach(({ description, VAT, price }) =>
|
|
69
|
+
result.lines.forEach(({ description, VAT, price }) =>
|
|
70
|
+
Logger.println(`${description}\tVAT: ${VAT}%\tPrice: ${price}€`),
|
|
71
|
+
);
|
|
72
72
|
Logger.println(`Total (without taxes): ${result.totalHT}€`);
|
|
73
|
-
Logger.println(
|
|
73
|
+
Logger.println(styleText('bold', `Total (with taxes): ${result.totalTTC}€`));
|
|
74
74
|
|
|
75
75
|
await confirm(
|
|
76
76
|
`You're about to pay ${result.totalTTC}€, confirm?`,
|
|
@@ -79,25 +79,26 @@ async function acceptPayment (result, skipConfirmation) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
export async function add
|
|
82
|
+
export async function add(params) {
|
|
83
83
|
const { alias, app: appIdOrName, namespace, yes: skipConfirmation } = params.options;
|
|
84
84
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
85
85
|
|
|
86
|
-
const { port } = await addTcpRedir({ id: ownerId, appId }, { namespace })
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
86
|
+
const { port } = await addTcpRedir({ id: ownerId, appId }, { namespace })
|
|
87
|
+
.then(sendToApi)
|
|
88
|
+
.catch((error) => {
|
|
89
|
+
if (error.status === 402) {
|
|
90
|
+
return acceptPayment(error.response.body, skipConfirmation).then(() => {
|
|
91
|
+
return addTcpRedir({ id: ownerId, appId, payment: 'accepted' }, { namespace }).then(sendToApi);
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
96
97
|
|
|
97
98
|
Logger.println('Successfully added tcp redirection on port: ' + port);
|
|
98
|
-
}
|
|
99
|
+
}
|
|
99
100
|
|
|
100
|
-
export async function remove
|
|
101
|
+
export async function remove(params) {
|
|
101
102
|
const [port] = params.args;
|
|
102
103
|
const { alias, app: appIdOrName, namespace } = params.options;
|
|
103
104
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
@@ -105,4 +106,4 @@ export async function remove (params) {
|
|
|
105
106
|
await removeTcpRedir({ id: ownerId, appId, sourcePort: port, namespace }).then(sendToApi);
|
|
106
107
|
|
|
107
108
|
Logger.println('Successfully removed tcp redirection.');
|
|
108
|
-
}
|
|
109
|
+
}
|