This is an automated email from the ASF dual-hosted git repository.
petrov-mg pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new e18edd87deb IGNITE-28969 Added remote node features validation during
the TCP Discovery handshake (#13456)
e18edd87deb is described below
commit e18edd87debc8a6deec4a98c94d14f4b598e2277
Author: Mikhail Petrov <[email protected]>
AuthorDate: Mon Aug 10 14:43:20 2026 +0300
IGNITE-28969 Added remote node features validation during the TCP Discovery
handshake (#13456)
---
.../apache/ignite/internal/GridKernalContext.java | 4 ++
.../ignite/internal/GridKernalContextImpl.java | 6 +++
.../wal/reader/StandaloneGridKernalContext.java | 10 +++++
.../feature/IgnitePluginFeatureSet.java | 4 +-
.../ignite/spi/discovery/tcp/ClientImpl.java | 15 +++++++-
.../ignite/spi/discovery/tcp/ServerImpl.java | 45 +++++++++++++++++-----
.../ignite/spi/discovery/tcp/TcpDiscoverySpi.java | 43 +++++++++++++++++++++
.../internal/UnsupportedNodeVersionException.java} | 13 +++++--
.../tcp/messages/TcpDiscoveryHandshakeRequest.java | 16 +++++++-
.../messages/TcpDiscoveryHandshakeResponse.java | 16 +++++++-
.../rollingupgrade/AbstractRollingUpgradeTest.java | 12 ++++--
.../CoreVersionRollingUpgradeTest.java | 24 +++++++++++-
.../PluginVersionRollingUpgradeTest.java | 4 +-
...0.java => TestPluginReleaseFeatures_2_1_0.java} | 7 +++-
.../feature/TestPluginReleaseFeatures_3_0_0.java | 3 ++
15 files changed, 195 insertions(+), 27 deletions(-)
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
index 3d92dfa0d56..0dee5ba7674 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
@@ -68,6 +68,7 @@ import
org.apache.ignite.internal.processors.query.GridQueryProcessor;
import org.apache.ignite.internal.processors.resource.GridResourceProcessor;
import org.apache.ignite.internal.processors.rest.IgniteRestProcessor;
import
org.apache.ignite.internal.processors.rollingupgrade.RollingUpgradeProcessor;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet;
import
org.apache.ignite.internal.processors.schedule.IgniteScheduleProcessorAdapter;
import org.apache.ignite.internal.processors.security.IgniteSecurity;
import
org.apache.ignite.internal.processors.segmentation.GridSegmentationProcessor;
@@ -140,6 +141,9 @@ public interface GridKernalContext extends
Iterable<GridComponent> {
*/
public GridKernalGateway gateway();
+ /** @return Local node features. */
+ public IgniteNodeFeatureSet localNodeFeatures();
+
/**
* Gets grid instance managed by kernal.
*
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
index 8981207abf2..9892823a091 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
@@ -90,6 +90,7 @@ import
org.apache.ignite.internal.processors.query.QueryEngine;
import org.apache.ignite.internal.processors.resource.GridResourceProcessor;
import org.apache.ignite.internal.processors.rest.IgniteRestProcessor;
import
org.apache.ignite.internal.processors.rollingupgrade.RollingUpgradeProcessor;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet;
import
org.apache.ignite.internal.processors.schedule.IgniteScheduleProcessorAdapter;
import org.apache.ignite.internal.processors.security.IgniteSecurity;
import
org.apache.ignite.internal.processors.segmentation.GridSegmentationProcessor;
@@ -632,6 +633,11 @@ public class GridKernalContextImpl implements
GridKernalContext, Externalizable
return gw;
}
+ /** {@inheritDoc} */
+ @Override public IgniteNodeFeatureSet localNodeFeatures() {
+ return rollUpProc.features().localVersionFeatures();
+ }
+
/** {@inheritDoc} */
@Override public IgniteEx grid() {
return grid;
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
index 3b0acf2e65e..44a5bdabcf5 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
@@ -91,6 +91,8 @@ import
org.apache.ignite.internal.processors.query.GridQueryProcessor;
import org.apache.ignite.internal.processors.resource.GridResourceProcessor;
import org.apache.ignite.internal.processors.rest.IgniteRestProcessor;
import
org.apache.ignite.internal.processors.rollingupgrade.RollingUpgradeProcessor;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteCoreFeatureSet;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet;
import
org.apache.ignite.internal.processors.schedule.IgniteScheduleProcessorAdapter;
import org.apache.ignite.internal.processors.security.IgniteSecurity;
import
org.apache.ignite.internal.processors.security.NoOpIgniteSecurityProcessor;
@@ -177,6 +179,9 @@ public class StandaloneGridKernalContext implements
GridKernalContext {
/** Operation context dispacther. */
private final OperationContextDispatcher opCtxDispatcher = new
OperationContextDispatcher();
+ /** */
+ private final IgniteNodeFeatureSet locNodeFeatures = new
IgniteNodeFeatureSet(IgniteCoreFeatureSet.local());
+
/**
* @param log Logger.
* @param ft Node file tree.
@@ -317,6 +322,11 @@ public class StandaloneGridKernalContext implements
GridKernalContext {
return null;
}
+ /** {@inheritDoc} */
+ @Override public IgniteNodeFeatureSet localNodeFeatures() {
+ return locNodeFeatures;
+ }
+
/** {@inheritDoc} */
@Override public IgniteEx grid() {
final IgniteEx kernal = new IgniteKernal() {
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgnitePluginFeatureSet.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgnitePluginFeatureSet.java
index 7c444c9d4fe..0334ef04b1b 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgnitePluginFeatureSet.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgnitePluginFeatureSet.java
@@ -50,8 +50,8 @@ public class IgnitePluginFeatureSet extends
IgniteComponentFeatureSet {
this.compName = compName;
}
- /** */
- public String componentName() {
+ /** {@inheritDoc} */
+ @Override public String componentName() {
return compName;
}
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java
index 43d4f1b4867..4d7e1e22f57 100644
---
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java
+++
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java
@@ -88,6 +88,7 @@ import org.apache.ignite.spi.discovery.DiscoverySpiListener;
import org.apache.ignite.spi.discovery.tcp.internal.DiscoveryDataPacket;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNodesRing;
+import
org.apache.ignite.spi.discovery.tcp.internal.UnsupportedNodeVersionException;
import
org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder;
import
org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage;
import
org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAuthFailedMessage;
@@ -708,7 +709,7 @@ class ClientImpl extends TcpDiscoveryImpl {
TcpDiscoveryIoSession ses = createSession(sock);
- TcpDiscoveryHandshakeRequest req = new
TcpDiscoveryHandshakeRequest(locNodeId);
+ TcpDiscoveryHandshakeRequest req = new
TcpDiscoveryHandshakeRequest(locNodeId, spi.localNodeFeatures());
req.client(true);
req.dcId(locNode.dataCenterId());
@@ -717,6 +718,8 @@ class ClientImpl extends TcpDiscoveryImpl {
TcpDiscoveryHandshakeResponse res =
spi.readHandshakeResponse(ses, ackTimeout0);
+ spi.validateRemoteFeatures(res.nodeFeatures());
+
// Convert the addresses once.
Collection<InetSocketAddress> redirectAddrs =
res.redirectAddresses();
@@ -791,6 +794,16 @@ class ClientImpl extends TcpDiscoveryImpl {
errs.add(e);
+ if (e instanceof UnsupportedNodeVersionException
unsupportedVerEx) {
+ LT.error(log, e, "Failed to initialize a connection with
the remote node. The remote node is running" +
+ " components with an incompatible versions, so the
nodes cannot agree on serialization protocol" +
+ " [rmtAddr=" + addr + ", errMsg=" +
unsupportedVerEx.getMessage() + ']');
+
+ throw new IgniteSpiException("Failed to initialize a
connection with the remote node. The remote node" +
+ " is running components with an incompatible versions,
so the nodes cannot agree on serialization" +
+ " protocol [rmtAddr=" + addr + ", errMsg=" +
unsupportedVerEx.getMessage() + ']', e);
+ }
+
if (X.hasCause(e, SSLException.class)) {
if (--sslConnectAttempts == 0)
throw new IgniteSpiException("Unable to establish
secure connection. " +
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java
index c17346d10e7..9a221431b1d 100644
---
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java
+++
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java
@@ -129,6 +129,7 @@ import
org.apache.ignite.spi.discovery.tcp.internal.FutureTask;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNodesRing;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoverySpiState;
+import
org.apache.ignite.spi.discovery.tcp.internal.UnsupportedNodeVersionException;
import
org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage;
import
org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAuthFailedMessage;
import
org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryCheckFailedMessage;
@@ -1502,13 +1503,15 @@ class ServerImpl extends TcpDiscoveryImpl {
openSock = true;
- TcpDiscoveryHandshakeRequest req = new
TcpDiscoveryHandshakeRequest(locNodeId);
+ TcpDiscoveryHandshakeRequest req = new
TcpDiscoveryHandshakeRequest(locNodeId, spi.localNodeFeatures());
// Handshake.
spi.writeMessage(ses, req,
timeoutHelper.nextTimeoutChunk(spi.getSocketTimeout()));
TcpDiscoveryHandshakeResponse res =
spi.readHandshakeResponse(ses, timeoutHelper.nextTimeoutChunk(ackTimeout0));
+ spi.validateRemoteFeatures(res.nodeFeatures());
+
if (msg instanceof TcpDiscoveryJoinRequestMessage) {
boolean ignore = false;
@@ -1587,6 +1590,16 @@ class ServerImpl extends TcpDiscoveryImpl {
errs.add(e);
+ if (e instanceof UnsupportedNodeVersionException
unsupportedVerEx) {
+ LT.error(log, e, "Failed to initialize a connection with
the remote node. The remote node is running" +
+ " components with an incompatible versions, so the
nodes cannot agree on serialization protocol" +
+ " [rmtAddr=" + addr + ", errMsg=" +
unsupportedVerEx.getMessage() + ']');
+
+ throw new IgniteException("Failed to initialize a
connection with the remote node. The remote node" +
+ " is running components with an incompatible versions,
so the nodes cannot agree on a serialization" +
+ " protocol [rmtAddr=" + addr + ", errMsg=" +
unsupportedVerEx.getMessage() + ']', e);
+ }
+
if (X.hasCause(e, SSLException.class)) {
if (--sslConnectAttempts == 0)
throw new IgniteException("Unable to establish secure
connection. " +
@@ -3402,7 +3415,7 @@ class ServerImpl extends TcpDiscoveryImpl {
openSock = true;
// Handshake.
- TcpDiscoveryHandshakeRequest hndMsg = new
TcpDiscoveryHandshakeRequest(locNodeId);
+ TcpDiscoveryHandshakeRequest hndMsg = new
TcpDiscoveryHandshakeRequest(locNodeId, spi.localNodeFeatures());
if (sndState != null) {
// If want a forced connection, we set the
change-topology node flag to current node id.
@@ -3431,6 +3444,8 @@ class ServerImpl extends TcpDiscoveryImpl {
if (log.isDebugEnabled())
log.debug("Handshake response: " + res);
+ spi.validateRemoteFeatures(res.nodeFeatures());
+
// We should take previousNodeAlive flag into
account
// only if we received the response from the
correct node.
if (res.creatorNodeId().equals(next.id()) &&
res.previousNodeAlive() && sndState != null) {
@@ -6579,8 +6594,11 @@ class ServerImpl extends TcpDiscoveryImpl {
U.enhanceThreadName(U.id8(nodeId) + ' ' +
sock.getInetAddress().getHostAddress()
+ ":" + sock.getPort() + (req.client() ? " client" :
""));
- TcpDiscoveryHandshakeResponse res =
- new TcpDiscoveryHandshakeResponse(locNodeId,
locNode.internalOrder());
+ TcpDiscoveryHandshakeResponse res = new
TcpDiscoveryHandshakeResponse(
+ locNodeId,
+ locNode.internalOrder(),
+ spi.localNodeFeatures()
+ );
if (req.client()) {
if (req.dcId() != null && !Objects.equals(req.dcId(),
locNode.dataCenterId())) {
@@ -6694,6 +6712,8 @@ class ServerImpl extends TcpDiscoveryImpl {
spi.writeMessage(ses, res,
spi.getEffectiveSocketTimeout(srvSock));
+ spi.validateRemoteFeatures(req.nodeFeatures());
+
// It can happen if a remote node is stopped and it has a
loopback address in the list of addresses,
// the local node sends a handshake request message on the
loopback address, so we get here.
if (locNodeId.equals(nodeId)) {
@@ -6790,21 +6810,26 @@ class ServerImpl extends TcpDiscoveryImpl {
onException("Caught exception on handshake [err=" + e + ",
sock=" + sock + ']', e);
- if (e.hasCause(SocketTimeoutException.class))
+ if (e instanceof UnsupportedNodeVersionException
unsupportedVerEx) {
+ LT.warn(log, "Failed to initialize a connection with
the remote node. The remote node is running" +
+ " components with an incompatible versions, so the
nodes cannot agree on serialization protocol" +
+ " [rmtAddr=" + rmtAddr + ", errMsg=" +
unsupportedVerEx.getMessage() + ']', e);
+ }
+ else if (e.hasCause(SocketTimeoutException.class)) {
LT.warn(log, "Socket operation timed out on handshake
" +
"(consider increasing 'networkTimeout'
configuration property) " +
"[netTimeout=" + spi.netTimeout + ']');
-
- else if (e.hasCause(ClassNotFoundException.class))
+ }
+ else if (e.hasCause(ClassNotFoundException.class)) {
LT.warn(log, "Failed to read message due to
ClassNotFoundException " +
"(make sure same versions of all classes are
available on all nodes) " +
"[rmtAddr=" + rmtAddr +
", err=" + X.cause(e,
ClassNotFoundException.class).getMessage() + ']');
-
+ }
+ else if (e.hasCause(ObjectStreamException.class) ||
(!sock.isClosed() && !e.hasCause(IOException.class))) {
// Always report marshalling problems.
- else if (e.hasCause(ObjectStreamException.class) ||
- (!sock.isClosed() && !e.hasCause(IOException.class)))
LT.error(log, e, "Failed to initialize connection
[sock=" + sock + ']');
+ }
return;
}
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
index b1cc53cbee5..9c52f9504be 100644
---
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
+++
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
@@ -60,6 +60,8 @@ import
org.apache.ignite.internal.managers.communication.UnknownMessageException
import org.apache.ignite.internal.managers.discovery.IgniteDiscoverySpi;
import org.apache.ignite.internal.processors.failure.FailureProcessor;
import org.apache.ignite.internal.processors.metric.MetricRegistryImpl;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteComponentFeatureSet;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
@@ -99,6 +101,7 @@ import
org.apache.ignite.spi.discovery.DiscoverySpiOrderSupport;
import org.apache.ignite.spi.discovery.tcp.internal.DiscoveryDataPacket;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryStatistics;
+import
org.apache.ignite.spi.discovery.tcp.internal.UnsupportedNodeVersionException;
import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
import
org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinder;
import
org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder;
@@ -468,6 +471,9 @@ public class TcpDiscoverySpi extends IgniteSpiAdapter
implements IgniteDiscovery
/** For test purposes. */
private boolean skipAddrsRandomization = false;
+ /** */
+ private IgniteNodeFeatureSet locNodeFeatures;
+
/**
* Gets current SPI state.
*
@@ -1189,6 +1195,8 @@ public class TcpDiscoverySpi extends IgniteSpiAdapter
implements IgniteDiscovery
// Init local node.
initAddresses();
+ locNodeFeatures = ((IgniteEx)ignite).context().localNodeFeatures();
+
locNode = new TcpDiscoveryNode(
ignite.configuration().getNodeId(),
addrs.get1(),
@@ -1686,6 +1694,41 @@ public class TcpDiscoverySpi extends IgniteSpiAdapter
implements IgniteDiscovery
}
}
+ /** */
+ IgniteNodeFeatureSet localNodeFeatures() {
+ return locNodeFeatures;
+ }
+
+ /** */
+ void validateRemoteFeatures(IgniteNodeFeatureSet rmtFeatures) throws
IgniteCheckedException {
+ if (rmtFeatures == null) {
+ throw new UnsupportedNodeVersionException(
+ "Failed to obtain remote node features. The remote node may be
running an unsupported Ignite version," +
+ " which may result in unexpected handshake message
serialization");
+ }
+
+ for (IgniteComponentFeatureSet rmtCmpFeatures : rmtFeatures.values()) {
+ IgniteComponentFeatureSet locCmpFeatures =
locNodeFeatures.componentFeatures(rmtCmpFeatures.componentName());
+
+ if (locCmpFeatures == null)
+ continue;
+
+ int c =
locCmpFeatures.version().compareTo(rmtCmpFeatures.version());
+
+ if (c == 0)
+ continue;
+
+ IgniteComponentFeatureSet src = c > 0 ? rmtCmpFeatures :
locCmpFeatures;
+ IgniteComponentFeatureSet target = c > 0 ? locCmpFeatures :
rmtCmpFeatures;
+
+ if (!src.isUpgradableTo(target)) {
+ throw new UnsupportedNodeVersionException("Remote node
component versions are not supported" +
+ " [locComponents=" + locNodeFeatures +
+ ", rmtComponents=" + rmtFeatures + ']');
+ }
+ }
+ }
+
/**
* Writes message to the socket.
*
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/UnsupportedNodeVersionException.java
similarity index 70%
copy from
modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
copy to
modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/UnsupportedNodeVersionException.java
index b40cea1590d..ca46b73cc20 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
+++
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/UnsupportedNodeVersionException.java
@@ -15,10 +15,17 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.processors.rollingupgrade.feature;
+package org.apache.ignite.spi.discovery.tcp.internal;
+
+import org.apache.ignite.IgniteCheckedException;
/** */
-public class TestPluginReleaseFeatures_3_0_0 {
+public class UnsupportedNodeVersionException extends IgniteCheckedException {
+ /** */
+ private static final long serialVersionUID = 0L;
+
/** */
- public static final IgniteFeature VER_3_0_0_ID_2_FEATURE = new
TestPluginFeature(2);
+ public UnsupportedNodeVersionException(String message) {
+ super(message);
+ }
}
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/messages/TcpDiscoveryHandshakeRequest.java
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/messages/TcpDiscoveryHandshakeRequest.java
index 6ce9f16a7bc..d2a33c6d059 100644
---
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/messages/TcpDiscoveryHandshakeRequest.java
+++
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/messages/TcpDiscoveryHandshakeRequest.java
@@ -19,6 +19,8 @@ package org.apache.ignite.spi.discovery.tcp.messages;
import java.util.UUID;
import org.apache.ignite.internal.Order;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteComponentFeatureSet;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.plugin.extensions.communication.MessageFactory;
import org.jetbrains.annotations.Nullable;
@@ -35,6 +37,10 @@ public class TcpDiscoveryHandshakeRequest extends
TcpDiscoveryAbstractMessage {
@Order(1)
@Nullable String dcId;
+ /** */
+ @Order(2)
+ IgniteComponentFeatureSet[] nodeFeatures;
+
/**
* Default constructor for {@link MessageFactory}.
*/
@@ -46,9 +52,12 @@ public class TcpDiscoveryHandshakeRequest extends
TcpDiscoveryAbstractMessage {
* Constructor.
*
* @param creatorNodeId Creator node ID.
+ * @param locNodeFeatures Local node features.
*/
- public TcpDiscoveryHandshakeRequest(UUID creatorNodeId) {
+ public TcpDiscoveryHandshakeRequest(UUID creatorNodeId,
IgniteNodeFeatureSet locNodeFeatures) {
super(creatorNodeId);
+
+ this.nodeFeatures = locNodeFeatures.values();
}
/**
@@ -79,6 +88,11 @@ public class TcpDiscoveryHandshakeRequest extends
TcpDiscoveryAbstractMessage {
this.dcId = dcId;
}
+ /** @return Features supported by the sender node. */
+ public IgniteNodeFeatureSet nodeFeatures() {
+ return nodeFeatures == null ? null : new
IgniteNodeFeatureSet(nodeFeatures);
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(TcpDiscoveryHandshakeRequest.class, this, "super",
super.toString(),
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/messages/TcpDiscoveryHandshakeResponse.java
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/messages/TcpDiscoveryHandshakeResponse.java
index b5ca363ae01..d11e0814ef8 100644
---
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/messages/TcpDiscoveryHandshakeResponse.java
+++
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/messages/TcpDiscoveryHandshakeResponse.java
@@ -21,6 +21,8 @@ import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.UUID;
import org.apache.ignite.internal.Order;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteComponentFeatureSet;
+import
org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.plugin.extensions.communication.MessageFactory;
@@ -42,6 +44,10 @@ public class TcpDiscoveryHandshakeResponse extends
TcpDiscoveryAbstractMessage {
@Order(2)
@Nullable Collection<InetSocketAddressMessage> redirectAddrsMsgs;
+ /** */
+ @Order(3)
+ IgniteComponentFeatureSet[] nodeFeatures;
+
/**
* Default constructor for {@link MessageFactory}.
*/
@@ -54,11 +60,14 @@ public class TcpDiscoveryHandshakeResponse extends
TcpDiscoveryAbstractMessage {
*
* @param creatorNodeId Creator node ID.
* @param locNodeOrder Local node order.
+ * @param locNodeFeatures Local node features.
*/
- public TcpDiscoveryHandshakeResponse(UUID creatorNodeId, long
locNodeOrder) {
+ public TcpDiscoveryHandshakeResponse(UUID creatorNodeId, long
locNodeOrder, IgniteNodeFeatureSet locNodeFeatures) {
super(creatorNodeId);
order = locNodeOrder;
+
+ this.nodeFeatures = locNodeFeatures.values();
}
/**
@@ -104,6 +113,11 @@ public class TcpDiscoveryHandshakeResponse extends
TcpDiscoveryAbstractMessage {
: F.viewReadOnly(sockAddrs, addr -> new
InetSocketAddressMessage(addr.getAddress(), addr.getPort()));
}
+ /** @return Features supported by the sender node. */
+ public IgniteNodeFeatureSet nodeFeatures() {
+ return nodeFeatures == null ? null : new
IgniteNodeFeatureSet(nodeFeatures);
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(TcpDiscoveryHandshakeResponse.class, this, "super",
super.toString(),
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/AbstractRollingUpgradeTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/AbstractRollingUpgradeTest.java
index 1802069a95a..0bbdb9824a5 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/AbstractRollingUpgradeTest.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/AbstractRollingUpgradeTest.java
@@ -71,6 +71,7 @@ import org.apache.ignite.spi.IgniteNodeValidationResult;
import org.apache.ignite.spi.IgniteSpiException;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.TestBlockingTcpDiscoverySpi;
+import
org.apache.ignite.spi.discovery.tcp.internal.UnsupportedNodeVersionException;
import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.jspecify.annotations.Nullable;
@@ -148,8 +149,7 @@ public abstract class AbstractRollingUpgradeTest extends
GridCommonAbstractTest
"One or more component versions on the joining node differ from the
corresponding versions active in the cluster";
/** */
- protected static final String RU_UNAVAILABLE_BETWEEN_VER_ERR = "Ignite
component Rolling Upgrade is not supported" +
- " between the component version active in the cluster and the version
running on the joining node";
+ protected static final String NOT_SUPPORTED_VER_ERR = "Remote node
component versions are not supported";
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String
igniteInstanceName) throws Exception {
@@ -346,10 +346,14 @@ public abstract class AbstractRollingUpgradeTest extends
GridCommonAbstractTest
protected void checkJoinFailed(int nodeIdx, String ver, boolean
checkClientNode, String msg) {
int expClusterSize = clusterNode().cluster().nodes().size();
- GridTestUtils.assertThrowsAnyCause(log, () -> startGrid(nodeIdx, ver),
IgniteSpiException.class, msg);
+ Class<? extends Throwable> errCls = Objects.equals(msg,
NOT_SUPPORTED_VER_ERR)
+ ? UnsupportedNodeVersionException.class
+ : IgniteSpiException.class;
+
+ GridTestUtils.assertThrowsAnyCause(log, () -> startGrid(nodeIdx, ver),
errCls, msg);
if (checkClientNode)
- GridTestUtils.assertThrowsAnyCause(log, () ->
startClientGrid(nodeIdx, ver), IgniteSpiException.class, msg);
+ GridTestUtils.assertThrowsAnyCause(log, () ->
startClientGrid(nodeIdx, ver), errCls, msg);
assertEquals(expClusterSize, clusterNode().cluster().nodes().size());
}
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/CoreVersionRollingUpgradeTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/CoreVersionRollingUpgradeTest.java
index e3f65fd3565..8483d87647a 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/CoreVersionRollingUpgradeTest.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/CoreVersionRollingUpgradeTest.java
@@ -311,6 +311,28 @@ public class CoreVersionRollingUpgradeTest extends
AbstractRollingUpgradeTest {
finalizeClusterVersion(1, "2.19.3");
}
+ /** */
+ @Test
+ public void testJoinOfNodeWithGreaterUnsupportedVersion() throws Exception
{
+ startCluster("2.19.0");
+
+ checkJoinFailed(3, "2.21.0", NOT_SUPPORTED_VER_ERR);
+
+ checkJoinSuccess(3, "2.19.0", false);
+ checkJoinSuccess(4, "2.19.0", true);
+ }
+
+ /** */
+ @Test
+ public void testJoinOfNodeWithSmallerUnsupportedVersion() throws Exception
{
+ startCluster("2.21.0");
+
+ checkJoinFailed(3, "2.19.0", NOT_SUPPORTED_VER_ERR);
+
+ checkJoinSuccess(3, "2.21.0", false);
+ checkJoinSuccess(4, "2.21.0", true);
+ }
+
/** */
@Test
public void testUpgradeBetweenVersionsWithCherryPicks() throws Exception {
@@ -318,7 +340,7 @@ public class CoreVersionRollingUpgradeTest extends
AbstractRollingUpgradeTest {
ru(1).enableVersionUpgrade();
- checkJoinFailed(3, "2.20.0", RU_UNAVAILABLE_BETWEEN_VER_ERR);
+ checkJoinFailed(3, "2.20.0", NOT_SUPPORTED_VER_ERR);
forAllNodes(nodeIdx -> upgradeNodeVersion(nodeIdx, "2.19.3",
"2.20.1"));
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/PluginVersionRollingUpgradeTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/PluginVersionRollingUpgradeTest.java
index 0f7291a9665..68b404de6f3 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/PluginVersionRollingUpgradeTest.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/PluginVersionRollingUpgradeTest.java
@@ -131,11 +131,11 @@ public class PluginVersionRollingUpgradeTest extends
AbstractRollingUpgradeTest
checkJoinFailed(3, "2.20.0", false, "Some components active in the
cluster are not configured on the joining server node");
checkJoinSuccess(3, "2.20.0", true);
- checkJoinFailed(4, "2.20.0 | 3.0.0", RU_UNAVAILABLE_BETWEEN_VER_ERR);
+ checkJoinFailed(4, "2.20.0 | 3.0.0", NOT_SUPPORTED_VER_ERR);
checkJoinSuccess(4, "2.20.0 | 2.0.0", true);
- checkJoinFailed(5, "2.20.0 | 3.0.0", VER_INCOMPATIBLE_ERR);
+ checkJoinFailed(5, "2.20.0 | 2.1.0", VER_INCOMPATIBLE_ERR);
upgradeNodeVersion(0, "2.19.0 | 1.0.0", "2.20.0 | 2.0.0");
upgradeNodeVersion(1, "2.19.0 | 1.0.0", "2.20.0 | 2.0.0");
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java
similarity index 81%
copy from
modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
copy to
modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java
index b40cea1590d..edb4b3b3646 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java
@@ -18,7 +18,10 @@
package org.apache.ignite.internal.processors.rollingupgrade.feature;
/** */
-public class TestPluginReleaseFeatures_3_0_0 {
+public class TestPluginReleaseFeatures_2_1_0 {
/** */
- public static final IgniteFeature VER_3_0_0_ID_2_FEATURE = new
TestPluginFeature(2);
+ public static final IgniteFeature VER_2_1_0_ID_1_FEATURE = new
TestPluginFeature(1);
+
+ /** */
+ public static final IgniteFeature VER_2_1_0_ID_2_FEATURE = new
TestPluginFeature(2);
}
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
index b40cea1590d..d5c7b4f2e23 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_3_0_0.java
@@ -21,4 +21,7 @@ package
org.apache.ignite.internal.processors.rollingupgrade.feature;
public class TestPluginReleaseFeatures_3_0_0 {
/** */
public static final IgniteFeature VER_3_0_0_ID_2_FEATURE = new
TestPluginFeature(2);
+
+ /** */
+ public static final IgniteFeature VER_3_0_0_ID_3_FEATURE = new
TestPluginFeature(3);
}