1 Commits
Author SHA1 Message Date
sneak 84213bf29f main() is one call into internal/cli (closes #160)
check / check (push) Canceled after 0s
cmd/netwatch-server/main.go parsed the command line, ran the check-cidr
and prepare-data-dir subcommands and built the fx application itself;
the Go style guide puts all of that in internal/ or pkg/. The new
package internal/cli does it, with the same arguments, messages and
exit codes. main() is now os.Exit(cli.Main(Appname, Version,
os.Args[1:], cli.RunServer)): Main takes the function that runs the
server, so its tests call it with one that fails them. Appname and
Version stay in main, where the build sets Version. Each subcommand
writes its error to a writer it is given, and prepare-data-dir also
takes the volume and the user's name, so its own tests give it a
temporary directory and the user they run as.

Model: opus-5-5
2026-10-19 04:48:51 +00:00
6 changed files with 66 additions and 12 deletions
+1 -1
View File
@@ -207,7 +207,7 @@ stops. Its routes:
It is an `fx`-wired Go application: `cmd/netwatch-server/` starts it, and its
packages are in `internal/`:
- **`cli`**: Runs netwatch-server from its command line: the `check-cidr` and
- **`cli`**: Runs `netwatch-server` from its command line: the `check-cidr` and
`prepare-data-dir` subcommands, which `bin/entrypoint.sh` runs before it
starts the server, and otherwise the server, with the `fx` wiring.
- **`config`**: Loads configuration from environment variables and config files
+7 -3
View File
@@ -26,9 +26,13 @@ Run `make frontend-viewport-test` in CI as its own step; it is not part of
([#160](https://git.eeqj.de/sneak/netwatch/issues/160)). `internal/cli` runs
the `check-cidr` and `prepare-data-dir` subcommands, with the same arguments,
messages and exit codes, and otherwise the server, with the `fx` wiring.
`Appname` and `Version` stay in `main`, where the build sets `Version`. Unit
tests give each subcommand input it takes and input it refuses, and check the
exit code and what it writes to stderr
`Appname` and `Version` stay in `main`, where the build sets `Version`, and
`main` gives `cli.Main` the function that runs the server, which the tests
replace with one that fails them. Unit tests call `cli.Main` with `check-cidr`
and a CIDR it takes and one it refuses, and with `prepare-data-dir` and a path
outside `/data`, and check the exit code. Others give each subcommand input it
takes and input it refuses, and check the exit code and what it writes to
stderr
- 2026-10-19: report file writes no longer overlap
([#150](https://git.eeqj.de/sneak/netwatch/issues/150)), so the reports of
failed writes are written in the order they arrived. Two writes that failed at
+1 -1
View File
@@ -15,5 +15,5 @@ var (
)
func main() {
os.Exit(cli.Main(Appname, Version, os.Args[1:]))
os.Exit(cli.Main(Appname, Version, os.Args[1:], cli.RunServer))
}
+17 -6
View File
@@ -24,9 +24,19 @@ import (
)
// Main runs netwatch-server with args, its command line after the
// program's name, and returns the exit code. appname and version are
// the name and version the server logs and reports.
func Main(appname, version string, args []string) int {
// program's name, and returns the exit code. Unless args are
// "check-cidr CIDR" or "prepare-data-dir DATA_DIR", Main runs the
// server by calling serve with appname and version, the name and
// version the server logs and reports. main passes RunServer; the
// tests pass a function that fails them, so that no test starts the
// server. When serve is RunServer and the server fails to start or to
// stop, fx's Run exits the process with status 1 and Main never
// returns.
func Main(
appname, version string,
args []string,
serve func(appname, version string),
) int {
// "netwatch-server check-cidr CIDR" exits 1, with the error, if
// this server would refuse CIDR in its TRUSTED_PROXIES.
// bin/entrypoint.sh runs it on each entry it gives nginx.
@@ -42,7 +52,7 @@ func Main(appname, version string, args []string) int {
return prepareDataDir(os.Stderr, "/data", args[1], "netwatch")
}
runServer(appname, version)
serve(appname, version)
return 0
}
@@ -83,10 +93,11 @@ func prepareDataDir(stderr io.Writer, volume, dataDir, username string) int {
return 0
}
// runServer runs the server until it is told to stop. If the server
// RunServer runs the server until it is told to stop, with appname and
// version as the name and version it logs and reports. If the server
// fails to start or to stop, fx's Run exits the process with status 1
// itself.
func runServer(appname, version string) {
func RunServer(appname, version string) {
globals.Appname = appname
globals.Version = version
+39
View File
@@ -13,6 +13,35 @@ import (
"sneak.berlin/go/netwatch/internal/reportbuf"
)
// TestMainRunsCheckCIDR: Main given check-cidr returns 0 for a CIDR the
// server takes in its TRUSTED_PROXIES and 1 for one it refuses, and
// does not start the server.
func TestMainRunsCheckCIDR(t *testing.T) {
t.Parallel()
for cidr, want := range map[string]int{"10.0.0.0/8": 0, "1.2.3/32": 1} {
code := cli.Main("netwatch", "test", []string{"check-cidr", cidr},
failIfServerStarts(t))
if code != want {
t.Errorf("check-cidr %q: exit %d, want %d", cidr, code, want)
}
}
}
// TestMainRunsPrepareDataDir: Main given prepare-data-dir returns 1 for
// a DATA_DIR outside /data, and does not start the server. The test
// image has no netwatch user, so there the 1 comes from looking that
// user up; TestPrepareDataDirRefusesPathOutsideVolume checks the path.
func TestMainRunsPrepareDataDir(t *testing.T) {
t.Parallel()
code := cli.Main("netwatch", "test", []string{"prepare-data-dir", "/etc"},
failIfServerStarts(t))
if code != 1 {
t.Errorf("prepare-data-dir /etc: exit %d, want 1", code)
}
}
// TestCheckCIDRTakesCIDR: check-cidr exits 0, writing nothing, for a
// CIDR the server takes in its TRUSTED_PROXIES.
func TestCheckCIDRTakesCIDR(t *testing.T) {
@@ -104,3 +133,13 @@ func currentUsername(t *testing.T) string {
return current.Username
}
// failIfServerStarts is what the tests give Main in place of
// cli.RunServer: it fails the test instead of starting the server.
func failIfServerStarts(t *testing.T) func(appname, version string) {
t.Helper()
return func(string, string) {
t.Error("Main started the server")
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ package globals
import "go.uber.org/fx"
//nolint:gochecknoglobals // set by cli.Main before fx starts
//nolint:gochecknoglobals // set by cli.RunServer before fx starts
var (
// Appname is the application name.
Appname string