1515import org .elasticsearch .action .support .master .TransportMasterNodeReadAction ;
1616import org .elasticsearch .client .internal .OriginSettingClient ;
1717import org .elasticsearch .client .internal .node .NodeClient ;
18+ import org .elasticsearch .cluster .ClusterInfo ;
19+ import org .elasticsearch .cluster .ClusterInfoService ;
1820import org .elasticsearch .cluster .ClusterState ;
21+ import org .elasticsearch .cluster .DiskUsage ;
1922import org .elasticsearch .cluster .block .ClusterBlockException ;
2023import org .elasticsearch .cluster .block .ClusterBlockLevel ;
2124import org .elasticsearch .cluster .metadata .ComponentTemplate ;
2528import org .elasticsearch .cluster .metadata .Metadata ;
2629import org .elasticsearch .cluster .metadata .ProjectMetadata ;
2730import org .elasticsearch .cluster .metadata .Template ;
31+ import org .elasticsearch .cluster .node .DiscoveryNode ;
32+ import org .elasticsearch .cluster .node .DiscoveryNodes ;
2833import org .elasticsearch .cluster .project .ProjectResolver ;
34+ import org .elasticsearch .cluster .routing .allocation .DiskThresholdSettings ;
2935import org .elasticsearch .cluster .service .ClusterService ;
3036import org .elasticsearch .common .regex .Regex ;
37+ import org .elasticsearch .common .settings .ClusterSettings ;
3138import org .elasticsearch .common .settings .Setting ;
3239import org .elasticsearch .common .settings .Settings ;
40+ import org .elasticsearch .common .unit .ByteSizeValue ;
41+ import org .elasticsearch .common .util .CollectionUtils ;
3342import org .elasticsearch .core .Tuple ;
3443import org .elasticsearch .injection .guice .Inject ;
3544import org .elasticsearch .tasks .Task ;
4655import java .util .Collections ;
4756import java .util .HashMap ;
4857import java .util .List ;
58+ import java .util .Locale ;
4959import java .util .Map ;
60+ import java .util .Objects ;
5061import java .util .stream .Collectors ;
5162import java .util .stream .Stream ;
5263
64+ import static org .elasticsearch .cluster .routing .allocation .DiskThresholdSettings .CLUSTER_ROUTING_ALLOCATION_LOW_DISK_WATERMARK_SETTING ;
65+
5366public class TransportDeprecationInfoAction extends TransportMasterNodeReadAction <
5467 DeprecationInfoAction .Request ,
5568 DeprecationInfoAction .Response > {
@@ -64,6 +77,7 @@ public class TransportDeprecationInfoAction extends TransportMasterNodeReadActio
6477 private final IndexNameExpressionResolver indexNameExpressionResolver ;
6578 private final Settings settings ;
6679 private final NamedXContentRegistry xContentRegistry ;
80+ private final ClusterInfoService clusterInfoService ;
6781 private volatile List <String > skipTheseDeprecations ;
6882 private final NodeDeprecationChecker nodeDeprecationChecker ;
6983 private final ClusterDeprecationChecker clusterDeprecationChecker ;
@@ -80,6 +94,7 @@ public TransportDeprecationInfoAction(
8094 IndexNameExpressionResolver indexNameExpressionResolver ,
8195 NodeClient client ,
8296 NamedXContentRegistry xContentRegistry ,
97+ ClusterInfoService clusterInfoService ,
8398 ProjectResolver projectResolver
8499 ) {
85100 super (
@@ -96,6 +111,7 @@ public TransportDeprecationInfoAction(
96111 this .indexNameExpressionResolver = indexNameExpressionResolver ;
97112 this .settings = settings ;
98113 this .xContentRegistry = xContentRegistry ;
114+ this .clusterInfoService = clusterInfoService ;
99115 skipTheseDeprecations = SKIP_DEPRECATIONS_SETTING .get (settings );
100116 nodeDeprecationChecker = new NodeDeprecationChecker (threadPool );
101117 clusterDeprecationChecker = new ClusterDeprecationChecker (xContentRegistry );
@@ -127,7 +143,13 @@ protected final void masterOperation(
127143 ClusterState state ,
128144 final ActionListener <DeprecationInfoAction .Response > listener
129145 ) {
130- PrecomputedData precomputedData = new PrecomputedData ();
146+ DeprecationIssue lowWatermarkIssue = checkDiskLowWatermark (
147+ clusterService .getClusterSettings (),
148+ clusterInfoService .getClusterInfo (),
149+ state .nodes ()
150+ );
151+ PrecomputedData precomputedData = new PrecomputedData (lowWatermarkIssue );
152+
131153 final var project = projectResolver .getProjectMetadata (state );
132154 try (var refs = new RefCountingListener (checkAndCreateResponse (project , request , precomputedData , listener ))) {
133155 nodeDeprecationChecker .check (client , refs .acquire (precomputedData ::setOnceNodeSettingsIssues ));
@@ -155,7 +177,7 @@ protected final void masterOperation(
155177 * @return The listener that should be executed after all the remote requests have completed and the {@link PrecomputedData}
156178 * is initialised.
157179 */
158- public ActionListener <Void > checkAndCreateResponse (
180+ private ActionListener <Void > checkAndCreateResponse (
159181 ProjectMetadata project ,
160182 DeprecationInfoAction .Request request ,
161183 PrecomputedData precomputedData ,
@@ -241,6 +263,11 @@ public static class PrecomputedData {
241263 private final SetOnce <List <DeprecationIssue >> nodeSettingsIssues = new SetOnce <>();
242264 private final SetOnce <Map <String , List <DeprecationIssue >>> pluginIssues = new SetOnce <>();
243265 private final SetOnce <List <TransformConfig >> transformConfigs = new SetOnce <>();
266+ private final DeprecationIssue diskWatermarkIssue ;
267+
268+ public PrecomputedData (DeprecationIssue diskWatermarkIssue ) {
269+ this .diskWatermarkIssue = diskWatermarkIssue ;
270+ }
244271
245272 public void setOnceNodeSettingsIssues (List <DeprecationIssue > nodeSettingsIssues ) {
246273 this .nodeSettingsIssues .set (nodeSettingsIssues );
@@ -255,7 +282,12 @@ public void setOnceTransformConfigs(List<TransformConfig> transformConfigs) {
255282 }
256283
257284 public List <DeprecationIssue > nodeSettingsIssues () {
258- return nodeSettingsIssues .get ();
285+ List <DeprecationIssue > deprecationIssues = nodeSettingsIssues .get ();
286+ assert deprecationIssues != null : "nodeSettingsIssues must be set before calling this method" ;
287+ if (diskWatermarkIssue == null ) {
288+ return deprecationIssues ;
289+ }
290+ return CollectionUtils .appendToCopy (deprecationIssues , diskWatermarkIssue );
259291 }
260292
261293 public Map <String , List <DeprecationIssue >> pluginIssues () {
@@ -396,4 +428,44 @@ private void transformConfigs(PageParams currentPage, ActionListener<Stream<Tran
396428 private <T > ActionListener <T > executeInGenericThreadpool (ActionListener <T > listener ) {
397429 return new ThreadedActionListener <>(threadPool .generic (), listener );
398430 }
431+
432+ static DeprecationIssue checkDiskLowWatermark (ClusterSettings clusterSettings , ClusterInfo clusterInfo , DiscoveryNodes discoveryNodes ) {
433+ Map <String , DiskUsage > nodeMostAvailableDiskUsages = clusterInfo .getNodeMostAvailableDiskUsages ();
434+
435+ List <String > impactedNodeNames = nodeMostAvailableDiskUsages .entrySet ()
436+ .stream ()
437+ .filter (e -> exceedsLowWatermark (clusterSettings , e .getValue ()))
438+ .map (Map .Entry ::getKey )
439+ .map (discoveryNodes ::get )
440+ .filter (Objects ::nonNull )
441+ .map (DiscoveryNode ::getName )
442+ .sorted ()
443+ .toList ();
444+
445+ if (impactedNodeNames .isEmpty ()) {
446+ return null ;
447+ }
448+
449+ return new DeprecationIssue (
450+ DeprecationIssue .Level .CRITICAL ,
451+ "Disk usage exceeds low watermark" ,
452+ "https://ela.st/es-deprecation-7-disk-watermark-exceeded" ,
453+ String .format (
454+ Locale .ROOT ,
455+ "Disk usage exceeds low watermark, which will prevent reindexing indices during upgrade. Get disk usage on "
456+ + "all nodes below the value specified in %s (nodes impacted: %s)" ,
457+ CLUSTER_ROUTING_ALLOCATION_LOW_DISK_WATERMARK_SETTING .getKey (),
458+ impactedNodeNames
459+ ),
460+ false ,
461+ null
462+ );
463+ }
464+
465+ private static boolean exceedsLowWatermark (ClusterSettings clusterSettings , DiskUsage usage ) {
466+ long freeBytes = usage .freeBytes ();
467+ long totalBytes = usage .totalBytes ();
468+ return freeBytes < DiskThresholdSettings .getFreeBytesThresholdLowStage (ByteSizeValue .ofBytes (totalBytes ), clusterSettings )
469+ .getBytes ();
470+ }
399471}
0 commit comments