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  }