clever-tools 3.8.3 → 3.9.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.
Files changed (79) hide show
  1. package/bin/clever.js +177 -427
  2. package/package.json +10 -3
  3. package/src/command-options.js +4 -10
  4. package/src/command-promise-handler.js +5 -7
  5. package/src/commands/accesslogs.js +9 -13
  6. package/src/commands/activity.js +10 -14
  7. package/src/commands/addon.js +68 -38
  8. package/src/commands/applications.js +9 -16
  9. package/src/commands/cancel-deploy.js +5 -9
  10. package/src/commands/config.js +7 -11
  11. package/src/commands/console.js +5 -9
  12. package/src/commands/create.js +5 -9
  13. package/src/commands/curl.js +7 -11
  14. package/src/commands/database.js +13 -16
  15. package/src/commands/delete.js +4 -8
  16. package/src/commands/deploy.js +10 -14
  17. package/src/commands/diag.js +9 -11
  18. package/src/commands/domain.js +348 -17
  19. package/src/commands/drain.js +10 -20
  20. package/src/commands/env.js +13 -17
  21. package/src/commands/link.js +3 -7
  22. package/src/commands/login.js +13 -15
  23. package/src/commands/logout.js +3 -7
  24. package/src/commands/logs.js +9 -13
  25. package/src/commands/makeDefault.js +3 -7
  26. package/src/commands/notify-email.js +9 -19
  27. package/src/commands/open.js +5 -9
  28. package/src/commands/profile.js +21 -12
  29. package/src/commands/published-config.js +11 -15
  30. package/src/commands/restart.js +7 -11
  31. package/src/commands/scale.js +3 -7
  32. package/src/commands/service.js +8 -18
  33. package/src/commands/ssh.js +4 -8
  34. package/src/commands/status.js +7 -11
  35. package/src/commands/stop.js +5 -9
  36. package/src/commands/tcp-redirs.js +11 -15
  37. package/src/commands/unlink.js +4 -8
  38. package/src/commands/version.js +4 -6
  39. package/src/commands/webhooks.js +8 -16
  40. package/src/format-table.js +4 -8
  41. package/src/initial-setup.js +49 -0
  42. package/src/load-package-json.cjs +5 -0
  43. package/src/logger.js +5 -8
  44. package/src/models/activity.js +3 -7
  45. package/src/models/addon.js +37 -56
  46. package/src/models/app_configuration.js +18 -31
  47. package/src/models/application.js +37 -61
  48. package/src/models/application_configuration.js +11 -15
  49. package/src/models/configuration.js +11 -15
  50. package/src/models/deployments.js +6 -10
  51. package/src/models/domain.js +6 -10
  52. package/src/models/drain.js +5 -13
  53. package/src/models/exit-strategy-option.js +4 -8
  54. package/src/models/fs-utils.js +3 -7
  55. package/src/models/git.js +19 -34
  56. package/src/models/ids-resolver.js +6 -12
  57. package/src/models/interact.js +2 -6
  58. package/src/models/isomorphic-http-with-agent.js +3 -9
  59. package/src/models/json-array.js +1 -3
  60. package/src/models/log-v4.js +10 -12
  61. package/src/models/log.js +11 -15
  62. package/src/models/namespaces.js +7 -13
  63. package/src/models/node-dns-resolver.js +43 -0
  64. package/src/models/notification.js +8 -17
  65. package/src/models/organisation.js +4 -10
  66. package/src/models/send-to-api.js +17 -13
  67. package/src/models/user.js +16 -9
  68. package/src/models/utils.js +2 -6
  69. package/src/models/variables.js +4 -10
  70. package/src/parsers.js +21 -71
  71. package/src/commands/networkgroups/commands.js +0 -7
  72. package/src/commands/networkgroups/index.js +0 -67
  73. package/src/commands/networkgroups/members.js +0 -80
  74. package/src/commands/networkgroups/peers.js +0 -83
  75. package/src/models/format-ng-table.js +0 -115
  76. package/src/models/format-string.js +0 -44
  77. package/src/models/networkgroup.js +0 -64
  78. package/src/models/wireguard-conf.js +0 -95
  79. package/src/models/wireguard.js +0 -75
@@ -1,83 +0,0 @@
1
- 'use strict';
2
-
3
- const ngApi = require('@clevercloud/client/cjs/api/v4/network-group.js');
4
-
5
- const { sendToApi } = require('../../models/send-to-api.js');
6
-
7
- const Logger = require('../../logger.js');
8
- const NetworkGroup = require('../../models/networkgroup.js');
9
- const Formatter = require('../../models/format-string.js');
10
- const TableFormatter = require('../../models/format-ng-table.js');
11
-
12
- async function listPeers (params) {
13
- const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, json } = params.options;
14
- const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
15
- const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
16
-
17
- Logger.info(`Listing peers from Network Group ${Formatter.formatString(networkGroupId)}`);
18
- const result = await ngApi.listNetworkGroupPeers({ ownerId, networkGroupId }).then(sendToApi);
19
-
20
- if (json) {
21
- Logger.println(JSON.stringify(result, null, 2));
22
- }
23
- else {
24
- if (result.length === 0) {
25
- Logger.println(`No peer found. You can add an external one with ${Formatter.formatCommand('clever networkgroups peers add-external')}.`);
26
- }
27
- else {
28
- TableFormatter.printPeersTableHeader();
29
- result.forEach((peer) => {
30
- Logger.println(TableFormatter.formatPeersLine(peer));
31
- });
32
- }
33
- }
34
- }
35
-
36
- async function getPeer (params) {
37
- const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, 'peer-id': peerId, json } = params.options;
38
- const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
39
- const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
40
-
41
- Logger.info(`Getting details for peer ${Formatter.formatString(peerId)} in Network Group ${Formatter.formatString(networkGroupId)}`);
42
- const peer = await ngApi.getNetworkGroupPeer({ ownerId, networkGroupId, peerId }).then(sendToApi);
43
-
44
- if (json) {
45
- Logger.println(JSON.stringify(peer, null, 2));
46
- }
47
- else {
48
- TableFormatter.printPeersTableHeader();
49
- Logger.println(TableFormatter.formatPeersLine(peer));
50
- }
51
- }
52
-
53
- async function addExternalPeer (params) {
54
- const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, role, 'public-key': publicKey, label, parent, ip, port } = params.options;
55
- const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
56
- const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
57
-
58
- const body = { peer_role: role, public_key: publicKey, label, parent_member: parent, ip, port };
59
- Logger.info(`Adding external peer to Network Group ${Formatter.formatString(networkGroupId)}`);
60
- Logger.debug('Sending body: ' + JSON.stringify(body, null, 2));
61
- const { id: peerId } = await ngApi.createNetworkGroupExternalPeer({ ownerId, networkGroupId }, body).then(sendToApi);
62
-
63
- Logger.println(`External peer ${Formatter.formatString(peerId)} must have been added to Network Group ${Formatter.formatString(networkGroupId)}.`);
64
- return peerId;
65
- }
66
-
67
- async function removeExternalPeer (params) {
68
- const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, 'peer-id': peerId } = params.options;
69
- const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
70
- const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
71
-
72
- Logger.info(`Removing external peer ${Formatter.formatString(peerId)} from Network Group ${Formatter.formatString(networkGroupId)}`);
73
- await ngApi.deleteNetworkGroupExternalPeer({ ownerId, networkGroupId, peerId }).then(sendToApi);
74
-
75
- Logger.println(`External peer ${Formatter.formatString(peerId)} must have been removed from Network Group ${Formatter.formatString(networkGroupId)}.`);
76
- }
77
-
78
- module.exports = {
79
- listPeers,
80
- getPeer,
81
- addExternalPeer,
82
- removeExternalPeer,
83
- };
@@ -1,115 +0,0 @@
1
- 'use strict';
2
-
3
- const formatNgTable = require('../format-table.js');
4
- const colors = require('colors/safe');
5
-
6
- const AppConfig = require('./app_configuration.js');
7
- const Logger = require('../logger.js');
8
- const Formatter = require('./format-string.js');
9
-
10
- function printSeparator (columnLengths) {
11
- Logger.println('─'.repeat(columnLengths.reduce((a, b) => a + b + 2)));
12
- }
13
-
14
- // We use examples of maximum width text to have a clean display
15
- const networkGroupsTableColumnLengths = [
16
- 39, /* id length */
17
- 48, /* label length */
18
- 7, /* members length */
19
- 5, /* peers length */
20
- 48, /* description */
21
- ];
22
- const formatNetworkGroupsTable = formatNgTable(networkGroupsTableColumnLengths);
23
- function formatNetworkGroupsLine (ng) {
24
- return formatNetworkGroupsTable([
25
- [
26
- Formatter.formatId(ng.id),
27
- Formatter.formatString(ng.label, false),
28
- Formatter.formatNumber(ng.members.length),
29
- Formatter.formatNumber(ng.peers.length),
30
- Formatter.formatString(ng.description || ' ', false),
31
- ],
32
- ]);
33
- };
34
- function printNetworkGroupsTableHeader () {
35
- Logger.println(colors.bold(formatNetworkGroupsTable([
36
- ['Network Group ID', 'Label', 'Members', 'Peers', 'Description'],
37
- ])));
38
- printSeparator(networkGroupsTableColumnLengths);
39
- }
40
-
41
- const membersTableColumnLengths = [
42
- 48, /* id length */
43
- 12, /* type length */
44
- 48, /* label length */
45
- 110, /* domain-name length */
46
- ];
47
- const formatMembersTable = formatNgTable(membersTableColumnLengths);
48
- async function formatMembersLine (member, showAliases = false) {
49
- return formatMembersTable([
50
- [
51
- showAliases
52
- ? Formatter.formatString(await AppConfig.getMostNaturalName(member.id), false)
53
- : Formatter.formatId(member.id),
54
- Formatter.formatString(member.type, false),
55
- Formatter.formatString(member.label, false),
56
- Formatter.formatString(member.domain_name || ' ', false),
57
- ],
58
- ]);
59
- };
60
- async function printMembersTableHeader (naturalName = false) {
61
- Logger.println(colors.bold(formatMembersTable([
62
- [
63
- naturalName ? 'Member' : 'Member ID',
64
- 'Member Type',
65
- 'Label',
66
- 'Domain Name',
67
- ],
68
- ])));
69
- printSeparator(membersTableColumnLengths);
70
- }
71
-
72
- const peersTableColumnLengths = [
73
- 45, /* id length */
74
- 15, /* ip */
75
- 36, /* hostname */
76
- 12, /* type length */
77
- 14, /* endpoint type length */
78
- 36, /* label length */
79
- ];
80
- const formatPeersTable = formatNgTable(peersTableColumnLengths);
81
- function formatPeersLine (peer) {
82
- const ip = (peer.endpoint.type === 'ServerEndpoint') ? peer.endpoint.ng_term.host : peer.endpoint.ng_ip;
83
- return formatPeersTable([
84
- [
85
- Formatter.formatId(peer.id),
86
- Formatter.formatIp(ip),
87
- Formatter.formatString(peer.hostname, false),
88
- Formatter.formatString(peer.type, false),
89
- Formatter.formatString(peer.endpoint.type, false),
90
- Formatter.formatString(peer.label, false),
91
- ],
92
- ]);
93
- };
94
- function printPeersTableHeader () {
95
- Logger.println(colors.bold(formatPeersTable([
96
- [
97
- 'Peer ID',
98
- 'IP Address',
99
- 'Hostname',
100
- 'Peer Type',
101
- 'Endpoint Type',
102
- 'Label',
103
- ],
104
- ])));
105
- printSeparator(peersTableColumnLengths);
106
- }
107
-
108
- module.exports = {
109
- formatNetworkGroupsLine,
110
- printNetworkGroupsTableHeader,
111
- formatMembersLine,
112
- printMembersTableHeader,
113
- formatPeersLine,
114
- printPeersTableHeader,
115
- };
@@ -1,44 +0,0 @@
1
- 'use strict';
2
-
3
- const colors = require('colors/safe');
4
-
5
- function formatId (id) {
6
- return colors.dim(id);
7
- }
8
-
9
- function formatString (str, decorated = true) {
10
- const string = decorated ? `'${str}'` : str;
11
- return colors.green(string);
12
- }
13
-
14
- function formatNumber (number) {
15
- return colors.yellow(number);
16
- }
17
-
18
- function formatIp (ip) {
19
- return colors.cyan(ip);
20
- }
21
-
22
- function formatUrl (url, decorated = true) {
23
- const string = decorated ? `<${url}>` : url;
24
- return colors.cyan(string);
25
- }
26
-
27
- function formatCommand (command, decorated = true) {
28
- const string = decorated ? `\`${command}\`` : command;
29
- return colors.magenta(string);
30
- }
31
-
32
- function formatCode (code, decorated = true) {
33
- return formatCommand(code, decorated);
34
- }
35
-
36
- module.exports = {
37
- formatId,
38
- formatString,
39
- formatNumber,
40
- formatIp,
41
- formatUrl,
42
- formatCommand,
43
- formatCode,
44
- };
@@ -1,64 +0,0 @@
1
- 'use strict';
2
-
3
- const autocomplete = require('cliparse').autocomplete;
4
- const Organisation = require('../models/organisation.js');
5
- const User = require('../models/user.js');
6
- const AppConfig = require('./app_configuration.js');
7
- const ngApi = require('@clevercloud/client/cjs/api/v4/network-group.js');
8
- const { sendToApi } = require('./send-to-api.js');
9
-
10
- async function getOwnerId (orgaIdOrName, alias) {
11
- if (orgaIdOrName == null) {
12
- try {
13
- return (await AppConfig.getAppDetails({ alias })).ownerId;
14
- }
15
- catch (error) {
16
- return (await User.getCurrentId());
17
- }
18
- }
19
- else {
20
- return (await Organisation.getId(orgaIdOrName));
21
- }
22
- }
23
-
24
- async function getId (ownerId, ngIdOrLabel) {
25
- if (ngIdOrLabel == null) {
26
- return null;
27
- }
28
-
29
- if (ngIdOrLabel.ng_id != null) {
30
- return ngIdOrLabel.ng_id;
31
- }
32
-
33
- return getByLabel(ownerId, ngIdOrLabel.ng_label)
34
- .then((ng) => ng.id);
35
- }
36
-
37
- async function getByLabel (owner_id, label) {
38
- const networkGroups = await ngApi.listNetworkGroups({ owner_id }).then(sendToApi);
39
- const filteredNgs = networkGroups.filter((ng) => ng.label === label);
40
-
41
- if (filteredNgs.length === 0) {
42
- throw new Error('Network Group not found');
43
- }
44
- if (filteredNgs.length > 1) {
45
- throw new Error('Ambiguous Network Group label');
46
- }
47
-
48
- return filteredNgs[0];
49
- }
50
-
51
- function listAvailablePeerRoles () {
52
- return autocomplete.words(['client', 'server']);
53
- }
54
-
55
- function listAvailableMemberTypes () {
56
- return autocomplete.words(['application', 'addon', 'external']);
57
- }
58
-
59
- module.exports = {
60
- getOwnerId,
61
- getId,
62
- listAvailablePeerRoles,
63
- listAvailableMemberTypes,
64
- };
@@ -1,95 +0,0 @@
1
- 'use strict';
2
-
3
- const os = require('os');
4
- const { promises: fs, existsSync } = require('fs');
5
- const path = require('path');
6
-
7
- const Logger = require('../logger.js');
8
- const Formatter = require('./format-string.js');
9
-
10
- function getWgConfFolder () {
11
- // TODO: See if we can use runtime dirs
12
- return path.join(os.tmpdir(), 'com.clever-cloud.networkgroups');
13
- }
14
-
15
- async function createWgConfFolderIfNeeded () {
16
- const confFolder = getWgConfFolder();
17
- if (!existsSync(confFolder)) {
18
- await fs.mkdir(confFolder);
19
- }
20
- }
21
-
22
- function getWgConfInformation (ngId) {
23
- const confName = `wgcc${ngId.slice(-8)}`;
24
- const confPath = path.join(getWgConfFolder(), `${confName}.conf`);
25
-
26
- return { confName, confPath };
27
- }
28
-
29
- function getPeerIdPath (confName) {
30
- return path.join(getWgConfFolder(), `${confName}.id`);
31
- }
32
-
33
- async function storePeerId (peerId, confName) {
34
- const filePath = getPeerIdPath(confName);
35
-
36
- try {
37
- await fs.writeFile(filePath, peerId, { mode: 0o600, flag: 'wx' });
38
- Logger.info(`Saved peer ID file to ${Formatter.formatUrl(filePath)}`);
39
- }
40
- catch (error) {
41
- throw new Error(`Error saving peer ID: ${error}`);
42
- }
43
- }
44
-
45
- async function getPeerId (ngId) {
46
- const { confName } = getWgConfInformation(ngId);
47
- const filePath = getPeerIdPath(confName);
48
- if (existsSync(filePath)) {
49
- Logger.debug(`Reading peer ID from ${Formatter.formatUrl(filePath)}`);
50
- return (await fs.readFile(filePath, { encoding: 'utf-8' })).trim();
51
- }
52
- else {
53
- Logger.debug(`No file found at ${Formatter.formatUrl(filePath)}`);
54
- return null;
55
- }
56
- }
57
-
58
- async function deletePeerIdFile (ngId) {
59
- const { confName } = getWgConfInformation(ngId);
60
- const filePath = getPeerIdPath(confName);
61
- // We need `force: true` to avoid errors if file doesn't exist
62
- await fs.rm(filePath, { force: true });
63
- Logger.info(`Deleted peer ID from ${Formatter.formatUrl(filePath)}`);
64
- }
65
-
66
- async function getInterfaceName (confName) {
67
- // This file is created by WireGuard®, hence the file path (`/var/run/…`)
68
- // TODO: Handle Windows (not yet supported by `wg-quick` anyway)
69
- const interfaceNameFile = path.join('/var', 'run', 'wireguard', `${confName}.name`);
70
-
71
- Logger.debug(`Reading WireGuard® interface name in ${Formatter.formatUrl(interfaceNameFile)}…`);
72
- const interfaceName = (await fs.readFile(interfaceNameFile, { encoding: 'utf-8' })).trim();
73
- Logger.debug(`Found WireGuard® interface name ${Formatter.formatString(interfaceName)} for ${Formatter.formatString(confName)}`);
74
- return interfaceName;
75
- }
76
-
77
- function confWithoutPlaceholders (conf, { privateKey }) {
78
- conf = conf.replace('<%PrivateKey%>', privateKey);
79
-
80
- // TODO: This just removes leading and trailing new lines in the configuration file
81
- // It should be better formatted on the API's side
82
- conf = conf.trim();
83
-
84
- return conf;
85
- }
86
-
87
- module.exports = {
88
- createWgConfFolderIfNeeded,
89
- getWgConfInformation,
90
- storePeerId,
91
- getPeerId,
92
- deletePeerIdFile,
93
- getInterfaceName,
94
- confWithoutPlaceholders,
95
- };
@@ -1,75 +0,0 @@
1
- 'use strict';
2
-
3
- const { execSync, spawnSync } = require('child_process');
4
- const Logger = require('../logger.js');
5
-
6
- function privateKey () {
7
- return execSync('wg genkey', { encoding: 'utf-8' }).trim();
8
- }
9
-
10
- function publicKey (privateKey) {
11
- return execSync(`echo '${privateKey}' | wg pubkey`, { encoding: 'utf-8' }).trim();
12
- }
13
-
14
- function up (confPath) {
15
- // We must use `spawn` with `detached: true` instead of `exec`
16
- // because `wg-quick up` starts a `wireguard-go` used by `wg-quick down`
17
- const { stdout, stderr } = spawnSync('wg-quick', ['up', confPath], { detached: true, encoding: 'utf-8' });
18
- if (stdout.length > 0) {
19
- Logger.debug(stdout.trim());
20
- }
21
- if (stderr.length > 0) {
22
- Logger.debug(stderr.trim());
23
- }
24
- Logger.println('Activated WireGuard® tunnel');
25
- }
26
-
27
- function update (confPath, interfaceName) {
28
- try {
29
- // Update WireGuard® configuration
30
- execSync(`wg-quick strip ${confPath} | wg syncconf ${interfaceName} /dev/stdin`);
31
- Logger.info('Updated WireGuard® tunnel configuration');
32
- }
33
- catch (error) {
34
- throw new Error(`Error updating WireGuard® tunnel configuration: ${error}`);
35
- }
36
- }
37
-
38
- function down (confPath) {
39
- const { stdout, stderr } = spawnSync('wg-quick', ['down', confPath], { encoding: 'utf-8' });
40
- if (stdout.length > 0) {
41
- Logger.debug(stdout.trim());
42
- }
43
- if (stderr.length > 0) {
44
- Logger.debug(stderr.trim());
45
- }
46
- }
47
-
48
- /**
49
- * Check that `wg` and `wg-quick` are installed
50
- */
51
- function checkAvailable () {
52
- try {
53
- // The redirect to `/dev/null` ensures that your program does not produce the output of these commands.
54
- execSync('which wg > /dev/null 2>&1');
55
- execSync('which wg-quick > /dev/null 2>&1');
56
-
57
- // TODO: Handle Windows
58
- // - Those checks won't work on Windows, and wg-quick doesn't exist anyway.
59
- // - We need to wait for a Windows version of wg-quick to support the rest of the operations
60
- // - Or we could use vanilla wg on Windows, and wg-quick on other OSs
61
- return true;
62
- }
63
- catch {
64
- return false;
65
- }
66
- }
67
-
68
- module.exports = {
69
- privateKey,
70
- publicKey,
71
- up,
72
- update,
73
- down,
74
- checkAvailable,
75
- };