Skip to content

Commit 96b5ccf

Browse files
authored
Merge pull request #99 from jxs1211/feat/update-deployment
[feat] implement update deployment
2 parents 4342f1f + f35654f commit 96b5ccf

File tree

2 files changed

+208
-1
lines changed

2 files changed

+208
-1
lines changed

src/index.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import {
6767
describeDeployment,
6868
describeDeploymentSchema,
6969
} from "./tools/describe_deployment.js";
70+
import { updateDeployment, updateDeploymentSchema } from "./tools/update_deployment.js";
7071
import { createConfigMap, CreateConfigMapSchema } from "./tools/create_configmap.js";
7172
import { createService, createServiceSchema } from "./tools/create_service.js";
7273
import { listContexts, listContextsSchema } from "./tools/list_contexts.js";
@@ -129,6 +130,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
129130
listPodsSchema,
130131
listServicesSchema,
131132
uninstallHelmChartSchema,
133+
updateDeploymentSchema,
132134
upgradeHelmChartSchema,
133135
PortForwardSchema,
134136
StopPortForwardSchema,
@@ -477,7 +479,19 @@ server.setRequestHandler(
477479
}
478480
);
479481
}
480-
482+
case "update_deployment": {
483+
return await updateDeployment(
484+
k8sManager,
485+
input as {
486+
name: string;
487+
namespace: string;
488+
template: string;
489+
containerName?: string;
490+
replicas?: number;
491+
customConfig?: any;
492+
}
493+
);
494+
}
481495
case "describe_deployment": {
482496
return await describeDeployment(
483497
k8sManager,

src/tools/update_deployment.ts

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { KubernetesManager } from "../types.js";
2+
import * as k8s from "@kubernetes/client-node";
3+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
4+
import {
5+
ContainerTemplate,
6+
containerTemplates,
7+
CustomContainerConfig,
8+
CustomContainerConfigType,
9+
} from "../config/container-templates.js";
10+
11+
export const updateDeploymentSchema = {
12+
name: "update_deployment",
13+
description: "Update an existing kubernetes deployment in cluster",
14+
inputSchema: {
15+
type: "object",
16+
required: ["name", "namespace", "template"],
17+
properties: {
18+
name: { type: "string" },
19+
namespace: { type: "string" },
20+
template: {
21+
type: "string",
22+
enum: ContainerTemplate.options,
23+
},
24+
containerName: {
25+
type: "string",
26+
description: "Name of the container to update",
27+
},
28+
replicas: { type: "number" },
29+
customConfig: {
30+
type: "object",
31+
properties: {
32+
image: { type: "string" },
33+
command: { type: "array", items: { type: "string" } },
34+
args: { type: "array", items: { type: "string" } },
35+
ports: {
36+
type: "array",
37+
items: {
38+
type: "object",
39+
properties: {
40+
containerPort: { type: "number" },
41+
name: { type: "string" },
42+
protocol: { type: "string" },
43+
},
44+
},
45+
},
46+
resources: {
47+
type: "object",
48+
properties: {
49+
limits: {
50+
type: "object",
51+
additionalProperties: { type: "string" },
52+
},
53+
requests: {
54+
type: "object",
55+
additionalProperties: { type: "string" },
56+
},
57+
},
58+
},
59+
env: {
60+
type: "array",
61+
items: {
62+
type: "object",
63+
properties: {
64+
name: { type: "string" },
65+
value: { type: "string" },
66+
valueFrom: { type: "object" },
67+
},
68+
},
69+
},
70+
},
71+
},
72+
},
73+
},
74+
};
75+
76+
export async function updateDeployment(
77+
k8sManager: KubernetesManager,
78+
params: {
79+
name: string;
80+
namespace: string;
81+
template: string;
82+
containerName?: string;
83+
replicas?: number;
84+
customConfig?: CustomContainerConfigType;
85+
}
86+
) {
87+
// Get existing deployment
88+
const { body: existingDeployment } = await k8sManager
89+
.getAppsApi()
90+
.readNamespacedDeployment(params.name, params.namespace)
91+
.catch((error: any) => {
92+
console.error("Deployment read error:", {
93+
status: error.response?.statusCode,
94+
message: error.response?.body?.message || error.message,
95+
details: error.response?.body,
96+
});
97+
throw error;
98+
});
99+
100+
// Find target container
101+
const containers = existingDeployment.spec!.template.spec!.containers;
102+
let targetContainerIndex = params.containerName
103+
? containers.findIndex(c => c.name === params.containerName)
104+
: 0;
105+
106+
if (targetContainerIndex === -1) {
107+
throw new McpError(
108+
ErrorCode.InvalidRequest,
109+
`Container '${params.containerName}' not found in deployment`
110+
);
111+
}
112+
113+
// Prepare container config
114+
const templateConfig = containerTemplates[params.template];
115+
let containerConfig: k8s.V1Container;
116+
117+
if (params.template === "custom") {
118+
if (!params.customConfig) {
119+
throw new McpError(
120+
ErrorCode.InvalidRequest,
121+
"Custom container configuration is required when using 'custom' template"
122+
);
123+
}
124+
125+
const validatedConfig = CustomContainerConfig.parse(params.customConfig);
126+
containerConfig = {
127+
...containers[targetContainerIndex],
128+
...templateConfig,
129+
image: validatedConfig.image,
130+
command: validatedConfig.command,
131+
args: validatedConfig.args,
132+
ports: validatedConfig.ports,
133+
resources: validatedConfig.resources,
134+
env: validatedConfig.env,
135+
};
136+
} else {
137+
containerConfig = {
138+
...containers[targetContainerIndex],
139+
...templateConfig,
140+
};
141+
}
142+
143+
// Update deployment
144+
const updatedContainers = [...containers];
145+
updatedContainers[targetContainerIndex] = containerConfig;
146+
147+
const updatedDeployment: k8s.V1Deployment = {
148+
...existingDeployment,
149+
spec: {
150+
...existingDeployment.spec!,
151+
replicas: params.replicas ?? existingDeployment.spec!.replicas,
152+
template: {
153+
...existingDeployment.spec!.template,
154+
spec: {
155+
...existingDeployment.spec!.template.spec,
156+
containers: updatedContainers,
157+
},
158+
},
159+
},
160+
};
161+
162+
const { body } = await k8sManager
163+
.getAppsApi()
164+
.replaceNamespacedDeployment(params.name, params.namespace, updatedDeployment)
165+
.catch((error) => {
166+
if (error instanceof McpError) throw error;
167+
throw new McpError(
168+
ErrorCode.InternalError,
169+
`Failed to update deployment: ${error}`
170+
);
171+
})
172+
return {
173+
content: [
174+
{
175+
type: "text",
176+
text: JSON.stringify(
177+
{
178+
message: "Deployment updated successfully",
179+
deployment: {
180+
name: body.metadata?.name,
181+
namespace: body.metadata?.namespace,
182+
replicas: body.spec?.replicas,
183+
image: body.spec?.template.spec?.containers[targetContainerIndex].image,
184+
containerName: body.spec?.template.spec?.containers[targetContainerIndex].name,
185+
},
186+
},
187+
null,
188+
2
189+
),
190+
},
191+
],
192+
};
193+
}

0 commit comments

Comments
 (0)