Skip to content

Commit 8fce15d

Browse files
authored
Allow project reserved state handlers to update any cluster state (#128636)
1 parent 4b7a9bd commit 8fce15d

File tree

51 files changed

+573
-458
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+573
-458
lines changed

server/src/main/java/module-info.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import org.elasticsearch.plugins.internal.RestExtension;
11+
import org.elasticsearch.reservedstate.ReservedStateHandlerProvider;
1112

1213
/** The Elasticsearch Server Module. */
1314
module org.elasticsearch.server {
@@ -411,7 +412,7 @@
411412
org.elasticsearch.index.shard.ShardToolCliProvider;
412413

413414
uses org.elasticsearch.reservedstate.service.FileSettingsServiceProvider;
414-
uses org.elasticsearch.reservedstate.ReservedClusterStateHandlerProvider;
415+
uses ReservedStateHandlerProvider;
415416
uses org.elasticsearch.jdk.ModuleQualifiedExportsService;
416417
uses org.elasticsearch.node.internal.TerminationHandlerProvider;
417418
uses org.elasticsearch.internal.VersionExtension;

server/src/main/java/org/elasticsearch/action/ActionModule.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,7 @@
211211
import org.elasticsearch.action.termvectors.TransportTermVectorsAction;
212212
import org.elasticsearch.action.update.TransportUpdateAction;
213213
import org.elasticsearch.client.internal.node.NodeClient;
214-
import org.elasticsearch.cluster.ClusterState;
215214
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
216-
import org.elasticsearch.cluster.metadata.ProjectMetadata;
217215
import org.elasticsearch.cluster.node.DiscoveryNodes;
218216
import org.elasticsearch.cluster.project.ProjectIdResolver;
219217
import org.elasticsearch.cluster.routing.RerouteService;
@@ -254,6 +252,7 @@
254252
import org.elasticsearch.repositories.VerifyNodeRepositoryAction;
255253
import org.elasticsearch.repositories.VerifyNodeRepositoryCoordinationAction;
256254
import org.elasticsearch.reservedstate.ReservedClusterStateHandler;
255+
import org.elasticsearch.reservedstate.ReservedProjectStateHandler;
257256
import org.elasticsearch.reservedstate.service.ReservedClusterStateService;
258257
import org.elasticsearch.rest.RestController;
259258
import org.elasticsearch.rest.RestHandler;
@@ -474,8 +473,8 @@ public ActionModule(
474473
TelemetryProvider telemetryProvider,
475474
ClusterService clusterService,
476475
RerouteService rerouteService,
477-
List<ReservedClusterStateHandler<ClusterState, ?>> reservedClusterStateHandlers,
478-
List<ReservedClusterStateHandler<ProjectMetadata, ?>> reservedProjectStateHandlers,
476+
List<ReservedClusterStateHandler<?>> reservedClusterStateHandlers,
477+
List<ReservedProjectStateHandler<?>> reservedProjectStateHandlers,
479478
RestExtension restExtension,
480479
IncrementalBulkService bulkService,
481480
ProjectIdResolver projectIdResolver

server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/reservedstate/ReservedRepositoryAction.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
* It is used by the ReservedClusterStateService to add/update or remove snapshot repositories. Typical usage
3535
* for this action is in the context of file based settings.
3636
*/
37-
public class ReservedRepositoryAction implements ReservedClusterStateHandler<ClusterState, List<PutRepositoryRequest>> {
37+
public class ReservedRepositoryAction implements ReservedClusterStateHandler<List<PutRepositoryRequest>> {
3838
public static final String NAME = "snapshot_repositories";
3939

4040
private final RepositoriesService repositoriesService;
@@ -67,8 +67,7 @@ public Collection<PutRepositoryRequest> prepare(Object input) {
6767
}
6868

6969
@Override
70-
public TransformState<ClusterState> transform(List<PutRepositoryRequest> source, TransformState<ClusterState> prevState)
71-
throws Exception {
70+
public TransformState transform(List<PutRepositoryRequest> source, TransformState prevState) throws Exception {
7271
var requests = prepare(source);
7372

7473
ClusterState state = prevState.state();
@@ -88,7 +87,7 @@ public TransformState<ClusterState> transform(List<PutRepositoryRequest> source,
8887
state = task.execute(state);
8988
}
9089

91-
return new TransformState<>(state, entities);
90+
return new TransformState(state, entities);
9291

9392
}
9493

server/src/main/java/org/elasticsearch/action/admin/indices/template/reservedstate/ReservedComposableIndexTemplateAction.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212
import org.elasticsearch.action.admin.indices.template.put.PutComponentTemplateAction;
1313
import org.elasticsearch.action.admin.indices.template.put.TransportPutComponentTemplateAction;
1414
import org.elasticsearch.action.admin.indices.template.put.TransportPutComposableIndexTemplateAction;
15+
import org.elasticsearch.cluster.ClusterState;
1516
import org.elasticsearch.cluster.metadata.ComponentTemplate;
1617
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
1718
import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService;
19+
import org.elasticsearch.cluster.metadata.ProjectId;
1820
import org.elasticsearch.cluster.metadata.ProjectMetadata;
1921
import org.elasticsearch.common.settings.IndexScopedSettings;
2022
import org.elasticsearch.common.util.set.Sets;
2123
import org.elasticsearch.reservedstate.ReservedClusterStateHandler;
24+
import org.elasticsearch.reservedstate.ReservedProjectStateHandler;
2225
import org.elasticsearch.reservedstate.TransformState;
2326
import org.elasticsearch.xcontent.XContentParser;
2427
import org.elasticsearch.xcontent.XContentParserConfiguration;
@@ -45,7 +48,7 @@
4548
*/
4649
public class ReservedComposableIndexTemplateAction
4750
implements
48-
ReservedClusterStateHandler<ProjectMetadata, ReservedComposableIndexTemplateAction.ComponentsAndComposables> {
51+
ReservedProjectStateHandler<ReservedComposableIndexTemplateAction.ComponentsAndComposables> {
4952
public static final String NAME = "index_templates";
5053
public static final String COMPONENTS = "component_templates";
5154
private static final String COMPONENT_PREFIX = "component_template:";
@@ -133,10 +136,10 @@ private ComponentsAndComposables prepare(ComponentsAndComposables componentsAndC
133136
}
134137

135138
@Override
136-
public TransformState<ProjectMetadata> transform(ComponentsAndComposables source, TransformState<ProjectMetadata> prevState)
137-
throws Exception {
139+
public TransformState transform(ProjectId projectId, ComponentsAndComposables source, TransformState prevState) throws Exception {
138140
var requests = prepare(source);
139-
ProjectMetadata project = prevState.state();
141+
ClusterState clusterState = prevState.state();
142+
ProjectMetadata project = clusterState.getMetadata().getProject(projectId);
140143

141144
// We transform in the following order:
142145
// 1. create or update component templates (composable templates depend on them)
@@ -192,7 +195,10 @@ public TransformState<ProjectMetadata> transform(ComponentsAndComposables source
192195
project = MetadataIndexTemplateService.innerRemoveComponentTemplate(project, componentNames);
193196
}
194197

195-
return new TransformState<>(project, Sets.union(componentEntities, composableEntities));
198+
return new TransformState(
199+
ClusterState.builder(clusterState).putProjectMetadata(project).build(),
200+
Sets.union(componentEntities, composableEntities)
201+
);
196202
}
197203

198204
@Override

server/src/main/java/org/elasticsearch/action/ingest/ReservedPipelineAction.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
package org.elasticsearch.action.ingest;
1111

1212
import org.elasticsearch.ElasticsearchGenerationException;
13+
import org.elasticsearch.cluster.ClusterState;
14+
import org.elasticsearch.cluster.metadata.ProjectId;
1315
import org.elasticsearch.cluster.metadata.ProjectMetadata;
1416
import org.elasticsearch.common.bytes.BytesReference;
1517
import org.elasticsearch.ingest.IngestMetadata;
1618
import org.elasticsearch.ingest.IngestService;
17-
import org.elasticsearch.reservedstate.ReservedClusterStateHandler;
19+
import org.elasticsearch.reservedstate.ReservedProjectStateHandler;
1820
import org.elasticsearch.reservedstate.TransformState;
1921
import org.elasticsearch.xcontent.XContentBuilder;
2022
import org.elasticsearch.xcontent.XContentFactory;
@@ -36,7 +38,7 @@
3638
* It is used by the ReservedClusterStateService to add/update or remove ingest pipelines. Typical usage
3739
* for this action is in the context of file based state.
3840
*/
39-
public class ReservedPipelineAction implements ReservedClusterStateHandler<ProjectMetadata, List<PutPipelineRequest>> {
41+
public class ReservedPipelineAction implements ReservedProjectStateHandler<List<PutPipelineRequest>> {
4042
public static final String NAME = "ingest_pipelines";
4143

4244
/**
@@ -78,21 +80,21 @@ private static ProjectMetadata wrapIngestTaskExecute(IngestService.PipelineClust
7880
}
7981

8082
@Override
81-
public TransformState<ProjectMetadata> transform(List<PutPipelineRequest> source, TransformState<ProjectMetadata> prevState)
82-
throws Exception {
83+
public TransformState transform(ProjectId projectId, List<PutPipelineRequest> source, TransformState prevState) throws Exception {
8384
var requests = prepare(source);
8485

85-
ProjectMetadata state = prevState.state();
86+
ClusterState clusterState = prevState.state();
87+
ProjectMetadata projectMetadata = clusterState.metadata().getProject(projectId);
8688

8789
for (var request : requests) {
88-
var nopUpdate = IngestService.isNoOpPipelineUpdate(state, request);
90+
var nopUpdate = IngestService.isNoOpPipelineUpdate(projectMetadata, request);
8991

9092
if (nopUpdate) {
9193
continue;
9294
}
9395

94-
var task = new IngestService.PutPipelineClusterStateUpdateTask(state.id(), request);
95-
state = wrapIngestTaskExecute(task, state);
96+
var task = new IngestService.PutPipelineClusterStateUpdateTask(projectMetadata.id(), request);
97+
projectMetadata = wrapIngestTaskExecute(task, projectMetadata);
9698
}
9799

98100
Set<String> entities = requests.stream().map(PutPipelineRequest::getId).collect(Collectors.toSet());
@@ -102,18 +104,18 @@ public TransformState<ProjectMetadata> transform(List<PutPipelineRequest> source
102104

103105
for (var pipelineToDelete : toDelete) {
104106
var task = new IngestService.DeletePipelineClusterStateUpdateTask(
105-
state.id(),
107+
projectMetadata.id(),
106108
null,
107109
new DeletePipelineRequest(
108110
RESERVED_CLUSTER_STATE_HANDLER_IGNORED_TIMEOUT,
109111
RESERVED_CLUSTER_STATE_HANDLER_IGNORED_TIMEOUT,
110112
pipelineToDelete
111113
)
112114
);
113-
state = wrapIngestTaskExecute(task, state);
115+
projectMetadata = wrapIngestTaskExecute(task, projectMetadata);
114116
}
115117

116-
return new TransformState<>(state, entities);
118+
return new TransformState(ClusterState.builder(clusterState).putProjectMetadata(projectMetadata).build(), entities);
117119
}
118120

119121
@Override

server/src/main/java/org/elasticsearch/node/NodeConstruction.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
import org.elasticsearch.cluster.metadata.MetadataDataStreamsService;
5151
import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService;
5252
import org.elasticsearch.cluster.metadata.MetadataUpdateSettingsService;
53-
import org.elasticsearch.cluster.metadata.ProjectMetadata;
5453
import org.elasticsearch.cluster.metadata.SystemIndexMetadataUpgradeService;
5554
import org.elasticsearch.cluster.metadata.TemplateUpgradeService;
5655
import org.elasticsearch.cluster.node.DiscoveryNode;
@@ -189,7 +188,8 @@
189188
import org.elasticsearch.repositories.RepositoriesModule;
190189
import org.elasticsearch.repositories.RepositoriesService;
191190
import org.elasticsearch.reservedstate.ReservedClusterStateHandler;
192-
import org.elasticsearch.reservedstate.ReservedClusterStateHandlerProvider;
191+
import org.elasticsearch.reservedstate.ReservedProjectStateHandler;
192+
import org.elasticsearch.reservedstate.ReservedStateHandlerProvider;
193193
import org.elasticsearch.reservedstate.action.ReservedClusterSettingsAction;
194194
import org.elasticsearch.reservedstate.service.FileSettingsService;
195195
import org.elasticsearch.reservedstate.service.FileSettingsService.FileSettingsHealthIndicatorService;
@@ -995,7 +995,7 @@ public Map<String, String> queryFields() {
995995
final ResponseCollectorService responseCollectorService = new ResponseCollectorService(clusterService);
996996
modules.bindToInstance(ResponseCollectorService.class, responseCollectorService);
997997

998-
var reservedStateHandlerProviders = pluginsService.loadServiceProviders(ReservedClusterStateHandlerProvider.class);
998+
var reservedStateHandlerProviders = pluginsService.loadServiceProviders(ReservedStateHandlerProvider.class);
999999

10001000
ActionModule actionModule = new ActionModule(
10011001
settings,
@@ -1599,11 +1599,11 @@ private Module loadPersistedClusterStateService(
15991599
return b -> b.bind(PersistedClusterStateService.class).toInstance(service);
16001600
}
16011601

1602-
private List<ReservedClusterStateHandler<ClusterState, ?>> buildReservedClusterStateHandlers(
1603-
List<? extends ReservedClusterStateHandlerProvider> handlers,
1602+
private List<ReservedClusterStateHandler<?>> buildReservedClusterStateHandlers(
1603+
List<? extends ReservedStateHandlerProvider> handlers,
16041604
SettingsModule settingsModule
16051605
) {
1606-
List<ReservedClusterStateHandler<ClusterState, ?>> reservedStateHandlers = new ArrayList<>();
1606+
List<ReservedClusterStateHandler<?>> reservedStateHandlers = new ArrayList<>();
16071607

16081608
// add all reserved state handlers from server
16091609
reservedStateHandlers.add(new ReservedClusterSettingsAction(settingsModule.getClusterSettings()));
@@ -1614,8 +1614,8 @@ private Module loadPersistedClusterStateService(
16141614
return reservedStateHandlers;
16151615
}
16161616

1617-
private List<ReservedClusterStateHandler<ProjectMetadata, ?>> buildReservedProjectStateHandlers(
1618-
List<? extends ReservedClusterStateHandlerProvider> handlers,
1617+
private List<ReservedProjectStateHandler<?>> buildReservedProjectStateHandlers(
1618+
List<? extends ReservedStateHandlerProvider> handlers,
16191619
SettingsModule settingsModule,
16201620
ClusterService clusterService,
16211621
IndicesService indicesService,
@@ -1624,7 +1624,7 @@ private Module loadPersistedClusterStateService(
16241624
MetadataCreateIndexService metadataCreateIndexService,
16251625
DataStreamGlobalRetentionSettings globalRetentionSettings
16261626
) {
1627-
List<ReservedClusterStateHandler<ProjectMetadata, ?>> reservedStateHandlers = new ArrayList<>();
1627+
List<ReservedProjectStateHandler<?>> reservedStateHandlers = new ArrayList<>();
16281628

16291629
var templateService = new MetadataIndexTemplateService(
16301630
clusterService,

server/src/main/java/org/elasticsearch/reservedstate/ReservedClusterStateHandler.java

Lines changed: 4 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,12 @@
99

1010
package org.elasticsearch.reservedstate;
1111

12-
import org.elasticsearch.action.ActionRequestValidationException;
13-
import org.elasticsearch.action.support.master.MasterNodeRequest;
14-
import org.elasticsearch.core.TimeValue;
15-
import org.elasticsearch.xcontent.XContentParser;
16-
17-
import java.io.IOException;
18-
import java.util.Collection;
19-
import java.util.Collections;
20-
2112
/**
22-
* Base interface used for implementing 'operator mode' cluster state updates.
23-
*
24-
* <p>
25-
* Reserving cluster state, for file based settings and modules/plugins, requires
26-
* that we have a separate update handler interface that is different than the REST handlers. This interface class declares
27-
* the basic contract for implementing cluster state update handlers that result in a cluster state that is effectively immutable
28-
* by the REST handlers. The only way the reserved cluster state can be updated is through the 'operator mode' actions, e.g. updating
29-
* the file settings.
30-
* </p>
13+
* {@link ReservedStateHandler} for updating cluster-wide cluster state.
3114
*
32-
* @param <S> The state type to be updated by this handler
3315
* @param <T> The type used to represent the state update
3416
*/
35-
public interface ReservedClusterStateHandler<S, T> {
36-
/**
37-
* Unique identifier for the handler.
38-
*
39-
* <p>
40-
* The handler name is a unique identifier that is matched to a section in a
41-
* cluster state update content. The reserved cluster state updates are done as a single
42-
* cluster state update and the cluster state is typically supplied as a combined content,
43-
* unlike the REST handlers. This name must match a desired content key name in the combined
44-
* cluster state update, e.g. "ilm" or "cluster_settings" (for persistent cluster settings update).
45-
*
46-
* @return a String with the handler name, e.g "ilm".
47-
*/
48-
String name();
17+
public interface ReservedClusterStateHandler<T> extends ReservedStateHandler<T> {
4918

5019
/**
5120
* The transformation method implemented by the handler.
@@ -58,77 +27,11 @@ public interface ReservedClusterStateHandler<S, T> {
5827
* {@link TransformState}, which contains the current cluster state as well as any previous keys
5928
* set by this handler on prior invocation.
6029
*
61-
* @param source The parsed information specific to this handler from the combined cluster state content
30+
* @param source The parsed information specific to this handler from the combined cluster state content
6231
* @param prevState The previous cluster state and keys set by this handler (if any)
6332
* @return The modified state and the current keys set by this handler
6433
* @throws Exception
6534
*/
66-
TransformState<S> transform(T source, TransformState<S> prevState) throws Exception;
67-
68-
/**
69-
* List of dependent handler names for this handler.
70-
*
71-
* <p>
72-
* Sometimes certain parts of the cluster state cannot be created/updated without previously
73-
* setting other cluster state components, e.g. composable templates. Since the reserved cluster state handlers
74-
* are processed in random order by the ReservedClusterStateService, this method gives an opportunity
75-
* to any reserved handler to declare other state handlers it depends on. Given dependencies exist,
76-
* the ReservedClusterStateService will order those handlers such that the handlers that are dependent
77-
* on are processed first.
78-
*
79-
* @return a collection of reserved state handler names
80-
*/
81-
default Collection<String> dependencies() {
82-
return Collections.emptyList();
83-
}
84-
85-
/**
86-
* List of optional dependent handler names for this handler.
87-
*
88-
* <p>
89-
* These are dependent handlers which may or may not exist for this handler to be
90-
* processed. If the optional dependency exists, then they are simply ordered to be
91-
* merged into the cluster state before this handler.
92-
*
93-
* @return a collection of optional reserved state handler names
94-
*/
95-
default Collection<String> optionalDependencies() {
96-
return Collections.emptyList();
97-
}
98-
99-
/**
100-
* Generic validation helper method that throws consistent exception for all handlers.
101-
*
102-
* <p>
103-
* All implementations of {@link ReservedClusterStateHandler} should call the request validate method, by calling this default
104-
* implementation. To aid in any special validation logic that may need to be implemented by the reserved cluster state handler
105-
* we provide this convenience method.
106-
*
107-
* @param request the master node request that we base this reserved state handler on
108-
*/
109-
default void validate(MasterNodeRequest<?> request) {
110-
ActionRequestValidationException exception = request.validate();
111-
if (exception != null) {
112-
throw new IllegalStateException("Validation error", exception);
113-
}
114-
}
35+
TransformState transform(T source, TransformState prevState) throws Exception;
11536

116-
/**
117-
* The parse content method which is called during parsing of file based content.
118-
*
119-
* <p>
120-
* The immutable state can be provided as XContent, which means that each handler needs
121-
* to implement a method to convert an XContent to an object it can consume later in
122-
* transform
123-
*
124-
* @param parser the XContent parser we are parsing from
125-
* @return
126-
* @throws IOException
127-
*/
128-
T fromXContent(XContentParser parser) throws IOException;
129-
130-
/**
131-
* Reserved-state handlers create master-node requests but never actually send them to the master node so the timeouts are not relevant.
132-
*/
133-
TimeValue RESERVED_CLUSTER_STATE_HANDLER_IGNORED_TIMEOUT = TimeValue.THIRTY_SECONDS;
13437
}

0 commit comments

Comments
 (0)