jerryshao commented on code in PR #13539:
URL: https://github.com/apache/gravitino/pull/13539#discussion_r4130270421
##########
catalogs/catalog-glue/src/main/java/org/apache/gravitino/catalog/glue/GlueCatalog.java:
##########
@@ -91,12 +93,17 @@ public Map<String, String>
propertiesWithCredentialProviders() {
// super() skips addCatalogSpecificCredentialProviders() when
credential-providers is already
// set, so the aws-* → s3-* key mapping never runs. Apply it
unconditionally here so that
// S3SecretKeyProvider.initialize() can read s3-access-key-id regardless
of how the catalog
- // was configured.
+ // was configured. Also ensure aws-secret-key is listed so Glue API keys
remain available via
+ // getCredentials after getSecrets stopped returning them.
String accessKeyId = props.get(GlueConstants.AWS_ACCESS_KEY_ID);
String secretAccessKey = props.get(GlueConstants.AWS_SECRET_ACCESS_KEY);
if (StringUtils.isNotBlank(accessKeyId) &&
StringUtils.isNotBlank(secretAccessKey)) {
props.putIfAbsent(S3Properties.GRAVITINO_S3_ACCESS_KEY_ID, accessKeyId);
props.putIfAbsent(S3Properties.GRAVITINO_S3_SECRET_ACCESS_KEY,
secretAccessKey);
+ ensureCredentialProviderListed(props,
AwsSecretKeyCredential.AWS_SECRET_KEY_CREDENTIAL_TYPE);
+ // Remap creates s3-* keys; register s3-secret-key so getCredentials can
vend them even when
+ // credential-providers was already set (super skips auto-detect).
+ ensureCredentialProviderListed(props,
S3SecretKeyCredential.S3_SECRET_KEY_CREDENTIAL_TYPE);
Review Comment:
[Important] This appends `s3-secret-key` even when the operator set
`credential-providers` explicitly, which is exactly what
`core/.../BaseCatalog.java:538-540` says not to do ("do not auto-append
detected static providers (e.g. s3-secret-key beside s3-token)"). For a Glue
catalog configured the normal way — static `aws-*` keys for the Glue API plus
`credential-providers=s3-token` for data access — the provider list becomes
`s3-token,aws-secret-key,s3-secret-key`, and lines 101-102 have already
remapped the static keys into `s3-access-key-id` / `s3-secret-access-key` so
`S3SecretKeyProvider` can serve them.
The consequence is a widening of what is vended, not just a listing detail:
`CredentialOperationDispatcher.getCatalogCredentialContexts` builds one context
per listed provider and returns every non-null credential
(`CredentialOperationDispatcher.java:69-88`), `S3TokenGenerator:73-74` returns
`null` for a catalog context, so the client receives the long-lived static S3
pair where the operator asked for short-lived STS tokens — and each connector
merges it straight into engine config
(`spark-connector/.../BaseCatalog.java:759`,
`trino-connector/.../CatalogConnectorManager.java:1118`).
Line 103 (`aws-secret-key`) is all this PR's stated goal needs: it is the
Glue API credential, and it is scheme-neutral. Suggest dropping line 106 and
letting `addStorageCredentialProviders` keep owning `s3-secret-key` on the
auto-detect path only; if it really is needed when providers are explicit, gate
it on no other `s3-*` provider already being listed.
Verified by: read `GlueCatalog.propertiesWithCredentialProviders` and
`addCatalogSpecificCredentialProviders` in full at this head; traced
`ensureCredentialProviderListed` (`core/.../BaseCatalog.java:574-587`), the
early return it works around (`:536-541`), catalog-level fan-out in
`CredentialOperationDispatcher.java:69-113`, and `S3TokenGenerator.java:73`
returning null without a path context. `TestGlueCatalogCredentials.java:73-101`
pins this behaviour but with `custom-provider`, never a real s3 provider.
##########
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/catalog/BaseCatalog.java:
##########
@@ -725,7 +732,43 @@ protected Table loadSparkTable(Identifier ident) {
private static Map<String, String> propsWithSecrets(Catalog catalog) {
Map<String, String> props =
new HashMap<>(catalog.properties() == null ? Collections.emptyMap() :
catalog.properties());
- props.putAll(catalog.supportsSecrets().getSecrets());
+ try {
+ Map<String, String> secrets = catalog.supportsSecrets().getSecrets();
+ if (secrets != null) {
+ props.putAll(secrets);
+ }
+ } catch (UnsupportedOperationException | NotFoundException e) {
+ // Stubs may not implement SupportsSecrets; older servers lack /secrets.
+ LOG.debug("Skipping getSecrets while resolving Spark catalog properties:
{}", e.toString());
+ } catch (RESTException e) {
+ LOG.warn(
+ "Failed to resolve getSecrets while building Spark catalog
properties; continuing with"
+ + " masked properties: {}",
+ e.toString());
+ }
+ try {
+ Credential[] credentials =
catalog.supportsCredentials().getCredentials();
+ if (credentials != null) {
+ for (Credential credential : credentials) {
+ // Skip expiring credentials: Spark catalog properties are fixed at
initialize time.
+ if (credential == null
+ || credential.expireTimeInMs() != 0
+ || credential.credentialInfo() == null) {
+ continue;
+ }
+ props.putAll(credential.credentialInfo());
+ }
+ }
Review Comment:
[Nit] This loop — null check, `expireTimeInMs() != 0` skip,
`putAll(credentialInfo())`, plus the same three-branch catch — is byte-for-byte
the same at four sites in this PR: here,
`flink-connector/flink-common/src/main/java/org/apache/gravitino/flink/connector/utils/PropertyUtils.java:101-130`,
`trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java:1110-1140`,
and
`iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/provider/DynamicIcebergConfigProvider.java:151-179`.
Any later change to the filtering policy (per the Question on
`core/.../BaseCatalog.java`) has to land in all four. A small shared helper —
`staticCredentialInfo(SupportsCredentials)` returning a merged map — would
collapse them and give the policy one test target instead of four.
Verified by: read all four sites at this head and diffed them by eye; the
only differences are the logger name and the log message wording.
##########
trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java:
##########
@@ -1058,33 +1061,86 @@ static Map<String, String> visibleProps(Catalog
catalog) {
}
/**
- * Overlays the secrets the Gravitino server vends for this catalog onto its
properties.
+ * Overlays secrets and credential info the Gravitino server vends for this
catalog onto its
+ * properties.
*
* <p>Resolved here, on the node that is about to build the connector,
rather than once at
* registration time: the registered definition travels through a CREATE
CATALOG statement that
* Trino persists as a catalog properties file, and a secret placed in it
would be readable there
- * for as long as the catalog exists.
+ * for as long as the catalog exists. Cloud/JDBC credential fields come from
{@code
+ * getCredentials()}; other secrets come from {@code getSecrets()}.
*/
private GravitinoCatalog withResolvedSecrets(
GravitinoCatalog catalog, GravitinoMetalake metalake) {
- Map<String, String> secrets;
+ Catalog loaded;
try {
- secrets =
metalake.loadCatalog(catalog.getName()).supportsSecrets().getSecrets();
+ loaded = metalake.loadCatalog(catalog.getName());
} catch (Exception e) {
- // Named explicitly: the caller's message only says the connector could
not be created, and
- // this step is the one that needs the Gravitino server reachable from
this node.
throw new TrinoException(
GravitinoErrorCode.GRAVITINO_OPERATION_FAILED,
String.format(
"Failed to resolve the secrets of catalog %s in metalake %s: %s",
catalog.getName(), catalog.getMetalake(), toErrorMessage(e)),
e);
}
- if (secrets.isEmpty()) {
+ Map<String, String> properties = new HashMap<>(catalog.getProperties());
+ try {
+ Map<String, String> secrets = loaded.supportsSecrets().getSecrets();
+ if (secrets != null && !secrets.isEmpty()) {
+ properties.putAll(secrets);
+ }
+ } catch (UnsupportedOperationException | NotFoundException e) {
+ // Catalog may not support secrets, or older servers lack /secrets.
+ LOG.debug(
+ "Skipping getSecrets for catalog %s in metalake %s: %s",
+ catalog.getName(), catalog.getMetalake(), e.toString());
+ } catch (RESTException e) {
+ LOG.warn(
+ "Failed to resolve getSecrets for catalog %s in metalake %s;
continuing with masked"
+ + " properties: %s",
+ catalog.getName(), catalog.getMetalake(), e.toString());
Review Comment:
[Important] Swallowing `RESTException` from `getSecrets()` turns a fail-fast
path into a silently broken, cached connector. Before this change any failure
here became a `TrinoException` naming this step; now a transport failure or 5xx
only logs a WARN, and control falls through to
`createCatalogConnectorContextBuilder` /
`catalogConnectors.put(fullCatalogName, connectorContext)` at lines 997-1002.
The connector is therefore built with the masked values from
`catalog.getProperties()` — `jdbc-password=******` — and cached. Since
registration *succeeded*, the refresh loop will not rebuild it
(`testUnchangedCatalogIsNotReRegistered`), so one transient blip leaves that
Trino catalog failing every query with an authentication error until the
catalog definition changes or Trino restarts.
The back-compat case this is meant to cover is already handled by the
`NotFoundException` branch on line 1092. Suggest either rethrowing
`RESTException` for `getSecrets` (keeping the tolerant behaviour for
`getCredentials` at line 1136, where the endpoint genuinely may not exist), or,
if it must stay tolerant, not caching a context that was built from incomplete
properties.
Verified by: read `withResolvedSecrets` and `createCatalogConnectorContext`
in full at this head (the `catalogConnectors.put` on line 1002 is what makes it
sticky); `properties` starts from the masked `catalog.getProperties()` on line
1086. `LOG` is `io.airlift.log.Logger` (line 24/69), so the `%s` placeholders
are right. The new `testConnectorContextToleratesMissingCredentialsEndpoint`
asserts the build proceeds but not what the cached connector then contains.
##########
core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java:
##########
@@ -535,6 +535,9 @@ public Map<String, String>
propertiesWithCredentialProviders() {
props = Maps.newHashMap(secretManager.toPlaintextProperties(props));
}
if
(StringUtils.isNotBlank(props.get(CredentialConstants.CREDENTIAL_PROVIDERS))) {
+ // Explicit credential-providers wins: do not auto-append detected
static providers (e.g.
+ // s3-secret-key beside s3-token), which breaks path-based credential
selection. Catalogs that
+ // must keep jdbc/aws/dlf listed call ensureCredentialProviderListed in
their overrides.
Review Comment:
[Question] This policy means storage pairs are never listed once
`credential-providers` is set: the four catalogs that need it re-list `jdbc` /
`aws` / `dlf` in their own overrides, but nothing re-lists `s3-secret-key`,
`oss-secret-key`, `cos-secret-key` or `azure-account-key`. So for a catalog
carrying, say, `credential-providers=s3-token` plus a static S3 pair,
`getCredentials()` never returns that pair.
That is harmless in this PR, because the pair is still in `getSecrets()` —
`SecretPropertyUtils` is untouched here. But the stacked PR's job is to stop
`getSecrets()` returning these keys, and at that point the recovery path this
PR is supposed to establish does not exist for that configuration: the client
gets `******` and no credential. Is the intent that the stacked PR keeps
storage keys in `getSecrets()` whenever providers are explicit, or that such
catalogs are expected to drop `s3-token`? Worth writing down here, since this
comment is the only record of the decision.
Verified by: read `propertiesWithCredentialProviders` and
`addStorageCredentialProviders` in full at this head; confirmed the only
`ensureCredentialProviderListed` callers are `JdbcCatalog.java:168`,
`IcebergCatalog.java:123`, `PaimonCatalog.java:107,113` and
`GlueCatalog.java:103,106` (none for oss/cos/azure), and confirmed
`core/.../secret/SecretPropertyUtils.java` is not in this diff.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]