Skip to content

refactor: split traceroute implementation from check #304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

lvlcn-t
Copy link
Collaborator

@lvlcn-t lvlcn-t commented Jun 23, 2025

Motivation

The current traceroute implementation in the Sparrow monitoring check was tightly coupled to the check logic and difficult to test or reuse. By extracting the core traceroute functionality into its own internal/traceroute package, we achieve:

  • Modularity & Reusability: Consumers of the package can now import a simple traceroute.Client abstraction without dragging in all of the Sparrow check code. In the future, this package can be promoted to a standalone CLI tool or library for other Go projects.
  • Testability: Splitting the traceroute logic into clearly defined components (clients, hoppers, listeners) and defining interfaces for raw socket interactions allows us to write focused unit tests for each piece, without relying on end-to-end network behavior.
  • Protocol Flexibility: The new genericClient abstraction lays the groundwork for supporting multiple traceroute protocols (TCP/UDP/ICMP) side-by-side, even though we currently only implement TCP.
  • Maintainability: Isolating ICMP parsing, TTL hopping, and TCP connection logic into dedicated types makes the codebase easier to navigate, reason about, and extend—whether fixing protocol-specific bugs or adding new features like IPv6 support.

This refactor ensures that Sparrow’s traceroute check remains robust and that the underlying traceroute machinery can grow independently as needs evolve.

Changes

Note: I highly recommend reviewing this PR in your editor, you need to open this file where all the logic, that this PR tries to simplify, is currently buried.

In order to improve the traceroute check, I needed to split the traceroute logic into a dedicated package which will be in internal but could be promoted to a dedicated CLI/go library in the future.

In the internal/traceroute package we now have the following types to split up the different parts of the traceroute into components that work symbiotically together:

  • The traceroute.Client interface is the main interaction entrypoint for external users. For consumers of the package they just create a new client that has one simple run method, which allows to pass the targets to traceroute to and some additional optional options.
  • Under the hood the traceroute.Client is a genericClient that will later do the hard-lifiting of deciding which traceroute client to use, for which protocol. Since we currently only have TCP traceroute, we could remove this and just use the tcpClient.
  • The tcpClient is responsible for implementing the TCP traceroute specific logic. Here is where the heavy-lifting is done to control the further parts of traceroute. To still be able to test this, I needed to leverage over anonymous functions on the struct that can be monkey patched in tests.
  • tracer is an abstraction to be able to test the hopper. It shall be implemented by all traceroute clients that implement a certain communication protocol (TCP/UDP/ICMP).
  • The hopper is a small abstraction to pull the ttl iterations and otel traces out of the tracers. It's also nice to have this because of easier unit testing.
  • The icmpListener is responsible for privilege guarding the raw socket interactions and reading/closing the raw socket that receives the ICMP messages by intermediate routers. It returns a simple icmpPacket which contains all the parsed information received over the raw socket. Here I also fixed a bug, where ICMPTypeDestinationUnreachable wasn't correctly considered. If we receive this type of message with the code 3, it means that we reached the target, but didn't use the correct port. Since the traceroute was able to reached the target, we can safely set the reached boolean. For more on this, see: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-3

For additional information look at the commits.

Tests done

  • Unit tests succeeded
  • E2E tests succeeded - Current traceroute e2e tests still use the old implementation, as this PR only introduces the refactor as new package without touching anything of the current traceroute check.

✔️ Added Unit tests

I've copied some old unit tests from the old implementation and added some new unit tests for:

  • error handling
  • helper functions
  • hop collection logic
  • TCP traces

Unfortunately, it's still hard to test the ICMP related logic since its quite low-level. For a later PR, we could add an interface for icmp.PacketConn, which could then be mocked thus unit tested!

🧑‍💼 Manual e2e tests

Since it was easiest to adjust the current traceroute check to test if the behavior still works, I did so in refactor/use-traceroute-pkg.

Logs
root@pc1:/shared# ./sparrow run -c config.yaml
Using config file: config.yaml
time=21:03:01 level=INFO source=/home/tom/dev/github.com/telekom/sparrow/cmd/run.go:75 msg="Running sparrow"
time=21:03:01 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/pkg/sparrow/metrics/metrics.go:103 msg="Tracing initialized with new provider" provider=""
time=21:03:01 level=INFO source=/home/tom/dev/github.com/telekom/sparrow/pkg/api/api.go:87 msg="Serving Api" addr=:8080
time=21:03:01 level=INFO source=/home/tom/dev/github.com/telekom/sparrow/pkg/checks/traceroute/check.go:58 msg="Starting traceroute check" interval=5s
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:73 msg="Starting TCP trace" target=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=39827 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=34249 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=33555 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=34170 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=33055 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=34557 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=39529 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=39630 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=35862 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=39493 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=44729 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=44729
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=50884 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=50884
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=47932 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=47932
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=52209 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=52209
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=34723 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=34723
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=44729 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=44729
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=38230 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=38230
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=50884 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=50884
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=49974 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=49974
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=47932 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=47932
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=60297 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=60297
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=52209 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=52209
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=48718 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=34723 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=48718
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=34723
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=58586 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=58586
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=38230 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=38230
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=35427 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=35427
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=40541 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=40541
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=49974 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=49974
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=54244 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=54244
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=60297 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=60297
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=48718 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=48718
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=60875 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=58586 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=60875
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=58586
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=46645 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=46645
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=35427 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=48890 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=48890
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=59200 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=59200
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=35427
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=50665 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=50665
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=39869 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=39869
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=36871 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=32931 receivedPort=36871
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=40541 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=33995 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=40541
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=38220 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="time exceeded" routerAddr=100.0.0.10 port=32931 reached=false
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=54244 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=54244
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=60875 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=60875
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=46645 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=46645
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=48890 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=48890
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=38640 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=59200 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:161 msg="Received ICMP message" port=32931 routerAddr=100.0.0.10
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=59200
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=50665 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=50665
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=39869 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=39869
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="destination unreachable" routerAddr=127.0.0.1 port=36871 reached=true
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=36871
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=30107 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="time exceeded" routerAddr=100.0.0.10 port=32931 reached=false
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:71 msg="Received ICMP message on another port, ignoring" expectedPort=33174 receivedPort=32931
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:63 msg="Reading ICMP message"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/icmp.go:109 msg="Received ICMP packet" type="time exceeded" routerAddr=195.11.14.1 port=33174 reached=false
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:161 msg="Received ICMP message" port=33174 routerAddr=195.11.14.1
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=33976 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=34128 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=30524 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=36603 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=32749 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=34251 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=38318 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=31703 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=33979 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=33264 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=33521 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=34707 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=33646 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/tcp.go:101 msg="TCP connection established" port=35759 addr=200.1.1.7:80
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/helpers.go:104 msg="1   195.11.14.1                                    3.918362ms"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/helpers.go:104 msg="2   100.0.0.10                                     3.81524ms"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/internal/traceroute/helpers.go:104 msg="3   200.1.1.7:80                                   3.892985ms  (reached)"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/pkg/checks/traceroute/check.go:69 msg="Traceroute check result" result="{\"200.1.1.7:80\":[{\"latency\":\"3.918362ms\",\"addr\":{\"ip\":\"195.11.14.1\"},\"name\":\"\",\"ttl\":1,\"reached\":false},{\"latency\":\"3.81524ms\",\"addr\":{\"ip\":\"100.0.0.10\"},\"name\":\"\",\"ttl\":2,\"reached\":false},{\"latency\":\"3.892985ms\",\"addr\":{\"ip\":\"200.1.1.7\",\"port\":80},\"name\":\"\",\"ttl\":3,\"reached\":true}]}"
time=21:03:06 level=DEBUG source=/home/tom/dev/github.com/telekom/sparrow/pkg/checks/traceroute/check.go:78 msg="Successfully finished traceroute check run"
API response
{
  "data": {
    "200.1.1.7:80": [
      {
        "latency": "599.587µs",
        "addr": {
          "ip": "195.11.14.1"
        },
        "name": "",
        "ttl": 1,
        "reached": false
      },
      {
        "latency": "1.221083ms",
        "addr": {
          "ip": "100.0.0.10"
        },
        "name": "",
        "ttl": 2,
        "reached": false
      },
      {
        "latency": "3.005795ms",
        "addr": {
          "ip": "200.1.1.7",
          "port": 80
        },
        "name": "",
        "ttl": 3,
        "reached": true
      }
    ]
  },
  "timestamp": "2025-06-23T21:03:26.53178972Z"
}

TODO

  • I've assigned this PR to myself
  • I've labeled this PR correctly
  • Test if the otel traces still work/contain usable information.

lvlcn-t added 3 commits June 23, 2025 00:08
…ed internal package

- Added TCP client implementation for traceroute and corresponding tests.
- Introduced ICMP listener for reading ICMP messages with tests.
- Created mock implementations for tracer and ICMP listener to facilitate testing.
- Defined data structures for traceroute results, targets, and hops with tests.

Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com>
This commit adds handling for ICMP Destination Unreachable messages when parsing the incoming ICMP packets. It ensures that the traceroute hop is correctly marked as reached if the code is 3 (Port Unreachable), which indicates that the destination itself is reachable but the specific port is not.

Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com>
Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com>
@lvlcn-t lvlcn-t self-assigned this Jun 23, 2025
@lvlcn-t lvlcn-t added the refactoring Refactoring of existing code label Jun 23, 2025
@lvlcn-t lvlcn-t requested a review from puffitos as a code owner June 23, 2025 23:11
@lvlcn-t lvlcn-t added the area/checks Issues/PRs related to Checks label Jun 23, 2025
Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com>
Comment on lines +202 to +212
conn, err := dialer.DialContext(ctx, "tcp", addr.String())
switch {
case err == nil:
return tcpConn{conn: conn, port: port}, nil
case errors.Is(err, unix.EADDRINUSE):
// If the address is already in use,
// we just retry with a new random port.
return dialTCP(ctx, addr, ttl, timeout)
default:
return tcpConn{conn: conn, port: port}, err
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I could simplify this to the following:

Suggested change
conn, err := dialer.DialContext(ctx, "tcp", addr.String())
switch {
case err == nil:
return tcpConn{conn: conn, port: port}, nil
case errors.Is(err, unix.EADDRINUSE):
// If the address is already in use,
// we just retry with a new random port.
return dialTCP(ctx, addr, ttl, timeout)
default:
return tcpConn{conn: conn, port: port}, err
}
conn, err := dialer.DialContext(ctx, "tcp", addr.String())
if errors.Is(err, unix.EADDRINUSE) {
// If the address is already in use,
// we just retry with a new random port.
return dialTCP(ctx, addr, ttl, timeout)
}
return tcpConn{conn: conn, port: port}, err

Do you think it's more readable that way or should we keep it like it currently is?

Comment on lines +130 to +133
// User error: we don't have the necessary capabilities
// to open a raw socket for reading ICMP messages.
case errors.Is(err, errICMPNotAvailable):
return wrapError(ctx, err, "ICMP not available for reading")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about this? Should we consider it an error or not?

With the current implementation, I treated it as an usual error that gets retried, but maybe we should just send a hop with minimal information on the channel and return nil or check errors.Is(err, errICMPNotAvailable) further up the stack?

Comment on lines +59 to +60
// Address is the target address to trace to.
Address string `json:"address" yaml:"address" mapstructure:"address"`
Copy link
Collaborator Author

@lvlcn-t lvlcn-t Jun 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This config API change is done consciously. I find the addr ugly, but if you think we should keep it for backwards compatibility, I'm okay with changing it back to addr in the struct field's tags.

@lvlcn-t lvlcn-t requested a review from Copilot June 23, 2025 23:41
Copilot

This comment was marked as outdated.

Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com>
@lvlcn-t lvlcn-t requested a review from Copilot July 1, 2025 17:34
Copilot

This comment was marked as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/checks Issues/PRs related to Checks refactoring Refactoring of existing code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant