mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			331 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2014 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package config
 | 
						|
 | 
						|
import (
 | 
						|
	"reflect"
 | 
						|
	"sort"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/util/wait"
 | 
						|
	"k8s.io/kubernetes/pkg/api"
 | 
						|
)
 | 
						|
 | 
						|
const TomcatPort int = 8080
 | 
						|
const TomcatName = "tomcat"
 | 
						|
 | 
						|
var TomcatEndpoints = map[string]string{"c0": "1.1.1.1:18080", "c1": "2.2.2.2:18081"}
 | 
						|
 | 
						|
const MysqlPort int = 3306
 | 
						|
const MysqlName = "mysql"
 | 
						|
 | 
						|
var MysqlEndpoints = map[string]string{"c0": "1.1.1.1:13306", "c3": "2.2.2.2:13306"}
 | 
						|
 | 
						|
type sortedServices []api.Service
 | 
						|
 | 
						|
func (s sortedServices) Len() int {
 | 
						|
	return len(s)
 | 
						|
}
 | 
						|
func (s sortedServices) Swap(i, j int) {
 | 
						|
	s[i], s[j] = s[j], s[i]
 | 
						|
}
 | 
						|
func (s sortedServices) Less(i, j int) bool {
 | 
						|
	return s[i].Name < s[j].Name
 | 
						|
}
 | 
						|
 | 
						|
type ServiceHandlerMock struct {
 | 
						|
	updated chan []api.Service
 | 
						|
	waits   int
 | 
						|
}
 | 
						|
 | 
						|
func NewServiceHandlerMock() *ServiceHandlerMock {
 | 
						|
	return &ServiceHandlerMock{updated: make(chan []api.Service, 5)}
 | 
						|
}
 | 
						|
 | 
						|
func (h *ServiceHandlerMock) OnServiceUpdate(services []api.Service) {
 | 
						|
	sort.Sort(sortedServices(services))
 | 
						|
	h.updated <- services
 | 
						|
}
 | 
						|
 | 
						|
func (h *ServiceHandlerMock) ValidateServices(t *testing.T, expectedServices []api.Service) {
 | 
						|
	// We might get 1 or more updates for N service updates, because we
 | 
						|
	// over write older snapshots of services from the producer go-routine
 | 
						|
	// if the consumer falls behind.
 | 
						|
	var services []api.Service
 | 
						|
	for {
 | 
						|
		select {
 | 
						|
		case services = <-h.updated:
 | 
						|
			if reflect.DeepEqual(services, expectedServices) {
 | 
						|
				return
 | 
						|
			}
 | 
						|
		// Unittests will hard timeout in 5m with a stack trace, prevent that
 | 
						|
		// and surface a clearer reason for failure.
 | 
						|
		case <-time.After(wait.ForeverTestTimeout):
 | 
						|
			t.Errorf("Timed out. Expected %#v, Got %#v", expectedServices, services)
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type sortedEndpoints []api.Endpoints
 | 
						|
 | 
						|
func (s sortedEndpoints) Len() int {
 | 
						|
	return len(s)
 | 
						|
}
 | 
						|
func (s sortedEndpoints) Swap(i, j int) {
 | 
						|
	s[i], s[j] = s[j], s[i]
 | 
						|
}
 | 
						|
func (s sortedEndpoints) Less(i, j int) bool {
 | 
						|
	return s[i].Name < s[j].Name
 | 
						|
}
 | 
						|
 | 
						|
type EndpointsHandlerMock struct {
 | 
						|
	updated chan []api.Endpoints
 | 
						|
	waits   int
 | 
						|
}
 | 
						|
 | 
						|
func NewEndpointsHandlerMock() *EndpointsHandlerMock {
 | 
						|
	return &EndpointsHandlerMock{updated: make(chan []api.Endpoints, 5)}
 | 
						|
}
 | 
						|
 | 
						|
func (h *EndpointsHandlerMock) OnEndpointsUpdate(endpoints []api.Endpoints) {
 | 
						|
	sort.Sort(sortedEndpoints(endpoints))
 | 
						|
	h.updated <- endpoints
 | 
						|
}
 | 
						|
 | 
						|
func (h *EndpointsHandlerMock) ValidateEndpoints(t *testing.T, expectedEndpoints []api.Endpoints) {
 | 
						|
	// We might get 1 or more updates for N endpoint updates, because we
 | 
						|
	// over write older snapshots of endpoints from the producer go-routine
 | 
						|
	// if the consumer falls behind. Unittests will hard timeout in 5m.
 | 
						|
	var endpoints []api.Endpoints
 | 
						|
	for {
 | 
						|
		select {
 | 
						|
		case endpoints = <-h.updated:
 | 
						|
			if reflect.DeepEqual(endpoints, expectedEndpoints) {
 | 
						|
				return
 | 
						|
			}
 | 
						|
		// Unittests will hard timeout in 5m with a stack trace, prevent that
 | 
						|
		// and surface a clearer reason for failure.
 | 
						|
		case <-time.After(wait.ForeverTestTimeout):
 | 
						|
			t.Errorf("Timed out. Expected %#v, Got %#v", expectedEndpoints, endpoints)
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func CreateServiceUpdate(op Operation, service *api.Service) ServiceUpdate {
 | 
						|
	return ServiceUpdate{Op: op, Service: service}
 | 
						|
}
 | 
						|
 | 
						|
func CreateEndpointsUpdate(op Operation, endpoints *api.Endpoints) EndpointsUpdate {
 | 
						|
	return EndpointsUpdate{Op: op, Endpoints: endpoints}
 | 
						|
}
 | 
						|
 | 
						|
func TestNewServiceAddedAndNotified(t *testing.T) {
 | 
						|
	config := NewServiceConfig()
 | 
						|
	config.store.synced = true
 | 
						|
	channel := config.Channel("one")
 | 
						|
	handler := NewServiceHandlerMock()
 | 
						|
	config.RegisterHandler(handler)
 | 
						|
	serviceUpdate := CreateServiceUpdate(ADD, &api.Service{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
 | 
						|
		Spec:       api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
 | 
						|
	})
 | 
						|
	channel <- serviceUpdate
 | 
						|
	handler.ValidateServices(t, []api.Service{*serviceUpdate.Service})
 | 
						|
}
 | 
						|
 | 
						|
func TestServiceAddedRemovedSetAndNotified(t *testing.T) {
 | 
						|
	config := NewServiceConfig()
 | 
						|
	config.store.synced = true
 | 
						|
	channel := config.Channel("one")
 | 
						|
	handler := NewServiceHandlerMock()
 | 
						|
	config.RegisterHandler(handler)
 | 
						|
	serviceUpdate := CreateServiceUpdate(ADD, &api.Service{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
 | 
						|
		Spec:       api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
 | 
						|
	})
 | 
						|
	channel <- serviceUpdate
 | 
						|
	handler.ValidateServices(t, []api.Service{*serviceUpdate.Service})
 | 
						|
 | 
						|
	serviceUpdate2 := CreateServiceUpdate(ADD, &api.Service{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
 | 
						|
		Spec:       api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 20}}},
 | 
						|
	})
 | 
						|
	channel <- serviceUpdate2
 | 
						|
	services := []api.Service{*serviceUpdate2.Service, *serviceUpdate.Service}
 | 
						|
	handler.ValidateServices(t, services)
 | 
						|
 | 
						|
	serviceUpdate3 := CreateServiceUpdate(REMOVE, &api.Service{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
 | 
						|
	})
 | 
						|
	channel <- serviceUpdate3
 | 
						|
	services = []api.Service{*serviceUpdate2.Service}
 | 
						|
	handler.ValidateServices(t, services)
 | 
						|
}
 | 
						|
 | 
						|
func TestNewMultipleSourcesServicesAddedAndNotified(t *testing.T) {
 | 
						|
	config := NewServiceConfig()
 | 
						|
	config.store.synced = true
 | 
						|
	channelOne := config.Channel("one")
 | 
						|
	channelTwo := config.Channel("two")
 | 
						|
	if channelOne == channelTwo {
 | 
						|
		t.Error("Same channel handed back for one and two")
 | 
						|
	}
 | 
						|
	handler := NewServiceHandlerMock()
 | 
						|
	config.RegisterHandler(handler)
 | 
						|
	serviceUpdate1 := CreateServiceUpdate(ADD, &api.Service{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
 | 
						|
		Spec:       api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
 | 
						|
	})
 | 
						|
	serviceUpdate2 := CreateServiceUpdate(ADD, &api.Service{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
 | 
						|
		Spec:       api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 20}}},
 | 
						|
	})
 | 
						|
	channelOne <- serviceUpdate1
 | 
						|
	channelTwo <- serviceUpdate2
 | 
						|
	services := []api.Service{*serviceUpdate2.Service, *serviceUpdate1.Service}
 | 
						|
	handler.ValidateServices(t, services)
 | 
						|
}
 | 
						|
 | 
						|
func TestNewMultipleSourcesServicesMultipleHandlersAddedAndNotified(t *testing.T) {
 | 
						|
	config := NewServiceConfig()
 | 
						|
	config.store.synced = true
 | 
						|
	channelOne := config.Channel("one")
 | 
						|
	channelTwo := config.Channel("two")
 | 
						|
	handler := NewServiceHandlerMock()
 | 
						|
	handler2 := NewServiceHandlerMock()
 | 
						|
	config.RegisterHandler(handler)
 | 
						|
	config.RegisterHandler(handler2)
 | 
						|
	serviceUpdate1 := CreateServiceUpdate(ADD, &api.Service{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
 | 
						|
		Spec:       api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
 | 
						|
	})
 | 
						|
	serviceUpdate2 := CreateServiceUpdate(ADD, &api.Service{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
 | 
						|
		Spec:       api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 20}}},
 | 
						|
	})
 | 
						|
	channelOne <- serviceUpdate1
 | 
						|
	channelTwo <- serviceUpdate2
 | 
						|
	services := []api.Service{*serviceUpdate2.Service, *serviceUpdate1.Service}
 | 
						|
	handler.ValidateServices(t, services)
 | 
						|
	handler2.ValidateServices(t, services)
 | 
						|
}
 | 
						|
 | 
						|
func TestNewMultipleSourcesEndpointsMultipleHandlersAddedAndNotified(t *testing.T) {
 | 
						|
	config := NewEndpointsConfig()
 | 
						|
	config.store.synced = true
 | 
						|
	channelOne := config.Channel("one")
 | 
						|
	channelTwo := config.Channel("two")
 | 
						|
	handler := NewEndpointsHandlerMock()
 | 
						|
	handler2 := NewEndpointsHandlerMock()
 | 
						|
	config.RegisterHandler(handler)
 | 
						|
	config.RegisterHandler(handler2)
 | 
						|
	endpointsUpdate1 := CreateEndpointsUpdate(ADD, &api.Endpoints{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
 | 
						|
		Subsets: []api.EndpointSubset{{
 | 
						|
			Addresses: []api.EndpointAddress{{IP: "1.1.1.1"}, {IP: "2.2.2.2"}},
 | 
						|
			Ports:     []api.EndpointPort{{Port: 80}},
 | 
						|
		}},
 | 
						|
	})
 | 
						|
	endpointsUpdate2 := CreateEndpointsUpdate(ADD, &api.Endpoints{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
 | 
						|
		Subsets: []api.EndpointSubset{{
 | 
						|
			Addresses: []api.EndpointAddress{{IP: "3.3.3.3"}, {IP: "4.4.4.4"}},
 | 
						|
			Ports:     []api.EndpointPort{{Port: 80}},
 | 
						|
		}},
 | 
						|
	})
 | 
						|
	channelOne <- endpointsUpdate1
 | 
						|
	channelTwo <- endpointsUpdate2
 | 
						|
 | 
						|
	endpoints := []api.Endpoints{*endpointsUpdate2.Endpoints, *endpointsUpdate1.Endpoints}
 | 
						|
	handler.ValidateEndpoints(t, endpoints)
 | 
						|
	handler2.ValidateEndpoints(t, endpoints)
 | 
						|
}
 | 
						|
 | 
						|
func TestNewMultipleSourcesEndpointsMultipleHandlersAddRemoveSetAndNotified(t *testing.T) {
 | 
						|
	config := NewEndpointsConfig()
 | 
						|
	config.store.synced = true
 | 
						|
	channelOne := config.Channel("one")
 | 
						|
	channelTwo := config.Channel("two")
 | 
						|
	handler := NewEndpointsHandlerMock()
 | 
						|
	handler2 := NewEndpointsHandlerMock()
 | 
						|
	config.RegisterHandler(handler)
 | 
						|
	config.RegisterHandler(handler2)
 | 
						|
	endpointsUpdate1 := CreateEndpointsUpdate(ADD, &api.Endpoints{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
 | 
						|
		Subsets: []api.EndpointSubset{{
 | 
						|
			Addresses: []api.EndpointAddress{{IP: "1.1.1.1"}, {IP: "2.2.2.2"}},
 | 
						|
			Ports:     []api.EndpointPort{{Port: 80}},
 | 
						|
		}},
 | 
						|
	})
 | 
						|
	endpointsUpdate2 := CreateEndpointsUpdate(ADD, &api.Endpoints{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
 | 
						|
		Subsets: []api.EndpointSubset{{
 | 
						|
			Addresses: []api.EndpointAddress{{IP: "3.3.3.3"}, {IP: "4.4.4.4"}},
 | 
						|
			Ports:     []api.EndpointPort{{Port: 80}},
 | 
						|
		}},
 | 
						|
	})
 | 
						|
	channelOne <- endpointsUpdate1
 | 
						|
	channelTwo <- endpointsUpdate2
 | 
						|
 | 
						|
	endpoints := []api.Endpoints{*endpointsUpdate2.Endpoints, *endpointsUpdate1.Endpoints}
 | 
						|
	handler.ValidateEndpoints(t, endpoints)
 | 
						|
	handler2.ValidateEndpoints(t, endpoints)
 | 
						|
 | 
						|
	// Add one more
 | 
						|
	endpointsUpdate3 := CreateEndpointsUpdate(ADD, &api.Endpoints{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foobar"},
 | 
						|
		Subsets: []api.EndpointSubset{{
 | 
						|
			Addresses: []api.EndpointAddress{{IP: "5.5.5.5"}, {IP: "6.6.6.6"}},
 | 
						|
			Ports:     []api.EndpointPort{{Port: 80}},
 | 
						|
		}},
 | 
						|
	})
 | 
						|
	channelTwo <- endpointsUpdate3
 | 
						|
	endpoints = []api.Endpoints{*endpointsUpdate2.Endpoints, *endpointsUpdate1.Endpoints, *endpointsUpdate3.Endpoints}
 | 
						|
	handler.ValidateEndpoints(t, endpoints)
 | 
						|
	handler2.ValidateEndpoints(t, endpoints)
 | 
						|
 | 
						|
	// Update the "foo" service with new endpoints
 | 
						|
	endpointsUpdate1 = CreateEndpointsUpdate(ADD, &api.Endpoints{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
 | 
						|
		Subsets: []api.EndpointSubset{{
 | 
						|
			Addresses: []api.EndpointAddress{{IP: "7.7.7.7"}},
 | 
						|
			Ports:     []api.EndpointPort{{Port: 80}},
 | 
						|
		}},
 | 
						|
	})
 | 
						|
	channelOne <- endpointsUpdate1
 | 
						|
	endpoints = []api.Endpoints{*endpointsUpdate2.Endpoints, *endpointsUpdate1.Endpoints, *endpointsUpdate3.Endpoints}
 | 
						|
	handler.ValidateEndpoints(t, endpoints)
 | 
						|
	handler2.ValidateEndpoints(t, endpoints)
 | 
						|
 | 
						|
	// Remove "bar" service
 | 
						|
	endpointsUpdate2 = CreateEndpointsUpdate(REMOVE, &api.Endpoints{ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"}})
 | 
						|
	channelTwo <- endpointsUpdate2
 | 
						|
 | 
						|
	endpoints = []api.Endpoints{*endpointsUpdate1.Endpoints, *endpointsUpdate3.Endpoints}
 | 
						|
	handler.ValidateEndpoints(t, endpoints)
 | 
						|
	handler2.ValidateEndpoints(t, endpoints)
 | 
						|
}
 | 
						|
 | 
						|
// TODO: Add a unittest for interrupts getting processed in a timely manner.
 | 
						|
// Currently this module has a circular dependency with config, and so it's
 | 
						|
// named config_test, which means even test methods need to be public. This
 | 
						|
// is refactoring that we can avoid by resolving the dependency.
 |