mirror of
				https://github.com/go-micro/go-micro.git
				synced 2025-10-30 23:27:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			493 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			493 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package kubernetes
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/asim/go-micro/plugins/registry/kubernetes/v4/client"
 | |
| 	"github.com/asim/go-micro/plugins/registry/kubernetes/v4/client/mock"
 | |
| 	"go-micro.dev/v4/logger"
 | |
| 	"go-micro.dev/v4/registry"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	mockClient = mock.NewClient()
 | |
| 	podIP      = 1
 | |
| )
 | |
| 
 | |
| func setupPod(name string) *client.Pod {
 | |
| 	p, ok := mockClient.Pods[name]
 | |
| 	if ok || p != nil {
 | |
| 		return p
 | |
| 	}
 | |
| 
 | |
| 	// inc pod
 | |
| 	podIP++
 | |
| 
 | |
| 	p = &client.Pod{
 | |
| 		Metadata: &client.Meta{
 | |
| 			Name:        name,
 | |
| 			Labels:      make(map[string]*string),
 | |
| 			Annotations: make(map[string]*string),
 | |
| 		},
 | |
| 		Status: &client.Status{
 | |
| 			PodIP: "10.0.0." + strconv.Itoa(podIP),
 | |
| 			Phase: podRunning,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	mockClient.Pods[name] = p
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // registers a service against a given pod
 | |
| func register(r registry.Registry, podName string, svc *registry.Service) {
 | |
| 	os.Setenv("HOSTNAME", podName)
 | |
| 
 | |
| 	pod := setupPod(podName)
 | |
| 
 | |
| 	svc.Nodes = append(svc.Nodes, ®istry.Node{
 | |
| 		Id:       svc.Name + ":" + pod.Metadata.Name,
 | |
| 		Address:  fmt.Sprintf("%s:%d", pod.Status.PodIP, 80),
 | |
| 		Metadata: map[string]string{},
 | |
| 	})
 | |
| 
 | |
| 	if err := r.Register(svc); err != nil {
 | |
| 		logger.Fatalf("did not expect Register() to fail: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	os.Setenv("HOSTNAME", "")
 | |
| }
 | |
| 
 | |
| func teardownRegistry() {
 | |
| 	mock.Teardown(mockClient)
 | |
| }
 | |
| 
 | |
| func setupRegistry(opts ...registry.Option) registry.Registry {
 | |
| 	return &kregistry{
 | |
| 		client:  mockClient,
 | |
| 		timeout: time.Second * 1,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| //
 | |
| //
 | |
| // Tests start here
 | |
| //
 | |
| //
 | |
| 
 | |
| func TestRegister(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 	defer teardownRegistry()
 | |
| 
 | |
| 	svc := ®istry.Service{Name: "foo.service", Version: "1"}
 | |
| 	register(r, "pod-1", svc)
 | |
| 
 | |
| 	// check pod has correct labels/annotations
 | |
| 	p := mockClient.Pods["pod-1"]
 | |
| 	svcLabel, ok := p.Metadata.Labels[svcSelectorPrefix+"foo.service"]
 | |
| 	if !ok || *svcLabel != svcSelectorValue {
 | |
| 		t.Fatalf("expected to have pod selector label")
 | |
| 	}
 | |
| 
 | |
| 	svcData, ok := p.Metadata.Annotations[annotationServiceKeyPrefix+"foo.service"]
 | |
| 	if !ok || len(*svcData) == 0 {
 | |
| 		t.Fatalf("expected to have annotation")
 | |
| 	}
 | |
| 
 | |
| 	// unmarshal service data from annotation and compare
 | |
| 	// service passed in to .Register()
 | |
| 	var service *registry.Service
 | |
| 	if err := json.Unmarshal([]byte(*svcData), &service); err != nil {
 | |
| 		t.Fatalf("did not expect register unmarshal to fail %v", err)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(svc, service) {
 | |
| 		t.Fatal("services did not match")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRegisterTwoDifferentServicesOnePod(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 	defer teardownRegistry()
 | |
| 
 | |
| 	svc1 := ®istry.Service{Name: "foo.service"}
 | |
| 	svc2 := ®istry.Service{Name: "bar.service"}
 | |
| 
 | |
| 	register(r, "pod-1", svc1)
 | |
| 	register(r, "pod-1", svc2)
 | |
| 
 | |
| 	// check pod has correct labels/annotations
 | |
| 	p := mockClient.Pods["pod-1"]
 | |
| 	if svcLabel1, ok := p.Metadata.Labels[svcSelectorPrefix+"foo.service"]; !ok || *svcLabel1 != svcSelectorValue {
 | |
| 		t.Fatalf("expected to have pod selector label for service one")
 | |
| 	}
 | |
| 	if svcLabel2, ok := p.Metadata.Labels[svcSelectorPrefix+"bar.service"]; !ok || *svcLabel2 != svcSelectorValue {
 | |
| 		t.Fatalf("expected to have pod selector label for service two")
 | |
| 	}
 | |
| 	svcData1, ok := p.Metadata.Annotations[annotationServiceKeyPrefix+"foo.service"]
 | |
| 	if !ok || len(*svcData1) == 0 {
 | |
| 		t.Fatalf("expected to have annotation")
 | |
| 	}
 | |
| 	svcData2, ok := p.Metadata.Annotations[annotationServiceKeyPrefix+"bar.service"]
 | |
| 	if !ok || len(*svcData1) == 0 {
 | |
| 		t.Fatalf("expected to have annotation")
 | |
| 	}
 | |
| 
 | |
| 	// unmarshal service data from annotation and compare
 | |
| 	// service passed in to .Register()
 | |
| 	var service1 *registry.Service
 | |
| 	if err := json.Unmarshal([]byte(*svcData1), &service1); err != nil {
 | |
| 		t.Fatalf("did not expect register unmarshal to fail %v", err)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(svc1, service1) {
 | |
| 		t.Fatal("services did not match")
 | |
| 	}
 | |
| 
 | |
| 	var service2 *registry.Service
 | |
| 	if err := json.Unmarshal([]byte(*svcData2), &service2); err != nil {
 | |
| 		t.Fatalf("did not expect register unmarshal to fail %v", err)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(svc2, service2) {
 | |
| 		t.Fatal("services did not match")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRegisterTwoDifferentServicesTwoPods(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 	defer teardownRegistry()
 | |
| 
 | |
| 	svc1 := ®istry.Service{Name: "foo.service"}
 | |
| 	svc2 := ®istry.Service{Name: "bar.service"}
 | |
| 	register(r, "pod-1", svc1)
 | |
| 	register(r, "pod-2", svc2)
 | |
| 
 | |
| 	// check pod-1 has correct labels/annotations
 | |
| 	p1 := mockClient.Pods["pod-1"]
 | |
| 	if svcLabel1, ok := p1.Metadata.Labels[svcSelectorPrefix+"foo.service"]; !ok || *svcLabel1 != svcSelectorValue {
 | |
| 		t.Fatalf("expected to have pod selector label for foo.service")
 | |
| 	}
 | |
| 	if _, ok := p1.Metadata.Labels[svcSelectorPrefix+"bar.service"]; ok {
 | |
| 		t.Fatal("pod 1 shouldnt have label for bar.service")
 | |
| 	}
 | |
| 
 | |
| 	// check pod-2 has correct labels/annotations
 | |
| 	p2 := mockClient.Pods["pod-2"]
 | |
| 	if svcLabel2, ok := p2.Metadata.Labels[svcSelectorPrefix+"bar.service"]; !ok || *svcLabel2 != svcSelectorValue {
 | |
| 		t.Fatalf("expected to have pod selector label for bar.service")
 | |
| 	}
 | |
| 	if _, ok := p2.Metadata.Labels[svcSelectorPrefix+"foo.service"]; ok {
 | |
| 		t.Fatal("pod 2 shouldnt have label for foo.service")
 | |
| 	}
 | |
| 
 | |
| 	svcData1, ok := p1.Metadata.Annotations[annotationServiceKeyPrefix+"foo.service"]
 | |
| 	if !ok || len(*svcData1) == 0 {
 | |
| 		t.Fatalf("expected to have annotation")
 | |
| 	}
 | |
| 	if _, okp2 := p1.Metadata.Annotations[annotationServiceKeyPrefix+"bar.service"]; okp2 {
 | |
| 		t.Fatal("bar.service shouldnt have annotation for pod2")
 | |
| 	}
 | |
| 
 | |
| 	svcData2, ok := p2.Metadata.Annotations[annotationServiceKeyPrefix+"bar.service"]
 | |
| 	if !ok || len(*svcData2) == 0 {
 | |
| 		t.Fatalf("expected to have annotation")
 | |
| 	}
 | |
| 	if _, okp1 := p2.Metadata.Annotations[annotationServiceKeyPrefix+"foo.service"]; okp1 {
 | |
| 		t.Fatal("bar.service shouldnt have annotation for pod1")
 | |
| 	}
 | |
| 
 | |
| 	// unmarshal service data from annotation and compare
 | |
| 	// service passed in to .Register()
 | |
| 	var service1 *registry.Service
 | |
| 	if err := json.Unmarshal([]byte(*svcData1), &service1); err != nil {
 | |
| 		t.Fatalf("did not expect register unmarshal to fail %v", err)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(svc1, service1) {
 | |
| 		t.Fatal("services did not match")
 | |
| 	}
 | |
| 
 | |
| 	var service2 *registry.Service
 | |
| 	if err := json.Unmarshal([]byte(*svcData2), &service2); err != nil {
 | |
| 		t.Fatalf("did not expect register unmarshal to fail %v", err)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(svc2, service2) {
 | |
| 		t.Fatal("services did not match")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRegisterSingleVersionedServiceTwoPods(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 	defer teardownRegistry()
 | |
| 
 | |
| 	svc1 := ®istry.Service{Name: "foo.service"}
 | |
| 	svc2 := ®istry.Service{Name: "foo.service"}
 | |
| 	register(r, "pod-1", svc1)
 | |
| 	register(r, "pod-2", svc2)
 | |
| 
 | |
| 	// check pod-1 has correct labels/annotations
 | |
| 	p1 := mockClient.Pods["pod-1"]
 | |
| 	if svcLabel1, ok := p1.Metadata.Labels[svcSelectorPrefix+"foo.service"]; !ok || *svcLabel1 != svcSelectorValue {
 | |
| 		t.Fatalf("expected to have pod selector label for foo.service")
 | |
| 	}
 | |
| 
 | |
| 	// check pod-2 has correct labels/annotations
 | |
| 	p2 := mockClient.Pods["pod-2"]
 | |
| 	if svcLabel2, ok := p2.Metadata.Labels[svcSelectorPrefix+"foo.service"]; !ok || *svcLabel2 != svcSelectorValue {
 | |
| 		t.Fatalf("expected to have pod selector label for foo.service")
 | |
| 	}
 | |
| 
 | |
| 	svcData1, ok := p1.Metadata.Annotations[annotationServiceKeyPrefix+"foo.service"]
 | |
| 	if !ok || len(*svcData1) == 0 {
 | |
| 		t.Fatalf("expected to have annotation")
 | |
| 	}
 | |
| 	svcData2, ok := p2.Metadata.Annotations[annotationServiceKeyPrefix+"foo.service"]
 | |
| 	if !ok || len(*svcData2) == 0 {
 | |
| 		t.Fatalf("expected to have annotation")
 | |
| 	}
 | |
| 
 | |
| 	// unmarshal service data from annotation and compare
 | |
| 	// service passed in to .Register()
 | |
| 	var service1 *registry.Service
 | |
| 	if err := json.Unmarshal([]byte(*svcData1), &service1); err != nil {
 | |
| 		t.Fatalf("did not expect register unmarshal to fail %v", err)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(svc1, service1) {
 | |
| 		t.Fatal("services did not match")
 | |
| 	}
 | |
| 
 | |
| 	var service2 *registry.Service
 | |
| 	if err := json.Unmarshal([]byte(*svcData2), &service2); err != nil {
 | |
| 		t.Fatalf("did not expect register unmarshal to fail %v", err)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(svc2, service2) {
 | |
| 		t.Fatal("services did not match")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDeregister(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 	defer teardownRegistry()
 | |
| 
 | |
| 	svc1 := ®istry.Service{Name: "foo.service"}
 | |
| 	svc2 := ®istry.Service{Name: "foo.service"}
 | |
| 	register(r, "pod-1", svc1)
 | |
| 	register(r, "pod-2", svc2)
 | |
| 
 | |
| 	// deregister one service
 | |
| 	os.Setenv("HOSTNAME", "pod-1")
 | |
| 	if err := r.Deregister(svc1); err != nil {
 | |
| 		t.Fatalf("did not expect Deregister to fail %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// check pod-1 has correct labels/annotations
 | |
| 	p1 := mockClient.Pods["pod-1"]
 | |
| 	if svcLabel1, ok := p1.Metadata.Labels[svcSelectorPrefix+"foo.service"]; ok && *svcLabel1 == svcSelectorValue {
 | |
| 		t.Fatalf("expected to NOT have pod selector label for foo.service")
 | |
| 	}
 | |
| 
 | |
| 	// check pod-2 has correct labels/annotations
 | |
| 	p2 := mockClient.Pods["pod-2"]
 | |
| 	if svcLabel2, ok := p2.Metadata.Labels[svcSelectorPrefix+"foo.service"]; !ok || *svcLabel2 != svcSelectorValue {
 | |
| 		t.Fatalf("expected to have pod selector label for foo.service")
 | |
| 	}
 | |
| 
 | |
| 	svcData1, ok := p1.Metadata.Annotations[annotationServiceKeyPrefix+"foo.service"]
 | |
| 	if ok && len(*svcData1) != 0 {
 | |
| 		t.Fatalf("expected to NOT have annotation")
 | |
| 	}
 | |
| 	svcData2, ok := p2.Metadata.Annotations[annotationServiceKeyPrefix+"foo.service"]
 | |
| 	if !ok || len(*svcData2) == 0 {
 | |
| 		t.Fatalf("expected to have annotation")
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| func TestGetService(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 	defer teardownRegistry()
 | |
| 
 | |
| 	svc1 := ®istry.Service{Name: "foo.service"}
 | |
| 	register(r, "pod-1", svc1)
 | |
| 
 | |
| 	service, err := r.GetService("foo.service")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("did not expect GetService to fail %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// compare services
 | |
| 	if !hasServices(service, []*registry.Service{svc1}) {
 | |
| 		t.Fatal("expected service to match")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetServiceSameServiceTwoPods(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 	defer teardownRegistry()
 | |
| 
 | |
| 	svc1 := ®istry.Service{Name: "foo.service", Version: "1"}
 | |
| 	svc2 := ®istry.Service{Name: "foo.service", Version: "1"}
 | |
| 	register(r, "pod-1", svc1)
 | |
| 	register(r, "pod-2", svc2)
 | |
| 
 | |
| 	service, err := r.GetService("foo.service")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("did not expect GetService to fail %v", err)
 | |
| 	}
 | |
| 
 | |
| 	if len(service) != 1 {
 | |
| 		t.Fatal("expected there to be only 1 service")
 | |
| 	}
 | |
| 
 | |
| 	if len(service[0].Nodes) != 2 {
 | |
| 		t.Fatal("expected there to be 2 nodes")
 | |
| 	}
 | |
| 	if !hasNodes(service[0].Nodes, []*registry.Node{svc1.Nodes[0], svc2.Nodes[0]}) {
 | |
| 		t.Fatal("nodes dont match")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetServiceTwoVersionsTwoPods(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 	defer teardownRegistry()
 | |
| 
 | |
| 	svc1 := ®istry.Service{Name: "foo.service", Version: "1"}
 | |
| 	svc2 := ®istry.Service{Name: "foo.service", Version: "2"}
 | |
| 
 | |
| 	register(r, "pod-1", svc1)
 | |
| 	register(r, "pod-2", svc2)
 | |
| 
 | |
| 	service, err := r.GetService("foo.service")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("did not expect GetService to fail %v", err)
 | |
| 	}
 | |
| 
 | |
| 	if len(service) != 2 {
 | |
| 		t.Fatal("expected there to be 2 services")
 | |
| 	}
 | |
| 
 | |
| 	// compare services
 | |
| 	if !hasServices(service, []*registry.Service{svc1, svc2}) {
 | |
| 		t.Fatal("expected service to match")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestListServices(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 	defer teardownRegistry()
 | |
| 
 | |
| 	svc1 := ®istry.Service{Name: "foo.service", Version: "1"}
 | |
| 	svc2 := ®istry.Service{Name: "bar.service", Version: "2"}
 | |
| 
 | |
| 	register(r, "pod-1", svc1)
 | |
| 	register(r, "pod-2", svc2)
 | |
| 
 | |
| 	services, err := r.ListServices()
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("did not expect ListServices to fail %v", err)
 | |
| 	}
 | |
| 	if !hasServices(services, []*registry.Service{
 | |
| 		{Name: "foo.service", Version: "1"},
 | |
| 		{Name: "bar.service", Version: "2"},
 | |
| 	}) {
 | |
| 		t.Fatal("expected services to equal")
 | |
| 	}
 | |
| 
 | |
| 	os.Setenv("HOSTNAME", "pod-1")
 | |
| 	r.Deregister(svc1)
 | |
| 	services2, err := r.ListServices()
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("did not expect ListServices to fail %v", err)
 | |
| 	}
 | |
| 	if !hasServices(services2, []*registry.Service{
 | |
| 		{Name: "bar.service", Version: "2"},
 | |
| 	}) {
 | |
| 		t.Fatal("expected services to equal")
 | |
| 	}
 | |
| 
 | |
| 	// kill pod without deregistering.
 | |
| 	delete(mockClient.Pods, "pod-2")
 | |
| 
 | |
| 	// shoudnt return old data
 | |
| 	services3, err := r.ListServices()
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("did not expect ListServices to fail %v", err)
 | |
| 	}
 | |
| 	if len(services3) != 0 {
 | |
| 		t.Fatal("expected there to be no services")
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| func TestWatcher(t *testing.T) {
 | |
| 	r := setupRegistry()
 | |
| 
 | |
| 	// check that service is blank
 | |
| 	if _, err := r.GetService("foo.service"); err != registry.ErrNotFound {
 | |
| 		logger.Fatal("expected ErrNotFound")
 | |
| 	}
 | |
| 
 | |
| 	// setup svc
 | |
| 	svc1 := ®istry.Service{Name: "foo.service", Version: "1"}
 | |
| 	register(r, "pod-1", svc1)
 | |
| 
 | |
| 	if routes, err := r.GetService("foo.service"); err != nil {
 | |
| 		t.Fatalf("Querying service returned an error: %v", err)
 | |
| 	} else if len(routes) != 1 {
 | |
| 		t.Fatalf("Expected one route, found %v", len(routes))
 | |
| 	}
 | |
| 
 | |
| 	// setup svc
 | |
| 	svc2 := ®istry.Service{Name: "foo.service", Version: "1"}
 | |
| 	register(r, "pod-2", svc2)
 | |
| 	time.Sleep(time.Millisecond * 100)
 | |
| 
 | |
| 	if routes, err := r.GetService("foo.service"); err != nil {
 | |
| 		t.Fatalf("Querying service returned an error: %v", err)
 | |
| 	} else if len(routes) != 1 {
 | |
| 		t.Fatalf("Expected one service, found %v", len(routes))
 | |
| 	}
 | |
| 
 | |
| 	// remove pods
 | |
| 	teardownRegistry()
 | |
| 	time.Sleep(time.Millisecond * 100)
 | |
| 
 | |
| }
 | |
| 
 | |
| func hasNodes(a, b []*registry.Node) bool {
 | |
| 	found := 0
 | |
| 	for _, nodeA := range a {
 | |
| 		for _, nodeB := range b {
 | |
| 			if nodeA.Id == nodeB.Id {
 | |
| 				found++
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return found == len(b)
 | |
| }
 | |
| 
 | |
| func hasServices(a, b []*registry.Service) bool {
 | |
| 	found := 0
 | |
| 
 | |
| 	for _, aV := range a {
 | |
| 		for _, bV := range b {
 | |
| 			if aV.Name != bV.Name {
 | |
| 				continue
 | |
| 			}
 | |
| 			if aV.Version != bV.Version {
 | |
| 				continue
 | |
| 			}
 | |
| 			if !hasNodes(aV.Nodes, bV.Nodes) {
 | |
| 				continue
 | |
| 			}
 | |
| 			found++
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	return found == len(b)
 | |
| }
 |