client-go/tools/cache/listwatch: intro ToListWatcherWithWatchListSemantics

returns a ListerWatcher that knows whether the provided client explicitly
does NOT support the WatchList semantics. This allows Reflectors
to adapt their behavior based on client capabilities.

Kubernetes-commit: 3b93755c0c07ce898f1c2a3924adef6c3143f247
This commit is contained in:
Lukasz Szaszkiewicz
2025-10-12 00:29:31 +02:00
committed by Kubernetes Publisher
parent f217e7096a
commit 6777feb020
2 changed files with 95 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/util/watchlist"
)
// Lister is any object that knows how to perform an initial list.
@@ -130,6 +131,35 @@ type listerWatcherWrapper struct {
ListerWithContext
WatcherWithContext
}
type listWatcherWithWatchListSemanticsWrapper struct {
*ListWatch
// unsupportedWatchListSemantics indicates whether a client explicitly does NOT support
// WatchList semantics.
//
// Over the years, unit tests in kube have been written in many different ways.
// After enabling the WatchListClient feature by default, existing tests started failing.
// To avoid breaking lots of existing client-go users after upgrade,
// we introduced this field as an opt-in.
//
// When true, the reflector disables WatchList even if the feature gate is enabled.
unsupportedWatchListSemantics bool
}
func (lw *listWatcherWithWatchListSemanticsWrapper) IsWatchListSemanticsUnSupported() bool {
return lw.unsupportedWatchListSemantics
}
// ToListWatcherWithWatchListSemantics returns a ListerWatcher
// that knows whether the provided client explicitly
// does NOT support the WatchList semantics. This allows Reflectors
// to adapt their behavior based on client capabilities.
func ToListWatcherWithWatchListSemantics(lw *ListWatch, client any) ListerWatcher {
return &listWatcherWithWatchListSemanticsWrapper{
lw,
watchlist.DoesClientNotSupportWatchListSemantics(client),
}
}
// ListFunc knows how to list resources
//

65
tools/cache/listwatch_test.go vendored Normal file
View File

@@ -0,0 +1,65 @@
/*
Copyright 2025 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 cache
import (
"testing"
"k8s.io/client-go/util/watchlist"
)
type fakeWatchListClient struct {
unSupportedWatchListSemantics bool
}
func (f fakeWatchListClient) IsWatchListSemanticsUnSupported() bool {
return f.unSupportedWatchListSemantics
}
func TestToListWatcherWithWatchListSemantics(t *testing.T) {
scenarios := []struct {
name string
client any
expectUnSupportedWatchListSemantics bool
}{
{
name: "client which doesn't implement the interface supports WatchList semantics",
client: nil,
expectUnSupportedWatchListSemantics: false,
},
{
name: "client does not support WatchList semantics",
client: fakeWatchListClient{unSupportedWatchListSemantics: true},
expectUnSupportedWatchListSemantics: true,
},
{
name: "client supports WatchList semantics",
client: fakeWatchListClient{unSupportedWatchListSemantics: false},
expectUnSupportedWatchListSemantics: false,
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
target := ToListWatcherWithWatchListSemantics(&ListWatch{}, scenario.client)
if got := watchlist.DoesClientNotSupportWatchListSemantics(target); got != scenario.expectUnSupportedWatchListSemantics {
t.Fatalf("DoesClientNotSupportWatchListSemantics returned: %v, want: %v", got, scenario.expectUnSupportedWatchListSemantics)
}
})
}
}