From b64ec3ecafa55c8f1114072911fdd3a411b81b9a Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Wed, 18 Sep 2024 06:45:49 +0100 Subject: [PATCH] [v14] [kube] support filtering for entire objects in tables (#46696) * [kube] support filtering for entire objects in tables This PR fixes a bug where Teleport incorrectly returned an error when ordering resources by some field because the returned table included the full object instead of the PartialMetadataObject. This PR extends support for ordering fields by a field that isn't present in default table view. * add tests --- lib/kube/proxy/resource_filters.go | 23 +++- lib/kube/proxy/resource_filters_test.go | 20 ++++ .../testing/data/partial_table_full_obj.json | 102 ++++++++++++++++++ 3 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 lib/kube/proxy/testing/data/partial_table_full_obj.json diff --git a/lib/kube/proxy/resource_filters.go b/lib/kube/proxy/resource_filters.go index db5444e4874e1..1db951d2a8344 100644 --- a/lib/kube/proxy/resource_filters.go +++ b/lib/kube/proxy/resource_filters.go @@ -689,19 +689,32 @@ func (d *resourceFilterer) filterMetaV1Table(table *metav1.Table, allowedResourc return table, nil } -// getKubeResourcePartialMetadataObject checks if obj is of type *metav1.PartialObjectMetadata +// getKubeResourcePartialMetadataObject checks if obj satisfies namespaceNamer or namer interfaces // otherwise returns an error. func getKubeResourcePartialMetadataObject(kind, verb string, obj runtime.Object) (types.KubernetesResource, error) { + type namer interface { + GetName() string + } + type namespaceNamer interface { + GetNamespace() string + namer + } switch o := obj.(type) { - case *metav1.PartialObjectMetadata: + case namespaceNamer: return types.KubernetesResource{ - Namespace: o.Namespace, - Name: o.Name, + Namespace: o.GetNamespace(), + Name: o.GetName(), Kind: kind, Verbs: []string{verb}, }, nil + case namer: + return types.KubernetesResource{ + Name: o.GetName(), + Kind: kind, + Verbs: []string{verb}, + }, nil default: - return types.KubernetesResource{}, trace.BadParameter("expected *metav1.PartialObjectMetadata object, got %T", obj) + return types.KubernetesResource{}, trace.BadParameter("unexpected %T type", obj) } } diff --git a/lib/kube/proxy/resource_filters_test.go b/lib/kube/proxy/resource_filters_test.go index d8f5ada4ca61b..2ad1e58fba05f 100644 --- a/lib/kube/proxy/resource_filters_test.go +++ b/lib/kube/proxy/resource_filters_test.go @@ -120,6 +120,26 @@ func Test_filterBuffer(t *testing.T) { "default/kubernetes", }, }, + { + name: "table response full object compressed with gzip", + args: args{ + dataFile: "testing/data/partial_table_full_obj.json", + contentEncoding: "gzip", + }, + want: []string{ + "default/kubernetes", + }, + }, + { + name: "table response full object uncompressed", + args: args{ + dataFile: "testing/data/partial_table_full_obj.json", + contentEncoding: "", + }, + want: []string{ + "default/kubernetes", + }, + }, } for _, tt := range tests { for _, r := range types.KubernetesResourcesKinds { diff --git a/lib/kube/proxy/testing/data/partial_table_full_obj.json b/lib/kube/proxy/testing/data/partial_table_full_obj.json new file mode 100644 index 0000000000000..b4cc80a8fb1f9 --- /dev/null +++ b/lib/kube/proxy/testing/data/partial_table_full_obj.json @@ -0,0 +1,102 @@ +{ + "kind": "Table", + "apiVersion": "meta.k8s.io/v1", + "metadata": { + "resourceVersion": "98657" + }, + "columnDefinitions": [ + { + "name": "Name", + "type": "string", + "format": "name", + "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "priority": 0 + }, + { + "name": "Ready", + "type": "string", + "format": "", + "description": "The aggregate readiness state of this pod for accepting traffic.", + "priority": 0 + }, + { + "name": "Status", + "type": "string", + "format": "", + "description": "The aggregate status of the containers in this pod.", + "priority": 0 + }, + { + "name": "Restarts", + "type": "string", + "format": "", + "description": "The number of times the containers in this pod have been restarted and when the last container in this pod has restarted.", + "priority": 0 + }, + { + "name": "Age", + "type": "string", + "format": "", + "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "priority": 0 + }, + { + "name": "IP", + "type": "string", + "format": "", + "description": "podIP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated.", + "priority": 1 + }, + { + "name": "Node", + "type": "string", + "format": "", + "description": "NodeName indicates in which node this pod is scheduled. If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName. Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod. This field should not be used to express a desire for the pod to be scheduled on a specific node. https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename", + "priority": 1 + }, + { + "name": "Nominated Node", + "type": "string", + "format": "", + "description": "nominatedNodeName is set only when this pod preempts other pods on the node, but it cannot be scheduled right away as preemption victims receive their graceful termination periods. This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to give the resources on this node to a higher priority pod that is created after preemption. As a result, this field may be different than PodSpec.nodeName when the pod is scheduled.", + "priority": 1 + }, + { + "name": "Readiness Gates", + "type": "string", + "format": "", + "description": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates", + "priority": 1 + } + ], + "rows": [ + { + "cells": [ + "kubernetes", + "1/1", + "Running", + "0", + "33h", + "10.244.29.7", + "minikube", + "", + "" + ], + "object": { + "kind": "Pod", + "apiVersion": "v1", + "metadata": { + "name": "kubernetes", + "namespace": "default", + "labels": { + "run": "alpine" + } + }, + "spec": { }, + "status": { + "phase": "Running" + } + } + } + ] +} \ No newline at end of file