Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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 @@ -1024,6 +1024,37 @@ public void setTimes(String keyName, long mtime, long atime)
proxy.setTimes(ozoneObj, keyName, mtime, atime);
}

/**
* Gets the tags for an existing key.
* @param keyName Key name.
* @return Tags for the specified key.
* @throws IOException
*/
public Map<String, String> getObjectTagging(String keyName)
throws IOException {
return proxy.getObjectTagging(volumeName, name, keyName);
}

/**
* Sets the tags to an existing key.
* @param keyName Key name.
* @param tags Tags to set on the key.
* @throws IOException
*/
public void putObjectTagging(String keyName, Map<String, String> tags)
throws IOException {
proxy.putObjectTagging(volumeName, name, keyName, tags);
}

/**
* Removes all the tags from an existing key.
* @param keyName Key name
* @throws IOException
*/
public void deleteObjectTagging(String keyName) throws IOException {
proxy.deleteObjectTagging(volumeName, name, keyName);
}

public void setSourcePathExist(boolean b) {
this.sourcePathExist = b;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1290,4 +1290,38 @@ List<OzoneSnapshotDiff> listSnapshotDiffJobs(String volumeName,
* */
void setTimes(OzoneObj obj, String keyName, long mtime, long atime)
throws IOException;

/**
* Gets the tags for an existing key.
* @param volumeName Volume name.
* @param bucketName Bucket name.
* @param keyName Key name.
* @return tags for the specified key.
* @throws IOException
*/
Map<String, String> getObjectTagging(String volumeName, String bucketName, String keyName)
throws IOException;

/**
* Sets the tags to an existing key.
* @param volumeName Volume name.
* @param bucketName Bucket name.
* @param keyName Key name.
* @param tags Tags to set on the key.
* @throws IOException
*/
void putObjectTagging(String volumeName, String bucketName, String keyName,
Map<String, String> tags) throws IOException;


/**
* Removes all the tags from the specified key.
* @param volumeName Volume name.
* @param bucketName Bucket name.
* @param keyName Key name.
* @throws IOException
*/
void deleteObjectTagging(String volumeName, String bucketName, String keyName)
throws IOException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -2645,6 +2645,61 @@ public void setTimes(OzoneObj obj, String keyName, long mtime, long atime)
ozoneManagerClient.setTimes(builder.build(), mtime, atime);
}

@Override
public Map<String, String> getObjectTagging(String volumeName, String bucketName, String keyName)
throws IOException {
if (omVersion.compareTo(OzoneManagerVersion.OBJECT_TAG) < 0) {
throw new IOException("OzoneManager does not support object tags");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OzoneManagerVersion.OBJECT_TAG was introduced in 8a23991. Any OM built since that commit but preceding this change does not support these methods. So request from client to such OM would fail with InvalidProtocolBufferException. Failing with IOException("OzoneManager does not support object tags") might be friendlier, but we need a new OzoneManagerVersion for that.

CC @errose28

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduced a new OzoneManagerVersion.


verifyVolumeName(volumeName);
verifyBucketName(bucketName);
Preconditions.checkNotNull(keyName);
OmKeyArgs keyArgs = new OmKeyArgs.Builder()
.setVolumeName(volumeName)
.setBucketName(bucketName)
.setKeyName(keyName)
.build();
return ozoneManagerClient.getObjectTagging(keyArgs);
}

@Override
public void putObjectTagging(String volumeName, String bucketName,
String keyName, Map<String, String> tags) throws IOException {
if (omVersion.compareTo(OzoneManagerVersion.OBJECT_TAG) < 0) {
throw new IOException("OzoneManager does not support object tags");
}

verifyVolumeName(volumeName);
verifyBucketName(bucketName);
Preconditions.checkNotNull(keyName);
OmKeyArgs keyArgs = new OmKeyArgs.Builder()
.setVolumeName(volumeName)
.setBucketName(bucketName)
.setKeyName(keyName)
.addAllTags(tags)
.build();
ozoneManagerClient.putObjectTagging(keyArgs);
}

@Override
public void deleteObjectTagging(String volumeName, String bucketName,
String keyName) throws IOException {
if (omVersion.compareTo(OzoneManagerVersion.OBJECT_TAG) < 0) {
throw new IOException("OzoneManager does not support object tags");
}

verifyVolumeName(volumeName);
verifyBucketName(bucketName);
Preconditions.checkNotNull(keyName);
OmKeyArgs keyArgs = new OmKeyArgs.Builder()
.setVolumeName(volumeName)
.setBucketName(bucketName)
.setKeyName(keyName)
.build();
ozoneManagerClient.deleteObjectTagging(keyArgs);
}

private static ExecutorService createThreadPoolExecutor(
int corePoolSize, int maximumPoolSize, String threadNameFormat) {
return new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ public static boolean isReadOnly(
case SetSafeMode:
case PrintCompactionLogDag:
case GetSnapshotInfo:
case GetObjectTagging:
return true;
case CreateVolume:
case SetVolumeProperty:
Expand Down Expand Up @@ -326,6 +327,8 @@ public static boolean isReadOnly(
case SetTimes:
case AbortExpiredMultiPartUploads:
case SetSnapshotProperty:
case PutObjectTagging:
case DeleteObjectTagging:
case UnknownCommand:
return false;
case EchoRPC:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
* Protocol for OmMetadataReader's.
Expand Down Expand Up @@ -165,4 +166,11 @@ ListKeysLightResult listKeysLight(String volumeName, String bucketName,
* @throws IOException if there is error.
*/
List<OzoneAcl> getAcl(OzoneObj obj) throws IOException;

/**
* Gets the tags for the specified key.
* @param args Key args
* @return Tags associated with the key.
*/
Map<String, String> getObjectTagging(OmKeyArgs args) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ private KeyValueUtil() {
/**
* Parse Key,Value map data from protobuf representation.
*/
public static Map<String, String> getFromProtobuf(List<KeyValue> metadata) {
return metadata.stream()
public static Map<String, String> getFromProtobuf(List<KeyValue> keyValueList) {
return keyValueList.stream()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can avoid this change as it is only variable name change in the whole file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. I changed it since the user of this method is not for metadata only (i.e. tags).

.collect(Collectors.toMap(KeyValue::getKey,
KeyValue::getValue));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1152,4 +1152,31 @@ void setTimes(OmKeyArgs keyArgs, long mtime, long atime)
*/
boolean setSafeMode(SafeModeAction action, boolean isChecked)
throws IOException;

/**
* Gets the tags for the specified key.
* @param args Key args
* @return Tags associated with the key.
*/
Map<String, String> getObjectTagging(OmKeyArgs args) throws IOException;


/**
* Sets the tags to an existing key.
* @param args Key args
*/
default void putObjectTagging(OmKeyArgs args) throws IOException {
throw new UnsupportedOperationException("OzoneManager does not require " +
"this to be implemented, as write requests use a new approach.");
}

/**
* Removes all the tags from the specified key.
* @param args Key args
*/
default void deleteObjectTagging(OmKeyArgs args) throws IOException {
throw new UnsupportedOperationException("OzoneManager does not require " +
"this to be implemented, as write requests use a new approach.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeyArgs;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeyRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeysRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteObjectTaggingRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteSnapshotRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteTenantRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteTenantResponse;
Expand All @@ -124,6 +125,8 @@
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetFileStatusResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetKeyInfoRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetKeyInfoResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetObjectTaggingRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetObjectTaggingResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3VolumeContextRequest;
Expand Down Expand Up @@ -173,6 +176,7 @@
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrintCompactionLogDagRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PutObjectTaggingRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RangerBGSyncRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RangerBGSyncResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RecoverLeaseRequest;
Expand Down Expand Up @@ -2598,6 +2602,72 @@ public boolean setSafeMode(SafeModeAction action, boolean isChecked)
return setSafeModeResponse.getResponse();
}

@Override
public Map<String, String> getObjectTagging(OmKeyArgs args) throws IOException {
KeyArgs keyArgs = KeyArgs.newBuilder()
.setVolumeName(args.getVolumeName())
.setBucketName(args.getBucketName())
.setKeyName(args.getKeyName())
.build();

GetObjectTaggingRequest req =
GetObjectTaggingRequest.newBuilder()
.setKeyArgs(keyArgs)
.build();

OMRequest omRequest = createOMRequest(Type.GetObjectTagging)
.setGetObjectTaggingRequest(req)
.build();

GetObjectTaggingResponse resp =
handleError(submitRequest(omRequest)).getGetObjectTaggingResponse();

return KeyValueUtil.getFromProtobuf(resp.getTagsList());
}

@Override
public void putObjectTagging(OmKeyArgs args) throws IOException {
KeyArgs keyArgs = KeyArgs.newBuilder()
.setVolumeName(args.getVolumeName())
.setBucketName(args.getBucketName())
.setKeyName(args.getKeyName())
.addAllTags(KeyValueUtil.toProtobuf(args.getTags()))
.build();

PutObjectTaggingRequest req =
PutObjectTaggingRequest.newBuilder()
.setKeyArgs(keyArgs)
.build();

OMRequest omRequest = createOMRequest(Type.PutObjectTagging)
.setPutObjectTaggingRequest(req)
.build();

OMResponse omResponse = submitRequest(omRequest);
handleError(omResponse);
}

@Override
public void deleteObjectTagging(OmKeyArgs args) throws IOException {
KeyArgs keyArgs = KeyArgs.newBuilder()
.setVolumeName(args.getVolumeName())
.setBucketName(args.getBucketName())
.setKeyName(args.getKeyName())
.build();

DeleteObjectTaggingRequest req =
DeleteObjectTaggingRequest.newBuilder()
.setKeyArgs(keyArgs)
.build();

OMRequest omRequest = createOMRequest(Type.DeleteObjectTagging)
.setDeleteObjectTaggingRequest(req)
.build();

OMResponse omResponse = submitRequest(omRequest);
handleError(omResponse);
}

private SafeMode toProtoBuf(SafeModeAction action) {
switch (action) {
case ENTER:
Expand Down
1 change: 1 addition & 0 deletions hadoop-ozone/dist/src/main/smoketest/s3/objectputget.robot
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Put object to s3
Get object from s3
${result} = Execute AWSS3ApiCli get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/f1 /tmp/testfile.result
Compare files /tmp/testfile /tmp/testfile.result
Should not contain ${result} TagCount
${result} = Execute AWSS3ApiCli get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/zerobyte /tmp/zerobyte.result
Compare files /tmp/zerobyte /tmp/zerobyte.result

Expand Down
69 changes: 69 additions & 0 deletions hadoop-ozone/dist/src/main/smoketest/s3/objecttagging.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# 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

*** Settings ***
Documentation S3 gateway test with aws cli
Library OperatingSystem
Library String
Resource ../commonlib.robot
Resource commonawslib.robot
Test Timeout 5 minutes
Suite Setup Setup s3 tests

*** Variables ***
${ENDPOINT_URL} http://s3g:9878
${OZONE_TEST} true
${BUCKET} generated


*** Test Cases ***

Put object tagging

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to add negative test cases (e.g. request for non-existent key). Can be done in follow-up.

Also would be nice to create reusable keywords to avoid duplication. Also can be in follow-up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. I added for case non-existent key. The other cases like empty tag key and empty tag values are usually thrown in the AWS SDK client-side. I added the validation server-side just in case.

@ivandika3 ivandika3 Nov 11, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed https://issues.apache.org/jira/browse/HDDS-11678 for extracting the object tagging keywords.

# Create an object and call put-object-tagging
Execute echo "Randomtext" > /tmp/testfile
${result} = Execute AWSS3ApiCli put-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/f1 --body /tmp/testfile
${result} = Execute AWSS3ApiCli list-objects --bucket ${BUCKET} --prefix ${PREFIX}/putobject/key=value/
Should contain ${result} f1

${result} = Execute AWSS3ApiCli put-object-tagging --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/f1 --tagging '{"TagSet": [{ "Key": "tag-key1", "Value": "tag-value1" }]}'
${result} = Execute AWSS3APICli get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/f1 /tmp/testfile2.result
Should contain ${result} TagCount
${tagCount} = Execute and checkrc echo '${result}' | jq -r '.TagCount' 0
Should Be Equal ${tagCount} 1

# Calling put-object-tagging again to overwrite the existing tags
${result} = Execute AWSS3ApiCli put-object-tagging --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/f1 --tagging '{"TagSet": [{ "Key": "tag-key2", "Value": "tag-value2" },{ "Key": "tag-key3", "Value": "tag-value3" }]}'
${result} = Execute AWSS3APICli get-object --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/f1 /tmp/testfile2.result
Should contain ${result} TagCount
${tagCount} = Execute and checkrc echo '${result}' | jq -r '.TagCount' 0
Should Be Equal ${tagCount} 2

#This test depends on the previous test case. Can't be executes alone
Get object tagging

${result} = Execute AWSS3ApiCli get-object-tagging --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/f1
Should contain ${result} TagSet
${tagCount} = Execute and checkrc echo '${result}' | jq '.TagSet | length' 0
Should Be Equal ${tagCount} 2


#This test depends on the previous test case. Can't be executes alone
Delete object tagging

${result} = Execute AWSS3ApiCli delete-object-tagging --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/f1
${result} = Execute AWSS3ApiCli get-object-tagging --bucket ${BUCKET} --key ${PREFIX}/putobject/key=value/f1
Should contain ${result} TagSet
${tagCount} = Execute and checkrc echo '${result}' | jq '.TagSet | length' 0
Should Be Equal ${tagCount} 0
Loading