blob: 0a5fab260b582cbdf0b076840ee8ef2115a524ca [file] [log] [blame]
/**
* 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.protobuf.ServiceException;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.hadoop.hdds.protocol.StorageType;
import org.apache.hadoop.ozone.ClientVersion;
import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.OzoneTestUtils;
import org.apache.hadoop.ozone.client.ObjectStore;
import org.apache.hadoop.ozone.client.OzoneBucket;
import org.apache.hadoop.ozone.client.BucketArgs;
import org.apache.hadoop.ozone.client.OzoneVolume;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.client.VolumeArgs;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.exceptions.OMNotLeaderException;
import org.apache.hadoop.ozone.om.ha.HadoopRpcOMFailoverProxyProvider;
import org.apache.hadoop.ozone.om.ha.OMProxyInfo;
import org.apache.hadoop.ozone.om.helpers.OMRatisHelper;
import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer;
import org.apache.hadoop.ozone.om.request.volume.OMVolumeCreateRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.VolumeInfo;
import org.apache.hadoop.ozone.protocolPB.OzoneManagerProtocolServerSideTranslatorPB;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.ozone.test.GenericTestUtils;
import org.apache.ratis.protocol.ClientId;
import org.apache.ratis.protocol.Message;
import org.apache.ratis.protocol.RaftClientReply;
import org.apache.ratis.protocol.RaftClientRequest;
import org.apache.ratis.server.RaftServer;
import org.junit.jupiter.api.Test;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetSocketAddress;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static java.util.UUID.randomUUID;
import static org.apache.hadoop.ozone.OzoneAcl.AclScope.ACCESS;
import static org.apache.hadoop.ozone.OzoneAcl.AclScope.DEFAULT;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.DIRECTORY_NOT_FOUND;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FILE_ALREADY_EXISTS;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.NOT_A_FILE;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PARTIAL_DELETE;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType.GROUP;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType.USER;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.WRITE;
import static org.apache.ratis.metrics.RatisMetrics.RATIS_APPLICATION_NAME_METRICS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* Ozone Manager HA tests where all OMs are running throughout all tests.
* @see TestOzoneManagerHAWithStoppedNodes
*/
class TestOzoneManagerHAWithAllRunning extends TestOzoneManagerHA {
@Test
void testFileOperationsWithRecursive() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String data = "random data";
// one level key name
testCreateFile(ozoneBucket, randomUUID().toString(), data, true, false);
// multi level key name
String keyName = "dir1/dir2/dir3/file1";
testCreateFile(ozoneBucket, keyName, data, true, false);
String newData = "random data random data";
// multi level key name with overwrite set.
testCreateFile(ozoneBucket, keyName, newData, true, true);
OMException ex = assertThrows(OMException.class,
() -> testCreateFile(ozoneBucket, keyName, "any", true, false));
assertEquals(FILE_ALREADY_EXISTS, ex.getResult());
// Try now with a file name which is same as a directory.
String dir = "folder/folder2";
ozoneBucket.createDirectory(dir);
ex = assertThrows(OMException.class,
() -> testCreateFile(ozoneBucket, dir, "any", true, false));
assertEquals(NOT_A_FILE, ex.getResult());
}
@Test
void testKeysDelete() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String data = "random data";
String keyName1 = "dir/file1";
String keyName2 = "dir/file2";
String keyName3 = "dir/file3";
String keyName4 = "dir/file4";
List<String> keyList1 = new ArrayList<>();
keyList1.add(keyName2);
keyList1.add(keyName3);
testCreateFile(ozoneBucket, keyName1, data, true, false);
testCreateFile(ozoneBucket, keyName2, data, true, false);
testCreateFile(ozoneBucket, keyName3, data, true, false);
testCreateFile(ozoneBucket, keyName4, data, true, false);
// Delete keyName1 use deleteKey api.
ozoneBucket.deleteKey(keyName1);
// Delete keyName2 and keyName3 in keyList1 using the deleteKeys api.
ozoneBucket.deleteKeys(keyList1);
// In keyList2 keyName3 was previously deleted and KeyName4 exists .
List<String> keyList2 = new ArrayList<>();
keyList2.add(keyName3);
keyList2.add(keyName4);
// Because keyName3 has been deleted, there should be a KEY_NOT_FOUND
// exception. In this case, we test for deletion failure.
OMException ex = assertThrows(OMException.class,
() -> ozoneBucket.deleteKeys(keyList2));
// The expected exception PARTIAL_DELETE, as if not able to delete, we
// return error codee PARTIAL_DElETE.
assertEquals(PARTIAL_DELETE, ex.getResult());
}
@Test
void testFileOperationsWithNonRecursive() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String data = "random data";
// one level key name
testCreateFile(ozoneBucket, randomUUID().toString(), data, false, false);
// multi level key name
String keyName = "dir1/dir2/dir3/file1";
// Should fail, as this is non-recursive and no parent directories exist
try {
testCreateFile(ozoneBucket, keyName, data, false, false);
} catch (OMException ex) {
assertEquals(DIRECTORY_NOT_FOUND, ex.getResult());
}
// create directory, now this should pass.
ozoneBucket.createDirectory("dir1/dir2/dir3");
testCreateFile(ozoneBucket, keyName, data, false, false);
data = "random data random data";
// multi level key name with overwrite set.
testCreateFile(ozoneBucket, keyName, data, false, true);
OMException ex = assertThrows(OMException.class,
() -> testCreateFile(ozoneBucket, keyName, "any", false, false));
assertEquals(FILE_ALREADY_EXISTS, ex.getResult());
// Try now with a file which already exists under the path
ozoneBucket.createDirectory("folder1/folder2/folder3/folder4");
testCreateFile(ozoneBucket, "folder1/folder2/folder3/folder4/file1", data,
false, false);
testCreateFile(ozoneBucket, "folder1/folder2/folder3/file1", data, false,
false);
// Try now with a file under path already. This should fail.
String dir = "folder/folder2";
ozoneBucket.createDirectory(dir);
ex = assertThrows(OMException.class,
() -> testCreateFile(ozoneBucket, dir, "any", false, false)
);
assertEquals(NOT_A_FILE, ex.getResult());
}
private OzoneVolume createAndCheckVolume(String volumeName)
throws Exception {
String userName = "user" + RandomStringUtils.randomNumeric(5);
String adminName = "admin" + RandomStringUtils.randomNumeric(5);
VolumeArgs createVolumeArgs = VolumeArgs.newBuilder()
.setOwner(userName)
.setAdmin(adminName)
.build();
ObjectStore objectStore = getObjectStore();
objectStore.createVolume(volumeName, createVolumeArgs);
OzoneVolume retVolume = objectStore.getVolume(volumeName);
assertEquals(volumeName, retVolume.getName());
assertEquals(userName, retVolume.getOwner());
assertEquals(adminName, retVolume.getAdmin());
return retVolume;
}
@Test
public void testAllVolumeOperations() throws Exception {
String volumeName = "volume" + RandomStringUtils.randomNumeric(5);
createAndCheckVolume(volumeName);
ObjectStore objectStore = getObjectStore();
objectStore.deleteVolume(volumeName);
OzoneTestUtils.expectOmException(OMException.ResultCodes.VOLUME_NOT_FOUND,
() -> objectStore.getVolume(volumeName));
OzoneTestUtils.expectOmException(OMException.ResultCodes.VOLUME_NOT_FOUND,
() -> objectStore.deleteVolume(volumeName));
}
@Test
public void testAllBucketOperations() throws Exception {
String volumeName = "volume" + RandomStringUtils.randomNumeric(5);
String bucketName = "volume" + RandomStringUtils.randomNumeric(5);
OzoneVolume retVolume = createAndCheckVolume(volumeName);
BucketArgs bucketArgs =
BucketArgs.newBuilder().setStorageType(StorageType.DISK)
.setVersioning(true).build();
retVolume.createBucket(bucketName, bucketArgs);
OzoneBucket ozoneBucket = retVolume.getBucket(bucketName);
assertEquals(volumeName, ozoneBucket.getVolumeName());
assertEquals(bucketName, ozoneBucket.getName());
assertTrue(ozoneBucket.getVersioning());
assertEquals(StorageType.DISK, ozoneBucket.getStorageType());
assertFalse(ozoneBucket.getCreationTime().isAfter(Instant.now()));
// Change versioning to false
ozoneBucket.setVersioning(false);
ozoneBucket = retVolume.getBucket(bucketName);
assertFalse(ozoneBucket.getVersioning());
retVolume.deleteBucket(bucketName);
OzoneTestUtils.expectOmException(OMException.ResultCodes.BUCKET_NOT_FOUND,
() -> retVolume.deleteBucket(bucketName));
}
/**
* Test that HadoopRpcOMFailoverProxyProvider creates an OM proxy
* for each OM in the cluster.
*/
@Test
void testOMProxyProviderInitialization() {
OzoneClient rpcClient = getClient();
HadoopRpcOMFailoverProxyProvider omFailoverProxyProvider =
OmFailoverProxyUtil.getFailoverProxyProvider(
rpcClient.getObjectStore().getClientProxy());
List<OMProxyInfo> omProxies =
omFailoverProxyProvider.getOMProxyInfos();
assertEquals(getNumOfOMs(), omProxies.size());
for (int i = 0; i < getNumOfOMs(); i++) {
OzoneManager om = getCluster().getOzoneManager(i);
InetSocketAddress omRpcServerAddr = om.getOmRpcServerAddr();
boolean omClientProxyExists = false;
for (OMProxyInfo omProxyInfo : omProxies) {
if (omProxyInfo.getAddress().equals(omRpcServerAddr)) {
omClientProxyExists = true;
break;
}
}
assertTrue(omClientProxyExists,
() -> "No Client Proxy for node " + om.getOMNodeId());
}
}
/**
* Test HadoopRpcOMFailoverProxyProvider failover when current OM proxy is not
* the current OM Leader.
*/
@Test
public void testOMProxyProviderFailoverToCurrentLeader() throws Exception {
ObjectStore objectStore = getObjectStore();
HadoopRpcOMFailoverProxyProvider omFailoverProxyProvider =
OmFailoverProxyUtil
.getFailoverProxyProvider(objectStore.getClientProxy());
// Run couple of createVolume tests to discover the current Leader OM
createVolumeTest(true);
createVolumeTest(true);
// The oMFailoverProxyProvider will point to the current leader OM node.
String leaderOMNodeId = omFailoverProxyProvider.getCurrentProxyOMNodeId();
// Perform a manual failover of the proxy provider to move the
// currentProxyIndex to a node other than the leader OM.
omFailoverProxyProvider.selectNextOmProxy();
omFailoverProxyProvider.performFailover(null);
String newProxyNodeId = omFailoverProxyProvider.getCurrentProxyOMNodeId();
assertNotEquals(leaderOMNodeId, newProxyNodeId);
// Once another request is sent to this new proxy node, the leader
// information must be returned via the response and a failover must
// happen to the leader proxy node.
createVolumeTest(true);
Thread.sleep(2000);
String newLeaderOMNodeId =
omFailoverProxyProvider.getCurrentProxyOMNodeId();
// The old and new Leader OM NodeId must match since there was no new
// election in the Ratis ring.
assertEquals(leaderOMNodeId, newLeaderOMNodeId);
}
/**
* Choose a follower to send the request, the returned exception should
* include the suggested leader node.
*/
@Test
public void testFailoverWithSuggestedLeader() throws Exception {
HadoopRpcOMFailoverProxyProvider omFailoverProxyProvider =
OmFailoverProxyUtil
.getFailoverProxyProvider(getObjectStore().getClientProxy());
// Make sure All OMs are ready.
createVolumeTest(true);
// The OMFailoverProxyProvider will point to the current leader OM node.
String leaderOMNodeId = omFailoverProxyProvider.getCurrentProxyOMNodeId();
String leaderOMAddress = ((OMProxyInfo)
omFailoverProxyProvider.getOMProxyInfoMap().get(leaderOMNodeId))
.getAddress().getAddress().toString();
OzoneManager followerOM = null;
for (OzoneManager om: getCluster().getOzoneManagersList()) {
if (!om.isLeaderReady()) {
followerOM = om;
break;
}
}
assertNotNull(followerOM);
assertSame(followerOM.getOmRatisServer().checkLeaderStatus(),
OzoneManagerRatisServer.RaftServerStatus.NOT_LEADER);
OzoneManagerProtocolProtos.OMRequest writeRequest =
OzoneManagerProtocolProtos.OMRequest.newBuilder()
.setCmdType(OzoneManagerProtocolProtos.Type.ListVolume)
.setVersion(ClientVersion.CURRENT_VERSION)
.setClientId(randomUUID().toString())
.build();
OzoneManagerProtocolServerSideTranslatorPB omServerProtocol =
followerOM.getOmServerProtocol();
ServiceException ex = assertThrows(ServiceException.class,
() -> omServerProtocol.submitRequest(null, writeRequest));
assertThat(ex).hasCauseInstanceOf(OMNotLeaderException.class)
.hasMessageEndingWith("Suggested leader is OM:" + leaderOMNodeId + "[" + leaderOMAddress + "].");
}
@Test
public void testReadRequest() throws Exception {
String volumeName = "volume" + RandomStringUtils.randomNumeric(5);
ObjectStore objectStore = getObjectStore();
objectStore.createVolume(volumeName);
HadoopRpcOMFailoverProxyProvider omFailoverProxyProvider =
OmFailoverProxyUtil
.getFailoverProxyProvider(objectStore.getClientProxy());
String leaderId = omFailoverProxyProvider.getCurrentProxyOMNodeId();
// A read request from any proxy should failover to the current leader OM
for (int i = 0; i < getNumOfOMs(); i++) {
// Failover omFailoverProxyProvider to OM at index i
OzoneManager ozoneManager = getCluster().getOzoneManager(i);
// Get the ObjectStore and FailoverProxyProvider for OM at index i
final ObjectStore store = getClient().getObjectStore();
final HadoopRpcOMFailoverProxyProvider proxyProvider =
OmFailoverProxyUtil.getFailoverProxyProvider(store.getClientProxy());
// Failover to the OM node that the objectStore points to
omFailoverProxyProvider.setNextOmProxy(
ozoneManager.getOMNodeId());
// A read request should result in the proxyProvider failing over to
// leader node.
OzoneVolume volume = store.getVolume(volumeName);
assertEquals(volumeName, volume.getName());
assertEquals(leaderId, proxyProvider.getCurrentProxyOMNodeId());
}
}
@Test
public void testJMXMetrics() throws Exception {
// Verify any one ratis metric is exposed by JMX MBeanServer
OzoneManagerRatisServer ratisServer =
getCluster().getOzoneManager(0).getOmRatisServer();
ObjectName oname = new ObjectName(RATIS_APPLICATION_NAME_METRICS, "name",
RATIS_APPLICATION_NAME_METRICS + ".log_worker." +
ratisServer.getRaftPeerId().toString() +
"@" + ratisServer.getRaftGroup().getGroupId() + ".flushCount");
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
MBeanInfo mBeanInfo = mBeanServer.getMBeanInfo(oname);
assertNotNull(mBeanInfo);
Object flushCount = mBeanServer.getAttribute(oname, "Count");
assertTrue((long) flushCount >= 0);
}
@Test
public void testOMRetryCache() throws Exception {
ObjectStore objectStore = getObjectStore();
objectStore.createVolume(randomUUID().toString());
HadoopRpcOMFailoverProxyProvider omFailoverProxyProvider =
OmFailoverProxyUtil
.getFailoverProxyProvider(objectStore.getClientProxy());
String currentLeaderNodeId = omFailoverProxyProvider
.getCurrentProxyOMNodeId();
OzoneManagerRatisServer ozoneManagerRatisServer =
getCluster().getOzoneManager(currentLeaderNodeId).getOmRatisServer();
final RaftServer raftServer = ozoneManagerRatisServer.getServerDivision().getRaftServer();
ClientId clientId = ClientId.randomId();
long callId = 2000L;
String userName = UserGroupInformation.getCurrentUser().getUserName();
String volumeName = randomUUID().toString();
GenericTestUtils.LogCapturer logCapturer = GenericTestUtils.LogCapturer
.captureLogs(OMVolumeCreateRequest.getLogger());
OzoneManagerProtocolProtos.UserInfo userInfo =
OzoneManagerProtocolProtos.UserInfo.newBuilder()
.setUserName("user")
.setHostName("host")
.setRemoteAddress("0.0.0.0")
.build();
OMRequest omRequest =
OMRequest.newBuilder().setCreateVolumeRequest(
CreateVolumeRequest.newBuilder().setVolumeInfo(
VolumeInfo.newBuilder().setOwnerName(userName)
.setAdminName(userName).setVolume(volumeName).build())
.build()).setClientId(randomUUID().toString())
.setCmdType(OzoneManagerProtocolProtos.Type.CreateVolume)
.setUserInfo(userInfo)
.build();
RaftClientReply raftClientReply =
raftServer.submitClientRequest(RaftClientRequest.newBuilder()
.setClientId(clientId)
.setServerId(raftServer.getId())
.setGroupId(ozoneManagerRatisServer.getRaftGroup().getGroupId())
.setCallId(callId)
.setMessage(
Message.valueOf(
OMRatisHelper.convertRequestToByteString(omRequest)))
.setType(RaftClientRequest.writeRequestType())
.build());
assertTrue(raftClientReply.isSuccess());
assertTrue(logCapturer.getOutput().contains("created volume:"
+ volumeName));
logCapturer.clearOutput();
raftClientReply =
raftServer.submitClientRequest(RaftClientRequest.newBuilder()
.setClientId(clientId)
.setServerId(raftServer.getId())
.setGroupId(ozoneManagerRatisServer.getRaftGroup().getGroupId())
.setCallId(callId)
.setMessage(
Message.valueOf(
OMRatisHelper.convertRequestToByteString(omRequest)))
.setType(RaftClientRequest.writeRequestType())
.build());
assertTrue(raftClientReply.isSuccess());
// As second time with same client id and call id, this request should
// not be executed ratis server should return from cache.
// If 2nd time executed, it will fail with Volume creation failed. check
// for that.
assertFalse(logCapturer.getOutput().contains(
"Volume creation failed"));
//Sleep for little above retry cache duration to get cache clear.
Thread.sleep(getRetryCacheDuration().toMillis() + 5000);
raftClientReply =
raftServer.submitClientRequest(RaftClientRequest.newBuilder()
.setClientId(clientId)
.setServerId(raftServer.getId())
.setGroupId(ozoneManagerRatisServer.getRaftGroup().getGroupId())
.setCallId(callId)
.setMessage(
Message.valueOf(
OMRatisHelper.convertRequestToByteString(omRequest)))
.setType(RaftClientRequest.writeRequestType())
.build());
assertTrue(raftClientReply.isSuccess());
// As second time with same client id and call id, this request should
// be executed by ratis server as we are sending this request after cache
// expiry duration.
assertTrue(logCapturer.getOutput().contains(
"Volume creation failed"));
}
@Test
void testAddBucketAcl() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String remoteUserName = "remoteUser";
OzoneAcl defaultUserAcl = new OzoneAcl(USER, remoteUserName,
READ, DEFAULT);
OzoneObj ozoneObj = buildBucketObj(ozoneBucket);
testAddAcl(remoteUserName, ozoneObj, defaultUserAcl);
}
@Test
void testRemoveBucketAcl() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String remoteUserName = "remoteUser";
OzoneAcl defaultUserAcl = new OzoneAcl(USER, remoteUserName,
READ, DEFAULT);
OzoneObj ozoneObj = buildBucketObj(ozoneBucket);
testRemoveAcl(remoteUserName, ozoneObj, defaultUserAcl);
}
@Test
void testSetBucketAcl() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String remoteUserName = "remoteUser";
OzoneAcl defaultUserAcl = new OzoneAcl(USER, remoteUserName,
READ, DEFAULT);
OzoneObj ozoneObj = buildBucketObj(ozoneBucket);
testSetAcl(remoteUserName, ozoneObj, defaultUserAcl);
}
private boolean containsAcl(OzoneAcl ozoneAcl, List<OzoneAcl> ozoneAcls) {
for (OzoneAcl acl : ozoneAcls) {
boolean result = compareAcls(ozoneAcl, acl);
if (result) {
// We found a match, return.
return result;
}
}
return false;
}
private boolean compareAcls(OzoneAcl givenAcl, OzoneAcl existingAcl) {
if (givenAcl.getType().equals(existingAcl.getType())
&& givenAcl.getName().equals(existingAcl.getName())
&& givenAcl.getAclScope().equals(existingAcl.getAclScope())) {
return givenAcl.equals(existingAcl);
}
return false;
}
@Test
void testAddKeyAcl() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String remoteUserName = "remoteUser";
OzoneAcl userAcl = new OzoneAcl(USER, remoteUserName,
READ, DEFAULT);
String key = createKey(ozoneBucket);
OzoneObj ozoneObj = buildKeyObj(ozoneBucket, key);
testAddAcl(remoteUserName, ozoneObj, userAcl);
}
@Test
void testRemoveKeyAcl() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String remoteUserName = "remoteUser";
OzoneAcl userAcl = new OzoneAcl(USER, remoteUserName,
READ, DEFAULT);
String key = createKey(ozoneBucket);
OzoneObj ozoneObj = buildKeyObj(ozoneBucket, key);
testRemoveAcl(remoteUserName, ozoneObj, userAcl);
}
@Test
void testSetKeyAcl() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String remoteUserName = "remoteUser";
OzoneAcl userAcl = new OzoneAcl(USER, remoteUserName,
READ, DEFAULT);
String key = createKey(ozoneBucket);
OzoneObj ozoneObj = buildKeyObj(ozoneBucket, key);
testSetAcl(remoteUserName, ozoneObj, userAcl);
}
@Test
void testAddPrefixAcl() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String remoteUserName = "remoteUser";
String prefixName = RandomStringUtils.randomAlphabetic(5) + "/";
OzoneAcl defaultUserAcl = new OzoneAcl(USER, remoteUserName,
READ, DEFAULT);
OzoneObj ozoneObj = buildPrefixObj(ozoneBucket, prefixName);
testAddAcl(remoteUserName, ozoneObj, defaultUserAcl);
}
@Test
void testRemovePrefixAcl() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String remoteUserName = "remoteUser";
String prefixName = RandomStringUtils.randomAlphabetic(5) + "/";
OzoneAcl userAcl = new OzoneAcl(USER, remoteUserName,
READ, ACCESS);
OzoneAcl userAcl1 = new OzoneAcl(USER, "remote",
READ, ACCESS);
OzoneObj ozoneObj = buildPrefixObj(ozoneBucket, prefixName);
ObjectStore objectStore = getObjectStore();
boolean result = objectStore.addAcl(ozoneObj, userAcl);
assertTrue(result);
result = objectStore.addAcl(ozoneObj, userAcl1);
assertTrue(result);
result = objectStore.removeAcl(ozoneObj, userAcl);
assertTrue(result);
// try removing already removed acl.
result = objectStore.removeAcl(ozoneObj, userAcl);
assertFalse(result);
result = objectStore.removeAcl(ozoneObj, userAcl1);
assertTrue(result);
}
@Test
void testSetPrefixAcl() throws Exception {
OzoneBucket ozoneBucket = setupBucket();
String remoteUserName = "remoteUser";
String prefixName = RandomStringUtils.randomAlphabetic(5) + "/";
OzoneAcl defaultUserAcl = new OzoneAcl(USER, remoteUserName,
READ, DEFAULT);
OzoneObj ozoneObj = buildPrefixObj(ozoneBucket, prefixName);
testSetAcl(remoteUserName, ozoneObj, defaultUserAcl);
}
@Test
void testLinkBucketAddBucketAcl() throws Exception {
OzoneBucket srcBucket = setupBucket();
OzoneBucket linkedBucket = linkBucket(srcBucket);
OzoneObj linkObj = buildBucketObj(linkedBucket);
OzoneObj srcObj = buildBucketObj(srcBucket);
// Add ACL to the LINK and verify that it is added to the source bucket
OzoneAcl acl1 = new OzoneAcl(USER, "remoteUser1", READ, DEFAULT);
boolean addAcl = getObjectStore().addAcl(linkObj, acl1);
assertTrue(addAcl);
assertEqualsAcls(srcObj, linkObj);
// Add ACL to the SOURCE and verify that it from link
OzoneAcl acl2 = new OzoneAcl(USER, "remoteUser2", WRITE, DEFAULT);
boolean addAcl2 = getObjectStore().addAcl(srcObj, acl2);
assertTrue(addAcl2);
assertEqualsAcls(srcObj, linkObj);
}
@Test
void testLinkBucketRemoveBucketAcl() throws Exception {
// case1 : test remove link acl
OzoneBucket srcBucket = setupBucket();
OzoneBucket linkedBucket = linkBucket(srcBucket);
OzoneObj linkObj = buildBucketObj(linkedBucket);
OzoneObj srcObj = buildBucketObj(srcBucket);
// As by default create will add some default acls in RpcClient.
List<OzoneAcl> acls = getObjectStore().getAcl(linkObj);
assertTrue(acls.size() > 0);
// Remove an existing acl.
boolean removeAcl = getObjectStore().removeAcl(linkObj, acls.get(0));
assertTrue(removeAcl);
assertEqualsAcls(srcObj, linkObj);
// case2 : test remove src acl
OzoneBucket srcBucket2 = setupBucket();
OzoneBucket linkedBucket2 = linkBucket(srcBucket2);
OzoneObj linkObj2 = buildBucketObj(linkedBucket2);
OzoneObj srcObj2 = buildBucketObj(srcBucket2);
// As by default create will add some default acls in RpcClient.
List<OzoneAcl> acls2 = getObjectStore().getAcl(srcObj2);
assertTrue(acls2.size() > 0);
// Remove an existing acl.
boolean removeAcl2 = getObjectStore().removeAcl(srcObj2, acls.get(0));
assertTrue(removeAcl2);
assertEqualsAcls(srcObj2, linkObj2);
}
@Test
void testLinkBucketSetBucketAcl() throws Exception {
OzoneBucket srcBucket = setupBucket();
OzoneBucket linkedBucket = linkBucket(srcBucket);
OzoneObj linkObj = buildBucketObj(linkedBucket);
OzoneObj srcObj = buildBucketObj(srcBucket);
// Set ACL to the LINK and verify that it is set to the source bucket
List<OzoneAcl> acl1 = Collections.singletonList(
new OzoneAcl(USER, "remoteUser1", READ, DEFAULT));
boolean setAcl1 = getObjectStore().setAcl(linkObj, acl1);
assertTrue(setAcl1);
assertEqualsAcls(srcObj, linkObj);
// Set ACL to the SOURCE and verify that it from link
List<OzoneAcl> acl2 = Collections.singletonList(
new OzoneAcl(USER, "remoteUser2", WRITE, DEFAULT));
boolean setAcl2 = getObjectStore().setAcl(srcObj, acl2);
assertTrue(setAcl2);
assertEqualsAcls(srcObj, linkObj);
}
@Test
void testLinkBucketAddKeyAcl() throws Exception {
OzoneBucket srcBucket = setupBucket();
OzoneBucket linkedBucket = linkBucket(srcBucket);
String key = createKey(linkedBucket);
OzoneObj linkObj = buildKeyObj(linkedBucket, key);
OzoneObj srcObj = buildKeyObj(srcBucket, key);
String user1 = "remoteUser1";
OzoneAcl acl1 = new OzoneAcl(USER, user1, READ, DEFAULT);
testAddAcl(user1, linkObj, acl1); // case1: set link acl
assertEqualsAcls(srcObj, linkObj);
String user2 = "remoteUser2";
OzoneAcl acl2 = new OzoneAcl(USER, user2, READ, DEFAULT);
testAddAcl(user2, srcObj, acl2); // case2: set src acl
assertEqualsAcls(srcObj, linkObj);
}
@Test
void testLinkBucketRemoveKeyAcl() throws Exception {
// CASE 1: from link bucket
OzoneBucket srcBucket = setupBucket();
OzoneBucket linkedBucket = linkBucket(srcBucket);
String key = createKey(linkedBucket);
OzoneObj linkObj = buildKeyObj(linkedBucket, key);
OzoneObj srcObj = buildKeyObj(srcBucket, key);
String user = "remoteUser1";
OzoneAcl acl = new OzoneAcl(USER, user, READ, DEFAULT);
testRemoveAcl(user, linkObj, acl);
assertEqualsAcls(srcObj, linkObj);
// CASE 2: from src bucket
OzoneBucket srcBucket2 = setupBucket();
OzoneBucket linkedBucket2 = linkBucket(srcBucket2);
String key2 = createKey(srcBucket2);
OzoneObj linkObj2 = buildKeyObj(linkedBucket2, key2);
OzoneObj srcObj2 = buildKeyObj(srcBucket2, key2);
String user2 = "remoteUser2";
OzoneAcl acl2 = new OzoneAcl(USER, user2, READ, DEFAULT);
testRemoveAcl(user2, srcObj2, acl2);
assertEqualsAcls(srcObj2, linkObj2);
}
@Test
void testLinkBucketSetKeyAcl() throws Exception {
OzoneBucket srcBucket = setupBucket();
OzoneBucket linkedBucket = linkBucket(srcBucket);
String key = createKey(linkedBucket);
OzoneObj linkObj = buildKeyObj(linkedBucket, key);
OzoneObj srcObj = buildKeyObj(srcBucket, key);
String user1 = "remoteUser1";
OzoneAcl acl1 = new OzoneAcl(USER, user1, READ, DEFAULT);
testSetAcl(user1, linkObj, acl1); // case1: set link acl
assertEqualsAcls(srcObj, linkObj);
String user2 = "remoteUser2";
OzoneAcl acl2 = new OzoneAcl(USER, user2, READ, DEFAULT);
testSetAcl(user2, srcObj, acl2); // case2: set src acl
assertEqualsAcls(srcObj, linkObj);
}
@Test
void testLinkBucketAddPrefixAcl() throws Exception {
OzoneBucket srcBucket = setupBucket();
OzoneBucket linkedBucket = linkBucket(srcBucket);
String prefix = createPrefixName();
OzoneObj linkObj = buildPrefixObj(linkedBucket, prefix);
OzoneObj srcObj = buildPrefixObj(srcBucket, prefix);
createPrefix(linkObj);
String user1 = "remoteUser1";
OzoneAcl acl1 = new OzoneAcl(USER, user1, READ, DEFAULT);
testAddAcl(user1, linkObj, acl1); // case1: set link acl
assertEqualsAcls(srcObj, linkObj);
String user2 = "remoteUser2";
OzoneAcl acl2 = new OzoneAcl(USER, user2, READ, DEFAULT);
testAddAcl(user2, srcObj, acl2); // case2: set src acl
assertEqualsAcls(srcObj, linkObj);
}
@Test
void testLinkBucketRemovePrefixAcl() throws Exception {
// CASE 1: from link bucket
OzoneBucket srcBucket = setupBucket();
OzoneBucket linkedBucket = linkBucket(srcBucket);
String prefix = createPrefixName();
OzoneObj linkObj = buildPrefixObj(linkedBucket, prefix);
OzoneObj srcObj = buildPrefixObj(srcBucket, prefix);
createPrefix(linkObj);
String user = "remoteUser1";
OzoneAcl acl = new OzoneAcl(USER, user, READ, DEFAULT);
testRemoveAcl(user, linkObj, acl);
assertEqualsAcls(srcObj, linkObj);
// CASE 2: from src bucket
OzoneBucket srcBucket2 = setupBucket();
OzoneBucket linkedBucket2 = linkBucket(srcBucket2);
String prefix2 = createPrefixName();
OzoneObj linkObj2 = buildPrefixObj(linkedBucket2, prefix2);
OzoneObj srcObj2 = buildPrefixObj(srcBucket2, prefix2);
createPrefix(srcObj2);
String user2 = "remoteUser2";
OzoneAcl acl2 = new OzoneAcl(USER, user2, READ, DEFAULT);
testRemoveAcl(user2, srcObj2, acl2);
assertEqualsAcls(srcObj2, linkObj2);
}
@Test
void testLinkBucketSetPrefixAcl() throws Exception {
OzoneBucket srcBucket = setupBucket();
OzoneBucket linkedBucket = linkBucket(srcBucket);
String prefix = createPrefixName();
OzoneObj linkObj = buildPrefixObj(linkedBucket, prefix);
OzoneObj srcObj = buildPrefixObj(srcBucket, prefix);
createPrefix(linkObj);
String user1 = "remoteUser1";
OzoneAcl acl1 = new OzoneAcl(USER, user1, READ, DEFAULT);
testSetAcl(user1, linkObj, acl1); // case1: set link acl
assertEqualsAcls(srcObj, linkObj);
String user2 = "remoteUser2";
OzoneAcl acl2 = new OzoneAcl(USER, user2, READ, DEFAULT);
testSetAcl(user2, srcObj, acl2); // case2: set src acl
assertEqualsAcls(srcObj, linkObj);
}
private OzoneObj buildBucketObj(OzoneBucket bucket) {
return OzoneObjInfo.Builder.newBuilder()
.setResType(OzoneObj.ResourceType.BUCKET)
.setStoreType(OzoneObj.StoreType.OZONE)
.setVolumeName(bucket.getVolumeName())
.setBucketName(bucket.getName()).build();
}
private OzoneObj buildKeyObj(OzoneBucket bucket, String key) {
return OzoneObjInfo.Builder.newBuilder()
.setResType(OzoneObj.ResourceType.KEY)
.setStoreType(OzoneObj.StoreType.OZONE)
.setVolumeName(bucket.getVolumeName())
.setBucketName(bucket.getName())
.setKeyName(key).build();
}
private OzoneObj buildPrefixObj(OzoneBucket bucket, String prefix) {
return OzoneObjInfo.Builder.newBuilder()
.setResType(OzoneObj.ResourceType.PREFIX)
.setStoreType(OzoneObj.StoreType.OZONE)
.setVolumeName(bucket.getVolumeName())
.setBucketName(bucket.getName())
.setPrefixName(prefix).build();
}
private void assertEqualsAcls(OzoneObj srcObj, OzoneObj linkObj)
throws IOException {
if (linkObj.getResourceType() == OzoneObj.ResourceType.BUCKET) {
linkObj = getSourceBucketObj(linkObj);
}
assertEquals(getObjectStore().getAcl(srcObj),
getObjectStore().getAcl(linkObj));
}
private OzoneObj getSourceBucketObj(OzoneObj obj)
throws IOException {
assert obj.getResourceType() == OzoneObj.ResourceType.BUCKET;
OzoneBucket bucket = getObjectStore()
.getVolume(obj.getVolumeName())
.getBucket(obj.getBucketName());
if (!bucket.isLink()) {
return obj;
}
obj = OzoneObjInfo.Builder.newBuilder()
.setBucketName(bucket.getSourceBucket())
.setVolumeName(bucket.getSourceVolume())
.setKeyName(obj.getKeyName())
.setResType(obj.getResourceType())
.setStoreType(obj.getStoreType())
.build();
return getSourceBucketObj(obj);
}
private void testSetAcl(String remoteUserName, OzoneObj ozoneObj,
OzoneAcl userAcl) throws Exception {
// As by default create will add some default acls in RpcClient.
ObjectStore objectStore = getObjectStore();
if (!ozoneObj.getResourceType().name().equals(
OzoneObj.ResourceType.PREFIX.name())) {
List<OzoneAcl> acls = objectStore.getAcl(ozoneObj);
assertTrue(acls.size() > 0);
}
OzoneAcl modifiedUserAcl = new OzoneAcl(USER, remoteUserName,
WRITE, DEFAULT);
List<OzoneAcl> newAcls = Collections.singletonList(modifiedUserAcl);
boolean setAcl = objectStore.setAcl(ozoneObj, newAcls);
assertTrue(setAcl);
// Get acls and check whether they are reset or not.
List<OzoneAcl> getAcls = objectStore.getAcl(ozoneObj);
assertEquals(newAcls.size(), getAcls.size());
int i = 0;
for (OzoneAcl ozoneAcl : newAcls) {
assertTrue(compareAcls(getAcls.get(i++), ozoneAcl));
}
}
private void testAddAcl(String remoteUserName, OzoneObj ozoneObj,
OzoneAcl userAcl) throws Exception {
ObjectStore objectStore = getObjectStore();
boolean addAcl = objectStore.addAcl(ozoneObj, userAcl);
assertTrue(addAcl);
List<OzoneAcl> acls = objectStore.getAcl(ozoneObj);
assertTrue(containsAcl(userAcl, acls));
// Add an already existing acl.
addAcl = objectStore.addAcl(ozoneObj, userAcl);
assertFalse(addAcl);
// Add an acl by changing acl type with same type, name and scope.
userAcl = new OzoneAcl(USER, remoteUserName,
WRITE, DEFAULT);
addAcl = objectStore.addAcl(ozoneObj, userAcl);
assertTrue(addAcl);
}
private void testAddLinkAcl(String remoteUserName, OzoneObj ozoneObj,
OzoneAcl userAcl) throws Exception {
ObjectStore objectStore = getObjectStore();
boolean addAcl = objectStore.addAcl(ozoneObj, userAcl);
assertTrue(addAcl);
List<OzoneAcl> acls = objectStore.getAcl(ozoneObj);
assertTrue(containsAcl(userAcl, acls));
// Add an already existing acl.
addAcl = objectStore.addAcl(ozoneObj, userAcl);
assertFalse(addAcl);
// Add an acl by changing acl type with same type, name and scope.
userAcl = new OzoneAcl(USER, remoteUserName,
WRITE, DEFAULT);
addAcl = objectStore.addAcl(ozoneObj, userAcl);
assertTrue(addAcl);
}
private void testRemoveAcl(String remoteUserName, OzoneObj ozoneObj,
OzoneAcl userAcl) throws Exception {
ObjectStore objectStore = getObjectStore();
// Other than prefix, by default create will add some default acls in RpcClient.
List<OzoneAcl> acls;
if (ozoneObj.getResourceType().equals(OzoneObj.ResourceType.PREFIX)) {
objectStore.addAcl(ozoneObj, userAcl);
// Add another arbitrary group ACL since the prefix will be removed when removing
// the last ACL for the prefix and PREFIX_NOT_FOUND will be thrown
OzoneAcl groupAcl = new OzoneAcl(GROUP, "arbitrary-group", READ, ACCESS);
objectStore.addAcl(ozoneObj, groupAcl);
}
acls = objectStore.getAcl(ozoneObj);
assertTrue(acls.size() > 0);
// Remove an existing acl.
boolean removeAcl = objectStore.removeAcl(ozoneObj, acls.get(0));
assertTrue(removeAcl);
// Trying to remove an already removed acl.
removeAcl = objectStore.removeAcl(ozoneObj, acls.get(0));
assertFalse(removeAcl);
boolean addAcl = objectStore.addAcl(ozoneObj, userAcl);
assertTrue(addAcl);
// Just changed acl type here to write, rest all is same as defaultUserAcl.
OzoneAcl modifiedUserAcl = new OzoneAcl(USER, remoteUserName,
WRITE, DEFAULT);
addAcl = objectStore.addAcl(ozoneObj, modifiedUserAcl);
assertTrue(addAcl);
removeAcl = objectStore.removeAcl(ozoneObj, modifiedUserAcl);
assertTrue(removeAcl);
removeAcl = objectStore.removeAcl(ozoneObj, userAcl);
assertTrue(removeAcl);
}
@Test
void testOMRatisSnapshot() throws Exception {
String userName = "user" + RandomStringUtils.randomNumeric(5);
String adminName = "admin" + RandomStringUtils.randomNumeric(5);
String volumeName = "volume" + RandomStringUtils.randomNumeric(5);
String bucketName = "bucket" + RandomStringUtils.randomNumeric(5);
VolumeArgs createVolumeArgs = VolumeArgs.newBuilder()
.setOwner(userName)
.setAdmin(adminName)
.build();
ObjectStore objectStore = getObjectStore();
objectStore.createVolume(volumeName, createVolumeArgs);
OzoneVolume retVolumeinfo = objectStore.getVolume(volumeName);
retVolumeinfo.createBucket(bucketName);
OzoneBucket ozoneBucket = retVolumeinfo.getBucket(bucketName);
String leaderOMNodeId = OmFailoverProxyUtil
.getFailoverProxyProvider(objectStore.getClientProxy())
.getCurrentProxyOMNodeId();
OzoneManager ozoneManager = getCluster().getOzoneManager(leaderOMNodeId);
// Send commands to ratis to increase the log index so that ratis
// triggers a snapshot on the state machine.
long appliedLogIndex = 0;
while (appliedLogIndex <= getSnapshotThreshold()) {
createKey(ozoneBucket);
appliedLogIndex = ozoneManager.getOmRatisServer()
.getLastAppliedTermIndex().getIndex();
}
GenericTestUtils.waitFor(() -> {
try {
if (ozoneManager.getRatisSnapshotIndex() > 0) {
return true;
}
} catch (IOException ex) {
fail("test failed during transactionInfo read");
}
return false;
}, 1000, 100000);
ozoneManager.awaitDoubleBufferFlush();
// The current lastAppliedLogIndex on the state machine should be greater
// than or equal to the saved snapshot index.
long smLastAppliedIndex =
ozoneManager.getOmRatisServer().getLastAppliedTermIndex().getIndex();
long ratisSnapshotIndex = ozoneManager.getRatisSnapshotIndex();
assertTrue(smLastAppliedIndex >= ratisSnapshotIndex,
"LastAppliedIndex on OM State Machine ("
+ smLastAppliedIndex + ") is less than the saved snapshot index("
+ ratisSnapshotIndex + ").");
// Add more transactions to Ratis to trigger another snapshot
while (appliedLogIndex <= (smLastAppliedIndex + getSnapshotThreshold())) {
createKey(ozoneBucket);
appliedLogIndex = ozoneManager.getOmRatisServer()
.getLastAppliedTermIndex().getIndex();
}
GenericTestUtils.waitFor(() -> {
try {
if (ozoneManager.getRatisSnapshotIndex() > 0) {
return true;
}
} catch (IOException ex) {
fail("test failed during transactionInfo read");
}
return false;
}, 1000, 100000);
// The new snapshot index must be greater than the previous snapshot index
long ratisSnapshotIndexNew = ozoneManager.getRatisSnapshotIndex();
assertTrue(ratisSnapshotIndexNew > ratisSnapshotIndex,
"Latest snapshot index must be greater than previous " +
"snapshot indices");
}
}