From cbce558740c661968eb610ad8d452607389ef8f3 Mon Sep 17 00:00:00 2001 From: Steve Loughran Date: Mon, 25 Jan 2021 14:32:38 +0000 Subject: [PATCH 1/4] HADOOP-15710. ABFS checkException to map 403 to AccessDeniedException. This maps both 401 and 403 to AccessDeniedException. The test does not work, because even though I'm removing permissions from the file and parent dir, it's still opening. Change-Id: Ieb65eb6bb057085b312fb1582eaf65c55ef2e634 --- .../fs/azurebfs/AzureBlobFileSystem.java | 20 +++++++++++------- .../ITestAbfsRestOperationException.java | 21 ++++++++++++++++++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystem.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystem.java index 75522462bb0e7..9c4795c67a581 100644 --- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystem.java +++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystem.java @@ -26,6 +26,7 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.AccessDeniedException; import java.util.Hashtable; import java.util.List; import java.util.ArrayList; @@ -1125,16 +1126,21 @@ private void checkException(final Path path, if (ArrayUtils.contains(allowedErrorCodesList, ere.getErrorCode())) { return; } - int statusCode = ere.getStatusCode(); - //AbfsRestOperationException.getMessage() contains full error info including path/uri. - if (statusCode == HttpURLConnection.HTTP_NOT_FOUND) { - throw (IOException) new FileNotFoundException(ere.getMessage()) + String message = ere.getMessage(); + + switch (ere.getStatusCode()) { + case HttpURLConnection.HTTP_NOT_FOUND: + throw (IOException) new FileNotFoundException(message) + .initCause(exception); + case HttpURLConnection.HTTP_CONFLICT: + throw (IOException) new FileAlreadyExistsException(message) .initCause(exception); - } else if (statusCode == HttpURLConnection.HTTP_CONFLICT) { - throw (IOException) new FileAlreadyExistsException(ere.getMessage()) + case HttpURLConnection.HTTP_FORBIDDEN: + case HttpURLConnection.HTTP_UNAUTHORIZED: + throw (IOException) new AccessDeniedException(message) .initCause(exception); - } else { + default: throw ere; } } else if (exception instanceof SASTokenProviderException) { diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java index 1d86de7ebeb5d..ab72101c896d2 100644 --- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java +++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java @@ -19,17 +19,21 @@ package org.apache.hadoop.fs.azurebfs; import java.io.IOException; +import java.nio.file.AccessDeniedException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.azurebfs.oauth2.RetryTestTokenProvider; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.fs.contract.ContractTestUtils; +import org.apache.hadoop.fs.permission.FsPermission; import org.junit.Assert; import org.junit.Test; import static org.apache.hadoop.test.LambdaTestUtils.intercept; +import static org.junit.Assume.assumeTrue; /** * Verify the AbfsRestOperationException error message format. @@ -114,4 +118,19 @@ public void testWithDifferentCustomTokenFetchRetry(int numOfRetries) throws Exce + ") done, does not match with fs.azure.custom.token.fetch.retry.count configured (" + numOfRetries + ")", RetryTestTokenProvider.reTryCount == numOfRetries); } -} \ No newline at end of file + + @Test + public void testPermissionDenied() throws Throwable { + final AzureBlobFileSystem fs = getFileSystem(); + assumeTrue(fs.getIsNamespaceEnabled()); + Path dir = new Path("testPermissionDenied"); + Path path = new Path(dir, "file"); + ContractTestUtils.writeTextFile(fs, path, "some text", true); + // no permissions + fs.setPermission(path, new FsPermission((short) 00000)); + fs.setPermission(dir, new FsPermission((short) 00000)); + intercept(AccessDeniedException.class, () -> + fs.delete(path, false)); + intercept(AccessDeniedException.class, () -> + ContractTestUtils.readUTF8(fs, path, -1)); + }} \ No newline at end of file From 135ad9ad3b5c4bb067b005ed09cc77b2d185ca87 Mon Sep 17 00:00:00 2001 From: Steve Loughran Date: Thu, 28 Jan 2021 16:30:17 +0000 Subject: [PATCH 2/4] HADOOP-15710. updated tests, including Mehakmeet's comments. Change-Id: I5fa1a1f5da7f581bd22571f2ce1f1c8e5b896398 --- .../ITestAbfsRestOperationException.java | 43 +++++++++++++------ ...ITestAzureBlobFileSystemDelegationSAS.java | 19 ++++---- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java index ab72101c896d2..f3bec3c979dda 100644 --- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java +++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java @@ -32,13 +32,18 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.AUTHORIZATION_PERMISSION_MISS_MATCH; import static org.apache.hadoop.test.LambdaTestUtils.intercept; import static org.junit.Assume.assumeTrue; /** * Verify the AbfsRestOperationException error message format. * */ -public class ITestAbfsRestOperationException extends AbstractAbfsIntegrationTest{ +public class ITestAbfsRestOperationException extends AbstractAbfsIntegrationTest { + + private static final String E_UNAUTH + = AUTHORIZATION_PERMISSION_MISS_MATCH.getErrorCode(); + public ITestAbfsRestOperationException() throws Exception { super(); } @@ -56,7 +61,9 @@ public void testAbfsRestOperationExceptionFormat() throws IOException { Assert.assertEquals(4, errorFields.length); // Check status message, status code, HTTP Request Type and URL. - Assert.assertEquals("Operation failed: \"The specified path does not exist.\"", errorFields[0].trim()); + Assert.assertEquals( + "Operation failed: \"The specified path does not exist.\"", + errorFields[0].trim()); Assert.assertEquals("404", errorFields[1].trim()); Assert.assertEquals("HEAD", errorFields[2].trim()); Assert.assertTrue(errorFields[3].trim().startsWith("http")); @@ -71,14 +78,16 @@ public void testAbfsRestOperationExceptionFormat() throws IOException { Assert.assertEquals(6, errorFields.length); // Check status message, status code, HTTP Request Type and URL. - Assert.assertEquals("Operation failed: \"The specified path does not exist.\"", errorFields[0].trim()); + Assert.assertEquals( + "Operation failed: \"The specified path does not exist.\"", + errorFields[0].trim()); Assert.assertEquals("404", errorFields[1].trim()); Assert.assertEquals("GET", errorFields[2].trim()); Assert.assertTrue(errorFields[3].trim().startsWith("http")); // Check storage error code and storage error message. Assert.assertEquals("PathNotFound", errorFields[4].trim()); Assert.assertTrue(errorFields[5].contains("RequestId") - && errorFields[5].contains("Time")); + && errorFields[5].contains("Time")); } } @@ -89,38 +98,43 @@ public void testCustomTokenFetchRetryCount() throws Exception { testWithDifferentCustomTokenFetchRetry(5); } - public void testWithDifferentCustomTokenFetchRetry(int numOfRetries) throws Exception { + public void testWithDifferentCustomTokenFetchRetry(int numOfRetries) + throws Exception { AzureBlobFileSystem fs = this.getFileSystem(); Configuration config = new Configuration(this.getRawConfiguration()); String accountName = config.get("fs.azure.abfs.account.name"); // Setup to configure custom token provider config.set("fs.azure.account.auth.type." + accountName, "Custom"); - config.set("fs.azure.account.oauth.provider.type." + accountName, "org.apache.hadoop.fs" - + ".azurebfs.oauth2.RetryTestTokenProvider"); - config.set("fs.azure.custom.token.fetch.retry.count", Integer.toString(numOfRetries)); + config.set("fs.azure.account.oauth.provider.type." + accountName, + "org.apache.hadoop.fs" + + ".azurebfs.oauth2.RetryTestTokenProvider"); + config.set("fs.azure.custom.token.fetch.retry.count", + Integer.toString(numOfRetries)); // Stop filesystem creation as it will lead to calls to store. config.set("fs.azure.createRemoteFileSystemDuringInitialization", "false"); final AzureBlobFileSystem fs1 = (AzureBlobFileSystem) FileSystem.newInstance(fs.getUri(), - config); + config); RetryTestTokenProvider.ResetStatusToFirstTokenFetch(); intercept(Exception.class, - ()-> { + () -> { fs1.getFileStatus(new Path("/")); }); // Number of retries done should be as configured Assert.assertTrue( "Number of token fetch retries (" + RetryTestTokenProvider.reTryCount - + ") done, does not match with fs.azure.custom.token.fetch.retry.count configured (" + numOfRetries + + ") done, does not match with fs.azure.custom.token.fetch.retry.count configured (" + + numOfRetries + ")", RetryTestTokenProvider.reTryCount == numOfRetries); } @Test public void testPermissionDenied() throws Throwable { + describe("Verify that permission denial manifests as failures"); final AzureBlobFileSystem fs = getFileSystem(); assumeTrue(fs.getIsNamespaceEnabled()); Path dir = new Path("testPermissionDenied"); @@ -129,8 +143,9 @@ public void testPermissionDenied() throws Throwable { // no permissions fs.setPermission(path, new FsPermission((short) 00000)); fs.setPermission(dir, new FsPermission((short) 00000)); - intercept(AccessDeniedException.class, () -> + intercept(AccessDeniedException.class, E_UNAUTH, () -> fs.delete(path, false)); - intercept(AccessDeniedException.class, () -> + intercept(AccessDeniedException.class, E_UNAUTH, () -> ContractTestUtils.readUTF8(fs, path, -1)); - }} \ No newline at end of file + } +} diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemDelegationSAS.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemDelegationSAS.java index 0cff518524ee4..50ce257b4a844 100644 --- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemDelegationSAS.java +++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemDelegationSAS.java @@ -20,13 +20,12 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.AccessDeniedException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; -import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException; -import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode; import org.assertj.core.api.Assertions; import org.junit.Assume; import org.junit.Test; @@ -52,6 +51,7 @@ import org.apache.hadoop.security.AccessControlException; import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_SAS_TOKEN_PROVIDER_TYPE; +import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.AUTHORIZATION_PERMISSION_MISS_MATCH; import static org.apache.hadoop.fs.azurebfs.utils.AclTestHelpers.aclEntry; import static org.apache.hadoop.fs.permission.AclEntryScope.ACCESS; import static org.apache.hadoop.fs.permission.AclEntryScope.DEFAULT; @@ -432,15 +432,12 @@ public void testSetPermissionForNonOwner() throws Exception { rootStatus.getOwner()); // Attempt to set permission without being the owner. - try { - fs.setPermission(rootPath, new FsPermission(FsAction.ALL, - FsAction.READ_EXECUTE, FsAction.EXECUTE)); - assertTrue("Set permission should fail because saoid is not the owner.", false); - } catch (AbfsRestOperationException ex) { - // Should fail with permission mismatch - assertEquals(AzureServiceErrorCode.AUTHORIZATION_PERMISSION_MISS_MATCH, - ex.getErrorCode()); - } + intercept(AccessDeniedException.class, + AUTHORIZATION_PERMISSION_MISS_MATCH.getErrorCode(), () -> { + fs.setPermission(rootPath, new FsPermission(FsAction.ALL, + FsAction.READ_EXECUTE, FsAction.EXECUTE)); + return "Set permission should fail because saoid is not the owner."; + }); // Attempt to set permission as the owner. fs.setOwner(rootPath, MockDelegationSASTokenProvider.TEST_OWNER, null); From 6d081a98cecd04cf0f1568d0279416c727215a25 Mon Sep 17 00:00:00 2001 From: Steve Loughran Date: Thu, 28 Jan 2021 16:36:22 +0000 Subject: [PATCH 3/4] HADOOP-15710. Reorder test and add better assertion failure text Change-Id: I55f45d7bdb2861b21a96ae19699da64438921abf --- .../fs/azurebfs/ITestAbfsRestOperationException.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java index f3bec3c979dda..e5cef41f4c8d8 100644 --- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java +++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java @@ -139,13 +139,15 @@ public void testPermissionDenied() throws Throwable { assumeTrue(fs.getIsNamespaceEnabled()); Path dir = new Path("testPermissionDenied"); Path path = new Path(dir, "file"); - ContractTestUtils.writeTextFile(fs, path, "some text", true); + ContractTestUtils.writeTextFile(fs, path, "This should not be readable", true); // no permissions fs.setPermission(path, new FsPermission((short) 00000)); fs.setPermission(dir, new FsPermission((short) 00000)); - intercept(AccessDeniedException.class, E_UNAUTH, () -> - fs.delete(path, false)); intercept(AccessDeniedException.class, E_UNAUTH, () -> ContractTestUtils.readUTF8(fs, path, -1)); + intercept(AccessDeniedException.class, E_UNAUTH, () -> { + boolean d = fs.delete(path, false); + return "Expected delete to fail, but got " + d; + }); } } From ba1fd6a24651d8803e4ea1e075dccd5c43c18345 Mon Sep 17 00:00:00 2001 From: Steve Loughran Date: Fri, 29 Jan 2021 13:27:04 +0000 Subject: [PATCH 4/4] HADOOP-15710. exception translation unit tests Needed to make the translation method static and package scoped Change-Id: I1120bac9d271f224ef117d49ce82fd61e91e8149 --- .../fs/azurebfs/AzureBlobFileSystem.java | 3 +- .../ITestAbfsRestOperationException.java | 60 +++--------- .../fs/azurebfs/TestAbfsErrorTranslation.java | 92 +++++++++++++++++++ 3 files changed, 106 insertions(+), 49 deletions(-) create mode 100644 hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/TestAbfsErrorTranslation.java diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystem.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystem.java index 9c4795c67a581..4d285534b0641 100644 --- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystem.java +++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/AzureBlobFileSystem.java @@ -1117,7 +1117,8 @@ private void checkCheckAccessException(final Path path, * @param allowedErrorCodesList varargs list of error codes. * @throws IOException if the exception error code is not on the allowed list. */ - private void checkException(final Path path, + @VisibleForTesting + static void checkException(final Path path, final AzureBlobFileSystemException exception, final AzureServiceErrorCode... allowedErrorCodesList) throws IOException { if (exception instanceof AbfsRestOperationException) { diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java index e5cef41f4c8d8..1d86de7ebeb5d 100644 --- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java +++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAbfsRestOperationException.java @@ -19,31 +19,22 @@ package org.apache.hadoop.fs.azurebfs; import java.io.IOException; -import java.nio.file.AccessDeniedException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.azurebfs.oauth2.RetryTestTokenProvider; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; -import org.apache.hadoop.fs.contract.ContractTestUtils; -import org.apache.hadoop.fs.permission.FsPermission; import org.junit.Assert; import org.junit.Test; -import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.AUTHORIZATION_PERMISSION_MISS_MATCH; import static org.apache.hadoop.test.LambdaTestUtils.intercept; -import static org.junit.Assume.assumeTrue; /** * Verify the AbfsRestOperationException error message format. * */ -public class ITestAbfsRestOperationException extends AbstractAbfsIntegrationTest { - - private static final String E_UNAUTH - = AUTHORIZATION_PERMISSION_MISS_MATCH.getErrorCode(); - +public class ITestAbfsRestOperationException extends AbstractAbfsIntegrationTest{ public ITestAbfsRestOperationException() throws Exception { super(); } @@ -61,9 +52,7 @@ public void testAbfsRestOperationExceptionFormat() throws IOException { Assert.assertEquals(4, errorFields.length); // Check status message, status code, HTTP Request Type and URL. - Assert.assertEquals( - "Operation failed: \"The specified path does not exist.\"", - errorFields[0].trim()); + Assert.assertEquals("Operation failed: \"The specified path does not exist.\"", errorFields[0].trim()); Assert.assertEquals("404", errorFields[1].trim()); Assert.assertEquals("HEAD", errorFields[2].trim()); Assert.assertTrue(errorFields[3].trim().startsWith("http")); @@ -78,16 +67,14 @@ public void testAbfsRestOperationExceptionFormat() throws IOException { Assert.assertEquals(6, errorFields.length); // Check status message, status code, HTTP Request Type and URL. - Assert.assertEquals( - "Operation failed: \"The specified path does not exist.\"", - errorFields[0].trim()); + Assert.assertEquals("Operation failed: \"The specified path does not exist.\"", errorFields[0].trim()); Assert.assertEquals("404", errorFields[1].trim()); Assert.assertEquals("GET", errorFields[2].trim()); Assert.assertTrue(errorFields[3].trim().startsWith("http")); // Check storage error code and storage error message. Assert.assertEquals("PathNotFound", errorFields[4].trim()); Assert.assertTrue(errorFields[5].contains("RequestId") - && errorFields[5].contains("Time")); + && errorFields[5].contains("Time")); } } @@ -98,56 +85,33 @@ public void testCustomTokenFetchRetryCount() throws Exception { testWithDifferentCustomTokenFetchRetry(5); } - public void testWithDifferentCustomTokenFetchRetry(int numOfRetries) - throws Exception { + public void testWithDifferentCustomTokenFetchRetry(int numOfRetries) throws Exception { AzureBlobFileSystem fs = this.getFileSystem(); Configuration config = new Configuration(this.getRawConfiguration()); String accountName = config.get("fs.azure.abfs.account.name"); // Setup to configure custom token provider config.set("fs.azure.account.auth.type." + accountName, "Custom"); - config.set("fs.azure.account.oauth.provider.type." + accountName, - "org.apache.hadoop.fs" - + ".azurebfs.oauth2.RetryTestTokenProvider"); - config.set("fs.azure.custom.token.fetch.retry.count", - Integer.toString(numOfRetries)); + config.set("fs.azure.account.oauth.provider.type." + accountName, "org.apache.hadoop.fs" + + ".azurebfs.oauth2.RetryTestTokenProvider"); + config.set("fs.azure.custom.token.fetch.retry.count", Integer.toString(numOfRetries)); // Stop filesystem creation as it will lead to calls to store. config.set("fs.azure.createRemoteFileSystemDuringInitialization", "false"); final AzureBlobFileSystem fs1 = (AzureBlobFileSystem) FileSystem.newInstance(fs.getUri(), - config); + config); RetryTestTokenProvider.ResetStatusToFirstTokenFetch(); intercept(Exception.class, - () -> { + ()-> { fs1.getFileStatus(new Path("/")); }); // Number of retries done should be as configured Assert.assertTrue( "Number of token fetch retries (" + RetryTestTokenProvider.reTryCount - + ") done, does not match with fs.azure.custom.token.fetch.retry.count configured (" - + numOfRetries + + ") done, does not match with fs.azure.custom.token.fetch.retry.count configured (" + numOfRetries + ")", RetryTestTokenProvider.reTryCount == numOfRetries); } - - @Test - public void testPermissionDenied() throws Throwable { - describe("Verify that permission denial manifests as failures"); - final AzureBlobFileSystem fs = getFileSystem(); - assumeTrue(fs.getIsNamespaceEnabled()); - Path dir = new Path("testPermissionDenied"); - Path path = new Path(dir, "file"); - ContractTestUtils.writeTextFile(fs, path, "This should not be readable", true); - // no permissions - fs.setPermission(path, new FsPermission((short) 00000)); - fs.setPermission(dir, new FsPermission((short) 00000)); - intercept(AccessDeniedException.class, E_UNAUTH, () -> - ContractTestUtils.readUTF8(fs, path, -1)); - intercept(AccessDeniedException.class, E_UNAUTH, () -> { - boolean d = fs.delete(path, false); - return "Expected delete to fail, but got " + d; - }); - } -} +} \ No newline at end of file diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/TestAbfsErrorTranslation.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/TestAbfsErrorTranslation.java new file mode 100644 index 0000000000000..2c14b7af2821a --- /dev/null +++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/TestAbfsErrorTranslation.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.azurebfs; + +import java.io.FileNotFoundException; +import java.net.HttpURLConnection; +import java.nio.file.AccessDeniedException; + +import org.junit.Test; + +import org.apache.hadoop.fs.FileAlreadyExistsException; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException; +import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode; +import org.apache.hadoop.test.AbstractHadoopTestBase; + +import static org.apache.hadoop.fs.azurebfs.AzureBlobFileSystem.checkException; +import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.AUTHORIZATION_PERMISSION_MISS_MATCH; +import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.PATH_ALREADY_EXISTS; +import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.PATH_NOT_FOUND; +import static org.apache.hadoop.test.LambdaTestUtils.intercept; + +/** + * Test suite to verify exception conversion, filtering etc. + */ +public class TestAbfsErrorTranslation extends AbstractHadoopTestBase { + + public static final Path PATH = new Path("abfs//store/path"); + + @Test + public void testConvert403ToAccessDenied() throws Throwable { + assertTranslated(HttpURLConnection.HTTP_FORBIDDEN, + AUTHORIZATION_PERMISSION_MISS_MATCH, + AccessDeniedException.class, + AUTHORIZATION_PERMISSION_MISS_MATCH.getErrorCode()); + } + + @Test + public void testConvert404ToFNFE() throws Throwable { + assertTranslated(HttpURLConnection.HTTP_NOT_FOUND, + PATH_NOT_FOUND, + FileNotFoundException.class, + PATH_NOT_FOUND.getErrorCode()); + } + + @Test + public void testConvert409ToFileAlreadyExistsException() throws Throwable { + assertTranslated(HttpURLConnection.HTTP_CONFLICT, + PATH_ALREADY_EXISTS, + FileAlreadyExistsException.class, + PATH_ALREADY_EXISTS.getErrorCode()); + } + + /** + * Assert that for a given status code and AzureServiceErrorCode, a specific + * exception class is raised. + * @param type of exception + * @param httpStatus http status code + * @param exitCode AzureServiceErrorCode + * @param clazz class of raised exception + * @param expectedText text to expect in the exception + * @throws Exception any other exception than the one expected + */ + private void assertTranslated( + int httpStatus, AzureServiceErrorCode exitCode, + Class clazz, String expectedText) throws Exception { + AbfsRestOperationException ex = + new AbfsRestOperationException(httpStatus, exitCode.getErrorCode(), + "", null); + intercept(clazz, expectedText, () -> { + checkException(PATH, ex); + return "expected exception translation from " + ex; + }); + } + +}