|
@@ -22,6 +22,7 @@ package org.elasticsearch.transport;
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
import org.apache.logging.log4j.Logger;
|
|
|
import org.apache.logging.log4j.message.ParameterizedMessage;
|
|
|
+import org.elasticsearch.Build;
|
|
|
import org.elasticsearch.Version;
|
|
|
import org.elasticsearch.action.ActionListener;
|
|
|
import org.elasticsearch.action.ActionListenerResponseHandler;
|
|
@@ -34,6 +35,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
|
|
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
|
|
import org.elasticsearch.common.io.stream.Writeable;
|
|
|
import org.elasticsearch.common.lease.Releasable;
|
|
|
+import org.elasticsearch.common.logging.DeprecationLogger;
|
|
|
import org.elasticsearch.common.logging.Loggers;
|
|
|
import org.elasticsearch.common.regex.Regex;
|
|
|
import org.elasticsearch.common.settings.ClusterSettings;
|
|
@@ -68,10 +70,27 @@ import java.util.function.Function;
|
|
|
import java.util.function.Predicate;
|
|
|
import java.util.function.Supplier;
|
|
|
|
|
|
-public class TransportService extends AbstractLifecycleComponent implements ReportingService<TransportInfo>, TransportMessageListener,
|
|
|
- TransportConnectionListener {
|
|
|
+public class TransportService extends AbstractLifecycleComponent
|
|
|
+ implements ReportingService<TransportInfo>, TransportMessageListener, TransportConnectionListener {
|
|
|
+
|
|
|
private static final Logger logger = LogManager.getLogger(TransportService.class);
|
|
|
|
|
|
+ private static final String PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS_KEY = "es.unsafely_permit_handshake_from_incompatible_builds";
|
|
|
+ private static final boolean PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS;
|
|
|
+
|
|
|
+ static {
|
|
|
+ final String value = System.getProperty(PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS_KEY);
|
|
|
+ if (value == null) {
|
|
|
+ PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS = false;
|
|
|
+ } else if (Boolean.parseBoolean(value)) {
|
|
|
+ PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS = true;
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("invalid value [" + value + "] for system property ["
|
|
|
+ + PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS_KEY + "]");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
public static final String DIRECT_RESPONSE_PROFILE = ".direct";
|
|
|
public static final String HANDSHAKE_ACTION_NAME = "internal:transport/handshake";
|
|
|
|
|
@@ -182,7 +201,14 @@ public class TransportService extends AbstractLifecycleComponent implements Repo
|
|
|
false, false,
|
|
|
HandshakeRequest::new,
|
|
|
(request, channel, task) -> channel.sendResponse(
|
|
|
- new HandshakeResponse(localNode, clusterName, localNode.getVersion())));
|
|
|
+ new HandshakeResponse(localNode.getVersion(), Build.CURRENT.hash(), localNode, clusterName)));
|
|
|
+
|
|
|
+ if (PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS) {
|
|
|
+ logger.warn("transport handshakes from incompatible builds are unsafely permitted on this node; remove system property [" +
|
|
|
+ PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS_KEY + "] to resolve this warning");
|
|
|
+ DeprecationLogger.getLogger(TransportService.class).deprecate("permit_handshake_from_incompatible_builds",
|
|
|
+ "system property [" + PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS_KEY + "] is deprecated and should be removed");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public RemoteClusterService getRemoteClusterService() {
|
|
@@ -440,8 +466,8 @@ public class TransportService extends AbstractLifecycleComponent implements Repo
|
|
|
public void onFailure(Exception e) {
|
|
|
listener.onFailure(e);
|
|
|
}
|
|
|
- }
|
|
|
- , HandshakeResponse::new, ThreadPool.Names.GENERIC
|
|
|
+ },
|
|
|
+ HandshakeResponse::new, ThreadPool.Names.GENERIC
|
|
|
));
|
|
|
}
|
|
|
|
|
@@ -463,28 +489,89 @@ public class TransportService extends AbstractLifecycleComponent implements Repo
|
|
|
}
|
|
|
|
|
|
public static class HandshakeResponse extends TransportResponse {
|
|
|
+
|
|
|
+ private static final Version BUILD_HASH_HANDSHAKE_VERSION = Version.V_8_0_0;
|
|
|
+
|
|
|
+ private final Version version;
|
|
|
+
|
|
|
+ @Nullable // if version < BUILD_HASH_HANDSHAKE_VERSION
|
|
|
+ private final String buildHash;
|
|
|
+
|
|
|
private final DiscoveryNode discoveryNode;
|
|
|
+
|
|
|
private final ClusterName clusterName;
|
|
|
- private final Version version;
|
|
|
|
|
|
- public HandshakeResponse(DiscoveryNode discoveryNode, ClusterName clusterName, Version version) {
|
|
|
- this.discoveryNode = discoveryNode;
|
|
|
- this.version = version;
|
|
|
- this.clusterName = clusterName;
|
|
|
+ public HandshakeResponse(Version version, String buildHash, DiscoveryNode discoveryNode, ClusterName clusterName) {
|
|
|
+ this.buildHash = Objects.requireNonNull(buildHash);
|
|
|
+ this.discoveryNode = Objects.requireNonNull(discoveryNode);
|
|
|
+ this.version = Objects.requireNonNull(version);
|
|
|
+ this.clusterName = Objects.requireNonNull(clusterName);
|
|
|
}
|
|
|
|
|
|
public HandshakeResponse(StreamInput in) throws IOException {
|
|
|
super(in);
|
|
|
- discoveryNode = in.readOptionalWriteable(DiscoveryNode::new);
|
|
|
- clusterName = new ClusterName(in);
|
|
|
- version = Version.readVersion(in);
|
|
|
+ if (in.getVersion().onOrAfter(BUILD_HASH_HANDSHAKE_VERSION)) {
|
|
|
+ // the first two fields need only VInts and raw (ASCII) characters, so we cross our fingers and hope that they appear
|
|
|
+ // on the wire as we expect them to even if this turns out to be an incompatible build
|
|
|
+ version = Version.readVersion(in);
|
|
|
+ buildHash = in.readString();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // If the remote node is incompatible then make an effort to identify it anyway, so we can mention it in the exception
|
|
|
+ // message, but recognise that this may fail
|
|
|
+ discoveryNode = new DiscoveryNode(in);
|
|
|
+ } catch (Exception e) {
|
|
|
+ if (isIncompatibleBuild(version, buildHash)) {
|
|
|
+ throw new IllegalArgumentException("unidentifiable remote node is build [" + buildHash +
|
|
|
+ "] of version [" + version + "] but this node is build [" + Build.CURRENT.hash() +
|
|
|
+ "] of version [" + Version.CURRENT + "] which has an incompatible wire format", e);
|
|
|
+ } else {
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isIncompatibleBuild(version, buildHash)) {
|
|
|
+ if (PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS) {
|
|
|
+ logger.warn("remote node [{}] is build [{}] of version [{}] but this node is build [{}] of version [{}] " +
|
|
|
+ "which may not be compatible; remove system property [{}] to resolve this warning",
|
|
|
+ discoveryNode, buildHash, version, Build.CURRENT.hash(), Version.CURRENT,
|
|
|
+ PERMIT_HANDSHAKES_FROM_INCOMPATIBLE_BUILDS_KEY);
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("remote node [" + discoveryNode + "] is build [" + buildHash +
|
|
|
+ "] of version [" + version + "] but this node is build [" + Build.CURRENT.hash() +
|
|
|
+ "] of version [" + Version.CURRENT + "] which has an incompatible wire format");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ clusterName = new ClusterName(in);
|
|
|
+ } else {
|
|
|
+ discoveryNode = in.readOptionalWriteable(DiscoveryNode::new);
|
|
|
+ clusterName = new ClusterName(in);
|
|
|
+ version = Version.readVersion(in);
|
|
|
+ buildHash = null;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void writeTo(StreamOutput out) throws IOException {
|
|
|
- out.writeOptionalWriteable(discoveryNode);
|
|
|
- clusterName.writeTo(out);
|
|
|
- Version.writeVersion(version, out);
|
|
|
+ if (out.getVersion().onOrAfter(BUILD_HASH_HANDSHAKE_VERSION)) {
|
|
|
+ Version.writeVersion(version, out);
|
|
|
+ out.writeString(buildHash);
|
|
|
+ discoveryNode.writeTo(out);
|
|
|
+ clusterName.writeTo(out);
|
|
|
+ } else {
|
|
|
+ out.writeOptionalWriteable(discoveryNode);
|
|
|
+ clusterName.writeTo(out);
|
|
|
+ Version.writeVersion(version, out);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public Version getVersion() {
|
|
|
+ return version;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getBuildHash() {
|
|
|
+ return buildHash;
|
|
|
}
|
|
|
|
|
|
public DiscoveryNode getDiscoveryNode() {
|
|
@@ -494,6 +581,10 @@ public class TransportService extends AbstractLifecycleComponent implements Repo
|
|
|
public ClusterName getClusterName() {
|
|
|
return clusterName;
|
|
|
}
|
|
|
+
|
|
|
+ private static boolean isIncompatibleBuild(Version version, String buildHash) {
|
|
|
+ return version == Version.CURRENT && Build.CURRENT.hash().equals(buildHash) == false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public void disconnectFromNode(DiscoveryNode node) {
|
|
@@ -1293,4 +1384,5 @@ public class TransportService extends AbstractLifecycleComponent implements Repo
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
}
|