diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/TransportUpdateDataStreamSettingsAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/TransportUpdateDataStreamSettingsAction.java index c008a10f93c06..dc25e2ce4f4e1 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/TransportUpdateDataStreamSettingsAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/TransportUpdateDataStreamSettingsAction.java @@ -16,7 +16,6 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.CountDownActionListener; import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.action.support.master.TransportMasterNodeAction; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.block.ClusterBlockException; @@ -107,46 +106,38 @@ protected void masterOperation( request.indices() ); List dataStreamSettingsResponse = new ArrayList<>(); - CountDownActionListener countDownListener = new CountDownActionListener(dataStreamNames.size() + 1, new ActionListener<>() { - @Override - public void onResponse(Void unused) { - listener.onResponse(new UpdateDataStreamSettingsAction.Response(dataStreamSettingsResponse)); - } - - @Override - public void onFailure(Exception e) { - listener.onFailure(e); - } - }); + CountDownActionListener countDownListener = new CountDownActionListener( + dataStreamNames.size() + 1, + listener.delegateFailure( + (responseActionListener, unused) -> responseActionListener.onResponse( + new UpdateDataStreamSettingsAction.Response(dataStreamSettingsResponse) + ) + ) + ); countDownListener.onResponse(null); for (String dataStreamName : dataStreamNames) { updateSingleDataStream( dataStreamName, request.getSettings(), + request.isDryRun(), request.masterNodeTimeout(), request.ackTimeout(), - new ActionListener<>() { - @Override - public void onResponse(UpdateDataStreamSettingsAction.DataStreamSettingsResponse dataStreamResponse) { - dataStreamSettingsResponse.add(dataStreamResponse); - countDownListener.onResponse(null); - } - - @Override - public void onFailure(Exception e) { - dataStreamSettingsResponse.add( - new UpdateDataStreamSettingsAction.DataStreamSettingsResponse( - dataStreamName, - false, - e.getMessage(), - EMPTY, - EMPTY, - UpdateDataStreamSettingsAction.DataStreamSettingsResponse.IndicesSettingsResult.EMPTY - ) - ); - countDownListener.onResponse(null); - } - } + ActionListener.wrap(dataStreamResponse -> { + dataStreamSettingsResponse.add(dataStreamResponse); + countDownListener.onResponse(null); + }, e -> { + dataStreamSettingsResponse.add( + new UpdateDataStreamSettingsAction.DataStreamSettingsResponse( + dataStreamName, + false, + e.getMessage(), + EMPTY, + EMPTY, + UpdateDataStreamSettingsAction.DataStreamSettingsResponse.IndicesSettingsResult.EMPTY + ) + ); + countDownListener.onResponse(null); + }) ); } } @@ -154,6 +145,7 @@ public void onFailure(Exception e) { private void updateSingleDataStream( String dataStreamName, Settings settingsOverrides, + boolean dryRun, TimeValue masterNodeTimeout, TimeValue ackTimeout, ActionListener listener @@ -198,36 +190,30 @@ private void updateSingleDataStream( ackTimeout, dataStreamName, settingsOverrides, - new ActionListener<>() { - @Override - public void onResponse(AcknowledgedResponse acknowledgedResponse) { - if (acknowledgedResponse.isAcknowledged()) { - updateSettingsOnIndices(dataStreamName, settingsOverrides, masterNodeTimeout, ackTimeout, listener); - } else { - listener.onResponse( - new UpdateDataStreamSettingsAction.DataStreamSettingsResponse( - dataStreamName, - false, - "Updating settings not accepted for unknown reasons", - EMPTY, - EMPTY, - UpdateDataStreamSettingsAction.DataStreamSettingsResponse.IndicesSettingsResult.EMPTY - ) - ); - } - } - - @Override - public void onFailure(Exception e) { - listener.onFailure(e); + dryRun, + listener.delegateFailure((dataStreamSettingsResponseActionListener, dataStream) -> { + if (dataStream != null) { + updateSettingsOnIndices(dataStream, settingsOverrides, dryRun, masterNodeTimeout, ackTimeout, listener); + } else { + dataStreamSettingsResponseActionListener.onResponse( + new UpdateDataStreamSettingsAction.DataStreamSettingsResponse( + dataStreamName, + false, + "Updating settings not accepted for unknown reasons", + EMPTY, + EMPTY, + UpdateDataStreamSettingsAction.DataStreamSettingsResponse.IndicesSettingsResult.EMPTY + ) + ); } - } + }) ); } private void updateSettingsOnIndices( - String dataStreamName, + DataStream dataStream, Settings requestSettings, + boolean dryRun, TimeValue masterNodeTimeout, TimeValue ackTimeout, ActionListener listener @@ -243,26 +229,15 @@ private void updateSettingsOnIndices( appliedToDataStreamOnly.add(settingName); } } - final List concreteIndices = clusterService.state() - .projectState(projectResolver.getProjectId()) - .metadata() - .dataStreams() - .get(dataStreamName) - .getIndices(); + final List concreteIndices = dataStream.getIndices(); final List indexSettingErrors = new ArrayList<>(); - CountDownActionListener indexCountDownListener = new CountDownActionListener(concreteIndices.size() + 1, new ActionListener<>() { - // Called when all indices for all settings are complete - @Override - public void onResponse(Void unused) { - DataStream dataStream = clusterService.state() - .projectState(projectResolver.getProjectId()) - .metadata() - .dataStreams() - .get(dataStreamName); - listener.onResponse( + CountDownActionListener indexCountDownListener = new CountDownActionListener( + concreteIndices.size() + 1, + listener.delegateFailure( + (dataStreamSettingsResponseActionListener, unused) -> dataStreamSettingsResponseActionListener.onResponse( new UpdateDataStreamSettingsAction.DataStreamSettingsResponse( - dataStreamName, + dataStream.getName(), true, null, settingsFilter.filter(dataStream.getSettings()), @@ -275,37 +250,33 @@ public void onResponse(Void unused) { indexSettingErrors ) ) - ); - } + ) + ) + ); - @Override - public void onFailure(Exception e) { - listener.onFailure(e); - } - }); indexCountDownListener.onResponse(null); // handles the case where there were zero indices Settings applyToIndexSettings = builder().loadFromMap(settingsToApply).build(); for (Index index : concreteIndices) { - updateSettingsOnSingleIndex(index, applyToIndexSettings, masterNodeTimeout, ackTimeout, new ActionListener<>() { - @Override - public void onResponse(UpdateDataStreamSettingsAction.DataStreamSettingsResponse.IndexSettingError indexSettingError) { + updateSettingsOnSingleIndex( + index, + applyToIndexSettings, + dryRun, + masterNodeTimeout, + ackTimeout, + indexCountDownListener.delegateFailure((listener1, indexSettingError) -> { if (indexSettingError != null) { indexSettingErrors.add(indexSettingError); } - indexCountDownListener.onResponse(null); - } - - @Override - public void onFailure(Exception e) { - indexCountDownListener.onFailure(e); - } - }); + listener1.onResponse(null); + }) + ); } } private void updateSettingsOnSingleIndex( Index index, Settings requestSettings, + boolean dryRun, TimeValue masterNodeTimeout, TimeValue ackTimeout, ActionListener listener @@ -326,19 +297,24 @@ private void updateSettingsOnSingleIndex( ); return; } - updateSettingsService.updateSettings( - new UpdateSettingsClusterStateUpdateRequest( - projectResolver.getProjectId(), - masterNodeTimeout, - ackTimeout, - requestSettings, - UpdateSettingsClusterStateUpdateRequest.OnExisting.OVERWRITE, - UpdateSettingsClusterStateUpdateRequest.OnStaticSetting.REOPEN_INDICES, - index - ), - new ActionListener<>() { - @Override - public void onResponse(AcknowledgedResponse response) { + if (dryRun) { + /* + * This is as far as we go with dry run mode. We get the benefit of having checked that all the indices that will be touced + * are not blocked, but there is no value in going beyond this. So just respond to the listener and move on. + */ + listener.onResponse(null); + } else { + updateSettingsService.updateSettings( + new UpdateSettingsClusterStateUpdateRequest( + projectResolver.getProjectId(), + masterNodeTimeout, + ackTimeout, + requestSettings, + UpdateSettingsClusterStateUpdateRequest.OnExisting.OVERWRITE, + UpdateSettingsClusterStateUpdateRequest.OnStaticSetting.REOPEN_INDICES, + index + ), + ActionListener.wrap(response -> { UpdateDataStreamSettingsAction.DataStreamSettingsResponse.IndexSettingError error; if (response.isAcknowledged() == false) { error = new UpdateDataStreamSettingsAction.DataStreamSettingsResponse.IndexSettingError( @@ -349,16 +325,13 @@ public void onResponse(AcknowledgedResponse response) { error = null; } listener.onResponse(error); - } - - @Override - public void onFailure(Exception e) { - listener.onResponse( + }, + e -> listener.onResponse( new UpdateDataStreamSettingsAction.DataStreamSettingsResponse.IndexSettingError(index.getName(), e.getMessage()) - ); - } - } - ); + ) + ) + ); + } } } diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/rest/RestUpdateDataStreamSettingsAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/rest/RestUpdateDataStreamSettingsAction.java index 1b0a4b4637156..d21efb247a231 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/rest/RestUpdateDataStreamSettingsAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/rest/RestUpdateDataStreamSettingsAction.java @@ -45,8 +45,10 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli try (XContentParser parser = request.contentParser()) { settings = Settings.fromXContent(parser); } + boolean dryRun = request.paramAsBoolean("dry_run", false); UpdateDataStreamSettingsAction.Request putDataStreamRequest = new UpdateDataStreamSettingsAction.Request( settings, + dryRun, RestUtils.getMasterNodeTimeout(request), RestUtils.getAckTimeout(request) ).indices(Strings.splitStringByCommaToArray(request.param("name"))); diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/240_data_stream_settings.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/240_data_stream_settings.yml index 489913c7976d6..e642ef0083701 100644 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/240_data_stream_settings.yml +++ b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/240_data_stream_settings.yml @@ -192,3 +192,103 @@ setup: - match: { data_streams.0.name: my-data-stream-1 } - match: { data_streams.0.applied_to_data_stream: false } - match: { data_streams.0.error: "Cannot set the following settings on a data stream: [index.fake_setting]" } + +--- +"Test dry run": + - requires: + cluster_features: [ "logs_stream" ] + reason: requires setting 'logs_stream' to get or set data stream settings + - do: + allowed_warnings: + - "index template [my-template] has index patterns [my-data-stream-*] matching patterns from existing older templates [global] with patterns (global => [*]); this template [my-template] will take precedence during new index creation" + indices.put_index_template: + name: my-template + body: + index_patterns: [ my-data-stream-* ] + data_stream: { } + template: + settings: + number_of_replicas: 0 + lifecycle.name: my-policy + + - do: + indices.create_data_stream: + name: my-data-stream-1 + + - do: + cluster.health: + index: "my-data-stream-1" + wait_for_status: green + + - do: + indices.get_data_stream_settings: + name: my-data-stream-1 + - match: { data_streams.0.name: my-data-stream-1 } + - match: { data_streams.0.settings: {} } + - match: { data_streams.0.effective_settings.index.number_of_shards: null } + - match: { data_streams.0.effective_settings.index.number_of_replicas: "0" } + - match: { data_streams.0.effective_settings.index.lifecycle.name: "my-policy" } + + - do: + indices.get_data_stream: + name: my-data-stream-1 + - match: { data_streams.0.name: my-data-stream-1 } + - match: { data_streams.0.settings: {} } + - match: { data_streams.0.effective_settings: null } + + - do: + indices.put_data_stream_settings: + name: my-data-stream-1 + dry_run: true + body: + index: + number_of_shards: 2 + lifecycle.name: my-new-policy + - match: { data_streams.0.name: my-data-stream-1 } + - match: { data_streams.0.applied_to_data_stream: true } + - match: { data_streams.0.index_settings_results.applied_to_data_stream_only: [index.number_of_shards]} + - match: { data_streams.0.index_settings_results.applied_to_data_stream_and_backing_indices: [index.lifecycle.name] } + - match: { data_streams.0.settings.index.number_of_shards: "2" } + - match: { data_streams.0.settings.index.lifecycle.name: "my-new-policy" } + - match: { data_streams.0.effective_settings.index.number_of_shards: "2" } + - match: { data_streams.0.effective_settings.index.number_of_replicas: "0" } + - match: { data_streams.0.effective_settings.index.lifecycle.name: "my-new-policy" } + + - do: + indices.rollover: + alias: "my-data-stream-1" + + - do: + cluster.health: + index: "my-data-stream-1" + wait_for_status: green + + - do: + indices.get_data_stream_settings: + name: my-data-stream-1 + - match: { data_streams.0.name: my-data-stream-1 } + - match: { data_streams.0.settings: {} } + - match: { data_streams.0.effective_settings.index.number_of_shards: null } + - match: { data_streams.0.effective_settings.index.number_of_replicas: "0" } + - match: { data_streams.0.effective_settings.index.lifecycle.name: "my-policy" } + + - do: + indices.get_data_stream: + name: my-data-stream-1 + - match: { data_streams.0.name: my-data-stream-1 } + - match: { data_streams.0.settings: {} } + - match: { data_streams.0.effective_settings: null } + + - do: + indices.get_data_stream: + name: my-data-stream-1 + - set: { data_streams.0.indices.0.index_name: idx0name } + - set: { data_streams.0.indices.1.index_name: idx1name } + + - do: + indices.get_settings: + index: my-data-stream-1 + - match: { .$idx0name.settings.index.number_of_shards: "1" } + - match: { .$idx0name.settings.index.lifecycle.name: "my-policy" } + - match: { .$idx1name.settings.index.number_of_shards: "1" } + - match: { .$idx1name.settings.index.lifecycle.name: "my-policy" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_data_stream_settings.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_data_stream_settings.json index ca6ed7d8faf30..96e6af8990357 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_data_stream_settings.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_data_stream_settings.json @@ -27,6 +27,11 @@ ] }, "params":{ + "dry_run":{ + "type":"boolean", + "description":"Perform a dry run but do not actually change any settings", + "default":false + }, "timeout":{ "type":"time", "description":"Specify timeout for acknowledging the cluster state update" diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 8aa3f894bae64..fd6a052b527bd 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -263,6 +263,7 @@ static TransportVersion def(int id) { public static final TransportVersion ML_INFERENCE_HUGGING_FACE_CHAT_COMPLETION_ADDED = def(9_078_0_00); public static final TransportVersion NODES_STATS_SUPPORTS_MULTI_PROJECT = def(9_079_0_00); public static final TransportVersion ML_INFERENCE_HUGGING_FACE_RERANK_ADDED = def(9_080_0_00); + public static final TransportVersion SETTINGS_IN_DATA_STREAMS_DRY_RUN = def(9_081_0_00); /* * STOP! READ THIS FIRST! No, really, * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ diff --git a/server/src/main/java/org/elasticsearch/action/datastreams/UpdateDataStreamSettingsAction.java b/server/src/main/java/org/elasticsearch/action/datastreams/UpdateDataStreamSettingsAction.java index 42ab1dd15ff38..be79a0755d4bd 100644 --- a/server/src/main/java/org/elasticsearch/action/datastreams/UpdateDataStreamSettingsAction.java +++ b/server/src/main/java/org/elasticsearch/action/datastreams/UpdateDataStreamSettingsAction.java @@ -9,6 +9,7 @@ package org.elasticsearch.action.datastreams; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionType; import org.elasticsearch.action.IndicesRequest; @@ -47,10 +48,16 @@ public UpdateDataStreamSettingsAction() { public static class Request extends AcknowledgedRequest implements IndicesRequest.Replaceable { private final Settings settings; private String[] dataStreamNames = Strings.EMPTY_ARRAY; + private final boolean dryRun; public Request(Settings settings, TimeValue masterNodeTimeout, TimeValue ackTimeout) { + this(settings, false, masterNodeTimeout, ackTimeout); + } + + public Request(Settings settings, boolean dryRun, TimeValue masterNodeTimeout, TimeValue ackTimeout) { super(masterNodeTimeout, ackTimeout); this.settings = settings; + this.dryRun = dryRun; } @Override @@ -63,6 +70,10 @@ public Settings getSettings() { return settings; } + public boolean isDryRun() { + return dryRun; + } + @Override public boolean includeDataStreams() { return true; @@ -72,6 +83,11 @@ public Request(StreamInput in) throws IOException { super(in); this.dataStreamNames = in.readStringArray(); this.settings = Settings.readSettingsFromStream(in); + if (in.getTransportVersion().onOrAfter(TransportVersions.SETTINGS_IN_DATA_STREAMS)) { + this.dryRun = in.readBoolean(); + } else { + this.dryRun = false; + } } @Override @@ -79,6 +95,9 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeStringArray(dataStreamNames); settings.writeTo(out); + if (out.getTransportVersion().onOrAfter(TransportVersions.SETTINGS_IN_DATA_STREAMS_DRY_RUN)) { + out.writeBoolean(dryRun); + } } @Override @@ -103,13 +122,14 @@ public boolean equals(Object o) { Request request = (Request) o; return Arrays.equals(dataStreamNames, request.dataStreamNames) && settings.equals(request.settings) + && dryRun == request.dryRun && Objects.equals(masterNodeTimeout(), request.masterNodeTimeout()) && Objects.equals(ackTimeout(), request.ackTimeout()); } @Override public int hashCode() { - return Objects.hash(Arrays.hashCode(dataStreamNames), settings, masterNodeTimeout(), ackTimeout()); + return Objects.hash(Arrays.hashCode(dataStreamNames), settings, dryRun, masterNodeTimeout(), ackTimeout()); } } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataDataStreamsService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataDataStreamsService.java index d3784253b536e..75b9f138225c9 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataDataStreamsService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataDataStreamsService.java @@ -9,6 +9,7 @@ package org.elasticsearch.cluster.metadata; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ResourceNotFoundException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.datastreams.ModifyDataStreamsAction; @@ -144,31 +145,17 @@ public Tuple executeTask( UpdateSettingsTask updateSettingsTask, ClusterState clusterState ) throws Exception { - + DataStream dataStream = createDataStreamForUpdatedDataStreamSettings( + updateSettingsTask.projectId, + updateSettingsTask.dataStreamName, + updateSettingsTask.settingsOverrides, + clusterState + ); ProjectMetadata projectMetadata = clusterState.metadata().getProject(updateSettingsTask.projectId); ProjectMetadata.Builder projectMetadataBuilder = ProjectMetadata.builder(projectMetadata); - Map dataStreamMap = projectMetadata.dataStreams(); - DataStream dataStream = dataStreamMap.get(updateSettingsTask.dataStreamName); - Settings existingSettings = dataStream.getSettings(); - - Template.Builder templateBuilder = Template.builder(); - Settings.Builder mergedSettingsBuilder = Settings.builder().put(existingSettings).put(updateSettingsTask.settingsOverrides); - Settings mergedSettings = mergedSettingsBuilder.build(); - - final ComposableIndexTemplate template = lookupTemplateForDataStream(updateSettingsTask.dataStreamName, projectMetadata); - ComposableIndexTemplate mergedTemplate = template.mergeSettings(mergedSettings); - MetadataIndexTemplateService.validateTemplate( - mergedTemplate.template().settings(), - mergedTemplate.template().mappings(), - indicesService - ); - - templateBuilder.settings(mergedSettingsBuilder); - DataStream.Builder dataStreamBuilder = dataStream.copy().setSettings(mergedSettings); projectMetadataBuilder.removeDataStream(updateSettingsTask.dataStreamName); - projectMetadataBuilder.put(dataStreamBuilder.build()); + projectMetadataBuilder.put(dataStream); ClusterState updatedClusterState = ClusterState.builder(clusterState).putProjectMetadata(projectMetadataBuilder).build(); - return new Tuple<>(updatedClusterState, updateSettingsTask); } }; @@ -420,13 +407,67 @@ public void updateSettings( TimeValue ackTimeout, String dataStreamName, Settings settingsOverrides, - ActionListener listener + boolean dryRun, + ActionListener listener ) { - updateSettingsTaskQueue.submitTask( - "updating settings on data stream", - new UpdateSettingsTask(projectId, dataStreamName, settingsOverrides, ackTimeout, listener), - masterNodeTimeout + if (dryRun) { + /* + * If this is a dry run, we'll do the settings validation and apply the changes to the data stream locally, but we won't run + * the task that actually updates the cluster state. + */ + try { + DataStream updatedDataStream = createDataStreamForUpdatedDataStreamSettings( + projectId, + dataStreamName, + settingsOverrides, + clusterService.state() + ); + listener.onResponse(updatedDataStream); + } catch (Exception e) { + listener.onFailure(e); + } + } else { + UpdateSettingsTask updateSettingsTask = new UpdateSettingsTask( + projectId, + dataStreamName, + settingsOverrides, + clusterService, + ackTimeout, + listener + ); + updateSettingsTaskQueue.submitTask("updating settings on data stream", updateSettingsTask, masterNodeTimeout); + } + } + + /* + * This method validates that the settings won't cause any validation problems with existing templates. If successful, a copy of the + * data stream is returned with the new settings applied. + */ + private DataStream createDataStreamForUpdatedDataStreamSettings( + ProjectId projectId, + String dataStreamName, + Settings settingsOverrides, + ClusterState clusterState + ) throws Exception { + ProjectMetadata projectMetadata = clusterState.metadata().getProject(projectId); + Map dataStreamMap = projectMetadata.dataStreams(); + DataStream dataStream = dataStreamMap.get(dataStreamName); + Settings existingSettings = dataStream.getSettings(); + + Template.Builder templateBuilder = Template.builder(); + Settings.Builder mergedSettingsBuilder = Settings.builder().put(existingSettings).put(settingsOverrides); + Settings mergedSettings = mergedSettingsBuilder.build(); + + final ComposableIndexTemplate template = lookupTemplateForDataStream(dataStreamName, projectMetadata); + ComposableIndexTemplate mergedTemplate = template.mergeSettings(mergedSettings); + MetadataIndexTemplateService.validateTemplate( + mergedTemplate.template().settings(), + mergedTemplate.template().mappings(), + indicesService ); + + templateBuilder.settings(mergedSettingsBuilder); + return dataStream.copy().setSettings(mergedSettings).build(); } private static void addBackingIndex( @@ -683,10 +724,17 @@ static class UpdateSettingsTask extends AckedBatchedClusterStateUpdateTask { ProjectId projectId, String dataStreamName, Settings settingsOverrides, + ClusterService clusterService, TimeValue ackTimeout, - ActionListener listener + ActionListener listener ) { - super(ackTimeout, listener); + super(ackTimeout, listener.safeMap(response -> { + if (response.isAcknowledged()) { + return clusterService.state().projectState(projectId).metadata().dataStreams().get(dataStreamName); + } else { + throw new ElasticsearchException("Updating settings not accepted for unknown reasons"); + } + })); this.projectId = projectId; this.dataStreamName = dataStreamName; this.settingsOverrides = settingsOverrides; diff --git a/server/src/test/java/org/elasticsearch/action/datastreams/UpdateDataStreamSettingsActionRequestTests.java b/server/src/test/java/org/elasticsearch/action/datastreams/UpdateDataStreamSettingsActionRequestTests.java index 8ab80aee3fd80..e3f7e4063a4e3 100644 --- a/server/src/test/java/org/elasticsearch/action/datastreams/UpdateDataStreamSettingsActionRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/datastreams/UpdateDataStreamSettingsActionRequestTests.java @@ -33,6 +33,7 @@ protected Writeable.Reader instanceReade protected UpdateDataStreamSettingsAction.Request createTestInstance() { UpdateDataStreamSettingsAction.Request request = new UpdateDataStreamSettingsAction.Request( randomSettings(), + randomBoolean(), randomTimeValue(), randomTimeValue() ); @@ -44,9 +45,10 @@ protected UpdateDataStreamSettingsAction.Request createTestInstance() { protected UpdateDataStreamSettingsAction.Request mutateInstance(UpdateDataStreamSettingsAction.Request instance) throws IOException { String[] indices = instance.indices(); Settings settings = instance.getSettings(); + boolean dryRun = instance.isDryRun(); TimeValue masterNodeTimeout = instance.masterNodeTimeout(); TimeValue ackTimeout = instance.ackTimeout(); - switch (between(0, 3)) { + switch (between(0, 4)) { case 0 -> { indices = randomArrayValueOtherThan(indices, this::randomIndices); } @@ -54,14 +56,17 @@ protected UpdateDataStreamSettingsAction.Request mutateInstance(UpdateDataStream settings = randomValueOtherThan(settings, ComponentTemplateTests::randomSettings); } case 2 -> { - masterNodeTimeout = randomValueOtherThan(masterNodeTimeout, ESTestCase::randomTimeValue); + dryRun = dryRun == false; } case 3 -> { + masterNodeTimeout = randomValueOtherThan(masterNodeTimeout, ESTestCase::randomTimeValue); + } + case 4 -> { ackTimeout = randomValueOtherThan(ackTimeout, ESTestCase::randomTimeValue); } default -> throw new AssertionError("Should not be here"); } - return new UpdateDataStreamSettingsAction.Request(settings, masterNodeTimeout, ackTimeout).indices(indices); + return new UpdateDataStreamSettingsAction.Request(settings, dryRun, masterNodeTimeout, ackTimeout).indices(indices); } private String[] randomIndices() {