github.com/google/cloudprober@v0.11.3/rds/kubernetes/services_test.go (about)

     1  // Copyright 2019 The Cloudprober Authors.
     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  package kubernetes
    16  
    17  import (
    18  	"io/ioutil"
    19  	"reflect"
    20  	"testing"
    21  
    22  	"github.com/golang/protobuf/proto"
    23  	pb "github.com/google/cloudprober/rds/proto"
    24  )
    25  
    26  func testServiceInfo(name, ns, ip, publicIP, hostname string, labels map[string]string, ports []int) *serviceInfo {
    27  	si := &serviceInfo{Metadata: kMetadata{Name: name, Namespace: ns, Labels: labels}}
    28  	si.Spec.ClusterIP = ip
    29  
    30  	for _, port := range ports {
    31  		si.Spec.Ports = append(si.Spec.Ports, struct {
    32  			Name string
    33  			Port int
    34  		}{
    35  			Name: "",
    36  			Port: port,
    37  		})
    38  	}
    39  
    40  	if publicIP != "" || hostname != "" {
    41  		si.Status.LoadBalancer.Ingress = []struct{ IP, Hostname string }{
    42  			{
    43  				IP:       publicIP,
    44  				Hostname: hostname,
    45  			},
    46  		}
    47  	}
    48  	return si
    49  }
    50  
    51  func TestListSvcResources(t *testing.T) {
    52  	sl := &servicesLister{
    53  		cache: make(map[resourceKey]*serviceInfo),
    54  	}
    55  	for _, svc := range []*serviceInfo{
    56  		testServiceInfo("serviceA", "nsAB", "10.1.1.1", "", "", map[string]string{"app": "appA"}, []int{9313, 9314}),
    57  		testServiceInfo("serviceB", "nsAB", "10.1.1.2", "192.16.16.199", "", map[string]string{"app": "appB"}, []int{443}),
    58  		testServiceInfo("serviceC", "nsC", "10.1.1.3", "192.16.16.200", "serviceC.test.com", map[string]string{"app": "appC", "func": "web"}, []int{3141}),
    59  		testServiceInfo("serviceD", "nsD", "10.1.1.4", "", "serviceD.test.com", map[string]string{"app": "appD", "func": "web"}, []int{3141}),
    60  		testServiceInfo("serviceD", "devD", "10.2.1.4", "", "", map[string]string{"app": "appD", "func": "web"}, []int{3141}),
    61  	} {
    62  		rkey := resourceKey{svc.Metadata.Namespace, svc.Metadata.Name}
    63  		sl.keys = append(sl.keys, rkey)
    64  		sl.cache[rkey] = svc
    65  	}
    66  
    67  	tests := []struct {
    68  		desc          string
    69  		nameFilter    string
    70  		filters       map[string]string
    71  		labelsFilter  map[string]string
    72  		wantServices  []string
    73  		wantIPs       []string
    74  		wantPorts     []int32
    75  		wantPublicIPs []string
    76  		wantErr       bool
    77  	}{
    78  		{
    79  			desc:    "bad filter key, expect error",
    80  			filters: map[string]string{"names": "service(B|C)"},
    81  			wantErr: true,
    82  		},
    83  		{
    84  			desc:         "only name filter for serviceB and serviceC",
    85  			filters:      map[string]string{"name": "service(B|C)"},
    86  			wantServices: []string{"serviceB", "serviceC"},
    87  			wantIPs:      []string{"10.1.1.2", "10.1.1.3"},
    88  			wantPorts:    []int32{443, 3141},
    89  		},
    90  		{
    91  			desc:         "only port filter for ports 9314 and 3141",
    92  			filters:      map[string]string{"port": "314", "namespace": "ns.*"},
    93  			wantServices: []string{"serviceA", "serviceC", "serviceD"},
    94  			wantIPs:      []string{"10.1.1.1", "10.1.1.3", "10.1.1.4"},
    95  			wantPorts:    []int32{9314, 3141, 3141},
    96  		},
    97  		{
    98  			desc:         "name and namespace filter for serviceB",
    99  			filters:      map[string]string{"name": "service(B|C)", "namespace": "nsAB"},
   100  			wantServices: []string{"serviceB"},
   101  			wantIPs:      []string{"10.1.1.2"},
   102  			wantPorts:    []int32{443},
   103  		},
   104  		{
   105  			desc:         "only namespace filter for serviceA and serviceB",
   106  			filters:      map[string]string{"namespace": "nsAB"},
   107  			wantServices: []string{"serviceA_9313", "serviceA_9314", "serviceB"},
   108  			wantIPs:      []string{"10.1.1.1", "10.1.1.1", "10.1.1.2"},
   109  			wantPorts:    []int32{9313, 9314, 443},
   110  		},
   111  		{
   112  			desc:          "only services with public IPs",
   113  			wantServices:  []string{"serviceB", "serviceC", "serviceD"},
   114  			wantPublicIPs: []string{"192.16.16.199", "192.16.16.200", "serviceD.test.com"},
   115  			wantPorts:     []int32{443, 3141, 3141},
   116  		},
   117  		{
   118  			desc:         "only dev namespace",
   119  			filters:      map[string]string{"namespace": "dev.*"},
   120  			wantServices: []string{"serviceD"},
   121  			wantIPs:      []string{"10.2.1.4"},
   122  			wantPorts:    []int32{3141},
   123  		},
   124  	}
   125  
   126  	for _, test := range tests {
   127  		t.Run(test.desc, func(t *testing.T) {
   128  			var filtersPB []*pb.Filter
   129  			for k, v := range test.filters {
   130  				filtersPB = append(filtersPB, &pb.Filter{Key: proto.String(k), Value: proto.String(v)})
   131  			}
   132  
   133  			req := &pb.ListResourcesRequest{Filter: filtersPB}
   134  
   135  			if len(test.wantPublicIPs) != 0 {
   136  				req.IpConfig = &pb.IPConfig{
   137  					IpType: pb.IPConfig_PUBLIC.Enum(),
   138  				}
   139  			}
   140  
   141  			results, err := sl.listResources(req)
   142  			if err != nil {
   143  				if !test.wantErr {
   144  					t.Errorf("got unexpected error: %v", err)
   145  				}
   146  				return
   147  			}
   148  
   149  			var gotNames, gotIPs []string
   150  			var gotPorts []int32
   151  			for _, res := range results {
   152  				gotNames = append(gotNames, res.GetName())
   153  				gotIPs = append(gotIPs, res.GetIp())
   154  				gotPorts = append(gotPorts, res.GetPort())
   155  			}
   156  
   157  			if !reflect.DeepEqual(gotNames, test.wantServices) {
   158  				t.Errorf("services.listResources: got=%v, expected=%v", gotNames, test.wantServices)
   159  			}
   160  
   161  			wantIPs := test.wantIPs
   162  			if len(test.wantPublicIPs) != 0 {
   163  				wantIPs = test.wantPublicIPs
   164  			}
   165  
   166  			if !reflect.DeepEqual(gotIPs, wantIPs) {
   167  				t.Errorf("services.listResources IPs: got=%v, expected=%v", gotIPs, wantIPs)
   168  			}
   169  
   170  			if !reflect.DeepEqual(gotPorts, test.wantPorts) {
   171  				t.Errorf("services.listResources Ports: got=%v, expected=%v", gotPorts, test.wantPorts)
   172  			}
   173  		})
   174  	}
   175  }
   176  
   177  func TestParseSvcResourceList(t *testing.T) {
   178  	servicesListFile := "./testdata/services.json"
   179  	data, err := ioutil.ReadFile(servicesListFile)
   180  
   181  	if err != nil {
   182  		t.Fatalf("error reading test data file: %s", servicesListFile)
   183  	}
   184  	_, services, err := parseServicesJSON(data)
   185  
   186  	if err != nil {
   187  		t.Fatalf("Error while parsing services JSON data: %v", err)
   188  	}
   189  
   190  	expectedSvcs := map[string]struct {
   191  		namespace string
   192  		ip        string
   193  		publicIP  string
   194  		ports     []int
   195  		labels    map[string]string
   196  	}{
   197  		"cloudprober": {
   198  			namespace: "default",
   199  			ip:        "10.31.252.209",
   200  			ports:     []int{9313},
   201  			labels:    map[string]string{"app": "cloudprober"},
   202  		},
   203  		"cloudprober-rds": {
   204  			namespace: "default",
   205  			ip:        "10.96.15.88",
   206  			publicIP:  "192.88.99.199",
   207  			ports:     []int{9314, 9313},
   208  			labels:    map[string]string{"app": "cloudprober"},
   209  		},
   210  		"cloudprober-test": {
   211  			namespace: "default",
   212  			ip:        "10.31.246.77",
   213  			ports:     []int{9313},
   214  			labels:    map[string]string{"app": "cloudprober"},
   215  		},
   216  		"kubernetes": {
   217  			namespace: "system",
   218  			ip:        "10.31.240.1",
   219  			ports:     []int{443},
   220  			labels:    map[string]string{"component": "apiserver", "provider": "kubernetes"},
   221  		},
   222  	}
   223  
   224  	for name, svc := range expectedSvcs {
   225  		rkey := resourceKey{svc.namespace, name}
   226  		if services[rkey] == nil {
   227  			t.Errorf("didn't get service by the name: %s", name)
   228  		}
   229  
   230  		gotLabels := services[rkey].Metadata.Labels
   231  		if !reflect.DeepEqual(gotLabels, svc.labels) {
   232  			t.Errorf("%s service labels: got=%v, want=%v", name, gotLabels, svc.labels)
   233  		}
   234  
   235  		if services[rkey].Spec.ClusterIP != svc.ip {
   236  			t.Errorf("%s service ip: got=%s, want=%s", name, services[rkey].Spec.ClusterIP, svc.ip)
   237  		}
   238  
   239  		if svc.publicIP != "" {
   240  			if services[rkey].Status.LoadBalancer.Ingress[0].IP != svc.publicIP {
   241  				t.Errorf("%s service load balancer ip: got=%s, want=%s", name, services[rkey].Status.LoadBalancer.Ingress[0].IP, svc.publicIP)
   242  			}
   243  		}
   244  
   245  		var gotPorts []int
   246  		for _, port := range services[rkey].Spec.Ports {
   247  			gotPorts = append(gotPorts, port.Port)
   248  		}
   249  		if !reflect.DeepEqual(gotPorts, svc.ports) {
   250  			t.Errorf("%s service ports: got=%v, want=%v", name, gotPorts, svc.ports)
   251  		}
   252  	}
   253  }