@kubernetesjs/cli 0.0.3 → 0.1.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/LICENSE +1 -1
- package/README.md +54 -55
- package/commands/apply.d.ts +4 -0
- package/commands/apply.js +171 -0
- package/commands/cluster-info.d.ts +4 -0
- package/commands/cluster-info.js +26 -0
- package/commands/config-handler.d.ts +11 -0
- package/commands/config-handler.js +81 -0
- package/commands/config.d.ts +4 -0
- package/commands/config.js +72 -0
- package/commands/delete.d.ts +4 -0
- package/commands/delete.js +256 -0
- package/commands/deploy.d.ts +6 -0
- package/commands/deploy.js +209 -0
- package/commands/describe.d.ts +4 -0
- package/commands/describe.js +216 -0
- package/commands/exec.d.ts +4 -0
- package/commands/exec.js +145 -0
- package/commands/get.d.ts +4 -0
- package/commands/get.js +164 -0
- package/commands/logs.d.ts +4 -0
- package/commands/logs.js +110 -0
- package/commands/port-forward.d.ts +4 -0
- package/commands/port-forward.js +143 -0
- package/commands.d.ts +3 -0
- package/commands.js +93 -0
- package/config.d.ts +22 -0
- package/config.js +113 -0
- package/esm/commands/apply.js +133 -0
- package/esm/commands/cluster-info.js +21 -0
- package/esm/commands/config-handler.js +43 -0
- package/esm/commands/config.js +67 -0
- package/esm/commands/delete.js +218 -0
- package/esm/commands/deploy.js +207 -0
- package/esm/commands/describe.js +211 -0
- package/esm/commands/exec.js +140 -0
- package/esm/commands/get.js +159 -0
- package/esm/commands/logs.js +105 -0
- package/esm/commands/port-forward.js +138 -0
- package/esm/commands.js +86 -0
- package/esm/config.js +74 -0
- package/esm/index.js +19 -0
- package/esm/package.js +26 -0
- package/esm/utils.js +49 -0
- package/index.d.ts +3 -0
- package/index.js +22 -0
- package/package.d.ts +1 -0
- package/package.js +29 -0
- package/package.json +37 -61
- package/utils.d.ts +11 -0
- package/utils.js +58 -0
- package/main/client.js +0 -156
- package/main/index.js +0 -2598
- package/module/client.js +0 -129
- package/module/index.js +0 -2594
- package/src/client.ts +0 -156
- package/src/index.ts +0 -14187
- package/types/client.d.ts +0 -31
- package/types/index.d.ts +0 -11331
package/commands/exec.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
7
|
+
const kubernetesjs_1 = require("kubernetesjs");
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
async function promptPodName(prompter, argv, namespace, client) {
|
|
11
|
+
try {
|
|
12
|
+
const pods = await client.listCoreV1NamespacedPod({
|
|
13
|
+
path: { namespace },
|
|
14
|
+
query: { limit: 100 }
|
|
15
|
+
});
|
|
16
|
+
if (!pods.items || pods.items.length === 0) {
|
|
17
|
+
console.log(chalk_1.default.yellow(`No pods found in namespace ${namespace}`));
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
const options = pods.items.map(pod => ({
|
|
21
|
+
name: pod.metadata.name,
|
|
22
|
+
value: pod.metadata.name
|
|
23
|
+
}));
|
|
24
|
+
const question = {
|
|
25
|
+
type: 'autocomplete',
|
|
26
|
+
name: 'podName',
|
|
27
|
+
message: 'Select pod',
|
|
28
|
+
options,
|
|
29
|
+
maxDisplayLines: 10,
|
|
30
|
+
required: true
|
|
31
|
+
};
|
|
32
|
+
const { podName } = await prompter.prompt(argv, [question]);
|
|
33
|
+
return podName;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error(chalk_1.default.red(`Error getting pods: ${error}`));
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function promptContainerName(prompter, argv, namespace, podName, client) {
|
|
41
|
+
try {
|
|
42
|
+
const pod = await client.readCoreV1NamespacedPod({
|
|
43
|
+
path: {
|
|
44
|
+
namespace,
|
|
45
|
+
name: podName
|
|
46
|
+
},
|
|
47
|
+
query: {}
|
|
48
|
+
});
|
|
49
|
+
if (!pod.spec || !pod.spec.containers || pod.spec.containers.length === 0) {
|
|
50
|
+
console.log(chalk_1.default.yellow(`No containers found in pod ${podName}`));
|
|
51
|
+
return '';
|
|
52
|
+
}
|
|
53
|
+
if (pod.spec.containers.length === 1) {
|
|
54
|
+
return pod.spec.containers[0].name;
|
|
55
|
+
}
|
|
56
|
+
const options = pod.spec.containers.map(container => ({
|
|
57
|
+
name: container.name,
|
|
58
|
+
value: container.name
|
|
59
|
+
}));
|
|
60
|
+
const question = {
|
|
61
|
+
type: 'autocomplete',
|
|
62
|
+
name: 'containerName',
|
|
63
|
+
message: 'Select container',
|
|
64
|
+
options,
|
|
65
|
+
maxDisplayLines: 10,
|
|
66
|
+
required: true
|
|
67
|
+
};
|
|
68
|
+
const { containerName } = await prompter.prompt(argv, [question]);
|
|
69
|
+
return containerName;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error(chalk_1.default.red(`Error getting containers: ${error}`));
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function execInPod(namespace, podName, containerName, command) {
|
|
77
|
+
console.log(chalk_1.default.blue(`Executing command in ${containerName ? 'container ' + containerName + ' of ' : ''}pod ${podName} in namespace ${namespace}...`));
|
|
78
|
+
const kubectlArgs = [
|
|
79
|
+
'exec',
|
|
80
|
+
'-it',
|
|
81
|
+
'-n', namespace,
|
|
82
|
+
podName
|
|
83
|
+
];
|
|
84
|
+
if (containerName) {
|
|
85
|
+
kubectlArgs.push('-c', containerName);
|
|
86
|
+
}
|
|
87
|
+
kubectlArgs.push('--', ...command);
|
|
88
|
+
const kubectl = (0, child_process_1.spawn)('kubectl', kubectlArgs, {
|
|
89
|
+
stdio: 'inherit',
|
|
90
|
+
shell: true
|
|
91
|
+
});
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
kubectl.on('close', (code) => {
|
|
94
|
+
if (code === 0) {
|
|
95
|
+
resolve();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
reject(new Error(`kubectl exec exited with code ${code}`));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
kubectl.on('error', (error) => {
|
|
102
|
+
reject(error);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
exports.default = async (argv, prompter, _options) => {
|
|
107
|
+
try {
|
|
108
|
+
const client = new kubernetesjs_1.KubernetesClient({
|
|
109
|
+
restEndpoint: argv.clientUrl
|
|
110
|
+
});
|
|
111
|
+
const namespace = argv.n || argv.namespace || (0, config_1.getCurrentNamespace)();
|
|
112
|
+
const podName = argv._?.[0] || await promptPodName(prompter, argv, namespace, client);
|
|
113
|
+
if (!podName) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
let containerName = argv.c || argv.container;
|
|
117
|
+
if (!containerName) {
|
|
118
|
+
containerName = await promptContainerName(prompter, argv, namespace, podName, client);
|
|
119
|
+
if (!containerName) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
let command = [];
|
|
124
|
+
const dashIndex = argv._.findIndex(arg => arg === '--');
|
|
125
|
+
if (dashIndex !== -1 && dashIndex < argv._.length - 1) {
|
|
126
|
+
command = argv._.slice(dashIndex + 1);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const commandQuestion = {
|
|
130
|
+
type: 'text',
|
|
131
|
+
name: 'command',
|
|
132
|
+
message: 'Enter command to execute',
|
|
133
|
+
required: true,
|
|
134
|
+
default: '/bin/sh',
|
|
135
|
+
useDefault: true
|
|
136
|
+
};
|
|
137
|
+
const { command: cmd } = await prompter.prompt(argv, [commandQuestion]);
|
|
138
|
+
command = cmd.split(' ');
|
|
139
|
+
}
|
|
140
|
+
await execInPod(namespace, podName, containerName, command);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error(chalk_1.default.red(`Error: ${error}`));
|
|
144
|
+
}
|
|
145
|
+
};
|
package/commands/get.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
7
|
+
const kubernetesjs_1 = require("kubernetesjs");
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
async function promptResourceType(prompter, argv) {
|
|
10
|
+
const resourceTypes = [
|
|
11
|
+
'pods',
|
|
12
|
+
'services',
|
|
13
|
+
'deployments',
|
|
14
|
+
'replicasets',
|
|
15
|
+
'statefulsets',
|
|
16
|
+
'daemonsets',
|
|
17
|
+
'configmaps',
|
|
18
|
+
'secrets',
|
|
19
|
+
'namespaces',
|
|
20
|
+
'all'
|
|
21
|
+
];
|
|
22
|
+
const question = {
|
|
23
|
+
type: 'autocomplete',
|
|
24
|
+
name: 'resourceType',
|
|
25
|
+
message: 'Select resource type',
|
|
26
|
+
options: resourceTypes,
|
|
27
|
+
maxDisplayLines: 10,
|
|
28
|
+
required: true
|
|
29
|
+
};
|
|
30
|
+
const { resourceType } = await prompter.prompt(argv, [question]);
|
|
31
|
+
return resourceType;
|
|
32
|
+
}
|
|
33
|
+
function formatPodData(pod) {
|
|
34
|
+
const name = pod.metadata.name;
|
|
35
|
+
const ready = `${pod.status.containerStatuses?.filter((c) => c.ready).length || 0}/${pod.status.containerStatuses?.length || 0}`;
|
|
36
|
+
const status = pod.status.phase;
|
|
37
|
+
const restarts = pod.status.containerStatuses?.reduce((sum, c) => sum + c.restartCount, 0) || 0;
|
|
38
|
+
const age = new Date(pod.metadata.creationTimestamp).toLocaleString();
|
|
39
|
+
console.log(chalk_1.default.green(name.padEnd(50)) +
|
|
40
|
+
ready.padEnd(10) +
|
|
41
|
+
status.padEnd(15) +
|
|
42
|
+
restarts.toString().padEnd(10) +
|
|
43
|
+
age);
|
|
44
|
+
}
|
|
45
|
+
function formatServiceData(service) {
|
|
46
|
+
const name = service.metadata.name;
|
|
47
|
+
const type = service.spec.type;
|
|
48
|
+
const clusterIP = service.spec.clusterIP;
|
|
49
|
+
const externalIP = service.spec.externalIPs?.join(',') || '<none>';
|
|
50
|
+
const ports = service.spec.ports?.map((p) => `${p.port}:${p.targetPort}`).join(',') || '<none>';
|
|
51
|
+
const age = new Date(service.metadata.creationTimestamp).toLocaleString();
|
|
52
|
+
console.log(chalk_1.default.green(name.padEnd(30)) +
|
|
53
|
+
type.padEnd(15) +
|
|
54
|
+
clusterIP.padEnd(20) +
|
|
55
|
+
externalIP.padEnd(20) +
|
|
56
|
+
ports.padEnd(20) +
|
|
57
|
+
age);
|
|
58
|
+
}
|
|
59
|
+
function formatDeploymentData(deployment) {
|
|
60
|
+
const name = deployment.metadata.name;
|
|
61
|
+
const ready = `${deployment.status.readyReplicas || 0}/${deployment.status.replicas || 0}`;
|
|
62
|
+
const upToDate = deployment.status.updatedReplicas || 0;
|
|
63
|
+
const available = deployment.status.availableReplicas || 0;
|
|
64
|
+
const age = new Date(deployment.metadata.creationTimestamp).toLocaleString();
|
|
65
|
+
console.log(chalk_1.default.green(name.padEnd(30)) +
|
|
66
|
+
ready.padEnd(10) +
|
|
67
|
+
upToDate.toString().padEnd(10) +
|
|
68
|
+
available.toString().padEnd(10) +
|
|
69
|
+
age);
|
|
70
|
+
}
|
|
71
|
+
async function getAllResources(client, namespace) {
|
|
72
|
+
try {
|
|
73
|
+
const pods = await client.listCoreV1NamespacedPod({
|
|
74
|
+
path: { namespace },
|
|
75
|
+
query: { limit: 100 }
|
|
76
|
+
});
|
|
77
|
+
if (pods.items && pods.items.length > 0) {
|
|
78
|
+
console.log(chalk_1.default.bold('\nPODS:'));
|
|
79
|
+
console.log(chalk_1.default.bold('NAME'.padEnd(50) + 'READY'.padEnd(10) + 'STATUS'.padEnd(15) + 'RESTARTS'.padEnd(10) + 'AGE'));
|
|
80
|
+
pods.items.forEach(formatPodData);
|
|
81
|
+
}
|
|
82
|
+
const services = await client.listCoreV1NamespacedService({
|
|
83
|
+
path: { namespace },
|
|
84
|
+
query: { limit: 100 }
|
|
85
|
+
});
|
|
86
|
+
if (services.items && services.items.length > 0) {
|
|
87
|
+
console.log(chalk_1.default.bold('\nSERVICES:'));
|
|
88
|
+
console.log(chalk_1.default.bold('NAME'.padEnd(30) + 'TYPE'.padEnd(15) + 'CLUSTER-IP'.padEnd(20) + 'EXTERNAL-IP'.padEnd(20) + 'PORT(S)'.padEnd(20) + 'AGE'));
|
|
89
|
+
services.items.forEach(formatServiceData);
|
|
90
|
+
}
|
|
91
|
+
const deployments = await client.listAppsV1NamespacedDeployment({
|
|
92
|
+
path: { namespace },
|
|
93
|
+
query: { limit: 100 }
|
|
94
|
+
});
|
|
95
|
+
if (deployments.items && deployments.items.length > 0) {
|
|
96
|
+
console.log(chalk_1.default.bold('\nDEPLOYMENTS:'));
|
|
97
|
+
console.log(chalk_1.default.bold('NAME'.padEnd(30) + 'READY'.padEnd(10) + 'UP-TO-DATE'.padEnd(10) + 'AVAILABLE'.padEnd(10) + 'AGE'));
|
|
98
|
+
deployments.items.forEach(formatDeploymentData);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error(chalk_1.default.red(`Error getting resources: ${error}`));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.default = async (argv, prompter, _options) => {
|
|
106
|
+
try {
|
|
107
|
+
const client = new kubernetesjs_1.KubernetesClient({
|
|
108
|
+
restEndpoint: argv.clientUrl
|
|
109
|
+
});
|
|
110
|
+
const namespace = argv.n || argv.namespace || (0, config_1.getCurrentNamespace)();
|
|
111
|
+
const resourceType = argv._?.[0] || await promptResourceType(prompter, argv);
|
|
112
|
+
console.log(chalk_1.default.blue(`Getting ${resourceType} in namespace ${namespace}...`));
|
|
113
|
+
if (resourceType === 'all') {
|
|
114
|
+
await getAllResources(client, namespace);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
switch (resourceType) {
|
|
118
|
+
case 'pods':
|
|
119
|
+
const pods = await client.listCoreV1NamespacedPod({
|
|
120
|
+
path: { namespace },
|
|
121
|
+
query: { limit: 100 }
|
|
122
|
+
});
|
|
123
|
+
console.log(chalk_1.default.bold('NAME'.padEnd(50) + 'READY'.padEnd(10) + 'STATUS'.padEnd(15) + 'RESTARTS'.padEnd(10) + 'AGE'));
|
|
124
|
+
if (pods.items && pods.items.length > 0) {
|
|
125
|
+
pods.items.forEach(formatPodData);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.log(chalk_1.default.yellow('No pods found'));
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
case 'services':
|
|
132
|
+
const services = await client.listCoreV1NamespacedService({
|
|
133
|
+
path: { namespace },
|
|
134
|
+
query: { limit: 100 }
|
|
135
|
+
});
|
|
136
|
+
console.log(chalk_1.default.bold('NAME'.padEnd(30) + 'TYPE'.padEnd(15) + 'CLUSTER-IP'.padEnd(20) + 'EXTERNAL-IP'.padEnd(20) + 'PORT(S)'.padEnd(20) + 'AGE'));
|
|
137
|
+
if (services.items && services.items.length > 0) {
|
|
138
|
+
services.items.forEach(formatServiceData);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
console.log(chalk_1.default.yellow('No services found'));
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
case 'deployments':
|
|
145
|
+
const deployments = await client.listAppsV1NamespacedDeployment({
|
|
146
|
+
path: { namespace },
|
|
147
|
+
query: { limit: 100 }
|
|
148
|
+
});
|
|
149
|
+
console.log(chalk_1.default.bold('NAME'.padEnd(30) + 'READY'.padEnd(10) + 'UP-TO-DATE'.padEnd(10) + 'AVAILABLE'.padEnd(10) + 'AGE'));
|
|
150
|
+
if (deployments.items && deployments.items.length > 0) {
|
|
151
|
+
deployments.items.forEach(formatDeploymentData);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
console.log(chalk_1.default.yellow('No deployments found'));
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
default:
|
|
158
|
+
console.log(chalk_1.default.yellow(`Resource type '${resourceType}' not implemented yet`));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.error(chalk_1.default.red(`Error: ${error}`));
|
|
163
|
+
}
|
|
164
|
+
};
|
package/commands/logs.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
7
|
+
const kubernetesjs_1 = require("kubernetesjs");
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
async function promptPodName(prompter, argv, namespace, client) {
|
|
10
|
+
try {
|
|
11
|
+
const pods = await client.listCoreV1NamespacedPod({
|
|
12
|
+
path: { namespace },
|
|
13
|
+
query: { limit: 100 }
|
|
14
|
+
});
|
|
15
|
+
if (!pods.items || pods.items.length === 0) {
|
|
16
|
+
console.log(chalk_1.default.yellow(`No pods found in namespace ${namespace}`));
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
const options = pods.items.map(pod => ({
|
|
20
|
+
name: pod.metadata.name,
|
|
21
|
+
value: pod.metadata.name
|
|
22
|
+
}));
|
|
23
|
+
const question = {
|
|
24
|
+
type: 'autocomplete',
|
|
25
|
+
name: 'podName',
|
|
26
|
+
message: 'Select pod',
|
|
27
|
+
options,
|
|
28
|
+
maxDisplayLines: 10,
|
|
29
|
+
required: true
|
|
30
|
+
};
|
|
31
|
+
const { podName } = await prompter.prompt(argv, [question]);
|
|
32
|
+
return podName;
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.error(chalk_1.default.red(`Error getting pods: ${error}`));
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function promptContainerName(prompter, argv, namespace, podName, client) {
|
|
40
|
+
try {
|
|
41
|
+
const pod = await client.readCoreV1NamespacedPod({
|
|
42
|
+
path: {
|
|
43
|
+
namespace,
|
|
44
|
+
name: podName
|
|
45
|
+
},
|
|
46
|
+
query: {}
|
|
47
|
+
});
|
|
48
|
+
if (!pod.spec || !pod.spec.containers || pod.spec.containers.length === 0) {
|
|
49
|
+
console.log(chalk_1.default.yellow(`No containers found in pod ${podName}`));
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
if (pod.spec.containers.length === 1) {
|
|
53
|
+
return pod.spec.containers[0].name;
|
|
54
|
+
}
|
|
55
|
+
const options = pod.spec.containers.map(container => ({
|
|
56
|
+
name: container.name,
|
|
57
|
+
value: container.name
|
|
58
|
+
}));
|
|
59
|
+
const question = {
|
|
60
|
+
type: 'autocomplete',
|
|
61
|
+
name: 'containerName',
|
|
62
|
+
message: 'Select container',
|
|
63
|
+
options,
|
|
64
|
+
maxDisplayLines: 10,
|
|
65
|
+
required: true
|
|
66
|
+
};
|
|
67
|
+
const { containerName } = await prompter.prompt(argv, [question]);
|
|
68
|
+
return containerName;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error(chalk_1.default.red(`Error getting containers: ${error}`));
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.default = async (argv, prompter, _options) => {
|
|
76
|
+
try {
|
|
77
|
+
const client = new kubernetesjs_1.KubernetesClient({
|
|
78
|
+
restEndpoint: argv.clientUrl
|
|
79
|
+
});
|
|
80
|
+
const namespace = argv.n || argv.namespace || (0, config_1.getCurrentNamespace)();
|
|
81
|
+
const podName = argv._?.[0] || await promptPodName(prompter, argv, namespace, client);
|
|
82
|
+
if (!podName) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let containerName = argv.c || argv.container;
|
|
86
|
+
if (!containerName) {
|
|
87
|
+
containerName = await promptContainerName(prompter, argv, namespace, podName, client);
|
|
88
|
+
if (!containerName) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
console.log(chalk_1.default.blue(`Getting logs for ${containerName ? 'container ' + containerName + ' in ' : ''}pod ${podName} in namespace ${namespace}...`));
|
|
93
|
+
const logs = await client.readCoreV1NamespacedPodLog({
|
|
94
|
+
path: {
|
|
95
|
+
namespace,
|
|
96
|
+
name: podName
|
|
97
|
+
},
|
|
98
|
+
query: {
|
|
99
|
+
container: containerName,
|
|
100
|
+
tailLines: argv.tail ? parseInt(argv.tail, 10) : undefined,
|
|
101
|
+
follow: false,
|
|
102
|
+
previous: argv.previous === true
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
console.log(logs);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error(chalk_1.default.red(`Error: ${error}`));
|
|
109
|
+
}
|
|
110
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
7
|
+
const kubernetesjs_1 = require("kubernetesjs");
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
async function promptServiceName(prompter, argv, namespace, client) {
|
|
11
|
+
try {
|
|
12
|
+
const services = await client.listCoreV1NamespacedService({
|
|
13
|
+
path: { namespace },
|
|
14
|
+
query: { limit: 100 }
|
|
15
|
+
});
|
|
16
|
+
if (!services.items || services.items.length === 0) {
|
|
17
|
+
console.log(chalk_1.default.yellow(`No services found in namespace ${namespace}`));
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
const options = services.items.map(service => ({
|
|
21
|
+
name: service.metadata.name,
|
|
22
|
+
value: service.metadata.name
|
|
23
|
+
}));
|
|
24
|
+
const question = {
|
|
25
|
+
type: 'autocomplete',
|
|
26
|
+
name: 'serviceName',
|
|
27
|
+
message: 'Select service',
|
|
28
|
+
options,
|
|
29
|
+
maxDisplayLines: 10,
|
|
30
|
+
required: true
|
|
31
|
+
};
|
|
32
|
+
const { serviceName } = await prompter.prompt(argv, [question]);
|
|
33
|
+
return serviceName;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error(chalk_1.default.red(`Error getting services: ${error}`));
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function promptPortMapping(prompter, argv, namespace, serviceName, client) {
|
|
41
|
+
try {
|
|
42
|
+
const service = await client.readCoreV1NamespacedService({
|
|
43
|
+
path: {
|
|
44
|
+
namespace,
|
|
45
|
+
name: serviceName
|
|
46
|
+
},
|
|
47
|
+
query: {}
|
|
48
|
+
});
|
|
49
|
+
if (!service.spec || !service.spec.ports || service.spec.ports.length === 0) {
|
|
50
|
+
console.log(chalk_1.default.yellow(`No ports found in service ${serviceName}`));
|
|
51
|
+
return '';
|
|
52
|
+
}
|
|
53
|
+
if (service.spec.ports.length === 1) {
|
|
54
|
+
const port = service.spec.ports[0];
|
|
55
|
+
const localPort = port.port;
|
|
56
|
+
const remotePort = port.targetPort || port.port;
|
|
57
|
+
const confirmQuestion = {
|
|
58
|
+
type: 'confirm',
|
|
59
|
+
name: 'confirmPortMapping',
|
|
60
|
+
message: `Use port mapping ${localPort}:${remotePort}?`,
|
|
61
|
+
required: true
|
|
62
|
+
};
|
|
63
|
+
const { confirmPortMapping } = await prompter.prompt(argv, [confirmQuestion]);
|
|
64
|
+
if (confirmPortMapping) {
|
|
65
|
+
return `${localPort}:${remotePort}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const portMappingQuestion = {
|
|
69
|
+
type: 'text',
|
|
70
|
+
name: 'portMapping',
|
|
71
|
+
message: 'Enter port mapping (local:remote)',
|
|
72
|
+
required: true
|
|
73
|
+
};
|
|
74
|
+
const { portMapping } = await prompter.prompt(argv, [portMappingQuestion]);
|
|
75
|
+
return portMapping;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
console.error(chalk_1.default.red(`Error getting service ports: ${error}`));
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function portForward(namespace, resourceType, resourceName, portMapping) {
|
|
83
|
+
console.log(chalk_1.default.blue(`Forwarding ports ${portMapping} to ${resourceType}/${resourceName} in namespace ${namespace}...`));
|
|
84
|
+
console.log(chalk_1.default.yellow('Press Ctrl+C to stop port forwarding'));
|
|
85
|
+
const kubectlArgs = [
|
|
86
|
+
'port-forward',
|
|
87
|
+
'-n', namespace,
|
|
88
|
+
`${resourceType}/${resourceName}`,
|
|
89
|
+
portMapping
|
|
90
|
+
];
|
|
91
|
+
const kubectl = (0, child_process_1.spawn)('kubectl', kubectlArgs, {
|
|
92
|
+
stdio: 'inherit',
|
|
93
|
+
shell: true
|
|
94
|
+
});
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
kubectl.on('close', (code) => {
|
|
97
|
+
if (code === 0 || code === 130) { // 130 is the exit code when terminated by Ctrl+C
|
|
98
|
+
resolve();
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
reject(new Error(`kubectl port-forward exited with code ${code}`));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
kubectl.on('error', (error) => {
|
|
105
|
+
reject(error);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
exports.default = async (argv, prompter, _options) => {
|
|
110
|
+
try {
|
|
111
|
+
const client = new kubernetesjs_1.KubernetesClient({
|
|
112
|
+
restEndpoint: argv.clientUrl
|
|
113
|
+
});
|
|
114
|
+
const namespace = argv.n || argv.namespace || (0, config_1.getCurrentNamespace)();
|
|
115
|
+
let resourceType = 'svc';
|
|
116
|
+
let resourceName = '';
|
|
117
|
+
if (argv._?.[0]) {
|
|
118
|
+
const resourceArg = argv._[0];
|
|
119
|
+
if (resourceArg.includes('/')) {
|
|
120
|
+
const [type, name] = resourceArg.split('/');
|
|
121
|
+
resourceType = type;
|
|
122
|
+
resourceName = name;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
resourceName = resourceArg;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!resourceName) {
|
|
129
|
+
resourceName = await promptServiceName(prompter, argv, namespace, client);
|
|
130
|
+
if (!resourceName) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const portMapping = argv._?.[1] || await promptPortMapping(prompter, argv, namespace, resourceName, client);
|
|
135
|
+
if (!portMapping) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
await portForward(namespace, resourceType, resourceName, portMapping);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
console.error(chalk_1.default.red(`Error: ${error}`));
|
|
142
|
+
}
|
|
143
|
+
};
|
package/commands.d.ts
ADDED
package/commands.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.commands = void 0;
|
|
7
|
+
const package_1 = require("./package");
|
|
8
|
+
const utils_1 = require("./utils");
|
|
9
|
+
// Commands
|
|
10
|
+
const deploy_1 = __importDefault(require("./commands/deploy"));
|
|
11
|
+
const get_1 = __importDefault(require("./commands/get"));
|
|
12
|
+
const describe_1 = __importDefault(require("./commands/describe"));
|
|
13
|
+
const logs_1 = __importDefault(require("./commands/logs"));
|
|
14
|
+
const apply_1 = __importDefault(require("./commands/apply"));
|
|
15
|
+
const delete_1 = __importDefault(require("./commands/delete"));
|
|
16
|
+
const exec_1 = __importDefault(require("./commands/exec"));
|
|
17
|
+
const port_forward_1 = __importDefault(require("./commands/port-forward"));
|
|
18
|
+
const cluster_info_1 = __importDefault(require("./commands/cluster-info"));
|
|
19
|
+
const config_1 = __importDefault(require("./commands/config"));
|
|
20
|
+
const commandMap = {
|
|
21
|
+
deploy: deploy_1.default,
|
|
22
|
+
get: get_1.default,
|
|
23
|
+
describe: describe_1.default,
|
|
24
|
+
logs: logs_1.default,
|
|
25
|
+
apply: apply_1.default,
|
|
26
|
+
delete: delete_1.default,
|
|
27
|
+
exec: exec_1.default,
|
|
28
|
+
'port-forward': port_forward_1.default,
|
|
29
|
+
'cluster-info': cluster_info_1.default,
|
|
30
|
+
config: config_1.default
|
|
31
|
+
};
|
|
32
|
+
const config_handler_1 = __importDefault(require("./commands/config-handler"));
|
|
33
|
+
const commands = async (argv, prompter, options) => {
|
|
34
|
+
if (argv.version || argv.v) {
|
|
35
|
+
const pkg = (0, package_1.readAndParsePackageJson)();
|
|
36
|
+
console.log(pkg.version);
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
if (argv.config) {
|
|
40
|
+
const handled = await (0, config_handler_1.default)(argv, prompter, options, commandMap);
|
|
41
|
+
if (handled) {
|
|
42
|
+
prompter.close();
|
|
43
|
+
return argv;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
let { first: command, newArgv } = (0, utils_1.extractFirst)(argv);
|
|
47
|
+
// Show usage if explicitly requested
|
|
48
|
+
if (argv.help || argv.h || command === 'help') {
|
|
49
|
+
console.log(utils_1.usageText);
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
// Prompt if no command provided
|
|
53
|
+
if (!command) {
|
|
54
|
+
const answer = await prompter.prompt(argv, [
|
|
55
|
+
{
|
|
56
|
+
type: 'autocomplete',
|
|
57
|
+
name: 'command',
|
|
58
|
+
message: 'What do you want to do?',
|
|
59
|
+
options: Object.keys(commandMap)
|
|
60
|
+
}
|
|
61
|
+
]);
|
|
62
|
+
command = answer.command;
|
|
63
|
+
}
|
|
64
|
+
// Prompt for working directory and client URL
|
|
65
|
+
newArgv = await prompter.prompt(newArgv, [
|
|
66
|
+
{
|
|
67
|
+
type: 'text',
|
|
68
|
+
name: 'cwd',
|
|
69
|
+
message: 'Working directory',
|
|
70
|
+
required: false,
|
|
71
|
+
default: process.cwd(),
|
|
72
|
+
useDefault: true
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: 'text',
|
|
76
|
+
name: 'clientUrl',
|
|
77
|
+
message: 'Kubernetes API URL',
|
|
78
|
+
required: false,
|
|
79
|
+
default: 'http://127.0.0.1:8001',
|
|
80
|
+
useDefault: true
|
|
81
|
+
}
|
|
82
|
+
]);
|
|
83
|
+
const commandFn = commandMap[command];
|
|
84
|
+
if (!commandFn) {
|
|
85
|
+
console.error(`Unknown command: ${command}`);
|
|
86
|
+
console.log(utils_1.usageText);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
await commandFn(newArgv, prompter, options);
|
|
90
|
+
prompter.close();
|
|
91
|
+
return argv;
|
|
92
|
+
};
|
|
93
|
+
exports.commands = commands;
|