I am thinking of the other way, when folks check sentry ha related stuffs
into master could merge into redesign-sentry-ha branch. There are also new
tests we might want to have to validate the feature branch.

Best,
Anne

On Wed, Jun 22, 2016 at 11:18 AM, Colin McCabe <[email protected]> wrote:

> Let's try to focus on getting our branch done, and then worry about
> merging back to master.
>
> People move stuff around all the time, and following the latest churn
> takes time.
>
> Colin
>
> On Wed, Jun 22, 2016 at 9:45 AM, Anne Yu <[email protected]> wrote:
>
>> Hi Colin and Team,
>>
>> We are implementing Sentry-ha redesign in the branch
>> *redesign-sentry-ha*,  do you think this commit need also go into that
>> branch. From now on, we might want to check in ha fixes in both trunk and
>> branch.
>>
>> Thanks,
>> Anne
>> On Jun 21, 2016 10:27 PM, <[email protected]> wrote:
>>
>>> Repository: sentry
>>> Updated Branches:
>>>   refs/heads/SENTRY-1205 aef769858 -> 4767ec38e
>>>
>>>
>>> SENTRY-1348: Move HA related class from sentry-provider-db to
>>> sentry-service-common(Colin Ma, reviewed by Dapeng Sun)
>>>
>>>
>>> Project: http://git-wip-us.apache.org/repos/asf/sentry/repo
>>> Commit: http://git-wip-us.apache.org/repos/asf/sentry/commit/4767ec38
>>> Tree: http://git-wip-us.apache.org/repos/asf/sentry/tree/4767ec38
>>> Diff: http://git-wip-us.apache.org/repos/asf/sentry/diff/4767ec38
>>>
>>> Branch: refs/heads/SENTRY-1205
>>> Commit: 4767ec38ef2a5d967d37539c64c787c986afe5cb
>>> Parents: aef7698
>>> Author: Colin Ma <[email protected]>
>>> Authored: Wed Jun 22 14:11:57 2016 +0800
>>> Committer: Colin Ma <[email protected]>
>>> Committed: Wed Jun 22 14:11:57 2016 +0800
>>>
>>> ----------------------------------------------------------------------
>>>  sentry-provider/sentry-provider-db/pom.xml      |  17 --
>>>  .../persistent/FixedJsonInstanceSerializer.java | 163 ------------
>>>  .../db/service/persistent/HAContext.java        | 262
>>> -------------------
>>>  .../service/thrift/JaasConfiguration.java       | 133 ----------
>>>  sentry-service/sentry-service-common/pom.xml    |  17 ++
>>>  .../persistent/FixedJsonInstanceSerializer.java | 163 ++++++++++++
>>>  .../db/service/persistent/HAContext.java        | 262
>>> +++++++++++++++++++
>>>  .../service/thrift/JaasConfiguration.java       | 133 ++++++++++
>>>  8 files changed, 575 insertions(+), 575 deletions(-)
>>> ----------------------------------------------------------------------
>>>
>>>
>>>
>>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-provider/sentry-provider-db/pom.xml
>>> ----------------------------------------------------------------------
>>> diff --git a/sentry-provider/sentry-provider-db/pom.xml
>>> b/sentry-provider/sentry-provider-db/pom.xml
>>> index f3029fa..3d76198 100644
>>> --- a/sentry-provider/sentry-provider-db/pom.xml
>>> +++ b/sentry-provider/sentry-provider-db/pom.xml
>>> @@ -38,11 +38,6 @@ limitations under the License.
>>>      </dependency>
>>>      <dependency>
>>>        <groupId>org.apache.hadoop</groupId>
>>> -      <artifactId>hadoop-common</artifactId>
>>> -      <scope>provided</scope>
>>> -    </dependency>
>>> -    <dependency>
>>> -      <groupId>org.apache.hadoop</groupId>
>>>        <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
>>>      </dependency>
>>>      <dependency>
>>> @@ -166,18 +161,6 @@ limitations under the License.
>>>        <scope>test</scope>
>>>      </dependency>
>>>      <dependency>
>>> -      <groupId>org.apache.curator</groupId>
>>> -      <artifactId>curator-recipes</artifactId>
>>> -    </dependency>
>>> -    <dependency>
>>> -      <groupId>org.apache.curator</groupId>
>>> -      <artifactId>curator-x-discovery</artifactId>
>>> -    </dependency>
>>> -    <dependency>
>>> -      <groupId>org.apache.curator</groupId>
>>> -      <artifactId>curator-test</artifactId>
>>> -    </dependency>
>>> -    <dependency>
>>>        <groupId>org.apache.commons</groupId>
>>>        <artifactId>commons-pool2</artifactId>
>>>      </dependency>
>>>
>>>
>>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java
>>> ----------------------------------------------------------------------
>>> diff --git
>>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java
>>> b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java
>>> deleted file mode 100644
>>> index 476bf6a..0000000
>>> ---
>>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java
>>> +++ /dev/null
>>> @@ -1,163 +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.provider.db.service.persistent;
>>> -
>>> -import java.io.ByteArrayInputStream;
>>> -import java.io.ByteArrayOutputStream;
>>> -import java.io.IOException;
>>> -
>>> -import org.codehaus.jackson.JsonNode;
>>> -import org.codehaus.jackson.JsonParseException;
>>> -import org.codehaus.jackson.map.DeserializationConfig;
>>> -import org.codehaus.jackson.map.JsonMappingException;
>>> -import org.codehaus.jackson.map.ObjectMapper;
>>> -
>>> -import com.google.common.base.Preconditions;
>>> -import org.apache.curator.x.discovery.ServiceInstance;
>>> -import org.apache.curator.x.discovery.ServiceInstanceBuilder;
>>> -import org.apache.curator.x.discovery.ServiceType;
>>> -import org.apache.curator.x.discovery.UriSpec;
>>> -import org.apache.curator.x.discovery.details.InstanceSerializer;
>>> -
>>> -// TODO: Workaround for CURATOR-5 (
>>> https://issues.apache.org/jira/browse/CURATOR-5)
>>> -// Remove this class (code from pull request listed on JIRA) and use
>>> regular JsonInstanceSerializer once fixed
>>> -// (Otherwise we can't properly serialize objects for the ZK Service
>>> Discovery)
>>> -public class FixedJsonInstanceSerializer<T> implements
>>> InstanceSerializer<T>
>>> -{
>>> -
>>> -    private final ObjectMapper mMapper;
>>> -    private final Class<T> mPayloadClass;
>>> -
>>> -    /**
>>> -     * @param payloadClass
>>> -     *            used to validate payloads when deserializing
>>> -     */
>>> -    public FixedJsonInstanceSerializer(final Class<T> payloadClass) {
>>> -        this(payloadClass, new ObjectMapper());
>>> -    }
>>> -
>>> -    public FixedJsonInstanceSerializer(final Class<T> pPayloadClass,
>>> final ObjectMapper pMapper) {
>>> -        mPayloadClass = pPayloadClass;
>>> -        mMapper = pMapper;
>>> -
>>> mMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,
>>> false);
>>> -    }
>>> -
>>> -    @Override
>>> -    public byte[] serialize(final ServiceInstance<T> pInstance) throws
>>> Exception {
>>> -        final ByteArrayOutputStream out = new ByteArrayOutputStream();
>>> -        mMapper.writeValue(out, pInstance);
>>> -        return out.toByteArray();
>>> -
>>> -    }
>>> -
>>> -    private String getTextField(final JsonNode pNode, final String
>>> pFieldName) {
>>> -        Preconditions.checkNotNull(pNode);
>>> -        Preconditions.checkNotNull(pFieldName);
>>> -        return pNode.get(pFieldName) != null ?
>>> pNode.get(pFieldName).getTextValue() : null;
>>> -    }
>>> -
>>> -    private Integer getIntegerField(final JsonNode pNode, final String
>>> pFieldName) {
>>> -        Preconditions.checkNotNull(pNode);
>>> -        Preconditions.checkNotNull(pFieldName);
>>> -        return pNode.get(pFieldName) != null &&
>>> pNode.get(pFieldName).isNumber() ? pNode.get(pFieldName)
>>> -            .getIntValue() : null;
>>> -    }
>>> -
>>> -    private Long getLongField(final JsonNode pNode, final String
>>> pFieldName) {
>>> -        Preconditions.checkNotNull(pNode);
>>> -        Preconditions.checkNotNull(pFieldName);
>>> -        return pNode.get(pFieldName) != null &&
>>> pNode.get(pFieldName).isLong() ? pNode.get(pFieldName).getLongValue()
>>> -            : null;
>>> -    }
>>> -
>>> -    private <O> O getObject(final JsonNode pNode, final String
>>> pFieldName, final Class<O> pObjectClass)
>>> -        throws JsonParseException, JsonMappingException, IOException {
>>> -        Preconditions.checkNotNull(pNode);
>>> -        Preconditions.checkNotNull(pFieldName);
>>> -        Preconditions.checkNotNull(pObjectClass);
>>> -        if (pNode.get(pFieldName) != null &&
>>> pNode.get(pFieldName).isObject()) {
>>> -            return mMapper.readValue(pNode.get(pFieldName),
>>> pObjectClass);
>>> -        } else {
>>> -            return null;
>>> -        }
>>> -    }
>>> -
>>> -    @Override
>>> -    public ServiceInstance<T> deserialize(final byte[] pBytes) throws
>>> Exception {
>>> -        final ByteArrayInputStream bais = new
>>> ByteArrayInputStream(pBytes);
>>> -        final JsonNode rootNode = mMapper.readTree(bais);
>>> -        final ServiceInstanceBuilder<T> builder =
>>> ServiceInstance.builder();
>>> -        {
>>> -            final String address = getTextField(rootNode, "address");
>>> -            if (address != null) {
>>> -                builder.address(address);
>>> -            }
>>> -        }
>>> -        {
>>> -            final String id = getTextField(rootNode, "id");
>>> -            if (id != null) {
>>> -                builder.id(id);
>>> -            }
>>> -        }
>>> -        {
>>> -            final String name = getTextField(rootNode, "name");
>>> -            if (name != null) {
>>> -                builder.name(name);
>>> -            }
>>> -        }
>>> -        {
>>> -            final Integer port = getIntegerField(rootNode, "port");
>>> -            if (port != null) {
>>> -                builder.port(port);
>>> -            }
>>> -        }
>>> -        {
>>> -            final Integer sslPort = getIntegerField(rootNode,
>>> "sslPort");
>>> -            if (sslPort != null) {
>>> -                builder.sslPort(sslPort);
>>> -            }
>>> -        }
>>> -        {
>>> -            final Long registrationTimeUTC = getLongField(rootNode,
>>> "registrationTimeUTC");
>>> -            if (registrationTimeUTC != null) {
>>> -                builder.registrationTimeUTC(registrationTimeUTC);
>>> -            }
>>> -        }
>>> -        {
>>> -            final T payload = getObject(rootNode, "payload",
>>> mPayloadClass);
>>> -            if (payload != null) {
>>> -                builder.payload(payload);
>>> -            }
>>> -        }
>>> -        {
>>> -            final ServiceType serviceType = getObject(rootNode,
>>> "serviceType", ServiceType.class);
>>> -            if (serviceType != null) {
>>> -                builder.serviceType(serviceType);
>>> -            }
>>> -        }
>>> -        {
>>> -            final UriSpec uriSpec = getObject(rootNode, "uriSpec",
>>> UriSpec.class);
>>> -            if (uriSpec != null) {
>>> -                builder.uriSpec(uriSpec);
>>> -            }
>>> -        }
>>> -        return builder.build();
>>> -    }
>>> -
>>> -}
>>>
>>>
>>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java
>>> ----------------------------------------------------------------------
>>> diff --git
>>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java
>>> b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java
>>> deleted file mode 100644
>>> index cacc29f..0000000
>>> ---
>>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java
>>> +++ /dev/null
>>> @@ -1,262 +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.provider.db.service.persistent;
>>> -
>>> -import java.io.IOException;
>>> -import java.util.Arrays;
>>> -import java.util.List;
>>> -
>>> -import org.apache.curator.RetryPolicy;
>>> -import org.apache.curator.framework.CuratorFramework;
>>> -import org.apache.curator.framework.CuratorFrameworkFactory;
>>> -import org.apache.curator.framework.api.ACLProvider;
>>> -import org.apache.curator.framework.imps.CuratorFrameworkState;
>>> -import org.apache.curator.framework.imps.DefaultACLProvider;
>>> -import org.apache.curator.retry.RetryNTimes;
>>> -import org.apache.hadoop.conf.Configuration;
>>> -import org.apache.hadoop.security.SecurityUtil;
>>> -import org.apache.sentry.service.thrift.JaasConfiguration;
>>> -import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
>>> -import org.apache.zookeeper.ZooDefs.Perms;
>>> -import org.apache.zookeeper.client.ZooKeeperSaslClient;
>>> -import org.apache.zookeeper.data.ACL;
>>> -import org.apache.zookeeper.data.Id;
>>> -import org.apache.zookeeper.data.Stat;
>>> -import org.slf4j.Logger;
>>> -import org.slf4j.LoggerFactory;
>>> -
>>> -import com.google.common.annotations.VisibleForTesting;
>>> -import com.google.common.base.Preconditions;
>>> -import com.google.common.base.Strings;
>>> -import com.google.common.collect.Lists;
>>> -
>>> -/**
>>> - * Stores the HA related context
>>> - */
>>> -public class HAContext {
>>> -
>>> -  private static final Logger LOGGER =
>>> LoggerFactory.getLogger(HAContext.class);
>>> -  private static volatile HAContext serverHAContext = null;
>>> -  private static boolean aclChecked = false;
>>> -
>>> -  public final static String SENTRY_SERVICE_REGISTER_NAMESPACE =
>>> "sentry-service";
>>> -  public static final String SENTRY_ZK_JAAS_NAME = "SentryClient";
>>> -  private final String zookeeperQuorum;
>>> -  private final int retriesMaxCount;
>>> -  private final int sleepMsBetweenRetries;
>>> -  private final String namespace;
>>> -
>>> -  private final boolean zkSecure;
>>> -  private List<ACL> saslACL;
>>> -
>>> -  private final CuratorFramework curatorFramework;
>>> -  private final RetryPolicy retryPolicy;
>>> -
>>> -  protected HAContext(Configuration conf) throws Exception {
>>> -    this.zookeeperQuorum =
>>> conf.get(ServerConfig.SENTRY_HA_ZOOKEEPER_QUORUM,
>>> -        ServerConfig.SENTRY_HA_ZOOKEEPER_QUORUM_DEFAULT);
>>> -    this.retriesMaxCount =
>>> conf.getInt(ServerConfig.SENTRY_HA_ZOOKEEPER_RETRIES_MAX_COUNT,
>>> -        ServerConfig.SENTRY_HA_ZOOKEEPER_RETRIES_MAX_COUNT_DEFAULT);
>>> -    this.sleepMsBetweenRetries =
>>> conf.getInt(ServerConfig.SENTRY_HA_ZOOKEEPER_SLEEP_BETWEEN_RETRIES_MS,
>>> -
>>> ServerConfig.SENTRY_HA_ZOOKEEPER_SLEEP_BETWEEN_RETRIES_MS_DEFAULT);
>>> -    this.namespace =
>>> conf.get(ServerConfig.SENTRY_HA_ZOOKEEPER_NAMESPACE,
>>> -        ServerConfig.SENTRY_HA_ZOOKEEPER_NAMESPACE_DEFAULT);
>>> -    this.zkSecure =
>>> conf.getBoolean(ServerConfig.SENTRY_HA_ZOOKEEPER_SECURITY,
>>> -        ServerConfig.SENTRY_HA_ZOOKEEPER_SECURITY_DEFAULT);
>>> -    ACLProvider aclProvider;
>>> -    validateConf();
>>> -    if (zkSecure) {
>>> -      LOGGER.info("Connecting to ZooKeeper with SASL/Kerberos and using
>>> 'sasl' ACLs");
>>> -      setJaasConfiguration(conf);
>>> -      System.setProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY,
>>> -          SENTRY_ZK_JAAS_NAME);
>>> -      saslACL = Lists.newArrayList();
>>> -      saslACL.add(new ACL(Perms.ALL, new Id("sasl",
>>> getServicePrincipal(conf,
>>> -          ServerConfig.PRINCIPAL))));
>>> -      saslACL.add(new ACL(Perms.ALL, new Id("sasl",
>>> getServicePrincipal(conf,
>>> -              ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_PRINCIPAL))));
>>> -      aclProvider = new SASLOwnerACLProvider();
>>> -      String allowConnect = conf.get(ServerConfig.ALLOW_CONNECT);
>>> -
>>> -      if (!Strings.isNullOrEmpty(allowConnect)) {
>>> -        for (String principal :
>>> Arrays.asList(allowConnect.split("\\s*,\\s*"))) {
>>> -          LOGGER.info("Adding acls for " + principal);
>>> -          saslACL.add(new ACL(Perms.ALL, new Id("sasl", principal)));
>>> -        }
>>> -      }
>>> -    } else {
>>> -      LOGGER.info("Connecting to ZooKeeper without authentication");
>>> -      aclProvider = new DefaultACLProvider();
>>> -    }
>>> -
>>> -    retryPolicy = new RetryNTimes(retriesMaxCount,
>>> sleepMsBetweenRetries);
>>> -    this.curatorFramework = CuratorFrameworkFactory.builder()
>>> -        .namespace(this.namespace)
>>> -        .connectString(this.zookeeperQuorum)
>>> -        .retryPolicy(retryPolicy)
>>> -        .aclProvider(aclProvider)
>>> -        .build();
>>> -    startCuratorFramework();
>>> -  }
>>> -
>>> -  /**
>>> -   * Use common HAContext (ie curator framework connection to ZK)
>>> -   *
>>> -   * @param conf
>>> -   * @throws Exception
>>> -   */
>>> -  public static HAContext getHAContext(Configuration conf) throws
>>> Exception {
>>> -    if (serverHAContext == null) {
>>> -      serverHAContext = new HAContext(conf);
>>> -      Runtime.getRuntime().addShutdownHook(new Thread() {
>>> -        @Override
>>> -        public void run() {
>>> -          LOGGER.info("ShutdownHook closing curator framework");
>>> -          try {
>>> -            clearServerContext();
>>> -          } catch (Throwable t) {
>>> -            LOGGER.error("Error stopping SentryService", t);
>>> -          }
>>> -        }
>>> -      });
>>> -
>>> -    }
>>> -    return serverHAContext;
>>> -  }
>>> -
>>> -  // HA context for server which verifies the ZK ACLs on namespace
>>> -  public static HAContext getHAServerContext(Configuration conf) throws
>>> Exception {
>>> -    HAContext serverContext = getHAContext(conf);
>>> -    serverContext.checkAndSetACLs();
>>> -    return serverContext;
>>> -  }
>>> -
>>> -  @VisibleForTesting
>>> -  public static synchronized void clearServerContext() {
>>> -    if (serverHAContext != null) {
>>> -      serverHAContext.getCuratorFramework().close();
>>> -      serverHAContext = null;
>>> -    }
>>> -  }
>>> -
>>> -  public void startCuratorFramework() {
>>> -    if (curatorFramework.getState() != CuratorFrameworkState.STARTED) {
>>> -      curatorFramework.start();
>>> -    }
>>> -  }
>>> -
>>> -  public CuratorFramework getCuratorFramework() {
>>> -    return this.curatorFramework;
>>> -  }
>>> -
>>> -  public String getZookeeperQuorum() {
>>> -    return zookeeperQuorum;
>>> -  }
>>> -
>>> -  public static boolean isHaEnabled(Configuration conf) {
>>> -    return conf.getBoolean(ServerConfig.SENTRY_HA_ENABLED,
>>> ServerConfig.SENTRY_HA_ENABLED_DEFAULT);
>>> -  }
>>> -
>>> -  public String getNamespace() {
>>> -    return namespace;
>>> -  }
>>> -
>>> -  public RetryPolicy getRetryPolicy() {
>>> -    return retryPolicy;
>>> -  }
>>> -
>>> -  private void validateConf() {
>>> -    Preconditions.checkNotNull(zookeeperQuorum, "Zookeeper Quorum
>>> should not be null.");
>>> -    Preconditions.checkNotNull(namespace, "Zookeeper namespace should
>>> not be null.");
>>> -  }
>>> -
>>> -  protected String getServicePrincipal(Configuration conf, String
>>> confProperty)
>>> -      throws IOException {
>>> -    String principal = conf.get(confProperty);
>>> -    Preconditions.checkNotNull(principal);
>>> -    Preconditions.checkArgument(principal.length() != 0, "Server
>>> principal is not right.");
>>> -    return principal.split("[/@]")[0];
>>> -  }
>>> -
>>> -  private void checkAndSetACLs() throws Exception {
>>> -    if (zkSecure && !aclChecked) {
>>> -      // If znodes were previously created without security enabled,
>>> and now it is, we need to go through all existing znodes
>>> -      // and set the ACLs for them. This is done just once at the
>>> startup
>>> -      // We can't get the namespace znode through curator; have to go
>>> through zk client
>>> -      startCuratorFramework();
>>> -      String newNamespace = "/" + curatorFramework.getNamespace();
>>> -      if
>>> (curatorFramework.getZookeeperClient().getZooKeeper().exists(newNamespace,
>>> null) != null) {
>>> -        List<ACL> acls =
>>> curatorFramework.getZookeeperClient().getZooKeeper().getACL(newNamespace,
>>> new Stat());
>>> -        if (acls.isEmpty() ||
>>> !acls.get(0).getId().getScheme().equals("sasl")) {
>>> -          LOGGER.info("'sasl' ACLs not set; setting...");
>>> -          List<String> children =
>>> curatorFramework.getZookeeperClient().getZooKeeper().getChildren(newNamespace,
>>> null);
>>> -          for (String child : children) {
>>> -            checkAndSetACLs("/" + child);
>>> -          }
>>> -
>>> curatorFramework.getZookeeperClient().getZooKeeper().setACL(newNamespace,
>>> saslACL, -1);
>>> -        }
>>> -      }
>>> -      aclChecked = true;
>>> -
>>> -    }
>>> -  }
>>> -
>>> -  private void checkAndSetACLs(String path) throws Exception {
>>> -      LOGGER.info("Setting acls on " + path);
>>> -      List<String> children =
>>> curatorFramework.getChildren().forPath(path);
>>> -      for (String child : children) {
>>> -        checkAndSetACLs(path + "/" + child);
>>> -      }
>>> -      curatorFramework.setACL().withACL(saslACL).forPath(path);
>>> -  }
>>> -
>>> -  // This gets ignored during most tests, see
>>> ZKXTestCaseWithSecurity#setupZKServer()
>>> -  private void setJaasConfiguration(Configuration conf) throws
>>> IOException {
>>> -    if ("false".equalsIgnoreCase(conf.get(
>>> -          ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_TICKET_CACHE,
>>> -
>>> ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_TICKET_CACHE_DEFAULT))) {
>>> -      String keytabFile =
>>> conf.get(ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_KEYTAB);
>>> -      Preconditions.checkArgument(keytabFile.length() != 0, "Keytab
>>> File is not right.");
>>> -      String principal =
>>> conf.get(ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_PRINCIPAL);
>>> -      principal = SecurityUtil.getServerPrincipal(principal,
>>> -        conf.get(ServerConfig.RPC_ADDRESS,
>>> ServerConfig.RPC_ADDRESS_DEFAULT));
>>> -      Preconditions.checkArgument(principal.length() != 0, "Kerberos
>>> principal is not right.");
>>> -
>>> -      // This is equivalent to writing a jaas.conf file and setting the
>>> system property, "java.security.auth.login.config", to
>>> -      // point to it (but this way we don't have to write a file, and
>>> it works better for the tests)
>>> -      JaasConfiguration.addEntryForKeytab(SENTRY_ZK_JAAS_NAME,
>>> principal, keytabFile);
>>> -    } else {
>>> -      // Create jaas conf for ticket cache
>>> -      JaasConfiguration.addEntryForTicketCache(SENTRY_ZK_JAAS_NAME);
>>> -    }
>>> -
>>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance());
>>> -  }
>>> -
>>> -  public class SASLOwnerACLProvider implements ACLProvider {
>>> -    @Override
>>> -    public List<ACL> getDefaultAcl() {
>>> -        return saslACL;
>>> -    }
>>> -
>>> -    @Override
>>> -    public List<ACL> getAclForPath(String path) {
>>> -        return saslACL;
>>> -    }
>>> -  }
>>> -}
>>>
>>>
>>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java
>>> ----------------------------------------------------------------------
>>> diff --git
>>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java
>>> b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java
>>> deleted file mode 100644
>>> index a79ce5f..0000000
>>> ---
>>> a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java
>>> +++ /dev/null
>>> @@ -1,133 +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.service.thrift;
>>> -
>>> -import java.util.HashMap;
>>> -import java.util.Map;
>>> -
>>> -import javax.security.auth.login.AppConfigurationEntry;
>>> -import javax.security.auth.login.Configuration;
>>> -
>>> -/**
>>> - * Creates a programmatic version of a jaas.conf file.  This can be
>>> used instead of writing a jaas.conf file and setting
>>> - * the system property, "java.security.auth.login.config", to point to
>>> that file.  It is meant to be used for connecting to
>>> - * ZooKeeper.
>>> - * <p>
>>> - * example usage:
>>> - * JaasConfiguration.addEntry("Client", principal, keytabFile);
>>> - *
>>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance());
>>> - */
>>> -public final class JaasConfiguration extends Configuration {
>>> -  private static Map<String, AppConfigurationEntry> entries = new
>>> HashMap<String, AppConfigurationEntry>();
>>> -  private static JaasConfiguration me = null;
>>> -  private static final String krb5LoginModuleName;
>>> -
>>> -  static  {
>>> -    if (System.getProperty("java.vendor").contains("IBM")) {
>>> -      krb5LoginModuleName =
>>> "com.ibm.security.auth.module.Krb5LoginModule";
>>> -    }
>>> -    else {
>>> -      krb5LoginModuleName =
>>> "com.sun.security.auth.module.Krb5LoginModule";
>>> -    }
>>> -  }
>>> -
>>> -  private JaasConfiguration() {
>>> -    // don't need to do anything here but we want to make it private
>>> -  }
>>> -
>>> -  /**
>>> -   * Return the singleton.  You'd typically use it only to do this:
>>> -   * <p>
>>> -   *
>>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance());
>>> -   *
>>> -   * @return
>>> -   */
>>> -  public static Configuration getInstance() {
>>> -    if (me == null) {
>>> -      me = new JaasConfiguration();
>>> -    }
>>> -    return me;
>>> -  }
>>> -
>>> -  /**
>>> -   * Add an entry to the jaas configuration with the passed in name,
>>> principal, and keytab.  The other necessary options will be
>>> -   * set for you.
>>> -   *
>>> -   * @param name The name of the entry (e.g. "Client")
>>> -   * @param principal The principal of the user
>>> -   * @param keytab The location of the keytab
>>> -   */
>>> -  public static void addEntryForKeytab(String name, String principal,
>>> String keytab) {
>>> -    Map<String, String> options = new HashMap<String, String>();
>>> -    options.put("keyTab", keytab);
>>> -    options.put("principal", principal);
>>> -    options.put("useKeyTab", "true");
>>> -    options.put("storeKey", "true");
>>> -    options.put("useTicketCache", "false");
>>> -    AppConfigurationEntry entry = new
>>> AppConfigurationEntry(krb5LoginModuleName,
>>> -        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
>>> -    entries.put(name, entry);
>>> -  }
>>> -
>>> -  /**
>>> -   * Add an entry to the jaas configuration with the passed in name.
>>> The other
>>> -   * necessary options will be set for you.
>>> -   *
>>> -   * @param name The name of the entry (e.g. "Client")
>>> -   */
>>> -  public static void addEntryForTicketCache(String sectionName) {
>>> -    Map<String, String> options = new HashMap<String, String>();
>>> -    options.put("useKeyTab", "false");
>>> -    options.put("storeKey", "false");
>>> -    options.put("useTicketCache", "true");
>>> -    AppConfigurationEntry entry = new
>>> AppConfigurationEntry(krb5LoginModuleName,
>>> -        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
>>> -    entries.put(sectionName, entry);
>>> -  }
>>> -
>>> -  /**
>>> -   * Removes the specified entry.
>>> -   *
>>> -   * @param name  The name of the entry to remove
>>> -   */
>>> -  public static void removeEntry(String name) {
>>> -    entries.remove(name);
>>> -  }
>>> -
>>> -  /**
>>> -   * Clears all entries.
>>> -   */
>>> -  public static void clearEntries() {
>>> -    entries.clear();
>>> -  }
>>> -
>>> -  /**
>>> -   * Returns the entries map.
>>> -   *
>>> -   * @return the entries map
>>> -   */
>>> -  public static Map<String, AppConfigurationEntry> getEntries() {
>>> -    return entries;
>>> -  }
>>> -
>>> -  @Override
>>> -  public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
>>> -    return new AppConfigurationEntry[]{entries.get(name)};
>>> -  }
>>> -}
>>> -
>>>
>>>
>>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-service/sentry-service-common/pom.xml
>>> ----------------------------------------------------------------------
>>> diff --git a/sentry-service/sentry-service-common/pom.xml
>>> b/sentry-service/sentry-service-common/pom.xml
>>> index d47cafa..512c341 100644
>>> --- a/sentry-service/sentry-service-common/pom.xml
>>> +++ b/sentry-service/sentry-service-common/pom.xml
>>> @@ -52,6 +52,23 @@ limitations under the License.
>>>        <groupId>org.apache.sentry</groupId>
>>>        <artifactId>sentry-core-common</artifactId>
>>>      </dependency>
>>> +    <dependency>
>>> +      <groupId>org.apache.curator</groupId>
>>> +      <artifactId>curator-recipes</artifactId>
>>> +    </dependency>
>>> +    <dependency>
>>> +      <groupId>org.apache.curator</groupId>
>>> +      <artifactId>curator-x-discovery</artifactId>
>>> +    </dependency>
>>> +    <dependency>
>>> +      <groupId>org.apache.curator</groupId>
>>> +      <artifactId>curator-test</artifactId>
>>> +    </dependency>
>>> +    <dependency>
>>> +      <groupId>org.apache.hadoop</groupId>
>>> +      <artifactId>hadoop-common</artifactId>
>>> +      <scope>provided</scope>
>>> +    </dependency>
>>>    </dependencies>
>>>
>>>    <build>
>>>
>>>
>>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java
>>> ----------------------------------------------------------------------
>>> diff --git
>>> a/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java
>>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java
>>> new file mode 100644
>>> index 0000000..476bf6a
>>> --- /dev/null
>>> +++
>>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/FixedJsonInstanceSerializer.java
>>> @@ -0,0 +1,163 @@
>>> +/**
>>> + * 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.provider.db.service.persistent;
>>> +
>>> +import java.io.ByteArrayInputStream;
>>> +import java.io.ByteArrayOutputStream;
>>> +import java.io.IOException;
>>> +
>>> +import org.codehaus.jackson.JsonNode;
>>> +import org.codehaus.jackson.JsonParseException;
>>> +import org.codehaus.jackson.map.DeserializationConfig;
>>> +import org.codehaus.jackson.map.JsonMappingException;
>>> +import org.codehaus.jackson.map.ObjectMapper;
>>> +
>>> +import com.google.common.base.Preconditions;
>>> +import org.apache.curator.x.discovery.ServiceInstance;
>>> +import org.apache.curator.x.discovery.ServiceInstanceBuilder;
>>> +import org.apache.curator.x.discovery.ServiceType;
>>> +import org.apache.curator.x.discovery.UriSpec;
>>> +import org.apache.curator.x.discovery.details.InstanceSerializer;
>>> +
>>> +// TODO: Workaround for CURATOR-5 (
>>> https://issues.apache.org/jira/browse/CURATOR-5)
>>> +// Remove this class (code from pull request listed on JIRA) and use
>>> regular JsonInstanceSerializer once fixed
>>> +// (Otherwise we can't properly serialize objects for the ZK Service
>>> Discovery)
>>> +public class FixedJsonInstanceSerializer<T> implements
>>> InstanceSerializer<T>
>>> +{
>>> +
>>> +    private final ObjectMapper mMapper;
>>> +    private final Class<T> mPayloadClass;
>>> +
>>> +    /**
>>> +     * @param payloadClass
>>> +     *            used to validate payloads when deserializing
>>> +     */
>>> +    public FixedJsonInstanceSerializer(final Class<T> payloadClass) {
>>> +        this(payloadClass, new ObjectMapper());
>>> +    }
>>> +
>>> +    public FixedJsonInstanceSerializer(final Class<T> pPayloadClass,
>>> final ObjectMapper pMapper) {
>>> +        mPayloadClass = pPayloadClass;
>>> +        mMapper = pMapper;
>>> +
>>> mMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,
>>> false);
>>> +    }
>>> +
>>> +    @Override
>>> +    public byte[] serialize(final ServiceInstance<T> pInstance) throws
>>> Exception {
>>> +        final ByteArrayOutputStream out = new ByteArrayOutputStream();
>>> +        mMapper.writeValue(out, pInstance);
>>> +        return out.toByteArray();
>>> +
>>> +    }
>>> +
>>> +    private String getTextField(final JsonNode pNode, final String
>>> pFieldName) {
>>> +        Preconditions.checkNotNull(pNode);
>>> +        Preconditions.checkNotNull(pFieldName);
>>> +        return pNode.get(pFieldName) != null ?
>>> pNode.get(pFieldName).getTextValue() : null;
>>> +    }
>>> +
>>> +    private Integer getIntegerField(final JsonNode pNode, final String
>>> pFieldName) {
>>> +        Preconditions.checkNotNull(pNode);
>>> +        Preconditions.checkNotNull(pFieldName);
>>> +        return pNode.get(pFieldName) != null &&
>>> pNode.get(pFieldName).isNumber() ? pNode.get(pFieldName)
>>> +            .getIntValue() : null;
>>> +    }
>>> +
>>> +    private Long getLongField(final JsonNode pNode, final String
>>> pFieldName) {
>>> +        Preconditions.checkNotNull(pNode);
>>> +        Preconditions.checkNotNull(pFieldName);
>>> +        return pNode.get(pFieldName) != null &&
>>> pNode.get(pFieldName).isLong() ? pNode.get(pFieldName).getLongValue()
>>> +            : null;
>>> +    }
>>> +
>>> +    private <O> O getObject(final JsonNode pNode, final String
>>> pFieldName, final Class<O> pObjectClass)
>>> +        throws JsonParseException, JsonMappingException, IOException {
>>> +        Preconditions.checkNotNull(pNode);
>>> +        Preconditions.checkNotNull(pFieldName);
>>> +        Preconditions.checkNotNull(pObjectClass);
>>> +        if (pNode.get(pFieldName) != null &&
>>> pNode.get(pFieldName).isObject()) {
>>> +            return mMapper.readValue(pNode.get(pFieldName),
>>> pObjectClass);
>>> +        } else {
>>> +            return null;
>>> +        }
>>> +    }
>>> +
>>> +    @Override
>>> +    public ServiceInstance<T> deserialize(final byte[] pBytes) throws
>>> Exception {
>>> +        final ByteArrayInputStream bais = new
>>> ByteArrayInputStream(pBytes);
>>> +        final JsonNode rootNode = mMapper.readTree(bais);
>>> +        final ServiceInstanceBuilder<T> builder =
>>> ServiceInstance.builder();
>>> +        {
>>> +            final String address = getTextField(rootNode, "address");
>>> +            if (address != null) {
>>> +                builder.address(address);
>>> +            }
>>> +        }
>>> +        {
>>> +            final String id = getTextField(rootNode, "id");
>>> +            if (id != null) {
>>> +                builder.id(id);
>>> +            }
>>> +        }
>>> +        {
>>> +            final String name = getTextField(rootNode, "name");
>>> +            if (name != null) {
>>> +                builder.name(name);
>>> +            }
>>> +        }
>>> +        {
>>> +            final Integer port = getIntegerField(rootNode, "port");
>>> +            if (port != null) {
>>> +                builder.port(port);
>>> +            }
>>> +        }
>>> +        {
>>> +            final Integer sslPort = getIntegerField(rootNode,
>>> "sslPort");
>>> +            if (sslPort != null) {
>>> +                builder.sslPort(sslPort);
>>> +            }
>>> +        }
>>> +        {
>>> +            final Long registrationTimeUTC = getLongField(rootNode,
>>> "registrationTimeUTC");
>>> +            if (registrationTimeUTC != null) {
>>> +                builder.registrationTimeUTC(registrationTimeUTC);
>>> +            }
>>> +        }
>>> +        {
>>> +            final T payload = getObject(rootNode, "payload",
>>> mPayloadClass);
>>> +            if (payload != null) {
>>> +                builder.payload(payload);
>>> +            }
>>> +        }
>>> +        {
>>> +            final ServiceType serviceType = getObject(rootNode,
>>> "serviceType", ServiceType.class);
>>> +            if (serviceType != null) {
>>> +                builder.serviceType(serviceType);
>>> +            }
>>> +        }
>>> +        {
>>> +            final UriSpec uriSpec = getObject(rootNode, "uriSpec",
>>> UriSpec.class);
>>> +            if (uriSpec != null) {
>>> +                builder.uriSpec(uriSpec);
>>> +            }
>>> +        }
>>> +        return builder.build();
>>> +    }
>>> +
>>> +}
>>>
>>>
>>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java
>>> ----------------------------------------------------------------------
>>> diff --git
>>> a/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java
>>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java
>>> new file mode 100644
>>> index 0000000..cacc29f
>>> --- /dev/null
>>> +++
>>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/provider/db/service/persistent/HAContext.java
>>> @@ -0,0 +1,262 @@
>>> +/**
>>> + * 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.provider.db.service.persistent;
>>> +
>>> +import java.io.IOException;
>>> +import java.util.Arrays;
>>> +import java.util.List;
>>> +
>>> +import org.apache.curator.RetryPolicy;
>>> +import org.apache.curator.framework.CuratorFramework;
>>> +import org.apache.curator.framework.CuratorFrameworkFactory;
>>> +import org.apache.curator.framework.api.ACLProvider;
>>> +import org.apache.curator.framework.imps.CuratorFrameworkState;
>>> +import org.apache.curator.framework.imps.DefaultACLProvider;
>>> +import org.apache.curator.retry.RetryNTimes;
>>> +import org.apache.hadoop.conf.Configuration;
>>> +import org.apache.hadoop.security.SecurityUtil;
>>> +import org.apache.sentry.service.thrift.JaasConfiguration;
>>> +import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
>>> +import org.apache.zookeeper.ZooDefs.Perms;
>>> +import org.apache.zookeeper.client.ZooKeeperSaslClient;
>>> +import org.apache.zookeeper.data.ACL;
>>> +import org.apache.zookeeper.data.Id;
>>> +import org.apache.zookeeper.data.Stat;
>>> +import org.slf4j.Logger;
>>> +import org.slf4j.LoggerFactory;
>>> +
>>> +import com.google.common.annotations.VisibleForTesting;
>>> +import com.google.common.base.Preconditions;
>>> +import com.google.common.base.Strings;
>>> +import com.google.common.collect.Lists;
>>> +
>>> +/**
>>> + * Stores the HA related context
>>> + */
>>> +public class HAContext {
>>> +
>>> +  private static final Logger LOGGER =
>>> LoggerFactory.getLogger(HAContext.class);
>>> +  private static volatile HAContext serverHAContext = null;
>>> +  private static boolean aclChecked = false;
>>> +
>>> +  public final static String SENTRY_SERVICE_REGISTER_NAMESPACE =
>>> "sentry-service";
>>> +  public static final String SENTRY_ZK_JAAS_NAME = "SentryClient";
>>> +  private final String zookeeperQuorum;
>>> +  private final int retriesMaxCount;
>>> +  private final int sleepMsBetweenRetries;
>>> +  private final String namespace;
>>> +
>>> +  private final boolean zkSecure;
>>> +  private List<ACL> saslACL;
>>> +
>>> +  private final CuratorFramework curatorFramework;
>>> +  private final RetryPolicy retryPolicy;
>>> +
>>> +  protected HAContext(Configuration conf) throws Exception {
>>> +    this.zookeeperQuorum =
>>> conf.get(ServerConfig.SENTRY_HA_ZOOKEEPER_QUORUM,
>>> +        ServerConfig.SENTRY_HA_ZOOKEEPER_QUORUM_DEFAULT);
>>> +    this.retriesMaxCount =
>>> conf.getInt(ServerConfig.SENTRY_HA_ZOOKEEPER_RETRIES_MAX_COUNT,
>>> +        ServerConfig.SENTRY_HA_ZOOKEEPER_RETRIES_MAX_COUNT_DEFAULT);
>>> +    this.sleepMsBetweenRetries =
>>> conf.getInt(ServerConfig.SENTRY_HA_ZOOKEEPER_SLEEP_BETWEEN_RETRIES_MS,
>>> +
>>> ServerConfig.SENTRY_HA_ZOOKEEPER_SLEEP_BETWEEN_RETRIES_MS_DEFAULT);
>>> +    this.namespace =
>>> conf.get(ServerConfig.SENTRY_HA_ZOOKEEPER_NAMESPACE,
>>> +        ServerConfig.SENTRY_HA_ZOOKEEPER_NAMESPACE_DEFAULT);
>>> +    this.zkSecure =
>>> conf.getBoolean(ServerConfig.SENTRY_HA_ZOOKEEPER_SECURITY,
>>> +        ServerConfig.SENTRY_HA_ZOOKEEPER_SECURITY_DEFAULT);
>>> +    ACLProvider aclProvider;
>>> +    validateConf();
>>> +    if (zkSecure) {
>>> +      LOGGER.info("Connecting to ZooKeeper with SASL/Kerberos and using
>>> 'sasl' ACLs");
>>> +      setJaasConfiguration(conf);
>>> +      System.setProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY,
>>> +          SENTRY_ZK_JAAS_NAME);
>>> +      saslACL = Lists.newArrayList();
>>> +      saslACL.add(new ACL(Perms.ALL, new Id("sasl",
>>> getServicePrincipal(conf,
>>> +          ServerConfig.PRINCIPAL))));
>>> +      saslACL.add(new ACL(Perms.ALL, new Id("sasl",
>>> getServicePrincipal(conf,
>>> +              ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_PRINCIPAL))));
>>> +      aclProvider = new SASLOwnerACLProvider();
>>> +      String allowConnect = conf.get(ServerConfig.ALLOW_CONNECT);
>>> +
>>> +      if (!Strings.isNullOrEmpty(allowConnect)) {
>>> +        for (String principal :
>>> Arrays.asList(allowConnect.split("\\s*,\\s*"))) {
>>> +          LOGGER.info("Adding acls for " + principal);
>>> +          saslACL.add(new ACL(Perms.ALL, new Id("sasl", principal)));
>>> +        }
>>> +      }
>>> +    } else {
>>> +      LOGGER.info("Connecting to ZooKeeper without authentication");
>>> +      aclProvider = new DefaultACLProvider();
>>> +    }
>>> +
>>> +    retryPolicy = new RetryNTimes(retriesMaxCount,
>>> sleepMsBetweenRetries);
>>> +    this.curatorFramework = CuratorFrameworkFactory.builder()
>>> +        .namespace(this.namespace)
>>> +        .connectString(this.zookeeperQuorum)
>>> +        .retryPolicy(retryPolicy)
>>> +        .aclProvider(aclProvider)
>>> +        .build();
>>> +    startCuratorFramework();
>>> +  }
>>> +
>>> +  /**
>>> +   * Use common HAContext (ie curator framework connection to ZK)
>>> +   *
>>> +   * @param conf
>>> +   * @throws Exception
>>> +   */
>>> +  public static HAContext getHAContext(Configuration conf) throws
>>> Exception {
>>> +    if (serverHAContext == null) {
>>> +      serverHAContext = new HAContext(conf);
>>> +      Runtime.getRuntime().addShutdownHook(new Thread() {
>>> +        @Override
>>> +        public void run() {
>>> +          LOGGER.info("ShutdownHook closing curator framework");
>>> +          try {
>>> +            clearServerContext();
>>> +          } catch (Throwable t) {
>>> +            LOGGER.error("Error stopping SentryService", t);
>>> +          }
>>> +        }
>>> +      });
>>> +
>>> +    }
>>> +    return serverHAContext;
>>> +  }
>>> +
>>> +  // HA context for server which verifies the ZK ACLs on namespace
>>> +  public static HAContext getHAServerContext(Configuration conf) throws
>>> Exception {
>>> +    HAContext serverContext = getHAContext(conf);
>>> +    serverContext.checkAndSetACLs();
>>> +    return serverContext;
>>> +  }
>>> +
>>> +  @VisibleForTesting
>>> +  public static synchronized void clearServerContext() {
>>> +    if (serverHAContext != null) {
>>> +      serverHAContext.getCuratorFramework().close();
>>> +      serverHAContext = null;
>>> +    }
>>> +  }
>>> +
>>> +  public void startCuratorFramework() {
>>> +    if (curatorFramework.getState() != CuratorFrameworkState.STARTED) {
>>> +      curatorFramework.start();
>>> +    }
>>> +  }
>>> +
>>> +  public CuratorFramework getCuratorFramework() {
>>> +    return this.curatorFramework;
>>> +  }
>>> +
>>> +  public String getZookeeperQuorum() {
>>> +    return zookeeperQuorum;
>>> +  }
>>> +
>>> +  public static boolean isHaEnabled(Configuration conf) {
>>> +    return conf.getBoolean(ServerConfig.SENTRY_HA_ENABLED,
>>> ServerConfig.SENTRY_HA_ENABLED_DEFAULT);
>>> +  }
>>> +
>>> +  public String getNamespace() {
>>> +    return namespace;
>>> +  }
>>> +
>>> +  public RetryPolicy getRetryPolicy() {
>>> +    return retryPolicy;
>>> +  }
>>> +
>>> +  private void validateConf() {
>>> +    Preconditions.checkNotNull(zookeeperQuorum, "Zookeeper Quorum
>>> should not be null.");
>>> +    Preconditions.checkNotNull(namespace, "Zookeeper namespace should
>>> not be null.");
>>> +  }
>>> +
>>> +  protected String getServicePrincipal(Configuration conf, String
>>> confProperty)
>>> +      throws IOException {
>>> +    String principal = conf.get(confProperty);
>>> +    Preconditions.checkNotNull(principal);
>>> +    Preconditions.checkArgument(principal.length() != 0, "Server
>>> principal is not right.");
>>> +    return principal.split("[/@]")[0];
>>> +  }
>>> +
>>> +  private void checkAndSetACLs() throws Exception {
>>> +    if (zkSecure && !aclChecked) {
>>> +      // If znodes were previously created without security enabled,
>>> and now it is, we need to go through all existing znodes
>>> +      // and set the ACLs for them. This is done just once at the
>>> startup
>>> +      // We can't get the namespace znode through curator; have to go
>>> through zk client
>>> +      startCuratorFramework();
>>> +      String newNamespace = "/" + curatorFramework.getNamespace();
>>> +      if
>>> (curatorFramework.getZookeeperClient().getZooKeeper().exists(newNamespace,
>>> null) != null) {
>>> +        List<ACL> acls =
>>> curatorFramework.getZookeeperClient().getZooKeeper().getACL(newNamespace,
>>> new Stat());
>>> +        if (acls.isEmpty() ||
>>> !acls.get(0).getId().getScheme().equals("sasl")) {
>>> +          LOGGER.info("'sasl' ACLs not set; setting...");
>>> +          List<String> children =
>>> curatorFramework.getZookeeperClient().getZooKeeper().getChildren(newNamespace,
>>> null);
>>> +          for (String child : children) {
>>> +            checkAndSetACLs("/" + child);
>>> +          }
>>> +
>>> curatorFramework.getZookeeperClient().getZooKeeper().setACL(newNamespace,
>>> saslACL, -1);
>>> +        }
>>> +      }
>>> +      aclChecked = true;
>>> +
>>> +    }
>>> +  }
>>> +
>>> +  private void checkAndSetACLs(String path) throws Exception {
>>> +      LOGGER.info("Setting acls on " + path);
>>> +      List<String> children =
>>> curatorFramework.getChildren().forPath(path);
>>> +      for (String child : children) {
>>> +        checkAndSetACLs(path + "/" + child);
>>> +      }
>>> +      curatorFramework.setACL().withACL(saslACL).forPath(path);
>>> +  }
>>> +
>>> +  // This gets ignored during most tests, see
>>> ZKXTestCaseWithSecurity#setupZKServer()
>>> +  private void setJaasConfiguration(Configuration conf) throws
>>> IOException {
>>> +    if ("false".equalsIgnoreCase(conf.get(
>>> +          ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_TICKET_CACHE,
>>> +
>>> ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_TICKET_CACHE_DEFAULT))) {
>>> +      String keytabFile =
>>> conf.get(ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_KEYTAB);
>>> +      Preconditions.checkArgument(keytabFile.length() != 0, "Keytab
>>> File is not right.");
>>> +      String principal =
>>> conf.get(ServerConfig.SERVER_HA_ZOOKEEPER_CLIENT_PRINCIPAL);
>>> +      principal = SecurityUtil.getServerPrincipal(principal,
>>> +        conf.get(ServerConfig.RPC_ADDRESS,
>>> ServerConfig.RPC_ADDRESS_DEFAULT));
>>> +      Preconditions.checkArgument(principal.length() != 0, "Kerberos
>>> principal is not right.");
>>> +
>>> +      // This is equivalent to writing a jaas.conf file and setting the
>>> system property, "java.security.auth.login.config", to
>>> +      // point to it (but this way we don't have to write a file, and
>>> it works better for the tests)
>>> +      JaasConfiguration.addEntryForKeytab(SENTRY_ZK_JAAS_NAME,
>>> principal, keytabFile);
>>> +    } else {
>>> +      // Create jaas conf for ticket cache
>>> +      JaasConfiguration.addEntryForTicketCache(SENTRY_ZK_JAAS_NAME);
>>> +    }
>>> +
>>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance());
>>> +  }
>>> +
>>> +  public class SASLOwnerACLProvider implements ACLProvider {
>>> +    @Override
>>> +    public List<ACL> getDefaultAcl() {
>>> +        return saslACL;
>>> +    }
>>> +
>>> +    @Override
>>> +    public List<ACL> getAclForPath(String path) {
>>> +        return saslACL;
>>> +    }
>>> +  }
>>> +}
>>>
>>>
>>> http://git-wip-us.apache.org/repos/asf/sentry/blob/4767ec38/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java
>>> ----------------------------------------------------------------------
>>> diff --git
>>> a/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java
>>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java
>>> new file mode 100644
>>> index 0000000..a79ce5f
>>> --- /dev/null
>>> +++
>>> b/sentry-service/sentry-service-common/src/main/java/org/apache/sentry/service/thrift/JaasConfiguration.java
>>> @@ -0,0 +1,133 @@
>>> +/**
>>> + * 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 java.util.HashMap;
>>> +import java.util.Map;
>>> +
>>> +import javax.security.auth.login.AppConfigurationEntry;
>>> +import javax.security.auth.login.Configuration;
>>> +
>>> +/**
>>> + * Creates a programmatic version of a jaas.conf file.  This can be
>>> used instead of writing a jaas.conf file and setting
>>> + * the system property, "java.security.auth.login.config", to point to
>>> that file.  It is meant to be used for connecting to
>>> + * ZooKeeper.
>>> + * <p>
>>> + * example usage:
>>> + * JaasConfiguration.addEntry("Client", principal, keytabFile);
>>> + *
>>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance());
>>> + */
>>> +public final class JaasConfiguration extends Configuration {
>>> +  private static Map<String, AppConfigurationEntry> entries = new
>>> HashMap<String, AppConfigurationEntry>();
>>> +  private static JaasConfiguration me = null;
>>> +  private static final String krb5LoginModuleName;
>>> +
>>> +  static  {
>>> +    if (System.getProperty("java.vendor").contains("IBM")) {
>>> +      krb5LoginModuleName =
>>> "com.ibm.security.auth.module.Krb5LoginModule";
>>> +    }
>>> +    else {
>>> +      krb5LoginModuleName =
>>> "com.sun.security.auth.module.Krb5LoginModule";
>>> +    }
>>> +  }
>>> +
>>> +  private JaasConfiguration() {
>>> +    // don't need to do anything here but we want to make it private
>>> +  }
>>> +
>>> +  /**
>>> +   * Return the singleton.  You'd typically use it only to do this:
>>> +   * <p>
>>> +   *
>>> javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance());
>>> +   *
>>> +   * @return
>>> +   */
>>> +  public static Configuration getInstance() {
>>> +    if (me == null) {
>>> +      me = new JaasConfiguration();
>>> +    }
>>> +    return me;
>>> +  }
>>> +
>>> +  /**
>>> +   * Add an entry to the jaas configuration with the passed in name,
>>> principal, and keytab.  The other necessary options will be
>>> +   * set for you.
>>> +   *
>>> +   * @param name The name of the entry (e.g. "Client")
>>> +   * @param principal The principal of the user
>>> +   * @param keytab The location of the keytab
>>> +   */
>>> +  public static void addEntryForKeytab(String name, String principal,
>>> String keytab) {
>>> +    Map<String, String> options = new HashMap<String, String>();
>>> +    options.put("keyTab", keytab);
>>> +    options.put("principal", principal);
>>> +    options.put("useKeyTab", "true");
>>> +    options.put("storeKey", "true");
>>> +    options.put("useTicketCache", "false");
>>> +    AppConfigurationEntry entry = new
>>> AppConfigurationEntry(krb5LoginModuleName,
>>> +        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
>>> +    entries.put(name, entry);
>>> +  }
>>> +
>>> +  /**
>>> +   * Add an entry to the jaas configuration with the passed in name.
>>> The other
>>> +   * necessary options will be set for you.
>>> +   *
>>> +   * @param name The name of the entry (e.g. "Client")
>>> +   */
>>> +  public static void addEntryForTicketCache(String sectionName) {
>>> +    Map<String, String> options = new HashMap<String, String>();
>>> +    options.put("useKeyTab", "false");
>>> +    options.put("storeKey", "false");
>>> +    options.put("useTicketCache", "true");
>>> +    AppConfigurationEntry entry = new
>>> AppConfigurationEntry(krb5LoginModuleName,
>>> +        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
>>> +    entries.put(sectionName, entry);
>>> +  }
>>> +
>>> +  /**
>>> +   * Removes the specified entry.
>>> +   *
>>> +   * @param name  The name of the entry to remove
>>> +   */
>>> +  public static void removeEntry(String name) {
>>> +    entries.remove(name);
>>> +  }
>>> +
>>> +  /**
>>> +   * Clears all entries.
>>> +   */
>>> +  public static void clearEntries() {
>>> +    entries.clear();
>>> +  }
>>> +
>>> +  /**
>>> +   * Returns the entries map.
>>> +   *
>>> +   * @return the entries map
>>> +   */
>>> +  public static Map<String, AppConfigurationEntry> getEntries() {
>>> +    return entries;
>>> +  }
>>> +
>>> +  @Override
>>> +  public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
>>> +    return new AppConfigurationEntry[]{entries.get(name)};
>>> +  }
>>> +}
>>> +
>>>
>>>
>
>
> --
> Celebrating a decade of community accomplishments
> cloudera.com/hadoop10
> #hadoop10
>



-- 
Anne

Reply via email to