/** * 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.azure; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URI; import java.util.Arrays; import java.util.HashMap; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.azure.AzureNativeFileSystemStore.TestHookOperationContext; import org.junit.Test; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.SendingRequestEvent; import com.microsoft.azure.storage.StorageEvent; public class TestAzureFileSystemErrorConditions { private static final int ALL_THREE_FILE_SIZE = 1024; @Test public void testNoInitialize() throws Exception { AzureNativeFileSystemStore store = new AzureNativeFileSystemStore(); boolean passed = false; try { store.retrieveMetadata("foo"); passed = true; } catch (AssertionError e) { } assertFalse( "Doing an operation on the store should throw if not initalized.", passed); } /** * Try accessing an unauthorized or non-existent (treated the same) container * from WASB. */ @Test public void testAccessUnauthorizedPublicContainer() throws Exception { Path noAccessPath = new Path( "wasb://nonExistentContainer@hopefullyNonExistentAccount/someFile"); NativeAzureFileSystem.suppressRetryPolicy(); try { FileSystem.get(noAccessPath.toUri(), new Configuration()) .open(noAccessPath); assertTrue("Should've thrown.", false); } catch (AzureException ex) { assertTrue("Unexpected message in exception " + ex, ex.getMessage().contains( "Unable to access container nonExistentContainer in account" + " hopefullyNonExistentAccount")); } finally { NativeAzureFileSystem.resumeRetryPolicy(); } } @Test public void testAccessContainerWithWrongVersion() throws Exception { AzureNativeFileSystemStore store = new AzureNativeFileSystemStore(); MockStorageInterface mockStorage = new MockStorageInterface(); store.setAzureStorageInteractionLayer(mockStorage); FileSystem fs = new NativeAzureFileSystem(store); try { Configuration conf = new Configuration(); AzureBlobStorageTestAccount.setMockAccountKey(conf); HashMap<String, String> metadata = new HashMap<String, String>(); metadata.put(AzureNativeFileSystemStore.VERSION_METADATA_KEY, "2090-04-05"); // It's from the future! mockStorage.addPreExistingContainer( AzureBlobStorageTestAccount.getMockContainerUri(), metadata); boolean passed = false; try { fs.initialize(new URI(AzureBlobStorageTestAccount.MOCK_WASB_URI), conf); fs.listStatus(new Path("/")); passed = true; } catch (AzureException ex) { assertTrue("Unexpected exception message: " + ex, ex.getMessage().contains("unsupported version: 2090-04-05.")); } assertFalse("Should've thrown an exception because of the wrong version.", passed); } finally { fs.close(); } } private interface ConnectionRecognizer { boolean isTargetConnection(HttpURLConnection connection); } private class TransientErrorInjector extends StorageEvent<SendingRequestEvent> { final ConnectionRecognizer connectionRecognizer; private boolean injectedErrorOnce = false; public TransientErrorInjector(ConnectionRecognizer connectionRecognizer) { this.connectionRecognizer = connectionRecognizer; } @Override public void eventOccurred(SendingRequestEvent eventArg) { HttpURLConnection connection = (HttpURLConnection)eventArg.getConnectionObject(); if (!connectionRecognizer.isTargetConnection(connection)) { return; } if (!injectedErrorOnce) { connection.setReadTimeout(1); connection.disconnect(); injectedErrorOnce = true; } } } private void injectTransientError(NativeAzureFileSystem fs, final ConnectionRecognizer connectionRecognizer) { fs.getStore().addTestHookToOperationContext(new TestHookOperationContext() { @Override public OperationContext modifyOperationContext(OperationContext original) { original.getSendingRequestEventHandler().addListener( new TransientErrorInjector(connectionRecognizer)); return original; } }); } @Test public void testTransientErrorOnDelete() throws Exception { // Need to do this test against a live storage account AzureBlobStorageTestAccount testAccount = AzureBlobStorageTestAccount.create(); assumeNotNull(testAccount); try { NativeAzureFileSystem fs = testAccount.getFileSystem(); injectTransientError(fs, new ConnectionRecognizer() { @Override public boolean isTargetConnection(HttpURLConnection connection) { return connection.getRequestMethod().equals("DELETE"); } }); Path testFile = new Path("/a/b"); assertTrue(fs.createNewFile(testFile)); assertTrue(fs.rename(testFile, new Path("/x"))); } finally { testAccount.cleanup(); } } private void writeAllThreeFile(NativeAzureFileSystem fs, Path testFile) throws IOException { byte[] buffer = new byte[ALL_THREE_FILE_SIZE]; Arrays.fill(buffer, (byte)3); OutputStream stream = fs.create(testFile); stream.write(buffer); stream.close(); } private void readAllThreeFile(NativeAzureFileSystem fs, Path testFile) throws IOException { byte[] buffer = new byte[ALL_THREE_FILE_SIZE]; InputStream inStream = fs.open(testFile); assertEquals(buffer.length, inStream.read(buffer, 0, buffer.length)); inStream.close(); for (int i = 0; i < buffer.length; i++) { assertEquals(3, buffer[i]); } } @Test public void testTransientErrorOnCommitBlockList() throws Exception { // Need to do this test against a live storage account AzureBlobStorageTestAccount testAccount = AzureBlobStorageTestAccount.create(); assumeNotNull(testAccount); try { NativeAzureFileSystem fs = testAccount.getFileSystem(); injectTransientError(fs, new ConnectionRecognizer() { @Override public boolean isTargetConnection(HttpURLConnection connection) { return connection.getRequestMethod().equals("PUT") && connection.getURL().getQuery() != null && connection.getURL().getQuery().contains("blocklist"); } }); Path testFile = new Path("/a/b"); writeAllThreeFile(fs, testFile); readAllThreeFile(fs, testFile); } finally { testAccount.cleanup(); } } @Test public void testTransientErrorOnRead() throws Exception { // Need to do this test against a live storage account AzureBlobStorageTestAccount testAccount = AzureBlobStorageTestAccount.create(); assumeNotNull(testAccount); try { NativeAzureFileSystem fs = testAccount.getFileSystem(); Path testFile = new Path("/a/b"); writeAllThreeFile(fs, testFile); injectTransientError(fs, new ConnectionRecognizer() { @Override public boolean isTargetConnection(HttpURLConnection connection) { return connection.getRequestMethod().equals("GET"); } }); readAllThreeFile(fs, testFile); } finally { testAccount.cleanup(); } } }