Repository: sentry Updated Branches: refs/heads/master b00285486 -> 94cfb1b42
SENTRY-2115: Incorrect behavior of HMsFollower when HDFSSync feature is disabled.(Kalyan Kumar kalvagadda, reviewed-by Na Li, Sergio Pena and Xinran Tinney) Project: http://git-wip-us.apache.org/repos/asf/sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/sentry/commit/94cfb1b4 Tree: http://git-wip-us.apache.org/repos/asf/sentry/tree/94cfb1b4 Diff: http://git-wip-us.apache.org/repos/asf/sentry/diff/94cfb1b4 Branch: refs/heads/master Commit: 94cfb1b425b64285385fcb716a1c12877c24d6d0 Parents: b002854 Author: Kalyan Kumar Kalvagadda <kkal...@cloudera.com> Authored: Thu Feb 22 07:38:19 2018 -0600 Committer: Kalyan Kumar Kalvagadda <kkal...@cloudera.com> Committed: Thu Feb 22 07:38:19 2018 -0600 ---------------------------------------------------------------------- .../apache/sentry/hdfs/DBUpdateForwarder.java | 1 + .../org/apache/sentry/hdfs/SentryPlugin.java | 4 +- .../db/service/persistent/HMSFollower.java | 67 +++- .../persistent/NotificationProcessor.java | 2 +- .../db/service/persistent/SentryStore.java | 25 +- .../sentry/service/thrift/SentryService.java | 11 + .../db/service/persistent/TestHMSFollower.java | 308 ++++++++++++++++--- .../tests/e2e/hdfs/TestHDFSIntegrationBase.java | 2 +- .../hdfs/TestHDFSIntegrationTogglingConf.java | 211 +++++++++++++ .../tests/e2e/minisentry/InternalSentrySrv.java | 11 + .../sentry/tests/e2e/minisentry/SentrySrv.java | 10 + 11 files changed, 591 insertions(+), 61 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/DBUpdateForwarder.java ---------------------------------------------------------------------- diff --git a/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/DBUpdateForwarder.java b/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/DBUpdateForwarder.java index eae7861..71ef5f9 100644 --- a/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/DBUpdateForwarder.java +++ b/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/DBUpdateForwarder.java @@ -108,6 +108,7 @@ class DBUpdateForwarder<K extends Updateable.Update> { if (seqNum > curSeqNum) { // No new notifications were processed. + LOGGER.debug("{}, No new updates", retrieverType); return Collections.emptyList(); } http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java ---------------------------------------------------------------------- diff --git a/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java b/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java index cf764ed..8485ca3 100644 --- a/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java +++ b/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java @@ -158,7 +158,7 @@ public class SentryPlugin implements SentryPolicyStorePlugin, SigUtils.SigListen public List<PathsUpdate> getAllPathsUpdatesFrom(long pathSeqNum, long pathImgNum) throws Exception { if (!fullUpdateNN.get()) { // Most common case - Sentry is NOT handling a full update. - LOGGER.debug("Sending partial PATH update to NameNode for pathSeqNum {} and pathImgNum {}", pathSeqNum, pathImgNum); + LOGGER.debug("Received request for PATH update from NameNode for pathSeqNum {} and pathImgNum {}", pathSeqNum, pathImgNum); return pathsUpdater.getAllUpdatesFrom(pathSeqNum, pathImgNum); } @@ -199,7 +199,7 @@ public class SentryPlugin implements SentryPolicyStorePlugin, SigUtils.SigListen } public List<PermissionsUpdate> getAllPermsUpdatesFrom(long permSeqNum) throws Exception { - LOGGER.debug("Sending partial PERM update to NameNode for permSeqNum {}", permSeqNum); + LOGGER.debug("Received request for PERM update from NameNode for permSeqNum {}", permSeqNum); return permsUpdater.getAllUpdatesFrom(permSeqNum, UNUSED_PATH_UPDATE_IMG_NUM); } http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HMSFollower.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HMSFollower.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HMSFollower.java index 2f2b984..929e6be 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HMSFollower.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HMSFollower.java @@ -116,7 +116,17 @@ public class HMSFollower implements Runnable, AutoCloseable, PubSub.Subscriber { LOGGER.info(FULL_UPDATE_TRIGGER + "subscribing to topic " + PubSub.Topic.HDFS_SYNC_HMS.getName()); PubSub.getInstance().subscribe(PubSub.Topic.HDFS_SYNC_HMS, this); } - + if(!hdfsSyncEnabled) { + try { + // Clear all the HMS metadata learned so far and learn it fresh when the feature + // is enabled back. + store.clearHmsPathInformation(); + } catch (Exception ex) { + LOGGER.error("Failed to clear HMS path info", ex); + LOGGER.error("Please manually clear data from SENTRY_PATH_CHANGE/AUTHZ_PATH/AUTHZ_PATHS_MAPPING tables." + + "If not, HDFS ACL's will be inconsistent when HDFS sync feature is enabled back."); + } + } } @VisibleForTesting @@ -190,7 +200,7 @@ public class HMSFollower implements Runnable, AutoCloseable, PubSub.Subscriber { * <p>Clients connections waiting for an event notification will be * woken up afterwards. */ - private void syncupWithHms(long notificationId) { + void syncupWithHms(long notificationId) { try { client.connect(); connectedToHms = true; @@ -201,18 +211,27 @@ public class HMSFollower implements Runnable, AutoCloseable, PubSub.Subscriber { } try { - /* Before getting notifications, it checks if a full HMS snapshot is required. */ - if (isFullSnapshotRequired(notificationId)) { - createFullSnapshot(); - return; + if (hdfsSyncEnabled) { + // Before getting notifications, checking if a full HMS snapshot is required. + if (isFullSnapshotRequired(notificationId)) { + createFullSnapshot(); + return; + } + } else if (isSentryOutOfSync(notificationId)) { + // Out-of-sync, fetching all the notifications + // in HMS NOTIFICATION_LOG. + sentryStore.setLastProcessedNotificationID(0L); + notificationId = 0L; } Collection<NotificationEvent> notifications = notificationFetcher.fetchNotifications(notificationId); - // After getting notifications, it checks if the HMS did some clean-up and notifications + // After getting notifications, check if HMS did some clean-up and notifications // are out-of-sync with Sentry. - if (areNotificationsOutOfSync(notifications, notificationId)) { + if (hdfsSyncEnabled && + areNotificationsOutOfSync(notifications, notificationId)) { + // Out-of-sync, taking a HMS full snapshot. createFullSnapshot(); return; } @@ -258,18 +277,15 @@ public class HMSFollower implements Runnable, AutoCloseable, PubSub.Subscriber { return true; } - // Once HDFS sync is enabled, and if MAuthzPathsSnapshotId + // Once HDFS sync is enabled, and if MAuthzPathsMapping // table is still empty, we need to request a full snapshot - if(hdfsSyncEnabled && sentryStore.isAuthzPathsSnapshotEmpty()) { - LOGGER.debug("HDFSSync is enabled and MAuthzPathsSnapshotId table is empty. Need to request a full snapshot"); + if (sentryStore.isAuthzPathsSnapshotEmpty()) { + LOGGER.debug("HDFSSync is enabled and MAuthzPathsMapping table is empty." + + " Need to request a full snapshot"); return true; } - long currentHmsNotificationId = notificationFetcher.getCurrentNotificationId(); - if (currentHmsNotificationId < latestSentryNotificationId) { - LOGGER.info("The current notification ID on HMS = {} is less than the latest processed Sentry " - + "notification ID = {}. Need to request a full HMS snapshot", - currentHmsNotificationId, latestSentryNotificationId); + if(isSentryOutOfSync(latestSentryNotificationId)) { return true; } @@ -284,6 +300,25 @@ public class HMSFollower implements Runnable, AutoCloseable, PubSub.Subscriber { } /** + * Checks the last notification processed by sentry and the current event-id of + * HMS to see if sentry is out of sync. + * + * @param latestSentryNotificationId The notification Id to check against the HMS + * @return True, sentry is out-of-sync, False otherwise + * @throws Exception If an error occurs while fetching the current notification from HMS + */ + private boolean isSentryOutOfSync(long latestSentryNotificationId) throws Exception { + long currentHmsNotificationId = notificationFetcher.getCurrentNotificationId(); + if (currentHmsNotificationId < latestSentryNotificationId) { + LOGGER.info("The current notification ID on HMS = {} is less than the latest processed Sentry " + + "notification ID = {}. Sentry, Out-of-sync", + currentHmsNotificationId, latestSentryNotificationId); + return true; + } + return false; + } + + /** * Checks if the HMS and Sentry processed notifications are out-of-sync. * This could happen because the HMS did some clean-up of old notifications * and Sentry was not requesting notifications during that time. http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/NotificationProcessor.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/NotificationProcessor.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/NotificationProcessor.java index e755837..94a0b0f 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/NotificationProcessor.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/NotificationProcessor.java @@ -99,7 +99,7 @@ final class NotificationProcessor { AUTHZ_SYNC_CREATE_WITH_POLICY_STORE.getDefault())); syncStoreOnDrop = Boolean.parseBoolean(conf.get(AUTHZ_SYNC_DROP_WITH_POLICY_STORE.getVar(), AUTHZ_SYNC_DROP_WITH_POLICY_STORE.getDefault())); - hdfsSyncEnabled = SentryServiceUtil.isHDFSSyncEnabled(conf); + hdfsSyncEnabled = SentryServiceUtil.isHDFSSyncEnabledNoCache(conf); } /** http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java index edea5b6..7a31a01 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java @@ -577,6 +577,25 @@ public class SentryStore { } /** + * Removes all the information related to HMS Objects from sentry store. + */ + @VisibleForTesting + public void clearHmsPathInformation() throws Exception { + tm.executeTransactionWithRetry( + new TransactionBlock<Object>() { + public Object execute(PersistenceManager pm) throws Exception { + // Data in MAuthzPathsSnapshotId.class is not cleared intentionally. + // This data will help sentry retain the history of snapshots taken before + // and help in picking appropriate ID even when hdfs sync is enabled/disabled. + pm.newQuery(MSentryPathChange.class).deletePersistentAll(); + pm.newQuery(MAuthzPathsMapping.class).deletePersistentAll(); + pm.newQuery(MPath.class).deletePersistentAll(); + return null; + } + }); + } + + /** * Purge a given delta change table, with a specified number of changes to be kept. * * @param cls the class of a perm/path delta change {@link MSentryPermChange} or @@ -3101,9 +3120,9 @@ public class SentryStore { } /** - * Tells if there are any records in MAuthzPathsSnapshotId + * Tells if there are any records in MAuthzPathsMapping * - * @return true if there are no entries in <code>MAuthzPathsSnapshotId</code> + * @return true if there are no entries in <code>MAuthzPathsMapping</code> * false if there are entries * @throws Exception */ @@ -3112,7 +3131,7 @@ public class SentryStore { new TransactionBlock<Boolean>() { public Boolean execute(PersistenceManager pm) throws Exception { pm.setDetachAllOnCommit(false); // No need to detach objects - return isTableEmptyCore(pm, MAuthzPathsSnapshotId.class); + return isTableEmptyCore(pm, MAuthzPathsMapping.class); } }); } http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java index 96c6810..bf5d85b 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java @@ -642,4 +642,15 @@ public class SentryService implements Callable, SigUtils.SigListener { // Become follower leaderMonitor.deactivate(); } + + /** + * Restart HMSFollower with new configuration + * @param newConf Configuration + * @throws Exception + */ + @VisibleForTesting + public void restartHMSFollower(Configuration newConf) throws Exception{ + stopHMSFollower(conf); + startHMSFollower(newConf); + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollower.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollower.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollower.java index 7903078..61e3f06 100644 --- a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollower.java +++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollower.java @@ -60,6 +60,7 @@ import org.apache.sentry.provider.db.service.thrift.TSentryAuthorizable; import static org.apache.sentry.hdfs.ServiceConstants.ServerConfig.SENTRY_SERVICE_FULL_UPDATE_PUBSUB; import org.junit.Before; +import org.junit.After; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -87,17 +88,34 @@ public class TestHMSFollower { configuration.set("sentry.hive.sync.create", "true"); configuration.set(SENTRY_SERVICE_FULL_UPDATE_PUBSUB, "true"); - // enable HDFS sync, so perm and path changes will be saved into DB - configuration.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES, "org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory"); - configuration.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, "org.apache.sentry.hdfs.SentryPlugin"); + enableHdfsSyncInSentry(configuration); } @Before public void setupMocks() throws Exception { - reset(hmsConnectionMock, hmsClientMock); + reset(hmsConnectionMock, hmsClientMock, sentryStore); when(hmsConnectionMock.connect()).thenReturn(new HMSClient(hmsClientMock)); } + @After + public void resetConfig() throws Exception { + enableHdfsSyncInSentry(configuration); + } + + private static void enableHdfsSyncInSentry(Configuration conf) { + // enable HDFS sync, so perm and path changes will be saved into DB + conf.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES, + "org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory"); + conf.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, + "org.apache.sentry.hdfs.SentryPlugin"); + } + + private static void disableHdfsSyncInSentry(Configuration conf) { + // enable HDFS sync, so perm and path changes will be saved into DB + conf.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES, ""); + conf.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, ""); + } + @Test public void testPersistAFullSnapshotWhenNoSnapshotAreProcessedYet() throws Exception { /* @@ -252,19 +270,17 @@ public class TestHMSFollower { * * HMS client always returns the paths image with the eventId == 1. * - * On the 1st run: Sentry notification table is empty, so this - * should trigger a new full HMS snapshot request with the eventId = 1 - * but it should not persist it, in stead only set last - * last processed notification Id. This will prevent a - * unless until notifications are out of sync or hdfs sync is enabled + * On the 1st run: Hdfs sync is disabled in sentry server + * Sentry notification table is empty, so this + * should not trigger a new full HMS snapshot request but should + * fetch all the HMS notifications and persist them. * * On the 2nd run: Just enable hdfs sync and a full snapshot should be triggered - * because MAuthzPathsSnapshotId table is empty + * because MAuthzPathsMapping table is empty * */ - configuration.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES, ""); - configuration.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, ""); + disableHdfsSyncInSentry(configuration); final long SENTRY_PROCESSED_EVENT_ID = SentryStore.EMPTY_NOTIFICATION_ID; final long HMS_PROCESSED_EVENT_ID = 1L; @@ -287,19 +303,20 @@ public class TestHMSFollower { when(sentryStore.getLastProcessedNotificationID()).thenReturn(SENTRY_PROCESSED_EVENT_ID); when(sentryStore.isHmsNotificationEmpty()).thenReturn(true); hmsFollower.run(); + // Since HDFS sync is disabled, fullsnapshot should not be fetched from HMS verify(sentryStore, times(0)).persistFullPathsImage( fullSnapshot.getPathImage(), fullSnapshot.getId()); - // Since hdfs sync is disabled we would set last processed notifications - // and since we did trigger createFullSnapshot() method we won't process any notifications - verify(sentryStore, times(1)).setLastProcessedNotificationID(fullSnapshot.getId()); - verify(sentryStore, times(0)).persistLastProcessedNotificationID(fullSnapshot.getId()); + verify(sentryStore, times(1)).getLastProcessedNotificationID(); + // Making sure that HMS client is invoked to get all the notifications + // starting from event-id 0 + verify(hmsClientMock, times(1)).getNextNotification(Mockito.eq(0L), + Mockito.eq(Integer.MAX_VALUE), Mockito.anyObject()); reset(sentryStore); //Re-enable HDFS Sync and simply start the HMS follower thread, full snap shot - // should be triggered because MAuthzPathsSnapshotId table is empty - configuration.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES, "org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory"); - configuration.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, "org.apache.sentry.hdfs.SentryPlugin"); + // should be triggered because MAuthzPathsMapping table is empty + enableHdfsSyncInSentry(configuration); //Create a new hmsFollower instance since configuration is changing hmsFollower = new HMSFollower(configuration, sentryStore, null, @@ -333,7 +350,141 @@ public class TestHMSFollower { verify(sentryStore, times(0)).setLastProcessedNotificationID(fullSnapshot.getId()); verify(sentryStore, times(0)).persistLastProcessedNotificationID(fullSnapshot.getId()); - reset(sentryStore); + } + + @Test + public void testDisablingAndEnablingHDFSSync() throws Exception { + /* + * TEST CASE + * + * Simulates (by using mocks) the following: + * + * Disable HDFSSync before and enable it later. + * + * HMS client always returns the paths image with the eventId == 1. + * + * On the 1st run: Hdfs sync is disabled in sentry server. + * This should not trigger a new full HMS snapshot request but should + * fetch all the HMS notifications and persist them. + * + * On the 2nd run: Hdfs sync is enabled in sentry server + * Full snapshot should be fetched and persisted because MAuthzPathsMapping table is empty + * + * On 3rd run: Hdfs sync is disabled in sentry server. + * Sentry should remove the HMS path information(MAuthzPathsMapping and MSentryPathChange) + * but continue to process the notifications based on the information persisted in MSentryHmsNotification. + * + * on 4th run: Hdfs sync is enabled in sentry server + * Full snapshot should be fetched and persisted because MAuthzPathsMapping table is empty + * + */ + + disableHdfsSyncInSentry(configuration); + + final long SENTRY_PROCESSED_EVENT_ID = SentryStore.EMPTY_NOTIFICATION_ID; + final long HMS_PROCESSED_EVENT_ID = 1L; + + // Mock that returns a full snapshot + Map<String, Collection<String>> snapshotObjects = new HashMap<>(); + snapshotObjects.put("db", Sets.newHashSet("/db")); + snapshotObjects.put("db.table", Sets.newHashSet("/db/table")); + PathsImage fullSnapshot = new PathsImage(snapshotObjects, HMS_PROCESSED_EVENT_ID, 1); + + SentryHMSClient sentryHmsClient = Mockito.mock(SentryHMSClient.class); + when(sentryHmsClient.getFullSnapshot()).thenReturn(fullSnapshot); + + NotificationEventResponse response = new NotificationEventResponse(); + + response.addToEvents(new NotificationEvent(1L, 0, "CREATE_DATABASE", "")); + response.addToEvents(new NotificationEvent(2L, 0, "CREATE_TABLE", "")); + response.addToEvents(new NotificationEvent(3L, 0, "ALTER_TABLE", "")); + + when(hmsClientMock.getNextNotification(Mockito.eq(0L), Mockito.eq(Integer.MAX_VALUE), + Mockito.anyObject())).thenReturn(response); + + HMSFollower hmsFollower = new HMSFollower(configuration, sentryStore, null, + hmsConnectionMock, hiveInstance); + hmsFollower.setSentryHmsClient(sentryHmsClient); + + // 1st run should not fetch full snapshot but should fetch notifications from 0 + // and persists them + when(sentryStore.getLastProcessedNotificationID()).thenReturn(SENTRY_PROCESSED_EVENT_ID); + when(sentryStore.isHmsNotificationEmpty()).thenReturn(true); + hmsFollower.run(); + verify(sentryStore, times(0)).persistFullPathsImage( + fullSnapshot.getPathImage(), fullSnapshot.getId()); + verify(sentryStore, times(1)).clearHmsPathInformation(); + // Making sure that HMS client is invoked to get all the notifications + // starting from event-id 0 + verify(hmsClientMock, times(1)).getNextNotification(Mockito.eq(0L), + Mockito.eq(Integer.MAX_VALUE), Mockito.anyObject()); + verify(sentryStore, times(1)).persistLastProcessedNotificationID(1L); + verify(sentryStore, times(1)).persistLastProcessedNotificationID(2L); + verify(sentryStore, times(1)).persistLastProcessedNotificationID(3L); + + reset(sentryStore, hmsClientMock); + + //Enable HDFS sync to make sure that Full snapshot is fetched from HMS and persisted. + enableHdfsSyncInSentry(configuration); + when(sentryStore.isHmsNotificationEmpty()).thenReturn(false); + when(sentryStore.getLastProcessedNotificationID()).thenReturn(HMS_PROCESSED_EVENT_ID); + when(sentryStore.isAuthzPathsSnapshotEmpty()).thenReturn(true); + // Mock that sets the current HMS notification ID. Set it to match + // last processed notification Id so that doesn't trigger a full snapshot + when(hmsClientMock.getCurrentNotificationEventId()) + .thenReturn(new CurrentNotificationEventId(HMS_PROCESSED_EVENT_ID)); + hmsFollower = new HMSFollower(configuration, sentryStore, null, + hmsConnectionMock, hiveInstance); + hmsFollower.setSentryHmsClient(sentryHmsClient); + + // 2nd run get a full snapshot because there was no snapshot persisted before. + hmsFollower.run(); + verify(sentryStore, times(0)).clearHmsPathInformation(); + verify(sentryStore, times(1)).persistFullPathsImage( + fullSnapshot.getPathImage(), fullSnapshot.getId()); + verify(sentryStore, times(0)).setLastProcessedNotificationID(fullSnapshot.getId()); + + reset(sentryStore, hmsClientMock); + + disableHdfsSyncInSentry(configuration); + when(sentryStore.isHmsNotificationEmpty()).thenReturn(false); + when(sentryStore.getLastProcessedNotificationID()).thenReturn(HMS_PROCESSED_EVENT_ID); + when(sentryStore.isAuthzPathsSnapshotEmpty()).thenReturn(true); + // Mock that sets the current HMS notification ID. Set it to match + // last processed notification Id so that doesn't trigger a full snapshot + when(hmsClientMock.getCurrentNotificationEventId()) + .thenReturn(new CurrentNotificationEventId(HMS_PROCESSED_EVENT_ID)); + + hmsFollower = new HMSFollower(configuration, sentryStore, null, + hmsConnectionMock, hiveInstance); + hmsFollower.setSentryHmsClient(sentryHmsClient); + // 3rd run + hmsFollower.run(); + verify(sentryStore, times(0)).persistFullPathsImage( + fullSnapshot.getPathImage(), fullSnapshot.getId()); + verify(sentryStore, times(1)).clearHmsPathInformation(); + verify(sentryStore, times(0)).setLastProcessedNotificationID(Mockito.anyLong()); + //Make sure that HMSFollower continues to fetch notifications based on persisted notifications. + verify(hmsClientMock, times(1)).getNextNotification(Mockito.eq(fullSnapshot.getId()-1), + Mockito.eq(Integer.MAX_VALUE), Mockito.anyObject()); + + reset(sentryStore, hmsClientMock); + enableHdfsSyncInSentry(configuration); + when(sentryStore.isHmsNotificationEmpty()).thenReturn(false); + when(sentryStore.getLastProcessedNotificationID()).thenReturn(HMS_PROCESSED_EVENT_ID); + when(sentryStore.isAuthzPathsSnapshotEmpty()).thenReturn(true); + + hmsFollower = new HMSFollower(configuration, sentryStore, null, + hmsConnectionMock, hiveInstance); + hmsFollower.setSentryHmsClient(sentryHmsClient); + // 4th run + hmsFollower.run(); + verify(sentryStore, times(0)).clearHmsPathInformation(); + verify(sentryStore, times(1)).persistFullPathsImage( + fullSnapshot.getPathImage(), fullSnapshot.getId()); + verify(sentryStore, times(0)).setLastProcessedNotificationID(fullSnapshot.getId()); + + reset(sentryStore, hmsClientMock); } @Test @@ -843,8 +994,7 @@ public class TestHMSFollower { Configuration configuration = new Configuration(); // enable HDFS sync, so perm and path changes will be saved into DB - configuration.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES, "org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory"); - configuration.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, "org.apache.sentry.hdfs.SentryPlugin"); + enableHdfsSyncInSentry(configuration); HMSFollower hmsFollower = new HMSFollower(configuration, sentryStore, null, hiveConnectionFactory, hiveInstance); @@ -968,6 +1118,7 @@ public class TestHMSFollower { events.add(notificationEvent); Configuration configuration = new Configuration(); + enableHdfsSyncInSentry(configuration); HMSFollower hmsFollower = new HMSFollower(configuration, sentryStore, null, hiveConnectionFactory, hiveInstance); hmsFollower.processNotifications(events); @@ -983,28 +1134,32 @@ public class TestHMSFollower { @Test public void testNoHdfsNoPersistAFullSnapshot() throws Exception { - /* - * TEST CASE - * - * Simulates (by using mocks) that Sentry has not processed any notifications, so this - * should trigger a new full HMS snapshot request with the eventId = 1 - */ + + // TEST CASE + // + // Simulates (by using mocks) that Sentry has not processed any notifications. + // Test makes sure that this does not trigger a full snapshot and also makes sure that + // HMSFollower tries to fetch all notifications from HMS. + final long SENTRY_PROCESSED_EVENT_ID = SentryStore.EMPTY_NOTIFICATION_ID; final long HMS_PROCESSED_EVENT_ID = 1L; - // Mock that returns a full snapshot - Map<String, Collection<String>> snapshotObjects = new HashMap<>(); - snapshotObjects.put("db", Sets.newHashSet("/db")); - snapshotObjects.put("db.table", Sets.newHashSet("/db/table")); - PathsImage fullSnapshot = new PathsImage(snapshotObjects, HMS_PROCESSED_EVENT_ID, 1); + NotificationEventResponse response = new NotificationEventResponse(); + + response.addToEvents(new NotificationEvent(1L, 0, "CREATE_DATABASE", "")); + response.addToEvents(new NotificationEvent(1L, 0, "CREATE_TABLE", "")); + response.addToEvents(new NotificationEvent(2L, 0, "ALTER_TABLE", "")); // Mock that returns the current HMS notification ID when(hmsClientMock.getCurrentNotificationEventId()) - .thenReturn(new CurrentNotificationEventId(fullSnapshot.getId())); + .thenReturn(new CurrentNotificationEventId(HMS_PROCESSED_EVENT_ID)); + + when(hmsClientMock.getNextNotification(Mockito.eq(0L), Mockito.eq(Integer.MAX_VALUE), + Mockito.anyObject())).thenReturn(response); SentryHMSClient sentryHmsClient = Mockito.mock(SentryHMSClient.class); - when(sentryHmsClient.getFullSnapshot()).thenReturn(fullSnapshot); + // when(sentryHmsClient.getFullSnapshot()).thenReturn(fullSnapshot); Configuration configuration = new Configuration(); HMSFollower hmsFollower = new HMSFollower(configuration, sentryStore, null, @@ -1016,10 +1171,87 @@ public class TestHMSFollower { when(sentryStore.isAuthzPathsMappingEmpty()).thenReturn(false); when(sentryStore.isHmsNotificationEmpty()).thenReturn(true); hmsFollower.run(); - verify(sentryStore, times(0)).persistFullPathsImage(fullSnapshot.getPathImage(), fullSnapshot.getId()); - verify(sentryStore, times(1)).setLastProcessedNotificationID(fullSnapshot.getId()); - verify(sentryStore, times(1)).isHmsNotificationEmpty(); + verify(sentryStore, times(0)).persistFullPathsImage(Mockito.anyMap(), Mockito.anyLong()); verify(sentryStore, times(0)).isAuthzPathsMappingEmpty(); + verify(hmsClientMock, times(1)).getNextNotification(Mockito.eq(0L), + Mockito.eq(Integer.MAX_VALUE), Mockito.anyObject()); + verify(sentryStore, times(3)).persistLastProcessedNotificationID(Mockito.anyLong()); + } + + /** + * Tests the out-of-sync scenario when HDFS sync is disabled to make sure that + * HMSFollower starting fetching notifications from beginning after out-of-sync + * is detected. + * @throws Exception + */ + @Test + public void testNoHdfsOutofSync() throws Exception { + + // TEST CASE + // + // Simulates (by using mocks) that Sentry is out-of-sync with sentry + + final long SENTRY_PROCESSED_EVENT_ID = SentryStore.EMPTY_NOTIFICATION_ID; + + NotificationEventResponse response = new NotificationEventResponse(); + + response.addToEvents(new NotificationEvent(1L, 0, "CREATE_DATABASE", "")); + response.addToEvents(new NotificationEvent(2L, 0, "CREATE_TABLE", "")); + response.addToEvents(new NotificationEvent(3L, 0, "ALTER_TABLE", "")); + + // Mock that returns the current HMS notification ID + when(hmsClientMock.getCurrentNotificationEventId()) + .thenReturn(new CurrentNotificationEventId(3L)); + + when(hmsClientMock.getNextNotification(Mockito.eq(0L), Mockito.eq(Integer.MAX_VALUE), + Mockito.anyObject())).thenReturn(response); + + SentryHMSClient sentryHmsClient = Mockito.mock(SentryHMSClient.class); + + Configuration configuration = new Configuration(); + HMSFollower hmsFollower = new HMSFollower(configuration, sentryStore, null, + hmsConnectionMock, hiveInstance); + hmsFollower.setSentryHmsClient(sentryHmsClient); + + // 1st run should not fetch t he full snapshot but should fetch all the notifications + // from HMS. + when(sentryStore.getLastProcessedNotificationID()).thenReturn(SENTRY_PROCESSED_EVENT_ID); + when(sentryStore.isAuthzPathsMappingEmpty()).thenReturn(false); + when(sentryStore.isHmsNotificationEmpty()).thenReturn(true); + hmsFollower.run(); + verify(sentryStore, times(0)).persistFullPathsImage(Mockito.anyMap(), Mockito.anyLong()); + verify(sentryStore, times(0)).isAuthzPathsMappingEmpty(); + verify(hmsClientMock, times(1)).getNextNotification(Mockito.eq(0L), + Mockito.eq(Integer.MAX_VALUE), Mockito.anyObject()); + verify(sentryStore, times(3)).persistLastProcessedNotificationID(Mockito.anyLong()); + reset(sentryStore, hmsClientMock); + + //Update the mock so that it returns the max(event-id) that was fetched in previous run + when(sentryStore.getLastProcessedNotificationID()).thenReturn(3L); + // Mock HMSClient so that it returns appropriate event-id + when(hmsClientMock.getCurrentNotificationEventId()) + .thenReturn(new CurrentNotificationEventId(3L)); + + //2nd run + hmsFollower.run(); + // Verify that HMSFollower starting fetching the notifications beyond what it already processed. + verify(hmsClientMock, times(1)).getNextNotification(Mockito.eq(3L-1), + Mockito.eq(Integer.MAX_VALUE), Mockito.anyObject()); + + + // Mock that returns the current HMS notification ID which is less than what + // sentry already processed. + when(hmsClientMock.getCurrentNotificationEventId()) + .thenReturn(new CurrentNotificationEventId(1L)); + //Update the mock so that it returns the max(event-id) that was fetched in previous run + when(sentryStore.getLastProcessedNotificationID()).thenReturn(0L); + + // 3rd run + hmsFollower.syncupWithHms(3L); + verify(hmsClientMock, times(1)).getNextNotification(Mockito.eq(0L), + Mockito.eq(Integer.MAX_VALUE), Mockito.anyObject()); + + } @Test http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hdfs/TestHDFSIntegrationBase.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hdfs/TestHDFSIntegrationBase.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hdfs/TestHDFSIntegrationBase.java index 4cd00e6..9b0aeb2 100644 --- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hdfs/TestHDFSIntegrationBase.java +++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hdfs/TestHDFSIntegrationBase.java @@ -204,6 +204,7 @@ public abstract class TestHDFSIntegrationBase { protected static Boolean hiveSyncOnDrop = true; protected static Configuration hadoopConf; protected static final Map<String, String> sentryProperties = Maps.newHashMap(); + protected static Configuration sentryConf = new Configuration(false); protected static File assertCreateDir(File dir) { if(!dir.isDirectory()) { @@ -831,7 +832,6 @@ public abstract class TestHDFSIntegrationBase { hiveUgi.doAs(new PrivilegedExceptionAction<Void>() { @Override public Void run() throws Exception { - Configuration sentryConf = new Configuration(false); sentryConf.set(SENTRY_HDFS_INTEGRATION_PATH_PREFIXES, MANAGED_PREFIXES); sentryProperties.put(HiveServerFactory.AUTHZ_PROVIDER_BACKEND, SimpleDBProviderBackend.class.getName()); http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hdfs/TestHDFSIntegrationTogglingConf.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hdfs/TestHDFSIntegrationTogglingConf.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hdfs/TestHDFSIntegrationTogglingConf.java new file mode 100644 index 0000000..e54ffce --- /dev/null +++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hdfs/TestHDFSIntegrationTogglingConf.java @@ -0,0 +1,211 @@ +/* + * 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.tests.e2e.hdfs; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.permission.FsAction; +import org.apache.sentry.hdfs.SentryAuthorizationConstants; +import org.apache.sentry.service.thrift.ServiceConstants; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.sql.Connection; +import java.sql.Statement; + +/** + * this test class includes tests to verify the behaviour of sentry server + * when the HDFS sync feature is toggled on/off + */ +public class TestHDFSIntegrationTogglingConf extends TestHDFSIntegrationBase { + + private static long getSleepTimeAfterFollowerRestart(Configuration conf) { + long followerInitDelay = conf.getLong(ServiceConstants.ServerConfig.SENTRY_HMSFOLLOWER_INIT_DELAY_MILLS, + ServiceConstants.ServerConfig.SENTRY_HMSFOLLOWER_INIT_DELAY_MILLS_DEFAULT); + long followerInterval = conf.getLong(ServiceConstants.ServerConfig.SENTRY_HMSFOLLOWER_INTERVAL_MILLS, + ServiceConstants.ServerConfig.SENTRY_HMSFOLLOWER_INTERVAL_MILLS_DEFAULT); + long refreshIntervalMillisec = conf.getInt( + SentryAuthorizationConstants.CACHE_REFRESH_INTERVAL_KEY, + SentryAuthorizationConstants.CACHE_REFRESH_INTERVAL_DEFAULT); + + return (followerInitDelay + followerInterval + refreshIntervalMillisec) * 2; + } + + private static void enableHdfsSync(int serverIndex) throws Exception { + Configuration newConfig = new Configuration(sentryConf); + newConfig.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES, + "org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory"); + newConfig.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, + "org.apache.sentry.hdfs.SentryPlugin"); + newConfig.set(ServiceConstants.ServerConfig.SENTRY_HMSFOLLOWER_INIT_DELAY_MILLS, + "1000"); + sentryServer.restartHMSFollower(newConfig, serverIndex, + getSleepTimeAfterFollowerRestart(newConfig)); + } + + private static void disableHdfsSync(int serverIndex) throws Exception { + Configuration newConfig = new Configuration(sentryConf); + newConfig.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES, ""); + newConfig.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, ""); + newConfig.set(ServiceConstants.ServerConfig.SENTRY_HMSFOLLOWER_INIT_DELAY_MILLS, + "1000"); + + sentryServer.restartHMSFollower(newConfig, serverIndex, + getSleepTimeAfterFollowerRestart(newConfig)); + } + + @BeforeClass + public static void setup() throws Exception { + hdfsSyncEnabled = true; + TestHDFSIntegrationBase.setup(); + } + + /** + * Test makes sure that the namenode is not synced with the new change to HMS when + * processor and sentry_plugin for HDFS sync are not configured. + * + * @throws Throwable + */ + @Test + public void testDisablingHDFSSync() throws Throwable { + disableHdfsSync(0); + dbNames = new String[]{"db1"}; + roles = new String[]{"admin_role", "tab_role"}; + admin = "hive"; + + Connection conn; + Statement stmt; + conn = hiveServer2.createConnection("hive", "hive"); + stmt = conn.createStatement(); + stmt.execute("create role admin_role"); + stmt.execute("grant role admin_role to group hive"); + stmt.execute("grant all on server server1 to role admin_role"); + + // db privileges + stmt.execute("create database db1"); + stmt.execute("create role tab_role"); + stmt.execute("grant role tab_role to group flume"); + stmt.execute("create table db1.p2(id int)"); + + stmt.execute("use db1"); + stmt.execute("grant all on table p2 to role tab_role"); + stmt.execute("use default"); + verifyOnAllSubDirs("/user/hive/warehouse/db1.db", FsAction.ALL, "hbase", false); + verifyOnAllSubDirs("/user/hive/warehouse/db1.db/p2", FsAction.ALL, "flume", false); + verifyOnPath("/user/hive/warehouse/db1.db", FsAction.ALL, "flume", false); + + //Enabling HDFS sync back in sentry server + enableHdfsSync(0); + } + + /** + * Test makes sure that HDFS sync configurations in sentryserver are toggled multiple times. + * <ul> + * <li>When processor and sentry_plugin for HDFS sync are configured, + * Namenode should have all the HMS path and permission updates.</li> + * <li>When processor and sentry_plugin for HDFS sync are configured, + * Namenode should not have the HMS path updates.</li> + * <li>When processor and sentry_plugin for HDFS sync are configured again, + * Namenode should not have the HMS path updates by getting HMS full snapshot + * from sentry server.</li> + * </ul> + * + * @throws Throwable + */ + @Test + public void testEnablingDisablingHDFSSync() throws Throwable { + dbNames = new String[]{"db1", "db6"}; + roles = new String[]{"admin_role", "db_role", "tab_role", "p1_admin"}; + admin = "hive"; + + Connection conn; + Statement stmt; + conn = hiveServer2.createConnection("hive", "hive"); + stmt = conn.createStatement(); + stmt.execute("create role admin_role"); + stmt.execute("grant role admin_role to group hive"); + stmt.execute("grant all on server server1 to role admin_role"); + stmt.execute("create table p1 (s string) partitioned by (month int, day " + + "int)"); + stmt.execute("alter table p1 add partition (month=1, day=1)"); + + // db privileges + stmt.execute("create database db1"); + stmt.execute("create role db_role"); + stmt.execute("create role tab_role"); + stmt.execute("grant role db_role to group hbase"); + stmt.execute("grant role tab_role to group flume"); + stmt.execute("create table db1.p2(id int)"); + + stmt.execute("create role p1_admin"); + stmt.execute("grant role p1_admin to group hbase"); + + // Verify default db is inaccessible initially + verifyOnAllSubDirs("/user/hive/warehouse", null, "hbase", false); + + verifyOnAllSubDirs("/user/hive/warehouse/p1", null, "hbase", false); + + stmt.execute("grant all on database db1 to role db_role"); + stmt.execute("use db1"); + stmt.execute("grant all on table p2 to role tab_role"); + stmt.execute("use default"); + verifyOnAllSubDirs("/user/hive/warehouse/db1.db", FsAction.ALL, "hbase", true); + verifyOnAllSubDirs("/user/hive/warehouse/db1.db/p2", FsAction.ALL, "hbase", true); + verifyOnAllSubDirs("/user/hive/warehouse/db1.db/p2", FsAction.ALL, "flume", true); + verifyOnPath("/user/hive/warehouse/db1.db", FsAction.ALL, "flume", false); + + loadData(stmt); + + verifyHDFSandMR(stmt); + + //Disabling HDFS sync in sentry server + disableHdfsSync(0); + + stmt.execute("revoke all on database db1 from role db_role"); + verifyOnAllSubDirs("/user/hive/warehouse/db1.db", FsAction.ALL, "hbase", false); + + // create a table and grant all to db_role + stmt.execute("create database db6"); + stmt.execute("grant all on database db6 to role db_role"); + + // verify that db_role does not have required ACL's as HDFS sync is disabled in sentry server. + verifyOnAllSubDirs("/user/hive/warehouse/db6.db", FsAction.ALL, "hbase", false); + + //Create table in db6 and grant all privileges to tab role + stmt.execute("use db6"); + stmt.execute("create table db6.p1(id int)"); + stmt.execute("grant all on table db6.p1 to role tab_role"); + + // verify that tab_role does not have required permissions + verifyOnAllSubDirs("/user/hive/warehouse/db6.db/p1", FsAction.ALL, "flume", false); + + //Enabling HDFS sync in sentry server + enableHdfsSync(0); + + // As HDFS sync is re-enabled, sentry should take full snapshot and send it NN. + // db_role and tab_role should have required privileges. + // Checks below will make sure that sentry/NN have the updates that happened + // to HMS objects when HDFS was disabled. + verifyOnAllSubDirs("/user/hive/warehouse/db6.db", FsAction.ALL, "hbase", true); + verifyOnAllSubDirs("/user/hive/warehouse/db6.db/p1", FsAction.ALL, "flume", true); + + stmt.close(); + conn.close(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/InternalSentrySrv.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/InternalSentrySrv.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/InternalSentrySrv.java index e64f5cd..6ebb273 100644 --- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/InternalSentrySrv.java +++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/InternalSentrySrv.java @@ -151,6 +151,17 @@ public class InternalSentrySrv implements SentrySrv { } @Override + public void restartHMSFollower(Configuration newConf, int serverNum, + long sleepTime) throws Exception { + if (!isActive) { + throw new IllegalStateException("SentrySrv is no longer active"); + } + SentryService sentryServer = sentryServers.get(serverNum); + sentryServer.restartHMSFollower(newConf); + Thread.sleep(sleepTime); + } + + @Override public void stopAll() throws Exception { boolean cleanStop = true; if (!isActive) { http://git-wip-us.apache.org/repos/asf/sentry/blob/94cfb1b4/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/SentrySrv.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/SentrySrv.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/SentrySrv.java index 9139706..0e82b86 100644 --- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/SentrySrv.java +++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/minisentry/SentrySrv.java @@ -17,6 +17,7 @@ */ package org.apache.sentry.tests.e2e.minisentry; +import org.apache.hadoop.conf.Configuration; import org.apache.sentry.service.thrift.SentryService; public interface SentrySrv { @@ -36,6 +37,15 @@ public interface SentrySrv { void start(int serverNum) throws Exception ; /** + * retart HMSFollower with new configuration + * @param newConf new configuration + * @param serverNum Server number + * @throws Exception + */ + void restartHMSFollower(Configuration newConf, int serverNum, + long sleepTime) throws Exception ; + + /** * Stop all the Sentry servers * @throws Exception */