diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index 8d952e12bd..b915fd8f0d 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -13,6 +13,8 @@ Trunk (unreleased changes) (suresh) IMPROVEMENTS + HADOOP-8048. Allow merging of Credentials (Daryn Sharp via tgraves) + HADOOP-8032. mvn site:stage-deploy should be able to use the scp protocol to stage documents (Ravi Prakash via tgraves) diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Credentials.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Credentials.java index 152a35496b..9883604a2f 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Credentials.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Credentials.java @@ -230,14 +230,34 @@ public void readFields(DataInput in) throws IOException { /** * Copy all of the credentials from one credential object into another. + * Existing secrets and tokens are overwritten. * @param other the credentials to copy */ public void addAll(Credentials other) { + addAll(other, true); + } + + /** + * Copy all of the credentials from one credential object into another. + * Existing secrets and tokens are not overwritten. + * @param other the credentials to copy + */ + public void mergeAll(Credentials other) { + addAll(other, false); + } + + private void addAll(Credentials other, boolean overwrite) { for(Map.Entry secret: other.secretKeysMap.entrySet()) { - secretKeysMap.put(secret.getKey(), secret.getValue()); + Text key = secret.getKey(); + if (!secretKeysMap.containsKey(key) || overwrite) { + secretKeysMap.put(key, secret.getValue()); + } } for(Map.Entry> token: other.tokenMap.entrySet()){ - tokenMap.put(token.getKey(), token.getValue()); + Text key = token.getKey(); + if (!tokenMap.containsKey(key) || overwrite) { + tokenMap.put(key, token.getValue()); + } } } } diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestCredentials.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestCredentials.java index 7bedd2d028..56b5c32521 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestCredentials.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestCredentials.java @@ -137,4 +137,81 @@ public void testReadWriteStorage() } tmpFileName.delete(); } + + static Text secret[] = { + new Text("secret1"), + new Text("secret2"), + new Text("secret3"), + new Text("secret4") + }; + static Text service[] = { + new Text("service1"), + new Text("service2"), + new Text("service3"), + new Text("service4") + }; + static Token token[] = { + new Token(), + new Token(), + new Token(), + new Token() + }; + + @Test + public void addAll() { + Credentials creds = new Credentials(); + creds.addToken(service[0], token[0]); + creds.addToken(service[1], token[1]); + creds.addSecretKey(secret[0], secret[0].getBytes()); + creds.addSecretKey(secret[1], secret[1].getBytes()); + + Credentials credsToAdd = new Credentials(); + // one duplicate with different value, one new + credsToAdd.addToken(service[0], token[3]); + credsToAdd.addToken(service[2], token[2]); + credsToAdd.addSecretKey(secret[0], secret[3].getBytes()); + credsToAdd.addSecretKey(secret[2], secret[2].getBytes()); + + creds.addAll(credsToAdd); + assertEquals(3, creds.numberOfTokens()); + assertEquals(3, creds.numberOfSecretKeys()); + // existing token & secret should be overwritten + assertEquals(token[3], creds.getToken(service[0])); + assertEquals(secret[3], new Text(creds.getSecretKey(secret[0]))); + // non-duplicate token & secret should be present + assertEquals(token[1], creds.getToken(service[1])); + assertEquals(secret[1], new Text(creds.getSecretKey(secret[1]))); + // new token & secret should be added + assertEquals(token[2], creds.getToken(service[2])); + assertEquals(secret[2], new Text(creds.getSecretKey(secret[2]))); + } + + @Test + public void mergeAll() { + Credentials creds = new Credentials(); + creds.addToken(service[0], token[0]); + creds.addToken(service[1], token[1]); + creds.addSecretKey(secret[0], secret[0].getBytes()); + creds.addSecretKey(secret[1], secret[1].getBytes()); + + Credentials credsToAdd = new Credentials(); + // one duplicate with different value, one new + credsToAdd.addToken(service[0], token[3]); + credsToAdd.addToken(service[2], token[2]); + credsToAdd.addSecretKey(secret[0], secret[3].getBytes()); + credsToAdd.addSecretKey(secret[2], secret[2].getBytes()); + + creds.mergeAll(credsToAdd); + assertEquals(3, creds.numberOfTokens()); + assertEquals(3, creds.numberOfSecretKeys()); + // existing token & secret should not be overwritten + assertEquals(token[0], creds.getToken(service[0])); + assertEquals(secret[0], new Text(creds.getSecretKey(secret[0]))); + // non-duplicate token & secret should be present + assertEquals(token[1], creds.getToken(service[1])); + assertEquals(secret[1], new Text(creds.getSecretKey(secret[1]))); + // new token & secret should be added + assertEquals(token[2], creds.getToken(service[2])); + assertEquals(secret[2], new Text(creds.getSecretKey(secret[2]))); } +} \ No newline at end of file