PageRenderTime 50ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/discovery-gce/src/main/java/org/elasticsearch/discovery/gce/GceUnicastHostsProvider.java

https://gitlab.com/sharadag/elasticsearch
Java | 264 lines | 186 code | 33 blank | 45 comment | 58 complexity | 55c467e62decda84a4f6e67b6cf898b3 MD5 | raw file
  1. /*
  2. * Licensed to Elasticsearch under one or more contributor
  3. * license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright
  5. * ownership. Elasticsearch licenses this file to you under
  6. * the Apache License, Version 2.0 (the "License"); you may
  7. * not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package org.elasticsearch.discovery.gce;
  20. import com.google.api.services.compute.model.AccessConfig;
  21. import com.google.api.services.compute.model.Instance;
  22. import com.google.api.services.compute.model.NetworkInterface;
  23. import org.elasticsearch.Version;
  24. import org.elasticsearch.cloud.gce.GceComputeService;
  25. import org.elasticsearch.cluster.node.DiscoveryNode;
  26. import org.elasticsearch.common.Strings;
  27. import org.elasticsearch.common.component.AbstractComponent;
  28. import org.elasticsearch.common.inject.Inject;
  29. import org.elasticsearch.common.network.NetworkAddress;
  30. import org.elasticsearch.common.network.NetworkService;
  31. import org.elasticsearch.common.settings.Setting;
  32. import org.elasticsearch.common.settings.Setting.Property;
  33. import org.elasticsearch.common.settings.Settings;
  34. import org.elasticsearch.common.transport.TransportAddress;
  35. import org.elasticsearch.common.unit.TimeValue;
  36. import org.elasticsearch.discovery.zen.ping.unicast.UnicastHostsProvider;
  37. import org.elasticsearch.transport.TransportService;
  38. import java.io.IOException;
  39. import java.net.InetAddress;
  40. import java.util.ArrayList;
  41. import java.util.Collection;
  42. import java.util.List;
  43. import java.util.function.Function;
  44. import static java.util.Collections.emptyList;
  45. import static java.util.Collections.emptyMap;
  46. import static java.util.Collections.emptySet;
  47. /**
  48. *
  49. */
  50. public class GceUnicastHostsProvider extends AbstractComponent implements UnicastHostsProvider {
  51. /**
  52. * discovery.gce.tags: The gce discovery can filter machines to include in the cluster based on tags.
  53. */
  54. public static final Setting<List<String>> TAGS_SETTING =
  55. Setting.listSetting("discovery.gce.tags", emptyList(), Function.identity(), Property.NodeScope);
  56. static final class Status {
  57. private static final String TERMINATED = "TERMINATED";
  58. }
  59. private final GceComputeService gceComputeService;
  60. private TransportService transportService;
  61. private NetworkService networkService;
  62. private final Version version;
  63. private final String project;
  64. private final List<String> zones;
  65. private final List<String> tags;
  66. private final TimeValue refreshInterval;
  67. private long lastRefresh;
  68. private List<DiscoveryNode> cachedDiscoNodes;
  69. @Inject
  70. public GceUnicastHostsProvider(Settings settings, GceComputeService gceComputeService,
  71. TransportService transportService,
  72. NetworkService networkService,
  73. Version version) {
  74. super(settings);
  75. this.gceComputeService = gceComputeService;
  76. this.transportService = transportService;
  77. this.networkService = networkService;
  78. this.version = version;
  79. this.refreshInterval = GceComputeService.REFRESH_SETTING.get(settings);
  80. this.project = GceComputeService.PROJECT_SETTING.get(settings);
  81. this.zones = GceComputeService.ZONE_SETTING.get(settings);
  82. this.tags = TAGS_SETTING.get(settings);
  83. if (logger.isDebugEnabled()) {
  84. logger.debug("using tags {}", this.tags);
  85. }
  86. }
  87. /**
  88. * We build the list of Nodes from GCE Management API
  89. * Information can be cached using `cloud.gce.refresh_interval` property if needed.
  90. */
  91. @Override
  92. public List<DiscoveryNode> buildDynamicNodes() {
  93. // We check that needed properties have been set
  94. if (this.project == null || this.project.isEmpty() || this.zones == null || this.zones.isEmpty()) {
  95. throw new IllegalArgumentException("one or more gce discovery settings are missing. " +
  96. "Check elasticsearch.yml file. Should have [" + GceComputeService.PROJECT_SETTING.getKey() +
  97. "] and [" + GceComputeService.ZONE_SETTING.getKey() + "].");
  98. }
  99. if (refreshInterval.millis() != 0) {
  100. if (cachedDiscoNodes != null &&
  101. (refreshInterval.millis() < 0 || (System.currentTimeMillis() - lastRefresh) < refreshInterval.millis())) {
  102. if (logger.isTraceEnabled()) logger.trace("using cache to retrieve node list");
  103. return cachedDiscoNodes;
  104. }
  105. lastRefresh = System.currentTimeMillis();
  106. }
  107. logger.debug("start building nodes list using GCE API");
  108. cachedDiscoNodes = new ArrayList<>();
  109. String ipAddress = null;
  110. try {
  111. InetAddress inetAddress = networkService.resolvePublishHostAddresses(null);
  112. if (inetAddress != null) {
  113. ipAddress = NetworkAddress.format(inetAddress);
  114. }
  115. } catch (IOException e) {
  116. // We can't find the publish host address... Hmmm. Too bad :-(
  117. // We won't simply filter it
  118. }
  119. try {
  120. Collection<Instance> instances = gceComputeService.instances();
  121. if (instances == null) {
  122. logger.trace("no instance found for project [{}], zones [{}].", this.project, this.zones);
  123. return cachedDiscoNodes;
  124. }
  125. for (Instance instance : instances) {
  126. String name = instance.getName();
  127. String type = instance.getMachineType();
  128. String status = instance.getStatus();
  129. logger.trace("gce instance {} with status {} found.", name, status);
  130. // We don't want to connect to TERMINATED status instances
  131. // See https://github.com/elastic/elasticsearch-cloud-gce/issues/3
  132. if (Status.TERMINATED.equals(status)) {
  133. logger.debug("node {} is TERMINATED. Ignoring", name);
  134. continue;
  135. }
  136. // see if we need to filter by tag
  137. boolean filterByTag = false;
  138. if (tags.isEmpty() == false) {
  139. logger.trace("start filtering instance {} with tags {}.", name, tags);
  140. if (instance.getTags() == null || instance.getTags().isEmpty()
  141. || instance.getTags().getItems() == null || instance.getTags().getItems().isEmpty()) {
  142. // If this instance have no tag, we filter it
  143. logger.trace("no tags for this instance but we asked for tags. {} won't be part of the cluster.", name);
  144. filterByTag = true;
  145. } else {
  146. // check that all tags listed are there on the instance
  147. logger.trace("comparing instance tags {} with tags filter {}.", instance.getTags().getItems(), tags);
  148. for (String tag : tags) {
  149. boolean found = false;
  150. for (String instancetag : instance.getTags().getItems()) {
  151. if (instancetag.equals(tag)) {
  152. found = true;
  153. break;
  154. }
  155. }
  156. if (!found) {
  157. filterByTag = true;
  158. break;
  159. }
  160. }
  161. }
  162. }
  163. if (filterByTag) {
  164. logger.trace("filtering out instance {} based tags {}, not part of {}", name, tags,
  165. instance.getTags() == null || instance.getTags().getItems() == null ? "" : instance.getTags());
  166. continue;
  167. } else {
  168. logger.trace("instance {} with tags {} is added to discovery", name, tags);
  169. }
  170. String ip_public = null;
  171. String ip_private = null;
  172. List<NetworkInterface> interfaces = instance.getNetworkInterfaces();
  173. for (NetworkInterface networkInterface : interfaces) {
  174. if (ip_public == null) {
  175. // Trying to get Public IP Address (For future use)
  176. if (networkInterface.getAccessConfigs() != null) {
  177. for (AccessConfig accessConfig : networkInterface.getAccessConfigs()) {
  178. if (Strings.hasText(accessConfig.getNatIP())) {
  179. ip_public = accessConfig.getNatIP();
  180. break;
  181. }
  182. }
  183. }
  184. }
  185. if (ip_private == null) {
  186. ip_private = networkInterface.getNetworkIP();
  187. }
  188. // If we have both public and private, we can stop here
  189. if (ip_private != null && ip_public != null) break;
  190. }
  191. try {
  192. if (ip_private.equals(ipAddress)) {
  193. // We found the current node.
  194. // We can ignore it in the list of DiscoveryNode
  195. logger.trace("current node found. Ignoring {} - {}", name, ip_private);
  196. } else {
  197. String address = ip_private;
  198. // Test if we have es_port metadata defined here
  199. if (instance.getMetadata() != null && instance.getMetadata().containsKey("es_port")) {
  200. Object es_port = instance.getMetadata().get("es_port");
  201. logger.trace("es_port is defined with {}", es_port);
  202. if (es_port instanceof String) {
  203. address = address.concat(":").concat((String) es_port);
  204. } else {
  205. // Ignoring other values
  206. logger.trace("es_port is instance of {}. Ignoring...", es_port.getClass().getName());
  207. }
  208. }
  209. // ip_private is a single IP Address. We need to build a TransportAddress from it
  210. // If user has set `es_port` metadata, we don't need to ping all ports
  211. // we only limit to 1 addresses, makes no sense to ping 100 ports
  212. TransportAddress[] addresses = transportService.addressesFromString(address, 1);
  213. for (TransportAddress transportAddress : addresses) {
  214. logger.trace("adding {}, type {}, address {}, transport_address {}, status {}", name, type,
  215. ip_private, transportAddress, status);
  216. cachedDiscoNodes.add(new DiscoveryNode("#cloud-" + name + "-" + 0, transportAddress,
  217. emptyMap(), emptySet(), version.minimumCompatibilityVersion()));
  218. }
  219. }
  220. } catch (Exception e) {
  221. logger.warn("failed to add {}, address {}", e, name, ip_private);
  222. }
  223. }
  224. } catch (Throwable e) {
  225. logger.warn("Exception caught during discovery: {}", e, e.getMessage());
  226. }
  227. logger.debug("{} node(s) added", cachedDiscoNodes.size());
  228. logger.debug("using dynamic discovery nodes {}", cachedDiscoNodes);
  229. return cachedDiscoNodes;
  230. }
  231. }