Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 5F0CC200B32 for ; Thu, 23 Jun 2016 21:23:26 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 5DC40160A68; Thu, 23 Jun 2016 19:23:26 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 55AFD160A35 for ; Thu, 23 Jun 2016 21:23:25 +0200 (CEST) Received: (qmail 38471 invoked by uid 500); 23 Jun 2016 19:23:24 -0000 Mailing-List: contact commits-help@sentry.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@sentry.apache.org Delivered-To: mailing list commits@sentry.apache.org Received: (qmail 38462 invoked by uid 99); 23 Jun 2016 19:23:24 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 23 Jun 2016 19:23:24 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 67B0CE38B1; Thu, 23 Jun 2016 19:23:24 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: hahao@apache.org To: commits@sentry.apache.org Date: Thu, 23 Jun 2016 19:23:24 -0000 Message-Id: <7e8b9bf8364f4719a32c01d66bfcd51d@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] sentry git commit: SENTRY-1316: Implement Sentry leadership election (Colin Patrick McCabe, Reviewed by Sravya Tirukkovalur, Hao Hao) archived-at: Thu, 23 Jun 2016 19:23:26 -0000 Repository: sentry Updated Branches: refs/heads/sentry-ha-redesign 24b686ede -> 5630fc5ce http://git-wip-us.apache.org/repos/asf/sentry/blob/5630fc5c/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestLeaderStatus.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestLeaderStatus.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestLeaderStatus.java new file mode 100644 index 0000000..434ac41 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestLeaderStatus.java @@ -0,0 +1,219 @@ +/* + * 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 org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.curator.test.TestingServer; +import org.apache.curator.utils.CloseableUtils; +import org.apache.hadoop.conf.Configuration; +import org.junit.Assert; +import org.junit.Test; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Semaphore; + +import static org.apache.sentry.service.thrift.ServiceConstants.ClientConfig.SENTRY_HA_ENABLED; +import static org.apache.sentry.service.thrift.ServiceConstants.ClientConfig.SENTRY_HA_ZOOKEEPER_QUORUM; + +final public class TestLeaderStatus { + private static final Log LOG = + LogFactory.getLog(TestLeaderStatus.class); + + /** + * Test that when the configuration is non-HA, we always become active. + */ + @Test(timeout = 60000) + public void testNonHaLeaderStatus() throws Exception { + Configuration conf = new Configuration(); + conf.set(SENTRY_HA_ZOOKEEPER_QUORUM, ""); + final Semaphore activeSem = new Semaphore(0); + final Semaphore standbySem = new Semaphore(0); + LeaderStatus status = new LeaderStatus(new LeaderStatus.Listener() { + @Override + public void becomeActive() throws Exception { + LOG.info("testNonHaLeaderStatus: becoming active"); + activeSem.release(2); + } + + @Override + public void becomeStandby() { + activeSem.acquireUninterruptibly(); + LOG.info("testNonHaLeaderStatus: becoming standby"); + standbySem.release(); + } + }, conf); + status.start(); + activeSem.acquire(); + status.close(); + standbySem.acquire(); + } + + private static class CurrentTestActive { + private String incarnationId; + private String error = null; + + CurrentTestActive() { + this.incarnationId = null; + this.error = null; + } + + synchronized void set(String incarnationId) { + if (this.incarnationId != null) { + error("set: there is already an " + + "active incarnation " + this.incarnationId); + return; + } + this.incarnationId = incarnationId; + } + + synchronized void unset(String incarnationId) { + if (this.incarnationId == null) { + error("unset: there is no active incarnation."); + return; + } + if (!this.incarnationId.equals(incarnationId)) { + error("unset: can't deactivate " + + incarnationId + " because " + this.incarnationId + + " is the current active incarnation."); + return; + } + this.incarnationId = null; + } + + synchronized String get() { + return this.incarnationId; + } + + synchronized String getError() { + return error; + } + + synchronized void error(String error) { + if (this.error == null) { + this.error = error; + } + LOG.error(error); + } + + String busyWaitForActive() throws InterruptedException { + for (; ; ) { + String cur = get(); + if (cur != null) { + return cur; + } + Thread.sleep(2); + } + } + + String busyWaitForNextActive(String prevIncarnation) + throws InterruptedException { + for (; ; ) { + String cur = get(); + if ((cur != null) && (!cur.equals(prevIncarnation))) { + return cur; + } + Thread.sleep(2); + } + } + } + + static class LeaderStatusContext implements Closeable { + final LeaderStatus status; + + LeaderStatusContext(final CurrentTestActive active, + Configuration conf) throws Exception { + this.status = new LeaderStatus(new LeaderStatus.Listener() { + @Override + public void becomeActive() throws Exception { + LOG.info("LeaderStatusContext " + status.getIncarnationId() + + " becoming active"); + active.set(status.getIncarnationId()); + } + + @Override + public void becomeStandby() { + LOG.info("LeaderStatusContext " + status.getIncarnationId() + + " becoming standby"); + active.unset(status.getIncarnationId()); + } + }, conf); + this.status.start(); + } + + @Override + public void close() throws IOException { + this.status.close(); + } + + @Override + public String toString() { + return "LeaderStatusContext(" + status.getIncarnationId() + ")"; + } + + String getIncarnationId() { + return status.getIncarnationId(); + } + } + + @Test(timeout = 60000) + public void testRacingClients() throws Exception { + final int NUM_CLIENTS = 3; + final Configuration conf = new Configuration(); + TestingServer server = new TestingServer(); + server.start(); + conf.setBoolean(SENTRY_HA_ENABLED, true); + conf.set(SENTRY_HA_ZOOKEEPER_QUORUM, server.getConnectString()); + final CurrentTestActive active = new CurrentTestActive(); + List contexts = new LinkedList<>(); + for (int i = 0; i < NUM_CLIENTS; i++) { + try { + contexts.add(new LeaderStatusContext(active, conf)); + } catch (Throwable t) { + LOG.error("WATERMELON", t); + throw new RuntimeException(t); + } + } + LOG.info("Created " + NUM_CLIENTS + " SentryLeaderSelectorClient " + + "objects."); + String curIncarnation = active.busyWaitForActive(); + LOG.info("Closing LeaderStatus(" + curIncarnation + ")."); + for (Iterator iter = contexts.iterator(); + iter.hasNext(); ) { + LeaderStatusContext context = iter.next(); + if (context.getIncarnationId().equals(curIncarnation)) { + CloseableUtils.closeQuietly(context); + iter.remove(); + } + } + active.busyWaitForNextActive(curIncarnation); + for (Iterator iter = contexts.iterator(); + iter.hasNext(); ) { + LeaderStatusContext context = iter.next(); + CloseableUtils.closeQuietly(context); + iter.remove(); + } + LOG.info("Closed all " + NUM_CLIENTS + " SentryLeaderSelectorClient " + + "objects."); + Assert.assertTrue(null == active.getError()); + server.close(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/5630fc5c/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/ha/TestHaEnd2End.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/ha/TestHaEnd2End.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/ha/TestHaEnd2End.java deleted file mode 100644 index 07d74b5..0000000 --- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/ha/TestHaEnd2End.java +++ /dev/null @@ -1,171 +0,0 @@ -/** - * 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.ha; - -import java.io.File; -import java.io.FileOutputStream; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.Statement; - -import org.apache.sentry.core.common.exception.SentryAccessDeniedException; -import org.apache.sentry.provider.file.PolicyFile; -import org.apache.sentry.service.thrift.HAClientInvocationHandler; -import org.apache.sentry.tests.e2e.hive.AbstractTestWithStaticConfiguration; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.google.common.io.Resources; - -/** - * End2End tests with Sentry service HA enabled. - */ -public class TestHaEnd2End extends AbstractTestWithStaticConfiguration { - - private final String SINGLE_TYPE_DATA_FILE_NAME = "kv1.dat"; - private File dataFile; - - @BeforeClass - public static void setupTestStaticConfiguration() throws Exception { - useSentryService = true; - enableSentryHA = true; - AbstractTestWithStaticConfiguration.setupTestStaticConfiguration(); - } - - @Override - @Before - public void setup() throws Exception { - super.setupAdmin(); - super.setup(); - dataFile = new File(dataDir, SINGLE_TYPE_DATA_FILE_NAME); - FileOutputStream to = new FileOutputStream(dataFile); - Resources.copy(Resources.getResource(SINGLE_TYPE_DATA_FILE_NAME), to); - to.close(); - PolicyFile.setAdminOnServer1(ADMINGROUP); - } - - /** - * Basic test with two Sentry service running. - * @throws Exception - */ - @Test - public void testBasic() throws Exception { - Connection connection = context.createConnection(ADMIN1); - Statement statement = context.createStatement(connection); - statement.execute("CREATE TABLE t1 (c1 string)"); - statement.execute("CREATE ROLE user_role"); - statement.execute("GRANT SELECT ON TABLE t1 TO ROLE user_role"); - statement.execute("GRANT ROLE user_role TO GROUP " + USERGROUP1); - statement.close(); - connection.close(); - connection = context.createConnection(USER1_1); - statement = context.createStatement(connection); - context.assertSentryException(statement, "CREATE ROLE r2", - SentryAccessDeniedException.class.getSimpleName()); - // test default of ALL - statement.execute("SELECT * FROM t1"); - // test a specific role - statement.execute("SET ROLE user_role"); - statement.execute("SELECT * FROM t1"); - - // test ALL - statement.execute("SET ROLE ALL"); - statement.execute("SELECT * FROM t1"); - statement.close(); - connection.close(); - - // cleanup - connection = context.createConnection(ADMIN1); - statement = context.createStatement(connection); - statement.execute("DROP ROLE user_role"); - statement.close(); - connection.close(); - } - - /** - * Test service failover. Run Sentry operations with shutting down one or more - * of the services. - * @throws Exception - */ - @Test - public void testFailover() throws Exception { - String roleName1 = "test_role_1"; - String roleName2 = "test_role_2"; - String roleName3 = "test_role_3"; - - dropDb(ADMIN1, DB1); - createDb(ADMIN1, DB1); - createTable(ADMIN1, DB1, dataFile, TBL1); - - Connection adminCon = context.createConnection(ADMIN1); - Statement adminStmt = context.createStatement(adminCon); - // access the new databases - adminStmt.execute("use " + DB1); - - // stop server1 and verify sentry continues to work - getSentrySrv().stop(0); - adminStmt.execute("CREATE ROLE " + roleName1); - verifyRoleExists(adminStmt, roleName1); - - // restart server1 and stop server2 - getSentrySrv().start(0); - getSentrySrv().stop(1); - adminStmt.execute("CREATE ROLE " + roleName2); - verifyRoleExists(adminStmt, roleName2); - - // stop both servers and verify it fails - getSentrySrv().stop(0); - getSentrySrv().stop(1); - context.assertAuthzExecHookException(adminStmt, "CREATE ROLE " + roleName3, - HAClientInvocationHandler.SENTRY_HA_ERROR_MESSAGE); - - getSentrySrv().start(0); - getSentrySrv().start(1); - adminStmt.execute("CREATE ROLE " + roleName3); - verifyRoleExists(adminStmt, roleName3); - - // cleanup - - dropDb(ADMIN1, DB1); - adminStmt.execute("DROP ROLE " + roleName1); - adminStmt.execute("DROP ROLE " + roleName2); - adminStmt.execute("DROP ROLE " + roleName3); - adminStmt.close(); - adminCon.close(); - - } - - private void verifyRoleExists(Statement statement, String roleName) - throws Exception { - ResultSet resultSet = null; - try { - resultSet = statement.executeQuery("SHOW ROLES "); - while (resultSet.next()) { - if (roleName.equalsIgnoreCase(resultSet.getString(1))) { - return; - } - } - throw new Exception("Role " + roleName + " does not exist"); - } finally { - if (resultSet != null) { - resultSet.close(); - } - } - } -} http://git-wip-us.apache.org/repos/asf/sentry/blob/5630fc5c/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/AbstractTestWithStaticConfiguration.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/AbstractTestWithStaticConfiguration.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/AbstractTestWithStaticConfiguration.java index ced9d1c..2add2d0 100644 --- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/AbstractTestWithStaticConfiguration.java +++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/AbstractTestWithStaticConfiguration.java @@ -500,7 +500,7 @@ public abstract class AbstractTestWithStaticConfiguration { sentryConf.set(ClientConfig.SERVER_RPC_PORT, String.valueOf(sentryServer.get(0).getAddress().getPort())); if (enableSentryHA) { - properties.put(ClientConfig.SERVER_HA_ENABLED, "true"); + properties.put(ClientConfig.SENTRY_HA_ENABLED, "true"); properties.put(ClientConfig.SENTRY_HA_ZOOKEEPER_QUORUM, sentryServer.getZKQuorum()); }