clever-tools 4.8.0 → 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/package.json +1 -1
- package/src/clever-client/k8s.js +197 -0
- package/src/commands/addon/addon.docs.md +1 -1
- package/src/commands/global.commands.js +34 -0
- package/src/commands/global.options.js +1 -1
- package/src/commands/k8s/k8s.activity.command.js +53 -0
- package/src/commands/k8s/k8s.args.js +6 -0
- package/src/commands/k8s/k8s.create.command.js +87 -3
- package/src/commands/k8s/k8s.delete.command.js +13 -18
- 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/oauth-consumers/oauth-consumers.docs.md +1 -1
- package/src/commands/otoroshi/otoroshi.docs.md +14 -0
- package/src/commands/otoroshi/otoroshi.open.swaggerui.command.js +13 -0
- package/src/lib/k8s.js +411 -11
- package/src/lib/operator-commands.js +15 -1
- package/src/lib/prompts.js +6 -2
- package/src/parsers.js +10 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
3
|
+
import { defineOption } from '../../lib/define-option.js';
|
|
4
|
+
import { k8sUpdateNodeGroup } from '../../lib/k8s.js';
|
|
5
|
+
import { styleText } from '../../lib/style-text.js';
|
|
6
|
+
import { Logger } from '../../logger.js';
|
|
7
|
+
import { orgaIdOrNameOption } from '../global.options.js';
|
|
8
|
+
import { k8sIdOrNameArg, k8sNodeGroupIdOrNameArg } from './k8s.args.js';
|
|
9
|
+
|
|
10
|
+
export const k8sNodeGroupUpdateCommand = defineCommand({
|
|
11
|
+
description: 'Update a node group on a Kubernetes cluster',
|
|
12
|
+
since: '4.9.0',
|
|
13
|
+
options: {
|
|
14
|
+
count: defineOption({
|
|
15
|
+
name: 'count',
|
|
16
|
+
schema: z.coerce.number().int().min(0).max(256).optional(),
|
|
17
|
+
description: 'Target node count',
|
|
18
|
+
placeholder: 'count',
|
|
19
|
+
}),
|
|
20
|
+
min: defineOption({
|
|
21
|
+
name: 'min',
|
|
22
|
+
schema: z.coerce.number().int().min(0).max(256).optional(),
|
|
23
|
+
description: 'Minimum node count (autoscaling bound)',
|
|
24
|
+
placeholder: 'min',
|
|
25
|
+
}),
|
|
26
|
+
max: defineOption({
|
|
27
|
+
name: 'max',
|
|
28
|
+
schema: z.coerce.number().int().min(0).max(256).optional(),
|
|
29
|
+
description: 'Maximum node count (autoscaling bound)',
|
|
30
|
+
placeholder: 'max',
|
|
31
|
+
}),
|
|
32
|
+
autoscaling: defineOption({
|
|
33
|
+
name: 'autoscaling',
|
|
34
|
+
schema: z.boolean().default(false),
|
|
35
|
+
description: 'Enable the cluster autoscaler',
|
|
36
|
+
}),
|
|
37
|
+
disableAutoscaling: defineOption({
|
|
38
|
+
name: 'disable-autoscaling',
|
|
39
|
+
schema: z.boolean().default(false),
|
|
40
|
+
description: 'Disable the cluster autoscaler',
|
|
41
|
+
}),
|
|
42
|
+
description: defineOption({
|
|
43
|
+
name: 'description',
|
|
44
|
+
schema: z.string().max(4096).optional(),
|
|
45
|
+
description: 'Free-form node group description',
|
|
46
|
+
placeholder: 'description',
|
|
47
|
+
}),
|
|
48
|
+
tag: defineOption({
|
|
49
|
+
name: 'tag',
|
|
50
|
+
schema: z.string().max(1024).optional(),
|
|
51
|
+
description: 'Arbitrary tag attached to the node group',
|
|
52
|
+
placeholder: 'tag',
|
|
53
|
+
}),
|
|
54
|
+
org: orgaIdOrNameOption,
|
|
55
|
+
},
|
|
56
|
+
args: [k8sIdOrNameArg, k8sNodeGroupIdOrNameArg],
|
|
57
|
+
async handler(options, clusterIdOrName, nodeGroupIdOrName) {
|
|
58
|
+
const { org: orgIdOrName } = options;
|
|
59
|
+
|
|
60
|
+
if (options.autoscaling && options.disableAutoscaling) {
|
|
61
|
+
throw new Error('--autoscaling and --disable-autoscaling are mutually exclusive');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const updates = {};
|
|
65
|
+
if (options.count != null) updates.targetNodeCount = options.count;
|
|
66
|
+
if (options.min != null) updates.minNodeCount = options.min;
|
|
67
|
+
if (options.max != null) updates.maxNodeCount = options.max;
|
|
68
|
+
if (options.autoscaling) updates.autoscalingEnabled = true;
|
|
69
|
+
if (options.disableAutoscaling) updates.autoscalingEnabled = false;
|
|
70
|
+
if (options.description != null) updates.description = options.description;
|
|
71
|
+
if (options.tag != null) updates.tag = options.tag;
|
|
72
|
+
|
|
73
|
+
if (Object.keys(updates).length === 0) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
'No update specified. Provide at least one of --count, --min, --max, --autoscaling, --disable-autoscaling, --description, --tag',
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (updates.minNodeCount != null && updates.maxNodeCount != null && updates.minNodeCount > updates.maxNodeCount) {
|
|
80
|
+
throw new Error('--min must be less than or equal to --max');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const nodeGroup = await k8sUpdateNodeGroup(orgIdOrName, clusterIdOrName, nodeGroupIdOrName, updates);
|
|
84
|
+
Logger.printSuccess(`Node group ${styleText('green', nodeGroup.name)} updated`);
|
|
85
|
+
},
|
|
86
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { k8sGetQuota, k8sListUsage } from '../../lib/k8s.js';
|
|
3
|
+
import { Logger } from '../../logger.js';
|
|
4
|
+
import { humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
|
|
5
|
+
|
|
6
|
+
const BYTES_PER_GB = 1024 ** 3;
|
|
7
|
+
const UNIT_TO_BYTES = {
|
|
8
|
+
B: 1,
|
|
9
|
+
KB: 1024,
|
|
10
|
+
MB: 1024 ** 2,
|
|
11
|
+
GB: BYTES_PER_GB,
|
|
12
|
+
TB: 1024 ** 4,
|
|
13
|
+
PB: 1024 ** 5,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const k8sQuotaCommand = defineCommand({
|
|
17
|
+
description: 'Get the Kubernetes quota, usage and remaining of an organisation',
|
|
18
|
+
since: '4.9.0',
|
|
19
|
+
options: {
|
|
20
|
+
org: orgaIdOrNameOption,
|
|
21
|
+
format: humanJsonOutputFormatOption,
|
|
22
|
+
},
|
|
23
|
+
args: [],
|
|
24
|
+
async handler(options) {
|
|
25
|
+
const { format, org: orgIdOrName } = options;
|
|
26
|
+
const [quota, usageItems] = await Promise.all([k8sGetQuota(orgIdOrName), k8sListUsage(orgIdOrName)]);
|
|
27
|
+
|
|
28
|
+
const cpuQuota = quota.quotas?.find((i) => i.type === 'MillivCPUMaxLimit');
|
|
29
|
+
const ramQuota = quota.quotas?.find((i) => i.type === 'RamMaxUsage');
|
|
30
|
+
const usedMillicores = usageItems.reduce((sum, i) => sum + i.cpuMillicores, 0);
|
|
31
|
+
const usedBytes = usageItems.reduce((sum, i) => sum + i.ramBytes, 0);
|
|
32
|
+
|
|
33
|
+
switch (format) {
|
|
34
|
+
case 'json': {
|
|
35
|
+
const { quotas, ...quotaRest } = quota;
|
|
36
|
+
Logger.printJson({
|
|
37
|
+
quota: {
|
|
38
|
+
...quotaRest,
|
|
39
|
+
vCPUMax: cpuQuota != null ? { maximum: cpuQuota.maximum, unit: 'mCPU' } : null,
|
|
40
|
+
ramMax: ramQuota != null ? { maximum: ramQuota.information.number, unit: ramQuota.information.unit } : null,
|
|
41
|
+
},
|
|
42
|
+
usage: { cpuMillicores: usedMillicores, ramBytes: usedBytes },
|
|
43
|
+
remaining: {
|
|
44
|
+
cpuMillicores: cpuQuota != null ? cpuQuota.maximum - usedMillicores : null,
|
|
45
|
+
ramBytes: ramQuota != null ? ramToBytes(ramQuota.information) - usedBytes : null,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case 'human':
|
|
51
|
+
default: {
|
|
52
|
+
const ramQuotaBytes = ramQuota != null ? ramToBytes(ramQuota.information) : null;
|
|
53
|
+
console.table({
|
|
54
|
+
'Max CPU': {
|
|
55
|
+
Quota: cpuQuota != null ? `${cpuQuota.maximum / 1000} vCPU` : 'unlimited',
|
|
56
|
+
Usage: `${usedMillicores / 1000} vCPU`,
|
|
57
|
+
Remaining: cpuQuota != null ? `${(cpuQuota.maximum - usedMillicores) / 1000} vCPU` : 'unlimited',
|
|
58
|
+
},
|
|
59
|
+
'Max RAM': {
|
|
60
|
+
Quota: ramQuota != null ? `${ramQuota.information.number} ${ramQuota.information.unit}` : 'unlimited',
|
|
61
|
+
Usage: `${formatGb(usedBytes)} GB`,
|
|
62
|
+
Remaining: ramQuotaBytes != null ? `${formatGb(ramQuotaBytes - usedBytes)} GB` : 'unlimited',
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
function ramToBytes({ number, unit }) {
|
|
72
|
+
return number * (UNIT_TO_BYTES[unit] ?? BYTES_PER_GB);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function formatGb(bytes) {
|
|
76
|
+
return Math.round((bytes / BYTES_PER_GB) * 100) / 100;
|
|
77
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
3
|
+
import { defineOption } from '../../lib/define-option.js';
|
|
4
|
+
import { k8sUpdate } from '../../lib/k8s.js';
|
|
5
|
+
import { styleText } from '../../lib/style-text.js';
|
|
6
|
+
import { Logger } from '../../logger.js';
|
|
7
|
+
import { tags } from '../../parsers.js';
|
|
8
|
+
import { orgaIdOrNameOption } from '../global.options.js';
|
|
9
|
+
import { k8sIdOrNameArg } from './k8s.args.js';
|
|
10
|
+
|
|
11
|
+
export const k8sUpdateCommand = defineCommand({
|
|
12
|
+
description: 'Update a Kubernetes cluster metadata or features',
|
|
13
|
+
since: '4.9.0',
|
|
14
|
+
options: {
|
|
15
|
+
name: defineOption({
|
|
16
|
+
name: 'name',
|
|
17
|
+
schema: z.string().max(128).optional(),
|
|
18
|
+
description: 'Rename the cluster',
|
|
19
|
+
placeholder: 'name',
|
|
20
|
+
}),
|
|
21
|
+
description: defineOption({
|
|
22
|
+
name: 'description',
|
|
23
|
+
schema: z.string().max(4096).optional(),
|
|
24
|
+
description: 'Free-form cluster description',
|
|
25
|
+
placeholder: 'description',
|
|
26
|
+
}),
|
|
27
|
+
tag: defineOption({
|
|
28
|
+
name: 'tag',
|
|
29
|
+
schema: z.string().transform(tags).optional(),
|
|
30
|
+
description: 'Replace tags (comma-separated, e.g.: env:prod,team:platform)',
|
|
31
|
+
placeholder: 'tag[,tag...]',
|
|
32
|
+
}),
|
|
33
|
+
autoscaling: defineOption({
|
|
34
|
+
name: 'autoscaling',
|
|
35
|
+
schema: z.boolean().default(false),
|
|
36
|
+
description: 'Enable the cluster autoscaler',
|
|
37
|
+
}),
|
|
38
|
+
disableAutoscaling: defineOption({
|
|
39
|
+
name: 'disable-autoscaling',
|
|
40
|
+
schema: z.boolean().default(false),
|
|
41
|
+
description: 'Disable the cluster autoscaler',
|
|
42
|
+
}),
|
|
43
|
+
org: orgaIdOrNameOption,
|
|
44
|
+
},
|
|
45
|
+
args: [k8sIdOrNameArg],
|
|
46
|
+
async handler(options, clusterIdOrName) {
|
|
47
|
+
const { org: orgIdOrName } = options;
|
|
48
|
+
|
|
49
|
+
if (options.autoscaling && options.disableAutoscaling) {
|
|
50
|
+
throw new Error('--autoscaling and --disable-autoscaling are mutually exclusive');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const updates = {};
|
|
54
|
+
if (options.name != null) updates.name = options.name;
|
|
55
|
+
if (options.description != null) updates.description = options.description;
|
|
56
|
+
if (options.tag != null) updates.tags = options.tag;
|
|
57
|
+
|
|
58
|
+
const features = {};
|
|
59
|
+
if (options.autoscaling) features.autoscalingEnabled = true;
|
|
60
|
+
if (options.disableAutoscaling) features.autoscalingEnabled = false;
|
|
61
|
+
if (Object.keys(features).length > 0) updates.features = features;
|
|
62
|
+
|
|
63
|
+
if (Object.keys(updates).length === 0) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'No update specified. Provide at least one of --name, --description, --tag, --autoscaling, --disable-autoscaling',
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const cluster = await k8sUpdate(orgIdOrName, clusterIdOrName, updates);
|
|
70
|
+
Logger.printSuccess(`Cluster ${styleText('green', cluster.name)} updated`);
|
|
71
|
+
},
|
|
72
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { k8sCheckVersion } from '../../lib/k8s.js';
|
|
3
|
+
import { humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
|
|
4
|
+
import { k8sIdOrNameArg } from './k8s.args.js';
|
|
5
|
+
|
|
6
|
+
export const k8sVersionCheckCommand = defineCommand({
|
|
7
|
+
description: 'Check a Kubernetes cluster deployed version',
|
|
8
|
+
since: '4.9.0',
|
|
9
|
+
options: {
|
|
10
|
+
org: orgaIdOrNameOption,
|
|
11
|
+
format: humanJsonOutputFormatOption,
|
|
12
|
+
},
|
|
13
|
+
args: [k8sIdOrNameArg],
|
|
14
|
+
async handler(options, clusterIdOrName) {
|
|
15
|
+
const { format, org: orgIdOrName } = options;
|
|
16
|
+
await k8sCheckVersion(orgIdOrName, clusterIdOrName, format);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { k8sCheckVersion } from '../../lib/k8s.js';
|
|
3
|
+
import { humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
|
|
4
|
+
import { k8sIdOrNameArg } from './k8s.args.js';
|
|
5
|
+
|
|
6
|
+
export const k8sVersionCommand = defineCommand({
|
|
7
|
+
description: 'Check a Kubernetes cluster deployed version',
|
|
8
|
+
since: '4.9.0',
|
|
9
|
+
options: {
|
|
10
|
+
org: orgaIdOrNameOption,
|
|
11
|
+
format: humanJsonOutputFormatOption,
|
|
12
|
+
},
|
|
13
|
+
args: [k8sIdOrNameArg],
|
|
14
|
+
async handler(options, clusterIdOrName) {
|
|
15
|
+
const { format, org: orgIdOrName } = options;
|
|
16
|
+
await k8sCheckVersion(orgIdOrName, clusterIdOrName, format);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { k8sUpdateVersion } from '../../lib/k8s.js';
|
|
3
|
+
import { orgaIdOrNameOption, targetVersionOption } from '../global.options.js';
|
|
4
|
+
import { k8sIdOrNameArg } from './k8s.args.js';
|
|
5
|
+
|
|
6
|
+
export const k8sVersionUpdateCommand = defineCommand({
|
|
7
|
+
description: 'Update a Kubernetes cluster to a target version',
|
|
8
|
+
since: '4.9.0',
|
|
9
|
+
options: {
|
|
10
|
+
org: orgaIdOrNameOption,
|
|
11
|
+
target: targetVersionOption,
|
|
12
|
+
},
|
|
13
|
+
args: [k8sIdOrNameArg],
|
|
14
|
+
async handler(options, clusterIdOrName) {
|
|
15
|
+
const { target, org: orgIdOrName } = options;
|
|
16
|
+
await k8sUpdateVersion(orgIdOrName, clusterIdOrName, target);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -52,7 +52,7 @@ clever oauth-consumers delete <consumer-key|consumer-name> [options]
|
|
|
52
52
|
|
|
53
53
|
|Name|Description|
|
|
54
54
|
|---|---|
|
|
55
|
-
|`-y`, `--yes`|Skip confirmation
|
|
55
|
+
|`-y`, `--yes`|Skip confirmation prompts|
|
|
56
56
|
|
|
57
57
|
## ➡️ `clever oauth-consumers get` <kbd>Since 4.8.0</kbd>
|
|
58
58
|
|
|
@@ -108,6 +108,20 @@ clever otoroshi open logs <addon-id|addon-name>
|
|
|
108
108
|
|---|---|
|
|
109
109
|
|`addon-id|addon-name`|Add-on ID (or name, if unambiguous)|
|
|
110
110
|
|
|
111
|
+
## ➡️ `clever otoroshi open swaggerui` <kbd>Since 4.9.0</kbd>
|
|
112
|
+
|
|
113
|
+
Open the Otoroshi Swagger UI in your browser
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
clever otoroshi open swaggerui <addon-id|addon-name>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 📥 Arguments
|
|
120
|
+
|
|
121
|
+
|Name|Description|
|
|
122
|
+
|---|---|
|
|
123
|
+
|`addon-id|addon-name`|Add-on ID (or name, if unambiguous)|
|
|
124
|
+
|
|
111
125
|
## ➡️ `clever otoroshi open webui` <kbd>Since 3.13.0</kbd>
|
|
112
126
|
|
|
113
127
|
Open the Otoroshi admin console in your browser
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { operatorOpenSwaggerUi } from '../../lib/operator-commands.js';
|
|
3
|
+
import { addonIdOrNameArg } from '../global.args.js';
|
|
4
|
+
|
|
5
|
+
export const otoroshiOpenSwaggeruiCommand = defineCommand({
|
|
6
|
+
description: 'Open the Otoroshi Swagger UI in your browser',
|
|
7
|
+
since: '4.9.0',
|
|
8
|
+
options: {},
|
|
9
|
+
args: [addonIdOrNameArg],
|
|
10
|
+
async handler(_options, addonIdOrName) {
|
|
11
|
+
await operatorOpenSwaggerUi(addonIdOrName);
|
|
12
|
+
},
|
|
13
|
+
});
|