github.com/noironetworks/cilium-net@v1.6.12/pkg/k8s/service_cache_test.go (about)

     1  // Copyright 2018-2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build !privileged_tests
    16  
    17  package k8s
    18  
    19  import (
    20  	"net"
    21  	"time"
    22  
    23  	"github.com/cilium/cilium/pkg/checker"
    24  	"github.com/cilium/cilium/pkg/k8s/types"
    25  	"github.com/cilium/cilium/pkg/loadbalancer"
    26  	"github.com/cilium/cilium/pkg/option"
    27  	"github.com/cilium/cilium/pkg/service"
    28  	"github.com/cilium/cilium/pkg/testutils"
    29  
    30  	"gopkg.in/check.v1"
    31  	"k8s.io/api/core/v1"
    32  	"k8s.io/api/extensions/v1beta1"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/util/intstr"
    35  )
    36  
    37  func (s *K8sSuite) TestGetUniqueServiceFrontends(c *check.C) {
    38  	svcID1 := ServiceID{Name: "svc1", Namespace: "default"}
    39  	svcID2 := ServiceID{Name: "svc2", Namespace: "default"}
    40  
    41  	endpoints := Endpoints{
    42  		Backends: map[string]service.PortConfiguration{
    43  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
    44  				"port": {
    45  					Protocol: loadbalancer.TCP,
    46  					Port:     80,
    47  				},
    48  			},
    49  		},
    50  	}
    51  
    52  	cache := NewServiceCache()
    53  	cache.services = map[ServiceID]*Service{
    54  		svcID1: {
    55  			FrontendIP: net.ParseIP("1.1.1.1"),
    56  			Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{
    57  				loadbalancer.FEPortName("foo"): {
    58  					L4Addr: &loadbalancer.L4Addr{
    59  						Protocol: loadbalancer.TCP,
    60  						Port:     10,
    61  					},
    62  				},
    63  				loadbalancer.FEPortName("bar"): {
    64  					L4Addr: &loadbalancer.L4Addr{
    65  						Protocol: loadbalancer.TCP,
    66  						Port:     20,
    67  					},
    68  				},
    69  			},
    70  		},
    71  		svcID2: {
    72  			FrontendIP: net.ParseIP("2.2.2.2"),
    73  			Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{
    74  				loadbalancer.FEPortName("bar"): {
    75  					L4Addr: &loadbalancer.L4Addr{
    76  						Protocol: loadbalancer.UDP,
    77  						Port:     20,
    78  					},
    79  				},
    80  			},
    81  		},
    82  	}
    83  	cache.endpoints = map[ServiceID]*Endpoints{
    84  		svcID1: &endpoints,
    85  		svcID2: &endpoints,
    86  	}
    87  
    88  	frontends := cache.UniqueServiceFrontends()
    89  	c.Assert(frontends, checker.DeepEquals, FrontendList{
    90  		"1.1.1.1:10/TCP": {},
    91  		"1.1.1.1:20/TCP": {},
    92  		"2.2.2.2:20/UDP": {},
    93  	})
    94  
    95  	// Validate all frontends as exact matches
    96  	frontend := loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("1.1.1.1"), 10)
    97  	c.Assert(frontends.LooseMatch(*frontend), check.Equals, true)
    98  	frontend = loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("1.1.1.1"), 20)
    99  	c.Assert(frontends.LooseMatch(*frontend), check.Equals, true)
   100  	frontend = loadbalancer.NewL3n4Addr(loadbalancer.UDP, net.ParseIP("2.2.2.2"), 20)
   101  	c.Assert(frontends.LooseMatch(*frontend), check.Equals, true)
   102  
   103  	// Validate protocol mismatch on exact match
   104  	frontend = loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("2.2.2.2"), 20)
   105  	c.Assert(frontends.LooseMatch(*frontend), check.Equals, false)
   106  
   107  	// Validate protocol wildcard matching
   108  	frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, net.ParseIP("2.2.2.2"), 20)
   109  	c.Assert(frontends.LooseMatch(*frontend), check.Equals, true)
   110  	frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, net.ParseIP("1.1.1.1"), 10)
   111  	c.Assert(frontends.LooseMatch(*frontend), check.Equals, true)
   112  	frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, net.ParseIP("1.1.1.1"), 20)
   113  	c.Assert(frontends.LooseMatch(*frontend), check.Equals, true)
   114  }
   115  
   116  func (s *K8sSuite) TestServiceCache(c *check.C) {
   117  	svcCache := NewServiceCache()
   118  
   119  	k8sSvc := &types.Service{
   120  		Service: &v1.Service{
   121  			ObjectMeta: metav1.ObjectMeta{
   122  				Name:      "foo",
   123  				Namespace: "bar",
   124  				Labels: map[string]string{
   125  					"foo": "bar",
   126  				},
   127  			},
   128  			Spec: v1.ServiceSpec{
   129  				ClusterIP: "127.0.0.1",
   130  				Selector: map[string]string{
   131  					"foo": "bar",
   132  				},
   133  				Type: v1.ServiceTypeClusterIP,
   134  			},
   135  		},
   136  	}
   137  
   138  	svcID := svcCache.UpdateService(k8sSvc)
   139  
   140  	time.Sleep(100 * time.Millisecond)
   141  
   142  	select {
   143  	case <-svcCache.Events:
   144  		c.Error("Unexpected service event received before endpoints have been imported")
   145  	default:
   146  	}
   147  
   148  	k8sEndpoints := &types.Endpoints{
   149  		Endpoints: &v1.Endpoints{
   150  			ObjectMeta: metav1.ObjectMeta{
   151  				Name:      "foo",
   152  				Namespace: "bar",
   153  			},
   154  			Subsets: []v1.EndpointSubset{
   155  				{
   156  					Addresses: []v1.EndpointAddress{{IP: "2.2.2.2"}},
   157  					Ports: []v1.EndpointPort{
   158  						{
   159  							Name:     "http-test-svc",
   160  							Port:     8080,
   161  							Protocol: v1.ProtocolTCP,
   162  						},
   163  					},
   164  				},
   165  			},
   166  		},
   167  	}
   168  
   169  	svcCache.UpdateEndpoints(k8sEndpoints)
   170  
   171  	// The service should be ready as both service and endpoints have been
   172  	// imported
   173  	c.Assert(testutils.WaitUntil(func() bool {
   174  		event := <-svcCache.Events
   175  		c.Assert(event.Action, check.Equals, UpdateService)
   176  		c.Assert(event.ID, check.Equals, svcID)
   177  		return true
   178  	}, 2*time.Second), check.IsNil)
   179  
   180  	endpoints, ready := svcCache.correlateEndpoints(svcID)
   181  	c.Assert(ready, check.Equals, true)
   182  	c.Assert(endpoints.String(), check.Equals, "2.2.2.2:8080/TCP")
   183  
   184  	// Updating the service without chaning it should not result in an event
   185  	svcCache.UpdateService(k8sSvc)
   186  	time.Sleep(100 * time.Millisecond)
   187  	select {
   188  	case <-svcCache.Events:
   189  		c.Error("Unexpected service event received for unchanged service object")
   190  	default:
   191  	}
   192  
   193  	// Deleting the service will result in a service delete event
   194  	svcCache.DeleteService(k8sSvc)
   195  	c.Assert(testutils.WaitUntil(func() bool {
   196  		event := <-svcCache.Events
   197  		c.Assert(event.Action, check.Equals, DeleteService)
   198  		c.Assert(event.ID, check.Equals, svcID)
   199  		return true
   200  	}, 2*time.Second), check.IsNil)
   201  
   202  	// Reinserting the service should re-match with the still existing endpoints
   203  	svcCache.UpdateService(k8sSvc)
   204  	c.Assert(testutils.WaitUntil(func() bool {
   205  		event := <-svcCache.Events
   206  		c.Assert(event.Action, check.Equals, UpdateService)
   207  		c.Assert(event.ID, check.Equals, svcID)
   208  		return true
   209  	}, 2*time.Second), check.IsNil)
   210  
   211  	// Deleting the endpoints will result in a service update event
   212  	svcCache.DeleteEndpoints(k8sEndpoints)
   213  	c.Assert(testutils.WaitUntil(func() bool {
   214  		event := <-svcCache.Events
   215  		c.Assert(event.Action, check.Equals, UpdateService)
   216  		c.Assert(event.ID, check.Equals, svcID)
   217  		return true
   218  	}, 2*time.Second), check.IsNil)
   219  
   220  	endpoints, serviceReady := svcCache.correlateEndpoints(svcID)
   221  	c.Assert(serviceReady, check.Equals, false)
   222  	c.Assert(endpoints.String(), check.Equals, "")
   223  
   224  	// Reinserting the endpoints should re-match with the still existing service
   225  	svcCache.UpdateEndpoints(k8sEndpoints)
   226  	c.Assert(testutils.WaitUntil(func() bool {
   227  		event := <-svcCache.Events
   228  		c.Assert(event.Action, check.Equals, UpdateService)
   229  		c.Assert(event.ID, check.Equals, svcID)
   230  		return true
   231  	}, 2*time.Second), check.IsNil)
   232  
   233  	endpoints, serviceReady = svcCache.correlateEndpoints(svcID)
   234  	c.Assert(serviceReady, check.Equals, true)
   235  	c.Assert(endpoints.String(), check.Equals, "2.2.2.2:8080/TCP")
   236  
   237  	// Deleting the service will result in a service delete event
   238  	svcCache.DeleteService(k8sSvc)
   239  	c.Assert(testutils.WaitUntil(func() bool {
   240  		event := <-svcCache.Events
   241  		c.Assert(event.Action, check.Equals, DeleteService)
   242  		c.Assert(event.ID, check.Equals, svcID)
   243  		return true
   244  	}, 2*time.Second), check.IsNil)
   245  
   246  	// Deleting the endpoints will not emit an event as the notification
   247  	// was sent out when the service was deleted.
   248  	svcCache.DeleteEndpoints(k8sEndpoints)
   249  	time.Sleep(100 * time.Millisecond)
   250  	select {
   251  	case <-svcCache.Events:
   252  		c.Error("Unexpected service delete event received")
   253  	default:
   254  	}
   255  
   256  	k8sIngress := &types.Ingress{
   257  		Ingress: &v1beta1.Ingress{
   258  			ObjectMeta: metav1.ObjectMeta{
   259  				Namespace: "bar",
   260  			},
   261  			Spec: v1beta1.IngressSpec{
   262  				Backend: &v1beta1.IngressBackend{
   263  					ServiceName: "svc1",
   264  					ServicePort: intstr.IntOrString{
   265  						IntVal: 8080,
   266  						StrVal: "foo",
   267  						Type:   intstr.Int,
   268  					},
   269  				},
   270  			},
   271  		},
   272  	}
   273  	ingressID, err := svcCache.UpdateIngress(k8sIngress, net.ParseIP("1.1.1.1"))
   274  	c.Assert(err, check.IsNil)
   275  
   276  	c.Assert(testutils.WaitUntil(func() bool {
   277  		event := <-svcCache.Events
   278  		c.Assert(event.Action, check.Equals, UpdateIngress)
   279  		c.Assert(event.ID, check.Equals, ingressID)
   280  		return true
   281  	}, 2*time.Second), check.IsNil)
   282  
   283  	// Updating the ingress without changes should not result in an event
   284  	_, err = svcCache.UpdateIngress(k8sIngress, net.ParseIP("1.1.1.1"))
   285  	c.Assert(err, check.IsNil)
   286  	time.Sleep(100 * time.Millisecond)
   287  	select {
   288  	case <-svcCache.Events:
   289  		c.Error("Unexpected ingress event received for unchanged ingress object")
   290  	default:
   291  	}
   292  
   293  	// Deleting the ingress resource will emit a delete event
   294  	svcCache.DeleteIngress(k8sIngress)
   295  	c.Assert(testutils.WaitUntil(func() bool {
   296  		event := <-svcCache.Events
   297  		c.Assert(event.Action, check.Equals, DeleteIngress)
   298  		c.Assert(event.ID, check.Equals, ingressID)
   299  		return true
   300  	}, 2*time.Second), check.IsNil)
   301  }
   302  
   303  func (s *K8sSuite) TestCacheActionString(c *check.C) {
   304  	c.Assert(UpdateService.String(), check.Equals, "service-updated")
   305  	c.Assert(DeleteService.String(), check.Equals, "service-deleted")
   306  	c.Assert(UpdateIngress.String(), check.Equals, "ingress-updated")
   307  	c.Assert(DeleteIngress.String(), check.Equals, "ingress-deleted")
   308  }
   309  
   310  func (s *K8sSuite) TestServiceMerging(c *check.C) {
   311  	svcCache := NewServiceCache()
   312  
   313  	k8sSvc := &types.Service{
   314  		Service: &v1.Service{
   315  			ObjectMeta: metav1.ObjectMeta{
   316  				Name:      "foo",
   317  				Namespace: "bar",
   318  				Annotations: map[string]string{
   319  					"io.cilium/global-service": "true",
   320  				},
   321  			},
   322  			Spec: v1.ServiceSpec{
   323  				ClusterIP: "127.0.0.1",
   324  				Type:      v1.ServiceTypeClusterIP,
   325  				Ports: []v1.ServicePort{
   326  					{
   327  						Name:     "foo",
   328  						Protocol: v1.ProtocolTCP,
   329  						Port:     80,
   330  					},
   331  				},
   332  			},
   333  		},
   334  	}
   335  
   336  	svcID := svcCache.UpdateService(k8sSvc)
   337  
   338  	k8sEndpoints := &types.Endpoints{
   339  		Endpoints: &v1.Endpoints{
   340  			ObjectMeta: metav1.ObjectMeta{
   341  				Name:      "foo",
   342  				Namespace: "bar",
   343  			},
   344  			Subsets: []v1.EndpointSubset{
   345  				{
   346  					Addresses: []v1.EndpointAddress{{IP: "2.2.2.2"}},
   347  					Ports: []v1.EndpointPort{
   348  						{
   349  							Name:     "http-test-svc",
   350  							Port:     8080,
   351  							Protocol: v1.ProtocolTCP,
   352  						},
   353  					},
   354  				},
   355  			},
   356  		},
   357  	}
   358  
   359  	svcCache.UpdateEndpoints(k8sEndpoints)
   360  
   361  	// The service should be ready as both service and endpoints have been
   362  	// imported
   363  	c.Assert(testutils.WaitUntil(func() bool {
   364  		event := <-svcCache.Events
   365  		c.Assert(event.Action, check.Equals, UpdateService)
   366  		c.Assert(event.ID, check.Equals, svcID)
   367  		return true
   368  	}, 2*time.Second), check.IsNil)
   369  
   370  	// Merging a service update with own cluster name must not result in update
   371  	svcCache.MergeExternalServiceUpdate(&service.ClusterService{
   372  		Cluster:   option.Config.ClusterName,
   373  		Namespace: "bar",
   374  		Name:      "foo",
   375  		Frontends: map[string]service.PortConfiguration{
   376  			"1.1.1.1": {},
   377  		},
   378  		Backends: map[string]service.PortConfiguration{
   379  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   380  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   381  			},
   382  		},
   383  	})
   384  
   385  	time.Sleep(100 * time.Millisecond)
   386  
   387  	select {
   388  	case <-svcCache.Events:
   389  		c.Error("Unexpected service event received")
   390  	default:
   391  	}
   392  
   393  	svcCache.MergeExternalServiceUpdate(&service.ClusterService{
   394  		Cluster:   "cluster1",
   395  		Namespace: "bar",
   396  		Name:      "foo",
   397  		Frontends: map[string]service.PortConfiguration{
   398  			"1.1.1.1": {},
   399  		},
   400  		Backends: map[string]service.PortConfiguration{
   401  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   402  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   403  			},
   404  		},
   405  	})
   406  
   407  	// Adding remote endpoints will trigger a service update
   408  	c.Assert(testutils.WaitUntil(func() bool {
   409  		event := <-svcCache.Events
   410  		c.Assert(event.Action, check.Equals, UpdateService)
   411  		c.Assert(event.ID, check.Equals, svcID)
   412  
   413  		c.Assert(event.Endpoints.Backends["2.2.2.2"], checker.DeepEquals, service.PortConfiguration{
   414  			"http-test-svc": {Protocol: loadbalancer.TCP, Port: 8080},
   415  		})
   416  
   417  		c.Assert(event.Endpoints.Backends["3.3.3.3"], checker.DeepEquals, service.PortConfiguration{
   418  			"port": {Protocol: loadbalancer.TCP, Port: 80},
   419  		})
   420  
   421  		return true
   422  	}, 2*time.Second), check.IsNil)
   423  
   424  	// Merging a service for another name should not trigger any updates
   425  	svcCache.MergeExternalServiceUpdate(&service.ClusterService{
   426  		Cluster:   "cluster",
   427  		Namespace: "bar",
   428  		Name:      "foo2",
   429  		Frontends: map[string]service.PortConfiguration{
   430  			"1.1.1.1": {},
   431  		},
   432  		Backends: map[string]service.PortConfiguration{
   433  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   434  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   435  			},
   436  		},
   437  	})
   438  
   439  	time.Sleep(100 * time.Millisecond)
   440  
   441  	select {
   442  	case <-svcCache.Events:
   443  		c.Error("Unexpected service event received")
   444  	default:
   445  	}
   446  
   447  	// Adding the service later must trigger an update
   448  	svcID2 := svcCache.UpdateService(&types.Service{
   449  		Service: &v1.Service{
   450  			ObjectMeta: metav1.ObjectMeta{
   451  				Name:      "foo2",
   452  				Namespace: "bar",
   453  				Labels: map[string]string{
   454  					"foo": "bar",
   455  				},
   456  				Annotations: map[string]string{
   457  					"io.cilium/global-service": "true",
   458  				},
   459  			},
   460  			Spec: v1.ServiceSpec{
   461  				ClusterIP: "127.0.0.2",
   462  				Selector: map[string]string{
   463  					"foo": "bar",
   464  				},
   465  				Type: v1.ServiceTypeClusterIP,
   466  			},
   467  		},
   468  	})
   469  
   470  	c.Assert(testutils.WaitUntil(func() bool {
   471  		event := <-svcCache.Events
   472  		c.Assert(event.Action, check.Equals, UpdateService)
   473  		c.Assert(event.ID, check.Equals, svcID2)
   474  		return true
   475  	}, 2*time.Second), check.IsNil)
   476  
   477  	cluster2svc := &service.ClusterService{
   478  		Cluster:   "cluster2",
   479  		Namespace: "bar",
   480  		Name:      "foo",
   481  		Frontends: map[string]service.PortConfiguration{
   482  			"1.1.1.1": {},
   483  		},
   484  		Backends: map[string]service.PortConfiguration{
   485  			"4.4.4.4": map[string]*loadbalancer.L4Addr{
   486  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   487  			},
   488  		},
   489  	}
   490  
   491  	// Adding another cluster to the first service will triger an event
   492  	svcCache.MergeExternalServiceUpdate(cluster2svc)
   493  	c.Assert(testutils.WaitUntil(func() bool {
   494  		event := <-svcCache.Events
   495  		c.Assert(event.Action, check.Equals, UpdateService)
   496  
   497  		c.Assert(event.Endpoints.Backends["4.4.4.4"], checker.DeepEquals, service.PortConfiguration{
   498  			"port": {Protocol: loadbalancer.TCP, Port: 80},
   499  		})
   500  
   501  		return true
   502  	}, 2*time.Second), check.IsNil)
   503  
   504  	svcCache.MergeExternalServiceDelete(cluster2svc)
   505  	c.Assert(testutils.WaitUntil(func() bool {
   506  		event := <-svcCache.Events
   507  		c.Assert(event.Action, check.Equals, UpdateService)
   508  		c.Assert(event.Endpoints.Backends["4.4.4.4"], check.IsNil)
   509  		return true
   510  	}, 2*time.Second), check.IsNil)
   511  
   512  	// Deletion of the service frontend will trigger the delete notification
   513  	svcCache.DeleteService(k8sSvc)
   514  	c.Assert(testutils.WaitUntil(func() bool {
   515  		event := <-svcCache.Events
   516  		c.Assert(event.Action, check.Equals, DeleteService)
   517  		c.Assert(event.ID, check.Equals, svcID)
   518  		return true
   519  	}, 2*time.Second), check.IsNil)
   520  
   521  	// When readding the service, the remote endpoints of cluster1 must still be present
   522  	svcCache.UpdateService(k8sSvc)
   523  	c.Assert(testutils.WaitUntil(func() bool {
   524  		event := <-svcCache.Events
   525  		c.Assert(event.Action, check.Equals, UpdateService)
   526  		c.Assert(event.ID, check.Equals, svcID)
   527  		c.Assert(event.Endpoints.Backends["3.3.3.3"], checker.DeepEquals, service.PortConfiguration{
   528  			"port": {Protocol: loadbalancer.TCP, Port: 80},
   529  		})
   530  		return true
   531  	}, 2*time.Second), check.IsNil)
   532  
   533  	k8sSvcID, _ := ParseService(k8sSvc)
   534  	addresses := svcCache.GetServiceIP(k8sSvcID)
   535  	c.Assert(addresses, checker.DeepEquals, loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("127.0.0.1"), 80))
   536  }
   537  
   538  func (s *K8sSuite) TestNonSharedServie(c *check.C) {
   539  	svcCache := NewServiceCache()
   540  
   541  	k8sSvc := &types.Service{
   542  		Service: &v1.Service{
   543  			ObjectMeta: metav1.ObjectMeta{
   544  				Name:      "foo",
   545  				Namespace: "bar",
   546  				Annotations: map[string]string{
   547  					"io.cilium/global-service": "false",
   548  				},
   549  			},
   550  			Spec: v1.ServiceSpec{
   551  				ClusterIP: "127.0.0.1",
   552  				Type:      v1.ServiceTypeClusterIP,
   553  			},
   554  		},
   555  	}
   556  
   557  	svcCache.UpdateService(k8sSvc)
   558  
   559  	svcCache.MergeExternalServiceUpdate(&service.ClusterService{
   560  		Cluster:   "cluster1",
   561  		Namespace: "bar",
   562  		Name:      "foo",
   563  		Backends: map[string]service.PortConfiguration{
   564  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   565  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   566  			},
   567  		},
   568  	})
   569  
   570  	// The service is unshared, it should not trigger an update
   571  	time.Sleep(100 * time.Millisecond)
   572  
   573  	select {
   574  	case <-svcCache.Events:
   575  		c.Error("Unexpected service event received")
   576  	default:
   577  	}
   578  }