1
16
17 package servicecidr
18
19 import (
20 "context"
21 "fmt"
22 "strings"
23 "testing"
24 "time"
25
26 v1 "k8s.io/api/core/v1"
27 apierrors "k8s.io/apimachinery/pkg/api/errors"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/runtime"
30 "k8s.io/apimachinery/pkg/util/wait"
31 utilfeature "k8s.io/apiserver/pkg/util/feature"
32 "k8s.io/client-go/kubernetes"
33 featuregatetesting "k8s.io/component-base/featuregate/testing"
34 "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
35 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
36 "k8s.io/kubernetes/pkg/api/legacyscheme"
37 "k8s.io/kubernetes/pkg/features"
38 "k8s.io/kubernetes/test/integration/framework"
39 "k8s.io/kubernetes/test/utils/ktesting"
40 netutils "k8s.io/utils/net"
41 )
42
43 func TestServiceAlloc(t *testing.T) {
44
45 serviceCIDR := "192.168.0.0/29"
46
47 tCtx := ktesting.Init(t)
48 client, _, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{
49 ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
50 opts.ServiceClusterIPRanges = serviceCIDR
51 },
52 })
53 defer tearDownFn()
54
55 svc := func(i int) *v1.Service {
56 return &v1.Service{
57 ObjectMeta: metav1.ObjectMeta{
58 Name: fmt.Sprintf("svc-%v", i),
59 },
60 Spec: v1.ServiceSpec{
61 Type: v1.ServiceTypeClusterIP,
62 Ports: []v1.ServicePort{
63 {Port: 80},
64 },
65 },
66 }
67 }
68
69
70 if err := wait.Poll(250*time.Millisecond, time.Minute, func() (bool, error) {
71 _, err := client.CoreV1().Services(metav1.NamespaceDefault).Get(context.TODO(), "kubernetes", metav1.GetOptions{})
72 if err != nil && !apierrors.IsNotFound(err) {
73 return false, err
74 }
75 return !apierrors.IsNotFound(err), nil
76 }); err != nil {
77 t.Fatalf("creating kubernetes service timed out")
78 }
79
80
81 for i := 0; i < 5; i++ {
82 if _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.TODO(), svc(i), metav1.CreateOptions{}); err != nil {
83 t.Error(err)
84 }
85 }
86
87
88 if _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.TODO(), svc(8), metav1.CreateOptions{}); err != nil {
89 if !strings.Contains(err.Error(), "range is full") {
90 t.Errorf("unexpected error text: %v", err)
91 }
92 } else {
93 svcs, err := client.CoreV1().Services(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
94 if err != nil {
95 t.Fatalf("unexpected success, and error getting the services: %v", err)
96 }
97 allIPs := []string{}
98 for _, s := range svcs.Items {
99 allIPs = append(allIPs, s.Spec.ClusterIP)
100 }
101 t.Fatalf("unexpected creation success. The following IPs exist: %#v. It should only be possible to allocate 2 IP addresses in this cluster.\n\n%#v", allIPs, svcs)
102 }
103
104
105 if err := client.CoreV1().Services(metav1.NamespaceDefault).Delete(context.TODO(), svc(1).ObjectMeta.Name, metav1.DeleteOptions{}); err != nil {
106 t.Fatalf("got unexpected error: %v", err)
107 }
108
109
110 if _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.TODO(), svc(8), metav1.CreateOptions{}); err != nil {
111 t.Fatalf("got unexpected error: %v", err)
112 }
113 }
114
115 func TestServiceAllocIPAddress(t *testing.T) {
116
117 serviceCIDR := "2001:db8::/64"
118 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MultiCIDRServiceAllocator, true)()
119
120 tCtx := ktesting.Init(t)
121 client, _, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{
122 ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
123 opts.ServiceClusterIPRanges = serviceCIDR
124 opts.GenericServerRunOptions.AdvertiseAddress = netutils.ParseIPSloppy("2001:db8::10")
125 opts.APIEnablement.RuntimeConfig.Set("networking.k8s.io/v1alpha1=true")
126 },
127 })
128 defer tearDownFn()
129
130 svc := func(i int) *v1.Service {
131 return &v1.Service{
132 ObjectMeta: metav1.ObjectMeta{
133 Name: fmt.Sprintf("svc-%v", i),
134 },
135 Spec: v1.ServiceSpec{
136 Type: v1.ServiceTypeClusterIP,
137 Ports: []v1.ServicePort{
138 {Port: 80},
139 },
140 },
141 }
142 }
143
144
145 if err := wait.Poll(250*time.Millisecond, time.Minute, func() (bool, error) {
146 _, err := client.CoreV1().Services(metav1.NamespaceDefault).Get(tCtx, "kubernetes", metav1.GetOptions{})
147 if err != nil && !apierrors.IsNotFound(err) {
148 return false, err
149 }
150 return !apierrors.IsNotFound(err), nil
151 }); err != nil {
152 t.Fatalf("creating kubernetes service timed out")
153 }
154
155
156 for i := 0; i < 5; i++ {
157 svc, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(tCtx, svc(i), metav1.CreateOptions{})
158 if err != nil {
159 t.Error(err)
160 }
161 _, err = client.NetworkingV1alpha1().IPAddresses().Get(tCtx, svc.Spec.ClusterIP, metav1.GetOptions{})
162 if err != nil {
163 t.Error(err)
164 }
165 }
166
167
168
169 lastSvc := svc(8)
170 lastSvc.Spec.ClusterIP = "2001:db8::ffff:ffff:ffff:ffff"
171 if _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.TODO(), lastSvc, metav1.CreateOptions{}); err != nil {
172 t.Errorf("unexpected error text: %v", err)
173 }
174 _, err := client.NetworkingV1alpha1().IPAddresses().Get(context.TODO(), lastSvc.Spec.ClusterIP, metav1.GetOptions{})
175 if err != nil {
176 t.Error(err)
177 }
178
179 }
180
181 func TestMigrateService(t *testing.T) {
182 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MultiCIDRServiceAllocator, true)()
183
184
185 etcdOptions := framework.SharedEtcd()
186 apiServerOptions := kubeapiservertesting.NewDefaultTestServerOptions()
187 s := kubeapiservertesting.StartTestServerOrDie(t,
188 apiServerOptions,
189 []string{
190 "--runtime-config=networking.k8s.io/v1alpha1=true",
191 "--service-cluster-ip-range=10.0.0.0/24",
192 "--advertise-address=10.1.1.1",
193 "--disable-admission-plugins=ServiceAccount",
194 },
195 etcdOptions)
196 defer s.TearDownFn()
197 serviceName := "test-old-service"
198 namespace := "old-service-ns"
199
200 svc := &v1.Service{
201 ObjectMeta: metav1.ObjectMeta{
202 Name: serviceName,
203 Namespace: namespace,
204 CreationTimestamp: metav1.Now(),
205 UID: "08675309-9376-9376-9376-086753099999",
206 },
207 Spec: v1.ServiceSpec{
208 ClusterIP: "10.0.0.11",
209 Ports: []v1.ServicePort{
210 {
211 Name: "test-port",
212 Port: 81,
213 },
214 },
215 },
216 }
217 svcJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), svc)
218 if err != nil {
219 t.Fatalf("Failed creating service JSON: %v", err)
220 }
221 key := "/" + etcdOptions.Prefix + "/services/specs/" + namespace + "/" + serviceName
222 if _, err := s.EtcdClient.Put(context.Background(), key, string(svcJSON)); err != nil {
223 t.Error(err)
224 }
225 t.Logf("Service stored in etcd %v", string(svcJSON))
226
227 kubeclient, err := kubernetes.NewForConfig(s.ClientConfig)
228 if err != nil {
229 t.Fatalf("Unexpected error: %v", err)
230 }
231 ns := framework.CreateNamespaceOrDie(kubeclient, namespace, t)
232 defer framework.DeleteNamespaceOrDie(kubeclient, ns, t)
233
234
235
236 _, err = kubeclient.CoreV1().Services(namespace).Update(context.Background(), svc, metav1.UpdateOptions{})
237 if err != nil {
238 t.Error(err)
239 }
240
241 err = wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
242
243 _, err = kubeclient.NetworkingV1alpha1().IPAddresses().Get(context.TODO(), svc.Spec.ClusterIP, metav1.GetOptions{})
244 if err != nil {
245 return false, nil
246 }
247 return true, nil
248 })
249 if err != nil {
250 t.Error(err)
251 }
252
253 }
254
255 func TestSkewedAllocators(t *testing.T) {
256 svc := func(i int) *v1.Service {
257 return &v1.Service{
258 ObjectMeta: metav1.ObjectMeta{
259 Name: fmt.Sprintf("svc-%v", i),
260 },
261 Spec: v1.ServiceSpec{
262 Type: v1.ServiceTypeClusterIP,
263 Ports: []v1.ServicePort{
264 {Port: 80},
265 },
266 },
267 }
268 }
269
270 etcdOptions := framework.SharedEtcd()
271 apiServerOptions := kubeapiservertesting.NewDefaultTestServerOptions()
272
273 s1 := kubeapiservertesting.StartTestServerOrDie(t, apiServerOptions,
274 []string{
275 "--runtime-config=networking.k8s.io/v1alpha1=true",
276 "--service-cluster-ip-range=10.0.0.0/24",
277 "--disable-admission-plugins=ServiceAccount",
278 fmt.Sprintf("--feature-gates=%s=true", features.MultiCIDRServiceAllocator)},
279 etcdOptions)
280 defer s1.TearDownFn()
281
282 kubeclient1, err := kubernetes.NewForConfig(s1.ClientConfig)
283 if err != nil {
284 t.Fatalf("Unexpected error: %v", err)
285 }
286
287
288 for i := 0; i < 5; i++ {
289 service, err := kubeclient1.CoreV1().Services(metav1.NamespaceDefault).Create(context.TODO(), svc(i), metav1.CreateOptions{})
290 if err != nil {
291 t.Error(err)
292 continue
293 }
294 _, err = kubeclient1.NetworkingV1alpha1().IPAddresses().Get(context.TODO(), service.Spec.ClusterIP, metav1.GetOptions{})
295 if err != nil {
296 t.Error(err)
297 }
298 }
299
300
301 s2 := kubeapiservertesting.StartTestServerOrDie(t, apiServerOptions,
302 []string{
303 "--runtime-config=networking.k8s.io/v1alpha1=false",
304 "--service-cluster-ip-range=10.0.0.0/24",
305 "--disable-admission-plugins=ServiceAccount",
306 fmt.Sprintf("--feature-gates=%s=false", features.MultiCIDRServiceAllocator)},
307 etcdOptions)
308 defer s2.TearDownFn()
309
310 kubeclient2, err := kubernetes.NewForConfig(s2.ClientConfig)
311 if err != nil {
312 t.Fatalf("Unexpected error: %v", err)
313 }
314
315
316 for i := 5; i < 10; i++ {
317 service, err := kubeclient2.CoreV1().Services(metav1.NamespaceDefault).Create(context.TODO(), svc(i), metav1.CreateOptions{})
318 if err != nil {
319 t.Error(err)
320 }
321
322 err = wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
323
324 _, err = kubeclient1.NetworkingV1alpha1().IPAddresses().Get(context.TODO(), service.Spec.ClusterIP, metav1.GetOptions{})
325 if err != nil {
326 return false, nil
327 }
328 return true, nil
329 })
330 if err != nil {
331 t.Error(err)
332 }
333
334 }
335
336 }
337
338 func TestFlagsIPAllocator(t *testing.T) {
339 svc := func(i int) *v1.Service {
340 return &v1.Service{
341 ObjectMeta: metav1.ObjectMeta{
342 Name: fmt.Sprintf("svc-%v", i),
343 },
344 Spec: v1.ServiceSpec{
345 Type: v1.ServiceTypeClusterIP,
346 Ports: []v1.ServicePort{
347 {Port: 80},
348 },
349 },
350 }
351 }
352
353 etcdOptions := framework.SharedEtcd()
354 apiServerOptions := kubeapiservertesting.NewDefaultTestServerOptions()
355
356 s1 := kubeapiservertesting.StartTestServerOrDie(t, apiServerOptions,
357 []string{
358 "--runtime-config=networking.k8s.io/v1alpha1=true",
359 "--service-cluster-ip-range=10.0.0.0/24",
360 fmt.Sprintf("--feature-gates=%s=true", features.MultiCIDRServiceAllocator)},
361 etcdOptions)
362 defer s1.TearDownFn()
363
364 kubeclient1, err := kubernetes.NewForConfig(s1.ClientConfig)
365 if err != nil {
366 t.Fatalf("Unexpected error: %v", err)
367 }
368
369
370 for i := 0; i < 5; i++ {
371 service, err := kubeclient1.CoreV1().Services(metav1.NamespaceDefault).Create(context.TODO(), svc(i), metav1.CreateOptions{})
372 if err != nil {
373 t.Error(err)
374 continue
375 }
376 _, err = kubeclient1.NetworkingV1alpha1().IPAddresses().Get(context.TODO(), service.Spec.ClusterIP, metav1.GetOptions{})
377 if err != nil {
378 t.Error(err)
379 }
380 }
381
382 }
383
View as plain text