github.com/google/cloudprober@v0.11.3/targets/targets_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 targets
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"net"
    21  	"reflect"
    22  	"regexp"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/golang/protobuf/proto"
    28  	"github.com/google/cloudprober/logger"
    29  	rdsclientpb "github.com/google/cloudprober/rds/client/proto"
    30  	"github.com/google/cloudprober/targets/endpoint"
    31  	targetspb "github.com/google/cloudprober/targets/proto"
    32  	testdatapb "github.com/google/cloudprober/targets/testdata"
    33  )
    34  
    35  // getMissing returns a list of items in "elems" missing from "from". Cannot
    36  // handle duplicate elements.
    37  func getMissing(elems []string, from []string) []string {
    38  	var missing []string
    39  	set := make(map[string]bool, len(from))
    40  	for _, e := range from {
    41  		set[e] = true
    42  	}
    43  
    44  	for _, e := range elems {
    45  		if !set[e] {
    46  			missing = append(missing, e)
    47  		}
    48  	}
    49  	return missing
    50  }
    51  
    52  type mockLister struct {
    53  	list []endpoint.Endpoint
    54  }
    55  
    56  func (mldl *mockLister) ListEndpoints() []endpoint.Endpoint {
    57  	return mldl.list
    58  }
    59  
    60  // TestList does not test the New function, and is specifically testing
    61  // the implementation of targets directly
    62  func TestList(t *testing.T) {
    63  	var rows = []struct {
    64  		desc   string
    65  		hosts  []string
    66  		re     string
    67  		ldList []endpoint.Endpoint
    68  		expect []string
    69  	}{
    70  		{
    71  			desc:   "hostB is lameduck",
    72  			hosts:  []string{"www.google.com", "127.0.0.1", "hostA", "hostB", "hostC"},
    73  			ldList: endpoint.EndpointsFromNames([]string{"hostB"}), // hostB is lameduck.
    74  			expect: []string{"www.google.com", "127.0.0.1", "hostA", "hostC"},
    75  		},
    76  		{
    77  			desc:   "all hosts no lameduck",
    78  			hosts:  []string{"www.google.com", "127.0.0.1", "hostA", "hostB", "hostC"},
    79  			re:     ".*",
    80  			expect: []string{"www.google.com", "127.0.0.1", "hostA", "hostB", "hostC"},
    81  		},
    82  		{
    83  			desc:   "only hosts starting with host and hostC is lameduck",
    84  			hosts:  []string{"www.google.com", "127.0.0.1", "hostA", "hostB", "hostC"},
    85  			re:     "host.*",
    86  			ldList: endpoint.EndpointsFromNames([]string{"hostC"}), // hostC is lameduck.
    87  			expect: []string{"hostA", "hostB"},
    88  		},
    89  		{
    90  			desc:   "only hosts starting with host and hostC was lameducked before hostC was updated",
    91  			hosts:  []string{"www.google.com", "127.0.0.1", "hostA", "hostB", "hostC"},
    92  			re:     "host.*",
    93  			ldList: []endpoint.Endpoint{{Name: "hostC", LastUpdated: time.Now().Add(-time.Hour)}}, // hostC is lameduck.
    94  			expect: []string{"hostA", "hostB", "hostC"},
    95  		},
    96  		{
    97  			desc:  "empty as no hosts match the regex",
    98  			hosts: []string{"www.google.com", "127.0.0.1", "hostA", "hostB", "hostC"},
    99  			re:    "empty.*",
   100  		},
   101  	}
   102  
   103  	for _, r := range rows {
   104  		baseTime := time.Now()
   105  
   106  		var targetEP []endpoint.Endpoint
   107  		for _, host := range r.hosts {
   108  			targetEP = append(targetEP, endpoint.Endpoint{
   109  				Name:        host,
   110  				LastUpdated: baseTime,
   111  			})
   112  		}
   113  
   114  		t.Run(r.desc, func(t *testing.T) {
   115  			bt, err := baseTargets(nil, &mockLister{r.ldList}, nil)
   116  			if err != nil {
   117  				t.Errorf("Unexpected error building targets: %v", err)
   118  				return
   119  			}
   120  			bt.re = regexp.MustCompile(r.re)
   121  			bt.lister = &mockLister{targetEP}
   122  
   123  			got := endpoint.NamesFromEndpoints(bt.ListEndpoints())
   124  			if !reflect.DeepEqual(got, r.expect) {
   125  				// Ignore the case when both slices are zero length, DeepEqual doesn't
   126  				// handle initialized but zero and non-initialized comparison very well.
   127  				if !(len(got) == 0 && len(r.expect) == 0) {
   128  					t.Errorf("tgts.List(): got=%v, want=%v", got, r.expect)
   129  				}
   130  			}
   131  
   132  			gotEndpoints := endpoint.NamesFromEndpoints(bt.ListEndpoints())
   133  			if !reflect.DeepEqual(gotEndpoints, r.expect) {
   134  				// Ignore the case when both slices are zero length, DeepEqual doesn't
   135  				// handle initialized but zero and non-initialized comparison very well.
   136  				if !(len(got) == 0 && len(r.expect) == 0) {
   137  					t.Errorf("tgts.ListEndpoints(): got=%v, want=%v", gotEndpoints, r.expect)
   138  				}
   139  			}
   140  
   141  		})
   142  	}
   143  }
   144  
   145  func TestDummyTargets(t *testing.T) {
   146  	targetsDef := &targetspb.TargetsDef{
   147  		Type: &targetspb.TargetsDef_DummyTargets{
   148  			DummyTargets: &targetspb.DummyTargets{},
   149  		},
   150  	}
   151  	l := &logger.Logger{}
   152  	tgts, err := New(targetsDef, nil, nil, nil, l)
   153  	if err != nil {
   154  		t.Fatalf("New(...) Unexpected errors %v", err)
   155  	}
   156  	got := endpoint.NamesFromEndpoints(tgts.ListEndpoints())
   157  	want := []string{""}
   158  	if !reflect.DeepEqual(got, []string{""}) {
   159  		t.Errorf("tgts.List() = %q, want %q", got, want)
   160  	}
   161  	ip, err := tgts.Resolve(got[0], 4)
   162  	if err != nil {
   163  		t.Errorf("tgts.Resolve(%q, 4) Unexpected errors %v", got[0], err)
   164  	} else if !ip.IsUnspecified() {
   165  		t.Errorf("tgts.Resolve(%q, 4) = %v is specified, expected unspecified", got[0], ip)
   166  	}
   167  	ip, err = tgts.Resolve(got[0], 6)
   168  	if err != nil {
   169  		t.Errorf("tgts.Resolve(%q, 6) Unexpected errors %v", got[0], err)
   170  	} else if !ip.IsUnspecified() {
   171  		t.Errorf("tgts.Resolve(%q, 6) = %v is specified, expected unspecified", got[0], ip)
   172  	}
   173  }
   174  
   175  type testTargetsType struct {
   176  	names []string
   177  }
   178  
   179  func (tgts *testTargetsType) List() []string {
   180  	return tgts.names
   181  }
   182  
   183  func (tgts *testTargetsType) ListEndpoints() []endpoint.Endpoint {
   184  	return endpoint.EndpointsFromNames(tgts.names)
   185  }
   186  
   187  func (tgts *testTargetsType) Resolve(name string, ipVer int) (net.IP, error) {
   188  	return nil, errors.New("resolve not implemented")
   189  }
   190  
   191  func TestGetExtensionTargets(t *testing.T) {
   192  	targetsDef := &targetspb.TargetsDef{}
   193  
   194  	// This has the same effect as using the following in your config:
   195  	// targets {
   196  	//    [cloudprober.testdata.fancy_targets] {
   197  	//      name: "fancy"
   198  	//    }
   199  	// }
   200  	err := proto.SetExtension(targetsDef, testdatapb.E_FancyTargets, &testdatapb.FancyTargets{Name: proto.String("fancy")})
   201  	if err != nil {
   202  		t.Fatalf("error setting up extension in test targets proto: %v", err)
   203  	}
   204  	tgts, err := New(targetsDef, nil, nil, nil, nil)
   205  	if err == nil {
   206  		t.Errorf("Expected error in building targets from extensions, got nil. targets: %v", tgts)
   207  	}
   208  	testTargets := []string{"a", "b"}
   209  	RegisterTargetsType(200, func(conf interface{}, l *logger.Logger) (Targets, error) {
   210  		return &testTargetsType{names: testTargets}, nil
   211  	})
   212  	tgts, err = New(targetsDef, nil, nil, nil, nil)
   213  	if err != nil {
   214  		t.Errorf("Got error in building targets from extensions: %v.", err)
   215  	}
   216  	tgtsList := endpoint.NamesFromEndpoints(tgts.ListEndpoints())
   217  	if !reflect.DeepEqual(tgtsList, testTargets) {
   218  		t.Errorf("Extended targets: tgts.List()=%v, expected=%v", tgtsList, testTargets)
   219  	}
   220  }
   221  
   222  func TestSharedTargets(t *testing.T) {
   223  	testHosts := []string{"host1", "host2"}
   224  
   225  	// Create shared targets and re-use them.
   226  	SetSharedTargets("shared_test_targets", StaticTargets(strings.Join(testHosts, ",")))
   227  
   228  	var tgts [2]Targets
   229  
   230  	for i := range tgts {
   231  		targetsDef := &targetspb.TargetsDef{
   232  			Type: &targetspb.TargetsDef_SharedTargets{SharedTargets: "shared_test_targets"},
   233  		}
   234  
   235  		var err error
   236  		tgts[i], err = New(targetsDef, nil, nil, nil, nil)
   237  
   238  		if err != nil {
   239  			t.Errorf("got error while creating targets from shared targets: %v", err)
   240  		}
   241  
   242  		got := endpoint.NamesFromEndpoints(tgts[i].ListEndpoints())
   243  		if !reflect.DeepEqual(got, testHosts) {
   244  			t.Errorf("Unexpected targets: tgts.List()=%v, expected=%v", got, testHosts)
   245  		}
   246  	}
   247  }
   248  
   249  func TestRDSClientConf(t *testing.T) {
   250  	provider := "test-provider"
   251  	rPath := "test-rsources"
   252  
   253  	var rows = []struct {
   254  		desc       string
   255  		localAddr  string
   256  		globalAddr string
   257  		provider   string
   258  		wantErr    bool
   259  		wantAddr   string
   260  	}{
   261  		{
   262  			desc:     "Error as RDS server address is not initialized",
   263  			provider: provider,
   264  			wantErr:  true,
   265  		},
   266  		{
   267  			desc:       "Pick global address",
   268  			provider:   provider,
   269  			globalAddr: "test-global-addr",
   270  			wantAddr:   "test-global-addr",
   271  		},
   272  		{
   273  			desc:       "Pick local address over global",
   274  			provider:   provider,
   275  			localAddr:  "test-local-addr",
   276  			globalAddr: "test-global-addr",
   277  			wantAddr:   "test-local-addr",
   278  		},
   279  		{
   280  			desc:      "Error because no provider",
   281  			provider:  "",
   282  			localAddr: "test-local-addr",
   283  			wantAddr:  "test-local-addr",
   284  			wantErr:   true,
   285  		},
   286  	}
   287  
   288  	for _, r := range rows {
   289  		t.Run(r.desc, func(t *testing.T) {
   290  			pb := &targetspb.RDSTargets{
   291  				ResourcePath: proto.String(fmt.Sprintf("%s://%s", r.provider, rPath)),
   292  			}
   293  			if r.localAddr != "" {
   294  				pb.RdsServerOptions = &rdsclientpb.ClientConf_ServerOptions{
   295  					ServerAddress: proto.String(r.localAddr),
   296  				}
   297  			}
   298  
   299  			globalOpts := &targetspb.GlobalTargetsOptions{}
   300  			if r.globalAddr != "" {
   301  				globalOpts.RdsServerOptions = &rdsclientpb.ClientConf_ServerOptions{
   302  					ServerAddress: proto.String(r.globalAddr),
   303  				}
   304  			}
   305  
   306  			_, cc, err := RDSClientConf(pb, globalOpts, nil)
   307  			if (err != nil) != r.wantErr {
   308  				t.Errorf("wantErr: %v, got err: %v", r.wantErr, err)
   309  			}
   310  
   311  			if err != nil {
   312  				return
   313  			}
   314  
   315  			if cc.GetServerOptions().GetServerAddress() != r.wantAddr {
   316  				t.Errorf("Got RDS server address: %s, wanted: %s", cc.GetServerOptions().GetServerAddress(), r.wantAddr)
   317  			}
   318  			if cc.GetRequest().GetProvider() != provider {
   319  				t.Errorf("Got provider: %s, wanted: %s", cc.GetRequest().GetProvider(), provider)
   320  			}
   321  			if cc.GetRequest().GetResourcePath() != rPath {
   322  				t.Errorf("Got resource path: %s, wanted: %s", cc.GetRequest().GetResourcePath(), rPath)
   323  			}
   324  		})
   325  	}
   326  }