HDDS-1543. Implement addAcl,removeAcl,setAcl,getAcl for Prefix. Contr… (#927)
This commit is contained in:
parent
205dd2d8e1
commit
a43f4440f7
|
@ -46,6 +46,9 @@ import static org.apache.hadoop.ozone.OzoneConsts.OM_USER_PREFIX;
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> 2 </td> <td> Bucket Lock </td>
|
* <td> 2 </td> <td> Bucket Lock </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td> 3 </td> <td> Prefix Lock </td>
|
||||||
|
* </tr>
|
||||||
* </table>
|
* </table>
|
||||||
*
|
*
|
||||||
* One cannot obtain a lower weight lock while holding a lock with higher
|
* One cannot obtain a lower weight lock while holding a lock with higher
|
||||||
|
@ -66,6 +69,7 @@ public final class OzoneManagerLock {
|
||||||
|
|
||||||
private static final String VOLUME_LOCK = "volumeLock";
|
private static final String VOLUME_LOCK = "volumeLock";
|
||||||
private static final String BUCKET_LOCK = "bucketLock";
|
private static final String BUCKET_LOCK = "bucketLock";
|
||||||
|
private static final String PREFIX_LOCK = "prefixLock";
|
||||||
private static final String S3_BUCKET_LOCK = "s3BucketLock";
|
private static final String S3_BUCKET_LOCK = "s3BucketLock";
|
||||||
private static final String S3_SECRET_LOCK = "s3SecretetLock";
|
private static final String S3_SECRET_LOCK = "s3SecretetLock";
|
||||||
|
|
||||||
|
@ -77,6 +81,7 @@ public final class OzoneManagerLock {
|
||||||
() -> ImmutableMap.of(
|
() -> ImmutableMap.of(
|
||||||
VOLUME_LOCK, new AtomicInteger(0),
|
VOLUME_LOCK, new AtomicInteger(0),
|
||||||
BUCKET_LOCK, new AtomicInteger(0),
|
BUCKET_LOCK, new AtomicInteger(0),
|
||||||
|
PREFIX_LOCK, new AtomicInteger(0),
|
||||||
S3_BUCKET_LOCK, new AtomicInteger(0),
|
S3_BUCKET_LOCK, new AtomicInteger(0),
|
||||||
S3_SECRET_LOCK, new AtomicInteger(0)
|
S3_SECRET_LOCK, new AtomicInteger(0)
|
||||||
)
|
)
|
||||||
|
@ -241,4 +246,24 @@ public final class OzoneManagerLock {
|
||||||
manager.unlock(awsAccessId);
|
manager.unlock(awsAccessId);
|
||||||
myLocks.get().get(S3_SECRET_LOCK).decrementAndGet();
|
myLocks.get().get(S3_SECRET_LOCK).decrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void acquirePrefixLock(String prefixPath) {
|
||||||
|
if (hasAnyPrefixLock()) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Thread '" + Thread.currentThread().getName() +
|
||||||
|
"' cannot acquire prefix path lock while holding prefix " +
|
||||||
|
"path lock(s) for path: " + prefixPath + ".");
|
||||||
|
}
|
||||||
|
manager.lock(prefixPath);
|
||||||
|
myLocks.get().get(PREFIX_LOCK).incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasAnyPrefixLock() {
|
||||||
|
return myLocks.get().get(PREFIX_LOCK).get() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void releasePrefixLock(String prefixPath) {
|
||||||
|
manager.unlock(prefixPath);
|
||||||
|
myLocks.get().get(PREFIX_LOCK).decrementAndGet();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,6 +199,9 @@ public class OMException extends IOException {
|
||||||
|
|
||||||
PERMISSION_DENIED, // Error codes used during acl validation
|
PERMISSION_DENIED, // Error codes used during acl validation
|
||||||
|
|
||||||
TIMEOUT // Error codes used during acl validation
|
TIMEOUT, // Error codes used during acl validation
|
||||||
|
|
||||||
|
PREFIX_NOT_FOUND,
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,19 @@ public abstract class OzoneObj implements IOzoneObj {
|
||||||
|
|
||||||
public abstract String getKeyName();
|
public abstract String getKeyName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get PrefixName.
|
||||||
|
* A prefix name is like a key name under the bucket but
|
||||||
|
* are mainly used for ACL for now and persisted into a separate prefix table.
|
||||||
|
*
|
||||||
|
* @return prefix name.
|
||||||
|
*/
|
||||||
|
public abstract String getPrefixName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get full path of a key or prefix including volume and bucket.
|
||||||
|
* @return full path of a key or prefix.
|
||||||
|
*/
|
||||||
public abstract String getPath();
|
public abstract String getPath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,7 +92,8 @@ public abstract class OzoneObj implements IOzoneObj {
|
||||||
public enum ResourceType {
|
public enum ResourceType {
|
||||||
VOLUME(OzoneConsts.VOLUME),
|
VOLUME(OzoneConsts.VOLUME),
|
||||||
BUCKET(OzoneConsts.BUCKET),
|
BUCKET(OzoneConsts.BUCKET),
|
||||||
KEY(OzoneConsts.KEY);
|
KEY(OzoneConsts.KEY),
|
||||||
|
PREFIX(OzoneConsts.PREFIX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String value for this Enum.
|
* String value for this Enum.
|
||||||
|
|
|
@ -23,32 +23,51 @@ import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing an ozone object.
|
* Class representing an ozone object.
|
||||||
|
* It can be a volume with non-null volumeName (bucketName=null & name=null)
|
||||||
|
* or a bucket with non-null volumeName and bucketName (name=null)
|
||||||
|
* or a key with non-null volumeName, bucketName and key name
|
||||||
|
* (via getKeyName)
|
||||||
|
* or a prefix with non-null volumeName, bucketName and prefix name
|
||||||
|
* (via getPrefixName)
|
||||||
*/
|
*/
|
||||||
public final class OzoneObjInfo extends OzoneObj {
|
public final class OzoneObjInfo extends OzoneObj {
|
||||||
|
|
||||||
private final String volumeName;
|
private final String volumeName;
|
||||||
private final String bucketName;
|
private final String bucketName;
|
||||||
private final String keyName;
|
private final String name;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param resType
|
||||||
|
* @param storeType
|
||||||
|
* @param volumeName
|
||||||
|
* @param bucketName
|
||||||
|
* @param name - keyName/PrefixName
|
||||||
|
*/
|
||||||
private OzoneObjInfo(ResourceType resType, StoreType storeType,
|
private OzoneObjInfo(ResourceType resType, StoreType storeType,
|
||||||
String volumeName, String bucketName, String keyName) {
|
String volumeName, String bucketName, String name) {
|
||||||
super(resType, storeType);
|
super(resType, storeType);
|
||||||
this.volumeName = volumeName;
|
this.volumeName = volumeName;
|
||||||
this.bucketName = bucketName;
|
this.bucketName = bucketName;
|
||||||
this.keyName = keyName;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
switch (getResourceType()) {
|
switch (getResourceType()) {
|
||||||
case VOLUME:
|
case VOLUME:
|
||||||
return getVolumeName();
|
return OZONE_URI_DELIMITER + getVolumeName();
|
||||||
case BUCKET:
|
case BUCKET:
|
||||||
return getVolumeName() + OZONE_URI_DELIMITER + getBucketName();
|
return OZONE_URI_DELIMITER + getVolumeName()
|
||||||
|
+ OZONE_URI_DELIMITER + getBucketName();
|
||||||
case KEY:
|
case KEY:
|
||||||
return getVolumeName() + OZONE_URI_DELIMITER + getBucketName()
|
return OZONE_URI_DELIMITER + getVolumeName()
|
||||||
|
+ OZONE_URI_DELIMITER + getBucketName()
|
||||||
+ OZONE_URI_DELIMITER + getKeyName();
|
+ OZONE_URI_DELIMITER + getKeyName();
|
||||||
|
case PREFIX:
|
||||||
|
return OZONE_URI_DELIMITER + getVolumeName()
|
||||||
|
+ OZONE_URI_DELIMITER + getBucketName()
|
||||||
|
+ OZONE_URI_DELIMITER + getPrefixName();
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown resource " +
|
throw new IllegalArgumentException("Unknown resource " +
|
||||||
"type" + getResourceType());
|
"type" + getResourceType());
|
||||||
|
@ -67,9 +86,15 @@ public final class OzoneObjInfo extends OzoneObj {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getKeyName() {
|
public String getKeyName() {
|
||||||
return keyName;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPrefixName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static OzoneObjInfo fromProtobuf(OzoneManagerProtocolProtos.OzoneObj
|
public static OzoneObjInfo fromProtobuf(OzoneManagerProtocolProtos.OzoneObj
|
||||||
proto) {
|
proto) {
|
||||||
Builder builder = new Builder()
|
Builder builder = new Builder()
|
||||||
|
@ -88,7 +113,7 @@ public final class OzoneObjInfo extends OzoneObj {
|
||||||
case BUCKET:
|
case BUCKET:
|
||||||
if (tokens.length < 2) {
|
if (tokens.length < 2) {
|
||||||
throw new IllegalArgumentException("Unexpected argument for " +
|
throw new IllegalArgumentException("Unexpected argument for " +
|
||||||
"Ozone key. Path:" + proto.getPath());
|
"Ozone bucket. Path:" + proto.getPath());
|
||||||
}
|
}
|
||||||
builder.setVolumeName(tokens[0]);
|
builder.setVolumeName(tokens[0]);
|
||||||
builder.setBucketName(tokens[1]);
|
builder.setBucketName(tokens[1]);
|
||||||
|
@ -102,6 +127,15 @@ public final class OzoneObjInfo extends OzoneObj {
|
||||||
builder.setBucketName(tokens[1]);
|
builder.setBucketName(tokens[1]);
|
||||||
builder.setKeyName(tokens[2]);
|
builder.setKeyName(tokens[2]);
|
||||||
break;
|
break;
|
||||||
|
case PREFIX:
|
||||||
|
if (tokens.length < 3) {
|
||||||
|
throw new IllegalArgumentException("Unexpected argument for " +
|
||||||
|
"Ozone Prefix. Path:" + proto.getPath());
|
||||||
|
}
|
||||||
|
builder.setVolumeName(tokens[0]);
|
||||||
|
builder.setBucketName(tokens[1]);
|
||||||
|
builder.setPrefixName(tokens[2]);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unexpected type for " +
|
throw new IllegalArgumentException("Unexpected type for " +
|
||||||
"Ozone key. Type:" + proto.getResType());
|
"Ozone key. Type:" + proto.getResType());
|
||||||
|
@ -118,7 +152,7 @@ public final class OzoneObjInfo extends OzoneObj {
|
||||||
private OzoneObj.StoreType storeType;
|
private OzoneObj.StoreType storeType;
|
||||||
private String volumeName;
|
private String volumeName;
|
||||||
private String bucketName;
|
private String bucketName;
|
||||||
private String keyName;
|
private String name;
|
||||||
|
|
||||||
public static Builder newBuilder() {
|
public static Builder newBuilder() {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
|
@ -145,14 +179,17 @@ public final class OzoneObjInfo extends OzoneObj {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setKeyName(String key) {
|
public Builder setKeyName(String key) {
|
||||||
this.keyName = key;
|
this.name = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setPrefixName(String prefix) {
|
||||||
|
this.name = prefix;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OzoneObjInfo build() {
|
public OzoneObjInfo build() {
|
||||||
return new OzoneObjInfo(resType, storeType, volumeName, bucketName,
|
return new OzoneObjInfo(resType, storeType, volumeName, bucketName, name);
|
||||||
keyName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,9 +202,15 @@ public class RadixTree<T> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return level >= 1 ?
|
|
||||||
Paths.get(root.getName()).resolve(p.subpath(0, level)).toString() :
|
if (level >= 1) {
|
||||||
root.getName();
|
Path longestMatch =
|
||||||
|
Paths.get(root.getName()).resolve(p.subpath(0, level));
|
||||||
|
String ret = longestMatch.toString();
|
||||||
|
return path.endsWith("/") ? ret + "/" : ret;
|
||||||
|
} else {
|
||||||
|
return root.getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// root of a radix tree has a name of "/" and may optionally has it value.
|
// root of a radix tree has a name of "/" and may optionally has it value.
|
||||||
|
|
|
@ -276,6 +276,7 @@ enum Status {
|
||||||
NOT_A_FILE = 47;
|
NOT_A_FILE = 47;
|
||||||
PERMISSION_DENIED = 48;
|
PERMISSION_DENIED = 48;
|
||||||
TIMEOUT = 49;
|
TIMEOUT = 49;
|
||||||
|
PREFIX_NOT_FOUND=50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,6 @@ public class TestRadixTree {
|
||||||
assertEquals("g", lpn.getName());
|
assertEquals("g", lpn.getName());
|
||||||
lpn.setValue(100);
|
lpn.setValue(100);
|
||||||
|
|
||||||
|
|
||||||
List<RadixNode<Integer>> lpq =
|
List<RadixNode<Integer>> lpq =
|
||||||
ROOT.getLongestPrefixPath("/a/b/c/d/g/q");
|
ROOT.getLongestPrefixPath("/a/b/c/d/g/q");
|
||||||
RadixNode<Integer> lqn = lpp.get(lpq.size()-1);
|
RadixNode<Integer> lqn = lpp.get(lpq.size()-1);
|
||||||
|
@ -93,7 +92,6 @@ public class TestRadixTree {
|
||||||
assertEquals("g", lqn.getName());
|
assertEquals("g", lqn.getName());
|
||||||
assertEquals(100, (int)lqn.getValue());
|
assertEquals(100, (int)lqn.getValue());
|
||||||
|
|
||||||
|
|
||||||
assertEquals("/a/", RadixTree.radixPathToString(
|
assertEquals("/a/", RadixTree.radixPathToString(
|
||||||
ROOT.getLongestPrefixPath("/a/g")));
|
ROOT.getLongestPrefixPath("/a/g")));
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.BitSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -2200,6 +2201,66 @@ public abstract class TestOzoneRpcClientAbstract {
|
||||||
validateOzoneAcl(ozObj);
|
validateOzoneAcl(ozObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNativeAclsForPrefix() throws Exception {
|
||||||
|
String volumeName = UUID.randomUUID().toString();
|
||||||
|
String bucketName = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
String prefix1 = "PF" + UUID.randomUUID().toString() + "/";
|
||||||
|
String key1 = prefix1 + "KEY" + UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
String prefix2 = "PF" + UUID.randomUUID().toString() + "/";
|
||||||
|
String key2 = prefix2 + "KEY" + UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
store.createVolume(volumeName);
|
||||||
|
OzoneVolume volume = store.getVolume(volumeName);
|
||||||
|
volume.createBucket(bucketName);
|
||||||
|
OzoneBucket bucket = volume.getBucket(bucketName);
|
||||||
|
assertNotNull("Bucket creation failed", bucket);
|
||||||
|
|
||||||
|
writeKey(key1, bucket);
|
||||||
|
writeKey(key2, bucket);
|
||||||
|
|
||||||
|
OzoneObj ozObj = new OzoneObjInfo.Builder()
|
||||||
|
.setVolumeName(volumeName)
|
||||||
|
.setBucketName(bucketName)
|
||||||
|
.setPrefixName(prefix1)
|
||||||
|
.setResType(OzoneObj.ResourceType.PREFIX)
|
||||||
|
.setStoreType(OzoneObj.StoreType.OZONE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// add acl
|
||||||
|
BitSet aclRights1 = new BitSet();
|
||||||
|
aclRights1.set(ACLType.READ.ordinal());
|
||||||
|
OzoneAcl user1Acl = new OzoneAcl(ACLIdentityType.USER,
|
||||||
|
"user1", aclRights1);
|
||||||
|
assertTrue(store.addAcl(ozObj, user1Acl));
|
||||||
|
|
||||||
|
// get acl
|
||||||
|
List<OzoneAcl> aclsGet = store.getAcl(ozObj);
|
||||||
|
Assert.assertEquals(1, aclsGet.size());
|
||||||
|
Assert.assertEquals(user1Acl, aclsGet.get(0));
|
||||||
|
|
||||||
|
// remove acl
|
||||||
|
Assert.assertTrue(store.removeAcl(ozObj, user1Acl));
|
||||||
|
aclsGet = store.getAcl(ozObj);
|
||||||
|
Assert.assertEquals(0, aclsGet.size());
|
||||||
|
|
||||||
|
// set acl
|
||||||
|
BitSet aclRights2 = new BitSet();
|
||||||
|
aclRights2.set(ACLType.ALL.ordinal());
|
||||||
|
OzoneAcl group1Acl = new OzoneAcl(ACLIdentityType.GROUP,
|
||||||
|
"group1", aclRights2);
|
||||||
|
List<OzoneAcl> acls = new ArrayList<>();
|
||||||
|
acls.add(user1Acl);
|
||||||
|
acls.add(group1Acl);
|
||||||
|
Assert.assertTrue(store.setAcl(ozObj, acls));
|
||||||
|
|
||||||
|
// get acl
|
||||||
|
aclsGet = store.getAcl(ozObj);
|
||||||
|
Assert.assertEquals(2, aclsGet.size());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to get default acl list for current user.
|
* Helper function to get default acl list for current user.
|
||||||
*
|
*
|
||||||
|
@ -2218,8 +2279,7 @@ public abstract class TestOzoneRpcClientAbstract {
|
||||||
listOfAcls.add(new OzoneAcl(ACLIdentityType.USER,
|
listOfAcls.add(new OzoneAcl(ACLIdentityType.USER,
|
||||||
ugi.getUserName(), userRights));
|
ugi.getUserName(), userRights));
|
||||||
//Group ACLs of the User
|
//Group ACLs of the User
|
||||||
List<String> userGroups = Arrays.asList(UserGroupInformation
|
List<String> userGroups = Arrays.asList(ugi.getGroupNames());
|
||||||
.createRemoteUser(ugi.getUserName()).getGroupNames());
|
|
||||||
userGroups.stream().forEach((group) -> listOfAcls.add(
|
userGroups.stream().forEach((group) -> listOfAcls.add(
|
||||||
new OzoneAcl(ACLIdentityType.GROUP, group, groupRights)));
|
new OzoneAcl(ACLIdentityType.GROUP, group, groupRights)));
|
||||||
return listOfAcls;
|
return listOfAcls;
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.apache.hadoop.ozone.om;
|
||||||
|
|
||||||
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
|
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
|
||||||
import org.apache.hadoop.hdds.scm.container.common.helpers.ExcludeList;
|
import org.apache.hadoop.hdds.scm.container.common.helpers.ExcludeList;
|
||||||
import org.apache.hadoop.ozone.OzoneAcl;
|
|
||||||
import org.apache.hadoop.ozone.common.BlockGroup;
|
import org.apache.hadoop.ozone.common.BlockGroup;
|
||||||
import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
|
import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
|
||||||
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
|
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
|
||||||
|
@ -36,7 +35,6 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
|
||||||
.KeyInfo;
|
.KeyInfo;
|
||||||
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
|
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
|
||||||
.KeyLocation;
|
.KeyLocation;
|
||||||
import org.apache.hadoop.ozone.security.acl.OzoneObj;
|
|
||||||
import org.apache.hadoop.utils.BackgroundService;
|
import org.apache.hadoop.utils.BackgroundService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -286,43 +284,4 @@ public interface KeyManager extends OzoneManagerFS {
|
||||||
OmMultipartUploadListParts listParts(String volumeName, String bucketName,
|
OmMultipartUploadListParts listParts(String volumeName, String bucketName,
|
||||||
String keyName, String uploadID, int partNumberMarker,
|
String keyName, String uploadID, int partNumberMarker,
|
||||||
int maxParts) throws IOException;
|
int maxParts) throws IOException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Add acl for Ozone object. Return true if acl is added successfully else
|
|
||||||
* false.
|
|
||||||
* @param obj Ozone object for which acl should be added.
|
|
||||||
* @param acl ozone acl top be added.
|
|
||||||
*
|
|
||||||
* @throws IOException if there is error.
|
|
||||||
* */
|
|
||||||
boolean addAcl(OzoneObj obj, OzoneAcl acl) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove acl for Ozone object. Return true if acl is removed successfully
|
|
||||||
* else false.
|
|
||||||
* @param obj Ozone object.
|
|
||||||
* @param acl Ozone acl to be removed.
|
|
||||||
*
|
|
||||||
* @throws IOException if there is error.
|
|
||||||
* */
|
|
||||||
boolean removeAcl(OzoneObj obj, OzoneAcl acl) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Acls to be set for given Ozone object. This operations reset ACL for
|
|
||||||
* given object to list of ACLs provided in argument.
|
|
||||||
* @param obj Ozone object.
|
|
||||||
* @param acls List of acls.
|
|
||||||
*
|
|
||||||
* @throws IOException if there is error.
|
|
||||||
* */
|
|
||||||
boolean setAcl(OzoneObj obj, List<OzoneAcl> acls) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns list of ACLs for given Ozone object.
|
|
||||||
* @param obj Ozone object.
|
|
||||||
*
|
|
||||||
* @throws IOException if there is error.
|
|
||||||
* */
|
|
||||||
List<OzoneAcl> getAcl(OzoneObj obj) throws IOException;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,6 +237,7 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
|
||||||
private final VolumeManager volumeManager;
|
private final VolumeManager volumeManager;
|
||||||
private final BucketManager bucketManager;
|
private final BucketManager bucketManager;
|
||||||
private final KeyManager keyManager;
|
private final KeyManager keyManager;
|
||||||
|
private final PrefixManagerImpl prefixManager;
|
||||||
private final OMMetrics metrics;
|
private final OMMetrics metrics;
|
||||||
private OzoneManagerHttpServer httpServer;
|
private OzoneManagerHttpServer httpServer;
|
||||||
private final OMStorage omStorage;
|
private final OMStorage omStorage;
|
||||||
|
@ -365,6 +366,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
|
||||||
new ScmClient(scmBlockClient, scmContainerClient), metadataManager,
|
new ScmClient(scmBlockClient, scmContainerClient), metadataManager,
|
||||||
configuration, omStorage.getOmId(), blockTokenMgr, getKmsProvider());
|
configuration, omStorage.getOmId(), blockTokenMgr, getKmsProvider());
|
||||||
|
|
||||||
|
prefixManager = new PrefixManagerImpl(metadataManager);
|
||||||
|
|
||||||
shutdownHook = () -> {
|
shutdownHook = () -> {
|
||||||
saveOmMetrics();
|
saveOmMetrics();
|
||||||
};
|
};
|
||||||
|
@ -3033,6 +3036,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
|
||||||
return bucketManager.addAcl(obj, acl);
|
return bucketManager.addAcl(obj, acl);
|
||||||
case KEY:
|
case KEY:
|
||||||
return keyManager.addAcl(obj, acl);
|
return keyManager.addAcl(obj, acl);
|
||||||
|
case PREFIX:
|
||||||
|
return prefixManager.addAcl(obj, acl);
|
||||||
default:
|
default:
|
||||||
throw new OMException("Unexpected resource type: " +
|
throw new OMException("Unexpected resource type: " +
|
||||||
obj.getResourceType(), INVALID_REQUEST);
|
obj.getResourceType(), INVALID_REQUEST);
|
||||||
|
@ -3057,11 +3062,13 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
|
||||||
switch (obj.getResourceType()) {
|
switch (obj.getResourceType()) {
|
||||||
case VOLUME:
|
case VOLUME:
|
||||||
return volumeManager.removeAcl(obj, acl);
|
return volumeManager.removeAcl(obj, acl);
|
||||||
|
|
||||||
case BUCKET:
|
case BUCKET:
|
||||||
return bucketManager.removeAcl(obj, acl);
|
return bucketManager.removeAcl(obj, acl);
|
||||||
case KEY:
|
case KEY:
|
||||||
return keyManager.removeAcl(obj, acl);
|
return keyManager.removeAcl(obj, acl);
|
||||||
|
case PREFIX:
|
||||||
|
return prefixManager.removeAcl(obj, acl);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new OMException("Unexpected resource type: " +
|
throw new OMException("Unexpected resource type: " +
|
||||||
obj.getResourceType(), INVALID_REQUEST);
|
obj.getResourceType(), INVALID_REQUEST);
|
||||||
|
@ -3090,6 +3097,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
|
||||||
return bucketManager.setAcl(obj, acls);
|
return bucketManager.setAcl(obj, acls);
|
||||||
case KEY:
|
case KEY:
|
||||||
return keyManager.setAcl(obj, acls);
|
return keyManager.setAcl(obj, acls);
|
||||||
|
case PREFIX:
|
||||||
|
return prefixManager.setAcl(obj, acls);
|
||||||
default:
|
default:
|
||||||
throw new OMException("Unexpected resource type: " +
|
throw new OMException("Unexpected resource type: " +
|
||||||
obj.getResourceType(), INVALID_REQUEST);
|
obj.getResourceType(), INVALID_REQUEST);
|
||||||
|
@ -3116,6 +3125,9 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
|
||||||
return bucketManager.getAcl(obj);
|
return bucketManager.getAcl(obj);
|
||||||
case KEY:
|
case KEY:
|
||||||
return keyManager.getAcl(obj);
|
return keyManager.getAcl(obj);
|
||||||
|
case PREFIX:
|
||||||
|
return prefixManager.getAcl(obj);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new OMException("Unexpected resource type: " +
|
throw new OMException("Unexpected resource type: " +
|
||||||
obj.getResourceType(), INVALID_REQUEST);
|
obj.getResourceType(), INVALID_REQUEST);
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.ozone.om;
|
||||||
|
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OmPrefixInfo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles prefix commands.
|
||||||
|
* //TODO: support OzoneManagerFS for ozfs optimization using prefix tree.
|
||||||
|
*/
|
||||||
|
public interface PrefixManager extends IOzoneAcl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the metadataManager.
|
||||||
|
* @return OMMetadataManager.
|
||||||
|
*/
|
||||||
|
OMMetadataManager getMetadataManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of path components that match with obj's path.
|
||||||
|
* longest prefix.
|
||||||
|
* Note: the number of the entries include a root "/"
|
||||||
|
* so if you have a longtest prefix path /a/b/c/
|
||||||
|
* the returned list will be ["/", "a", "b", "c"]
|
||||||
|
* @param path ozone object path
|
||||||
|
* @return list of longest path components that matches obj's path.
|
||||||
|
*/
|
||||||
|
List<OmPrefixInfo> getLongestPrefixPath(String path);
|
||||||
|
}
|
|
@ -0,0 +1,316 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.ozone.om;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import org.apache.hadoop.ozone.OzoneAcl;
|
||||||
|
import org.apache.hadoop.ozone.om.exceptions.OMException;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OmPrefixInfo;
|
||||||
|
import org.apache.hadoop.ozone.security.acl.OzoneObj;
|
||||||
|
import org.apache.hadoop.ozone.util.RadixNode;
|
||||||
|
import org.apache.hadoop.ozone.util.RadixTree;
|
||||||
|
import org.apache.hadoop.utils.db.*;
|
||||||
|
import org.apache.hadoop.utils.db.Table.KeyValue;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.BUCKET_NOT_FOUND;
|
||||||
|
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PREFIX_NOT_FOUND;
|
||||||
|
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.VOLUME_NOT_FOUND;
|
||||||
|
import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.PREFIX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of PreManager.
|
||||||
|
*/
|
||||||
|
public class PrefixManagerImpl implements PrefixManager {
|
||||||
|
private static final Logger LOG =
|
||||||
|
LoggerFactory.getLogger(PrefixManagerImpl.class);
|
||||||
|
|
||||||
|
private static final List<OzoneAcl> EMPTY_ACL_LIST = new ArrayList<>();
|
||||||
|
private final OMMetadataManager metadataManager;
|
||||||
|
|
||||||
|
// In-memory prefix tree to optimize ACL evaluation
|
||||||
|
private RadixTree<OmPrefixInfo> prefixTree;
|
||||||
|
|
||||||
|
public PrefixManagerImpl(OMMetadataManager metadataManager) {
|
||||||
|
this.metadataManager = metadataManager;
|
||||||
|
loadPrefixTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPrefixTree() {
|
||||||
|
prefixTree = new RadixTree<>();
|
||||||
|
try (TableIterator<String, ? extends
|
||||||
|
KeyValue<String, OmPrefixInfo>> iterator =
|
||||||
|
getMetadataManager().getPrefixTable().iterator()) {
|
||||||
|
iterator.seekToFirst();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
KeyValue<String, OmPrefixInfo> kv = iterator.next();
|
||||||
|
prefixTree.insert(kv.getKey(), kv.getValue());
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOG.error("Fail to load prefix tree");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OMMetadataManager getMetadataManager() {
|
||||||
|
return metadataManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add acl for Ozone object. Return true if acl is added successfully else
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @param obj Ozone object for which acl should be added.
|
||||||
|
* @param acl ozone acl top be added.
|
||||||
|
* @throws IOException if there is error.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean addAcl(OzoneObj obj, OzoneAcl acl) throws IOException {
|
||||||
|
validateOzoneObj(obj);
|
||||||
|
|
||||||
|
String prefixPath = obj.getPath();
|
||||||
|
metadataManager.getLock().acquirePrefixLock(prefixPath);
|
||||||
|
try {
|
||||||
|
OmPrefixInfo prefixInfo =
|
||||||
|
metadataManager.getPrefixTable().get(prefixPath);
|
||||||
|
List<OzoneAcl> list = null;
|
||||||
|
if (prefixInfo != null) {
|
||||||
|
list = prefixInfo.getAcls();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list == null) {
|
||||||
|
list = new ArrayList<>();
|
||||||
|
list.add(acl);
|
||||||
|
} else {
|
||||||
|
boolean found = false;
|
||||||
|
for (OzoneAcl a: list) {
|
||||||
|
if (a.getName().equals(acl.getName()) &&
|
||||||
|
a.getType() == acl.getType()) {
|
||||||
|
found = true;
|
||||||
|
a.getAclBitSet().or(acl.getAclBitSet());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
list.add(acl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OmPrefixInfo.Builder upiBuilder = OmPrefixInfo.newBuilder();
|
||||||
|
upiBuilder.setName(prefixPath).setAcls(list);
|
||||||
|
if (prefixInfo != null && prefixInfo.getMetadata() != null) {
|
||||||
|
upiBuilder.addAllMetadata(prefixInfo.getMetadata());
|
||||||
|
}
|
||||||
|
prefixInfo = upiBuilder.build();
|
||||||
|
// Persist into prefix table first
|
||||||
|
metadataManager.getPrefixTable().put(prefixPath, prefixInfo);
|
||||||
|
// update the in-memory prefix tree
|
||||||
|
prefixTree.insert(prefixPath, prefixInfo);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
if (!(ex instanceof OMException)) {
|
||||||
|
LOG.error("Add acl operation failed for prefix path:{} acl:{}",
|
||||||
|
prefixPath, acl, ex);
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
} finally {
|
||||||
|
metadataManager.getLock().releasePrefixLock(prefixPath);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove acl for Ozone object. Return true if acl is removed successfully
|
||||||
|
* else false.
|
||||||
|
*
|
||||||
|
* @param obj Ozone object.
|
||||||
|
* @param acl Ozone acl to be removed.
|
||||||
|
* @throws IOException if there is error.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean removeAcl(OzoneObj obj, OzoneAcl acl) throws IOException {
|
||||||
|
validateOzoneObj(obj);
|
||||||
|
String prefixPath = obj.getPath();
|
||||||
|
metadataManager.getLock().acquirePrefixLock(prefixPath);
|
||||||
|
try {
|
||||||
|
OmPrefixInfo prefixInfo =
|
||||||
|
metadataManager.getPrefixTable().get(prefixPath);
|
||||||
|
List<OzoneAcl> list = null;
|
||||||
|
if (prefixInfo != null) {
|
||||||
|
list = prefixInfo.getAcls();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list == null) {
|
||||||
|
LOG.debug("acl {} does not exist for prefix path {}", acl, prefixPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean found = false;
|
||||||
|
for (OzoneAcl a: list) {
|
||||||
|
if (a.getName().equals(acl.getName())
|
||||||
|
&& a.getType() == acl.getType()) {
|
||||||
|
found = true;
|
||||||
|
a.getAclBitSet().andNot(acl.getAclBitSet());
|
||||||
|
if (a.getAclBitSet().isEmpty()) {
|
||||||
|
list.remove(a);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
LOG.debug("acl {} does not exist for prefix path {}", acl, prefixPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
OmPrefixInfo.Builder upiBuilder = OmPrefixInfo.newBuilder();
|
||||||
|
upiBuilder.setName(prefixPath).setAcls(list);
|
||||||
|
if (prefixInfo != null && prefixInfo.getMetadata() != null) {
|
||||||
|
upiBuilder.addAllMetadata(prefixInfo.getMetadata());
|
||||||
|
}
|
||||||
|
prefixInfo = upiBuilder.build();
|
||||||
|
metadataManager.getPrefixTable().put(prefixPath, prefixInfo);
|
||||||
|
prefixTree.insert(prefixPath, prefixInfo);
|
||||||
|
} else {
|
||||||
|
// Remove prefix entry in table and prefix tree if the # of acls is 0
|
||||||
|
metadataManager.getPrefixTable().delete(prefixPath);
|
||||||
|
prefixTree.removePrefixPath(prefixPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
if (!(ex instanceof OMException)) {
|
||||||
|
LOG.error("Remove prefix acl operation failed for prefix path:{}" +
|
||||||
|
" acl:{}", prefixPath, acl, ex);
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
} finally {
|
||||||
|
metadataManager.getLock().releasePrefixLock(prefixPath);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acls to be set for given Ozone object. This operations reset ACL for given
|
||||||
|
* object to list of ACLs provided in argument.
|
||||||
|
*
|
||||||
|
* @param obj Ozone object.
|
||||||
|
* @param acls List of acls.
|
||||||
|
* @throws IOException if there is error.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean setAcl(OzoneObj obj, List<OzoneAcl> acls) throws IOException {
|
||||||
|
validateOzoneObj(obj);
|
||||||
|
String prefixPath = obj.getPath();
|
||||||
|
metadataManager.getLock().acquirePrefixLock(prefixPath);
|
||||||
|
try {
|
||||||
|
OmPrefixInfo prefixInfo =
|
||||||
|
metadataManager.getPrefixTable().get(prefixPath);
|
||||||
|
OmPrefixInfo.Builder upiBuilder = OmPrefixInfo.newBuilder();
|
||||||
|
upiBuilder.setName(prefixPath).setAcls(acls);
|
||||||
|
if (prefixInfo != null && prefixInfo.getMetadata() != null) {
|
||||||
|
upiBuilder.addAllMetadata(prefixInfo.getMetadata());
|
||||||
|
}
|
||||||
|
prefixInfo = upiBuilder.build();
|
||||||
|
prefixTree.insert(prefixPath, prefixInfo);
|
||||||
|
metadataManager.getPrefixTable().put(prefixPath, prefixInfo);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
if (!(ex instanceof OMException)) {
|
||||||
|
LOG.error("Set prefix acl operation failed for prefix path:{} acls:{}",
|
||||||
|
prefixPath, acls, ex);
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
} finally {
|
||||||
|
metadataManager.getLock().releasePrefixLock(prefixPath);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of ACLs for given Ozone object.
|
||||||
|
*
|
||||||
|
* @param obj Ozone object.
|
||||||
|
* @throws IOException if there is error.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<OzoneAcl> getAcl(OzoneObj obj) throws IOException {
|
||||||
|
validateOzoneObj(obj);
|
||||||
|
String prefixPath = obj.getPath();
|
||||||
|
metadataManager.getLock().acquirePrefixLock(prefixPath);
|
||||||
|
try {
|
||||||
|
String longestPrefix = prefixTree.getLongestPrefix(prefixPath);
|
||||||
|
if (prefixPath.equals(longestPrefix)) {
|
||||||
|
RadixNode<OmPrefixInfo> lastNode =
|
||||||
|
prefixTree.getLastNodeInPrefixPath(prefixPath);
|
||||||
|
if (lastNode != null && lastNode.getValue() != null) {
|
||||||
|
return lastNode.getValue().getAcls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
metadataManager.getLock().releasePrefixLock(prefixPath);
|
||||||
|
}
|
||||||
|
return EMPTY_ACL_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OmPrefixInfo> getLongestPrefixPath(String path) {
|
||||||
|
String prefixPath = prefixTree.getLongestPrefix(path);
|
||||||
|
metadataManager.getLock().acquirePrefixLock(prefixPath);
|
||||||
|
try {
|
||||||
|
return prefixTree.getLongestPrefixPath(prefixPath).stream()
|
||||||
|
.map(c -> c.getValue()).collect(Collectors.toList());
|
||||||
|
} finally {
|
||||||
|
metadataManager.getLock().releasePrefixLock(prefixPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to validate ozone object.
|
||||||
|
* @param obj
|
||||||
|
* */
|
||||||
|
private void validateOzoneObj(OzoneObj obj) throws OMException {
|
||||||
|
Objects.requireNonNull(obj);
|
||||||
|
|
||||||
|
if (!obj.getResourceType().equals(PREFIX)) {
|
||||||
|
throw new IllegalArgumentException("Unexpected argument passed to " +
|
||||||
|
"PrefixManager. OzoneObj type:" + obj.getResourceType());
|
||||||
|
}
|
||||||
|
String volume = obj.getVolumeName();
|
||||||
|
String bucket = obj.getBucketName();
|
||||||
|
String prefixName = obj.getPrefixName();
|
||||||
|
|
||||||
|
if (Strings.isNullOrEmpty(volume)) {
|
||||||
|
throw new OMException("Volume name is required.", VOLUME_NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (Strings.isNullOrEmpty(bucket)) {
|
||||||
|
throw new OMException("Bucket name is required.", BUCKET_NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (Strings.isNullOrEmpty(prefixName)) {
|
||||||
|
throw new OMException("Prefix name is required.", PREFIX_NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (!prefixName.endsWith("/")) {
|
||||||
|
throw new OMException("Invalid prefix name: " + prefixName,
|
||||||
|
PREFIX_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.apache.hadoop.ozone.om.fs;
|
package org.apache.hadoop.ozone.om.fs;
|
||||||
|
|
||||||
|
import org.apache.hadoop.ozone.om.IOzoneAcl;
|
||||||
import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
|
import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
|
||||||
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
|
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
|
||||||
import org.apache.hadoop.ozone.om.helpers.OpenKeySession;
|
import org.apache.hadoop.ozone.om.helpers.OpenKeySession;
|
||||||
|
@ -29,7 +30,7 @@ import java.util.List;
|
||||||
/**
|
/**
|
||||||
* Ozone Manager FileSystem interface.
|
* Ozone Manager FileSystem interface.
|
||||||
*/
|
*/
|
||||||
public interface OzoneManagerFS {
|
public interface OzoneManagerFS extends IOzoneAcl {
|
||||||
OzoneFileStatus getFileStatus(OmKeyArgs args) throws IOException;
|
OzoneFileStatus getFileStatus(OmKeyArgs args) throws IOException;
|
||||||
|
|
||||||
void createDirectory(OmKeyArgs args) throws IOException;
|
void createDirectory(OmKeyArgs args) throws IOException;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -47,21 +48,38 @@ import org.apache.hadoop.hdds.scm.node.NodeManager;
|
||||||
import org.apache.hadoop.hdds.scm.protocol.ScmBlockLocationProtocol;
|
import org.apache.hadoop.hdds.scm.protocol.ScmBlockLocationProtocol;
|
||||||
import org.apache.hadoop.hdds.scm.server.SCMConfigurator;
|
import org.apache.hadoop.hdds.scm.server.SCMConfigurator;
|
||||||
import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
|
import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
|
||||||
|
import org.apache.hadoop.ozone.OzoneAcl;
|
||||||
import org.apache.hadoop.ozone.om.exceptions.OMException;
|
import org.apache.hadoop.ozone.om.exceptions.OMException;
|
||||||
import org.apache.hadoop.ozone.om.helpers.*;
|
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OmPrefixInfo;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OpenKeySession;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OzoneFSUtils;
|
||||||
|
import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
|
||||||
|
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
|
||||||
|
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType;
|
||||||
|
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
|
||||||
|
import org.apache.hadoop.ozone.security.acl.OzoneObj;
|
||||||
|
import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
|
||||||
import org.apache.hadoop.ozone.web.utils.OzoneUtils;
|
import org.apache.hadoop.ozone.web.utils.OzoneUtils;
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
import org.apache.hadoop.test.GenericTestUtils;
|
import org.apache.hadoop.test.GenericTestUtils;
|
||||||
import org.apache.hadoop.test.LambdaTestUtils;
|
import org.apache.hadoop.test.LambdaTestUtils;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.AfterClass;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import static org.apache.hadoop.ozone.OzoneConfigKeys.*;
|
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_KEY_PREALLOCATION_BLOCKS_MAX;
|
||||||
|
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SCM_BLOCK_SIZE;
|
||||||
|
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SCM_BLOCK_SIZE_DEFAULT;
|
||||||
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.ALL;
|
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.ALL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,6 +87,7 @@ import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.ALL
|
||||||
*/
|
*/
|
||||||
public class TestKeyManagerImpl {
|
public class TestKeyManagerImpl {
|
||||||
|
|
||||||
|
private static PrefixManager prefixManager;
|
||||||
private static KeyManagerImpl keyManager;
|
private static KeyManagerImpl keyManager;
|
||||||
private static VolumeManagerImpl volumeManager;
|
private static VolumeManagerImpl volumeManager;
|
||||||
private static BucketManagerImpl bucketManager;
|
private static BucketManagerImpl bucketManager;
|
||||||
|
@ -82,6 +101,9 @@ public class TestKeyManagerImpl {
|
||||||
private static final String BUCKET_NAME = "bucket1";
|
private static final String BUCKET_NAME = "bucket1";
|
||||||
private static final String VOLUME_NAME = "vol1";
|
private static final String VOLUME_NAME = "vol1";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ExpectedException exception = ExpectedException.none();
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void setUp() throws Exception {
|
public static void setUp() throws Exception {
|
||||||
conf = new OzoneConfiguration();
|
conf = new OzoneConfiguration();
|
||||||
|
@ -105,6 +127,8 @@ public class TestKeyManagerImpl {
|
||||||
keyManager =
|
keyManager =
|
||||||
new KeyManagerImpl(scm.getBlockProtocolServer(), metadataManager, conf,
|
new KeyManagerImpl(scm.getBlockProtocolServer(), metadataManager, conf,
|
||||||
"om1", null);
|
"om1", null);
|
||||||
|
prefixManager = new PrefixManagerImpl(metadataManager);
|
||||||
|
|
||||||
Mockito.when(mockScmBlockLocationProtocol
|
Mockito.when(mockScmBlockLocationProtocol
|
||||||
.allocateBlock(Mockito.anyLong(), Mockito.anyInt(),
|
.allocateBlock(Mockito.anyLong(), Mockito.anyInt(),
|
||||||
Mockito.any(ReplicationType.class),
|
Mockito.any(ReplicationType.class),
|
||||||
|
@ -323,6 +347,213 @@ public class TestKeyManagerImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrefixAclOps() throws IOException {
|
||||||
|
String volumeName = "vol1";
|
||||||
|
String bucketName = "bucket1";
|
||||||
|
String prefix1 = "pf1/";
|
||||||
|
|
||||||
|
OzoneObj ozPrefix1 = new OzoneObjInfo.Builder()
|
||||||
|
.setVolumeName(volumeName)
|
||||||
|
.setBucketName(bucketName)
|
||||||
|
.setPrefixName(prefix1)
|
||||||
|
.setResType(OzoneObj.ResourceType.PREFIX)
|
||||||
|
.setStoreType(OzoneObj.StoreType.OZONE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
OzoneAcl ozAcl1 = new OzoneAcl(ACLIdentityType.USER, "user1",
|
||||||
|
ACLType.READ);
|
||||||
|
prefixManager.addAcl(ozPrefix1, ozAcl1);
|
||||||
|
|
||||||
|
List<OzoneAcl> ozAclGet = prefixManager.getAcl(ozPrefix1);
|
||||||
|
Assert.assertEquals(1, ozAclGet.size());
|
||||||
|
Assert.assertEquals(ozAcl1, ozAclGet.get(0));
|
||||||
|
|
||||||
|
List<OzoneAcl> acls = new ArrayList<>();
|
||||||
|
OzoneAcl ozAcl2 = new OzoneAcl(ACLIdentityType.USER, "admin",
|
||||||
|
ACLType.ALL);
|
||||||
|
|
||||||
|
BitSet rwRights = new BitSet();
|
||||||
|
rwRights.set(IAccessAuthorizer.ACLType.WRITE.ordinal());
|
||||||
|
rwRights.set(IAccessAuthorizer.ACLType.READ.ordinal());
|
||||||
|
OzoneAcl ozAcl3 = new OzoneAcl(ACLIdentityType.GROUP, "dev",
|
||||||
|
rwRights);
|
||||||
|
|
||||||
|
BitSet wRights = new BitSet();
|
||||||
|
wRights.set(IAccessAuthorizer.ACLType.WRITE.ordinal());
|
||||||
|
OzoneAcl ozAcl4 = new OzoneAcl(ACLIdentityType.GROUP, "dev",
|
||||||
|
wRights);
|
||||||
|
|
||||||
|
BitSet rRights = new BitSet();
|
||||||
|
rRights.set(IAccessAuthorizer.ACLType.READ.ordinal());
|
||||||
|
OzoneAcl ozAcl5 = new OzoneAcl(ACLIdentityType.GROUP, "dev",
|
||||||
|
rRights);
|
||||||
|
|
||||||
|
acls.add(ozAcl2);
|
||||||
|
acls.add(ozAcl3);
|
||||||
|
|
||||||
|
prefixManager.setAcl(ozPrefix1, acls);
|
||||||
|
ozAclGet = prefixManager.getAcl(ozPrefix1);
|
||||||
|
Assert.assertEquals(2, ozAclGet.size());
|
||||||
|
|
||||||
|
int matchEntries = 0;
|
||||||
|
for (OzoneAcl acl : ozAclGet) {
|
||||||
|
if (acl.getType() == ACLIdentityType.GROUP) {
|
||||||
|
Assert.assertEquals(ozAcl3, acl);
|
||||||
|
matchEntries++;
|
||||||
|
}
|
||||||
|
if (acl.getType() == ACLIdentityType.USER) {
|
||||||
|
Assert.assertEquals(ozAcl2, acl);
|
||||||
|
matchEntries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.assertEquals(2, matchEntries);
|
||||||
|
|
||||||
|
boolean result = prefixManager.removeAcl(ozPrefix1, ozAcl4);
|
||||||
|
Assert.assertEquals(true, result);
|
||||||
|
|
||||||
|
ozAclGet = prefixManager.getAcl(ozPrefix1);
|
||||||
|
Assert.assertEquals(2, ozAclGet.size());
|
||||||
|
|
||||||
|
result = prefixManager.removeAcl(ozPrefix1, ozAcl3);
|
||||||
|
Assert.assertEquals(true, result);
|
||||||
|
ozAclGet = prefixManager.getAcl(ozPrefix1);
|
||||||
|
Assert.assertEquals(1, ozAclGet.size());
|
||||||
|
|
||||||
|
Assert.assertEquals(ozAcl2, ozAclGet.get(0));
|
||||||
|
|
||||||
|
// add dev:w
|
||||||
|
prefixManager.addAcl(ozPrefix1, ozAcl4);
|
||||||
|
ozAclGet = prefixManager.getAcl(ozPrefix1);
|
||||||
|
Assert.assertEquals(2, ozAclGet.size());
|
||||||
|
|
||||||
|
// add dev:r and validate the acl bitset combined
|
||||||
|
prefixManager.addAcl(ozPrefix1, ozAcl5);
|
||||||
|
ozAclGet = prefixManager.getAcl(ozPrefix1);
|
||||||
|
Assert.assertEquals(2, ozAclGet.size());
|
||||||
|
|
||||||
|
matchEntries = 0;
|
||||||
|
for (OzoneAcl acl : ozAclGet) {
|
||||||
|
if (acl.getType() == ACLIdentityType.GROUP) {
|
||||||
|
Assert.assertEquals(ozAcl3, acl);
|
||||||
|
matchEntries++;
|
||||||
|
}
|
||||||
|
if (acl.getType() == ACLIdentityType.USER) {
|
||||||
|
Assert.assertEquals(ozAcl2, acl);
|
||||||
|
matchEntries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.assertEquals(2, matchEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidPrefixAcl() throws IOException {
|
||||||
|
String volumeName = "vol1";
|
||||||
|
String bucketName = "bucket1";
|
||||||
|
String prefix1 = "pf1/";
|
||||||
|
|
||||||
|
// Invalid prefix not ending with "/"
|
||||||
|
String invalidPrefix = "invalid/pf";
|
||||||
|
OzoneAcl ozAcl1 = new OzoneAcl(ACLIdentityType.USER, "user1",
|
||||||
|
ACLType.READ);
|
||||||
|
|
||||||
|
OzoneObj ozInvalidPrefix = new OzoneObjInfo.Builder()
|
||||||
|
.setVolumeName(volumeName)
|
||||||
|
.setBucketName(bucketName)
|
||||||
|
.setPrefixName(invalidPrefix)
|
||||||
|
.setResType(OzoneObj.ResourceType.PREFIX)
|
||||||
|
.setStoreType(OzoneObj.StoreType.OZONE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// add acl with invalid prefix name
|
||||||
|
exception.expect(OMException.class);
|
||||||
|
exception.expectMessage("Invalid prefix name");
|
||||||
|
prefixManager.addAcl(ozInvalidPrefix, ozAcl1);
|
||||||
|
|
||||||
|
OzoneObj ozPrefix1 = new OzoneObjInfo.Builder()
|
||||||
|
.setVolumeName(volumeName)
|
||||||
|
.setBucketName(bucketName)
|
||||||
|
.setPrefixName(prefix1)
|
||||||
|
.setResType(OzoneObj.ResourceType.PREFIX)
|
||||||
|
.setStoreType(OzoneObj.StoreType.OZONE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
List<OzoneAcl> ozAclGet = prefixManager.getAcl(ozPrefix1);
|
||||||
|
Assert.assertEquals(1, ozAclGet.size());
|
||||||
|
Assert.assertEquals(ozAcl1, ozAclGet.get(0));
|
||||||
|
|
||||||
|
// get acl with invalid prefix name
|
||||||
|
exception.expect(OMException.class);
|
||||||
|
exception.expectMessage("Invalid prefix name");
|
||||||
|
ozAclGet = prefixManager.getAcl(ozInvalidPrefix);
|
||||||
|
Assert.assertEquals(null, ozAcl1);
|
||||||
|
|
||||||
|
// set acl with invalid prefix name
|
||||||
|
List<OzoneAcl> ozoneAcls = new ArrayList<OzoneAcl>();
|
||||||
|
ozoneAcls.add(ozAcl1);
|
||||||
|
exception.expect(OMException.class);
|
||||||
|
exception.expectMessage("Invalid prefix name");
|
||||||
|
prefixManager.setAcl(ozInvalidPrefix, ozoneAcls);
|
||||||
|
|
||||||
|
// remove acl with invalid prefix name
|
||||||
|
exception.expect(OMException.class);
|
||||||
|
exception.expectMessage("Invalid prefix name");
|
||||||
|
prefixManager.removeAcl(ozInvalidPrefix, ozAcl1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLongestPrefixPath() throws IOException {
|
||||||
|
String volumeName = "vol1";
|
||||||
|
String bucketName = "bucket1";
|
||||||
|
String prefix1 = "pf1/pf11/pf111/pf1111/";
|
||||||
|
String file1 = "pf1/pf11/file1";
|
||||||
|
String file2 = "pf1/pf11/pf111/pf1111/file2";
|
||||||
|
|
||||||
|
OzoneObj ozPrefix1 = new OzoneObjInfo.Builder()
|
||||||
|
.setVolumeName(volumeName)
|
||||||
|
.setBucketName(bucketName)
|
||||||
|
.setPrefixName(prefix1)
|
||||||
|
.setResType(OzoneObj.ResourceType.PREFIX)
|
||||||
|
.setStoreType(OzoneObj.StoreType.OZONE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
OzoneAcl ozAcl1 = new OzoneAcl(ACLIdentityType.USER, "user1",
|
||||||
|
ACLType.READ);
|
||||||
|
prefixManager.addAcl(ozPrefix1, ozAcl1);
|
||||||
|
|
||||||
|
OzoneObj ozFile1 = new OzoneObjInfo.Builder()
|
||||||
|
.setVolumeName(volumeName)
|
||||||
|
.setBucketName(bucketName)
|
||||||
|
.setKeyName(file1)
|
||||||
|
.setResType(OzoneObj.ResourceType.KEY)
|
||||||
|
.setStoreType(OzoneObj.StoreType.OZONE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<OmPrefixInfo> prefixInfos =
|
||||||
|
prefixManager.getLongestPrefixPath(ozFile1.getPath());
|
||||||
|
Assert.assertEquals(5, prefixInfos.size());
|
||||||
|
|
||||||
|
OzoneObj ozFile2 = new OzoneObjInfo.Builder()
|
||||||
|
.setVolumeName(volumeName)
|
||||||
|
.setBucketName(bucketName)
|
||||||
|
.setPrefixName(file2)
|
||||||
|
.setResType(OzoneObj.ResourceType.KEY)
|
||||||
|
.setStoreType(OzoneObj.StoreType.OZONE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
prefixInfos =
|
||||||
|
prefixManager.getLongestPrefixPath(ozFile2.getPath());
|
||||||
|
Assert.assertEquals(7, prefixInfos.size());
|
||||||
|
// Only the last node has acl on it
|
||||||
|
Assert.assertEquals(ozAcl1, prefixInfos.get(6).getAcls().get(0));
|
||||||
|
// All other nodes don't have acl value associate with it
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
Assert.assertEquals(null, prefixInfos.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLookupFile() throws IOException {
|
public void testLookupFile() throws IOException {
|
||||||
String keyName = RandomStringUtils.randomAlphabetic(5);
|
String keyName = RandomStringUtils.randomAlphabetic(5);
|
||||||
|
|
Loading…
Reference in New Issue