Repository: sentry Updated Branches: refs/heads/sentry-ha-redesign 50a127bf3 -> e5bb466ef
http://git-wip-us.apache.org/repos/asf/sentry/blob/e5bb466e/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestNotificationProcessor.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestNotificationProcessor.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestNotificationProcessor.java new file mode 100644 index 0000000..c6c9448 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestNotificationProcessor.java @@ -0,0 +1,465 @@ +/* + * 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.sentry.service.thrift; + +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.ArrayList; +import java.util.List; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hive.metastore.api.Database; +import org.apache.hadoop.hive.metastore.api.FieldSchema; +import org.apache.hadoop.hive.metastore.api.NotificationEvent; +import org.apache.hadoop.hive.metastore.api.StorageDescriptor; +import org.apache.hadoop.hive.metastore.api.Table; +import org.apache.hive.hcatalog.messaging.HCatEventMessage; +import org.apache.sentry.binding.metastore.messaging.json.SentryJSONMessageFactory; +import org.apache.sentry.hdfs.Updateable; +import org.apache.sentry.provider.db.service.persistent.SentryStore; +import org.apache.sentry.provider.db.service.thrift.TSentryAuthorizable; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +// TODO 1. More tests should be added here. +// TODO 2. Tests using actual sentry store where. +@SuppressWarnings("unused") +public class TestNotificationProcessor { + + private static final SentryStore sentryStore = Mockito.mock(SentryStore.class); + private final static String hiveInstance = "server2"; + private final static Configuration conf = new Configuration(); + private final SentryJSONMessageFactory messageFactory = new SentryJSONMessageFactory(); + private NotificationProcessor notificationProcessor; + + @BeforeClass + public static void setup() { + conf.set("sentry.hive.sync.create", "true"); + conf.set("sentry.hive.sync.drop", "true"); + } + + @After + public void resetConf() { + conf.set("sentry.hive.sync.create", "true"); + conf.set("sentry.hive.sync.drop", "true"); + reset(sentryStore); + } + + @Test + /* + Makes sure that appropriate sentry store methods are invoked when create database event is + processed. + + Also, checks the hive sync configuration. + */ + public void testCreateDatabase() throws Exception { + long seqNum = 1; + String dbName = "db1"; + String uriPrefix = "hdfs:///"; + String location = "user/hive/warehouse"; + NotificationEvent notificationEvent; + TSentryAuthorizable authorizable; + notificationProcessor = new NotificationProcessor(sentryStore, + hiveInstance, conf); + + // Create notification event + notificationEvent = new NotificationEvent(seqNum, 0, + HCatEventMessage.EventType.CREATE_DATABASE.toString(), + messageFactory.buildCreateDatabaseMessage(new Database(dbName, + null, uriPrefix + location, null)).toString()); + + notificationProcessor.processNotificationEvent(notificationEvent); + + authorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + authorizable.setDb("db1"); + //noinspection unchecked + verify(sentryStore, times(1)).addAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + + verify(sentryStore, times(1)).dropPrivilege(authorizable, + NotificationProcessor.getPermUpdatableOnDrop(authorizable)); + reset(sentryStore); + + //Change the configuration and make sure that exiting privileges are not dropped + notificationProcessor.setSyncStoreOnCreate(false); + dbName = "db2"; + notificationEvent = new NotificationEvent(1, 0, + HCatEventMessage.EventType.CREATE_DATABASE.toString(), + messageFactory.buildCreateDatabaseMessage(new Database(dbName, + null, "hdfs:///db2", null)).toString()); + + notificationProcessor.processNotificationEvent(notificationEvent); + + authorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + authorizable.setDb(dbName); + + //noinspection unchecked + verify(sentryStore, times(1)).addAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + //making sure that privileges are not dropped + verify(sentryStore, times(0)).dropPrivilege(authorizable, + NotificationProcessor.getPermUpdatableOnDrop(authorizable)); + + } + + @Test + /* + Makes sure that appropriate sentry store methods are invoked when drop database event is + processed. + + Also, checks the hive sync configuration. + */ + public void testDropDatabase() throws Exception { + String dbName = "db1"; + + notificationProcessor = new NotificationProcessor(sentryStore, + hiveInstance, conf); + + // Create notification event + NotificationEvent notificationEvent = new NotificationEvent(1, 0, + HCatEventMessage.EventType.DROP_DATABASE.toString(), + messageFactory.buildDropDatabaseMessage(new Database(dbName, null, + "hdfs:///db1", null)).toString()); + + notificationProcessor.processNotificationEvent(notificationEvent); + + TSentryAuthorizable authorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + authorizable.setDb("db1"); + + //noinspection unchecked + verify(sentryStore, times(1)).deleteAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + verify(sentryStore, times(1)).dropPrivilege(authorizable, + NotificationProcessor.getPermUpdatableOnDrop(authorizable)); + reset(sentryStore); + + // Change the configuration and make sure that exiting privileges are not dropped + notificationProcessor.setSyncStoreOnDrop(false); + dbName = "db2"; + // Create notification event + notificationEvent = new NotificationEvent(1, 0, + HCatEventMessage.EventType.DROP_DATABASE.toString(), + messageFactory.buildDropDatabaseMessage(new Database(dbName, null, + "hdfs:///db2", null)).toString()); + + notificationProcessor.processNotificationEvent(notificationEvent); + + authorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + authorizable.setDb(dbName); + + //noinspection unchecked + verify(sentryStore, times(1)).deleteAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + verify(sentryStore, times(0)).dropPrivilege(authorizable, + NotificationProcessor.getPermUpdatableOnDrop(authorizable)); + } + + @Test + /* + Makes sure that appropriate sentry store methods are invoked when create table event is + processed. + + Also, checks the hive sync configuration. + */ + public void testCreateTable() throws Exception { + String dbName = "db1"; + String tableName = "table1"; + + notificationProcessor = new NotificationProcessor(sentryStore, + hiveInstance, conf); + + // Create notification event + StorageDescriptor sd = new StorageDescriptor(); + sd.setLocation("hdfs:///db1.db/table1"); + NotificationEvent notificationEvent = + new NotificationEvent(1, 0, HCatEventMessage.EventType.CREATE_TABLE.toString(), + messageFactory.buildCreateTableMessage(new Table(tableName, + dbName, null, 0, 0, 0, sd, null, null, null, null, null)).toString()); + + notificationProcessor.processNotificationEvent(notificationEvent); + + TSentryAuthorizable authorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + authorizable.setDb("db1"); + authorizable.setTable(tableName); + + //noinspection unchecked + verify(sentryStore, times(1)).addAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + + verify(sentryStore, times(1)).dropPrivilege(authorizable, + NotificationProcessor.getPermUpdatableOnDrop(authorizable)); + reset(sentryStore); + + // Change the configuration and make sure that existing privileges are not dropped + notificationProcessor.setSyncStoreOnCreate(false); + + // Create notification event + dbName = "db2"; + tableName = "table2"; + sd = new StorageDescriptor(); + sd.setLocation("hdfs:///db1.db/table2"); + notificationEvent = + new NotificationEvent(1, 0, HCatEventMessage.EventType.CREATE_TABLE.toString(), + messageFactory.buildCreateTableMessage(new Table(tableName, + dbName, null, 0, 0, 0, sd, null, null, null, null, null)).toString()); + + notificationProcessor.processNotificationEvent(notificationEvent); + + authorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + authorizable.setDb(dbName); + authorizable.setTable(tableName); + + //noinspection unchecked + verify(sentryStore, times(1)).addAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + // Making sure that privileges are not dropped + verify(sentryStore, times(0)).dropPrivilege(authorizable, + NotificationProcessor.getPermUpdatableOnDrop(authorizable)); + } + + @Test + /* + Makes sure that appropriate sentry store methods are invoked when drop table event is + processed. + + Also, checks the hive sync configuration. + */ + public void testDropTable() throws Exception { + String dbName = "db1"; + String tableName = "table1"; + + Configuration authConf = new Configuration(); + notificationProcessor = new NotificationProcessor(sentryStore, + hiveInstance, authConf); + + // Create notification event + StorageDescriptor sd = new StorageDescriptor(); + sd.setLocation("hdfs:///db1.db/table1"); + NotificationEvent notificationEvent = new NotificationEvent(1, 0, + HCatEventMessage.EventType.DROP_TABLE.toString(), + messageFactory.buildDropTableMessage(new Table(tableName, + dbName, null, 0, 0, 0, sd, null, null, null, null, null)).toString()); + + notificationProcessor.processNotificationEvent(notificationEvent); + + TSentryAuthorizable authorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + authorizable.setDb("db1"); + authorizable.setTable(tableName); + + verify(sentryStore, times(1)).deleteAllAuthzPathsMapping(Mockito.anyString(), + Mockito.any(Updateable.Update.class)); + + verify(sentryStore, times(1)).dropPrivilege(authorizable, + NotificationProcessor.getPermUpdatableOnDrop(authorizable)); + } + + @Test + /* + Makes sure that appropriate sentry store methods are invoked when alter tables event is + processed. + */ + public void testAlterTable() throws Exception { + String dbName = "db1"; + String tableName = "table1"; + + String newDbName = "db1"; + String newTableName = "table2"; + + Configuration authConf = new Configuration(); + notificationProcessor = new NotificationProcessor(sentryStore, + hiveInstance, authConf); + + // Create notification event + StorageDescriptor sd = new StorageDescriptor(); + sd.setLocation("hdfs:///db1.db/table1"); + NotificationEvent notificationEvent = new NotificationEvent(1, 0, + HCatEventMessage.EventType.ALTER_TABLE.toString(), + messageFactory.buildAlterTableMessage( + new Table(tableName, dbName, null, 0, 0, 0, sd, null, null, null, null, null), + new Table(newTableName, newDbName, null, 0, 0, 0, sd, null, null, null, null, null)) + .toString()); + notificationEvent.setDbName(newDbName); + notificationEvent.setTableName(newTableName); + + notificationProcessor.processNotificationEvent(notificationEvent); + + TSentryAuthorizable authorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + authorizable.setDb(dbName); + authorizable.setTable(tableName); + + TSentryAuthorizable newAuthorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + newAuthorizable.setDb(newDbName); + newAuthorizable.setTable(newTableName); + + verify(sentryStore, times(1)).renameAuthzObj(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Updateable.Update.class)); + + verify(sentryStore, times(1)).renamePrivilege(authorizable, newAuthorizable, + NotificationProcessor.getPermUpdatableOnRename(authorizable, newAuthorizable)); + } + + @Test + /* + Makes sure that appropriate sentry store methods are invoked when alter tables event is + processed. + */ + public void testRenameTableWithLocationUpdate() throws Exception { + String dbName = "db1"; + String tableName = "table1"; + + String newDbName = "db1"; + String newTableName = "table2"; + + Configuration authConf = new Configuration(); + notificationProcessor = new NotificationProcessor(sentryStore, + hiveInstance, authConf); + + // Create notification event + StorageDescriptor sd = new StorageDescriptor(); + sd.setLocation("hdfs:///db1.db/table1"); + StorageDescriptor new_sd = new StorageDescriptor(); + new_sd.setLocation("hdfs:///db1.db/table2"); + NotificationEvent notificationEvent = new NotificationEvent(1, 0, + HCatEventMessage.EventType.ALTER_TABLE.toString(), + messageFactory.buildAlterTableMessage( + new Table(tableName, dbName, null, 0, 0, 0, sd, null, null, null, null, null), + new Table(newTableName, newDbName, null, 0, 0, 0, new_sd, null, null, null, null, null)) + .toString()); + notificationEvent.setDbName(newDbName); + notificationEvent.setTableName(newTableName); + + notificationProcessor.processNotificationEvent(notificationEvent); + + TSentryAuthorizable authorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + authorizable.setDb(dbName); + authorizable.setTable(tableName); + + TSentryAuthorizable newAuthorizable = new TSentryAuthorizable(hiveInstance); + authorizable.setServer(hiveInstance); + newAuthorizable.setDb(newDbName); + newAuthorizable.setTable(newTableName); + + verify(sentryStore, times(1)).renameAuthzPathsMapping(Mockito.anyString(), Mockito.anyString(), + Mockito.anyString(), Mockito.anyString(), Mockito.any(Updateable.Update.class)); + + verify(sentryStore, times(1)).renamePrivilege(authorizable, newAuthorizable, + NotificationProcessor.getPermUpdatableOnRename(authorizable, newAuthorizable)); + } + + @Test + /* + Test to made sure that sentry store is not invoked when invalid alter table event is + processed. + */ + public void testAlterTableWithInvalidEvent() throws Exception { + String dbName = "db1"; + String tableName1 = "table1"; + String tableName2 = "table2"; + long inputEventId = 1; + NotificationEvent notificationEvent; + List<FieldSchema> partCols; + StorageDescriptor sd; + Mockito.doNothing().when(sentryStore).persistLastProcessedNotificationID(Mockito.anyLong()); + //noinspection unchecked + Mockito.doNothing().when(sentryStore).addAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + + Configuration authConf = new Configuration(); + notificationProcessor = new NotificationProcessor(sentryStore, + hiveInstance, authConf); + + // Create a table + sd = new StorageDescriptor(); + sd.setLocation("hdfs://db1.db/table1"); + partCols = new ArrayList<>(); + partCols.add(new FieldSchema("ds", "string", "")); + Table table = new Table(tableName1, dbName, null, 0, 0, 0, sd, partCols, + null, null, null, null); + notificationEvent = new NotificationEvent(inputEventId, 0, + HCatEventMessage.EventType.CREATE_TABLE.toString(), + messageFactory.buildCreateTableMessage(table).toString()); + notificationEvent.setDbName(dbName); + notificationEvent.setTableName(tableName1); + inputEventId += 1; + // Process the notification + notificationProcessor.processNotificationEvent(notificationEvent); + // Make sure that addAuthzPathsMapping was invoked once to handle CREATE_TABLE notification + // and persistLastProcessedNotificationID was not invoked. + //noinspection unchecked + verify(sentryStore, times(1)).addAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + reset(sentryStore); + + // Create alter table notification with out actually changing anything. + // This notification should not be processed by sentry server + // Notification should be persisted explicitly + notificationEvent = new NotificationEvent(1, 0, + HCatEventMessage.EventType.ALTER_TABLE.toString(), + messageFactory.buildAlterTableMessage( + new Table(tableName1, dbName, null, 0, 0, 0, sd, null, null, null, null, null), + new Table(tableName1, dbName, null, 0, 0, 0, sd, null, + null, null, null, null)).toString()); + notificationEvent.setDbName(dbName); + notificationEvent.setTableName(tableName1); + inputEventId += 1; + // Process the notification + notificationProcessor.processNotificationEvent(notificationEvent); + // Make sure that renameAuthzObj and deleteAuthzPathsMapping were not invoked + // to handle CREATE_TABLE notification + // and persistLastProcessedNotificationID is explicitly invoked + verify(sentryStore, times(0)).renameAuthzObj(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Updateable.Update.class)); + //noinspection unchecked + verify(sentryStore, times(0)).deleteAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + reset(sentryStore); + + // Create a table + sd = new StorageDescriptor(); + sd.setLocation("hdfs://db1.db/table2"); + partCols = new ArrayList<>(); + partCols.add(new FieldSchema("ds", "string", "")); + Table table1 = new Table(tableName2, dbName, null, 0, 0, 0, sd, + partCols, null, null, null, null); + notificationEvent = new NotificationEvent(inputEventId, 0, + HCatEventMessage.EventType.CREATE_TABLE.toString(), + messageFactory.buildCreateTableMessage(table1).toString()); + notificationEvent.setDbName(dbName); + notificationEvent.setTableName(tableName2); + // Process the notification + notificationProcessor.processNotificationEvent(notificationEvent); + // Make sure that addAuthzPathsMapping was invoked once to handle CREATE_TABLE notification + // and persistLastProcessedNotificationID was not invoked. + //noinspection unchecked + verify(sentryStore, times(1)).addAuthzPathsMapping(Mockito.anyString(), + Mockito.anyCollection(), Mockito.any(Updateable.Update.class)); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e5bb466e/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryHmsClient.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryHmsClient.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryHmsClient.java new file mode 100644 index 0000000..3cc6541 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryHmsClient.java @@ -0,0 +1,470 @@ +/* + * 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.sentry.service.thrift; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hive.conf.HiveConf; +import org.apache.hadoop.hive.metastore.HiveMetaStoreClient; +import org.apache.hadoop.hive.metastore.IMetaStoreClient.NotificationFilter; +import org.apache.hadoop.hive.metastore.api.CurrentNotificationEventId; +import org.apache.hadoop.hive.metastore.api.Database; +import org.apache.hadoop.hive.metastore.api.MetaException; +import org.apache.hadoop.hive.metastore.api.NotificationEvent; +import org.apache.hadoop.hive.metastore.api.NotificationEventResponse; +import org.apache.hadoop.hive.metastore.api.Partition; +import org.apache.hadoop.hive.metastore.api.StorageDescriptor; +import org.apache.hadoop.hive.metastore.api.Table; +import org.apache.hive.hcatalog.messaging.HCatEventMessage; +import org.apache.sentry.binding.metastore.messaging.json.SentryJSONMessageFactory; +import org.apache.sentry.provider.db.service.persistent.PathsImage; +import org.apache.thrift.TException; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import javax.security.auth.login.LoginException; + +/** + * Test mocks HiveMetaStoreClient class and tests SentryHmsClient. + */ +public class TestSentryHmsClient { + + private static final Configuration conf = new Configuration(); + private static final SentryJSONMessageFactory messageFactory = new SentryJSONMessageFactory(); + private static SentryHmsClient client; + private static MockHMSClientFactory hiveConnectionFactory; + + /** + * Create mock database with the given name + * + * @param name Database name + * @return Mock database object + */ + private static Database makeDb(String name) { + Database db = Mockito.mock(Database.class); + Mockito.when(db.getName()).thenReturn(name); + Mockito.when(db.getLocationUri()).thenReturn("hdfs:///" + name); + return db; + } + + /** + * Create mock table + * + * @param dbName db for this table + * @param tableName name of the table + * @return mock table object + */ + private static Table makeTable(String dbName, String tableName) { + Table table = Mockito.mock(Table.class); + Mockito.when(table.getDbName()).thenReturn(dbName); + Mockito.when(table.getTableName()).thenReturn(tableName); + StorageDescriptor sd = Mockito.mock(StorageDescriptor.class); + Mockito.when(sd.getLocation()).thenReturn( + String.format("hdfs:///%s/%s", dbName, tableName)); + Mockito.when(table.getSd()).thenReturn(sd); + return table; + } + + /** + * Create mock partition + * + * @param dbName database for this partition + * @param tableName table for this partition + * @param partName partition name + * @return mock partition object + */ + private static Partition makePartition(String dbName, String tableName, String partName) { + Partition partition = Mockito.mock(Partition.class); + StorageDescriptor sd = Mockito.mock(StorageDescriptor.class); + Mockito.when(sd.getLocation()).thenReturn( + String.format("hdfs:///%s/%s/%s", dbName, tableName, partName)); + Mockito.when(partition.getSd()).thenReturn(sd); + return partition; + } + + /** + * Creates create database notification + * + * @return NotificationEvent + */ + private static NotificationEvent getCreateDatabaseNotification(long id) { + Random rand = new Random(); + int n = rand.nextInt(100) + 1; + String dbName = "db" + n; + return new NotificationEvent(id, 0, HCatEventMessage.EventType.CREATE_DATABASE.toString(), + messageFactory + .buildCreateDatabaseMessage(new Database(dbName, null, "hdfs:///" + dbName, null)) + .toString()); + } + + /** + * Creates drop database notification + * + * @return NotificationEvent + */ + private static NotificationEvent getDropDatabaseNotification(long id) { + Random rand = new Random(); + int n = rand.nextInt(100) + 1; + String dbName = "db" + n; + return new NotificationEvent(id, 0, HCatEventMessage.EventType.DROP_DATABASE.toString(), + messageFactory + .buildDropDatabaseMessage(new Database(dbName, null, "hdfs:///" + dbName, null)) + .toString()); + } + + @BeforeClass + static public void initialize() throws IOException, LoginException { + hiveConnectionFactory = new MockHMSClientFactory(); + client = new SentryHmsClient(conf, (HiveConnectionFactory)hiveConnectionFactory); + } + + /** + * Creating snapshot when SentryHmsClient is not connected to HMS + */ + @Test + public void testSnapshotCreationWithOutClientConnected() throws Exception { + // Make sure that client is not connected + Assert.assertFalse(client.isConnected()); + PathsImage snapshotInfo = client.getFullSnapshot(); + Assert.assertTrue(snapshotInfo.getPathImage().isEmpty()); + } + + /** + * Creating snapshot when HMS doesn't have any data + */ + @Test + public void testSnapshotCreationWithNoHmsData() throws Exception { + MockClient mockClient = new MockClient(new HiveSnapshot(), 1); + client.setClient(mockClient.client); + // Make sure that client is connected + Assert.assertTrue(client.isConnected()); + PathsImage snapshotInfo = client.getFullSnapshot(); + Assert.assertTrue(snapshotInfo.getPathImage().isEmpty()); + } + + /** + * Creating a snapshot when there is data but there are updates to HMS data mean while + */ + @Test + public void testSnapshotCreationWhenDataIsActivelyUpdated() throws Exception { + HiveTable tab21 = new HiveTable("tab21"); + HiveTable tab31 = new HiveTable("tab31").add("part311").add("part312"); + HiveDb db3 = new HiveDb("db3", Lists.newArrayList(tab31)); + HiveDb db2 = new HiveDb("db2", Lists.newArrayList(tab21)); + HiveDb db1 = new HiveDb("db1"); + HiveSnapshot snap = new HiveSnapshot().add(db1).add(db2).add(db3); + final MockClient mockClient = new MockClient(snap, 1); + + client.setClient(mockClient.client); + hiveConnectionFactory.setClient(mockClient); + // Make sure that client is connected + Assert.assertTrue(client.isConnected()); + PathsImage snapshotInfo = client.getFullSnapshot(); + // Make sure that snapshot is not empty + Assert.assertTrue(!snapshotInfo.getPathImage().isEmpty()); + + Mockito.when(mockClient.client.getCurrentNotificationEventId()). + thenAnswer(new Answer<CurrentNotificationEventId>() { + @Override + public CurrentNotificationEventId answer(InvocationOnMock invocation) + throws Throwable { + return new CurrentNotificationEventId(mockClient.incrementNotificationEventId()); + } + + }); + + snapshotInfo = client.getFullSnapshot(); + Assert.assertTrue(snapshotInfo.getPathImage().isEmpty()); + } + + /** + * Creating a snapshot when there is data in HMS. + */ + @Test + public void testSnapshotCreationSuccess() throws Exception { + HiveTable tab21 = new HiveTable("tab21"); + HiveTable tab31 = new HiveTable("tab31"); + HiveDb db3 = new HiveDb("db3", Lists.newArrayList(tab31)); + HiveDb db2 = new HiveDb("db2", Lists.newArrayList(tab21)); + HiveDb db1 = new HiveDb("db1"); + HiveSnapshot snap = new HiveSnapshot().add(db1).add(db2).add(db3); + MockClient mockClient = new MockClient(snap, 1); + Mockito.when(mockClient.client.getCurrentNotificationEventId()). + thenReturn(new CurrentNotificationEventId(mockClient.eventId)); + client.setClient(mockClient.client); + hiveConnectionFactory.setClient(mockClient); + // Make sure that client is connected + Assert.assertTrue(client.isConnected()); + + PathsImage snapshotInfo = client.getFullSnapshot(); + Assert.assertEquals(5, snapshotInfo.getPathImage().size()); + Assert.assertEquals(Sets.newHashSet("db1"), snapshotInfo.getPathImage().get("db1")); + Assert.assertEquals(Sets.newHashSet("db2"), snapshotInfo.getPathImage().get("db2")); + Assert.assertEquals(Sets.newHashSet("db3"), snapshotInfo.getPathImage().get("db3")); + Assert.assertEquals(Sets.newHashSet("db2/tab21"), + snapshotInfo.getPathImage().get("db2.tab21")); + Assert.assertEquals(Sets.newHashSet("db3/tab31"), snapshotInfo.getPathImage().get("db3.tab31")); + Assert.assertEquals(snapshotInfo.getId(), mockClient.eventId); + + } + + /** + * Test scenario when there is no HMS connection + * Getting new notifications + */ + @Test + public void testGetNewNotificationsWithOutClientConnected() throws Exception { + HiveTable tab21 = new HiveTable("tab21"); + HiveTable tab31 = new HiveTable("tab31"); + HiveDb db3 = new HiveDb("db3", Lists.newArrayList(tab31)); + HiveDb db2 = new HiveDb("db2", Lists.newArrayList(tab21)); + HiveDb db1 = new HiveDb("db1"); + client.setClient(null); + HiveSnapshot snap = new HiveSnapshot().add(db1).add(db2).add(db3); + MockClient mockClient = new MockClient(snap, 100); + Mockito.when(mockClient.client.getCurrentNotificationEventId()). + thenReturn(new CurrentNotificationEventId(mockClient.eventId)); + // Make sure that client is not connected + Assert.assertTrue(!client.isConnected()); + Collection<NotificationEvent> events = client.getNotifications(100); + Assert.assertTrue(events.isEmpty()); + + } + + /** + * Test scenario where there are no notifications + * Getting new notifications + */ + @Test + public void testGetNewNotificationsWithNoHmsUpdates() throws Exception { + HiveTable tab21 = new HiveTable("tab21"); + HiveTable tab31 = new HiveTable("tab31"); + HiveDb db3 = new HiveDb("db3", Lists.newArrayList(tab31)); + HiveDb db2 = new HiveDb("db2", Lists.newArrayList(tab21)); + HiveDb db1 = new HiveDb("db1"); + HiveSnapshot snap = new HiveSnapshot().add(db1).add(db2).add(db3); + MockClient mockClient = new MockClient(snap, 100); + Mockito.when(mockClient.client.getCurrentNotificationEventId()). + thenReturn(new CurrentNotificationEventId(mockClient.eventId)); + client.setClient(mockClient.client); + hiveConnectionFactory.setClient(mockClient); + // Make sure that client is connected + Assert.assertTrue(client.isConnected()); + Collection<NotificationEvent> events = client.getNotifications(100); + Assert.assertTrue(events.isEmpty()); + } + + /** + * Test scenario where there are notifications + * Getting new notifications + */ + @Test + public void testGetNewNotificationsSuccess() throws Exception { + final MockClient mockClient = new MockClient(new HiveSnapshot(), 100); + client.setClient(mockClient.client); + hiveConnectionFactory.setClient(mockClient); + // Make sure that client is connected + Assert.assertTrue(client.isConnected()); + + Mockito.when(mockClient.client.getCurrentNotificationEventId()). + thenAnswer(new Answer<CurrentNotificationEventId>() { + @Override + public CurrentNotificationEventId answer(InvocationOnMock invocation) + throws Throwable { + return new CurrentNotificationEventId(mockClient.incrementNotificationEventId()); + } + }); + Mockito.when(mockClient.client.getNextNotification(Mockito.anyLong(), Mockito.anyInt(), + Mockito.any(NotificationFilter.class))). + thenAnswer(new Answer<NotificationEventResponse>() { + @Override + public NotificationEventResponse answer(InvocationOnMock invocation) + throws Throwable { + long id = 1; + List<NotificationEvent> events = new ArrayList<>(); + events.add(getCreateDatabaseNotification(id++)); + events.add(getDropDatabaseNotification(id++)); + return new NotificationEventResponse(events); + } + }); + + Collection<NotificationEvent> events = client.getNotifications(100); + long id = 1; + for (NotificationEvent event : events) { + Assert.assertEquals(event.getEventId(), id++); + } + Assert.assertTrue(events.size() == 2); + } + + /** + * Representation of a Hive table. A table has a name and a list of partitions. + */ + private static class HiveTable { + + private final String name; + private final List<String> partitions; + + HiveTable(String name) { + this.name = name; + this.partitions = new ArrayList<>(); + } + + HiveTable add(String partition) { + partitions.add(partition); + return this; + } + } + + /** + * Representation of a Hive database. A database has a name and a list of tables + */ + private static class HiveDb { + + final String name; + Collection<HiveTable> tables; + + @SuppressWarnings("SameParameterValue") + HiveDb(String name) { + this.name = name; + tables = new ArrayList<>(); + } + + HiveDb(String name, Collection<HiveTable> tables) { + this.name = name; + this.tables = tables; + if (this.tables == null) { + this.tables = new ArrayList<>(); + } + } + + void add(HiveTable table) { + this.tables.add(table); + } + } + + /** + * Representation of a full Hive snapshot. A snapshot is collection of databases + */ + private static class HiveSnapshot { + + final List<HiveDb> databases = new ArrayList<>(); + + HiveSnapshot() { + } + + HiveSnapshot(Collection<HiveDb> dblist) { + if (dblist != null) { + databases.addAll(dblist); + } + } + + HiveSnapshot add(HiveDb db) { + this.databases.add(db); + return this; + } + } + + /** + * Mock for HMSClientFactory + */ + private static class MockHMSClientFactory implements HiveConnectionFactory { + + private HiveMetaStoreClient mClient; + + public MockHMSClientFactory() { + mClient = null; + } + + void setClient(MockClient mockClient) { + this.mClient = mockClient.client; + } + @Override + public HMSClient connect() throws IOException, InterruptedException, MetaException { + return new HMSClient(mClient); + } + + @Override + public void close() throws Exception { + } + } + + /** + * Convert Hive snapshot to mock client that will return proper values + * for the snapshot. + */ + private static class MockClient { + + public HiveMetaStoreClient client; + public long eventId; + + MockClient(HiveSnapshot snapshot, long eventId) throws TException { + this.eventId = eventId; + client = Mockito.mock(HiveMetaStoreClient.class); + List<String> dbNames = new ArrayList<>(snapshot.databases.size()); + // Walk over all databases and mock appropriate objects + for (HiveDb mdb : snapshot.databases) { + String dbName = mdb.name; + dbNames.add(dbName); + Database db = makeDb(dbName); + Mockito.when(client.getDatabase(dbName)).thenReturn(db); + List<String> tableNames = new ArrayList<>(mdb.tables.size()); + // Walk over all tables for the database and mock appropriate objects + for (HiveTable table : mdb.tables) { + String tableName = table.name; + tableNames.add(tableName); + Table mockTable = makeTable(dbName, tableName); + Mockito.when(client.getTableObjectsByName(dbName, + Lists.newArrayList(tableName))) + .thenReturn(Lists.newArrayList(mockTable)); + Mockito.when(client.listPartitionNames(dbName, tableName, (short) -1)) + .thenReturn(table.partitions); + // Walk across all partitions and mock appropriate objects + for (String partName : table.partitions) { + Partition p = makePartition(dbName, tableName, partName); + Mockito.when(client.getPartitionsByNames(dbName, tableName, + Lists.<String>newArrayList(partName))) + .thenReturn(Lists.<Partition>newArrayList(p)); + } + } + Mockito.when(client.getAllTables(dbName)).thenReturn(tableNames); + } + // Return all database names + Mockito.when(client.getAllDatabases()).thenReturn(dbNames); + Mockito.when(client.getCurrentNotificationEventId()). + thenReturn(new CurrentNotificationEventId(eventId)); + + } + + public Long incrementNotificationEventId() { + eventId = eventId + 1; + return eventId; + } + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e5bb466e/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestDbPrivilegeCleanupOnDrop.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestDbPrivilegeCleanupOnDrop.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestDbPrivilegeCleanupOnDrop.java index b9330cc..d619623 100644 --- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestDbPrivilegeCleanupOnDrop.java +++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestDbPrivilegeCleanupOnDrop.java @@ -31,7 +31,7 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.List; -import org.apache.sentry.service.thrift.HMSFollower; +import org.apache.sentry.service.thrift.HmsFollower; import org.apache.sentry.tests.e2e.hive.AbstractTestWithStaticConfiguration; import org.junit.BeforeClass; import org.junit.Before; @@ -78,7 +78,7 @@ public class TestDbPrivilegeCleanupOnDrop extends to.close(); // Check the HMS connection only when notification log is enabled. if (enableNotificationLog) { - while (!HMSFollower.isConnectedToHMS()) { + while (!HmsFollower.isConnectedToHms()) { Thread.sleep(1000); } }