clever-tools 4.11.0 → 5.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.
Files changed (45) hide show
  1. package/bin/clever.js +5 -10
  2. package/package.json +27 -27
  3. package/src/commands/accesslogs/accesslogs.command.js +27 -19
  4. package/src/commands/accesslogs/accesslogs.docs.md +1 -1
  5. package/src/commands/addon/addon.docs.md +2 -2
  6. package/src/commands/config-provider/config-provider.docs.md +5 -5
  7. package/src/commands/database/database.docs.md +2 -2
  8. package/src/commands/deploy/deploy.docs.md +7 -7
  9. package/src/commands/drain/drain.args.js +6 -0
  10. package/src/commands/drain/drain.create.betterstack.command.js +36 -0
  11. package/src/commands/drain/drain.create.command.js +0 -132
  12. package/src/commands/drain/drain.create.datadog.command.js +27 -0
  13. package/src/commands/drain/drain.create.elasticsearch.command.js +48 -0
  14. package/src/commands/drain/drain.create.newrelic.command.js +36 -0
  15. package/src/commands/drain/drain.create.ovh-tcp.command.js +29 -0
  16. package/src/commands/drain/drain.create.raw-http.command.js +30 -0
  17. package/src/commands/drain/drain.create.splunk.command.js +61 -0
  18. package/src/commands/drain/drain.create.syslog-tcp.command.js +29 -0
  19. package/src/commands/drain/drain.create.syslog-udp.command.js +29 -0
  20. package/src/commands/drain/drain.docs.md +273 -8
  21. package/src/commands/drain/drain.options.js +26 -0
  22. package/src/commands/features/features.list.command.js +6 -13
  23. package/src/commands/global.commands.js +23 -1
  24. package/src/commands/global.options.js +1 -1
  25. package/src/commands/k8s/k8s.docs.md +17 -17
  26. package/src/commands/keycloak/keycloak.docs.md +11 -11
  27. package/src/commands/kv/kv.docs.md +1 -1
  28. package/src/commands/link/link.docs.md +1 -1
  29. package/src/commands/login/login.command.js +11 -1
  30. package/src/commands/matomo/matomo.docs.md +6 -6
  31. package/src/commands/metabase/metabase.docs.md +9 -9
  32. package/src/commands/ng/ng.docs.md +10 -10
  33. package/src/commands/oauth-consumers/oauth-consumers.docs.md +4 -4
  34. package/src/commands/otoroshi/otoroshi.docs.md +13 -13
  35. package/src/commands/service/service.docs.md +4 -4
  36. package/src/commands/ssh/ssh.command.js +6 -0
  37. package/src/config/features.js +61 -29
  38. package/src/format-table.js +14 -10
  39. package/src/initial-setup.js +13 -0
  40. package/src/initial-update-notifier.js +2 -5
  41. package/src/lib/access-logs-transport.js +29 -0
  42. package/src/lib/cliparse-patched.js +1 -1
  43. package/src/models/drain.js +39 -13
  44. package/src/models/git-system.js +2 -2
  45. package/src/models/git.js +1 -1
package/bin/clever.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  verboseOption,
15
15
  versionOption,
16
16
  } from '../src/commands/global.options.js';
17
- import { EXPERIMENTAL_FEATURES, getFeatures } from '../src/config/features.js';
17
+ import { EXPERIMENTAL_FEATURES, isFeatureEnabled } from '../src/config/features.js';
18
18
  import { cliparse } from '../src/lib/cliparse-patched.js';
19
19
  import { styleText } from '../src/lib/style-text.js';
20
20
  import { getDefault, getEnumValues, isBoolean, isRequired } from '../src/lib/zod-utils.js';
@@ -47,14 +47,10 @@ if (process.argv[2] === 'curl') {
47
47
  }
48
48
 
49
49
  async function run() {
50
- // Get enabled experimental features
51
- /** @type {Record<string, boolean>} */
52
- const featuresFromConf = await getFeatures();
53
-
54
50
  // Build all commands from globalCommands
55
51
  const commands = [];
56
52
  for (const [name, entry] of /** @type {[string, CommandEntry][]} */ (Object.entries(globalCommands))) {
57
- const command = buildCommand(name, entry, featuresFromConf);
53
+ const command = buildCommand(name, entry);
58
54
  if (command != null) {
59
55
  commands.push(command);
60
56
  }
@@ -96,10 +92,9 @@ async function run() {
96
92
  * Recursively build commands from the global commands structure
97
93
  * @param {string} name - Command name
98
94
  * @param {CommandEntry} commandEntry - Command entry (either a command object or [command, subcommands])
99
- * @param {Record<string, boolean>} featuresFromConf - Enabled features configuration
100
95
  * @returns {Object|null} cliparse command or null if filtered out
101
96
  */
102
- function buildCommand(name, commandEntry, featuresFromConf) {
97
+ function buildCommand(name, commandEntry) {
103
98
  /** @type {CommandDefinition} */
104
99
  let commandDef;
105
100
  /** @type {Record<string, CommandEntry>} */
@@ -113,14 +108,14 @@ function buildCommand(name, commandEntry, featuresFromConf) {
113
108
  }
114
109
 
115
110
  // Check if this is an experimental feature that needs to be enabled
116
- if (commandDef.featureFlag && !featuresFromConf[commandDef.featureFlag]) {
111
+ if (commandDef.featureFlag && !isFeatureEnabled(commandDef.featureFlag)) {
117
112
  return null;
118
113
  }
119
114
 
120
115
  // Build subcommands recursively
121
116
  const subcommands = [];
122
117
  for (const [subName, subEntry] of Object.entries(subcommandsMap)) {
123
- const subcommand = buildCommand(subName, subEntry, featuresFromConf);
118
+ const subcommand = buildCommand(subName, subEntry);
124
119
  if (subcommand != null) {
125
120
  subcommands.push(subcommand);
126
121
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clever-tools",
3
- "version": "4.11.0",
3
+ "version": "5.0.0",
4
4
  "description": "Command Line Interface for Clever Cloud.",
5
5
  "keywords": [
6
6
  "cli",
@@ -49,56 +49,56 @@
49
49
  "typecheck": "tsc -p tsconfig.json"
50
50
  },
51
51
  "dependencies": {
52
- "@babel/traverse": "7.28.5",
53
- "@clevercloud/client": "12.0.0",
54
- "@inquirer/prompts": "7.8.4",
52
+ "@clevercloud/client": "12.6.3",
53
+ "@inquirer/prompts": "8.7.2",
55
54
  "char-regex": "2.0.2",
56
55
  "cliparse": "0.5.0",
57
- "dedent": "1.7.0",
56
+ "dedent": "1.7.2",
58
57
  "eventsource": "4.1.0",
59
58
  "ioredis": "5.8.2",
60
- "iso8601-duration": "2.1.3",
61
- "isomorphic-git": "1.35.1",
59
+ "iso8601-duration": "2.1.4",
60
+ "isomorphic-git": "1.41.9",
62
61
  "linux-release-info": "3.0.0",
63
- "lodash": "4.17.21",
64
- "open": "10.2.0",
65
- "semver": "7.7.3",
66
- "simple-git": "3.30.0",
67
- "slugify": "1.6.6",
68
- "tldts": "7.0.19",
62
+ "lodash": "4.18.1",
63
+ "open": "11.0.2",
64
+ "semver": "7.8.5",
65
+ "simple-git": "3.36.0",
66
+ "slugify": "1.6.9",
67
+ "tldts": "7.4.12",
68
+ "undici": "8.10.2",
69
69
  "update-notifier": "7.3.1",
70
70
  "xdg": "0.1.1",
71
- "zod": "4.1.13"
71
+ "zod": "4.5.4"
72
72
  },
73
73
  "devDependencies": {
74
- "@aws-sdk/client-s3": "3.940.0",
74
+ "@aws-sdk/client-s3": "3.1128.0",
75
75
  "@clevercloud/eslint-config": "1.1.0",
76
- "@commitlint/cli": "19.8.1",
77
- "@commitlint/config-conventional": "19.8.1",
78
- "@eslint/compat": "1.3.2",
79
- "@rollup/plugin-commonjs": "28.0.6",
76
+ "@commitlint/cli": "21.2.2",
77
+ "@commitlint/config-conventional": "21.2.2",
78
+ "@eslint/compat": "2.1.1",
79
+ "@rollup/plugin-commonjs": "29.0.3",
80
80
  "@rollup/plugin-json": "6.1.0",
81
81
  "@rollup/plugin-node-resolve": "16.0.3",
82
82
  "@types/mime-types": "3.0.1",
83
- "@types/picomatch": "4.0.2",
84
- "@yao-pkg/pkg": "6.10.1",
83
+ "@types/picomatch": "4.0.3",
84
+ "@yao-pkg/pkg": "6.22.0",
85
85
  "eslint": "9.39.1",
86
- "globals": "16.5.0",
86
+ "globals": "17.12.0",
87
87
  "mime-types": "3.0.2",
88
- "prettier": "3.7.3",
88
+ "prettier": "3.9.6",
89
89
  "prettier-plugin-organize-imports": "4.3.0",
90
90
  "remark-parse": "11.0.0",
91
91
  "remark-stringify": "11.0.0",
92
- "rollup": "4.53.3",
93
- "tinyglobby": "0.2.15",
92
+ "rollup": "4.63.1",
93
+ "tinyglobby": "0.2.17",
94
94
  "typescript": "5.9.3",
95
95
  "unified": "11.0.5"
96
96
  },
97
97
  "engines": {
98
- "node": ">=22"
98
+ "node": ">=24"
99
99
  },
100
100
  "volta": {
101
- "node": "22.17.0",
101
+ "node": "24.18.1",
102
102
  "npm": "11.6.2"
103
103
  }
104
104
  }
@@ -1,6 +1,7 @@
1
1
  import { ApplicationAccessLogStream } from '@clevercloud/client/esm/streams/access-logs.js';
2
2
  import { formatTable } from '../../format-table.js';
3
3
  import { formatClf } from '../../lib/access-logs-clf.js';
4
+ import { guessAccessLogTransport } from '../../lib/access-logs-transport.js';
4
5
  import { defineCommand } from '../../lib/define-command.js';
5
6
  import { styleText } from '../../lib/style-text.js';
6
7
  import { Logger } from '../../logger.js';
@@ -23,28 +24,40 @@ const THROTTLE_PER_IN_MILLISECONDS = 100;
23
24
 
24
25
  const CITY_MAX_LENGTH = 20;
25
26
 
27
+ /**
28
+ * Format an access log as a human readable table row.
29
+ *
30
+ * Non-HTTP access logs (TCP redirections, SSH connections, or HTTP connections cut before the
31
+ * proxy could answer) have no `http` section: their status and request columns are omitted.
32
+ *
33
+ * @param {object} log an access log, as emitted by `ApplicationAccessLogStream`
34
+ * @returns {string}
35
+ */
26
36
  function formatHuman(log) {
27
37
  const { date, http, source } = log;
28
38
  const country = source.countryCode ?? '(unknown)';
29
39
  const hasSourceCity = source.city ?? '';
30
40
 
31
- return formatTable(
32
- [
33
- [
34
- styleText('grey', date.toISOString(date)),
35
- source.ip,
36
- `${country}${hasSourceCity ? '/' + truncateWithEllipsis(CITY_MAX_LENGTH, source.city) : ''}`,
37
- colorStatusCode(http.response.statusCode),
38
- http.request.method.toString().padEnd(4, ' ') + ' ' + http.request.path,
39
- ],
40
- ],
41
-
42
- ACCESSLOG_COLUMN_WIDTHS,
43
- );
41
+ const columns = [
42
+ styleText('grey', date.toISOString(date)),
43
+ guessAccessLogTransport(log),
44
+ source.ip,
45
+ `${country}${hasSourceCity ? '/' + truncateWithEllipsis(CITY_MAX_LENGTH, source.city) : ''}`,
46
+ ];
47
+
48
+ if (http != null) {
49
+ columns.push(colorStatusCode(http.response.statusCode));
50
+ columns.push(http.request.method);
51
+ columns.push(http.request.path);
52
+ }
53
+
54
+ return formatTable([columns], ACCESSLOG_COLUMN_WIDTHS);
44
55
  }
45
56
 
46
57
  const ACCESSLOG_COLUMN_WIDTHS = [
47
58
  '2024-06-24T08:05:43.880Z',
59
+ // longest transport name
60
+ 'HTTP',
48
61
  '255.255.255.255',
49
62
  // country / city
50
63
  2 + 1 + CITY_MAX_LENGTH,
@@ -134,7 +147,7 @@ export const accesslogsCommand = defineCommand({
134
147
  Logger.printJson(log);
135
148
  break;
136
149
  case 'clf':
137
- // when the connection is cut too early, or for TCP redirections, we don't have HTTP section
150
+ // CLF only describes HTTP requests, so logs without an HTTP section are skipped
138
151
  if (log.http == null) {
139
152
  break;
140
153
  }
@@ -143,11 +156,6 @@ export const accesslogsCommand = defineCommand({
143
156
  break;
144
157
  case 'human':
145
158
  default:
146
- // when the connection is cut too early, or for TCP redirections, we don't have HTTP section
147
- if (log.http == null) {
148
- break;
149
- }
150
-
151
159
  Logger.println(formatHuman(log));
152
160
  break;
153
161
  }
@@ -17,4 +17,4 @@ clever accesslogs [options]
17
17
  |`-a`, `--alias` `<alias>`|Short name for the application|
18
18
  |`--app` `<app-id\|app-name>`|Application to manage by its ID (or name, if unambiguous)|
19
19
  |`--before`, `--until` `<before>`|Fetch logs before this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h)|
20
- |`-F`, `--format` `<format>`|Output format (human, json, json-stream, clf) (default: human)|
20
+ |`-F`, `--format` `<format>`|Output format (clf only outputs HTTP access logs) (human, json, json-stream, clf) (default: human)|
@@ -55,7 +55,7 @@ clever addon delete <addon-id|addon-name> [options]
55
55
 
56
56
  |Name|Description|
57
57
  |---|---|
58
- |`addon-id|addon-name`|Add-on ID (or name, if unambiguous)|
58
+ |`addon-id\|addon-name`|Add-on ID (or name, if unambiguous)|
59
59
 
60
60
  ### ⚙️ Options
61
61
 
@@ -148,7 +148,7 @@ clever addon rename <addon-id|addon-name> <addon-name> [options]
148
148
 
149
149
  |Name|Description|
150
150
  |---|---|
151
- |`addon-id|addon-name`|Add-on ID (or name, if unambiguous)|
151
+ |`addon-id\|addon-name`|Add-on ID (or name, if unambiguous)|
152
152
  |`addon-name`|Add-on name|
153
153
 
154
154
  ### ⚙️ Options
@@ -20,7 +20,7 @@ clever config-provider get <addon-id|config-provider-id|addon-name> [options]
20
20
 
21
21
  |Name|Description|
22
22
  |---|---|
23
- |`addon-id|config-provider-id|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
23
+ |`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
24
24
 
25
25
  ### ⚙️ Options
26
26
 
@@ -41,7 +41,7 @@ clever config-provider import <addon-id|config-provider-id|addon-name> [options]
41
41
 
42
42
  |Name|Description|
43
43
  |---|---|
44
- |`addon-id|config-provider-id|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
44
+ |`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
45
45
 
46
46
  ### ⚙️ Options
47
47
 
@@ -75,7 +75,7 @@ clever config-provider open <addon-id|config-provider-id|addon-name>
75
75
 
76
76
  |Name|Description|
77
77
  |---|---|
78
- |`addon-id|config-provider-id|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
78
+ |`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
79
79
 
80
80
  ## ➡️ `clever config-provider rm` <kbd>Since 4.6.0</kbd>
81
81
 
@@ -89,7 +89,7 @@ clever config-provider rm <addon-id|config-provider-id|addon-name> <variable-nam
89
89
 
90
90
  |Name|Description|
91
91
  |---|---|
92
- |`addon-id|config-provider-id|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
92
+ |`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
93
93
  |`variable-name`|Name of the environment variable|
94
94
 
95
95
  ## ➡️ `clever config-provider set` <kbd>Since 4.6.0</kbd>
@@ -104,6 +104,6 @@ clever config-provider set <addon-id|config-provider-id|addon-name> <variable-na
104
104
 
105
105
  |Name|Description|
106
106
  |---|---|
107
- |`addon-id|config-provider-id|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
107
+ |`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
108
108
  |`variable-name`|Name of the environment variable|
109
109
  |`variable-value`|Value of the environment variable|
@@ -20,7 +20,7 @@ clever database backups <database-id|addon-id> [options]
20
20
 
21
21
  |Name|Description|
22
22
  |---|---|
23
- |`database-id|addon-id`|Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)|
23
+ |`database-id\|addon-id`|Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)|
24
24
 
25
25
  ### ⚙️ Options
26
26
 
@@ -41,7 +41,7 @@ clever database backups download <database-id|addon-id> <backup-id> [options]
41
41
 
42
42
  |Name|Description|
43
43
  |---|---|
44
- |`database-id|addon-id`|Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)|
44
+ |`database-id\|addon-id`|Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)|
45
45
  |`backup-id`|A Database backup ID (format: UUID)|
46
46
 
47
47
  ### ⚙️ Options
@@ -23,21 +23,21 @@ clever deploy [options]
23
23
 
24
24
  ### 🧪 Experimental: System git backend
25
25
 
26
- Clever Tools uses a current JS implementation for git operations. This works without requiring git to be installed on your system, but has some limitations:
26
+ Clever Tools uses the `git` command installed on your system for git operations (it must be in your `PATH` environment variable).
27
+
28
+ If `git` is not available, or you experience an issue with this backend, you can fall back to the previous pure JS implementation. This works without requiring git to be installed on your system, but has some limitations:
27
29
 
28
30
  * **HTTP-only**: cannot use SSH-based git protocols
29
31
  * **Slow performance** on repositories with rewritten history (rebases, squashes)
30
32
  * **Connection timeouts** on large repositories or when pushing big files, due to HTTP-based transfers
31
- * **No git worktree support**: deploying from a linked git worktree (`git worktree add`) fails with `Could not find HEAD`. The system git backend deploys from worktrees just like from the main working tree
32
-
33
- If you experience any of these issues, you can enable the **system git backend** which uses the `git` command installed on your system (it must be in your `PATH` environment variable).
33
+ * **No git worktree support**: deploying from a linked git worktree (`git worktree add`) fails with `Could not find HEAD`
34
34
 
35
35
  ```bash
36
- clever features enable system-git
36
+ clever features disable system-git
37
37
  ```
38
38
 
39
- To disable and return to the current JS implementation:
39
+ To switch back to the system git backend:
40
40
 
41
41
  ```bash
42
- clever features disable system-git
42
+ clever features enable system-git
43
43
  ```
@@ -6,3 +6,9 @@ export const drainIdArg = defineArgument({
6
6
  description: 'Drain ID',
7
7
  placeholder: 'drain-id',
8
8
  });
9
+
10
+ export const drainUrlArg = defineArgument({
11
+ schema: z.string(),
12
+ description: 'Drain URL',
13
+ placeholder: 'drain-url',
14
+ });
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ import { defineCommand } from '../../lib/define-command.js';
3
+ import { defineOption } from '../../lib/define-option.js';
4
+ import { styleText } from '../../lib/style-text.js';
5
+ import { Logger } from '../../logger.js';
6
+ import { createLogDrain, resolveDrainResource } from '../../models/drain.js';
7
+ import { addonIdOrRealIdOption, aliasOption, appIdOrNameOption } from '../global.options.js';
8
+ import { drainUrlArg } from './drain.args.js';
9
+
10
+ export const drainCreateBetterstackCommand = defineCommand({
11
+ description: 'Create a Better Stack drain',
12
+ since: '4.11.0',
13
+ options: {
14
+ sourceToken: defineOption({
15
+ name: 'source-token',
16
+ schema: z.string().min(1),
17
+ description: 'Source token',
18
+ aliases: ['t'],
19
+ placeholder: 'source-token',
20
+ }),
21
+ alias: aliasOption,
22
+ appIdOrName: appIdOrNameOption,
23
+ addonIdOrRealId: addonIdOrRealIdOption,
24
+ },
25
+ args: [drainUrlArg],
26
+ async handler(options, url) {
27
+ const { alias, appIdOrName, addonIdOrRealId, sourceToken } = options;
28
+ const { ownerId, resourceId } = await resolveDrainResource(alias, appIdOrName, addonIdOrRealId);
29
+
30
+ const drain = await createLogDrain('BETTERSTACK', ownerId, resourceId, url, { sourceToken });
31
+
32
+ Logger.printSuccess(
33
+ `Better Stack drain ${styleText(['bold', 'green'], drain.id)} has been successfully created and enabled!`,
34
+ );
35
+ },
36
+ });
@@ -1,138 +1,6 @@
1
- import { z } from 'zod';
2
- import { createDrain } from '../../clever-client/drains.js';
3
- import { defineArgument } from '../../lib/define-argument.js';
4
1
  import { defineCommand } from '../../lib/define-command.js';
5
- import { defineOption } from '../../lib/define-option.js';
6
- import { styleText } from '../../lib/style-text.js';
7
- import { Logger } from '../../logger.js';
8
- import { DRAIN_TYPE_CLI_CODES, DRAIN_TYPES, resolveDrainResource } from '../../models/drain.js';
9
- import { sendToApi } from '../../models/send-to-api.js';
10
- import { addonIdOrRealIdOption, aliasOption, appIdOrNameOption } from '../global.options.js';
11
2
 
12
3
  export const drainCreateCommand = defineCommand({
13
4
  description: 'Create a drain',
14
5
  since: '0.9.0',
15
- options: {
16
- username: defineOption({
17
- name: 'username',
18
- schema: z.string().optional(),
19
- description: 'Basic auth username (for elasticsearch or raw-http)',
20
- aliases: ['u'],
21
- placeholder: 'username',
22
- }),
23
- password: defineOption({
24
- name: 'password',
25
- schema: z.string().optional(),
26
- description: 'Basic auth password (for elasticsearch or raw-http)',
27
- aliases: ['p'],
28
- placeholder: 'password',
29
- }),
30
- apiKey: defineOption({
31
- name: 'api-key',
32
- schema: z.string().optional(),
33
- description: 'API key (for newrelic)',
34
- aliases: ['k'],
35
- placeholder: 'api-key',
36
- }),
37
- sourceToken: defineOption({
38
- name: 'source-token',
39
- schema: z.string().optional(),
40
- description: 'Source token (for betterstack)',
41
- aliases: ['t'],
42
- placeholder: 'source-token',
43
- }),
44
- indexPrefix: defineOption({
45
- name: 'index-prefix',
46
- schema: z.string().optional(),
47
- description: 'Optional index prefix (for elasticsearch), `logstash` value is used if not set',
48
- aliases: ['i'],
49
- placeholder: 'index-prefix',
50
- }),
51
- rfc5424StructuredDataParameters: defineOption({
52
- name: 'sd-params',
53
- schema: z.string().optional(),
54
- description: 'RFC5424 structured data parameters (for ovh-tcp), e.g.: `X-OVH-TOKEN=\\\"REDACTED\\\"`',
55
- aliases: ['s'],
56
- placeholder: 'sd-params',
57
- }),
58
- alias: aliasOption,
59
- appIdOrName: appIdOrNameOption,
60
- addonIdOrRealId: addonIdOrRealIdOption,
61
- },
62
- args: [
63
- defineArgument({
64
- schema: z.enum(DRAIN_TYPE_CLI_CODES),
65
- description: 'Drain type',
66
- placeholder: 'drain-type',
67
- }),
68
- defineArgument({
69
- schema: z.string(),
70
- description: 'Drain URL',
71
- placeholder: 'drain-url',
72
- }),
73
- ],
74
- async handler(options, drainTypeCliCode, url) {
75
- const { alias, appIdOrName, addonIdOrRealId } = options;
76
- const { username, password, apiKey, sourceToken, indexPrefix, rfc5424StructuredDataParameters } = options;
77
-
78
- const drainType = Object.values(DRAIN_TYPES).find((drainType) => drainType.cliCode === drainTypeCliCode);
79
-
80
- const { ownerId, resourceId } = await resolveDrainResource(alias, appIdOrName, addonIdOrRealId);
81
-
82
- const body = {
83
- kind: 'LOG',
84
- recipient: {
85
- type: drainType.apiCode,
86
- url,
87
- },
88
- };
89
-
90
- if (drainTypeCliCode === DRAIN_TYPES.ELASTICSEARCH.cliCode) {
91
- if (!indexPrefix) {
92
- throw new Error(
93
- `${DRAIN_TYPES.ELASTICSEARCH.cliCode} drains require an index prefix (--index-prefix) to be set`,
94
- );
95
- }
96
- if (!url.endsWith('/_bulk')) {
97
- throw new Error(`${DRAIN_TYPES.ELASTICSEARCH.cliCode} drain URL must end with '/_bulk'`);
98
- }
99
- body.recipient.index = indexPrefix;
100
- }
101
-
102
- if (drainTypeCliCode === DRAIN_TYPES.ELASTICSEARCH.cliCode || drainTypeCliCode === DRAIN_TYPES.RAW_HTTP.cliCode) {
103
- if (username) {
104
- body.recipient.username = username;
105
- }
106
- if (password) {
107
- body.recipient.password = password;
108
- }
109
- }
110
-
111
- if (drainTypeCliCode === DRAIN_TYPES.NEWRELIC.cliCode) {
112
- if (!apiKey) {
113
- throw new Error(`${DRAIN_TYPES.NEWRELIC.cliCode} drains require an API key (--api-key) to be set`);
114
- }
115
- body.recipient.apiKey = apiKey;
116
- }
117
-
118
- if (drainTypeCliCode === DRAIN_TYPES.BETTERSTACK.cliCode) {
119
- if (!sourceToken) {
120
- throw new Error(`${DRAIN_TYPES.BETTERSTACK.cliCode} drains require a source token (--source-token) to be set`);
121
- }
122
- body.recipient.sourceToken = sourceToken;
123
- }
124
-
125
- if (
126
- drainTypeCliCode === DRAIN_TYPES.OVH_TCP.cliCode ||
127
- drainTypeCliCode === DRAIN_TYPES.SYSLOG_TCP.cliCode ||
128
- drainTypeCliCode === DRAIN_TYPES.SYSLOG_UDP.cliCode
129
- ) {
130
- if (rfc5424StructuredDataParameters) {
131
- body.recipient.rfc5424StructuredDataParameters = rfc5424StructuredDataParameters;
132
- }
133
- }
134
-
135
- const drain = await createDrain({ ownerId, resourceId, body }).then(sendToApi);
136
- Logger.printSuccess(`Drain ${styleText(['bold', 'green'], drain.id)} has been successfully created and enabled!`);
137
- },
138
6
  });
@@ -0,0 +1,27 @@
1
+ import { defineCommand } from '../../lib/define-command.js';
2
+ import { styleText } from '../../lib/style-text.js';
3
+ import { Logger } from '../../logger.js';
4
+ import { createLogDrain, resolveDrainResource } from '../../models/drain.js';
5
+ import { addonIdOrRealIdOption, aliasOption, appIdOrNameOption } from '../global.options.js';
6
+ import { drainUrlArg } from './drain.args.js';
7
+
8
+ export const drainCreateDatadogCommand = defineCommand({
9
+ description: 'Create a Datadog drain',
10
+ since: '0.9.0',
11
+ options: {
12
+ alias: aliasOption,
13
+ appIdOrName: appIdOrNameOption,
14
+ addonIdOrRealId: addonIdOrRealIdOption,
15
+ },
16
+ args: [drainUrlArg],
17
+ async handler(options, url) {
18
+ const { alias, appIdOrName, addonIdOrRealId } = options;
19
+ const { ownerId, resourceId } = await resolveDrainResource(alias, appIdOrName, addonIdOrRealId);
20
+
21
+ const drain = await createLogDrain('DATADOG', ownerId, resourceId, url);
22
+
23
+ Logger.printSuccess(
24
+ `Datadog drain ${styleText(['bold', 'green'], drain.id)} has been successfully created and enabled!`,
25
+ );
26
+ },
27
+ });
@@ -0,0 +1,48 @@
1
+ import { z } from 'zod';
2
+ import { defineArgument } from '../../lib/define-argument.js';
3
+ import { defineCommand } from '../../lib/define-command.js';
4
+ import { defineOption } from '../../lib/define-option.js';
5
+ import { styleText } from '../../lib/style-text.js';
6
+ import { Logger } from '../../logger.js';
7
+ import { createLogDrain, resolveDrainResource } from '../../models/drain.js';
8
+ import { addonIdOrRealIdOption, aliasOption, appIdOrNameOption } from '../global.options.js';
9
+ import { drainPasswordOption, drainUsernameOption } from './drain.options.js';
10
+
11
+ export const drainCreateElasticsearchCommand = defineCommand({
12
+ description: 'Create an Elasticsearch drain',
13
+ since: '0.9.0',
14
+ options: {
15
+ index: defineOption({
16
+ name: 'index-prefix',
17
+ schema: z.string().min(1),
18
+ description: 'Index prefix, indexes are created as `<index-prefix>-YYYY-MM-DD`',
19
+ aliases: ['i'],
20
+ placeholder: 'index-prefix',
21
+ }),
22
+ username: drainUsernameOption,
23
+ password: drainPasswordOption,
24
+ alias: aliasOption,
25
+ appIdOrName: appIdOrNameOption,
26
+ addonIdOrRealId: addonIdOrRealIdOption,
27
+ },
28
+ args: [
29
+ defineArgument({
30
+ // Elasticsearch drains target the bulk API
31
+ schema: z.string().refine((url) => url.endsWith('/_bulk'), {
32
+ message: "elasticsearch drain URL must end with '/_bulk'",
33
+ }),
34
+ description: "Drain URL, must end with '/_bulk'",
35
+ placeholder: 'drain-url',
36
+ }),
37
+ ],
38
+ async handler(options, url) {
39
+ const { alias, appIdOrName, addonIdOrRealId, index, username, password } = options;
40
+ const { ownerId, resourceId } = await resolveDrainResource(alias, appIdOrName, addonIdOrRealId);
41
+
42
+ const drain = await createLogDrain('ELASTICSEARCH', ownerId, resourceId, url, { index, username, password });
43
+
44
+ Logger.printSuccess(
45
+ `Elasticsearch drain ${styleText(['bold', 'green'], drain.id)} has been successfully created and enabled!`,
46
+ );
47
+ },
48
+ });
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ import { defineCommand } from '../../lib/define-command.js';
3
+ import { defineOption } from '../../lib/define-option.js';
4
+ import { styleText } from '../../lib/style-text.js';
5
+ import { Logger } from '../../logger.js';
6
+ import { createLogDrain, resolveDrainResource } from '../../models/drain.js';
7
+ import { addonIdOrRealIdOption, aliasOption, appIdOrNameOption } from '../global.options.js';
8
+ import { drainUrlArg } from './drain.args.js';
9
+
10
+ export const drainCreateNewRelicCommand = defineCommand({
11
+ description: 'Create a New Relic drain',
12
+ since: '0.9.0',
13
+ options: {
14
+ apiKey: defineOption({
15
+ name: 'api-key',
16
+ schema: z.string().min(1),
17
+ description: 'API key',
18
+ aliases: ['k'],
19
+ placeholder: 'api-key',
20
+ }),
21
+ alias: aliasOption,
22
+ appIdOrName: appIdOrNameOption,
23
+ addonIdOrRealId: addonIdOrRealIdOption,
24
+ },
25
+ args: [drainUrlArg],
26
+ async handler(options, url) {
27
+ const { alias, appIdOrName, addonIdOrRealId, apiKey } = options;
28
+ const { ownerId, resourceId } = await resolveDrainResource(alias, appIdOrName, addonIdOrRealId);
29
+
30
+ const drain = await createLogDrain('NEWRELIC', ownerId, resourceId, url, { apiKey });
31
+
32
+ Logger.printSuccess(
33
+ `New Relic drain ${styleText(['bold', 'green'], drain.id)} has been successfully created and enabled!`,
34
+ );
35
+ },
36
+ });