sentry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ha...@apache.org
Subject [1/2] sentry git commit: SENTRY-1316: Implement Sentry leadership election (Colin Patrick McCabe, Reviewed by Sravya Tirukkovalur, Hao Hao)
Date Thu, 23 Jun 2016 19:23:24 GMT
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<LeaderStatusContext> 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<LeaderStatusContext> iter = contexts.iterator();
+         iter.hasNext(); ) {
+      LeaderStatusContext context = iter.next();
+      if (context.getIncarnationId().equals(curIncarnation)) {
+        CloseableUtils.closeQuietly(context);
+        iter.remove();
+      }
+    }
+    active.busyWaitForNextActive(curIncarnation);
+    for (Iterator<LeaderStatusContext> 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());
     }


Mime
View raw message