99from app .core .logging import logger
1010from app .runtime_registry import RUNTIME_REGISTRY
1111from app .services .circuit_breaker import CircuitBreaker
12+ from app .services .network_policy import NetworkPolicyBuilder
1213from app .services .pod_manifest_builder import PodManifestBuilder
1314from fastapi import Depends , Request
1415from kubernetes import client as k8s_client
@@ -61,13 +62,15 @@ class KubernetesService:
6162
6263 v1 : Optional [k8s_client .CoreV1Api ]
6364 apps_v1 : Optional [k8s_client .AppsV1Api ]
65+ networking_v1 : Optional [k8s_client .NetworkingV1Api ]
6466 version_api : Optional [k8s_client .VersionApi ]
6567
6668 def __init__ (self , manager : KubernetesServiceManager ):
6769 self .settings = get_settings ()
6870 self .manager = manager
6971 self .v1 = None
7072 self .apps_v1 = None
73+ self .networking_v1 = None
7174 self .version_api = None
7275 self ._initialize_kubernetes_client ()
7376
@@ -112,7 +115,7 @@ async def graceful_shutdown(self) -> None:
112115 return
113116 execution_id = pod_name [len ("execution-" ):]
114117 config_map_name = f"script-{ execution_id } "
115- await self ._cleanup_resources (pod_name , config_map_name )
118+ await self ._cleanup_resources (pod_name , config_map_name , execution_id )
116119 except Exception as e :
117120 logger .error (f"Error during pod cleanup on shutdown: { str (e )} " )
118121
@@ -121,13 +124,15 @@ def _initialize_kubernetes_client(self) -> None:
121124 self ._setup_kubernetes_config ()
122125 self .v1 = k8s_client .CoreV1Api ()
123126 self .apps_v1 = k8s_client .AppsV1Api ()
127+ self .networking_v1 = k8s_client .NetworkingV1Api ()
124128 self .version_api = k8s_client .VersionApi ()
125129 self ._test_api_connection ()
126130 logger .info ("Kubernetes client initialized successfully." )
127131 except Exception as e :
128132 logger .error (f"Failed to initialize Kubernetes client: { str (e )} " )
129133 self .v1 = None
130134 self .apps_v1 = None
135+ self .networking_v1 = None
131136 self .version_api = None
132137 raise KubernetesConfigError (f"Failed to initialize Kubernetes client: { str (e )} " ) from e
133138
@@ -203,14 +208,18 @@ async def create_execution_pod(
203208 pod_manifest = builder .build ()
204209 await self ._create_namespaced_pod (pod_manifest )
205210
211+ policy_builder = NetworkPolicyBuilder (execution_id , self .NAMESPACE )
212+ policy_manifest = policy_builder .build ()
213+ await self ._create_network_policy (policy_manifest )
214+
206215 self ._active_pods [execution_id ] = datetime .now (timezone .utc )
207216 logger .info (f"Successfully created pod '{ pod_name } ' with image '{ image } '" )
208217 self .circuit_breaker .record_success ()
209218
210219 except Exception as e :
211220 logger .error (f"Failed to create execution pod '{ execution_id } ': { str (e )} " , exc_info = True )
212221 self .circuit_breaker .record_failure ()
213- await self ._cleanup_resources (pod_name , config_map_name )
222+ await self ._cleanup_resources (pod_name , config_map_name , execution_id )
214223 raise KubernetesPodError (f"Failed to create execution pod: { str (e )} " ) from e
215224
216225 async def get_pod_logs (self , execution_id : str ) -> tuple [dict , str ]:
@@ -237,7 +246,7 @@ async def get_pod_logs(self, execution_id: str) -> tuple[dict, str]:
237246 return error_payload , pod_phase
238247 finally :
239248 logger .info (f"Initiating cleanup for execution '{ execution_id } '..." )
240- await self ._cleanup_resources (pod_name , config_map_name )
249+ await self ._cleanup_resources (pod_name , config_map_name , execution_id )
241250 self ._active_pods .pop (execution_id , None )
242251
243252 async def _wait_for_pod_completion (self , pod_name : str ) -> k8s_client .V1Pod :
@@ -293,7 +302,7 @@ async def _create_namespaced_pod(self, pod_manifest: Dict[str, Any]) -> None:
293302 logger .error (f"Failed to create pod '{ pod_name } ': { e .status } { e .reason } " )
294303 raise KubernetesPodError (f"Failed to create pod: { str (e )} " ) from e
295304
296- async def _cleanup_resources (self , pod_name : str , config_map_name : str ) -> None :
305+ async def _cleanup_resources (self , pod_name : str , config_map_name : str , execution_id : Optional [ str ] = None ) -> None :
297306 if not self .v1 :
298307 return
299308 try :
@@ -309,6 +318,38 @@ async def _cleanup_resources(self, pod_name: str, config_map_name: str) -> None:
309318 except ApiException as e :
310319 logger .error (f"Failed to delete config map '{ config_map_name } ': { e .reason } " )
311320
321+ if execution_id :
322+ policy_name = f"deny-external-{ execution_id } "
323+ await self ._delete_network_policy (policy_name )
324+
325+ async def _create_network_policy (self , policy_manifest : Dict [str , Any ]) -> None :
326+ if not self .networking_v1 :
327+ raise KubernetesServiceError ("NetworkingV1Api client not initialized." )
328+ policy_name = policy_manifest .get ("metadata" , {}).get ("name" , "unknown-policy" )
329+ try :
330+ await asyncio .to_thread (
331+ self .networking_v1 .create_namespaced_network_policy ,
332+ body = policy_manifest ,
333+ namespace = self .NAMESPACE
334+ )
335+ logger .info (f"NetworkPolicy '{ policy_name } ' created successfully." )
336+ except ApiException as e :
337+ logger .error (f"Failed to create NetworkPolicy '{ policy_name } ': { e .status } { e .reason } " )
338+ raise KubernetesServiceError (f"Failed to create NetworkPolicy: { str (e )} " ) from e
339+
340+ async def _delete_network_policy (self , policy_name : str ) -> None :
341+ if not self .networking_v1 :
342+ return
343+ try :
344+ await asyncio .to_thread (
345+ self .networking_v1 .delete_namespaced_network_policy ,
346+ name = policy_name ,
347+ namespace = self .NAMESPACE
348+ )
349+ logger .info (f"Deletion request sent for NetworkPolicy '{ policy_name } '" )
350+ except ApiException as e :
351+ logger .error (f"Failed to delete NetworkPolicy '{ policy_name } ': { e .reason } " )
352+
312353 # DaemonSet: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/
313354 async def ensure_image_pre_puller_daemonset (self ) -> None :
314355 if not self .apps_v1 :
0 commit comments