-
Notifications
You must be signed in to change notification settings - Fork 25.6k
GET /_migration/deprecations doesn't check disk watermarks against correct settings values #138115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
58f459a
48fde3a
4ea451a
ca2fec1
004395a
106039c
0f79c08
04cea26
b9e5317
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| pr: 138115 | ||
| summary: GET /_migration/deprecations doesn't check disk watermarks against correct | ||
| settings values | ||
| area: Infra/Core | ||
| type: bug | ||
| issues: | ||
| - 137005 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,10 @@ | |
| import org.elasticsearch.action.support.master.TransportMasterNodeReadAction; | ||
| import org.elasticsearch.client.internal.OriginSettingClient; | ||
| import org.elasticsearch.client.internal.node.NodeClient; | ||
| import org.elasticsearch.cluster.ClusterInfo; | ||
| import org.elasticsearch.cluster.ClusterInfoService; | ||
| import org.elasticsearch.cluster.ClusterState; | ||
| import org.elasticsearch.cluster.DiskUsage; | ||
| import org.elasticsearch.cluster.block.ClusterBlockException; | ||
| import org.elasticsearch.cluster.block.ClusterBlockLevel; | ||
| import org.elasticsearch.cluster.metadata.ComponentTemplate; | ||
|
|
@@ -25,11 +28,17 @@ | |
| import org.elasticsearch.cluster.metadata.Metadata; | ||
| import org.elasticsearch.cluster.metadata.ProjectMetadata; | ||
| import org.elasticsearch.cluster.metadata.Template; | ||
| import org.elasticsearch.cluster.node.DiscoveryNode; | ||
| import org.elasticsearch.cluster.node.DiscoveryNodes; | ||
| import org.elasticsearch.cluster.project.ProjectResolver; | ||
| import org.elasticsearch.cluster.routing.allocation.DiskThresholdSettings; | ||
| import org.elasticsearch.cluster.service.ClusterService; | ||
| import org.elasticsearch.common.regex.Regex; | ||
| import org.elasticsearch.common.settings.ClusterSettings; | ||
| import org.elasticsearch.common.settings.Setting; | ||
| import org.elasticsearch.common.settings.Settings; | ||
| import org.elasticsearch.common.unit.ByteSizeValue; | ||
| import org.elasticsearch.common.util.CollectionUtils; | ||
| import org.elasticsearch.core.Tuple; | ||
| import org.elasticsearch.injection.guice.Inject; | ||
| import org.elasticsearch.tasks.Task; | ||
|
|
@@ -46,10 +55,14 @@ | |
| import java.util.Collections; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Locale; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.stream.Collectors; | ||
| import java.util.stream.Stream; | ||
|
|
||
| import static org.elasticsearch.cluster.routing.allocation.DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_LOW_DISK_WATERMARK_SETTING; | ||
|
|
||
| public class TransportDeprecationInfoAction extends TransportMasterNodeReadAction< | ||
| DeprecationInfoAction.Request, | ||
| DeprecationInfoAction.Response> { | ||
|
|
@@ -64,6 +77,7 @@ public class TransportDeprecationInfoAction extends TransportMasterNodeReadActio | |
| private final IndexNameExpressionResolver indexNameExpressionResolver; | ||
| private final Settings settings; | ||
| private final NamedXContentRegistry xContentRegistry; | ||
| private final ClusterInfoService clusterInfoService; | ||
| private volatile List<String> skipTheseDeprecations; | ||
| private final NodeDeprecationChecker nodeDeprecationChecker; | ||
| private final ClusterDeprecationChecker clusterDeprecationChecker; | ||
|
|
@@ -80,6 +94,7 @@ public TransportDeprecationInfoAction( | |
| IndexNameExpressionResolver indexNameExpressionResolver, | ||
| NodeClient client, | ||
| NamedXContentRegistry xContentRegistry, | ||
| ClusterInfoService clusterInfoService, | ||
| ProjectResolver projectResolver | ||
| ) { | ||
| super( | ||
|
|
@@ -96,6 +111,7 @@ public TransportDeprecationInfoAction( | |
| this.indexNameExpressionResolver = indexNameExpressionResolver; | ||
| this.settings = settings; | ||
| this.xContentRegistry = xContentRegistry; | ||
| this.clusterInfoService = clusterInfoService; | ||
| skipTheseDeprecations = SKIP_DEPRECATIONS_SETTING.get(settings); | ||
| nodeDeprecationChecker = new NodeDeprecationChecker(threadPool); | ||
| clusterDeprecationChecker = new ClusterDeprecationChecker(xContentRegistry); | ||
|
|
@@ -128,6 +144,15 @@ protected final void masterOperation( | |
| final ActionListener<DeprecationInfoAction.Response> listener | ||
| ) { | ||
| PrecomputedData precomputedData = new PrecomputedData(); | ||
| DeprecationIssue lowWatermarkIssue = checkDiskLowWatermark( | ||
| clusterService.getClusterSettings(), | ||
| clusterInfoService.getClusterInfo(), | ||
| state.nodes() | ||
| ); | ||
| if (lowWatermarkIssue != null) { | ||
| precomputedData.setOnceDiskWatermarkIssue(lowWatermarkIssue); | ||
| } | ||
|
|
||
| final var project = projectResolver.getProjectMetadata(state); | ||
| try (var refs = new RefCountingListener(checkAndCreateResponse(project, request, precomputedData, listener))) { | ||
| nodeDeprecationChecker.check(client, refs.acquire(precomputedData::setOnceNodeSettingsIssues)); | ||
|
|
@@ -241,6 +266,7 @@ public static class PrecomputedData { | |
| private final SetOnce<List<DeprecationIssue>> nodeSettingsIssues = new SetOnce<>(); | ||
| private final SetOnce<Map<String, List<DeprecationIssue>>> pluginIssues = new SetOnce<>(); | ||
| private final SetOnce<List<TransformConfig>> transformConfigs = new SetOnce<>(); | ||
| private final SetOnce<DeprecationIssue> diskWatermarkIssue = new SetOnce<>(); | ||
|
|
||
| public void setOnceNodeSettingsIssues(List<DeprecationIssue> nodeSettingsIssues) { | ||
| this.nodeSettingsIssues.set(nodeSettingsIssues); | ||
|
|
@@ -254,8 +280,17 @@ public void setOnceTransformConfigs(List<TransformConfig> transformConfigs) { | |
| this.transformConfigs.set(transformConfigs); | ||
| } | ||
|
|
||
| public void setOnceDiskWatermarkIssue(DeprecationIssue diskWatermarkIssue) { | ||
| this.diskWatermarkIssue.set(diskWatermarkIssue); | ||
| } | ||
|
|
||
| public List<DeprecationIssue> nodeSettingsIssues() { | ||
| return nodeSettingsIssues.get(); | ||
| DeprecationIssue watermarkIssue = diskWatermarkIssue.get(); | ||
| List<DeprecationIssue> deprecationIssues = nodeSettingsIssues.get(); | ||
mosche marked this conversation as resolved.
Show resolved
Hide resolved
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. optional nit, how about better distinguishing between the enhanced |
||
| if (watermarkIssue == null) { | ||
| return deprecationIssues; | ||
| } | ||
| return CollectionUtils.appendToCopy(deprecationIssues, watermarkIssue); | ||
alexey-ivanov-es marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| public Map<String, List<DeprecationIssue>> pluginIssues() { | ||
|
|
@@ -396,4 +431,44 @@ private void transformConfigs(PageParams currentPage, ActionListener<Stream<Tran | |
| private <T> ActionListener<T> executeInGenericThreadpool(ActionListener<T> listener) { | ||
| return new ThreadedActionListener<>(threadPool.generic(), listener); | ||
| } | ||
|
|
||
| static DeprecationIssue checkDiskLowWatermark(ClusterSettings clusterSettings, ClusterInfo clusterInfo, DiscoveryNodes discoveryNodes) { | ||
| Map<String, DiskUsage> nodeMostAvailableDiskUsages = clusterInfo.getNodeMostAvailableDiskUsages(); | ||
|
|
||
| List<String> impactedNodeNames = nodeMostAvailableDiskUsages.entrySet() | ||
| .stream() | ||
| .filter(e -> exceedsLowWatermark(clusterSettings, e.getValue())) | ||
| .map(Map.Entry::getKey) | ||
| .map(discoveryNodes::get) | ||
| .filter(Objects::nonNull) | ||
| .map(DiscoveryNode::getName) | ||
| .sorted() | ||
| .toList(); | ||
|
|
||
| if (impactedNodeNames.isEmpty()) { | ||
| return null; | ||
| } | ||
|
|
||
| return new DeprecationIssue( | ||
| DeprecationIssue.Level.CRITICAL, | ||
| "Disk usage exceeds low watermark", | ||
| "https://ela.st/es-deprecation-7-disk-watermark-exceeded", | ||
| String.format( | ||
| Locale.ROOT, | ||
| "Disk usage exceeds low watermark, which will prevent reindexing indices during upgrade. Get disk usage on " | ||
| + "all nodes below the value specified in %s (nodes impacted: %s)", | ||
| CLUSTER_ROUTING_ALLOCATION_LOW_DISK_WATERMARK_SETTING.getKey(), | ||
| impactedNodeNames | ||
| ), | ||
| false, | ||
| null | ||
| ); | ||
| } | ||
|
|
||
| private static boolean exceedsLowWatermark(ClusterSettings clusterSettings, DiskUsage usage) { | ||
| long freeBytes = usage.freeBytes(); | ||
| long totalBytes = usage.totalBytes(); | ||
| return freeBytes < DiskThresholdSettings.getFreeBytesThresholdLowStage(ByteSizeValue.ofBytes(totalBytes), clusterSettings) | ||
| .getBytes(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,14 +9,19 @@ | |
| import org.elasticsearch.ElasticsearchStatusException; | ||
| import org.elasticsearch.action.ActionListener; | ||
| import org.elasticsearch.action.support.PlainActionFuture; | ||
| import org.elasticsearch.cluster.ClusterInfo; | ||
| import org.elasticsearch.cluster.DiskUsage; | ||
| import org.elasticsearch.cluster.metadata.ComponentTemplate; | ||
| import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; | ||
| import org.elasticsearch.cluster.metadata.DataStream; | ||
| import org.elasticsearch.cluster.metadata.IndexMetadata; | ||
| import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; | ||
| import org.elasticsearch.cluster.metadata.ProjectMetadata; | ||
| import org.elasticsearch.cluster.metadata.Template; | ||
| import org.elasticsearch.cluster.node.DiscoveryNode; | ||
| import org.elasticsearch.cluster.node.DiscoveryNodes; | ||
| import org.elasticsearch.common.Strings; | ||
| import org.elasticsearch.common.settings.ClusterSettings; | ||
| import org.elasticsearch.common.settings.Settings; | ||
| import org.elasticsearch.index.IndexVersion; | ||
| import org.elasticsearch.indices.TestIndexNameExpressionResolver; | ||
|
|
@@ -40,6 +45,7 @@ | |
| import java.util.stream.Collectors; | ||
| import java.util.stream.Stream; | ||
|
|
||
| import static org.elasticsearch.common.settings.ClusterSettings.BUILT_IN_CLUSTER_SETTINGS; | ||
| import static org.elasticsearch.xpack.deprecation.DeprecationInfoAction.Response.RESERVED_NAMES; | ||
| import static org.elasticsearch.xpack.deprecation.DeprecationInfoActionResponseTests.createTestDeprecationIssue; | ||
| import static org.hamcrest.Matchers.containsString; | ||
|
|
@@ -316,6 +322,58 @@ public void testPluginSettingIssuesWithFailures() { | |
| assertThat(exception.getCause().getMessage(), containsString("boom")); | ||
| } | ||
|
|
||
| public void testCheckDiskLowWatermark() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added a unit test for it. I wanted to add an integration or REST test, but found it's quite difficult to set up the behavior needed I wanted to test |
||
| Settings settingsWithLowWatermark = Settings.builder().put("cluster.routing.allocation.disk.watermark.low", "10%").build(); | ||
| ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, BUILT_IN_CLUSTER_SETTINGS); | ||
| String nodeId1 = "123"; | ||
| String nodeId2 = "124"; | ||
| String nodeId3 = "125"; | ||
| long totalBytesOnMachine = 100; | ||
| long totalBytesFree = 70; | ||
| ClusterInfo clusterInfo = ClusterInfo.builder() | ||
| .mostAvailableSpaceUsage( | ||
| Map.of( | ||
| nodeId1, | ||
| new DiskUsage(nodeId1, "", "", totalBytesOnMachine, totalBytesFree), | ||
| nodeId2, | ||
| new DiskUsage(nodeId2, "", "", totalBytesOnMachine, totalBytesFree), | ||
| nodeId3, | ||
| new DiskUsage(nodeId2, "", "", totalBytesOnMachine, totalBytesOnMachine) | ||
| ) | ||
| ) | ||
| .build(); | ||
| DiscoveryNode node1 = mock(DiscoveryNode.class); | ||
| when(node1.getId()).thenReturn(nodeId1); | ||
| when(node1.getName()).thenReturn("node1"); | ||
| DiscoveryNodes discoveryNodes = mock(DiscoveryNodes.class); | ||
| when(discoveryNodes.get(nodeId1)).thenReturn(node1); | ||
| DiscoveryNode node2 = mock(DiscoveryNode.class); | ||
| when(node2.getId()).thenReturn(nodeId2); | ||
| when(node2.getName()).thenReturn("node2"); | ||
| when(discoveryNodes.get(nodeId2)).thenReturn(node2); | ||
| DiscoveryNode node3 = mock(DiscoveryNode.class); | ||
| when(node3.getId()).thenReturn(nodeId3); | ||
| when(node3.getName()).thenReturn("node3"); | ||
| when(discoveryNodes.get(nodeId3)).thenReturn(node3); | ||
|
|
||
| clusterSettings.applySettings(settingsWithLowWatermark); | ||
| DeprecationIssue issue = TransportDeprecationInfoAction.checkDiskLowWatermark(clusterSettings, clusterInfo, discoveryNodes); | ||
| assertNotNull(issue); | ||
| assertEquals("Disk usage exceeds low watermark", issue.getMessage()); | ||
|
|
||
| // Making sure there's no warning when we clear out the cluster settings: | ||
| clusterSettings.applySettings(Settings.EMPTY); | ||
| issue = TransportDeprecationInfoAction.checkDiskLowWatermark(clusterSettings, clusterInfo, discoveryNodes); | ||
| assertNull(issue); | ||
|
|
||
| // And make sure there is a warning when the setting is in the node settings but not the cluster settings: | ||
| clusterSettings = new ClusterSettings(settingsWithLowWatermark, BUILT_IN_CLUSTER_SETTINGS); | ||
| issue = TransportDeprecationInfoAction.checkDiskLowWatermark(clusterSettings, clusterInfo, discoveryNodes); | ||
| assertNotNull(issue); | ||
| assertEquals("Disk usage exceeds low watermark", issue.getMessage()); | ||
| assertThat(issue.getDetails(), containsString("(nodes impacted: [node1, node2])")); | ||
| } | ||
|
|
||
| private static ResourceDeprecationChecker createResourceChecker( | ||
| String name, | ||
| BiFunction<ProjectMetadata, DeprecationInfoAction.Request, Map<String, List<DeprecationIssue>>> check | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.