Skip to content

Commit a9dd590

Browse files
authored
Merge pull request #31024 from pgellert/slsr/mode-config-sycn
Schema Migrations: config/mode/delete syncing and context remapping
2 parents 2f9dd1b + 2f6c9b4 commit a9dd590

18 files changed

Lines changed: 2169 additions & 70 deletions

src/v/cluster_link/schema_registry_sync/http_source_reader.cc

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,51 @@ source_error to_source_error(rc::domain_error err) {
7878
return source_error{.kind = kind, .message = fmt::format("{}", err)};
7979
}
8080

81+
// Narrow the open-enum source mode to Redpanda's mode. READONLY_OVERRIDE is
82+
// preserved as read_only; FORWARD and unknown have no representation and yield
83+
// nullopt, which the caller counts as a per-item error.
84+
std::optional<ppsr::mode> narrow_mode(rc::registry_mode m) {
85+
switch (m) {
86+
case rc::registry_mode::read_write:
87+
return ppsr::mode::read_write;
88+
case rc::registry_mode::read_only:
89+
case rc::registry_mode::read_only_override:
90+
return ppsr::mode::read_only;
91+
case rc::registry_mode::import:
92+
return ppsr::mode::import;
93+
case rc::registry_mode::forward:
94+
case rc::registry_mode::unknown:
95+
return std::nullopt;
96+
}
97+
return std::nullopt;
98+
}
99+
100+
// Narrow the open-enum compatibility level to Redpanda's closed enum; only
101+
// unknown has no representation and yields nullopt.
102+
std::optional<ppsr::compatibility_level>
103+
narrow_compat(rc::registry_compatibility_level c) {
104+
using enum rc::registry_compatibility_level;
105+
switch (c) {
106+
case none:
107+
return ppsr::compatibility_level::none;
108+
case backward:
109+
return ppsr::compatibility_level::backward;
110+
case backward_transitive:
111+
return ppsr::compatibility_level::backward_transitive;
112+
case forward:
113+
return ppsr::compatibility_level::forward;
114+
case forward_transitive:
115+
return ppsr::compatibility_level::forward_transitive;
116+
case full:
117+
return ppsr::compatibility_level::full;
118+
case full_transitive:
119+
return ppsr::compatibility_level::full_transitive;
120+
case unknown:
121+
return std::nullopt;
122+
}
123+
return std::nullopt;
124+
}
125+
81126
} // namespace
82127

83128
std::optional<net::unresolved_address>
@@ -245,6 +290,78 @@ http_source_reader::read_subject_version(
245290
co_return std::move(res.value().schema);
246291
}
247292

293+
ss::future<source_result<std::optional<ppsr::mode>>>
294+
http_source_reader::read_mode(ppsr::context_subject sub, ss::abort_source& as) {
295+
auto units = co_await ss::get_units(_inflight, 1, as);
296+
auto client = co_await ensure_client(as);
297+
if (!client.has_value()) {
298+
co_return std::unexpected(std::move(client.error()));
299+
}
300+
retry_chain_node rtc(as, request_timeout, request_backoff);
301+
// Prefer GET /mode over GET /mode/. for the default context to be
302+
// backwards-compatible with a source that does not implement contexts.
303+
auto res = sub.is_default_context()
304+
? co_await client.value()->get_mode(rtc)
305+
: co_await client.value()->get_subject_mode(
306+
sub, rtc, ppsr::default_to_global::no);
307+
if (!res.has_value()) {
308+
if (std::holds_alternative<rc::subject_mode_not_found>(res.error())) {
309+
co_return std::optional<ppsr::mode>{std::nullopt};
310+
}
311+
co_return std::unexpected(to_source_error(std::move(res.error())));
312+
}
313+
auto narrowed = narrow_mode(res.value().mode);
314+
if (!narrowed.has_value()) {
315+
co_return std::unexpected(
316+
source_error{
317+
.kind = source_error_kind::operation_failed,
318+
.message = fmt::format(
319+
"source mode '{}' of {} is not supported by the destination",
320+
res.value().raw,
321+
sub)});
322+
}
323+
co_return narrowed;
324+
}
325+
326+
ss::future<source_result<std::optional<ppsr::compatibility_level>>>
327+
http_source_reader::read_config(
328+
ppsr::context_subject sub, ss::abort_source& as) {
329+
auto units = co_await ss::get_units(_inflight, 1, as);
330+
auto client = co_await ensure_client(as);
331+
if (!client.has_value()) {
332+
co_return std::unexpected(std::move(client.error()));
333+
}
334+
retry_chain_node rtc(as, request_timeout, request_backoff);
335+
// Prefer GET /config over GET /config/. for the default context to be
336+
// backwards-compatible with a source that does not implement contexts.
337+
auto res = sub.is_default_context()
338+
? co_await client.value()->get_config(rtc)
339+
: co_await client.value()->get_subject_config(
340+
sub, rtc, ppsr::default_to_global::no);
341+
if (!res.has_value()) {
342+
if (std::holds_alternative<rc::subject_config_not_found>(res.error())) {
343+
co_return std::optional<ppsr::compatibility_level>{std::nullopt};
344+
}
345+
co_return std::unexpected(to_source_error(std::move(res.error())));
346+
}
347+
auto narrowed = narrow_compat(res.value().level);
348+
if (!narrowed.has_value()) {
349+
co_return std::unexpected(
350+
source_error{
351+
.kind = source_error_kind::operation_failed,
352+
.message = fmt::format(
353+
"source compatibility level '{}' of {} is not supported by the "
354+
"destination",
355+
res.value().raw,
356+
sub)});
357+
}
358+
// Unsupported config fields (config_info::unknown_fields, e.g.
359+
// defaultRuleSet or compatibilityGroup) are ignored; honoring
360+
// unsupported_schema_feature_policy is future work, as it is for the
361+
// schema-body path in read_subject_version.
362+
co_return narrowed;
363+
}
364+
248365
ss::future<> http_source_reader::stop() {
249366
// Idempotent: the reader can be stopped more than once (e.g. an in-flight
250367
// reconciler stopping the task before link teardown stops it again). The

src/v/cluster_link/schema_registry_sync/http_source_reader.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ class http_source_reader final : public source_reader {
8080
ss::future<source_result<ppsr::stored_schema>> read_subject_version(
8181
ppsr::context_subject, ppsr::schema_version, ss::abort_source&) override;
8282

83+
ss::future<source_result<std::optional<ppsr::mode>>>
84+
read_mode(ppsr::context_subject, ss::abort_source&) override;
85+
86+
ss::future<source_result<std::optional<ppsr::compatibility_level>>>
87+
read_config(ppsr::context_subject, ss::abort_source&) override;
88+
8389
ss::future<> stop() override;
8490

8591
private:

0 commit comments

Comments
 (0)