11"use client"
22
3+ import { AlertCircle , CheckCircle , Loader2 } from "lucide-react"
34import * as React from "react"
4- import { Plus , Loader2 , CheckCircle , AlertCircle } from "lucide-react"
55
6+ import { Alert , AlertDescription } from "@/components/ui/alert"
67import { Button } from "@/components/ui/button"
78import {
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"
1716import { 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
2020const defaultPodYaml = `apiVersion: v1
2121kind: 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