HADOOP-13103 Group resolution from LDAP may fail on javax.naming.ServiceUnavailableException

This commit is contained in:
Tsz-Wo Nicholas Sze 2016-05-05 15:50:50 -07:00
parent 4311e5f353
commit 2340e29084
3 changed files with 22 additions and 30 deletions

View File

@ -18,15 +18,14 @@
package org.apache.hadoop.security; package org.apache.hadoop.security;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List; import java.util.List;
import javax.naming.CommunicationException;
import javax.naming.Context; import javax.naming.Context;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
import javax.naming.NamingException; import javax.naming.NamingException;
@ -43,7 +42,6 @@ import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configurable; import org.apache.hadoop.conf.Configurable;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.IOUtils;
/** /**
* An implementation of {@link GroupMappingServiceProvider} which * An implementation of {@link GroupMappingServiceProvider} which
@ -179,7 +177,7 @@ public class LdapGroupsMapping
private String groupMemberAttr; private String groupMemberAttr;
private String groupNameAttr; private String groupNameAttr;
public static int RECONNECT_RETRY_COUNT = 3; public static final int RECONNECT_RETRY_COUNT = 3;
/** /**
* Returns list of groups for a user. * Returns list of groups for a user.
@ -192,40 +190,26 @@ public class LdapGroupsMapping
* @return list of groups for a given user * @return list of groups for a given user
*/ */
@Override @Override
public synchronized List<String> getGroups(String user) throws IOException { public synchronized List<String> getGroups(String user) {
List<String> emptyResults = new ArrayList<String>();
/* /*
* Normal garbage collection takes care of removing Context instances when they are no longer in use. * Normal garbage collection takes care of removing Context instances when they are no longer in use.
* Connections used by Context instances being garbage collected will be closed automatically. * Connections used by Context instances being garbage collected will be closed automatically.
* So in case connection is closed and gets CommunicationException, retry some times with new new DirContext/connection. * So in case connection is closed and gets CommunicationException, retry some times with new new DirContext/connection.
*/ */
try { for(int retry = 0; retry < RECONNECT_RETRY_COUNT; retry++) {
return doGetGroups(user);
} catch (CommunicationException e) {
LOG.warn("Connection is closed, will try to reconnect");
} catch (NamingException e) {
LOG.warn("Exception trying to get groups for user " + user + ": "
+ e.getMessage());
return emptyResults;
}
int retryCount = 0;
while (retryCount ++ < RECONNECT_RETRY_COUNT) {
//reset ctx so that new DirContext can be created with new connection
this.ctx = null;
try { try {
return doGetGroups(user); return doGetGroups(user);
} catch (CommunicationException e) {
LOG.warn("Connection being closed, reconnecting failed, retryCount = " + retryCount);
} catch (NamingException e) { } catch (NamingException e) {
LOG.warn("Exception trying to get groups for user " + user + ":" LOG.warn("Failed to get groups for user " + user + " (retry=" + retry
+ e.getMessage()); + ") by " + e);
return emptyResults; LOG.trace("TRACE", e);
} }
//reset ctx so that new DirContext can be created with new connection
this.ctx = null;
} }
return emptyResults; return Collections.emptyList();
} }
List<String> doGetGroups(String user) throws NamingException { List<String> doGetGroups(String user) throws NamingException {
@ -254,6 +238,9 @@ public class LdapGroupsMapping
} }
} }
if (LOG.isDebugEnabled()) {
LOG.debug("doGetGroups(" + user + ") return " + groups);
}
return groups; return groups;
} }

View File

@ -68,6 +68,7 @@ import org.apache.hadoop.security.authentication.util.KerberosUtil;
import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.security.token.TokenIdentifier;
import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.Shell;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.util.Time; import org.apache.hadoop.util.Time;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
@ -1554,8 +1555,12 @@ public class UserGroupInformation {
(groups.getGroups(getShortUserName())); (groups.getGroups(getShortUserName()));
return result.toArray(new String[result.size()]); return result.toArray(new String[result.size()]);
} catch (IOException ie) { } catch (IOException ie) {
LOG.warn("No groups available for user " + getShortUserName()); if (LOG.isDebugEnabled()) {
return new String[0]; LOG.debug("Failed to get groups for user " + getShortUserName()
+ " by " + ie);
LOG.trace("TRACE", ie);
}
return StringUtils.emptyStringArray;
} }
} }

View File

@ -124,7 +124,7 @@ public class TestLdapGroupsMapping {
// Ldap server is down, no groups should be retrieved // Ldap server is down, no groups should be retrieved
doTestGetGroups(Arrays.asList(new String[] {}), doTestGetGroups(Arrays.asList(new String[] {}),
1 + LdapGroupsMapping.RECONNECT_RETRY_COUNT); // 1 is the first normal call LdapGroupsMapping.RECONNECT_RETRY_COUNT);
} }
private void doTestGetGroups(List<String> expectedGroups, int searchTimes) throws IOException, NamingException { private void doTestGetGroups(List<String> expectedGroups, int searchTimes) throws IOException, NamingException {