v0.4.3 bdc41cd

netsetup_test.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package netsetup
4
5import (
6	"context"
7	"errors"
8	"net/netip"
9	"os"
10	"path/filepath"
11	"strings"
12	"testing"
13
14	"bnema.dev/ermite-runner/internal/cmdrun"
15	"bnema.dev/ermite-runner/internal/netpolicy"
16)
17
18func guest() netpolicy.Guest {
19	return netpolicy.Guest{Iface: "ertap0", Gateway: netip.MustParseAddr("127.0.0.1"), Addr: netip.MustParseAddr("10.200.0.2")}
20}
21
22func TestAttachDetach(t *testing.T) {
23	f := &cmdrun.Fake{}
24	n := New(f, 0, "")
25	t.Cleanup(n.Close)
26	ctx := context.Background()
27	if err := n.Attach(ctx, guest()); err != nil {
28		t.Skip("cannot bind loopback:", err)
29	}
30	if _, ok := n.DNS.Addr(guest().Gateway); !ok {
31		t.Fatal("DNS forwarder not listening on the gateway")
32	}
33	if err := n.Detach(ctx, "ertap0", guest().Gateway); err != nil {
34		t.Fatal(err)
35	}
36	if _, ok := n.DNS.Addr(guest().Gateway); ok {
37		t.Fatal("DNS forwarder still listening after Detach")
38	}
39	if len(f.Calls) != 2 || f.Calls[1].Stdin != netpolicy.Ruleset(nil) {
40		t.Fatalf("calls: %v", f.Lines())
41	}
42	if err := n.Detach(ctx, "ertap0", guest().Gateway); err != nil {
43		t.Fatalf("Detach must be idempotent: %v", err)
44	}
45}
46
47// The host network surface of a guest is DNS and the firewall policy, nothing
48// else: Attach opens no cache port, neither in the policy nor as a listener.
49func TestAttachOpensNoCachePort(t *testing.T) {
50	f := &cmdrun.Fake{}
51	n := New(f, 0, "")
52	t.Cleanup(n.Close)
53	if err := n.Attach(context.Background(), guest()); err != nil {
54		t.Skip("cannot bind loopback:", err)
55	}
56	rs := f.Calls[0].Stdin
57	for _, port := range []string{"8089", "9089"} {
58		if strings.Contains(rs, port) {
59			t.Fatalf("the policy mentions the cache port %s:\n%s", port, rs)
60		}
61	}
62	if !strings.Contains(rs, "dport 53 accept") {
63		t.Fatalf("the policy lost DNS:\n%s", rs)
64	}
65}
66
67func TestAttachRollsBackPolicyWhenDNSFails(t *testing.T) {
68	f := &cmdrun.Fake{}
69	n := New(f, 0, "")
70	t.Cleanup(n.Close)
71	ctx := context.Background()
72	if err := n.Attach(ctx, guest()); err != nil {
73		t.Skip(err)
74	}
75	before := len(f.Calls)
76	if err := n.Attach(ctx, guest()); err == nil { // already listening
77		t.Fatal("second Attach on the same gateway must fail")
78	}
79	if len(f.Calls) != before+2 {
80		t.Fatalf("expected add then rollback remove, got %v", f.Lines())
81	}
82}
83
84func TestAttachPolicyError(t *testing.T) {
85	boom := errors.New("nft failed")
86	n := New(&cmdrun.Fake{Handle: func(cmdrun.Call) ([]byte, error) { return nil, boom }}, 0, "")
87	if err := n.Attach(context.Background(), guest()); !errors.Is(err, boom) {
88		t.Fatalf("got %v", err)
89	}
90	if _, ok := n.DNS.Addr(guest().Gateway); ok {
91		t.Fatal("DNS must not start when the policy failed")
92	}
93}
94
95func TestEnableForwarding(t *testing.T) {
96	n := New(&cmdrun.Fake{}, 0, "")
97	n.ForwardFile = filepath.Join(t.TempDir(), "ip_forward")
98	_ = os.WriteFile(n.ForwardFile, []byte("0\n"), 0o644)
99	if err := n.EnableForwarding(); err != nil {
100		t.Fatal(err)
101	}
102	if b, _ := os.ReadFile(n.ForwardFile); strings.TrimSpace(string(b)) != "1" {
103		t.Fatalf("not enabled: %q", b)
104	}
105	n.ForwardFile = filepath.Join(t.TempDir(), "missing", "ip_forward")
106	if err := n.EnableForwarding(); err == nil || !strings.Contains(err.Error(), "ip_forward") {
107		t.Fatalf("want a clear error, got %v", err)
108	}
109}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 11

↑↓ moveenter openesc close