@@ -3,24 +3,30 @@ require "kernel_introspection"
33module KernelIntrospection
44 module K8s
55 module Node
6- def self.pids (node )
6+ def self.pids (node ) : Array ( String )
77 Log .info { " pids" }
88 ls_proc = ClusterTools .exec_by_node(" ls /proc/" , node)
9+ unless ls_proc[:status ].success?
10+ return [] of String
11+ end
912 Log .info { " pids ls_proc: #{ ls_proc } " }
1013 parsed_ls = KernelIntrospection .parse_ls(ls_proc[:output ])
1114 pids = KernelIntrospection .pids_from_ls_proc(parsed_ls)
1215 pids
1316 end
1417
15- def self.pids_by_container (container_id, node)
18+ def self.pids_by_container (container_id, node) : Array ( String )
1619 # Command explanation:
1720 # 1. Get all process directories in /proc with find.
18- # 2. Exec grep <container_id> for every returned process cgroup file,
21+ # 2. Exec grep <container_id> for every returned process cgroup file,
1922 # redirect any errors (in case some temporary process disappears/find cannot access a directory)
2023 # 3. Grep returns paths with desired cgroup in format /proc/<pid>/cgroup, this output gets trimmed
2124 # by sed to only return a list of <pid>s
2225 command = " /bin/sh -c \" find /proc -maxdepth 1 -regex '/proc/[0-9]+' -exec grep -l '#{ container_id } ' {}/cgroup \\ ; 2>/dev/null | sed -e 's,/proc/\\ ([0-9]*\\ )/cgroup,\\ 1,'\" "
2326 result = ClusterTools .exec_by_node(command, node)
27+ unless result[:status ].success?
28+ return [] of String
29+ end
2430 output = result[" output" ].strip
2531
2632 pids = output.split(" \n " )
@@ -32,8 +38,9 @@ module KernelIntrospection
3238 proc_statuses = pids.map do |pid |
3339 Log .info { " all_statuses_by_pids pid: #{ pid } " }
3440 proc_status = ClusterTools .exec_by_node(" cat /proc/#{ pid } /status" , node)
35- proc_status[:output ]
36- end
41+ # if /proc/#{pid}/status cannot be read it means that the process is no longer available
42+ proc_status[:output ] if proc_status[:status ].success?
43+ end .compact
3744
3845 Log .debug { " proc process_statuses_by_node: #{ proc_statuses } " }
3946 proc_statuses
@@ -46,11 +53,11 @@ module KernelIntrospection
4653 status[:output ]
4754 end
4855
49- def self.cmdline_by_pid (pid : String , node)
56+ def self.cmdline_by_pid (pid : String , node)
5057 Log .info { " cmdline_by_pid" }
5158 cmdline = ClusterTools .exec_by_node(" cat /proc/#{ pid } /cmdline" , node)
5259 Log .info { " cmdline_by_node cmdline: #{ cmdline } " }
53- cmdline
60+ cmdline
5461 end
5562
5663 def self.verify_single_proc_tree (original_parent_pid, name, proctree : Array (Hash (String , String )), excluded_processes = [] of String )
@@ -62,28 +69,27 @@ module KernelIntrospection
6269 status_name = " #{ pt[" Name" ] } " .strip
6370
6471 if current_pid == original_parent_pid && ppid != " " &&
65- status_name != name
72+ status_name != name
6673 if excluded_processes.includes?(status_name)
6774 next
6875 end
6976 Log .info { " top level parent (i.e. superviser -- first parent with different name): #{ status_name } " }
7077 verified = false
71-
7278 elsif current_pid == original_parent_pid && ppid != " " &&
73- status_name == name
79+ status_name == name
7480 verified = verify_single_proc_tree(ppid, name, proctree, excluded_processes)
7581 end
7682 end
7783 Log .info { " verified?: #{ verified } " }
7884 verified
7985 end
8086
81- def self.proctree_by_pid (potential_parent_pid : String , node : JSON ::Any , proc_statuses : (Array (String ) | Nil ) = nil ) : Array (Hash (String , String )) # array of status hashes
87+ def self.proctree_by_pid (potential_parent_pid : String , node : JSON ::Any , proc_statuses : (Array (String ) | Nil ) = nil ) : Array (Hash (String , String )) # array of status hashes
8288 Log .for(" proctree_by_pid" ).info { " proctree_by_pid potential_parent_pid: #{ potential_parent_pid } " }
8389 proctree = [] of Hash (String , String )
8490 potential_parent_status : Hash (String , String ) | Nil = nil
8591 unless proc_statuses
86- pids = pids(node)
92+ pids = pids(node)
8793 Log .for(" proctree_by_pid" ).debug { " pids: #{ pids } " }
8894 proc_statuses = all_statuses_by_pids(pids, node)
8995 end
@@ -92,7 +98,7 @@ module KernelIntrospection
9298 parsed_status = KernelIntrospection .parse_status(proc_status)
9399 Log .for(" proctree_by_pid" ).debug { " parsed_status: #{ parsed_status } " }
94100 if parsed_status
95- ppid = parsed_status[" PPid" ].strip
101+ ppid = parsed_status[" PPid" ].strip
96102 current_pid = parsed_status[" Pid" ].strip
97103 Log .for(" proctree_by_pid" ).debug(& .emit(
98104 potential_parent_pid: potential_parent_pid,
@@ -107,9 +113,9 @@ module KernelIntrospection
107113 current_pid: current_pid,
108114 cmdline: cmdline
109115 ))
110- potential_parent_status = parsed_status.merge({" cmdline" => cmdline})
116+ potential_parent_status = parsed_status.merge({" cmdline" => cmdline})
111117 proctree << potential_parent_status
112- # Add descendants of the parent pid
118+ # Add descendants of the parent pid
113119 elsif ppid == potential_parent_pid && ppid != current_pid
114120 Log .for(" proctree_by_pid" ).debug(& .emit(
115121 " proctree_by_pid ppid == pid && ppid != current_pid" ,
@@ -131,25 +137,37 @@ module KernelIntrospection
131137 end
132138 end
133139
134- def self.proc (pod_name, container_name, namespace : String | Nil = nil )
140+ def self.proc (pod_name, container_name, namespace : String ?) : Array ( Int32 )
135141 Log .info { " proc namespace: #{ namespace } " }
136- # todo if container_name nil, dont use container (assume one container)
137- resp = KubectlClient .exec(" -ti #{ pod_name } --container #{ container_name } -- ls /proc/" , namespace: namespace)
138- KernelIntrospection .parse_proc(resp[:output ].to_s)
142+ begin
143+ resp = KubectlClient ::Utils .exec(pod_name.to_s, " ls /proc/" , container_name: container_name, namespace: namespace)
144+ rescue ex : KubectlClient ::ShellCMD ::K8sClientCMDException
145+ Log .warn { " Exception rescued: #{ ex.message } " }
146+ return [] of Int32
147+ end
148+ KernelIntrospection .parse_proc(resp[:output ])
139149 end
140150
141151 def self.cmdline (pod_name, container_name, pid, namespace : String | Nil = nil )
142152 Log .info { " cmdline namespace: #{ namespace } " }
143- # todo if container_name nil, dont use container (assume one container)
144- resp = KubectlClient .exec(" -ti #{ pod_name } --container #{ container_name } -- cat /proc/#{ pid } /cmdline" , namespace: namespace)
145- resp[:output ].to_s.strip
153+ begin
154+ resp = KubectlClient ::Utils .exec(pod_name.to_s, " cat /proc/#{ pid } /cmdline" , container_name: container_name, namespace: namespace)
155+ rescue ex : KubectlClient ::ShellCMD ::K8sClientCMDException
156+ Log .warn { " Exception rescued: #{ ex.message } " }
157+ return " "
158+ end
159+ resp[:output ].strip
146160 end
147161
148162 def self.status (pod_name, container_name, pid, namespace : String | Nil = nil )
149- # todo if container_name nil, dont use container (assume one container)
150163 Log .info { " status namespace: #{ namespace } " }
151- resp = KubectlClient .exec(" -ti #{ pod_name } --container #{ container_name } -- cat /proc/#{ pid } /status" , namespace: namespace)
152- KernelIntrospection .parse_status(resp[:output ].to_s)
164+ begin
165+ resp = KubectlClient ::Utils .exec(pod_name.to_s, " cat /proc/#{ pid } /status" , container_name: container_name, namespace: namespace)
166+ rescue ex : KubectlClient ::ShellCMD ::K8sClientCMDException
167+ Log .warn { " Exception rescued: #{ ex.message } " }
168+ return nil
169+ end
170+ KernelIntrospection .parse_status(resp[:output ])
153171 end
154172
155173 def self.status_by_proc (pod_name, container_name, namespace : String | Nil = nil )
@@ -160,15 +178,13 @@ module KernelIntrospection
160178 }.compact
161179 end
162180
163-
164181 alias MatchingProcessInfo = NamedTuple (
165182 node: JSON ::Any ,
166183 pod: JSON ::Any ,
167- container_status: JSON ::Any ,
184+ container_status: JSON ::Any ,
168185 status: String ,
169186 pid: String ,
170- cmdline: String
171- )
187+ cmdline: String )
172188
173189 # #todo overload with regex
174190 def self.find_first_process (process_name ) : (MatchingProcessInfo | Nil )
@@ -180,7 +196,7 @@ module KernelIntrospection
180196 pods.map do |pod |
181197 status = pod[" status" ]
182198 if status[" containerStatuses" ]?
183- container_statuses = status[" containerStatuses" ].as_a
199+ container_statuses = status[" containerStatuses" ].as_a
184200 Log .debug { " container_statuses: #{ container_statuses } " }
185201 container_statuses.map do |container_status |
186202 ready = container_status.dig(" ready" ).as_bool
@@ -200,7 +216,7 @@ module KernelIntrospection
200216 if process[:output ] =~ /#{ process_name } /
201217 ret = {node: node, pod: pod, container_status: container_status, status: status[:output ], pid: pid.to_s, cmdline: process[:output ]}
202218 Log .for(" find_first_process" ).info { " status found: #{ ret } " }
203- break
219+ break
204220 end
205221 end
206222 end
@@ -241,14 +257,13 @@ module KernelIntrospection
241257 result = {node: node, pod: pod, container_status: container_status, status: status[:output ], pid: pid.to_s, cmdline: process[:output ]}
242258 results.push(result)
243259 Log .for(" find_matching_processes" ).info { " status found: #{ result } " }
244- # break
260+ # break
245261 end
246262 end
247263 end
248264 end
249265 end
250266 results
251267 end
252-
253268 end
254269end
0 commit comments