FWD: Analysis generated by Claude Sonnet 5, then edited / confirmed by a human (me)
What happened:
After upgrading metrics-server from v0.8.1 to v0.9.0 on an EKS 1.35 cluster, the cluster's merged /openapi/v2 document became corrupt. The apiserver logs (CloudWatch, kube-apiserver-* stream) show:
E0901 02:01:25.001 handler.go:160] Error in OpenAPI handler:
...resource.Quantity_v2 has invalid properties: ArbitraryKeys, SchemaProps, SwaggerSchemaProps
...meta.v1.APIGroupList_v2 / APIGroup_v2 / APIResourceList...
and equivalent errors for meta.v1.APIGroupList_v2, meta.v1.APIGroup_v2, meta.v1.APIResourceList_v2 - To reproduce:
$ curl -sk -o /dev/null -w "protobuf -> HTTP %{http_code}\n" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/com.github.proto-openapi.spec.v2@v1.0+protobuf" \
https://<cluster-endpoint>/openapi/v2
protobuf -> HTTP 503
$ curl -sk -o /dev/null -w "json -> HTTP %{http_code}\n" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
https://<cluster-endpoint>/openapi/v2
json -> HTTP 200
The 503 effectively DDOS's the KubeAPI server for everyone else using this KubeAPI server, leading to:
Error from server (ServiceUnavailable): the server is currently unable to handle the request
From other Clients.
metrics-server v0.9.0 bumped k8s.io/apiserver from v0.33.7 → v0.36.2, which transitively bumped k8s.io/kube-openapi from v0.0.0-20250318190949-c8a335a9a2ff (v0.8.1) to v0.0.0-20260520065146-aa012df4f4af (v0.9.0).
Within that range, commit add4291a9eec163878911f65bbb7534d08b05d31 ("Migrate callers to post-split go-json-experiment API", merged in #604) changed Schema.MarshalJSONTo in pkg/validation/spec/schema.go:
func (s Schema) MarshalJSONTo(enc *jsontext.Encoder) error {
type ArbitraryKeys map[string]interface{}
var x struct {
- ArbitraryKeys
+ ArbitraryKeys ArbitraryKeys `json:",inline"`
SchemaProps schemaPropsOmitZero `json:",inline"`
SwaggerSchemaProps swaggerSchemaPropsOmitZero `json:",inline"`
Schema string `json:"$schema,omitempty"`
Before this change, ArbitraryKeys was an anonymous/embedded field, implicitly flattened by the pre-split experimental JSON v2 API (MarshalNextJSON). After the change, it's a named field that depends on the new jsontext/post-split API correctly honoring the explicit json:",inline" tag to flatten it (along with SchemaProps and SwaggerSchemaProps) into the parent object.
The field names ArbitraryKeys, SchemaProps, SwaggerSchemaProps are leaking into the served schema as literal JSON properties instead of being inlined.
What you expected to happen:
Upgrading an aggregated APIService's implementation (metrics-server) to a newer line of its Kubernetes library dependencies shouldn't corrupt OpenAPI aggregation on a parent apiserver that is still within the normal supported version window.
Anything else we need to know?:
Not yet experimented: whether the malformed document is produced by metrics-server itself when serving its own spec, or by the parent apiserver's aggregator when merging it; and whether this requires a specific Go toolchain/build flag (e.g. GOEXPERIMENT=jsonv2) to route through MarshalJSONTo at all, versus the MarshalJSON path.
Environment:
-
Parent apiserver: EKS v1.35.6-eks-bca9cf6
-
Aggregated APIService: metrics-server, image 0.9.0 (previously 0.8.1, working)
-
Metrics Server manifest
spoiler for Metrics Server manifest:
spoiler for Kubelet config:
spoiler for Metrics Server logs:
spolier for Status of Metrics API:
kubectl describe apiservice v1beta1.metrics.k8s.io
/kind bug
FWD: Analysis generated by Claude Sonnet 5, then edited / confirmed by a human (me)
What happened:
After upgrading metrics-server from
v0.8.1tov0.9.0on an EKS 1.35 cluster, the cluster's merged /openapi/v2 document became corrupt. The apiserver logs (CloudWatch, kube-apiserver-* stream) show:and equivalent errors for
meta.v1.APIGroupList_v2,meta.v1.APIGroup_v2,meta.v1.APIResourceList_v2- To reproduce:The 503 effectively DDOS's the KubeAPI server for everyone else using this KubeAPI server, leading to:
From other Clients.
metrics-server v0.9.0 bumped k8s.io/apiserver from v0.33.7 → v0.36.2, which transitively bumped k8s.io/kube-openapi from v0.0.0-20250318190949-c8a335a9a2ff (v0.8.1) to v0.0.0-20260520065146-aa012df4f4af (v0.9.0).
Within that range, commit add4291a9eec163878911f65bbb7534d08b05d31 ("Migrate callers to post-split go-json-experiment API", merged in #604) changed Schema.MarshalJSONTo in pkg/validation/spec/schema.go:
Before this change, ArbitraryKeys was an anonymous/embedded field, implicitly flattened by the pre-split experimental JSON v2 API (MarshalNextJSON). After the change, it's a named field that depends on the new jsontext/post-split API correctly honoring the explicit json:",inline" tag to flatten it (along with SchemaProps and SwaggerSchemaProps) into the parent object.
The field names ArbitraryKeys, SchemaProps, SwaggerSchemaProps are leaking into the served schema as literal JSON properties instead of being inlined.
What you expected to happen:
Upgrading an aggregated APIService's implementation (metrics-server) to a newer line of its Kubernetes library dependencies shouldn't corrupt OpenAPI aggregation on a parent apiserver that is still within the normal supported version window.
Anything else we need to know?:
Not yet experimented: whether the malformed document is produced by metrics-server itself when serving its own spec, or by the parent apiserver's aggregator when merging it; and whether this requires a specific Go toolchain/build flag (e.g. GOEXPERIMENT=jsonv2) to route through MarshalJSONTo at all, versus the MarshalJSON path.
Environment:
Parent apiserver: EKS v1.35.6-eks-bca9cf6
Aggregated APIService: metrics-server, image 0.9.0 (previously 0.8.1, working)
Metrics Server manifest
spoiler for Metrics Server manifest:
spoiler for Kubelet config:
spoiler for Metrics Server logs:
spolier for Status of Metrics API:
/kind bug