Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3a0d8c4
Fail request for bucket with FSO layout where key is a directory
mladjan-gadzic Feb 8, 2023
637b95f
Fail request when key is not a file and path does not end with '/'
mladjan-gadzic Feb 9, 2023
20cb8f4
Address George's comments
mladjan-gadzic Feb 20, 2023
7b82e8f
Merge branch 'master' into HDDS-7922
mladjan-gadzic Feb 20, 2023
84d7b23
Address Neil's comments
mladjan-gadzic Feb 20, 2023
e04d3a3
Make isFile required in constructors and cleanup not used code
mladjan-gadzic Feb 21, 2023
7f09e13
Fix smoketest to be inline with implementation
mladjan-gadzic Feb 23, 2023
03ee03f
Increase readability and maintainability for unit tests
mladjan-gadzic Feb 23, 2023
e92dd34
Change object name because it interferes with "Zero byte file" tests
mladjan-gadzic Feb 23, 2023
6f467ba
Revert "Fix smoketest to be inline with implementation"
mladjan-gadzic Feb 25, 2023
00306f7
Revert "Change object name because it interferes with "Zero byte file…
mladjan-gadzic Feb 25, 2023
9afb488
Remove GET patch
mladjan-gadzic Feb 25, 2023
a495d0d
Remove test for GET patch
mladjan-gadzic Feb 25, 2023
bbb746c
Temp fix for Zero byte file acceptance test
mladjan-gadzic Feb 25, 2023
e001dcf
Revert "Remove test for GET patch"
mladjan-gadzic Feb 25, 2023
821c591
Revert "Remove GET patch"
mladjan-gadzic Feb 25, 2023
0ceb8f0
Refactor for readability
mladjan-gadzic Mar 22, 2023
204a4d9
Improve readability
mladjan-gadzic Mar 30, 2023
8232ed8
Add test when key is a file and key path ends with a slash(/)
mladjan-gadzic Apr 3, 2023
95b4c72
Merge remote-tracking branch 'upstream/master' into HDDS-7922
mladjan-gadzic Jun 5, 2023
286eb92
Resolve conflicts with upstream/master
mladjan-gadzic Jun 5, 2023
9366bd2
Merge remote-tracking branch 'upstream/master' into HDDS-7922
mladjan-gadzic Jun 30, 2023
9e218fb
Cleanup after resolving merge conflicts.
mladjan-gadzic Jun 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1367,7 +1367,8 @@ private boolean getChildrenKeys(String keyPrefix, String startKey,
keyInfo.getBucketName(), keyName,
keyInfo.getDataSize(), keyInfo.getCreationTime(),
keyInfo.getModificationTime(),
keyInfo.getReplicationConfig());
keyInfo.getReplicationConfig(),
keyInfo.isFile());

keysResultList.add(ozoneKey);

Expand Down Expand Up @@ -1468,7 +1469,8 @@ private void addKeyPrefixInfoToResultList(String keyPrefix,
keyInfo.getBucketName(), keyName,
keyInfo.getDataSize(), keyInfo.getCreationTime(),
keyInfo.getModificationTime(),
keyInfo.getReplicationConfig());
keyInfo.getReplicationConfig(),
keyInfo.isFile());
keysResultList.add(ozoneKey);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.apache.hadoop.hdds.client.ReplicationConfig;
import org.apache.hadoop.hdds.client.ReplicationFactor;
import org.apache.hadoop.hdds.client.ReplicationType;
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;

Expand Down Expand Up @@ -61,45 +60,38 @@ public class OzoneKey {
private ReplicationConfig replicationConfig;

private Map<String, String> metadata = new HashMap<>();

/**
* Constructs OzoneKey from OmKeyInfo.
*
* Indicator if key is a file.
*/
@SuppressWarnings("parameternumber")
@Deprecated
public OzoneKey(String volumeName, String bucketName,
String keyName, long size, long creationTime,
long modificationTime, ReplicationType type,
int replicationFactor) {
this(volumeName, bucketName, keyName, size, creationTime, modificationTime,
ReplicationConfig.fromTypeAndFactor(type,
ReplicationFactor.valueOf(replicationFactor)));
}
private final boolean isFile;

/**
* Constructs OzoneKey from OmKeyInfo.
*
*/
@SuppressWarnings("parameternumber")
public OzoneKey(String volumeName, String bucketName,
String keyName, long size, long creationTime,
long modificationTime, ReplicationConfig replicationConfig) {
String keyName, long size, long creationTime,
long modificationTime, ReplicationConfig replicationConfig,
boolean isFile) {
this.volumeName = volumeName;
this.bucketName = bucketName;
this.name = keyName;
this.dataSize = size;
this.creationTime = Instant.ofEpochMilli(creationTime);
this.modificationTime = Instant.ofEpochMilli(modificationTime);
this.replicationConfig = replicationConfig;
this.isFile = isFile;
}

@SuppressWarnings("parameternumber")
public OzoneKey(String volumeName, String bucketName,
String keyName, long size, long creationTime,
long modificationTime, ReplicationConfig replicationConfig,
Map<String, String> metadata) {
Map<String, String> metadata, boolean isFile) {
this(volumeName, bucketName, keyName, size, creationTime,
modificationTime, replicationConfig);
modificationTime, replicationConfig, isFile);
this.metadata.putAll(metadata);
}

Expand Down Expand Up @@ -187,11 +179,19 @@ public ReplicationConfig getReplicationConfig() {
return replicationConfig;
}

/**
* Returns indicator if key is a file.
* @return file
*/
public boolean isFile() {
return isFile;
}

public static OzoneKey fromKeyInfo(OmKeyInfo keyInfo) {
return new OzoneKey(keyInfo.getVolumeName(), keyInfo.getBucketName(),
keyInfo.getKeyName(), keyInfo.getDataSize(), keyInfo.getCreationTime(),
keyInfo.getModificationTime(), keyInfo.getReplicationConfig(),
keyInfo.getMetadata());
keyInfo.getMetadata(), keyInfo.isFile());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.apache.hadoop.fs.FileEncryptionInfo;
import org.apache.hadoop.hdds.client.ReplicationConfig;
import org.apache.hadoop.hdds.client.ReplicationType;
import org.apache.hadoop.ozone.client.io.OzoneInputStream;
import org.apache.ratis.util.function.CheckedSupplier;

Expand All @@ -43,25 +42,6 @@ public class OzoneKeyDetails extends OzoneKey {

private final CheckedSupplier<OzoneInputStream, IOException> contentSupplier;

/**
* Constructs OzoneKeyDetails from OmKeyInfo.
*/
@SuppressWarnings("parameternumber")
@Deprecated
public OzoneKeyDetails(String volumeName, String bucketName, String keyName,
long size, long creationTime, long modificationTime,
List<OzoneKeyLocation> ozoneKeyLocations,
ReplicationType type, Map<String, String> metadata,
FileEncryptionInfo feInfo, int replicationFactor) {
super(volumeName, bucketName, keyName, size, creationTime,
modificationTime, type, replicationFactor);
this.ozoneKeyLocations = ozoneKeyLocations;
this.feInfo = feInfo;
contentSupplier = null;
this.setMetadata(metadata);
}


/**
* Constructs OzoneKeyDetails from OmKeyInfo.
*/
Expand All @@ -72,9 +52,10 @@ public OzoneKeyDetails(String volumeName, String bucketName, String keyName,
ReplicationConfig replicationConfig,
Map<String, String> metadata,
FileEncryptionInfo feInfo,
CheckedSupplier<OzoneInputStream, IOException> contentSupplier) {
CheckedSupplier<OzoneInputStream, IOException> contentSupplier,
boolean isFile) {
super(volumeName, bucketName, keyName, size, creationTime,
modificationTime, replicationConfig, metadata);
modificationTime, replicationConfig, metadata, isFile);
this.ozoneKeyLocations = ozoneKeyLocations;
this.feInfo = feInfo;
this.contentSupplier = contentSupplier;
Expand All @@ -93,6 +74,8 @@ public FileEncryptionInfo getFileEncryptionInfo() {

/**
* Get OzoneInputStream to read the content of the key.
* @return OzoneInputStream
* @throws IOException
*/
@JsonIgnore
public OzoneInputStream getContent() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1543,7 +1543,8 @@ public List<OzoneKey> listKeys(String volumeName, String bucketName,
key.getDataSize(),
key.getCreationTime(),
key.getModificationTime(),
key.getReplicationConfig()))
key.getReplicationConfig(),
key.isFile()))
.collect(Collectors.toList());
}

Expand Down Expand Up @@ -1594,7 +1595,7 @@ private OzoneKeyDetails getOzoneKeyDetails(OmKeyInfo keyInfo) {
keyInfo.getModificationTime(), ozoneKeyLocations,
keyInfo.getReplicationConfig(), keyInfo.getMetadata(),
keyInfo.getFileEncryptionInfo(),
() -> getInputStreamWithRetryFunction(keyInfo));
() -> getInputStreamWithRetryFunction(keyInfo), keyInfo.isFile());
}

@Override
Expand Down
36 changes: 28 additions & 8 deletions hadoop-ozone/dist/src/main/smoketest/s3/objectputget.robot
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,34 @@ Incorrect values for end and start offset
Should Be Equal ${expectedData} ${actualData}

Zero byte file
${result} = Execute AWSS3APICli and checkrc get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/zerobyte --range bytes=0-0 /tmp/testfile2.result 255
Should contain ${result} InvalidRange

${result} = Execute AWSS3APICli and checkrc get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/zerobyte --range bytes=0-1 /tmp/testfile2.result 255
Should contain ${result} InvalidRange

${result} = Execute AWSS3APICli and checkrc get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/zerobyte --range bytes=0-10000 /tmp/testfile2.result 255
Should contain ${result} InvalidRange
${result} = Execute ozone sh bucket info /s3v/${BUCKET}
${linked} = Execute echo '${result}' | jq -j '.sourceVolume,"/",.sourceBucket'
${eval} = Evaluate "source" in """${linked}"""
IF ${eval} == ${True}
${result} = Execute ozone sh bucket info ${linked}
END
${fsolayout} = Evaluate "OPTIMIZED" in """${result}"""

${result} = Execute AWSS3APICli and checkrc get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/zerobyte --range bytes=0-0 /tmp/testfile2.result 255
IF ${fsolayout} == ${True}
Should contain ${result} NoSuchKey
ELSE
Should contain ${result} InvalidRange
END

${result} = Execute AWSS3APICli and checkrc get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/zerobyte --range bytes=0-1 /tmp/testfile2.result 255
IF ${fsolayout} == ${True}
Should contain ${result} NoSuchKey
ELSE
Should contain ${result} InvalidRange
END

${result} = Execute AWSS3APICli and checkrc get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/zerobyte --range bytes=0-10000 /tmp/testfile2.result 255
IF ${fsolayout} == ${True}
Should contain ${result} NoSuchKey
ELSE
Should contain ${result} InvalidRange
END

Create file with user defined metadata
Execute echo "Randomtext" > /tmp/testfile2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ public Response get(
OzoneKeyDetails keyDetails = getClientProtocol()
.getS3KeyDetails(bucketName, keyPath);

isFile(keyPath, keyDetails);

long length = keyDetails.getDataSize();

LOG.debug("Data length of the key {} is {}", keyPath, length);
Expand Down Expand Up @@ -503,6 +505,8 @@ public Response head(
OzoneKey key;
try {
key = getClientProtocol().headS3Object(bucketName, keyPath);

isFile(keyPath, key);
// TODO: return the specified range bytes of this object.
} catch (OMException ex) {
AUDIT.logReadFailure(
Expand Down Expand Up @@ -536,6 +540,22 @@ public Response head(
return response.build();
}

private void isFile(String keyPath, OzoneKey key) throws OMException {
/*
Necessary for directories in buckets with FSO layout.
Intended for apps which use Hadoop S3A.
Example of such app is Trino (through Hive connector).
*/
boolean isFsoDirCreationEnabled = ozoneConfiguration
.getBoolean(OZONE_S3G_FSO_DIRECTORY_CREATION_ENABLED,
OZONE_S3G_FSO_DIRECTORY_CREATION_ENABLED_DEFAULT);
if (isFsoDirCreationEnabled &&
!key.isFile() &&
!keyPath.endsWith("/")) {
throw new OMException(ResultCodes.KEY_NOT_FOUND);
}
}

/**
* Abort multipart upload request.
* @param bucket
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public void close() throws IOException {
System.currentTimeMillis(),
System.currentTimeMillis(),
new ArrayList<>(), replicationConfig, metadata, null,
() -> readKey(key)
() -> readKey(key), true
));
super.close();
}
Expand Down Expand Up @@ -183,7 +183,7 @@ public void close() throws IOException {
System.currentTimeMillis(),
System.currentTimeMillis(),
new ArrayList<>(), finalReplicationCon, metadata, null,
() -> readKey(key)
() -> readKey(key), true
));
super.close();
}
Expand Down Expand Up @@ -215,7 +215,8 @@ public OzoneKey headObject(String key) throws IOException {
ozoneKeyDetails.getDataSize(),
ozoneKeyDetails.getCreationTime().toEpochMilli(),
ozoneKeyDetails.getModificationTime().toEpochMilli(),
ozoneKeyDetails.getReplicationConfig());
ozoneKeyDetails.getReplicationConfig(),
ozoneKeyDetails.isFile());
} else {
throw new OMException(ResultCodes.KEY_NOT_FOUND);
}
Expand Down Expand Up @@ -446,4 +447,17 @@ public void setReplicationConfig(ReplicationConfig replicationConfig) {
public ReplicationConfig getReplicationConfig() {
return this.replicationConfig;
}

@Override
public void createDirectory(String keyName) throws IOException {
keyDetails.put(keyName, new OzoneKeyDetails(
getVolumeName(),
getName(),
keyName,
0,
System.currentTimeMillis(),
System.currentTimeMillis(),
new ArrayList<>(), replicationConfig, new HashMap<>(), null,
() -> readKey(keyName), false));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.mockito.Mockito;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_FSO_DIRECTORY_CREATION_ENABLED;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.NO_SUCH_KEY;
import static org.apache.hadoop.ozone.s3.util.S3Consts.RANGE_HEADER;
import static org.mockito.Mockito.doReturn;

Expand Down Expand Up @@ -218,4 +221,26 @@ private void setDefaultHeader() {
doReturn(CONTENT_ENCODING1)
.when(headers).getHeaderString("Content-Encoding");
}

@Test
public void testGetWhenKeyIsDirectoryAndDoesNotEndWithASlash()
throws IOException {
// GIVEN
final String bucketName = "b1";
final String keyPath = "keyDir";
OzoneConfiguration config = new OzoneConfiguration();
config.set(OZONE_S3G_FSO_DIRECTORY_CREATION_ENABLED, "true");
rest.setOzoneConfiguration(config);
OzoneBucket bucket = client.getObjectStore().getS3Bucket(bucketName);
bucket.createDirectory(keyPath);

// WHEN
final OS3Exception ex =
Assertions.assertThrows(OS3Exception.class,
() -> rest.get(bucketName, keyPath, null, 0, null));

// THEN
Assertions.assertEquals(NO_SUCH_KEY.getCode(), ex.getCode());
bucket.deleteKey(keyPath);
}
}
Loading