package org.github.etcd.rest;
import java.util.List;
import javax.xml.bind.DatatypeConverter;
import org.github.etcd.service.rest.EtcdException;
import org.github.etcd.service.rest.EtcdMember;
import org.github.etcd.service.rest.EtcdNode;
import org.github.etcd.service.rest.EtcdProxy;
import org.github.etcd.service.rest.EtcdSelfStats;
import org.github.etcd.service.rest.impl.EtcdProxyImpl;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestEtcdProxy extends Assert {
private static final Logger log = LoggerFactory.getLogger(TestEtcdProxy.class);
private static EtcdProxy etcdProxy;
private static final String JUNIT_ROOT = "/junit_" + System.currentTimeMillis();
private static EtcdSimulator simulator;
@BeforeClass
public static void setUp() {
boolean hasClientURL = System.getProperty("etcd.clientURL") != null;
String clientURL = System.getProperty("etcd.clientURL", "http://localhost:2379/");
// clientURL = "http://192.168.122.103:4001/";
String username = "root";
String password = "root";
String token = DatatypeConverter.printBase64Binary((username + ":" + password).getBytes());
etcdProxy = new EtcdProxyImpl(clientURL, token);
// just check accessibility of etcd server
// if it is not reachable for any reason then
// start an inner simulator to do the tests
try {
etcdProxy.getVersion();
} catch (Exception e) {
if (!hasClientURL) {
simulator = new EtcdSimulator();
simulator.start();
}
}
}
@AfterClass
public static void tearDown() throws Exception {
if (etcdProxy != null) {
etcdProxy.close();
}
if (simulator != null) {
simulator.stop();
}
}
@Test
public void testGetVersion() {
String version = etcdProxy.getVersion();
assertNotNull(version);
assertTrue("Version: " + version + " does not contain etcd", version.contains("etcd"));
}
@Test
public void testGetSelfStatistics() {
EtcdSelfStats selfStats = etcdProxy.getSelfStats();
log.info("Self Statistics: " + selfStats);
assertNotNull(selfStats);
// id is null on etcd v0.4.9
assertNotNull(selfStats.getName());
assertNotNull(selfStats.getLeaderInfo());
}
@Test
public void testGetMembers() {
List<EtcdMember> members = etcdProxy.getMembers();
assertNotNull(members);
assertFalse(members.isEmpty());
StringBuffer sb = new StringBuffer(256);
for (EtcdMember m : members) {
sb.append('\n');
sb.append(m.toString());
}
log.info("The cluster members are:" + sb.toString());
}
@Test
public void testGetRoot() {
EtcdNode root = etcdProxy.getNode("/");
log.info("Retrieved: " + root);
assertNotNull(root);
assertTrue("/".equals(root.getKey()) || root.getKey() == null);
assertTrue(root.isDir());
}
private String createTestKey() {
String testName = new Throwable().getStackTrace()[1].getMethodName();
return JUNIT_ROOT + "/" + testName;
}
@Test
public void testSaveAndGetValueNode() {
String key = createTestKey();
String value = "some_test_content";
EtcdNode toSave = new EtcdNode(key, value);
try {
etcdProxy.saveNode(toSave);
} catch (Exception e) {
fail("Failed to save node: " + toSave);
}
EtcdNode retrieved = etcdProxy.getNode(key);
assertEquals(key, retrieved.getKey());
assertEquals(value, retrieved.getValue());
assertEquals(false, retrieved.isDir());
assertNull(retrieved.getTtl());
}
@Test
public void testSaveAndGetDirectory() {
String key = createTestKey();
EtcdNode toSave = new EtcdNode(key);
try {
etcdProxy.saveNode(toSave);
} catch (Exception e) {
fail("Failed to save node: " + toSave);
}
EtcdNode retrieved = etcdProxy.getNode(key);
assertEquals(key, retrieved.getKey());
assertEquals(true, retrieved.isDir());
assertNull(retrieved.getTtl());
}
@Test
public void testDeleteValue() {
String key = createTestKey();
etcdProxy.saveNode(new EtcdNode(key, "foobar"));
EtcdNode retrieved = etcdProxy.getNode(key);
assertNotNull(retrieved);
assertFalse(retrieved.isDir());
assertEquals(key, retrieved.getKey());
EtcdNode deleted = etcdProxy.deleteNode(retrieved);
assertNotNull(deleted);
assertEquals(key, deleted.getKey());
try {
retrieved = etcdProxy.getNode(key);
fail("Node: " + key + " should be deleted");
} catch (Exception e) {
}
}
@Test
public void testDeleteDirectory() {
String key = createTestKey();
etcdProxy.saveNode(new EtcdNode(key));
EtcdNode retrieved = etcdProxy.getNode(key);
assertNotNull(retrieved);
assertTrue(retrieved.isDir());
assertEquals(key, retrieved.getKey());
EtcdNode deleted = etcdProxy.deleteNode(retrieved);
assertNotNull(deleted);
assertEquals(key, deleted.getKey());
try {
retrieved = etcdProxy.getNode(key);
fail("Node: " + key + " should be deleted");
} catch (Exception e) {
}
}
@Test
public void testDeleteNonEmptyDirectory() {
String key = createTestKey();
etcdProxy.saveNode(new EtcdNode(key));
etcdProxy.saveNode(new EtcdNode(key + "/hello", "hello world"));
EtcdNode retrieved = etcdProxy.getNode(key);
assertNotNull(retrieved);
assertTrue(retrieved.isDir());
assertEquals(key, retrieved.getKey());
EtcdNode deleted = null;
try {
deleted = etcdProxy.deleteNode(retrieved);
fail("Should not delete non empty directories without recursive=true");
} catch (EtcdException e) {
log.warn(e.getLocalizedMessage() + " API: " + e.getApiError(), e);
}
assertNull(deleted);
assertNotNull(etcdProxy.getNode(key));
}
@Test
public void testDeleteDirectoryRecursive() {
String key = createTestKey();
etcdProxy.saveNode(new EtcdNode(key));
etcdProxy.saveNode(new EtcdNode(key + "/hello", "hello world"));
EtcdNode retrieved = etcdProxy.getNode(key);
assertNotNull(retrieved);
assertTrue(retrieved.isDir());
assertEquals(key, retrieved.getKey());
EtcdNode deleted = etcdProxy.deleteNode(retrieved, true);
assertNotNull(deleted);
assertEquals(key, deleted.getKey());
try {
retrieved = etcdProxy.getNode(key);
fail("Node: " + key + " should be deleted");
} catch (Exception e) {
}
}
@Test
public void testCreateExpiringValue() throws Exception {
String key = createTestKey();
String value = "some_test_content";
Long ttl = 2L;
EtcdNode toSave = new EtcdNode(key, value, ttl);
try {
etcdProxy.saveNode(toSave);
} catch (Exception e) {
fail("Failed to save node: " + toSave);
}
EtcdNode retrieved = etcdProxy.getNode(key);
log.info("Retrieved: " + retrieved);
assertEquals(key, retrieved.getKey());
assertEquals(value, retrieved.getValue());
assertEquals(false, retrieved.isDir());
assertNotNull(retrieved.getTtl());
// wait for node to expire
Thread.sleep(ttl * 1000L + 500L);
try {
retrieved = etcdProxy.getNode(key);
fail("Node: " + retrieved + " should have expire by now");
} catch (EtcdException e) {
log.info("Node is expired as expected: " + e.getLocalizedMessage() + " - " + e.getApiError(), e);
}
}
@Test
public void testCreateValueNodeTwice() {
String key = createTestKey();
String value = "some_test_content";
etcdProxy.saveNode(new EtcdNode(key, value));
try {
etcdProxy.saveNode(new EtcdNode(key, value));
fail("Save again with the same key should fail. We should update instead.");
} catch (EtcdException e) {
log.info("Node already exists. Update instead: " + e.getLocalizedMessage() + " - " + e.getApiError(), e);
}
}
@Test
public void testUpdateValue() {
String key = createTestKey();
String[] values = new String[] { "initial value", "first update", "second update", "third update" };
etcdProxy.saveNode(new EtcdNode(key, values[0]));
EtcdNode retrieved = etcdProxy.getNode(key);
assertNotNull(retrieved);
assertEquals(key, retrieved.getKey());
assertEquals(values[0], retrieved.getValue());
for (int i=1; i<values.length; i++) {
EtcdNode previous = etcdProxy.updateNode(new EtcdNode(key, values[i]));
assertNotNull(previous);
assertEquals(key, previous.getKey());
assertEquals(values[i-1], previous.getValue());
retrieved = etcdProxy.getNode(key);
assertNotNull(retrieved);
assertEquals(key, retrieved.getKey());
assertEquals(values[i], retrieved.getValue());
}
}
@Test
public void testRemoveDirectoryTTL() throws Exception {
if (etcdProxy.getVersion().contains("etcd 0.4")) {
// skip this test
return;
}
String key = createTestKey();
Long ttl = 3L;
etcdProxy.saveNode(new EtcdNode(key, ttl));
Thread.sleep(1000L);
EtcdNode previous = null;
try {
previous = etcdProxy.updateNode(new EtcdNode(key));
} catch (EtcdException e) {
log.error(e.getLocalizedMessage() + " - " + e.getApiError(), e);
fail(e.getLocalizedMessage() + " - " + e.getApiError());
}
assertNotNull(previous);
assertEquals(key, previous.getKey());
assertNotNull(previous.getTtl());
assertTrue(previous.getTtl() > 0);
Thread.sleep(ttl * 1000);
EtcdNode updated = etcdProxy.getNode(key);
assertNotNull(updated);
assertEquals(key, updated.getKey());
assertNull(updated.getTtl());
}
@Test
public void testCreateDirectoryTwice() {
String key = createTestKey();
etcdProxy.saveNode(new EtcdNode(key));
try {
etcdProxy.saveNode(new EtcdNode(key));
fail("Save again with the same key should fail. We should update instead.");
} catch (EtcdException e) {
log.info("Node already exists. Update instead: " + e.getLocalizedMessage() + " - " + e.getApiError(), e);
}
}
@Test
public void testAddDirectoryTTL() throws Exception {
String key = createTestKey();
Long ttl = 3L;
etcdProxy.saveNode(new EtcdNode(key));
EtcdNode previous = null;
try {
previous = etcdProxy.updateNode(new EtcdNode(key, ttl));
} catch (EtcdException e) {
fail(e.getLocalizedMessage() + " - " + e.getApiError());
}
assertNotNull(previous);
assertEquals(key, previous.getKey());
assertTrue(previous.isDir());
EtcdNode current = etcdProxy.getNode(key);
assertNotNull(current);
assertNotNull(current.getTtl());
assertTrue(current.getTtl() > 0);
Thread.sleep(ttl * 1000 + 500);
try {
EtcdNode retrieved = etcdProxy.getNode(key);
fail("Node: " + retrieved + " should have expire by now");
} catch (EtcdException e) {
log.info("Node is expired as expected: " + e.getLocalizedMessage() + " - " + e.getApiError(), e);
}
}
}