docs: rewrite for people running nxdns; query-history refusals name their remedy and a refused start gives no wrong hints
This commit is contained in:
+37
-14
@@ -89,17 +89,8 @@ const download_budget_s = 300;
|
||||
pub fn run(runner: cli.Runner, args: cli.RunArgs) u8 {
|
||||
const code = serve(runner, args) catch |err| code: {
|
||||
runner.err.print("nxdns run failed: {s}\n", .{@errorName(err)}) catch {};
|
||||
const mapped = failureExitCode(err);
|
||||
if (mapped == cli.exit_check) {
|
||||
runner.err.writeAll("run `nxdns check` to see the configuration in full\n") catch {};
|
||||
// Ruling 6: after bootstrap seeding died, a fresh database fails
|
||||
// validation naturally (`NoUsableUpstreams`) and the operator needs
|
||||
// to be told how a database gets a configuration at all. In file
|
||||
// mode they already have a file, and the diagnostics above name what
|
||||
// is wrong with it.
|
||||
if (args.config == null) cli.writeDbSourceHint(runner.err);
|
||||
}
|
||||
break :code mapped;
|
||||
writeFailureHints(runner.err, err, args.config != null);
|
||||
break :code failureExitCode(err);
|
||||
};
|
||||
|
||||
// Output the operator never received is not output, so a failed flush
|
||||
@@ -109,6 +100,18 @@ pub fn run(runner: cli.Runner, args: cli.RunArgs) u8 {
|
||||
return code;
|
||||
}
|
||||
|
||||
/// What to do next, for a failure the operator has to fix. A schema refusal is
|
||||
/// not about the configuration: its log line already names the remedy, and
|
||||
/// `nxdns check` has nothing to add. `NoUsableUpstreams` in database mode is an
|
||||
/// unconfigured database, which needs telling how a database gets a
|
||||
/// configuration at all; in file mode the diagnostics above name what is wrong
|
||||
/// with the file.
|
||||
fn writeFailureHints(w: *Writer, err: anyerror, file_mode: bool) void {
|
||||
if (!faults.isConfigFault(err) or faults.isSchemaRefusal(err)) return;
|
||||
w.writeAll("run `nxdns check` to see the configuration in full\n") catch {};
|
||||
if (!file_mode and err == error.NoUsableUpstreams) cli.writeDbSourceHint(w);
|
||||
}
|
||||
|
||||
/// The one classification, from `config/faults.zig`. This file keeps no list of
|
||||
/// its own: `run` exiting 1 on a seed file `check` and `import` exit 2 on was
|
||||
/// exactly the cost of the second list that used to be here.
|
||||
@@ -1656,6 +1659,26 @@ test "a recreate under a long data directory keeps the watermark and a usable na
|
||||
try deep.access(io, name, .{});
|
||||
}
|
||||
|
||||
test "a failed run points at the next step only where it applies" {
|
||||
const Case = struct { err: anyerror, file_mode: bool, check: bool, import: bool };
|
||||
const cases = [_]Case{
|
||||
.{ .err = error.NoUsableUpstreams, .file_mode = false, .check = true, .import = true },
|
||||
.{ .err = error.NoUsableUpstreams, .file_mode = true, .check = true, .import = false },
|
||||
.{ .err = error.MissingDefaultGroup, .file_mode = false, .check = true, .import = false },
|
||||
.{ .err = error.SchemaTooNew, .file_mode = false, .check = false, .import = false },
|
||||
.{ .err = error.MigrationFailed, .file_mode = true, .check = false, .import = false },
|
||||
.{ .err = error.AddressInUse, .file_mode = false, .check = false, .import = false },
|
||||
};
|
||||
for (cases) |case| {
|
||||
var buf: [512]u8 = undefined;
|
||||
var w: Writer = .fixed(&buf);
|
||||
writeFailureHints(&w, case.err, case.file_mode);
|
||||
const text = w.buffered();
|
||||
try testing.expectEqual(case.check, std.mem.indexOf(u8, text, "nxdns check") != null);
|
||||
try testing.expectEqual(case.import, std.mem.indexOf(u8, text, cli.db_source_hint) != null);
|
||||
}
|
||||
}
|
||||
|
||||
test "parseBind refuses a bind address of the wrong family" {
|
||||
var out_buf: [8]u8 = undefined;
|
||||
var err_buf: [256]u8 = undefined;
|
||||
@@ -1760,9 +1783,9 @@ test "a configuration whose blocklist source is in no group imports and checks c
|
||||
const io = threaded.io();
|
||||
const gpa = std.testing.allocator;
|
||||
|
||||
// A source is created before it is attached — `docs/tutorial/first-run.md`
|
||||
// POSTs the blocklist and then PUTs the group's sources — so an unattached
|
||||
// source is a legal intermediate state on every write path. It warns; it
|
||||
// A source is created before it is attached — the Protection page adds the
|
||||
// source and then saves the group's sources — so an unattached source is a
|
||||
// legal intermediate state on every write path. It warns; it
|
||||
// never fails.
|
||||
const source: [:0]const u8 =
|
||||
\\.{
|
||||
|
||||
Reference in New Issue
Block a user