11import { Request , Response } from "express" ;
22import fs from "fs" ;
33import path from "path" ;
4- import yaml from "js-yaml" ;
4+ import { exec as execCb } from "child_process" ;
5+ import { promisify } from "util" ;
56import Application from "../../models/application.model" ;
67import asyncHandler from "../../utils/handlers/asyncHandler" ;
78import generateK8sManifests from "../../utils/k8s/k8sManifests" ;
89import log , { formatNotification } from "../../utils/logging/logger" ;
910import type IApplication from "../../types/application.types" ;
1011import type { Document } from "mongoose" ;
1112import env from "../../config/env" ;
12- import k8sClient from "../../utils/k8s/client" ;
13-
14- /**
15- * Safely parse and apply YAML content to Kubernetes
16- */
17- async function applyYamlContent (
18- yamlContent : string ,
19- description ?: string
20- ) : Promise < void > {
21- // Skip empty or whitespace-only content
22- if ( ! yamlContent || yamlContent . trim ( ) === "" ) {
23- if ( description ) {
24- log ( {
25- type : "info" ,
26- message : `Skipping empty ${ description } - no content to apply` ,
27- } ) ;
28- }
29- return ;
30- }
3113
32- try {
33- // Parse YAML to JavaScript object
34- const resource = yaml . load ( yamlContent ) as any ;
35-
36- // Skip if parsing resulted in null or empty object
37- if ( ! resource || typeof resource !== "object" ) {
38- if ( description ) {
39- log ( {
40- type : "info" ,
41- message : `Skipping ${ description } - no valid resource found` ,
42- } ) ;
43- }
44- return ;
45- }
46-
47- // Apply the parsed resource
48- await k8sClient . applyResource ( resource ) ;
49- } catch ( error ) {
50- const errorMsg = `Failed to apply ${ description || "YAML content" } ` ;
51- log ( {
52- type : "error" ,
53- message : errorMsg ,
54- meta : { error, content : yamlContent } ,
55- } ) ;
56- throw new Error (
57- `${ errorMsg } : ${ error instanceof Error ? error . message : "Unknown error" } `
58- ) ;
59- }
60- }
14+ const exec = promisify ( execCb ) ;
6115
6216function writeManifestFiles (
6317 appDir : string ,
@@ -77,68 +31,31 @@ async function applyManifestSequence(
7731 res : Response
7832) {
7933 try {
80- // Apply namespace first using secure Kubernetes client
81- const namespaceContent = fs . readFileSync (
82- path . join ( appDir , "namespace.yaml" ) ,
83- "utf8"
84- ) ;
85- await applyYamlContent ( namespaceContent , "namespace" ) ;
34+ // Apply namespace first
35+ await exec ( `kubectl apply -f namespace.yaml` , { cwd : appDir } ) ;
8636
8737 // Apply PVs first if present
8838 if ( fs . existsSync ( path . join ( appDir , "pvs.yaml" ) ) ) {
89- const pvsContent = fs . readFileSync ( path . join ( appDir , "pvs.yaml" ) , "utf8" ) ;
90- await applyYamlContent ( pvsContent , "persistent volumes" ) ;
39+ await exec ( `kubectl apply -f pvs.yaml` , { cwd : appDir } ) ;
9140 }
9241
9342 // Apply PVCs next if present
9443 if ( fs . existsSync ( path . join ( appDir , "pvcs.yaml" ) ) ) {
95- const pvcsContent = fs . readFileSync (
96- path . join ( appDir , "pvcs.yaml" ) ,
97- "utf8"
98- ) ;
99- await applyYamlContent ( pvcsContent , "persistent volume claims" ) ;
44+ await exec ( `kubectl apply -f pvcs.yaml` , { cwd : appDir } ) ;
10045 }
10146
102- // Apply secret using secure client
103- const secretContent = fs . readFileSync (
104- path . join ( appDir , "secret.yaml" ) ,
105- "utf8"
106- ) ;
107- await applyYamlContent ( secretContent , "secret" ) ;
108-
109- // Apply image pull secret if present
47+ await exec ( `kubectl apply -f secret.yaml` , { cwd : appDir } ) ;
11048 if ( fs . existsSync ( path . join ( appDir , "imagepullsecret.yaml" ) ) ) {
111- const imagePullSecretContent = fs . readFileSync (
112- path . join ( appDir , "imagepullsecret.yaml" ) ,
113- "utf8"
114- ) ;
115- await applyYamlContent ( imagePullSecretContent , "image pull secret" ) ;
49+ await exec ( `kubectl apply -f imagepullsecret.yaml` , { cwd : appDir } ) ;
11650 }
117-
118- // Apply remaining manifests
119- const results = [ ] ;
120- for ( const filename of manifestFiles ) {
121- if (
122- filename !== "namespace.yaml" &&
123- filename !== "pvs.yaml" &&
124- filename !== "pvcs.yaml" &&
125- filename !== "secret.yaml" &&
126- filename !== "imagepullsecret.yaml"
127- ) {
128- const filePath = path . join ( appDir , filename ) ;
129- if ( fs . existsSync ( filePath ) ) {
130- const content = fs . readFileSync ( filePath , "utf8" ) ;
131- await applyYamlContent ( content , filename ) ;
132- results . push ( { file : filename , status : "applied" } ) ;
133- }
134- }
135- }
136-
51+ const { stdout } = await exec (
52+ `kubectl apply -f . --prune -l app=${ appName } --field-manager=application-controller` ,
53+ { cwd : appDir }
54+ ) ;
13755 log ( { type : "success" , message : `Application applied: ${ appName } ` } ) ;
13856 res . json ( {
13957 ...formatNotification ( "Application applied" , "success" ) ,
140- output : `Successfully applied ${ results . length } resources` ,
141- results,
58+ output : stdout ,
14259 } ) ;
14360 } catch ( err : any ) {
14461 log ( { type : "error" , message : "Failed to apply manifests" , meta : err } ) ;
@@ -152,7 +69,7 @@ async function applyManifestSequence(
15269 }
15370 res . status ( 500 ) . json ( {
15471 ...formatNotification ( "Failed to apply manifests" , "error" ) ,
155- error : err . message ,
72+ error : err . stderr || err . message ,
15673 manifests,
15774 } ) ;
15875 }
@@ -229,22 +146,18 @@ const applyApplication = asyncHandler(async (req: Request, res: Response) => {
229146 }
230147 }
231148
232- // Apply namespace first using secure client
233- const namespaceContent = fs . readFileSync (
234- path . join ( appDir , "namespace.yaml" ) ,
235- "utf8"
236- ) ;
237- await applyYamlContent ( namespaceContent , "namespace" ) ;
149+ // Apply namespace first
150+ await exec ( `kubectl apply -f namespace.yaml` , { cwd : appDir } ) ;
238151
239152 // Apply PVs first if present
240153 if ( manifestsResult . pvs ) {
241154 fs . writeFileSync ( path . join ( appDir , "pvs.yaml" ) , manifestsResult . pvs ) ;
242- await applyYamlContent ( manifestsResult . pvs , "persistent volumes" ) ;
155+ await exec ( `kubectl apply -f pvs.yaml` , { cwd : appDir } ) ;
243156 }
244157
245158 // Apply PVCs next if present
246159 if ( pvcsYaml ) {
247- await applyYamlContent ( pvcsYaml , "persistent volume claims" ) ;
160+ await exec ( `kubectl apply -f pvcs.yaml` , { cwd : appDir } ) ;
248161 }
249162
250163 // Now apply the rest (deployment, service, ingress, secrets, etc.)
0 commit comments