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 }