upstream: a diagnostics episode follows health, and a peer fault carries its cause
Gates / frontend (push) Successful in 2m5s
Gates / test (push) Successful in 2m43s
Gates / test-aarch64 (push) Successful in 8m19s
Gates / package (push) Successful in 4m21s
Gates / container (push) Successful in 13s
CI / gates (push) Successful in 30m43s
Gates / frontend (push) Successful in 2m5s
Gates / test (push) Successful in 2m43s
Gates / test-aarch64 (push) Successful in 8m19s
Gates / package (push) Successful in 4m21s
Gates / container (push) Successful in 13s
CI / gates (push) Successful in 30m43s
This commit is contained in:
@@ -93,15 +93,21 @@ fn queryWithId(buf: *[query_bytes.len]u8, id: u16) []const u8 {
|
||||
/// Echoes the question and appends one A record. This is the smallest thing a
|
||||
/// real upstream could return that the handler forwards unchanged, so the
|
||||
/// assertions below check bytes that travelled the whole path.
|
||||
fn answerQuery(query: []const u8, response_buf: []u8) transport.ExchangeError![]u8 {
|
||||
const request = packet.parse(query) catch return error.BadResponse;
|
||||
const q = packet.firstQuestion(request) orelse return error.BadResponse;
|
||||
fn answerQuery(query: []const u8, response_buf: []u8) transport.Outcome {
|
||||
const request = packet.parse(query) catch return peerFault(error.BadResponse);
|
||||
const q = packet.firstQuestion(request) orelse return peerFault(error.BadResponse);
|
||||
|
||||
var b = packet.ResponseBuilder.init(response_buf, request.header, q) catch
|
||||
return error.ResponseTooLarge;
|
||||
return peerFault(error.ResponseTooLarge);
|
||||
b.addAnswer(q.name, .a, .in, answer_ttl, &answer_rdata) catch
|
||||
return error.ResponseTooLarge;
|
||||
return b.finish();
|
||||
return peerFault(error.ResponseTooLarge);
|
||||
return .{ .reply = b.finish() };
|
||||
}
|
||||
|
||||
/// A fault whose cause is its own classification: these fixtures fail on
|
||||
/// purpose and have no concrete cause behind the classification.
|
||||
fn peerFault(kind: transport.PeerFault) transport.Outcome {
|
||||
return .{ .fault = .{ .kind = kind, .cause = kind } };
|
||||
}
|
||||
|
||||
/// The healthy upstream. `calls` is atomic because the listener tasks run on
|
||||
@@ -114,16 +120,14 @@ const GoodUpstream = struct {
|
||||
io: std.Io,
|
||||
query: []const u8,
|
||||
response_buf: []u8,
|
||||
selected: *?[]const u8,
|
||||
) transport.ExchangeError![]u8 {
|
||||
) transport.LeafError!transport.Outcome {
|
||||
_ = io;
|
||||
selected.* = "fake://good-upstream";
|
||||
const self: *GoodUpstream = @ptrCast(@alignCast(ptr));
|
||||
_ = self.calls.fetchAdd(1, .monotonic);
|
||||
return answerQuery(query, response_buf);
|
||||
}
|
||||
|
||||
fn client(self: *GoodUpstream) transport.Client {
|
||||
fn leaf(self: *GoodUpstream) transport.Leaf {
|
||||
return .{ .ptr = self, .exchangeFn = exchangeFn };
|
||||
}
|
||||
};
|
||||
@@ -140,17 +144,15 @@ const FaultyUpstream = struct {
|
||||
io: std.Io,
|
||||
query: []const u8,
|
||||
response_buf: []u8,
|
||||
selected: *?[]const u8,
|
||||
) transport.ExchangeError![]u8 {
|
||||
) transport.LeafError!transport.Outcome {
|
||||
_ = io;
|
||||
selected.* = "fake://faulty-upstream";
|
||||
const self: *FaultyUpstream = @ptrCast(@alignCast(ptr));
|
||||
const seen = self.calls.fetchAdd(1, .monotonic);
|
||||
if (seen < self.fail_first) return self.fault;
|
||||
if (seen < self.fail_first) return peerFault(self.fault);
|
||||
return answerQuery(query, response_buf);
|
||||
}
|
||||
|
||||
fn client(self: *FaultyUpstream) transport.Client {
|
||||
fn leaf(self: *FaultyUpstream) transport.Leaf {
|
||||
return .{ .ptr = self, .exchangeFn = exchangeFn };
|
||||
}
|
||||
};
|
||||
@@ -166,10 +168,10 @@ const EntryStorage = struct {
|
||||
fn entry(
|
||||
self: *EntryStorage,
|
||||
url: []const u8,
|
||||
upstream_client: transport.Client,
|
||||
upstream_leaf: transport.Leaf,
|
||||
priority: i32,
|
||||
) pool.Entry {
|
||||
self.slots[0] = .{ .client = upstream_client };
|
||||
self.slots[0] = .{ .client = upstream_leaf };
|
||||
return .{
|
||||
.endpoint = transport.Endpoint.parse(url) catch unreachable,
|
||||
.slots = &self.slots,
|
||||
@@ -270,8 +272,8 @@ test "the whole resolver answers over udp and tcp and fails over to a healthy up
|
||||
var bad_storage: EntryStorage = .{};
|
||||
var good_storage: EntryStorage = .{};
|
||||
var entries = [_]pool.Entry{
|
||||
bad_storage.entry("https://bad.example/dns-query", bad.client(), 10),
|
||||
good_storage.entry("tls://good.example", good.client(), 20),
|
||||
bad_storage.entry("https://bad.example/dns-query", bad.leaf(), 10),
|
||||
good_storage.entry("tls://good.example", good.leaf(), 20),
|
||||
};
|
||||
var upstreams: pool.Pool = .init(&entries, test_cfg, pool_timeouts, 1);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user