HADOOP-14461 Azure: handle failure gracefully in case of missing account access key.
Contributed by Mingliang Liu.
This commit is contained in:
parent
379f19a2c7
commit
48f4a229a9
|
@ -303,6 +303,14 @@ public class AzureNativeFileSystemStore implements NativeFileSystemStore {
|
||||||
private boolean useLocalSasKeyMode = false;
|
private boolean useLocalSasKeyMode = false;
|
||||||
|
|
||||||
private String delegationToken;
|
private String delegationToken;
|
||||||
|
|
||||||
|
/** The error message template when container is not accessible. */
|
||||||
|
static final String NO_ACCESS_TO_CONTAINER_MSG = "No credentials found for "
|
||||||
|
+ "account %s in the configuration, and its container %s is not "
|
||||||
|
+ "accessible using anonymous credentials. Please check if the container "
|
||||||
|
+ "exists first. If it is not publicly available, you have to provide "
|
||||||
|
+ "account credentials.";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A test hook interface that can modify the operation context we use for
|
* A test hook interface that can modify the operation context we use for
|
||||||
* Azure Storage operations, e.g. to inject errors.
|
* Azure Storage operations, e.g. to inject errors.
|
||||||
|
@ -778,18 +786,17 @@ public class AzureNativeFileSystemStore implements NativeFileSystemStore {
|
||||||
rootDirectory = container.getDirectoryReference("");
|
rootDirectory = container.getDirectoryReference("");
|
||||||
|
|
||||||
// Check for container existence, and our ability to access it.
|
// Check for container existence, and our ability to access it.
|
||||||
|
boolean canAccess;
|
||||||
try {
|
try {
|
||||||
if (!container.exists(getInstrumentedContext())) {
|
canAccess = container.exists(getInstrumentedContext());
|
||||||
throw new AzureException("Container " + containerName + " in account "
|
|
||||||
+ accountName + " not found, and we can't create"
|
|
||||||
+ " it using anoynomous credentials, and no credentials found for them"
|
|
||||||
+ " in the configuration.");
|
|
||||||
}
|
|
||||||
} catch (StorageException ex) {
|
} catch (StorageException ex) {
|
||||||
throw new AzureException("Unable to access container " + containerName
|
LOG.error("Service returned StorageException when checking existence "
|
||||||
+ " in account " + accountName
|
+ "of container {} in account {}", containerName, accountName, ex);
|
||||||
+ " using anonymous credentials, and no credentials found for them "
|
canAccess = false;
|
||||||
+ " in the configuration.", ex);
|
}
|
||||||
|
if (!canAccess) {
|
||||||
|
throw new AzureException(String.format(NO_ACCESS_TO_CONTAINER_MSG,
|
||||||
|
accountName, containerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessing the storage server unauthenticated using
|
// Accessing the storage server unauthenticated using
|
||||||
|
@ -999,22 +1006,17 @@ public class AzureNativeFileSystemStore implements NativeFileSystemStore {
|
||||||
// Check whether the account is configured with an account key.
|
// Check whether the account is configured with an account key.
|
||||||
propertyValue = getAccountKeyFromConfiguration(accountName,
|
propertyValue = getAccountKeyFromConfiguration(accountName,
|
||||||
sessionConfiguration);
|
sessionConfiguration);
|
||||||
if (propertyValue != null) {
|
if (StringUtils.isNotEmpty(propertyValue)) {
|
||||||
|
|
||||||
// Account key was found.
|
// Account key was found.
|
||||||
// Create the Azure storage session using the account key and container.
|
// Create the Azure storage session using the account key and container.
|
||||||
connectUsingConnectionStringCredentials(
|
connectUsingConnectionStringCredentials(
|
||||||
getAccountFromAuthority(sessionUri),
|
getAccountFromAuthority(sessionUri),
|
||||||
getContainerFromAuthority(sessionUri), propertyValue);
|
getContainerFromAuthority(sessionUri), propertyValue);
|
||||||
|
} else {
|
||||||
// Return to caller
|
LOG.debug("The account access key is not configured for {}. "
|
||||||
return;
|
+ "Now try anonymous access.", sessionUri);
|
||||||
|
connectUsingAnonymousCredentials(sessionUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The account access is not configured for this cluster. Try anonymous
|
|
||||||
// access.
|
|
||||||
connectUsingAnonymousCredentials(sessionUri);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Caught exception while attempting to initialize the Azure File
|
// Caught exception while attempting to initialize the Azure File
|
||||||
// System store, re-throw the exception.
|
// System store, re-throw the exception.
|
||||||
|
|
|
@ -22,6 +22,9 @@ import com.microsoft.azure.storage.*;
|
||||||
import com.microsoft.azure.storage.blob.*;
|
import com.microsoft.azure.storage.blob.*;
|
||||||
import com.microsoft.azure.storage.core.Base64;
|
import com.microsoft.azure.storage.core.Base64;
|
||||||
import org.apache.commons.configuration2.SubsetConfiguration;
|
import org.apache.commons.configuration2.SubsetConfiguration;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.fs.azure.metrics.AzureFileSystemInstrumentation;
|
import org.apache.hadoop.fs.azure.metrics.AzureFileSystemInstrumentation;
|
||||||
|
@ -46,6 +49,8 @@ import static org.apache.hadoop.fs.azure.AzureNativeFileSystemStore.KEY_USE_LOCA
|
||||||
* for instructions on how to connect to a real Azure Storage account.
|
* for instructions on how to connect to a real Azure Storage account.
|
||||||
*/
|
*/
|
||||||
public final class AzureBlobStorageTestAccount {
|
public final class AzureBlobStorageTestAccount {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(
|
||||||
|
AzureBlobStorageTestAccount.class);
|
||||||
|
|
||||||
private static final String ACCOUNT_KEY_PROPERTY_NAME = "fs.azure.account.key.";
|
private static final String ACCOUNT_KEY_PROPERTY_NAME = "fs.azure.account.key.";
|
||||||
private static final String SAS_PROPERTY_NAME = "fs.azure.sas.";
|
private static final String SAS_PROPERTY_NAME = "fs.azure.sas.";
|
||||||
|
@ -299,10 +304,9 @@ public final class AzureBlobStorageTestAccount {
|
||||||
Configuration conf = createTestConfiguration();
|
Configuration conf = createTestConfiguration();
|
||||||
if (!conf.getBoolean(USE_EMULATOR_PROPERTY_NAME, false)) {
|
if (!conf.getBoolean(USE_EMULATOR_PROPERTY_NAME, false)) {
|
||||||
// Not configured to test against the storage emulator.
|
// Not configured to test against the storage emulator.
|
||||||
System.out
|
LOG.warn("Skipping emulator Azure test because configuration doesn't "
|
||||||
.println("Skipping emulator Azure test because configuration " +
|
+ "indicate that it's running. Please see RunningLiveWasbTests.txt "
|
||||||
"doesn't indicate that it's running." +
|
+ "for guidance.");
|
||||||
" Please see RunningLiveWasbTests.txt for guidance.");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
CloudStorageAccount account =
|
CloudStorageAccount account =
|
||||||
|
@ -456,18 +460,22 @@ public final class AzureBlobStorageTestAccount {
|
||||||
KeyProviderException {
|
KeyProviderException {
|
||||||
String accountKey = AzureNativeFileSystemStore
|
String accountKey = AzureNativeFileSystemStore
|
||||||
.getAccountKeyFromConfiguration(accountName, conf);
|
.getAccountKeyFromConfiguration(accountName, conf);
|
||||||
StorageCredentials credentials;
|
final StorageCredentials credentials;
|
||||||
if (accountKey == null && allowAnonymous) {
|
if (accountKey == null) {
|
||||||
credentials = StorageCredentialsAnonymous.ANONYMOUS;
|
if (allowAnonymous) {
|
||||||
|
credentials = StorageCredentialsAnonymous.ANONYMOUS;
|
||||||
|
} else {
|
||||||
|
LOG.warn("Skipping live Azure test because of missing key for"
|
||||||
|
+ " account '" + accountName + "'. "
|
||||||
|
+ "Please see RunningLiveWasbTests.txt for guidance.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
credentials = new StorageCredentialsAccountAndKey(
|
credentials = new StorageCredentialsAccountAndKey(
|
||||||
accountName.split("\\.")[0], accountKey);
|
accountName.split("\\.")[0], accountKey);
|
||||||
}
|
}
|
||||||
if (credentials == null) {
|
|
||||||
return null;
|
return new CloudStorageAccount(credentials);
|
||||||
} else {
|
|
||||||
return new CloudStorageAccount(credentials);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Configuration createTestConfiguration() {
|
public static Configuration createTestConfiguration() {
|
||||||
|
@ -493,9 +501,8 @@ public final class AzureBlobStorageTestAccount {
|
||||||
throws URISyntaxException, KeyProviderException {
|
throws URISyntaxException, KeyProviderException {
|
||||||
String testAccountName = conf.get(TEST_ACCOUNT_NAME_PROPERTY_NAME);
|
String testAccountName = conf.get(TEST_ACCOUNT_NAME_PROPERTY_NAME);
|
||||||
if (testAccountName == null) {
|
if (testAccountName == null) {
|
||||||
System.out
|
LOG.warn("Skipping live Azure test because of missing test account. "
|
||||||
.println("Skipping live Azure test because of missing test account." +
|
+ "Please see RunningLiveWasbTests.txt for guidance.");
|
||||||
" Please see RunningLiveWasbTests.txt for guidance.");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return createStorageAccount(testAccountName, conf, false);
|
return createStorageAccount(testAccountName, conf, false);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azure;
|
package org.apache.hadoop.fs.azure;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.fs.azure.AzureNativeFileSystemStore.NO_ACCESS_TO_CONTAINER_MSG;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
@ -35,6 +36,8 @@ import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.fs.azure.AzureNativeFileSystemStore.TestHookOperationContext;
|
import org.apache.hadoop.fs.azure.AzureNativeFileSystemStore.TestHookOperationContext;
|
||||||
|
import org.apache.hadoop.test.GenericTestUtils;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.microsoft.azure.storage.OperationContext;
|
import com.microsoft.azure.storage.OperationContext;
|
||||||
|
@ -64,18 +67,18 @@ public class TestAzureFileSystemErrorConditions {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAccessUnauthorizedPublicContainer() throws Exception {
|
public void testAccessUnauthorizedPublicContainer() throws Exception {
|
||||||
|
final String container = "nonExistentContainer";
|
||||||
|
final String account = "hopefullyNonExistentAccount";
|
||||||
Path noAccessPath = new Path(
|
Path noAccessPath = new Path(
|
||||||
"wasb://nonExistentContainer@hopefullyNonExistentAccount/someFile");
|
"wasb://" + container + "@" + account + "/someFile");
|
||||||
NativeAzureFileSystem.suppressRetryPolicy();
|
NativeAzureFileSystem.suppressRetryPolicy();
|
||||||
try {
|
try {
|
||||||
FileSystem.get(noAccessPath.toUri(), new Configuration())
|
FileSystem.get(noAccessPath.toUri(), new Configuration())
|
||||||
.open(noAccessPath);
|
.open(noAccessPath);
|
||||||
assertTrue("Should've thrown.", false);
|
assertTrue("Should've thrown.", false);
|
||||||
} catch (AzureException ex) {
|
} catch (AzureException ex) {
|
||||||
assertTrue("Unexpected message in exception " + ex,
|
GenericTestUtils.assertExceptionContains(
|
||||||
ex.getMessage().contains(
|
String.format(NO_ACCESS_TO_CONTAINER_MSG, account, container), ex);
|
||||||
"Unable to access container nonExistentContainer in account" +
|
|
||||||
" hopefullyNonExistentAccount"));
|
|
||||||
} finally {
|
} finally {
|
||||||
NativeAzureFileSystem.resumeRetryPolicy();
|
NativeAzureFileSystem.resumeRetryPolicy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,13 @@ import java.net.URI;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.test.GenericTestUtils;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.fs.azure.AzureNativeFileSystemStore.NO_ACCESS_TO_CONTAINER_MSG;
|
||||||
|
|
||||||
|
|
||||||
public class TestFileSystemOperationExceptionMessage extends
|
public class TestFileSystemOperationExceptionMessage extends
|
||||||
NativeAzureFileSystemBaseTest {
|
NativeAzureFileSystemBaseTest {
|
||||||
|
@ -41,11 +45,6 @@ public class TestFileSystemOperationExceptionMessage extends
|
||||||
String wasbUri = String.format("wasb://%s@%s",
|
String wasbUri = String.format("wasb://%s@%s",
|
||||||
testContainer, testStorageAccount);
|
testContainer, testStorageAccount);
|
||||||
|
|
||||||
String expectedErrorMessage =
|
|
||||||
String.format("Container %s in account %s not found, and we can't create it "
|
|
||||||
+ "using anoynomous credentials, and no credentials found for "
|
|
||||||
+ "them in the configuration.", testContainer, testStorageAccount);
|
|
||||||
|
|
||||||
fs = new NativeAzureFileSystem();
|
fs = new NativeAzureFileSystem();
|
||||||
try {
|
try {
|
||||||
fs.initialize(new URI(wasbUri), conf);
|
fs.initialize(new URI(wasbUri), conf);
|
||||||
|
@ -63,7 +62,9 @@ public class TestFileSystemOperationExceptionMessage extends
|
||||||
|| exceptionMessage.length() == 0) {
|
|| exceptionMessage.length() == 0) {
|
||||||
Assert.fail();}
|
Assert.fail();}
|
||||||
else {
|
else {
|
||||||
Assert.assertTrue(exceptionMessage.equals(expectedErrorMessage));
|
GenericTestUtils.assertExceptionContains(String.format(
|
||||||
|
NO_ACCESS_TO_CONTAINER_MSG, testStorageAccount, testContainer),
|
||||||
|
ex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
|
|
Loading…
Reference in New Issue