Skip to content

Commit fae35b1

Browse files
authored
Updates according to cnf-testsuite/kubectl_client#17 (#5)
Updates according to cnf-testsuite/kubectl_client#17 (#5) Signed-off-by: Rafal Lal <[email protected]>
1 parent b7e9d7a commit fae35b1

File tree

4 files changed

+64
-51
lines changed

4 files changed

+64
-51
lines changed

shard.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: 2.0
22
shards:
33
cluster_tools:
44
git: https://github.com/cnf-testsuite/cluster_tools.git
5-
version: 1.0.0
5+
version: 1.0.11
66

77
docker_client:
88
git: https://github.com/cnf-testsuite/docker_client.git
@@ -14,7 +14,7 @@ shards:
1414

1515
kubectl_client:
1616
git: https://github.com/cnf-testsuite/kubectl_client.git
17-
version: 1.0.1
17+
version: 1.0.8
1818

1919
popcorn:
2020
git: https://github.com/icyleaf/popcorn.git

shard.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ dependencies:
2020
version: ~> 0.1.0
2121
kubectl_client:
2222
github: cnf-testsuite/kubectl_client
23-
version: ~> 1.0.0
23+
version: ~> 1.0.8
2424
cluster_tools:
2525
github: cnf-testsuite/cluster_tools
26-
version: ~> 1.0.0
26+
version: ~> 1.0.11
2727

spec/kernel_introspection/k8s_spec.cr

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ require "../../src/kernel_introspection/k8s.cr"
66
describe "KernelIntrospection::K8s" do
77
before_all do
88
begin
9-
KubectlClient::Create.namespace("cnf-testsuite")
10-
rescue e : KubectlClient::Create::AlreadyExistsError
9+
KubectlClient::Apply.namespace("cnf-testsuite")
10+
rescue e : KubectlClient::ShellCMD::AlreadyExistsError
1111
end
1212
ClusterTools.install
1313
end
1414

1515
it "'#status_by_proc' should return all statuses for all containers in a pod", tags: ["status_by_proc"] do
16-
result = KubectlClient::ShellCmd.run("kubectl run nginx --image=nginx --labels='name=nginx'", "kubectl_run_nginx", force_output=true)
16+
result = KubectlClient::ShellCMD.run("kubectl run nginx --image=nginx --labels='name=nginx'")
1717
pods = KubectlClient::Get.pods_by_nodes(KubectlClient::Get.schedulable_nodes_list)
1818
pods.should_not be_nil
19-
pods = KubectlClient::Get.pods_by_label(pods, "name", "nginx")
19+
pods = KubectlClient::Get.pods_by_labels(pods, {"name" => "nginx"})
2020
pods.should_not be_nil
2121

22-
KubectlClient::Get.resource_wait_for_install("pod", "nginx")
22+
KubectlClient::Wait.resource_wait_for_install("pod", "nginx")
2323
pods.size.should be > 0
2424
first_node = pods[0]
2525
statuses = KernelIntrospection::K8s.status_by_proc(first_node.dig("metadata", "name"), "nginx")
@@ -28,32 +28,30 @@ describe "KernelIntrospection::K8s" do
2828

2929
(statuses.find{|x| x["cmdline"].includes?("nginx: master process")} ).should_not be_nil
3030

31-
KubectlClient::Delete.command("pod/nginx")
31+
KubectlClient::Delete.resource("pod", "nginx")
3232
end
3333

3434
it "'#find_first_process' should return first matching process", tags: ["find_first_proc"] do
35-
result = KubectlClient::ShellCmd.run("kubectl run nginx --image=nginx --labels='name=nginx'", "kubectl_run_nginx", force_output=true)
36-
KubectlClient::Get.resource_wait_for_install("pod", "nginx")
35+
result = KubectlClient::ShellCMD.run("kubectl run nginx --image=nginx --labels='name=nginx'")
36+
KubectlClient::Wait.resource_wait_for_install("pod", "nginx")
3737
begin
38-
KubectlClient::ShellCmd.run("kubectl get pods", "kubectl_get_pods",force_output=true)
3938
pod_info = KernelIntrospection::K8s.find_first_process("nginx: master process")
4039
Log.info { "pod_info: #{pod_info}"}
4140
(pod_info).should_not be_nil
4241
ensure
43-
KubectlClient::Delete.command("pod/nginx")
42+
KubectlClient::Delete.resource("pod", "nginx")
4443
end
4544
end
4645

4746
it "'#find_matching_processes' should return all matching processes", tags: ["find_first_proc"] do
48-
result = KubectlClient::ShellCmd.run("kubectl run nginx --image=nginx --labels='name=nginx'", "kubectl_run_nginx", force_output=true)
49-
KubectlClient::Get.resource_wait_for_install("pod", "nginx")
50-
KubectlClient::ShellCmd.run("kubectl get pods", "kubectl_get_pods",force_output=true)
47+
result = KubectlClient::ShellCMD.run("kubectl run nginx --image=nginx --labels='name=nginx'")
48+
KubectlClient::Wait.resource_wait_for_install("pod", "nginx")
5149
begin
5250
pods_info = KernelIntrospection::K8s.find_matching_processes("nginx")
5351
Log.info { "pods_info: #{pods_info}"}
5452
(pods_info).size.should be > 0
5553
ensure
56-
KubectlClient::Delete.command("pod/nginx")
54+
KubectlClient::Delete.resource("pod", "nginx")
5755
end
5856
end
5957

src/kernel_introspection/k8s.cr

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,30 @@ require "kernel_introspection"
33
module 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
254269
end

0 commit comments

Comments
 (0)