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
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { k8sGetNodeGroup } from '../../lib/k8s.js';
|
|
3
|
+
import { Logger } from '../../logger.js';
|
|
4
|
+
import { humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
|
|
5
|
+
import { k8sIdOrNameArg, k8sNodeGroupIdOrNameArg } from './k8s.args.js';
|
|
6
|
+
|
|
7
|
+
export const k8sNodeGroupGetCommand = defineCommand({
|
|
8
|
+
description: 'Get information about a Kubernetes node group',
|
|
9
|
+
since: '4.9.0',
|
|
10
|
+
options: {
|
|
11
|
+
org: orgaIdOrNameOption,
|
|
12
|
+
format: humanJsonOutputFormatOption,
|
|
13
|
+
},
|
|
14
|
+
args: [k8sIdOrNameArg, k8sNodeGroupIdOrNameArg],
|
|
15
|
+
async handler(options, clusterIdOrName, nodeGroupIdOrName) {
|
|
16
|
+
const { format, org: orgIdOrName } = options;
|
|
17
|
+
const nodeGroup = await k8sGetNodeGroup(orgIdOrName, clusterIdOrName, nodeGroupIdOrName);
|
|
18
|
+
|
|
19
|
+
switch (format) {
|
|
20
|
+
case 'json':
|
|
21
|
+
Logger.printJson(nodeGroup);
|
|
22
|
+
break;
|
|
23
|
+
case 'human':
|
|
24
|
+
default: {
|
|
25
|
+
const overview = {
|
|
26
|
+
Name: nodeGroup.name,
|
|
27
|
+
ID: nodeGroup.id,
|
|
28
|
+
Status: nodeGroup.status,
|
|
29
|
+
Flavor: nodeGroup.flavor,
|
|
30
|
+
Nodes: `${nodeGroup.currentNodeCount}/${nodeGroup.targetNodeCount}`,
|
|
31
|
+
Autoscaling: nodeGroup.autoscalingEnabled ? 'enabled' : 'disabled',
|
|
32
|
+
'Min / Max': `${nodeGroup.minNodeCount} / ${nodeGroup.maxNodeCount}${nodeGroup.autoscalingEnabled ? '' : ' (inactive)'}`,
|
|
33
|
+
Created: formatDate(nodeGroup.createdAt),
|
|
34
|
+
};
|
|
35
|
+
const createdFmt = formatDate(nodeGroup.createdAt);
|
|
36
|
+
const updatedFmt = formatDate(nodeGroup.updatedAt);
|
|
37
|
+
if (nodeGroup.updatedAt && updatedFmt !== createdFmt) {
|
|
38
|
+
overview.Updated = updatedFmt;
|
|
39
|
+
}
|
|
40
|
+
if (nodeGroup.description) overview.Description = nodeGroup.description;
|
|
41
|
+
if (nodeGroup.tag) overview.Tag = nodeGroup.tag;
|
|
42
|
+
console.table(overview);
|
|
43
|
+
|
|
44
|
+
const labels = Object.entries(nodeGroup.labels ?? {});
|
|
45
|
+
if (labels.length > 0) {
|
|
46
|
+
Logger.println('');
|
|
47
|
+
Logger.println(`🏷️ Labels (${labels.length})`);
|
|
48
|
+
console.table(Object.fromEntries(labels.map(([k, v]) => [k, { Value: v }])));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const taints = nodeGroup.taints ?? [];
|
|
52
|
+
if (taints.length > 0) {
|
|
53
|
+
Logger.println('');
|
|
54
|
+
Logger.println(`🚫 Taints (${taints.length})`);
|
|
55
|
+
console.table(Object.fromEntries(taints.map((t) => [t.key, { Value: t.value ?? '-', Effect: t.effect }])));
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
function formatDate(iso) {
|
|
64
|
+
if (iso == null) return '-';
|
|
65
|
+
return `${iso.slice(0, 16).replace('T', ' ')}Z`;
|
|
66
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { k8sListNodeGroups } from '../../lib/k8s.js';
|
|
3
|
+
import { styleText } from '../../lib/style-text.js';
|
|
4
|
+
import { Logger } from '../../logger.js';
|
|
5
|
+
import { humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
|
|
6
|
+
import { k8sIdOrNameArg } from './k8s.args.js';
|
|
7
|
+
|
|
8
|
+
export const k8sNodeGroupListCommand = defineCommand({
|
|
9
|
+
description: 'List the node groups of a Kubernetes cluster',
|
|
10
|
+
since: '4.9.0',
|
|
11
|
+
options: {
|
|
12
|
+
org: orgaIdOrNameOption,
|
|
13
|
+
format: humanJsonOutputFormatOption,
|
|
14
|
+
},
|
|
15
|
+
args: [k8sIdOrNameArg],
|
|
16
|
+
async handler(options, clusterIdOrName) {
|
|
17
|
+
const { format, org: orgIdOrName } = options;
|
|
18
|
+
const nodeGroups = await k8sListNodeGroups(orgIdOrName, clusterIdOrName);
|
|
19
|
+
|
|
20
|
+
switch (format) {
|
|
21
|
+
case 'json':
|
|
22
|
+
Logger.printJson(nodeGroups);
|
|
23
|
+
break;
|
|
24
|
+
case 'human':
|
|
25
|
+
default:
|
|
26
|
+
if (nodeGroups.length === 0) {
|
|
27
|
+
Logger.println(
|
|
28
|
+
`🔎 No node group found, create one with ${styleText('blue', 'clever k8s nodegroups create')}`,
|
|
29
|
+
);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
console.table(
|
|
33
|
+
Object.fromEntries(
|
|
34
|
+
nodeGroups.map((ng) => [
|
|
35
|
+
ng.id,
|
|
36
|
+
{
|
|
37
|
+
Name: ng.name,
|
|
38
|
+
Status: ng.status,
|
|
39
|
+
Flavor: ng.flavor,
|
|
40
|
+
Nodes: `${ng.currentNodeCount}/${ng.targetNodeCount}`,
|
|
41
|
+
Autoscaling: ng.autoscalingEnabled ? `${ng.minNodeCount}-${ng.maxNodeCount}` : 'disabled',
|
|
42
|
+
},
|
|
43
|
+
]),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
});
|
|
@@ -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
|
+
});
|
|
@@ -4,11 +4,11 @@ import { defineOption } from '../../lib/define-option.js';
|
|
|
4
4
|
import { styleText } from '../../lib/style-text.js';
|
|
5
5
|
import { Logger } from '../../logger.js';
|
|
6
6
|
import * as Application from '../../models/application.js';
|
|
7
|
-
import {
|
|
7
|
+
import { resolveAddon } from '../../models/ids-resolver.js';
|
|
8
8
|
import * as Log from '../../models/log.js';
|
|
9
9
|
import { Deferred } from '../../models/utils.js';
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
addonIdOrRealIdOption,
|
|
12
12
|
afterOption,
|
|
13
13
|
aliasOption,
|
|
14
14
|
appIdOrNameOption,
|
|
@@ -36,7 +36,7 @@ export const logsCommand = defineCommand({
|
|
|
36
36
|
app: appIdOrNameOption,
|
|
37
37
|
before: beforeOption,
|
|
38
38
|
after: afterOption,
|
|
39
|
-
addon:
|
|
39
|
+
addon: addonIdOrRealIdOption,
|
|
40
40
|
format: logsFormatOption,
|
|
41
41
|
},
|
|
42
42
|
args: [],
|
|
@@ -57,8 +57,7 @@ export const logsCommand = defineCommand({
|
|
|
57
57
|
const isForHuman = format === 'human';
|
|
58
58
|
|
|
59
59
|
if (addonIdOrRealId != null) {
|
|
60
|
-
const realId = await
|
|
61
|
-
const ownerId = await resolveOwnerId(realId);
|
|
60
|
+
const { ownerId, realId } = await resolveAddon(addonIdOrRealId);
|
|
62
61
|
if (isForHuman) {
|
|
63
62
|
Logger.println(styleText('blue', 'Waiting for addon logs…'));
|
|
64
63
|
}
|
|
@@ -146,7 +146,6 @@ clever ng get-config <peer-id|peer-label> <ng-id|ng-label> [options]
|
|
|
146
146
|
|
|
147
147
|
|Name|Description|
|
|
148
148
|
|---|---|
|
|
149
|
-
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
150
149
|
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
151
150
|
|
|
152
151
|
## ➡️ `clever ng link` <kbd>Since 3.12.0</kbd>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineCommand } from '../../lib/define-command.js';
|
|
2
2
|
import { Logger } from '../../logger.js';
|
|
3
3
|
import * as networkGroup from '../../models/ng.js';
|
|
4
|
-
import {
|
|
4
|
+
import { orgaIdOrNameOption } from '../global.options.js';
|
|
5
5
|
import { ngExternalIdOrLabelArg, ngIdOrLabelArg } from './ng.args.js';
|
|
6
6
|
|
|
7
7
|
export const ngGetConfigCommand = defineCommand({
|
|
@@ -9,24 +9,13 @@ export const ngGetConfigCommand = defineCommand({
|
|
|
9
9
|
since: '3.12.0',
|
|
10
10
|
options: {
|
|
11
11
|
org: orgaIdOrNameOption,
|
|
12
|
-
format: humanJsonOutputFormatOption,
|
|
13
12
|
},
|
|
14
13
|
args: [ngExternalIdOrLabelArg, ngIdOrLabelArg],
|
|
15
14
|
async handler(options, peerIdOrLabel, ngIdOrLabel) {
|
|
16
|
-
const { org
|
|
15
|
+
const { org } = options;
|
|
17
16
|
|
|
18
17
|
const peerConfig = await networkGroup.getPeerConfig(peerIdOrLabel, ngIdOrLabel, org);
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
case 'json': {
|
|
22
|
-
Logger.printJson(peerConfig);
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
case 'human':
|
|
26
|
-
default: {
|
|
27
|
-
const decodedConfiguration = Buffer.from(peerConfig.configuration, 'base64').toString('utf8');
|
|
28
|
-
Logger.println(decodedConfiguration);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
19
|
+
Logger.println(peerConfig);
|
|
31
20
|
},
|
|
32
21
|
});
|
|
@@ -4,6 +4,6 @@ import { defineOption } from '../../lib/define-option.js';
|
|
|
4
4
|
export const ngResourceTypeOption = defineOption({
|
|
5
5
|
name: 'type',
|
|
6
6
|
schema: z.enum(['NetworkGroup', 'Member', 'CleverPeer', 'ExternalPeer']).optional(),
|
|
7
|
-
description: 'Type of resource to look for
|
|
7
|
+
description: 'Type of resource to look for',
|
|
8
8
|
placeholder: 'resource-type',
|
|
9
9
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { deleteEmailhook } from '@clevercloud/client/esm/api/v2/notification.js';
|
|
2
2
|
import { defineCommand } from '../../lib/define-command.js';
|
|
3
3
|
import { Logger } from '../../logger.js';
|
|
4
|
-
import {
|
|
4
|
+
import { getOwnerIdFromOrgIdOrName } from '../../models/ids-resolver.js';
|
|
5
5
|
import { sendToApi } from '../../models/send-to-api.js';
|
|
6
6
|
import { notificationIdArg } from '../global.args.js';
|
|
7
7
|
import { orgaIdOrNameOption } from '../global.options.js';
|
|
@@ -16,7 +16,7 @@ export const notifyEmailRemoveCommand = defineCommand({
|
|
|
16
16
|
async handler(options, notificationId) {
|
|
17
17
|
const { org } = options;
|
|
18
18
|
|
|
19
|
-
const ownerId = await
|
|
19
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(org);
|
|
20
20
|
await deleteEmailhook({ ownerId, id: notificationId }).then(sendToApi);
|
|
21
21
|
|
|
22
22
|
Logger.println('The notification has been successfully removed');
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineArgument } from '../../lib/define-argument.js';
|
|
3
|
+
|
|
4
|
+
export const consumerKeyOrNameArg = defineArgument({
|
|
5
|
+
schema: z.string(),
|
|
6
|
+
description: 'OAuth consumer key (or name, if unambiguous)',
|
|
7
|
+
placeholder: 'consumer-key|consumer-name',
|
|
8
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { create } from '@clevercloud/client/esm/api/v2/oauth-consumer.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { defineArgument } from '../../lib/define-argument.js';
|
|
4
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
5
|
+
import { promptTextOption } from '../../lib/prompts.js';
|
|
6
|
+
import { styleText } from '../../lib/style-text.js';
|
|
7
|
+
import { Logger } from '../../logger.js';
|
|
8
|
+
import { getOwnerIdFromOrgIdOrName } from '../../models/ids-resolver.js';
|
|
9
|
+
import { promptRights, rightsFromList } from '../../models/oauth-consumer.js';
|
|
10
|
+
import { sendToApi } from '../../models/send-to-api.js';
|
|
11
|
+
import { humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
|
|
12
|
+
import { baseUrlOption, descriptionOption, pictureOption, rightsOption, urlOption } from './oauth-consumers.options.js';
|
|
13
|
+
|
|
14
|
+
export const oauthConsumersCreateCommand = defineCommand({
|
|
15
|
+
description: 'Create an OAuth consumer',
|
|
16
|
+
since: '4.8.0',
|
|
17
|
+
options: {
|
|
18
|
+
org: orgaIdOrNameOption,
|
|
19
|
+
description: descriptionOption,
|
|
20
|
+
url: urlOption,
|
|
21
|
+
picture: pictureOption,
|
|
22
|
+
baseUrl: baseUrlOption,
|
|
23
|
+
rights: rightsOption,
|
|
24
|
+
format: humanJsonOutputFormatOption,
|
|
25
|
+
},
|
|
26
|
+
args: [
|
|
27
|
+
defineArgument({
|
|
28
|
+
schema: z.string(),
|
|
29
|
+
description: 'Consumer name',
|
|
30
|
+
placeholder: 'name',
|
|
31
|
+
}),
|
|
32
|
+
],
|
|
33
|
+
async handler(options, name) {
|
|
34
|
+
const { org, description, url, picture, baseUrl, rights, format } = options;
|
|
35
|
+
|
|
36
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(org);
|
|
37
|
+
|
|
38
|
+
const body = {
|
|
39
|
+
name,
|
|
40
|
+
description: description ?? (await promptTextOption(descriptionOption)),
|
|
41
|
+
url: url ?? (await promptTextOption(urlOption)),
|
|
42
|
+
picture: picture ?? (await promptTextOption(pictureOption)),
|
|
43
|
+
baseUrl: baseUrl ?? (await promptTextOption(baseUrlOption)),
|
|
44
|
+
rights: rights != null ? rightsFromList(rights) : await promptRights(),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const oauthConsumer = await create({ id: ownerId }, body).then(sendToApi);
|
|
48
|
+
|
|
49
|
+
switch (format) {
|
|
50
|
+
case 'json': {
|
|
51
|
+
Logger.printJson(oauthConsumer);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case 'human':
|
|
55
|
+
default: {
|
|
56
|
+
Logger.printSuccess(`OAuth consumer ${styleText(['bold', 'green'], oauthConsumer.key)} has been created!`);
|
|
57
|
+
Logger.println();
|
|
58
|
+
Logger.println(
|
|
59
|
+
`Retrieve the secret with ${styleText('blue', `clever oauth-consumers get ${oauthConsumer.key} --with-secret`)}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { remove } from '@clevercloud/client/esm/api/v2/oauth-consumer.js';
|
|
2
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
3
|
+
import { confirm } from '../../lib/prompts.js';
|
|
4
|
+
import { styleText } from '../../lib/style-text.js';
|
|
5
|
+
import { Logger } from '../../logger.js';
|
|
6
|
+
import { resolveOauthConsumer } from '../../models/oauth-consumer.js';
|
|
7
|
+
import { sendToApi } from '../../models/send-to-api.js';
|
|
8
|
+
import { skipConfirmationOption } from '../global.options.js';
|
|
9
|
+
import { consumerKeyOrNameArg } from './oauth-consumers.args.js';
|
|
10
|
+
|
|
11
|
+
export const oauthConsumersDeleteCommand = defineCommand({
|
|
12
|
+
description: 'Delete an OAuth consumer',
|
|
13
|
+
since: '4.8.0',
|
|
14
|
+
options: {
|
|
15
|
+
skipConfirmation: skipConfirmationOption,
|
|
16
|
+
},
|
|
17
|
+
args: [consumerKeyOrNameArg],
|
|
18
|
+
async handler(options, keyOrName) {
|
|
19
|
+
const { skipConfirmation } = options;
|
|
20
|
+
|
|
21
|
+
const oauthConsumer = await resolveOauthConsumer(keyOrName);
|
|
22
|
+
|
|
23
|
+
if (!skipConfirmation) {
|
|
24
|
+
await confirm(
|
|
25
|
+
`Are you sure you want to delete the OAuth consumer ${styleText('blue', oauthConsumer.key)}?`,
|
|
26
|
+
'OAuth consumer deletion cancelled.',
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
await remove({ id: oauthConsumer.ownerId, key: oauthConsumer.key }).then(sendToApi);
|
|
31
|
+
Logger.printSuccess(`OAuth consumer ${styleText(['bold', 'green'], oauthConsumer.key)} has been deleted!`);
|
|
32
|
+
},
|
|
33
|
+
});
|