Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package goldpinger for openSUSE:Factory checked in at 2026-04-25 21:35:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/goldpinger (Old) and /work/SRC/openSUSE:Factory/.goldpinger.new.11940 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "goldpinger" Sat Apr 25 21:35:38 2026 rev:5 rq:1349018 version:3.11.2 Changes: -------- --- /work/SRC/openSUSE:Factory/goldpinger/goldpinger.changes 2026-04-21 12:43:49.167931569 +0200 +++ /work/SRC/openSUSE:Factory/.goldpinger.new.11940/goldpinger.changes 2026-04-25 21:35:56.010263119 +0200 @@ -1,0 +2,12 @@ +Thu Apr 23 19:30:17 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 3.11.2: + * chore(version): bump to v3.11.2 + * Also prune response-time histogram "check" call_type on peer + removal + * Use table-driven subtests with IPv4, IPv6, and mixed address + families + * Prune stale Prometheus metrics for defunct peer pod IPs on + teardown + +------------------------------------------------------------------- Old: ---- goldpinger-3.11.1.obscpio New: ---- goldpinger-3.11.2.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ goldpinger.spec ++++++ --- /var/tmp/diff_new_pack.vf8GZs/_old 2026-04-25 21:35:56.846297138 +0200 +++ /var/tmp/diff_new_pack.vf8GZs/_new 2026-04-25 21:35:56.846297138 +0200 @@ -17,7 +17,7 @@ Name: goldpinger -Version: 3.11.1 +Version: 3.11.2 Release: 0 Summary: Tests and displays connectivity between nodes in a Kubernetes cluster License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.vf8GZs/_old 2026-04-25 21:35:56.882298603 +0200 +++ /var/tmp/diff_new_pack.vf8GZs/_new 2026-04-25 21:35:56.886298766 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/bloomberg/goldpinger</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v3.11.1</param> + <param name="revision">v3.11.2</param> <param name="match-tag">v*</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.vf8GZs/_old 2026-04-25 21:35:56.910299742 +0200 +++ /var/tmp/diff_new_pack.vf8GZs/_new 2026-04-25 21:35:56.918300068 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/bloomberg/goldpinger</param> - <param name="changesrevision">fdb70968b886d341de2ea9300058ab308186717f</param></service></servicedata> + <param name="changesrevision">6f2fec60743f13d2235345b09ffc4f797d617bd9</param></service></servicedata> (No newline at EOF) ++++++ goldpinger-3.11.1.obscpio -> goldpinger-3.11.2.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goldpinger-3.11.1/Makefile new/goldpinger-3.11.2/Makefile --- old/goldpinger-3.11.1/Makefile 2026-04-20 16:03:22.000000000 +0200 +++ new/goldpinger-3.11.2/Makefile 2026-04-23 17:37:08.000000000 +0200 @@ -1,5 +1,5 @@ name ?= goldpinger -version ?= v3.11.1 +version ?= v3.11.2 bin ?= goldpinger pkg ?= "github.com/bloomberg/goldpinger" tag = $(name):$(version) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goldpinger-3.11.1/charts/goldpinger/Chart.yaml new/goldpinger-3.11.2/charts/goldpinger/Chart.yaml --- old/goldpinger-3.11.1/charts/goldpinger/Chart.yaml 2026-04-20 16:03:22.000000000 +0200 +++ new/goldpinger-3.11.2/charts/goldpinger/Chart.yaml 2026-04-23 17:37:08.000000000 +0200 @@ -1,7 +1,7 @@ apiVersion: v1 name: goldpinger -appVersion: "3.11.1" -version: 1.1.1 +appVersion: "3.11.2" +version: 1.1.2 description: Goldpinger is a tool to help debug, troubleshoot and visualize network connectivity and slowness issues. home: https://github.com/bloomberg/goldpinger sources: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goldpinger-3.11.1/pkg/goldpinger/stats.go new/goldpinger-3.11.2/pkg/goldpinger/stats.go --- old/goldpinger-3.11.1/pkg/goldpinger/stats.go 2026-04-20 16:03:22.000000000 +0200 +++ new/goldpinger-3.11.2/pkg/goldpinger/stats.go 2026-04-23 17:37:08.000000000 +0200 @@ -300,6 +300,15 @@ ).Set(float64(hopCount)) } +// DeletePeerMetrics removes stale metric labels for a destroyed peer. +// The response-time histogram is observed with two call_type values: +// "ping" (continuous from Pinger) and "check" (from CheckAllPods), so both +// must be pruned. Must be called unconditionally when a peer is removed. +func DeletePeerMetrics(hostIP, podIP string) { + goldpingerResponseTimePeersHistogram.DeleteLabelValues(GoldpingerConfig.Hostname, "ping", hostIP, podIP) + goldpingerResponseTimePeersHistogram.DeleteLabelValues(GoldpingerConfig.Hostname, "check", hostIP, podIP) +} + // DeletePeerUDPMetrics removes stale UDP metric labels for a destroyed peer. // This must be kept in sync with all per-peer UDP metrics to avoid stale // label sets lingering in /metrics after a pod rolls. @@ -309,6 +318,7 @@ goldpingerPeersUDPRtt.DeleteLabelValues(GoldpingerConfig.Hostname, hostIP, podIP) goldpingerUDPDuplicatesCounter.DeleteLabelValues(GoldpingerConfig.Hostname, hostIP, podIP) goldpingerUDPOutOfOrderCounter.DeleteLabelValues(GoldpingerConfig.Hostname, hostIP, podIP) + goldpingerUDPErrorsCounter.DeleteLabelValues(GoldpingerConfig.Hostname, pickPodHostIP(podIP, hostIP)) } // ObservePeerUDPRtt records a UDP RTT observation in seconds diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goldpinger-3.11.1/pkg/goldpinger/stats_test.go new/goldpinger-3.11.2/pkg/goldpinger/stats_test.go --- old/goldpinger-3.11.1/pkg/goldpinger/stats_test.go 2026-04-20 16:03:22.000000000 +0200 +++ new/goldpinger-3.11.2/pkg/goldpinger/stats_test.go 2026-04-23 17:37:08.000000000 +0200 @@ -7,49 +7,186 @@ dto "github.com/prometheus/client_model/go" ) +// TestDeletePeerMetrics_CleansResponseTimeHistogram verifies that +// DeletePeerMetrics removes the response-time histogram label set for +// a destroyed peer. This prevents stale pod IPs from lingering in +// /metrics after rolling updates (see #167). +func TestDeletePeerMetrics_CleansResponseTimeHistogram(t *testing.T) { + tests := []struct { + name string + hostIP string + podIP string + }{ + {"IPv4", "10.0.0.1", "10.0.0.2"}, + {"IPv6", "2001:db8::1", "2001:db8:1::2"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + origHostname := GoldpingerConfig.Hostname + GoldpingerConfig.Hostname = "test-instance" + defer func() { GoldpingerConfig.Hostname = origHostname }() + + // Simulate observations for both call_type values the histogram uses + goldpingerResponseTimePeersHistogram.WithLabelValues( + GoldpingerConfig.Hostname, "ping", tt.hostIP, tt.podIP, + ).Observe(0.005) + goldpingerResponseTimePeersHistogram.WithLabelValues( + GoldpingerConfig.Hostname, "check", tt.hostIP, tt.podIP, + ).Observe(0.010) + + if n := countMetrics(goldpingerResponseTimePeersHistogram); n != 2 { + t.Fatalf("response time histogram has %d label sets before cleanup, want 2 — test setup is broken", n) + } + + DeletePeerMetrics(tt.hostIP, tt.podIP) + + if n := countMetrics(goldpingerResponseTimePeersHistogram); n != 0 { + t.Errorf("response time histogram still has %d label set(s) after DeletePeerMetrics", n) + } + }) + } +} + +// TestDeletePeerMetrics_LeavesOtherPeersIntact verifies that pruning +// metrics for one peer does not affect a different peer's label set. +func TestDeletePeerMetrics_LeavesOtherPeersIntact(t *testing.T) { + tests := []struct { + name string + peerA [2]string // {hostIP, podIP} + peerB [2]string + }{ + { + "IPv4", + [2]string{"10.0.0.1", "10.0.0.2"}, + [2]string{"10.0.0.3", "10.0.0.4"}, + }, + { + "IPv6", + [2]string{"2001:db8::1", "2001:db8:1::2"}, + [2]string{"2001:db8::3", "2001:db8:2::4"}, + }, + { + "MixedV4DeleteV6Survives", + [2]string{"10.0.0.1", "10.0.0.2"}, + [2]string{"2001:db8::3", "2001:db8:2::4"}, + }, + { + "MixedV6DeleteV4Survives", + [2]string{"2001:db8::1", "2001:db8:1::2"}, + [2]string{"10.0.0.3", "10.0.0.4"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + origHostname := GoldpingerConfig.Hostname + GoldpingerConfig.Hostname = "test-instance" + defer func() { GoldpingerConfig.Hostname = origHostname }() + + // Peer A — observe both call types + goldpingerResponseTimePeersHistogram.WithLabelValues( + GoldpingerConfig.Hostname, "ping", tt.peerA[0], tt.peerA[1], + ).Observe(0.005) + goldpingerResponseTimePeersHistogram.WithLabelValues( + GoldpingerConfig.Hostname, "check", tt.peerA[0], tt.peerA[1], + ).Observe(0.006) + SetPeerLossPct(tt.peerA[0], tt.peerA[1], 0) + + // Peer B — observe both call types + goldpingerResponseTimePeersHistogram.WithLabelValues( + GoldpingerConfig.Hostname, "ping", tt.peerB[0], tt.peerB[1], + ).Observe(0.010) + goldpingerResponseTimePeersHistogram.WithLabelValues( + GoldpingerConfig.Hostname, "check", tt.peerB[0], tt.peerB[1], + ).Observe(0.011) + SetPeerLossPct(tt.peerB[0], tt.peerB[1], 1.5) + + // Delete peer A only + DeletePeerMetrics(tt.peerA[0], tt.peerA[1]) + DeletePeerUDPMetrics(tt.peerA[0], tt.peerA[1]) + + // Peer B's ping and check histogram entries should both survive + if n := countMetrics(goldpingerResponseTimePeersHistogram); n != 2 { + t.Errorf("response time histogram has %d label set(s), want 2 for peer B (ping+check)", n) + } + if countMetrics(goldpingerPeersLossPct) == 0 { + t.Error("loss pct gauge lost all label sets — peer B should still exist") + } + + // Clean up peer B so it doesn't leak into other tests + goldpingerResponseTimePeersHistogram.DeleteLabelValues( + GoldpingerConfig.Hostname, "ping", tt.peerB[0], tt.peerB[1], + ) + goldpingerResponseTimePeersHistogram.DeleteLabelValues( + GoldpingerConfig.Hostname, "check", tt.peerB[0], tt.peerB[1], + ) + goldpingerPeersLossPct.DeleteLabelValues( + GoldpingerConfig.Hostname, tt.peerB[0], tt.peerB[1], + ) + }) + } +} + // TestDeletePeerUDPMetrics_CleansAllPerPeerMetrics verifies that // DeletePeerUDPMetrics removes label sets from every per-peer UDP metric. // If a new per-peer metric is added but not cleaned up in // DeletePeerUDPMetrics, this test will fail. func TestDeletePeerUDPMetrics_CleansAllPerPeerMetrics(t *testing.T) { - // Save and restore hostname since we set it for the test - origHostname := GoldpingerConfig.Hostname - GoldpingerConfig.Hostname = "test-instance" - defer func() { GoldpingerConfig.Hostname = origHostname }() - - hostIP := "10.0.0.1" - podIP := "10.0.0.2" - - // Populate all per-peer UDP metrics so they have label values - SetPeerLossPct(hostIP, podIP, 5.0) - SetPeerHopCount(hostIP, podIP, 2) - ObservePeerUDPRtt(hostIP, podIP, 0.001) - CountUDPDuplicates(hostIP, podIP, 1) - CountUDPOutOfOrder(hostIP, podIP, 1) - - // Verify they exist before cleanup - perPeerCollectors := map[string]prometheus.Collector{ - "goldpinger_peers_loss_pct": goldpingerPeersLossPct, - "goldpinger_peers_hop_count": goldpingerPeersHopCount, - "goldpinger_peers_udp_rtt_s": goldpingerPeersUDPRtt, - "goldpinger_udp_duplicates_total": goldpingerUDPDuplicatesCounter, - "goldpinger_udp_out_of_order_total": goldpingerUDPOutOfOrderCounter, - } - - for name, collector := range perPeerCollectors { - if countMetrics(collector) == 0 { - t.Fatalf("metric %s has no label values before cleanup — test setup is broken", name) - } - } - - // Run cleanup - DeletePeerUDPMetrics(hostIP, podIP) - - // Verify all per-peer metrics are cleaned up - for name, collector := range perPeerCollectors { - if n := countMetrics(collector); n != 0 { - t.Errorf("metric %s still has %d label set(s) after DeletePeerUDPMetrics — add it to the cleanup function", name, n) - } + tests := []struct { + name string + hostIP string + podIP string + }{ + {"IPv4", "10.0.0.1", "10.0.0.2"}, + {"IPv6", "2001:db8::1", "2001:db8:1::2"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + origHostname := GoldpingerConfig.Hostname + origUseHostIP := GoldpingerConfig.UseHostIP + GoldpingerConfig.Hostname = "test-instance" + GoldpingerConfig.UseHostIP = false + defer func() { + GoldpingerConfig.Hostname = origHostname + GoldpingerConfig.UseHostIP = origUseHostIP + }() + + // Populate all per-peer UDP metrics so they have label values + SetPeerLossPct(tt.hostIP, tt.podIP, 5.0) + SetPeerHopCount(tt.hostIP, tt.podIP, 2) + ObservePeerUDPRtt(tt.hostIP, tt.podIP, 0.001) + CountUDPDuplicates(tt.hostIP, tt.podIP, 1) + CountUDPOutOfOrder(tt.hostIP, tt.podIP, 1) + CountUDPError(tt.podIP) // UseHostIP=false so target=podIP + + // Verify they exist before cleanup + perPeerCollectors := map[string]prometheus.Collector{ + "goldpinger_peers_loss_pct": goldpingerPeersLossPct, + "goldpinger_peers_hop_count": goldpingerPeersHopCount, + "goldpinger_peers_udp_rtt_s": goldpingerPeersUDPRtt, + "goldpinger_udp_duplicates_total": goldpingerUDPDuplicatesCounter, + "goldpinger_udp_out_of_order_total": goldpingerUDPOutOfOrderCounter, + "goldpinger_udp_errors_total": goldpingerUDPErrorsCounter, + } + + for name, collector := range perPeerCollectors { + if countMetrics(collector) == 0 { + t.Fatalf("metric %s has no label values before cleanup — test setup is broken", name) + } + } + + // Run cleanup + DeletePeerUDPMetrics(tt.hostIP, tt.podIP) + + // Verify all per-peer metrics are cleaned up + for name, collector := range perPeerCollectors { + if n := countMetrics(collector); n != 0 { + t.Errorf("metric %s still has %d label set(s) after DeletePeerUDPMetrics — add it to the cleanup function", name, n) + } + } + }) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goldpinger-3.11.1/pkg/goldpinger/updater.go new/goldpinger-3.11.2/pkg/goldpinger/updater.go --- old/goldpinger-3.11.1/pkg/goldpinger/updater.go 2026-04-20 16:03:22.000000000 +0200 +++ new/goldpinger-3.11.2/pkg/goldpinger/updater.go 2026-04-23 17:37:08.000000000 +0200 @@ -137,7 +137,9 @@ // Close the channel to stop pinging close(pinger.stopChan) - // Clean up stale UDP metric labels for this peer + // Clean up stale metric labels for this peer so defunct pod IPs + // don't linger in /metrics after rolling updates (see #167) + DeletePeerMetrics(pod.HostIP, pod.PodIP) if GoldpingerConfig.UDPEnabled { DeletePeerUDPMetrics(pod.HostIP, pod.PodIP) } ++++++ goldpinger.obsinfo ++++++ --- /var/tmp/diff_new_pack.vf8GZs/_old 2026-04-25 21:35:57.374318624 +0200 +++ /var/tmp/diff_new_pack.vf8GZs/_new 2026-04-25 21:35:57.378318787 +0200 @@ -1,5 +1,5 @@ name: goldpinger -version: 3.11.1 -mtime: 1776693802 -commit: fdb70968b886d341de2ea9300058ab308186717f +version: 3.11.2 +mtime: 1776958628 +commit: 6f2fec60743f13d2235345b09ffc4f797d617bd9 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/goldpinger/vendor.tar.gz /work/SRC/openSUSE:Factory/.goldpinger.new.11940/vendor.tar.gz differ: char 142, line 2
