mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-06-08 16:30:57 -04:00
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:
parent
d46f15bd12
commit
e627b3b2bf
5 changed files with 391 additions and 305 deletions
44
test/conformance/testdata/conformance.yaml
vendored
44
test/conformance/testdata/conformance.yaml
vendored
|
|
@ -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]'
|
||||
|
|
|
|||
140
test/e2e/network/apiserver.go
Normal file
140
test/e2e/network/apiserver.go
Normal 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
|
||||
}
|
||||
229
test/e2e/network/endpoints.go
Normal file
229
test/e2e/network/endpoints.go
Normal 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")
|
||||
})
|
||||
})
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue