haydn-j-evans commented on code in PR #17389:
URL: https://github.com/apache/iceberg/pull/17389#discussion_r4116559330
##########
core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Util.java:
##########
@@ -651,6 +655,121 @@ public static AuthSession fromAccessToken(
return session;
}
+ /**
+ * Creates a session whose token is sourced from a file, such as a
Kubernetes-mounted projected
+ * service account token. The file is periodically re-read ahead of the
current token's
+ * expiration (see {@code refreshBufferMillis}) so that a token rotated in
place is picked up
+ * without restarting the process. No {@link RESTClient} is required since
refreshing never
+ * calls out over the network; it only re-reads the file.
+ */
+ public static AuthSession fromTokenFile(
+ ScheduledExecutorService executor,
+ String tokenPath,
+ long refreshBufferMillis,
+ AuthSession parent) {
+ String token;
+ try {
+ token = readTokenFile(tokenPath);
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to read token file: " +
tokenPath, e);
+ }
+
+ Long expiresAtMillis = OAuth2Util.expiresAtMillis(token);
+ if (null == expiresAtMillis) {
+ expiresAtMillis = System.currentTimeMillis() +
OAuth2Properties.TOKEN_EXPIRES_IN_MS_DEFAULT;
+ }
+
+ AuthSession session =
+ new AuthSession(
+ RESTUtil.merge(parent.headers(), authHeaders(token)),
+ AuthConfig.builder()
+ .from(parent.config())
+ .token(token)
+ .tokenPath(tokenPath)
+ .tokenType(OAuth2Properties.ACCESS_TOKEN_TYPE)
+ .expiresAtMillis(expiresAtMillis)
+ .tokenPathRefreshBufferMillis(refreshBufferMillis)
+ .build());
+
+ if (null != executor) {
+ scheduleFileTokenRefresh(executor, session, expiresAtMillis,
refreshBufferMillis);
+ }
+
+ return session;
+ }
+
+ private static String readTokenFile(String tokenPath) throws IOException {
+ return Files.readString(Path.of(tokenPath)).trim();
+ }
+
+ /**
+ * Re-reads the token from {@link AuthConfig#tokenPath()} and updates this
session's headers and
+ * config accordingly.
+ *
+ * @return the new token's expiration time in epoch millis, or null if the
file could not be
+ * read
+ */
+ Long refreshFromFile() {
+ String tokenPath = config.tokenPath();
+ String token;
+ try {
+ token = readTokenFile(tokenPath);
+ } catch (IOException e) {
+ LOG.warn("Failed to re-read token file {}, will retry", tokenPath, e);
+ return null;
+ }
+
+ Long expiresAtMillis = OAuth2Util.expiresAtMillis(token);
+ if (null == expiresAtMillis) {
+ expiresAtMillis = System.currentTimeMillis() +
OAuth2Properties.TOKEN_EXPIRES_IN_MS_DEFAULT;
+ }
+
+ this.config =
+ AuthConfig.builder()
+ .from(config())
+ .token(token)
+ .tokenType(OAuth2Properties.ACCESS_TOKEN_TYPE)
+ .expiresAtMillis(expiresAtMillis)
+ .build();
+ this.headers = RESTUtil.merge(this.headers, authHeaders(token));
+
+ return expiresAtMillis;
+ }
+
+ /**
+ * Schedule the next file-based token refresh, {@code refreshBufferMillis}
ahead of {@code
+ * expiresAtMillis}. Unlike {@link #scheduleTokenRefresh}, this never
calls out over the
+ * network: on a transient read failure, it retries after a short fixed
delay instead of giving
+ * up.
+ */
+ @SuppressWarnings("FutureReturnValueIgnored")
+ private static void scheduleFileTokenRefresh(
Review Comment:
scheduleFileTokenRefresh now takes a delay directly instead of an expiry,
so a failed read just reschedules after FILE_REFRESH_RETRY_WAIT_MILLIS.
also added a case where the read suceeds but for some reason the kubelet
hasnt rotated the file yet (i.e. node cpu saturation,) - So every reschedule
now goes through fileRefreshDelayMillis, which floors the delay at
FILE_REFRESH_RETRY_WAIT_MILLIS. Worst case is now one cheap local file read
every 5 s until the kubelet rotates the token.
--
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]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]