Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public enum ClientVersion implements ComponentVersion {
ERASURE_CODING_SUPPORT(2, "This client version has support for Erasure"
+ " Coding."),

BUCKET_LAYOUT_SUPPORT(3,
"This client version has support for Object Store and File " +
"System Optimized Bucket Layouts."),

FUTURE_VERSION(-1, "Used internally when the server side is older and an"
+ " unknown client version has arrived from the client.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.hadoop.ozone.om.helpers;

import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;

/**
Expand Down Expand Up @@ -88,4 +89,20 @@ public static BucketLayout fromString(String value) {
// Added safer `isBlank` check for unit test cases.
return StringUtils.isBlank(value) ? LEGACY : BucketLayout.valueOf(value);
}

/**
* Helper method for upgrade scenarios. Throws an exception if a bucket layout
* is not supported on an older client.
*
* @throws OMException if bucket layout is not supported on older clients.
*/
public void validateSupportedOperation() throws OMException {
// Older clients do not support any bucket layout other than LEGACY.
if (!isLegacy()) {
throw new OMException("Client is attempting to modify a bucket which" +
" uses non-LEGACY bucket layout features. Please upgrade the client" +
" to a compatible version before performing this operation.",
OMException.ResultCodes.NOT_SUPPORTED_OPERATION);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.common.base.Preconditions;
import com.google.protobuf.RpcController;
import org.apache.hadoop.ozone.ClientVersion;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.permission.FsPermission;
Expand Down Expand Up @@ -446,6 +447,7 @@ FileStatus getStatus() {
try {
omRequest = OzoneManagerProtocolProtos.OMRequest.newBuilder()
.setClientId(CLIENT_ID.toString())
.setVersion(ClientVersion.CURRENT_VERSION)
.setUserInfo(getUserInfo())
.setRenameKeyRequest(renameKeyRequest)
.setCmdType(OzoneManagerProtocolProtos.Type.RenameKey)
Expand Down Expand Up @@ -510,6 +512,7 @@ private OzoneManagerProtocolProtos.OMRequest getDeleteKeyRequest(
omRequest =
OzoneManagerProtocolProtos.OMRequest.newBuilder()
.setClientId(CLIENT_ID.toString())
.setVersion(ClientVersion.CURRENT_VERSION)
.setUserInfo(getUserInfo())
.setDeleteKeyRequest(deleteKeyRequest)
.setCmdType(OzoneManagerProtocolProtos.Type.DeleteKey)
Expand Down Expand Up @@ -577,6 +580,7 @@ boolean processKeyPath(List<String> keyPathList) {
try {
omRequest = OzoneManagerProtocolProtos.OMRequest.newBuilder()
.setClientId(CLIENT_ID.toString())
.setVersion(ClientVersion.CURRENT_VERSION)
.setUserInfo(getUserInfo())
.setDeleteKeysRequest(deleteKeysRequest)
.setCmdType(OzoneManagerProtocolProtos.Type.DeleteKeys)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@
import java.util.Map;

import com.google.common.base.Optional;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
import org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.om.request.validation.ValidationCondition;
import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -42,6 +47,7 @@
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.DeleteBucketRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
Expand Down Expand Up @@ -192,4 +198,32 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager,
}
}

/**
* Validates bucket delete requests.
* Handles the cases where an older client attempts to delete a bucket
* a new bucket layout.
* We do not want to allow this to happen, since this would cause the client
* to be able to delete buckets it cannot understand.
*
* @param req - the request to validate
* @param ctx - the validation context
* @return the validated request
* @throws OMException if the request is invalid
*/
@RequestFeatureValidator(
conditions = ValidationCondition.OLDER_CLIENT_REQUESTS,
processingPhase = RequestProcessingPhase.PRE_PROCESS,
requestType = Type.DeleteBucket
)
public static OMRequest blockBucketDeleteWithBucketLayoutFromOldClient(
OMRequest req, ValidationContext ctx) throws IOException {
DeleteBucketRequest request = req.getDeleteBucketRequest();

if (request.hasBucketName() && request.hasVolumeName()) {
BucketLayout bucketLayout = ctx.getBucketLayout(
request.getVolumeName(), request.getBucketName());
bucketLayout.validateSupportedOperation();
}
return req;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -415,4 +415,35 @@ public static OMRequest disallowCreateDirectoryWithECReplicationConfig(
}
return req;
}

/**
* Validates directory create requests.
* Handles the cases where an older client attempts to create a directory
* inside a bucket with a non LEGACY bucket layout.
* We do not want an older client modifying a bucket that it cannot
* understand.
*
* @param req - the request to validate
* @param ctx - the validation context
* @return the validated request
* @throws OMException if the request is invalid
*/
@RequestFeatureValidator(
conditions = ValidationCondition.OLDER_CLIENT_REQUESTS,
processingPhase = RequestProcessingPhase.PRE_PROCESS,
requestType = Type.CreateDirectory
)
public static OMRequest blockCreateDirectoryWithBucketLayoutFromOldClient(
OMRequest req, ValidationContext ctx) throws IOException {
if (req.getCreateDirectoryRequest().hasKeyArgs()) {
KeyArgs keyArgs = req.getCreateDirectoryRequest().getKeyArgs();

if (keyArgs.hasVolumeName() && keyArgs.hasBucketName()) {
BucketLayout bucketLayout = ctx.getBucketLayout(
keyArgs.getVolumeName(), keyArgs.getBucketName());
bucketLayout.validateSupportedOperation();
}
}
return req;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
import org.apache.hadoop.ozone.om.response.file.OMFileCreateResponse;
import org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -60,7 +62,6 @@
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateFileRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateFileResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
Expand Down Expand Up @@ -408,4 +409,36 @@ public static OMRequest disallowCreateFileWithECReplicationConfig(
}
return req;
}

/**
* Validates file create requests.
* Handles the cases where an older client attempts to create a file
* inside a bucket with a non LEGACY bucket layout.
* We do not want an older client modifying a bucket that it cannot
* understand.
*
* @param req - the request to validate
* @param ctx - the validation context
* @return the validated request
* @throws OMException if the request is invalid
*/
@RequestFeatureValidator(
conditions = ValidationCondition.OLDER_CLIENT_REQUESTS,
processingPhase = RequestProcessingPhase.PRE_PROCESS,
requestType = Type.CreateFile
)
public static OMRequest blockCreateFileWithBucketLayoutFromOldClient(
OMRequest req, ValidationContext ctx) throws IOException {
if (req.getCreateFileRequest().hasKeyArgs()) {

KeyArgs keyArgs = req.getCreateFileRequest().getKeyArgs();

if (keyArgs.hasVolumeName() && keyArgs.hasBucketName()) {
BucketLayout bucketLayout = ctx.getBucketLayout(
keyArgs.getVolumeName(), keyArgs.getBucketName());
bucketLayout.validateSupportedOperation();
}
}
return req;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,33 @@ public static OMRequest disallowAllocateBlockWithECReplicationConfig(
}
return req;
}

/**
* Validates block allocation requests.
* We do not want to allow older clients to create block allocation requests
* for keys that are present in buckets which use non LEGACY layouts.
*
* @param req - the request to validate
* @param ctx - the validation context
* @return the validated request
* @throws OMException if the request is invalid
*/
@RequestFeatureValidator(
conditions = ValidationCondition.OLDER_CLIENT_REQUESTS,
processingPhase = RequestProcessingPhase.PRE_PROCESS,
requestType = Type.AllocateBlock
)
public static OMRequest blockAllocateBlockWithBucketLayoutFromOldClient(
OMRequest req, ValidationContext ctx) throws IOException {
if (req.getAllocateBlockRequest().hasKeyArgs()) {
KeyArgs keyArgs = req.getAllocateBlockRequest().getKeyArgs();

if (keyArgs.hasVolumeName() && keyArgs.hasBucketName()) {
BucketLayout bucketLayout = ctx.getBucketLayout(
keyArgs.getVolumeName(), keyArgs.getBucketName());
bucketLayout.validateSupportedOperation();
}
}
return req;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,33 @@ public static OMRequest disallowCommitKeyWithECReplicationConfig(
}
return req;
}

/**
* Validates key commit requests.
* We do not want to allow older clients to commit keys associated with
* buckets which use non LEGACY layouts.
*
* @param req - the request to validate
* @param ctx - the validation context
* @return the validated request
* @throws OMException if the request is invalid
*/
@RequestFeatureValidator(
conditions = ValidationCondition.OLDER_CLIENT_REQUESTS,
processingPhase = RequestProcessingPhase.PRE_PROCESS,
requestType = Type.CommitKey
)
public static OMRequest blockCommitKeyWithBucketLayoutFromOldClient(
OMRequest req, ValidationContext ctx) throws IOException {
if (req.getCommitKeyRequest().hasKeyArgs()) {
KeyArgs keyArgs = req.getCommitKeyRequest().getKeyArgs();

if (keyArgs.hasVolumeName() && keyArgs.hasBucketName()) {
BucketLayout bucketLayout = ctx.getBucketLayout(
keyArgs.getVolumeName(), keyArgs.getBucketName());
bucketLayout.validateSupportedOperation();
}
}
return req;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,33 @@ public static OMRequest disallowCreateKeyWithECReplicationConfig(
}
return req;
}

/**
* Validates key create requests.
* We do not want to allow older clients to create keys in buckets which use
* non LEGACY layouts.
*
* @param req - the request to validate
* @param ctx - the validation context
* @return the validated request
* @throws OMException if the request is invalid
*/
@RequestFeatureValidator(
conditions = ValidationCondition.OLDER_CLIENT_REQUESTS,
processingPhase = RequestProcessingPhase.PRE_PROCESS,
requestType = Type.CreateKey
)
public static OMRequest blockCreateKeyWithBucketLayoutFromOldClient(
OMRequest req, ValidationContext ctx) throws IOException {
if (req.getCreateKeyRequest().hasKeyArgs()) {
KeyArgs keyArgs = req.getCreateKeyRequest().getKeyArgs();

if (keyArgs.hasVolumeName() && keyArgs.hasBucketName()) {
BucketLayout bucketLayout = ctx.getBucketLayout(
keyArgs.getVolumeName(), keyArgs.getBucketName());
bucketLayout.validateSupportedOperation();
}
}
return req;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.request.validation.RequestFeatureValidator;
import org.apache.hadoop.ozone.om.request.validation.RequestProcessingPhase;
import org.apache.hadoop.ozone.om.request.validation.ValidationCondition;
import org.apache.hadoop.ozone.om.request.validation.ValidationContext;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
import org.slf4j.Logger;
Expand All @@ -42,6 +46,8 @@
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.om.response.key.OMKeyDeleteResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeyRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteKeyResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
Expand Down Expand Up @@ -209,4 +215,33 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager,

return omClientResponse;
}

/**
* Validates key delete requests.
* We do not want to allow older clients to delete keys in buckets which use
* non LEGACY layouts.
*
* @param req - the request to validate
* @param ctx - the validation context
* @return the validated request
* @throws OMException if the request is invalid
*/
@RequestFeatureValidator(
conditions = ValidationCondition.OLDER_CLIENT_REQUESTS,
processingPhase = RequestProcessingPhase.PRE_PROCESS,
requestType = Type.DeleteKey
)
public static OMRequest blockDeleteKeyWithBucketLayoutFromOldClient(
OMRequest req, ValidationContext ctx) throws IOException {
if (req.getDeleteKeyRequest().hasKeyArgs()) {
KeyArgs keyArgs = req.getDeleteKeyRequest().getKeyArgs();

if (keyArgs.hasVolumeName() && keyArgs.hasBucketName()) {
BucketLayout bucketLayout = ctx.getBucketLayout(
keyArgs.getVolumeName(), keyArgs.getBucketName());
bucketLayout.validateSupportedOperation();
}
}
return req;
}
}
Loading