This is an automated email from the ASF dual-hosted git repository. dlmarion pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/accumulo.git
commit 9426881c145dbe45fd8fea1cf3a09306d58bc974 Merge: b9bf07e68f f02d6a880e Author: Dave Marion <dlmar...@apache.org> AuthorDate: Thu Apr 3 13:24:58 2025 +0000 Merge branch '2.1' .../accumulo/manager/FateServiceHandler.java | 19 ++- .../accumulo/manager/tableOps/clone/CloneInfo.java | 74 ++++++++++-- .../manager/tableOps/clone/CloneMetadata.java | 11 +- .../manager/tableOps/clone/ClonePermissions.java | 8 +- .../manager/tableOps/clone/CloneTable.java | 30 ++--- .../manager/tableOps/clone/CloneZookeeper.java | 30 ++--- .../manager/tableOps/clone/FinishCloneTable.java | 28 +++-- .../accumulo/test/functional/CloneTestIT.java | 132 +++++++++++++++++++++ 8 files changed, 260 insertions(+), 72 deletions(-) diff --cc server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java index 0dac7412cf,c9ecd1d4d8..56ce16b7b1 --- a/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java +++ b/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java @@@ -363,9 -345,9 +372,9 @@@ class FateServiceHandler implements Fat goalMessage += " and keep offline."; } - manager.fate(type).seedTransaction( - op, fateId, new TraceRepo<>(new CloneTable(c.getPrincipal(), namespaceId, srcTableId, - tableName, propertiesToSet, propertiesToExclude, keepOffline)), - manager.fate().seedTransaction(op.toString(), opid, ++ manager.fate(type).seedTransaction(op, fateId, + new TraceRepo<>(new CloneTable(c.getPrincipal(), srcNamespaceId, srcTableId, + namespaceId, tableName, propertiesToSet, propertiesToExclude, keepOffline)), autoCleanup, goalMessage); break; diff --cc server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneInfo.java index 676e4d88e7,2a697827ad..5403cffc55 --- 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 +29,68 @@@ class CloneInfo implements Serializabl 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 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 --cc server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneMetadata.java index e7b554b559,82f73d0383..8f82ddbf3d --- 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 @@@ -40,21 -39,22 +40,22 @@@ class CloneMetadata extends ManagerRep } @Override - public Repo<Manager> call(long tid, Manager environment) throws Exception { + public Repo<Manager> call(FateId fateId, 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 { + public void undo(FateId fateId, Manager environment) throws Exception { - MetadataTableUtil.deleteTable(cloneInfo.tableId, false, environment.getContext(), + MetadataTableUtil.deleteTable(cloneInfo.getTableId(), false, environment.getContext(), environment.getManagerLock()); } diff --cc server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/ClonePermissions.java index 474aab0a14,ac1432c3d2..79ba23a940 --- 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 @@@ -72,8 -71,8 +72,8 @@@ class ClonePermissions extends ManagerR } @Override - public void undo(long tid, Manager environment) throws Exception { + public void undo(FateId fateId, Manager environment) throws Exception { environment.getContext().getSecurityOperation().deleteTable(environment.getContext().rpcCreds(), - cloneInfo.tableId, cloneInfo.namespaceId); + cloneInfo.getTableId(), cloneInfo.getNamespaceId()); } } diff --cc server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneTable.java index 4899723952,c5587ee692..a389bfe981 --- 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 @@@ -49,10 -42,10 +44,10 @@@ public class CloneTable extends Manager } @Override - public long isReady(long tid, Manager environment) throws Exception { - long val = Utils.reserveNamespace(environment, cloneInfo.getNamespaceId(), tid, false, true, - TableOperation.CLONE); - val += Utils.reserveTable(environment, cloneInfo.getSrcTableId(), tid, false, true, + public long isReady(FateId fateId, Manager environment) throws Exception { - long val = Utils.reserveNamespace(environment, cloneInfo.srcNamespaceId, fateId, LockType.READ, - true, TableOperation.CLONE); - val += Utils.reserveTable(environment, cloneInfo.srcTableId, fateId, LockType.READ, true, ++ long val = Utils.reserveNamespace(environment, cloneInfo.getNamespaceId(), fateId, ++ LockType.READ, true, TableOperation.CLONE); ++ val += Utils.reserveTable(environment, cloneInfo.getSrcTableId(), fateId, LockType.READ, true, TableOperation.CLONE); return val; } @@@ -72,9 -64,9 +66,9 @@@ } @Override - public void undo(long tid, Manager environment) { - Utils.unreserveNamespace(environment, cloneInfo.getNamespaceId(), tid, false); - Utils.unreserveTable(environment, cloneInfo.getSrcTableId(), tid, false); + public void undo(FateId fateId, Manager environment) { - Utils.unreserveNamespace(environment, cloneInfo.srcNamespaceId, fateId, LockType.READ); - Utils.unreserveTable(environment, cloneInfo.srcTableId, fateId, LockType.READ); ++ Utils.unreserveNamespace(environment, cloneInfo.getNamespaceId(), fateId, LockType.READ); ++ Utils.unreserveTable(environment, cloneInfo.getSrcTableId(), fateId, LockType.READ); } } diff --cc server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/CloneZookeeper.java index c9872e7051,fe9c9fcd13..0cb1dde6bd --- 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 @@@ -20,12 -20,10 +20,10 @@@ package org.apache.accumulo.manager.tab import org.apache.accumulo.core.client.NamespaceNotFoundException; import org.apache.accumulo.core.clientImpl.ClientContext; --import org.apache.accumulo.core.clientImpl.Namespaces; import org.apache.accumulo.core.clientImpl.thrift.TableOperation; +import org.apache.accumulo.core.fate.FateId; import org.apache.accumulo.core.fate.Repo; -import org.apache.accumulo.core.util.tables.TableNameUtil; +import org.apache.accumulo.core.fate.zookeeper.DistributedReadWriteLock.LockType; - import org.apache.accumulo.core.util.tables.TableNameUtil; import org.apache.accumulo.manager.Manager; import org.apache.accumulo.manager.tableOps.ManagerRepo; import org.apache.accumulo.manager.tableOps.Utils; @@@ -39,20 -37,24 +37,18 @@@ class CloneZookeeper extends ManagerRep 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 { - return Utils.reserveTable(environment, cloneInfo.getTableId(), tid, true, false, + public long isReady(FateId fateId, Manager environment) throws Exception { + long val = 0; - if (!cloneInfo.srcNamespaceId.equals(cloneInfo.namespaceId)) { - val += Utils.reserveNamespace(environment, cloneInfo.namespaceId, fateId, LockType.READ, true, - TableOperation.CLONE); ++ if (!cloneInfo.getSrcNamespaceId().equals(cloneInfo.getNamespaceId())) { ++ val += Utils.reserveNamespace(environment, cloneInfo.getNamespaceId(), fateId, LockType.READ, ++ true, TableOperation.CLONE); + } - val += Utils.reserveTable(environment, cloneInfo.tableId, fateId, LockType.WRITE, false, ++ val += Utils.reserveTable(environment, cloneInfo.getTableId(), fateId, LockType.WRITE, false, TableOperation.CLONE); + return val; } @Override @@@ -76,12 -78,9 +72,12 @@@ } @Override - public void undo(long tid, Manager environment) throws Exception { + public void undo(FateId fateId, Manager environment) throws Exception { - environment.getTableManager().removeTable(cloneInfo.tableId); - if (!cloneInfo.srcNamespaceId.equals(cloneInfo.namespaceId)) { - Utils.unreserveNamespace(environment, cloneInfo.namespaceId, fateId, LockType.READ); + environment.getTableManager().removeTable(cloneInfo.getTableId()); - Utils.unreserveTable(environment, cloneInfo.getTableId(), tid, true); ++ if (!cloneInfo.getSrcNamespaceId().equals(cloneInfo.getNamespaceId())) { ++ Utils.unreserveNamespace(environment, cloneInfo.getNamespaceId(), fateId, LockType.READ); + } - Utils.unreserveTable(environment, cloneInfo.tableId, fateId, LockType.WRITE); ++ Utils.unreserveTable(environment, cloneInfo.getTableId(), fateId, LockType.WRITE); environment.getContext().clearTableListCache(); } diff --cc server/manager/src/main/java/org/apache/accumulo/manager/tableOps/clone/FinishCloneTable.java index 1730a81792,5fedbcfed7..dd7c0fa908 --- 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 @@@ -52,27 -50,23 +52,29 @@@ class FinishCloneTable extends ManagerR // 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.getTableId(), TableState.ONLINE, + // transition clone table state to state of original table - TableState ts = environment.getTableManager().getTableState(cloneInfo.srcTableId); - environment.getTableManager().transitionTableState(cloneInfo.tableId, ts, expectedCurrStates); ++ TableState ts = environment.getTableManager().getTableState(cloneInfo.getSrcTableId()); ++ environment.getTableManager().transitionTableState(cloneInfo.getTableId(), ts, + 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, fateId, LockType.READ); - if (!cloneInfo.srcNamespaceId.equals(cloneInfo.namespaceId)) { - Utils.unreserveNamespace(environment, cloneInfo.namespaceId, fateId, LockType.READ); - environment.getEventCoordinator().event("Cloned table %s from %s", cloneInfo.getTableName(), - cloneInfo.getSrcTableId()); ++ Utils.unreserveNamespace(environment, cloneInfo.getSrcNamespaceId(), fateId, LockType.READ); ++ if (!cloneInfo.getSrcNamespaceId().equals(cloneInfo.getNamespaceId())) { ++ Utils.unreserveNamespace(environment, cloneInfo.getNamespaceId(), fateId, LockType.READ); + } - Utils.unreserveTable(environment, cloneInfo.srcTableId, fateId, LockType.READ); - Utils.unreserveTable(environment, cloneInfo.tableId, fateId, LockType.WRITE); ++ Utils.unreserveTable(environment, cloneInfo.getSrcTableId(), fateId, LockType.READ); ++ Utils.unreserveTable(environment, cloneInfo.getTableId(), fateId, LockType.WRITE); + - environment.getEventCoordinator().event(cloneInfo.tableId, "Cloned table %s from %s", - cloneInfo.tableName, cloneInfo.srcTableId); ++ environment.getEventCoordinator().event(cloneInfo.getTableId(), "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 --cc test/src/main/java/org/apache/accumulo/test/functional/CloneTestIT.java index a586bd4c7e,7963043049..71e3dfdad0 --- a/test/src/main/java/org/apache/accumulo/test/functional/CloneTestIT.java +++ b/test/src/main/java/org/apache/accumulo/test/functional/CloneTestIT.java @@@ -62,7 -64,9 +65,9 @@@ import org.apache.accumulo.core.metadat 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.harness.SharedMiniClusterBase; import org.apache.accumulo.miniclusterImpl.MiniAccumuloClusterImpl; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; @@@ -362,8 -351,135 +367,135 @@@ public class CloneTestIT extends Shared @Test public void testCloneMetadataTable() { try (AccumuloClient client = Accumulo.newClient().from(getClientProps()).build()) { - assertThrows(AccumuloException.class, () -> client.tableOperations().clone(MetadataTable.NAME, - "mc1", CloneConfiguration.empty())); + assertThrows(AccumuloException.class, () -> client.tableOperations() + .clone(AccumuloTable.METADATA.tableName(), "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); ++ client.namespaceOperations().create("old3"); ++ client.tableOperations().create("old3." + tableName); + assertThrows(TableExistsException.class, - () -> baseCloneNamespace(client, "old." + tableName, "old." + tableName)); ++ () -> baseCloneNamespace(client, "old3." + tableName, "old3." + 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); ++ client.namespaceOperations().create("old4"); ++ client.tableOperations().create("old4." + tableName); ++ client.namespaceOperations().create("new4"); ++ baseCloneNamespace(client, "old4." + tableName, "new4." + 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); ++ client.namespaceOperations().create("old5"); ++ client.tableOperations().create("old5." + tableName); ++ client.namespaceOperations().create("new5"); ++ client.tableOperations().create("new5." + tableName); + assertThrows(TableExistsException.class, - () -> baseCloneNamespace(client, "old." + tableName, "new." + tableName)); ++ () -> baseCloneNamespace(client, "old5." + tableName, "new5." + 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); ++ client.namespaceOperations().create("old1"); ++ client.tableOperations().create("old1." + tableName); + assertThrows(AccumuloException.class, - () -> baseCloneNamespace(client, "old." + tableName, "missing." + tableName)); ++ () -> baseCloneNamespace(client, "old1." + 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); ++ client.namespaceOperations().create("old2"); ++ client.tableOperations().create("old2." + tableName); + assertThrows(AccumuloException.class, - () -> baseCloneNamespace(client, "old." + tableName, "accumulo." + tableName)); ++ () -> baseCloneNamespace(client, "old2." + 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 newUserName = "NEW_USER2"; ++ final String srcNs = "src2"; + final String srcTableName = srcNs + "." + tableName; - final String destNs = "dst"; ++ final String destNs = "dst2"; + 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()); + } + } }