This is an automated email from the ASF dual-hosted git repository.
dlmarion pushed a commit to branch 2.1
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/2.1 by this push:
new f02d6a880e Fix src and dest namespace handling in clone table
operation (#5334)
f02d6a880e is described below
commit f02d6a880ed822820881f2fdf7d0827fcbd8db52
Author: Dave Marion <[email protected]>
AuthorDate: Thu Apr 3 07:33:38 2025 -0400
Fix src and dest namespace handling in clone table operation (#5334)
Updates clone table fate operation to use both the source and dest
namespaces when checking permissions.
Closes #5324
---
.../accumulo/manager/FateServiceHandler.java | 19 ++-
.../accumulo/manager/tableOps/clone/CloneInfo.java | 79 ++++++++++--
.../manager/tableOps/clone/CloneMetadata.java | 11 +-
.../manager/tableOps/clone/ClonePermissions.java | 8 +-
.../manager/tableOps/clone/CloneTable.java | 28 ++---
.../manager/tableOps/clone/CloneZookeeper.java | 41 +++----
.../manager/tableOps/clone/FinishCloneTable.java | 25 ++--
.../accumulo/test/functional/CloneTestIT.java | 132 +++++++++++++++++++++
8 files changed, 268 insertions(+), 75 deletions(-)
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java
b/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java
index 0444099675..c9ecd1d4d8 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java
@@ -286,12 +286,21 @@ class FateServiceHandler implements FateService.Iface {
keepOffline =
Boolean.parseBoolean(ByteBufferUtil.toString(arguments.get(2)));
}
+ NamespaceId srcNamespaceId;
+ try {
+ srcNamespaceId = manager.getContext().getNamespaceId(srcTableId);
+ } catch (TableNotFoundException e) {
+ // could happen if the table was deleted while processing this
request
+ throw new ThriftTableOperationException(srcTableId.canonical(),
null, tableOp,
+ TableOperationExceptionType.NOTFOUND, "");
+ }
+
NamespaceId namespaceId;
try {
namespaceId = Namespaces.getNamespaceId(manager.getContext(),
TableNameUtil.qualify(tableName).getFirst());
} catch (NamespaceNotFoundException e) {
- // shouldn't happen, but possible once cloning between namespaces is
supported
+ // dest namespace does not exist yet, needs to be created
throw new ThriftTableOperationException(null, tableName, tableOp,
TableOperationExceptionType.NAMESPACE_NOTFOUND, "");
}
@@ -299,7 +308,7 @@ class FateServiceHandler implements FateService.Iface {
final boolean canCloneTable;
try {
canCloneTable =
- manager.security.canCloneTable(c, srcTableId, tableName,
namespaceId, namespaceId);
+ manager.security.canCloneTable(c, srcTableId, tableName,
namespaceId, srcNamespaceId);
} catch (ThriftSecurityException e) {
throwIfTableMissingSecurityException(e, srcTableId, null,
TableOperation.CLONE);
throw e;
@@ -336,9 +345,9 @@ class FateServiceHandler implements FateService.Iface {
goalMessage += " and keep offline.";
}
- manager.fate().seedTransaction(
- op.toString(), opid, new TraceRepo<>(new
CloneTable(c.getPrincipal(), namespaceId,
- srcTableId, tableName, propertiesToSet, propertiesToExclude,
keepOffline)),
+ manager.fate().seedTransaction(op.toString(), opid,
+ new TraceRepo<>(new CloneTable(c.getPrincipal(), srcNamespaceId,
srcTableId,
+ namespaceId, tableName, propertiesToSet, propertiesToExclude,
keepOffline)),
autoCleanup, goalMessage);
break;
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneInfo.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneInfo.java
index 676e4d88e7..2a697827ad 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneInfo.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneInfo.java
@@ -29,14 +29,73 @@ class CloneInfo implements Serializable {
private static final long serialVersionUID = 1L;
- TableId srcTableId;
- String tableName;
- TableId tableId;
- NamespaceId namespaceId;
- NamespaceId srcNamespaceId;
- Map<String,String> propertiesToSet;
- Set<String> propertiesToExclude;
- boolean keepOffline;
-
- public String user;
+ private final TableId srcTableId;
+ private final String tableName;
+ private TableId tableId;
+ // TODO: Make final in 3.1
+ private NamespaceId namespaceId;
+ private final NamespaceId srcNamespaceId;
+ private final Map<String,String> propertiesToSet;
+ private final Set<String> propertiesToExclude;
+ private final boolean keepOffline;
+ private final String user;
+
+ public CloneInfo(NamespaceId srcNamespaceId, TableId srcTableId, NamespaceId
dstNamespaceId,
+ String dstTableName, Map<String,String> propertiesToSet, Set<String>
propertiesToExclude,
+ boolean keepOffline, String user) {
+ super();
+ this.srcNamespaceId = srcNamespaceId;
+ this.srcTableId = srcTableId;
+ this.tableName = dstTableName;
+ this.namespaceId = dstNamespaceId;
+ this.propertiesToSet = propertiesToSet;
+ this.propertiesToExclude = propertiesToExclude;
+ this.keepOffline = keepOffline;
+ this.user = user;
+ }
+
+ public TableId getSrcTableId() {
+ return srcTableId;
+ }
+
+ public String getTableName() {
+ return tableName;
+ }
+
+ public void setTableId(TableId dstTableId) {
+ this.tableId = dstTableId;
+ }
+
+ public TableId getTableId() {
+ return tableId;
+ }
+
+ public NamespaceId getNamespaceId() {
+ return namespaceId;
+ }
+
+ public void setNamespaceId(NamespaceId nid) {
+ this.namespaceId = nid;
+ }
+
+ public NamespaceId getSrcNamespaceId() {
+ return srcNamespaceId;
+ }
+
+ public Map<String,String> getPropertiesToSet() {
+ return propertiesToSet;
+ }
+
+ public Set<String> getPropertiesToExclude() {
+ return propertiesToExclude;
+ }
+
+ public boolean isKeepOffline() {
+ return keepOffline;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneMetadata.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneMetadata.java
index 0616edb1eb..82f73d0383 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneMetadata.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneMetadata.java
@@ -41,19 +41,20 @@ class CloneMetadata extends ManagerRepo {
@Override
public Repo<Manager> call(long tid, Manager environment) throws Exception {
LoggerFactory.getLogger(CloneMetadata.class)
- .info(String.format("Cloning %s with tableId %s from srcTableId %s",
cloneInfo.tableName,
- cloneInfo.tableId, cloneInfo.srcTableId));
+ .info(String.format("Cloning %s with tableId %s from srcTableId %s",
+ cloneInfo.getTableName(), cloneInfo.getTableId(),
cloneInfo.getSrcTableId()));
// need to clear out any metadata entries for tableId just in case this
// died before and is executing again
- MetadataTableUtil.deleteTable(cloneInfo.tableId, false,
environment.getContext(),
+ MetadataTableUtil.deleteTable(cloneInfo.getTableId(), false,
environment.getContext(),
environment.getManagerLock());
- MetadataTableUtil.cloneTable(environment.getContext(),
cloneInfo.srcTableId, cloneInfo.tableId);
+ MetadataTableUtil.cloneTable(environment.getContext(),
cloneInfo.getSrcTableId(),
+ cloneInfo.getTableId());
return new FinishCloneTable(cloneInfo);
}
@Override
public void undo(long tid, Manager environment) throws Exception {
- MetadataTableUtil.deleteTable(cloneInfo.tableId, false,
environment.getContext(),
+ MetadataTableUtil.deleteTable(cloneInfo.getTableId(), false,
environment.getContext(),
environment.getManagerLock());
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/ClonePermissions.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/ClonePermissions.java
index e448e289ef..ac1432c3d2 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/ClonePermissions.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/ClonePermissions.java
@@ -50,8 +50,8 @@ class ClonePermissions extends ManagerRepo {
for (TablePermission permission : TablePermission.values()) {
try {
environment.getContext().getSecurityOperation().grantTablePermission(
- environment.getContext().rpcCreds(), cloneInfo.user,
cloneInfo.tableId,
- cloneInfo.tableName, permission, cloneInfo.namespaceId);
+ environment.getContext().rpcCreds(), cloneInfo.getUser(),
cloneInfo.getTableId(),
+ cloneInfo.getTableName(), permission, cloneInfo.getNamespaceId());
} catch (ThriftSecurityException e) {
LoggerFactory.getLogger(ClonePermissions.class).error("{}",
e.getMessage(), e);
throw e;
@@ -64,7 +64,7 @@ class ClonePermissions extends ManagerRepo {
try {
return new CloneZookeeper(cloneInfo, environment.getContext());
} catch (NamespaceNotFoundException e) {
- throw new AcceptableThriftTableOperationException(null,
cloneInfo.tableName,
+ throw new AcceptableThriftTableOperationException(null,
cloneInfo.getTableName(),
TableOperation.CLONE, TableOperationExceptionType.NAMESPACE_NOTFOUND,
"Namespace for target table not found");
}
@@ -73,6 +73,6 @@ class ClonePermissions extends ManagerRepo {
@Override
public void undo(long tid, Manager environment) throws Exception {
environment.getContext().getSecurityOperation().deleteTable(environment.getContext().rpcCreds(),
- cloneInfo.tableId, cloneInfo.namespaceId);
+ cloneInfo.getTableId(), cloneInfo.getNamespaceId());
}
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneTable.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneTable.java
index 0a03526c38..c5587ee692 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneTable.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneTable.java
@@ -34,23 +34,18 @@ public class CloneTable extends ManagerRepo {
private static final long serialVersionUID = 1L;
private final CloneInfo cloneInfo;
- public CloneTable(String user, NamespaceId namespaceId, TableId srcTableId,
String tableName,
- Map<String,String> propertiesToSet, Set<String> propertiesToExclude,
boolean keepOffline) {
- cloneInfo = new CloneInfo();
- cloneInfo.user = user;
- cloneInfo.srcTableId = srcTableId;
- cloneInfo.tableName = tableName;
- cloneInfo.propertiesToExclude = propertiesToExclude;
- cloneInfo.propertiesToSet = propertiesToSet;
- cloneInfo.srcNamespaceId = namespaceId;
- cloneInfo.keepOffline = keepOffline;
+ public CloneTable(String user, NamespaceId srcNamespaceId, TableId
srcTableId,
+ NamespaceId namespaceId, String tableName, Map<String,String>
propertiesToSet,
+ Set<String> propertiesToExclude, boolean keepOffline) {
+ cloneInfo = new CloneInfo(srcNamespaceId, srcTableId, namespaceId,
tableName, propertiesToSet,
+ propertiesToExclude, keepOffline, user);
}
@Override
public long isReady(long tid, Manager environment) throws Exception {
- long val = Utils.reserveNamespace(environment, cloneInfo.srcNamespaceId,
tid, false, true,
+ long val = Utils.reserveNamespace(environment, cloneInfo.getNamespaceId(),
tid, false, true,
TableOperation.CLONE);
- val += Utils.reserveTable(environment, cloneInfo.srcTableId, tid, false,
true,
+ val += Utils.reserveTable(environment, cloneInfo.getSrcTableId(), tid,
false, true,
TableOperation.CLONE);
return val;
}
@@ -60,9 +55,8 @@ public class CloneTable extends ManagerRepo {
Utils.getIdLock().lock();
try {
- cloneInfo.tableId =
- Utils.getNextId(cloneInfo.tableName, environment.getContext(),
TableId::of);
-
+ cloneInfo.setTableId(
+ Utils.getNextId(cloneInfo.getTableName(), environment.getContext(),
TableId::of));
return new ClonePermissions(cloneInfo);
} finally {
Utils.getIdLock().unlock();
@@ -71,8 +65,8 @@ public class CloneTable extends ManagerRepo {
@Override
public void undo(long tid, Manager environment) {
- Utils.unreserveNamespace(environment, cloneInfo.srcNamespaceId, tid,
false);
- Utils.unreserveTable(environment, cloneInfo.srcTableId, tid, false);
+ Utils.unreserveNamespace(environment, cloneInfo.getNamespaceId(), tid,
false);
+ Utils.unreserveTable(environment, cloneInfo.getSrcTableId(), tid, false);
}
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneZookeeper.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneZookeeper.java
index 77ef41df12..fe9c9fcd13 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneZookeeper.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneZookeeper.java
@@ -37,20 +37,24 @@ class CloneZookeeper extends ManagerRepo {
public CloneZookeeper(CloneInfo cloneInfo, ClientContext context)
throws NamespaceNotFoundException {
this.cloneInfo = cloneInfo;
- this.cloneInfo.namespaceId = Namespaces.getNamespaceId(context,
- TableNameUtil.qualify(this.cloneInfo.tableName).getFirst());
+ if (this.cloneInfo.getNamespaceId() == null) {
+ // Prior to 2.1.4 the namespaceId was calculated in this
+ // step and set on the cloneInfo object here. If for some
+ // reason we are processing a pre-2.1.3 CloneTable operation,
+ // then we need to continue to set this here as it will be
+ // null in the deserialized CloneInfo object.
+ //
+ // TODO: Remove this check in 3.1 as Fate operations
+ // need to be cleaned up before a major upgrade.
+ this.cloneInfo.setNamespaceId(Namespaces.getNamespaceId(context,
+ TableNameUtil.qualify(this.cloneInfo.getTableName()).getFirst()));
+ }
}
@Override
public long isReady(long tid, Manager environment) throws Exception {
- long val = 0;
- if (!cloneInfo.srcNamespaceId.equals(cloneInfo.namespaceId)) {
- val += Utils.reserveNamespace(environment, cloneInfo.namespaceId, tid,
false, true,
- TableOperation.CLONE);
- }
- val +=
- Utils.reserveTable(environment, cloneInfo.tableId, tid, true, false,
TableOperation.CLONE);
- return val;
+ return Utils.reserveTable(environment, cloneInfo.getTableId(), tid, true,
false,
+ TableOperation.CLONE);
}
@Override
@@ -59,12 +63,12 @@ class CloneZookeeper extends ManagerRepo {
try {
// write tableName & tableId to zookeeper
- Utils.checkTableNameDoesNotExist(environment.getContext(),
cloneInfo.tableName,
- cloneInfo.namespaceId, cloneInfo.tableId, TableOperation.CLONE);
+ Utils.checkTableNameDoesNotExist(environment.getContext(),
cloneInfo.getTableName(),
+ cloneInfo.getNamespaceId(), cloneInfo.getTableId(),
TableOperation.CLONE);
- environment.getTableManager().cloneTable(cloneInfo.srcTableId,
cloneInfo.tableId,
- cloneInfo.tableName, cloneInfo.namespaceId,
cloneInfo.propertiesToSet,
- cloneInfo.propertiesToExclude);
+ environment.getTableManager().cloneTable(cloneInfo.getSrcTableId(),
cloneInfo.getTableId(),
+ cloneInfo.getTableName(), cloneInfo.getNamespaceId(),
cloneInfo.getPropertiesToSet(),
+ cloneInfo.getPropertiesToExclude());
environment.getContext().clearTableListCache();
return new CloneMetadata(cloneInfo);
@@ -75,11 +79,8 @@ class CloneZookeeper extends ManagerRepo {
@Override
public void undo(long tid, Manager environment) throws Exception {
- environment.getTableManager().removeTable(cloneInfo.tableId);
- if (!cloneInfo.srcNamespaceId.equals(cloneInfo.namespaceId)) {
- Utils.unreserveNamespace(environment, cloneInfo.namespaceId, tid, false);
- }
- Utils.unreserveTable(environment, cloneInfo.tableId, tid, true);
+ environment.getTableManager().removeTable(cloneInfo.getTableId());
+ Utils.unreserveTable(environment, cloneInfo.getTableId(), tid, true);
environment.getContext().clearTableListCache();
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/FinishCloneTable.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/FinishCloneTable.java
index 8eed682359..5fedbcfed7 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/FinishCloneTable.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/FinishCloneTable.java
@@ -50,26 +50,23 @@ class FinishCloneTable extends ManagerRepo {
// that are not used... tablet will create directories as needed
final EnumSet<TableState> expectedCurrStates = EnumSet.of(TableState.NEW);
- if (cloneInfo.keepOffline) {
- environment.getTableManager().transitionTableState(cloneInfo.tableId,
TableState.OFFLINE,
+ if (cloneInfo.isKeepOffline()) {
+
environment.getTableManager().transitionTableState(cloneInfo.getTableId(),
TableState.OFFLINE,
expectedCurrStates);
} else {
- environment.getTableManager().transitionTableState(cloneInfo.tableId,
TableState.ONLINE,
+
environment.getTableManager().transitionTableState(cloneInfo.getTableId(),
TableState.ONLINE,
expectedCurrStates);
}
+ Utils.unreserveTable(environment, cloneInfo.getTableId(), tid, true);
+ Utils.unreserveNamespace(environment, cloneInfo.getNamespaceId(), tid,
false);
+ Utils.unreserveTable(environment, cloneInfo.getSrcTableId(), tid, false);
- Utils.unreserveNamespace(environment, cloneInfo.srcNamespaceId, tid,
false);
- if (!cloneInfo.srcNamespaceId.equals(cloneInfo.namespaceId)) {
- Utils.unreserveNamespace(environment, cloneInfo.namespaceId, tid, false);
- }
- Utils.unreserveTable(environment, cloneInfo.srcTableId, tid, false);
- Utils.unreserveTable(environment, cloneInfo.tableId, tid, true);
-
- environment.getEventCoordinator().event("Cloned table %s from %s",
cloneInfo.tableName,
- cloneInfo.srcTableId);
+ environment.getEventCoordinator().event("Cloned table %s from %s",
cloneInfo.getTableName(),
+ cloneInfo.getSrcTableId());
- LoggerFactory.getLogger(FinishCloneTable.class).debug("Cloned table " +
cloneInfo.srcTableId
- + " " + cloneInfo.tableId + " " + cloneInfo.tableName);
+ LoggerFactory.getLogger(FinishCloneTable.class)
+ .debug("Cloned table " + cloneInfo.getSrcTableId() + " " +
cloneInfo.getTableId() + " "
+ + cloneInfo.getTableName());
return null;
}
diff --git
a/test/src/main/java/org/apache/accumulo/test/functional/CloneTestIT.java
b/test/src/main/java/org/apache/accumulo/test/functional/CloneTestIT.java
index 6390a2ca1c..7963043049 100644
--- a/test/src/main/java/org/apache/accumulo/test/functional/CloneTestIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/functional/CloneTestIT.java
@@ -42,12 +42,15 @@ import org.apache.accumulo.cluster.AccumuloCluster;
import org.apache.accumulo.core.client.Accumulo;
import org.apache.accumulo.core.client.AccumuloClient;
import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.BatchWriter;
import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.client.TableExistsException;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.client.admin.CloneConfiguration;
import org.apache.accumulo.core.client.admin.DiskUsage;
import org.apache.accumulo.core.client.admin.NewTableConfiguration;
+import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.clientImpl.ClientContext;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.Key;
@@ -61,6 +64,8 @@ import org.apache.accumulo.core.metadata.RootTable;
import
org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.DataFileColumnFamily;
import
org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily;
import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.NamespacePermission;
+import org.apache.accumulo.core.security.TablePermission;
import org.apache.accumulo.harness.AccumuloClusterHarness;
import org.apache.accumulo.miniclusterImpl.MiniAccumuloClusterImpl;
import org.apache.hadoop.fs.FileStatus;
@@ -350,4 +355,131 @@ public class CloneTestIT extends AccumuloClusterHarness {
"mc1", CloneConfiguration.empty()));
}
}
+
+ private void baseCloneNamespace(AccumuloClient client, String src, String
dest) throws Exception {
+ writeData(src, client).close();
+ // Don't force a flush on the table, let's make sure the
+ // clone operation does this
+ client.tableOperations().clone(src, dest, CloneConfiguration.empty());
+ checkData(dest, client);
+ }
+
+ @Test
+ public void testCloneSameNamespace() throws Exception {
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ String tableName = getUniqueNames(1)[0];
+ client.namespaceOperations().create("old");
+ client.tableOperations().create("old." + tableName);
+ assertThrows(TableExistsException.class,
+ () -> baseCloneNamespace(client, "old." + tableName, "old." +
tableName));
+ }
+ }
+
+ @Test
+ public void testCloneIntoDiffNamespace() throws Exception {
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ String tableName = getUniqueNames(1)[0];
+ client.namespaceOperations().create("old");
+ client.tableOperations().create("old." + tableName);
+ client.namespaceOperations().create("new");
+ baseCloneNamespace(client, "old." + tableName, "new." + tableName);
+ }
+ }
+
+ @Test
+ public void testCloneIntoDiffNamespaceTableExists() throws Exception {
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ String tableName = getUniqueNames(1)[0];
+ client.namespaceOperations().create("old");
+ client.tableOperations().create("old." + tableName);
+ client.namespaceOperations().create("new");
+ client.tableOperations().create("new." + tableName);
+ assertThrows(TableExistsException.class,
+ () -> baseCloneNamespace(client, "old." + tableName, "new." +
tableName));
+ }
+ }
+
+ @Test
+ public void testCloneIntoDiffNamespaceDoesntExist() throws Exception {
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ String tableName = getUniqueNames(1)[0];
+ client.namespaceOperations().create("old");
+ client.tableOperations().create("old." + tableName);
+ assertThrows(AccumuloException.class,
+ () -> baseCloneNamespace(client, "old." + tableName, "missing." +
tableName));
+ }
+ }
+
+ @Test
+ public void testCloneIntoAccumuloNamespace() throws Exception {
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ String tableName = getUniqueNames(1)[0];
+ client.namespaceOperations().create("old");
+ client.tableOperations().create("old." + tableName);
+ assertThrows(AccumuloException.class,
+ () -> baseCloneNamespace(client, "old." + tableName, "accumulo." +
tableName));
+ }
+ }
+
+ @Test
+ public void testCloneNamespaceIncorrectPermissions() throws Exception {
+ final String tableName = getUniqueNames(1)[0];
+ final String newUserName = "NEW_USER";
+ final String srcNs = "src";
+ final String srcTableName = srcNs + "." + tableName;
+ final String destNs = "dst";
+ final String dstTableName = destNs + "." + tableName;
+
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ client.namespaceOperations().create(srcNs);
+ client.tableOperations().create(srcTableName);
+ client.namespaceOperations().create(destNs);
+ client.securityOperations().createLocalUser(newUserName, new
PasswordToken(newUserName));
+ // User needs WRITE or ALTER_TABLE on the src table to flush it as part
of the clone operation
+ client.securityOperations().grantTablePermission(newUserName,
srcTableName,
+ TablePermission.ALTER_TABLE);
+ client.securityOperations().grantNamespacePermission(newUserName, destNs,
+ NamespacePermission.READ);
+ client.securityOperations().grantNamespacePermission(newUserName, destNs,
+ NamespacePermission.CREATE_TABLE);
+ }
+
+ try (AccumuloClient client =
+ Accumulo.newClient().to(getCluster().getInstanceName(),
getCluster().getZooKeepers())
+ .as(newUserName, newUserName).build()) {
+ // READ permission is needed on the src table, not the dst namespace
+ assertThrows(AccumuloSecurityException.class, () ->
client.tableOperations()
+ .clone(srcTableName, dstTableName, CloneConfiguration.empty()));
+ }
+ }
+
+ @Test
+ public void testCloneNamespacePermissions() throws Exception {
+ final String tableName = getUniqueNames(1)[0];
+ final String newUserName = "NEW_USER";
+ final String srcNs = "src";
+ final String srcTableName = srcNs + "." + tableName;
+ final String destNs = "dst";
+ final String dstTableName = destNs + "." + tableName;
+
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ client.namespaceOperations().create(srcNs);
+ client.tableOperations().create(srcTableName);
+ client.namespaceOperations().create(destNs);
+ client.securityOperations().createLocalUser(newUserName, new
PasswordToken(newUserName));
+ // User needs WRITE or ALTER_TABLE on the src table to flush it as part
of the clone operation
+ client.securityOperations().grantTablePermission(newUserName,
srcTableName,
+ TablePermission.ALTER_TABLE);
+ client.securityOperations().grantTablePermission(newUserName,
srcTableName,
+ TablePermission.READ);
+ client.securityOperations().grantNamespacePermission(newUserName, destNs,
+ NamespacePermission.CREATE_TABLE);
+ }
+
+ try (AccumuloClient client =
+ Accumulo.newClient().to(getCluster().getInstanceName(),
getCluster().getZooKeepers())
+ .as(newUserName, newUserName).build()) {
+ client.tableOperations().clone(srcTableName, dstTableName,
CloneConfiguration.empty());
+ }
+ }
}