This repository has no description
1//go:build linux
2
3package microvm
4
5import (
6 "io"
7 "log/slog"
8 "net/http"
9 "net/http/httptest"
10 "net/url"
11 "strings"
12 "testing"
13)
14
15func TestUploadProxyRewritesHostAndAuth(t *testing.T) {
16 var upstreamHost string
17 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
18 if req.Host != upstreamHost {
19 t.Errorf("host: got %q, want %q", req.Host, upstreamHost)
20 }
21 if req.URL.Path != "/sub/abc.narinfo" {
22 t.Errorf("path: got %q, want /sub/abc.narinfo", req.URL.Path)
23 }
24 if user, pass, ok := req.BasicAuth(); !ok || user != "dawn" || pass != "woof" {
25 t.Errorf("basic auth: got %q/%q/%v, want dawn/hunter2/true", user, pass, ok)
26 }
27 _, _ = io.WriteString(w, "ok")
28 }))
29 defer upstream.Close()
30 upstreamHost = strings.TrimPrefix(upstream.URL, "http://")
31
32 target, err := url.Parse("http://dawn:woof@" + upstreamHost + "/sub/")
33 if err != nil {
34 t.Fatal(err)
35 }
36
37 req := httptest.NewRequest(http.MethodPut, "http://127.0.0.1:10501/abc.narinfo", strings.NewReader("narinfo"))
38 req.Host = "127.0.0.1:10501"
39 rec := httptest.NewRecorder()
40 uploadProxyHandler(target, nil, slog.Default()).ServeHTTP(rec, req)
41
42 if rec.Code != http.StatusOK {
43 t.Fatalf("status: got %d, want 200; body=%q", rec.Code, rec.Body.String())
44 }
45}
46
47func mustParseURL(t *testing.T, raw string) *url.URL {
48 t.Helper()
49 u, err := url.Parse(raw)
50 if err != nil {
51 t.Fatalf("parse %q: %v", raw, err)
52 }
53 return u
54}
55
56func TestUploadProxySkipsNarinfoAvailableUpstream(t *testing.T) {
57 var uploadHits int
58 target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
59 uploadHits++
60 w.WriteHeader(http.StatusNotFound)
61 }))
62 defer target.Close()
63
64 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
65 if req.URL.Path != "/abc.narinfo" {
66 t.Errorf("upstream path: got %q, want /abc.narinfo", req.URL.Path)
67 }
68 _, _ = io.WriteString(w, "StorePath: /nix/store/abc\n")
69 }))
70 defer upstream.Close()
71
72 handler := uploadProxyHandler(
73 mustParseURL(t, target.URL),
74 []CacheUpstream{{url: mustParseURL(t, upstream.URL)}},
75 slog.Default(),
76 )
77
78 req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1:10501/abc.narinfo", nil)
79 rec := httptest.NewRecorder()
80 handler.ServeHTTP(rec, req)
81
82 if rec.Code != http.StatusOK {
83 t.Fatalf("status: got %d, want 200 (so nix treats the path as present and skips upload)", rec.Code)
84 }
85 if !strings.Contains(rec.Body.String(), "StorePath: /nix/store/abc") {
86 t.Fatalf("body: got %q, want the upstream narinfo body", rec.Body.String())
87 }
88}
89
90func TestUploadProxyUploadsNarinfoNobodyHas(t *testing.T) {
91 target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
92 w.WriteHeader(http.StatusNotFound)
93 }))
94 defer target.Close()
95 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
96 w.WriteHeader(http.StatusNotFound)
97 }))
98 defer upstream.Close()
99
100 handler := uploadProxyHandler(
101 mustParseURL(t, target.URL),
102 []CacheUpstream{{url: mustParseURL(t, upstream.URL)}},
103 slog.Default(),
104 )
105
106 req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1:10501/abc.narinfo", nil)
107 rec := httptest.NewRecorder()
108 handler.ServeHTTP(rec, req)
109
110 if rec.Code != http.StatusNotFound {
111 t.Fatalf("status: got %d, want 404 (so nix uploads the path)", rec.Code)
112 }
113}
114
115func TestUploadProxySkipsNarinfoAlreadyOnTarget(t *testing.T) {
116 target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
117 _, _ = io.WriteString(w, "StorePath: /nix/store/abc\n")
118 }))
119 defer target.Close()
120
121 handler := uploadProxyHandler(mustParseURL(t, target.URL), nil, slog.Default())
122
123 req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1:10501/abc.narinfo", nil)
124 rec := httptest.NewRecorder()
125 handler.ServeHTTP(rec, req)
126
127 if rec.Code != http.StatusOK {
128 t.Fatalf("status: got %d, want 200", rec.Code)
129 }
130}