From e627b3b2bff5dd57ea41e0f424c8ecb101db67ed Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 2 Jun 2025 10:39:07 -0400 Subject: [PATCH] Move some network conformance tests around. Move the Endpoints API test from endpointslice.go to endpoints.go Move the "kubernetes.default Service exists" and "kubernetes.default endpoints exist" tests to apiserver.go, since (unlike the rest of service.go/endpointslice.go) they aren't testing the behavior of the Service/EndpointSlice/Endpoints APIs. (No code changes, but fixed a typo in a comment.) --- test/conformance/testdata/conformance.yaml | 44 ++-- test/e2e/network/apiserver.go | 140 +++++++++++++ test/e2e/network/endpoints.go | 229 +++++++++++++++++++++ test/e2e/network/endpointslice.go | 87 -------- test/e2e/network/service.go | 196 ------------------ 5 files changed, 391 insertions(+), 305 deletions(-) create mode 100644 test/e2e/network/apiserver.go create mode 100644 test/e2e/network/endpoints.go diff --git a/test/conformance/testdata/conformance.yaml b/test/conformance/testdata/conformance.yaml index b059483d071..52847989f96 100755 --- a/test/conformance/testdata/conformance.yaml +++ b/test/conformance/testdata/conformance.yaml @@ -1643,6 +1643,19 @@ The event is deleted and MUST NOT show up when listing all events. release: v1.25 file: test/e2e/instrumentation/core_events.go +- testname: kubernetes.default Endpoints and EndpointSlices + codename: '[sig-network] API Server should have Endpoints and EndpointSlices pointing + to API Server [Conformance]' + description: The "kubernetes.default" service MUST have Endpoints and EndpointSlices + pointing to each API server instance. + release: v1.21 + file: test/e2e/network/apiserver.go +- testname: Kubernetes Service + codename: '[sig-network] API Server should provide secure master service [Conformance]' + description: By default when a kubernetes cluster is running there MUST be a 'kubernetes' + service running in the cluster. + release: v1.9 + file: test/e2e/network/apiserver.go - testname: DNS, cluster codename: '[sig-network] DNS should provide /etc/hosts entries for the cluster [Conformance]' description: When a Pod is created, the pod MUST be able to resolve cluster dns @@ -1717,13 +1730,6 @@ for a Service that matches no pods. release: v1.21 file: test/e2e/network/endpointslice.go -- testname: kubernetes.default Endpoints and EndpointSlices - codename: '[sig-network] EndpointSlice should have Endpoints and EndpointSlices - pointing to API Server [Conformance]' - description: The "kubernetes.default" service MUST have Endpoints and EndpointSlices - pointing to each API server instance. - release: v1.21 - file: test/e2e/network/endpointslice.go - testname: EndpointSlice, multiple IPs, multiple ports codename: '[sig-network] EndpointSlice should support a Service with multiple endpoint IPs specified in multiple EndpointSlices [Conformance]' @@ -1760,6 +1766,15 @@ update, and delete actions. release: v1.21 file: test/e2e/network/endpointslicemirroring.go +- testname: Endpoint resource lifecycle + codename: '[sig-network] Endpoints should test the lifecycle of an Endpoint [Conformance]' + description: Create an endpoint, the endpoint MUST exist. The endpoint is updated + with a new label, a check after the update MUST find the changes. The endpoint + is then patched with a new IPv4 address and port, a check after the patch MUST + the changes. The endpoint is deleted by its label, a watch listens for the deleted + watch event. + release: v1.19 + file: test/e2e/network/endpoints.go - testname: Scheduling, HostPort matching and HostIP and Protocol not-matching codename: '[sig-network] HostPort validates that there is no conflict between pods with same hostPort but different hostIP and protocol [LinuxOnly] [Conformance]' @@ -2032,12 +2047,6 @@ Windows does not support session affinity.' release: v1.19 file: test/e2e/network/service.go -- testname: Kubernetes Service - codename: '[sig-network] Services should provide secure master service [Conformance]' - description: By default when a kubernetes cluster is running there MUST be a 'kubernetes' - service running in the cluster. - release: v1.9 - file: test/e2e/network/service.go - testname: Service, endpoints codename: '[sig-network] Services should serve a basic endpoint from pods [Conformance]' description: Create a service with a endpoint without any Pods, the service MUST @@ -2069,15 +2078,6 @@ Pod and the service must now have empty set of endpoints. release: v1.9 file: test/e2e/network/service.go -- testname: Endpoint resource lifecycle - codename: '[sig-network] Services should test the lifecycle of an Endpoint [Conformance]' - description: Create an endpoint, the endpoint MUST exist. The endpoint is updated - with a new label, a check after the update MUST find the changes. The endpoint - is then patched with a new IPv4 address and port, a check after the patch MUST - the changes. The endpoint is deleted by it's label, a watch listens for the deleted - watch event. - release: v1.19 - file: test/e2e/network/service.go - testname: ConfigMap, from environment field with various prefixes codename: '[sig-node] ConfigMap should be consumable as environment variable names with various prefixes [Conformance]' diff --git a/test/e2e/network/apiserver.go b/test/e2e/network/apiserver.go new file mode 100644 index 00000000000..682cfef43af --- /dev/null +++ b/test/e2e/network/apiserver.go @@ -0,0 +1,140 @@ +/* +Copyright 2019 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 network + +import ( + "context" + + v1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/test/e2e/framework" + "k8s.io/kubernetes/test/e2e/network/common" + admissionapi "k8s.io/pod-security-admission/api" + + "github.com/onsi/ginkgo/v2" +) + +var _ = common.SIGDescribe("API Server", func() { + f := framework.NewDefaultFramework("apiserver") + f.NamespacePodSecurityLevel = admissionapi.LevelBaseline + + var cs clientset.Interface + + ginkgo.BeforeEach(func() { + cs = f.ClientSet + }) + + /* + Release: v1.9 + Testname: Kubernetes Service + Description: By default when a kubernetes cluster is running there MUST be a 'kubernetes' service running in the cluster. + */ + framework.ConformanceIt("should provide secure master service", func(ctx context.Context) { + _, err := cs.CoreV1().Services(metav1.NamespaceDefault).Get(ctx, "kubernetes", metav1.GetOptions{}) + framework.ExpectNoError(err, "failed to fetch the service object for the service named kubernetes") + }) + + /* + Release: v1.21 + Testname: kubernetes.default Endpoints and EndpointSlices + Description: The "kubernetes.default" service MUST have Endpoints and EndpointSlices pointing to each API server instance. + */ + framework.ConformanceIt("should have Endpoints and EndpointSlices pointing to API Server", func(ctx context.Context) { + namespace := "default" + name := "kubernetes" + // verify "kubernetes.default" service exist + _, err := cs.CoreV1().Services(namespace).Get(ctx, name, metav1.GetOptions{}) + framework.ExpectNoError(err, "error obtaining API server \"kubernetes\" Service resource on \"default\" namespace") + + // verify Endpoints for the API servers exist + endpoints, err := cs.CoreV1().Endpoints(namespace).Get(ctx, name, metav1.GetOptions{}) + framework.ExpectNoError(err, "error obtaining API server \"kubernetes\" Endpoint resource on \"default\" namespace") + if len(endpoints.Subsets) == 0 { + framework.Failf("Expected at least 1 subset in endpoints, got %d: %#v", len(endpoints.Subsets), endpoints.Subsets) + } + // verify EndpointSlices for the API servers exist + endpointSliceList, err := cs.DiscoveryV1().EndpointSlices(namespace).List(ctx, metav1.ListOptions{ + LabelSelector: "kubernetes.io/service-name=" + name, + }) + framework.ExpectNoError(err, "error obtaining API server \"kubernetes\" EndpointSlice resource on \"default\" namespace") + if len(endpointSliceList.Items) == 0 { + framework.Failf("Expected at least 1 EndpointSlice, got %d: %#v", len(endpoints.Subsets), endpoints.Subsets) + } + + if !endpointSlicesEqual(endpoints, endpointSliceList) { + framework.Failf("Expected EndpointSlice to have same addresses and port as Endpoints, got %#v: %#v", endpoints, endpointSliceList) + } + + }) +}) + +// endpointSlicesEqual compare if the Endpoint and the EndpointSliceList contains the same endpoints values +// as in addresses and ports, considering Ready and Unready addresses +func endpointSlicesEqual(endpoints *v1.Endpoints, endpointSliceList *discoveryv1.EndpointSliceList) bool { + // get the apiserver endpoint addresses + epAddresses := sets.NewString() + epPorts := sets.NewInt32() + for _, subset := range endpoints.Subsets { + for _, addr := range subset.Addresses { + epAddresses.Insert(addr.IP) + } + for _, addr := range subset.NotReadyAddresses { + epAddresses.Insert(addr.IP) + } + for _, port := range subset.Ports { + epPorts.Insert(port.Port) + } + } + framework.Logf("Endpoints addresses: %v , ports: %v", epAddresses.List(), epPorts.List()) + + // Endpoints are single stack, and must match the primary IP family of the Service kubernetes.default + // However, EndpointSlices can be IPv4 or IPv6, we can only compare the Slices that match the same IP family + // framework.TestContext.ClusterIsIPv6() reports the IP family of the kubernetes.default service + var addrType discoveryv1.AddressType + if framework.TestContext.ClusterIsIPv6() { + addrType = discoveryv1.AddressTypeIPv6 + } else { + addrType = discoveryv1.AddressTypeIPv4 + } + + // get the apiserver addresses from the endpoint slice list + sliceAddresses := sets.NewString() + slicePorts := sets.NewInt32() + for _, slice := range endpointSliceList.Items { + if slice.AddressType != addrType { + framework.Logf("Skipping slice %s: wanted %s family, got %s", slice.Name, addrType, slice.AddressType) + continue + } + for _, s := range slice.Endpoints { + sliceAddresses.Insert(s.Addresses...) + } + for _, ports := range slice.Ports { + if ports.Port != nil { + slicePorts.Insert(*ports.Port) + } + } + } + + framework.Logf("EndpointSlices addresses: %v , ports: %v", sliceAddresses.List(), slicePorts.List()) + if sliceAddresses.Equal(epAddresses) && slicePorts.Equal(epPorts) { + return true + } + return false +} diff --git a/test/e2e/network/endpoints.go b/test/e2e/network/endpoints.go new file mode 100644 index 00000000000..bbd9fb7df15 --- /dev/null +++ b/test/e2e/network/endpoints.go @@ -0,0 +1,229 @@ +/* +Copyright 2019 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 network + +import ( + "context" + "encoding/json" + "time" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/resourceversion" + watch "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" + watchtools "k8s.io/client-go/tools/watch" + apimachineryutils "k8s.io/kubernetes/test/e2e/common/apimachinery" + "k8s.io/kubernetes/test/e2e/framework" + "k8s.io/kubernetes/test/e2e/network/common" + admissionapi "k8s.io/pod-security-admission/api" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +var _ = common.SIGDescribe("Endpoints", func() { + f := framework.NewDefaultFramework("endpoints") + f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged + + /* + Release: v1.19 + Testname: Endpoint resource lifecycle + Description: Create an endpoint, the endpoint MUST exist. + The endpoint is updated with a new label, a check after the update MUST find the changes. + The endpoint is then patched with a new IPv4 address and port, a check after the patch MUST the changes. + The endpoint is deleted by its label, a watch listens for the deleted watch event. + */ + framework.ConformanceIt("should test the lifecycle of an Endpoint", func(ctx context.Context) { + testNamespaceName := f.Namespace.Name + testEndpointName := "testservice" + testEndpoints := v1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: testEndpointName, + Labels: map[string]string{ + "test-endpoint-static": "true", + }, + }, + Subsets: []v1.EndpointSubset{{ + Addresses: []v1.EndpointAddress{{ + IP: "10.0.0.24", + }}, + Ports: []v1.EndpointPort{{ + Name: "http", + Port: 80, + Protocol: v1.ProtocolTCP, + }}, + }}, + } + w := &cache.ListWatch{ + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + options.LabelSelector = "test-endpoint-static=true" + return f.ClientSet.CoreV1().Endpoints(testNamespaceName).Watch(ctx, options) + }, + } + endpointsList, err := f.ClientSet.CoreV1().Endpoints("").List(ctx, metav1.ListOptions{LabelSelector: "test-endpoint-static=true"}) + framework.ExpectNoError(err, "failed to list Endpoints") + + ginkgo.By("creating an Endpoint") + createdEP, err := f.ClientSet.CoreV1().Endpoints(testNamespaceName).Create(ctx, &testEndpoints, metav1.CreateOptions{}) + framework.ExpectNoError(err, "failed to create Endpoint") + gomega.Expect(createdEP).To(apimachineryutils.HaveValidResourceVersion()) + ginkgo.By("waiting for available Endpoint") + ctxUntil, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + _, err = watchtools.Until(ctxUntil, endpointsList.ResourceVersion, w, func(event watch.Event) (bool, error) { + switch event.Type { + case watch.Added: + if endpoints, ok := event.Object.(*v1.Endpoints); ok { + found := endpoints.ObjectMeta.Name == endpoints.Name && + endpoints.Labels["test-endpoint-static"] == "true" + return found, nil + } + default: + framework.Logf("observed event type %v", event.Type) + } + return false, nil + }) + framework.ExpectNoError(err, "failed to see %v event", watch.Added) + + ginkgo.By("listing all Endpoints") + endpointsList, err = f.ClientSet.CoreV1().Endpoints("").List(ctx, metav1.ListOptions{LabelSelector: "test-endpoint-static=true"}) + framework.ExpectNoError(err, "failed to list Endpoints") + eventFound := false + var foundEndpoint v1.Endpoints + for _, endpoint := range endpointsList.Items { + if endpoint.ObjectMeta.Name == testEndpointName && endpoint.ObjectMeta.Namespace == testNamespaceName { + eventFound = true + foundEndpoint = endpoint + break + } + } + if !eventFound { + framework.Fail("unable to find Endpoint Service in list of Endpoints") + } + + ginkgo.By("updating the Endpoint") + foundEndpoint.ObjectMeta.Labels["test-service"] = "updated" + _, err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).Update(ctx, &foundEndpoint, metav1.UpdateOptions{}) + framework.ExpectNoError(err, "failed to update Endpoint with new label") + + ctxUntil, cancel = context.WithTimeout(ctx, 30*time.Second) + defer cancel() + _, err = watchtools.Until(ctxUntil, endpointsList.ResourceVersion, w, func(event watch.Event) (bool, error) { + switch event.Type { + case watch.Modified: + if endpoints, ok := event.Object.(*v1.Endpoints); ok { + found := endpoints.ObjectMeta.Name == endpoints.Name && + endpoints.Labels["test-endpoint-static"] == "true" + return found, nil + } + default: + framework.Logf("observed event type %v", event.Type) + } + return false, nil + }) + framework.ExpectNoError(err, "failed to see %v event", watch.Modified) + + ginkgo.By("fetching the Endpoint") + endpoints, err := f.ClientSet.CoreV1().Endpoints(testNamespaceName).Get(ctx, testEndpointName, metav1.GetOptions{}) + framework.ExpectNoError(err, "failed to fetch Endpoint") + gomega.Expect(foundEndpoint.ObjectMeta.Labels).To(gomega.HaveKeyWithValue("test-service", "updated"), "failed to update Endpoint %v in namespace %v label not updated", testEndpointName, testNamespaceName) + + endpointPatch, err := json.Marshal(map[string]interface{}{ + "metadata": map[string]interface{}{ + "labels": map[string]string{ + "test-service": "patched", + }, + }, + "subsets": []map[string]interface{}{ + { + "addresses": []map[string]string{ + { + "ip": "10.0.0.25", + }, + }, + "ports": []map[string]interface{}{ + { + "name": "http-test", + "port": int32(8080), + }, + }, + }, + }, + }) + framework.ExpectNoError(err, "failed to marshal JSON for WatchEvent patch") + ginkgo.By("patching the Endpoint") + patchedEP, err := f.ClientSet.CoreV1().Endpoints(testNamespaceName).Patch(ctx, testEndpointName, types.StrategicMergePatchType, []byte(endpointPatch), metav1.PatchOptions{}) + framework.ExpectNoError(err, "failed to patch Endpoint") + gomega.Expect(resourceversion.CompareResourceVersion(createdEP.ResourceVersion, patchedEP.ResourceVersion)).To(gomega.BeNumerically("==", -1), "patched object should have a larger resource version") + + ctxUntil, cancel = context.WithTimeout(ctx, 30*time.Second) + defer cancel() + _, err = watchtools.Until(ctxUntil, endpoints.ResourceVersion, w, func(event watch.Event) (bool, error) { + switch event.Type { + case watch.Modified: + if endpoints, ok := event.Object.(*v1.Endpoints); ok { + found := endpoints.ObjectMeta.Name == endpoints.Name && + endpoints.Labels["test-endpoint-static"] == "true" + return found, nil + } + default: + framework.Logf("observed event type %v", event.Type) + } + return false, nil + }) + framework.ExpectNoError(err, "failed to see %v event", watch.Modified) + + ginkgo.By("fetching the Endpoint") + endpoints, err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).Get(ctx, testEndpointName, metav1.GetOptions{}) + framework.ExpectNoError(err, "failed to fetch Endpoint") + gomega.Expect(endpoints.ObjectMeta.Labels).To(gomega.HaveKeyWithValue("test-service", "patched"), "failed to patch Endpoint with Label") + endpointSubsetOne := endpoints.Subsets[0] + endpointSubsetOneAddresses := endpointSubsetOne.Addresses[0] + endpointSubsetOnePorts := endpointSubsetOne.Ports[0] + gomega.Expect(endpointSubsetOneAddresses.IP).To(gomega.Equal("10.0.0.25"), "failed to patch Endpoint") + gomega.Expect(endpointSubsetOnePorts.Name).To(gomega.Equal("http-test"), "failed to patch Endpoint") + gomega.Expect(endpointSubsetOnePorts.Port).To(gomega.Equal(int32(8080)), "failed to patch Endpoint") + + ginkgo.By("deleting the Endpoint by Collection") + err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: "test-endpoint-static=true"}) + framework.ExpectNoError(err, "failed to delete Endpoint by Collection") + + ginkgo.By("waiting for Endpoint deletion") + ctxUntil, cancel = context.WithTimeout(ctx, 30*time.Second) + defer cancel() + _, err = watchtools.Until(ctxUntil, endpoints.ResourceVersion, w, func(event watch.Event) (bool, error) { + switch event.Type { + case watch.Deleted: + if endpoints, ok := event.Object.(*v1.Endpoints); ok { + found := endpoints.ObjectMeta.Name == endpoints.Name && + endpoints.Labels["test-endpoint-static"] == "true" + return found, nil + } + default: + framework.Logf("observed event type %v", event.Type) + } + return false, nil + }) + framework.ExpectNoError(err, "failed to see %v event", watch.Deleted) + + ginkgo.By("fetching the Endpoint") + _, err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).Get(ctx, testEndpointName, metav1.GetOptions{}) + gomega.Expect(err).To(gomega.HaveOccurred(), "should not be able to fetch Endpoint") + }) +}) diff --git a/test/e2e/network/endpointslice.go b/test/e2e/network/endpointslice.go index 463fa0cb08e..400257b7f82 100644 --- a/test/e2e/network/endpointslice.go +++ b/test/e2e/network/endpointslice.go @@ -59,39 +59,6 @@ var _ = common.SIGDescribe("EndpointSlice", func() { podClient = e2epod.NewPodClient(f) }) - /* - Release: v1.21 - Testname: kubernetes.default Endpoints and EndpointSlices - Description: The "kubernetes.default" service MUST have Endpoints and EndpointSlices pointing to each API server instance. - */ - framework.ConformanceIt("should have Endpoints and EndpointSlices pointing to API Server", func(ctx context.Context) { - namespace := "default" - name := "kubernetes" - // verify "kubernetes.default" service exist - _, err := cs.CoreV1().Services(namespace).Get(ctx, name, metav1.GetOptions{}) - framework.ExpectNoError(err, "error obtaining API server \"kubernetes\" Service resource on \"default\" namespace") - - // verify Endpoints for the API servers exist - endpoints, err := cs.CoreV1().Endpoints(namespace).Get(ctx, name, metav1.GetOptions{}) - framework.ExpectNoError(err, "error obtaining API server \"kubernetes\" Endpoint resource on \"default\" namespace") - if len(endpoints.Subsets) == 0 { - framework.Failf("Expected at least 1 subset in endpoints, got %d: %#v", len(endpoints.Subsets), endpoints.Subsets) - } - // verify EndpointSlices for the API servers exist - endpointSliceList, err := cs.DiscoveryV1().EndpointSlices(namespace).List(ctx, metav1.ListOptions{ - LabelSelector: "kubernetes.io/service-name=" + name, - }) - framework.ExpectNoError(err, "error obtaining API server \"kubernetes\" EndpointSlice resource on \"default\" namespace") - if len(endpointSliceList.Items) == 0 { - framework.Failf("Expected at least 1 EndpointSlice, got %d: %#v", len(endpoints.Subsets), endpoints.Subsets) - } - - if !endpointSlicesEqual(endpoints, endpointSliceList) { - framework.Failf("Expected EndpointSlice to have same addresses and port as Endpoints, got %#v: %#v", endpoints, endpointSliceList) - } - - }) - /* Release: v1.21 Testname: EndpointSlice, "empty" Service @@ -1017,57 +984,3 @@ func ensurePodTargetRef(pod *v1.Pod, targetRef *v1.ObjectReference) { framework.Failf("Expected TargetRef.UID to be %s, got %s", pod.UID, targetRef.UID) } } - -// endpointSlicesEqual compare if the Endpoint and the EndpointSliceList contains the same endpoints values -// as in addresses and ports, considering Ready and Unready addresses -func endpointSlicesEqual(endpoints *v1.Endpoints, endpointSliceList *discoveryv1.EndpointSliceList) bool { - // get the apiserver endpoint addresses - epAddresses := sets.NewString() - epPorts := sets.NewInt32() - for _, subset := range endpoints.Subsets { - for _, addr := range subset.Addresses { - epAddresses.Insert(addr.IP) - } - for _, addr := range subset.NotReadyAddresses { - epAddresses.Insert(addr.IP) - } - for _, port := range subset.Ports { - epPorts.Insert(port.Port) - } - } - framework.Logf("Endpoints addresses: %v , ports: %v", epAddresses.List(), epPorts.List()) - - // Endpoints are single stack, and must match the primary IP family of the Service kubernetes.default - // However, EndpointSlices can be IPv4 or IPv6, we can only compare the Slices that match the same IP family - // framework.TestContext.ClusterIsIPv6() reports the IP family of the kubernetes.default service - var addrType discoveryv1.AddressType - if framework.TestContext.ClusterIsIPv6() { - addrType = discoveryv1.AddressTypeIPv6 - } else { - addrType = discoveryv1.AddressTypeIPv4 - } - - // get the apiserver addresses from the endpoint slice list - sliceAddresses := sets.NewString() - slicePorts := sets.NewInt32() - for _, slice := range endpointSliceList.Items { - if slice.AddressType != addrType { - framework.Logf("Skipping slice %s: wanted %s family, got %s", slice.Name, addrType, slice.AddressType) - continue - } - for _, s := range slice.Endpoints { - sliceAddresses.Insert(s.Addresses...) - } - for _, ports := range slice.Ports { - if ports.Port != nil { - slicePorts.Insert(*ports.Port) - } - } - } - - framework.Logf("EndpointSlices addresses: %v , ports: %v", sliceAddresses.List(), slicePorts.List()) - if sliceAddresses.Equal(epAddresses) && slicePorts.Equal(epPorts) { - return true - } - return false -} diff --git a/test/e2e/network/service.go b/test/e2e/network/service.go index 8d30ebe885f..58708f11eb8 100644 --- a/test/e2e/network/service.go +++ b/test/e2e/network/service.go @@ -724,16 +724,6 @@ var _ = common.SIGDescribe("Services", func() { // TODO: We get coverage of TCP/UDP and multi-port services through the DNS test. We should have a simpler test for multi-port TCP here. - /* - Release: v1.9 - Testname: Kubernetes Service - Description: By default when a kubernetes cluster is running there MUST be a 'kubernetes' service running in the cluster. - */ - framework.ConformanceIt("should provide secure master service", func(ctx context.Context) { - _, err := cs.CoreV1().Services(metav1.NamespaceDefault).Get(ctx, "kubernetes", metav1.GetOptions{}) - framework.ExpectNoError(err, "failed to fetch the service object for the service named kubernetes") - }) - /* Release: v1.9 Testname: Service, endpoints @@ -3244,192 +3234,6 @@ var _ = common.SIGDescribe("Services", func() { } }) - /* - Release: v1.19 - Testname: Endpoint resource lifecycle - Description: Create an endpoint, the endpoint MUST exist. - The endpoint is updated with a new label, a check after the update MUST find the changes. - The endpoint is then patched with a new IPv4 address and port, a check after the patch MUST the changes. - The endpoint is deleted by it's label, a watch listens for the deleted watch event. - */ - framework.ConformanceIt("should test the lifecycle of an Endpoint", func(ctx context.Context) { - testNamespaceName := f.Namespace.Name - testEndpointName := "testservice" - testEndpoints := v1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: testEndpointName, - Labels: map[string]string{ - "test-endpoint-static": "true", - }, - }, - Subsets: []v1.EndpointSubset{{ - Addresses: []v1.EndpointAddress{{ - IP: "10.0.0.24", - }}, - Ports: []v1.EndpointPort{{ - Name: "http", - Port: 80, - Protocol: v1.ProtocolTCP, - }}, - }}, - } - w := &cache.ListWatch{ - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - options.LabelSelector = "test-endpoint-static=true" - return f.ClientSet.CoreV1().Endpoints(testNamespaceName).Watch(ctx, options) - }, - } - endpointsList, err := f.ClientSet.CoreV1().Endpoints("").List(ctx, metav1.ListOptions{LabelSelector: "test-endpoint-static=true"}) - framework.ExpectNoError(err, "failed to list Endpoints") - - ginkgo.By("creating an Endpoint") - createdEP, err := f.ClientSet.CoreV1().Endpoints(testNamespaceName).Create(ctx, &testEndpoints, metav1.CreateOptions{}) - framework.ExpectNoError(err, "failed to create Endpoint") - gomega.Expect(createdEP).To(apimachineryutils.HaveValidResourceVersion()) - ginkgo.By("waiting for available Endpoint") - ctxUntil, cancel := context.WithTimeout(ctx, 30*time.Second) - defer cancel() - _, err = watchtools.Until(ctxUntil, endpointsList.ResourceVersion, w, func(event watch.Event) (bool, error) { - switch event.Type { - case watch.Added: - if endpoints, ok := event.Object.(*v1.Endpoints); ok { - found := endpoints.ObjectMeta.Name == endpoints.Name && - endpoints.Labels["test-endpoint-static"] == "true" - return found, nil - } - default: - framework.Logf("observed event type %v", event.Type) - } - return false, nil - }) - framework.ExpectNoError(err, "failed to see %v event", watch.Added) - - ginkgo.By("listing all Endpoints") - endpointsList, err = f.ClientSet.CoreV1().Endpoints("").List(ctx, metav1.ListOptions{LabelSelector: "test-endpoint-static=true"}) - framework.ExpectNoError(err, "failed to list Endpoints") - eventFound := false - var foundEndpoint v1.Endpoints - for _, endpoint := range endpointsList.Items { - if endpoint.ObjectMeta.Name == testEndpointName && endpoint.ObjectMeta.Namespace == testNamespaceName { - eventFound = true - foundEndpoint = endpoint - break - } - } - if !eventFound { - framework.Fail("unable to find Endpoint Service in list of Endpoints") - } - - ginkgo.By("updating the Endpoint") - foundEndpoint.ObjectMeta.Labels["test-service"] = "updated" - _, err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).Update(ctx, &foundEndpoint, metav1.UpdateOptions{}) - framework.ExpectNoError(err, "failed to update Endpoint with new label") - - ctxUntil, cancel = context.WithTimeout(ctx, 30*time.Second) - defer cancel() - _, err = watchtools.Until(ctxUntil, endpointsList.ResourceVersion, w, func(event watch.Event) (bool, error) { - switch event.Type { - case watch.Modified: - if endpoints, ok := event.Object.(*v1.Endpoints); ok { - found := endpoints.ObjectMeta.Name == endpoints.Name && - endpoints.Labels["test-endpoint-static"] == "true" - return found, nil - } - default: - framework.Logf("observed event type %v", event.Type) - } - return false, nil - }) - framework.ExpectNoError(err, "failed to see %v event", watch.Modified) - - ginkgo.By("fetching the Endpoint") - endpoints, err := f.ClientSet.CoreV1().Endpoints(testNamespaceName).Get(ctx, testEndpointName, metav1.GetOptions{}) - framework.ExpectNoError(err, "failed to fetch Endpoint") - gomega.Expect(foundEndpoint.ObjectMeta.Labels).To(gomega.HaveKeyWithValue("test-service", "updated"), "failed to update Endpoint %v in namespace %v label not updated", testEndpointName, testNamespaceName) - - endpointPatch, err := json.Marshal(map[string]interface{}{ - "metadata": map[string]interface{}{ - "labels": map[string]string{ - "test-service": "patched", - }, - }, - "subsets": []map[string]interface{}{ - { - "addresses": []map[string]string{ - { - "ip": "10.0.0.25", - }, - }, - "ports": []map[string]interface{}{ - { - "name": "http-test", - "port": int32(8080), - }, - }, - }, - }, - }) - framework.ExpectNoError(err, "failed to marshal JSON for WatchEvent patch") - ginkgo.By("patching the Endpoint") - patchedEP, err := f.ClientSet.CoreV1().Endpoints(testNamespaceName).Patch(ctx, testEndpointName, types.StrategicMergePatchType, []byte(endpointPatch), metav1.PatchOptions{}) - framework.ExpectNoError(err, "failed to patch Endpoint") - gomega.Expect(resourceversion.CompareResourceVersion(createdEP.ResourceVersion, patchedEP.ResourceVersion)).To(gomega.BeNumerically("==", -1), "patched object should have a larger resource version") - - ctxUntil, cancel = context.WithTimeout(ctx, 30*time.Second) - defer cancel() - _, err = watchtools.Until(ctxUntil, endpoints.ResourceVersion, w, func(event watch.Event) (bool, error) { - switch event.Type { - case watch.Modified: - if endpoints, ok := event.Object.(*v1.Endpoints); ok { - found := endpoints.ObjectMeta.Name == endpoints.Name && - endpoints.Labels["test-endpoint-static"] == "true" - return found, nil - } - default: - framework.Logf("observed event type %v", event.Type) - } - return false, nil - }) - framework.ExpectNoError(err, "failed to see %v event", watch.Modified) - - ginkgo.By("fetching the Endpoint") - endpoints, err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).Get(ctx, testEndpointName, metav1.GetOptions{}) - framework.ExpectNoError(err, "failed to fetch Endpoint") - gomega.Expect(endpoints.ObjectMeta.Labels).To(gomega.HaveKeyWithValue("test-service", "patched"), "failed to patch Endpoint with Label") - endpointSubsetOne := endpoints.Subsets[0] - endpointSubsetOneAddresses := endpointSubsetOne.Addresses[0] - endpointSubsetOnePorts := endpointSubsetOne.Ports[0] - gomega.Expect(endpointSubsetOneAddresses.IP).To(gomega.Equal("10.0.0.25"), "failed to patch Endpoint") - gomega.Expect(endpointSubsetOnePorts.Name).To(gomega.Equal("http-test"), "failed to patch Endpoint") - gomega.Expect(endpointSubsetOnePorts.Port).To(gomega.Equal(int32(8080)), "failed to patch Endpoint") - - ginkgo.By("deleting the Endpoint by Collection") - err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: "test-endpoint-static=true"}) - framework.ExpectNoError(err, "failed to delete Endpoint by Collection") - - ginkgo.By("waiting for Endpoint deletion") - ctxUntil, cancel = context.WithTimeout(ctx, 30*time.Second) - defer cancel() - _, err = watchtools.Until(ctxUntil, endpoints.ResourceVersion, w, func(event watch.Event) (bool, error) { - switch event.Type { - case watch.Deleted: - if endpoints, ok := event.Object.(*v1.Endpoints); ok { - found := endpoints.ObjectMeta.Name == endpoints.Name && - endpoints.Labels["test-endpoint-static"] == "true" - return found, nil - } - default: - framework.Logf("observed event type %v", event.Type) - } - return false, nil - }) - framework.ExpectNoError(err, "failed to see %v event", watch.Deleted) - - ginkgo.By("fetching the Endpoint") - _, err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).Get(ctx, testEndpointName, metav1.GetOptions{}) - gomega.Expect(err).To(gomega.HaveOccurred(), "should not be able to fetch Endpoint") - }) - /* Release: v1.21 Testname: Service, complete ServiceStatus lifecycle