github.com/google/cloudprober@v0.11.3/targets/gce/gce_test.go (about) 1 // Copyright 2017-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 gce 16 17 import ( 18 "context" 19 "errors" 20 "net" 21 "reflect" 22 "sort" 23 "strings" 24 "testing" 25 26 "github.com/golang/protobuf/proto" 27 "github.com/google/cloudprober/logger" 28 "github.com/google/cloudprober/rds/client" 29 rdspb "github.com/google/cloudprober/rds/proto" 30 configpb "github.com/google/cloudprober/targets/gce/proto" 31 ) 32 33 type testNetIf struct { 34 privateIP string 35 publicIP string 36 aliasIPRange string 37 } 38 39 type testInstance struct { 40 name string 41 labels map[string]string 42 netIf []*testNetIf 43 } 44 45 var testInstancesDB = map[string][]*testInstance{ 46 "proj1": []*testInstance{ 47 &testInstance{ 48 name: "ins1", 49 netIf: []*testNetIf{ 50 &testNetIf{"10.216.0.1", "104.100.143.1", "192.168.1.0/24"}, 51 }, 52 labels: map[string]string{ 53 "key1": "a", 54 "key2": "b", 55 }, 56 }, 57 &testInstance{ 58 name: "ins2", 59 netIf: []*testNetIf{ 60 &testNetIf{"10.216.0.2", "104.100.143.2", "192.168.2.0/24"}, 61 &testNetIf{"10.216.1.2", "", ""}, 62 }, 63 labels: map[string]string{ 64 "key1": "a", 65 }, 66 }, 67 }, 68 "proj2": []*testInstance{ 69 &testInstance{ 70 name: "ins21", 71 labels: map[string]string{ 72 "key1": "a", 73 }, 74 }, 75 }, 76 "proj3": []*testInstance{ 77 &testInstance{ 78 name: "ins31", 79 labels: make(map[string]string), 80 }, 81 }, 82 } 83 84 func TestInstancesTargets(t *testing.T) { 85 var testIndex int 86 87 // ################################################################# 88 // Targets, with first NIC and private IP 89 // ################################################################# 90 c := &configpb.Instances{} 91 testListAndResolve(t, c, testIndex, "private") 92 93 // ################################################################# 94 // Targets, with first NIC and public IP 95 // ################################################################# 96 ipType := configpb.Instances_NetworkInterface_PUBLIC 97 c = &configpb.Instances{ 98 NetworkInterface: &configpb.Instances_NetworkInterface{ 99 IpType: &ipType, 100 }, 101 } 102 testListAndResolve(t, c, testIndex, "public") 103 104 // ################################################################# 105 // Targets, with first NIC and alias IP 106 // ################################################################# 107 ipType = configpb.Instances_NetworkInterface_ALIAS 108 c = &configpb.Instances{ 109 NetworkInterface: &configpb.Instances_NetworkInterface{ 110 IpType: &ipType, 111 }, 112 } 113 testListAndResolve(t, c, testIndex, "ipAliasRange") 114 115 // ################################################################# 116 // Targets, with second NIC and private IP 117 // Resolve will fail for ins1 as it has only one NIC 118 // ################################################################# 119 c = &configpb.Instances{ 120 NetworkInterface: &configpb.Instances_NetworkInterface{ 121 Index: proto.Int32(int32(testIndex)), 122 }, 123 } 124 testListAndResolve(t, c, testIndex, "private") 125 126 // ################################################################# 127 // Targets, with second NIC and public IP 128 // Resolve will fail for both instances as first one doesn't have 129 // second NIC and second instance's second NIC doesn't have public IP 130 // ################################################################## 131 testIndex = 1 132 ipType = configpb.Instances_NetworkInterface_PUBLIC 133 c = &configpb.Instances{ 134 NetworkInterface: &configpb.Instances_NetworkInterface{ 135 Index: proto.Int32(int32(testIndex)), 136 IpType: &ipType, 137 }, 138 } 139 testListAndResolve(t, c, testIndex, "public") 140 } 141 142 func testGCEResources(t *testing.T, c *configpb.Instances) *gceResources { 143 gr := &gceResources{ 144 resourceType: "gce_instances", 145 clients: make(map[string]*client.Client), 146 ipConfig: instancesIPConfig(c), 147 } 148 149 if len(c.GetLabel()) > 0 { 150 filters, err := parseLabels(c.GetLabel()) 151 if err != nil { 152 t.Errorf("Error parsing configured labels: %v", err) 153 } 154 gr.filters = filters 155 } 156 157 for project := range testInstancesDB { 158 client, err := newRDSClient(gr.rdsRequest(project), funcListResources, &logger.Logger{}) 159 if err != nil { 160 t.Errorf("Error creating RDS clients for testing: %v", err) 161 } 162 gr.clients[project] = client 163 } 164 165 return gr 166 } 167 168 func testListAndResolve(t *testing.T, c *configpb.Instances, testIndex int, ipTypeStr string) { 169 gr := testGCEResources(t, c) 170 171 // Merge instances from all projects to get expectedList 172 var expectedList []string 173 for _, pTestInstances := range testInstancesDB { 174 for _, ti := range pTestInstances { 175 expectedList = append(expectedList, ti.name) 176 } 177 } 178 sort.Strings(expectedList) 179 180 var gotList []string 181 for _, ep := range gr.ListEndpoints() { 182 gotList = append(gotList, ep.Name) 183 } 184 sort.Strings(gotList) 185 186 if !reflect.DeepEqual(gotList, expectedList) { 187 t.Errorf("Got wrong list of targets. Expected: %v, Got: %v", expectedList, gotList) 188 } 189 190 // Verify resolve functionality. 191 for _, pTestInstances := range testInstancesDB { 192 for _, ti := range pTestInstances { 193 ip, err := gr.Resolve(ti.name, 4) 194 // If instance doesn't have required number of NICs. 195 if len(ti.netIf) <= testIndex { 196 if err == nil { 197 t.Errorf("Expected error while resolving for network interface that doesn't exist. Network interface index: %d, Instance name: %s.", testIndex, ti.name) 198 } 199 continue 200 } 201 var expectedIP string 202 switch ipTypeStr { 203 case "private": 204 expectedIP = ti.netIf[testIndex].privateIP 205 case "public": 206 expectedIP = ti.netIf[testIndex].publicIP 207 case "ipAliasRange": 208 expectedNetIP, _, _ := net.ParseCIDR(ti.netIf[testIndex].aliasIPRange) 209 expectedIP = expectedNetIP.String() 210 } 211 // Didn't expect an IP address and we didn't get error 212 if expectedIP == "" { 213 if err == nil { 214 t.Errorf("Expected error while resolving for an IP type <%s> that doesn't exist on network interface. Network interface index: %d, Instance name: %s, Got IP: %s", ipTypeStr, testIndex, ti.name, ip.String()) 215 } 216 continue 217 } 218 if err != nil { 219 t.Errorf("Got unexpected error while resolving private IP for %s. Err: %v", ti.name, err) 220 } 221 if ip.String() != expectedIP { 222 t.Errorf("Got wrong <%s> IP for %s. Expected: %s, Got: %s", ipTypeStr, ti.name, expectedIP, ip) 223 } 224 } 225 } 226 } 227 228 // We use funcListResources to create RDS clients for testing purpose. 229 func funcListResources(ctx context.Context, in *rdspb.ListResourcesRequest) (*rdspb.ListResourcesResponse, error) { 230 path := in.GetResourcePath() 231 if !strings.HasPrefix(path, "gce_instances/") { 232 return nil, errors.New("unsupported resource_type") 233 } 234 235 project := strings.TrimPrefix(path, "gce_instances/") 236 var resources []*rdspb.Resource 237 238 for _, ti := range testInstancesDB[project] { 239 res := &rdspb.Resource{Name: &ti.name} 240 241 // We do a minimal labels check for testing purpose. Detailed tests are 242 // done within the RDS package. 243 matched := true 244 for _, f := range in.GetFilter() { 245 if strings.HasPrefix(f.GetKey(), "labels.") { 246 if _, ok := ti.labels[strings.TrimPrefix(f.GetKey(), "labels.")]; !ok { 247 matched = false 248 } 249 } 250 } 251 if !matched { 252 continue 253 } 254 255 if len(ti.netIf) < int(in.GetIpConfig().GetNicIndex())+1 { 256 res.Ip = proto.String("") 257 } else { 258 ni := ti.netIf[in.GetIpConfig().GetNicIndex()] 259 var ip string 260 261 switch in.GetIpConfig().GetIpType().String() { 262 case "DEFAULT": 263 ip = ni.privateIP 264 case "PUBLIC": 265 ip = ni.publicIP 266 case "ALIAS": 267 ip1, _, _ := net.ParseCIDR(ni.aliasIPRange) 268 ip = ip1.String() 269 } 270 271 res.Ip = &ip 272 } 273 274 resources = append(resources, res) 275 } 276 277 return &rdspb.ListResourcesResponse{Resources: resources}, nil 278 } 279 280 func TestIPListFilteringByLabels(t *testing.T) { 281 tests := []struct { 282 desc string 283 label string 284 want []string 285 }{ 286 { 287 "key1", 288 "key1:a", 289 []string{"ins1", "ins2", "ins21"}, 290 }, 291 { 292 "key2", 293 "key2:a", 294 []string{"ins1"}, 295 }, 296 } 297 298 for _, test := range tests { 299 t.Run(test.desc, func(t *testing.T) { 300 c := &configpb.Instances{ 301 Label: []string{test.label}, 302 } 303 gr := testGCEResources(t, c) 304 305 var gotList []string 306 for _, ep := range gr.ListEndpoints() { 307 gotList = append(gotList, ep.Name) 308 } 309 sort.Strings(gotList) 310 311 if !reflect.DeepEqual(gotList, test.want) { 312 t.Errorf("Got wrong list of targets. Got: %v, Want: %v", gotList, test.want) 313 } 314 }) 315 } 316 }