HDFS-11403. Zookeper ACLs on NN HA enabled clusters should be handled consistently. Contributed by Hanisha Koneru.

This commit is contained in:
Arpit Agarwal 2017-02-11 01:17:56 -08:00
parent 33c62d2d19
commit 32a0c2b5e7
2 changed files with 65 additions and 3 deletions

View File

@ -346,8 +346,13 @@ public class ActiveStandbyElector implements StatCallback, StringCallback {
createWithRetries(prefixPath, new byte[]{}, zkAcl, CreateMode.PERSISTENT); createWithRetries(prefixPath, new byte[]{}, zkAcl, CreateMode.PERSISTENT);
} catch (KeeperException e) { } catch (KeeperException e) {
if (isNodeExists(e.code())) { if (isNodeExists(e.code())) {
// This is OK - just ensuring existence. // Set ACLs for parent node, if they do not exist or are different
continue; try {
setAclsWithRetries(prefixPath);
} catch (KeeperException e1) {
throw new IOException("Couldn't set ACLs on parent ZNode: " +
prefixPath, e1);
}
} else { } else {
throw new IOException("Couldn't create " + prefixPath, e); throw new IOException("Couldn't create " + prefixPath, e);
} }
@ -1066,14 +1071,36 @@ public class ActiveStandbyElector implements StatCallback, StringCallback {
}); });
} }
private void setAclsWithRetries(final String path)
throws KeeperException, InterruptedException {
Stat stat = new Stat();
zkDoWithRetries(new ZKAction<Void>() {
@Override
public Void run() throws KeeperException, InterruptedException {
List<ACL> acl = zkClient.getACL(path, stat);
if (acl == null || !acl.containsAll(zkAcl) ||
!zkAcl.containsAll(acl)) {
zkClient.setACL(path, zkAcl, stat.getVersion());
}
return null;
}
}, Code.BADVERSION);
}
private <T> T zkDoWithRetries(ZKAction<T> action) throws KeeperException, private <T> T zkDoWithRetries(ZKAction<T> action) throws KeeperException,
InterruptedException { InterruptedException {
return zkDoWithRetries(action, null);
}
private <T> T zkDoWithRetries(ZKAction<T> action, Code retryCode)
throws KeeperException, InterruptedException {
int retry = 0; int retry = 0;
while (true) { while (true) {
try { try {
return action.run(); return action.run();
} catch (KeeperException ke) { } catch (KeeperException ke) {
if (shouldRetry(ke.code()) && ++retry < maxRetryNum) { if ((shouldRetry(ke.code()) || shouldRetry(ke.code(), retryCode))
&& ++retry < maxRetryNum) {
continue; continue;
} }
throw ke; throw ke;
@ -1189,6 +1216,10 @@ public class ActiveStandbyElector implements StatCallback, StringCallback {
private static boolean shouldRetry(Code code) { private static boolean shouldRetry(Code code) {
return code == Code.CONNECTIONLOSS || code == Code.OPERATIONTIMEOUT; return code == Code.CONNECTIONLOSS || code == Code.OPERATIONTIMEOUT;
} }
private static boolean shouldRetry(Code code, Code retryIfCode) {
return (retryIfCode == null ? false : retryIfCode == code);
}
@Override @Override
public String toString() { public String toString() {

View File

@ -715,6 +715,37 @@ public class TestActiveStandbyElector {
} }
} }
} }
/**
* Test that ACLs are set on parent zNode even if the node already exists.
*/
@Test
public void testParentZNodeACLs() throws Exception {
KeeperException ke = new KeeperException(Code.NODEEXISTS) {
@Override
public Code code() {
return super.code();
}
};
Mockito.when(mockZK.create(Mockito.anyString(), Mockito.eq(new byte[]{}),
Mockito.anyListOf(ACL.class),
Mockito.eq(CreateMode.PERSISTENT))).thenThrow(ke);
elector.ensureParentZNode();
StringBuilder prefix = new StringBuilder();
for (String part : ZK_PARENT_NAME.split("/")) {
if (part.isEmpty()) continue;
prefix.append("/").append(part);
if (!"/".equals(prefix.toString())) {
Mockito.verify(mockZK).getACL(Mockito.eq(prefix.toString()),
Mockito.eq(new Stat()));
Mockito.verify(mockZK).setACL(Mockito.eq(prefix.toString()),
Mockito.eq(Ids.OPEN_ACL_UNSAFE), Mockito.anyInt());
}
}
}
/** /**
* Test for a bug encountered during development of HADOOP-8163: * Test for a bug encountered during development of HADOOP-8163: