Updated Branches: refs/heads/ACCUMULO-802 [created] 2f2ba30b2
http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/master/src/main/java/org/apache/accumulo/master/tableOps/CreateTableNamespace.java ---------------------------------------------------------------------- diff --git a/server/master/src/main/java/org/apache/accumulo/master/tableOps/CreateTableNamespace.java b/server/master/src/main/java/org/apache/accumulo/master/tableOps/CreateTableNamespace.java new file mode 100644 index 0000000..d6c6fc4 --- /dev/null +++ b/server/master/src/main/java/org/apache/accumulo/master/tableOps/CreateTableNamespace.java @@ -0,0 +1,188 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.accumulo.master.tableOps; + +import java.io.Serializable; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.impl.Tables; +import org.apache.accumulo.core.client.impl.thrift.TableOperation; +import org.apache.accumulo.fate.Repo; +import org.apache.accumulo.fate.zookeeper.ZooUtil.NodeExistsPolicy; +import org.apache.accumulo.master.Master; +import org.apache.accumulo.server.tables.TableManager; +import org.apache.accumulo.server.util.NamespacePropUtil; +import org.apache.log4j.Logger; + +class TableNamespaceInfo implements Serializable { + + private static final long serialVersionUID = 1L; + + String namespaceName; + String namespaceId; + String user; + + public Map<String,String> props; +} + +class FinishCreateTableNamespace extends MasterRepo { + + private static final long serialVersionUID = 1L; + + private TableNamespaceInfo tableNamespaceInfo; + + public FinishCreateTableNamespace(TableNamespaceInfo ti) { + this.tableNamespaceInfo = ti; + } + + @Override + public long isReady(long tid, Master environment) throws Exception { + return 0; + } + + @Override + public Repo<Master> call(long id, Master env) throws Exception { + + Utils.unreserveTableNamespace(tableNamespaceInfo.namespaceId, id, true); + + env.getEventCoordinator().event("Created table namespace %s ", tableNamespaceInfo.namespaceName); + + Logger.getLogger(FinishCreateTableNamespace.class).debug("Created table " + tableNamespaceInfo.namespaceId + " " + tableNamespaceInfo.namespaceName); + + return null; + } + + @Override + public String getReturn() { + return tableNamespaceInfo.namespaceId; + } + + @Override + public void undo(long tid, Master env) throws Exception {} + +} + +class PopulateZookeeperWithNamespace extends MasterRepo { + + private static final long serialVersionUID = 1L; + + private TableNamespaceInfo tableNamespaceInfo; + + PopulateZookeeperWithNamespace(TableNamespaceInfo ti) { + this.tableNamespaceInfo = ti; + } + + @Override + public long isReady(long id, Master environment) throws Exception { + return Utils.reserveTableNamespace(tableNamespaceInfo.namespaceId, id, true, false, TableOperation.CREATE); + } + + @Override + public Repo<Master> call(long tid, Master master) throws Exception { + + Utils.tableNameLock.lock(); + try { + Instance instance = master.getInstance(); + + Utils.checkTableNamespaceDoesNotExist(instance, tableNamespaceInfo.namespaceName, tableNamespaceInfo.namespaceId, TableOperation.CREATE); + + TableManager.getInstance().addNamespace(tableNamespaceInfo.namespaceId, tableNamespaceInfo.namespaceName, NodeExistsPolicy.OVERWRITE); + + for (Entry<String,String> entry : tableNamespaceInfo.props.entrySet()) + NamespacePropUtil.setNamespaceProperty(tableNamespaceInfo.namespaceId, entry.getKey(), entry.getValue()); + + Tables.clearCache(instance); + + return new FinishCreateTableNamespace(tableNamespaceInfo); + } finally { + Utils.tableNameLock.unlock(); + } + } + + @Override + public void undo(long tid, Master master) throws Exception { + TableManager.getInstance().removeNamespace(tableNamespaceInfo.namespaceId); + Tables.clearCache(master.getInstance()); + } + +} + +class SetupNamespacePermissions extends MasterRepo { + + private static final long serialVersionUID = 1L; + + private TableNamespaceInfo tableNamespaceInfo; + + public SetupNamespacePermissions(TableNamespaceInfo ti) { + this.tableNamespaceInfo = ti; + } + + @Override + public Repo<Master> call(long tid, Master env) throws Exception { + // TODO implement once namespace permissions exist (ACCUMULO-1479) + + // give all table permissions to the creator + /* + * SecurityOperation security = AuditedSecurityOperation.getInstance(); for (TableNamespacePermission permission : TableNamespacePermission.values()) { try + * { security.grantTableNamespacePermission(SecurityConstants.getSystemCredentials(), tableNamespaceInfo.user, tableNamespaceInfo.tableId, permission); } + * catch (ThriftSecurityException e) { Logger.getLogger(FinishCreateTableNamespace.class).error(e.getMessage(), e); throw e; } } + */ + + // setup permissions in zookeeper before table info in zookeeper + // this way concurrent users will not get a spurious permission denied + // error + return new PopulateZookeeperWithNamespace(tableNamespaceInfo); + } +} + +public class CreateTableNamespace extends MasterRepo { + private static final long serialVersionUID = 1L; + + private TableNamespaceInfo tableNamespaceInfo; + + public CreateTableNamespace(String user, String namespaceName, Map<String,String> props) { + tableNamespaceInfo = new TableNamespaceInfo(); + tableNamespaceInfo.namespaceName = namespaceName; + tableNamespaceInfo.user = user; + tableNamespaceInfo.props = props; + } + + @Override + public long isReady(long tid, Master environment) throws Exception { + return 0; + } + + @Override + public Repo<Master> call(long tid, Master master) throws Exception { + Utils.idLock.lock(); + try { + tableNamespaceInfo.namespaceId = Utils.getNextTableId(tableNamespaceInfo.namespaceName, master.getInstance()); + return new SetupNamespacePermissions(tableNamespaceInfo); + } finally { + Utils.idLock.unlock(); + } + + } + + @Override + public void undo(long tid, Master env) throws Exception { + // nothing to do, the namespace id was allocated! + } + +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/master/src/main/java/org/apache/accumulo/master/tableOps/DeleteTableNamespace.java ---------------------------------------------------------------------- diff --git a/server/master/src/main/java/org/apache/accumulo/master/tableOps/DeleteTableNamespace.java b/server/master/src/main/java/org/apache/accumulo/master/tableOps/DeleteTableNamespace.java new file mode 100644 index 0000000..5013a2f --- /dev/null +++ b/server/master/src/main/java/org/apache/accumulo/master/tableOps/DeleteTableNamespace.java @@ -0,0 +1,100 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.accumulo.master.tableOps; + +import org.apache.accumulo.core.client.impl.Tables; +import org.apache.accumulo.core.client.impl.thrift.TableOperation; +import org.apache.accumulo.fate.Repo; +import org.apache.accumulo.master.Master; +import org.apache.accumulo.server.tables.TableManager; +import org.apache.log4j.Logger; + +class NamespaceCleanUp extends MasterRepo { + + final private static Logger log = Logger.getLogger(CleanUp.class); + + private static final long serialVersionUID = 1L; + + private String namespaceId; + + public NamespaceCleanUp(String namespaceId) { + this.namespaceId = namespaceId; + } + + @Override + public long isReady(long tid, Master master) throws Exception { + return 0; + } + + @Override + public Repo<Master> call(long id, Master master) throws Exception { + + // remove from zookeeper + try { + TableManager.getInstance().removeNamespace(namespaceId); + } catch (Exception e) { + log.error("Failed to find table namespace in zookeeper", e); + } + Tables.clearCache(master.getInstance()); + + // TODO remove any permissions associated with this once they exist (ACCUMULO-1479) + /* + * try { AuditedSecurityOperation.getInstance().deleteTable(SecurityConstants.getSystemCredentials(), namespaceName); } catch (ThriftSecurityException e) { + * log.error(e.getMessage(), e); } + */ + + Utils.unreserveTableNamespace(namespaceId, id, true); + + Logger.getLogger(CleanUp.class).debug("Deleted table namespace " + namespaceId); + + return null; + } + + @Override + public void undo(long tid, Master environment) throws Exception { + // nothing to do + } + +} + +public class DeleteTableNamespace extends MasterRepo { + + private static final long serialVersionUID = 1L; + + private String namespaceId; + + public DeleteTableNamespace(String namespaceId) { + this.namespaceId = namespaceId; + } + + @Override + public long isReady(long id, Master environment) throws Exception { + return Utils.reserveTableNamespace(namespaceId, id, true, true, TableOperation.DELETE); + } + + @Override + public Repo<Master> call(long tid, Master environment) throws Exception { + environment.getEventCoordinator().event("deleting table namespace %s ", namespaceId); + return new NamespaceCleanUp(namespaceId); + } + + @Override + public void undo(long id, Master environment) throws Exception { + Utils.unreserveTableNamespace(namespaceId, id, true); + } + +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/master/src/main/java/org/apache/accumulo/master/tableOps/ImportTable.java ---------------------------------------------------------------------- diff --git a/server/master/src/main/java/org/apache/accumulo/master/tableOps/ImportTable.java b/server/master/src/main/java/org/apache/accumulo/master/tableOps/ImportTable.java index b91da52..4032ce5 100644 --- a/server/master/src/main/java/org/apache/accumulo/master/tableOps/ImportTable.java +++ b/server/master/src/main/java/org/apache/accumulo/master/tableOps/ImportTable.java @@ -35,6 +35,7 @@ import org.apache.accumulo.core.client.BatchWriter; import org.apache.accumulo.core.client.BatchWriterConfig; import org.apache.accumulo.core.client.Instance; import org.apache.accumulo.core.client.admin.TableOperationsImpl; +import org.apache.accumulo.core.client.impl.TableNamespaces; import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.client.impl.thrift.TableOperation; import org.apache.accumulo.core.client.impl.thrift.TableOperationExceptionType; @@ -445,6 +446,10 @@ class ImportPopulateZookeeper extends MasterRepo { TableManager.getInstance().addTable(tableInfo.tableId, tableInfo.tableName, NodeExistsPolicy.OVERWRITE); + String namespace = Tables.extractNamespace(tableInfo.tableName); + String namespaceId = TableNamespaces.getNamespaceId(instance, namespace); + TableManager.getInstance().addNamespaceToTable(tableInfo.tableId, namespaceId); + Tables.clearCache(instance); } finally { Utils.tableNameLock.unlock(); http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/master/src/main/java/org/apache/accumulo/master/tableOps/RenameTable.java ---------------------------------------------------------------------- diff --git a/server/master/src/main/java/org/apache/accumulo/master/tableOps/RenameTable.java b/server/master/src/main/java/org/apache/accumulo/master/tableOps/RenameTable.java index 3069aaf..900d9ea 100644 --- a/server/master/src/main/java/org/apache/accumulo/master/tableOps/RenameTable.java +++ b/server/master/src/main/java/org/apache/accumulo/master/tableOps/RenameTable.java @@ -18,6 +18,7 @@ package org.apache.accumulo.master.tableOps; import org.apache.accumulo.core.Constants; import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.impl.TableNamespaces; import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.client.impl.thrift.TableOperation; import org.apache.accumulo.core.client.impl.thrift.TableOperationExceptionType; @@ -27,40 +28,56 @@ import org.apache.accumulo.fate.Repo; import org.apache.accumulo.fate.zookeeper.IZooReaderWriter; import org.apache.accumulo.fate.zookeeper.IZooReaderWriter.Mutator; import org.apache.accumulo.master.Master; +import org.apache.accumulo.server.tables.TableManager; import org.apache.accumulo.server.zookeeper.ZooReaderWriter; import org.apache.log4j.Logger; public class RenameTable extends MasterRepo { - + private static final long serialVersionUID = 1L; private String tableId; private String oldTableName; private String newTableName; - + @Override public long isReady(long tid, Master environment) throws Exception { return Utils.reserveTable(tableId, tid, true, true, TableOperation.RENAME); } - + public RenameTable(String tableId, String oldTableName, String newTableName) { this.tableId = tableId; this.oldTableName = oldTableName; this.newTableName = newTableName; } - + @Override public Repo<Master> call(long tid, Master master) throws Exception { - + Instance instance = master.getInstance(); - + + final String namespace = Tables.extractNamespace(newTableName); + String namespaceId = TableNamespaces.getNamespaceId(instance, namespace); + final String oldNamespace = Tables.extractNamespace(oldTableName); + String oldNamespaceId = TableNamespaces.getNamespaceId(instance, oldNamespace); + + if (!namespaceId.equals(oldNamespaceId)) { + TableManager tm = TableManager.getInstance(); + tm.addNamespaceToTable(tableId, namespaceId); + } + + newTableName = Tables.extractTableName(newTableName); + oldTableName = Tables.extractTableName(oldTableName); + IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance(); + Utils.tableNameLock.lock(); try { Utils.checkTableDoesNotExist(instance, newTableName, tableId, TableOperation.RENAME); - + final String tap = ZooUtil.getRoot(instance) + Constants.ZTABLES + "/" + tableId + Constants.ZTABLE_NAME; - + zoo.mutate(tap, null, null, new Mutator() { + @Override public byte[] mutate(byte[] current) throws Exception { final String currentName = new String(current); if (currentName.equals(newTableName)) @@ -77,15 +94,15 @@ public class RenameTable extends MasterRepo { Utils.tableNameLock.unlock(); Utils.unreserveTable(tableId, tid, true); } - + Logger.getLogger(RenameTable.class).debug("Renamed table " + tableId + " " + oldTableName + " " + newTableName); - + return null; } - + @Override public void undo(long tid, Master env) throws Exception { Utils.unreserveTable(tableId, tid, true); } - + } http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/master/src/main/java/org/apache/accumulo/master/tableOps/RenameTableNamespace.java ---------------------------------------------------------------------- diff --git a/server/master/src/main/java/org/apache/accumulo/master/tableOps/RenameTableNamespace.java b/server/master/src/main/java/org/apache/accumulo/master/tableOps/RenameTableNamespace.java new file mode 100644 index 0000000..2da5d9f --- /dev/null +++ b/server/master/src/main/java/org/apache/accumulo/master/tableOps/RenameTableNamespace.java @@ -0,0 +1,91 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.accumulo.master.tableOps; + +import org.apache.accumulo.core.Constants; +import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.impl.Tables; +import org.apache.accumulo.core.client.impl.thrift.TableOperation; +import org.apache.accumulo.core.client.impl.thrift.TableOperationExceptionType; +import org.apache.accumulo.core.client.impl.thrift.ThriftTableOperationException; +import org.apache.accumulo.core.zookeeper.ZooUtil; +import org.apache.accumulo.fate.Repo; +import org.apache.accumulo.fate.zookeeper.IZooReaderWriter; +import org.apache.accumulo.fate.zookeeper.IZooReaderWriter.Mutator; +import org.apache.accumulo.master.Master; +import org.apache.accumulo.server.zookeeper.ZooReaderWriter; +import org.apache.log4j.Logger; + +public class RenameTableNamespace extends MasterRepo { + + private static final long serialVersionUID = 1L; + private String namespaceId; + private String oldName; + private String newName; + + @Override + public long isReady(long id, Master environment) throws Exception { + return Utils.reserveTableNamespace(namespaceId, id, true, true, TableOperation.RENAME); + } + + public RenameTableNamespace(String namespaceId, String oldName, String newName) { + this.namespaceId = namespaceId; + this.oldName = oldName; + this.newName = newName; + } + + @Override + public Repo<Master> call(long id, Master master) throws Exception { + + Instance instance = master.getInstance(); + + IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance(); + + Utils.tableNameLock.lock(); + try { + Utils.checkTableNamespaceDoesNotExist(instance, newName, namespaceId, TableOperation.RENAME); + + final String tap = ZooUtil.getRoot(instance) + Constants.ZNAMESPACES + "/" + namespaceId + Constants.ZNAMESPACE_NAME; + + zoo.mutate(tap, null, null, new Mutator() { + public byte[] mutate(byte[] current) throws Exception { + final String currentName = new String(current); + if (currentName.equals(newName)) + return null; // assume in this case the operation is running again, so we are done + if (!currentName.equals(oldName)) { + throw new ThriftTableOperationException(null, oldName, TableOperation.RENAME, TableOperationExceptionType.NOTFOUND, "Name changed while processing"); + } + return newName.getBytes(); + } + }); + Tables.clearCache(instance); + } finally { + Utils.tableNameLock.unlock(); + Utils.unreserveTable(namespaceId, id, true); + } + + Logger.getLogger(RenameTableNamespace.class).debug("Renamed table namespace " + namespaceId + " " + oldName + " " + newName); + + return null; + } + + @Override + public void undo(long tid, Master env) throws Exception { + Utils.unreserveTableNamespace(namespaceId, tid, true); + } + +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/master/src/main/java/org/apache/accumulo/master/tableOps/Utils.java ---------------------------------------------------------------------- diff --git a/server/master/src/main/java/org/apache/accumulo/master/tableOps/Utils.java b/server/master/src/main/java/org/apache/accumulo/master/tableOps/Utils.java index fa14f43..c01b5f0 100644 --- a/server/master/src/main/java/org/apache/accumulo/master/tableOps/Utils.java +++ b/server/master/src/main/java/org/apache/accumulo/master/tableOps/Utils.java @@ -22,6 +22,7 @@ import java.util.concurrent.locks.ReentrantLock; import org.apache.accumulo.core.Constants; import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.impl.TableNamespaces; import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.client.impl.thrift.TableOperation; import org.apache.accumulo.core.client.impl.thrift.TableOperationExceptionType; @@ -79,7 +80,7 @@ public class Utils { Instance instance = HdfsZooInstance.getInstance(); IZooReaderWriter zk = ZooReaderWriter.getRetryingInstance(); if (!zk.exists(ZooUtil.getRoot(instance) + Constants.ZTABLES + "/" + tableId)) - throw new ThriftTableOperationException(tableId, "", op, TableOperationExceptionType.NOTFOUND, "Table does not exists"); + throw new ThriftTableOperationException(tableId, "", op, TableOperationExceptionType.NOTFOUND, "Table does not exist"); } log.info("table " + tableId + " (" + Long.toHexString(tid) + ") locked for " + (writeLock ? "write" : "read") + " operation: " + op); return 0; @@ -87,6 +88,25 @@ public class Utils { return 100; } + public static void unreserveTableNamespace(String namespaceId, long id, boolean writeLock) throws Exception { + getLock(namespaceId, id, writeLock).unlock(); + log.info("table namespace " + namespaceId + " (" + Long.toHexString(id) + ") unlocked for " + (writeLock ? "write" : "read")); + } + + public static long reserveTableNamespace(String namespaceId, long id, boolean writeLock, boolean mustExist, TableOperation op) throws Exception { + if (getLock(namespaceId, id, writeLock).tryLock()) { + if (mustExist) { + Instance instance = HdfsZooInstance.getInstance(); + IZooReaderWriter zk = ZooReaderWriter.getRetryingInstance(); + if (!zk.exists(ZooUtil.getRoot(instance) + Constants.ZNAMESPACES+ "/" + namespaceId)) + throw new ThriftTableOperationException(namespaceId, "", op, TableOperationExceptionType.NOTFOUND, "Table namespace does not exist"); + } + log.info("table namespace " + namespaceId + " (" + Long.toHexString(id) + ") locked for " + (writeLock ? "write" : "read") + " operation: " + op); + return 0; + } else + return 100; + } + public static void unreserveTable(String tableId, long tid, boolean writeLock) throws Exception { getLock(tableId, tid, writeLock).unlock(); log.info("table " + tableId + " (" + Long.toHexString(tid) + ") unlocked for " + (writeLock ? "write" : "read")); @@ -129,4 +149,12 @@ public class Utils { return Utils.getLock(tableId, tid, false); } + + static void checkTableNamespaceDoesNotExist(Instance instance, String namespace, String namespaceId, TableOperation operation) throws ThriftTableOperationException { + + String n = TableNamespaces.getNameToIdMap(instance).get(namespace); + + if (n != null && !n.equals(namespaceId)) + throw new ThriftTableOperationException(null, namespace, operation, TableOperationExceptionType.EXISTS, null); + } } http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java ---------------------------------------------------------------------- diff --git a/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java b/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java index 31867f3..7f094c1 100644 --- a/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java +++ b/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java @@ -826,6 +826,29 @@ public class ShellServerIT extends SimpleMacIT { assertTrue(err.contains("BAD_CREDENTIALS for user NoSuchUser")); } + @Test(timeout = 30 * 1000) + public void tablenamespaces() throws Exception { + exec("namespaces", true, Constants.DEFAULT_TABLE_NAMESPACE, true); + exec("createnamespace thing1", true); + String namespaces = exec("namespaces"); + assertTrue(namespaces.contains("thing1")); + + exec("renamenamespace thing1 thing2"); + namespaces = exec("namespaces"); + assertTrue(namespaces.contains("thing2")); + assertTrue(!namespaces.contains("thing1")); + + exec("createtable thing2.thingy", true); + exec("deletenamespace thing2"); + exec("y"); + exec("namespaces", true, "thing2", true); + + exec("deletenamespace -f thing2", true); + namespaces = exec("namespaces"); + assertTrue(!namespaces.contains("thing2")); + exec("tables", true, "thing2.thingy", false); + } + private int countkeys(String table) throws IOException { exec("scan -np -t " + table); return output.get().split("\n").length - 1; http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/test/src/test/java/org/apache/accumulo/test/TableNamespacesTest.java ---------------------------------------------------------------------- diff --git a/test/src/test/java/org/apache/accumulo/test/TableNamespacesTest.java b/test/src/test/java/org/apache/accumulo/test/TableNamespacesTest.java new file mode 100644 index 0000000..6685103 --- /dev/null +++ b/test/src/test/java/org/apache/accumulo/test/TableNamespacesTest.java @@ -0,0 +1,281 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.accumulo.test; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Map.Entry; +import java.util.Random; + +import org.apache.accumulo.core.Constants; +import org.apache.accumulo.core.client.Connector; +import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.TableNamespaceNotEmptyException; +import org.apache.accumulo.core.client.impl.TableNamespaces; +import org.apache.accumulo.core.client.impl.Tables; +import org.apache.accumulo.core.conf.Property; +import org.apache.accumulo.minicluster.MiniAccumuloCluster; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class TableNamespacesTest { + + Random random = new Random(); + public static TemporaryFolder folder = new TemporaryFolder(); + static private MiniAccumuloCluster accumulo; + static private String secret = "secret"; + + @BeforeClass + static public void setUp() throws Exception { + folder.create(); + accumulo = new MiniAccumuloCluster(folder.getRoot(), secret); + accumulo.start(); + } + + @AfterClass + static public void tearDown() throws Exception { + accumulo.stop(); + folder.delete(); + } + + /** + * This test creates a table without specifying a namespace. In this case, it puts the table into the default namespace. + * + * @throws Exception + */ + @Test + public void testDefaultNamespace() throws Exception { + String tableName = "test"; + Connector c = accumulo.getConnector("root", secret); + + assertTrue(c.tableNamespaceOperations().exists(Constants.DEFAULT_TABLE_NAMESPACE)); + c.tableOperations().create(tableName); + assertTrue(c.tableOperations().exists(tableName)); + } + + /** + * This test creates a new namespace "testing" and a table "testing.table1" which puts "table1" into the "testing" namespace. Then we create "testing.table2" + * which creates "table2" and puts it into "testing" as well. Then we make sure that you can't delete a namespace with tables in it, and then we delete the + * tables and delete the namespace. + * + * @throws Exception + */ + @Test + public void testCreateAndDeleteNamespace() throws Exception { + String namespace = "testing"; + String tableName1 = namespace + ".table1"; + String tableName2 = namespace + ".table2"; + + Connector c = accumulo.getConnector("root", secret); + + c.tableNamespaceOperations().create(namespace); + assertTrue(c.tableNamespaceOperations().exists(namespace)); + + c.tableOperations().create(tableName1); + assertTrue(c.tableOperations().exists(tableName1)); + + c.tableOperations().create(tableName2); + assertTrue(c.tableOperations().exists(tableName2)); + + // deleting + try { + // can't delete a namespace with tables in it + c.tableNamespaceOperations().delete(namespace); + fail(); + } catch (TableNamespaceNotEmptyException e) { + // ignore, supposed to happen + } + assertTrue(c.tableNamespaceOperations().exists(namespace)); + assertTrue(c.tableOperations().exists(tableName1)); + assertTrue(c.tableOperations().exists(tableName2)); + + c.tableOperations().delete(tableName2); + assertTrue(!c.tableOperations().exists(tableName2)); + assertTrue(c.tableNamespaceOperations().exists(namespace)); + + c.tableOperations().delete(tableName1); + assertTrue(!c.tableOperations().exists(tableName1)); + c.tableNamespaceOperations().delete(namespace); + assertTrue(!c.tableNamespaceOperations().exists(namespace)); + } + + /** + * This test creates a namespace, modifies it's properties, and checks to make sure that those properties are applied to its tables. To do something on a + * namespace-wide level, use TableNamespaceOperations. + * + * Checks to make sure namespace-level properties are overridden by table-level properties. + * + * Checks to see if the default namespace's properties work as well. + * + * @throws Exception + */ + + @Test + public void testNamespaceProperties() throws Exception { + String namespace = "propchange"; + String tableName1 = namespace + ".table1"; + String tableName2 = namespace + ".table2"; + + String propKey = Property.TABLE_SCAN_MAXMEM.getKey(); + String propVal = "42K"; + + Connector c = accumulo.getConnector("root", secret); + + c.tableNamespaceOperations().create(namespace); + c.tableOperations().create(tableName1); + c.tableNamespaceOperations().setProperty(namespace, propKey, propVal); + + // check the namespace has the property + boolean itWorked = false; + for (Entry<String,String> prop : c.tableNamespaceOperations().getProperties(namespace)) { + if (prop.getKey().equals(propKey) && prop.getValue().equals(propVal)) { + itWorked = true; + break; + } + } + + assertTrue(itWorked); + + // check that the table gets it from the namespace + itWorked = false; + for (Entry<String,String> prop : c.tableOperations().getProperties(tableName1)) { + if (prop.getKey().equals(propKey) && prop.getValue().equals(propVal)) { + itWorked = true; + break; + } + } + assertTrue(itWorked); + + // test a second table to be sure the first wasn't magical + // (also, changed the order, the namespace already exists with the property) + itWorked = false; + c.tableOperations().create(tableName2); + for (Entry<String,String> prop : c.tableOperations().getProperties(tableName2)) { + if (prop.getKey().equals(propKey) && prop.getValue().equals(propVal)) { + itWorked = true; + break; + } + } + assertTrue(itWorked); + + // test that table properties override namespace properties + String propKey2 = Property.TABLE_FILE_MAX.getKey(); + String propVal2 = "42"; + String tablePropVal = "13"; + + c.tableOperations().setProperty(tableName2, propKey2, tablePropVal); + c.tableNamespaceOperations().setProperty("propchange", propKey2, propVal2); + + itWorked = false; + for (Entry<String,String> prop : c.tableOperations().getProperties(tableName2)) { + if (prop.getKey().equals(propKey2) && prop.getValue().equals(tablePropVal)) { + itWorked = true; + break; + } + } + assertTrue(itWorked); + + // now check that you can change the default namespace's properties + propVal = "13K"; + propVal2 = "44"; + String tableName = "some_table"; + c.tableOperations().create(tableName); + c.tableNamespaceOperations().setProperty(Constants.DEFAULT_TABLE_NAMESPACE, propKey, propVal); + + itWorked = false; + for (Entry<String,String> prop : c.tableOperations().getProperties(tableName)) { + if (prop.getKey().equals(propKey) && prop.getValue().equals(propVal)) { + itWorked = true; + break; + } + } + assertTrue(itWorked); + } + + /** + * This test creates a new user and a namespace. It checks to make sure the user can't modify anything in the namespace at first, then it grants the user + * permissions and makes sure that they can modify the namespace. Then it also checks if the user has the correct permissions on tables both already existing + * in the namespace and ones they create. + * + * @throws Exception + */ + @Test + public void testNamespacePermissions() throws Exception { + // TODO make the test once namespace-level permissions are implemented. (ACCUMULO-1479) + } + + /** + * This test renames and clones two separate table into different namespaces. different namespace. + * + * @throws Exception + */ + @Test + public void testRenameAndCloneTableToNewNamespace() throws Exception { + String namespace1 = "renamed"; + String namespace2 = "cloned"; + String tableName = "table"; + String tableName1 = "renamed.table1"; + String tableName2 = "cloned.table2"; + + Connector c = accumulo.getConnector("root", secret); + + c.tableOperations().create(tableName); + c.tableNamespaceOperations().create(namespace1); + c.tableNamespaceOperations().create(namespace2); + + c.tableOperations().rename(tableName, tableName1); + + assertTrue(c.tableOperations().exists(tableName1)); + assertTrue(!c.tableOperations().exists(tableName)); + + c.tableOperations().clone(tableName1, tableName2, false, null, null); + + assertTrue(c.tableOperations().exists(tableName1)); + assertTrue(c.tableOperations().exists(tableName2)); + return; + } + + /** + * This test renames a table namespace and ensures that its tables are still correct + */ + @Test + public void testNamespaceRename() throws Exception { + String namespace1 = "n1"; + String namespace2 = "n2"; + String table = "t"; + + Connector c = accumulo.getConnector("root", secret); + Instance instance = c.getInstance(); + + c.tableNamespaceOperations().create(namespace1); + c.tableOperations().create(namespace1 + "." + table); + + c.tableNamespaceOperations().rename(namespace1, namespace2); + + assertTrue(!c.tableNamespaceOperations().exists(namespace1)); + assertTrue(c.tableNamespaceOperations().exists(namespace2)); + assertTrue(c.tableOperations().exists(namespace2 + "." + table)); + String tid = Tables.getTableId(instance, namespace2 + "." + table); + String tnid = Tables.getNamespace(instance, tid); + String tnamespace = TableNamespaces.getNamespaceName(instance, tnid); + assertTrue(namespace2.equals(tnamespace)); + } +}
