Skip to content

add refresh credentials property to loadTableResult #1164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

wolflex888
Copy link
Contributor

This PR will add necessary refresh-vended-credentials properties to LoadTableResponse to allow each IoFile will get appropriate properties to initiate VendedCredentialsProvider.

return responseBuilder.build();
if(accessDelegationModes.contains(AccessDelegationMode.VENDED_CREDENTIALS)){
try {
String hostName = InetAddress.getLocalHost().getCanonicalHostName();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there a better way to grab the server host here? I don't think this is reliable but I do not know a better way to get the host. Potentially, we can get it from the request header? but it looks like the headers are being filtered out.

if (accessDelegationModes.contains(AccessDelegationMode.VENDED_CREDENTIALS)) {
try {
// String hostName = InetAddress.getLocalHost().getCanonicalHostName();
String hostName = "localhost";
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not so simple, I'm afraid :) We need to think of proxies too.

At the very minimum, I think we need to get a UriInfo injected and use uriInfo.getBaseUri().

A more robust approach is here: https://github.com/projectnessie/nessie/blob/a9028f12c5152ebc14ba32731b52fdc9a107db94/servers/quarkus-catalog/src/main/java/org/projectnessie/server/catalog/ExternalBaseUriImpl.java#L38

@wolflex888 wolflex888 marked this pull request as draft March 13, 2025 14:45
@wolflex888 wolflex888 marked this pull request as ready for review March 19, 2025 16:53
@wolflex888 wolflex888 requested a review from dimas-b March 19, 2025 16:53
@wolflex888 wolflex888 requested a review from dimas-b March 24, 2025 16:55
String credentialsEndpoint =
String.format(
"/v1/%s/namespaces/%s/tables/%s/credentials",
catalogName, tableIdentifier.namespace().toString(), tableIdentifier.name());
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this do proper escaping of special chars in the URI path?

Cf. IcebergCatalogAdapter.decodeNamespace()

Map<String, String> vendedCredentialConfig = new HashMap<>();
String credentialsEndpoint =
String.format(
"/v1/%s/namespaces/%s/tables/%s/credentials",
Copy link
Contributor

Choose a reason for hiding this comment

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

IcebergCatalog does not deal with the REST API at all, but this config requires understanding namespace path encoding/decoding.

I believe it is preferable to inject this config in IcebergCatalogAdapter (where the decoding code exists).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved the string construction to IcebergCatalogAdapter where the decoding code exists, but I feel that's a little bit clumsy to pass it all the way down to SupportsCredentialDelegation. I wonder if you have a more elegant way of doing this

@@ -65,6 +66,11 @@ public PolarisCallContext getPolarisCallContext() {
public Map<String, Object> contextVariables() {
return Map.of();
}

@Override
public URI getBaseUri() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change still needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is removed

catalogName, tableIdentifier.namespace().toString(), tableIdentifier.name());
vendedCredentialConfig.put(AwsClientProperties.REFRESH_CREDENTIALS_ENABLED, "true");
vendedCredentialConfig.put(
AwsClientProperties.REFRESH_CREDENTIALS_ENDPOINT, credentialsEndpoint);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this property specific to the AWS client? I did not find any formal definition for it in Iceberg.

I believe it would be preferable to have a local constant for it in Polaris to avoid the impression that it is supposed to work only for AWS.

Given apache/iceberg#11281, why do we have to set this property at all? Why is it not discovered automatically?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That was my question to the iceberg team as well, but they insist on having this property set so iceberg knows which endpoint to reach out to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Regarding the AwsClientProperties, there's also discussion here

@@ -24,6 +24,7 @@
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.net.URI;
Copy link
Contributor

Choose a reason for hiding this comment

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

unnecessary import?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has been removed

@@ -378,6 +378,7 @@ public Response loadTable(
Namespace ns = decodeNamespace(namespace);
TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(table));


Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know why these extra blank lines are added

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this has been removed

Comment on lines +403 to +406
String credentialsEndpoint =
String.format(
"/v1/%s/namespaces/%s/tables/%s/credentials",
prefix, tableIdentifier.namespace().toString(), tableIdentifier.name());
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to be configurable. E.g., some hosts may prefix the URL with /polaris or with /your_account_name or any number of prefixes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

does that mean they will do something like /polaris-catalog-name instead of just /catalog-name? or I'm misunderstanding this?

Copy link
Contributor

Choose a reason for hiding this comment

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

prefix identifies the catalog, does it not? Why would the .../credentials endpoint for a table in one catalog need to be under a different prefix?

@@ -25,6 +25,7 @@
import com.google.common.collect.ImmutableMap;
import jakarta.annotation.Nonnull;
import java.lang.reflect.Method;
import java.net.URI;
Copy link
Contributor

Choose a reason for hiding this comment

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

unnecessary import?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is removed.

@@ -21,6 +21,7 @@
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import jakarta.ws.rs.core.SecurityContext;
import java.net.URI;
Copy link
Contributor

Choose a reason for hiding this comment

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

same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sorry, i forgot to do spotlessApply. these should all be fixed now.

@@ -856,6 +857,15 @@ public Map<String, String> getCredentialConfig(
storageInfo.get());
}

@Override
public Map<String, String> getVendedCredentialConfig(TableIdentifier tableIdentifier, String decodedCredentialsPath) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this a whole new method rather than just putting this directly in the IcebergCatalogHandler. You're just putting values into a map, so...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. I thought it can be more organized that way. I removed it.

ifNoneMatch,
snapshots,
delegationModes,
RESTUtil.decodeString(credentialsEndpoint))
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this class (or class above it in the call chain) should inject credentialsEndpoint in this response. We should not push this parameter down to loadTableWithAccessDelegationIfStale because the lower level class has nothing to do with REST URIs.

Copy link
Contributor Author

@wolflex888 wolflex888 Apr 15, 2025

Choose a reason for hiding this comment

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

I see. does that mean we should be building the response here instead of having a complete response returned from loadTableAccessDelegationIfStale?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I created a function to do the injection at the proper level. Let me know if that's what you have in mind! Thanks for all the feedback! really appreciate it

@wolflex888 wolflex888 requested a review from dimas-b April 15, 2025 16:35
@dimas-b
Copy link
Contributor

dimas-b commented May 22, 2025

@wolflex888 : Are you still working on this? Sorry for the delay with reviews. Could you resolve conflicts, please?

String credentialsEndpoint =
String.format(
"/v1/%s/namespaces/%s/tables/%s/credentials",
prefix, tableIdentifier.namespace().toString(), tableIdentifier.name());
Copy link
Contributor

Choose a reason for hiding this comment

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

(Saw that #1164 (comment) got lost but think it still holds)

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree the uri should be :

  public String tableCredentials(TableIdentifier ident) {
    return SLASH.join(
        "v1",
        prefix,
        "namespaces",
        RESTUtil.encodeNamespace(ident.namespace()),
        "tables",
        RESTUtil.encodeString(ident.name()),
        "credentials");
  }

@jasonf20
Copy link

jasonf20 commented Aug 4, 2025

Hi,

Just following up—any updates on this? It should address apache/polaris#2177.

I understand this is an open-source project, but this is a small yet important change, especially given this projects role as a backend for enterprise products like Snowflake Open Catalog. Any update or timeline would be greatly appreciated.

Thank you.

@flyrain
Copy link
Contributor

flyrain commented Aug 4, 2025

cc @singhpk234

Comment on lines +427 to +429
loadResponseBuilder.addConfig(
AwsClientProperties.REFRESH_CREDENTIALS_ENDPOINT, credentialsEndpoint);
loadResponseBuilder.addConfig(AwsClientProperties.REFRESH_CREDENTIALS_ENABLED, "true");
Copy link
Contributor

Choose a reason for hiding this comment

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

ADLS also now supports creds refresh

String credentialsEndpoint =
String.format(
"/v1/%s/namespaces/%s/tables/%s/credentials",
prefix, tableIdentifier.namespace().toString(), tableIdentifier.name());
Copy link
Contributor

Choose a reason for hiding this comment

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

Agree the uri should be :

  public String tableCredentials(TableIdentifier ident) {
    return SLASH.join(
        "v1",
        prefix,
        "namespaces",
        RESTUtil.encodeNamespace(ident.namespace()),
        "tables",
        RESTUtil.encodeString(ident.name()),
        "credentials");
  }

Comment on lines +423 to +424
LoadTableResponse.Builder loadResponseBuilder =
LoadTableResponse.builder().withTableMetadata(originalResponse.tableMetadata());
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need to update metadata-location too ?

Comment on lines +427 to +429
loadResponseBuilder.addConfig(
AwsClientProperties.REFRESH_CREDENTIALS_ENDPOINT, credentialsEndpoint);
loadResponseBuilder.addConfig(AwsClientProperties.REFRESH_CREDENTIALS_ENABLED, "true");
Copy link
Contributor

Choose a reason for hiding this comment

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

we should check the credential type too and send this only when storage is AWS

@singhpk234
Copy link
Contributor

@wolflex888 would you mind rebasing i can help in review

@dimas-b
Copy link
Contributor

dimas-b commented Aug 12, 2025

@jasonf20 : If you have the capacity to make an alternative PR for this, I think it might be a reasonable path forward. If you do, please see my comments about URI handling I made under this PR.

@jasonf20
Copy link

@dimas-b Please see here: #2341

This PR has gotten a little messy so please leave new comments if I missed anything.

I understand everyone’s busy, but I’m a bit concerned that no one from the Snowflake team or other contributors has taken this on yet. I’ll do my best to assist where I can. I don't have a lot of capacity for this, but long as we keep it simple it should be fine.

@wolflex888
Copy link
Contributor Author

I'm gonna be closing this PR as this has gotten a little bit messy. we can move forward with #2341

@wolflex888 wolflex888 closed this Aug 13, 2025
@github-project-automation github-project-automation bot moved this from PRs In Progress to Done in Basic Kanban Board Aug 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants