HADOOP-9574. Added new methods in AbstractDelegationTokenSecretManager for helping YARN ResourceManager to reuse code for RM restart. Contributed by Jian He.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1487692 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Vinod Kumar Vavilapalli 2013-05-30 00:52:53 +00:00
parent 5420f287cc
commit fdfedf4c31
4 changed files with 161 additions and 10 deletions

View File

@ -447,6 +447,10 @@ Release 2.0.5-beta - UNRELEASED
HADOOP-9218 Document the Rpc-wrappers used internally (sanjay Radia) HADOOP-9218 Document the Rpc-wrappers used internally (sanjay Radia)
HADOOP-9574. Added new methods in AbstractDelegationTokenSecretManager for
helping YARN ResourceManager to reuse code for RM restart. (Jian He via
vinodkv)
OPTIMIZATIONS OPTIMIZATIONS
HADOOP-9150. Avoid unnecessary DNS resolution attempts for logical URIs HADOOP-9150. Avoid unnecessary DNS resolution attempts for logical URIs

View File

@ -141,15 +141,73 @@ extends AbstractDelegationTokenIdentifier>
public synchronized DelegationKey[] getAllKeys() { public synchronized DelegationKey[] getAllKeys() {
return allKeys.values().toArray(new DelegationKey[0]); return allKeys.values().toArray(new DelegationKey[0]);
} }
// HDFS
protected void logUpdateMasterKey(DelegationKey key) throws IOException { protected void logUpdateMasterKey(DelegationKey key) throws IOException {
return; return;
} }
// HDFS
protected void logExpireToken(TokenIdent ident) throws IOException { protected void logExpireToken(TokenIdent ident) throws IOException {
return; return;
} }
// RM
protected void storeNewMasterKey(DelegationKey key) throws IOException {
return;
}
// RM
protected void removeStoredMasterKey(DelegationKey key) {
return;
}
// RM
protected void storeNewToken(TokenIdent ident, long renewDate) {
return;
}
// RM
protected void removeStoredToken(TokenIdent ident) throws IOException {
}
// RM
protected void updateStoredToken(TokenIdent ident, long renewDate) {
return;
}
/**
* This method is intended to be used for recovering persisted delegation
* tokens
* @param identifier identifier read from persistent storage
* @param renewDate token renew time
* @throws IOException
*/
public synchronized void addPersistedDelegationToken(
TokenIdent identifier, long renewDate) throws IOException {
if (running) {
// a safety check
throw new IOException(
"Can't add persisted delegation token to a running SecretManager.");
}
int keyId = identifier.getMasterKeyId();
DelegationKey dKey = allKeys.get(keyId);
if (dKey == null) {
LOG.warn("No KEY found for persisted identifier " + identifier.toString());
return;
}
byte[] password = createPassword(identifier.getBytes(), dKey.getKey());
if (identifier.getSequenceNumber() > this.delegationTokenSequenceNumber) {
this.delegationTokenSequenceNumber = identifier.getSequenceNumber();
}
if (currentTokens.get(identifier) == null) {
currentTokens.put(identifier, new DelegationTokenInformation(renewDate,
password));
} else {
throw new IOException(
"Same delegation token being added twice.");
}
}
/** /**
* Update the current master key * Update the current master key
* This is called once by startThreads before tokenRemoverThread is created, * This is called once by startThreads before tokenRemoverThread is created,
@ -167,6 +225,7 @@ extends AbstractDelegationTokenIdentifier>
+ keyUpdateInterval + tokenMaxLifetime, generateSecret()); + keyUpdateInterval + tokenMaxLifetime, generateSecret());
//Log must be invoked outside the lock on 'this' //Log must be invoked outside the lock on 'this'
logUpdateMasterKey(newKey); logUpdateMasterKey(newKey);
storeNewMasterKey(newKey);
synchronized (this) { synchronized (this) {
currentId = newKey.getKeyId(); currentId = newKey.getKeyId();
currentKey = newKey; currentKey = newKey;
@ -200,6 +259,10 @@ extends AbstractDelegationTokenIdentifier>
Map.Entry<Integer, DelegationKey> e = it.next(); Map.Entry<Integer, DelegationKey> e = it.next();
if (e.getValue().getExpiryDate() < now) { if (e.getValue().getExpiryDate() < now) {
it.remove(); it.remove();
// ensure the tokens generated by this current key can be recovered
// with this current key after this current key is rolled
if(!e.getValue().equals(currentKey))
removeStoredMasterKey(e.getValue());
} }
} }
} }
@ -215,6 +278,7 @@ extends AbstractDelegationTokenIdentifier>
identifier.setSequenceNumber(sequenceNum); identifier.setSequenceNumber(sequenceNum);
LOG.info("Creating password for identifier: " + identifier); LOG.info("Creating password for identifier: " + identifier);
byte[] password = createPassword(identifier.getBytes(), currentKey.getKey()); byte[] password = createPassword(identifier.getBytes(), currentKey.getKey());
storeNewToken(identifier, now + tokenRenewInterval);
currentTokens.put(identifier, new DelegationTokenInformation(now currentTokens.put(identifier, new DelegationTokenInformation(now
+ tokenRenewInterval, password)); + tokenRenewInterval, password));
return password; return password;
@ -302,6 +366,7 @@ extends AbstractDelegationTokenIdentifier>
throw new InvalidToken("Renewal request for unknown token"); throw new InvalidToken("Renewal request for unknown token");
} }
currentTokens.put(id, info); currentTokens.put(id, info);
updateStoredToken(id, renewTime);
return renewTime; return renewTime;
} }
@ -337,6 +402,7 @@ extends AbstractDelegationTokenIdentifier>
if (info == null) { if (info == null) {
throw new InvalidToken("Token not found"); throw new InvalidToken("Token not found");
} }
removeStoredToken(id);
return id; return id;
} }
@ -387,6 +453,7 @@ extends AbstractDelegationTokenIdentifier>
// don't hold lock on 'this' to avoid edit log updates blocking token ops // don't hold lock on 'this' to avoid edit log updates blocking token ops
for (TokenIdent ident : expiredTokens) { for (TokenIdent ident : expiredTokens) {
logExpireToken(ident); logExpireToken(ident);
removeStoredToken(ident);
} }
} }

View File

@ -18,18 +18,18 @@
package org.apache.hadoop.security.token.delegation; package org.apache.hadoop.security.token.delegation;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import org.apache.avro.reflect.Nullable;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.io.Writable; import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableUtils; import org.apache.hadoop.io.WritableUtils;
import org.apache.avro.reflect.Nullable;
/** /**
* Key used for generating and verifying delegation tokens * Key used for generating and verifying delegation tokens
@ -117,4 +117,29 @@ public class DelegationKey implements Writable {
in.readFully(keyBytes); in.readFully(keyBytes);
} }
} }
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (expiryDate ^ (expiryDate >>> 32));
result = prime * result + Arrays.hashCode(keyBytes);
result = prime * result + keyId;
return result;
}
@Override
public boolean equals(Object right) {
if (this == right) {
return true;
} else if (right == null || getClass() != right.getClass()) {
return false;
} else {
DelegationKey r = (DelegationKey) right;
return keyId == r.keyId &&
expiryDate == r.expiryDate &&
Arrays.equals(keyBytes, r.keyBytes);
}
}
} }

View File

@ -84,6 +84,11 @@ public class TestDelegationToken {
public static class TestDelegationTokenSecretManager public static class TestDelegationTokenSecretManager
extends AbstractDelegationTokenSecretManager<TestDelegationTokenIdentifier> { extends AbstractDelegationTokenSecretManager<TestDelegationTokenIdentifier> {
public boolean isStoreNewMasterKeyCalled = false;
public boolean isRemoveStoredMasterKeyCalled = false;
public boolean isStoreNewTokenCalled = false;
public boolean isRemoveStoredTokenCalled = false;
public boolean isUpdateStoredTokenCalled = false;
public TestDelegationTokenSecretManager(long delegationKeyUpdateInterval, public TestDelegationTokenSecretManager(long delegationKeyUpdateInterval,
long delegationTokenMaxLifetime, long delegationTokenMaxLifetime,
long delegationTokenRenewInterval, long delegationTokenRenewInterval,
@ -101,7 +106,40 @@ public class TestDelegationToken {
protected byte[] createPassword(TestDelegationTokenIdentifier t) { protected byte[] createPassword(TestDelegationTokenIdentifier t) {
return super.createPassword(t); return super.createPassword(t);
} }
@Override
protected void storeNewMasterKey(DelegationKey key) throws IOException {
isStoreNewMasterKeyCalled = true;
super.storeNewMasterKey(key);
}
@Override
protected void removeStoredMasterKey(DelegationKey key) {
isRemoveStoredMasterKeyCalled = true;
Assert.assertFalse(key.equals(allKeys.get(currentId)));
}
@Override
protected void storeNewToken(TestDelegationTokenIdentifier ident,
long renewDate) {
super.storeNewToken(ident, renewDate);
isStoreNewTokenCalled = true;
}
@Override
protected void removeStoredToken(TestDelegationTokenIdentifier ident)
throws IOException {
super.removeStoredToken(ident);
isRemoveStoredTokenCalled = true;
}
@Override
protected void updateStoredToken(TestDelegationTokenIdentifier ident,
long renewDate) {
super.updateStoredToken(ident, renewDate);
isUpdateStoredTokenCalled = true;
}
public byte[] createPassword(TestDelegationTokenIdentifier t, DelegationKey key) { public byte[] createPassword(TestDelegationTokenIdentifier t, DelegationKey key) {
return SecretManager.createPassword(t.getBytes(), key.getKey()); return SecretManager.createPassword(t.getBytes(), key.getKey());
} }
@ -229,6 +267,7 @@ public class TestDelegationToken {
final Token<TestDelegationTokenIdentifier> token = final Token<TestDelegationTokenIdentifier> token =
generateDelegationToken( generateDelegationToken(
dtSecretManager, "SomeUser", "JobTracker"); dtSecretManager, "SomeUser", "JobTracker");
Assert.assertTrue(dtSecretManager.isStoreNewTokenCalled);
// Fake renewer should not be able to renew // Fake renewer should not be able to renew
shouldThrow(new PrivilegedExceptionAction<Object>() { shouldThrow(new PrivilegedExceptionAction<Object>() {
@Override @Override
@ -238,6 +277,7 @@ public class TestDelegationToken {
} }
}, AccessControlException.class); }, AccessControlException.class);
long time = dtSecretManager.renewToken(token, "JobTracker"); long time = dtSecretManager.renewToken(token, "JobTracker");
Assert.assertTrue(dtSecretManager.isUpdateStoredTokenCalled);
assertTrue("renew time is in future", time > Time.now()); assertTrue("renew time is in future", time > Time.now());
TestDelegationTokenIdentifier identifier = TestDelegationTokenIdentifier identifier =
new TestDelegationTokenIdentifier(); new TestDelegationTokenIdentifier();
@ -289,6 +329,7 @@ public class TestDelegationToken {
} }
}, AccessControlException.class); }, AccessControlException.class);
dtSecretManager.cancelToken(token, "JobTracker"); dtSecretManager.cancelToken(token, "JobTracker");
Assert.assertTrue(dtSecretManager.isRemoveStoredTokenCalled);
shouldThrow(new PrivilegedExceptionAction<Object>() { shouldThrow(new PrivilegedExceptionAction<Object>() {
@Override @Override
public Object run() throws Exception { public Object run() throws Exception {
@ -304,8 +345,8 @@ public class TestDelegationToken {
@Test @Test
public void testRollMasterKey() throws Exception { public void testRollMasterKey() throws Exception {
TestDelegationTokenSecretManager dtSecretManager = TestDelegationTokenSecretManager dtSecretManager =
new TestDelegationTokenSecretManager(24*60*60*1000, new TestDelegationTokenSecretManager(800,
10*1000,1*1000,3600000); 800,1*1000,3600000);
try { try {
dtSecretManager.startThreads(); dtSecretManager.startThreads();
//generate a token and store the password //generate a token and store the password
@ -316,7 +357,8 @@ public class TestDelegationToken {
int prevNumKeys = dtSecretManager.getAllKeys().length; int prevNumKeys = dtSecretManager.getAllKeys().length;
dtSecretManager.rollMasterKey(); dtSecretManager.rollMasterKey();
Assert.assertTrue(dtSecretManager.isStoreNewMasterKeyCalled);
//after rolling, the length of the keys list must increase //after rolling, the length of the keys list must increase
int currNumKeys = dtSecretManager.getAllKeys().length; int currNumKeys = dtSecretManager.getAllKeys().length;
Assert.assertEquals((currNumKeys - prevNumKeys) >= 1, true); Assert.assertEquals((currNumKeys - prevNumKeys) >= 1, true);
@ -333,6 +375,10 @@ public class TestDelegationToken {
dtSecretManager.retrievePassword(identifier); dtSecretManager.retrievePassword(identifier);
//compare the passwords //compare the passwords
Assert.assertEquals(oldPasswd, newPasswd); Assert.assertEquals(oldPasswd, newPasswd);
// wait for keys to exipire
Thread.sleep(2200);
Assert.assertTrue(dtSecretManager.isRemoveStoredMasterKeyCalled);
} finally { } finally {
dtSecretManager.stopThreads(); dtSecretManager.stopThreads();
} }
@ -484,4 +530,13 @@ public class TestDelegationToken {
assertFalse(testDelegationTokenIdentiferSerializationRoundTrip( assertFalse(testDelegationTokenIdentiferSerializationRoundTrip(
new Text("owner"), new Text("renewer"), new Text(bigBuf))); new Text("owner"), new Text("renewer"), new Text(bigBuf)));
} }
@Test
public void testDelegationKeyEqualAndHash() {
DelegationKey key1 = new DelegationKey(1111, 2222, "keyBytes".getBytes());
DelegationKey key2 = new DelegationKey(1111, 2222, "keyBytes".getBytes());
DelegationKey key3 = new DelegationKey(3333, 2222, "keyBytes".getBytes());
Assert.assertEquals(key1, key2);
Assert.assertFalse(key2.equals(key3));
}
} }