1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 package org.apache.hadoop.fs.azure;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertTrue;
25 
26 import java.util.HashMap;
27 
28 import org.apache.hadoop.fs.FileStatus;
29 import org.apache.hadoop.fs.FileSystem;
30 import org.apache.hadoop.fs.Path;
31 import org.apache.hadoop.fs.permission.FsPermission;
32 import org.junit.After;
33 import org.junit.Before;
34 import org.junit.Test;
35 
36 /**
37  * Tests that WASB handles things gracefully when users add blobs to the Azure
38  * Storage container from outside WASB's control.
39  */
40 public class TestOutOfBandAzureBlobOperations {
41   private AzureBlobStorageTestAccount testAccount;
42   private FileSystem fs;
43   private InMemoryBlockBlobStore backingStore;
44 
45   @Before
setUp()46   public void setUp() throws Exception {
47     testAccount = AzureBlobStorageTestAccount.createMock();
48     fs = testAccount.getFileSystem();
49     backingStore = testAccount.getMockStorage().getBackingStore();
50   }
51 
52   @After
tearDown()53   public void tearDown() throws Exception {
54     testAccount.cleanup();
55     fs = null;
56     backingStore = null;
57   }
58 
createEmptyBlobOutOfBand(String path)59   private void createEmptyBlobOutOfBand(String path) {
60     backingStore.setContent(
61         AzureBlobStorageTestAccount.toMockUri(path),
62         new byte[] { 1, 2 },
63         new HashMap<String, String>(),
64         false, 0);
65   }
66 
67   @SuppressWarnings("deprecation")
68   @Test
testImplicitFolderListed()69   public void testImplicitFolderListed() throws Exception {
70     createEmptyBlobOutOfBand("root/b");
71 
72     // List the blob itself.
73     FileStatus[] obtained = fs.listStatus(new Path("/root/b"));
74     assertNotNull(obtained);
75     assertEquals(1, obtained.length);
76     assertFalse(obtained[0].isDir());
77     assertEquals("/root/b", obtained[0].getPath().toUri().getPath());
78 
79     // List the directory
80     obtained = fs.listStatus(new Path("/root"));
81     assertNotNull(obtained);
82     assertEquals(1, obtained.length);
83     assertFalse(obtained[0].isDir());
84     assertEquals("/root/b", obtained[0].getPath().toUri().getPath());
85 
86     // Get the directory's file status
87     FileStatus dirStatus = fs.getFileStatus(new Path("/root"));
88     assertNotNull(dirStatus);
89     assertTrue(dirStatus.isDir());
90     assertEquals("/root", dirStatus.getPath().toUri().getPath());
91   }
92 
93   @Test
testImplicitFolderDeleted()94   public void testImplicitFolderDeleted() throws Exception {
95     createEmptyBlobOutOfBand("root/b");
96     assertTrue(fs.exists(new Path("/root")));
97     assertTrue(fs.delete(new Path("/root"), true));
98     assertFalse(fs.exists(new Path("/root")));
99   }
100 
101   @Test
testFileInImplicitFolderDeleted()102   public void testFileInImplicitFolderDeleted() throws Exception {
103     createEmptyBlobOutOfBand("root/b");
104     assertTrue(fs.exists(new Path("/root")));
105     assertTrue(fs.delete(new Path("/root/b"), true));
106     assertTrue(fs.exists(new Path("/root")));
107   }
108 
109   @SuppressWarnings("deprecation")
110   @Test
testFileAndImplicitFolderSameName()111   public void testFileAndImplicitFolderSameName() throws Exception {
112     createEmptyBlobOutOfBand("root/b");
113     createEmptyBlobOutOfBand("root/b/c");
114     FileStatus[] listResult = fs.listStatus(new Path("/root/b"));
115     // File should win.
116     assertEquals(1, listResult.length);
117     assertFalse(listResult[0].isDir());
118     try {
119       // Trying to delete root/b/c would cause a dilemma for WASB, so
120       // it should throw.
121       fs.delete(new Path("/root/b/c"), true);
122       assertTrue("Should've thrown.", false);
123     } catch (AzureException e) {
124       assertEquals("File /root/b/c has a parent directory /root/b"
125           + " which is also a file. Can't resolve.", e.getMessage());
126     }
127   }
128 
129   private static enum DeepCreateTestVariation {
130     File, Folder
131   };
132 
133   /**
134    * Tests that when we create the file (or folder) x/y/z, we also create
135    * explicit folder blobs for x and x/y
136    */
137   @Test
testCreatingDeepFileCreatesExplicitFolder()138   public void testCreatingDeepFileCreatesExplicitFolder() throws Exception {
139     for (DeepCreateTestVariation variation : DeepCreateTestVariation.values()) {
140       switch (variation) {
141       case File:
142         assertTrue(fs.createNewFile(new Path("/x/y/z")));
143         break;
144       case Folder:
145         assertTrue(fs.mkdirs(new Path("/x/y/z")));
146         break;
147       }
148       assertTrue(backingStore
149           .exists(AzureBlobStorageTestAccount.toMockUri("x")));
150       assertTrue(backingStore.exists(AzureBlobStorageTestAccount
151           .toMockUri("x/y")));
152       fs.delete(new Path("/x"), true);
153     }
154   }
155 
156   @Test
testSetPermissionOnImplicitFolder()157   public void testSetPermissionOnImplicitFolder() throws Exception {
158     createEmptyBlobOutOfBand("root/b");
159     FsPermission newPermission = new FsPermission((short) 0600);
160     fs.setPermission(new Path("/root"), newPermission);
161     FileStatus newStatus = fs.getFileStatus(new Path("/root"));
162     assertNotNull(newStatus);
163     assertEquals(newPermission, newStatus.getPermission());
164   }
165 
166   @Test
testSetOwnerOnImplicitFolder()167   public void testSetOwnerOnImplicitFolder() throws Exception {
168     createEmptyBlobOutOfBand("root/b");
169     fs.setOwner(new Path("/root"), "newOwner", null);
170     FileStatus newStatus = fs.getFileStatus(new Path("/root"));
171     assertNotNull(newStatus);
172     assertEquals("newOwner", newStatus.getOwner());
173   }
174 }
175