clever-tools 3.6.1 → 3.8.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 -7
- package/bin/clever.js +99 -47
- package/package.json +48 -50
- package/src/command-options.js +22 -3
- package/src/commands/accesslogs.js +114 -34
- package/src/commands/activity.js +70 -16
- package/src/commands/addon.js +153 -75
- package/src/commands/applications.js +83 -1
- package/src/commands/cancel-deploy.js +3 -3
- package/src/commands/config.js +6 -7
- package/src/commands/console.js +16 -4
- package/src/commands/curl.js +13 -16
- package/src/commands/delete.js +16 -6
- package/src/commands/deploy.js +9 -6
- package/src/commands/diag.js +68 -28
- package/src/commands/domain.js +13 -13
- package/src/commands/drain.js +42 -27
- package/src/commands/env.js +53 -24
- package/src/commands/logs.js +3 -3
- package/src/commands/notify-email.js +33 -18
- package/src/commands/open.js +3 -3
- package/src/commands/profile.js +29 -9
- package/src/commands/published-config.js +26 -15
- package/src/commands/restart.js +12 -5
- package/src/commands/scale.js +2 -3
- package/src/commands/service.js +42 -15
- package/src/commands/ssh.js +3 -3
- package/src/commands/status.js +72 -46
- package/src/commands/stop.js +3 -3
- package/src/commands/tcp-redirs.js +54 -18
- package/src/commands/webhooks.js +28 -13
- package/src/logger.js +9 -6
- package/src/models/addon.js +21 -12
- package/src/models/app_configuration.js +11 -3
- package/src/models/application.js +106 -7
- package/src/models/exit-strategy-option.js +24 -0
- package/src/models/json-array.js +28 -0
- package/src/models/log-v4.js +9 -29
- package/src/models/log.js +9 -2
- package/src/models/namespaces.js +20 -0
- package/src/models/organisation.js +0 -20
- package/src/models/utils.js +8 -1
- package/src/models/accesslogs.js +0 -54
- package/vendors/README_VENDORS.md +0 -18
- package/vendors/curlconverter-parse.js +0 -3709
|
@@ -1,53 +1,133 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const { getWarp10AccessLogsToken } = require('@clevercloud/client/cjs/api/v2/warp-10.js');
|
|
5
|
-
const { ONE_HOUR_MICROS, ONE_SECOND_MICROS, toMicroTimestamp } = require('@clevercloud/client/cjs/utils/date.js');
|
|
6
|
-
|
|
7
|
-
const Addon = require('../models/addon.js');
|
|
8
|
-
const AppConfig = require('../models/app_configuration.js');
|
|
3
|
+
const Application = require('../models/application.js');
|
|
9
4
|
const Logger = require('../logger.js');
|
|
10
|
-
const {
|
|
11
|
-
const {
|
|
5
|
+
const { getHostAndTokens } = require('../models/send-to-api.js');
|
|
6
|
+
const { ApplicationAccessLogStream } = require('@clevercloud/client/cjs/streams/access-logs.js');
|
|
7
|
+
const { JsonArray } = require('../models/json-array.js');
|
|
8
|
+
const colors = require('colors/safe');
|
|
9
|
+
const formatTable = require('../format-table.js');
|
|
10
|
+
const { truncateWithEllipsis } = require('../models/utils.js');
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
// 2000 logs per 100ms maximum
|
|
13
|
+
const THROTTLE_ELEMENTS = 2000;
|
|
14
|
+
const THROTTLE_PER_IN_MILLISECONDS = 100;
|
|
15
|
+
const CITY_MAX_LENGTH = 20;
|
|
14
16
|
|
|
15
17
|
async function accessLogs (params) {
|
|
16
|
-
const { alias, format, before, after, addon: addonId, follow } = params.options;
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
// TODO: drop when add-ons are supported in API
|
|
20
|
+
if (params.options.addon) {
|
|
21
|
+
throw new Error('Access Logs are not available for add-ons yet');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { apiHost, tokens } = await getHostAndTokens();
|
|
25
|
+
const { alias, app: appIdOrName, format, before: until, after: since } = params.options;
|
|
26
|
+
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
27
|
+
|
|
28
|
+
const stream = new ApplicationAccessLogStream({
|
|
29
|
+
apiHost,
|
|
30
|
+
tokens,
|
|
31
|
+
ownerId,
|
|
32
|
+
appId,
|
|
33
|
+
since,
|
|
34
|
+
until,
|
|
35
|
+
throttleElements: THROTTLE_ELEMENTS,
|
|
36
|
+
throttlePerInMilliseconds: THROTTLE_PER_IN_MILLISECONDS,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (format === 'human') {
|
|
40
|
+
Logger.warn(colors.yellow('/!\\ Access Logs feature is in Alpha testing phase'));
|
|
41
|
+
}
|
|
22
42
|
|
|
23
|
-
if (
|
|
24
|
-
|
|
43
|
+
if (format === 'json' && (!until)) {
|
|
44
|
+
throw new Error('JSON format only works with a limiting parameter such as `before`');
|
|
25
45
|
}
|
|
26
46
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
: getAccessLogsFromWarp10InBatches({ appId, realAddonId, from, to, warpToken }, sendToWarp10);
|
|
47
|
+
// used for 'json' format
|
|
48
|
+
const jsonArray = new JsonArray();
|
|
30
49
|
|
|
31
|
-
|
|
50
|
+
stream
|
|
51
|
+
.on('open', (event) => {
|
|
52
|
+
Logger.debug(colors.blue(`Logs stream (open) ${JSON.stringify({ appId })}`));
|
|
53
|
+
if (format === 'json') {
|
|
54
|
+
jsonArray.open();
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
.on('error', (event) => {
|
|
58
|
+
Logger.debug(colors.red(`Logs stream (error) ${event.error.message}`));
|
|
59
|
+
})
|
|
60
|
+
.onLog((log) => {
|
|
61
|
+
switch (format) {
|
|
62
|
+
case 'json':
|
|
63
|
+
jsonArray.push(log);
|
|
64
|
+
break;
|
|
65
|
+
case 'json-stream':
|
|
66
|
+
Logger.printJson(log);
|
|
67
|
+
break;
|
|
68
|
+
case 'human':
|
|
69
|
+
default:
|
|
70
|
+
// when the connection is cut too early, or for TCP redirections, we don't have HTTP section
|
|
71
|
+
if (log.http == null) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
32
74
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
75
|
+
Logger.println(formatHuman(log));
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
36
79
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
80
|
+
// Properly close the stream
|
|
81
|
+
process.once('SIGINT', (signal) => stream.close(signal));
|
|
82
|
+
|
|
83
|
+
const closeReason = await stream.start();
|
|
84
|
+
|
|
85
|
+
if (format === 'json') {
|
|
86
|
+
jsonArray.close();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Logger.debug(`stream closed: ${closeReason?.type}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function formatHuman (log) {
|
|
93
|
+
const { date, http, source } = log;
|
|
94
|
+
const country = source.countryCode ?? '(unknown)';
|
|
95
|
+
const hasSourceCity = source.city ?? '';
|
|
96
|
+
|
|
97
|
+
return row([[
|
|
98
|
+
colors.grey(date.toISOString(date)),
|
|
99
|
+
source.ip,
|
|
100
|
+
`${country}${hasSourceCity ? '/' + truncateWithEllipsis(CITY_MAX_LENGTH, source.city) : ''}`,
|
|
101
|
+
colorStatusCode(http.response.statusCode),
|
|
102
|
+
http.request.method.toString().padEnd(4, ' ') + ' ' + http.request.path,
|
|
103
|
+
]]);
|
|
40
104
|
}
|
|
41
105
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
106
|
+
const row = formatTable([
|
|
107
|
+
'2024-06-24T08:05:43.880Z',
|
|
108
|
+
'255.255.255.255',
|
|
109
|
+
// country / city
|
|
110
|
+
2 + 1 + CITY_MAX_LENGTH,
|
|
111
|
+
'XXX',
|
|
112
|
+
// longest method name
|
|
113
|
+
'OPTIONS',
|
|
114
|
+
// path
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
function colorStatusCode (code) {
|
|
118
|
+
if (code >= 500) {
|
|
119
|
+
return colors.red(code);
|
|
120
|
+
}
|
|
121
|
+
if (code >= 400) {
|
|
122
|
+
return colors.yellow(code);
|
|
123
|
+
}
|
|
124
|
+
if (code >= 300) {
|
|
125
|
+
return colors.blue(code);
|
|
126
|
+
}
|
|
127
|
+
if (code >= 200) {
|
|
128
|
+
return colors.green(code);
|
|
49
129
|
}
|
|
50
|
-
return
|
|
130
|
+
return code;
|
|
51
131
|
}
|
|
52
132
|
|
|
53
133
|
module.exports = { accessLogs };
|
package/src/commands/activity.js
CHANGED
|
@@ -4,12 +4,12 @@ const colors = require('colors/safe');
|
|
|
4
4
|
const moment = require('moment');
|
|
5
5
|
|
|
6
6
|
const Activity = require('../models/activity.js');
|
|
7
|
-
const AppConfig = require('../models/app_configuration.js');
|
|
8
7
|
const formatTable = require('../format-table');
|
|
9
8
|
const Logger = require('../logger.js');
|
|
10
9
|
const { Deferred } = require('../models/utils.js');
|
|
11
10
|
const { EventsStream } = require('@clevercloud/client/cjs/streams/events.node.js');
|
|
12
11
|
const { getHostAndTokens } = require('../models/send-to-api.js');
|
|
12
|
+
const Application = require('../models/application.js');
|
|
13
13
|
|
|
14
14
|
function getColoredState (state, isLast) {
|
|
15
15
|
if (state === 'OK') {
|
|
@@ -31,24 +31,37 @@ function getColoredState (state, isLast) {
|
|
|
31
31
|
// We use examples of maximum width text to have a clean display
|
|
32
32
|
const formatActivityTable = formatTable([
|
|
33
33
|
moment().format(),
|
|
34
|
+
47,
|
|
34
35
|
'IN PROGRESS',
|
|
35
36
|
'downscale',
|
|
36
|
-
// a git commit id is
|
|
37
|
-
|
|
37
|
+
// a git commit id is 8 chars long
|
|
38
|
+
8,
|
|
38
39
|
0,
|
|
39
40
|
]);
|
|
40
41
|
|
|
42
|
+
function convertEventToJson (event) {
|
|
43
|
+
return {
|
|
44
|
+
uuid: event.uuid,
|
|
45
|
+
date: event.date,
|
|
46
|
+
state: event.state,
|
|
47
|
+
action: event.action,
|
|
48
|
+
commit: event.commit,
|
|
49
|
+
cause: event.cause,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
41
53
|
function formatActivityLine (event) {
|
|
42
54
|
return formatActivityTable([
|
|
43
55
|
[
|
|
44
56
|
moment(event.date).format(),
|
|
57
|
+
event.uuid,
|
|
45
58
|
getColoredState(event.state, event.isLast),
|
|
46
59
|
event.action,
|
|
47
|
-
event
|
|
60
|
+
event?.commit?.substring(0, 8) ?? 'N/A',
|
|
48
61
|
event.cause,
|
|
49
62
|
],
|
|
50
63
|
]);
|
|
51
|
-
}
|
|
64
|
+
}
|
|
52
65
|
|
|
53
66
|
function isTemporaryEvent (ev) {
|
|
54
67
|
if (ev == null) {
|
|
@@ -65,28 +78,65 @@ function clearPreviousLine () {
|
|
|
65
78
|
}
|
|
66
79
|
}
|
|
67
80
|
|
|
68
|
-
function handleEvent (previousEvent, event) {
|
|
81
|
+
function handleEvent (previousEvent, event, handler) {
|
|
69
82
|
if (isTemporaryEvent(previousEvent)) {
|
|
70
|
-
|
|
83
|
+
handler.pop();
|
|
71
84
|
}
|
|
72
85
|
|
|
73
|
-
|
|
74
|
-
Logger.println(activityLine);
|
|
86
|
+
handler.print(event);
|
|
75
87
|
|
|
76
88
|
return event;
|
|
77
89
|
}
|
|
78
90
|
|
|
79
|
-
function onEvent (previousEvent, newEvent) {
|
|
80
|
-
const { event, date, data: { state, action, commit, cause } } = newEvent;
|
|
91
|
+
function onEvent (previousEvent, newEvent, handler) {
|
|
92
|
+
const { event, date, data: { uuid, state, action, commit, cause } } = newEvent;
|
|
81
93
|
if (event !== 'DEPLOYMENT_ACTION_BEGIN' && event !== 'DEPLOYMENT_ACTION_END') {
|
|
82
94
|
return previousEvent;
|
|
83
95
|
}
|
|
84
|
-
return handleEvent(previousEvent, { date, state, action, commit, cause, isLast: true });
|
|
96
|
+
return handleEvent(previousEvent, { date, uuid, state, action, commit, cause, isLast: true }, handler);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getEventHandler (format, follow) {
|
|
100
|
+
switch (format) {
|
|
101
|
+
case 'json': {
|
|
102
|
+
if (follow) {
|
|
103
|
+
throw new Error('The `follow` option and "json" format are not compatible. Use "json-stream" format instead.');
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const buf = [];
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
print: (event) => buf.push(convertEventToJson(event)),
|
|
110
|
+
pop: () => buf.pop(),
|
|
111
|
+
end: () => {
|
|
112
|
+
Logger.printJson(buf);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
case 'json-stream': {
|
|
118
|
+
return {
|
|
119
|
+
print: (event) => {
|
|
120
|
+
Logger.println(JSON.stringify(convertEventToJson(event)));
|
|
121
|
+
},
|
|
122
|
+
pop: clearPreviousLine,
|
|
123
|
+
end: () => {},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
case 'human':
|
|
127
|
+
default: {
|
|
128
|
+
return {
|
|
129
|
+
print: (event) => Logger.println(formatActivityLine(event)),
|
|
130
|
+
pop: clearPreviousLine,
|
|
131
|
+
end: () => {},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
85
135
|
}
|
|
86
136
|
|
|
87
137
|
async function activity (params) {
|
|
88
|
-
const { alias, 'show-all': showAll, follow } = params.options;
|
|
89
|
-
const { ownerId, appId } = await
|
|
138
|
+
const { alias, app: appIdOrName, 'show-all': showAll, follow, format } = params.options;
|
|
139
|
+
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
90
140
|
const events = await Activity.list(ownerId, appId, showAll);
|
|
91
141
|
const reversedArrayWithIndex = events
|
|
92
142
|
.reverse()
|
|
@@ -94,9 +144,13 @@ async function activity (params) {
|
|
|
94
144
|
const isLast = index === all.length - 1;
|
|
95
145
|
return ({ ...event, isLast });
|
|
96
146
|
});
|
|
97
|
-
|
|
147
|
+
|
|
148
|
+
const handler = getEventHandler(format, follow);
|
|
149
|
+
|
|
150
|
+
let lastEvent = reversedArrayWithIndex.reduce((previousEvent, newEvent) => handleEvent(previousEvent, newEvent, handler), {});
|
|
98
151
|
|
|
99
152
|
if (!follow) {
|
|
153
|
+
handler.end();
|
|
100
154
|
return lastEvent;
|
|
101
155
|
}
|
|
102
156
|
|
|
@@ -108,7 +162,7 @@ async function activity (params) {
|
|
|
108
162
|
eventsStream
|
|
109
163
|
.on('open', () => Logger.debug('WS for events (open) ' + JSON.stringify({ appId })))
|
|
110
164
|
.on('event', (event) => {
|
|
111
|
-
lastEvent = onEvent(lastEvent, event);
|
|
165
|
+
lastEvent = onEvent(lastEvent, event, handler);
|
|
112
166
|
return lastEvent;
|
|
113
167
|
})
|
|
114
168
|
.on('ping', () => Logger.debug('WS for events (ping)'))
|
package/src/commands/addon.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const _ = require('lodash');
|
|
4
3
|
const colors = require('colors/safe');
|
|
5
4
|
|
|
6
5
|
const Addon = require('../models/addon.js');
|
|
@@ -15,22 +14,6 @@ const { sendToApi } = require('../models/send-to-api.js');
|
|
|
15
14
|
const { toNameEqualsValueString } = require('@clevercloud/client/cjs/utils/env-vars.js');
|
|
16
15
|
const { resolveAddonId } = require('../models/ids-resolver.js');
|
|
17
16
|
|
|
18
|
-
function getPlan (inputPlan, providerName) {
|
|
19
|
-
|
|
20
|
-
// Return user plan
|
|
21
|
-
if (inputPlan !== '') {
|
|
22
|
-
return inputPlan;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Choose a default plan based on the add-on provider
|
|
26
|
-
switch (providerName) {
|
|
27
|
-
case 'kv':
|
|
28
|
-
return 'alpha';
|
|
29
|
-
default:
|
|
30
|
-
return 'dev';
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
17
|
async function list (params) {
|
|
35
18
|
const { org: orgaIdOrName, format } = params.options;
|
|
36
19
|
|
|
@@ -74,7 +57,7 @@ async function create (params) {
|
|
|
74
57
|
const [providerName, name] = params.args;
|
|
75
58
|
const {
|
|
76
59
|
link: linkedAppAlias,
|
|
77
|
-
plan:
|
|
60
|
+
plan: planName,
|
|
78
61
|
region,
|
|
79
62
|
yes: skipConfirmation,
|
|
80
63
|
org: orgaIdOrName,
|
|
@@ -82,7 +65,6 @@ async function create (params) {
|
|
|
82
65
|
} = params.options;
|
|
83
66
|
const version = params.options['addon-version'];
|
|
84
67
|
const addonOptions = parseAddonOptions(params.options.option);
|
|
85
|
-
const planName = getPlan(inputPlan, providerName);
|
|
86
68
|
|
|
87
69
|
const ownerId = (orgaIdOrName != null)
|
|
88
70
|
? await Organisation.getId(orgaIdOrName)
|
|
@@ -118,6 +100,45 @@ async function create (params) {
|
|
|
118
100
|
}
|
|
119
101
|
|
|
120
102
|
function displayAddon (format, addon, providerName, message) {
|
|
103
|
+
|
|
104
|
+
const WIP_PROVIDERS = {
|
|
105
|
+
keycloak: {
|
|
106
|
+
statut: 'alpha',
|
|
107
|
+
postCreateInstructions: [
|
|
108
|
+
'Learn more about Keycloak on Clever Cloud: https://developers.clever-cloud.com/doc/addons/keycloak/',
|
|
109
|
+
].join('\n'),
|
|
110
|
+
},
|
|
111
|
+
kv: {
|
|
112
|
+
status: 'alpha',
|
|
113
|
+
postCreateInstructions: [
|
|
114
|
+
colors.yellow('You can easily use Materia KV with \'redis-cli\', with such commands:'),
|
|
115
|
+
colors.blue(`source <(clever addon env ${addon.id} -F shell)`),
|
|
116
|
+
colors.blue('redis-cli -h $KV_HOST -p $KV_PORT --tls'),
|
|
117
|
+
'Learn more about Materia KV on Clever Cloud: https://developers.clever-cloud.com/doc/addons/materia-kv/',
|
|
118
|
+
].join('\n'),
|
|
119
|
+
},
|
|
120
|
+
'addon-pulsar': {
|
|
121
|
+
status: 'beta',
|
|
122
|
+
postCreateInstructions: [
|
|
123
|
+
'Learn more about Pulsar on Clever Cloud: https://developers.clever-cloud.com/doc/addons/pulsar/',
|
|
124
|
+
].join('\n'),
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
let providerNameToShow = '';
|
|
129
|
+
let statusMessage = '';
|
|
130
|
+
if (providerName in WIP_PROVIDERS) {
|
|
131
|
+
|
|
132
|
+
providerNameToShow = providerName === 'kv'
|
|
133
|
+
? 'Materia KV'
|
|
134
|
+
: providerName;
|
|
135
|
+
|
|
136
|
+
statusMessage = `The ${providerNameToShow} provider is in ${WIP_PROVIDERS[providerName].status} testing phase`;
|
|
137
|
+
statusMessage += WIP_PROVIDERS[providerName].status === 'alpha'
|
|
138
|
+
? '. Don\'t store sensitive or production grade data.'
|
|
139
|
+
: '';
|
|
140
|
+
}
|
|
141
|
+
|
|
121
142
|
switch (format) {
|
|
122
143
|
|
|
123
144
|
case 'json': {
|
|
@@ -126,8 +147,9 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
126
147
|
realId: addon.realId,
|
|
127
148
|
name: addon.name,
|
|
128
149
|
};
|
|
129
|
-
|
|
130
|
-
|
|
150
|
+
|
|
151
|
+
Logger.printJson((WIP_PROVIDERS[providerName] != null)
|
|
152
|
+
? { ...jsonAddon, availability: WIP_PROVIDERS[providerName].status, message: statusMessage }
|
|
131
153
|
: jsonAddon);
|
|
132
154
|
break;
|
|
133
155
|
}
|
|
@@ -140,15 +162,12 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
140
162
|
`Real ID: ${addon.realId}`,
|
|
141
163
|
`Name: ${addon.name}`,
|
|
142
164
|
].join('\n'));
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
colors.blue('redis-cli -h $KV_HOST -p $KV_PORT --tls'),
|
|
150
|
-
].join('\n');
|
|
151
|
-
Logger.println(materiaMessage);
|
|
165
|
+
|
|
166
|
+
if (providerName in WIP_PROVIDERS) {
|
|
167
|
+
|
|
168
|
+
Logger.println();
|
|
169
|
+
Logger.println(colors.yellow(`/!\\ ${statusMessage}`));
|
|
170
|
+
Logger.println(WIP_PROVIDERS[providerName].postCreateInstructions);
|
|
152
171
|
}
|
|
153
172
|
}
|
|
154
173
|
}
|
|
@@ -173,64 +192,123 @@ async function rename (params) {
|
|
|
173
192
|
Logger.println(`Addon ${addon.addon_id || addon.addon_name} successfully renamed to ${newName}`);
|
|
174
193
|
}
|
|
175
194
|
|
|
176
|
-
async function listProviders () {
|
|
195
|
+
async function listProviders (params) {
|
|
196
|
+
const { format } = params.options;
|
|
177
197
|
|
|
178
198
|
const providers = await Addon.listProviders();
|
|
179
199
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
200
|
+
switch (format) {
|
|
201
|
+
case 'json': {
|
|
202
|
+
const formattedProviders = providers.map((provider) => ({
|
|
203
|
+
id: provider.id,
|
|
204
|
+
name: provider.name,
|
|
205
|
+
shortDesc: provider.shortDesc,
|
|
206
|
+
regions: provider.regions,
|
|
207
|
+
plans: provider.plans.map((plan) => ({
|
|
208
|
+
id: plan.id,
|
|
209
|
+
name: plan.name,
|
|
210
|
+
slug: plan.slug,
|
|
211
|
+
})),
|
|
212
|
+
}));
|
|
213
|
+
Logger.printJson(formattedProviders);
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
case 'human':
|
|
217
|
+
default: {
|
|
218
|
+
const formattedProviders = providers.map((provider) => {
|
|
219
|
+
return [
|
|
220
|
+
colors.bold(provider.id),
|
|
221
|
+
provider.name,
|
|
222
|
+
provider.shortDesc || '',
|
|
223
|
+
];
|
|
224
|
+
});
|
|
225
|
+
Logger.println(formatTable(formattedProviders));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
188
228
|
}
|
|
189
229
|
|
|
190
230
|
async function showProvider (params) {
|
|
191
231
|
const [providerName] = params.args;
|
|
232
|
+
const { format } = params.options;
|
|
192
233
|
|
|
193
234
|
const provider = await Addon.getProvider(providerName);
|
|
194
235
|
const providerInfos = await Addon.getProviderInfos(providerName);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
Logger.println(` Options for version ${version}:`);
|
|
227
|
-
features.forEach(({ name, enabled }) => {
|
|
228
|
-
Logger.println(` ${name}: default=${enabled}`);
|
|
236
|
+
|
|
237
|
+
const formattedPlans = [...provider.plans]
|
|
238
|
+
.sort((a, b) => a.price - b.price)
|
|
239
|
+
.map((plan) => {
|
|
240
|
+
const formattedFeatures = plan.features
|
|
241
|
+
.map((feature) => ({
|
|
242
|
+
name: feature.name,
|
|
243
|
+
value: feature.value,
|
|
244
|
+
}))
|
|
245
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
246
|
+
|
|
247
|
+
const formattedPlan = {
|
|
248
|
+
id: plan.id,
|
|
249
|
+
name: plan.name,
|
|
250
|
+
slug: plan.slug,
|
|
251
|
+
features: formattedFeatures,
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
if (providerInfos != null) {
|
|
255
|
+
const planType = plan.features.find(({ name }) => name.toLowerCase() === 'type');
|
|
256
|
+
if (planType != null && planType.value.toLowerCase() === 'dedicated') {
|
|
257
|
+
const planVersions = Object.keys(providerInfos.dedicated);
|
|
258
|
+
formattedPlan.versions = planVersions.map((version) => {
|
|
259
|
+
return {
|
|
260
|
+
version,
|
|
261
|
+
isDefault: version === providerInfos.defaultDedicatedVersion,
|
|
262
|
+
options: providerInfos.dedicated[version].features.map((feature) => ({
|
|
263
|
+
name: feature.name,
|
|
264
|
+
enabledByDefault: feature.enabled,
|
|
265
|
+
})),
|
|
266
|
+
};
|
|
229
267
|
});
|
|
230
|
-
}
|
|
268
|
+
}
|
|
231
269
|
}
|
|
270
|
+
|
|
271
|
+
return formattedPlan;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const formattedProvider = {
|
|
275
|
+
id: provider.id,
|
|
276
|
+
name: provider.name,
|
|
277
|
+
shortDesc: provider.shortDesc,
|
|
278
|
+
regions: provider.regions,
|
|
279
|
+
plans: formattedPlans,
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
switch (format) {
|
|
283
|
+
case 'json': {
|
|
284
|
+
Logger.printJson(formattedProvider);
|
|
285
|
+
break;
|
|
232
286
|
}
|
|
233
|
-
|
|
287
|
+
case 'human':
|
|
288
|
+
default: {
|
|
289
|
+
Logger.println(colors.bold(formattedProvider.id));
|
|
290
|
+
Logger.println(`${formattedProvider.name}: ${formattedProvider.shortDesc}`);
|
|
291
|
+
Logger.println();
|
|
292
|
+
Logger.println(`Available regions: ${formattedProvider.regions.join(', ')}`);
|
|
293
|
+
Logger.println();
|
|
294
|
+
Logger.println('Available plans');
|
|
295
|
+
|
|
296
|
+
formattedProvider.plans.forEach((plan) => {
|
|
297
|
+
Logger.println(`Plan ${colors.bold(plan.slug)}`);
|
|
298
|
+
plan.features.forEach(({ name, value }) => Logger.println(` ${name}: ${value}`));
|
|
299
|
+
|
|
300
|
+
if (plan.versions != null) {
|
|
301
|
+
Logger.println(` Available versions: ${plan.versions.map(({ version, isDefault }) => isDefault ? `${version} (default)` : version).join(', ')}`);
|
|
302
|
+
plan.versions.forEach(({ version, options }) => {
|
|
303
|
+
Logger.println(` Options for version ${version}:`);
|
|
304
|
+
options.forEach(({ name, enabledByDefault }) => {
|
|
305
|
+
Logger.println(` ${name}: default=${enabledByDefault}`);
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
234
312
|
}
|
|
235
313
|
|
|
236
314
|
async function env (params) {
|