1 package destination
2
3 import (
4 "context"
5 "testing"
6 "time"
7
8 pb "github.com/linkerd/linkerd2-proxy-api/go/destination"
9 "github.com/linkerd/linkerd2/controller/api/util"
10 corev1 "k8s.io/api/core/v1"
11 discovery "k8s.io/api/discovery/v1"
12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13 "k8s.io/apimachinery/pkg/types"
14 )
15
16 func TestIPv6(t *testing.T) {
17 port := int32(port)
18 protocol := corev1.ProtocolTCP
19
20 server := makeServer(t)
21 defer server.clusterStore.UnregisterGauges()
22
23 stream := &bufferingGetStream{
24 updates: make(chan *pb.Update, 50),
25 MockServerStream: util.NewMockServerStream(),
26 }
27 defer stream.Cancel()
28
29 t.Run("Return only IPv6 endpoint for dual-stack service", func(t *testing.T) {
30 testReturnEndpointsForServer(t, server, stream, fullyQualifiedNameDual, podIPv6Dual, uint32(port))
31 })
32
33 t.Run("Returns only IPv4 endpoint when service becomes single-stack IPv4", func(t *testing.T) {
34 patch := []byte(`{"spec":{"clusterIPs": ["172.17.13.0"], "ipFamilies":["IPv4"]}}`)
35 _, err := server.k8sAPI.Client.CoreV1().Services("ns").Patch(context.Background(), "name-ds", types.MergePatchType, patch, metav1.PatchOptions{})
36 if err != nil {
37 t.Fatalf("Failed patching name-ds service: %s", err)
38 }
39 if err = server.k8sAPI.Client.DiscoveryV1().EndpointSlices("ns").Delete(context.Background(), "name-ds-ipv6", metav1.DeleteOptions{}); err != nil {
40 t.Fatalf("Failed deleting name-ds-ipv6 ES: %s", err)
41 }
42
43 update := <-stream.updates
44 if updateAddAddress(t, update)[0] != "172.17.0.19:8989" {
45 t.Fatalf("Expected %s but got %s", "172.17.0.19:8989", updateAddAddress(t, update)[0])
46 }
47
48 update = <-stream.updates
49 if updateRemoveAddress(t, update)[0] != "[2001:db8::94]:8989" {
50 t.Fatalf("Expected %s but got %s", "[2001:db8::94]:8989", updateRemoveAddress(t, update)[0])
51 }
52 })
53
54 t.Run("Returns only IPv6 endpoint when service becomes dual-stack again", func(t *testing.T) {
55
56
57
58 patch := []byte(`{"spec":{"clusterIPs": ["172.17.13.0","2001:db8::88"], "ipFamilies":["IPv4","IPv6"]}}`)
59 _, err := server.k8sAPI.Client.CoreV1().Services("ns").Patch(context.Background(), "name-ds", types.MergePatchType, patch, metav1.PatchOptions{})
60 if err != nil {
61 t.Fatalf("Failed patching name-ds service: %s", err)
62 }
63
64 es := &discovery.EndpointSlice{
65 TypeMeta: metav1.TypeMeta{
66 Kind: "EndpointSlice",
67 APIVersion: "discovery.k8s.io/v1",
68 },
69 ObjectMeta: metav1.ObjectMeta{
70 Name: "name-ds-ipv6",
71 Namespace: "ns",
72 Labels: map[string]string{
73 "kubernetes.io/service-name": "name-ds",
74 },
75 },
76 AddressType: discovery.AddressTypeIPv6,
77 Ports: []discovery.EndpointPort{
78 {
79 Port: &port,
80 Protocol: &protocol,
81 },
82 },
83 Endpoints: []discovery.Endpoint{
84 {
85 Addresses: []string{"2001:db8::94"},
86 TargetRef: &corev1.ObjectReference{
87 Kind: "Pod",
88 Namespace: "ns",
89 Name: "name-ds",
90 },
91 },
92 },
93 }
94 if _, err := server.k8sAPI.Client.DiscoveryV1().EndpointSlices("ns").Create(context.Background(), es, metav1.CreateOptions{}); err != nil {
95 t.Fatalf("Failed creating name-ds-ipv6 ES: %s", err)
96 }
97
98 update := <-stream.updates
99 if updateAddAddress(t, update)[0] != "[2001:db8::94]:8989" {
100 t.Fatalf("Expected %s but got %s", "[2001:db8::94]:8989", updateAddAddress(t, update)[0])
101 }
102
103 update = <-stream.updates
104 if updateRemoveAddress(t, update)[0] != "172.17.0.19:8989" {
105 t.Fatalf("Expected %s but got %s", "172.17.0.19:8989", updateRemoveAddress(t, update)[0])
106 }
107 })
108
109 t.Run("Doesn't return anything when adding an IPv4 to the dual-stack service", func(t *testing.T) {
110 es := &discovery.EndpointSlice{
111 TypeMeta: metav1.TypeMeta{
112 Kind: "EndpointSlice",
113 APIVersion: "discovery.k8s.io/v1",
114 },
115 ObjectMeta: metav1.ObjectMeta{
116 Name: "name-ds-ipv4-2",
117 Namespace: "ns",
118 Labels: map[string]string{
119 "kubernetes.io/service-name": "name-ds",
120 },
121 },
122 AddressType: discovery.AddressTypeIPv4,
123 Ports: []discovery.EndpointPort{
124 {
125 Port: &port,
126 Protocol: &protocol,
127 },
128 },
129 Endpoints: []discovery.Endpoint{
130 {
131 Addresses: []string{"172.17.0.20"},
132 TargetRef: &corev1.ObjectReference{
133 Kind: "Pod",
134 Namespace: "ns",
135 Name: "name-ds",
136 },
137 },
138 },
139 }
140 if _, err := server.k8sAPI.Client.DiscoveryV1().EndpointSlices("ns").Create(context.Background(), es, metav1.CreateOptions{}); err != nil {
141 t.Fatalf("Failed creating name-ds-ipv4-2 ES: %s", err)
142 }
143
144 time.Sleep(50 * time.Millisecond)
145
146 if len(stream.updates) != 0 {
147 t.Fatalf("Expected no events but got %#v", stream.updates)
148 }
149 })
150
151 t.Run("Doesn't return anything when removing an IPv4 ES from the dual-stack service", func(t *testing.T) {
152 if err := server.k8sAPI.Client.DiscoveryV1().EndpointSlices("ns").Delete(context.Background(), "name-ds-ipv4-2", metav1.DeleteOptions{}); err != nil {
153 t.Fatalf("Failed deleting name-ds-ipv4-2 ES: %s", err)
154 }
155
156 time.Sleep(50 * time.Millisecond)
157
158 if len(stream.updates) != 0 {
159 t.Fatalf("Expected no events but got %#v", stream.updates)
160 }
161 })
162
163 t.Run("Doesn't return anything when the service becomes single-stack IPv6", func(t *testing.T) {
164 patch := []byte(`{"spec":{"clusterIPs": ["2001:db8::88"], "ipFamilies":["IPv6"]}}`)
165 _, err := server.k8sAPI.Client.CoreV1().Services("ns").Patch(context.Background(), "name-ds", types.MergePatchType, patch, metav1.PatchOptions{})
166 if err != nil {
167 t.Fatalf("Failed patching name-ds service: %s", err)
168 }
169 if err := server.k8sAPI.Client.DiscoveryV1().EndpointSlices("ns").Delete(context.Background(), "name-ds-ipv4", metav1.DeleteOptions{}); err != nil {
170 t.Fatalf("Failed deleting name-ds-ipv4 ES: %s", err)
171 }
172
173 time.Sleep(50 * time.Millisecond)
174
175 if len(stream.updates) != 0 {
176 t.Fatalf("Expected no events but got %#v", stream.updates)
177 }
178 })
179 }
180
View as plain text