Contents · pkgproxyctl

pkgproxy design: pkgproxyctl

pkgproxyctl as a multi-call binary: the command layout, the piped setup scripts and the help layout. Part of the pkgproxy design, whose index says which file holds each § number.

14. pkgproxyctl: a multi-call binary, not a second binary

pkgproxyctl is the operator-facing CLI: stats reads the stats API (§13), setup prints client configuration (§16.4), open opens the setup page in a browser (§20.5), and cache invalidate and offline drive the admin API (§16.9, §21) — write-capable admin actions grow this same binary rather than start a third one. It’s the same compiled binary as the pkgproxy daemon, not a second cmd/ directory: cmd/pkgproxy/main.go wraps both in one go-cmdr/router.Cmd with UseArgv0: true, which dispatches on the base name the binary was invoked as, and every packaging (snap/snapcraft.yaml, and image-garden’s own) installs pkgproxyctl as a symlink to pkgproxy.

The reason is binary size: pkgproxyctl is thin (an HTTP-over-UNIX-socket client and some table formatting), but a second Go binary would statically link its own copy of the runtime, net/http, encoding/json and the rest already inside pkgproxy, roughly doubling what ships for a few hundred lines of CLI. Below the name dispatch, pkgproxyctl’s own command tree is an ordinary go-cmdr router (ctl.go).

14.1 One cmd_$foo.go file per command

cmd/pkgproxy groups source files by command rather than by topic: one cmdr.Cmd implementation (and its cmd_$foo_test.go) per file, named after the command (cmd_setup_lxd.go is pkgproxyctl setup lxd), so a command is findable by name alone. Code several commands share lives in files that are not named cmd_* (guide.go, usage.go, instructions.go, statssocket.go, …). pkgproxyctl stats decodes the daemon’s own stats.Entry rather than declaring a parallel response type.

14.2 Piped setup scripts

Every pkgproxyctl setup command prints a guide on a terminal and a shell script when its output is piped (| sh); the setup page serves the same scripts. Each piped script is one { ... } block (scriptBegin): sh reads a script from a pipe as it goes, so a command that reads its stdin, as lxc profile create does when stdin is not a terminal, would otherwise take the rest of it. A script with nothing to do prints nothing, as an empty block is not valid sh. A setup command that fails prints exit 1 to a pipe, its error going to stderr as always, so that pkgproxyctl setup X | sh fails rather than run an empty script and succeed. setup go -undo, setup lxd -undo, setup lxd-remote -undo and the undo of setup snapcraft, setup rockcraft and setup charmcraft are safe to repeat, and to run before setting up: a project’s settings are cleared only when the project exists, as a craft tool’s does not before its first build. The scripts’ own messages are translated (§22).

14.3 One help layout, with the positional arguments

go-cmdr’s stock usage for a flagcmd command (Usage of <name>: and the flags) has no notion of positional arguments, so it cannot say that cache invalidate takes a URL or offline mode a mode. No go-cmdr change is needed: flagcmd.MakeCmd installs a command’s own FlagUsageContext as fl.Usage, and every leaf command, the daemon included, implements it with printUsage (cmd/pkgproxy/usage.go), in the layout go-cmdr’s router uses for its own help:

Usage: pkgproxyctl offline mode [OPTIONS] MODE

<a short description, a guide (§22): wrapped prose, verbatim lists>

Available options:
  <flagcmd.PrintDefaults>

The usage line is one msgid per argument shape (Usage: %s [OPTIONS], ... URL, ... MODE), translated whole, with the placeholders translated the way go-cmdr’s own catalog does ([OPCJE], TRYB). The description is short: what the command does, never what the setup guides print on a terminal. offline has status as its router’s Implicit command, so its help says [COMMAND].

A wrong number of positional arguments prints the usage and then a “cannot …” error, exit status 1, the way the router reports a missing command. A command that takes none (the daemon, stats, every setup command, offline status|on|off) rejects stray ones with rejectArgs (cannot run <name>: unexpected arguments: "...") instead of ignoring them: a misplaced word (pkgproxyctl setup apt -addr :3142 now) is a mistake to report, not to guess around. Nothing that ships starts the daemon with positional arguments (snap/snapcraft.yaml, pkgproxy-env, the configure hook’s -check-config).