|
|
@@ -0,0 +1,73 @@
|
|
|
+//go:build linux || darwin
|
|
|
+
|
|
|
+package nebula
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "net/netip"
|
|
|
+ "testing"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/rcrowley/go-metrics"
|
|
|
+ "github.com/slackhq/nebula/cert"
|
|
|
+ "github.com/slackhq/nebula/firewall"
|
|
|
+ "github.com/slackhq/nebula/overlay/overlaytest"
|
|
|
+ "github.com/slackhq/nebula/test"
|
|
|
+ "github.com/slackhq/nebula/udp"
|
|
|
+ "github.com/stretchr/testify/assert"
|
|
|
+ "github.com/stretchr/testify/require"
|
|
|
+)
|
|
|
+
|
|
|
+// Test_emitStats_primesGauges covers issue #907: a Prometheus scrape that
|
|
|
+// landed before the first ticker fire used to read 0 for the cert gauges.
|
|
|
+// emitStats now primes the gauges before entering the ticker loop. We assert
|
|
|
+// the gauge is zero before the first call and non-zero after.
|
|
|
+func Test_emitStats_primesGauges(t *testing.T) {
|
|
|
+ defer metrics.DefaultRegistry.UnregisterAll()
|
|
|
+
|
|
|
+ l := test.NewLogger()
|
|
|
+ hostMap := newHostMap(l)
|
|
|
+ preferredRanges := []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")}
|
|
|
+ hostMap.preferredRanges.Store(&preferredRanges)
|
|
|
+
|
|
|
+ notAfter := time.Now().Add(time.Hour)
|
|
|
+ cs := &CertState{
|
|
|
+ initiatingVersion: cert.Version1,
|
|
|
+ privateKey: []byte{},
|
|
|
+ v1Cert: &dummyCert{version: cert.Version1, notAfter: notAfter},
|
|
|
+ v1Credential: nil,
|
|
|
+ }
|
|
|
+
|
|
|
+ lh := newTestLighthouse()
|
|
|
+ ifce := &Interface{
|
|
|
+ hostMap: hostMap,
|
|
|
+ inside: &overlaytest.NoopTun{},
|
|
|
+ outside: &udp.NoopConn{},
|
|
|
+ firewall: &Firewall{Conntrack: &FirewallConntrack{Conns: map[firewall.Packet]*conn{}}},
|
|
|
+ lightHouse: lh,
|
|
|
+ pki: &PKI{},
|
|
|
+ handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
|
|
|
+ l: l,
|
|
|
+ // On linux, udp.NewUDPStatsEmitter indexes writers[0] and asserts to
|
|
|
+ // *udp.StdConn. A zero value works: getMemInfo sees a nil rawConn,
|
|
|
+ // returns an error, and the emitter falls through to a no-op.
|
|
|
+ writers: []udp.Conn{&udp.StdConn{}},
|
|
|
+ }
|
|
|
+ ifce.pki.cs.Store(cs)
|
|
|
+
|
|
|
+ ttlGauge := metrics.GetOrRegisterGauge("certificate.ttl_seconds", nil)
|
|
|
+ require.Zero(t, ttlGauge.Value(), "gauge should be zero before emitStats runs")
|
|
|
+
|
|
|
+ // Pre-cancel the context so emitStats returns after priming the gauges
|
|
|
+ // without ever reading from ticker.C. The one hour interval is just a
|
|
|
+ // belt-and-suspenders, the test does not expect the ticker to fire.
|
|
|
+ ctx, cancel := context.WithCancel(context.Background())
|
|
|
+ cancel()
|
|
|
+ ifce.emitStats(ctx, time.Hour)
|
|
|
+
|
|
|
+ ttl := ttlGauge.Value()
|
|
|
+ assert.Positive(t, ttl, "ttl gauge should be primed by emitStats before its first tick")
|
|
|
+ assert.LessOrEqual(t, ttl, int64(3600))
|
|
|
+ assert.Equal(t, int64(cert.Version1), metrics.GetOrRegisterGauge("certificate.initiating_version", nil).Value())
|
|
|
+ assert.Equal(t, int64(cert.Version1), metrics.GetOrRegisterGauge("certificate.max_version", nil).Value())
|
|
|
+}
|