package com.yammer.breakerbox.turbine;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.netflix.turbine.discovery.Instance;
import com.netflix.turbine.discovery.InstanceDiscovery;
import com.yammer.breakerbox.turbine.client.RancherClient;
import com.yammer.breakerbox.turbine.config.RancherInstanceConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class RancherInstanceDiscovery implements InstanceDiscovery {
private static final Logger LOGGER = LoggerFactory.getLogger(RancherInstanceDiscovery.class);
private final RancherClient rancherClient;
private final ObjectMapper mapper;
public RancherInstanceDiscovery(RancherInstanceConfiguration instanceConfiguration,
ObjectMapper mapper) {
this(new RancherClient(instanceConfiguration), mapper);
}
public RancherInstanceDiscovery(RancherClient rancherClient, ObjectMapper mapper) {
this.rancherClient = rancherClient;
this.mapper = mapper;
}
@Override
public Collection<Instance> getInstanceList() throws Exception {
Response response = rancherClient.getServiceInstanceDetails();
return response.getStatus() == 200
? createServiceInstanceList(response.readEntity(String.class))
: Collections.emptyList();
}
private Collection<Instance> createServiceInstanceList(String rancherServiceApiResponse) throws IOException {
JsonNode serviceJsonResponseNode = mapper.readValue(rancherServiceApiResponse, JsonNode.class);
List<JsonNode> dataList = convertJsonArrayToList((ArrayNode) serviceJsonResponseNode.get("data"));
Collection<Instance> instances = dataList.stream()
.filter(this::isServiceDashboardEnabled)
.map(this::createInstanceList)
.flatMap(Collection::stream)
.collect(Collectors.toList());
instances.addAll(createProductionDashboard(instances));
return instances;
}
private Collection<? extends Instance> createProductionDashboard(Collection<Instance> instances) {
return instances.stream()
.map(instance -> new Instance(instance.getHostname(), "production", true))
.collect(Collectors.toList());
}
private boolean isServiceDashboardEnabled(JsonNode dataNode) {
JsonNode serviceStatus = getRancherLabels(dataNode).get("tenacity.metrics.stream.enabled");
return !Objects.isNull(serviceStatus) ? serviceStatus.asBoolean() : Boolean.FALSE;
}
private List<Instance> createInstanceList(JsonNode dataNode) {
String clusterName = getServiceClusterName(dataNode);
int metricsStreamPort = getServiceMetricsStreamPort(dataNode);
List<JsonNode> publicEndpoints = getPublicEndpoints(dataNode);
return publicEndpoints.stream()
.filter(jsonNodes -> jsonNodes.get("port").asInt() == metricsStreamPort)
.map(jsonNode -> createTurbineInstance(jsonNode, clusterName))
.collect(Collectors.toList());
}
private String getServiceClusterName(JsonNode dataNode) {
JsonNode clusterNameLabel = getRancherLabels(dataNode).get("service.cluster.name");
return !Objects.isNull(clusterNameLabel) ? clusterNameLabel.asText() : dataNode.get("name").asText();
}
private int getServiceMetricsStreamPort(JsonNode dataNode) {
JsonNode portLabel = getRancherLabels(dataNode).get("tenacity.metrics.stream.port");
return !Objects.isNull(portLabel) ? portLabel.asInt() : 8080;
}
private List<JsonNode> getPublicEndpoints(JsonNode objectNode) {
return objectNode.get("publicEndpoints").getNodeType().equals(JsonNodeType.ARRAY)
? convertJsonArrayToList((ArrayNode) objectNode.get("publicEndpoints"))
: Collections.emptyList();
}
private Instance createTurbineInstance(JsonNode jsonNode, String clusterName) {
String hostAndPort = jsonNode.get("ipAddress").asText() + ":" + jsonNode.get("port").asText();
return new Instance(hostAndPort, clusterName, true);
}
private JsonNode getRancherLabels(JsonNode dataNode) {
return dataNode.get("launchConfig").get("labels");
}
private List<JsonNode> convertJsonArrayToList(ArrayNode arrayNode) {
try {
return mapper.readValue(arrayNode.toString(), TypeFactory.defaultInstance()
.constructCollectionType(List.class, JsonNode.class));
} catch (IOException e) {
LOGGER.error("Failed to convert ArrayNode to List<JsonNode>", e);
return Collections.emptyList();
}
}
}