From 6848cea8bd47b50a0f2bd9ade12385d52287f492 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 28 Oct 2025 18:19:38 +0000 Subject: [PATCH 1/2] kube: register metrics api resources kubectl is moving towards using protobuf behind the scenes for kubectl top, this means our fallback unstrucutred.Unstructured object couldn't unmarshal the protobuf and failed. This PR properly registers the metrics apis making them available for unmarshaling. Signed-off-by: Tiago Silva --- go.mod | 2 +- lib/kube/proxy/scheme.go | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9b36bfa77fcc1..4486011031529 100644 --- a/go.mod +++ b/go.mod @@ -275,6 +275,7 @@ require ( k8s.io/component-base v0.34.1 k8s.io/klog/v2 v2.130.1 k8s.io/kubectl v0.34.1 + k8s.io/metrics v0.34.1 k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 sigs.k8s.io/controller-runtime v0.22.1 sigs.k8s.io/controller-tools v0.17.1 @@ -628,7 +629,6 @@ require ( gotest.tools/gotestsum v1.13.0 // indirect k8s.io/component-helpers v0.34.1 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect - k8s.io/metrics v0.34.1 // indirect mvdan.cc/sh/v3 v3.7.0 // indirect oras.land/oras-go/v2 v2.6.0 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect diff --git a/lib/kube/proxy/scheme.go b/lib/kube/proxy/scheme.go index d21dd7bb60729..c3735451969c1 100644 --- a/lib/kube/proxy/scheme.go +++ b/lib/kube/proxy/scheme.go @@ -38,6 +38,8 @@ import ( "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/metrics/pkg/apis/metrics" + metricsv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" ) const ( @@ -69,6 +71,15 @@ func init() { func registerDefaultKubeTypes(s *runtime.Scheme) error { // Register external types for Scheme metav1.AddToGroupVersion(s, schema.GroupVersion{Group: "", Version: "v1"}) + + if err := metrics.AddToScheme(s); err != nil { + return trace.Wrap(err) + } + + if err := metricsv1beta1.AddToScheme(s); err != nil { + return trace.Wrap(err) + } + if err := metav1.AddMetaToScheme(s); err != nil { return trace.Wrap(err) } From 8aa129b59487a6cbf9f9a2992ad6e3add0fa864a Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Wed, 29 Oct 2025 09:57:00 +0000 Subject: [PATCH 2/2] add unit tests --- integrations/terraform-mwi/go.mod | 1 + integrations/terraform-mwi/go.sum | 2 + integrations/terraform/go.mod | 1 + integrations/terraform/go.sum | 2 + lib/kube/proxy/scheme_test.go | 78 +++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+) diff --git a/integrations/terraform-mwi/go.mod b/integrations/terraform-mwi/go.mod index 505fcff8e1999..bed4b9c78ec1c 100644 --- a/integrations/terraform-mwi/go.mod +++ b/integrations/terraform-mwi/go.mod @@ -528,6 +528,7 @@ require ( k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect k8s.io/kubectl v0.34.1 // indirect + k8s.io/metrics v0.34.1 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect mvdan.cc/sh/v3 v3.7.0 // indirect oras.land/oras-go/v2 v2.6.0 // indirect diff --git a/integrations/terraform-mwi/go.sum b/integrations/terraform-mwi/go.sum index ebf46b62f63ad..f9d529d9fd7d0 100644 --- a/integrations/terraform-mwi/go.sum +++ b/integrations/terraform-mwi/go.sum @@ -3075,6 +3075,8 @@ k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOP k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI= k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A= +k8s.io/metrics v0.34.1 h1:374Rexmp1xxgRt64Bi0TsjAM8cA/Y8skwCoPdjtIslE= +k8s.io/metrics v0.34.1/go.mod h1:Drf5kPfk2NJrlpcNdSiAAHn/7Y9KqxpRNagByM7Ei80= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= diff --git a/integrations/terraform/go.mod b/integrations/terraform/go.mod index b75208aaf4a9f..44be0d30ae8fd 100644 --- a/integrations/terraform/go.mod +++ b/integrations/terraform/go.mod @@ -528,6 +528,7 @@ require ( k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect k8s.io/kubectl v0.34.1 // indirect + k8s.io/metrics v0.34.1 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect mvdan.cc/sh/v3 v3.7.0 // indirect oras.land/oras-go/v2 v2.6.0 // indirect diff --git a/integrations/terraform/go.sum b/integrations/terraform/go.sum index bcb96a57d509c..51076f2f9bcfb 100644 --- a/integrations/terraform/go.sum +++ b/integrations/terraform/go.sum @@ -3188,6 +3188,8 @@ k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOP k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI= k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A= +k8s.io/metrics v0.34.1 h1:374Rexmp1xxgRt64Bi0TsjAM8cA/Y8skwCoPdjtIslE= +k8s.io/metrics v0.34.1/go.mod h1:Drf5kPfk2NJrlpcNdSiAAHn/7Y9KqxpRNagByM7Ei80= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= diff --git a/lib/kube/proxy/scheme_test.go b/lib/kube/proxy/scheme_test.go index 1ce10bfa44c95..8e3df55e3e75c 100644 --- a/lib/kube/proxy/scheme_test.go +++ b/lib/kube/proxy/scheme_test.go @@ -22,9 +22,14 @@ import ( "testing" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" + "k8s.io/metrics/pkg/apis/metrics" + metricsv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" "github.com/gravitational/teleport/lib/utils/log/logtest" ) @@ -61,3 +66,76 @@ func (c *clientSet) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.AP &fakeAPIResource, }, nil } + +func TestRegisterDefaultKubeTypes(t *testing.T) { + scheme := runtime.NewScheme() + err := registerDefaultKubeTypes(scheme) + require.NoError(t, err) + + // Check that some known types are registered + tests := []struct { + gvk schema.GroupVersionKind + expectedType runtime.Object + }{ + { + gvk: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, + expectedType: &corev1.Pod{}, + }, + { + gvk: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "PodList"}, + expectedType: &corev1.PodList{}, + }, + { + gvk: schema.GroupVersionKind{Group: "metrics.k8s.io", Version: "v1beta1", Kind: "PodMetrics"}, + expectedType: &metricsv1beta1.PodMetrics{}, + }, + { + gvk: schema.GroupVersionKind{Group: "metrics.k8s.io", Version: "v1beta1", Kind: "NodeMetrics"}, + expectedType: &metricsv1beta1.NodeMetrics{}, + }, + { + gvk: schema.GroupVersionKind{Group: "metrics.k8s.io", Version: runtime.APIVersionInternal, Kind: "PodMetrics"}, + expectedType: &metrics.PodMetrics{}, + }, + { + gvk: schema.GroupVersionKind{Group: "metrics.k8s.io", Version: runtime.APIVersionInternal, Kind: "NodeMetrics"}, + expectedType: &metrics.NodeMetrics{}, + }, + + // --- metav1 types --- + { + gvk: schema.GroupVersionKind{Group: "meta.k8s.io", Version: "v1", Kind: "PartialObjectMetadata"}, + expectedType: &metav1.PartialObjectMetadata{}, + }, + { + gvk: schema.GroupVersionKind{Group: "meta.k8s.io", Version: "v1", Kind: "PartialObjectMetadataList"}, + expectedType: &metav1.PartialObjectMetadataList{}, + }, + { + gvk: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Status"}, + expectedType: &metav1.Status{}, + }, + { + gvk: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "APIVersions"}, + expectedType: &metav1.APIVersions{}, + }, + { + gvk: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "APIGroupList"}, + expectedType: &metav1.APIGroupList{}, + }, + { + gvk: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "APIGroup"}, + expectedType: &metav1.APIGroup{}, + }, + { + gvk: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "APIResourceList"}, + expectedType: &metav1.APIResourceList{}, + }, + } + + for _, testCase := range tests { + newType, err := scheme.New(testCase.gvk) + require.NoError(t, err, "expected type %v to be registered", testCase.gvk) + require.IsType(t, testCase.expectedType, newType, "expected type %v to be of type %T", testCase.gvk, testCase.expectedType) + } +}