[keadm ctl] keadm ctl get pod with -o json / -o yaml bypasses edge node filtering and outputs pods from all cluster nodes
What happened:
In keadm ctl get pod (keadm/cmd/keadm/app/cmd/ctl/get/pod.go), edge node filtering (pod.Spec.NodeName == nodeName) is only performed inside the table output format block:
if *o.PrintFlags.OutputFormat == "" || *o.PrintFlags.OutputFormat == "wide" {
podListFilter := &api.PodList{
Items: make([]api.Pod, 0, len(podList.Items)),
}
for _, pod := range podList.Items {
if pod.Spec.NodeName == nodeName {
var apiPod api.Pod
if err := k8s_v1_api.Convert_v1_Pod_To_core_Pod(&pod, &apiPod, nil); err != nil {
fmt.Printf("pod revert to apiPod with err:%v\n", err)
continue
}
podListFilter.Items = append(podListFilter.Items, apiPod)
}
}
table, err := ConvertDataToTable(podListFilter)
if err != nil {
return err
}
return o.PrintToTable(table, o.AllNamespaces, os.Stdout)
}
runtimeObjects := make([]runtime.Object, 0, len(podList.Items))
for _, pod := range podList.Items {
runtimeObjects = append(runtimeObjects, &pod)
}
return o.PrintToJSONYaml(runtimeObjects)When a user specifies -o json or -o yaml, the table formatting block is skipped. Execution falls through to lines 148–152, which iterate over podList.Items (the unfiltered cluster/namespace pod list returned by podRequest.GetPods(ctx)).
This leads to the following issues:
- Node Filtering Bypass in JSON/YAML Output:
keadm ctl get pod(table format) outputs only the pods running on the current edge node, butkeadm ctl get pod -o jsonorkeadm ctl get pod -o yamloutputs pods from all nodes across the queried namespace (or the entire cluster if-Ais used). - Incorrect Empty-State / "No resources found" Check: The check
if len(podList.Items) == 0(line 115) runs on the rawpodListbefore filtering bynodeName. If the namespace contains pods scheduled on other nodes, but zero pods on the local edge node:len(podList.Items) == 0isfalse.- In table format,
podListFilter.Itemsis empty, causingkeadm ctl get podto print an empty table header with 0 rows, instead of reporting"No resources found in <namespace> namespace.". - In JSON/YAML format, it outputs the pods from the other nodes.
- Inconsistent Behavior Between Querying Specific Pods vs Listing: When pod arguments are provided (
len(args) > 0, lines 81–102),podListis filtered bynodeName. But when listing all pods (len(args) == 0),podListis unfiltered, causing inconsistent behavior between commands likekeadm ctl get pod <name> -o jsonandkeadm ctl get pod -o json.
In contrast, keadm ctl get device (keadm/cmd/keadm/app/cmd/ctl/get/device.go:112–143) and keadm ctl describe pod (keadm/cmd/keadm/app/cmd/ctl/describe/pod.go:132–157) correctly filter into a filtered slice first, check emptiness against the filtered slice, and format both table and JSON/YAML outputs from the filtered slice.
What you expected to happen:
keadm ctl get pod -o jsonandkeadm ctl get pod -o yamlshould only return pods running on the current edge node (pod.Spec.NodeName == nodeName), consistent with the table format and withkeadm ctl get device.- When an edge node has no pods in the namespace,
keadm ctl get podshould print"No resources found in <namespace> namespace."instead of an empty table header.
How to reproduce it (as minimally and precisely as possible):
- On a cluster with an edge node
edge-node-1and another nodeedge-node-2(or cloud master): - Deploy a pod
pod-otherscheduled onedge-node-2, and a podpod-localscheduled onedge-node-1in thedefaultnamespace. - On
edge-node-1, run default table view:Result: Displays onlykeadm ctl get pod -n defaultpod-local. - Now run with JSON output:Result: Both
keadm ctl get pod -n default -o jsonpod-localandpod-otherare printed in the JSON output, ignoring the edge node filter. - If
edge-node-1has no pods butedge-node-2does:Result: Displays empty table headers with 0 rows instead ofkeadm ctl get pod -n default"No resources found in default namespace.".
Anything else we need to know?:
Comparison with keadm/cmd/keadm/app/cmd/ctl/get/device.go:
deviceListFilter = &v1beta1.DeviceList{
Items: make([]v1beta1.Device, 0, len(deviceList.Items)),
}
for _, device := range deviceList.Items {
if device.Spec.NodeName == nodeName {
deviceListFilter.Items = append(deviceListFilter.Items, device)
}
}
}
if len(deviceListFilter.Items) == 0 {
if len(args) > 0 {
return nil
}
if o.AllNamespaces {
klog.Info("No resources found in all namespaces.")
} else {
klog.Infof("No resources found in %s namespace.", o.Namespace)
}
return nil
}
if *o.PrintFlags.OutputFormat == "" || *o.PrintFlags.OutputFormat == "wide" {
return o.PrintToTable(deviceListFilter, o.AllNamespaces, os.Stdout)
}
runtimeObjects := make([]runtime.Object, 0, len(deviceListFilter.Items))
for _, device := range deviceListFilter.Items {
runtimeObjects = append(runtimeObjects, &device)
}
return o.PrintToJSONYaml(runtimeObjects)Proposed fix for keadm/cmd/keadm/app/cmd/ctl/get/pod.go:
--- a/keadm/cmd/keadm/app/cmd/ctl/get/pod.go
+++ b/keadm/cmd/keadm/app/cmd/ctl/get/pod.go
@@ -77,10 +77,10 @@ func (o *PodGetOptions) getPods(args []string) error {
return err
}
- var podList *v1.PodList
+ var podListFilter *v1.PodList
if len(args) > 0 {
- podList = &v1.PodList{
+ podListFilter = &v1.PodList{
Items: make([]v1.Pod, 0, len(args)),
}
var podRequest *client.PodRequest
@@ -95,9 +95,9 @@ func (o *PodGetOptions) getPods(args []string) error {
}
if pod.Spec.NodeName == nodeName {
- podList.Items = append(podList.Items, *pod)
+ podListFilter.Items = append(podListFilter.Items, *pod)
} else {
- fmt.Printf("can't to query pod: \"%s\" for node: \"%s\"\n", pod.Name, pod.Spec.NodeName)
+ fmt.Printf("can't query pod: \"%s\" for node: \"%s\"\n", pod.Name, pod.Spec.NodeName)
}
}
} else {
@@ -106,47 +106,56 @@ func (o *PodGetOptions) getPods(args []string) error {
AllNamespaces: o.AllNamespaces,
LabelSelector: o.LabelSelector,
}
- podList, err = podRequest.GetPods(ctx)
+ podList, err := podRequest.GetPods(ctx)
if err != nil {
return err
}
+
+ podListFilter = &v1.PodList{
+ Items: make([]v1.Pod, 0, len(podList.Items)),
+ }
+ for _, pod := range podList.Items {
+ if pod.Spec.NodeName == nodeName {
+ podListFilter.Items = append(podListFilter.Items, pod)
+ }
+ }
}
- if len(podList.Items) == 0 {
+ if len(podListFilter.Items) == 0 {
if len(args) > 0 {
return nil
}
if o.AllNamespaces {
- fmt.Println("No resources found in all namespace.")
+ fmt.Println("No resources found in all namespaces.")
} else {
fmt.Printf("No resources found in %s namespace.\n", o.Namespace)
}
return nil
}
if *o.PrintFlags.OutputFormat == "" || *o.PrintFlags.OutputFormat == "wide" {
- podListFilter := &api.PodList{
- Items: make([]api.Pod, 0, len(podList.Items)),
+ corePodList := &api.PodList{
+ Items: make([]api.Pod, 0, len(podListFilter.Items)),
}
- for _, pod := range podList.Items {
- if pod.Spec.NodeName == nodeName {
- var apiPod api.Pod
- if err := k8s_v1_api.Convert_v1_Pod_To_core_Pod(&pod, &apiPod, nil); err != nil {
- fmt.Printf("pod revert to apiPod with err:%v\n", err)
- continue
- }
- podListFilter.Items = append(podListFilter.Items, apiPod)
+ for i := range podListFilter.Items {
+ var apiPod api.Pod
+ if err := k8s_v1_api.Convert_v1_Pod_To_core_Pod(&podListFilter.Items[i], &apiPod, nil); err != nil {
+ fmt.Printf("pod revert to apiPod with err:%v\n", err)
+ continue
}
+ corePodList.Items = append(corePodList.Items, apiPod)
}
- table, err := ConvertDataToTable(podListFilter)
+ table, err := ConvertDataToTable(corePodList)
if err != nil {
return err
}
return o.PrintToTable(table, o.AllNamespaces, os.Stdout)
}
- runtimeObjects := make([]runtime.Object, 0, len(podList.Items))
- for _, pod := range podList.Items {
- runtimeObjects = append(runtimeObjects, &pod)
+ runtimeObjects := make([]runtime.Object, 0, len(podListFilter.Items))
+ for i := range podListFilter.Items {
+ runtimeObjects = append(runtimeObjects, &podListFilter.Items[i])
}
return o.PrintToJSONYaml(runtimeObjects)
}I would be happy to submit a pull request fixing this issue if the maintainers agree with the proposed solution!
Environment:
- Kubernetes version (use
kubectl version):v1.30.0 - KubeEdge version(e.g.
cloudcore --versionandedgecore --version):v1.20.0-alpha.0(commit62940139aacb211a98c3b55cf7229848812a0755)
Source: kubeedge/kubeedge