Drop generic apiserver and CEL from kube-proxy's import graph

pkg/cluster/ports: make CloudControllerManagerWebhookPort a literal
instead of an alias of k8s.io/cloud-provider/options; a test keeps the
two values equal. Importing cloud-provider/options pulled the generic
apiserver, admission and the etcd client into kube-proxy.

responsewriters: declare an unexported resourceStreamer with the method
set of rest.ResourceStreamer so the package no longer imports
registry/rest (and, through it, admission and cel-go). A compile-time
check in the tests keeps the two interfaces assignable to each other.

kube-proxy goes from 1452 to 1102 packages in its import graph.
This commit is contained in:
Davanum Srinivas committed 2026-09-25 10:01:09 -04:00
1 parent 90777f0b13
commit 9477681de0
4 files changed
+53 -10

No files matched your search

+5 -6
View File
@@ -16,10 +16,6 @@ limitations under the License.
package ports
import (
cpoptions "k8s.io/cloud-provider/options"
)
// In this file, we can see all default port of cluster.
// It's also an important documentation for us. So don't remove them easily.
const (
@@ -49,6 +45,9 @@ const (
CloudControllerManagerPort = 10258
// CloudControllerManagerWebhookPort is the default port for the cloud
// controller manager webhook server. May be overridden by a flag at
// startup.
CloudControllerManagerWebhookPort = cpoptions.CloudControllerManagerWebhookPort
// startup. It is a literal, not an alias of the k8s.io/cloud-provider
// constant, because importing cloud-provider/options pulls the generic
// apiserver (and CEL) into every binary that uses this package.
// ports_test.go checks that the two values stay equal.
CloudControllerManagerWebhookPort = 10260
)
+29
View File
@@ -0,0 +1,29 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ports
import (
"testing"
cpoptions "k8s.io/cloud-provider/options"
)
func TestCloudControllerManagerWebhookPort(t *testing.T) {
if CloudControllerManagerWebhookPort != cpoptions.CloudControllerManagerWebhookPort {
t.Errorf("CloudControllerManagerWebhookPort = %d, want %d (k8s.io/cloud-provider/options)", CloudControllerManagerWebhookPort, cpoptions.CloudControllerManagerWebhookPort)
}
}
@@ -37,17 +37,24 @@ import (
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/metrics"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/util/flushwriter"
"k8s.io/component-base/tracing"
"k8s.io/streaming/pkg/httpstream/wsstream"
)
// StreamObject performs input stream negotiation from a ResourceStreamer and writes that to the response.
// resourceStreamer is the method set of k8s.io/apiserver/pkg/registry/rest.ResourceStreamer. It is
// declared here so that this package does not import registry/rest, which pulls the admission and
// CEL packages into every binary that serves /flagz or /statusz. writers_test.go asserts that the
// two interfaces stay assignable to each other.
type resourceStreamer interface {
InputStream(ctx context.Context, apiVersion, acceptHeader string) (stream io.ReadCloser, flush bool, mimeType string, err error)
}
// StreamObject performs input stream negotiation from a rest.ResourceStreamer and writes that to the response.
// If the client requests a websocket upgrade, negotiate for a websocket reader protocol (because many
// browser clients cannot easily handle binary streaming protocols).
func StreamObject(statusCode int, gv schema.GroupVersion, s runtime.NegotiatedSerializer, stream rest.ResourceStreamer, w http.ResponseWriter, req *http.Request) {
func StreamObject(statusCode int, gv schema.GroupVersion, s runtime.NegotiatedSerializer, stream resourceStreamer, w http.ResponseWriter, req *http.Request) {
out, flush, contentType, err := stream.InputStream(req.Context(), gv.String(), req.Header.Get("Accept"))
if err != nil {
ErrorNegotiated(err, s, gv, w, req)
@@ -297,7 +304,7 @@ func (w *deferredResponseWriter) Close() (err error) {
// WriteObjectNegotiated renders an object in the content type negotiated by the client.
func WriteObjectNegotiated(s runtime.NegotiatedSerializer, restrictions negotiation.EndpointRestrictions, gv schema.GroupVersion, w http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object, listGVKInContentType bool) {
stream, ok := object.(rest.ResourceStreamer)
stream, ok := object.(resourceStreamer)
if ok {
requestInfo, _ := request.RequestInfoFrom(req.Context())
metrics.RecordLongRunning(req, requestInfo, metrics.APIServerComponent, func() {
@@ -45,10 +45,18 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/rand"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
)
// resourceStreamer must stay identical to rest.ResourceStreamer so that the type assertion in
// WriteObjectNegotiated keeps matching the same objects. Both directions are checked.
var (
_ resourceStreamer = rest.ResourceStreamer(nil)
_ rest.ResourceStreamer = resourceStreamer(nil)
)
const benchmarkSeed = 100
//go:embed testdata/exemplar_pod.yaml