From 0f0b0a5500e408a4b9ed60a0144806ea255beb2f Mon Sep 17 00:00:00 2001 From: Niklas Treml Date: Thu, 5 Sep 2024 17:42:26 +0200 Subject: [PATCH 01/12] feat: basic metrics Signed-off-by: Niklas Treml --- go.mod | 2 +- pkg/checks/traceroute/check.go | 16 +++++++++++++--- pkg/checks/traceroute/test-lab/lab.conf | 11 +++++++++++ pkg/checks/traceroute/test-lab/pc1.startup | 2 ++ pkg/checks/traceroute/traceroute.go | 13 +++++++++++++ 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 5b20c44c..e11dec42 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 go.opentelemetry.io/otel/sdk v1.28.0 + go.opentelemetry.io/otel/trace v1.28.0 golang.org/x/net v0.28.0 golang.org/x/sys v0.24.0 google.golang.org/grpc v1.65.0 @@ -59,7 +60,6 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect diff --git a/pkg/checks/traceroute/check.go b/pkg/checks/traceroute/check.go index ab6e22d4..a66d037f 100644 --- a/pkg/checks/traceroute/check.go +++ b/pkg/checks/traceroute/check.go @@ -2,6 +2,7 @@ package traceroute import ( "context" + "fmt" "sync" "time" @@ -10,6 +11,8 @@ import ( "github.com/caas-team/sparrow/pkg/checks" "github.com/getkin/kin-openapi/openapi3" "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" ) var _ checks.Check = (*Traceroute)(nil) @@ -23,6 +26,10 @@ type Target struct { Port int `json:"port" yaml:"port" mapstructure:"port"` } +func (t Target) String() string { + return fmt.Sprintf("%s:%d", t.Addr, t.Port) +} + func NewCheck() checks.Check { return &Traceroute{ CheckBase: checks.CheckBase{ @@ -63,6 +70,7 @@ func (tr *Traceroute) Run(ctx context.Context, cResult chan checks.ResultDTO) er ctx, cancel := logger.NewContextWithLogger(ctx) defer cancel() log := logger.FromContext(ctx) + tracer := otel.Tracer("tracer.traceroute") log.Info("Starting traceroute check", "interval", tr.config.Interval.String()) for { @@ -73,7 +81,7 @@ func (tr *Traceroute) Run(ctx context.Context, cResult chan checks.ResultDTO) er case <-tr.DoneChan: return nil case <-time.After(tr.config.Interval): - res := tr.check(ctx) + res := tr.check(tracer, ctx) tr.metrics.MinHops(res) cResult <- checks.ResultDTO{ Name: tr.Name(), @@ -94,7 +102,7 @@ func (tr *Traceroute) GetConfig() checks.Runtime { return &tr.config } -func (tr *Traceroute) check(ctx context.Context) map[string]result { +func (tr *Traceroute) check(tracer trace.Tracer, ctx context.Context) map[string]result { res := make(map[string]result) log := logger.FromContext(ctx) @@ -112,8 +120,10 @@ func (tr *Traceroute) check(ctx context.Context) map[string]result { for _, t := range tr.config.Targets { go func(t Target) { defer wg.Done() - l := log.With("target", t.Addr) + l := log.With("target", t.String()) l.Debug("Running traceroute") + ctx, span := tracer.Start(ctx, t.String()) + defer span.End() targetstart := time.Now() trace, err := tr.traceroute(ctx, tracerouteConfig{ diff --git a/pkg/checks/traceroute/test-lab/lab.conf b/pkg/checks/traceroute/test-lab/lab.conf index 6219303a..3b644f6d 100644 --- a/pkg/checks/traceroute/test-lab/lab.conf +++ b/pkg/checks/traceroute/test-lab/lab.conf @@ -4,6 +4,7 @@ LAB_AUTHOR="T. Caiazzi, G. Di Battista, M. Patrignani, M. Pizzonia, F. Ricci, M. LAB_EMAIL=contact@kathara.org LAB_WEB=http://www.kathara.org/ + r1[0]="A" r1[1]="B" r1[image]="kathara/base" @@ -16,6 +17,16 @@ pc1[0]="A" pc1[image]="kathara/base" pc1[env]="LOG_LEVEL=DEBUG" pc1[env]="LOG_FORMAT=TEXT" +pc1[env]="COLLECTOR_OTLP_HTTP_HOST_PORT=0.0.0.0:4318" +pc1[env]="COLLECTOR_OTLP_GRPC_HOST_PORT=0.0.0.0:4317" +pc1[env]="COLLECTOR_ZIPKIN_HOST_PORT=:9411" +pc1[env]="SPAN_STORAGE_TYPE=badger" +pc1[env]="BADGER_EPHEMERAL=false" +pc1[env]="BADGER_DIRECTORY_VALUE=/shared/badger/data" +pc1[env]="BADGER_DIRECTORY_KEY=/shared/badger/key" +# pc1[port]="16686:16686" +# pc1[port]="8080:8080" +# pc1[bridged]="true" pc2[0]="C" pc2[image]="kathara/base" diff --git a/pkg/checks/traceroute/test-lab/pc1.startup b/pkg/checks/traceroute/test-lab/pc1.startup index 46d9b5d0..9a5e9f56 100644 --- a/pkg/checks/traceroute/test-lab/pc1.startup +++ b/pkg/checks/traceroute/test-lab/pc1.startup @@ -1,2 +1,4 @@ ip address add 195.11.14.5/24 dev eth0 ip route add default via 195.11.14.1 dev eth0 +/shared/jaeger-all-in-one --query.http-server.host-port 0.0.0.0:16686 & +/shared/sparrow run --config /shared/debug.yaml & diff --git a/pkg/checks/traceroute/traceroute.go b/pkg/checks/traceroute/traceroute.go index f0d2fbc4..01959f73 100644 --- a/pkg/checks/traceroute/traceroute.go +++ b/pkg/checks/traceroute/traceroute.go @@ -11,6 +11,8 @@ import ( "syscall" "time" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "golang.org/x/sys/unix" "github.com/caas-team/sparrow/internal/helper" @@ -112,9 +114,13 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error // maps ttl -> attempted hops for that ttl hops := make(map[int][]Hop) log := logger.FromContext(ctx).With("target", cfg.Dest) + span := trace.SpanFromContext(ctx) + span.SetAttributes(attribute.String("target", cfg.Dest)) + defer span.End() addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", cfg.Dest, cfg.Port)) if err != nil { + span.RecordError(err) log.Error("failed to resolve target name", "err", err.Error()) return nil, err } @@ -126,12 +132,19 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error var wg sync.WaitGroup for ttl := 1; ttl <= cfg.MaxHops; ttl++ { + ctx, span := span.TracerProvider().Tracer("something.else").Start(ctx, fmt.Sprintf("%d", ttl)) wg.Add(1) go func(ttl int) { + defer span.End() defer wg.Done() l := log.With("ttl", ttl) logctx := logger.IntoContext(ctx, l) + retry := 0 err := helper.Retry(func(ctx context.Context) error { + defer func() { + retry++ + }() + span.AddEvent("traceroute", trace.WithAttributes(attribute.Int("ttl", ttl), attribute.Int("retry", retry))) hop, err := traceroute(ctx, addr, ttl, cfg.Timeout) if hop != nil { results <- *hop From d8fc900beb7673ae323751d0a33be234298613ed Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:58:09 +0000 Subject: [PATCH 02/12] debug: regularily flush span processor --- pkg/checks/traceroute/check.go | 18 ++++++++++-------- pkg/checks/traceroute/traceroute.go | 8 ++++---- pkg/sparrow/metrics/metrics.go | 12 +++++++++++- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/pkg/checks/traceroute/check.go b/pkg/checks/traceroute/check.go index a66d037f..84ff6cdb 100644 --- a/pkg/checks/traceroute/check.go +++ b/pkg/checks/traceroute/check.go @@ -39,6 +39,7 @@ func NewCheck() checks.Check { config: Config{}, traceroute: TraceRoute, metrics: newMetrics(), + tracer: otel.Tracer("tracer.traceroute"), } } @@ -47,6 +48,7 @@ type Traceroute struct { config Config traceroute tracerouteFactory metrics metrics + tracer trace.Tracer } type tracerouteConfig struct { @@ -56,6 +58,7 @@ type tracerouteConfig struct { MaxHops int Rc helper.RetryConfig } + type tracerouteFactory func(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error) type result struct { @@ -70,7 +73,6 @@ func (tr *Traceroute) Run(ctx context.Context, cResult chan checks.ResultDTO) er ctx, cancel := logger.NewContextWithLogger(ctx) defer cancel() log := logger.FromContext(ctx) - tracer := otel.Tracer("tracer.traceroute") log.Info("Starting traceroute check", "interval", tr.config.Interval.String()) for { @@ -81,7 +83,7 @@ func (tr *Traceroute) Run(ctx context.Context, cResult chan checks.ResultDTO) er case <-tr.DoneChan: return nil case <-time.After(tr.config.Interval): - res := tr.check(tracer, ctx) + res := tr.check(ctx) tr.metrics.MinHops(res) cResult <- checks.ResultDTO{ Name: tr.Name(), @@ -102,7 +104,7 @@ func (tr *Traceroute) GetConfig() checks.Runtime { return &tr.config } -func (tr *Traceroute) check(tracer trace.Tracer, ctx context.Context) map[string]result { +func (tr *Traceroute) check(ctx context.Context) map[string]result { res := make(map[string]result) log := logger.FromContext(ctx) @@ -122,11 +124,11 @@ func (tr *Traceroute) check(tracer trace.Tracer, ctx context.Context) map[string defer wg.Done() l := log.With("target", t.String()) l.Debug("Running traceroute") - ctx, span := tracer.Start(ctx, t.String()) + c, span := tr.tracer.Start(ctx, t.String()) defer span.End() targetstart := time.Now() - trace, err := tr.traceroute(ctx, tracerouteConfig{ + hops, err := tr.traceroute(c, tracerouteConfig{ Dest: t.Addr, Port: t.Port, Timeout: tr.config.Timeout, @@ -140,13 +142,13 @@ func (tr *Traceroute) check(tracer trace.Tracer, ctx context.Context) map[string tr.metrics.CheckDuration(t.Addr, elapsed) - l.Debug("Ran traceroute", "result", trace, "duration", elapsed) + l.Debug("Ran traceroute", "result", hops, "duration", elapsed) res := result{ - Hops: trace, + Hops: hops, MinHops: tr.config.MaxHops, } - for ttl, hop := range trace { + for ttl, hop := range hops { for _, attempt := range hop { if attempt.Reached && attempt.Ttl < res.MinHops { res.MinHops = ttl diff --git a/pkg/checks/traceroute/traceroute.go b/pkg/checks/traceroute/traceroute.go index 10fa1ba6..a32c77ef 100644 --- a/pkg/checks/traceroute/traceroute.go +++ b/pkg/checks/traceroute/traceroute.go @@ -132,19 +132,19 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error var wg sync.WaitGroup for ttl := 1; ttl <= cfg.MaxHops; ttl++ { - ctx, span := span.TracerProvider().Tracer("something.else").Start(ctx, fmt.Sprintf("%d", ttl)) + c, sp := span.TracerProvider().Tracer("something.else").Start(ctx, fmt.Sprintf("%d", ttl)) wg.Add(1) go func(ttl int) { - defer span.End() + defer sp.End() defer wg.Done() l := log.With("ttl", ttl) - logctx := logger.IntoContext(ctx, l) + logctx := logger.IntoContext(c, l) retry := 0 err := helper.Retry(func(ctx context.Context) error { defer func() { retry++ }() - span.AddEvent("traceroute", trace.WithAttributes(attribute.Int("ttl", ttl), attribute.Int("retry", retry))) + sp.AddEvent("traceroute", trace.WithAttributes(attribute.Int("ttl", ttl), attribute.Int("retry", retry))) hop, err := traceroute(ctx, addr, ttl, cfg.Timeout) if hop != nil { results <- *hop diff --git a/pkg/sparrow/metrics/metrics.go b/pkg/sparrow/metrics/metrics.go index 6f3a7235..2dc44217 100644 --- a/pkg/sparrow/metrics/metrics.go +++ b/pkg/sparrow/metrics/metrics.go @@ -21,6 +21,7 @@ package metrics import ( "context" "fmt" + "time" "github.com/caas-team/sparrow/internal/logger" "github.com/prometheus/client_golang/prometheus" @@ -93,7 +94,16 @@ func (m *manager) InitTracing(ctx context.Context) error { return fmt.Errorf("failed to create exporter: %v", err) } - bsp := sdktrace.NewBatchSpanProcessor(exporter) + const ( + batchTimeout = 5 * time.Second + maxQueueSize = 1000 + maxBatchSize = 100 + ) + bsp := sdktrace.NewBatchSpanProcessor(exporter, + sdktrace.WithBatchTimeout(batchTimeout), + sdktrace.WithMaxQueueSize(maxQueueSize), + sdktrace.WithMaxExportBatchSize(maxBatchSize), + ) tp := sdktrace.NewTracerProvider( // TODO: Keep track of the sampler if we run into traffic issues due to the high volume of data. sdktrace.WithSampler(sdktrace.AlwaysSample()), From 5971b0dd3342d7ad94c63b318181f6b9aa814a28 Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:14:10 +0000 Subject: [PATCH 03/12] fix: activate otlp collector for jaeger --- pkg/checks/traceroute/test-lab/lab.conf | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/checks/traceroute/test-lab/lab.conf b/pkg/checks/traceroute/test-lab/lab.conf index 3b644f6d..e53ae827 100644 --- a/pkg/checks/traceroute/test-lab/lab.conf +++ b/pkg/checks/traceroute/test-lab/lab.conf @@ -4,7 +4,6 @@ LAB_AUTHOR="T. Caiazzi, G. Di Battista, M. Patrignani, M. Pizzonia, F. Ricci, M. LAB_EMAIL=contact@kathara.org LAB_WEB=http://www.kathara.org/ - r1[0]="A" r1[1]="B" r1[image]="kathara/base" @@ -17,16 +16,17 @@ pc1[0]="A" pc1[image]="kathara/base" pc1[env]="LOG_LEVEL=DEBUG" pc1[env]="LOG_FORMAT=TEXT" +pc1[env]="COLLECTOR_OTLP_ENABLED=true" pc1[env]="COLLECTOR_OTLP_HTTP_HOST_PORT=0.0.0.0:4318" pc1[env]="COLLECTOR_OTLP_GRPC_HOST_PORT=0.0.0.0:4317" pc1[env]="COLLECTOR_ZIPKIN_HOST_PORT=:9411" -pc1[env]="SPAN_STORAGE_TYPE=badger" -pc1[env]="BADGER_EPHEMERAL=false" -pc1[env]="BADGER_DIRECTORY_VALUE=/shared/badger/data" -pc1[env]="BADGER_DIRECTORY_KEY=/shared/badger/key" -# pc1[port]="16686:16686" -# pc1[port]="8080:8080" -# pc1[bridged]="true" +pc1[env]="SPAN_STORAGE_TYPE=badger" +pc1[env]="BADGER_EPHEMERAL=false" +pc1[env]="BADGER_DIRECTORY_VALUE=/shared/badger/data" +pc1[env]="BADGER_DIRECTORY_KEY=/shared/badger/key" +pc1[port]="16686:16686" +pc1[port]="8080:8080" +pc1[bridged]="true" pc2[0]="C" pc2[image]="kathara/base" From 65a4aa9cce038338735d4b01155605ef16a879bc Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:30:18 +0000 Subject: [PATCH 04/12] fix: add tracer field for test to avoid nil pointer --- pkg/checks/traceroute/check_test.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pkg/checks/traceroute/check_test.go b/pkg/checks/traceroute/check_test.go index 1afafd8e..c6e2c7e7 100644 --- a/pkg/checks/traceroute/check_test.go +++ b/pkg/checks/traceroute/check_test.go @@ -9,6 +9,7 @@ import ( "github.com/caas-team/sparrow/pkg/checks" "github.com/google/go-cmp/cmp" + "go.opentelemetry.io/otel" ) func TestCheck(t *testing.T) { @@ -60,16 +61,11 @@ func newForTest(f tracerouteFactory, maxHops int, targets []string) *Traceroute t[i] = Target{Addr: target} } return &Traceroute{ - config: Config{ - Targets: t, - MaxHops: maxHops, - }, + CheckBase: checks.CheckBase{Mu: sync.Mutex{}, DoneChan: make(chan struct{})}, + config: Config{Targets: t, MaxHops: maxHops}, traceroute: f, metrics: newMetrics(), - CheckBase: checks.CheckBase{ - Mu: sync.Mutex{}, - DoneChan: make(chan struct{}), - }, + tracer: otel.Tracer("tracer.traceroute"), } } From a66acc9003e8241c432b8b920cdd84f9872d4362 Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:37:52 +0000 Subject: [PATCH 05/12] feat: instrument traceroute implementation --- pkg/checks/traceroute/check.go | 48 ++++++--- pkg/checks/traceroute/traceroute.go | 146 ++++++++++++++++++++++------ 2 files changed, 150 insertions(+), 44 deletions(-) diff --git a/pkg/checks/traceroute/check.go b/pkg/checks/traceroute/check.go index 84ff6cdb..783a4070 100644 --- a/pkg/checks/traceroute/check.go +++ b/pkg/checks/traceroute/check.go @@ -12,6 +12,8 @@ import ( "github.com/getkin/kin-openapi/openapi3" "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" ) @@ -74,11 +76,11 @@ func (tr *Traceroute) Run(ctx context.Context, cResult chan checks.ResultDTO) er defer cancel() log := logger.FromContext(ctx) - log.Info("Starting traceroute check", "interval", tr.config.Interval.String()) + log.InfoContext(ctx, "Starting traceroute check", "interval", tr.config.Interval.String()) for { select { case <-ctx.Done(): - log.Error("Context canceled", "error", ctx.Err()) + log.ErrorContext(ctx, "Context canceled", "error", ctx.Err()) return ctx.Err() case <-tr.DoneChan: return nil @@ -92,7 +94,7 @@ func (tr *Traceroute) Run(ctx context.Context, cResult chan checks.ResultDTO) er Timestamp: time.Now(), }, } - log.Debug("Successfully finished traceroute check run") + log.DebugContext(ctx, "Successfully finished traceroute check run") } } } @@ -115,19 +117,27 @@ func (tr *Traceroute) check(ctx context.Context) map[string]result { cResult := make(chan internalResult, len(tr.config.Targets)) var wg sync.WaitGroup - start := time.Now() - wg.Add(len(tr.config.Targets)) + for _, t := range tr.config.Targets { go func(t Target) { defer wg.Done() l := log.With("target", t.String()) - l.Debug("Running traceroute") - c, span := tr.tracer.Start(ctx, t.String()) + l.DebugContext(ctx, "Running traceroute") + + c, span := tr.tracer.Start(ctx, t.String(), trace.WithAttributes( + attribute.String("target.addr", t.Addr), + attribute.Int("target.port", t.Port), + attribute.Stringer("config.interval", tr.config.Interval), + attribute.Stringer("config.timeout", tr.config.Timeout), + attribute.Int("config.max_hops", tr.config.MaxHops), + attribute.Int("config.retry.count", tr.config.Retry.Count), + attribute.Stringer("config.retry.delay", tr.config.Retry.Delay), + )) defer span.End() - targetstart := time.Now() + s := time.Now() hops, err := tr.traceroute(c, tracerouteConfig{ Dest: t.Addr, Port: t.Port, @@ -135,19 +145,23 @@ func (tr *Traceroute) check(ctx context.Context) map[string]result { MaxHops: tr.config.MaxHops, Rc: tr.config.Retry, }) - elapsed := time.Since(targetstart) + elapsed := time.Since(s) + if err != nil { - l.Error("Error running traceroute", "error", err) + l.ErrorContext(ctx, "Error running traceroute", "error", err) + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + } else { + span.SetStatus(codes.Ok, "success") } tr.metrics.CheckDuration(t.Addr, elapsed) + l.DebugContext(ctx, "Ran traceroute", "result", hops, "duration", elapsed) - l.Debug("Ran traceroute", "result", hops, "duration", elapsed) res := result{ Hops: hops, MinHops: tr.config.MaxHops, } - for ttl, hop := range hops { for _, attempt := range hop { if attempt.Reached && attempt.Ttl < res.MinHops { @@ -156,6 +170,12 @@ func (tr *Traceroute) check(ctx context.Context) map[string]result { } } + span.AddEvent("Traceroute completed", trace.WithAttributes( + attribute.Int("result.min_hops", res.MinHops), + attribute.Int("result.hop_count", len(hops)), + attribute.Stringer("result.elapsed_time", elapsed), + )) + cResult <- internalResult{addr: t.Addr, res: res} }(t) } @@ -168,9 +188,7 @@ func (tr *Traceroute) check(ctx context.Context) map[string]result { } elapsed := time.Since(start) - - log.Info("Finished traceroute check", "duration", elapsed) - + log.InfoContext(ctx, "Finished traceroute check", "duration", elapsed) return res } diff --git a/pkg/checks/traceroute/traceroute.go b/pkg/checks/traceroute/traceroute.go index a32c77ef..38da71e1 100644 --- a/pkg/checks/traceroute/traceroute.go +++ b/pkg/checks/traceroute/traceroute.go @@ -6,12 +6,14 @@ import ( "fmt" "math/rand/v2" "net" + "os" "slices" "sync" "syscall" "time" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" "golang.org/x/sys/unix" @@ -40,9 +42,14 @@ func randomPort() int { return rand.N(portRange) + basePort // #nosec G404 // math.rand is fine here, we're not doing encryption } -func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) (net.Conn, int, error) { +// tcpHop attempts to connect to the target host using TCP with the specified TTL and timeout. +// It returns a [net.Conn], the port used for the connection, and an error if the connection failed. +func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) (conn net.Conn, port int, err error) { + span := trace.SpanFromContext(ctx) + for { port := randomPort() + // Dialer with control function to set IP_TTL dialer := net.Dialer{ LocalAddr: &net.TCPAddr{ @@ -60,11 +67,48 @@ func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) }, } + span.AddEvent("Attempting TCP connection", trace.WithAttributes( + attribute.String("remote_addr", addr.String()), + attribute.Int("ttl", ttl), + attribute.Int("port", port), + )) + // Attempt to connect to the target host conn, err := dialer.DialContext(ctx, "tcp", addr.String()) - if !errors.Is(err, unix.EADDRINUSE) { - return conn, port, err + + var opErr *net.OpError + switch { + case err == nil: + span.AddEvent("TCP connection succeeded", trace.WithAttributes( + attribute.Stringer("remote_addr", addr), + attribute.Int("ttl", ttl), + attribute.Int("port", port), + )) + return conn, port, nil + case errors.Is(err, unix.EADDRINUSE): + // Address in use, retry by continuing the loop + continue + case errors.As(err, &opErr): + // No route to host is no error because we of how tcp traceroute works + // we are expecting the connection to fail because of TTL expiry + if sysErr, ok := opErr.Err.(*os.SyscallError); ok { + if errno, ok := sysErr.Err.(syscall.Errno); ok { + if errno == syscall.EHOSTUNREACH { + span.AddEvent("No route to host", trace.WithAttributes( + attribute.String("error", err.Error()), + )) + logger.FromContext(ctx).DebugContext(ctx, "No route to host", "error", err.Error()) + return conn, port, err + } + } + } } + + span.AddEvent("TCP connection failed", trace.WithAttributes( + attribute.String("error", err.Error()), + )) + span.RecordError(err) + return conn, port, err } } @@ -99,7 +143,7 @@ func readIcmpMessage(ctx context.Context, icmpListener *icmp.PacketConn, timeout case ipv6.ICMPTypeTimeExceeded: tcpSegment = msg.Body.(*icmp.TimeExceeded).Data[IPv6HeaderSize:] default: - log.Debug("message is not 'Time Exceeded'", "type", msg.Type.Protocol()) + log.DebugContext(ctx, "message is not 'Time Exceeded'", "type", msg.Type.Protocol()) return 0, nil, errors.New("message is not 'Time Exceeded'") } @@ -111,17 +155,24 @@ func readIcmpMessage(ctx context.Context, icmpListener *icmp.PacketConn, timeout // TraceRoute performs a traceroute to the specified host using TCP and listens for ICMP Time Exceeded messages using ICMP. func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error) { - // maps ttl -> attempted hops for that ttl + span := trace.SpanFromContext(ctx) + tr := span.TracerProvider().Tracer("tracer.traceroute.trace") + ctx, sp := tr.Start(ctx, "TraceRoute", trace.WithAttributes( + attribute.String("target", cfg.Dest), + attribute.Int("port", cfg.Port), + attribute.Int("max_hops", cfg.MaxHops), + attribute.Stringer("timeout", cfg.Timeout), + )) + defer sp.End() + + // Maps ttl -> attempted hops for that ttl hops := make(map[int][]Hop) log := logger.FromContext(ctx).With("target", cfg.Dest) - span := trace.SpanFromContext(ctx) - span.SetAttributes(attribute.String("target", cfg.Dest)) - defer span.End() addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", cfg.Dest, cfg.Port)) if err != nil { - span.RecordError(err) - log.Error("failed to resolve target name", "err", err.Error()) + sp.RecordError(err) + log.ErrorContext(ctx, "failed to resolve target name", "err", err.Error()) return nil, err } @@ -132,35 +183,48 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error var wg sync.WaitGroup for ttl := 1; ttl <= cfg.MaxHops; ttl++ { - c, sp := span.TracerProvider().Tracer("something.else").Start(ctx, fmt.Sprintf("%d", ttl)) + c, hopSpan := tr.Start(ctx, fmt.Sprintf("Hop %d", ttl), trace.WithAttributes( + attribute.Int("ttl", ttl), + )) wg.Add(1) go func(ttl int) { - defer sp.End() defer wg.Done() + defer hopSpan.End() + l := log.With("ttl", ttl) logctx := logger.IntoContext(c, l) + retry := 0 err := helper.Retry(func(ctx context.Context) error { defer func() { retry++ }() - sp.AddEvent("traceroute", trace.WithAttributes(attribute.Int("ttl", ttl), attribute.Int("retry", retry))) + hopSpan.AddEvent("Attempting to hop", trace.WithAttributes( + attribute.Int("ttl", ttl), + attribute.Int("retry", retry), + )) + hop, err := traceroute(ctx, addr, ttl, cfg.Timeout) if hop != nil { results <- *hop } if err != nil { - l.Error("traceroute failed", "err", err.Error()) + l.ErrorContext(ctx, "Failed to trace route", "err", err.Error()) + hopSpan.RecordError(err) return err } if !hop.Reached { - l.Debug("failed to reach target, retrying") + l.DebugContext(ctx, "failed to reach target, retrying") return errors.New("failed to reach target") } return nil }, cfg.Rc)(logctx) + if err != nil { - l.Debug("traceroute could not reach target") + l.DebugContext(ctx, "Traceroute could not reach target") + hopSpan.SetStatus(codes.Error, "Hop failed") + } else { + hopSpan.SetStatus(codes.Ok, "Hop succeeded") } }(ttl) } @@ -168,12 +232,15 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error wg.Wait() close(results) + // Collect and log hops for r := range results { hops[r.Ttl] = append(hops[r.Ttl], r) } + logHops(ctx, hops) - printHops(ctx, hops) - + sp.AddEvent("TraceRoute completed", trace.WithAttributes( + attribute.Int("hops_count", len(hops)), + )) return hops, nil } @@ -192,10 +259,12 @@ func ipFromAddr(remoteAddr net.Addr) net.IP { // traceroute performs a traceroute to the given address with the specified TTL and timeout. // It returns a Hop struct containing the latency, TTL, address, and other details of the hop. func traceroute(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) (*Hop, error) { + span := trace.SpanFromContext(ctx) log := logger.FromContext(ctx) canIcmp, icmpListener, err := newIcmpListener() if err != nil { - log.Error("Failed to open ICMP socket", "err", err.Error()) + log.ErrorContext(ctx, "Failed to open ICMP socket", "err", err.Error()) + span.RecordError(err) return nil, err } defer closeIcmpListener(canIcmp, icmpListener) @@ -203,12 +272,17 @@ func traceroute(ctx context.Context, addr net.Addr, ttl int, timeout time.Durati start := time.Now() conn, clientPort, err := tcpHop(ctx, addr, ttl, timeout) latency := time.Since(start) + + span.SetAttributes(attribute.Int("ttl", ttl), attribute.String("addr", addr.String())) if err == nil { - return handleTcpSuccess(conn, addr, ttl, latency), nil + hop := handleTcpSuccess(conn, addr, ttl, latency) + span.AddEvent("Hop succeeded", trace.WithAttributes(attribute.String("hop_addr", hop.Addr.String()), attribute.Stringer("latency", latency))) + return hop, nil } if !canIcmp { - log.Debug("No permission for icmp socket") + span.AddEvent("ICMP socket not available") + log.DebugContext(ctx, "No permission for icmp socket") return &Hop{ Latency: latency, Ttl: ttl, @@ -216,9 +290,17 @@ func traceroute(ctx context.Context, addr net.Addr, ttl int, timeout time.Durati }, nil } - h := handleIcmpResponse(ctx, icmpListener, clientPort, ttl, timeout) - h.Latency = latency - return &h, nil + hop := handleIcmpResponse(ctx, icmpListener, clientPort, ttl, timeout) + hop.Latency = latency + if hop.Reached { + span.AddEvent("ICMP hop reached", trace.WithAttributes( + attribute.String("hop_addr", hop.Addr.String()), + attribute.Stringer("latency", latency), + )) + } else { + span.AddEvent("ICMP hop not reached", trace.WithAttributes(attribute.Stringer("latency", latency))) + } + return &hop, nil } func newIcmpListener() (bool, *icmp.PacketConn, error) { @@ -238,6 +320,7 @@ func closeIcmpListener(canIcmp bool, icmpListener *icmp.PacketConn) { } } +// newHopAddress creates a new HopAddress from a [net.Addr]. func newHopAddress(addr net.Addr) HopAddress { switch addr := addr.(type) { case *net.UDPAddr: @@ -259,6 +342,7 @@ func newHopAddress(addr net.Addr) HopAddress { } } +// handleTcpSuccess handles a successful TCP connection by closing the connection and returning a Hop struct. func handleTcpSuccess(conn net.Conn, addr net.Addr, ttl int, latency time.Duration) *Hop { conn.Close() // #nosec G104 @@ -286,10 +370,10 @@ func handleIcmpResponse(ctx context.Context, icmpListener *icmp.PacketConn, clie deadline := time.Now().Add(timeout) for time.Now().Unix() < deadline.Unix() { - log.Debug("Reading ICMP message") + log.DebugContext(ctx, "Reading ICMP message") gotPort, addr, err := readIcmpMessage(ctx, icmpListener, timeout) if err != nil { - log.Debug("Failed to read ICMP message", "err", err.Error()) + log.DebugContext(ctx, "Failed to read ICMP message", "err", err.Error()) continue } @@ -311,12 +395,13 @@ func handleIcmpResponse(ctx context.Context, icmpListener *icmp.PacketConn, clie } } - log.Debug("Deadline reached") + log.DebugContext(ctx, "Deadline reached") return Hop{ Ttl: ttl, } } +// Hop represents a single hop in a traceroute type Hop struct { Latency time.Duration `json:"latency" yaml:"latency" mapstructure:"latency"` Addr HopAddress `json:"addr" yaml:"addr" mapstructure:"addr"` @@ -325,11 +410,13 @@ type Hop struct { Reached bool `json:"reached" yaml:"reached" mapstructure:"reached"` } +// HopAddress represents an IP address and port type HopAddress struct { IP string `json:"ip" yaml:"ip" mapstructure:"ip"` Port int `json:"port" yaml:"port" mapstructure:"port"` } +// String returns the string representation of the [HopAddress]. func (a HopAddress) String() string { if a.Port != 0 { return fmt.Sprintf("%s:%d", a.IP, a.Port) @@ -337,7 +424,8 @@ func (a HopAddress) String() string { return a.IP } -func printHops(ctx context.Context, mapHops map[int][]Hop) { +// logHops logs the hops in the mapHops map +func logHops(ctx context.Context, mapHops map[int][]Hop) { log := logger.FromContext(ctx) keys := []int{} @@ -352,7 +440,7 @@ func printHops(ctx context.Context, mapHops map[int][]Hop) { if hop.Reached { out += "( Reached )" } - log.Debug(out) + log.DebugContext(ctx, out) } } } From deb04db95a518e1d2870beb46dca19bc5c047241 Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Tue, 10 Sep 2024 09:12:52 +0000 Subject: [PATCH 06/12] feat: add enabled flag for telemetry config --- README.md | 12 ++++++------ pkg/config/config.go | 19 ++++++++++++------- pkg/sparrow/metrics/config.go | 2 ++ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 9ce45f4a..431ceb2e 100644 --- a/README.md +++ b/README.md @@ -241,10 +241,9 @@ api: keyPath: mykey.key -# Configures the target manager -# Omitting this section will disable the target manager +# Configures the target manager. targetManager: - # whether to enable the target manager. defaults to false + # whether to enable the target manager. (default: false) enabled: true # Defines which target manager to use. type: gitlab @@ -274,8 +273,9 @@ targetManager: projectId: 18923 # Configures the telemetry exporter. -# Omitting this section will disable telemetry. telemetry: + # Whether to enable telemetry. (default: false) + enabled: true # The telemetry exporter to use. # Options: # grpc: Exports telemetry using OTLP via gRPC. @@ -506,8 +506,8 @@ dns: | ---------------- | ----------------- | ---------------------------------------------------------------------------- | | `interval` | `duration` | Interval to perform the Traceroute check. | | `timeout` | `duration` | Timeout for every hop. | -| `retry.count` | `integer` | Number of retries for the latency check. | -| `retry.delay` | `duration` | Initial delay between retries for the latency check. | +| `retry.count` | `integer` | Number of retries for the latency check. | +| `retry.delay` | `duration` | Initial delay between retries for the latency check. | | `maxHops` | `integer` | Maximum number of hops to try before giving up. | | `targets` | `list of objects` | List of targets to traceroute to. | | `targets[].addr` | `string` | The address of the target to traceroute to. Can be an IP address or DNS name | diff --git a/pkg/config/config.go b/pkg/config/config.go index f6760513..f1c4b81c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -30,11 +30,15 @@ import ( type Config struct { // SparrowName is the DNS name of the sparrow - SparrowName string `yaml:"name" mapstructure:"name"` - Loader LoaderConfig `yaml:"loader" mapstructure:"loader"` - Api api.Config `yaml:"api" mapstructure:"api"` + SparrowName string `yaml:"name" mapstructure:"name"` + // Loader is the configuration for the loader + Loader LoaderConfig `yaml:"loader" mapstructure:"loader"` + // Api is the configuration for the api server + Api api.Config `yaml:"api" mapstructure:"api"` + // TargetManager is the configuration for the target manager TargetManager targets.TargetManagerConfig `yaml:"targetManager" mapstructure:"targetManager"` - Telemetry metrics.Config `yaml:"telemetry" mapstructure:"telemetry"` + // Telemetry is the configuration for the telemetry + Telemetry metrics.Config `yaml:"telemetry" mapstructure:"telemetry"` } // LoaderConfig is the configuration for loader @@ -45,8 +49,7 @@ type LoaderConfig struct { File FileLoaderConfig `yaml:"file" mapstructure:"file"` } -// HttpLoaderConfig is the configuration -// for the specific http loader +// HttpLoaderConfig is the configuration for the http loader type HttpLoaderConfig struct { Url string `yaml:"url" mapstructure:"url"` Token string `yaml:"token" mapstructure:"token"` @@ -54,6 +57,7 @@ type HttpLoaderConfig struct { RetryCfg helper.RetryConfig `yaml:"retry" mapstructure:"retry"` } +// FileLoaderConfig is the configuration for the file loader type FileLoaderConfig struct { Path string `yaml:"path" mapstructure:"path"` } @@ -63,6 +67,7 @@ func (c *Config) HasTargetManager() bool { return c.TargetManager.Enabled } +// HasTelemetry returns true if the config has telemetry enabled func (c *Config) HasTelemetry() bool { - return c.Telemetry != metrics.Config{} + return c.Telemetry.Enabled } diff --git a/pkg/sparrow/metrics/config.go b/pkg/sparrow/metrics/config.go index 8deaa194..d49d1b39 100644 --- a/pkg/sparrow/metrics/config.go +++ b/pkg/sparrow/metrics/config.go @@ -27,6 +27,8 @@ import ( // Config holds the configuration for OpenTelemetry type Config struct { + // Enabled is a flag to enable or disable the OpenTelemetry + Enabled bool `yaml:"enabled" mapstructure:"enabled"` // Exporter is the otlp exporter used to export the traces Exporter Exporter `yaml:"exporter" mapstructure:"exporter"` // Url is the Url of the collector to which the traces are exported From e948f8d5ca6c9df0f68050c488728aae327984d9 Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Tue, 10 Sep 2024 13:47:16 +0000 Subject: [PATCH 07/12] fix: only record non EHOSTUNREACH errors --- pkg/checks/traceroute/traceroute.go | 39 +++++++++++++---------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/pkg/checks/traceroute/traceroute.go b/pkg/checks/traceroute/traceroute.go index 38da71e1..101bd626 100644 --- a/pkg/checks/traceroute/traceroute.go +++ b/pkg/checks/traceroute/traceroute.go @@ -6,7 +6,6 @@ import ( "fmt" "math/rand/v2" "net" - "os" "slices" "sync" "syscall" @@ -76,7 +75,6 @@ func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) // Attempt to connect to the target host conn, err := dialer.DialContext(ctx, "tcp", addr.String()) - var opErr *net.OpError switch { case err == nil: span.AddEvent("TCP connection succeeded", trace.WithAttributes( @@ -88,20 +86,15 @@ func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) case errors.Is(err, unix.EADDRINUSE): // Address in use, retry by continuing the loop continue - case errors.As(err, &opErr): - // No route to host is no error because we of how tcp traceroute works + case errors.Is(err, syscall.EHOSTUNREACH): + // No route to host is no error because of how tcp traceroute works // we are expecting the connection to fail because of TTL expiry - if sysErr, ok := opErr.Err.(*os.SyscallError); ok { - if errno, ok := sysErr.Err.(syscall.Errno); ok { - if errno == syscall.EHOSTUNREACH { - span.AddEvent("No route to host", trace.WithAttributes( - attribute.String("error", err.Error()), - )) - logger.FromContext(ctx).DebugContext(ctx, "No route to host", "error", err.Error()) - return conn, port, err - } - } - } + span.SetStatus(codes.Unset, "No route to host") + span.AddEvent("No route to host", trace.WithAttributes( + attribute.String("error", err.Error()), + )) + logger.FromContext(ctx).DebugContext(ctx, "No route to host", "error", err.Error()) + return conn, port, err } span.AddEvent("TCP connection failed", trace.WithAttributes( @@ -112,7 +105,7 @@ func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) } } -// readIcmpMessage reads a packet from the provided icmp Connection. If the packet is 'Time Exceeded', +// readIcmpMessage reads a packet from the provided [icmp.PacketConn]. If the packet is 'Time Exceeded', // it reads the address of the router that dropped created the icmp packet. It also reads the source port // from the payload and finds the source port used by the previous tcp connection. If any error is returned, // an icmp packet was either not received, or the received packet was not a time exceeded. @@ -219,13 +212,14 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error } return nil }, cfg.Rc)(logctx) - if err != nil { l.DebugContext(ctx, "Traceroute could not reach target") - hopSpan.SetStatus(codes.Error, "Hop failed") - } else { - hopSpan.SetStatus(codes.Ok, "Hop succeeded") + if !errors.Is(err, syscall.EHOSTUNREACH) { + hopSpan.SetStatus(codes.Error, err.Error()) + } + return } + hopSpan.SetStatus(codes.Ok, "Hop succeeded") }(ttl) } @@ -244,6 +238,7 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error return hops, nil } +// ipFromAddr returns the IP address from a [net.Addr]. func ipFromAddr(remoteAddr net.Addr) net.IP { switch addr := remoteAddr.(type) { case *net.UDPAddr: @@ -297,9 +292,9 @@ func traceroute(ctx context.Context, addr net.Addr, ttl int, timeout time.Durati attribute.String("hop_addr", hop.Addr.String()), attribute.Stringer("latency", latency), )) - } else { - span.AddEvent("ICMP hop not reached", trace.WithAttributes(attribute.Stringer("latency", latency))) + return &hop, nil } + span.AddEvent("ICMP hop not reached", trace.WithAttributes(attribute.Stringer("latency", latency))) return &hop, nil } From efdc10537c4ca02e43af5ec773d3efd8b244dfbb Mon Sep 17 00:00:00 2001 From: Bruno Bressi Date: Wed, 11 Sep 2024 14:39:14 +0200 Subject: [PATCH 08/12] chore: spelling Signed-off-by: Bruno Bressi --- pkg/checks/traceroute/traceroute.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/checks/traceroute/traceroute.go b/pkg/checks/traceroute/traceroute.go index 101bd626..c9f0984b 100644 --- a/pkg/checks/traceroute/traceroute.go +++ b/pkg/checks/traceroute/traceroute.go @@ -170,7 +170,7 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error } // if we don't add the +1, this causes issues, when the user does not want to retry, - // since the channels size would be zero, blocking all threads from sending + // since the channel's size would be zero, blocking all threads from sending queueSize := cfg.MaxHops * (1 + cfg.Rc.Count) results := make(chan Hop, queueSize) var wg sync.WaitGroup From 92c739cd7dcc514c64756ac44087eb2a3db63847 Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:26:09 +0000 Subject: [PATCH 09/12] chore: address review comments * refactor: use checkname as trace name * refactor: use explicit default case * fix: set error statuses for errors * refactor: function naming * feat: add more span attributes --- pkg/checks/traceroute/check.go | 5 +- pkg/checks/traceroute/traceroute.go | 97 +++++++++++++++++------------ 2 files changed, 60 insertions(+), 42 deletions(-) diff --git a/pkg/checks/traceroute/check.go b/pkg/checks/traceroute/check.go index 783a4070..defc6732 100644 --- a/pkg/checks/traceroute/check.go +++ b/pkg/checks/traceroute/check.go @@ -33,7 +33,7 @@ func (t Target) String() string { } func NewCheck() checks.Check { - return &Traceroute{ + c := &Traceroute{ CheckBase: checks.CheckBase{ Mu: sync.Mutex{}, DoneChan: make(chan struct{}, 1), @@ -41,8 +41,9 @@ func NewCheck() checks.Check { config: Config{}, traceroute: TraceRoute, metrics: newMetrics(), - tracer: otel.Tracer("tracer.traceroute"), } + c.tracer = otel.Tracer(c.Name()) + return c } type Traceroute struct { diff --git a/pkg/checks/traceroute/traceroute.go b/pkg/checks/traceroute/traceroute.go index c9f0984b..90b86777 100644 --- a/pkg/checks/traceroute/traceroute.go +++ b/pkg/checks/traceroute/traceroute.go @@ -95,13 +95,14 @@ func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) )) logger.FromContext(ctx).DebugContext(ctx, "No route to host", "error", err.Error()) return conn, port, err + default: + span.AddEvent("TCP connection failed", trace.WithAttributes( + attribute.String("error", err.Error()), + )) + span.SetStatus(codes.Error, err.Error()) + span.RecordError(err) + return conn, port, err } - - span.AddEvent("TCP connection failed", trace.WithAttributes( - attribute.String("error", err.Error()), - )) - span.RecordError(err) - return conn, port, err } } @@ -148,9 +149,8 @@ func readIcmpMessage(ctx context.Context, icmpListener *icmp.PacketConn, timeout // TraceRoute performs a traceroute to the specified host using TCP and listens for ICMP Time Exceeded messages using ICMP. func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error) { - span := trace.SpanFromContext(ctx) - tr := span.TracerProvider().Tracer("tracer.traceroute.trace") - ctx, sp := tr.Start(ctx, "TraceRoute", trace.WithAttributes( + tracer := trace.SpanFromContext(ctx).TracerProvider().Tracer("tracer.traceroute") + ctx, sp := tracer.Start(ctx, "TraceRoute", trace.WithAttributes( attribute.String("target", cfg.Dest), attribute.Int("port", cfg.Port), attribute.Int("max_hops", cfg.MaxHops), @@ -164,8 +164,9 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", cfg.Dest, cfg.Port)) if err != nil { + sp.SetStatus(codes.Error, err.Error()) sp.RecordError(err) - log.ErrorContext(ctx, "failed to resolve target name", "err", err.Error()) + log.ErrorContext(ctx, "failed to resolve target name", "err", err) return nil, err } @@ -176,7 +177,7 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error var wg sync.WaitGroup for ttl := 1; ttl <= cfg.MaxHops; ttl++ { - c, hopSpan := tr.Start(ctx, fmt.Sprintf("Hop %d", ttl), trace.WithAttributes( + c, hopSpan := tracer.Start(ctx, addr.String(), trace.WithAttributes( attribute.Int("ttl", ttl), )) wg.Add(1) @@ -188,7 +189,7 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error logctx := logger.IntoContext(c, l) retry := 0 - err := helper.Retry(func(ctx context.Context) error { + err = helper.Retry(func(ctx context.Context) error { defer func() { retry++ }() @@ -197,17 +198,20 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error attribute.Int("retry", retry), )) - hop, err := traceroute(ctx, addr, ttl, cfg.Timeout) + hop, hErr := hop(ctx, addr, ttl, cfg.Timeout) if hop != nil { results <- *hop } - if err != nil { - l.ErrorContext(ctx, "Failed to trace route", "err", err.Error()) - hopSpan.RecordError(err) - return err + if hErr != nil { + l.ErrorContext(ctx, "Failed to hop", "err", hErr) + hopSpan.SetStatus(codes.Error, hErr.Error()) + hopSpan.RecordError(hErr) + return hErr } + if !hop.Reached { - l.DebugContext(ctx, "failed to reach target, retrying") + hopSpan.SetName(hop.Addr.String()) + l.DebugContext(ctx, "Failed to reach target, retrying") return errors.New("failed to reach target") } return nil @@ -238,28 +242,16 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error return hops, nil } -// ipFromAddr returns the IP address from a [net.Addr]. -func ipFromAddr(remoteAddr net.Addr) net.IP { - switch addr := remoteAddr.(type) { - case *net.UDPAddr: - return addr.IP - case *net.TCPAddr: - return addr.IP - case *net.IPAddr: - return addr.IP - } - return nil -} - -// traceroute performs a traceroute to the given address with the specified TTL and timeout. +// hop performs a hop to the given address with the specified TTL and timeout. // It returns a Hop struct containing the latency, TTL, address, and other details of the hop. -func traceroute(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) (*Hop, error) { +func hop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) (*Hop, error) { span := trace.SpanFromContext(ctx) log := logger.FromContext(ctx) canIcmp, icmpListener, err := newIcmpListener() if err != nil { - log.ErrorContext(ctx, "Failed to open ICMP socket", "err", err.Error()) + span.SetStatus(codes.Error, err.Error()) span.RecordError(err) + log.ErrorContext(ctx, "Failed to open ICMP socket", "err", err.Error()) return nil, err } defer closeIcmpListener(canIcmp, icmpListener) @@ -268,10 +260,14 @@ func traceroute(ctx context.Context, addr net.Addr, ttl int, timeout time.Durati conn, clientPort, err := tcpHop(ctx, addr, ttl, timeout) latency := time.Since(start) - span.SetAttributes(attribute.Int("ttl", ttl), attribute.String("addr", addr.String())) + span.SetAttributes(attribute.Int("ttl", ttl), attribute.Stringer("addr", addr)) if err == nil { hop := handleTcpSuccess(conn, addr, ttl, latency) - span.AddEvent("Hop succeeded", trace.WithAttributes(attribute.String("hop_addr", hop.Addr.String()), attribute.Stringer("latency", latency))) + span.AddEvent("Hop succeeded", trace.WithAttributes( + attribute.String("hop_name", hop.Name), + attribute.Stringer("hop_addr", hop.Addr), + attribute.Stringer("latency", latency), + )) return hop, nil } @@ -287,17 +283,24 @@ func traceroute(ctx context.Context, addr net.Addr, ttl int, timeout time.Durati hop := handleIcmpResponse(ctx, icmpListener, clientPort, ttl, timeout) hop.Latency = latency - if hop.Reached { - span.AddEvent("ICMP hop reached", trace.WithAttributes( - attribute.String("hop_addr", hop.Addr.String()), + if !hop.Reached { + span.AddEvent("ICMP hop not reached", trace.WithAttributes( + attribute.String("hop_name", hop.Name), + attribute.Stringer("hop_addr", hop.Addr), attribute.Stringer("latency", latency), )) return &hop, nil } - span.AddEvent("ICMP hop not reached", trace.WithAttributes(attribute.Stringer("latency", latency))) + + span.AddEvent("ICMP hop reached", trace.WithAttributes( + attribute.String("hop_name", hop.Name), + attribute.Stringer("hop_addr", hop.Addr), + attribute.Stringer("latency", latency), + )) return &hop, nil } +// newIcmpListener creates a new ICMP listener and returns a boolean indicating if the necessary permissions were granted. func newIcmpListener() (bool, *icmp.PacketConn, error) { icmpListener, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { @@ -309,6 +312,7 @@ func newIcmpListener() (bool, *icmp.PacketConn, error) { return true, icmpListener, nil } +// closeIcmpListener closes the ICMP listener if it is not nil and the permissions were granted. func closeIcmpListener(canIcmp bool, icmpListener *icmp.PacketConn) { if canIcmp && icmpListener != nil { icmpListener.Close() // #nosec G104 @@ -396,6 +400,19 @@ func handleIcmpResponse(ctx context.Context, icmpListener *icmp.PacketConn, clie } } +// ipFromAddr returns the IP address from a [net.Addr]. +func ipFromAddr(remoteAddr net.Addr) net.IP { + switch addr := remoteAddr.(type) { + case *net.UDPAddr: + return addr.IP + case *net.TCPAddr: + return addr.IP + case *net.IPAddr: + return addr.IP + } + return nil +} + // Hop represents a single hop in a traceroute type Hop struct { Latency time.Duration `json:"latency" yaml:"latency" mapstructure:"latency"` From b32c465e5a17593f3538319177a3f53c9e4a2c87 Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:42:13 +0000 Subject: [PATCH 10/12] chore: bump deps --- go.mod | 30 ++++++++++++------------- go.sum | 71 +++++++++++++++++++++++++--------------------------------- 2 files changed, 45 insertions(+), 56 deletions(-) diff --git a/go.mod b/go.mod index ddac46e0..345e5acc 100644 --- a/go.mod +++ b/go.mod @@ -11,15 +11,15 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.20.0-alpha.6 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/otel v1.29.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.29.0 - go.opentelemetry.io/otel/sdk v1.29.0 - go.opentelemetry.io/otel/trace v1.29.0 + go.opentelemetry.io/otel v1.30.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.30.0 + go.opentelemetry.io/otel/sdk v1.30.0 + go.opentelemetry.io/otel/trace v1.30.0 golang.org/x/net v0.29.0 golang.org/x/sys v0.25.0 - google.golang.org/grpc v1.66.0 + google.golang.org/grpc v1.66.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -34,7 +34,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-viper/mapstructure/v2 v2.0.0 // indirect + github.com/go-viper/mapstructure/v2 v2.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -44,25 +44,25 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.59.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect - go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/text v0.18.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/protobuf v1.34.2 // indirect ) diff --git a/go.sum b/go.sum index d9df1848..28779784 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -29,8 +27,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= -github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= +github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -61,19 +59,18 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0= +github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -86,43 +83,36 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.0-alpha.6 h1:f65Cr/+2qk4GfHC0xqT/isoupQppwN5+VLRztUGTDbY= github.com/spf13/viper v1.20.0-alpha.6/go.mod h1:CGBZzv0c9fOUASm6rfus4wdeIjR/04NOLq1P4KRhX3k= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 h1:nSiV3s7wiCam610XcLbYOmMfJxB9gO4uK3Xgv5gmTgg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0/go.mod h1:hKn/e/Nmd19/x1gvIHwtOwVWM+VhuITSWip3JUDghj0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 h1:JAv0Jwtl01UFiyWZEMiJZBiTlv5A50zNs8lsthXqIio= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0/go.mod h1:QNKLmUEAq2QUbPQUfvw4fmv0bgbK7UlOSFCnXyfvSNc= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.29.0 h1:X3ZjNp36/WlkSYx0ul2jw4PtbNEDDeLskw3VPsrpYM0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.29.0/go.mod h1:2uL/xnOXh0CHOBFCWXz5u1A4GXLiW+0IQIzVbeOEQ0U= -go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= -go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= -go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 h1:m0yTiGDLUvVYaTFbAvCkVYIYcvwKt3G7OLoN77NUs/8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0/go.mod h1:wBQbT4UekBfegL2nx0Xk1vBcnzyBPsIVm9hRG4fYcr4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 h1:umZgi92IyxfXd/l4kaDhnKgY8rnN/cZcF1LKc6I8OQ8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0/go.mod h1:4lVs6obhSVRb1EW5FhOuBTyiQhtRtAnnva9vD3yRfq8= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.30.0 h1:kn1BudCgwtE7PxLqcZkErpD8GKqLZ6BSzeW9QihQJeM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.30.0/go.mod h1:ljkUDtAMdleoi9tIG1R6dJUpVwDcYjw3J2Q6Q/SuiC0= +go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= +go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= +go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -135,17 +125,16 @@ golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= -google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= -google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM= +google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 2423d982631ac724d8607ed5abbda4c655da63c9 Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:16:12 +0000 Subject: [PATCH 11/12] chore: record another error --- pkg/checks/traceroute/traceroute.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/checks/traceroute/traceroute.go b/pkg/checks/traceroute/traceroute.go index 90b86777..d970da61 100644 --- a/pkg/checks/traceroute/traceroute.go +++ b/pkg/checks/traceroute/traceroute.go @@ -220,6 +220,7 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error l.DebugContext(ctx, "Traceroute could not reach target") if !errors.Is(err, syscall.EHOSTUNREACH) { hopSpan.SetStatus(codes.Error, err.Error()) + hopSpan.RecordError(err) } return } From 0ceedeeee06ed00eb2b9695b0dc7b7edef970718 Mon Sep 17 00:00:00 2001 From: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:30:27 +0000 Subject: [PATCH 12/12] chore: address review comments * chore: rm named returns * chore: mark no route to host error as error * chore: rename hop method to doHop Signed-off-by: lvlcn-t <75443136+lvlcn-t@users.noreply.github.com> --- pkg/checks/traceroute/traceroute.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/checks/traceroute/traceroute.go b/pkg/checks/traceroute/traceroute.go index d970da61..b9300308 100644 --- a/pkg/checks/traceroute/traceroute.go +++ b/pkg/checks/traceroute/traceroute.go @@ -43,7 +43,7 @@ func randomPort() int { // tcpHop attempts to connect to the target host using TCP with the specified TTL and timeout. // It returns a [net.Conn], the port used for the connection, and an error if the connection failed. -func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) (conn net.Conn, port int, err error) { +func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) (net.Conn, int, error) { span := trace.SpanFromContext(ctx) for { @@ -86,10 +86,10 @@ func tcpHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) case errors.Is(err, unix.EADDRINUSE): // Address in use, retry by continuing the loop continue - case errors.Is(err, syscall.EHOSTUNREACH): - // No route to host is no error because of how tcp traceroute works + case errors.Is(err, unix.EHOSTUNREACH): + // No route to host is a special error because of how tcp traceroute works // we are expecting the connection to fail because of TTL expiry - span.SetStatus(codes.Unset, "No route to host") + span.SetStatus(codes.Error, "No route to host") span.AddEvent("No route to host", trace.WithAttributes( attribute.String("error", err.Error()), )) @@ -198,7 +198,7 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error attribute.Int("retry", retry), )) - hop, hErr := hop(ctx, addr, ttl, cfg.Timeout) + hop, hErr := doHop(ctx, addr, ttl, cfg.Timeout) if hop != nil { results <- *hop } @@ -243,9 +243,9 @@ func TraceRoute(ctx context.Context, cfg tracerouteConfig) (map[int][]Hop, error return hops, nil } -// hop performs a hop to the given address with the specified TTL and timeout. +// doHop performs a hop to the given address with the specified TTL and timeout. // It returns a Hop struct containing the latency, TTL, address, and other details of the hop. -func hop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) (*Hop, error) { +func doHop(ctx context.Context, addr net.Addr, ttl int, timeout time.Duration) (*Hop, error) { span := trace.SpanFromContext(ctx) log := logger.FromContext(ctx) canIcmp, icmpListener, err := newIcmpListener()