Skip to content

Support Kafka config providers in realtime table configs - #19202

Open
goutamadwant wants to merge 4 commits into
apache:masterfrom
goutamadwant:support-kafka-config-providers
Open

Support Kafka config providers in realtime table configs#19202
goutamadwant wants to merge 4 commits into
apache:masterfrom
goutamadwant:support-kafka-config-providers

Conversation

@goutamadwant

Copy link
Copy Markdown
Contributor

Summary

  • preserve Kafka ConfigProvider references while Pinot resolves environment variables and system properties in table configs
  • pass config.providers and config.providers.<alias>.* through Kafka consumer and AdminClient property filtering
  • apply the behavior to Kafka 3.x and Kafka 4.x, including the production shared AdminClient path
  • cover legacy and multi-stream table configs, provider scoping, provider parameters, and actual Kafka client construction

Why

Two independent transformations currently prevent Kafka ConfigProviders from working in realtime table configs:

  1. Pinot interprets a value such as ${file:/vault/secrets/kafka.properties:keystore.password} as its own ${name:default} expression while reading the table config. Kafka never receives the reference.
  2. Pinot filters Kafka client properties using ConsumerConfig.configNames() or AdminClientConfig.configNames(). Kafka's dynamic config.providers.* namespace is not part of those static name sets, so the provider configuration is removed before client construction.

This change preserves a ${provider:...} reference only when that provider alias is declared by config.providers in the containing config object. Normal Pinot environment and system-property substitution remains unchanged, including across separate stream config maps.

The Kafka client filter continues to reject unrelated Pinot stream settings and admits only the target client's known properties plus Kafka's reserved config-provider namespace. The shared AdminClient path now applies the same filtering.

Provider values are resolved when a Kafka client is constructed. Recreating a consumer therefore rereads the mounted provider file; hot reloading an already-running Kafka client is outside this change.

Kafka client properties should use their unprefixed names in streamConfigs, for example ssl.keystore.password. The stream.kafka.consumer.prop.* prefix is not a generic Kafka property namespace.

Testing

  • ./mvnw -pl pinot-spi test (787 tests passed)
  • ./mvnw -pl pinot-spi -Dtest=ConfigUtilsTest test (5 tests passed)
  • ./mvnw -pl pinot-plugins/pinot-stream-ingestion/pinot-kafka-3.0,pinot-plugins/pinot-stream-ingestion/pinot-kafka-4.0 -am -Dtest=KafkaPartitionLevelConnectionHandlerTest -Dsurefire.failIfNoSpecifiedTests=false test (3 tests passed for each connector)
  • ./mvnw -pl pinot-spi,pinot-plugins/pinot-stream-ingestion/pinot-kafka-3.0,pinot-plugins/pinot-stream-ingestion/pinot-kafka-4.0 spotless:check checkstyle:check license:check

The connector regression creates a temporary FileConfigProvider file, carries the unresolved password reference through Pinot table-config resolution and Kafka property filtering, forwards allowed.paths, verifies the resolved password, and constructs both a KafkaConsumer and the shared AdminClient. It runs against Kafka 3.9.2 and Kafka 4.2.0.

User documentation lives in pinot-contrib/pinot-docs and should be updated through its separate documentation workflow after this source change.

Addresses #19184

@codecov-commenter

codecov-commenter commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.44262% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.95%. Comparing base (3ffb614) to head (a4bf3c5).
⚠️ Report is 23 commits behind head on master.

Files with missing lines Patch % Lines
...he/pinot/plugin/stream/kafka/KafkaConfigUtils.java 91.89% 0 Missing and 3 partials ⚠️
.../java/org/apache/pinot/spi/config/ConfigUtils.java 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19202      +/-   ##
============================================
+ Coverage     66.63%   66.95%   +0.32%     
  Complexity     1423     1423              
============================================
  Files          3443     3453      +10     
  Lines        218663   218582      -81     
  Branches      34801    34752      -49     
============================================
+ Hits         145705   146355     +650     
+ Misses        61230    60547     -683     
+ Partials      11728    11680      -48     
Flag Coverage Δ
integration 100.00% <ø> (ø)
integration1 100.00% <ø> (ø)
integration2 0.00% <ø> (ø)
java-25 66.95% <93.44%> (+0.32%) ⬆️
lane-a 100.00% <ø> (ø)
lane-b 0.00% <ø> (ø)
temurin 66.95% <93.44%> (+0.32%) ⬆️
unittests 66.95% <93.44%> (+0.32%) ⬆️
unittests1 57.69% <50.00%> (+0.39%) ⬆️
unittests2 39.04% <93.44%> (+0.14%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Jackie-Jiang Jackie-Jiang added kafka Related to Kafka stream connector ingestion Related to data ingestion pipeline feature New functionality real-time Related to realtime table ingestion and serving labels Aug 10, 2026
@Jackie-Jiang

Copy link
Copy Markdown
Contributor

@xiangfu0 xiangfu0 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up — the diagnosis is right and the fix is in the right places.

I verified the core mechanism locally against kafka-clients 3.9.2: filtered properties carrying config.providers, config.providers.file.class and config.providers.file.param.allowed.paths do resolve ${file:/tmp/x:keystore.password} to the real value inside ConsumerConfig. AbstractConfig.CONFIG_PROVIDERS_CONFIG is public in both 3.9.2 and 4.2.0, so the constant reference is safe on both connectors.

Three points I'd like resolved before merge (inline), then two things that fall outside the diff:

KafkaSSLUtils.initSSL is incompatible with provider references. KafkaSSLUtils.java:98 reads ssl.keystore.password raw from _consumerProp, before any Kafka client is constructed. If the auto-keystore-generation feature is combined with ${file:...} passwords, the keystore gets written with the literal reference as its password while Kafka reads it back with the resolved one. It's opt-in (stream.kafka.ssl.server.certificate), so a collision is unlikely — but it should be documented or guarded rather than left to be discovered.

Rotation doesn't reach the shared AdminClient. KafkaAdminClientManager.createCacheKey (KafkaAdminClientManager.java:84-99) keys on bootstrap servers plus SSL locations, not passwords. A rotated secret won't take effect for the admin client until the refcount drops to zero. The "recreate the consumer and it rereads the file" story in the description is consumer-only; worth stating that limitation explicitly.


One correction worth folding in: the config example in #19184 uses stream.kafka.consumer.prop.ssl.keystore.password. You already flagged in the description that this prefix isn't a generic Kafka namespace, and that's correct — KafkaStreamConfigProperties.KAFKA_CONSUMER_PROP_PREFIX is declared at line 54 and referenced nowhere in the repo, and buildProperties does a raw putAll(streamConfig.getStreamConfigsMap()) with no stripping. Since the issue's acceptance criteria are written against the prefixed form, it's worth correcting there so the reporter's config actually works.

Acceptance criterion 4 (docs) is also still open. Given the allowed.paths point below, I'd treat the pinot-contrib/pinot-docs PR as part of shipping this rather than a follow-up.

}

private static final Map<String, String> ENVIRONMENT_VARIABLES = System.getenv();
private static final String CONFIG_PROVIDERS = "config.providers";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kafka-specific knowledge in pinot-spi's generic resolver.

ConfigUtils is Pinot's universal ${...} substitution for every BaseJsonConfig — it runs on every table-config read (ZKMetadataProvider:588) and every create/update validation (TableConfigValidationUtils:88). Hardcoding "config.providers" here means whether ${x:y} gets substituted now depends on the presence of a sibling key in the same JSON object. That's action at a distance, in a very generic and very hot path. Pulsar and Kinesis will hit the same ${...} collision eventually, with no equivalent signal to key off.

A generic escape — $${...} resolving to a literal ${...} — would keep pinot-spi connector-agnostic and put the intent at the point of use.

To be fair to the current design: it needs no syntax change from operators, and it keys off exactly the same signal Kafka itself uses to decide what is a provider reference. That's a real advantage for the rotation workflow in #19184. So I read this as a trade-off rather than a defect — but it's a pinot-spi API-shaped decision, and I'd rather a committer make that call explicitly than have it arrive as a side effect of a Kafka fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xiangfu0 Agreed. I replaced the provider-aware traversal in ConfigUtils with a connector-neutral escape. A whole value beginning with $${...} becomes the literal ${...} for downstream processing, so ConfigUtils no longer knows about Kafka or config.providers. Normal ${name:default} behavior is unchanged.

This is an upgrade-gated feature: all controllers and servers must be upgraded before a provider-backed table config is applied. Older Pinot versions cannot consume either form correctly, so the companion docs state that requirement explicitly.

// Kafka ConfigProvider references share Pinot's ${name:value} syntax. Keep references for providers declared
// in the containing config object so Kafka can resolve them when constructing the client.
if (field.startsWith("${") && field.endsWith("}")
&& !isConfigProviderReference(field, inheritedConfigProviders)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The silent-corruption path survives when config.providers is omitted.

With the declaration missing, ${file:/vault/secrets/kafka.properties:keystore.password} still falls through to split(":", 2) → key file, default /vault/secrets/kafka.properties:keystore.password. The password silently becomes that literal path string, and it surfaces later as an SSL handshake error pointing nowhere near the real cause.

That's the original bug's worst symptom, and it's the likeliest misconfiguration once this is documented. Worth failing loudly: when a value has three or more colon-separated segments (${alias:path:key}) and alias isn't a declared provider, that's far more likely a provider reference with a missing declaration than an env var with a colon in its default. A warn or a throw here would save a lot of support time.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in the shared Kafka helper. After Pinot unescapes $${...}, client property preparation validates every Kafka provider reference and throws ConfigException if the alias is not listed in config.providers or its class is missing. I added negative tests for both cases, so an escaped but incomplete provider setup fails during client creation instead of becoming a path-like password.

for (String key : props.stringPropertyNames()) {
if (validConfigNames.contains(key)) {
if (validConfigNames.contains(key) || key.equals(AbstractConfig.CONFIG_PROVIDERS_CONFIG)
|| key.startsWith(AbstractConfig.CONFIG_PROVIDERS_CONFIG + ".")) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This opens a new arbitrary-file-read primitive reachable from table config.

FileConfigProvider defaults to unrestricted paths. After this change, anyone who can write a table config can read any file readable by the server or controller process into a Kafka config value — a service-account token into client.id, say, which then goes over the wire to a broker they control.

Arbitrary class instantiation from table config already exists today (key.deserializer, SASL login callback handlers), so that half isn't new. File-contents-to-config-value exfiltration is. #19184 carries the security label, so I think this needs to be explicit rather than implicit.

Minimum bar: the docs lead with config.providers.file.param.allowed.paths as required, not optional. The test already sets it, which is the right instinct. For multi-tenant clusters a cluster-level opt-in is worth discussing separately.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xiangfu0 Addressed in code and documentation. A referenced FileConfigProvider now requires a non-empty config.providers.<alias>.param.allowed.paths. The companion docs also require a narrow path, the JVM provider-class allowlist, consistent mounts on controllers and servers, and trusted table-config writers.

I kept cluster-controlled root policy separate because table-level allowed.paths is useful defense in depth but is not a hard multi-tenant boundary. Docs compare: https://github.com/pinot-contrib/pinot-docs/compare/latest...goutamadwant:pinot-docs:support-kafka-config-providers-docs?expand=1

/// Filter properties to only include the specified Kafka configurations.
/// This prevents "was supplied but isn't a known config" warnings from Kafka clients.
/// Filter properties to include the specified Kafka configurations and the dynamic config-provider namespace.
/// This prevents "was supplied but isn't a known config" warnings without dropping config-provider settings.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small accuracy point on the javadoc: I confirmed the config.providers* keys remain in AbstractConfig.originals() after resolution, so Kafka will still log them as "supplied but isn't a known config".

That's unavoidable — Kafka needs them in originals to do the resolution at all — but "without dropping config-provider settings" now slightly oversells it. Worth a sentence noting the provider keys themselves will still be reported as unknown.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. The shared helper documentation now says that the provider namespace is retained and explicitly notes that Kafka may still report the provider keys themselves as unknown after using them for resolution.

if (ref == null) {
ref = KafkaAdminClientManager.getInstance().getOrCreateAdminClient(_consumerProp);
ref = KafkaAdminClientManager.getInstance()
.getOrCreateAdminClient(filterKafkaProperties(_consumerProp, ADMIN_CLIENT_CONFIG_NAMES));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch folding this in — it makes the shared path consistent with createAdminClient() above.

Worth confirming in the description that this doesn't change admin-client sharing: createCacheKey reads bootstrap.servers, security.protocol, sasl.mechanism, sasl.jaas.config, ssl.keystore.location and ssl.truststore.location, and all six are in AdminClientConfig.configNames(), so filtering leaves the cache key identical. I checked and it does hold — just call it out, since silently re-keying a shared client cache would be a nasty regression.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xiangfu0 confirmed and also called this out in the PR description. The shared path filters through AdminClientConfig.configNames() plus the provider namespace. All six cache-key fields remain included, so cache identity is unchanged.

I also documented the separate rotation limitation: an existing shared AdminClient keeps its current credentials until its references drop to zero and the client is recreated.

for (String key : props.stringPropertyNames()) {
if (validConfigNames.contains(key)) {
if (validConfigNames.contains(key) || key.equals(AbstractConfig.CONFIG_PROVIDERS_CONFIG)
|| key.startsWith(AbstractConfig.CONFIG_PROVIDERS_CONFIG + ".")) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filterKafkaProperties is now byte-identical across pinot-kafka-3.0 and pinot-kafka-4.0, and so is the ~65-line test. Nothing in it is version-specific — it could live in pinot-kafka-base alongside KafkaAdminClientManager and KafkaSSLUtils.

Pre-existing pattern, so I won't block on it, but this PR doubles the surface that has to stay in sync.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. Filtering and validation now live in pinot-kafka-base as KafkaConfigUtils, and the shared resolution setup lives in KafkaConfigProviderTestUtils. The Kafka 3.x and 4.x tests now contain only the small version-specific calls.

}

@Test
public void testConfigProviderReferencesAreScopedToDeclaringObject() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name promises descendant scoping, but this case actually verifies sibling isolation — two entries in streamConfigMaps, where the second never inherits from the first.

The implementation inherits the provider set downward into all descendants of the declaring object, which is a different property. Either rename to match what's asserted, or add a case that pins the descendant behavior (a provider declared at an outer level reaching a nested object).

Also missing: a negative case where a provider-shaped reference appears with no config.providers declared. That's the silent-corruption path I flagged in ConfigUtils, and it's the one most likely to bite operators.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provider-scoping design is gone with the Kafka-aware traversal. ConfigUtils tests now cover generic escaped and unescaped substitution directly. Kafka-side negative coverage now includes an undeclared alias, a missing provider class, and FileConfigProvider without allowed.paths.

assertEquals(new AdminClientConfig(adminProperties).getPassword(SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG).value(),
"test-password");
try (KafkaAdminClientManager.AdminClientReference adminClientReference =
KafkaAdminClientManager.getInstance().getOrCreateAdminClient(adminProperties)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This constructs a real AdminClient inside the process-wide KafkaAdminClientManager singleton, which spawns connection threads against localhost:9092 and leaves state in a static that other tests share.

The assertion above it — new AdminClientConfig(adminProperties).getPassword(...) — already proves the thing under test, which is that the provider reference survives filtering and resolves. Dropping the live client would give the same coverage without the singleton mutation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the live KafkaConsumer and AdminClient construction from this regression. It now verifies provider resolution through ConsumerConfig and AdminClientConfig only, so it does not mutate the process-wide singleton or create connection threads.

@xiangfu0 xiangfu0 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at f59403d. All eight of my earlier comments are addressed, and the ConfigUtils rework is better than what I asked for — a five-line connector-neutral escape with zero Kafka awareness in pinot-spi is exactly the right shape.

I verified independently:

  • GET returns the raw stored template (replaceVariables=false, PinotTableRestletResource:585), so a GET → edit → PUT round-trip doesn't destroy the escape. validateEnvironmentVariables also discards its resolved copy, so nothing unescaped is persisted back to ZK.
  • FileConfigProvider.ALLOWED_PATHS_CONFIG and ConfigTransformer.DEFAULT_PATTERN both exist in 3.9.2 and 4.2.0, and kafka-clients is provided scope in pinot-kafka-base — so the shared helper is binary-safe against both connectors.
  • CI is green, 12/12.

Two things I'd still like resolved, both inline. The first one matters because it defeats the fail-loudly behavior this revision added.

One doc note as well: the description frames this as upgrade-gated, which is right. Worth also saying what a rollback does — on an older Pinot, $${file:...} is left untouched and config.providers is stripped, so Kafka never resolves the reference and the client receives the literal string. That's a handshake failure rather than a plausible-looking wrong password, which is the good failure mode, but operators planning a rollback should be told.

case STRING:
final String field = jsonNode.asText();
if (field.startsWith("$${") && field.endsWith("}")) {
return JsonNodeFactory.instance.textNode(field.substring(1));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right shape — connector-neutral, five lines, and pinot-spi no longer knows Kafka exists. It also gives every downstream system a way to pass a literal ${...} through, which is generally useful beyond this feature.

One small note for the docs: the escape isn't composable. Expressing a literal $${x} would need $$${x}, which matches neither branch and passes through unchanged. Fine in practice, just worth a sentence so nobody discovers it the hard way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented in the companion docs branch at 63ca1f2e97. The escape is now explicitly described as whole-value only and non-recursive/non-composable. Embedded provider-shaped substrings do not have a literal escape.

retryPolicy.attempt(() -> {
try {
consumer.set(new KafkaConsumer<>(filterKafkaProperties(consumerProp, CONSUMER_CONFIG_NAMES)));
consumer.set(new KafkaConsumer<>(KafkaConfigUtils.filterAndValidateKafkaProperties(consumerProp,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The retry wrapper swallows the validation error this revision just added.

ConfigException extends KafkaException extends RuntimeException — I confirmed the hierarchy against 3.9.2. Validation runs inside the retry lambda here, so the catch (Exception e) on line 110 logs it at WARN, returns false, and the method ends up throwing RuntimeException(AttemptsExceededException). The operator sees "attempts exceeded" — never "Kafka ConfigProvider alias 'file' ... is not listed in 'config.providers'".

retry(Supplier, 5) on lines 124 and 129 has the same problem in milder form: it catches KafkaException, so a deterministic config error burns 5 attempts and ~8s of sleeps before surfacing. The message does survive there.

This undercuts the whole point of the new validation. Hoisting filterAndValidateKafkaProperties out of the retry — validate once in buildProperties or the constructor, then retry only the client construction — fixes both paths and gets the operator a clear message immediately.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this. Filtering and validation now run once before entering the retry logic for Kafka 3.x and 4.x consumer creation, direct AdminClient creation, and shared AdminClient initialization. Retries now cover client construction only, so deterministic ConfigException failures surface immediately with their original message.

Set<String> configuredProviders = getConfiguredProviders(properties);
for (String key : properties.stringPropertyNames()) {
Matcher matcher = ConfigTransformer.DEFAULT_PATTERN.matcher(properties.getProperty(key));
while (matcher.find()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth a line in the description: this is stricter than Kafka itself. Kafka leaves an unresolvable reference untouched; here any ${a:b}-shaped substring with an undeclared alias throws.

A whole-value reference can't reach this point — ConfigUtils would already have consumed it — so this only affects embedded ones, e.g. a literal ${x:y} inside sasl.jaas.config. Those previously passed through to Kafka untouched and now fail the table, and the new $${...} escape can't express them because it only matches whole values.

Narrow, and I think failing loudly is the right default here. But it is a behavior change with no escape hatch, so it should be stated rather than discovered.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented the stricter behavior in the companion docs branch. Embedded ${alias:...} substrings require a declared provider alias and there is no escape for preserving them literally. This behavior now fails explicitly instead of allowing unresolved provider-shaped content to pass through.

return Paths.get(keyStoreLocation);
}

private static void writeKeyStoreAtomically(Path storePath, KeyStore keyStore, String password)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and validatePrivateKeyMatchesPublicKey below are unrelated to config providers — I'd split them into their own PR.

Rejecting provider references under auto-SSL (line 134) is in scope and is what I asked for. These two are not:

  • writeKeyStoreAtomically replaces delete → create → write with temp file + ATOMIC_MOVE. Genuine improvement, and it closes a real torn-file window — but it's a separate bug fix, and it drops the existing FileAlreadyExistsException → warn-and-skip path, which is a behavior change for current auto-SSL users.
  • validatePrivateKeyMatchesPublicKey is ~100 new lines spanning RSA / EC / EdDSA / DSA / RSASSA-PSS with PSS parameter derivation and a signature round-trip, now on the initKeyStore path of every existing auto-SSL user. validateAutoSslMaterial likewise parses certs and keys eagerly and wraps any exception in IllegalArgumentException.

Both look like improvements to me. But neither is exercised by the config-provider feature, both can newly hard-fail a setup that works today (unusual key type, provider-specific algorithm naming, non-default keystore type), and security-sensitive shared code deserves review on its own merits rather than as a rider on a feature PR. Splitting also keeps this PR's blast radius to something a reviewer can reason about.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed this as well. I removed the unrelated atomic store replacement, eager certificate/key parsing, and key-pair proof changes from this PR. The remaining auto-SSL change is limited to ConfigProvider-reference preflight before any renewal, with focused coverage for truststore, keystore, and key passwords and rejection before mutation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New functionality ingestion Related to data ingestion pipeline kafka Related to Kafka stream connector real-time Related to realtime table ingestion and serving

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants