Ctrl K
ComunidadGuías…

internal/traffic/live_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package traffic

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"

	"github.com/sophotechlabs/spinoza/internal/prom"
)

type promServer struct {
	asked   []string
	answers map[string]string
}

func (p *promServer) handle(w http.ResponseWriter, r *http.Request) {
	if strings.HasSuffix(r.URL.Path, "/status/buildinfo") {
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"status":"success","data":{"version":"3.13.2"}}`))
		return
	}
	query := r.URL.Query().Get("query")
	p.asked = append(p.asked, query)
	body, found := p.answers[query]
	if !found {
		body = emptyVector
	}
	w.Header().Set("Content-Type", "application/json")
	_, _ = w.Write([]byte(body))
}

const emptyVector = `{"status":"success","data":{"resultType":"vector","result":[]}}`

func vector(t *testing.T, samples ...map[string]any) string {
	t.Helper()
	result := make([]map[string]any, 0, len(samples))
	result = append(result, samples...)
	body := map[string]any{
		"status": "success",
		"data":   map[string]any{"resultType": "vector", "result": result},
	}
	encoded, err := json.Marshal(body)
	if err != nil {
		t.Fatalf("marshal: %v", err)
	}
	return string(encoded)
}

func sample(labels map[string]string, value string) map[string]any {
	return map[string]any{"metric": labels, "value": []any{1787933018.510, value}}
}

func liveReader(t *testing.T, answers map[string]string) (*Reader, *promServer) {
	t.Helper()
	server := &promServer{answers: answers}
	apiserver := httptest.NewServer(http.HandlerFunc(server.handle))
	t.Cleanup(apiserver.Close)
	cs, err := kubernetes.NewForConfig(&rest.Config{Host: apiserver.URL})
	if err != nil {
		t.Fatalf("clientset: %v", err)
	}
	client := prom.NewClient(cs, prom.Target{Namespace: "monitoring", Service: "prometheus", Port: "9090"})
	return New(client), server
}

func TestAGraphIsOneQueryAgainstARealPrometheus(t *testing.T) {
	answers := map[string]string{
		cilium.flows: vector(
			t,
			sample(map[string]string{
				"source_namespace":      "apps",
				"source_workload":       "web",
				"destination_namespace": "data",
				"destination_workload":  "postgres",
				"verdict":               forwarded,
			}, "12.5"),
		),
	}
	reader, server := liveReader(t, answers)

	graph := reader.Graph(t.Context(), time.Unix(1787933018, 0))

	if graph.Error != "" {
		t.Fatalf("graph reported %q", graph.Error)
	}
	if len(graph.Edges) != 1 {
		t.Fatalf("edges = %d, want 1: %+v", len(graph.Edges), graph.Edges)
	}
	if graph.Edges[0].From != "apps/web" || graph.Edges[0].To != "data/postgres" {
		t.Fatalf("edge = %+v", graph.Edges[0])
	}
	if graph.Edges[0].Rate != 12.5 {
		t.Fatalf("rate = %v, want 12.5", graph.Edges[0].Rate)
	}
	if len(server.asked) != 1 {
		t.Fatalf("prometheus was asked %d queries, want 1: %v", len(server.asked), server.asked)
	}
	if server.asked[0] != cilium.flows {
		t.Fatalf("query = %q, want the flow query", server.asked[0])
	}
}

func TestAnUnlabeledMeshCostsThreeQueriesInThisOrder(t *testing.T) {
	answers := map[string]string{
		cilium.flows:   vector(t, sample(map[string]string{"verdict": forwarded}, "138")),
		cilium.present: vector(t, sample(map[string]string{}, "12")),
	}
	reader, server := liveReader(t, answers)

	support := reader.Support(t.Context(), time.Unix(1787933018, 0))

	if support.Available {
		t.Fatalf("unlabeled metrics were offered: %+v", support)
	}
	if support.Reason != cilium.hint {
		t.Fatalf("reason = %q, want the configuration hint", support.Reason)
	}
	want := []string{cilium.flows, cilium.labeled, cilium.present}
	if len(server.asked) != len(want) {
		t.Fatalf("queries = %v, want %v", server.asked, want)
	}
	for i, query := range want {
		if server.asked[i] != query {
			t.Fatalf("query %d = %q, want %q", i, server.asked[i], query)
		}
	}
}

func TestPrometheusRefusingAQuerySurfacesItsMessage(t *testing.T) {
	answers := map[string]string{
		cilium.flows: `{"status":"error","errorType":"bad_data","error":"parse error at char 5"}`,
	}
	reader, _ := liveReader(t, answers)

	graph := reader.Graph(t.Context(), time.Unix(1787933018, 0))

	if !strings.Contains(graph.Error, "parse error at char 5") {
		t.Fatalf("error = %q, want prometheus's own message", graph.Error)
	}
	if graph.Nodes == nil || graph.Edges == nil {
		t.Fatalf("a refused read sent null instead of an empty list: %+v", graph)
	}
}

func TestAnApiserverThatWillNotProxyIsReported(t *testing.T) {
	apiserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusForbidden)
		_, _ = w.Write([]byte(`{"kind":"Status","status":"Failure","reason":"Forbidden","code":403}`))
	}))
	t.Cleanup(apiserver.Close)
	cs, err := kubernetes.NewForConfig(&rest.Config{Host: apiserver.URL})
	if err != nil {
		t.Fatalf("clientset: %v", err)
	}
	client := prom.NewClient(cs, prom.Target{Namespace: "monitoring", Service: "prometheus", Port: "9090"})

	support := New(client).Support(t.Context(), time.Unix(1787933018, 0))

	if support.Available {
		t.Fatal("a forbidden proxy was reported as available")
	}
	if !strings.Contains(support.Reason, "may not proxy services") {
		t.Fatalf("reason = %q, want the proxy refusal", support.Reason)
	}
}