...

Source file src/k8s.io/kubernetes/pkg/proxy/conntrack/cleanup_test.go

Documentation: k8s.io/kubernetes/pkg/proxy/conntrack

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5  Copyright 2023 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package conntrack
    21  
    22  import (
    23  	"net"
    24  	"reflect"
    25  	"testing"
    26  
    27  	v1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	"k8s.io/apimachinery/pkg/util/sets"
    31  	"k8s.io/kubernetes/pkg/proxy"
    32  )
    33  
    34  const (
    35  	testClusterIP      = "172.30.1.1"
    36  	testExternalIP     = "192.168.99.100"
    37  	testLoadBalancerIP = "1.2.3.4"
    38  
    39  	testEndpointIP = "10.240.0.4"
    40  
    41  	testPort         = 53
    42  	testNodePort     = 5353
    43  	testEndpointPort = "5300"
    44  )
    45  
    46  func TestCleanStaleEntries(t *testing.T) {
    47  	// We need to construct a proxy.ServicePortMap to pass to CleanStaleEntries.
    48  	// ServicePortMap is just map[string]proxy.ServicePort, but there are no public
    49  	// constructors for any implementation of proxy.ServicePort, so we have to either
    50  	// provide our own implementation of that interface, or else use a
    51  	// proxy.ServiceChangeTracker to construct them and fill in the map for us.
    52  
    53  	sct := proxy.NewServiceChangeTracker(nil, v1.IPv4Protocol, nil, nil)
    54  	svc := &v1.Service{
    55  		ObjectMeta: metav1.ObjectMeta{
    56  			Name:      "cleanup-test",
    57  			Namespace: "test",
    58  		},
    59  		Spec: v1.ServiceSpec{
    60  			ClusterIP:   testClusterIP,
    61  			ExternalIPs: []string{testExternalIP},
    62  			Ports: []v1.ServicePort{
    63  				{
    64  					Name:     "dns-tcp",
    65  					Port:     testPort,
    66  					Protocol: v1.ProtocolTCP,
    67  				},
    68  				{
    69  					Name:     "dns-udp",
    70  					Port:     testPort,
    71  					NodePort: testNodePort,
    72  					Protocol: v1.ProtocolUDP,
    73  				},
    74  			},
    75  		},
    76  		Status: v1.ServiceStatus{
    77  			LoadBalancer: v1.LoadBalancerStatus{
    78  				Ingress: []v1.LoadBalancerIngress{{
    79  					IP: testLoadBalancerIP,
    80  				}},
    81  			},
    82  		},
    83  	}
    84  	sct.Update(nil, svc)
    85  
    86  	svcPortMap := make(proxy.ServicePortMap)
    87  	_ = svcPortMap.Update(sct)
    88  
    89  	// (At this point we are done with sct, and in particular, we don't use sct to
    90  	// construct UpdateServiceMapResults, because pkg/proxy already has its own tests
    91  	// for that. Also, svcPortMap is read-only from this point on.)
    92  
    93  	tcpPortName := proxy.ServicePortName{
    94  		NamespacedName: types.NamespacedName{
    95  			Namespace: svc.Namespace,
    96  			Name:      svc.Name,
    97  		},
    98  		Port:     svc.Spec.Ports[0].Name,
    99  		Protocol: svc.Spec.Ports[0].Protocol,
   100  	}
   101  
   102  	udpPortName := proxy.ServicePortName{
   103  		NamespacedName: types.NamespacedName{
   104  			Namespace: svc.Namespace,
   105  			Name:      svc.Name,
   106  		},
   107  		Port:     svc.Spec.Ports[1].Name,
   108  		Protocol: svc.Spec.Ports[1].Protocol,
   109  	}
   110  
   111  	unknownPortName := udpPortName
   112  	unknownPortName.Namespace = "unknown"
   113  
   114  	// Sanity-check to make sure we constructed the map correctly
   115  	if len(svcPortMap) != 2 {
   116  		t.Fatalf("expected svcPortMap to have 2 entries, got %+v", svcPortMap)
   117  	}
   118  	servicePort := svcPortMap[tcpPortName]
   119  	if servicePort == nil || servicePort.String() != "172.30.1.1:53/TCP" {
   120  		t.Fatalf("expected svcPortMap[%q] to be \"172.30.1.1:53/TCP\", got %q", tcpPortName.String(), servicePort.String())
   121  	}
   122  	servicePort = svcPortMap[udpPortName]
   123  	if servicePort == nil || servicePort.String() != "172.30.1.1:53/UDP" {
   124  		t.Fatalf("expected svcPortMap[%q] to be \"172.30.1.1:53/UDP\", got %q", udpPortName.String(), servicePort.String())
   125  	}
   126  
   127  	testCases := []struct {
   128  		description string
   129  
   130  		serviceUpdates   proxy.UpdateServiceMapResult
   131  		endpointsUpdates proxy.UpdateEndpointsMapResult
   132  
   133  		result FakeInterface
   134  	}{
   135  		{
   136  			description: "DeletedUDPClusterIPs clears entries for given clusterIPs (only)",
   137  
   138  			serviceUpdates: proxy.UpdateServiceMapResult{
   139  				// Note: this isn't testClusterIP; it's the IP of some
   140  				// unknown (because deleted) service.
   141  				DeletedUDPClusterIPs: sets.New("172.30.99.99"),
   142  			},
   143  			endpointsUpdates: proxy.UpdateEndpointsMapResult{},
   144  
   145  			result: FakeInterface{
   146  				ClearedIPs: sets.New("172.30.99.99"),
   147  
   148  				ClearedPorts:    sets.New[int](),
   149  				ClearedNATs:     map[string]string{},
   150  				ClearedPortNATs: map[int]string{},
   151  			},
   152  		},
   153  		{
   154  			description: "DeletedUDPEndpoints clears NAT entries for all IPs and NodePorts",
   155  
   156  			serviceUpdates: proxy.UpdateServiceMapResult{
   157  				DeletedUDPClusterIPs: sets.New[string](),
   158  			},
   159  			endpointsUpdates: proxy.UpdateEndpointsMapResult{
   160  				DeletedUDPEndpoints: []proxy.ServiceEndpoint{{
   161  					Endpoint:        net.JoinHostPort(testEndpointIP, testEndpointPort),
   162  					ServicePortName: udpPortName,
   163  				}},
   164  			},
   165  
   166  			result: FakeInterface{
   167  				ClearedIPs:   sets.New[string](),
   168  				ClearedPorts: sets.New[int](),
   169  
   170  				ClearedNATs: map[string]string{
   171  					testClusterIP:      testEndpointIP,
   172  					testExternalIP:     testEndpointIP,
   173  					testLoadBalancerIP: testEndpointIP,
   174  				},
   175  				ClearedPortNATs: map[int]string{
   176  					testNodePort: testEndpointIP,
   177  				},
   178  			},
   179  		},
   180  		{
   181  			description: "NewlyActiveUDPServices clears entries for all IPs and NodePorts",
   182  
   183  			serviceUpdates: proxy.UpdateServiceMapResult{
   184  				DeletedUDPClusterIPs: sets.New[string](),
   185  			},
   186  			endpointsUpdates: proxy.UpdateEndpointsMapResult{
   187  				DeletedUDPEndpoints: []proxy.ServiceEndpoint{},
   188  				NewlyActiveUDPServices: []proxy.ServicePortName{
   189  					udpPortName,
   190  				},
   191  			},
   192  
   193  			result: FakeInterface{
   194  				ClearedIPs:   sets.New(testClusterIP, testExternalIP, testLoadBalancerIP),
   195  				ClearedPorts: sets.New(testNodePort),
   196  
   197  				ClearedNATs:     map[string]string{},
   198  				ClearedPortNATs: map[int]string{},
   199  			},
   200  		},
   201  
   202  		{
   203  			description: "DeletedUDPEndpoints for unknown Service has no effect",
   204  
   205  			serviceUpdates: proxy.UpdateServiceMapResult{
   206  				DeletedUDPClusterIPs: sets.New[string](),
   207  			},
   208  			endpointsUpdates: proxy.UpdateEndpointsMapResult{
   209  				DeletedUDPEndpoints: []proxy.ServiceEndpoint{{
   210  					Endpoint:        "10.240.0.4:80",
   211  					ServicePortName: unknownPortName,
   212  				}},
   213  				NewlyActiveUDPServices: []proxy.ServicePortName{},
   214  			},
   215  
   216  			result: FakeInterface{
   217  				ClearedIPs:      sets.New[string](),
   218  				ClearedPorts:    sets.New[int](),
   219  				ClearedNATs:     map[string]string{},
   220  				ClearedPortNATs: map[int]string{},
   221  			},
   222  		},
   223  		{
   224  			description: "NewlyActiveUDPServices for unknown Service has no effect",
   225  
   226  			serviceUpdates: proxy.UpdateServiceMapResult{
   227  				DeletedUDPClusterIPs: sets.New[string](),
   228  			},
   229  			endpointsUpdates: proxy.UpdateEndpointsMapResult{
   230  				DeletedUDPEndpoints: []proxy.ServiceEndpoint{},
   231  				NewlyActiveUDPServices: []proxy.ServicePortName{
   232  					unknownPortName,
   233  				},
   234  			},
   235  
   236  			result: FakeInterface{
   237  				ClearedIPs:      sets.New[string](),
   238  				ClearedPorts:    sets.New[int](),
   239  				ClearedNATs:     map[string]string{},
   240  				ClearedPortNATs: map[int]string{},
   241  			},
   242  		},
   243  	}
   244  
   245  	for _, tc := range testCases {
   246  		t.Run(tc.description, func(t *testing.T) {
   247  			fake := NewFake()
   248  			CleanStaleEntries(fake, svcPortMap, tc.serviceUpdates, tc.endpointsUpdates)
   249  			if !fake.ClearedIPs.Equal(tc.result.ClearedIPs) {
   250  				t.Errorf("Expected ClearedIPs=%v, got %v", tc.result.ClearedIPs, fake.ClearedIPs)
   251  			}
   252  			if !fake.ClearedPorts.Equal(tc.result.ClearedPorts) {
   253  				t.Errorf("Expected ClearedPorts=%v, got %v", tc.result.ClearedPorts, fake.ClearedPorts)
   254  			}
   255  			if !reflect.DeepEqual(fake.ClearedNATs, tc.result.ClearedNATs) {
   256  				t.Errorf("Expected ClearedNATs=%v, got %v", tc.result.ClearedNATs, fake.ClearedNATs)
   257  			}
   258  			if !reflect.DeepEqual(fake.ClearedPortNATs, tc.result.ClearedPortNATs) {
   259  				t.Errorf("Expected ClearedPortNATs=%v, got %v", tc.result.ClearedPortNATs, fake.ClearedPortNATs)
   260  			}
   261  		})
   262  	}
   263  }
   264  

View as plain text