11from fastapi import APIRouter , HTTPException
22from kubernetes import client
3+ import re
34
45router = APIRouter ()
56
7+ def find_pod_dependencies (pod , all_pods , services , namespace , core_v1_api ):
8+ """
9+ Analyze pod dependencies by checking:
10+ 1. Environment variables pointing to other services
11+ 2. Service selectors matching pod labels
12+ 3. ConfigMaps and Secrets that might reference other pods
13+ 4. Init containers waiting for remote services
14+ """
15+ dependencies = []
16+
17+ # Get pod environment variables from main containers
18+ env_vars = []
19+ configmap_refs = []
20+
21+ if pod .spec .containers :
22+ for container in pod .spec .containers :
23+ # Direct environment variables
24+ if container .env :
25+ for env in container .env :
26+ if env .value :
27+ env_vars .append (env .value )
28+
29+ # Environment variables from ConfigMaps
30+ if container .env_from :
31+ for env_from in container .env_from :
32+ if env_from .config_map_ref :
33+ configmap_refs .append (env_from .config_map_ref .name )
34+
35+ # Get environment variables from init containers (for wait-for-remote-service patterns)
36+ init_env_vars = []
37+
38+ if pod .spec .init_containers :
39+ for init_container in pod .spec .init_containers :
40+ # Direct environment variables from init containers
41+ if init_container .env :
42+ for env in init_container .env :
43+ if env .value :
44+ init_env_vars .append (env .value )
45+
46+ # Environment variables from ConfigMaps in init containers
47+ if init_container .env_from :
48+ for env_from in init_container .env_from :
49+ if env_from .config_map_ref and env_from .config_map_ref .name not in configmap_refs :
50+ configmap_refs .append (env_from .config_map_ref .name )
51+
52+ # Check init container commands for wait-for-remote-service patterns
53+ if init_container .name == "wait-for-remote-service" or "wait-for" in init_container .name .lower ():
54+ if init_container .command :
55+ command_str = " " .join (init_container .command )
56+ # Look for service endpoints in commands like "nc -z -v -w30 $HEALTHCHECK_ENDPOINT"
57+ # Extract service names from HEALTHCHECK_ENDPOINT or similar patterns
58+ for env_var in init_env_vars :
59+ # Pattern like "tei-0:9003" or "redis-vector-store-0:9001"
60+ service_match = re .search (r'([a-zA-Z0-9-]+):\d+' , env_var )
61+ if service_match :
62+ service_name = service_match .group (1 )
63+ # Find pods that this service targets
64+ for service in services .items :
65+ if service .metadata .name == service_name and service .spec .selector :
66+ for target_pod in all_pods .items :
67+ if target_pod .metadata .name != pod .metadata .name :
68+ target_labels = target_pod .metadata .labels or {}
69+ if all (target_labels .get (k ) == v for k , v in service .spec .selector .items ()):
70+ if target_pod .metadata .name not in dependencies :
71+ dependencies .append (target_pod .metadata .name )
72+
73+ # Fetch ConfigMap data to analyze environment variables
74+ configmap_env_vars = []
75+ for configmap_name in configmap_refs :
76+ try :
77+ configmap = core_v1_api .read_namespaced_config_map (name = configmap_name , namespace = namespace )
78+ if configmap .data :
79+ for key , value in configmap .data .items ():
80+ if value :
81+ configmap_env_vars .append (value )
82+ except Exception as e :
83+ print (f"Error fetching ConfigMap { configmap_name } : { str (e )} " )
84+
85+ # Combine all environment variables for further analysis
86+ all_env_vars = env_vars + init_env_vars + configmap_env_vars
87+
88+ # Debug output
89+ print (f"Analyzing dependencies for pod: { pod .metadata .name } " )
90+ print (f"ConfigMap refs: { configmap_refs } " )
91+ print (f"Total env vars found: { len (all_env_vars )} " )
92+ if all_env_vars :
93+ print (f"Sample env vars: { all_env_vars [:3 ]} " ) # Show first 3 for debugging
94+
95+ # Check if environment variables reference other services
96+ for service in services .items :
97+ service_name = service .metadata .name
98+ # Check if service name is referenced in environment variables
99+ for env_val in all_env_vars :
100+ if service_name in env_val :
101+ # Find pods that this service targets
102+ if service .spec .selector :
103+ for target_pod in all_pods .items :
104+ if target_pod .metadata .name != pod .metadata .name :
105+ target_labels = target_pod .metadata .labels or {}
106+ if all (target_labels .get (k ) == v for k , v in service .spec .selector .items ()):
107+ if target_pod .metadata .name not in dependencies :
108+ dependencies .append (target_pod .metadata .name )
109+
110+ # Check for service endpoint patterns in environment variables
111+ # Pattern like "http://service-name:port" or "service-name:port"
112+ for env_val in all_env_vars :
113+ # HTTP URL pattern
114+ http_matches = re .findall (r'https?://([a-zA-Z0-9-]+):\d+' , env_val )
115+ for service_name in http_matches :
116+ for service in services .items :
117+ if service .metadata .name == service_name and service .spec .selector :
118+ for target_pod in all_pods .items :
119+ if target_pod .metadata .name != pod .metadata .name :
120+ target_labels = target_pod .metadata .labels or {}
121+ if all (target_labels .get (k ) == v for k , v in service .spec .selector .items ()):
122+ if target_pod .metadata .name not in dependencies :
123+ dependencies .append (target_pod .metadata .name )
124+
125+ # Direct service:port pattern
126+ service_port_matches = re .findall (r'([a-zA-Z0-9-]+):\d+' , env_val )
127+ for service_name in service_port_matches :
128+ for service in services .items :
129+ if service .metadata .name == service_name and service .spec .selector :
130+ for target_pod in all_pods .items :
131+ if target_pod .metadata .name != pod .metadata .name :
132+ target_labels = target_pod .metadata .labels or {}
133+ if all (target_labels .get (k ) == v for k , v in service .spec .selector .items ()):
134+ if target_pod .metadata .name not in dependencies :
135+ dependencies .append (target_pod .metadata .name )
136+
137+ # Check DNS-based dependencies (common pattern: service-name.namespace.svc.cluster.local)
138+ dns_pattern = r'([a-zA-Z0-9-]+)\.(' + re .escape (namespace ) + r')\.svc\.cluster\.local'
139+ for env_val in all_env_vars :
140+ matches = re .findall (dns_pattern , env_val )
141+ for match in matches :
142+ service_name = match [0 ]
143+ # Find the service and its target pods
144+ for service in services .items :
145+ if service .metadata .name == service_name and service .spec .selector :
146+ for target_pod in all_pods .items :
147+ if target_pod .metadata .name != pod .metadata .name :
148+ target_labels = target_pod .metadata .labels or {}
149+ if all (target_labels .get (k ) == v for k , v in service .spec .selector .items ()):
150+ if target_pod .metadata .name not in dependencies :
151+ dependencies .append (target_pod .metadata .name )
152+
153+ # Additional pattern matching for service names that might not include ports
154+ # Look for patterns where service names appear in environment variable values
155+ service_names = [service .metadata .name for service in services .items ]
156+ for env_val in all_env_vars :
157+ for service_name in service_names :
158+ # More flexible matching - look for service name as whole word
159+ if re .search (r'\b' + re .escape (service_name ) + r'\b' , env_val ):
160+ for service in services .items :
161+ if service .metadata .name == service_name and service .spec .selector :
162+ for target_pod in all_pods .items :
163+ if target_pod .metadata .name != pod .metadata .name :
164+ target_labels = target_pod .metadata .labels or {}
165+ if all (target_labels .get (k ) == v for k , v in service .spec .selector .items ()):
166+ if target_pod .metadata .name not in dependencies :
167+ dependencies .append (target_pod .metadata .name )
168+
169+ return dependencies
170+
6171@router .get ("/podlogs/{namespace}" , summary = "Fetch all pods in a namespace" )
7172async def get_all_pods_in_namespace (namespace : str ):
8173 core_v1_api = client .CoreV1Api ()
@@ -11,6 +176,13 @@ async def get_all_pods_in_namespace(namespace: str):
11176 if not pods .items :
12177 return {"namespace" : namespace , "pods" : []}
13178
179+ # Fetch all services in the namespace for dependency analysis
180+ try :
181+ services = core_v1_api .list_namespaced_service (namespace = namespace )
182+ except Exception as e :
183+ print (f"Error fetching services: { str (e )} " )
184+ services = None
185+
14186 pod_list = []
15187 for pod in pods .items :
16188 pod_name = pod .metadata .name
@@ -22,10 +194,14 @@ async def get_all_pods_in_namespace(namespace: str):
22194 # Fetch logs related to the pod
23195 try :
24196 pod_logs = core_v1_api .read_namespaced_pod_log (name = pod_name , namespace = namespace , tail_lines = 200 )
25- for line in pod_logs .splitlines ():
26- log_entries .append (line )
197+ if pod_logs and pod_logs .strip ():
198+ for line in pod_logs .splitlines ():
199+ log_entries .append (line )
200+ else :
201+ log_entries .append ("** Pod has no logs available" )
27202 except Exception as e :
28203 print (f"Error fetching logs: { str (e )} " )
204+ log_entries .append ("Unable to fetch logs: Pod may not be running or logs are not accessible" )
29205
30206 # Fetch events related to the pod
31207 try :
@@ -38,6 +214,17 @@ async def get_all_pods_in_namespace(namespace: str):
38214 except Exception as e :
39215 print (f"Error fetching events: { str (e )} " )
40216
217+ # Analyze pod dependencies
218+ dependencies = []
219+ if services :
220+ try :
221+ dependencies = find_pod_dependencies (pod , pods , services , namespace , core_v1_api )
222+ print (f"Pod { pod_name } dependencies: { dependencies } " )
223+ except Exception as e :
224+ print (f"Error analyzing dependencies for pod { pod_name } : { str (e )} " )
225+ import traceback
226+ traceback .print_exc ()
227+
41228 # Determine the Ready and Status of the pod
42229 ready_status = "Unknown"
43230 pod_status = pod .status .phase
@@ -63,6 +250,7 @@ async def get_all_pods_in_namespace(namespace: str):
63250 "annotations" : pod .metadata .annotations ,
64251 "logs" : log_entries ,
65252 "events" : event_entries ,
253+ "dependencies" : dependencies ,
66254 })
67255
68256 return {"namespace" : namespace , "pods" : pod_list }
0 commit comments