package com.sequenceiq.periscope.utils; import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils; import com.ecwid.consul.v1.ConsulClient; import com.ecwid.consul.v1.ConsulRawClient; import com.ecwid.consul.v1.OperationException; import com.ecwid.consul.v1.QueryParams; import com.ecwid.consul.v1.agent.model.Member; import com.ecwid.consul.v1.catalog.model.CatalogService; import com.ecwid.consul.v1.event.model.Event; import com.ecwid.consul.v1.event.model.EventParams; import com.ecwid.consul.v1.kv.model.GetValue; import com.ecwid.consul.v1.kv.model.PutParams; import com.sequenceiq.periscope.model.TlsConfiguration; public final class ConsulUtils { private static final Logger LOGGER = LoggerFactory.getLogger(ConsulUtils.class); private static final int DEFAULT_TIMEOUT_MS = 5000; private static final int ALIVE_STATUS = 1; private static final int LEFT_STATUS = 3; private ConsulUtils() { throw new IllegalStateException(); } public static List<CatalogService> getService(List<ConsulClient> clients, String serviceName) { for (ConsulClient consul : clients) { List<CatalogService> service = getService(consul, serviceName); if (!service.isEmpty()) { return service; } } return Collections.emptyList(); } public static List<CatalogService> getService(ConsulClient client, String serviceName) { try { return client.getCatalogService(serviceName, QueryParams.DEFAULT).getValue(); } catch (Exception e) { return Collections.emptyList(); } } public static Map<String, String> getAliveMembers(List<ConsulClient> clients) { return getMembers(clients, ALIVE_STATUS); } public static Map<String, String> getLeftMembers(List<ConsulClient> clients) { return getMembers(clients, LEFT_STATUS); } public static Map<String, String> getMembers(List<ConsulClient> clients, int status) { for (ConsulClient client : clients) { Map<String, String> members = getMembers(client, status); if (!members.isEmpty()) { return members; } } return Collections.emptyMap(); } public static Map<String, String> getMembers(ConsulClient client, int status) { try { Map<String, String> result = new HashMap<>(); List<Member> members = client.getAgentMembers().getValue(); for (Member member : members) { if (member.getStatus() == status) { result.put(member.getAddress(), member.getName()); } } return result; } catch (Exception e) { return Collections.emptyMap(); } } public static String fireEvent(List<ConsulClient> clients, String event, String payload, EventParams eventParams, QueryParams queryParams) { for (ConsulClient client : clients) { String eventId = fireEvent(client, event, payload, eventParams, queryParams); if (eventId != null) { return eventId; } } return null; } public static String fireEvent(ConsulClient client, String event, String payload, EventParams eventParams, QueryParams queryParams) { try { Event response = client.eventFire(event, payload, eventParams, queryParams).getValue(); return response.getId(); } catch (OperationException e) { LOGGER.info("Failed to fire Consul event '{}'. Status code: {}, Message: {}", event, e.getStatusCode(), e.getStatusMessage()); return null; } catch (Exception e) { LOGGER.info("Failed to fire Consul event '{}'. Message: {}", event, e.getMessage()); return null; } } public static String getKVValue(List<ConsulClient> clients, String key, QueryParams queryParams) { for (ConsulClient client : clients) { String value = getKVValue(client, key, queryParams); if (value != null) { return value; } } return null; } public static String getKVValue(ConsulClient client, String key, QueryParams queryParams) { try { GetValue getValue = client.getKVValue(key, queryParams).getValue(); return getValue == null ? null : new String(Base64.decodeBase64(getValue.getValue())); } catch (OperationException e) { LOGGER.info("Failed to get entry '{}' from Consul's key-value store. Status code: {}, Message: {}", key, e.getStatusCode(), e.getStatusMessage()); return null; } catch (Exception e) { LOGGER.info("Failed to get entry '{}' from Consul's key-value store. Error message: {}", key, e.getMessage()); return null; } } public static boolean putKVValue(List<ConsulClient> clients, String key, String value, PutParams putParams) { for (ConsulClient client : clients) { boolean result = putKVValue(client, key, value, putParams); if (result) { return true; } } return false; } public static Boolean putKVValue(ConsulClient client, String key, String value, PutParams putParams) { try { return client.setKVValue(key, value, putParams).getValue(); } catch (OperationException e) { LOGGER.info("Failed to put entry '{}' in Consul's key-value store. Status code: {}, Message: {}", key, e.getStatusCode(), e.getStatusMessage()); return false; } catch (Exception e) { LOGGER.info("Failed to put entry '{}' in Consul's key-value store. Error message: {}", key, e.getMessage()); return false; } } public static ConsulClient createClient(String apiAddress, String apiPort, TlsConfiguration tlsConfiguration) { return createClient(apiAddress, Integer.valueOf(apiPort), tlsConfiguration, DEFAULT_TIMEOUT_MS); } public static ConsulClient createClient(String apiAddress, int apiPort, TlsConfiguration tlsConfiguration, int timeout) { ConsulRawClient rawClient = new ConsulRawClient("https://" + apiAddress, apiPort, tlsConfiguration.getClientCertPath(), tlsConfiguration.getClientKeyPath(), tlsConfiguration.getServerCertPath(), timeout); Field agentAddress = ReflectionUtils.findField(ConsulRawClient.class, "agentAddress"); ReflectionUtils.makeAccessible(agentAddress); ReflectionUtils.setField(agentAddress, rawClient, "https://" + apiAddress + ":" + apiPort + "/consul"); return new ConsulClient(rawClient); } public static void agentForceLeave(List<ConsulClient> clients, String nodeName) { for (ConsulClient client : clients) { try { client.agentForceLeave(nodeName); } catch (Exception e) { return; } } } public static int getConsulServerCount(int nodeCount) { if (nodeCount < ConsulServers.SINGLE_NODE_COUNT_LOW.getMax()) { return ConsulServers.SINGLE_NODE_COUNT_LOW.getConsulServerCount(); } else if (nodeCount < ConsulServers.NODE_COUNT_LOW.getMax()) { return ConsulServers.NODE_COUNT_LOW.getConsulServerCount(); } else if (nodeCount < ConsulServers.NODE_COUNT_MEDIUM.getMax()) { return ConsulServers.NODE_COUNT_MEDIUM.getConsulServerCount(); } else { return ConsulServers.NODE_COUNT_HIGH.getConsulServerCount(); } } public enum ConsulServers { SINGLE_NODE_COUNT_LOW(1, 2, 1), NODE_COUNT_LOW(3, 1000, 3), NODE_COUNT_MEDIUM(1001, 5000, 5), NODE_COUNT_HIGH(5001, 100_000, 7); private final int min; private final int max; private final int consulServerCount; ConsulServers(int min, int max, int consulServerCount) { this.min = min; this.max = max; this.consulServerCount = consulServerCount; } public int getMin() { return min; } public int getMax() { return max; } public int getConsulServerCount() { return consulServerCount; } } }