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 }