|
@@ -18,6 +18,7 @@
|
|
|
|
|
|
package org.apache.hadoop.fs.azurebfs;
|
|
package org.apache.hadoop.fs.azurebfs;
|
|
|
|
|
|
|
|
+import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
import java.util.EnumSet;
|
|
import java.util.EnumSet;
|
|
|
|
|
|
@@ -28,9 +29,11 @@ import org.apache.hadoop.fs.Path;
|
|
import org.apache.hadoop.fs.XAttrSetFlag;
|
|
import org.apache.hadoop.fs.XAttrSetFlag;
|
|
import org.apache.hadoop.fs.azurebfs.constants.FSOperationType;
|
|
import org.apache.hadoop.fs.azurebfs.constants.FSOperationType;
|
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
|
|
|
+import org.apache.hadoop.fs.azurebfs.utils.DirectoryStateHelper;
|
|
import org.apache.hadoop.fs.azurebfs.utils.TracingHeaderValidator;
|
|
import org.apache.hadoop.fs.azurebfs.utils.TracingHeaderValidator;
|
|
|
|
|
|
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
|
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
|
|
|
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
|
|
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.ROOT_PATH;
|
|
import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.ROOT_PATH;
|
|
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
|
|
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
|
|
|
|
|
|
@@ -56,16 +59,15 @@ public class ITestAzureBlobFileSystemAttributes extends AbstractAbfsIntegrationT
|
|
@Test
|
|
@Test
|
|
public void testSetGetXAttrCreateReplace() throws Exception {
|
|
public void testSetGetXAttrCreateReplace() throws Exception {
|
|
AzureBlobFileSystem fs = getFileSystem();
|
|
AzureBlobFileSystem fs = getFileSystem();
|
|
- byte[] attributeValue = fs.getAbfsStore().encodeAttribute("one");
|
|
|
|
|
|
+ String decodedAttributeValue = "one";
|
|
|
|
+ byte[] attributeValue = fs.getAbfsStore().encodeAttribute(decodedAttributeValue);
|
|
String attributeName = "user.someAttribute";
|
|
String attributeName = "user.someAttribute";
|
|
Path testFile = path(getMethodName());
|
|
Path testFile = path(getMethodName());
|
|
|
|
|
|
// after creating a file, it must be possible to create a new xAttr
|
|
// after creating a file, it must be possible to create a new xAttr
|
|
touch(testFile);
|
|
touch(testFile);
|
|
fs.setXAttr(testFile, attributeName, attributeValue, CREATE_FLAG);
|
|
fs.setXAttr(testFile, attributeName, attributeValue, CREATE_FLAG);
|
|
- Assertions.assertThat(fs.getXAttr(testFile, attributeName))
|
|
|
|
- .describedAs("Retrieved Attribute Value is Not as Expected")
|
|
|
|
- .containsExactly(attributeValue);
|
|
|
|
|
|
+ assertAttributeEqual(fs.getXAttr(testFile, attributeName), attributeValue, decodedAttributeValue);
|
|
|
|
|
|
// however after the xAttr is created, creating it again must fail
|
|
// however after the xAttr is created, creating it again must fail
|
|
intercept(IOException.class, () -> fs.setXAttr(testFile, attributeName, attributeValue, CREATE_FLAG));
|
|
intercept(IOException.class, () -> fs.setXAttr(testFile, attributeName, attributeValue, CREATE_FLAG));
|
|
@@ -74,8 +76,10 @@ public class ITestAzureBlobFileSystemAttributes extends AbstractAbfsIntegrationT
|
|
@Test
|
|
@Test
|
|
public void testSetGetXAttrReplace() throws Exception {
|
|
public void testSetGetXAttrReplace() throws Exception {
|
|
AzureBlobFileSystem fs = getFileSystem();
|
|
AzureBlobFileSystem fs = getFileSystem();
|
|
- byte[] attributeValue1 = fs.getAbfsStore().encodeAttribute("one");
|
|
|
|
- byte[] attributeValue2 = fs.getAbfsStore().encodeAttribute("two");
|
|
|
|
|
|
+ String decodedAttribute1 = "one";
|
|
|
|
+ String decodedAttribute2 = "two";
|
|
|
|
+ byte[] attributeValue1 = fs.getAbfsStore().encodeAttribute(decodedAttribute1);
|
|
|
|
+ byte[] attributeValue2 = fs.getAbfsStore().encodeAttribute(decodedAttribute2);
|
|
String attributeName = "user.someAttribute";
|
|
String attributeName = "user.someAttribute";
|
|
Path testFile = path(getMethodName());
|
|
Path testFile = path(getMethodName());
|
|
|
|
|
|
@@ -88,9 +92,108 @@ public class ITestAzureBlobFileSystemAttributes extends AbstractAbfsIntegrationT
|
|
// however after the xAttr is created, replacing it must succeed
|
|
// however after the xAttr is created, replacing it must succeed
|
|
fs.setXAttr(testFile, attributeName, attributeValue1, CREATE_FLAG);
|
|
fs.setXAttr(testFile, attributeName, attributeValue1, CREATE_FLAG);
|
|
fs.setXAttr(testFile, attributeName, attributeValue2, REPLACE_FLAG);
|
|
fs.setXAttr(testFile, attributeName, attributeValue2, REPLACE_FLAG);
|
|
- Assertions.assertThat(fs.getXAttr(testFile, attributeName))
|
|
|
|
- .describedAs("Retrieved Attribute Value is Not as Expected")
|
|
|
|
- .containsExactly(attributeValue2);
|
|
|
|
|
|
+ assertAttributeEqual(fs.getXAttr(testFile, attributeName), attributeValue2,
|
|
|
|
+ decodedAttribute2);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Test get and set xattr fails fine on root.
|
|
|
|
+ * @throws Exception if test fails
|
|
|
|
+ */
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetSetXAttrOnRoot() throws Exception {
|
|
|
|
+ AzureBlobFileSystem fs = getFileSystem();
|
|
|
|
+ String attributeName = "user.attribute1";
|
|
|
|
+ byte[] attributeValue = fs.getAbfsStore().encodeAttribute("hi");
|
|
|
|
+ final Path testPath = new Path(ROOT_PATH);
|
|
|
|
+
|
|
|
|
+ AbfsRestOperationException ex = intercept(AbfsRestOperationException.class, () -> {
|
|
|
|
+ fs.getXAttr(testPath, attributeName);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ Assertions.assertThat(ex.getStatusCode())
|
|
|
|
+ .describedAs("GetXAttr() on root should fail with Bad Request")
|
|
|
|
+ .isEqualTo(HTTP_BAD_REQUEST);
|
|
|
|
+
|
|
|
|
+ ex = intercept(AbfsRestOperationException.class, () -> {
|
|
|
|
+ fs.setXAttr(testPath, attributeName, attributeValue, CREATE_FLAG);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ Assertions.assertThat(ex.getStatusCode())
|
|
|
|
+ .describedAs("SetXAttr() on root should fail with Bad Request")
|
|
|
|
+ .isEqualTo(HTTP_BAD_REQUEST);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Test get and set xattr works fine on marker blobs for directory.
|
|
|
|
+ * @throws Exception if test fails
|
|
|
|
+ */
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetSetXAttrOnExplicitDir() throws Exception {
|
|
|
|
+ AzureBlobFileSystem fs = getFileSystem();
|
|
|
|
+ final Path testPath = path(getMethodName());
|
|
|
|
+ fs.mkdirs(testPath);
|
|
|
|
+ testGetSetXAttrHelper(fs, testPath);
|
|
|
|
+
|
|
|
|
+ // Assert that the folder is now explicit
|
|
|
|
+ DirectoryStateHelper.isExplicitDirectory(testPath, fs, getTestTracingContext(fs, true));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Test get and set xattr fails fine on non-existing path.
|
|
|
|
+ * @throws Exception if test fails
|
|
|
|
+ */
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetSetXAttrOnNonExistingPath() throws Exception {
|
|
|
|
+ AzureBlobFileSystem fs = getFileSystem();
|
|
|
|
+ final Path testPath = path(getMethodName());
|
|
|
|
+ intercept(FileNotFoundException.class, String.valueOf(HTTP_NOT_FOUND),
|
|
|
|
+ "get/set XAttr() should fail for non-existing files",
|
|
|
|
+ () -> testGetSetXAttrHelper(fs, testPath));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Trying to set same attribute multiple times should result in no failure
|
|
|
|
+ * @throws Exception if test fails
|
|
|
|
+ */
|
|
|
|
+ @Test
|
|
|
|
+ public void testSetXAttrMultipleOperations() throws Exception {
|
|
|
|
+ AzureBlobFileSystem fs = getFileSystem();
|
|
|
|
+ final Path path = path(getMethodName());
|
|
|
|
+ fs.create(path);
|
|
|
|
+
|
|
|
|
+ String attributeName = "user.attribute1";
|
|
|
|
+ String decodedAttributeValue = "hi";
|
|
|
|
+ byte[] attributeValue = fs.getAbfsStore().encodeAttribute(decodedAttributeValue);
|
|
|
|
+
|
|
|
|
+ // Attribute not present initially
|
|
|
|
+ assertAttributeNull(fs.getXAttr(path, attributeName));
|
|
|
|
+
|
|
|
|
+ // Set the Attributes Multiple times
|
|
|
|
+ // Filesystem internally adds create and replace flags
|
|
|
|
+ fs.setXAttr(path, attributeName, attributeValue);
|
|
|
|
+ fs.setXAttr(path, attributeName, attributeValue);
|
|
|
|
+
|
|
|
|
+ // Check if the attribute is retrievable
|
|
|
|
+ byte[] rv = fs.getXAttr(path, attributeName);
|
|
|
|
+ assertAttributeEqual(rv, attributeValue, decodedAttributeValue);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Test get and set xattr works fine on implicit directory and ends up creating marker blobs.
|
|
|
|
+ * @throws Exception if test fails
|
|
|
|
+ */
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetSetXAttrOnImplicitDir() throws Exception {
|
|
|
|
+ assumeBlobServiceType();
|
|
|
|
+ AzureBlobFileSystem fs = getFileSystem();
|
|
|
|
+ final Path testPath = path(getMethodName());
|
|
|
|
+ createAzCopyFolder(testPath);
|
|
|
|
+ testGetSetXAttrHelper(fs, testPath);
|
|
|
|
+
|
|
|
|
+ // Assert that the folder is now explicit
|
|
|
|
+ DirectoryStateHelper.isExplicitDirectory(testPath, fs, getTestTracingContext(fs, true));
|
|
|
|
+ DirectoryStateHelper.isExplicitDirectory(testPath.getParent(), fs, getTestTracingContext(fs, true));
|
|
}
|
|
}
|
|
|
|
|
|
private void testGetSetXAttrHelper(final AzureBlobFileSystem fs,
|
|
private void testGetSetXAttrHelper(final AzureBlobFileSystem fs,
|
|
@@ -104,12 +207,8 @@ public class ITestAzureBlobFileSystemAttributes extends AbstractAbfsIntegrationT
|
|
byte[] attributeValue2 = fs.getAbfsStore().encodeAttribute(decodedAttributeValue2);
|
|
byte[] attributeValue2 = fs.getAbfsStore().encodeAttribute(decodedAttributeValue2);
|
|
|
|
|
|
// Attribute not present initially
|
|
// Attribute not present initially
|
|
- Assertions.assertThat(fs.getXAttr(testPath, attributeName1))
|
|
|
|
- .describedAs("Cannot get attribute before setting it")
|
|
|
|
- .isNull();
|
|
|
|
- Assertions.assertThat(fs.getXAttr(testPath, attributeName2))
|
|
|
|
- .describedAs("Cannot get attribute before setting it")
|
|
|
|
- .isNull();
|
|
|
|
|
|
+ assertAttributeNull(fs.getXAttr(testPath, attributeName1));
|
|
|
|
+ assertAttributeNull(fs.getXAttr(testPath, attributeName2));
|
|
|
|
|
|
// Set the Attributes
|
|
// Set the Attributes
|
|
fs.registerListener(
|
|
fs.registerListener(
|
|
@@ -121,12 +220,7 @@ public class ITestAzureBlobFileSystemAttributes extends AbstractAbfsIntegrationT
|
|
// Check if the attribute is retrievable
|
|
// Check if the attribute is retrievable
|
|
fs.setListenerOperation(FSOperationType.GET_ATTR);
|
|
fs.setListenerOperation(FSOperationType.GET_ATTR);
|
|
byte[] rv = fs.getXAttr(testPath, attributeName1);
|
|
byte[] rv = fs.getXAttr(testPath, attributeName1);
|
|
- Assertions.assertThat(rv)
|
|
|
|
- .describedAs("Retrieved Attribute Does not Matches in Encoded Form")
|
|
|
|
- .containsExactly(attributeValue1);
|
|
|
|
- Assertions.assertThat(fs.getAbfsStore().decodeAttribute(rv))
|
|
|
|
- .describedAs("Retrieved Attribute Does not Matches in Decoded Form")
|
|
|
|
- .isEqualTo(decodedAttributeValue1);
|
|
|
|
|
|
+ assertAttributeEqual(rv, attributeValue1, decodedAttributeValue1);
|
|
fs.registerListener(null);
|
|
fs.registerListener(null);
|
|
|
|
|
|
// Set the second Attribute
|
|
// Set the second Attribute
|
|
@@ -134,43 +228,25 @@ public class ITestAzureBlobFileSystemAttributes extends AbstractAbfsIntegrationT
|
|
|
|
|
|
// Check all the attributes present and previous Attribute not overridden
|
|
// Check all the attributes present and previous Attribute not overridden
|
|
rv = fs.getXAttr(testPath, attributeName1);
|
|
rv = fs.getXAttr(testPath, attributeName1);
|
|
- Assertions.assertThat(rv)
|
|
|
|
- .describedAs("Retrieved Attribute Does not Matches in Encoded Form")
|
|
|
|
- .containsExactly(attributeValue1);
|
|
|
|
- Assertions.assertThat(fs.getAbfsStore().decodeAttribute(rv))
|
|
|
|
- .describedAs("Retrieved Attribute Does not Matches in Decoded Form")
|
|
|
|
- .isEqualTo(decodedAttributeValue1);
|
|
|
|
|
|
+ assertAttributeEqual(rv, attributeValue1, decodedAttributeValue1);
|
|
|
|
|
|
rv = fs.getXAttr(testPath, attributeName2);
|
|
rv = fs.getXAttr(testPath, attributeName2);
|
|
- Assertions.assertThat(rv)
|
|
|
|
- .describedAs("Retrieved Attribute Does not Matches in Encoded Form")
|
|
|
|
- .containsExactly(attributeValue2);
|
|
|
|
- Assertions.assertThat(fs.getAbfsStore().decodeAttribute(rv))
|
|
|
|
- .describedAs("Retrieved Attribute Does not Matches in Decoded Form")
|
|
|
|
- .isEqualTo(decodedAttributeValue2);
|
|
|
|
|
|
+ assertAttributeEqual(rv, attributeValue2, decodedAttributeValue2);
|
|
}
|
|
}
|
|
|
|
|
|
- @Test
|
|
|
|
- public void testGetSetXAttrOnRoot() throws Exception {
|
|
|
|
- AzureBlobFileSystem fs = getFileSystem();
|
|
|
|
- String attributeName = "user.attribute1";
|
|
|
|
- byte[] attributeValue = fs.getAbfsStore().encodeAttribute("hi");
|
|
|
|
- final Path testPath = new Path(ROOT_PATH);
|
|
|
|
-
|
|
|
|
- AbfsRestOperationException ex = intercept(AbfsRestOperationException.class, () -> {
|
|
|
|
- fs.getXAttr(testPath, attributeName);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- Assertions.assertThat(ex.getStatusCode())
|
|
|
|
- .describedAs("GetXAttr() on root should fail with Bad Request")
|
|
|
|
- .isEqualTo(HTTP_BAD_REQUEST);
|
|
|
|
-
|
|
|
|
- ex = intercept(AbfsRestOperationException.class, () -> {
|
|
|
|
- fs.setXAttr(testPath, attributeName, attributeValue, CREATE_FLAG);
|
|
|
|
- });
|
|
|
|
|
|
+ private void assertAttributeNull(byte[] rv) {
|
|
|
|
+ Assertions.assertThat(rv)
|
|
|
|
+ .describedAs("Cannot get attribute before setting it")
|
|
|
|
+ .isNull();
|
|
|
|
+ }
|
|
|
|
|
|
- Assertions.assertThat(ex.getStatusCode())
|
|
|
|
- .describedAs("SetXAttr() on root should fail with Bad Request")
|
|
|
|
- .isEqualTo(HTTP_BAD_REQUEST);
|
|
|
|
|
|
+ private void assertAttributeEqual(byte[] rv, byte[] attributeValue,
|
|
|
|
+ String decodedAttributeValue) throws Exception {
|
|
|
|
+ Assertions.assertThat(rv)
|
|
|
|
+ .describedAs("Retrieved Attribute Does not Matches in Encoded Form")
|
|
|
|
+ .containsExactly(attributeValue);
|
|
|
|
+ Assertions.assertThat(getFileSystem().getAbfsStore().decodeAttribute(rv))
|
|
|
|
+ .describedAs("Retrieved Attribute Does not Matches in Decoded Form")
|
|
|
|
+ .isEqualTo(decodedAttributeValue);
|
|
}
|
|
}
|
|
}
|
|
}
|