mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 14:07:14 +00:00
Merge pull request #105511 from benluddy/apf-delegated-signal
Free APF seats for watches handled by an aggregated apiserver.
This commit is contained in:
commit
313b43a8cb
@ -52,6 +52,17 @@ func WatchInitialized(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestDelegated informs the priority and fairness dispatcher that
|
||||||
|
// a given request has been delegated to an aggregated API
|
||||||
|
// server. No-op when priority and fairness is disabled.
|
||||||
|
func RequestDelegated(ctx context.Context) {
|
||||||
|
// The watch initialization signal doesn't traverse request
|
||||||
|
// boundaries, so we generously fire it as soon as we know
|
||||||
|
// that the request won't be serviced locally. Safe to call
|
||||||
|
// for non-watch requests.
|
||||||
|
WatchInitialized(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// InitializationSignal is an interface that allows sending and handling
|
// InitializationSignal is an interface that allows sending and handling
|
||||||
// initialization signals.
|
// initialization signals.
|
||||||
type InitializationSignal interface {
|
type InitializationSignal interface {
|
||||||
|
@ -35,6 +35,7 @@ import (
|
|||||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/server/egressselector"
|
"k8s.io/apiserver/pkg/server/egressselector"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
utilflowcontrol "k8s.io/apiserver/pkg/util/flowcontrol"
|
||||||
"k8s.io/apiserver/pkg/util/x509metrics"
|
"k8s.io/apiserver/pkg/util/x509metrics"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/transport"
|
"k8s.io/client-go/transport"
|
||||||
@ -175,6 +176,7 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w})
|
handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w})
|
||||||
handler.InterceptRedirects = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StreamingProxyRedirects)
|
handler.InterceptRedirects = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StreamingProxyRedirects)
|
||||||
handler.RequireSameHostRedirects = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.ValidateProxyRedirects)
|
handler.RequireSameHostRedirects = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.ValidateProxyRedirects)
|
||||||
|
utilflowcontrol.RequestDelegated(req.Context())
|
||||||
handler.ServeHTTP(w, newReq)
|
handler.ServeHTTP(w, newReq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/server/egressselector"
|
"k8s.io/apiserver/pkg/server/egressselector"
|
||||||
|
utilflowcontrol "k8s.io/apiserver/pkg/util/flowcontrol"
|
||||||
"k8s.io/component-base/metrics"
|
"k8s.io/component-base/metrics"
|
||||||
"k8s.io/component-base/metrics/legacyregistry"
|
"k8s.io/component-base/metrics/legacyregistry"
|
||||||
apiregistration "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
|
apiregistration "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
|
||||||
@ -899,6 +900,135 @@ func TestProxyCertReload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fcInitSignal struct {
|
||||||
|
nSignals int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fcInitSignal) SignalCount() int {
|
||||||
|
return int(atomic.SwapInt32(&s.nSignals, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fcInitSignal) Signal() {
|
||||||
|
atomic.AddInt32(&s.nSignals, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fcInitSignal) Wait() {
|
||||||
|
}
|
||||||
|
|
||||||
|
type hookedListener struct {
|
||||||
|
l net.Listener
|
||||||
|
onAccept func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wl *hookedListener) Accept() (net.Conn, error) {
|
||||||
|
wl.onAccept()
|
||||||
|
return wl.l.Accept()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wl *hookedListener) Close() error {
|
||||||
|
return wl.l.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wl *hookedListener) Addr() net.Addr {
|
||||||
|
return wl.l.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlowControlSignal(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
Name string
|
||||||
|
Local bool
|
||||||
|
Available bool
|
||||||
|
Request http.Request
|
||||||
|
SignalExpected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "local",
|
||||||
|
Local: true,
|
||||||
|
SignalExpected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "unavailable",
|
||||||
|
Local: false,
|
||||||
|
Available: false,
|
||||||
|
SignalExpected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "request performed",
|
||||||
|
Local: false,
|
||||||
|
Available: true,
|
||||||
|
SignalExpected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "upgrade request performed",
|
||||||
|
Local: false,
|
||||||
|
Available: true,
|
||||||
|
Request: http.Request{
|
||||||
|
Header: http.Header{"Connection": []string{"Upgrade"}},
|
||||||
|
},
|
||||||
|
SignalExpected: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
okh := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
var sig fcInitSignal
|
||||||
|
|
||||||
|
var signalCountOnAccept int32
|
||||||
|
backend := httptest.NewUnstartedServer(okh)
|
||||||
|
backend.Listener = &hookedListener{
|
||||||
|
l: backend.Listener,
|
||||||
|
onAccept: func() {
|
||||||
|
atomic.StoreInt32(&signalCountOnAccept, int32(sig.SignalCount()))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
backend.Start()
|
||||||
|
defer backend.Close()
|
||||||
|
|
||||||
|
p := proxyHandler{
|
||||||
|
localDelegate: okh,
|
||||||
|
serviceResolver: &mockedRouter{destinationHost: backend.Listener.Addr().String()},
|
||||||
|
}
|
||||||
|
|
||||||
|
server := httptest.NewServer(contextHandler(
|
||||||
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
p.ServeHTTP(w, r.WithContext(utilflowcontrol.WithInitializationSignal(r.Context(), &sig)))
|
||||||
|
}),
|
||||||
|
&user.DefaultInfo{
|
||||||
|
Name: "username",
|
||||||
|
Groups: []string{"one", "two"},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
p.handlingInfo.Store(proxyHandlingInfo{
|
||||||
|
local: tc.Local,
|
||||||
|
serviceAvailable: tc.Available,
|
||||||
|
proxyRoundTripper: backend.Client().Transport,
|
||||||
|
})
|
||||||
|
|
||||||
|
surl, err := url.Parse(server.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := tc.Request
|
||||||
|
req.URL = surl
|
||||||
|
_, err = server.Client().Do(&req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fired := (atomic.LoadInt32(&signalCountOnAccept) > 0); tc.SignalExpected && !fired {
|
||||||
|
t.Errorf("flow control signal expected but not fired")
|
||||||
|
} else if fired && !tc.SignalExpected {
|
||||||
|
t.Errorf("flow control signal fired but not expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getCertAndKeyPaths(t *testing.T) (string, string, string) {
|
func getCertAndKeyPaths(t *testing.T) (string, string, string) {
|
||||||
dir, err := ioutil.TempDir(os.TempDir(), "k8s-test-handler-proxy-cert")
|
dir, err := ioutil.TempDir(os.TempDir(), "k8s-test-handler-proxy-cert")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user