tailscale/prober/dns_test.go
Anton Tolchanov 5336362e64 prober: export probe class and metrics from bandwidth prober
- Wrap each prober function into a probe class that allows associating
  metric labels and custom metrics with a given probe;
- Make sure all existing probe classes set a `class` metric label;
- Move bandwidth probe size from being a metric label to a separate
  gauge metric; this will make it possible to use it to calculate
  average used bandwidth using a PromQL query;
- Also export transfer time for the bandwidth prober (more accurate than
  the total probe time, since it excludes connection establishment
  time).

Updates tailscale/corp#17912

Signed-off-by: Anton Tolchanov <anton@tailscale.com>
2024-04-08 12:02:58 +01:00

117 lines
3.0 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package prober
import (
"context"
"fmt"
"net/netip"
"slices"
"sync"
"testing"
"tailscale.com/syncs"
)
func TestForEachAddr(t *testing.T) {
clk := newFakeTime()
p := newForTest(clk.Now, clk.NewTicker)
opts := ForEachAddrOpts{
Logf: t.Logf,
Networks: []string{"ip4", "ip6"},
}
var (
addr4_1 = netip.MustParseAddr("76.76.21.21")
addr4_2 = netip.MustParseAddr("127.0.0.1")
addr6_1 = netip.MustParseAddr("2600:9000:a602:b1e6:5b89:50a1:7cf7:67b8")
addr6_2 = netip.MustParseAddr("2600:9000:a51d:27c1:6748:d035:a989:fb3c")
)
var resolverAddrs4, resolverAddrs6 syncs.AtomicValue[[]netip.Addr]
resolverAddrs4.Store([]netip.Addr{addr4_1})
resolverAddrs6.Store([]netip.Addr{addr6_1, addr6_2})
opts.LookupNetIP = func(_ context.Context, network string, _ string) ([]netip.Addr, error) {
if network == "ip4" {
return resolverAddrs4.Load(), nil
} else if network == "ip6" {
return resolverAddrs6.Load(), nil
}
return nil, fmt.Errorf("unknown network %q", network)
}
var (
mu sync.Mutex // protects following
registered []netip.Addr
)
newProbe := func(addr netip.Addr) []*Probe {
// Called to register a new prober
t.Logf("called to register new probe for %v", addr)
mu.Lock()
defer mu.Unlock()
registered = append(registered, addr)
// Return a probe that does nothing; we don't care about what this does.
probe := p.Run(fmt.Sprintf("website/%s", addr), probeInterval, nil, FuncProbe(func(_ context.Context) error {
return nil
}))
return []*Probe{probe}
}
fep := makeForEachAddr("tailscale.com", newProbe, opts)
// Mimic a call from the prober; we do this ourselves instead of
// calling it via p.Run so we know that the probe has actually run.
ctx := context.Background()
if err := fep.run(ctx); err != nil {
t.Fatalf("run: %v", err)
}
mu.Lock()
wantAddrs := []netip.Addr{addr4_1, addr6_1, addr6_2}
if !slices.Equal(registered, wantAddrs) {
t.Errorf("got registered addrs %v; want %v", registered, wantAddrs)
}
mu.Unlock()
// Now, update our IP addresses to force the prober to close and
// re-create our probes.
resolverAddrs4.Store([]netip.Addr{addr4_2})
resolverAddrs6.Store([]netip.Addr{addr6_2})
// Clear out our test data.
mu.Lock()
registered = nil
mu.Unlock()
// Run our individual prober again manually (so we don't have to wait
// or coordinate with the created probers).
if err := fep.run(ctx); err != nil {
t.Fatalf("run: %v", err)
}
// Ensure that we only registered our net-new address (addr4_2).
mu.Lock()
wantAddrs = []netip.Addr{addr4_2}
if !slices.Equal(registered, wantAddrs) {
t.Errorf("got registered addrs %v; want %v", registered, wantAddrs)
}
mu.Unlock()
// Check that we don't have a probe for the addresses that we expect to
// have been removed (addr4_1 and addr6_1).
p.mu.Lock()
for _, addr := range []netip.Addr{addr4_1, addr6_1} {
_, ok := fep.probes[addr]
if ok {
t.Errorf("probe for %v still exists", addr)
}
}
p.mu.Unlock()
}