Merge pull request #98116 from aojea/mirror_dual

slice mirroring controller should mirror annotations (but endpoints.kubernetes.io/last-change-trigger-time annotation) and labels
This commit is contained in:
Kubernetes Prow Robot
2021-03-08 20:47:12 -08:00
committed by GitHub
5 changed files with 462 additions and 25 deletions

View File

@@ -19,11 +19,14 @@ package endpointslice
import (
"context"
"fmt"
"sort"
"testing"
"time"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
discovery "k8s.io/api/discovery/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/informers"
@@ -194,7 +197,7 @@ func TestEndpointSliceMirroring(t *testing.T) {
err = wait.PollImmediate(1*time.Second, wait.ForeverTestTimeout, func() (bool, error) {
lSelector := discovery.LabelServiceName + "=" + resourceName
esList, err := client.DiscoveryV1beta1().EndpointSlices(ns.Name).List(context.TODO(), metav1.ListOptions{LabelSelector: lSelector})
esList, err := client.DiscoveryV1().EndpointSlices(ns.Name).List(context.TODO(), metav1.ListOptions{LabelSelector: lSelector})
if err != nil {
t.Logf("Error listing EndpointSlices: %v", err)
return false, err
@@ -228,3 +231,192 @@ func TestEndpointSliceMirroring(t *testing.T) {
}
}
func TestEndpointSliceMirroringUpdates(t *testing.T) {
masterConfig := framework.NewIntegrationTestMasterConfig()
_, server, closeFn := framework.RunAMaster(masterConfig)
defer closeFn()
config := restclient.Config{Host: server.URL}
client, err := clientset.NewForConfig(&config)
if err != nil {
t.Fatalf("Error creating clientset: %v", err)
}
resyncPeriod := 12 * time.Hour
informers := informers.NewSharedInformerFactory(client, resyncPeriod)
epsmController := endpointslicemirroring.NewController(
informers.Core().V1().Endpoints(),
informers.Discovery().V1().EndpointSlices(),
informers.Core().V1().Services(),
int32(100),
client,
1*time.Second)
// Start informer and controllers
stopCh := make(chan struct{})
defer close(stopCh)
informers.Start(stopCh)
go epsmController.Run(1, stopCh)
testCases := []struct {
testName string
tweakEndpoint func(ep *corev1.Endpoints)
}{
{
testName: "Update labels",
tweakEndpoint: func(ep *corev1.Endpoints) {
ep.Labels["foo"] = "bar"
},
},
{
testName: "Update annotations",
tweakEndpoint: func(ep *corev1.Endpoints) {
ep.Annotations["foo2"] = "bar2"
},
},
{
testName: "Update annotations but triggertime",
tweakEndpoint: func(ep *corev1.Endpoints) {
ep.Annotations["foo2"] = "bar2"
ep.Annotations[corev1.EndpointsLastChangeTriggerTime] = "date"
},
},
{
testName: "Update addresses",
tweakEndpoint: func(ep *corev1.Endpoints) {
ep.Subsets[0].Addresses = []v1.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.6"}}
},
},
}
for i, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) {
ns := framework.CreateTestingNamespace(fmt.Sprintf("test-endpointslice-mirroring-%d", i), server, t)
defer framework.DeleteTestingNamespace(ns, server, t)
service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-123",
Namespace: ns.Name,
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{{
Port: int32(80),
}},
},
}
customEndpoints := &corev1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: "test-123",
Namespace: ns.Name,
Labels: map[string]string{},
Annotations: map[string]string{},
},
Subsets: []corev1.EndpointSubset{{
Ports: []corev1.EndpointPort{{
Port: 80,
}},
Addresses: []corev1.EndpointAddress{{
IP: "10.0.0.1",
}},
}},
}
_, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{})
if err != nil {
t.Fatalf("Error creating service: %v", err)
}
_, err = client.CoreV1().Endpoints(ns.Name).Create(context.TODO(), customEndpoints, metav1.CreateOptions{})
if err != nil {
t.Fatalf("Error creating endpoints: %v", err)
}
// update endpoint
tc.tweakEndpoint(customEndpoints)
_, err = client.CoreV1().Endpoints(ns.Name).Update(context.TODO(), customEndpoints, metav1.UpdateOptions{})
if err != nil {
t.Fatalf("Error updating endpoints: %v", err)
}
// verify the endpoint updates were mirrored
err = wait.PollImmediate(1*time.Second, wait.ForeverTestTimeout, func() (bool, error) {
lSelector := discovery.LabelServiceName + "=" + service.Name
esList, err := client.DiscoveryV1().EndpointSlices(ns.Name).List(context.TODO(), metav1.ListOptions{LabelSelector: lSelector})
if err != nil {
t.Logf("Error listing EndpointSlices: %v", err)
return false, err
}
if len(esList.Items) == 0 {
t.Logf("Waiting for EndpointSlice to be created")
return false, nil
}
for _, endpointSlice := range esList.Items {
if endpointSlice.Labels[discovery.LabelManagedBy] != "endpointslicemirroring-controller.k8s.io" {
return false, fmt.Errorf("Expected EndpointSlice to be managed by endpointslicemirroring-controller.k8s.io, got %s", endpointSlice.Labels[discovery.LabelManagedBy])
}
// compare addresses
epAddresses := []string{}
for _, address := range customEndpoints.Subsets[0].Addresses {
epAddresses = append(epAddresses, address.IP)
}
sliceAddresses := []string{}
for _, sliceEndpoint := range endpointSlice.Endpoints {
sliceAddresses = append(sliceAddresses, sliceEndpoint.Addresses...)
}
sort.Strings(epAddresses)
sort.Strings(sliceAddresses)
if !apiequality.Semantic.DeepEqual(epAddresses, sliceAddresses) {
t.Logf("Expected EndpointSlice to have the same IP addresses, expected %v got %v", epAddresses, sliceAddresses)
return false, nil
}
// check labels were mirrored
if !isSubset(customEndpoints.Labels, endpointSlice.Labels) {
t.Logf("Expected EndpointSlice to mirror labels, expected %v to be in received %v", customEndpoints.Labels, endpointSlice.Labels)
return false, nil
}
// check annotations but endpoints.kubernetes.io/last-change-trigger-time were mirrored
annotations := map[string]string{}
for k, v := range customEndpoints.Annotations {
if k == corev1.EndpointsLastChangeTriggerTime {
continue
}
annotations[k] = v
}
if !apiequality.Semantic.DeepEqual(annotations, endpointSlice.Annotations) {
t.Logf("Expected EndpointSlice to mirror annotations, expected %v received %v", customEndpoints.Annotations, endpointSlice.Annotations)
return false, nil
}
}
return true, nil
})
if err != nil {
t.Fatalf("Timed out waiting for conditions: %v", err)
}
})
}
}
// isSubset check if all the elements in a exist in b
func isSubset(a, b map[string]string) bool {
if len(a) > len(b) {
return false
}
for k, v1 := range a {
if v2, ok := b[k]; !ok || v1 != v2 {
return false
}
}
return true
}