-
Notifications
You must be signed in to change notification settings - Fork 9.2k
HADOOP-18675: Fix CachedSASToken noisy log errors when SAS token has YYYY-MM-DD expiration #5508
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,10 +20,14 @@ | |
|
|
||
| import java.io.UnsupportedEncodingException; | ||
| import java.net.URLDecoder; | ||
| import java.time.LocalDateTime; | ||
| import java.time.OffsetDateTime; | ||
| import java.time.ZoneOffset; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.time.format.DateTimeFormatterBuilder; | ||
| import java.time.format.DateTimeParseException; | ||
| import java.time.temporal.ChronoField; | ||
| import java.time.temporal.TemporalAccessor; | ||
|
|
||
| import org.apache.hadoop.classification.VisibleForTesting; | ||
| import org.slf4j.Logger; | ||
|
|
@@ -39,6 +43,13 @@ | |
| */ | ||
| public final class CachedSASToken { | ||
| public static final Logger LOG = LoggerFactory.getLogger(CachedSASToken.class); | ||
|
|
||
| private static final DateTimeFormatter ISO_DATE_MIDNIGHT = new DateTimeFormatterBuilder() | ||
| .append(DateTimeFormatter.ISO_DATE) | ||
| .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) | ||
| .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) | ||
| .toFormatter(); | ||
|
|
||
| private final long minExpirationInSeconds; | ||
| private String sasToken; | ||
| private OffsetDateTime sasExpiry; | ||
|
|
@@ -114,8 +125,20 @@ private static OffsetDateTime getExpiry(String token) { | |
| OffsetDateTime seDate = OffsetDateTime.MIN; | ||
| try { | ||
| seDate = OffsetDateTime.parse(seValue, DateTimeFormatter.ISO_DATE_TIME); | ||
| } catch (DateTimeParseException ex) { | ||
| LOG.error("Error parsing se query parameter ({}) from SAS.", seValue, ex); | ||
| } catch (DateTimeParseException dateTimeException) { | ||
| try { | ||
| TemporalAccessor dt = ISO_DATE_MIDNIGHT.parseBest(seValue, OffsetDateTime::from, LocalDateTime::from); | ||
| if (dt instanceof OffsetDateTime) { | ||
| seDate = (OffsetDateTime) dt; | ||
| } else if (dt instanceof LocalDateTime) { | ||
| seDate = ((LocalDateTime) dt).atOffset(ZoneOffset.UTC); | ||
| } else { | ||
| throw dateTimeException; | ||
| } | ||
| } catch (DateTimeParseException dateOnlyException) { | ||
| // log original exception | ||
| LOG.error("Error parsing se query parameter ({}) from SAS as ISO_DATE_TIME or ISO_DATE.", seValue, dateTimeException); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now, if there's a problem here it is still going to log a lot, isn't it? might be best to use a LogExactlyOnce log here for the error line, and log at debug the rest of the time |
||
| } | ||
| } | ||
|
|
||
| String signedKeyExpiry = "ske="; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,6 +78,31 @@ public void testUpdateAndGet() throws IOException { | |
| Assert.assertTrue(token3 == cachedToken); | ||
| } | ||
|
|
||
| @Test | ||
| public void testValidExpirationParsing() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a test for an invalid date time parsing now?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's an existing one testing invalid |
||
| CachedSASToken cachedSasToken = new CachedSASToken(); | ||
| String[] values = { | ||
| "2123-03-24T00:06:46Z", // sample timestamp from azure portal | ||
| "2124-03-30", // sample YYYY-MM-DD date format generated from az cli | ||
| "2125-03-30Z", // sample YYYY-MM-DD[offset] date format | ||
| }; | ||
|
|
||
| for (String se : values) { | ||
| cachedSasToken.setForTesting(null, null); | ||
| String token = "se=" + se; | ||
|
|
||
| // set first time and ensure reference equality | ||
| cachedSasToken.update(token); | ||
| String cachedToken = cachedSasToken.get(); | ||
| Assert.assertTrue(token == cachedToken); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. assertEquals(expected, actual) or AssertJ.asserThat(). thanks
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was following the existing tests which seem to want to assert reference equality a lot, I will change it for the new test.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, old code...just because it was done that way doesn't mean we should carry on |
||
|
|
||
| // update with same token and ensure reference equality | ||
| cachedSasToken.update(token); | ||
| cachedToken = cachedSasToken.get(); | ||
| Assert.assertTrue(token == cachedToken); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetExpiration() throws IOException { | ||
| CachedSASToken cachedSasToken = new CachedSASToken(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have something like:
private static final DateTimeFormatter[] formatters;And from this method we will just call
seDate = getParsedDateTime(seValue);This way I feel we can prevent nested code.