From 99983e395e81163eb4717bd5f42cb708f9d0d8d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 16:38:01 +0000 Subject: [PATCH 1/2] fix(runner/loki): send basic auth on the compat query path The compat handlers (query_loki / query_loki_labels / query_grafana_loki_label_values / query_grafana_loki_series) build their request in rawGet, which copied only ExtraHeaders and ignored the LOKI_USERNAME / LOKI_PASSWORD basic-auth credentials set on the client. Any basic-auth-protected Loki returned 401 for these actions, even though the primitive loki_* handlers authenticated correctly. Apply SetBasicAuth in rawGet when both username and password are set, mirroring the primitive loki.Client.get path. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01L2HAXQH1gEtX7h4sUJsi9j --- runner/pkg/enrichers/loki.go | 6 +++ runner/pkg/enrichers/loki_test.go | 63 +++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 runner/pkg/enrichers/loki_test.go diff --git a/runner/pkg/enrichers/loki.go b/runner/pkg/enrichers/loki.go index a3af769..cbab51c 100644 --- a/runner/pkg/enrichers/loki.go +++ b/runner/pkg/enrichers/loki.go @@ -117,6 +117,12 @@ func (l *LokiCompat) rawGet(ctx context.Context, path string) ([]byte, error) { if err != nil { return nil, err } + // LOKI_USERNAME/LOKI_PASSWORD → Basic-Auth, mirroring the primitive + // loki.Client.get (client.go). The compat path was dropping these, + // causing 401s on any basic-auth Loki. + if l.c.Username != "" && l.c.Password != "" { + req.SetBasicAuth(l.c.Username, l.c.Password) + } for k, vv := range l.c.ExtraHeaders { for _, v := range vv { req.Header.Add(k, v) diff --git a/runner/pkg/enrichers/loki_test.go b/runner/pkg/enrichers/loki_test.go new file mode 100644 index 0000000..7137706 --- /dev/null +++ b/runner/pkg/enrichers/loki_test.go @@ -0,0 +1,63 @@ +package enrichers + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/nudgebee/nudgebee-agent/pkg/observability/loki" +) + +// TestLokiCompat_SendsBasicAuth verifies the compat handlers apply +// LOKI_USERNAME/LOKI_PASSWORD basic auth, matching the primitive +// loki.Client path. Regression guard for the 401-on-basic-auth-Loki bug. +func TestLokiCompat_SendsBasicAuth(t *testing.T) { + var gotUser, gotPass string + var gotOK bool + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + gotUser, gotPass, gotOK = r.BasicAuth() + _, _ = w.Write([]byte(`{"status":"success"}`)) + })) + defer srv.Close() + + lc := loki.New(srv.URL, &http.Client{Timeout: 5 * time.Second}) + lc.Username = "loki-user" + lc.Password = "loki-pass" + compat := NewLokiCompat(lc) + + h := compat.Handlers()["query_loki"] + if _, err := h(context.Background(), map[string]any{"query": "query={foo=\"bar\"}"}); err != nil { + t.Fatal(err) + } + if !gotOK { + t.Fatal("no basic auth header reached upstream") + } + if gotUser != "loki-user" || gotPass != "loki-pass" { + t.Errorf("basic auth = %q/%q; want loki-user/loki-pass", gotUser, gotPass) + } +} + +// TestLokiCompat_NoAuthWhenUnset confirms we don't send basic auth when +// username/password aren't configured (only one set counts as unset). +func TestLokiCompat_NoAuthWhenUnset(t *testing.T) { + var sawAuth bool + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _, sawAuth = r.BasicAuth() + _, _ = w.Write([]byte(`{}`)) + })) + defer srv.Close() + + lc := loki.New(srv.URL, &http.Client{Timeout: 5 * time.Second}) + lc.Username = "only-user" // password empty → must not send basic auth + compat := NewLokiCompat(lc) + + h := compat.Handlers()["query_loki"] + if _, err := h(context.Background(), map[string]any{"query": "query=x"}); err != nil { + t.Fatal(err) + } + if sawAuth { + t.Error("basic auth sent even though password is empty") + } +} From 990bcccf8df0f931bb65b27058dd63cfc27302ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 14 Jul 2026 19:28:48 +0000 Subject: [PATCH 2/2] chore: update image tags for main release --- charts/nudgebee-agent/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/nudgebee-agent/values.yaml b/charts/nudgebee-agent/values.yaml index 2491e5f..b5c1693 100644 --- a/charts/nudgebee-agent/values.yaml +++ b/charts/nudgebee-agent/values.yaml @@ -65,7 +65,7 @@ runnerServiceAccount: runner: image: repository: ghcr.io/nudgebee/nudgebee-agent - tag: 2026-07-14T03-38-04_fbc9436d684526a0b45488ac510a33e5168b5ae7 + tag: 2026-07-14T09-20-14_17a3cbad58bd80c14dee85a158cc4ec2de19e09f # Image template the pod_profiler action launches debugger pods from. # The agent substitutes `{}` for the variant (bpf, jvm, python, perf, ruby). # Surfaces as PROFILER_IMAGE; leave empty to fall back to the binary default.