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.)
This commit is contained in:
Dan Winship 2025-06-02 10:39:07 -04:00
parent d46f15bd12
commit e627b3b2bf
5 changed files with 391 additions and 305 deletions

View file

@ -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]'

View file

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

View file

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

View file

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

View file

@ -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