1414import org .elasticsearch .Version ;
1515import org .elasticsearch .cluster .node .DiscoveryNode ;
1616import org .elasticsearch .cluster .node .DiscoveryNodeRole ;
17+ import org .elasticsearch .common .Strings ;
1718import org .elasticsearch .common .io .stream .StreamInput ;
1819import org .elasticsearch .common .io .stream .StreamOutput ;
1920import org .elasticsearch .common .io .stream .Writeable ;
2021import org .elasticsearch .common .settings .Settings ;
2122import org .elasticsearch .common .unit .ByteSizeValue ;
2223import org .elasticsearch .common .unit .Processors ;
2324import org .elasticsearch .core .Nullable ;
25+ import org .elasticsearch .core .UpdateForV9 ;
2426import org .elasticsearch .features .NodeFeature ;
2527import org .elasticsearch .xcontent .ConstructingObjectParser ;
2628import org .elasticsearch .xcontent .ObjectParser ;
3638import java .util .Set ;
3739import java .util .TreeSet ;
3840import java .util .function .Predicate ;
41+ import java .util .regex .Pattern ;
3942
4043import static java .lang .String .format ;
4144import static org .elasticsearch .node .Node .NODE_EXTERNAL_ID_SETTING ;
@@ -55,6 +58,8 @@ public final class DesiredNode implements Writeable, ToXContentObject, Comparabl
5558 private static final ParseField PROCESSORS_RANGE_FIELD = new ParseField ("processors_range" );
5659 private static final ParseField MEMORY_FIELD = new ParseField ("memory" );
5760 private static final ParseField STORAGE_FIELD = new ParseField ("storage" );
61+ @ UpdateForV9 (owner = UpdateForV9 .Owner .DISTRIBUTED_COORDINATION ) // Remove deprecated field
62+ private static final ParseField VERSION_FIELD = new ParseField ("node_version" );
5863
5964 public static final ConstructingObjectParser <DesiredNode , Void > PARSER = new ConstructingObjectParser <>(
6065 "desired_node" ,
@@ -64,7 +69,8 @@ public final class DesiredNode implements Writeable, ToXContentObject, Comparabl
6469 (Processors ) args [1 ],
6570 (ProcessorsRange ) args [2 ],
6671 (ByteSizeValue ) args [3 ],
67- (ByteSizeValue ) args [4 ]
72+ (ByteSizeValue ) args [4 ],
73+ (String ) args [5 ]
6874 )
6975 );
7076
@@ -98,6 +104,12 @@ static <T> void configureParser(ConstructingObjectParser<T, Void> parser) {
98104 STORAGE_FIELD ,
99105 ObjectParser .ValueType .STRING
100106 );
107+ parser .declareField (
108+ ConstructingObjectParser .optionalConstructorArg (),
109+ (p , c ) -> p .text (),
110+ VERSION_FIELD ,
111+ ObjectParser .ValueType .STRING
112+ );
101113 }
102114
103115 private final Settings settings ;
@@ -106,9 +118,21 @@ static <T> void configureParser(ConstructingObjectParser<T, Void> parser) {
106118 private final ByteSizeValue memory ;
107119 private final ByteSizeValue storage ;
108120
121+ @ UpdateForV9 (owner = UpdateForV9 .Owner .DISTRIBUTED_COORDINATION ) // Remove deprecated version field
122+ private final String version ;
109123 private final String externalId ;
110124 private final Set <DiscoveryNodeRole > roles ;
111125
126+ @ Deprecated
127+ public DesiredNode (Settings settings , ProcessorsRange processorsRange , ByteSizeValue memory , ByteSizeValue storage , String version ) {
128+ this (settings , null , processorsRange , memory , storage , version );
129+ }
130+
131+ @ Deprecated
132+ public DesiredNode (Settings settings , double processors , ByteSizeValue memory , ByteSizeValue storage , String version ) {
133+ this (settings , Processors .of (processors ), null , memory , storage , version );
134+ }
135+
112136 public DesiredNode (Settings settings , ProcessorsRange processorsRange , ByteSizeValue memory , ByteSizeValue storage ) {
113137 this (settings , null , processorsRange , memory , storage );
114138 }
@@ -118,6 +142,17 @@ public DesiredNode(Settings settings, double processors, ByteSizeValue memory, B
118142 }
119143
120144 DesiredNode (Settings settings , Processors processors , ProcessorsRange processorsRange , ByteSizeValue memory , ByteSizeValue storage ) {
145+ this (settings , processors , processorsRange , memory , storage , null );
146+ }
147+
148+ DesiredNode (
149+ Settings settings ,
150+ Processors processors ,
151+ ProcessorsRange processorsRange ,
152+ ByteSizeValue memory ,
153+ ByteSizeValue storage ,
154+ @ Deprecated String version
155+ ) {
121156 assert settings != null ;
122157 assert memory != null ;
123158 assert storage != null ;
@@ -151,6 +186,7 @@ public DesiredNode(Settings settings, double processors, ByteSizeValue memory, B
151186 this .processorsRange = processorsRange ;
152187 this .memory = memory ;
153188 this .storage = storage ;
189+ this .version = version ;
154190 this .externalId = NODE_EXTERNAL_ID_SETTING .get (settings );
155191 this .roles = Collections .unmodifiableSortedSet (new TreeSet <>(DiscoveryNode .getRolesFromSettings (settings )));
156192 }
@@ -174,7 +210,19 @@ public static DesiredNode readFrom(StreamInput in) throws IOException {
174210 } else {
175211 version = Version .readVersion (in ).toString ();
176212 }
177- return new DesiredNode (settings , processors , processorsRange , memory , storage );
213+ return new DesiredNode (settings , processors , processorsRange , memory , storage , version );
214+ }
215+
216+ private static final Pattern SEMANTIC_VERSION_PATTERN = Pattern .compile ("^(\\ d+\\ .\\ d+\\ .\\ d+)\\ D?.*" );
217+
218+ private static Version parseLegacyVersion (String version ) {
219+ if (version != null ) {
220+ var semanticVersionMatcher = SEMANTIC_VERSION_PATTERN .matcher (version );
221+ if (semanticVersionMatcher .matches ()) {
222+ return Version .fromString (semanticVersionMatcher .group (1 ));
223+ }
224+ }
225+ return null ;
178226 }
179227
180228 @ Override
@@ -191,9 +239,15 @@ public void writeTo(StreamOutput out) throws IOException {
191239 memory .writeTo (out );
192240 storage .writeTo (out );
193241 if (out .getTransportVersion ().onOrAfter (TransportVersions .V_8_13_0 )) {
194- out .writeOptionalString (null );
242+ out .writeOptionalString (version );
195243 } else {
196- Version .writeVersion (Version .CURRENT , out );
244+ Version parsedVersion = parseLegacyVersion (version );
245+ if (version == null ) {
246+ // Some node is from before we made the version field not required. If so, fill in with the current node version.
247+ Version .writeVersion (Version .CURRENT , out );
248+ } else {
249+ Version .writeVersion (parsedVersion , out );
250+ }
197251 }
198252 }
199253
@@ -221,6 +275,14 @@ public void toInnerXContent(XContentBuilder builder, Params params) throws IOExc
221275 }
222276 builder .field (MEMORY_FIELD .getPreferredName (), memory );
223277 builder .field (STORAGE_FIELD .getPreferredName (), storage );
278+ addDeprecatedVersionField (builder );
279+ }
280+
281+ @ UpdateForV9 (owner = UpdateForV9 .Owner .DISTRIBUTED_COORDINATION ) // Remove deprecated field from response
282+ private void addDeprecatedVersionField (XContentBuilder builder ) throws IOException {
283+ if (version != null ) {
284+ builder .field (VERSION_FIELD .getPreferredName (), version );
285+ }
224286 }
225287
226288 public boolean hasMasterRole () {
@@ -304,6 +366,7 @@ private boolean equalsWithoutProcessorsSpecification(DesiredNode that) {
304366 return Objects .equals (settings , that .settings )
305367 && Objects .equals (memory , that .memory )
306368 && Objects .equals (storage , that .storage )
369+ && Objects .equals (version , that .version )
307370 && Objects .equals (externalId , that .externalId )
308371 && Objects .equals (roles , that .roles );
309372 }
@@ -316,7 +379,7 @@ public boolean equalsWithProcessorsCloseTo(DesiredNode that) {
316379
317380 @ Override
318381 public int hashCode () {
319- return Objects .hash (settings , processors , processorsRange , memory , storage , externalId , roles );
382+ return Objects .hash (settings , processors , processorsRange , memory , storage , version , externalId , roles );
320383 }
321384
322385 @ Override
@@ -345,6 +408,10 @@ public String toString() {
345408 + '}' ;
346409 }
347410
411+ public boolean hasVersion () {
412+ return Strings .isNullOrBlank (version ) == false ;
413+ }
414+
348415 public record ProcessorsRange (Processors min , @ Nullable Processors max ) implements Writeable , ToXContentObject {
349416
350417 private static final ParseField MIN_FIELD = new ParseField ("min" );
0 commit comments