Skip to content

Commit 6755eb9

Browse files
committed
fix the create queues, and pods
Signed-off-by: Deep <[email protected]>
1 parent 7c36b23 commit 6755eb9

File tree

13 files changed

+762
-521
lines changed

13 files changed

+762
-521
lines changed

apps/web/src/components/(dashboard)/bar-chart.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
66
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
77
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"
88

9-
// Sample data structure matching the original
109
const sampleData = [
1110
{
1211
metadata: { name: "gpu-queue" },
@@ -118,21 +117,18 @@ const QueueResourcesBarChart = ({ data = sampleData }: QueueResourcesBarChartPro
118117

119118
const resourceTypes = new Set<string>()
120119

121-
// Traverse the queue data and get all resource types
122120
data.forEach((queue) => {
123121
const allocated = queue.status?.allocated || {}
124122
Object.keys(allocated).forEach((resource) => resourceTypes.add(resource))
125123
})
126124

127-
// Convert resource type from Set to Array
128125
return Array.from(resourceTypes).map((resource) => ({
129126
value: resource,
130127
label: `${resource.charAt(0).toUpperCase() + resource.slice(1)} Resources`.replace("Nvidia.com/gpu", "GPU"),
131128
}))
132129
}, [data])
133130

134131
useEffect(() => {
135-
// If there is a resource option, select the first resource by default
136132
if (resourceOptions.length > 0 && !selectedResource) {
137133
setSelectedResource(resourceOptions[0].value)
138134
}
@@ -156,19 +152,16 @@ const QueueResourcesBarChart = ({ data = sampleData }: QueueResourcesBarChartPro
156152
return cpuStr.toString().includes("m") ? value / 1000 : value // m is converted to the number of cores
157153
}
158154

159-
// Process queue data and convert memory and CPU units
160155
const processData = (data: typeof sampleData) => {
161156
return data.reduce(
162157
(acc, queue) => {
163158
const name = queue.metadata.name
164159
const allocated = queue.status?.allocated || {}
165160
const capability = queue.spec?.capability || {}
166161

167-
// Handle memory unit conversion
168162
const allocatedMemory = convertMemoryToGi(allocated.memory || "0")
169163
const capabilityMemory = convertMemoryToGi(capability.memory || "0")
170164

171-
// Handle CPU unit conversion
172165
const allocatedCPU = convertCPUToCores(allocated.cpu || "0")
173166
const capabilityCPU = convertCPUToCores(capability.cpu || "0")
174167

@@ -190,10 +183,8 @@ const QueueResourcesBarChart = ({ data = sampleData }: QueueResourcesBarChartPro
190183
)
191184
}
192185

193-
// Process queue data
194186
const processedData = useMemo(() => processData(data), [data])
195187

196-
// Build chart data for Recharts
197188
const chartData = useMemo(() => {
198189
return Object.keys(processedData).map((queueName) => ({
199190
name: queueName,
@@ -202,7 +193,6 @@ const QueueResourcesBarChart = ({ data = sampleData }: QueueResourcesBarChartPro
202193
}))
203194
}, [processedData, selectedResource])
204195

205-
// Get Y-axis label
206196
const getYAxisLabel = () => {
207197
switch (selectedResource) {
208198
case "memory":

apps/web/src/components/(dashboard)/pods/columns.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { ColumnDef } from "@tanstack/react-table"
1414
import { ArrowUpDown, Filter } from 'lucide-react'
1515
import { PodStatus } from "./pod-management"
1616

17-
// Function to create columns with dynamic namespace and status filtering
1817
export const createColumns = (availableNamespaces: string[], availableStatuses: string[]): ColumnDef<PodStatus>[] => [
1918
{
2019
accessorKey: "name",
@@ -166,5 +165,4 @@ export const createColumns = (availableNamespaces: string[], availableStatuses:
166165

167166
]
168167

169-
// Default columns for backward compatibility
170168
export const columns = createColumns([], [])

apps/web/src/components/(dashboard)/pods/pod-create-dialog.tsx

Lines changed: 108 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
"use client"
22

3+
import { AlertCircle, CheckCircle, Loader2 } from "lucide-react"
34
import * as React from "react"
4-
import { Plus, Loader2, CheckCircle, AlertCircle } from "lucide-react"
55

6+
import { Alert, AlertDescription } from "@/components/ui/alert"
67
import { Button } from "@/components/ui/button"
78
import {
89
Dialog,
910
DialogContent,
1011
DialogDescription,
1112
DialogFooter,
1213
DialogHeader,
13-
DialogTitle,
14-
DialogTrigger,
14+
DialogTitle
1515
} from "@/components/ui/dialog"
16-
import { Textarea } from "@/components/ui/textarea"
1716
import { Label } from "@/components/ui/label"
18-
import { Alert, AlertDescription } from "@/components/ui/alert"
17+
import { Textarea } from "@/components/ui/textarea"
18+
import { trpc } from "@volcano/trpc/react"
1919

2020
const defaultPodYaml = `apiVersion: v1
2121
kind: Pod
@@ -31,88 +31,130 @@ spec:
3131
- containerPort: 80
3232
restartPolicy: Always`
3333

34-
export function CreatePodDialog({open, setOpen}:{open: boolean, setOpen: (open: boolean) => void}) {
34+
export function CreatePodDialog({ open, setOpen, handleRefresh }: { open: boolean, setOpen: (open: boolean) => void, handleRefresh: () => void }) {
3535
const [yaml, setYaml] = React.useState(defaultPodYaml)
36-
const [isCreating, setIsCreating] = React.useState(false)
3736
const [status, setStatus] = React.useState<{
3837
type: "success" | "error" | null
3938
message: string
4039
}>({ type: null, message: "" })
4140

42-
const validateYaml = (yamlString: string) => {
43-
try {
44-
// Basic validation - check if it's valid YAML structure
45-
const lines = yamlString.trim().split("\n")
46-
47-
// Check for required Kubernetes fields
48-
const hasApiVersion = lines.some((line) => line.trim().startsWith("apiVersion:"))
49-
const hasKind = lines.some((line) => line.trim().startsWith("kind:"))
50-
const hasMetadata = lines.some((line) => line.trim().startsWith("metadata:"))
51-
const hasSpec = lines.some((line) => line.trim().startsWith("spec:"))
52-
53-
if (!hasApiVersion) throw new Error("Missing required field: apiVersion")
54-
if (!hasKind) throw new Error("Missing required field: kind")
55-
if (!hasMetadata) throw new Error("Missing required field: metadata")
56-
if (!hasSpec) throw new Error("Missing required field: spec")
57-
58-
// Check if kind is Pod
59-
const kindLine = lines.find((line) => line.trim().startsWith("kind:"))
60-
if (kindLine && !kindLine.includes("Pod")) {
61-
throw new Error('Kind must be "Pod"')
62-
}
63-
64-
return { valid: true, error: null }
65-
} catch (error) {
66-
return {
67-
valid: false,
68-
error: error instanceof Error ? error.message : "Invalid YAML format",
69-
}
41+
React.useEffect(() => {
42+
if (open) {
43+
setStatus({ type: null, message: "" })
7044
}
71-
}
45+
}, [open])
7246

73-
const handleCreatePod = async () => {
74-
const validation = validateYaml(yaml)
47+
const { mutateAsync: createPod, isPending: isCreating } = trpc.podRouter.createPod.useMutation({
48+
onSuccess: () => {
49+
setStatus({
50+
type: "success",
51+
message: "Pod created successfully!",
52+
})
7553

76-
if (!validation.valid) {
54+
setOpen(false)
55+
handleRefresh()
56+
},
57+
onError: (error) => {
7758
setStatus({
7859
type: "error",
79-
message: validation.error || "Invalid YAML format",
60+
message: error.message,
8061
})
81-
return
62+
},
63+
})
64+
65+
66+
67+
const parseYamlToManifest = (yamlString: string) => {
68+
const lines = yamlString.trim().split('\n')
69+
const manifest: any = {
70+
apiVersion: '',
71+
kind: '',
72+
metadata: { name: '' },
73+
spec: { containers: [] }
8274
}
8375

84-
setIsCreating(true)
85-
setStatus({ type: null, message: "" })
76+
const requiredFields = ['apiVersion', 'kind', 'metadata', 'spec']
77+
const foundFields = new Set<string>()
78+
let currentSection = ''
79+
let currentSubSection = ''
80+
let currentContainer: any = null
81+
let inContainers = false
82+
83+
for (const line of lines) {
84+
const trimmed = line.trim()
85+
if (!trimmed || trimmed.startsWith('#')) continue
86+
87+
for (const field of requiredFields) {
88+
if (trimmed.startsWith(`${field}:`)) {
89+
foundFields.add(field)
90+
if (field === 'apiVersion' || field === 'kind') {
91+
manifest[field] = trimmed.split(':')[1].trim()
92+
}
93+
currentSection = field
94+
currentSubSection = ''
95+
}
96+
}
8697

87-
try {
88-
// Simulate API call to create pod
89-
await new Promise((resolve) => setTimeout(resolve, 2000))
98+
if (trimmed.startsWith('name:') && currentSection === 'metadata') {
99+
manifest.metadata.name = trimmed.split(':')[1].trim()
100+
}
90101

91-
// In a real implementation, you would call your Kubernetes API here
92-
// const response = await fetch('/api/pods', {
93-
// method: 'POST',
94-
// headers: { 'Content-Type': 'application/yaml' },
95-
// body: yaml
96-
// })
102+
if (currentSection === 'spec') {
103+
if (trimmed.startsWith('containers:')) {
104+
inContainers = true
105+
currentSubSection = 'containers'
106+
} else if (trimmed.startsWith('- name:') && inContainers) {
107+
if (currentContainer) {
108+
manifest.spec.containers.push(currentContainer)
109+
}
110+
currentContainer = { name: trimmed.split(':')[1].trim() }
111+
} else if (trimmed.startsWith('image:') && currentContainer) {
112+
currentContainer.image = trimmed.split(':')[1].trim()
113+
} else if (trimmed.startsWith('ports:') && currentContainer) {
114+
currentContainer.ports = []
115+
currentSubSection = 'ports'
116+
} else if (trimmed.startsWith('- containerPort:') && currentSubSection === 'ports' && currentContainer) {
117+
const port = parseInt(trimmed.split(':')[1].trim())
118+
currentContainer.ports.push({ containerPort: port })
119+
} else if (trimmed.startsWith('restartPolicy:') && currentSection === 'spec') {
120+
manifest.spec.restartPolicy = trimmed.split(':')[1].trim()
121+
}
122+
}
123+
}
97124

98-
setStatus({
99-
type: "success",
100-
message: "Pod created successfully!",
101-
})
125+
if (currentContainer) {
126+
manifest.spec.containers.push(currentContainer)
127+
}
102128

103-
// Reset form after successful creation
104-
setTimeout(() => {
105-
setOpen(false)
106-
setYaml(defaultPodYaml)
107-
setStatus({ type: null, message: "" })
108-
}, 2000)
129+
const missingFields = requiredFields.filter(field => !foundFields.has(field))
130+
if (missingFields.length > 0) {
131+
throw new Error(`Missing required fields: ${missingFields.join(', ')}`)
132+
}
133+
134+
if (manifest.kind !== 'Pod') {
135+
throw new Error('Kind must be "Pod"')
136+
}
137+
138+
if (!manifest.metadata.name) {
139+
throw new Error('Missing required field: metadata.name')
140+
}
141+
142+
if (!manifest.spec.containers || manifest.spec.containers.length === 0) {
143+
throw new Error('Pod spec must include at least one container')
144+
}
145+
146+
return manifest
147+
}
148+
149+
const handleCreatePod = async () => {
150+
try {
151+
const podManifest = parseYamlToManifest(yaml)
152+
await createPod({ podManifest })
109153
} catch (error) {
110154
setStatus({
111155
type: "error",
112-
message: error instanceof Error ? error.message : "Failed to create pod",
156+
message: error instanceof Error ? error.message : "Failed to create pod"
113157
})
114-
} finally {
115-
setIsCreating(false)
116158
}
117159
}
118160

@@ -123,7 +165,7 @@ export function CreatePodDialog({open, setOpen}:{open: boolean, setOpen: (open:
123165

124166
return (
125167
<Dialog open={open} onOpenChange={setOpen}>
126-
168+
127169
<DialogContent className="max-w-4xl max-h-[80vh] flex flex-col">
128170
<DialogHeader>
129171
<DialogTitle>Create Kubernetes Pod</DialogTitle>

0 commit comments

Comments
 (0)