Merge pull request #134505 from p0lyn0mial/upstream-watchlist-unsupported-wl-semantics-reflector-listwatch

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

Kubernetes-commit: 3828756d90cf28e0ab5e0ccd550041b70c642b91
This commit is contained in:
Kubernetes Publisher
2025-10-16 02:10:05 -07:00
4 changed files with 98 additions and 3 deletions

2
go.mod
View File

@@ -25,7 +25,7 @@ require (
google.golang.org/protobuf v1.36.8
gopkg.in/evanphx/json-patch.v4 v4.13.0
k8s.io/api v0.0.0-20251014034829-180fdecad17a
k8s.io/apimachinery v0.0.0-20251013232933-de3ea85ba46a
k8s.io/apimachinery v0.0.0-20251015043641-db7bb2323050
k8s.io/klog/v2 v2.130.1
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397

4
go.sum
View File

@@ -119,8 +119,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.0.0-20251014034829-180fdecad17a h1:xQZUr3sFO3VRP4paNCKSwDBL//XW7MfgEIc6F16S7tc=
k8s.io/api v0.0.0-20251014034829-180fdecad17a/go.mod h1:G9i/7w0plhqKf252B96rN9Ng5YQFUYQcEjRg17gOBdc=
k8s.io/apimachinery v0.0.0-20251013232933-de3ea85ba46a h1:6rysSFhh7j8BX8v+cqAFLo+62x5NMeJHZ5OiFZZYcDE=
k8s.io/apimachinery v0.0.0-20251013232933-de3ea85ba46a/go.mod h1:wE5nOmI8k5gdg4Nuo6Csst6CE+WgeB7ZNhh7K5lLUbs=
k8s.io/apimachinery v0.0.0-20251015043641-db7bb2323050 h1:m5nhDhLbK0Gg1QHh7n9ekBWQ4oU7lr6Mmgi3TbW3JZ8=
k8s.io/apimachinery v0.0.0-20251015043641-db7bb2323050/go.mod h1:wE5nOmI8k5gdg4Nuo6Csst6CE+WgeB7ZNhh7K5lLUbs=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=

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)
}
})
}
}