/** * diqube: Distributed Query Base. * * Copyright (C) 2015 Bastian Gloeckle * * This file is part of diqube. * * diqube is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.diqube.itest.tests; import java.io.IOException; import java.util.UUID; import org.diqube.itest.AbstractDiqubeIntegrationTest; import org.diqube.itest.annotations.NeedsServer; import org.diqube.itest.control.ServerControl; import org.diqube.itest.util.QueryResultServiceTestUtil; import org.diqube.itest.util.QueryResultServiceTestUtil.TestQueryResultService; import org.diqube.itest.util.Waiter; import org.diqube.name.FlattenedTableNameUtil; import org.diqube.permission.Permissions; import org.diqube.remote.query.thrift.ROptionalTableMetadata; import org.diqube.server.ControlFileManager; import org.diqube.thrift.base.thrift.AuthorizationException; import org.diqube.thrift.base.thrift.FieldMetadata; import org.diqube.thrift.base.thrift.FieldType; import org.diqube.thrift.base.thrift.RUUID; import org.diqube.thrift.base.thrift.TableMetadata; import org.diqube.thrift.base.thrift.Ticket; import org.diqube.thrift.base.util.RUuidUtil; import org.diqube.tool.im.AddPermissionActualIdentityToolFunction; import org.diqube.tool.im.CreateUserActualIdentityToolFunction; import org.diqube.util.Holder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.Test; /** * Tests whether {@link TableMetadata} publishing works across the cluster. * * @author Bastian Gloeckle */ public class TableMetadataIntegrationTest extends AbstractDiqubeIntegrationTest { private static final Logger logger = LoggerFactory.getLogger(TableMetadataIntegrationTest.class); private static final String TABLE = "testtable"; private static final String FIELD1_CONTROL_FILE_ROW_0 = "/" + TableMetadataIntegrationTest.class.getSimpleName() + "/field1_rowId0" + ControlFileManager.CONTROL_FILE_EXTENSION; private static final String FIELD1_CONTROL_FILE_ROW_11 = "/" + TableMetadataIntegrationTest.class.getSimpleName() + "/field1_rowId11" + ControlFileManager.CONTROL_FILE_EXTENSION; private static final String FIELD1_JSON_FILE = "/" + TableMetadataIntegrationTest.class.getSimpleName() + "/field1.json"; private static final String FIELD2_CONTROL_FILE_ROW_11 = "/" + TableMetadataIntegrationTest.class.getSimpleName() + "/field2_rowId11" + ControlFileManager.CONTROL_FILE_EXTENSION; private static final String FIELD2_JSON_FILE = "/" + TableMetadataIntegrationTest.class.getSimpleName() + "/field2.json"; private static final String FLATTEN_CONTROL_FILE = "/" + TableMetadataIntegrationTest.class.getSimpleName() + "/flattendata0" + ControlFileManager.CONTROL_FILE_EXTENSION; private static final String FLATTEN_AUTOFLATTEN_CONTROL_FILE = "/" + TableMetadataIntegrationTest.class.getSimpleName() + "/flattendata0_autoflatten" + ControlFileManager.CONTROL_FILE_EXTENSION; private static final String FLATTEN_JSON_FILE = "/" + TableMetadataIntegrationTest.class.getSimpleName() + "/flattendata.json"; private static final String USER = "user1"; private static final String PASSWD = "passwd"; private static final String EMAIL = "a@b.c"; @Test @NeedsServer(servers = 2) public void deployOneOnly() { serverControl.get(0).deploy(cp(FIELD1_CONTROL_FILE_ROW_0), cp(FIELD1_JSON_FILE)); Ticket s = serverControl.get(0).loginSuperuser(); serverControl.get(1).getSerivceTestUtil().tableMetadataService(tableMetadataService -> { ROptionalTableMetadata m = tableMetadataService.getTableMetadata(s, TABLE); Assert.assertTrue(m.isSetTableMetadata(), "Expected to receive table metadata"); Assert.assertEquals(m.getTableMetadata().getTableName(), TABLE, "Expected correct table name in returned metadata"); Assert.assertEquals(m.getTableMetadata().getFields().size(), 1, "Expected correct number of fields in metadata"); FieldMetadata fieldMetadata = m.getTableMetadata().getFields().get(0); Assert.assertEquals(fieldMetadata.getFieldName(), "field1", "Expected correct field name"); Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.LONG, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); }); } @Test @NeedsServer(servers = 2) public void deployTwoSameOnly() { serverControl.get(0).deploy(cp(FIELD1_CONTROL_FILE_ROW_0), cp(FIELD1_JSON_FILE)); serverControl.get(1).deploy(cp(FIELD1_CONTROL_FILE_ROW_11), cp(FIELD1_JSON_FILE)); Ticket s = serverControl.get(0).loginSuperuser(); serverControl.get(1).getSerivceTestUtil().tableMetadataService(tableMetadataService -> { ROptionalTableMetadata m = tableMetadataService.getTableMetadata(s, TABLE); Assert.assertTrue(m.isSetTableMetadata(), "Expected to receive table metadata"); Assert.assertEquals(m.getTableMetadata().getTableName(), TABLE, "Expected correct table name in returned metadata"); Assert.assertEquals(m.getTableMetadata().getFields().size(), 1, "Expected correct number of fields in metadata"); FieldMetadata fieldMetadata = m.getTableMetadata().getFields().get(0); Assert.assertEquals(fieldMetadata.getFieldName(), "field1", "Expected correct field name"); Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.LONG, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); }); } @Test @NeedsServer(servers = 2) public void deployTwoDifferent() { serverControl.get(0).deploy(cp(FIELD1_CONTROL_FILE_ROW_0), cp(FIELD1_JSON_FILE)); serverControl.get(1).deploy(cp(FIELD2_CONTROL_FILE_ROW_11), cp(FIELD2_JSON_FILE)); Ticket s = serverControl.get(0).loginSuperuser(); serverControl.get(0).getSerivceTestUtil().tableMetadataService(tableMetadataService -> { ROptionalTableMetadata m = tableMetadataService.getTableMetadata(s, TABLE); Assert.assertTrue(m.isSetTableMetadata(), "Expected to receive table metadata"); Assert.assertEquals(m.getTableMetadata().getTableName(), TABLE, "Expected correct table name in returned metadata"); Assert.assertEquals(m.getTableMetadata().getFields().size(), 2, "Expected correct number of fields in metadata"); FieldMetadata fieldMetadata = m.getTableMetadata().getFields().get(0); if (fieldMetadata.getFieldName().equals("field1")) { Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.LONG, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } else { Assert.assertEquals(fieldMetadata.getFieldName(), "field2", "Expected correct field name"); Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.STRING, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } fieldMetadata = m.getTableMetadata().getFields().get(1); if (fieldMetadata.getFieldName().equals("field1")) { Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.LONG, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } else { Assert.assertEquals(fieldMetadata.getFieldName(), "field2", "Expected correct field name"); Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.STRING, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } }); } @Test @NeedsServer(servers = 2) public void deployTwoDifferentUndeployAgain() { serverControl.get(0).deploy(cp(FIELD1_CONTROL_FILE_ROW_0), cp(FIELD1_JSON_FILE)); serverControl.get(1).deploy(cp(FIELD2_CONTROL_FILE_ROW_11), cp(FIELD2_JSON_FILE)); Ticket s = serverControl.get(0).loginSuperuser(); serverControl.get(0).undeploy(cp(FIELD1_CONTROL_FILE_ROW_0)); serverControl.get(0).getSerivceTestUtil().tableMetadataService(tableMetadataService -> { new Waiter().waitUntil("New table metadata is available", 10, 500, () -> { ROptionalTableMetadata m; try { m = tableMetadataService.getTableMetadata(s, TABLE); } catch (Exception e) { logger.warn("Excpetion when trying to fetch metadata", e); return false; } return m.isSetTableMetadata() && m.getTableMetadata().getFields().size() == 1; }); ROptionalTableMetadata m = tableMetadataService.getTableMetadata(s, TABLE); Assert.assertTrue(m.isSetTableMetadata(), "Expected to receive table metadata"); Assert.assertEquals(m.getTableMetadata().getTableName(), TABLE, "Expected correct table name in returned metadata"); Assert.assertEquals(m.getTableMetadata().getFields().size(), 1, "Expected correct number of fields in metadata"); FieldMetadata fieldMetadata = m.getTableMetadata().getFields().get(0); Assert.assertEquals(fieldMetadata.getFieldName(), "field2", "Expected correct field name"); Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.STRING, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); }); } @Test @NeedsServer public void flattenByQueryMetadataPublished() { serverControl.get(0).deploy(cp(FLATTEN_CONTROL_FILE), cp(FLATTEN_JSON_FILE)); Ticket s = serverControl.get(0).loginSuperuser(); logger.info("Starting to execute query whcih should trigger flattening"); try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) { RUUID queryUuid = RUuidUtil.toRUuid(UUID.randomUUID()); logger.info("Executing query {}", RUuidUtil.toUuid(queryUuid)); serverControl.get(0).getSerivceTestUtil() .queryService((queryService) -> queryService.asyncExecuteQuery(s, queryUuid, "select a.b, count() from flatten(" + TABLE + ", a[*]) group by a.b order by a.b asc", true, queryRes.getThisServicesAddr().toRNodeAddress())); new Waiter().waitUntil("Final result of query received, flattening therefore done", 10, 500, () -> queryRes.check() && queryRes.getFinalUpdate() != null); } catch (IOException e) { throw new RuntimeException("Could not execute query", e); } logger.info("Accessing ClusterFlattenService to find the UUID of the flattening"); Holder<RUUID> flatteningRUuid = new Holder<>(); new Waiter().waitUntil("Newest flattening info is available on server", 2, 200, () -> { serverControl.get(0).getSerivceTestUtil().clusterFlattenService(clusterFlattenService -> { flatteningRUuid.setValue(clusterFlattenService.getLatestValidFlattening(TABLE, "a[*]").getUuid()); }); return flatteningRUuid.getValue() != null; }); FlattenedTableNameUtil flattenedTableNameGenerator = new FlattenedTableNameUtil(); String flattenedTableName = flattenedTableNameGenerator.createFlattenedTableName(TABLE, "a[*]", RUuidUtil.toUuid(flatteningRUuid.getValue())); logger.info("Checking if metadata is available for flattened table."); serverControl.get(0).getSerivceTestUtil().tableMetadataService(tableMetadataService -> { new Waiter().waitUntil("Metadata of flattened table is available", 10, 500, () -> { ROptionalTableMetadata m; try { m = tableMetadataService.getTableMetadata(s, flattenedTableName); } catch (Exception e) { logger.warn("Excpetion when trying to fetch metadata", e); return false; } return m.isSetTableMetadata() && m.getTableMetadata().getFields().size() == 2; }); ROptionalTableMetadata m = tableMetadataService.getTableMetadata(s, flattenedTableName); Assert.assertTrue(m.isSetTableMetadata(), "Expected to receive table metadata"); Assert.assertEquals(m.getTableMetadata().getTableName(), flattenedTableName, "Expected correct table name in returned metadata"); Assert.assertEquals(m.getTableMetadata().getFields().size(), 2, "Expected correct number of fields in metadata"); FieldMetadata fieldMetadata = m.getTableMetadata().getFields().get(0); if (fieldMetadata.getFieldName().equals("a")) { Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.CONTAINER, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } else { Assert.assertEquals(fieldMetadata.getFieldName(), "a.b", "Expected correct field name"); Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.LONG, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } }); } @Test @NeedsServer public void flattenByAutoflatten() { serverControl.get(0).deploy(cp(FLATTEN_AUTOFLATTEN_CONTROL_FILE), cp(FLATTEN_JSON_FILE)); Ticket s = serverControl.get(0).loginSuperuser(); logger.info("Accessing ClusterFlattenService to find the UUID of the flattening"); Holder<RUUID> flatteningRUuid = new Holder<>(); new Waiter().waitUntil("Newest flattening info is available on server", 2, 200, () -> { serverControl.get(0).getSerivceTestUtil().clusterFlattenService(clusterFlattenService -> { flatteningRUuid.setValue(clusterFlattenService.getLatestValidFlattening(TABLE, "a[*]").getUuid()); }); return flatteningRUuid.getValue() != null; }); FlattenedTableNameUtil flattenedTableNameGenerator = new FlattenedTableNameUtil(); String flattenedTableName = flattenedTableNameGenerator.createFlattenedTableName(TABLE, "a[*]", RUuidUtil.toUuid(flatteningRUuid.getValue())); logger.info("Checking if metadata is available for flattened table."); serverControl.get(0).getSerivceTestUtil().tableMetadataService(tableMetadataService -> { new Waiter().waitUntil("Metadata of flattened table is available", 10, 500, () -> { ROptionalTableMetadata m; try { m = tableMetadataService.getTableMetadata(s, flattenedTableName); } catch (Exception e) { logger.warn("Excpetion when trying to fetch metadata", e); return false; } return m.isSetTableMetadata() && m.getTableMetadata().getFields().size() == 2; }); ROptionalTableMetadata m = tableMetadataService.getTableMetadata(s, flattenedTableName); Assert.assertTrue(m.isSetTableMetadata(), "Expected to receive table metadata"); Assert.assertEquals(m.getTableMetadata().getTableName(), flattenedTableName, "Expected correct table name in returned metadata"); Assert.assertEquals(m.getTableMetadata().getFields().size(), 2, "Expected correct number of fields in metadata"); FieldMetadata fieldMetadata = m.getTableMetadata().getFields().get(0); if (fieldMetadata.getFieldName().equals("a")) { Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.CONTAINER, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } else { Assert.assertEquals(fieldMetadata.getFieldName(), "a.b", "Expected correct field name"); Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.LONG, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } }); } @Test @NeedsServer public void flattenByAutoflattenAndIncompleteFlattenTableNameQuery() { serverControl.get(0).deploy(cp(FLATTEN_AUTOFLATTEN_CONTROL_FILE), cp(FLATTEN_JSON_FILE)); Ticket s = serverControl.get(0).loginSuperuser(); // this one does not include the flattenId! It is more like the statement used in a diql query. String incompleteFlattenTableName = "flatten(" + TABLE + ", a[*])"; logger.info("Checking if metadata is available for flattened table."); serverControl.get(0).getSerivceTestUtil().tableMetadataService(tableMetadataService -> { new Waiter().waitUntil("Metadata of flattened table is available", 10, 500, () -> { ROptionalTableMetadata m; try { m = tableMetadataService.getTableMetadata(s, incompleteFlattenTableName); } catch (Exception e) { logger.warn("Excpetion when trying to fetch metadata", e); return false; } return m.isSetTableMetadata() && m.getTableMetadata().getFields().size() == 2; }); ROptionalTableMetadata m = tableMetadataService.getTableMetadata(s, incompleteFlattenTableName); Assert.assertTrue(m.isSetTableMetadata(), "Expected to receive table metadata"); // the function should NOT return the table name with the flattenId. If the user did not know the flattenId, we do // not want to tell him - because in the end this is some internal information he should not care about! Assert.assertEquals(m.getTableMetadata().getTableName(), incompleteFlattenTableName, "Expected correct table name in returned metadata"); Assert.assertEquals(m.getTableMetadata().getFields().size(), 2, "Expected correct number of fields in metadata"); FieldMetadata fieldMetadata = m.getTableMetadata().getFields().get(0); if (fieldMetadata.getFieldName().equals("a")) { Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.CONTAINER, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } else { Assert.assertEquals(fieldMetadata.getFieldName(), "a.b", "Expected correct field name"); Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.LONG, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } }); } @Test @NeedsServer public void flattenByAutoflattenNonRoot() { serverControl.get(0).deploy(cp(FLATTEN_AUTOFLATTEN_CONTROL_FILE), cp(FLATTEN_JSON_FILE)); logger.info("Creating new user with access to test table"); toolControl.im(serverControl.get(0).getAddr(), CreateUserActualIdentityToolFunction.FUNCTION_NAME, // ServerControl.ROOT_USER, ServerControl.ROOT_PASSWORD, // USER, // paramUser PASSWD, // paramPassword EMAIL, // paramEmail null, // paramPermission null // paramPermissionObject ); toolControl.im(serverControl.get(0).getAddr(), AddPermissionActualIdentityToolFunction.FUNCTION_NAME, // ServerControl.ROOT_USER, ServerControl.ROOT_PASSWORD, // USER, // paramUser null, // paramPassword null, // paramEmail Permissions.TABLE_ACCESS, // paramPermission TABLE // paramPermissionObject ); Ticket ticket = serverControl.get(0).login(USER, PASSWD); // this one does not include the flattenId! It is more like the statement used in a diql query. String incompleteFlattenTableName = "flatten(" + TABLE + ", a[*])"; logger.info("Checking if metadata is available for flattened table."); serverControl.get(0).getSerivceTestUtil().tableMetadataService(tableMetadataService -> { new Waiter().waitUntil("Metadata of flattened table is available", 10, 500, () -> { ROptionalTableMetadata m; try { m = tableMetadataService.getTableMetadata(ticket, incompleteFlattenTableName); } catch (Exception e) { logger.warn("Excpetion when trying to fetch metadata", e); return false; } return m.isSetTableMetadata() && m.getTableMetadata().getFields().size() == 2; }); ROptionalTableMetadata m = tableMetadataService.getTableMetadata(ticket, incompleteFlattenTableName); Assert.assertTrue(m.isSetTableMetadata(), "Expected to receive table metadata"); // the function should NOT return the table name with the flattenId. If the user did not know the flattenId, we do // not want to tell him - because in the end this is some internal information he should not care about! Assert.assertEquals(m.getTableMetadata().getTableName(), incompleteFlattenTableName, "Expected correct table name in returned metadata"); Assert.assertEquals(m.getTableMetadata().getFields().size(), 2, "Expected correct number of fields in metadata"); FieldMetadata fieldMetadata = m.getTableMetadata().getFields().get(0); if (fieldMetadata.getFieldName().equals("a")) { Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.CONTAINER, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } else { Assert.assertEquals(fieldMetadata.getFieldName(), "a.b", "Expected correct field name"); Assert.assertEquals(fieldMetadata.getFieldType(), FieldType.LONG, "Expected correct field type"); Assert.assertEquals(fieldMetadata.isRepeated(), false, "Expected correct repeated state of field"); } }); } @Test @NeedsServer public void flattenByAutoflattenNonRootNoAccess() { serverControl.get(0).deploy(cp(FLATTEN_AUTOFLATTEN_CONTROL_FILE), cp(FLATTEN_JSON_FILE)); logger.info("Creating new user WITHOUT access to test table"); toolControl.im(serverControl.get(0).getAddr(), CreateUserActualIdentityToolFunction.FUNCTION_NAME, // ServerControl.ROOT_USER, ServerControl.ROOT_PASSWORD, // USER, // paramUser PASSWD, // paramPassword EMAIL, // paramEmail null, // paramPermission null // paramPermissionObject ); Ticket ticket = serverControl.get(0).login(USER, PASSWD); Ticket superuserTicket = serverControl.get(0).loginSuperuser(); // this one does not include the flattenId! It is more like the statement used in a diql query. String incompleteFlattenTableName = "flatten(" + TABLE + ", a[*])"; logger.info("Checking if metadata is available for flattened table."); serverControl.get(0).getSerivceTestUtil().tableMetadataService(tableMetadataService -> { new Waiter().waitUntil("Metadata of flattened table is available for superuser", 10, 500, () -> { ROptionalTableMetadata m; try { m = tableMetadataService.getTableMetadata(superuserTicket, incompleteFlattenTableName); } catch (Exception e) { logger.warn("Excpetion when trying to fetch metadata", e); return false; } return m.isSetTableMetadata() && m.getTableMetadata().getFields().size() == 2; }); try { ROptionalTableMetadata res = tableMetadataService.getTableMetadata(ticket, incompleteFlattenTableName); Assert.fail("Did expect that service returns with " + "authorizationException, but did return something instead: " + res); } catch (AuthorizationException e) { // fine, exception is expected } }); } }