package org.github.etcd.service.impl; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import org.github.etcd.service.ClusterManager; import org.github.etcd.service.EtcdCluster; import org.github.etcd.service.EtcdProxyFactory; import org.github.etcd.service.rest.EtcdMember; import org.github.etcd.service.rest.EtcdProxy; import org.github.etcd.service.rest.EtcdSelfStats; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClusterManagerImpl implements ClusterManager { private static final Logger log = LoggerFactory.getLogger(ClusterManager.class); private static final Comparator<EtcdMember> MEMBER_SORTER = new Comparator<EtcdMember>() { @Override public int compare(EtcdMember o1, EtcdMember o2) { return o1.getName().compareTo(o2.getName()); } }; private static final Map<String, String> STATE_MAPPINGS = new HashMap<>(); static { STATE_MAPPINGS.put("leader", "leader"); STATE_MAPPINGS.put("follower", "follower"); STATE_MAPPINGS.put("StateLeader", "leader"); STATE_MAPPINGS.put("StateFollower", "follower"); } @Inject private EtcdProxyFactory proxyFactory; private Map<String, EtcdCluster> clusters = Collections.synchronizedMap(new LinkedHashMap<String, EtcdCluster>()); private static final String DEFAULT_ETCD_CLIENT = "ETCD_CLIENT_URL"; public ClusterManagerImpl() { String etcdAddress = System.getenv(DEFAULT_ETCD_CLIENT); if (etcdAddress == null) { etcdAddress = System.getProperty(DEFAULT_ETCD_CLIENT, "http://localhost:2379/"); } addCluster("default", etcdAddress); // addCluster("kvm", "http://192.168.122.201:2379/"); } @Override public boolean exists(String name) { return clusters.containsKey(name); } @Override public EtcdCluster getCluster(String name) { return name == null ? null : clusters.get(name); } @Override public EtcdCluster addCluster(String name, String etcdPeerAddress) { EtcdCluster cluster = new EtcdCluster(name, etcdPeerAddress); clusters.put(name, cluster); return cluster; } @Override public void removeCluster(String name) { clusters.remove(name); } @Override public List<EtcdCluster> getClusters() { return new ArrayList<>(clusters.values()); } @Override public List<String> getClusterNames() { return new ArrayList<>(clusters.keySet()); } @Override public void refreshCluster(String name) { if (name == null) { return; } EtcdCluster cluster = clusters.get(name); // default leader address is the provided one String leaderAddress = cluster.getAddress(); List<EtcdMember> members; try (EtcdProxy proxy = proxyFactory.getEtcdProxy(name, leaderAddress)) { members = proxy.getMembers(); Collections.sort(members, MEMBER_SORTER); cluster.setAuthEnabled(proxy.isAuthEnabled()); } catch (Exception e) { log.error("Last known leader " + leaderAddress + " is not accessible: " + e.getLocalizedMessage(), e); // use the previously discovered members if they exist if (cluster.getMembers() == null) { throw e; } members = cluster.getMembers(); } // collect self statistics from each member for (EtcdMember member : members) { member.setState(null); member.setVersion(null); for (String clientURL : member.getClientURLs()) { try (EtcdProxy proxy = proxyFactory.getEtcdProxy(name, clientURL)) { EtcdSelfStats memberStats = proxy.getSelfStats(); member.setState(STATE_MAPPINGS.get(memberStats.getState())); if ("leader".equals(member.getState())) { leaderAddress = clientURL; cluster.setAuthEnabled(proxy.isAuthEnabled()); } member.setVersion(proxy.getVersion()); break; } catch (Exception e) { log.warn("etcd server " + clientURL + " is not accessible: " + e.getLocalizedMessage(), e); } } } cluster.setMembers(members); cluster.setAddress(leaderAddress); cluster.setLastRefreshTime(new Date()); cluster.setRefreshed(true); } }