org.apache.sentry.tests.e2e.hdfs.TestHDFSIntegrationBase.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.sentry.tests.e2e.hdfs.TestHDFSIntegrationBase.java

Source

/*
 * 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 java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.URL;
import java.security.PrivilegedExceptionAction;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import com.google.common.base.Preconditions;

import com.google.common.collect.Lists;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.AclEntry;
import org.apache.hadoop.fs.permission.AclEntryType;
import org.apache.hadoop.fs.permission.AclStatus;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.*;
import org.apache.hadoop.hdfs.server.namenode.EditLogFileOutputStream;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.parquet.Strings;
import org.apache.sentry.binding.hive.SentryHiveAuthorizationTaskFactoryImpl;
import org.apache.sentry.binding.hive.authz.SentryHiveAuthorizerFactory;
import org.apache.sentry.binding.hive.conf.HiveAuthzConf;
import org.apache.sentry.hdfs.SentryHDFSServiceClientFactory;
import org.apache.sentry.hdfs.SentryINodeAttributesProvider;
import org.apache.sentry.core.common.exception.SentryAlreadyExistsException;
import org.apache.sentry.provider.file.LocalGroupResourceAuthorizationProvider;
import org.apache.sentry.provider.file.PolicyFile;
import org.apache.sentry.service.common.SentryOwnerPrivilegeType;
import org.apache.sentry.service.thrift.SentryServiceClientFactory;
import org.apache.sentry.tests.e2e.hive.StaticUserGroup;
import org.apache.sentry.tests.e2e.hive.fs.MiniDFS;
import org.apache.sentry.tests.e2e.hive.hiveserver.HiveServerFactory;
import org.apache.sentry.tests.e2e.hive.hiveserver.InternalHiveServer;
import org.apache.sentry.tests.e2e.hive.hiveserver.InternalMetastoreServer;
import org.apache.sentry.tests.e2e.minisentry.SentrySrv;
import org.apache.sentry.tests.e2e.minisentry.SentrySrvFactory;
import org.fest.reflect.core.Reflection;
import org.apache.sentry.service.common.ServiceConstants.ServerConfig;
import org.apache.sentry.provider.db.SimpleDBProviderBackend;

import org.junit.*;
import org.junit.rules.Timeout;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Maps;
import com.google.common.io.Files;
import com.google.common.io.Resources;

import static org.apache.sentry.hdfs.ServiceConstants.ServerConfig.SENTRY_HDFS_INTEGRATION_PATH_PREFIXES;
import static org.apache.sentry.service.common.ServiceConstants.ServerConfig.SENTRY_DB_POLICY_STORE_OWNER_AS_PRIVILEGE;
import static org.junit.Assert.assertFalse;

/**
 * Base abstract class for HDFS Sync integration
 * (both Non-HA and HA modes)
 */
public abstract class TestHDFSIntegrationBase {

    private static final Logger LOGGER = LoggerFactory.getLogger(TestHDFSIntegrationBase.class);

    @ClassRule
    public static Timeout classTimeout = new Timeout(1200000); //millis, each class runs less than 600s (10m)
    @Rule
    public Timeout timeout = new Timeout(360000); //millis, each test runs less than 180s (3m)

    public static class WordCountMapper extends MapReduceBase implements Mapper<LongWritable, Text, String, Long> {

        public void map(LongWritable key, Text value, OutputCollector<String, Long> output, Reporter reporter)
                throws IOException {
            StringTokenizer st = new StringTokenizer(value.toString());
            while (st.hasMoreTokens()) {
                output.collect(st.nextToken(), 1L);
            }
        }

    }

    public static class SumReducer extends MapReduceBase implements Reducer<Text, Long, Text, Long> {

        public void reduce(Text key, Iterator<Long> values, OutputCollector<Text, Long> output, Reporter reporter)
                throws IOException {

            long sum = 0;
            while (values.hasNext()) {
                sum += values.next();
            }
            output.collect(key, sum);
        }

    }

    private static final String MANAGED_PREFIXES = "/user/hive/warehouse,/tmp/external";

    protected static final int NUM_RETRIES = 10;
    protected static final int RETRY_WAIT = 1000; //ms
    protected static final String EXTERNAL_SENTRY_SERVICE = "sentry.e2etest.external.sentry";
    protected static final String hiveWarehouseLocation = "/user/hive/warehouse";

    protected static MiniDFSCluster miniDFS;
    protected static InternalHiveServer hiveServer2;
    protected static InternalMetastoreServer metastore;
    protected static HiveMetaStoreClient hmsClient;

    protected static int sentryPort = -1;
    protected static SentrySrv sentryServer;
    protected static boolean testSentryHA = false;
    protected static final long STALE_THRESHOLD = 5000;

    // It is the interval in milliseconds that hdfs uses to get acl from sentry. Default is 500, but
    // we want it to be low in our tests so that changes reflect soon
    protected static final long CACHE_REFRESH = 100;

    // It is Used to wait before verifying result in test.
    // We want to make sure the cache is updated in our tests so that changes reflect soon. The unit is milliseconds
    // It takes at most (ServerConfig.SENTRY_HMSFOLLOWER_INIT_DELAY_MILLS_DEFAULT + ServerConfig.SENTRY_HMSFOLLOWER_INTERVAL_MILLS_DEFAULT)
    // for sentry to get the path configured in Hive, adding ServerConfig.SENTRY_HMSFOLLOWER_INTERVAL_MILLS_DEFAULT to be safe.
    // And then it takes at most CACHE_REFRESH for HDFS to get this from sentry, adding CACHE_REFRESH to be sure
    protected static final long WAIT_BEFORE_TESTVERIFY = ServerConfig.SENTRY_HMSFOLLOWER_INIT_DELAY_MILLS_DEFAULT
            + ServerConfig.SENTRY_HMSFOLLOWER_INTERVAL_MILLS_DEFAULT * 2 + CACHE_REFRESH * 2;

    protected static long HMSFOLLOWER_INTERVAL_MILLS = 50;
    protected static long WAIT_FOR_NOTIFICATION_PROCESSING = HMSFOLLOWER_INTERVAL_MILLS * 3;

    // Time to wait before running next tests. The unit is milliseconds.
    // Deleting HDFS may finish, but HDFS may not be ready for creating the same file again.
    // We need to to make sure that creating the same file in the next test will succeed
    // If we don't wait, next test may get exception similar to
    // "org.apache.hadoop.security.AccessControlException Permission denied: user=hive, access=EXECUTE,
    // inode="/tmp/external/p1":hdfs:hdfs:drwxrwx---"
    protected static final long WAIT_BEFORE_NEXTTEST = 50;

    protected static String fsURI;
    protected static int hmsPort;
    protected static File baseDir;
    protected static File policyFileLocation;
    protected static UserGroupInformation adminUgi;
    protected static UserGroupInformation hiveUgi;
    protected static UserGroupInformation sentryUgi;

    // Variables which are used for cleanup after test
    // Please set these values in each test
    protected Path tmpHDFSDir;
    protected String tmpHDFSDirStr;
    protected String tmpHDFSPartitionStr;
    protected Path partitionDir;
    protected static final String SERVER_NAME = "server1";
    protected String[] dbNames;
    protected String[] roles;
    protected String admin;
    protected static Boolean hdfsSyncEnabled = true;
    protected static Boolean hiveSyncOnCreate = false;
    protected static Boolean hiveSyncOnDrop = true;
    protected static Boolean ownerPrivilegeEnabled = false;
    protected static Boolean ownerPrivilegeGrantEnabled = false;
    protected static Configuration hadoopConf;
    protected static final Map<String, String> sentryProperties = Maps.newHashMap();
    protected static Configuration sentryConf = new Configuration(true);

    protected static File assertCreateDir(File dir) {
        if (!dir.isDirectory()) {
            Assert.assertTrue("Failed creating " + dir, dir.mkdirs());
        }
        return dir;
    }

    private static int findPort() throws IOException {
        ServerSocket socket = new ServerSocket(0);
        int port = socket.getLocalPort();
        socket.close();
        return port;
    }

    protected void verifyGroupPermOnAllSubDirs(String path, FsAction fsAction, String group,
            boolean groupShouldExist) throws Throwable {
        verifyOnAllSubDirs(path, fsAction, null, group, groupShouldExist, true);
    }

    protected void verifyGroupPermOnPath(String path, FsAction fsAction, String group, boolean groupShouldExist)
            throws Throwable {
        long elapsed_Time = 0, start_time = System.nanoTime();
        final long TOTAL_SYNC_TIME = NUM_RETRIES * RETRY_WAIT; //ms
        while (elapsed_Time <= TOTAL_SYNC_TIME) {
            try {
                verifyOnAllSubDirs(path, fsAction, null, group, groupShouldExist, false);
                break;
            } catch (Exception ex) {
                LOGGER.warn("verifyGroupPermOnAllSubDirs fails: elapsed time = " + elapsed_Time + " ms.");
            }
            elapsed_Time = (System.nanoTime() - start_time) / 1000000L; //ms
        }
        Assert.assertTrue(elapsed_Time <= TOTAL_SYNC_TIME);
    }

    protected void verifyUserPermOnAllSubDirs(String path, FsAction fsAction, String user, boolean groupShouldExist)
            throws Throwable {
        verifyOnAllSubDirs(path, fsAction, user, null, groupShouldExist, true);
    }

    protected void verifyUserPermOnPath(String path, FsAction fsAction, String user, boolean groupShouldExist)
            throws Throwable {
        long elapsed_Time = 0, start_time = System.nanoTime();
        final long TOTAL_SYNC_TIME = NUM_RETRIES * RETRY_WAIT; //ms
        while (elapsed_Time <= TOTAL_SYNC_TIME) {
            try {
                verifyOnAllSubDirs(path, fsAction, user, null, groupShouldExist, false);
                break;
            } catch (Exception ex) {
                LOGGER.warn("verifyGroupPermOnAllSubDirs fails: elapsed time = " + elapsed_Time + " ms.");
            }
            elapsed_Time = (System.nanoTime() - start_time) / 1000000L; //ms
        }
        Assert.assertTrue(elapsed_Time <= TOTAL_SYNC_TIME);
    }

    protected void verifyOnAllSubDirs(String path, FsAction fsAction, String user, String group,
            boolean groupShouldExist, boolean recurse) throws Throwable {
        verifyOnAllSubDirs(new Path(path), fsAction, user, group, groupShouldExist, recurse, NUM_RETRIES);
    }

    protected void verifyOnAllSubDirs(Path p, FsAction fsAction, String user, String group,
            boolean groupShouldExist, boolean recurse, int retry) throws Throwable {
        verifyOnAllSubDirsHelper(p, fsAction, user, group, groupShouldExist, recurse, retry);
    }

    /* SENTRY-1471 - fixing the validation logic.
     * a) When the number of retries exceeds the limit, propagate the Assert exception to the caller.
     * b) Throw an exception instead of returning false, to pass valuable debugging info up the stack
     *    - expected vs. found permissions.
     */
    private void verifyOnAllSubDirsHelper(Path p, FsAction fsAction, String user, String group, boolean shouldExist,
            boolean recurse, int retry) throws Throwable {
        FileStatus fStatus = null;
        // validate parent dir's acls
        try {
            fStatus = miniDFS.getFileSystem().getFileStatus(p);
            if (shouldExist) {
                if (!Strings.isNullOrEmpty(group)) {
                    Assert.assertEquals("Error at verifying Path action : " + p + " ;", fsAction,
                            getAcls(AclEntryType.GROUP, p).get(group));
                }
                if (!Strings.isNullOrEmpty(user)) {
                    Assert.assertEquals("Error at verifying Path action : " + p + " ;", fsAction,
                            getAcls(AclEntryType.USER, p).get(user));
                }
            } else {
                if (!Strings.isNullOrEmpty(group)) {
                    assertFalse("Error at verifying Path : " + p + " ," + " group : " + group + " ;",
                            getAcls(AclEntryType.GROUP, p).containsKey(group));
                }
                if (!Strings.isNullOrEmpty(user)) {
                    assertFalse("Error at verifying Path : " + p + " ," + " user : " + user + " ;",
                            getAcls(AclEntryType.USER, p).containsKey(user));
                }
            }
            LOGGER.info("Successfully found acls for path = " + p.getName());
        } catch (Throwable th) {
            if (retry > 0) {
                LOGGER.info("Retry: " + retry);
                Thread.sleep(RETRY_WAIT);
                verifyOnAllSubDirsHelper(p, fsAction, user, group, shouldExist, recurse, retry - 1);
            } else {
                throw th;
            }
        }
        // validate children dirs
        if (recurse && fStatus.isDirectory()) {
            FileStatus[] children = miniDFS.getFileSystem().listStatus(p);
            for (FileStatus fs : children) {
                verifyOnAllSubDirsHelper(fs.getPath(), fsAction, user, group, shouldExist, recurse, NUM_RETRIES);
            }
        }
    }

    protected Map<String, FsAction> getAcls(AclEntryType type, Path path) throws Exception {
        AclStatus aclStatus = miniDFS.getFileSystem().getAclStatus(path);
        Map<String, FsAction> acls = new HashMap<String, FsAction>();
        for (AclEntry ent : aclStatus.getEntries()) {
            if (ent.getType().equals(type)) {

                // In case of duplicate acl exist, exception should be thrown.
                if (acls.containsKey(ent.getName())) {
                    throw new SentryAlreadyExistsException("The acl " + ent.getName() + " already exists.\n");
                } else {
                    acls.put(ent.getName(), ent.getPermission());
                }
            }
        }
        return acls;
    }

    protected void loadData(Statement stmt) throws IOException, SQLException {
        FSDataOutputStream f1 = miniDFS.getFileSystem().create(new Path("/tmp/f1.txt"));
        f1.writeChars("m1d1_t1\n");
        f1.writeChars("m1d1_t2\n");
        f1.writeChars("m1d1_t3\n");
        f1.flush();
        f1.close();
        stmt.execute("load data inpath \'/tmp/f1.txt\' overwrite into table p1 partition (month=1, day=1)");
        FSDataOutputStream f2 = miniDFS.getFileSystem().create(new Path("/tmp/f2.txt"));
        f2.writeChars("m2d2_t4\n");
        f2.writeChars("m2d2_t5\n");
        f2.writeChars("m2d2_t6\n");
        f2.flush();
        f2.close();
        stmt.execute("load data inpath \'/tmp/f2.txt\' overwrite into table p1 partition (month=2, day=2)");
        ResultSet rs = stmt.executeQuery("select * from p1");
        List<String> vals = new ArrayList<String>();
        while (rs.next()) {
            vals.add(rs.getString(1));
        }
        Assert.assertEquals(6, vals.size());
        rs.close();
    }

    protected void verifyQuery(Statement stmt, String table, int n) throws Throwable {
        verifyQuery(stmt, table, n, NUM_RETRIES);
    }

    /* SENTRY-1471 - fixing the validation logic.
     * a) When the number of retries exceeds the limit, propagate the Assert exception to the caller.
     * b) Throw an exception immediately, instead of using boolean variable, to pass valuable debugging
     *    info up the stack - expected vs. found number of rows.
     */
    protected void verifyQuery(Statement stmt, String table, int n, int retry) throws Throwable {
        ResultSet rs = null;
        try {
            rs = stmt.executeQuery("select * from " + table);
            int numRows = 0;
            while (rs.next()) {
                numRows++;
            }
            Assert.assertEquals(n, numRows);
        } catch (Throwable th) {
            if (retry > 0) {
                LOGGER.info("Retry: " + retry);
                Thread.sleep(RETRY_WAIT);
                verifyQuery(stmt, table, n, retry - 1);
            } else {
                throw th;
            }
        }
    }

    protected void verifyAccessToPath(String user, String group, String path, boolean hasPermission)
            throws Exception {
        Path p = new Path(path);
        FileSystem fs = miniDFS.getFileSystem();
        try {
            fs.listFiles(p, true);
            if (!hasPermission) {
                assertFalse("Expected listing files to fail", false);
            }
        } catch (Exception e) {
            if (hasPermission) {
                throw e;
            }
        }
    }

    protected void writeToPath(String path, int numRows, String user, String group) throws IOException {
        Path p = new Path(path);
        miniDFS.getFileSystem().mkdirs(p);
        miniDFS.getFileSystem().setOwner(p, user, group);
        FSDataOutputStream f1 = miniDFS.getFileSystem().create(new Path(path + "/stuff.txt"));
        for (int i = 0; i < numRows; i++) {
            f1.writeChars("random" + i + "\n");
        }
        f1.flush();
        f1.close();
        miniDFS.getFileSystem().setOwner(new Path(path + "/stuff.txt"), "asuresh", "supergroup");
        miniDFS.getFileSystem().setPermission(new Path(path + "/stuff.txt"), FsPermission.valueOf("-rwxrwx--x"));
    }

    protected void verifyHDFSandMR(Statement stmt) throws Throwable {
        // hbase user should not be allowed to read...
        UserGroupInformation hbaseUgi = UserGroupInformation.createUserForTesting("hbase",
                new String[] { "hbase" });
        hbaseUgi.doAs(new PrivilegedExceptionAction<Void>() {
            @Override
            public Void run() throws Exception {
                try {
                    miniDFS.getFileSystem().open(new Path("/user/hive/warehouse/p1/month=1/day=1/f1.txt"));
                    Assert.fail("Should not be allowed !!");
                } catch (Exception e) {
                    Assert.assertEquals("Wrong Error : " + e.getMessage(), true,
                            e.getMessage().contains("Permission denied: user=hbase"));
                }
                return null;
            }
        });

        // WordCount should fail..
        // runWordCount(new JobConf(miniMR.getConfig()), "/user/hive/warehouse/p1/month=1/day=1", "/tmp/wc_out");

        stmt.execute("grant select on table p1 to role p1_admin");

        verifyGroupPermOnAllSubDirs("/user/hive/warehouse/p1", FsAction.READ_EXECUTE, "hbase", true);
        // hbase user should now be allowed to read...
        hbaseUgi.doAs(new PrivilegedExceptionAction<Void>() {
            @Override
            public Void run() throws Exception {
                Path p = new Path("/user/hive/warehouse/p1/month=2/day=2/f2.txt");
                BufferedReader in = new BufferedReader(new InputStreamReader(miniDFS.getFileSystem().open(p)));
                String line = null;
                List<String> lines = new ArrayList<String>();
                do {
                    line = in.readLine();
                    if (line != null) {
                        lines.add(line);
                    }
                } while (line != null);
                Assert.assertEquals(3, lines.size());
                in.close();
                return null;
            }
        });

    }

    // verify given table/DB has no longer permissions
    protected void verifyIfAllPrivilegeAreDropped(Statement statement, List<String> roles, String objectName,
            int resultPos) throws Exception {
        for (String roleName : roles) {
            ResultSet resultSet = statement.executeQuery("SHOW GRANT ROLE " + roleName);
            while (resultSet.next()) {
                String returned = resultSet.getString(resultPos);
                assertFalse("value " + objectName + " shouldn't be detected, but actually " + returned
                        + " is found from resultSet", objectName.equalsIgnoreCase(returned));
            }
            resultSet.close();
        }
    }

    protected List<String> getRoles(Statement statement) throws Exception {
        ArrayList<String> roleList = Lists.newArrayList();
        ResultSet resultSet = statement.executeQuery("SHOW ROLES ");
        while (resultSet.next()) {
            roleList.add(resultSet.getString(1));
        }
        return roleList;
    }

    protected void loadDataTwoCols(Statement stmt) throws IOException, SQLException {
        FSDataOutputStream f1 = miniDFS.getFileSystem().create(new Path("/tmp/f2.txt"));
        f1.writeChars("m1d1_t1, m1d1_t2\n");
        f1.writeChars("m1d1_t2, m1d1_t2\n");
        f1.writeChars("m1d1_t3, m1d1_t2\n");
        f1.flush();
        f1.close();
        stmt.execute("load data inpath \'/tmp/f2.txt\' overwrite into table p1 partition (month=1, day=1)");
        ResultSet rs = stmt.executeQuery("select * from p1");
        List<String> vals = new ArrayList<String>();
        while (rs.next()) {
            vals.add(rs.getString(1));
        }
        Assert.assertEquals(3, vals.size());
        rs.close();
    }

    @BeforeClass
    public static void setup() throws Exception {
        Class.forName("org.apache.hive.jdbc.HiveDriver");
        baseDir = Files.createTempDir();
        policyFileLocation = new File(baseDir, HiveServerFactory.AUTHZ_PROVIDER_FILENAME);
        PolicyFile policyFile = PolicyFile.setAdminOnServer1("hive")
                .setUserGroupMapping(StaticUserGroup.getStaticMapping());
        policyFile.write(policyFileLocation);

        adminUgi = UserGroupInformation.createUserForTesting(System.getProperty("user.name"),
                new String[] { "supergroup" });

        hiveUgi = UserGroupInformation.createUserForTesting("hive", new String[] { "hive" });

        sentryUgi = UserGroupInformation.createUserForTesting("sentry", new String[] { "sentry" });

        // Create SentryService and its internal objects.
        // Set Sentry port
        createSentry();

        // Create hive-site.xml that contains the metastore uri
        // it is used by HMSFollower
        configureHiveAndMetastoreForSentry();

        // Start SentryService after Hive configuration hive-site.xml is available
        // So HMSFollower can contact metastore using its URI
        startSentry();

        // Start HDFS and MR with Sentry Port. Set fsURI
        startDFSandYARN();

        // Configure Hive and Metastore with Sentry Port and fsURI
        // Read src/test/resources/sentry-site.xml.
        // Create hive-site.xml and sentry-site.xml used by Hive.
        HiveConf hiveConf = configureHiveAndMetastore();

        // Start Hive and Metastore after SentryService is started
        startHiveAndMetastore(hiveConf);
    }

    @Before
    public void setUpTempDir() throws IOException {
        LOGGER.debug("setUpTempDir starts");
        tmpHDFSDirStr = "/tmp/external";
        tmpHDFSPartitionStr = tmpHDFSDirStr + "/p1";
        tmpHDFSDir = new Path(tmpHDFSDirStr);
        if (miniDFS.getFileSystem().exists(tmpHDFSDir)) {
            miniDFS.getFileSystem().delete(tmpHDFSDir, true);
        }
        Assert.assertTrue(miniDFS.getFileSystem().mkdirs(tmpHDFSDir));
        miniDFS.getFileSystem().setOwner(tmpHDFSDir, "hive", "hive");
        miniDFS.getFileSystem().setPermission(tmpHDFSDir, FsPermission.valueOf("drwxrwx--x"));
        partitionDir = new Path(tmpHDFSPartitionStr);
        if (miniDFS.getFileSystem().exists(partitionDir)) {
            miniDFS.getFileSystem().delete(partitionDir, true);
        }
        Assert.assertTrue(miniDFS.getFileSystem().mkdirs(partitionDir));
        LOGGER.debug("setUpTempDir ends");
    }

    private static HiveConf configureHiveAndMetastoreForSentry() throws IOException, InterruptedException {
        final HiveConf hiveConfiguration = new HiveConf();
        hiveUgi.doAs(new PrivilegedExceptionAction<Void>() {
            @Override
            public Void run() throws Exception {
                HiveConf hiveConf = hiveConfiguration;
                hmsPort = findPort();
                LOGGER.info("\n\n HMS port : " + hmsPort + "\n\n");

                // Sets hive.metastore.authorization.storage.checks to true, so that
                // disallow the operations such as drop-partition if the user in question
                // doesn't have permissions to delete the corresponding directory
                // on the storage.
                hiveConf.set("hive.metastore.authorization.storage.checks", "true");
                hiveConf.set("hive.metastore.uris", "thrift://localhost:" + hmsPort);
                // queries made by hive user (beeline) and sentry to HMS skip meta store check
                hiveConf.set("sentry.metastore.service.users", "hive,sentry");

                File confDir = assertCreateDir(new File(baseDir, "etc"));
                File hiveSite = new File(confDir, "hive-site.xml");
                hiveConf.set("hive.server2.enable.doAs", "false");
                OutputStream out = new FileOutputStream(hiveSite);
                hiveConf.writeXml(out);
                out.close();

                Reflection.staticField("hiveSiteURL").ofType(URL.class).in(HiveConf.class)
                        .set(hiveSite.toURI().toURL());
                return null;
            }
        });

        return hiveConfiguration;
    }

    private static HiveConf configureHiveAndMetastore() throws IOException, InterruptedException {
        final HiveConf hiveConfiguration = new HiveConf();
        hiveUgi.doAs(new PrivilegedExceptionAction<Void>() {
            @Override
            public Void run() throws Exception {
                HiveConf hiveConf = hiveConfiguration;
                hiveConf.set("sentry.service.client.server.rpc-addresses", "localhost");
                hiveConf.set("sentry.hdfs.service.client.server.rpc-addresses", "localhost");
                hiveConf.set("sentry.hdfs.service.client.server.rpc-port", String.valueOf(sentryPort));
                hiveConf.set("sentry.service.client.server.rpc-port", String.valueOf(sentryPort));
                //        hiveConf.set("sentry.service.server.compact.transport", "true");
                //        hiveConf.set("sentry.service.client.compact.transport", "true");
                hiveConf.set("sentry.service.security.mode", "none");
                hiveConf.set("sentry.hdfs.service.security.mode", "none");
                hiveConf.set("sentry.hdfs.init.update.retry.delay.ms", "500");
                hiveConf.set("sentry.hive.provider.backend",
                        "org.apache.sentry.provider.db.SimpleDBProviderBackend");
                hiveConf.set("sentry.provider", LocalGroupResourceAuthorizationProvider.class.getName());
                hiveConf.set("sentry.hive.provider", LocalGroupResourceAuthorizationProvider.class.getName());
                hiveConf.set("sentry.hive.provider.resource", policyFileLocation.getPath());
                hiveConf.set("sentry.hive.testing.mode", "true");
                hiveConf.set("sentry.hive.server", SERVER_NAME);

                hiveConf.set(ServerConfig.SENTRY_STORE_GROUP_MAPPING,
                        ServerConfig.SENTRY_STORE_LOCAL_GROUP_MAPPING);
                hiveConf.set(ServerConfig.SENTRY_STORE_GROUP_MAPPING_RESOURCE, policyFileLocation.getPath());
                hiveConf.set("fs.defaultFS", fsURI);
                hiveConf.set("fs.default.name", fsURI);
                hiveConf.set("hive.metastore.execute.setugi", "true");
                hiveConf.set("hive.metastore.warehouse.dir", "hdfs://" + hiveWarehouseLocation);
                hiveConf.set("javax.jdo.option.ConnectionURL",
                        "jdbc:derby:;databaseName=" + baseDir.getAbsolutePath() + "/metastore_db;create=true");
                hiveConf.set("javax.jdo.option.ConnectionDriverName", "org.apache.derby.jdbc.EmbeddedDriver");
                hiveConf.set("javax.jdo.option.ConnectionUserName", "hive");
                hiveConf.set("javax.jdo.option.ConnectionPassword", "hive");
                hiveConf.set("datanucleus.schema.autoCreateAll", "true");
                hiveConf.set("datanucleus.autoStartMechanism", "SchemaTable");
                hiveConf.set("datanucleus.schema.autoCreateTables", "true");

                hiveConf.set(ConfVars.HIVE_AUTHORIZATION_ENABLED.varname, "true");
                hiveConf.set(ConfVars.HIVE_AUTHORIZATION_MANAGER.varname,
                        SentryHiveAuthorizerFactory.class.getName());
                hiveConf.set(ConfVars.HIVE_CBO_ENABLED.varname, "false");
                hiveConf.set(ConfVars.METASTORE_DISALLOW_INCOMPATIBLE_COL_TYPE_CHANGES.varname, "false");
                hiveConf.set(ConfVars.HIVE_IN_TEST.varname, "true");

                // Sets the hadoop temporary directory specified by the java.io.tmpdir (already set to the
                // maven build directory to avoid writing to the /tmp directly
                String hadoopTempDir = System.getProperty("java.io.tmpdir") + File.separator + "hadoop-tmp";
                hiveConf.set("hadoop.tmp.dir", hadoopTempDir);

                // This configuration will avoid that the HMS fails if the metastore schema has not version
                // information. For some reason, HMS does not set a version initially on our tests.
                hiveConf.set(ConfVars.METASTORE_SCHEMA_VERIFICATION.varname, "false");

                // Sets hive.metastore.authorization.storage.checks to true, so that
                // disallow the operations such as drop-partition if the user in question
                // doesn't have permissions to delete the corresponding directory
                // on the storage.
                hiveConf.set("hive.metastore.authorization.storage.checks", "true");
                hiveConf.set("hive.metastore.uris", "thrift://localhost:" + hmsPort);
                hiveConf.set("hive.metastore.pre.event.listeners",
                        "org.apache.sentry.binding.metastore.MetastoreAuthzBinding");
                hiveConf.set("hive.metastore.transactional.event.listeners",
                        "org.apache.hive.hcatalog.listener.DbNotificationListener");
                hiveConf.set("hive.metastore.event.listeners",
                        "org.apache.sentry.binding.metastore.SentrySyncHMSNotificationsPostEventListener");
                hiveConf.set("hive.metastore.event.message.factory",
                        "org.apache.sentry.binding.metastore.messaging.json.SentryJSONMessageFactory");
                hiveConf.set("hive.security.authorization.task.factory",
                        "org.apache.sentry.binding.hive.SentryHiveAuthorizationTaskFactoryImpl");
                hiveConf.set("hive.server2.session.hook",
                        "org.apache.sentry.binding.hive.HiveAuthzBindingSessionHook");
                // queries made by hive user (beeline) and sentry to HMS skip meta store check
                hiveConf.set("sentry.metastore.service.users", "hive,sentry");
                // make sure metastore calls sentry post event listener
                hiveConf.set("hive.metastore.event.listeners",
                        "org.apache.sentry.binding.metastore.SentrySyncHMSNotificationsPostEventListener");

                HiveAuthzConf authzConf = new HiveAuthzConf(Resources.getResource("sentry-site.xml"));
                authzConf.addResource(hiveConf);
                File confDir = assertCreateDir(new File(baseDir, "etc"));
                File accessSite = new File(confDir, HiveAuthzConf.AUTHZ_SITE_FILE);
                OutputStream out = new FileOutputStream(accessSite);
                authzConf.set("fs.defaultFS", fsURI);
                authzConf.writeXml(out);
                out.close();

                hiveConf.set("hive.sentry.conf.url", accessSite.getPath());
                LOGGER.info("Sentry client file : " + accessSite.getPath());

                File hiveSite = new File(confDir, "hive-site.xml");
                hiveConf.set("hive.server2.enable.doAs", "false");
                hiveConf.set(HiveAuthzConf.HIVE_SENTRY_CONF_URL, accessSite.toURI().toURL().toExternalForm());
                out = new FileOutputStream(hiveSite);
                hiveConf.writeXml(out);
                out.close();

                Reflection.staticField("hiveSiteURL").ofType(URL.class).in(HiveConf.class)
                        .set(hiveSite.toURI().toURL());
                return null;
            }
        });

        return hiveConfiguration;
    }

    private static void startHiveAndMetastore(HiveConf hiveConfig) throws IOException, InterruptedException {
        startHiveAndMetastore(hiveConfig, NUM_RETRIES);
    }

    private static void startHiveAndMetastore(final HiveConf hiveConf, final int retries)
            throws IOException, InterruptedException {
        hiveUgi.doAs(new PrivilegedExceptionAction<Void>() {
            @Override
            public Void run() throws Exception {
                final CountDownLatch hmsStartedSignal = new CountDownLatch(1);

                metastore = new InternalMetastoreServer(hiveConf);
                new Thread() {
                    @Override
                    public void run() {
                        try {
                            metastore.start();
                            hmsStartedSignal.countDown();
                            while (true) {
                                Thread.sleep(1000L);
                            }
                        } catch (Exception e) {
                            LOGGER.info("Could not start Hive Server");
                        }
                    }
                }.start();

                hmsStartedSignal.await(30, TimeUnit.SECONDS);
                hmsClient = new HiveMetaStoreClient(hiveConf);
                startHiveServer2(retries, hiveConf);
                return null;
            }
        });
    }

    private static void startHiveServer2(final int retries, HiveConf hiveConf)
            throws IOException, InterruptedException, SQLException {
        Connection conn = null;
        Thread th = null;
        final AtomicBoolean keepRunning = new AtomicBoolean(true);
        try {
            hiveServer2 = new InternalHiveServer(hiveConf);
            th = new Thread() {
                @Override
                public void run() {
                    try {
                        hiveServer2.start();
                        while (keepRunning.get()) {
                            Thread.sleep(1000L);
                        }
                    } catch (Exception e) {
                        LOGGER.info("Could not start Hive Server");
                    }
                }
            };
            th.start();
            Thread.sleep(RETRY_WAIT * 5);
            conn = hiveServer2.createConnection("hive", "hive");
        } catch (Exception ex) {
            if (retries > 0) {
                try {
                    keepRunning.set(false);
                    hiveServer2.shutdown();
                } catch (Exception e) {
                    // Ignore
                }
                LOGGER.info("Re-starting Hive Server2 !!");
                startHiveServer2(retries - 1, hiveConf);
            }
        }
        if (conn != null) {
            conn.close();
        }
    }

    private static void startDFSandYARN() throws IOException, InterruptedException {
        adminUgi.doAs(new PrivilegedExceptionAction<Void>() {
            @Override
            public Void run() throws Exception {
                System.setProperty(MiniDFSCluster.PROP_TEST_BUILD_DATA, "target/test/data");
                hadoopConf = new HdfsConfiguration();
                hadoopConf.set(DFSConfigKeys.DFS_NAMENODE_INODE_ATTRIBUTES_PROVIDER_KEY,
                        SentryINodeAttributesProvider.class.getName());
                hadoopConf.setBoolean(DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY, true);
                hadoopConf.setInt(DFSConfigKeys.DFS_REPLICATION_KEY, 1);
                File dfsDir = assertCreateDir(new File(baseDir, "dfs"));
                hadoopConf.set(MiniDFSCluster.HDFS_MINIDFS_BASEDIR, dfsDir.getPath());
                hadoopConf.set("hadoop.security.group.mapping", MiniDFS.PseudoGroupMappingService.class.getName());
                Configuration.addDefaultResource("test.xml");

                hadoopConf.set("sentry.authorization-provider.hdfs-path-prefixes", MANAGED_PREFIXES);
                hadoopConf.set("sentry.authorization-provider.cache-refresh-retry-wait.ms", "5000");
                hadoopConf.set("sentry.authorization-provider.cache-refresh-interval.ms",
                        String.valueOf(CACHE_REFRESH));

                hadoopConf.set("sentry.authorization-provider.cache-stale-threshold.ms",
                        String.valueOf(STALE_THRESHOLD));

                hadoopConf.set("sentry.hdfs.service.security.mode", "none");
                hadoopConf.set("sentry.hdfs.service.client.server.rpc-addresses", "localhost");
                hadoopConf.set("sentry.hdfs.service.client.server.rpc-port", String.valueOf(sentryPort));
                EditLogFileOutputStream.setShouldSkipFsyncForTesting(true);
                miniDFS = new MiniDFSCluster.Builder(hadoopConf).build();
                Path tmpPath = new Path("/tmp");
                Path hivePath = new Path("/user/hive");
                Path warehousePath = new Path(hivePath, "warehouse");
                miniDFS.getFileSystem().mkdirs(warehousePath);
                boolean directory = miniDFS.getFileSystem().isDirectory(warehousePath);
                LOGGER.info("\n\n Is dir :" + directory + "\n\n");
                LOGGER.info("\n\n DefaultFS :" + miniDFS.getFileSystem().getUri() + "\n\n");
                fsURI = miniDFS.getFileSystem().getUri().toString();
                hadoopConf.set("fs.defaultFS", fsURI);

                // Create Yarn cluster
                // miniMR = MiniMRClientClusterFactory.create(this.getClass(), 1, conf);

                miniDFS.getFileSystem().mkdirs(tmpPath);
                miniDFS.getFileSystem().setPermission(tmpPath, FsPermission.valueOf("drwxrwxrwx"));
                miniDFS.getFileSystem().setOwner(hivePath, "hive", "hive");
                miniDFS.getFileSystem().setOwner(warehousePath, "hive", "hive");
                LOGGER.info("\n\n Owner :" + miniDFS.getFileSystem().getFileStatus(warehousePath).getOwner() + ", "
                        + miniDFS.getFileSystem().getFileStatus(warehousePath).getGroup() + "\n\n");
                LOGGER.info("\n\n Owner tmp :" + miniDFS.getFileSystem().getFileStatus(tmpPath).getOwner() + ", "
                        + miniDFS.getFileSystem().getFileStatus(tmpPath).getGroup() + ", "
                        + miniDFS.getFileSystem().getFileStatus(tmpPath).getPermission() + ", " + "\n\n");

                int dfsSafeCheckRetry = 30;
                boolean hasStarted = false;
                for (int i = dfsSafeCheckRetry; i > 0; i--) {
                    if (!miniDFS.getFileSystem().isInSafeMode()) {
                        hasStarted = true;
                        LOGGER.info("HDFS safemode check num times : " + (31 - i));
                        break;
                    }
                }
                if (!hasStarted) {
                    throw new RuntimeException("HDFS hasnt exited safe mode yet..");
                }

                return null;
            }
        });
    }

    private static void startSentry() throws Exception {
        SentryServiceClientFactory factory = SentryServiceClientFactory.factoryReset(null);
        if (factory != null) {
            factory.close();
        }
        SentryHDFSServiceClientFactory.factoryReset();
        try {
            sentryUgi.doAs(new PrivilegedExceptionAction() {
                @Override
                public Void run() throws Exception {
                    sentryServer.startAll();
                    LOGGER.info("\n\n Sentry service started \n\n");
                    return null;
                }
            });
        } catch (Exception ex) {
            //An exception happening in above block will result in a wrapped UndeclaredThrowableException.
            throw new Exception(ex.getCause());
        }
    }

    private static void createSentry() throws Exception {
        try {

            sentryUgi.doAs(new PrivilegedExceptionAction<Void>() {
                @Override
                public Void run() throws Exception {
                    sentryConf.set(SENTRY_HDFS_INTEGRATION_PATH_PREFIXES, MANAGED_PREFIXES);
                    sentryProperties.put(ServerConfig.PRINCIPAL, "sentry/_HOST@TEST.COM");
                    sentryProperties.put(HiveServerFactory.AUTHZ_PROVIDER_BACKEND,
                            SimpleDBProviderBackend.class.getName());
                    sentryProperties.put(ConfVars.HIVE_AUTHORIZATION_TASK_FACTORY.varname,
                            SentryHiveAuthorizationTaskFactoryImpl.class.getName());
                    sentryProperties.put(ConfVars.HIVE_SERVER2_THRIFT_MIN_WORKER_THREADS.varname, "2");
                    sentryProperties.put("hive.exec.local.scratchdir", Files.createTempDir().getAbsolutePath());
                    sentryProperties.put(ServerConfig.SECURITY_MODE, ServerConfig.SECURITY_MODE_NONE);
                    //        sentryProperties.put("sentry.service.server.compact.transport", "true");
                    sentryProperties.put("sentry.hive.testing.mode", "true");
                    sentryProperties.put("sentry.service.reporting", "JMX");
                    sentryProperties.put(ServerConfig.ADMIN_GROUPS, "hive,admin");
                    sentryProperties.put(ServerConfig.RPC_ADDRESS, "localhost");
                    sentryProperties.put(ServerConfig.RPC_PORT, String.valueOf(sentryPort > 0 ? sentryPort : 0));
                    sentryProperties.put(ServerConfig.SENTRY_VERIFY_SCHEM_VERSION, "false");
                    sentryProperties.put("sentry.hive.server", "server1");

                    sentryProperties.put(ServerConfig.SENTRY_STORE_GROUP_MAPPING,
                            ServerConfig.SENTRY_STORE_LOCAL_GROUP_MAPPING);
                    sentryProperties.put(ServerConfig.SENTRY_STORE_GROUP_MAPPING_RESOURCE,
                            policyFileLocation.getPath());
                    sentryProperties.put(ServerConfig.SENTRY_STORE_JDBC_URL,
                            "jdbc:derby:;databaseName=" + baseDir.getPath() + "/sentrystore_db;create=true");
                    sentryProperties.put(ServerConfig.SENTRY_STORE_JDBC_PASS, "dummy");
                    sentryProperties.put(ServerConfig.SENTRY_HMSFOLLOWER_INIT_DELAY_MILLS, "10000");
                    sentryProperties.put(ServerConfig.SENTRY_HMSFOLLOWER_INTERVAL_MILLS,
                            String.valueOf(HMSFOLLOWER_INTERVAL_MILLS));
                    sentryProperties.put(ServerConfig.RPC_MIN_THREADS, "3");
                    sentryProperties.put("sentry.hive.sync.drop", "true");
                    sentryProperties.put("sentry.hive.sync.create", "true");

                    if (hiveSyncOnCreate) {
                        sentryProperties.put("sentry.hive.sync.create", "true");
                    } else {
                        sentryProperties.put("sentry.hive.sync.create", "false");
                    }
                    if (hiveSyncOnDrop) {
                        sentryProperties.put("sentry.hive.sync.drop", "true");
                    } else {
                        sentryProperties.put("sentry.hive.sync.drop", "false");
                    }
                    if (hdfsSyncEnabled) {
                        sentryProperties.put("sentry.service.processor.factories",
                                "org.apache.sentry.api.service.thrift.SentryPolicyStoreProcessorFactory,org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory");
                        sentryProperties.put("sentry.policy.store.plugins", "org.apache.sentry.hdfs.SentryPlugin");
                    }

                    if (ownerPrivilegeEnabled) {
                        if (ownerPrivilegeGrantEnabled) {
                            sentryProperties.put(SENTRY_DB_POLICY_STORE_OWNER_AS_PRIVILEGE,
                                    SentryOwnerPrivilegeType.ALL_WITH_GRANT.toString());
                        } else {
                            sentryProperties.put(SENTRY_DB_POLICY_STORE_OWNER_AS_PRIVILEGE,
                                    SentryOwnerPrivilegeType.ALL.toString());
                        }
                    } else {
                        sentryProperties.put(SENTRY_DB_POLICY_STORE_OWNER_AS_PRIVILEGE,
                                SentryOwnerPrivilegeType.NONE.toString());
                    }

                    for (Map.Entry<String, String> entry : sentryProperties.entrySet()) {
                        sentryConf.set(entry.getKey(), entry.getValue());
                    }
                    sentryServer = SentrySrvFactory.create(SentrySrvFactory.SentrySrvType.INTERNAL_SERVER,
                            sentryConf, testSentryHA ? 2 : 1);
                    sentryPort = sentryServer.get(0).getAddress().getPort();
                    LOGGER.info("Sentry service is created on port {}", sentryPort);
                    return null;
                }
            });
        } catch (Exception e) {
            //An exception happening in above block will result in a wrapped UndeclaredThrowableException.
            throw new Exception(e.getCause());
        }
    }

    @After
    public void cleanAfterTest() throws Exception {
        //Clean up database
        Connection conn;
        Statement stmt;
        Preconditions.checkArgument(admin != null && dbNames != null && roles != null && tmpHDFSDir != null,
                "Test case did not set some of these values required for clean up: admin, dbNames, roles, tmpHDFSDir");

        conn = hiveServer2.createConnection(admin, admin);
        stmt = conn.createStatement();
        for (String dbName : dbNames) {
            stmt.execute("drop database if exists " + dbName + " cascade");
        }
        stmt.close();
        conn.close();

        //Clean up roles
        conn = hiveServer2.createConnection("hive", "hive");
        stmt = conn.createStatement();
        LOGGER.info("About to clear all roles");
        for (String role : roles) {
            stmt.execute("drop role " + role);
        }
        stmt.close();
        conn.close();

        //Clean up hdfs directories
        miniDFS.getFileSystem().delete(tmpHDFSDir, true);

        tmpHDFSDir = null;
        dbNames = null;
        roles = null;
        admin = null;
        Thread.sleep(WAIT_BEFORE_NEXTTEST); // make sure the clean up is done before next test starts. otherwise, the next test may fail
    }

    @AfterClass
    public static void cleanUp() throws Exception {
        try {
            if (miniDFS != null) {
                miniDFS.shutdown();
            }
        } finally {
            try {
                if (hiveServer2 != null) {
                    hiveServer2.shutdown();
                }
            } finally {
                try {
                    if (metastore != null) {
                        metastore.shutdown();
                    }
                } finally {
                    sentryServer.close();
                }
            }
        }
    }
}