Skip to content

Commit f4fde35

Browse files
committed
HADOOP-18889. S3A v2 SDK error translations and troubleshooting docs
Error translation uses reflection to create IOE of the right type. All IOEs at the bottom of an AWS stack chain are regenerated. Troubleshooting doc updated with the new classnames; cutting out stack traces rather than bother recreating them. Pulled the multipart param stuff, which can be addressed in a separate JIRA. Where I also think we should move listUploads() to S3AInternals as it is very internal. But did tweak ITestS3AMultipartUtils while debugging this... leaving that in as better assertions are always good. + checkstyles Change-Id: Iba325cc98a95d11911fe41546f91a912a1d9aca5
1 parent a6f80d3 commit f4fde35

File tree

9 files changed

+242
-835
lines changed

9 files changed

+242
-835
lines changed

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/MultipartUtils.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private MultipartUtils() { }
6161
*
6262
* @param storeContext store context
6363
* @param s3 AmazonS3 client to use.
64-
* @param prefix optional key prefix to narrow search. If null or "/" then whole
64+
* @param prefix optional key prefix to narrow search. If null then whole
6565
* bucket will be searched.
6666
* @param maxKeys maximum batch size to request at a time from S3.
6767
* @return an iterator of matching uploads
@@ -122,17 +122,9 @@ static class ListingIterator implements
122122
this.s3 = s3;
123123
this.requestFactory = storeContext.getRequestFactory();
124124
this.maxKeys = maxKeys;
125+
this.prefix = prefix;
125126
this.invoker = storeContext.getInvoker();
126127
this.auditSpan = storeContext.getActiveAuditSpan();
127-
String p;
128-
if (prefix == null) {
129-
p = "/";
130-
} else if (!prefix.endsWith("/")) {
131-
p = prefix + "/";
132-
} else {
133-
p = prefix;
134-
}
135-
this.prefix = p;
136128

137129
// request the first listing.
138130
requestNextBatch();

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/impl/ErrorTranslation.java

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
package org.apache.hadoop.fs.s3a.impl;
2020

2121
import java.io.IOException;
22-
import java.net.ConnectException;
23-
import java.net.NoRouteToHostException;
24-
import java.net.UnknownHostException;
22+
import java.lang.reflect.Constructor;
2523

2624
import software.amazon.awssdk.awscore.exception.AwsServiceException;
2725

26+
import org.apache.hadoop.fs.PathIOException;
27+
2828
import static org.apache.hadoop.fs.s3a.impl.InternalConstants.SC_404_NOT_FOUND;
2929

3030
/**
@@ -40,7 +40,7 @@
4040
* The existing code las been left in S3AUtils it is to avoid cherry-picking
4141
* problems on backports.
4242
*/
43-
public class ErrorTranslation {
43+
public final class ErrorTranslation {
4444

4545
/**
4646
* Private constructor for utility class.
@@ -87,7 +87,7 @@ public static IOException maybeExtractNetworkException(String path, Throwable th
8787

8888
// look inside
8989
Throwable cause = thrown.getCause();
90-
while(cause != null && cause.getCause() != null) {
90+
while (cause != null && cause.getCause() != null) {
9191
cause = cause.getCause();
9292
}
9393
if (!(cause instanceof IOException)) {
@@ -97,24 +97,41 @@ public static IOException maybeExtractNetworkException(String path, Throwable th
9797
// the cause can be extracted to an IOE.
9898
// rather than just return it, we try to preserve the stack trace
9999
// of the outer exception.
100-
IOException ioe = (IOException) cause;
101-
// now examine any IOE
102-
if (ioe instanceof UnknownHostException) {
103-
return (IOException) new UnknownHostException(thrown + ": " + ioe.getMessage())
104-
.initCause(thrown);
105-
}
106-
if (ioe instanceof NoRouteToHostException) {
107-
return (IOException) new NoRouteToHostException(thrown + ": " + ioe.getMessage())
108-
.initCause(thrown);
109-
}
110-
if (ioe instanceof ConnectException) {
111-
return (IOException) new ConnectException(thrown + ": " + ioe.getMessage())
112-
.initCause(thrown);
113-
}
100+
// as a new instance is created through reflection, the
101+
// class of the returned instance will be that of the innermost,
102+
// unless no suitable constructor is available.
103+
return wrapWithInnerIOE(path, thrown, (IOException) cause);
114104

115-
// currently not attempting to translate any other exception types.
116-
return null;
105+
}
117106

107+
/**
108+
* Given an outer and an inner exception, create a new IOE
109+
* of the inner type, with the outer exception as the cause.
110+
* The message is derived from both.
111+
* This only works if the inner exception has a constructor which
112+
* takes a string; if not a PathIOException is created.
113+
* <p>
114+
* See {@code NetUtils}.
115+
* @param <T> type of inner exception.
116+
* @param path path of the failure.
117+
* @param outer outermost exception.
118+
* @param inner inner exception.
119+
* @return the new exception.
120+
*/
121+
@SuppressWarnings("unchecked")
122+
private static <T extends IOException> IOException wrapWithInnerIOE(
123+
String path,
124+
Throwable outer,
125+
T inner) {
126+
String msg = outer.toString() + ": " + inner.getMessage();
127+
Class<? extends Throwable> clazz = inner.getClass();
128+
try {
129+
Constructor<? extends Throwable> ctor = clazz.getConstructor(String.class);
130+
Throwable t = ctor.newInstance(msg);
131+
return (T) (t.initCause(outer));
132+
} catch (Throwable e) {
133+
return new PathIOException(path, msg, outer);
134+
}
118135
}
119136

120137
/**

hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/assumed_roles.md

Lines changed: 28 additions & 169 deletions
Large diffs are not rendered by default.

hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/third_party_stores.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ The features which may be unavailable include:
3939
* Optional Bucket Probes at startup (`fs.s3a.bucket.probe = 0`).
4040
This is now the default -do not change it.
4141
* List API to use (`fs.s3a.list.version = 1`)
42-
*
42+
4343
## Configuring s3a to connect to a third party store
4444

4545

@@ -337,17 +337,16 @@ this makes renaming and deleting significantly slower.
337337
```xml
338338
<configuration>
339339

340-
341340
<property>
342341
<name>fs.s3a.bucket.gcs-container.access.key</name>
343342
<value>GOOG1EZ....</value>
344343
</property>
345-
344+
346345
<property>
347346
<name>fs.s3a.bucket.gcs-container.secret.key</name>
348347
<value>SECRETS</value>
349348
</property>
350-
349+
351350
<property>
352351
<name>fs.s3a.bucket.gcs-container.endpoint</name>
353352
<value>https://storage.googleapis.com</value>
@@ -372,18 +371,17 @@ this makes renaming and deleting significantly slower.
372371
<name>fs.s3a.bucket.gcs-container.select.enabled</name>
373372
<value>false</value>
374373
</property>
375-
374+
376375
<property>
377376
<name>fs.s3a.bucket.gcs-container.path.style.access</name>
378377
<value>true</value>
379378
</property>
380379

381-
382380
<property>
383381
<name>fs.s3a.bucket.gcs-container.endpoint.region</name>
384382
<value>dummy</value>
385383
</property>
386-
384+
387385
</configuration>
388386
```
389387

0 commit comments

Comments
 (0)