package tracing import ( "context" "errors" "testing" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) type fakeReconciler struct { res reconcile.Result err error } func (f *fakeReconciler) Reconcile(context.Context, reconcile.Request) (reconcile.Result, error) { return f.res, f.err } func TestNewReconciler_spanPerReconcile(t *testing.T) { t.Parallel() req := reconcile.Request{NamespacedName: types.NamespacedName{Namespace: "default", Name: "p1"}} tests := []struct { name string inner *fakeReconciler wantStatus codes.Code wantAttr attribute.KeyValue }{ { name: "success records requeue", inner: &fakeReconciler{res: reconcile.Result{RequeueAfter: 10 * time.Second}}, wantStatus: codes.Ok, wantAttr: attribute.Int64("reconcile.requeue_after_ms", 10_000), }, { name: "error sets error status", inner: &fakeReconciler{err: errors.New("boom")}, wantStatus: codes.Error, wantAttr: attribute.String("k8s.object.name", "p1"), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { t.Parallel() sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) r := NewReconciler("Proxy", tc.inner, WithTracerProvider(tp)) res, err := r.Reconcile(context.Background(), req) if res != tc.inner.res || !errors.Is(err, tc.inner.err) { t.Fatalf("decorator changed the result: (%v, %v)", res, err) } ended := sr.Ended() if len(ended) != 1 { t.Fatalf("got %d spans, want 1", len(ended)) } span := ended[0] if span.Name() != "Reconcile Proxy" { t.Errorf("span name = %q, want %q", span.Name(), "Reconcile Proxy") } if span.Status().Code != tc.wantStatus { t.Errorf("status = %v, want %v", span.Status().Code, tc.wantStatus) } found := false for _, a := range span.Attributes() { if a == tc.wantAttr { found = true } } if !found { t.Errorf("attribute %v missing in %v", tc.wantAttr, span.Attributes()) } }) } }