package com.hubspot.baragon;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import com.google.common.base.Optional;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.hubspot.baragon.client.BaragonServiceClient;
import com.hubspot.baragon.models.BaragonAgentMetadata;
import com.hubspot.baragon.models.BaragonRequest;
import com.hubspot.baragon.models.BaragonRequestState;
import com.hubspot.baragon.models.BaragonResponse;
import com.hubspot.baragon.models.BaragonService;
import com.hubspot.baragon.models.BaragonServiceState;
import com.hubspot.baragon.models.BaragonServiceStatus;
import com.hubspot.baragon.models.UpstreamInfo;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Stopwatch;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
public class BaragonServiceTestIT {
public static final String LOAD_BALANCER_GROUP = "docker-baragon";
public static final String UPSTREAM = "example.com:80";
public static final String SERVICE_BASE_PATH = "/testservice";
public static final int MAX_REQUEST_RETRIES = 15;
public static final List<BaragonRequestState> TERMINAL_STATES = (Arrays.asList(BaragonRequestState.SUCCESS, BaragonRequestState.INVALID_REQUEST_NOOP, BaragonRequestState.CANCELED));
private BaragonServiceClient baragonServiceClient;
private static final Logger LOG = LoggerFactory.getLogger(BaragonServiceTestIT.class);
// Helpers --------------------------------------------
private Injector getInjector() {
return Guice.createInjector(new DockerTestModule());
}
private String setupTestService() {
String requestId = "Service" + new Date().getTime();
setupTestService(requestId, requestId, SERVICE_BASE_PATH, Collections.<String>emptyList(), Optional.<String>absent(), Optional.<Map<String, Object>>absent(), Optional.<String>absent());
return requestId;
}
private void setupTestService(String requestId, String serviceId, String basePath, List<String> additionalPaths, Optional<String> replaceServiceId, Optional<Map<String, Object>> options, Optional<String> template) {
UpstreamInfo upstream = new UpstreamInfo(UPSTREAM, Optional.<String>absent(), Optional.<String>absent());
BaragonService lbService = new BaragonService(serviceId, new ArrayList<String>(), basePath, additionalPaths,
new HashSet<String>(Arrays.asList(LOAD_BALANCER_GROUP)), options.isPresent() ? options.get() : new HashMap<String, Object>(), template, Collections.<String>emptySet());
BaragonRequest request = new BaragonRequest(requestId, lbService, Arrays.asList(upstream), new ArrayList<UpstreamInfo>(), replaceServiceId);
baragonServiceClient.enqueueRequest(request);
}
private void removeService(String serviceId) throws Exception {
LOG.info("Cleaning up...");
if(!baragonServiceClient.getServiceState(serviceId).isPresent())
return;
BaragonResponse resp = baragonServiceClient.deleteService(serviceId).get();
waitForStatus(resp.getLoadBalancerRequestId(), BaragonRequestState.SUCCESS);
assertFalse(baragonServiceClient.getServiceState(serviceId).isPresent());
}
public void waitForStatus(String requestId, BaragonRequestState targetState) throws Exception {
LOG.info("Waiting...");
int retries = 1;
for(BaragonRequestState state = baragonServiceClient.getRequest(requestId).get().getLoadBalancerState();
!state.equals(targetState)
&& !TERMINAL_STATES.contains(state)
&& retries <= MAX_REQUEST_RETRIES;
retries++) {
Thread.sleep((long) (1000 + (Math.pow(1.8, retries) * 10)));
state = baragonServiceClient.getRequest(requestId).get().getLoadBalancerState();
}
BaragonResponse resp = baragonServiceClient.getRequest(requestId).get();
if (!resp.getLoadBalancerState().equals(targetState)) {
throw new Exception("Request " + requestId + " stuck while waiting to reach " + targetState + ": \n" +
resp.getLoadBalancerState() + " - " + resp.getMessage());
}
}
// ----------------------------------------------------
@Before
public void setup() throws Exception {
baragonServiceClient = getInjector().getInstance(BaragonServiceClient.class);
}
@Rule
public TestRule watcher = new TestWatcher() {
protected void starting(Description description) {
LOG.info("\nStarting: " + description.getMethodName());
}
protected void succeeded(Description description) {
LOG.info("\u001B[32mTest passed\u001B[0m");
}
protected void failed(Throwable e, Description description) {
LOG.info("\u001B[31mTest failed\u001B[0m");
}
};
@Rule
public Stopwatch stopwatch = new Stopwatch() {
@Override
protected void finished(long nanos, Description description) {
LOG.info("(" + nanos / 1000000000.0 + " s)");
}
};
@After
public void teardown() throws Exception {
for(BaragonServiceState state :baragonServiceClient.getGlobalState()) {
removeService(state.getService().getServiceId());
}
baragonServiceClient = null;
}
// ----------------------------------------------------
@Test
public void testStatus() throws Exception {
Optional<BaragonServiceStatus> status = baragonServiceClient.getAnyBaragonServiceStatus();
assertTrue(status.get().isLeader());
}
@Test
public void testWorkers() throws Exception {
assertFalse(baragonServiceClient.getBaragonServiceWorkers().isEmpty());
}
@Test
public void testLoadBalancers() throws Exception {
assertFalse(baragonServiceClient.getLoadBalancerGroups().isEmpty());
}
@Test
public void testClusterAgents() throws Exception {
BaragonAgentMetadata[] metadata = baragonServiceClient.getLoadBalancerGroupAgentMetadata(LOAD_BALANCER_GROUP).toArray(new BaragonAgentMetadata[0]);
for(BaragonAgentMetadata md : metadata)
assertTrue(md.getBaseAgentUri() != null && !md.getBaseAgentUri().isEmpty());
}
@Test
public void testClusterKnownAgents() throws Exception {
BaragonAgentMetadata metadata = baragonServiceClient.getLoadBalancerGroupKnownAgentMetadata(LOAD_BALANCER_GROUP).toArray(new BaragonAgentMetadata[0])[0];
assertTrue(metadata.getBaseAgentUri() != null && !metadata.getBaseAgentUri().isEmpty());
testDeleteKnownAgent();
}
// ----------------------------------------------------
@Test
public void testValidRequest() throws Exception {
String serviceId = setupTestService();
waitForStatus(serviceId, BaragonRequestState.SUCCESS);
testGetService(serviceId);
testState(serviceId);
testClusterBasePaths(serviceId);
}
public void testClusterBasePaths(String serviceId) {
Collection<String> basePaths = baragonServiceClient.getOccupiedBasePaths(LOAD_BALANCER_GROUP);
assertTrue(basePaths.size() > 0);
Optional<BaragonService> service = baragonServiceClient.getServiceForBasePath(LOAD_BALANCER_GROUP, basePaths.iterator().next());
assertTrue(service.isPresent());
assertEquals(service.get().getServiceId(), serviceId);
}
public void testGetService(String serviceId) {
Optional<BaragonService> service = baragonServiceClient.getServiceForBasePath(LOAD_BALANCER_GROUP, SERVICE_BASE_PATH);
assertTrue(service.isPresent());
assertEquals(service.get().getServiceId(), serviceId);
}
public void testState(String serviceId) {
Optional<BaragonServiceState> serviceState = baragonServiceClient.getServiceState(serviceId);
assertTrue(serviceState.isPresent());
assertEquals(serviceState.get().getService().getServiceBasePath(), SERVICE_BASE_PATH);
}
// ----------------------------------------------------
@Test
public void testValidPathChange() throws Exception {
String requestId = setupTestService();
waitForStatus(requestId, BaragonRequestState.SUCCESS);
setupTestService(requestId + "-2", requestId, SERVICE_BASE_PATH + "-2", Collections.<String>emptyList(), Optional.of(requestId), Optional.<Map<String, Object>>absent(), Optional.<String>absent());
waitForStatus(requestId + "-2", BaragonRequestState.SUCCESS);
assertTrue(baragonServiceClient.getOccupiedBasePaths(LOAD_BALANCER_GROUP).contains(SERVICE_BASE_PATH + "-2"));
}
@Test
public void testValidRequestNonexistantReplaceId() throws Exception {
String requestId = "Service" + new Date().getTime();
setupTestService(requestId, requestId, SERVICE_BASE_PATH, Collections.<String>emptyList(), Optional.of("someotherservice"), Optional.<Map<String, Object>>absent(), Optional.<String>absent());
waitForStatus(requestId, BaragonRequestState.SUCCESS);
}
@Test
public void testBasePathConflict() throws Exception {
String serviceId = setupTestService();
waitForStatus(serviceId, BaragonRequestState.SUCCESS);
String serviceId2 = setupTestService();
waitForStatus(serviceId2, BaragonRequestState.INVALID_REQUEST_NOOP);
}
@Test
public void testBasePathConflictInvalidReplaceId() throws Exception {
String serviceId = setupTestService();
waitForStatus(serviceId, BaragonRequestState.SUCCESS);
String serviceId2 = serviceId + "-2";
setupTestService(serviceId2, serviceId2, SERVICE_BASE_PATH, Collections.<String>emptyList(), Optional.of("someotherservice"), Optional.<Map<String, Object>>absent(), Optional.<String>absent());
waitForStatus(serviceId2, BaragonRequestState.INVALID_REQUEST_NOOP);
}
@Test
public void testAdditionalPathConflict() throws Exception {
String serviceId = setupTestService();
waitForStatus(serviceId, BaragonRequestState.SUCCESS);
String serviceId2 = serviceId + "-2";
setupTestService(serviceId2, serviceId2, "/some-other-path", Collections.singletonList(SERVICE_BASE_PATH), Optional.of("someotherservice"), Optional.<Map<String, Object>>absent(), Optional.<String>absent());
waitForStatus(serviceId2, BaragonRequestState.INVALID_REQUEST_NOOP);
}
@Test
public void testValidBasePathRename() throws Exception {
String serviceId = setupTestService();
waitForStatus(serviceId, BaragonRequestState.SUCCESS);
String renameId = serviceId + "-2";
setupTestService(renameId, renameId, SERVICE_BASE_PATH + "-2", Collections.<String>emptyList(), Optional.of(serviceId), Optional.<Map<String, Object>>absent(), Optional.<String>absent());
waitForStatus(renameId, BaragonRequestState.SUCCESS);
assertTrue(baragonServiceClient.getOccupiedBasePaths(LOAD_BALANCER_GROUP).iterator().next().equals(SERVICE_BASE_PATH + "-2"));
}
@Test
public void testValidBasePathServiceIdChange() throws Exception {
String serviceId = setupTestService();
waitForStatus(serviceId, BaragonRequestState.SUCCESS);
String renameId = serviceId + "-2";
setupTestService(renameId, renameId, SERVICE_BASE_PATH, Collections.<String>emptyList(), Optional.of(serviceId), Optional.<Map<String, Object>>absent(), Optional.<String>absent());
waitForStatus(renameId, BaragonRequestState.SUCCESS);
assertTrue(baragonServiceClient.getServiceState(renameId).get().getService().getServiceBasePath().equals(SERVICE_BASE_PATH));
}
@Test
public void testInvalidRequest() throws Exception {
String requestId = "Service" + new Date().getTime();
HashMap<String, Object> options = new HashMap<String, Object>();
options.put("nginxExtraConfigs", new String[] {"rewrite /this_is_invalid_yo"});
setupTestService(requestId, requestId, SERVICE_BASE_PATH, Collections.<String>emptyList(), Optional.<String>absent(), Optional.<Map<String, Object>>of(options), Optional.<String>absent());
waitForStatus(requestId, BaragonRequestState.FAILED);
BaragonResponse response = baragonServiceClient.getRequest(requestId).get();
assertEquals(response.getLoadBalancerState(), BaragonRequestState.FAILED);
assertEquals(response.getAgentResponses().get().get("REVERT").iterator().next().getStatusCode().get(), new Integer(200));
}
@Test
public void testInvalidRequestNonexistantReplaceId() throws Exception {
String requestId = "Service" + new Date().getTime();
HashMap<String, Object> options = new HashMap<String, Object>();
options.put("nginxExtraConfigs", new String[] {"rewrite /this_is_invalid_yo"});
setupTestService(requestId, requestId, SERVICE_BASE_PATH, Collections.<String>emptyList(), Optional.of("someotherservice"), Optional.<Map<String, Object>>of(options), Optional.<String>absent());
waitForStatus(requestId, BaragonRequestState.FAILED);
BaragonResponse response = baragonServiceClient.getRequest(requestId).get();
assertEquals(response.getLoadBalancerState(), BaragonRequestState.FAILED);
assertEquals(response.getAgentResponses().get().get("REVERT").iterator().next().getStatusCode().get(), new Integer(200));
}
@Test
public void testInvalidRequestWithBasePathChange() throws Exception {
String requestId = setupTestService();
waitForStatus(requestId, BaragonRequestState.SUCCESS);
String requestId2 = "Service" + new Date().getTime();
HashMap<String, Object> options = new HashMap<String, Object>();
options.put("nginxExtraConfigs", new String[] {"rewrite /this_is_invalid_yo"});
setupTestService(requestId2, requestId2, SERVICE_BASE_PATH, Collections.<String>emptyList(), Optional.of(requestId), Optional.<Map<String, Object>>of(options), Optional.<String>absent());
waitForStatus(requestId2, BaragonRequestState.FAILED);
BaragonResponse response2 = baragonServiceClient.getRequest(requestId2).get();
assertEquals(response2.getLoadBalancerState(), BaragonRequestState.FAILED);
assertEquals(response2.getAgentResponses().get().get("REVERT").iterator().next().getStatusCode().get(), new Integer(200));
}
@Test
public void testInvalidTemplateName() throws Exception {
String requestId = "Service" + new Date().getTime();
setupTestService(requestId, requestId, SERVICE_BASE_PATH, Collections.<String>emptyList(), Optional.<String>absent(), Optional.<Map<String, Object>>absent(), Optional.of("invalidtemplatename"));
waitForStatus(requestId, BaragonRequestState.INVALID_REQUEST_NOOP);
assertTrue(baragonServiceClient.getRequest(requestId).get().getLoadBalancerState().equals(BaragonRequestState.INVALID_REQUEST_NOOP));
}
@Test
public void testCancelledRequest() throws Exception {
String serviceId = setupTestService();
waitForStatus(serviceId, BaragonRequestState.SUCCESS);
String newId = serviceId + "-2";
setupTestService(newId, serviceId, SERVICE_BASE_PATH, Collections.<String>emptyList(), Optional.<String>absent(), Optional.<Map<String, Object>>absent(), Optional.<String>absent());
BaragonResponse response = baragonServiceClient.getRequest(newId).get();
assertEquals(response.getLoadBalancerState(), BaragonRequestState.WAITING);
baragonServiceClient.cancelRequest(newId);
waitForStatus(newId, BaragonRequestState.CANCELED);
assertEquals(baragonServiceClient.getRequest(newId).get().getLoadBalancerState(), BaragonRequestState.CANCELED);
assertEquals(baragonServiceClient.getServiceState(serviceId).get().getUpstreams().iterator().next().getUpstream(), UPSTREAM);
}
// ----------------------------------------------------
@Test
public void testDeleteBasePath() throws Exception {
assertFalse(baragonServiceClient.getOccupiedBasePaths(LOAD_BALANCER_GROUP).contains(SERVICE_BASE_PATH));
String serviceId = setupTestService();
waitForStatus(serviceId, BaragonRequestState.SUCCESS);
assertTrue(baragonServiceClient.getOccupiedBasePaths(LOAD_BALANCER_GROUP).contains(SERVICE_BASE_PATH));
baragonServiceClient.clearBasePath(LOAD_BALANCER_GROUP, SERVICE_BASE_PATH);
assertFalse(baragonServiceClient.getOccupiedBasePaths(LOAD_BALANCER_GROUP).contains(SERVICE_BASE_PATH));
}
//@Test -- No way to add the agent back after, need to run after testClusterKnownAgents for now.
public void testDeleteKnownAgent() {
Collection<BaragonAgentMetadata> knownAgents = baragonServiceClient.getLoadBalancerGroupKnownAgentMetadata(LOAD_BALANCER_GROUP);
assertTrue(knownAgents.size() > 0);
BaragonAgentMetadata agentToRemove = knownAgents.iterator().next();
baragonServiceClient.deleteLoadBalancerGroupKnownAgent(LOAD_BALANCER_GROUP, agentToRemove.getAgentId());
assertFalse(baragonServiceClient.getLoadBalancerGroupKnownAgentMetadata(LOAD_BALANCER_GROUP).contains(agentToRemove));
}
}