@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.
Files changed (59) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +54 -55
  3. package/commands/apply.d.ts +4 -0
  4. package/commands/apply.js +171 -0
  5. package/commands/cluster-info.d.ts +4 -0
  6. package/commands/cluster-info.js +26 -0
  7. package/commands/config-handler.d.ts +11 -0
  8. package/commands/config-handler.js +81 -0
  9. package/commands/config.d.ts +4 -0
  10. package/commands/config.js +72 -0
  11. package/commands/delete.d.ts +4 -0
  12. package/commands/delete.js +256 -0
  13. package/commands/deploy.d.ts +6 -0
  14. package/commands/deploy.js +209 -0
  15. package/commands/describe.d.ts +4 -0
  16. package/commands/describe.js +216 -0
  17. package/commands/exec.d.ts +4 -0
  18. package/commands/exec.js +145 -0
  19. package/commands/get.d.ts +4 -0
  20. package/commands/get.js +164 -0
  21. package/commands/logs.d.ts +4 -0
  22. package/commands/logs.js +110 -0
  23. package/commands/port-forward.d.ts +4 -0
  24. package/commands/port-forward.js +143 -0
  25. package/commands.d.ts +3 -0
  26. package/commands.js +93 -0
  27. package/config.d.ts +22 -0
  28. package/config.js +113 -0
  29. package/esm/commands/apply.js +133 -0
  30. package/esm/commands/cluster-info.js +21 -0
  31. package/esm/commands/config-handler.js +43 -0
  32. package/esm/commands/config.js +67 -0
  33. package/esm/commands/delete.js +218 -0
  34. package/esm/commands/deploy.js +207 -0
  35. package/esm/commands/describe.js +211 -0
  36. package/esm/commands/exec.js +140 -0
  37. package/esm/commands/get.js +159 -0
  38. package/esm/commands/logs.js +105 -0
  39. package/esm/commands/port-forward.js +138 -0
  40. package/esm/commands.js +86 -0
  41. package/esm/config.js +74 -0
  42. package/esm/index.js +19 -0
  43. package/esm/package.js +26 -0
  44. package/esm/utils.js +49 -0
  45. package/index.d.ts +3 -0
  46. package/index.js +22 -0
  47. package/package.d.ts +1 -0
  48. package/package.js +29 -0
  49. package/package.json +37 -61
  50. package/utils.d.ts +11 -0
  51. package/utils.js +58 -0
  52. package/main/client.js +0 -156
  53. package/main/index.js +0 -2598
  54. package/module/client.js +0 -129
  55. package/module/index.js +0 -2594
  56. package/src/client.ts +0 -156
  57. package/src/index.ts +0 -14187
  58. package/types/client.d.ts +0 -31
  59. package/types/index.d.ts +0 -11331
@@ -0,0 +1,207 @@
1
+ import { KubernetesClient } from 'kubernetesjs';
2
+ const createDeployment = (type) => {
3
+ const commonMetadata = {
4
+ name: `${type}-deployment`,
5
+ labels: {
6
+ app: type
7
+ }
8
+ };
9
+ const commonSpec = {
10
+ replicas: 1,
11
+ selector: {
12
+ matchLabels: {
13
+ app: type
14
+ }
15
+ },
16
+ template: {
17
+ metadata: {
18
+ labels: {
19
+ app: type
20
+ }
21
+ }
22
+ }
23
+ };
24
+ switch (type) {
25
+ case 'minio':
26
+ return {
27
+ apiVersion: 'apps/v1',
28
+ kind: 'Deployment',
29
+ metadata: commonMetadata,
30
+ spec: {
31
+ ...commonSpec,
32
+ template: {
33
+ ...commonSpec.template,
34
+ spec: {
35
+ containers: [{
36
+ name: 'minio',
37
+ image: 'minio/minio:latest',
38
+ ports: [{ containerPort: 9000, name: 'minio' }],
39
+ env: [
40
+ { name: 'MINIO_ROOT_USER', value: 'minioadmin' },
41
+ { name: 'MINIO_ROOT_PASSWORD', value: 'minioadmin' }
42
+ ],
43
+ args: ['server', '/data']
44
+ }]
45
+ }
46
+ }
47
+ }
48
+ };
49
+ case 'postgres':
50
+ return {
51
+ apiVersion: 'apps/v1',
52
+ kind: 'Deployment',
53
+ metadata: commonMetadata,
54
+ spec: {
55
+ ...commonSpec,
56
+ template: {
57
+ ...commonSpec.template,
58
+ spec: {
59
+ containers: [{
60
+ name: 'postgres',
61
+ image: 'pyramation/pgvector:13.3-alpine',
62
+ ports: [{ containerPort: 5432, name: 'postgres' }],
63
+ env: [
64
+ { name: 'POSTGRES_USER', value: 'postgres' },
65
+ { name: 'POSTGRES_PASSWORD', value: 'postgres' },
66
+ { name: 'POSTGRES_DB', value: 'postgres' }
67
+ ]
68
+ }]
69
+ }
70
+ }
71
+ }
72
+ };
73
+ case 'ollama':
74
+ return {
75
+ apiVersion: 'apps/v1',
76
+ kind: 'Deployment',
77
+ metadata: commonMetadata,
78
+ spec: {
79
+ ...commonSpec,
80
+ template: {
81
+ ...commonSpec.template,
82
+ spec: {
83
+ containers: [{
84
+ name: 'ollama',
85
+ image: 'ollama/ollama:latest',
86
+ ports: [{ containerPort: 11434, name: 'ollama' }]
87
+ }]
88
+ }
89
+ }
90
+ }
91
+ };
92
+ default:
93
+ throw new Error(`Unsupported deployment type: ${type}`);
94
+ }
95
+ };
96
+ const createService = (type) => {
97
+ const commonMetadata = {
98
+ name: `${type}-service`,
99
+ labels: {
100
+ app: type
101
+ }
102
+ };
103
+ const commonSpec = {
104
+ selector: {
105
+ app: type
106
+ }
107
+ };
108
+ switch (type) {
109
+ case 'minio':
110
+ return {
111
+ apiVersion: 'v1',
112
+ kind: 'Service',
113
+ metadata: commonMetadata,
114
+ spec: {
115
+ ...commonSpec,
116
+ ports: [{ port: 9000, targetPort: 'minio' }],
117
+ type: 'ClusterIP'
118
+ }
119
+ };
120
+ case 'postgres':
121
+ return {
122
+ apiVersion: 'v1',
123
+ kind: 'Service',
124
+ metadata: commonMetadata,
125
+ spec: {
126
+ ...commonSpec,
127
+ ports: [{ port: 5432, targetPort: 'postgres' }],
128
+ type: 'ClusterIP'
129
+ }
130
+ };
131
+ case 'ollama':
132
+ return {
133
+ apiVersion: 'v1',
134
+ kind: 'Service',
135
+ metadata: commonMetadata,
136
+ spec: {
137
+ ...commonSpec,
138
+ ports: [{ port: 11434, targetPort: 'ollama' }],
139
+ type: 'ClusterIP'
140
+ }
141
+ };
142
+ default:
143
+ throw new Error(`Unsupported service type: ${type}`);
144
+ }
145
+ };
146
+ export default async (argv, prompter, _options) => {
147
+ const deploymentOptions = ['minio', 'postgres', 'ollama'];
148
+ const questions = [
149
+ {
150
+ type: 'autocomplete',
151
+ name: 'deploymentType',
152
+ message: 'Select deployment type',
153
+ options: deploymentOptions,
154
+ maxDisplayLines: 5,
155
+ required: true
156
+ }
157
+ ];
158
+ const { deploymentType } = await prompter.prompt(argv, questions);
159
+ const confirmQuestion = {
160
+ type: 'confirm',
161
+ name: 'confirmDeployment',
162
+ message: `Are you sure you want to deploy ${deploymentType}?`,
163
+ required: true
164
+ };
165
+ const { confirmDeployment } = await prompter.prompt(argv, [confirmQuestion]);
166
+ if (!confirmDeployment) {
167
+ console.log('Deployment cancelled.');
168
+ return;
169
+ }
170
+ console.log(`Deploying ${deploymentType}...`);
171
+ try {
172
+ // Initialize Kubernetes client
173
+ const client = new KubernetesClient({
174
+ restEndpoint: argv.clientUrl
175
+ });
176
+ // Create deployment
177
+ const deployment = createDeployment(deploymentType);
178
+ await client.createAppsV1NamespacedDeployment({
179
+ path: {
180
+ namespace: 'default'
181
+ },
182
+ query: {
183
+ pretty: 'true'
184
+ },
185
+ body: deployment
186
+ });
187
+ console.log(`Created deployment: ${deployment.metadata.name}`);
188
+ // Create service
189
+ const service = createService(deploymentType);
190
+ await client.createCoreV1NamespacedService({
191
+ path: {
192
+ namespace: 'default'
193
+ },
194
+ query: {
195
+ pretty: 'true'
196
+ },
197
+ body: service
198
+ });
199
+ console.log(`Created service: ${service.metadata.name}`);
200
+ console.log(`Successfully deployed ${deploymentType}!`);
201
+ }
202
+ catch (error) {
203
+ console.error(`Failed to deploy ${deploymentType}:`, error);
204
+ throw error;
205
+ }
206
+ return { deploymentType };
207
+ };
@@ -0,0 +1,211 @@
1
+ import chalk from 'chalk';
2
+ import { KubernetesClient } from 'kubernetesjs';
3
+ import { getCurrentNamespace } from '../config';
4
+ async function promptResourceType(prompter, argv) {
5
+ const resourceTypes = [
6
+ 'pod',
7
+ 'service',
8
+ 'deployment',
9
+ 'replicaset',
10
+ 'statefulset',
11
+ 'daemonset',
12
+ 'configmap',
13
+ 'secret',
14
+ 'namespace'
15
+ ];
16
+ const question = {
17
+ type: 'autocomplete',
18
+ name: 'resourceType',
19
+ message: 'Select resource type',
20
+ options: resourceTypes,
21
+ maxDisplayLines: 10,
22
+ required: true
23
+ };
24
+ const { resourceType } = await prompter.prompt(argv, [question]);
25
+ return resourceType;
26
+ }
27
+ async function promptResourceName(prompter, argv, resourceType, namespace, client) {
28
+ let resources = [];
29
+ switch (resourceType) {
30
+ case 'pod':
31
+ const pods = await client.listCoreV1NamespacedPod({
32
+ path: { namespace },
33
+ query: { limit: 100 }
34
+ });
35
+ resources = pods.items || [];
36
+ break;
37
+ case 'service':
38
+ const services = await client.listCoreV1NamespacedService({
39
+ path: { namespace },
40
+ query: { limit: 100 }
41
+ });
42
+ resources = services.items || [];
43
+ break;
44
+ case 'deployment':
45
+ const deployments = await client.listAppsV1NamespacedDeployment({
46
+ path: { namespace },
47
+ query: { limit: 100 }
48
+ });
49
+ resources = deployments.items || [];
50
+ break;
51
+ default:
52
+ console.log(chalk.yellow(`Resource type '${resourceType}' not implemented yet for selection`));
53
+ return '';
54
+ }
55
+ if (resources.length === 0) {
56
+ console.log(chalk.yellow(`No ${resourceType}s found in namespace ${namespace}`));
57
+ return '';
58
+ }
59
+ const options = resources.map(r => ({
60
+ name: r.metadata.name,
61
+ value: r.metadata.name
62
+ }));
63
+ const question = {
64
+ type: 'autocomplete',
65
+ name: 'resourceName',
66
+ message: `Select ${resourceType} name`,
67
+ options,
68
+ maxDisplayLines: 10,
69
+ required: true
70
+ };
71
+ const { resourceName } = await prompter.prompt(argv, [question]);
72
+ return resourceName;
73
+ }
74
+ function describePod(pod) {
75
+ console.log(chalk.bold(`Name: ${pod.metadata.name}`));
76
+ console.log(chalk.bold(`Namespace: ${pod.metadata.namespace}`));
77
+ console.log(chalk.bold(`Priority: ${pod.spec.priority || 0}`));
78
+ console.log(chalk.bold(`Node: ${pod.spec.nodeName || '<none>'}`));
79
+ console.log(chalk.bold(`Start Time: ${pod.status.startTime || '<unknown>'}`));
80
+ console.log(chalk.bold('\nLabels:'));
81
+ if (pod.metadata.labels) {
82
+ Object.entries(pod.metadata.labels).forEach(([key, value]) => {
83
+ console.log(` ${key}=${value}`);
84
+ });
85
+ }
86
+ console.log(chalk.bold('\nStatus: ' + pod.status.phase));
87
+ console.log(chalk.bold('IP: ' + pod.status.podIP));
88
+ console.log(chalk.bold('\nContainers:'));
89
+ if (pod.spec.containers) {
90
+ pod.spec.containers.forEach((container) => {
91
+ console.log(chalk.bold(` ${container.name}:`));
92
+ console.log(` Image: ${container.image}`);
93
+ console.log(` Ports: ${container.ports?.map((p) => p.containerPort).join(', ') || '<none>'}`);
94
+ const status = pod.status.containerStatuses?.find((s) => s.name === container.name);
95
+ if (status) {
96
+ console.log(` Ready: ${status.ready}`);
97
+ console.log(` Restarts: ${status.restartCount}`);
98
+ const stateEntries = Object.entries(status.state || {});
99
+ if (stateEntries.length > 0) {
100
+ const [state, details] = stateEntries[0];
101
+ console.log(` State: ${state}`);
102
+ if (details && typeof details === 'object') {
103
+ Object.entries(details).forEach(([key, value]) => {
104
+ console.log(` ${key}: ${value}`);
105
+ });
106
+ }
107
+ }
108
+ }
109
+ console.log('');
110
+ });
111
+ }
112
+ console.log(chalk.bold('Events:'));
113
+ console.log(' <Events not available in this implementation>');
114
+ }
115
+ function describeService(service) {
116
+ console.log(chalk.bold(`Name: ${service.metadata.name}`));
117
+ console.log(chalk.bold(`Namespace: ${service.metadata.namespace}`));
118
+ console.log(chalk.bold(`Labels: ${JSON.stringify(service.metadata.labels || {})}`));
119
+ console.log(chalk.bold(`Selector: ${JSON.stringify(service.spec.selector || {})}`));
120
+ console.log(chalk.bold(`Type: ${service.spec.type}`));
121
+ console.log(chalk.bold(`IP: ${service.spec.clusterIP}`));
122
+ if (service.spec.ports) {
123
+ console.log(chalk.bold('\nPorts:'));
124
+ service.spec.ports.forEach((port) => {
125
+ console.log(` ${port.name || '<unnamed>'}: ${port.port}/${port.protocol} -> ${port.targetPort}`);
126
+ });
127
+ }
128
+ console.log(chalk.bold('\nSession Affinity: ' + (service.spec.sessionAffinity || 'None')));
129
+ console.log(chalk.bold('\nEvents:'));
130
+ console.log(' <Events not available in this implementation>');
131
+ }
132
+ function describeDeployment(deployment) {
133
+ console.log(chalk.bold(`Name: ${deployment.metadata.name}`));
134
+ console.log(chalk.bold(`Namespace: ${deployment.metadata.namespace}`));
135
+ console.log(chalk.bold(`CreationTimestamp: ${deployment.metadata.creationTimestamp}`));
136
+ console.log(chalk.bold(`Labels: ${JSON.stringify(deployment.metadata.labels || {})}`));
137
+ console.log(chalk.bold(`Annotations: ${JSON.stringify(deployment.metadata.annotations || {})}`));
138
+ console.log(chalk.bold(`Selector: ${JSON.stringify(deployment.spec.selector.matchLabels || {})}`));
139
+ console.log(chalk.bold(`Replicas: ${deployment.status.replicas || 0} desired | ${deployment.status.updatedReplicas || 0} updated | ${deployment.status.readyReplicas || 0} ready | ${deployment.status.availableReplicas || 0} available`));
140
+ console.log(chalk.bold(`StrategyType: ${deployment.spec.strategy.type}`));
141
+ if (deployment.spec.template && deployment.spec.template.spec.containers) {
142
+ console.log(chalk.bold('\nContainers:'));
143
+ deployment.spec.template.spec.containers.forEach((container) => {
144
+ console.log(chalk.bold(` ${container.name}:`));
145
+ console.log(` Image: ${container.image}`);
146
+ console.log(` Ports: ${container.ports?.map((p) => p.containerPort).join(', ') || '<none>'}`);
147
+ console.log(` Environment: ${container.env?.map((e) => `${e.name}=${e.value}`).join(', ') || '<none>'}`);
148
+ console.log('');
149
+ });
150
+ }
151
+ if (deployment.status.conditions) {
152
+ console.log(chalk.bold('\nConditions:'));
153
+ console.log(chalk.bold(' Type Status Reason'));
154
+ deployment.status.conditions.forEach((condition) => {
155
+ console.log(` ${condition.type.padEnd(15)}${condition.status.padEnd(8)}${condition.reason || ''}`);
156
+ });
157
+ }
158
+ console.log(chalk.bold('\nEvents:'));
159
+ console.log(' <Events not available in this implementation>');
160
+ }
161
+ export default async (argv, prompter, _options) => {
162
+ try {
163
+ const client = new KubernetesClient({
164
+ restEndpoint: 'http://localhost:8001' // Default kube-proxy endpoint
165
+ });
166
+ const namespace = argv.n || argv.namespace || getCurrentNamespace();
167
+ const resourceType = argv._?.[0] || await promptResourceType(prompter, argv);
168
+ const resourceName = argv._?.[1] || await promptResourceName(prompter, argv, resourceType, namespace, client);
169
+ if (!resourceName) {
170
+ return;
171
+ }
172
+ console.log(chalk.blue(`Describing ${resourceType} ${resourceName} in namespace ${namespace}...`));
173
+ switch (resourceType) {
174
+ case 'pod':
175
+ const pod = await client.readCoreV1NamespacedPod({
176
+ path: {
177
+ namespace,
178
+ name: resourceName
179
+ },
180
+ query: {}
181
+ });
182
+ describePod(pod);
183
+ break;
184
+ case 'service':
185
+ const service = await client.readCoreV1NamespacedService({
186
+ path: {
187
+ namespace,
188
+ name: resourceName
189
+ },
190
+ query: {}
191
+ });
192
+ describeService(service);
193
+ break;
194
+ case 'deployment':
195
+ const deployment = await client.readAppsV1NamespacedDeployment({
196
+ path: {
197
+ namespace,
198
+ name: resourceName
199
+ },
200
+ query: {}
201
+ });
202
+ describeDeployment(deployment);
203
+ break;
204
+ default:
205
+ console.log(chalk.yellow(`Resource type '${resourceType}' not implemented yet`));
206
+ }
207
+ }
208
+ catch (error) {
209
+ console.error(chalk.red(`Error: ${error}`));
210
+ }
211
+ };
@@ -0,0 +1,140 @@
1
+ import chalk from 'chalk';
2
+ import { KubernetesClient } from 'kubernetesjs';
3
+ import { getCurrentNamespace } from '../config';
4
+ import { spawn } from 'child_process';
5
+ async function promptPodName(prompter, argv, namespace, client) {
6
+ try {
7
+ const pods = await client.listCoreV1NamespacedPod({
8
+ path: { namespace },
9
+ query: { limit: 100 }
10
+ });
11
+ if (!pods.items || pods.items.length === 0) {
12
+ console.log(chalk.yellow(`No pods found in namespace ${namespace}`));
13
+ return '';
14
+ }
15
+ const options = pods.items.map(pod => ({
16
+ name: pod.metadata.name,
17
+ value: pod.metadata.name
18
+ }));
19
+ const question = {
20
+ type: 'autocomplete',
21
+ name: 'podName',
22
+ message: 'Select pod',
23
+ options,
24
+ maxDisplayLines: 10,
25
+ required: true
26
+ };
27
+ const { podName } = await prompter.prompt(argv, [question]);
28
+ return podName;
29
+ }
30
+ catch (error) {
31
+ console.error(chalk.red(`Error getting pods: ${error}`));
32
+ return '';
33
+ }
34
+ }
35
+ async function promptContainerName(prompter, argv, namespace, podName, client) {
36
+ try {
37
+ const pod = await client.readCoreV1NamespacedPod({
38
+ path: {
39
+ namespace,
40
+ name: podName
41
+ },
42
+ query: {}
43
+ });
44
+ if (!pod.spec || !pod.spec.containers || pod.spec.containers.length === 0) {
45
+ console.log(chalk.yellow(`No containers found in pod ${podName}`));
46
+ return '';
47
+ }
48
+ if (pod.spec.containers.length === 1) {
49
+ return pod.spec.containers[0].name;
50
+ }
51
+ const options = pod.spec.containers.map(container => ({
52
+ name: container.name,
53
+ value: container.name
54
+ }));
55
+ const question = {
56
+ type: 'autocomplete',
57
+ name: 'containerName',
58
+ message: 'Select container',
59
+ options,
60
+ maxDisplayLines: 10,
61
+ required: true
62
+ };
63
+ const { containerName } = await prompter.prompt(argv, [question]);
64
+ return containerName;
65
+ }
66
+ catch (error) {
67
+ console.error(chalk.red(`Error getting containers: ${error}`));
68
+ return '';
69
+ }
70
+ }
71
+ async function execInPod(namespace, podName, containerName, command) {
72
+ console.log(chalk.blue(`Executing command in ${containerName ? 'container ' + containerName + ' of ' : ''}pod ${podName} in namespace ${namespace}...`));
73
+ const kubectlArgs = [
74
+ 'exec',
75
+ '-it',
76
+ '-n', namespace,
77
+ podName
78
+ ];
79
+ if (containerName) {
80
+ kubectlArgs.push('-c', containerName);
81
+ }
82
+ kubectlArgs.push('--', ...command);
83
+ const kubectl = spawn('kubectl', kubectlArgs, {
84
+ stdio: 'inherit',
85
+ shell: true
86
+ });
87
+ return new Promise((resolve, reject) => {
88
+ kubectl.on('close', (code) => {
89
+ if (code === 0) {
90
+ resolve();
91
+ }
92
+ else {
93
+ reject(new Error(`kubectl exec exited with code ${code}`));
94
+ }
95
+ });
96
+ kubectl.on('error', (error) => {
97
+ reject(error);
98
+ });
99
+ });
100
+ }
101
+ export default async (argv, prompter, _options) => {
102
+ try {
103
+ const client = new KubernetesClient({
104
+ restEndpoint: argv.clientUrl
105
+ });
106
+ const namespace = argv.n || argv.namespace || getCurrentNamespace();
107
+ const podName = argv._?.[0] || await promptPodName(prompter, argv, namespace, client);
108
+ if (!podName) {
109
+ return;
110
+ }
111
+ let containerName = argv.c || argv.container;
112
+ if (!containerName) {
113
+ containerName = await promptContainerName(prompter, argv, namespace, podName, client);
114
+ if (!containerName) {
115
+ return;
116
+ }
117
+ }
118
+ let command = [];
119
+ const dashIndex = argv._.findIndex(arg => arg === '--');
120
+ if (dashIndex !== -1 && dashIndex < argv._.length - 1) {
121
+ command = argv._.slice(dashIndex + 1);
122
+ }
123
+ else {
124
+ const commandQuestion = {
125
+ type: 'text',
126
+ name: 'command',
127
+ message: 'Enter command to execute',
128
+ required: true,
129
+ default: '/bin/sh',
130
+ useDefault: true
131
+ };
132
+ const { command: cmd } = await prompter.prompt(argv, [commandQuestion]);
133
+ command = cmd.split(' ');
134
+ }
135
+ await execInPod(namespace, podName, containerName, command);
136
+ }
137
+ catch (error) {
138
+ console.error(chalk.red(`Error: ${error}`));
139
+ }
140
+ };