mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 14:07:14 +00:00
Merge pull request #50583 from liggitt/endpoints-empty-noop
Automatic merge from submit-queue (batch tested with PRs 49869, 47987, 50211, 50804, 50583) Make endpoints controller update based on semantic equality Fixes #50828 Split from https://github.com/kubernetes/kubernetes/pull/45294 for separate review Currently, endpoints objects containing no subsets are decoded by the go client as subsets:[] (when requested individually) or as subsets:null (when requested in a list of endpoints). Because the endpoints controller is fed via a lister/watcher, it gets the `subsets:null` version fed to it. The subsets computation then returns an empty slice, which fails reflect.DeepEqual, which triggers a write attempt. This PR makes the comparison use semantic.DeepEqual to avoid spurious writes. https://github.com/kubernetes/kubernetes/pull/45294 would remove the inconsistency between lists and individual gets.
This commit is contained in:
commit
402e48b072
@ -20,6 +20,7 @@ go_library(
|
|||||||
"//pkg/util/metrics:go_default_library",
|
"//pkg/util/metrics:go_default_library",
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
@ -434,8 +435,11 @@ func (e *EndpointController) syncService(key string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if reflect.DeepEqual(currentEndpoints.Subsets, subsets) &&
|
createEndpoints := len(currentEndpoints.ResourceVersion) == 0
|
||||||
reflect.DeepEqual(currentEndpoints.Labels, service.Labels) {
|
|
||||||
|
if !createEndpoints &&
|
||||||
|
apiequality.Semantic.DeepEqual(currentEndpoints.Subsets, subsets) &&
|
||||||
|
apiequality.Semantic.DeepEqual(currentEndpoints.Labels, service.Labels) {
|
||||||
glog.V(5).Infof("endpoints are equal for %s/%s, skipping update", service.Namespace, service.Name)
|
glog.V(5).Infof("endpoints are equal for %s/%s, skipping update", service.Namespace, service.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -451,7 +455,6 @@ func (e *EndpointController) syncService(key string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
glog.V(4).Infof("Update endpoints for %v/%v, ready: %d not ready: %d", service.Namespace, service.Name, totalReadyEps, totalNotReadyEps)
|
glog.V(4).Infof("Update endpoints for %v/%v, ready: %d not ready: %d", service.Namespace, service.Name, totalReadyEps, totalNotReadyEps)
|
||||||
createEndpoints := len(currentEndpoints.ResourceVersion) == 0
|
|
||||||
if createEndpoints {
|
if createEndpoints {
|
||||||
// No previous endpoints, create them
|
// No previous endpoints, create them
|
||||||
_, err = e.client.Core().Endpoints(service.Namespace).Create(newEndpoints)
|
_, err = e.client.Core().Endpoints(service.Namespace).Create(newEndpoints)
|
||||||
|
@ -175,6 +175,70 @@ func TestSyncEndpointsItemsPreserveNoSelector(t *testing.T) {
|
|||||||
endpointsHandler.ValidateRequestCount(t, 0)
|
endpointsHandler.ValidateRequestCount(t, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSyncEndpointsExistingNilSubsets(t *testing.T) {
|
||||||
|
ns := metav1.NamespaceDefault
|
||||||
|
testServer, endpointsHandler := makeTestServer(t, ns)
|
||||||
|
defer testServer.Close()
|
||||||
|
endpoints := newController(testServer.URL)
|
||||||
|
endpoints.endpointsStore.Add(&v1.Endpoints{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
Namespace: ns,
|
||||||
|
ResourceVersion: "1",
|
||||||
|
},
|
||||||
|
Subsets: nil,
|
||||||
|
})
|
||||||
|
endpoints.serviceStore.Add(&v1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
|
||||||
|
Spec: v1.ServiceSpec{
|
||||||
|
Selector: map[string]string{"foo": "bar"},
|
||||||
|
Ports: []v1.ServicePort{{Port: 80}},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
endpoints.syncService(ns + "/foo")
|
||||||
|
endpointsHandler.ValidateRequestCount(t, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncEndpointsExistingEmptySubsets(t *testing.T) {
|
||||||
|
ns := metav1.NamespaceDefault
|
||||||
|
testServer, endpointsHandler := makeTestServer(t, ns)
|
||||||
|
defer testServer.Close()
|
||||||
|
endpoints := newController(testServer.URL)
|
||||||
|
endpoints.endpointsStore.Add(&v1.Endpoints{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
Namespace: ns,
|
||||||
|
ResourceVersion: "1",
|
||||||
|
},
|
||||||
|
Subsets: []v1.EndpointSubset{},
|
||||||
|
})
|
||||||
|
endpoints.serviceStore.Add(&v1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
|
||||||
|
Spec: v1.ServiceSpec{
|
||||||
|
Selector: map[string]string{"foo": "bar"},
|
||||||
|
Ports: []v1.ServicePort{{Port: 80}},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
endpoints.syncService(ns + "/foo")
|
||||||
|
endpointsHandler.ValidateRequestCount(t, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncEndpointsNewNoSubsets(t *testing.T) {
|
||||||
|
ns := metav1.NamespaceDefault
|
||||||
|
testServer, endpointsHandler := makeTestServer(t, ns)
|
||||||
|
defer testServer.Close()
|
||||||
|
endpoints := newController(testServer.URL)
|
||||||
|
endpoints.serviceStore.Add(&v1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
|
||||||
|
Spec: v1.ServiceSpec{
|
||||||
|
Selector: map[string]string{"foo": "bar"},
|
||||||
|
Ports: []v1.ServicePort{{Port: 80}},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
endpoints.syncService(ns + "/foo")
|
||||||
|
endpointsHandler.ValidateRequestCount(t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCheckLeftoverEndpoints(t *testing.T) {
|
func TestCheckLeftoverEndpoints(t *testing.T) {
|
||||||
ns := metav1.NamespaceDefault
|
ns := metav1.NamespaceDefault
|
||||||
testServer, _ := makeTestServer(t, ns)
|
testServer, _ := makeTestServer(t, ns)
|
||||||
|
Loading…
Reference in New Issue
Block a user