|
@@ -28,6 +28,8 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
|
|
import org.elasticsearch.cluster.metadata.Metadata;
|
|
|
import org.elasticsearch.cluster.metadata.ProjectId;
|
|
|
import org.elasticsearch.cluster.metadata.ProjectMetadata;
|
|
|
+import org.elasticsearch.cluster.metadata.ReservedStateHandlerMetadata;
|
|
|
+import org.elasticsearch.cluster.metadata.ReservedStateMetadata;
|
|
|
import org.elasticsearch.cluster.project.ProjectResolver;
|
|
|
import org.elasticsearch.cluster.project.ProjectStateRegistry;
|
|
|
import org.elasticsearch.cluster.routing.GlobalRoutingTable;
|
|
@@ -47,7 +49,9 @@ import org.elasticsearch.transport.TransportService;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.util.Collection;
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
import java.util.Set;
|
|
|
import java.util.function.BiPredicate;
|
|
|
import java.util.function.Predicate;
|
|
@@ -189,12 +193,12 @@ public class TransportClusterStateAction extends TransportLocalClusterStateActio
|
|
|
}
|
|
|
|
|
|
private ClusterStateResponse buildResponse(final ClusterStateRequest request, final ClusterState rawState) {
|
|
|
- final ClusterState currentState = filterClusterState(rawState);
|
|
|
+ final ClusterState filteredState = filterClusterState(rawState);
|
|
|
|
|
|
ThreadPool.assertCurrentThreadPool(ThreadPool.Names.MANAGEMENT); // too heavy to construct & serialize cluster state without forking
|
|
|
|
|
|
if (request.blocks() == false) {
|
|
|
- final var blockException = currentState.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ);
|
|
|
+ final var blockException = filteredState.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ);
|
|
|
if (blockException != null) {
|
|
|
// There's a METADATA_READ block in place, but we aren't returning it to the caller, and yet the caller needs to know that
|
|
|
// this block exists (e.g. it's the STATE_NOT_RECOVERED_BLOCK, so the rest of the state is known to be incomplete). Thus we
|
|
@@ -203,22 +207,22 @@ public class TransportClusterStateAction extends TransportLocalClusterStateActio
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- logger.trace("Serving cluster state request using version {}", currentState.version());
|
|
|
- ClusterState.Builder builder = ClusterState.builder(currentState.getClusterName());
|
|
|
- builder.version(currentState.version());
|
|
|
- builder.stateUUID(currentState.stateUUID());
|
|
|
+ logger.trace("Serving cluster state request using version {}", filteredState.version());
|
|
|
+ ClusterState.Builder builder = ClusterState.builder(filteredState.getClusterName());
|
|
|
+ builder.version(filteredState.version());
|
|
|
+ builder.stateUUID(filteredState.stateUUID());
|
|
|
|
|
|
if (request.nodes()) {
|
|
|
- builder.nodes(currentState.nodes());
|
|
|
- builder.nodeIdsToCompatibilityVersions(getCompatibilityVersions(currentState));
|
|
|
- builder.nodeFeatures(getClusterFeatures(currentState));
|
|
|
+ builder.nodes(filteredState.nodes());
|
|
|
+ builder.nodeIdsToCompatibilityVersions(getCompatibilityVersions(filteredState));
|
|
|
+ builder.nodeFeatures(getClusterFeatures(filteredState));
|
|
|
}
|
|
|
if (request.routingTable()) {
|
|
|
if (request.indices().length > 0) {
|
|
|
- final GlobalRoutingTable.Builder globalRoutingTableBuilder = GlobalRoutingTable.builder(currentState.globalRoutingTable())
|
|
|
+ final GlobalRoutingTable.Builder globalRoutingTableBuilder = GlobalRoutingTable.builder(filteredState.globalRoutingTable())
|
|
|
.clear();
|
|
|
- for (ProjectMetadata project : currentState.metadata().projects().values()) {
|
|
|
- RoutingTable projectRouting = currentState.routingTable(project.id());
|
|
|
+ for (ProjectMetadata project : filteredState.metadata().projects().values()) {
|
|
|
+ RoutingTable projectRouting = filteredState.routingTable(project.id());
|
|
|
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
|
|
String[] indices = indexNameExpressionResolver.concreteIndexNames(project, request);
|
|
|
for (String filteredIndex : indices) {
|
|
@@ -230,18 +234,18 @@ public class TransportClusterStateAction extends TransportLocalClusterStateActio
|
|
|
}
|
|
|
builder.routingTable(globalRoutingTableBuilder.build());
|
|
|
} else {
|
|
|
- builder.routingTable(currentState.globalRoutingTable());
|
|
|
+ builder.routingTable(filteredState.globalRoutingTable());
|
|
|
}
|
|
|
} else {
|
|
|
builder.routingTable(GlobalRoutingTable.builder().build());
|
|
|
}
|
|
|
if (request.blocks()) {
|
|
|
- builder.blocks(currentState.blocks());
|
|
|
+ builder.blocks(filteredState.blocks());
|
|
|
}
|
|
|
|
|
|
Metadata.Builder mdBuilder = Metadata.builder();
|
|
|
- mdBuilder.clusterUUID(currentState.metadata().clusterUUID());
|
|
|
- mdBuilder.coordinationMetadata(currentState.coordinationMetadata());
|
|
|
+ mdBuilder.clusterUUID(filteredState.metadata().clusterUUID());
|
|
|
+ mdBuilder.coordinationMetadata(filteredState.coordinationMetadata());
|
|
|
|
|
|
if (request.metadata()) {
|
|
|
// filter out metadata that shouldn't be returned by the API
|
|
@@ -250,14 +254,30 @@ public class TransportClusterStateAction extends TransportLocalClusterStateActio
|
|
|
if (request.indices().length > 0) {
|
|
|
// if the request specified index names, then we don't want the whole metadata, just the version and projects (which will
|
|
|
// be filtered (below) to only include the relevant indices)
|
|
|
- mdBuilder.version(currentState.metadata().version());
|
|
|
+ mdBuilder.version(filteredState.metadata().version());
|
|
|
} else {
|
|
|
// If there are no requested indices, then we want all the metadata, except for customs that aren't exposed via the API
|
|
|
- mdBuilder = Metadata.builder(currentState.metadata());
|
|
|
+ mdBuilder = Metadata.builder(filteredState.metadata());
|
|
|
mdBuilder.removeCustomIf(notApi);
|
|
|
+
|
|
|
+ if (projectResolver.supportsMultipleProjects() && request.multiproject() == false) {
|
|
|
+ ProjectStateRegistry projectStateRegistry = ProjectStateRegistry.get(filteredState);
|
|
|
+ if (projectStateRegistry.size() > 1) {
|
|
|
+ throw new Metadata.MultiProjectPendingException(
|
|
|
+ "There are multiple projects " + projectStateRegistry.knownProjects()
|
|
|
+ );
|
|
|
+ }
|
|
|
+ var reservedStateMetadata = new HashMap<>(filteredState.metadata().reservedStateMetadata());
|
|
|
+ var singleProjectReservedStateMetadata = projectStateRegistry.reservedStateMetadata(projectResolver.getProjectId());
|
|
|
+ singleProjectReservedStateMetadata.forEach(
|
|
|
+ (key, value) -> reservedStateMetadata.merge(key, value, this::mergeReservedStateMetadata)
|
|
|
+ );
|
|
|
+
|
|
|
+ mdBuilder.put(reservedStateMetadata);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- for (ProjectMetadata project : currentState.metadata().projects().values()) {
|
|
|
+ for (ProjectMetadata project : filteredState.metadata().projects().values()) {
|
|
|
ProjectMetadata.Builder pBuilder;
|
|
|
if (request.indices().length > 0) {
|
|
|
// if the request specified index names, then only include the project-id and indices
|
|
@@ -289,7 +309,7 @@ public class TransportClusterStateAction extends TransportLocalClusterStateActio
|
|
|
mdBuilder.put(pBuilder);
|
|
|
}
|
|
|
} else {
|
|
|
- for (ProjectId project : currentState.metadata().projects().keySet()) {
|
|
|
+ for (ProjectId project : filteredState.metadata().projects().keySet()) {
|
|
|
// Request doesn't want to retrieve metadata, so we just fill in empty projects
|
|
|
// (because we can't have a truly empty Metadata)
|
|
|
mdBuilder.put(ProjectMetadata.builder(project));
|
|
@@ -298,14 +318,45 @@ public class TransportClusterStateAction extends TransportLocalClusterStateActio
|
|
|
builder.metadata(mdBuilder);
|
|
|
|
|
|
if (request.customs()) {
|
|
|
- for (Map.Entry<String, ClusterState.Custom> custom : currentState.customs().entrySet()) {
|
|
|
+ for (Map.Entry<String, ClusterState.Custom> custom : filteredState.customs().entrySet()) {
|
|
|
if (custom.getValue().isPrivate() == false) {
|
|
|
builder.putCustom(custom.getKey(), custom.getValue());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return new ClusterStateResponse(currentState.getClusterName(), builder.build(), false);
|
|
|
+ return new ClusterStateResponse(filteredState.getClusterName(), builder.build(), false);
|
|
|
}
|
|
|
|
|
|
+ private ReservedStateMetadata mergeReservedStateMetadata(
|
|
|
+ ReservedStateMetadata clusterReservedMetadata,
|
|
|
+ ReservedStateMetadata projectReservedMetadata
|
|
|
+ ) {
|
|
|
+ if (Objects.equals(clusterReservedMetadata.version(), projectReservedMetadata.version()) == false) {
|
|
|
+ logger.info(
|
|
|
+ "Reserved state metadata version is different for Metadata ({}) and the requested project ({})",
|
|
|
+ clusterReservedMetadata.version(),
|
|
|
+ projectReservedMetadata.version()
|
|
|
+ );
|
|
|
+ }
|
|
|
+ ReservedStateMetadata.Builder builder = ReservedStateMetadata.builder(clusterReservedMetadata.namespace())
|
|
|
+ .version(Math.max(clusterReservedMetadata.version(), projectReservedMetadata.version()));
|
|
|
+
|
|
|
+ for (ReservedStateHandlerMetadata handler : clusterReservedMetadata.handlers().values()) {
|
|
|
+ builder.putHandler(handler);
|
|
|
+ }
|
|
|
+ for (Map.Entry<String, ReservedStateHandlerMetadata> handlerEntry : projectReservedMetadata.handlers().entrySet()) {
|
|
|
+ assert clusterReservedMetadata.handlers().containsKey(handlerEntry.getKey()) == false
|
|
|
+ : "Duplicate of handler: " + handlerEntry.getKey();
|
|
|
+ builder.putHandler(handlerEntry.getValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (projectReservedMetadata.errorMetadata() != null) {
|
|
|
+ builder.errorMetadata(projectReservedMetadata.errorMetadata());
|
|
|
+ } else if (clusterReservedMetadata.errorMetadata() != null) {
|
|
|
+ builder.errorMetadata(clusterReservedMetadata.errorMetadata());
|
|
|
+ }
|
|
|
+
|
|
|
+ return builder.build();
|
|
|
+ }
|
|
|
}
|