clever-tools 4.7.1 → 4.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.
- package/bin/clever.js +11 -1
- package/package.json +2 -2
- package/src/clever-client/drains.js +18 -18
- package/src/clever-client/k8s.js +197 -0
- package/src/commands/README.md +1 -0
- package/src/commands/accesslogs/accesslogs.command.js +2 -2
- package/src/commands/activity/activity.command.js +1 -1
- package/src/commands/addon/addon.create.command.js +2 -3
- package/src/commands/addon/addon.delete.command.js +2 -2
- package/src/commands/addon/addon.docs.md +2 -2
- package/src/commands/addon/addon.env.command.js +4 -6
- package/src/commands/config-provider/config-provider.command.js +0 -4
- package/src/commands/config-provider/config-provider.import.command.js +1 -1
- package/src/commands/config-provider/config-provider.list.command.js +8 -19
- package/src/commands/config-provider/config-provider.open.command.js +1 -1
- package/src/commands/console/console.command.js +3 -2
- package/src/commands/curl/curl.command.js +0 -1
- package/src/commands/database/database.backups.command.js +4 -7
- package/src/commands/database/database.backups.download.command.js +5 -7
- package/src/commands/database/database.command.js +0 -4
- package/src/commands/database/database.docs.md +2 -2
- package/src/commands/deploy/deploy.command.js +1 -1
- package/src/commands/deploy/deploy.docs.md +1 -1
- package/src/commands/drain/drain.command.js +14 -9
- package/src/commands/drain/drain.create.command.js +9 -13
- package/src/commands/drain/drain.disable.command.js +7 -7
- package/src/commands/drain/drain.docs.md +6 -0
- package/src/commands/drain/drain.enable.command.js +7 -7
- package/src/commands/drain/drain.get.command.js +12 -8
- package/src/commands/drain/drain.remove.command.js +7 -7
- package/src/commands/emails/emails.open.command.js +1 -4
- package/src/commands/global.commands.js +52 -0
- package/src/commands/global.options.js +7 -7
- package/src/commands/help/help.command.js +0 -3
- package/src/commands/k8s/k8s.activity.command.js +53 -0
- package/src/commands/k8s/k8s.args.js +6 -0
- package/src/commands/k8s/k8s.command.js +0 -4
- package/src/commands/k8s/k8s.create.command.js +87 -3
- package/src/commands/k8s/k8s.delete.command.js +15 -20
- package/src/commands/k8s/k8s.docs.md +264 -1
- package/src/commands/k8s/k8s.get.command.js +65 -4
- package/src/commands/k8s/k8s.nodegroups.command.js +7 -0
- package/src/commands/k8s/k8s.nodegroups.create.command.js +79 -0
- package/src/commands/k8s/k8s.nodegroups.delete.command.js +34 -0
- package/src/commands/k8s/k8s.nodegroups.get.command.js +66 -0
- package/src/commands/k8s/k8s.nodegroups.list.command.js +49 -0
- package/src/commands/k8s/k8s.nodegroups.update.command.js +86 -0
- package/src/commands/k8s/k8s.quota.command.js +77 -0
- package/src/commands/k8s/k8s.update.command.js +72 -0
- package/src/commands/k8s/k8s.version.check.command.js +18 -0
- package/src/commands/k8s/k8s.version.command.js +18 -0
- package/src/commands/k8s/k8s.version.update.command.js +18 -0
- package/src/commands/logs/logs.command.js +4 -5
- package/src/commands/ng/ng.docs.md +0 -1
- package/src/commands/ng/ng.get-config.command.js +3 -14
- package/src/commands/ng/ng.options.js +1 -1
- package/src/commands/notify-email/notify-email.remove.command.js +2 -2
- package/src/commands/oauth-consumers/oauth-consumers.args.js +8 -0
- package/src/commands/oauth-consumers/oauth-consumers.command.js +6 -0
- package/src/commands/oauth-consumers/oauth-consumers.create.command.js +64 -0
- package/src/commands/oauth-consumers/oauth-consumers.delete.command.js +33 -0
- package/src/commands/oauth-consumers/oauth-consumers.docs.md +130 -0
- package/src/commands/oauth-consumers/oauth-consumers.get.command.js +67 -0
- package/src/commands/oauth-consumers/oauth-consumers.list.command.js +34 -0
- package/src/commands/oauth-consumers/oauth-consumers.open.command.js +18 -0
- package/src/commands/oauth-consumers/oauth-consumers.options.js +48 -0
- package/src/commands/oauth-consumers/oauth-consumers.update.command.js +83 -0
- package/src/commands/open/open.command.js +1 -1
- package/src/commands/otoroshi/otoroshi.docs.md +14 -0
- package/src/commands/otoroshi/otoroshi.open.swaggerui.command.js +13 -0
- package/src/commands/profile/profile.open.command.js +1 -1
- package/src/commands/restart/restart.docs.md +1 -1
- package/src/commands/ssh-keys/ssh-keys.open.command.js +1 -1
- package/src/commands/webhooks/webhooks.remove.command.js +2 -2
- package/src/lib/cliparse-patched.js +2 -0
- package/src/lib/get-command-info.js +3 -1
- package/src/lib/get-command-info.types.d.ts +4 -0
- package/src/lib/k8s.js +411 -11
- package/src/lib/operator-commands.js +24 -24
- package/src/lib/print-items-by-owner.js +38 -0
- package/src/lib/prompts.js +29 -3
- package/src/lib/zod-utils.js +51 -0
- package/src/models/drain.js +17 -0
- package/src/models/ids-resolver.js +38 -20
- package/src/models/ng.js +2 -2
- package/src/models/notification.js +0 -4
- package/src/models/oauth-consumer.js +77 -0
- package/src/models/utils.js +1 -1
- package/src/parsers.js +10 -0
|
@@ -3,10 +3,11 @@ import { typewriterLogo } from '../../lib/ascii.js';
|
|
|
3
3
|
import { defineArgument } from '../../lib/define-argument.js';
|
|
4
4
|
import { defineCommand } from '../../lib/define-command.js';
|
|
5
5
|
import { defineOption } from '../../lib/define-option.js';
|
|
6
|
-
import { getK8sCluster, k8sCreate } from '../../lib/k8s.js';
|
|
6
|
+
import { getK8sCluster, k8sCreate, k8sGetProduct } from '../../lib/k8s.js';
|
|
7
7
|
import { styleText } from '../../lib/style-text.js';
|
|
8
8
|
import { Logger } from '../../logger.js';
|
|
9
|
-
import {
|
|
9
|
+
import { flavorCount, tags } from '../../parsers.js';
|
|
10
|
+
import { orgaIdOrNameOption, skipConfirmationOption } from '../global.options.js';
|
|
10
11
|
|
|
11
12
|
const DEPLOY_POLL_DELAY_MS = 10000;
|
|
12
13
|
|
|
@@ -14,6 +15,78 @@ export const k8sCreateCommand = defineCommand({
|
|
|
14
15
|
description: 'Create a Kubernetes cluster',
|
|
15
16
|
since: '4.3.0',
|
|
16
17
|
options: {
|
|
18
|
+
clusterVersion: defineOption({
|
|
19
|
+
name: 'cluster-version',
|
|
20
|
+
schema: z.string().optional(),
|
|
21
|
+
description: 'Kubernetes version to deploy (e.g.: 1.36)',
|
|
22
|
+
placeholder: 'cluster-version',
|
|
23
|
+
complete: async () => {
|
|
24
|
+
const product = await k8sGetProduct();
|
|
25
|
+
return product.versions?.available ?? [];
|
|
26
|
+
},
|
|
27
|
+
}),
|
|
28
|
+
description: defineOption({
|
|
29
|
+
name: 'description',
|
|
30
|
+
schema: z.string().max(4096).optional(),
|
|
31
|
+
description: 'Free-form cluster description',
|
|
32
|
+
placeholder: 'description',
|
|
33
|
+
}),
|
|
34
|
+
tag: defineOption({
|
|
35
|
+
name: 'tag',
|
|
36
|
+
schema: z.string().transform(tags).optional(),
|
|
37
|
+
description: 'Semantic tags (comma-separated, e.g.: env:prod,team:platform)',
|
|
38
|
+
placeholder: 'tag[,tag...]',
|
|
39
|
+
}),
|
|
40
|
+
autoscaling: defineOption({
|
|
41
|
+
name: 'autoscaling',
|
|
42
|
+
schema: z.boolean().default(false),
|
|
43
|
+
description: 'Enable the cluster autoscaler',
|
|
44
|
+
}),
|
|
45
|
+
persistentStorage: defineOption({
|
|
46
|
+
name: 'persistent-storage',
|
|
47
|
+
schema: z.boolean().default(false),
|
|
48
|
+
description: 'Enable persistent storage (Ceph CSI)',
|
|
49
|
+
}),
|
|
50
|
+
topology: defineOption({
|
|
51
|
+
name: 'topology',
|
|
52
|
+
schema: z
|
|
53
|
+
.string()
|
|
54
|
+
.transform((v) => v.toUpperCase())
|
|
55
|
+
.optional(),
|
|
56
|
+
description: 'Cluster topology (must be set with --flavor and --replication-factor)',
|
|
57
|
+
placeholder: 'topology',
|
|
58
|
+
complete: async () => {
|
|
59
|
+
const product = await k8sGetProduct();
|
|
60
|
+
return (product.topologies ?? []).map((t) => t.topology);
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
flavor: defineOption({
|
|
64
|
+
name: 'flavor',
|
|
65
|
+
schema: z
|
|
66
|
+
.string()
|
|
67
|
+
.transform((v) => v.toUpperCase())
|
|
68
|
+
.optional(),
|
|
69
|
+
description: 'Control plane flavor',
|
|
70
|
+
placeholder: 'flavor',
|
|
71
|
+
complete: async () => {
|
|
72
|
+
const product = await k8sGetProduct();
|
|
73
|
+
const flavors = new Set((product.topologies ?? []).flatMap((t) => t.availableFlavors ?? []));
|
|
74
|
+
return [...flavors];
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
replicationFactor: defineOption({
|
|
78
|
+
name: 'replication-factor',
|
|
79
|
+
schema: z.coerce.number().int().optional(),
|
|
80
|
+
description: 'Control plane replication factor',
|
|
81
|
+
placeholder: 'replication-factor',
|
|
82
|
+
}),
|
|
83
|
+
nodeGroup: defineOption({
|
|
84
|
+
name: 'nodegroup',
|
|
85
|
+
schema: z.string().transform(flavorCount).optional(),
|
|
86
|
+
description: 'Initial node group (format: <flavor>:<count>, e.g.: XS:3)',
|
|
87
|
+
placeholder: 'flavor:count',
|
|
88
|
+
}),
|
|
89
|
+
yes: skipConfirmationOption,
|
|
17
90
|
watch: defineOption({
|
|
18
91
|
name: 'watch',
|
|
19
92
|
schema: z.boolean().default(false),
|
|
@@ -34,7 +107,18 @@ export const k8sCreateCommand = defineCommand({
|
|
|
34
107
|
const orgIdOrName = options.org;
|
|
35
108
|
|
|
36
109
|
try {
|
|
37
|
-
const cluster = await k8sCreate(clusterName, orgIdOrName
|
|
110
|
+
const cluster = await k8sCreate(clusterName, orgIdOrName, {
|
|
111
|
+
version: options.clusterVersion,
|
|
112
|
+
description: options.description,
|
|
113
|
+
tags: options.tag,
|
|
114
|
+
autoscaling: options.autoscaling,
|
|
115
|
+
persistentStorage: options.persistentStorage,
|
|
116
|
+
topology: options.topology,
|
|
117
|
+
flavor: options.flavor,
|
|
118
|
+
replicationFactor: options.replicationFactor,
|
|
119
|
+
nodeGroup: options.nodeGroup,
|
|
120
|
+
yes: options.yes,
|
|
121
|
+
});
|
|
38
122
|
|
|
39
123
|
if (options.watch) {
|
|
40
124
|
await typewriterLogo();
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineCommand } from '../../lib/define-command.js';
|
|
2
2
|
import { k8sDelete } from '../../lib/k8s.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ask } from '../../lib/prompts.js';
|
|
4
4
|
import { styleText } from '../../lib/style-text.js';
|
|
5
5
|
import { Logger } from '../../logger.js';
|
|
6
|
-
import {
|
|
6
|
+
import { orgaIdOrNameOption, skipConfirmationOption } from '../global.options.js';
|
|
7
7
|
import { k8sIdOrNameArg } from './k8s.args.js';
|
|
8
8
|
|
|
9
9
|
export const k8sDeleteCommand = defineCommand({
|
|
@@ -11,30 +11,25 @@ export const k8sDeleteCommand = defineCommand({
|
|
|
11
11
|
since: '4.3.0',
|
|
12
12
|
options: {
|
|
13
13
|
org: orgaIdOrNameOption,
|
|
14
|
-
yes:
|
|
14
|
+
yes: skipConfirmationOption,
|
|
15
15
|
},
|
|
16
16
|
args: [k8sIdOrNameArg],
|
|
17
17
|
async handler(options, clusterIdOrName) {
|
|
18
|
-
const { org: orgIdOrName, yes
|
|
18
|
+
const { org: orgIdOrName, yes } = options;
|
|
19
|
+
const display = clusterIdOrName.addon_name || clusterIdOrName.operator_id;
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
proceedDeletion = await confirm(
|
|
25
|
-
`Are you sure you want to delete the Kubernetes cluster ${styleText(
|
|
26
|
-
'blue',
|
|
27
|
-
clusterIdOrName.addon_name || clusterIdOrName.operator_id,
|
|
28
|
-
)}?`,
|
|
29
|
-
'Kubernetes cluster deletion cancelled.',
|
|
21
|
+
if (!yes) {
|
|
22
|
+
const proceed = await ask(
|
|
23
|
+
`Are you sure you want to delete the Kubernetes cluster ${styleText('blue', display)}?`,
|
|
24
|
+
false,
|
|
30
25
|
);
|
|
26
|
+
if (!proceed) {
|
|
27
|
+
Logger.println('Kubernetes cluster deletion cancelled');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Logger.printSuccess(
|
|
36
|
-
`Kubernetes cluster ${styleText('green', clusterIdOrName.addon_name || clusterIdOrName.operator_id)} successfully deleted`,
|
|
37
|
-
);
|
|
38
|
-
}
|
|
32
|
+
await k8sDelete(orgIdOrName, clusterIdOrName);
|
|
33
|
+
Logger.printSuccess(`Kubernetes cluster ${styleText('green', display)} successfully deleted`);
|
|
39
34
|
},
|
|
40
35
|
});
|
|
@@ -12,6 +12,28 @@ Manage Kubernetes clusters
|
|
|
12
12
|
clever k8s
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## ➡️ `clever k8s activity` <kbd>Since 4.9.0</kbd>
|
|
16
|
+
|
|
17
|
+
Show recent deployment events of a Kubernetes cluster
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
clever k8s activity <cluster-id|cluster-name> [options]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 📥 Arguments
|
|
24
|
+
|
|
25
|
+
|Name|Description|
|
|
26
|
+
|---|---|
|
|
27
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
28
|
+
|
|
29
|
+
### ⚙️ Options
|
|
30
|
+
|
|
31
|
+
|Name|Description|
|
|
32
|
+
|---|---|
|
|
33
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
34
|
+
|`--limit` `<limit>`|Number of events to fetch (1 to 1000) (default: 50)|
|
|
35
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
36
|
+
|
|
15
37
|
## ➡️ `clever k8s add-persistent-storage` <kbd>Since 4.3.0</kbd>
|
|
16
38
|
|
|
17
39
|
Activate persistent storage to a deployed Kubernetes cluster
|
|
@@ -50,8 +72,18 @@ clever k8s create <cluster-name> [options]
|
|
|
50
72
|
|
|
51
73
|
|Name|Description|
|
|
52
74
|
|---|---|
|
|
75
|
+
|`--autoscaling`|Enable the cluster autoscaler|
|
|
76
|
+
|`--cluster-version` `<cluster-version>`|Kubernetes version to deploy (e.g.: 1.36)|
|
|
77
|
+
|`--description` `<description>`|Free-form cluster description|
|
|
78
|
+
|`--flavor` `<flavor>`|Control plane flavor|
|
|
79
|
+
|`--nodegroup` `<flavor:count>`|Initial node group (format: <flavor>:<count>, e.g.: XS:3)|
|
|
53
80
|
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
81
|
+
|`--persistent-storage`|Enable persistent storage (Ceph CSI)|
|
|
82
|
+
|`--replication-factor` `<replication-factor>`|Control plane replication factor|
|
|
83
|
+
|`--tag` `<tag[,tag...]>`|Semantic tags (comma-separated, e.g.: env:prod,team:platform)|
|
|
84
|
+
|`--topology` `<topology>`|Cluster topology (must be set with --flavor and --replication-factor)|
|
|
54
85
|
|`-w`, `--watch`|Watch the deployment until the cluster is deployed|
|
|
86
|
+
|`-y`, `--yes`|Skip confirmation prompts|
|
|
55
87
|
|
|
56
88
|
## ➡️ `clever k8s delete` <kbd>Since 4.3.0</kbd>
|
|
57
89
|
|
|
@@ -72,7 +104,7 @@ clever k8s delete <cluster-id|cluster-name> [options]
|
|
|
72
104
|
|Name|Description|
|
|
73
105
|
|---|---|
|
|
74
106
|
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
75
|
-
|`-y`, `--yes`|Skip confirmation
|
|
107
|
+
|`-y`, `--yes`|Skip confirmation prompts|
|
|
76
108
|
|
|
77
109
|
## ➡️ `clever k8s get` <kbd>Since 4.3.0</kbd>
|
|
78
110
|
|
|
@@ -129,3 +161,234 @@ clever k8s list [options]
|
|
|
129
161
|
|---|---|
|
|
130
162
|
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
131
163
|
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
164
|
+
|
|
165
|
+
## ➡️ `clever k8s nodegroups` <kbd>Since 4.9.0</kbd>
|
|
166
|
+
|
|
167
|
+
Manage Kubernetes node groups
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
clever k8s nodegroups
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## ➡️ `clever k8s nodegroups create` <kbd>Since 4.9.0</kbd>
|
|
174
|
+
|
|
175
|
+
Create a node group on a Kubernetes cluster
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
clever k8s nodegroups create <cluster-id|cluster-name> <nodegroup-name> <flavor:count> [options]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 📥 Arguments
|
|
182
|
+
|
|
183
|
+
|Name|Description|
|
|
184
|
+
|---|---|
|
|
185
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
186
|
+
|`nodegroup-name`|Node group name (lowercase RFC 1123, max 63 chars)|
|
|
187
|
+
|`flavor:count`|Node group flavor and target node count (format: <flavor>:<count>, e.g.: XS:3)|
|
|
188
|
+
|
|
189
|
+
### ⚙️ Options
|
|
190
|
+
|
|
191
|
+
|Name|Description|
|
|
192
|
+
|---|---|
|
|
193
|
+
|`--autoscaling`|Enable cluster autoscaler for this node group (requires --min and --max)|
|
|
194
|
+
|`--description` `<description>`|Free-form node group description|
|
|
195
|
+
|`--max` `<max>`|Maximum node count when autoscaling is enabled|
|
|
196
|
+
|`--min` `<min>`|Minimum node count when autoscaling is enabled|
|
|
197
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
198
|
+
|`--tag` `<tag>`|Arbitrary tag attached to the node group|
|
|
199
|
+
|
|
200
|
+
## ➡️ `clever k8s nodegroups delete` <kbd>Since 4.9.0</kbd>
|
|
201
|
+
|
|
202
|
+
Delete a node group from a Kubernetes cluster
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
clever k8s nodegroups delete <cluster-id|cluster-name> <nodegroup-id|nodegroup-name> [options]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 📥 Arguments
|
|
209
|
+
|
|
210
|
+
|Name|Description|
|
|
211
|
+
|---|---|
|
|
212
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
213
|
+
|`nodegroup-id|nodegroup-name`|Kubernetes node group ID or name|
|
|
214
|
+
|
|
215
|
+
### ⚙️ Options
|
|
216
|
+
|
|
217
|
+
|Name|Description|
|
|
218
|
+
|---|---|
|
|
219
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
220
|
+
|`-y`, `--yes`|Skip confirmation prompts|
|
|
221
|
+
|
|
222
|
+
## ➡️ `clever k8s nodegroups get` <kbd>Since 4.9.0</kbd>
|
|
223
|
+
|
|
224
|
+
Get information about a Kubernetes node group
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
clever k8s nodegroups get <cluster-id|cluster-name> <nodegroup-id|nodegroup-name> [options]
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 📥 Arguments
|
|
231
|
+
|
|
232
|
+
|Name|Description|
|
|
233
|
+
|---|---|
|
|
234
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
235
|
+
|`nodegroup-id|nodegroup-name`|Kubernetes node group ID or name|
|
|
236
|
+
|
|
237
|
+
### ⚙️ Options
|
|
238
|
+
|
|
239
|
+
|Name|Description|
|
|
240
|
+
|---|---|
|
|
241
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
242
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
243
|
+
|
|
244
|
+
## ➡️ `clever k8s nodegroups list` <kbd>Since 4.9.0</kbd>
|
|
245
|
+
|
|
246
|
+
List the node groups of a Kubernetes cluster
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
clever k8s nodegroups list <cluster-id|cluster-name> [options]
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### 📥 Arguments
|
|
253
|
+
|
|
254
|
+
|Name|Description|
|
|
255
|
+
|---|---|
|
|
256
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
257
|
+
|
|
258
|
+
### ⚙️ Options
|
|
259
|
+
|
|
260
|
+
|Name|Description|
|
|
261
|
+
|---|---|
|
|
262
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
263
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
264
|
+
|
|
265
|
+
## ➡️ `clever k8s nodegroups update` <kbd>Since 4.9.0</kbd>
|
|
266
|
+
|
|
267
|
+
Update a node group on a Kubernetes cluster
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
clever k8s nodegroups update <cluster-id|cluster-name> <nodegroup-id|nodegroup-name> [options]
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 📥 Arguments
|
|
274
|
+
|
|
275
|
+
|Name|Description|
|
|
276
|
+
|---|---|
|
|
277
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
278
|
+
|`nodegroup-id|nodegroup-name`|Kubernetes node group ID or name|
|
|
279
|
+
|
|
280
|
+
### ⚙️ Options
|
|
281
|
+
|
|
282
|
+
|Name|Description|
|
|
283
|
+
|---|---|
|
|
284
|
+
|`--autoscaling`|Enable the cluster autoscaler|
|
|
285
|
+
|`--count` `<count>`|Target node count|
|
|
286
|
+
|`--description` `<description>`|Free-form node group description|
|
|
287
|
+
|`--disable-autoscaling`|Disable the cluster autoscaler|
|
|
288
|
+
|`--max` `<max>`|Maximum node count (autoscaling bound)|
|
|
289
|
+
|`--min` `<min>`|Minimum node count (autoscaling bound)|
|
|
290
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
291
|
+
|`--tag` `<tag>`|Arbitrary tag attached to the node group|
|
|
292
|
+
|
|
293
|
+
## ➡️ `clever k8s quota` <kbd>Since 4.9.0</kbd>
|
|
294
|
+
|
|
295
|
+
Get the Kubernetes quota, usage and remaining of an organisation
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
clever k8s quota [options]
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### ⚙️ Options
|
|
302
|
+
|
|
303
|
+
|Name|Description|
|
|
304
|
+
|---|---|
|
|
305
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
306
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
307
|
+
|
|
308
|
+
## ➡️ `clever k8s update` <kbd>Since 4.9.0</kbd>
|
|
309
|
+
|
|
310
|
+
Update a Kubernetes cluster metadata or features
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
clever k8s update <cluster-id|cluster-name> [options]
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 📥 Arguments
|
|
317
|
+
|
|
318
|
+
|Name|Description|
|
|
319
|
+
|---|---|
|
|
320
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
321
|
+
|
|
322
|
+
### ⚙️ Options
|
|
323
|
+
|
|
324
|
+
|Name|Description|
|
|
325
|
+
|---|---|
|
|
326
|
+
|`--autoscaling`|Enable the cluster autoscaler|
|
|
327
|
+
|`--description` `<description>`|Free-form cluster description|
|
|
328
|
+
|`--disable-autoscaling`|Disable the cluster autoscaler|
|
|
329
|
+
|`--name` `<name>`|Rename the cluster|
|
|
330
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
331
|
+
|`--tag` `<tag[,tag...]>`|Replace tags (comma-separated, e.g.: env:prod,team:platform)|
|
|
332
|
+
|
|
333
|
+
## ➡️ `clever k8s version` <kbd>Since 4.9.0</kbd>
|
|
334
|
+
|
|
335
|
+
Check a Kubernetes cluster deployed version
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
clever k8s version <cluster-id|cluster-name> [options]
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 📥 Arguments
|
|
342
|
+
|
|
343
|
+
|Name|Description|
|
|
344
|
+
|---|---|
|
|
345
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
346
|
+
|
|
347
|
+
### ⚙️ Options
|
|
348
|
+
|
|
349
|
+
|Name|Description|
|
|
350
|
+
|---|---|
|
|
351
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
352
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
353
|
+
|
|
354
|
+
## ➡️ `clever k8s version check` <kbd>Since 4.9.0</kbd>
|
|
355
|
+
|
|
356
|
+
Check a Kubernetes cluster deployed version
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
clever k8s version check <cluster-id|cluster-name> [options]
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### 📥 Arguments
|
|
363
|
+
|
|
364
|
+
|Name|Description|
|
|
365
|
+
|---|---|
|
|
366
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
367
|
+
|
|
368
|
+
### ⚙️ Options
|
|
369
|
+
|
|
370
|
+
|Name|Description|
|
|
371
|
+
|---|---|
|
|
372
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
373
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
374
|
+
|
|
375
|
+
## ➡️ `clever k8s version update` <kbd>Since 4.9.0</kbd>
|
|
376
|
+
|
|
377
|
+
Update a Kubernetes cluster to a target version
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
clever k8s version update <cluster-id|cluster-name> [options]
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### 📥 Arguments
|
|
384
|
+
|
|
385
|
+
|Name|Description|
|
|
386
|
+
|---|---|
|
|
387
|
+
|`cluster-id|cluster-name`|Kubernetes cluster ID or name|
|
|
388
|
+
|
|
389
|
+
### ⚙️ Options
|
|
390
|
+
|
|
391
|
+
|Name|Description|
|
|
392
|
+
|---|---|
|
|
393
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
394
|
+
|`--target` `<version>`|Target version to upgrade to (e.g.: 24, 2.4, 2.4.1)|
|
|
@@ -23,13 +23,67 @@ export const k8sGetCommand = defineCommand({
|
|
|
23
23
|
Logger.printJson(k8sInfo);
|
|
24
24
|
break;
|
|
25
25
|
case 'human':
|
|
26
|
-
default:
|
|
27
|
-
|
|
26
|
+
default: {
|
|
27
|
+
const topo = k8sInfo.topologyConfig;
|
|
28
|
+
const overview = {
|
|
28
29
|
Name: k8sInfo.name,
|
|
29
30
|
ID: k8sInfo.id,
|
|
30
|
-
Version: k8sInfo.version,
|
|
31
31
|
Status: k8sInfo.status,
|
|
32
|
-
|
|
32
|
+
Version: k8sInfo.version,
|
|
33
|
+
Topology: formatTopology(topo),
|
|
34
|
+
Autoscaling: k8sInfo.features?.autoscalingEnabled ? 'enabled' : 'disabled',
|
|
35
|
+
'Persistent storage': k8sInfo.features?.csi != null ? 'enabled' : 'disabled',
|
|
36
|
+
};
|
|
37
|
+
if (k8sInfo.tags?.length) overview.Tags = k8sInfo.tags.join(', ');
|
|
38
|
+
if (k8sInfo.description) overview.Description = k8sInfo.description;
|
|
39
|
+
if (k8sInfo.storageUsageBytes != null) {
|
|
40
|
+
overview.Storage = `${Math.round((k8sInfo.storageUsageBytes / 1024 ** 3) * 100) / 100} GB`;
|
|
41
|
+
}
|
|
42
|
+
console.table(overview);
|
|
43
|
+
|
|
44
|
+
if (topo?.topology === 'DISTRIBUTED' && topo.components) {
|
|
45
|
+
Logger.println('');
|
|
46
|
+
Logger.println('🧩 Control plane components');
|
|
47
|
+
console.table(
|
|
48
|
+
Object.fromEntries(
|
|
49
|
+
Object.entries(topo.components).map(([name, c]) => [
|
|
50
|
+
name,
|
|
51
|
+
{ Flavor: c?.flavor ?? '-', RF: c?.replicationFactor ?? '-' },
|
|
52
|
+
]),
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (k8sInfo.nodeGroups?.length) {
|
|
58
|
+
Logger.println('');
|
|
59
|
+
Logger.println(`🧮 Node groups (${k8sInfo.nodeGroups.length})`);
|
|
60
|
+
console.table(
|
|
61
|
+
Object.fromEntries(
|
|
62
|
+
k8sInfo.nodeGroups.map((ng) => [
|
|
63
|
+
ng.id,
|
|
64
|
+
{
|
|
65
|
+
Name: ng.name,
|
|
66
|
+
Status: ng.status,
|
|
67
|
+
Flavor: ng.flavor,
|
|
68
|
+
Nodes: `${ng.currentNodeCount}/${ng.targetNodeCount}`,
|
|
69
|
+
},
|
|
70
|
+
]),
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (k8sInfo.loadBalancers?.length) {
|
|
76
|
+
Logger.println('');
|
|
77
|
+
Logger.println(`🔀 Load balancers (${k8sInfo.loadBalancers.length})`);
|
|
78
|
+
k8sInfo.loadBalancers.forEach((lb) => {
|
|
79
|
+
console.table({
|
|
80
|
+
ID: lb.id,
|
|
81
|
+
Flavor: lb.flavor,
|
|
82
|
+
IPs: lb.ips?.join(', ') ?? '-',
|
|
83
|
+
Domain: lb.domainName ?? '-',
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
33
87
|
|
|
34
88
|
Logger.println('');
|
|
35
89
|
const orgMessageComplement = orgIdOrName ? `--org "${orgIdOrName.orga_id || orgIdOrName.orga_name}"` : '';
|
|
@@ -38,6 +92,13 @@ export const k8sGetCommand = defineCommand({
|
|
|
38
92
|
`Once ACTIVE, get the kubeconfig with ${styleText('blue', `clever k8s get-kubeconfig ${k8sInfo.id} ${orgMessageComplement}`)}`,
|
|
39
93
|
);
|
|
40
94
|
break;
|
|
95
|
+
}
|
|
41
96
|
}
|
|
42
97
|
},
|
|
43
98
|
});
|
|
99
|
+
|
|
100
|
+
function formatTopology(topo) {
|
|
101
|
+
if (topo == null) return '-';
|
|
102
|
+
if (topo.topology === 'DISTRIBUTED') return 'DISTRIBUTED';
|
|
103
|
+
return `${topo.topology} (${topo.flavor}, rf=${topo.replicationFactor})`;
|
|
104
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
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 { k8sCreateNodeGroup } from '../../lib/k8s.js';
|
|
6
|
+
import { styleText } from '../../lib/style-text.js';
|
|
7
|
+
import { Logger } from '../../logger.js';
|
|
8
|
+
import { flavorCount } from '../../parsers.js';
|
|
9
|
+
import { orgaIdOrNameOption } from '../global.options.js';
|
|
10
|
+
import { k8sIdOrNameArg } from './k8s.args.js';
|
|
11
|
+
|
|
12
|
+
export const k8sNodeGroupCreateCommand = defineCommand({
|
|
13
|
+
description: 'Create a node group on a Kubernetes cluster',
|
|
14
|
+
since: '4.9.0',
|
|
15
|
+
options: {
|
|
16
|
+
description: defineOption({
|
|
17
|
+
name: 'description',
|
|
18
|
+
schema: z.string().max(4096).optional(),
|
|
19
|
+
description: 'Free-form node group description',
|
|
20
|
+
placeholder: 'description',
|
|
21
|
+
}),
|
|
22
|
+
tag: defineOption({
|
|
23
|
+
name: 'tag',
|
|
24
|
+
schema: z.string().max(1024).optional(),
|
|
25
|
+
description: 'Arbitrary tag attached to the node group',
|
|
26
|
+
placeholder: 'tag',
|
|
27
|
+
}),
|
|
28
|
+
autoscaling: defineOption({
|
|
29
|
+
name: 'autoscaling',
|
|
30
|
+
schema: z.boolean().default(false),
|
|
31
|
+
description: 'Enable cluster autoscaler for this node group (requires --min and --max)',
|
|
32
|
+
}),
|
|
33
|
+
min: defineOption({
|
|
34
|
+
name: 'min',
|
|
35
|
+
schema: z.coerce.number().int().min(0).max(256).optional(),
|
|
36
|
+
description: 'Minimum node count when autoscaling is enabled',
|
|
37
|
+
placeholder: 'min',
|
|
38
|
+
}),
|
|
39
|
+
max: defineOption({
|
|
40
|
+
name: 'max',
|
|
41
|
+
schema: z.coerce.number().int().min(0).max(256).optional(),
|
|
42
|
+
description: 'Maximum node count when autoscaling is enabled',
|
|
43
|
+
placeholder: 'max',
|
|
44
|
+
}),
|
|
45
|
+
org: orgaIdOrNameOption,
|
|
46
|
+
},
|
|
47
|
+
args: [
|
|
48
|
+
k8sIdOrNameArg,
|
|
49
|
+
defineArgument({
|
|
50
|
+
schema: z
|
|
51
|
+
.string()
|
|
52
|
+
.regex(
|
|
53
|
+
/^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$/,
|
|
54
|
+
'Must be lowercase RFC 1123 (letters, digits, "-"), start/end alphanumeric, max 63 chars',
|
|
55
|
+
),
|
|
56
|
+
description: 'Node group name (lowercase RFC 1123, max 63 chars)',
|
|
57
|
+
placeholder: 'nodegroup-name',
|
|
58
|
+
}),
|
|
59
|
+
defineArgument({
|
|
60
|
+
schema: z.string().transform(flavorCount),
|
|
61
|
+
description: 'Node group flavor and target node count (format: <flavor>:<count>, e.g.: XS:3)',
|
|
62
|
+
placeholder: 'flavor:count',
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
async handler(options, clusterIdOrName, nodeGroupName, spec) {
|
|
66
|
+
const { org: orgIdOrName } = options;
|
|
67
|
+
const nodeGroup = await k8sCreateNodeGroup(orgIdOrName, clusterIdOrName, {
|
|
68
|
+
name: nodeGroupName,
|
|
69
|
+
flavor: spec.flavor,
|
|
70
|
+
targetNodeCount: spec.targetNodeCount,
|
|
71
|
+
description: options.description,
|
|
72
|
+
tag: options.tag,
|
|
73
|
+
autoscaling: options.autoscaling,
|
|
74
|
+
min: options.min,
|
|
75
|
+
max: options.max,
|
|
76
|
+
});
|
|
77
|
+
Logger.println(`🚀 Node group ${styleText('white', `${nodeGroup.name} (${nodeGroup.id})`)} is being deployed`);
|
|
78
|
+
},
|
|
79
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { k8sDeleteNodeGroup } from '../../lib/k8s.js';
|
|
3
|
+
import { ask } from '../../lib/prompts.js';
|
|
4
|
+
import { styleText } from '../../lib/style-text.js';
|
|
5
|
+
import { Logger } from '../../logger.js';
|
|
6
|
+
import { orgaIdOrNameOption, skipConfirmationOption } from '../global.options.js';
|
|
7
|
+
import { k8sIdOrNameArg, k8sNodeGroupIdOrNameArg } from './k8s.args.js';
|
|
8
|
+
|
|
9
|
+
export const k8sNodeGroupDeleteCommand = defineCommand({
|
|
10
|
+
description: 'Delete a node group from a Kubernetes cluster',
|
|
11
|
+
since: '4.9.0',
|
|
12
|
+
options: {
|
|
13
|
+
org: orgaIdOrNameOption,
|
|
14
|
+
yes: skipConfirmationOption,
|
|
15
|
+
},
|
|
16
|
+
args: [k8sIdOrNameArg, k8sNodeGroupIdOrNameArg],
|
|
17
|
+
async handler(options, clusterIdOrName, nodeGroupIdOrName) {
|
|
18
|
+
const { org: orgIdOrName, yes } = options;
|
|
19
|
+
|
|
20
|
+
if (!yes) {
|
|
21
|
+
const proceed = await ask(
|
|
22
|
+
`Are you sure you want to delete the node group ${styleText('blue', nodeGroupIdOrName)}?`,
|
|
23
|
+
false,
|
|
24
|
+
);
|
|
25
|
+
if (!proceed) {
|
|
26
|
+
Logger.println('Node group deletion cancelled');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await k8sDeleteNodeGroup(orgIdOrName, clusterIdOrName, nodeGroupIdOrName);
|
|
32
|
+
Logger.printSuccess(`Node group ${styleText('green', nodeGroupIdOrName)} successfully deleted`);
|
|
33
|
+
},
|
|
34
|
+
});
|