add refresh credentials property to loadTableResult#1164
add refresh credentials property to loadTableResult#1164wolflex888 wants to merge 25 commits intoapache:mainfrom
Conversation
| return responseBuilder.build(); | ||
| if(accessDelegationModes.contains(AccessDelegationMode.VENDED_CREDENTIALS)){ | ||
| try { | ||
| String hostName = InetAddress.getLocalHost().getCanonicalHostName(); |
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
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
| String credentialsEndpoint = | ||
| String.format( | ||
| "/v1/%s/namespaces/%s/tables/%s/credentials", | ||
| catalogName, tableIdentifier.namespace().toString(), tableIdentifier.name()); |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
| } | ||
|
|
||
| @Override | ||
| public URI getBaseUri() { |
There was a problem hiding this comment.
Is this change still needed?
| catalogName, tableIdentifier.namespace().toString(), tableIdentifier.name()); | ||
| vendedCredentialConfig.put(AwsClientProperties.REFRESH_CREDENTIALS_ENABLED, "true"); | ||
| vendedCredentialConfig.put( | ||
| AwsClientProperties.REFRESH_CREDENTIALS_ENDPOINT, credentialsEndpoint); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Regarding the AwsClientProperties, there's also discussion here
| import com.auth0.jwt.JWTVerifier; | ||
| import com.auth0.jwt.algorithms.Algorithm; | ||
| import com.auth0.jwt.interfaces.DecodedJWT; | ||
| import java.net.URI; |
There was a problem hiding this comment.
This has been removed
| Namespace ns = decodeNamespace(namespace); | ||
| TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(table)); | ||
|
|
||
|
|
There was a problem hiding this comment.
I don't know why these extra blank lines are added
There was a problem hiding this comment.
this has been removed
| String credentialsEndpoint = | ||
| String.format( | ||
| "/v1/%s/namespaces/%s/tables/%s/credentials", | ||
| prefix, tableIdentifier.namespace().toString(), tableIdentifier.name()); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
does that mean they will do something like /polaris-catalog-name instead of just /catalog-name? or I'm misunderstanding this?
There was a problem hiding this comment.
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?
| import com.google.common.collect.ImmutableMap; | ||
| import jakarta.annotation.Nonnull; | ||
| import java.lang.reflect.Method; | ||
| import java.net.URI; |
There was a problem hiding this comment.
this is removed.
| import com.google.auth.oauth2.AccessToken; | ||
| import com.google.auth.oauth2.GoogleCredentials; | ||
| import jakarta.ws.rs.core.SecurityContext; | ||
| import java.net.URI; |
There was a problem hiding this comment.
sorry, i forgot to do spotlessApply. these should all be fixed now.
| } | ||
|
|
||
| @Override | ||
| public Map<String, String> getVendedCredentialConfig(TableIdentifier tableIdentifier, String decodedCredentialsPath) { |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
I see. I thought it can be more organized that way. I removed it.
| ifNoneMatch, | ||
| snapshots, | ||
| delegationModes, | ||
| RESTUtil.decodeString(credentialsEndpoint)) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
I see. does that mean we should be building the response here instead of having a complete response returned from loadTableAccessDelegationIfStale?
There was a problem hiding this comment.
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 : 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()); |
There was a problem hiding this comment.
(Saw that #1164 (comment) got lost but think it still holds)
There was a problem hiding this comment.
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");
}
|
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. |
|
cc @singhpk234 |
| loadResponseBuilder.addConfig( | ||
| AwsClientProperties.REFRESH_CREDENTIALS_ENDPOINT, credentialsEndpoint); | ||
| loadResponseBuilder.addConfig(AwsClientProperties.REFRESH_CREDENTIALS_ENABLED, "true"); |
There was a problem hiding this comment.
ADLS also now supports creds refresh
| String credentialsEndpoint = | ||
| String.format( | ||
| "/v1/%s/namespaces/%s/tables/%s/credentials", | ||
| prefix, tableIdentifier.namespace().toString(), tableIdentifier.name()); |
There was a problem hiding this comment.
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");
}
| LoadTableResponse.Builder loadResponseBuilder = | ||
| LoadTableResponse.builder().withTableMetadata(originalResponse.tableMetadata()); |
There was a problem hiding this comment.
do we need to update metadata-location too ?
| loadResponseBuilder.addConfig( | ||
| AwsClientProperties.REFRESH_CREDENTIALS_ENDPOINT, credentialsEndpoint); | ||
| loadResponseBuilder.addConfig(AwsClientProperties.REFRESH_CREDENTIALS_ENABLED, "true"); |
There was a problem hiding this comment.
we should check the credential type too and send this only when storage is AWS
|
@wolflex888 would you mind rebasing i can help in review |
|
@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. |
|
@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. |
|
I'm gonna be closing this PR as this has gotten a little bit messy. we can move forward with #2341 |
This PR will add necessary refresh-vended-credentials properties to
LoadTableResponseto allow each IoFile will get appropriate properties to initiateVendedCredentialsProvider.