accumulo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From els...@apache.org
Subject [2/7] accumulo git commit: ACCUMULO-3513 Add delegation token support for kerberos configurations
Date Fri, 13 Feb 2015 19:33:26 GMT
http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/base/src/test/java/org/apache/accumulo/server/security/delegation/AuthenticationTokenSecretManagerTest.java
----------------------------------------------------------------------
diff --git a/server/base/src/test/java/org/apache/accumulo/server/security/delegation/AuthenticationTokenSecretManagerTest.java b/server/base/src/test/java/org/apache/accumulo/server/security/delegation/AuthenticationTokenSecretManagerTest.java
new file mode 100644
index 0000000..b614819
--- /dev/null
+++ b/server/base/src/test/java/org/apache/accumulo/server/security/delegation/AuthenticationTokenSecretManagerTest.java
@@ -0,0 +1,393 @@
+/*
+ * 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.accumulo.server.security.delegation;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import javax.crypto.KeyGenerator;
+
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.Instance;
+import org.apache.accumulo.core.client.admin.DelegationTokenConfig;
+import org.apache.accumulo.core.security.AuthenticationTokenIdentifier;
+import org.apache.hadoop.security.token.SecretManager.InvalidToken;
+import org.apache.hadoop.security.token.Token;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Iterables;
+
+public class AuthenticationTokenSecretManagerTest {
+  private static final Logger log = LoggerFactory.getLogger(AuthenticationTokenSecretManagerTest.class);
+
+  // From org.apache.hadoop.security.token.SecretManager
+  private static final String DEFAULT_HMAC_ALGORITHM = "HmacSHA1";
+  private static final int KEY_LENGTH = 64;
+  private static KeyGenerator keyGen;
+
+  @BeforeClass
+  public static void setupKeyGenerator() throws Exception {
+    // From org.apache.hadoop.security.token.SecretManager
+    keyGen = KeyGenerator.getInstance(DEFAULT_HMAC_ALGORITHM);
+    keyGen.init(KEY_LENGTH);
+  }
+
+  private Instance instance;
+  private String instanceId;
+  private DelegationTokenConfig cfg;
+
+  @Before
+  public void setupMocks() {
+    instance = createMock(Instance.class);
+    instanceId = UUID.randomUUID().toString();
+    cfg = new DelegationTokenConfig();
+    expect(instance.getInstanceID()).andReturn(instanceId).anyTimes();
+    replay(instance);
+  }
+
+  @After
+  public void verifyMocks() {
+    verify(instance);
+  }
+
+  @Test
+  public void testAddKey() {
+    // 1 minute
+    long tokenLifetime = 60 * 1000;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Add a single key
+    AuthenticationKey authKey = new AuthenticationKey(1, 0, tokenLifetime, keyGen.generateKey());
+    secretManager.addKey(authKey);
+
+    // Ensure it's in the cache
+    Map<Integer,AuthenticationKey> keys = secretManager.getKeys();
+    assertNotNull(keys);
+    assertEquals(1, keys.size());
+    assertEquals(authKey, Iterables.getOnlyElement(keys.values()));
+
+    // Add the same key
+    secretManager.addKey(authKey);
+
+    // Ensure we still have only one key
+    keys = secretManager.getKeys();
+    assertNotNull(keys);
+    assertEquals(1, keys.size());
+    assertEquals(authKey, Iterables.getOnlyElement(keys.values()));
+  }
+
+  @Test
+  public void testRemoveKey() {
+    // 1 minute
+    long tokenLifetime = 60 * 1000;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Add a single key
+    AuthenticationKey authKey = new AuthenticationKey(1, 0, tokenLifetime, keyGen.generateKey());
+    secretManager.addKey(authKey);
+
+    // Ensure it's in the cache
+    Map<Integer,AuthenticationKey> keys = secretManager.getKeys();
+    assertNotNull(keys);
+    assertEquals(1, keys.size());
+    assertEquals(authKey, Iterables.getOnlyElement(keys.values()));
+
+    assertTrue(secretManager.removeKey(authKey.getKeyId()));
+    assertEquals(0, secretManager.getKeys().size());
+  }
+
+  @Test
+  public void testGenerateToken() throws Exception {
+    // start of the test
+    long then = System.currentTimeMillis();
+
+    // 1 minute
+    long tokenLifetime = 60 * 1000;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Add a current key
+    secretManager.addKey(new AuthenticationKey(1, then, then + tokenLifetime, keyGen.generateKey()));
+
+    String principal = "user@EXAMPLE.COM";
+    Entry<Token<AuthenticationTokenIdentifier>,AuthenticationTokenIdentifier> pair = secretManager.generateToken(principal, cfg);
+
+    assertNotNull(pair);
+    Token<AuthenticationTokenIdentifier> token = pair.getKey();
+    assertNotNull(token);
+    assertEquals(AuthenticationTokenIdentifier.TOKEN_KIND, token.getKind());
+
+    // Reconstitute the token identifier (will happen when clients are involved)
+    AuthenticationTokenIdentifier id = new AuthenticationTokenIdentifier();
+    id.readFields(new DataInputStream(new ByteArrayInputStream(token.getIdentifier())));
+    long now = System.currentTimeMillis();
+
+    // Issue date should be after the test started, but before we deserialized the token
+    assertTrue("Issue date did not fall within the expected upper bound. Expected less than " + now + ", but was " + id.getIssueDate(),
+        id.getIssueDate() <= now);
+    assertTrue("Issue date did not fall within the expected lower bound. Expected greater than " + then + ", but was " + id.getIssueDate(),
+        id.getIssueDate() >= then);
+
+    // Expiration is the token lifetime plus the issue date
+    assertEquals(id.getIssueDate() + tokenLifetime, id.getExpirationDate());
+
+    // Verify instance ID
+    assertEquals(instanceId, id.getInstanceId());
+
+    // The returned id should be the same as the reconstructed id
+    assertEquals(pair.getValue(), id);
+  }
+
+  @Test
+  public void testVerifyPassword() throws Exception {
+    // start of the test
+    long then = System.currentTimeMillis();
+
+    // 1 minute
+    long tokenLifetime = 60 * 1000;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Add a current key
+    secretManager.addKey(new AuthenticationKey(1, then, then + tokenLifetime, keyGen.generateKey()));
+
+    String principal = "user@EXAMPLE.COM";
+    Entry<Token<AuthenticationTokenIdentifier>,AuthenticationTokenIdentifier> pair = secretManager.generateToken(principal, cfg);
+    Token<AuthenticationTokenIdentifier> token = pair.getKey();
+
+    AuthenticationTokenIdentifier id = new AuthenticationTokenIdentifier();
+    id.readFields(new DataInputStream(new ByteArrayInputStream(token.getIdentifier())));
+
+    byte[] password = secretManager.retrievePassword(id);
+
+    // The passwords line up against multiple calls with the same ID
+    assertArrayEquals(password, secretManager.retrievePassword(id));
+
+    // Make a second token for the same user
+    Entry<Token<AuthenticationTokenIdentifier>,AuthenticationTokenIdentifier> pair2 = secretManager.generateToken(principal, cfg);
+    Token<AuthenticationTokenIdentifier> token2 = pair2.getKey();
+    // Reconstitute the token identifier (will happen when clients are involved)
+    AuthenticationTokenIdentifier id2 = new AuthenticationTokenIdentifier();
+    id2.readFields(new DataInputStream(new ByteArrayInputStream(token2.getIdentifier())));
+
+    // Get the password
+    byte[] password2 = secretManager.retrievePassword(id2);
+
+    // It should be different than the password for the first user.
+    assertFalse("Different tokens for the same user shouldn't have the same password", Arrays.equals(password, password2));
+  }
+
+  @Test(expected = InvalidToken.class)
+  public void testExpiredPasswordsThrowError() throws Exception {
+    // start of the test
+    long then = System.currentTimeMillis();
+
+    // 500ms lifetime
+    long tokenLifetime = 500;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Add a current key
+    secretManager.addKey(new AuthenticationKey(1, then, then + tokenLifetime, keyGen.generateKey()));
+
+    String principal = "user@EXAMPLE.COM";
+    Entry<Token<AuthenticationTokenIdentifier>,AuthenticationTokenIdentifier> pair = secretManager.generateToken(principal, cfg);
+    Token<AuthenticationTokenIdentifier> token = pair.getKey();
+
+    // Add a small buffer to make sure we move past the expiration of 0 for the token.
+    Thread.sleep(1000);
+
+    // Reconstitute the token identifier (will happen when clients are involved)
+    AuthenticationTokenIdentifier id = new AuthenticationTokenIdentifier();
+    id.readFields(new DataInputStream(new ByteArrayInputStream(token.getIdentifier())));
+
+    secretManager.retrievePassword(id);
+  }
+
+  @Test(expected = InvalidToken.class)
+  public void testTokenIssuedInFuture() throws Exception {
+    // start of the test
+    long then = System.currentTimeMillis();
+
+    long tokenLifetime = 60 * 1000;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Add a current key
+    secretManager.addKey(new AuthenticationKey(1, then, then + tokenLifetime, keyGen.generateKey()));
+
+    String principal = "user@EXAMPLE.COM";
+    Entry<Token<AuthenticationTokenIdentifier>,AuthenticationTokenIdentifier> pair = secretManager.generateToken(principal, cfg);
+    Token<AuthenticationTokenIdentifier> token = pair.getKey();
+
+    // Reconstitute the token identifier (will happen when clients are involved)
+    AuthenticationTokenIdentifier id = new AuthenticationTokenIdentifier();
+    id.readFields(new DataInputStream(new ByteArrayInputStream(token.getIdentifier())));
+
+    // Increase the value of issueDate
+    id.setIssueDate(Long.MAX_VALUE);
+
+    secretManager.retrievePassword(id);
+  }
+
+  @Test(expected = InvalidToken.class)
+  public void testRolledMasterKey() throws Exception {
+    // start of the test
+    long then = System.currentTimeMillis();
+
+    long tokenLifetime = 60 * 1000;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Add a current key
+    AuthenticationKey authKey1 = new AuthenticationKey(1, then, then + tokenLifetime, keyGen.generateKey());
+    secretManager.addKey(authKey1);
+
+    String principal = "user@EXAMPLE.COM";
+    Entry<Token<AuthenticationTokenIdentifier>,AuthenticationTokenIdentifier> pair = secretManager.generateToken(principal, cfg);
+    Token<AuthenticationTokenIdentifier> token = pair.getKey();
+
+    AuthenticationTokenIdentifier id = new AuthenticationTokenIdentifier();
+    id.readFields(new DataInputStream(new ByteArrayInputStream(token.getIdentifier())));
+
+    long now = System.currentTimeMillis();
+    secretManager.addKey(new AuthenticationKey(2, now, now + tokenLifetime, keyGen.generateKey()));
+
+    // Should succeed -- the SecretManager still has authKey1
+    secretManager.retrievePassword(id);
+
+    // Remove authKey1
+    secretManager.removeKey(authKey1.getKeyId());
+
+    // Should fail -- authKey1 (presumably) expired, cannot authenticate
+    secretManager.retrievePassword(id);
+  }
+
+  @Test(timeout = 20 * 1000)
+  public void testMasterKeyExpiration() throws Exception {
+    ZooAuthenticationKeyDistributor keyDistributor = createMock(ZooAuthenticationKeyDistributor.class);
+    // start of the test
+    long then = System.currentTimeMillis();
+
+    // 10s lifetime
+    long tokenLifetime = 10 * 1000l;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Make 2 keys, and add only one. The second has double the expiration of the first
+    AuthenticationKey authKey1 = new AuthenticationKey(1, then, then + tokenLifetime, keyGen.generateKey()), authKey2 = new AuthenticationKey(2, then
+        + tokenLifetime, then + tokenLifetime * 2, keyGen.generateKey());
+    secretManager.addKey(authKey1);
+
+    keyDistributor.remove(authKey1);
+    expectLastCall().once();
+
+    replay(keyDistributor);
+
+    // Make sure expiration doesn't trigger anything yet
+    assertEquals(0, secretManager.removeExpiredKeys(keyDistributor));
+    assertEquals(1, secretManager.getKeys().size());
+
+    // Add the second key, still no expiration
+    secretManager.addKey(authKey2);
+    assertEquals(0, secretManager.removeExpiredKeys(keyDistributor));
+    assertEquals(2, secretManager.getKeys().size());
+    assertEquals(authKey2, secretManager.getCurrentKey());
+
+    // Wait for the expiration
+    long now = System.currentTimeMillis();
+    while (now - (then + tokenLifetime) < 0) {
+      Thread.sleep(500);
+      now = System.currentTimeMillis();
+    }
+
+    // Expire the first
+    assertEquals(1, secretManager.removeExpiredKeys(keyDistributor));
+
+    // Ensure the second still exists
+    assertEquals(1, secretManager.getKeys().size());
+    assertEquals(authKey2, Iterables.getOnlyElement(secretManager.getKeys().values()));
+    assertEquals(authKey2, secretManager.getCurrentKey());
+
+    verify(keyDistributor);
+  }
+
+  @Test
+  public void testRestrictExpirationDate() throws Exception {
+    // start of the test
+    long then = System.currentTimeMillis();
+
+    // 1 hr
+    long tokenLifetime = 60 * 60 * 1000;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Add a current key
+    secretManager.addKey(new AuthenticationKey(1, then, then + tokenLifetime, keyGen.generateKey()));
+
+    // 1 minute
+    cfg.setTokenLifetime(1, TimeUnit.MINUTES);
+
+    String principal = "user@EXAMPLE.COM";
+    Entry<Token<AuthenticationTokenIdentifier>,AuthenticationTokenIdentifier> pair = secretManager.generateToken(principal, cfg);
+
+    assertNotNull(pair);
+
+    long now = System.currentTimeMillis();
+    long actualExpiration = pair.getValue().getExpirationDate();
+    long approximateLifetime = actualExpiration - now;
+
+    log.info("actualExpiration={}, approximateLifetime={}", actualExpiration, approximateLifetime);
+
+    // We don't know the exact lifetime, but we know that it can be no more than what was requested
+    assertTrue("Expected lifetime to be on thet order of the token lifetime, but was " + approximateLifetime,
+        approximateLifetime <= cfg.getTokenLifetime(TimeUnit.MILLISECONDS));
+  }
+
+  @Test(expected = AccumuloException.class)
+  public void testInvalidRequestedExpirationDate() throws Exception {
+    // start of the test
+    long then = System.currentTimeMillis();
+
+    // 1 hr
+    long tokenLifetime = 60 * 60 * 1000;
+    AuthenticationTokenSecretManager secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+
+    // Add a current key
+    secretManager.addKey(new AuthenticationKey(1, then, then + tokenLifetime, keyGen.generateKey()));
+
+    // A longer timeout than the secret key has
+    cfg.setTokenLifetime(tokenLifetime + 1, TimeUnit.MILLISECONDS);
+
+    // Should throw an exception
+    secretManager.generateToken("user@EXAMPLE.COM", cfg);
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/base/src/test/java/org/apache/accumulo/server/security/delegation/ZooAuthenticationKeyDistributorTest.java
----------------------------------------------------------------------
diff --git a/server/base/src/test/java/org/apache/accumulo/server/security/delegation/ZooAuthenticationKeyDistributorTest.java b/server/base/src/test/java/org/apache/accumulo/server/security/delegation/ZooAuthenticationKeyDistributorTest.java
new file mode 100644
index 0000000..ed40a10
--- /dev/null
+++ b/server/base/src/test/java/org/apache/accumulo/server/security/delegation/ZooAuthenticationKeyDistributorTest.java
@@ -0,0 +1,270 @@
+/*
+ * 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.accumulo.server.security.delegation;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.aryEq;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.crypto.KeyGenerator;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.fate.zookeeper.ZooUtil;
+import org.apache.accumulo.fate.zookeeper.ZooUtil.NodeExistsPolicy;
+import org.apache.accumulo.server.zookeeper.ZooReaderWriter;
+import org.apache.zookeeper.KeeperException.AuthFailedException;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.apache.zookeeper.data.Stat;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class ZooAuthenticationKeyDistributorTest {
+
+  // From org.apache.hadoop.security.token.SecretManager
+  private static final String DEFAULT_HMAC_ALGORITHM = "HmacSHA1";
+  private static final int KEY_LENGTH = 64;
+  private static KeyGenerator keyGen;
+
+  @BeforeClass
+  public static void setupKeyGenerator() throws Exception {
+    // From org.apache.hadoop.security.token.SecretManager
+    keyGen = KeyGenerator.getInstance(DEFAULT_HMAC_ALGORITHM);
+    keyGen.init(KEY_LENGTH);
+  }
+
+  private ZooReaderWriter zrw;
+  private String baseNode = Constants.ZDELEGATION_TOKEN_KEYS;
+
+  @Before
+  public void setupMocks() {
+    zrw = createMock(ZooReaderWriter.class);
+  }
+
+  @Test(expected = AuthFailedException.class)
+  public void testInitialize() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+
+    // Attempt to create the directory and fail
+    expect(zrw.exists(baseNode)).andReturn(false);
+    expect(zrw.putPrivatePersistentData(eq(baseNode), aryEq(new byte[0]), eq(NodeExistsPolicy.FAIL))).andThrow(new AuthFailedException());
+
+    replay(zrw);
+
+    distributor.initialize();
+
+    verify(zrw);
+  }
+
+  @Test
+  public void testInitializeCreatesParentNode() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+
+    // Attempt to create the directory and fail
+    expect(zrw.exists(baseNode)).andReturn(false);
+    expect(zrw.putPrivatePersistentData(eq(baseNode), (byte[]) anyObject(), eq(NodeExistsPolicy.FAIL))).andReturn(true);
+
+    replay(zrw);
+
+    distributor.initialize();
+
+    verify(zrw);
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void testInitializedNotCalledAdvertise() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+    distributor.advertise(new AuthenticationKey(1, 0l, 5l, keyGen.generateKey()));
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void testInitializedNotCalledCurrentKeys() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+    distributor.getCurrentKeys();
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void testInitializedNotCalledRemove() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+    distributor.remove(new AuthenticationKey(1, 0l, 5l, keyGen.generateKey()));
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void testMissingAcl() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+
+    // Attempt to create the directory and fail
+    expect(zrw.exists(baseNode)).andReturn(true);
+    expect(zrw.getACL(eq(baseNode), anyObject(Stat.class))).andReturn(Collections.<ACL> emptyList());
+
+    replay(zrw);
+
+    try {
+      distributor.initialize();
+    } finally {
+      verify(zrw);
+    }
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void testBadAcl() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+
+    // Attempt to create the directory and fail
+    expect(zrw.exists(baseNode)).andReturn(true);
+    expect(zrw.getACL(eq(baseNode), anyObject(Stat.class))).andReturn(
+        Collections.singletonList(new ACL(ZooUtil.PRIVATE.get(0).getPerms(), new Id("digest", "somethingweird"))));
+
+    replay(zrw);
+
+    try {
+      distributor.initialize();
+    } finally {
+      verify(zrw);
+    }
+  }
+
+  @Test
+  public void testAdvertiseKey() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+    AuthenticationKey key = new AuthenticationKey(1, 0l, 10l, keyGen.generateKey());
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    key.write(new DataOutputStream(baos));
+    byte[] serialized = baos.toByteArray();
+    String path = baseNode + "/" + key.getKeyId();
+
+    // Attempt to create the directory and fail
+    expect(zrw.exists(baseNode)).andReturn(true);
+    expect(zrw.getACL(eq(baseNode), anyObject(Stat.class))).andReturn(
+        Collections.singletonList(new ACL(ZooUtil.PRIVATE.get(0).getPerms(), new Id("digest", "accumulo:DEFAULT"))));
+    expect(zrw.exists(path)).andReturn(false);
+    expect(zrw.putPrivatePersistentData(eq(path), aryEq(serialized), eq(NodeExistsPolicy.FAIL))).andReturn(true);
+
+    replay(zrw);
+
+    distributor.initialize();
+    distributor.advertise(key);
+
+    verify(zrw);
+  }
+
+  @Test
+  public void testAlreadyAdvertisedKey() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+    AuthenticationKey key = new AuthenticationKey(1, 0l, 10l, keyGen.generateKey());
+    String path = baseNode + "/" + key.getKeyId();
+
+    // Attempt to create the directory and fail
+    expect(zrw.exists(baseNode)).andReturn(true);
+    expect(zrw.getACL(eq(baseNode), anyObject(Stat.class))).andReturn(
+        Collections.singletonList(new ACL(ZooUtil.PRIVATE.get(0).getPerms(), new Id("digest", "accumulo:DEFAULT"))));
+    expect(zrw.exists(path)).andReturn(true);
+
+    replay(zrw);
+
+    distributor.initialize();
+    distributor.advertise(key);
+
+    verify(zrw);
+  }
+
+  @Test
+  public void testRemoveKey() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+    AuthenticationKey key = new AuthenticationKey(1, 0l, 10l, keyGen.generateKey());
+    String path = baseNode + "/" + key.getKeyId();
+
+    // Attempt to create the directory and fail
+    expect(zrw.exists(baseNode)).andReturn(true);
+    expect(zrw.getACL(eq(baseNode), anyObject(Stat.class))).andReturn(
+        Collections.singletonList(new ACL(ZooUtil.PRIVATE.get(0).getPerms(), new Id("digest", "accumulo:DEFAULT"))));
+    expect(zrw.exists(path)).andReturn(true);
+    zrw.delete(path, -1);
+    expectLastCall().once();
+
+    replay(zrw);
+
+    distributor.initialize();
+    distributor.remove(key);
+
+    verify(zrw);
+  }
+
+  @Test
+  public void testRemoveMissingKey() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+    AuthenticationKey key = new AuthenticationKey(1, 0l, 10l, keyGen.generateKey());
+    String path = baseNode + "/" + key.getKeyId();
+
+    // Attempt to create the directory and fail
+    expect(zrw.exists(baseNode)).andReturn(true);
+    expect(zrw.getACL(eq(baseNode), anyObject(Stat.class))).andReturn(
+        Collections.singletonList(new ACL(ZooUtil.PRIVATE.get(0).getPerms(), new Id("digest", "accumulo:DEFAULT"))));
+    expect(zrw.exists(path)).andReturn(false);
+
+    replay(zrw);
+
+    distributor.initialize();
+    distributor.remove(key);
+
+    verify(zrw);
+  }
+
+  @Test
+  public void testGetCurrentKeys() throws Exception {
+    ZooAuthenticationKeyDistributor distributor = new ZooAuthenticationKeyDistributor(zrw, baseNode);
+    List<AuthenticationKey> keys = new ArrayList<>(5);
+    List<byte[]> serializedKeys = new ArrayList<>(5);
+    List<String> children = new ArrayList<>(5);
+    for (int i = 1; i < 6; i++) {
+      children.add(Integer.toString(i));
+      AuthenticationKey key = new AuthenticationKey(i, 0l, 10l, keyGen.generateKey());
+      keys.add(key);
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      key.write(new DataOutputStream(baos));
+      serializedKeys.add(baos.toByteArray());
+    }
+
+    expect(zrw.exists(baseNode)).andReturn(true);
+    expect(zrw.getACL(eq(baseNode), anyObject(Stat.class))).andReturn(
+        Collections.singletonList(new ACL(ZooUtil.PRIVATE.get(0).getPerms(), new Id("digest", "accumulo:DEFAULT"))));
+    expect(zrw.getChildren(baseNode)).andReturn(children);
+    for (int i = 1; i < 6; i++) {
+      expect(zrw.getData(baseNode + "/" + i, null)).andReturn(serializedKeys.get(i - 1));
+    }
+
+    replay(zrw);
+
+    distributor.initialize();
+    assertEquals(keys, distributor.getCurrentKeys());
+
+    verify(zrw);
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/base/src/test/java/org/apache/accumulo/server/security/delegation/ZooAuthenticationKeyWatcherTest.java
----------------------------------------------------------------------
diff --git a/server/base/src/test/java/org/apache/accumulo/server/security/delegation/ZooAuthenticationKeyWatcherTest.java b/server/base/src/test/java/org/apache/accumulo/server/security/delegation/ZooAuthenticationKeyWatcherTest.java
new file mode 100644
index 0000000..a60c9bc
--- /dev/null
+++ b/server/base/src/test/java/org/apache/accumulo/server/security/delegation/ZooAuthenticationKeyWatcherTest.java
@@ -0,0 +1,323 @@
+/*
+ * 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.accumulo.server.security.delegation;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import javax.crypto.KeyGenerator;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.client.Instance;
+import org.apache.accumulo.fate.zookeeper.ZooReader;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher.Event.EventType;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class ZooAuthenticationKeyWatcherTest {
+
+  // From org.apache.hadoop.security.token.SecretManager
+  private static final String DEFAULT_HMAC_ALGORITHM = "HmacSHA1";
+  private static final int KEY_LENGTH = 64;
+  private static KeyGenerator keyGen;
+
+  @BeforeClass
+  public static void setupKeyGenerator() throws Exception {
+    // From org.apache.hadoop.security.token.SecretManager
+    keyGen = KeyGenerator.getInstance(DEFAULT_HMAC_ALGORITHM);
+    keyGen.init(KEY_LENGTH);
+  }
+
+  private ZooReader zk;
+  private Instance instance;
+  private String instanceId;
+  private String baseNode;
+  private long tokenLifetime = 7 * 24 * 60 * 60 * 1000; // 7days
+  private AuthenticationTokenSecretManager secretManager;
+  private ZooAuthenticationKeyWatcher keyWatcher;
+
+  @Before
+  public void setupMocks() {
+    zk = createMock(ZooReader.class);
+    instance = createMock(Instance.class);
+    instanceId = UUID.randomUUID().toString();
+    baseNode = "/accumulo/" + instanceId + Constants.ZDELEGATION_TOKEN_KEYS;
+    expect(instance.getInstanceID()).andReturn(instanceId).anyTimes();
+    secretManager = new AuthenticationTokenSecretManager(instance, tokenLifetime);
+    keyWatcher = new ZooAuthenticationKeyWatcher(secretManager, zk, baseNode);
+  }
+
+  @Test
+  public void testBaseNodeCreated() throws Exception {
+    WatchedEvent event = new WatchedEvent(EventType.NodeCreated, null, baseNode);
+
+    expect(zk.getChildren(baseNode, keyWatcher)).andReturn(Collections.<String> emptyList());
+    replay(instance, zk);
+
+    keyWatcher.process(event);
+
+    verify(instance, zk);
+    assertTrue(secretManager.getKeys().isEmpty());
+  }
+
+  @Test
+  public void testBaseNodeCreatedWithChildren() throws Exception {
+    WatchedEvent event = new WatchedEvent(EventType.NodeCreated, null, baseNode);
+    AuthenticationKey key1 = new AuthenticationKey(1, 0l, 10000l, keyGen.generateKey()), key2 = new AuthenticationKey(2, key1.getExpirationDate(), 20000l,
+        keyGen.generateKey());
+    byte[] serializedKey1 = serialize(key1), serializedKey2 = serialize(key2);
+    List<String> children = Arrays.asList("1", "2");
+
+    expect(zk.getChildren(baseNode, keyWatcher)).andReturn(children);
+    expect(zk.getData(baseNode + "/1", keyWatcher, null)).andReturn(serializedKey1);
+    expect(zk.getData(baseNode + "/2", keyWatcher, null)).andReturn(serializedKey2);
+    replay(instance, zk);
+
+    keyWatcher.process(event);
+
+    verify(instance, zk);
+    assertEquals(2, secretManager.getKeys().size());
+    assertEquals(key1, secretManager.getKeys().get(key1.getKeyId()));
+    assertEquals(key2, secretManager.getKeys().get(key2.getKeyId()));
+  }
+
+  @Test
+  public void testBaseNodeChildrenChanged() throws Exception {
+    WatchedEvent event = new WatchedEvent(EventType.NodeChildrenChanged, null, baseNode);
+    AuthenticationKey key1 = new AuthenticationKey(1, 0l, 10000l, keyGen.generateKey()), key2 = new AuthenticationKey(2, key1.getExpirationDate(), 20000l,
+        keyGen.generateKey());
+    byte[] serializedKey1 = serialize(key1), serializedKey2 = serialize(key2);
+    List<String> children = Arrays.asList("1", "2");
+
+    expect(zk.getChildren(baseNode, keyWatcher)).andReturn(children);
+    expect(zk.getData(baseNode + "/1", keyWatcher, null)).andReturn(serializedKey1);
+    expect(zk.getData(baseNode + "/2", keyWatcher, null)).andReturn(serializedKey2);
+    replay(instance, zk);
+
+    keyWatcher.process(event);
+
+    verify(instance, zk);
+    assertEquals(2, secretManager.getKeys().size());
+    assertEquals(key1, secretManager.getKeys().get(key1.getKeyId()));
+    assertEquals(key2, secretManager.getKeys().get(key2.getKeyId()));
+  }
+
+  @Test
+  public void testBaseNodeDeleted() throws Exception {
+    WatchedEvent event = new WatchedEvent(EventType.NodeDeleted, null, baseNode);
+    AuthenticationKey key1 = new AuthenticationKey(1, 0l, 10000l, keyGen.generateKey()), key2 = new AuthenticationKey(2, key1.getExpirationDate(), 20000l,
+        keyGen.generateKey());
+
+    secretManager.addKey(key1);
+    secretManager.addKey(key2);
+    assertEquals(2, secretManager.getKeys().size());
+
+    replay(instance, zk);
+
+    keyWatcher.process(event);
+
+    verify(instance, zk);
+    assertEquals(0, secretManager.getKeys().size());
+    assertFalse(secretManager.isCurrentKeySet());
+  }
+
+  @Test
+  public void testBaseNodeDataChanged() throws Exception {
+    WatchedEvent event = new WatchedEvent(EventType.NodeDataChanged, null, baseNode);
+
+    replay(instance, zk);
+
+    keyWatcher.process(event);
+
+    verify(instance, zk);
+    assertEquals(0, secretManager.getKeys().size());
+    assertFalse(secretManager.isCurrentKeySet());
+  }
+
+  @Test
+  public void testChildChanged() throws Exception {
+    WatchedEvent event = new WatchedEvent(EventType.NodeCreated, null, baseNode + "/2");
+    AuthenticationKey key1 = new AuthenticationKey(1, 0l, 10000l, keyGen.generateKey()), key2 = new AuthenticationKey(2, key1.getExpirationDate(), 20000l,
+        keyGen.generateKey());
+    secretManager.addKey(key1);
+    assertEquals(1, secretManager.getKeys().size());
+    byte[] serializedKey2 = serialize(key2);
+
+    expect(zk.getData(event.getPath(), keyWatcher, null)).andReturn(serializedKey2);
+    replay(instance, zk);
+
+    keyWatcher.process(event);
+
+    verify(instance, zk);
+    assertEquals(2, secretManager.getKeys().size());
+    assertEquals(key1, secretManager.getKeys().get(key1.getKeyId()));
+    assertEquals(key2, secretManager.getKeys().get(key2.getKeyId()));
+    assertEquals(key2, secretManager.getCurrentKey());
+  }
+
+  @Test
+  public void testChildDeleted() throws Exception {
+    WatchedEvent event = new WatchedEvent(EventType.NodeDeleted, null, baseNode + "/1");
+    AuthenticationKey key1 = new AuthenticationKey(1, 0l, 10000l, keyGen.generateKey()), key2 = new AuthenticationKey(2, key1.getExpirationDate(), 20000l,
+        keyGen.generateKey());
+    secretManager.addKey(key1);
+    secretManager.addKey(key2);
+    assertEquals(2, secretManager.getKeys().size());
+
+    replay(instance, zk);
+
+    keyWatcher.process(event);
+
+    verify(instance, zk);
+    assertEquals(1, secretManager.getKeys().size());
+    assertEquals(key2, secretManager.getKeys().get(key2.getKeyId()));
+    assertEquals(key2, secretManager.getCurrentKey());
+  }
+
+  @Test
+  public void testChildChildrenChanged() throws Exception {
+    WatchedEvent event = new WatchedEvent(EventType.NodeChildrenChanged, null, baseNode + "/2");
+    AuthenticationKey key1 = new AuthenticationKey(1, 0l, 10000l, keyGen.generateKey()), key2 = new AuthenticationKey(2, key1.getExpirationDate(), 20000l,
+        keyGen.generateKey());
+    secretManager.addKey(key1);
+    secretManager.addKey(key2);
+    assertEquals(2, secretManager.getKeys().size());
+
+    replay(instance, zk);
+
+    // Does nothing
+    keyWatcher.process(event);
+
+    verify(instance, zk);
+    assertEquals(2, secretManager.getKeys().size());
+    assertEquals(key1, secretManager.getKeys().get(key1.getKeyId()));
+    assertEquals(key2, secretManager.getKeys().get(key2.getKeyId()));
+    assertEquals(key2, secretManager.getCurrentKey());
+  }
+
+  @Test
+  public void testInitialUpdateNoNode() throws Exception {
+    expect(zk.exists(baseNode, keyWatcher)).andReturn(false);
+
+    replay(zk, instance);
+
+    keyWatcher.updateAuthKeys();
+
+    verify(zk, instance);
+    assertEquals(0, secretManager.getKeys().size());
+    assertNull(secretManager.getCurrentKey());
+  }
+
+  @Test
+  public void testInitialUpdateWithKeys() throws Exception {
+    List<String> children = Arrays.asList("1", "5");
+    AuthenticationKey key1 = new AuthenticationKey(1, 0l, 10000l, keyGen.generateKey()), key2 = new AuthenticationKey(5, key1.getExpirationDate(), 20000l,
+        keyGen.generateKey());
+
+    expect(zk.exists(baseNode, keyWatcher)).andReturn(true);
+    expect(zk.getChildren(baseNode, keyWatcher)).andReturn(children);
+    expect(zk.getData(baseNode + "/" + key1.getKeyId(), keyWatcher, null)).andReturn(serialize(key1));
+    expect(zk.getData(baseNode + "/" + key2.getKeyId(), keyWatcher, null)).andReturn(serialize(key2));
+
+    replay(zk, instance);
+
+    keyWatcher.updateAuthKeys();
+
+    verify(zk, instance);
+
+    assertEquals(2, secretManager.getKeys().size());
+    assertEquals(key1, secretManager.getKeys().get(key1.getKeyId()));
+    assertEquals(key2, secretManager.getKeys().get(key2.getKeyId()));
+  }
+
+  @Test
+  public void testDisconnectAndReconnect() throws Exception {
+    lostZooKeeperBase(new WatchedEvent(EventType.None, KeeperState.Disconnected, null), new WatchedEvent(EventType.None, KeeperState.SyncConnected, null));
+  }
+
+  @Test
+  public void testExpiredAndReconnect() throws Exception {
+    lostZooKeeperBase(new WatchedEvent(EventType.None, KeeperState.Expired, null), new WatchedEvent(EventType.None, KeeperState.SyncConnected, null));
+  }
+
+  private void lostZooKeeperBase(WatchedEvent disconnectEvent, WatchedEvent reconnectEvent) throws Exception {
+
+    List<String> children = Arrays.asList("1", "5");
+    AuthenticationKey key1 = new AuthenticationKey(1, 0l, 10000l, keyGen.generateKey()), key2 = new AuthenticationKey(5, key1.getExpirationDate(), 20000l,
+        keyGen.generateKey());
+
+    expect(zk.exists(baseNode, keyWatcher)).andReturn(true);
+    expect(zk.getChildren(baseNode, keyWatcher)).andReturn(children);
+    expect(zk.getData(baseNode + "/" + key1.getKeyId(), keyWatcher, null)).andReturn(serialize(key1));
+    expect(zk.getData(baseNode + "/" + key2.getKeyId(), keyWatcher, null)).andReturn(serialize(key2));
+
+    replay(zk, instance);
+
+    // Initialize and then get disconnected
+    keyWatcher.updateAuthKeys();
+    keyWatcher.process(disconnectEvent);
+
+    verify(zk, instance);
+
+    // We should have no auth keys when we're disconnected
+    assertEquals("Secret manager should be empty after a disconnect", 0, secretManager.getKeys().size());
+    assertNull("Current key should be null", secretManager.getCurrentKey());
+
+    reset(zk, instance);
+
+    expect(zk.exists(baseNode, keyWatcher)).andReturn(true);
+    expect(zk.getChildren(baseNode, keyWatcher)).andReturn(children);
+    expect(zk.getData(baseNode + "/" + key1.getKeyId(), keyWatcher, null)).andReturn(serialize(key1));
+    expect(zk.getData(baseNode + "/" + key2.getKeyId(), keyWatcher, null)).andReturn(serialize(key2));
+
+    replay(zk, instance);
+
+    // Reconnect again, get all the keys
+    keyWatcher.process(reconnectEvent);
+
+    verify(zk, instance);
+
+    // Verify we have both keys
+    assertEquals(2, secretManager.getKeys().size());
+    assertEquals(key1, secretManager.getKeys().get(key1.getKeyId()));
+    assertEquals(key2, secretManager.getKeys().get(key2.getKeyId()));
+  }
+
+  private byte[] serialize(AuthenticationKey key) throws IOException {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    key.write(new DataOutputStream(baos));
+    return baos.toByteArray();
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/gc/src/main/java/org/apache/accumulo/gc/SimpleGarbageCollector.java
----------------------------------------------------------------------
diff --git a/server/gc/src/main/java/org/apache/accumulo/gc/SimpleGarbageCollector.java b/server/gc/src/main/java/org/apache/accumulo/gc/SimpleGarbageCollector.java
index da0b07c..35005d8 100644
--- a/server/gc/src/main/java/org/apache/accumulo/gc/SimpleGarbageCollector.java
+++ b/server/gc/src/main/java/org/apache/accumulo/gc/SimpleGarbageCollector.java
@@ -723,7 +723,7 @@ public class SimpleGarbageCollector extends AccumuloServerContext implements Ifa
     log.debug("Starting garbage collector listening on " + result);
     try {
       return TServerUtils.startTServer(getConfiguration(), result, getThriftServerType(), processor, this.getClass().getSimpleName(), "GC Monitor Service", 2,
-          getConfiguration().getCount(Property.GENERAL_SIMPLETIMER_THREADPOOL_SIZE), 1000, maxMessageSize, getServerSslParams(), getServerSaslParams(), 0).address;
+          getConfiguration().getCount(Property.GENERAL_SIMPLETIMER_THREADPOOL_SIZE), 1000, maxMessageSize, getServerSslParams(), getSaslParams(), 0).address;
     } catch (Exception ex) {
       log.fatal(ex, ex);
       throw new RuntimeException(ex);

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/gc/src/test/java/org/apache/accumulo/gc/GarbageCollectWriteAheadLogsTest.java
----------------------------------------------------------------------
diff --git a/server/gc/src/test/java/org/apache/accumulo/gc/GarbageCollectWriteAheadLogsTest.java b/server/gc/src/test/java/org/apache/accumulo/gc/GarbageCollectWriteAheadLogsTest.java
index 1d7f90f..5224f28 100644
--- a/server/gc/src/test/java/org/apache/accumulo/gc/GarbageCollectWriteAheadLogsTest.java
+++ b/server/gc/src/test/java/org/apache/accumulo/gc/GarbageCollectWriteAheadLogsTest.java
@@ -117,6 +117,13 @@ public class GarbageCollectWriteAheadLogsTest {
         return systemConfig.get((Property) args[0]);
       }
     }).anyTimes();
+    EasyMock.expect(siteConfig.getBoolean(EasyMock.anyObject(Property.class))).andAnswer(new IAnswer<Boolean>() {
+      @Override
+      public Boolean answer() {
+        Object[] args = EasyMock.getCurrentArguments();
+        return systemConfig.getBoolean((Property) args[0]);
+      }
+    }).anyTimes();
 
     EasyMock.expect(siteConfig.iterator()).andAnswer(new IAnswer<Iterator<Entry<String,String>>>() {
       @Override

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/gc/src/test/java/org/apache/accumulo/gc/SimpleGarbageCollectorTest.java
----------------------------------------------------------------------
diff --git a/server/gc/src/test/java/org/apache/accumulo/gc/SimpleGarbageCollectorTest.java b/server/gc/src/test/java/org/apache/accumulo/gc/SimpleGarbageCollectorTest.java
index 6fcdd37..d30f00b 100644
--- a/server/gc/src/test/java/org/apache/accumulo/gc/SimpleGarbageCollectorTest.java
+++ b/server/gc/src/test/java/org/apache/accumulo/gc/SimpleGarbageCollectorTest.java
@@ -84,6 +84,13 @@ public class SimpleGarbageCollectorTest {
         return systemConfig.get((Property) args[0]);
       }
     }).anyTimes();
+    EasyMock.expect(siteConfig.getBoolean(EasyMock.anyObject(Property.class))).andAnswer(new IAnswer<Boolean>() {
+      @Override
+      public Boolean answer() {
+        Object[] args = EasyMock.getCurrentArguments();
+        return systemConfig.getBoolean((Property) args[0]);
+      }
+    }).anyTimes();
 
     EasyMock.expect(siteConfig.iterator()).andAnswer(new IAnswer<Iterator<Entry<String,String>>>() {
       @Override

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/gc/src/test/java/org/apache/accumulo/gc/replication/CloseWriteAheadLogReferencesTest.java
----------------------------------------------------------------------
diff --git a/server/gc/src/test/java/org/apache/accumulo/gc/replication/CloseWriteAheadLogReferencesTest.java b/server/gc/src/test/java/org/apache/accumulo/gc/replication/CloseWriteAheadLogReferencesTest.java
index 120692a..ba68890 100644
--- a/server/gc/src/test/java/org/apache/accumulo/gc/replication/CloseWriteAheadLogReferencesTest.java
+++ b/server/gc/src/test/java/org/apache/accumulo/gc/replication/CloseWriteAheadLogReferencesTest.java
@@ -107,6 +107,13 @@ public class CloseWriteAheadLogReferencesTest {
         return systemConf.get((Property) args[0]);
       }
     }).anyTimes();
+    EasyMock.expect(siteConfig.getBoolean(EasyMock.anyObject(Property.class))).andAnswer(new IAnswer<Boolean>() {
+      @Override
+      public Boolean answer() {
+        Object[] args = EasyMock.getCurrentArguments();
+        return systemConf.getBoolean((Property) args[0]);
+      }
+    }).anyTimes();
 
     EasyMock.expect(siteConfig.iterator()).andAnswer(new IAnswer<Iterator<Entry<String,String>>>() {
       @Override

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/master/src/main/java/org/apache/accumulo/master/Master.java
----------------------------------------------------------------------
diff --git a/server/master/src/main/java/org/apache/accumulo/master/Master.java b/server/master/src/main/java/org/apache/accumulo/master/Master.java
index be476de..cc6a6ce 100644
--- a/server/master/src/main/java/org/apache/accumulo/master/Master.java
+++ b/server/master/src/main/java/org/apache/accumulo/master/Master.java
@@ -124,6 +124,9 @@ import org.apache.accumulo.server.rpc.ThriftServerType;
 import org.apache.accumulo.server.security.AuditedSecurityOperation;
 import org.apache.accumulo.server.security.SecurityOperation;
 import org.apache.accumulo.server.security.SecurityUtil;
+import org.apache.accumulo.server.security.delegation.AuthenticationTokenKeyManager;
+import org.apache.accumulo.server.security.delegation.AuthenticationTokenSecretManager;
+import org.apache.accumulo.server.security.delegation.ZooAuthenticationKeyDistributor;
 import org.apache.accumulo.server.security.handler.ZKPermHandler;
 import org.apache.accumulo.server.tables.TableManager;
 import org.apache.accumulo.server.tables.TableObserver;
@@ -188,6 +191,11 @@ public class Master extends AccumuloServerContext implements LiveTServerSet.List
   private WorkDriver replicationWorkAssigner;
   RecoveryManager recoveryManager = null;
 
+  // Delegation Token classes
+  private final boolean delegationTokensAvailable;
+  private ZooAuthenticationKeyDistributor keyDistributor;
+  private AuthenticationTokenKeyManager authenticationTokenKeyManager;
+
   ZooLock masterLock = null;
   private TServer clientService = null;
   TabletBalancer tabletBalancer;
@@ -560,7 +568,7 @@ public class Master extends AccumuloServerContext implements LiveTServerSet.List
       throw new ThriftTableOperationException(tableId, null, TableOperation.MERGE, TableOperationExceptionType.OFFLINE, "table is not online");
   }
 
-  private Master(ServerConfigurationFactory config, VolumeManager fs, String hostname) throws IOException {
+  public Master(ServerConfigurationFactory config, VolumeManager fs, String hostname) throws IOException {
     super(config);
     this.serverConfig = config;
     this.fs = fs;
@@ -587,6 +595,24 @@ public class Master extends AccumuloServerContext implements LiveTServerSet.List
     }
 
     this.security = AuditedSecurityOperation.getInstance(this);
+
+    // Create the secret manager (can generate and verify delegation tokens)
+    final long tokenLifetime = aconf.getTimeInMillis(Property.GENERAL_DELEGATION_TOKEN_LIFETIME);
+    setSecretManager(new AuthenticationTokenSecretManager(getInstance(), tokenLifetime));
+
+    authenticationTokenKeyManager = null;
+    keyDistributor = null;
+    if (getConfiguration().getBoolean(Property.INSTANCE_RPC_SASL_ENABLED)) {
+      // SASL is enabled, create the key distributor (ZooKeeper) and manager (generates/rolls secret keys)
+      log.info("SASL is enabled, creating delegation token key manager and distributor");
+      final long tokenUpdateInterval = aconf.getTimeInMillis(Property.GENERAL_DELEGATION_TOKEN_UPDATE_INTERVAL);
+      keyDistributor = new ZooAuthenticationKeyDistributor(ZooReaderWriter.getInstance(), ZooUtil.getRoot(getInstance()) + Constants.ZDELEGATION_TOKEN_KEYS);
+      authenticationTokenKeyManager = new AuthenticationTokenKeyManager(getSecretManager(), keyDistributor, tokenUpdateInterval, tokenLifetime);
+      delegationTokensAvailable = true;
+    } else {
+      log.info("SASL is not enabled, delegation tokens will not be available");
+      delegationTokensAvailable = false;
+    }
   }
 
   public TServerConnection getConnection(TServerInstance server) {
@@ -1096,6 +1122,25 @@ public class Master extends AccumuloServerContext implements LiveTServerSet.List
 
     ZooKeeperInitialization.ensureZooKeeperInitialized(zReaderWriter, zroot);
 
+    // Make sure that we have a secret key (either a new one or an old one from ZK) before we start
+    // the master client service.
+    if (null != authenticationTokenKeyManager && null != keyDistributor) {
+      log.info("Starting delegation-token key manager");
+      keyDistributor.initialize();
+      authenticationTokenKeyManager.start();
+      boolean logged = false;
+      while (!authenticationTokenKeyManager.isInitialized()) {
+        // Print out a status message when we start waiting for the key manager to get initialized
+        if (!logged) {
+          log.info("Waiting for AuthenticationTokenKeyManager to be initialized");
+          logged = true;
+        }
+        UtilWaitThread.sleep(200);
+      }
+      // And log when we are initialized
+      log.info("AuthenticationTokenSecretManager is initialized");
+    }
+
     clientHandler = new MasterClientServiceHandler(this);
     Iface rpcProxy = RpcWrapper.service(clientHandler);
     final Processor<Iface> processor;
@@ -1162,6 +1207,9 @@ public class Master extends AccumuloServerContext implements LiveTServerSet.List
     replicationWorkAssigner.join(remaining(deadline));
     replicationWorkDriver.join(remaining(deadline));
     replAddress.server.stop();
+    // Signal that we want it to stop, and wait for it to do so.
+    authenticationTokenKeyManager.gracefulStop();
+    authenticationTokenKeyManager.join(remaining(deadline));
 
     // quit, even if the tablet servers somehow jam up and the watchers
     // don't stop
@@ -1476,4 +1524,11 @@ public class Master extends AccumuloServerContext implements LiveTServerSet.List
     result.deadTabletServers = obit.getList();
     return result;
   }
+
+  /**
+   * Can delegation tokens be generated for users
+   */
+  public boolean delegationTokensAvailable() {
+    return delegationTokensAvailable;
+  }
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/master/src/main/java/org/apache/accumulo/master/MasterClientServiceHandler.java
----------------------------------------------------------------------
diff --git a/server/master/src/main/java/org/apache/accumulo/master/MasterClientServiceHandler.java b/server/master/src/main/java/org/apache/accumulo/master/MasterClientServiceHandler.java
index 72cba26..3809a29 100644
--- a/server/master/src/main/java/org/apache/accumulo/master/MasterClientServiceHandler.java
+++ b/server/master/src/main/java/org/apache/accumulo/master/MasterClientServiceHandler.java
@@ -33,6 +33,8 @@ import org.apache.accumulo.core.client.IsolatedScanner;
 import org.apache.accumulo.core.client.RowIterator;
 import org.apache.accumulo.core.client.Scanner;
 import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.admin.DelegationTokenConfig;
+import org.apache.accumulo.core.client.impl.DelegationTokenConfigSerializer;
 import org.apache.accumulo.core.client.impl.Tables;
 import org.apache.accumulo.core.client.impl.thrift.SecurityErrorCode;
 import org.apache.accumulo.core.client.impl.thrift.TableOperation;
@@ -55,8 +57,11 @@ import org.apache.accumulo.core.metadata.RootTable;
 import org.apache.accumulo.core.metadata.schema.MetadataSchema;
 import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection;
 import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.LogColumnFamily;
+import org.apache.accumulo.core.security.AuthenticationTokenIdentifier;
 import org.apache.accumulo.core.security.Authorizations;
 import org.apache.accumulo.core.security.thrift.TCredentials;
+import org.apache.accumulo.core.security.thrift.TDelegationToken;
+import org.apache.accumulo.core.security.thrift.TDelegationTokenConfig;
 import org.apache.accumulo.core.trace.thrift.TInfo;
 import org.apache.accumulo.core.util.ByteBufferUtil;
 import org.apache.accumulo.core.util.UtilWaitThread;
@@ -69,12 +74,14 @@ import org.apache.accumulo.server.master.LiveTServerSet.TServerConnection;
 import org.apache.accumulo.server.master.balancer.DefaultLoadBalancer;
 import org.apache.accumulo.server.master.balancer.TabletBalancer;
 import org.apache.accumulo.server.master.state.TServerInstance;
+import org.apache.accumulo.server.security.delegation.AuthenticationTokenSecretManager;
 import org.apache.accumulo.server.util.NamespacePropUtil;
 import org.apache.accumulo.server.util.SystemPropUtil;
 import org.apache.accumulo.server.util.TablePropUtil;
 import org.apache.accumulo.server.util.TabletIterator.TabletDeletedException;
 import org.apache.accumulo.server.zookeeper.ZooReaderWriter;
 import org.apache.hadoop.io.Text;
+import org.apache.hadoop.security.token.Token;
 import org.apache.log4j.Logger;
 import org.apache.thrift.TException;
 import org.apache.zookeeper.KeeperException;
@@ -445,4 +452,27 @@ class MasterClientServiceHandler extends FateServiceHandler implements MasterCli
 
     return servers;
   }
+
+  @Override
+  public TDelegationToken getDelegationToken(TInfo tinfo, TCredentials credentials, TDelegationTokenConfig tConfig) throws ThriftSecurityException, TException {
+    if (!master.security.canObtainDelegationToken(credentials)) {
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    }
+
+    // Make sure we're actually generating the secrets to make delegation tokens
+    // Round-about way to verify that SASL is also enabled.
+    if (!master.delegationTokensAvailable()) {
+      throw new TException("Delegation tokens are not available for use");
+    }
+
+    final DelegationTokenConfig config = DelegationTokenConfigSerializer.deserialize(tConfig);
+    final AuthenticationTokenSecretManager secretManager = master.getSecretManager();
+    try {
+      Entry<Token<AuthenticationTokenIdentifier>,AuthenticationTokenIdentifier> pair = secretManager.generateToken(credentials.principal, config);
+
+      return new TDelegationToken(ByteBuffer.wrap(pair.getKey().getPassword()), pair.getValue().getThriftIdentifier());
+    } catch (Exception e) {
+      throw new TException(e.getMessage());
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
----------------------------------------------------------------------
diff --git a/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java b/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
index a5675dc..662ee31 100644
--- a/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
+++ b/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
@@ -180,6 +180,8 @@ import org.apache.accumulo.server.rpc.ThriftServerType;
 import org.apache.accumulo.server.security.AuditedSecurityOperation;
 import org.apache.accumulo.server.security.SecurityOperation;
 import org.apache.accumulo.server.security.SecurityUtil;
+import org.apache.accumulo.server.security.delegation.AuthenticationTokenSecretManager;
+import org.apache.accumulo.server.security.delegation.ZooAuthenticationKeyWatcher;
 import org.apache.accumulo.server.util.FileSystemMonitor;
 import org.apache.accumulo.server.util.Halt;
 import org.apache.accumulo.server.util.MasterMetadataUtil;
@@ -312,6 +314,8 @@ public class TabletServer extends AccumuloServerContext implements Runnable {
   private final AtomicLong totalMinorCompactions = new AtomicLong(0);
   private final ServerConfigurationFactory confFactory;
 
+  private final ZooAuthenticationKeyWatcher authKeyWatcher;
+
   public TabletServer(ServerConfigurationFactory confFactory, VolumeManager fs) {
     super(confFactory);
     this.confFactory = confFactory;
@@ -356,6 +360,17 @@ public class TabletServer extends AccumuloServerContext implements Runnable {
         TabletLocator.clearLocators();
       }
     }, jitter(TIME_BETWEEN_LOCATOR_CACHE_CLEARS), jitter(TIME_BETWEEN_LOCATOR_CACHE_CLEARS));
+
+    // Create the secret manager
+    setSecretManager(new AuthenticationTokenSecretManager(instance, aconf.getTimeInMillis(Property.GENERAL_DELEGATION_TOKEN_LIFETIME)));
+    if (aconf.getBoolean(Property.INSTANCE_RPC_SASL_ENABLED)) {
+      log.info("SASL is enabled, creating ZooKeeper watcher for AuthenticationKeys");
+      // Watcher to notice new AuthenticationKeys which enable delegation tokens
+      authKeyWatcher = new ZooAuthenticationKeyWatcher(getSecretManager(), ZooReaderWriter.getInstance(), ZooUtil.getRoot(instance)
+          + Constants.ZDELEGATION_TOKEN_KEYS);
+    } else {
+      authKeyWatcher = null;
+    }
   }
 
   private static long jitter(long ms) {
@@ -2421,6 +2436,17 @@ public class TabletServer extends AccumuloServerContext implements Runnable {
       log.error("Error registering with JMX", e);
     }
 
+    if (null != authKeyWatcher) {
+      log.info("Seeding ZooKeeper watcher for authentication keys");
+      try {
+        authKeyWatcher.updateAuthKeys();
+      } catch (KeeperException | InterruptedException e) {
+        // TODO Does there need to be a better check? What are the error conditions that we'd fall out here? AUTH_FAILURE?
+        // If we get the error, do we just put it on a timer and retry the exists(String, Watcher) call?
+        log.error("Failed to perform initial check for authentication tokens in ZooKeeper. Delegation token authentication will be unavailable.", e);
+      }
+    }
+
     try {
       clientAddress = startTabletClientService();
     } catch (UnknownHostException e1) {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousBatchWalker.java
----------------------------------------------------------------------
diff --git a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousBatchWalker.java b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousBatchWalker.java
index a2687bb..c8a1143 100644
--- a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousBatchWalker.java
+++ b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousBatchWalker.java
@@ -26,6 +26,7 @@ import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.accumulo.core.cli.BatchScannerOpts;
+import org.apache.accumulo.core.cli.ClientOnDefaultTable;
 import org.apache.accumulo.core.cli.ScannerOpts;
 import org.apache.accumulo.core.client.BatchScanner;
 import org.apache.accumulo.core.client.Connector;
@@ -52,16 +53,17 @@ public class ContinuousBatchWalker {
     Opts opts = new Opts();
     ScannerOpts scanOpts = new ScannerOpts();
     BatchScannerOpts bsOpts = new BatchScannerOpts();
-    opts.parseArgs(ContinuousBatchWalker.class.getName(), args, scanOpts, bsOpts);
+    ClientOnDefaultTable clientOpts = new ClientOnDefaultTable("ci");
+    clientOpts.parseArgs(ContinuousBatchWalker.class.getName(), args, scanOpts, bsOpts, opts);
 
     Random r = new Random();
     Authorizations auths = opts.randomAuths.getAuths(r);
 
-    Connector conn = opts.getConnector();
-    Scanner scanner = ContinuousUtil.createScanner(conn, opts.getTableName(), auths);
+    Connector conn = clientOpts.getConnector();
+    Scanner scanner = ContinuousUtil.createScanner(conn, clientOpts.getTableName(), auths);
     scanner.setBatchSize(scanOpts.scanBatchSize);
 
-    BatchScanner bs = conn.createBatchScanner(opts.getTableName(), auths, bsOpts.scanThreads);
+    BatchScanner bs = conn.createBatchScanner(clientOpts.getTableName(), auths, bsOpts.scanThreads);
     bs.setTimeout(bsOpts.scanTimeout, TimeUnit.MILLISECONDS);
 
     while (true) {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousIngest.java
----------------------------------------------------------------------
diff --git a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousIngest.java b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousIngest.java
index dba6ac9..ddc36aa 100644
--- a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousIngest.java
+++ b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousIngest.java
@@ -19,7 +19,6 @@ package org.apache.accumulo.test.continuous;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import java.io.BufferedReader;
-import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -29,9 +28,8 @@ import java.util.UUID;
 import java.util.zip.CRC32;
 import java.util.zip.Checksum;
 
-import org.apache.accumulo.core.Constants;
 import org.apache.accumulo.core.cli.BatchWriterOpts;
-import org.apache.accumulo.core.cli.MapReduceClientOnDefaultTable;
+import org.apache.accumulo.core.cli.ClientOnDefaultTable;
 import org.apache.accumulo.core.client.BatchWriter;
 import org.apache.accumulo.core.client.Connector;
 import org.apache.accumulo.core.client.MutationsRejectedException;
@@ -46,75 +44,14 @@ import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.io.Text;
-import org.apache.log4j.FileAppender;
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.apache.log4j.PatternLayout;
-
-import com.beust.jcommander.IStringConverter;
-import com.beust.jcommander.Parameter;
 
 public class ContinuousIngest {
 
-  static public class BaseOpts extends MapReduceClientOnDefaultTable {
-    public class DebugConverter implements IStringConverter<String> {
-      @Override
-      public String convert(String debugLog) {
-        Logger logger = Logger.getLogger(Constants.CORE_PACKAGE_NAME);
-        logger.setLevel(Level.TRACE);
-        logger.setAdditivity(false);
-        try {
-          logger.addAppender(new FileAppender(new PatternLayout("%d{dd HH:mm:ss,SSS} [%-8c{2}] %-5p: %m%n"), debugLog, true));
-        } catch (IOException ex) {
-          throw new RuntimeException(ex);
-        }
-        return debugLog;
-      }
-    }
-
-    @Parameter(names = "--min", description = "lowest random row number to use")
-    long min = 0;
-
-    @Parameter(names = "--max", description = "maximum random row number to use")
-    long max = Long.MAX_VALUE;
-
-    @Parameter(names = "--debugLog", description = "file to write debugging output", converter = DebugConverter.class)
-    String debugLog = null;
-
-    BaseOpts() {
-      super("ci");
-    }
-  }
-
-  public static class ShortConverter implements IStringConverter<Short> {
-    @Override
-    public Short convert(String value) {
-      return Short.valueOf(value);
-    }
-  }
-
-  static public class Opts extends BaseOpts {
-    @Parameter(names = "--num", description = "the number of entries to ingest")
-    long num = Long.MAX_VALUE;
-
-    @Parameter(names = "--maxColF", description = "maximum column family value to use", converter = ShortConverter.class)
-    short maxColF = Short.MAX_VALUE;
-
-    @Parameter(names = "--maxColQ", description = "maximum column qualifier value to use", converter = ShortConverter.class)
-    short maxColQ = Short.MAX_VALUE;
-
-    @Parameter(names = "--addCheckSum", description = "turn on checksums")
-    boolean checksum = false;
-
-    @Parameter(names = "--visibilities", description = "read the visibilities to ingest with from a file")
-    String visFile = null;
-  }
-
   private static final byte[] EMPTY_BYTES = new byte[0];
 
   private static List<ColumnVisibility> visibilities;
 
-  private static void initVisibilities(Opts opts) throws Exception {
+  private static void initVisibilities(ContinuousOpts opts) throws Exception {
     if (opts.visFile == null) {
       visibilities = Collections.singletonList(new ColumnVisibility());
       return;
@@ -140,22 +77,23 @@ public class ContinuousIngest {
 
   public static void main(String[] args) throws Exception {
 
-    Opts opts = new Opts();
+    ContinuousOpts opts = new ContinuousOpts();
     BatchWriterOpts bwOpts = new BatchWriterOpts();
-    opts.parseArgs(ContinuousIngest.class.getName(), args, bwOpts);
+    ClientOnDefaultTable clientOpts = new ClientOnDefaultTable("ci");
+    clientOpts.parseArgs(ContinuousIngest.class.getName(), args, bwOpts, opts);
 
     initVisibilities(opts);
 
     if (opts.min < 0 || opts.max < 0 || opts.max <= opts.min) {
       throw new IllegalArgumentException("bad min and max");
     }
-    Connector conn = opts.getConnector();
+    Connector conn = clientOpts.getConnector();
 
-    if (!conn.tableOperations().exists(opts.getTableName())) {
-      throw new TableNotFoundException(null, opts.getTableName(), "Consult the README and create the table before starting ingest.");
+    if (!conn.tableOperations().exists(clientOpts.getTableName())) {
+      throw new TableNotFoundException(null, clientOpts.getTableName(), "Consult the README and create the table before starting ingest.");
     }
 
-    BatchWriter bw = conn.createBatchWriter(opts.getTableName(), bwOpts.getBatchWriterConfig());
+    BatchWriter bw = conn.createBatchWriter(clientOpts.getTableName(), bwOpts.getBatchWriterConfig());
     bw = Trace.wrapAll(bw, new CountSampler(1024));
 
     Random r = new Random();
@@ -233,7 +171,7 @@ public class ContinuousIngest {
     }
 
     bw.close();
-    opts.stopTracing();
+    clientOpts.stopTracing();
   }
 
   private static long flush(BatchWriter bw, long count, final int flushInterval, long lastFlushTime) throws MutationsRejectedException {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousMoru.java
----------------------------------------------------------------------
diff --git a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousMoru.java b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousMoru.java
index 4b5c3e7..48154a6 100644
--- a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousMoru.java
+++ b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousMoru.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import java.util.UUID;
 
 import org.apache.accumulo.core.cli.BatchWriterOpts;
+import org.apache.accumulo.core.cli.MapReduceClientOnDefaultTable;
 import org.apache.accumulo.core.client.AccumuloSecurityException;
 import org.apache.accumulo.core.client.mapreduce.AccumuloInputFormat;
 import org.apache.accumulo.core.client.mapreduce.AccumuloOutputFormat;
@@ -33,8 +34,6 @@ import org.apache.accumulo.core.data.Range;
 import org.apache.accumulo.core.data.Value;
 import org.apache.accumulo.core.security.ColumnVisibility;
 import org.apache.accumulo.core.util.CachedConfiguration;
-import org.apache.accumulo.test.continuous.ContinuousIngest.BaseOpts;
-import org.apache.accumulo.test.continuous.ContinuousIngest.ShortConverter;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.conf.Configured;
 import org.apache.hadoop.io.Text;
@@ -116,7 +115,7 @@ public class ContinuousMoru extends Configured implements Tool {
     }
   }
 
-  static class Opts extends BaseOpts {
+  static class Opts extends ContinuousOpts {
     @Parameter(names = "--maxColF", description = "maximum column family value to use", converter = ShortConverter.class)
     short maxColF = Short.MAX_VALUE;
 
@@ -131,17 +130,18 @@ public class ContinuousMoru extends Configured implements Tool {
   public int run(String[] args) throws IOException, InterruptedException, ClassNotFoundException, AccumuloSecurityException {
     Opts opts = new Opts();
     BatchWriterOpts bwOpts = new BatchWriterOpts();
-    opts.parseArgs(ContinuousMoru.class.getName(), args, bwOpts);
+    MapReduceClientOnDefaultTable clientOpts = new MapReduceClientOnDefaultTable("ci");
+    clientOpts.parseArgs(ContinuousMoru.class.getName(), args, bwOpts, opts);
 
     Job job = Job.getInstance(getConf(), this.getClass().getSimpleName() + "_" + System.currentTimeMillis());
     job.setJarByClass(this.getClass());
 
     job.setInputFormatClass(AccumuloInputFormat.class);
-    opts.setAccumuloConfigs(job);
+    clientOpts.setAccumuloConfigs(job);
 
     // set up ranges
     try {
-      Set<Range> ranges = opts.getConnector().tableOperations().splitRangeByTablets(opts.getTableName(), new Range(), opts.maxMaps);
+      Set<Range> ranges = clientOpts.getConnector().tableOperations().splitRangeByTablets(clientOpts.getTableName(), new Range(), opts.maxMaps);
       AccumuloInputFormat.setRanges(job, ranges);
       AccumuloInputFormat.setAutoAdjustRanges(job, false);
     } catch (Exception e) {
@@ -163,7 +163,7 @@ public class ContinuousMoru extends Configured implements Tool {
     conf.set(CI_ID, UUID.randomUUID().toString());
 
     job.waitForCompletion(true);
-    opts.stopTracing();
+    clientOpts.stopTracing();
     return job.isSuccessful() ? 0 : 1;
   }
 

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousOpts.java
----------------------------------------------------------------------
diff --git a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousOpts.java b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousOpts.java
new file mode 100644
index 0000000..48a77e7
--- /dev/null
+++ b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousOpts.java
@@ -0,0 +1,80 @@
+/*
+ * 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.accumulo.test.continuous;
+
+import java.io.IOException;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
+
+import com.beust.jcommander.IStringConverter;
+import com.beust.jcommander.Parameter;
+
+/**
+ * Common CLI arguments for the Continuous Ingest suite.
+ */
+public class ContinuousOpts {
+
+  public static class DebugConverter implements IStringConverter<String> {
+    @Override
+    public String convert(String debugLog) {
+      Logger logger = Logger.getLogger(Constants.CORE_PACKAGE_NAME);
+      logger.setLevel(Level.TRACE);
+      logger.setAdditivity(false);
+      try {
+        logger.addAppender(new FileAppender(new PatternLayout("%d{dd HH:mm:ss,SSS} [%-8c{2}] %-5p: %m%n"), debugLog, true));
+      } catch (IOException ex) {
+        throw new RuntimeException(ex);
+      }
+      return debugLog;
+    }
+  }
+
+  public static class ShortConverter implements IStringConverter<Short> {
+    @Override
+    public Short convert(String value) {
+      return Short.valueOf(value);
+    }
+  }
+
+  @Parameter(names = "--min", description = "lowest random row number to use")
+  long min = 0;
+
+  @Parameter(names = "--max", description = "maximum random row number to use")
+  long max = Long.MAX_VALUE;
+
+  @Parameter(names = "--debugLog", description = "file to write debugging output", converter = DebugConverter.class)
+  String debugLog = null;
+
+  @Parameter(names = "--num", description = "the number of entries to ingest")
+  long num = Long.MAX_VALUE;
+
+  @Parameter(names = "--maxColF", description = "maximum column family value to use", converter = ShortConverter.class)
+  short maxColF = Short.MAX_VALUE;
+
+  @Parameter(names = "--maxColQ", description = "maximum column qualifier value to use", converter = ShortConverter.class)
+  short maxColQ = Short.MAX_VALUE;
+
+  @Parameter(names = "--addCheckSum", description = "turn on checksums")
+  boolean checksum = false;
+
+  @Parameter(names = "--visibilities", description = "read the visibilities to ingest with from a file")
+  String visFile = null;
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousQuery.java
----------------------------------------------------------------------
diff --git a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousQuery.java b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousQuery.java
index 73048f6..7f89a94 100644
--- a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousQuery.java
+++ b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousQuery.java
@@ -21,20 +21,21 @@ import static java.nio.charset.StandardCharsets.UTF_8;
 import java.util.Map.Entry;
 import java.util.Random;
 
+import org.apache.accumulo.core.cli.ClientOnDefaultTable;
+import org.apache.accumulo.core.cli.ClientOpts.TimeConverter;
 import org.apache.accumulo.core.cli.ScannerOpts;
 import org.apache.accumulo.core.client.Connector;
 import org.apache.accumulo.core.client.Scanner;
 import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Range;
 import org.apache.accumulo.core.data.Value;
-import org.apache.accumulo.test.continuous.ContinuousIngest.BaseOpts;
 import org.apache.hadoop.io.Text;
 
 import com.beust.jcommander.Parameter;
 
 public class ContinuousQuery {
 
-  public static class Opts extends BaseOpts {
+  public static class Opts extends ContinuousOpts {
     @Parameter(names = "--sleep", description = "the time to wait between queries", converter = TimeConverter.class)
     long sleepTime = 100;
   }
@@ -42,10 +43,11 @@ public class ContinuousQuery {
   public static void main(String[] args) throws Exception {
     Opts opts = new Opts();
     ScannerOpts scanOpts = new ScannerOpts();
-    opts.parseArgs(ContinuousQuery.class.getName(), args, scanOpts);
+    ClientOnDefaultTable clientOpts = new ClientOnDefaultTable("ci");
+    clientOpts.parseArgs(ContinuousQuery.class.getName(), args, scanOpts, opts);
 
-    Connector conn = opts.getConnector();
-    Scanner scanner = ContinuousUtil.createScanner(conn, opts.getTableName(), opts.auths);
+    Connector conn = clientOpts.getConnector();
+    Scanner scanner = ContinuousUtil.createScanner(conn, clientOpts.getTableName(), clientOpts.auths);
     scanner.setBatchSize(scanOpts.scanBatchSize);
 
     Random r = new Random();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousScanner.java
----------------------------------------------------------------------
diff --git a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousScanner.java b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousScanner.java
index f68377a..a77de3d 100644
--- a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousScanner.java
+++ b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousScanner.java
@@ -22,6 +22,7 @@ import java.util.Iterator;
 import java.util.Map.Entry;
 import java.util.Random;
 
+import org.apache.accumulo.core.cli.ClientOnDefaultTable;
 import org.apache.accumulo.core.cli.ScannerOpts;
 import org.apache.accumulo.core.client.Connector;
 import org.apache.accumulo.core.client.Scanner;
@@ -45,15 +46,16 @@ public class ContinuousScanner {
   public static void main(String[] args) throws Exception {
     Opts opts = new Opts();
     ScannerOpts scanOpts = new ScannerOpts();
-    opts.parseArgs(ContinuousScanner.class.getName(), args, scanOpts);
+    ClientOnDefaultTable clientOpts = new ClientOnDefaultTable("ci");
+    clientOpts.parseArgs(ContinuousScanner.class.getName(), args, scanOpts, opts);
 
     Random r = new Random();
 
     long distance = 1000000000000l;
 
-    Connector conn = opts.getConnector();
+    Connector conn = clientOpts.getConnector();
     Authorizations auths = opts.randomAuths.getAuths(r);
-    Scanner scanner = ContinuousUtil.createScanner(conn, opts.getTableName(), auths);
+    Scanner scanner = ContinuousUtil.createScanner(conn, clientOpts.getTableName(), auths);
     scanner.setBatchSize(scanOpts.scanBatchSize);
 
     double delta = Math.min(.05, .05 / (opts.numToScan / 1000.0));

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousWalk.java
----------------------------------------------------------------------
diff --git a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousWalk.java b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousWalk.java
index 60f8ec2..f2e4805 100644
--- a/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousWalk.java
+++ b/test/src/main/java/org/apache/accumulo/test/continuous/ContinuousWalk.java
@@ -28,6 +28,7 @@ import java.util.Map.Entry;
 import java.util.Random;
 import java.util.zip.CRC32;
 
+import org.apache.accumulo.core.cli.ClientOnDefaultTable;
 import org.apache.accumulo.core.client.Connector;
 import org.apache.accumulo.core.client.Scanner;
 import org.apache.accumulo.core.data.Key;
@@ -105,16 +106,17 @@ public class ContinuousWalk {
 
   public static void main(String[] args) throws Exception {
     Opts opts = new Opts();
-    opts.parseArgs(ContinuousWalk.class.getName(), args);
+    ClientOnDefaultTable clientOpts = new ClientOnDefaultTable("ci");
+    clientOpts.parseArgs(ContinuousWalk.class.getName(), args, opts);
 
-    Connector conn = opts.getConnector();
+    Connector conn = clientOpts.getConnector();
 
     Random r = new Random();
 
     ArrayList<Value> values = new ArrayList<Value>();
 
     while (true) {
-      Scanner scanner = ContinuousUtil.createScanner(conn, opts.getTableName(), opts.randomAuths.getAuths(r));
+      Scanner scanner = ContinuousUtil.createScanner(conn, clientOpts.getTableName(), opts.randomAuths.getAuths(r));
       String row = findAStartRow(opts.min, opts.max, scanner, r);
 
       while (row != null) {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/test/src/test/java/org/apache/accumulo/harness/MiniClusterHarness.java
----------------------------------------------------------------------
diff --git a/test/src/test/java/org/apache/accumulo/harness/MiniClusterHarness.java b/test/src/test/java/org/apache/accumulo/harness/MiniClusterHarness.java
index 06b4303..e53d686 100644
--- a/test/src/test/java/org/apache/accumulo/harness/MiniClusterHarness.java
+++ b/test/src/test/java/org/apache/accumulo/harness/MiniClusterHarness.java
@@ -70,7 +70,12 @@ public class MiniClusterHarness {
   }
 
   public MiniAccumuloClusterImpl create(AccumuloIT testBase, AuthenticationToken token, TestingKdc kdc) throws Exception {
-    return create(testBase.getClass().getName(), testBase.testName.getMethodName(), token, kdc);
+    return create(testBase, token, kdc, MiniClusterConfigurationCallback.NO_CALLBACK);
+  }
+
+  public MiniAccumuloClusterImpl create(AccumuloIT testBase, AuthenticationToken token, TestingKdc kdc, MiniClusterConfigurationCallback configCallback)
+      throws Exception {
+    return create(testBase.getClass().getName(), testBase.testName.getMethodName(), token, configCallback, kdc);
   }
 
   public MiniAccumuloClusterImpl create(AccumuloClusterIT testBase, AuthenticationToken token, TestingKdc kdc) throws Exception {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/2c983317/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java
----------------------------------------------------------------------
diff --git a/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java b/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java
index 9296548..3ffa40e 100644
--- a/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java
+++ b/test/src/test/java/org/apache/accumulo/test/ShellServerIT.java
@@ -1168,7 +1168,7 @@ public class ShellServerIT extends SharedMiniClusterIT {
   @Test
   public void systempermission() throws Exception {
     ts.exec("systempermissions");
-    assertEquals(11, ts.output.get().split("\n").length - 1);
+    assertEquals(12, ts.output.get().split("\n").length - 1);
     ts.exec("tablepermissions", true);
     assertEquals(6, ts.output.get().split("\n").length - 1);
   }


Mime
View raw message