From 930f7776e8ee6d46e5983a34ab59b5888e1ac964 Mon Sep 17 00:00:00 2001 From: Nikola Grcevski Date: Thu, 16 Jun 2022 15:01:23 -0400 Subject: [PATCH 1/7] Add operator state metadata --- .../cluster/metadata/Metadata.java | 98 +++++- .../metadata/OperatorErrorMetadata.java | 232 +++++++++++++ .../metadata/OperatorHandlerMetadata.java | 144 ++++++++ .../cluster/metadata/OperatorMetadata.java | 311 ++++++++++++++++++ .../reroute/ClusterRerouteResponseTests.java | 6 +- .../cluster/ClusterStateTests.java | 12 +- .../metadata/OperatorMetadataTests.java | 85 +++++ .../metadata/ToAndFromJsonMetadataTests.java | 211 +++++++++++- .../org/elasticsearch/test/ESTestCase.java | 4 + 9 files changed, 1088 insertions(+), 15 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java create mode 100644 server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java create mode 100644 server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java create mode 100644 server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java index 940ea7e777afe..0c64a75deaab6 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java @@ -207,6 +207,7 @@ default boolean isRestorable() { private final ImmutableOpenMap> aliasedIndices; private final ImmutableOpenMap templates; private final ImmutableOpenMap customs; + private final Map operatorMetadata; private final transient int totalNumberOfShards; // Transient ? not serializable anyway? private final int totalOpenIndexShards; @@ -246,7 +247,8 @@ private Metadata( String[] visibleClosedIndices, SortedMap indicesLookup, Map mappingsByHash, - Version oldestIndexVersion + Version oldestIndexVersion, + Map operatorMetadata ) { this.clusterUUID = clusterUUID; this.clusterUUIDCommitted = clusterUUIDCommitted; @@ -271,6 +273,7 @@ private Metadata( this.indicesLookup = indicesLookup; this.mappingsByHash = mappingsByHash; this.oldestIndexVersion = oldestIndexVersion; + this.operatorMetadata = operatorMetadata; } public Metadata withIncrementedVersion() { @@ -297,7 +300,8 @@ public Metadata withIncrementedVersion() { visibleClosedIndices, indicesLookup, mappingsByHash, - oldestIndexVersion + oldestIndexVersion, + operatorMetadata ); } @@ -357,7 +361,8 @@ public Metadata withLifecycleState(final Index index, final LifecycleExecutionSt visibleClosedIndices, indicesLookup, mappingsByHash, - oldestIndexVersion + oldestIndexVersion, + operatorMetadata ); } @@ -938,6 +943,24 @@ public Map customs() { return this.customs; } + /** + * Returns the full {@link OperatorMetadata} Map for all + * operator namespaces. + * @return a map of namespace to {@link OperatorMetadata} + */ + public Map operatorMetadata() { + return this.operatorMetadata; + } + + /** + * Returns the {@link OperatorMetadata} for a given namespace. + * @param namespace the namespace to lookup operator metadata for + * @return {@link OperatorMetadata} or null if not found + */ + public OperatorMetadata operatorMetadata(String namespace) { + return this.operatorMetadata.get(namespace); + } + /** * The collection of index deletions in the cluster. */ @@ -1079,6 +1102,7 @@ private static class MetadataDiff implements Diff { private final Diff> indices; private final Diff> templates; private final Diff> customs; + private final Diff> operatorMetadata; MetadataDiff(Metadata before, Metadata after) { clusterUUID = after.clusterUUID; @@ -1091,12 +1115,15 @@ private static class MetadataDiff implements Diff { indices = DiffableUtils.diff(before.indices, after.indices, DiffableUtils.getStringKeySerializer()); templates = DiffableUtils.diff(before.templates, after.templates, DiffableUtils.getStringKeySerializer()); customs = DiffableUtils.diff(before.customs, after.customs, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); + operatorMetadata = DiffableUtils.diff(before.operatorMetadata, after.operatorMetadata, DiffableUtils.getStringKeySerializer()); } private static final DiffableUtils.DiffableValueReader INDEX_METADATA_DIFF_VALUE_READER = new DiffableUtils.DiffableValueReader<>(IndexMetadata::readFrom, IndexMetadata::readDiffFrom); private static final DiffableUtils.DiffableValueReader TEMPLATES_DIFF_VALUE_READER = new DiffableUtils.DiffableValueReader<>(IndexTemplateMetadata::readFrom, IndexTemplateMetadata::readDiffFrom); + private static final DiffableUtils.DiffableValueReader OPERATOR_DIFF_VALUE_READER = + new DiffableUtils.DiffableValueReader<>(OperatorMetadata::readFrom, OperatorMetadata::readDiffFrom); MetadataDiff(StreamInput in) throws IOException { clusterUUID = in.readString(); @@ -1113,6 +1140,11 @@ private static class MetadataDiff implements Diff { indices = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), INDEX_METADATA_DIFF_VALUE_READER); templates = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), TEMPLATES_DIFF_VALUE_READER); customs = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); + if (in.getVersion().onOrAfter(Version.V_8_4_0)) { + operatorMetadata = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), OPERATOR_DIFF_VALUE_READER); + } else { + operatorMetadata = OperatorMetadata.EMPTY_DIFF; + } } @Override @@ -1129,6 +1161,9 @@ public void writeTo(StreamOutput out) throws IOException { indices.writeTo(out); templates.writeTo(out); customs.writeTo(out); + if (out.getVersion().onOrAfter(Version.V_8_4_0)) { + operatorMetadata.writeTo(out); + } } @Override @@ -1146,6 +1181,7 @@ public Metadata apply(Metadata part) { builder.indices(indices.apply(part.indices)); builder.templates(templates.apply(part.templates)); builder.customs(customs.apply(part.customs)); + builder.put(operatorMetadata.apply(part.operatorMetadata)); return builder.build(); } } @@ -1187,7 +1223,12 @@ public static Metadata readFrom(StreamInput in) throws IOException { Custom customIndexMetadata = in.readNamedWriteable(Custom.class); builder.putCustom(customIndexMetadata.getWriteableName(), customIndexMetadata); } - + if (in.getVersion().onOrAfter(Version.V_8_4_0)) { + int operatorSize = in.readVInt(); + for (int i = 0; i < operatorSize; i++) { + builder.put(OperatorMetadata.readFrom(in)); + } + } return builder.build(); } @@ -1214,6 +1255,9 @@ public void writeTo(StreamOutput out) throws IOException { } out.writeCollection(templates.values()); VersionedNamedWriteable.writeVersionedWritables(out, customs); + if (out.getVersion().onOrAfter(Version.V_8_4_0)) { + out.writeCollection(operatorMetadata.values()); + } } public static Builder builder() { @@ -1248,6 +1292,8 @@ public static class Builder { private SortedMap previousIndicesLookup; + private final Map operatorMetadata; + // If this is set to false we can skip checking #mappingsByHash for unused entries in #build(). Used as an optimization to save // the rather expensive call to #purgeUnusedEntries when building from another instance and we know that no mappings can have // become unused because no indices were updated or removed from this builder in a way that would cause unused entries in @@ -1275,6 +1321,7 @@ public Builder() { this.previousIndicesLookup = metadata.indicesLookup; this.mappingsByHash = new HashMap<>(metadata.mappingsByHash); this.checkForUnusedMappings = false; + this.operatorMetadata = new HashMap<>(metadata.operatorMetadata); } private Builder(Map mappingsByHash) { @@ -1283,6 +1330,7 @@ private Builder(Map mappingsByHash) { aliasedIndices = ImmutableOpenMap.builder(); templates = ImmutableOpenMap.builder(); customs = ImmutableOpenMap.builder(); + operatorMetadata = new HashMap<>(); indexGraveyard(IndexGraveyard.builder().build()); // create new empty index graveyard to initialize previousIndicesLookup = null; this.mappingsByHash = new HashMap<>(mappingsByHash); @@ -1635,6 +1683,35 @@ public Builder customs(Map customs) { return this; } + /** + * Returns the {@link OperatorMetadata} for a given namespace + * @param namespace the namespace we are looking for operator metadata + * @return {@link OperatorMetadata} or null + */ + public OperatorMetadata operatorMetadata(String namespace) { + return operatorMetadata.get(namespace); + } + + /** + * Adds a map of namespace to {@link OperatorMetadata} into the metadata builder + * @param operatorMetadata a map of namespace to {@link OperatorMetadata} + * @return {@link Builder} + */ + public Builder put(Map operatorMetadata) { + this.operatorMetadata.putAll(operatorMetadata); + return this; + } + + /** + * Adds a {@link OperatorMetadata} for a given namespace to the metadata builder + * @param metadata an {@link OperatorMetadata} + * @return {@link Builder} + */ + public Builder put(OperatorMetadata metadata) { + operatorMetadata.put(metadata.namespace(), metadata); + return this; + } + public Builder indexGraveyard(final IndexGraveyard indexGraveyard) { putCustom(IndexGraveyard.TYPE, indexGraveyard); return this; @@ -1833,7 +1910,8 @@ public Metadata build() { visibleClosedIndicesArray, indicesLookup, Collections.unmodifiableMap(mappingsByHash), - Version.fromId(oldestIndexVersionId) + Version.fromId(oldestIndexVersionId), + Collections.unmodifiableMap(operatorMetadata) ); } @@ -2136,6 +2214,12 @@ public static void toXContent(Metadata metadata, XContentBuilder builder, ToXCon } } + builder.startObject("operator"); + for (OperatorMetadata operatorMetadata : metadata.operatorMetadata().values()) { + OperatorMetadata.Builder.toXContent(operatorMetadata, builder, params); + } + builder.endObject(); + builder.endObject(); } @@ -2179,6 +2263,10 @@ public static Metadata fromXContent(XContentParser parser) throws IOException { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { builder.put(IndexTemplateMetadata.Builder.fromXContent(parser, parser.currentName())); } + } else if ("operator".equals(currentFieldName)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + builder.put(OperatorMetadata.fromXContent(parser)); + } } else { try { Custom custom = parser.namedObject(Custom.class, currentFieldName, null); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java new file mode 100644 index 0000000000000..569a53292985d --- /dev/null +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java @@ -0,0 +1,232 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster.metadata; + +import org.elasticsearch.cluster.Diff; +import org.elasticsearch.cluster.SimpleDiffable; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.xcontent.ToXContentFragment; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A metadata class to hold error information about errors encountered + * while applying a cluster state update for a given namespace. + *

+ * This information is held by the {@link OperatorMetadata} class. + */ +public record OperatorErrorMetadata( + Long version, + org.elasticsearch.cluster.metadata.OperatorErrorMetadata.ErrorKind errorKind, + List errors +) implements SimpleDiffable, ToXContentFragment { + /** + * Contructs an operator error metadata + * + * @param version the metadata version of the content which failed to apply + * @param errorKind the kind of error we encountered while processing + * @param errors the list of errors encountered during parsing and validation of the operator content + */ + public OperatorErrorMetadata {} + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeLong(version); + out.writeString(errorKind.getKindValue()); + out.writeCollection(errors, StreamOutput::writeString); + } + + /** + * Reads an {@link OperatorErrorMetadata} from a {@link StreamInput} + * @param in the {@link StreamInput} to read from + * @return {@link OperatorErrorMetadata} + * @throws IOException + */ + public static OperatorErrorMetadata readFrom(StreamInput in) throws IOException { + Builder builder = new Builder().version(in.readLong()) + .errorKind(ErrorKind.of(in.readString())) + .errors(in.readList(StreamInput::readString)); + return builder.build(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + Builder.toXContent(this, builder); + return builder; + } + + /** + * Reads an {@link OperatorErrorMetadata} {@link Diff} from {@link StreamInput} + * @param in the {@link StreamInput} to read the diff from + * @return a {@link Diff} of {@link OperatorErrorMetadata} + * @throws IOException + */ + public static Diff readDiffFrom(StreamInput in) throws IOException { + return SimpleDiffable.readDiffFrom(OperatorErrorMetadata::readFrom, in); + } + + /** + * Helper method to construct an {@link OperatorErrorMetadata} {@link Builder} + * @return {@link Builder} + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for {@link OperatorErrorMetadata} + */ + public static class Builder { + private static final String ERRORS = "errors"; + private static final String VERSION = "version"; + private static final String ERROR_KIND = "error_kind"; + + private Long version; + private List errors; + private ErrorKind errorKind; + + /** + * Default constructor + */ + public Builder() { + this.version = 0L; + this.errors = new ArrayList<>(); + } + + /** + * Set the error metadata version + * @param version the metadata version of the operator content that failed to apply + * @return {@link Builder} + */ + public Builder version(Long version) { + this.version = version; + return this; + } + + /** + * Set the list of errors we encountered while processing an operator state content + * @param errors a list of all errors + * @return {@link Builder} + */ + public Builder errors(List errors) { + this.errors = errors; + return this; + } + + /** + * Set the validation error kind + * @param errorKind one of {@link ErrorKind} + * @return {@link Builder} + */ + public Builder errorKind(ErrorKind errorKind) { + this.errorKind = errorKind; + return this; + } + + /** + * Builds the {@link OperatorErrorMetadata} + * @return {@link OperatorErrorMetadata} + */ + public OperatorErrorMetadata build() { + return new OperatorErrorMetadata(version, errorKind, Collections.unmodifiableList(errors)); + } + + /** + * Serializes an {@link OperatorErrorMetadata} to xContent + * + * @param metadata {@link OperatorErrorMetadata} + * @param builder {@link XContentBuilder} + */ + public static void toXContent(OperatorErrorMetadata metadata, XContentBuilder builder) throws IOException { + builder.startObject(); + builder.field(VERSION, metadata.version); + builder.field(ERROR_KIND, metadata.errorKind.getKindValue()); + builder.stringListField(ERRORS, metadata.errors); + builder.endObject(); + } + + /** + * Reads an {@link OperatorErrorMetadata} from xContent + * + * @param parser {@link XContentParser} + * @return {@link OperatorErrorMetadata} + * @throws IOException + */ + public static OperatorErrorMetadata fromXContent(XContentParser parser) throws IOException { + Builder builder = new Builder(); + + String currentFieldName = parser.currentName(); + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.START_ARRAY) { + if (ERRORS.equals(currentFieldName)) { + List errors = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + errors.add(parser.text()); + } + builder.errors(errors); + } + } else if (token.isValue()) { + if (VERSION.equals(currentFieldName)) { + builder.version(parser.longValue()); + } else if (ERROR_KIND.equals(currentFieldName)) { + builder.errorKind(ErrorKind.of(parser.text())); + } + } + } + return builder.build(); + } + } + + /** + * Enum for kinds of errors we might encounter while processing operator cluster state updates. + */ + public enum ErrorKind { + PARSING("parsing"), + VALIDATION("validation"), + TRANSIENT("transient"); + + private final String kind; + + ErrorKind(String kind) { + this.kind = kind; + } + + /** + * Returns the String value for this enum value + * @return the String value for the enum + */ + public String getKindValue() { + return kind; + } + + /** + * Helper method to construct {@link ErrorKind} from a String. The JDK default implementation + * throws incomprehensible error. + * @param kind String value + * @return {@link ErrorKind} + */ + public static ErrorKind of(String kind) { + for (var report : values()) { + if (report.kind.equals(kind)) { + return report; + } + } + throw new IllegalArgumentException("kind not supported [" + kind + "]"); + } + } +} diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java new file mode 100644 index 0000000000000..b4aa9379469ee --- /dev/null +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster.metadata; + +import org.elasticsearch.cluster.Diff; +import org.elasticsearch.cluster.SimpleDiffable; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.xcontent.ToXContentFragment; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +/** + * Metadata class to hold the keys set in operator mode for each operator handler. Since we + * hold operator metadata state for multiple namespaces, the same handler can appear in + * multiple namespaces. See {@link OperatorMetadata} and {@link Metadata}. + */ +public record OperatorHandlerMetadata(String name, Set keys) + implements + SimpleDiffable, + ToXContentFragment { + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(name); + out.writeCollection(keys, StreamOutput::writeString); + } + + /** + * Reads an {@link OperatorHandlerMetadata} from a {@link StreamInput} + * @param in the {@link StreamInput} to read from + * @return {@link OperatorHandlerMetadata} + * @throws IOException + */ + public static OperatorHandlerMetadata readFrom(StreamInput in) throws IOException { + Builder builder = new Builder(in.readString()).keys(in.readSet(StreamInput::readString)); + return builder.build(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + Builder.toXContent(this, builder); + return builder; + } + + /** + * Reads an {@link OperatorHandlerMetadata} {@link Diff} from {@link StreamInput} + * @param in the {@link StreamInput} to read the diff from + * @return a {@link Diff} of {@link OperatorHandlerMetadata} + * @throws IOException + */ + public static Diff readDiffFrom(StreamInput in) throws IOException { + return SimpleDiffable.readDiffFrom(OperatorHandlerMetadata::readFrom, in); + } + + /** + * Builder class for {@link OperatorHandlerMetadata} + */ + public static class Builder { + private static final String HANDLER_KEYS = "keys"; + + private final String name; + private Set keys; + + /** + * Default constructor, the handler name is a required parameter + * @param name the handler name + */ + public Builder(String name) { + this.name = name; + this.keys = new HashSet<>(); + } + + /** + * Set the cluster state keys recorded/written by this handler in the cluster state + * @param keys a set of String keys of the state that's written by this handler + * @return {@link Builder} + */ + public Builder keys(Set keys) { + this.keys = keys; + return this; + } + + /** + * Builds the {@link OperatorHandlerMetadata} + * @return {@link OperatorHandlerMetadata} + */ + public OperatorHandlerMetadata build() { + return new OperatorHandlerMetadata(name, Collections.unmodifiableSet(keys)); + } + + /** + * Serializes an {@link OperatorHandlerMetadata} to xContent + * + * @param metadata {@link OperatorHandlerMetadata} + * @param builder {@link XContentBuilder} + */ + public static void toXContent(OperatorHandlerMetadata metadata, XContentBuilder builder) throws IOException { + builder.startObject(metadata.name()); + builder.stringListField(HANDLER_KEYS, new TreeSet<>(metadata.keys)); // ordered set here to ensure output consistency + builder.endObject(); + } + + /** + * Reads an {@link OperatorHandlerMetadata} from xContent + * + * @param parser {@link XContentParser} + * @return {@link OperatorHandlerMetadata} + * @throws IOException + */ + public static OperatorHandlerMetadata fromXContent(XContentParser parser) throws IOException { + Builder builder = new Builder(parser.currentName()); + + String currentFieldName = parser.currentName(); + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.START_ARRAY) { + if (HANDLER_KEYS.equals(currentFieldName)) { + Set handlerKeys = new HashSet<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + handlerKeys.add(parser.text()); + } + builder.keys(handlerKeys); + } + } + } + return builder.build(); + } + } +} diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java new file mode 100644 index 0000000000000..e7708ef69026b --- /dev/null +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java @@ -0,0 +1,311 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster.metadata; + +import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.cluster.Diff; +import org.elasticsearch.cluster.DiffableUtils; +import org.elasticsearch.cluster.SimpleDiffable; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.xcontent.ToXContent; +import org.elasticsearch.xcontent.ToXContentFragment; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * Metadata class that contains information about cluster settings/entities set + * in an operator mode. These types of settings are read only through the REST API, + * and cannot be modified by the end user. + */ +public record OperatorMetadata( + String namespace, + Long version, + Map handlers, + OperatorErrorMetadata errorMetadata +) implements SimpleDiffable, ToXContentFragment { + /** + * OperatorMetadata contains information about settings set in operator mode. + * These settings cannot be updated by the end user and are set outside of the + * REST layer, e.g. through file based settings or by plugin/modules. + * + * @param namespace The namespace of the setting creator, e.g. file_settings, security plugin, etc. + * @param version The update version, must increase with each update + * @param handlers Per state update handler information on key set in by this update. These keys are validated at REST time. + * @param errorMetadata If the update failed for some reason, this is where we store the error information metadata. + */ + public OperatorMetadata {} + + /** + * Creates a set intersection between cluster state keys set by a given operator handler and the input set. + * This method is to be used to check if a REST action handler is allowed to modify certain cluster state. + * + * @param handlerName the name of the operator handler we need to check for keys + * @param modified a set of keys we want to see if we can modify. + * @return + */ + public Set conflicts(String handlerName, Set modified) { + OperatorHandlerMetadata handlerMetadata = handlers.get(handlerName); + if (handlerMetadata == null || handlerMetadata.keys().isEmpty()) { + return Collections.emptySet(); + } + + Set intersect = new HashSet<>(handlerMetadata.keys()); + intersect.retainAll(modified); + return Collections.unmodifiableSet(intersect); + } + + /** + * Reads an {@link OperatorMetadata} from a {@link StreamInput} + * @param in the {@link StreamInput} to read from + * @return {@link OperatorMetadata} + * @throws IOException + */ + public static OperatorMetadata readFrom(StreamInput in) throws IOException { + Builder builder = new Builder(in.readString()).version(in.readLong()); + + int handlersSize = in.readVInt(); + for (int i = 0; i < handlersSize; i++) { + OperatorHandlerMetadata handler = OperatorHandlerMetadata.readFrom(in); + builder.putHandler(handler); + } + + builder.errorMetadata(in.readOptionalWriteable(OperatorErrorMetadata::readFrom)); + return builder.build(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(namespace); + out.writeLong(version); + out.writeCollection(handlers.values()); + out.writeOptionalWriteable(errorMetadata); + } + + /** + * Reads an {@link OperatorMetadata} {@link Diff} from {@link StreamInput} + * @param in the {@link StreamInput} to read the diff from + * @return a {@link Diff} of {@link OperatorMetadata} + * @throws IOException + */ + public static Diff readDiffFrom(StreamInput in) throws IOException { + return SimpleDiffable.readDiffFrom(OperatorMetadata::readFrom, in); + } + + /** + * Empty {@link org.elasticsearch.cluster.DiffableUtils.MapDiff} helper for metadata backwards compatibility. + */ + public static final DiffableUtils.MapDiff> EMPTY_DIFF = + new DiffableUtils.MapDiff<>(null, null, List.of(), List.of(), List.of()) { + @Override + public Map apply(Map part) { + return part; + } + }; + + /** + * Convenience method for creating a {@link Builder} for {@link OperatorMetadata} + * + * @param namespace the namespace under which we'll store the {@link OperatorMetadata} + * @return {@link Builder} + */ + public static Builder builder(String namespace) { + return new Builder(namespace); + } + + /** + * Convenience method for creating a {@link Builder} for {@link OperatorMetadata} + * + * @param namespace the namespace under which we'll store the {@link OperatorMetadata} + * @param metadata an existing {@link OperatorMetadata} + * @return {@link Builder} + */ + public static Builder builder(String namespace, OperatorMetadata metadata) { + return new Builder(namespace, metadata); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + Builder.toXContent(this, builder, params); + return builder; + } + + /** + * Reads {@link OperatorMetadata} from {@link XContentParser} + * @param parser {@link XContentParser} + * @return {@link OperatorMetadata} + * @throws IOException + */ + public static OperatorMetadata fromXContent(final XContentParser parser) throws IOException { + parser.nextToken(); + return Builder.fromXContent(parser, parser.currentName()); + } + + /** + * Builder class for {@link OperatorMetadata} + */ + public static class Builder { + private static final String VERSION = "version"; + private static final String HANDLERS = "handlers"; + private static final String ERRORS_METADATA = "errors"; + + private final String namespace; + private Long version; + private Map handlers; + OperatorErrorMetadata errorMetadata; + + /** + * Empty builder for OperatorMetadata. The operator metadata namespace is a required parameter + * + * @param namespace The namespace for this metadata + */ + public Builder(String namespace) { + this.namespace = namespace; + this.version = 0L; + this.handlers = new HashMap<>(); + this.errorMetadata = null; + } + + /** + * Creates an operator metadata builder + * + * @param namespace the namespace for which we are storing metadata, e.g. file_settings + * @param metadata the previous metadata + */ + public Builder(String namespace, OperatorMetadata metadata) { + this(namespace); + if (metadata != null) { + this.version = metadata.version; + this.handlers = new HashMap<>(metadata.handlers); + this.errorMetadata = metadata.errorMetadata; + } + } + + /** + * Stores the version for the operator metadata. Each new cluster state update in operator + * mode requires a version bump. The version increase doesn't have to be monotonic. + * @param version the new operator metadata version + * @return {@link Builder} + */ + public Builder version(Long version) { + this.version = version; + return this; + } + + /** + * Adds {@link OperatorErrorMetadata} if we need to store error information about certain + * operator state processing. + * + * @param errorMetadata {@link OperatorErrorMetadata} + * @return {@link Builder} + */ + public Builder errorMetadata(OperatorErrorMetadata errorMetadata) { + this.errorMetadata = errorMetadata; + return this; + } + + /** + * Adds an {@link OperatorHandlerMetadata} for this {@link OperatorMetadata}. The handler + * metadata is stored in a map, keyed off the {@link OperatorHandlerMetadata} name. Previously + * stored {@link OperatorHandlerMetadata} for a given name is overwritten. + * + * @param handler {@link OperatorHandlerMetadata} + * @return {@link Builder} + */ + public Builder putHandler(OperatorHandlerMetadata handler) { + this.handlers.put(handler.name(), handler); + return this; + } + + /** + * Builds an {@link OperatorMetadata} from this builder. + * @return {@link OperatorMetadata} + */ + public OperatorMetadata build() { + return new OperatorMetadata(namespace, version, Collections.unmodifiableMap(handlers), errorMetadata); + } + + /** + * Serializes the operator metadata to xContent + * + * @param operatorMetadata {@link OperatorMetadata} + * @param builder {@link XContentBuilder} + * @param params {@link org.elasticsearch.xcontent.ToXContent.Params} + */ + public static void toXContent(OperatorMetadata operatorMetadata, XContentBuilder builder, ToXContent.Params params) + throws IOException { + builder.startObject(operatorMetadata.namespace()); + builder.field(VERSION, operatorMetadata.version()); + builder.startObject(HANDLERS); + var sortedKeys = new TreeSet<>(operatorMetadata.handlers.keySet()); + for (var key : sortedKeys) { + OperatorHandlerMetadata.Builder.toXContent(operatorMetadata.handlers.get(key), builder); + } + builder.endObject(); + builder.field(ERRORS_METADATA, operatorMetadata.errorMetadata); + builder.endObject(); + } + + /** + * Reads the operator metadata from xContent + * + * @param parser {@link XContentParser} + * @param namespace the operator metadata namespace. See {@link Metadata} operatorMetadata. + * @return {@link OperatorMetadata} + * @throws IOException + */ + public static OperatorMetadata fromXContent(XContentParser parser, String namespace) throws IOException { + OperatorMetadata.Builder builder = new OperatorMetadata.Builder(namespace); + + String currentFieldName = skipNamespace(parser); + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.START_OBJECT) { + if (HANDLERS.equals(currentFieldName)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + builder.putHandler(OperatorHandlerMetadata.Builder.fromXContent(parser)); + } + } else if (ERRORS_METADATA.equals(currentFieldName)) { + builder.errorMetadata(OperatorErrorMetadata.Builder.fromXContent(parser)); + } else { + throw new ElasticsearchParseException("unknown key [{}] for operator metadata", currentFieldName); + } + } else if (token.isValue()) { + if (VERSION.equals(currentFieldName)) { + builder.version(parser.longValue()); + } + } + } + return builder.build(); + } + + private static String skipNamespace(XContentParser parser) throws IOException { + XContentParser.Token token = parser.nextToken(); + if (token == XContentParser.Token.START_OBJECT) { + token = parser.nextToken(); + if (token == XContentParser.Token.FIELD_NAME) { + return parser.currentName(); + } + } + + return null; + } + } +} diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java index 332233d2b7d66..b9ee982a1f41e 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java @@ -142,7 +142,8 @@ public void testToXContent() throws IOException { }, "index-graveyard": { "tombstones": [] - } + }, + "operator":{} }, "routing_table": { "indices": {} @@ -246,7 +247,8 @@ public void testToXContent() throws IOException { }, "index-graveyard" : { "tombstones" : [ ] - } + }, + "operator":{} } } }"""), XContentHelper.stripWhitespace(Strings.toString(builder))); diff --git a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java index 5a93fa16359c0..05d417bef0077 100644 --- a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java @@ -286,7 +286,8 @@ public void testToXContent() throws IOException { }, "index-graveyard": { "tombstones": [] - } + }, + "operator" : { } }, "routing_table": { "indices": { @@ -489,7 +490,8 @@ public void testToXContent_FlatSettingTrue_ReduceMappingFalse() throws IOExcepti }, "index-graveyard" : { "tombstones" : [ ] - } + }, + "operator" : { } }, "routing_table" : { "indices" : { @@ -699,7 +701,8 @@ public void testToXContent_FlatSettingFalse_ReduceMappingTrue() throws IOExcepti }, "index-graveyard" : { "tombstones" : [ ] - } + }, + "operator" : { } }, "routing_table" : { "indices" : { @@ -840,7 +843,8 @@ public void testToXContentSameTypeName() throws IOException { }, "index-graveyard" : { "tombstones" : [ ] - } + }, + "operator" : { } }, "routing_table" : { "indices" : { } diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java new file mode 100644 index 0000000000000..239282bfe69e2 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster.metadata; + +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xcontent.ToXContent; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; +import org.elasticsearch.xcontent.json.JsonXContent; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; + +/** + * Tests for the {@link OperatorMetadata}, {@link OperatorErrorMetadata}, {@link OperatorHandlerMetadata} classes + */ +public class OperatorMetadataTests extends ESTestCase { + + public void testEquals() { + final OperatorMetadata meta = createRandom(); + assertThat(meta, equalTo(OperatorMetadata.builder(meta.namespace(), meta).build())); + final OperatorMetadata.Builder newMeta = OperatorMetadata.builder(meta.namespace(), meta); + newMeta.putHandler(new OperatorHandlerMetadata("1", Collections.emptySet())); + assertThat(newMeta.build(), not(meta)); + } + + public void testSerialization() throws IOException { + final OperatorMetadata meta = createRandom(); + final BytesStreamOutput out = new BytesStreamOutput(); + meta.writeTo(out); + assertThat(OperatorMetadata.readFrom(out.bytes().streamInput()), equalTo(meta)); + } + + public void testXContent() throws IOException { + final OperatorMetadata meta = createRandom(); + final XContentBuilder builder = JsonXContent.contentBuilder(); + builder.startObject(); + meta.toXContent(builder, ToXContent.EMPTY_PARAMS); + builder.endObject(); + XContentParser parser = createParser(JsonXContent.jsonXContent, BytesReference.bytes(builder)); + parser.nextToken(); // the beginning of the object + assertThat(OperatorMetadata.fromXContent(parser), equalTo(meta)); + } + + private static OperatorMetadata createRandom() { + List handlers = randomList( + 0, + 10, + () -> new OperatorHandlerMetadata.Builder(randomAlphaOfLength(5)).keys(randomSet(1, 5, () -> randomAlphaOfLength(6))).build() + ); + + List errors = randomList( + 0, + 10, + () -> new OperatorErrorMetadata.Builder().version(1L) + .errorKind(randomFrom(OperatorErrorMetadata.ErrorKind.values())) + .errors(randomList(1, 5, () -> randomAlphaOfLength(10))) + .build() + ); + + OperatorMetadata.Builder builder = OperatorMetadata.builder(randomAlphaOfLength(7)); + + for (var handlerMeta : handlers) { + builder.putHandler(handlerMeta); + } + + for (var errorMeta : errors) { + builder.errorMetadata(errorMeta); + } + + return builder.build(); + } +} diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java index 77ccb5bf93db5..c42a89f690c57 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java @@ -29,6 +29,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import static org.elasticsearch.cluster.metadata.AliasMetadata.newAliasMetadataBuilder; import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.createFirstBackingIndex; @@ -44,6 +45,23 @@ public class ToAndFromJsonMetadataTests extends ESTestCase { public void testSimpleJsonFromAndTo() throws IOException { IndexMetadata idx1 = createFirstBackingIndex("data-stream1").build(); IndexMetadata idx2 = createFirstBackingIndex("data-stream2").build(); + + OperatorHandlerMetadata hmOne = new OperatorHandlerMetadata.Builder("one").keys(Set.of("a", "b")).build(); + OperatorHandlerMetadata hmTwo = new OperatorHandlerMetadata.Builder("two").keys(Set.of("c", "d")).build(); + + OperatorErrorMetadata emOne = new OperatorErrorMetadata.Builder().version(1L) + .errorKind(OperatorErrorMetadata.ErrorKind.VALIDATION) + .errors(List.of("Test error 1", "Test error 2")) + .build(); + + OperatorMetadata operatorMetadata = OperatorMetadata.builder("namespace_one") + .errorMetadata(emOne) + .putHandler(hmOne) + .putHandler(hmTwo) + .build(); + + OperatorMetadata operatorMetadata1 = OperatorMetadata.builder("namespace_two").putHandler(hmTwo).build(); + Metadata metadata = Metadata.builder() .put( IndexTemplateMetadata.builder("foo") @@ -107,6 +125,8 @@ public void testSimpleJsonFromAndTo() throws IOException { .put(idx2, false) .put(DataStreamTestHelper.newInstance("data-stream1", List.of(idx1.getIndex()))) .put(DataStreamTestHelper.newInstance("data-stream2", List.of(idx2.getIndex()))) + .put(operatorMetadata) + .put(operatorMetadata1) .build(); XContentBuilder builder = JsonXContent.contentBuilder(); @@ -180,6 +200,10 @@ public void testSimpleJsonFromAndTo() throws IOException { assertThat(parsedMetadata.dataStreams().get("data-stream2").getName(), is("data-stream2")); assertThat(parsedMetadata.dataStreams().get("data-stream2").getTimeStampField().getName(), is("@timestamp")); assertThat(parsedMetadata.dataStreams().get("data-stream2").getIndices(), contains(idx2.getIndex())); + + // operator metadata + assertEquals(operatorMetadata, parsedMetadata.operatorMetadata(operatorMetadata.namespace())); + assertEquals(operatorMetadata1, parsedMetadata.operatorMetadata(operatorMetadata1.namespace())); } private static final String MAPPING_SOURCE1 = """ @@ -246,7 +270,8 @@ public void testToXContentGateway_FlatSettingTrue_ReduceMappingFalse() throws IO }, "index-graveyard" : { "tombstones" : [ ] - } + }, + "operator" : { } } }""".formatted(Version.CURRENT.id, Version.CURRENT.id), Strings.toString(builder)); } @@ -341,7 +366,8 @@ public void testToXContentAPI_SameTypeName() throws IOException { }, "index-graveyard" : { "tombstones" : [ ] - } + }, + "operator" : { } } }""".formatted(Version.CURRENT.id), Strings.toString(builder)); } @@ -405,7 +431,8 @@ public void testToXContentGateway_FlatSettingFalse_ReduceMappingTrue() throws IO }, "index-graveyard" : { "tombstones" : [ ] - } + }, + "operator" : { } } }""".formatted(Version.CURRENT.id, Version.CURRENT.id), Strings.toString(builder)); } @@ -507,7 +534,8 @@ public void testToXContentAPI_FlatSettingTrue_ReduceMappingFalse() throws IOExce }, "index-graveyard" : { "tombstones" : [ ] - } + }, + "operator" : { } } }""".formatted(Version.CURRENT.id, Version.CURRENT.id), Strings.toString(builder)); } @@ -615,6 +643,181 @@ public void testToXContentAPI_FlatSettingFalse_ReduceMappingTrue() throws IOExce }, "index-graveyard" : { "tombstones" : [ ] + }, + "operator" : { } + } + }""".formatted(Version.CURRENT.id, Version.CURRENT.id), Strings.toString(builder)); + } + + public void testToXContentAPIOperatorMetadata() throws IOException { + Map mapParams = new HashMap<>() { + { + put(Metadata.CONTEXT_MODE_PARAM, CONTEXT_MODE_API); + put("flat_settings", "false"); + put("reduce_mappings", "true"); + } + }; + + Metadata metadata = buildMetadata(); + + OperatorHandlerMetadata hmOne = new OperatorHandlerMetadata.Builder("one").keys(Set.of("a", "b")).build(); + OperatorHandlerMetadata hmTwo = new OperatorHandlerMetadata.Builder("two").keys(Set.of("c", "d")).build(); + OperatorHandlerMetadata hmThree = new OperatorHandlerMetadata.Builder("three").keys(Set.of("e", "f")).build(); + + OperatorErrorMetadata emOne = new OperatorErrorMetadata.Builder().version(1L) + .errorKind(OperatorErrorMetadata.ErrorKind.VALIDATION) + .errors(List.of("Test error 1", "Test error 2")) + .build(); + + OperatorErrorMetadata emTwo = new OperatorErrorMetadata.Builder().version(2L) + .errorKind(OperatorErrorMetadata.ErrorKind.TRANSIENT) + .errors(List.of("Test error 3", "Test error 4")) + .build(); + + OperatorMetadata omOne = OperatorMetadata.builder("namespace_one").errorMetadata(emOne).putHandler(hmOne).putHandler(hmTwo).build(); + + OperatorMetadata omTwo = OperatorMetadata.builder("namespace_two").errorMetadata(emTwo).putHandler(hmThree).build(); + + metadata = Metadata.builder(metadata).put(omOne).put(omTwo).build(); + + XContentBuilder builder = JsonXContent.contentBuilder().prettyPrint(); + builder.startObject(); + metadata.toXContent(builder, new ToXContent.MapParams(mapParams)); + builder.endObject(); + + assertEquals(""" + { + "metadata" : { + "cluster_uuid" : "clusterUUID", + "cluster_uuid_committed" : false, + "cluster_coordination" : { + "term" : 1, + "last_committed_config" : [ + "commitedConfigurationNodeId" + ], + "last_accepted_config" : [ + "acceptedConfigurationNodeId" + ], + "voting_config_exclusions" : [ + { + "node_id" : "exlucdedNodeId", + "node_name" : "excludedNodeName" + } + ] + }, + "templates" : { + "template" : { + "order" : 0, + "index_patterns" : [ + "pattern1", + "pattern2" + ], + "settings" : { + "index" : { + "version" : { + "created" : "%s" + } + } + }, + "mappings" : { }, + "aliases" : { } + } + }, + "indices" : { + "index" : { + "version" : 2, + "mapping_version" : 1, + "settings_version" : 1, + "aliases_version" : 1, + "routing_num_shards" : 1, + "state" : "open", + "settings" : { + "index" : { + "number_of_shards" : "1", + "number_of_replicas" : "2", + "version" : { + "created" : "%s" + } + } + }, + "mappings" : { + "type" : { + "type1" : { + "key" : "value" + } + } + }, + "aliases" : [ + "alias" + ], + "primary_terms" : { + "0" : 1 + }, + "in_sync_allocations" : { + "0" : [ + "allocationId" + ] + }, + "rollover_info" : { + "rolloveAlias" : { + "met_conditions" : { }, + "time" : 1 + } + }, + "system" : false, + "timestamp_range" : { + "shards" : [ ] + } + } + }, + "index-graveyard" : { + "tombstones" : [ ] + }, + "operator" : { + "namespace_one" : { + "version" : 0, + "handlers" : { + "one" : { + "keys" : [ + "a", + "b" + ] + }, + "two" : { + "keys" : [ + "c", + "d" + ] + } + }, + "errors" : { + "version" : 1, + "error_kind" : "validation", + "errors" : [ + "Test error 1", + "Test error 2" + ] + } + }, + "namespace_two" : { + "version" : 0, + "handlers" : { + "three" : { + "keys" : [ + "e", + "f" + ] + } + }, + "errors" : { + "version" : 2, + "error_kind" : "transient", + "errors" : [ + "Test error 3", + "Test error 4" + ] + } + } } } }""".formatted(Version.CURRENT.id, Version.CURRENT.id), Strings.toString(builder)); diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 8526f60e033b3..e3322a7b3a846 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -991,6 +991,10 @@ public static Map randomMap(int minMapSize, int maxMapSize, Supplie return list; } + public static Set randomSet(int minSetSize, int maxSetSize, Supplier valueConstructor) { + return new HashSet<>(randomList(minSetSize, maxSetSize, valueConstructor)); + } + private static final String[] TIME_SUFFIXES = new String[] { "d", "h", "ms", "s", "m", "micros", "nanos" }; public static String randomTimeValue(int lower, int upper, String... suffixes) { From c3b87a02b1250d4ec59afbe6f9c36d687b87bd16 Mon Sep 17 00:00:00 2001 From: Nikola Grcevski Date: Thu, 16 Jun 2022 15:04:35 -0400 Subject: [PATCH 2/7] Remove unused method --- .../org/elasticsearch/cluster/metadata/Metadata.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java index 0c64a75deaab6..0e638bbc10c11 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java @@ -1683,15 +1683,6 @@ public Builder customs(Map customs) { return this; } - /** - * Returns the {@link OperatorMetadata} for a given namespace - * @param namespace the namespace we are looking for operator metadata - * @return {@link OperatorMetadata} or null - */ - public OperatorMetadata operatorMetadata(String namespace) { - return operatorMetadata.get(namespace); - } - /** * Adds a map of namespace to {@link OperatorMetadata} into the metadata builder * @param operatorMetadata a map of namespace to {@link OperatorMetadata} From 0b6aafe96e9bef2697879cd6c556172c859ea70b Mon Sep 17 00:00:00 2001 From: Nikola Grcevski Date: Fri, 24 Jun 2022 16:22:21 -0400 Subject: [PATCH 3/7] Apply feedback --- .../cluster/metadata/Metadata.java | 13 +- .../metadata/OperatorErrorMetadata.java | 179 +++++------------- .../metadata/OperatorHandlerMetadata.java | 124 ++++-------- .../cluster/metadata/OperatorMetadata.java | 145 +++++++------- .../metadata/OperatorMetadataTests.java | 60 ++++-- .../metadata/ToAndFromJsonMetadataTests.java | 41 ++-- 6 files changed, 220 insertions(+), 342 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java index 9bddbd564b997..4150a3a3d859a 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java @@ -956,15 +956,6 @@ public Map operatorMetadata() { return this.operatorMetadata; } - /** - * Returns the {@link OperatorMetadata} for a given namespace. - * @param namespace the namespace to lookup operator metadata for - * @return {@link OperatorMetadata} or null if not found - */ - public OperatorMetadata operatorMetadata(String namespace) { - return this.operatorMetadata.get(namespace); - } - /** * The collection of index deletions in the cluster. */ @@ -1185,7 +1176,7 @@ public Metadata apply(Metadata part) { builder.indices(indices.apply(part.indices)); builder.templates(templates.apply(part.templates)); builder.customs(customs.apply(part.customs)); - builder.put(operatorMetadata.apply(part.operatorMetadata)); + builder.put(Collections.unmodifiableMap(operatorMetadata.apply(part.operatorMetadata))); return builder.build(); } } @@ -2210,7 +2201,7 @@ public static void toXContent(Metadata metadata, XContentBuilder builder, ToXCon builder.startObject("operator"); for (OperatorMetadata operatorMetadata : metadata.operatorMetadata().values()) { - OperatorMetadata.Builder.toXContent(operatorMetadata, builder, params); + operatorMetadata.toXContent(builder, params); } builder.endObject(); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java index 569a53292985d..abbe55460502b 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java @@ -12,28 +12,35 @@ import org.elasticsearch.cluster.SimpleDiffable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.xcontent.ConstructingObjectParser; +import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.ToXContentFragment; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg; + /** * A metadata class to hold error information about errors encountered * while applying a cluster state update for a given namespace. *

* This information is held by the {@link OperatorMetadata} class. + *

*/ -public record OperatorErrorMetadata( - Long version, - org.elasticsearch.cluster.metadata.OperatorErrorMetadata.ErrorKind errorKind, - List errors -) implements SimpleDiffable, ToXContentFragment { +public record OperatorErrorMetadata(Long version, ErrorKind errorKind, List errors) + implements + SimpleDiffable, + ToXContentFragment { + + static final ParseField ERRORS = new ParseField("errors"); + static final ParseField VERSION = new ParseField("version"); + static final ParseField ERROR_KIND = new ParseField("error_kind"); + /** - * Contructs an operator error metadata + * Constructs an operator error metadata * * @param version the metadata version of the content which failed to apply * @param errorKind the kind of error we encountered while processing @@ -50,146 +57,56 @@ public void writeTo(StreamOutput out) throws IOException { /** * Reads an {@link OperatorErrorMetadata} from a {@link StreamInput} + * * @param in the {@link StreamInput} to read from * @return {@link OperatorErrorMetadata} * @throws IOException */ public static OperatorErrorMetadata readFrom(StreamInput in) throws IOException { - Builder builder = new Builder().version(in.readLong()) - .errorKind(ErrorKind.of(in.readString())) - .errors(in.readList(StreamInput::readString)); - return builder.build(); + return new OperatorErrorMetadata(in.readLong(), ErrorKind.of(in.readString()), in.readList(StreamInput::readString)); } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - Builder.toXContent(this, builder); + builder.startObject(); + builder.field(VERSION.getPreferredName(), version); + builder.field(ERROR_KIND.getPreferredName(), errorKind.getKindValue()); + builder.stringListField(ERRORS.getPreferredName(), errors); + builder.endObject(); return builder; } - /** - * Reads an {@link OperatorErrorMetadata} {@link Diff} from {@link StreamInput} - * @param in the {@link StreamInput} to read the diff from - * @return a {@link Diff} of {@link OperatorErrorMetadata} - * @throws IOException - */ - public static Diff readDiffFrom(StreamInput in) throws IOException { - return SimpleDiffable.readDiffFrom(OperatorErrorMetadata::readFrom, in); + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "operator_error_metadata", + (a) -> new OperatorErrorMetadata((Long) a[0], ErrorKind.of((String) a[1]), (List) a[2]) + ); + + static { + PARSER.declareLong(constructorArg(), VERSION); + PARSER.declareString(constructorArg(), ERROR_KIND); + PARSER.declareStringArray(constructorArg(), ERRORS); } /** - * Helper method to construct an {@link OperatorErrorMetadata} {@link Builder} - * @return {@link Builder} + * Reads an {@link OperatorErrorMetadata} from xContent + * + * @param parser {@link XContentParser} + * @return {@link OperatorErrorMetadata} */ - public static Builder builder() { - return new Builder(); + public static OperatorErrorMetadata fromXContent(final XContentParser parser) { + return PARSER.apply(parser, null); } /** - * Builder class for {@link OperatorErrorMetadata} + * Reads an {@link OperatorErrorMetadata} {@link Diff} from {@link StreamInput} + * + * @param in the {@link StreamInput} to read the diff from + * @return a {@link Diff} of {@link OperatorErrorMetadata} + * @throws IOException */ - public static class Builder { - private static final String ERRORS = "errors"; - private static final String VERSION = "version"; - private static final String ERROR_KIND = "error_kind"; - - private Long version; - private List errors; - private ErrorKind errorKind; - - /** - * Default constructor - */ - public Builder() { - this.version = 0L; - this.errors = new ArrayList<>(); - } - - /** - * Set the error metadata version - * @param version the metadata version of the operator content that failed to apply - * @return {@link Builder} - */ - public Builder version(Long version) { - this.version = version; - return this; - } - - /** - * Set the list of errors we encountered while processing an operator state content - * @param errors a list of all errors - * @return {@link Builder} - */ - public Builder errors(List errors) { - this.errors = errors; - return this; - } - - /** - * Set the validation error kind - * @param errorKind one of {@link ErrorKind} - * @return {@link Builder} - */ - public Builder errorKind(ErrorKind errorKind) { - this.errorKind = errorKind; - return this; - } - - /** - * Builds the {@link OperatorErrorMetadata} - * @return {@link OperatorErrorMetadata} - */ - public OperatorErrorMetadata build() { - return new OperatorErrorMetadata(version, errorKind, Collections.unmodifiableList(errors)); - } - - /** - * Serializes an {@link OperatorErrorMetadata} to xContent - * - * @param metadata {@link OperatorErrorMetadata} - * @param builder {@link XContentBuilder} - */ - public static void toXContent(OperatorErrorMetadata metadata, XContentBuilder builder) throws IOException { - builder.startObject(); - builder.field(VERSION, metadata.version); - builder.field(ERROR_KIND, metadata.errorKind.getKindValue()); - builder.stringListField(ERRORS, metadata.errors); - builder.endObject(); - } - - /** - * Reads an {@link OperatorErrorMetadata} from xContent - * - * @param parser {@link XContentParser} - * @return {@link OperatorErrorMetadata} - * @throws IOException - */ - public static OperatorErrorMetadata fromXContent(XContentParser parser) throws IOException { - Builder builder = new Builder(); - - String currentFieldName = parser.currentName(); - XContentParser.Token token; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.START_ARRAY) { - if (ERRORS.equals(currentFieldName)) { - List errors = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - errors.add(parser.text()); - } - builder.errors(errors); - } - } else if (token.isValue()) { - if (VERSION.equals(currentFieldName)) { - builder.version(parser.longValue()); - } else if (ERROR_KIND.equals(currentFieldName)) { - builder.errorKind(ErrorKind.of(parser.text())); - } - } - } - return builder.build(); - } + public static Diff readDiffFrom(StreamInput in) throws IOException { + return SimpleDiffable.readDiffFrom(OperatorErrorMetadata::readFrom, in); } /** @@ -208,6 +125,7 @@ public enum ErrorKind { /** * Returns the String value for this enum value + * * @return the String value for the enum */ public String getKindValue() { @@ -215,8 +133,9 @@ public String getKindValue() { } /** - * Helper method to construct {@link ErrorKind} from a String. The JDK default implementation - * throws incomprehensible error. + * Helper method to construct {@link ErrorKind} from a String. + * + * The JDK default implementation throws incomprehensible error. * @param kind String value * @return {@link ErrorKind} */ diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java index b4aa9379469ee..4fbeea7a0bac2 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java @@ -12,26 +12,34 @@ import org.elasticsearch.cluster.SimpleDiffable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.xcontent.ConstructingObjectParser; +import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.ToXContentFragment; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.TreeSet; +import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg; + /** - * Metadata class to hold the keys set in operator mode for each operator handler. Since we - * hold operator metadata state for multiple namespaces, the same handler can appear in + * Metadata class to hold the keys set in operator mode for each operator handler. + * + *

+ * Since we hold operator metadata state for multiple namespaces, the same handler can appear in * multiple namespaces. See {@link OperatorMetadata} and {@link Metadata}. + *

*/ public record OperatorHandlerMetadata(String name, Set keys) implements SimpleDiffable, ToXContentFragment { + static final ParseField KEYS = new ParseField("keys"); + @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(name); @@ -40,23 +48,48 @@ public void writeTo(StreamOutput out) throws IOException { /** * Reads an {@link OperatorHandlerMetadata} from a {@link StreamInput} + * * @param in the {@link StreamInput} to read from * @return {@link OperatorHandlerMetadata} * @throws IOException */ public static OperatorHandlerMetadata readFrom(StreamInput in) throws IOException { - Builder builder = new Builder(in.readString()).keys(in.readSet(StreamInput::readString)); - return builder.build(); + return new OperatorHandlerMetadata(in.readString(), in.readSet(StreamInput::readString)); } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - Builder.toXContent(this, builder); + builder.startObject(name()); + builder.stringListField(KEYS.getPreferredName(), new TreeSet<>(keys())); // ordered set here to ensure output consistency + builder.endObject(); return builder; } + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "operator_handler_metadata", + false, + (a, name) -> new OperatorHandlerMetadata(name, Set.copyOf((List) a[0])) + ); + + static { + PARSER.declareStringArray(optionalConstructorArg(), KEYS); + } + + /** + * Reads an {@link OperatorHandlerMetadata} from xContent + * + * @param parser {@link XContentParser} + * @return {@link OperatorHandlerMetadata} + * @throws IOException + */ + public static OperatorHandlerMetadata fromXContent(XContentParser parser, String name) throws IOException { + return PARSER.apply(parser, name); + } + /** * Reads an {@link OperatorHandlerMetadata} {@link Diff} from {@link StreamInput} + * * @param in the {@link StreamInput} to read the diff from * @return a {@link Diff} of {@link OperatorHandlerMetadata} * @throws IOException @@ -64,81 +97,4 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws public static Diff readDiffFrom(StreamInput in) throws IOException { return SimpleDiffable.readDiffFrom(OperatorHandlerMetadata::readFrom, in); } - - /** - * Builder class for {@link OperatorHandlerMetadata} - */ - public static class Builder { - private static final String HANDLER_KEYS = "keys"; - - private final String name; - private Set keys; - - /** - * Default constructor, the handler name is a required parameter - * @param name the handler name - */ - public Builder(String name) { - this.name = name; - this.keys = new HashSet<>(); - } - - /** - * Set the cluster state keys recorded/written by this handler in the cluster state - * @param keys a set of String keys of the state that's written by this handler - * @return {@link Builder} - */ - public Builder keys(Set keys) { - this.keys = keys; - return this; - } - - /** - * Builds the {@link OperatorHandlerMetadata} - * @return {@link OperatorHandlerMetadata} - */ - public OperatorHandlerMetadata build() { - return new OperatorHandlerMetadata(name, Collections.unmodifiableSet(keys)); - } - - /** - * Serializes an {@link OperatorHandlerMetadata} to xContent - * - * @param metadata {@link OperatorHandlerMetadata} - * @param builder {@link XContentBuilder} - */ - public static void toXContent(OperatorHandlerMetadata metadata, XContentBuilder builder) throws IOException { - builder.startObject(metadata.name()); - builder.stringListField(HANDLER_KEYS, new TreeSet<>(metadata.keys)); // ordered set here to ensure output consistency - builder.endObject(); - } - - /** - * Reads an {@link OperatorHandlerMetadata} from xContent - * - * @param parser {@link XContentParser} - * @return {@link OperatorHandlerMetadata} - * @throws IOException - */ - public static OperatorHandlerMetadata fromXContent(XContentParser parser) throws IOException { - Builder builder = new Builder(parser.currentName()); - - String currentFieldName = parser.currentName(); - XContentParser.Token token; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.START_ARRAY) { - if (HANDLER_KEYS.equals(currentFieldName)) { - Set handlerKeys = new HashSet<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - handlerKeys.add(parser.text()); - } - builder.keys(handlerKeys); - } - } - } - return builder.build(); - } - } } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java index e7708ef69026b..3fe3264864833 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java @@ -8,13 +8,13 @@ package org.elasticsearch.cluster.metadata; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.cluster.Diff; import org.elasticsearch.cluster.DiffableUtils; import org.elasticsearch.cluster.SimpleDiffable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.xcontent.ToXContent; +import org.elasticsearch.xcontent.ConstructingObjectParser; +import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.ToXContentFragment; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; @@ -28,10 +28,17 @@ import java.util.Set; import java.util.TreeSet; +import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg; + /** * Metadata class that contains information about cluster settings/entities set - * in an operator mode. These types of settings are read only through the REST API, + * in an operator mode. + * + *

+ * These types of settings are read only through the REST API, * and cannot be modified by the end user. + *

*/ public record OperatorMetadata( String namespace, @@ -39,10 +46,18 @@ public record OperatorMetadata( Map handlers, OperatorErrorMetadata errorMetadata ) implements SimpleDiffable, ToXContentFragment { + + private static final ParseField VERSION = new ParseField("version"); + private static final ParseField HANDLERS = new ParseField("handlers"); + private static final ParseField ERRORS_METADATA = new ParseField("errors"); + /** * OperatorMetadata contains information about settings set in operator mode. + * + *

* These settings cannot be updated by the end user and are set outside of the * REST layer, e.g. through file based settings or by plugin/modules. + *

* * @param namespace The namespace of the setting creator, e.g. file_settings, security plugin, etc. * @param version The update version, must increase with each update @@ -53,7 +68,10 @@ public record OperatorMetadata( /** * Creates a set intersection between cluster state keys set by a given operator handler and the input set. + * + *

* This method is to be used to check if a REST action handler is allowed to modify certain cluster state. + *

* * @param handlerName the name of the operator handler we need to check for keys * @param modified a set of keys we want to see if we can modify. @@ -72,6 +90,7 @@ public Set conflicts(String handlerName, Set modified) { /** * Reads an {@link OperatorMetadata} from a {@link StreamInput} + * * @param in the {@link StreamInput} to read from * @return {@link OperatorMetadata} * @throws IOException @@ -99,6 +118,7 @@ public void writeTo(StreamOutput out) throws IOException { /** * Reads an {@link OperatorMetadata} {@link Diff} from {@link StreamInput} + * * @param in the {@link StreamInput} to read the diff from * @return a {@link Diff} of {@link OperatorMetadata} * @throws IOException @@ -141,29 +161,54 @@ public static Builder builder(String namespace, OperatorMetadata metadata) { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - Builder.toXContent(this, builder, params); + builder.startObject(namespace()); + builder.field(VERSION.getPreferredName(), version); + builder.startObject(HANDLERS.getPreferredName()); + var sortedKeys = new TreeSet<>(handlers.keySet()); + for (var key : sortedKeys) { + handlers.get(key).toXContent(builder, params); + } + builder.endObject(); + builder.field(ERRORS_METADATA.getPreferredName(), errorMetadata); + builder.endObject(); return builder; } + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "operator_metadata", + false, + (a, namespace) -> { + Map handlers = new HashMap<>(); + @SuppressWarnings("unchecked") + List handlersList = (List) a[1]; + handlersList.forEach(h -> handlers.put(h.name(), h)); + + return new OperatorMetadata(namespace, (Long) a[0], Map.copyOf(handlers), (OperatorErrorMetadata) a[2]); + } + ); + + static { + PARSER.declareLong(constructorArg(), VERSION); + PARSER.declareNamedObjects(optionalConstructorArg(), (p, c, name) -> OperatorHandlerMetadata.fromXContent(p, name), HANDLERS); + PARSER.declareObjectOrNull(optionalConstructorArg(), (p, c) -> OperatorErrorMetadata.fromXContent(p), null, ERRORS_METADATA); + } + /** * Reads {@link OperatorMetadata} from {@link XContentParser} + * * @param parser {@link XContentParser} * @return {@link OperatorMetadata} * @throws IOException */ public static OperatorMetadata fromXContent(final XContentParser parser) throws IOException { parser.nextToken(); - return Builder.fromXContent(parser, parser.currentName()); + return PARSER.apply(parser, parser.currentName()); } /** * Builder class for {@link OperatorMetadata} */ public static class Builder { - private static final String VERSION = "version"; - private static final String HANDLERS = "handlers"; - private static final String ERRORS_METADATA = "errors"; - private final String namespace; private Long version; private Map handlers; @@ -197,8 +242,12 @@ public Builder(String namespace, OperatorMetadata metadata) { } /** - * Stores the version for the operator metadata. Each new cluster state update in operator + * Stores the version for the operator metadata. + * + *

+ * Each new cluster state update in operator * mode requires a version bump. The version increase doesn't have to be monotonic. + *

* @param version the new operator metadata version * @return {@link Builder} */ @@ -220,9 +269,12 @@ public Builder errorMetadata(OperatorErrorMetadata errorMetadata) { } /** - * Adds an {@link OperatorHandlerMetadata} for this {@link OperatorMetadata}. The handler - * metadata is stored in a map, keyed off the {@link OperatorHandlerMetadata} name. Previously + * Adds an {@link OperatorHandlerMetadata} for this {@link OperatorMetadata}. + * + *

+ * The handler metadata is stored in a map, keyed off the {@link OperatorHandlerMetadata} name. Previously * stored {@link OperatorHandlerMetadata} for a given name is overwritten. + *

* * @param handler {@link OperatorHandlerMetadata} * @return {@link Builder} @@ -234,78 +286,11 @@ public Builder putHandler(OperatorHandlerMetadata handler) { /** * Builds an {@link OperatorMetadata} from this builder. + * * @return {@link OperatorMetadata} */ public OperatorMetadata build() { return new OperatorMetadata(namespace, version, Collections.unmodifiableMap(handlers), errorMetadata); } - - /** - * Serializes the operator metadata to xContent - * - * @param operatorMetadata {@link OperatorMetadata} - * @param builder {@link XContentBuilder} - * @param params {@link org.elasticsearch.xcontent.ToXContent.Params} - */ - public static void toXContent(OperatorMetadata operatorMetadata, XContentBuilder builder, ToXContent.Params params) - throws IOException { - builder.startObject(operatorMetadata.namespace()); - builder.field(VERSION, operatorMetadata.version()); - builder.startObject(HANDLERS); - var sortedKeys = new TreeSet<>(operatorMetadata.handlers.keySet()); - for (var key : sortedKeys) { - OperatorHandlerMetadata.Builder.toXContent(operatorMetadata.handlers.get(key), builder); - } - builder.endObject(); - builder.field(ERRORS_METADATA, operatorMetadata.errorMetadata); - builder.endObject(); - } - - /** - * Reads the operator metadata from xContent - * - * @param parser {@link XContentParser} - * @param namespace the operator metadata namespace. See {@link Metadata} operatorMetadata. - * @return {@link OperatorMetadata} - * @throws IOException - */ - public static OperatorMetadata fromXContent(XContentParser parser, String namespace) throws IOException { - OperatorMetadata.Builder builder = new OperatorMetadata.Builder(namespace); - - String currentFieldName = skipNamespace(parser); - XContentParser.Token token; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.START_OBJECT) { - if (HANDLERS.equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - builder.putHandler(OperatorHandlerMetadata.Builder.fromXContent(parser)); - } - } else if (ERRORS_METADATA.equals(currentFieldName)) { - builder.errorMetadata(OperatorErrorMetadata.Builder.fromXContent(parser)); - } else { - throw new ElasticsearchParseException("unknown key [{}] for operator metadata", currentFieldName); - } - } else if (token.isValue()) { - if (VERSION.equals(currentFieldName)) { - builder.version(parser.longValue()); - } - } - } - return builder.build(); - } - - private static String skipNamespace(XContentParser parser) throws IOException { - XContentParser.Token token = parser.nextToken(); - if (token == XContentParser.Token.START_OBJECT) { - token = parser.nextToken(); - if (token == XContentParser.Token.FIELD_NAME) { - return parser.currentName(); - } - } - - return null; - } } } diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java index 239282bfe69e2..9e02e2acb78ea 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java @@ -28,23 +28,37 @@ */ public class OperatorMetadataTests extends ESTestCase { - public void testEquals() { - final OperatorMetadata meta = createRandom(); + private void equalsTest(boolean addHandlers, boolean addErrors) { + final OperatorMetadata meta = createRandom(addHandlers, addErrors); assertThat(meta, equalTo(OperatorMetadata.builder(meta.namespace(), meta).build())); final OperatorMetadata.Builder newMeta = OperatorMetadata.builder(meta.namespace(), meta); newMeta.putHandler(new OperatorHandlerMetadata("1", Collections.emptySet())); assertThat(newMeta.build(), not(meta)); } - public void testSerialization() throws IOException { - final OperatorMetadata meta = createRandom(); + public void testEquals() { + equalsTest(true, true); + equalsTest(true, false); + equalsTest(false, true); + equalsTest(false, false); + } + + private void serializationTest(boolean addHandlers, boolean addErrors) throws IOException { + final OperatorMetadata meta = createRandom(addHandlers, addErrors); final BytesStreamOutput out = new BytesStreamOutput(); meta.writeTo(out); assertThat(OperatorMetadata.readFrom(out.bytes().streamInput()), equalTo(meta)); } - public void testXContent() throws IOException { - final OperatorMetadata meta = createRandom(); + public void testSerialization() throws IOException { + serializationTest(true, true); + serializationTest(true, false); + serializationTest(false, true); + serializationTest(false, false); + } + + private void xContentTest(boolean addHandlers, boolean addErrors) throws IOException { + final OperatorMetadata meta = createRandom(addHandlers, addErrors); final XContentBuilder builder = JsonXContent.contentBuilder(); builder.startObject(); meta.toXContent(builder, ToXContent.EMPTY_PARAMS); @@ -54,30 +68,40 @@ public void testXContent() throws IOException { assertThat(OperatorMetadata.fromXContent(parser), equalTo(meta)); } - private static OperatorMetadata createRandom() { + public void testXContent() throws IOException { + xContentTest(true, true); + xContentTest(false, true); + xContentTest(true, false); + xContentTest(false, false); + } + + private static OperatorMetadata createRandom(boolean addHandlers, boolean addErrors) { List handlers = randomList( 0, 10, - () -> new OperatorHandlerMetadata.Builder(randomAlphaOfLength(5)).keys(randomSet(1, 5, () -> randomAlphaOfLength(6))).build() + () -> new OperatorHandlerMetadata(randomAlphaOfLength(5), randomSet(1, 5, () -> randomAlphaOfLength(6))) ); List errors = randomList( 0, 10, - () -> new OperatorErrorMetadata.Builder().version(1L) - .errorKind(randomFrom(OperatorErrorMetadata.ErrorKind.values())) - .errors(randomList(1, 5, () -> randomAlphaOfLength(10))) - .build() + () -> new OperatorErrorMetadata( + 1L, + randomFrom(OperatorErrorMetadata.ErrorKind.values()), + randomList(1, 5, () -> randomAlphaOfLength(10)) + ) ); OperatorMetadata.Builder builder = OperatorMetadata.builder(randomAlphaOfLength(7)); - - for (var handlerMeta : handlers) { - builder.putHandler(handlerMeta); + if (addHandlers) { + for (var handlerMeta : handlers) { + builder.putHandler(handlerMeta); + } } - - for (var errorMeta : errors) { - builder.errorMetadata(errorMeta); + if (addErrors) { + for (var errorMeta : errors) { + builder.errorMetadata(errorMeta); + } } return builder.build(); diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java index c42a89f690c57..0c29e13389c6c 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java @@ -46,13 +46,14 @@ public void testSimpleJsonFromAndTo() throws IOException { IndexMetadata idx1 = createFirstBackingIndex("data-stream1").build(); IndexMetadata idx2 = createFirstBackingIndex("data-stream2").build(); - OperatorHandlerMetadata hmOne = new OperatorHandlerMetadata.Builder("one").keys(Set.of("a", "b")).build(); - OperatorHandlerMetadata hmTwo = new OperatorHandlerMetadata.Builder("two").keys(Set.of("c", "d")).build(); + OperatorHandlerMetadata hmOne = new OperatorHandlerMetadata("one", Set.of("a", "b")); + OperatorHandlerMetadata hmTwo = new OperatorHandlerMetadata("two", Set.of("c", "d")); - OperatorErrorMetadata emOne = new OperatorErrorMetadata.Builder().version(1L) - .errorKind(OperatorErrorMetadata.ErrorKind.VALIDATION) - .errors(List.of("Test error 1", "Test error 2")) - .build(); + OperatorErrorMetadata emOne = new OperatorErrorMetadata( + 1L, + OperatorErrorMetadata.ErrorKind.VALIDATION, + List.of("Test error 1", "Test error 2") + ); OperatorMetadata operatorMetadata = OperatorMetadata.builder("namespace_one") .errorMetadata(emOne) @@ -202,8 +203,8 @@ public void testSimpleJsonFromAndTo() throws IOException { assertThat(parsedMetadata.dataStreams().get("data-stream2").getIndices(), contains(idx2.getIndex())); // operator metadata - assertEquals(operatorMetadata, parsedMetadata.operatorMetadata(operatorMetadata.namespace())); - assertEquals(operatorMetadata1, parsedMetadata.operatorMetadata(operatorMetadata1.namespace())); + assertEquals(operatorMetadata, parsedMetadata.operatorMetadata().get(operatorMetadata.namespace())); + assertEquals(operatorMetadata1, parsedMetadata.operatorMetadata().get(operatorMetadata1.namespace())); } private static final String MAPPING_SOURCE1 = """ @@ -660,19 +661,21 @@ public void testToXContentAPIOperatorMetadata() throws IOException { Metadata metadata = buildMetadata(); - OperatorHandlerMetadata hmOne = new OperatorHandlerMetadata.Builder("one").keys(Set.of("a", "b")).build(); - OperatorHandlerMetadata hmTwo = new OperatorHandlerMetadata.Builder("two").keys(Set.of("c", "d")).build(); - OperatorHandlerMetadata hmThree = new OperatorHandlerMetadata.Builder("three").keys(Set.of("e", "f")).build(); + OperatorHandlerMetadata hmOne = new OperatorHandlerMetadata("one", Set.of("a", "b")); + OperatorHandlerMetadata hmTwo = new OperatorHandlerMetadata("two", Set.of("c", "d")); + OperatorHandlerMetadata hmThree = new OperatorHandlerMetadata("three", Set.of("e", "f")); - OperatorErrorMetadata emOne = new OperatorErrorMetadata.Builder().version(1L) - .errorKind(OperatorErrorMetadata.ErrorKind.VALIDATION) - .errors(List.of("Test error 1", "Test error 2")) - .build(); + OperatorErrorMetadata emOne = new OperatorErrorMetadata( + 1L, + OperatorErrorMetadata.ErrorKind.VALIDATION, + List.of("Test error 1", "Test error 2") + ); - OperatorErrorMetadata emTwo = new OperatorErrorMetadata.Builder().version(2L) - .errorKind(OperatorErrorMetadata.ErrorKind.TRANSIENT) - .errors(List.of("Test error 3", "Test error 4")) - .build(); + OperatorErrorMetadata emTwo = new OperatorErrorMetadata( + 2L, + OperatorErrorMetadata.ErrorKind.TRANSIENT, + List.of("Test error 3", "Test error 4") + ); OperatorMetadata omOne = OperatorMetadata.builder("namespace_one").errorMetadata(emOne).putHandler(hmOne).putHandler(hmTwo).build(); From 9b3bf1825fba59ddc72b96debc393aa2bda05283 Mon Sep 17 00:00:00 2001 From: Nikola Grcevski Date: Mon, 27 Jun 2022 15:13:59 -0400 Subject: [PATCH 4/7] Remove closing paragraph --- .../cluster/metadata/OperatorErrorMetadata.java | 1 - .../cluster/metadata/OperatorHandlerMetadata.java | 1 - .../elasticsearch/cluster/metadata/OperatorMetadata.java | 6 +----- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java index abbe55460502b..655a59e03982c 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java @@ -28,7 +28,6 @@ * while applying a cluster state update for a given namespace. *

* This information is held by the {@link OperatorMetadata} class. - *

*/ public record OperatorErrorMetadata(Long version, ErrorKind errorKind, List errors) implements diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java index 4fbeea7a0bac2..2de306597857c 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java @@ -31,7 +31,6 @@ *

* Since we hold operator metadata state for multiple namespaces, the same handler can appear in * multiple namespaces. See {@link OperatorMetadata} and {@link Metadata}. - *

*/ public record OperatorHandlerMetadata(String name, Set keys) implements diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java index 3fe3264864833..0376d260be92f 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java @@ -38,7 +38,6 @@ *

* These types of settings are read only through the REST API, * and cannot be modified by the end user. - *

*/ public record OperatorMetadata( String namespace, @@ -57,7 +56,6 @@ public record OperatorMetadata( *

* These settings cannot be updated by the end user and are set outside of the * REST layer, e.g. through file based settings or by plugin/modules. - *

* * @param namespace The namespace of the setting creator, e.g. file_settings, security plugin, etc. * @param version The update version, must increase with each update @@ -71,7 +69,6 @@ public record OperatorMetadata( * *

* This method is to be used to check if a REST action handler is allowed to modify certain cluster state. - *

* * @param handlerName the name of the operator handler we need to check for keys * @param modified a set of keys we want to see if we can modify. @@ -247,7 +244,7 @@ public Builder(String namespace, OperatorMetadata metadata) { *

* Each new cluster state update in operator * mode requires a version bump. The version increase doesn't have to be monotonic. - *

+ * * @param version the new operator metadata version * @return {@link Builder} */ @@ -274,7 +271,6 @@ public Builder errorMetadata(OperatorErrorMetadata errorMetadata) { *

* The handler metadata is stored in a map, keyed off the {@link OperatorHandlerMetadata} name. Previously * stored {@link OperatorHandlerMetadata} for a given name is overwritten. - *

* * @param handler {@link OperatorHandlerMetadata} * @return {@link Builder} From 6be97a98063a9e383c595bc8319313eb166f2609 Mon Sep 17 00:00:00 2001 From: Nikola Grcevski Date: Mon, 27 Jun 2022 15:21:16 -0400 Subject: [PATCH 5/7] Fix compile error --- .../main/java/org/elasticsearch/cluster/metadata/Metadata.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java index 2aaa3ceb38aa2..e7cd7f80e3d71 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java @@ -392,7 +392,8 @@ public Metadata withCoordinationMetadata(CoordinationMetadata coordinationMetada visibleClosedIndices, indicesLookup, mappingsByHash, - oldestIndexVersion + oldestIndexVersion, + operatorMetadata ); } From 4bea90669da78bfcc2042d724e870e82f8db2e1f Mon Sep 17 00:00:00 2001 From: Nikola Grcevski Date: Wed, 29 Jun 2022 13:10:23 -0400 Subject: [PATCH 6/7] Implement PR feedback --- server/src/main/java/module-info.java | 2 +- ....java => ImmutableStateErrorMetadata.java} | 42 +++--- ...ava => ImmutableStateHandlerMetadata.java} | 42 +++--- ...adata.java => ImmutableStateMetadata.java} | 139 +++++++++--------- .../cluster/metadata/Metadata.java | 88 ++++++----- .../ImmutableClusterStateHandler.java} | 38 ++--- .../ImmutableClusterStateHandlerProvider.java | 25 ++++ .../TransformState.java | 6 +- .../operator/OperatorHandlerProvider.java | 25 ---- ....java => ImmutableStateMetadataTests.java} | 34 ++--- .../metadata/ToAndFromJsonMetadataTests.java | 58 ++++---- .../ImmutableClusterStateHandlerTests.java} | 8 +- 12 files changed, 261 insertions(+), 246 deletions(-) rename server/src/main/java/org/elasticsearch/cluster/metadata/{OperatorErrorMetadata.java => ImmutableStateErrorMetadata.java} (71%) rename server/src/main/java/org/elasticsearch/cluster/metadata/{OperatorHandlerMetadata.java => ImmutableStateHandlerMetadata.java} (52%) rename server/src/main/java/org/elasticsearch/cluster/metadata/{OperatorMetadata.java => ImmutableStateMetadata.java} (58%) rename server/src/main/java/org/elasticsearch/{operator/OperatorHandler.java => immutablestate/ImmutableClusterStateHandler.java} (66%) create mode 100644 server/src/main/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandlerProvider.java rename server/src/main/java/org/elasticsearch/{operator => immutablestate}/TransformState.java (69%) delete mode 100644 server/src/main/java/org/elasticsearch/operator/OperatorHandlerProvider.java rename server/src/test/java/org/elasticsearch/cluster/metadata/{OperatorMetadataTests.java => ImmutableStateMetadataTests.java} (65%) rename server/src/test/java/org/elasticsearch/{operator/OperatorHandlerTests.java => immutablestate/ImmutableClusterStateHandlerTests.java} (92%) diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index a7fb33167dc23..d7bd5f2dd5058 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -275,7 +275,7 @@ exports org.elasticsearch.monitor.os; exports org.elasticsearch.monitor.process; exports org.elasticsearch.node; - exports org.elasticsearch.operator; + exports org.elasticsearch.immutablestate; exports org.elasticsearch.persistent; exports org.elasticsearch.persistent.decider; exports org.elasticsearch.plugins; diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/ImmutableStateErrorMetadata.java similarity index 71% rename from server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java rename to server/src/main/java/org/elasticsearch/cluster/metadata/ImmutableStateErrorMetadata.java index 655a59e03982c..bd96750565c7c 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorErrorMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/ImmutableStateErrorMetadata.java @@ -27,11 +27,11 @@ * A metadata class to hold error information about errors encountered * while applying a cluster state update for a given namespace. *

- * This information is held by the {@link OperatorMetadata} class. + * This information is held by the {@link ImmutableStateMetadata} class. */ -public record OperatorErrorMetadata(Long version, ErrorKind errorKind, List errors) +public record ImmutableStateErrorMetadata(Long version, ErrorKind errorKind, List errors) implements - SimpleDiffable, + SimpleDiffable, ToXContentFragment { static final ParseField ERRORS = new ParseField("errors"); @@ -39,13 +39,13 @@ public record OperatorErrorMetadata(Long version, ErrorKind errorKind, List PARSER = new ConstructingObjectParser<>( - "operator_error_metadata", - (a) -> new OperatorErrorMetadata((Long) a[0], ErrorKind.of((String) a[1]), (List) a[2]) + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "immutable_state_error_metadata", + (a) -> new ImmutableStateErrorMetadata((Long) a[0], ErrorKind.of((String) a[1]), (List) a[2]) ); static { @@ -88,28 +88,28 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } /** - * Reads an {@link OperatorErrorMetadata} from xContent + * Reads an {@link ImmutableStateErrorMetadata} from xContent * * @param parser {@link XContentParser} - * @return {@link OperatorErrorMetadata} + * @return {@link ImmutableStateErrorMetadata} */ - public static OperatorErrorMetadata fromXContent(final XContentParser parser) { + public static ImmutableStateErrorMetadata fromXContent(final XContentParser parser) { return PARSER.apply(parser, null); } /** - * Reads an {@link OperatorErrorMetadata} {@link Diff} from {@link StreamInput} + * Reads an {@link ImmutableStateErrorMetadata} {@link Diff} from {@link StreamInput} * * @param in the {@link StreamInput} to read the diff from - * @return a {@link Diff} of {@link OperatorErrorMetadata} + * @return a {@link Diff} of {@link ImmutableStateErrorMetadata} * @throws IOException */ - public static Diff readDiffFrom(StreamInput in) throws IOException { - return SimpleDiffable.readDiffFrom(OperatorErrorMetadata::readFrom, in); + public static Diff readDiffFrom(StreamInput in) throws IOException { + return SimpleDiffable.readDiffFrom(ImmutableStateErrorMetadata::readFrom, in); } /** - * Enum for kinds of errors we might encounter while processing operator cluster state updates. + * Enum for kinds of errors we might encounter while processing immutable cluster state updates. */ public enum ErrorKind { PARSING("parsing"), diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/ImmutableStateHandlerMetadata.java similarity index 52% rename from server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java rename to server/src/main/java/org/elasticsearch/cluster/metadata/ImmutableStateHandlerMetadata.java index 2de306597857c..4232b11ca2d2e 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorHandlerMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/ImmutableStateHandlerMetadata.java @@ -12,6 +12,7 @@ import org.elasticsearch.cluster.SimpleDiffable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.immutablestate.ImmutableClusterStateHandler; import org.elasticsearch.xcontent.ConstructingObjectParser; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.ToXContentFragment; @@ -21,20 +22,19 @@ import java.io.IOException; import java.util.List; import java.util.Set; -import java.util.TreeSet; import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg; /** - * Metadata class to hold the keys set in operator mode for each operator handler. + * Metadata class to hold a set of immutable keys in the cluster state, set by each {@link ImmutableClusterStateHandler}. * *

- * Since we hold operator metadata state for multiple namespaces, the same handler can appear in - * multiple namespaces. See {@link OperatorMetadata} and {@link Metadata}. + * Since we hold immutable metadata state for multiple namespaces, the same handler can appear in + * multiple namespaces. See {@link ImmutableStateMetadata} and {@link Metadata}. */ -public record OperatorHandlerMetadata(String name, Set keys) +public record ImmutableStateHandlerMetadata(String name, Set keys) implements - SimpleDiffable, + SimpleDiffable, ToXContentFragment { static final ParseField KEYS = new ParseField("keys"); @@ -46,29 +46,29 @@ public void writeTo(StreamOutput out) throws IOException { } /** - * Reads an {@link OperatorHandlerMetadata} from a {@link StreamInput} + * Reads an {@link ImmutableStateHandlerMetadata} from a {@link StreamInput} * * @param in the {@link StreamInput} to read from - * @return {@link OperatorHandlerMetadata} + * @return {@link ImmutableStateHandlerMetadata} * @throws IOException */ - public static OperatorHandlerMetadata readFrom(StreamInput in) throws IOException { - return new OperatorHandlerMetadata(in.readString(), in.readSet(StreamInput::readString)); + public static ImmutableStateHandlerMetadata readFrom(StreamInput in) throws IOException { + return new ImmutableStateHandlerMetadata(in.readString(), in.readSet(StreamInput::readString)); } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(name()); - builder.stringListField(KEYS.getPreferredName(), new TreeSet<>(keys())); // ordered set here to ensure output consistency + builder.stringListField(KEYS.getPreferredName(), keys().stream().sorted().toList()); // ordered keys for output consistency builder.endObject(); return builder; } @SuppressWarnings("unchecked") - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "operator_handler_metadata", + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "immutable_state_handler_metadata", false, - (a, name) -> new OperatorHandlerMetadata(name, Set.copyOf((List) a[0])) + (a, name) -> new ImmutableStateHandlerMetadata(name, Set.copyOf((List) a[0])) ); static { @@ -76,24 +76,24 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } /** - * Reads an {@link OperatorHandlerMetadata} from xContent + * Reads an {@link ImmutableStateHandlerMetadata} from xContent * * @param parser {@link XContentParser} - * @return {@link OperatorHandlerMetadata} + * @return {@link ImmutableStateHandlerMetadata} * @throws IOException */ - public static OperatorHandlerMetadata fromXContent(XContentParser parser, String name) throws IOException { + public static ImmutableStateHandlerMetadata fromXContent(XContentParser parser, String name) throws IOException { return PARSER.apply(parser, name); } /** - * Reads an {@link OperatorHandlerMetadata} {@link Diff} from {@link StreamInput} + * Reads an {@link ImmutableStateHandlerMetadata} {@link Diff} from {@link StreamInput} * * @param in the {@link StreamInput} to read the diff from - * @return a {@link Diff} of {@link OperatorHandlerMetadata} + * @return a {@link Diff} of {@link ImmutableStateHandlerMetadata} * @throws IOException */ - public static Diff readDiffFrom(StreamInput in) throws IOException { - return SimpleDiffable.readDiffFrom(OperatorHandlerMetadata::readFrom, in); + public static Diff readDiffFrom(StreamInput in) throws IOException { + return SimpleDiffable.readDiffFrom(ImmutableStateHandlerMetadata::readFrom, in); } } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/ImmutableStateMetadata.java similarity index 58% rename from server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java rename to server/src/main/java/org/elasticsearch/cluster/metadata/ImmutableStateMetadata.java index 0376d260be92f..f876cf1328c47 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/OperatorMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/ImmutableStateMetadata.java @@ -13,6 +13,7 @@ import org.elasticsearch.cluster.SimpleDiffable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.immutablestate.ImmutableClusterStateHandler; import org.elasticsearch.xcontent.ConstructingObjectParser; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.ToXContentFragment; @@ -26,32 +27,32 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg; import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg; /** - * Metadata class that contains information about cluster settings/entities set - * in an operator mode. + * Metadata class that contains information about immutable cluster state set + * through file based settings or by modules/plugins. * *

- * These types of settings are read only through the REST API, - * and cannot be modified by the end user. + * These types of cluster settings/entities can be read through the REST API, + * but can only be modified through a versioned 'operator mode' update, e.g. + * file based settings or module/plugin upgrade. */ -public record OperatorMetadata( +public record ImmutableStateMetadata( String namespace, Long version, - Map handlers, - OperatorErrorMetadata errorMetadata -) implements SimpleDiffable, ToXContentFragment { + Map handlers, + ImmutableStateErrorMetadata errorMetadata +) implements SimpleDiffable, ToXContentFragment { private static final ParseField VERSION = new ParseField("version"); private static final ParseField HANDLERS = new ParseField("handlers"); private static final ParseField ERRORS_METADATA = new ParseField("errors"); /** - * OperatorMetadata contains information about settings set in operator mode. + * ImmutableStateMetadata contains information about immutable cluster settings. * *

* These settings cannot be updated by the end user and are set outside of the @@ -62,20 +63,21 @@ public record OperatorMetadata( * @param handlers Per state update handler information on key set in by this update. These keys are validated at REST time. * @param errorMetadata If the update failed for some reason, this is where we store the error information metadata. */ - public OperatorMetadata {} + public ImmutableStateMetadata {} /** - * Creates a set intersection between cluster state keys set by a given operator handler and the input set. + * Creates a set intersection between cluster state keys set by a given {@link ImmutableClusterStateHandler} + * and the input set. * *

* This method is to be used to check if a REST action handler is allowed to modify certain cluster state. * - * @param handlerName the name of the operator handler we need to check for keys + * @param handlerName the name of the immutable state handler we need to check for keys * @param modified a set of keys we want to see if we can modify. * @return */ public Set conflicts(String handlerName, Set modified) { - OperatorHandlerMetadata handlerMetadata = handlers.get(handlerName); + ImmutableStateHandlerMetadata handlerMetadata = handlers.get(handlerName); if (handlerMetadata == null || handlerMetadata.keys().isEmpty()) { return Collections.emptySet(); } @@ -86,22 +88,22 @@ public Set conflicts(String handlerName, Set modified) { } /** - * Reads an {@link OperatorMetadata} from a {@link StreamInput} + * Reads an {@link ImmutableStateMetadata} from a {@link StreamInput} * * @param in the {@link StreamInput} to read from - * @return {@link OperatorMetadata} + * @return {@link ImmutableStateMetadata} * @throws IOException */ - public static OperatorMetadata readFrom(StreamInput in) throws IOException { + public static ImmutableStateMetadata readFrom(StreamInput in) throws IOException { Builder builder = new Builder(in.readString()).version(in.readLong()); int handlersSize = in.readVInt(); for (int i = 0; i < handlersSize; i++) { - OperatorHandlerMetadata handler = OperatorHandlerMetadata.readFrom(in); + ImmutableStateHandlerMetadata handler = ImmutableStateHandlerMetadata.readFrom(in); builder.putHandler(handler); } - builder.errorMetadata(in.readOptionalWriteable(OperatorErrorMetadata::readFrom)); + builder.errorMetadata(in.readOptionalWriteable(ImmutableStateErrorMetadata::readFrom)); return builder.build(); } @@ -114,31 +116,31 @@ public void writeTo(StreamOutput out) throws IOException { } /** - * Reads an {@link OperatorMetadata} {@link Diff} from {@link StreamInput} + * Reads an {@link ImmutableStateMetadata} {@link Diff} from {@link StreamInput} * * @param in the {@link StreamInput} to read the diff from - * @return a {@link Diff} of {@link OperatorMetadata} + * @return a {@link Diff} of {@link ImmutableStateMetadata} * @throws IOException */ - public static Diff readDiffFrom(StreamInput in) throws IOException { - return SimpleDiffable.readDiffFrom(OperatorMetadata::readFrom, in); + public static Diff readDiffFrom(StreamInput in) throws IOException { + return SimpleDiffable.readDiffFrom(ImmutableStateMetadata::readFrom, in); } /** * Empty {@link org.elasticsearch.cluster.DiffableUtils.MapDiff} helper for metadata backwards compatibility. */ - public static final DiffableUtils.MapDiff> EMPTY_DIFF = + public static final DiffableUtils.MapDiff> EMPTY_DIFF = new DiffableUtils.MapDiff<>(null, null, List.of(), List.of(), List.of()) { @Override - public Map apply(Map part) { + public Map apply(Map part) { return part; } }; /** - * Convenience method for creating a {@link Builder} for {@link OperatorMetadata} + * Convenience method for creating a {@link Builder} for {@link ImmutableStateMetadata} * - * @param namespace the namespace under which we'll store the {@link OperatorMetadata} + * @param namespace the namespace under which we'll store the {@link ImmutableStateMetadata} * @return {@link Builder} */ public static Builder builder(String namespace) { @@ -146,13 +148,13 @@ public static Builder builder(String namespace) { } /** - * Convenience method for creating a {@link Builder} for {@link OperatorMetadata} + * Convenience method for creating a {@link Builder} for {@link ImmutableStateMetadata} * - * @param namespace the namespace under which we'll store the {@link OperatorMetadata} - * @param metadata an existing {@link OperatorMetadata} + * @param namespace the namespace under which we'll store the {@link ImmutableStateMetadata} + * @param metadata an existing {@link ImmutableStateMetadata} * @return {@link Builder} */ - public static Builder builder(String namespace, OperatorMetadata metadata) { + public static Builder builder(String namespace, ImmutableStateMetadata metadata) { return new Builder(namespace, metadata); } @@ -161,9 +163,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.startObject(namespace()); builder.field(VERSION.getPreferredName(), version); builder.startObject(HANDLERS.getPreferredName()); - var sortedKeys = new TreeSet<>(handlers.keySet()); - for (var key : sortedKeys) { - handlers.get(key).toXContent(builder, params); + for (var i = handlers.entrySet().stream().sorted(Map.Entry.comparingByKey()).iterator(); i.hasNext();) { + i.next().getValue().toXContent(builder, params); } builder.endObject(); builder.field(ERRORS_METADATA.getPreferredName(), errorMetadata); @@ -171,50 +172,52 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "operator_metadata", + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "immutable_state_metadata", false, (a, namespace) -> { - Map handlers = new HashMap<>(); + Map handlers = new HashMap<>(); @SuppressWarnings("unchecked") - List handlersList = (List) a[1]; + List handlersList = (List) a[1]; handlersList.forEach(h -> handlers.put(h.name(), h)); - return new OperatorMetadata(namespace, (Long) a[0], Map.copyOf(handlers), (OperatorErrorMetadata) a[2]); + return new ImmutableStateMetadata(namespace, (Long) a[0], Map.copyOf(handlers), (ImmutableStateErrorMetadata) a[2]); } ); static { PARSER.declareLong(constructorArg(), VERSION); - PARSER.declareNamedObjects(optionalConstructorArg(), (p, c, name) -> OperatorHandlerMetadata.fromXContent(p, name), HANDLERS); - PARSER.declareObjectOrNull(optionalConstructorArg(), (p, c) -> OperatorErrorMetadata.fromXContent(p), null, ERRORS_METADATA); + PARSER.declareNamedObjects(optionalConstructorArg(), (p, c, name) -> ImmutableStateHandlerMetadata.fromXContent(p, name), HANDLERS); + PARSER.declareObjectOrNull(optionalConstructorArg(), (p, c) -> ImmutableStateErrorMetadata.fromXContent(p), null, ERRORS_METADATA); } /** - * Reads {@link OperatorMetadata} from {@link XContentParser} + * Reads {@link ImmutableStateMetadata} from {@link XContentParser} * * @param parser {@link XContentParser} - * @return {@link OperatorMetadata} + * @return {@link ImmutableStateMetadata} * @throws IOException */ - public static OperatorMetadata fromXContent(final XContentParser parser) throws IOException { + public static ImmutableStateMetadata fromXContent(final XContentParser parser) throws IOException { parser.nextToken(); return PARSER.apply(parser, parser.currentName()); } /** - * Builder class for {@link OperatorMetadata} + * Builder class for {@link ImmutableStateMetadata} */ public static class Builder { private final String namespace; private Long version; - private Map handlers; - OperatorErrorMetadata errorMetadata; + private Map handlers; + ImmutableStateErrorMetadata errorMetadata; /** - * Empty builder for OperatorMetadata. The operator metadata namespace is a required parameter + * Empty builder for ImmutableStateMetadata. + *

+ * The immutable metadata namespace is a required parameter * - * @param namespace The namespace for this metadata + * @param namespace The namespace for this immutable metadata */ public Builder(String namespace) { this.namespace = namespace; @@ -224,12 +227,12 @@ public Builder(String namespace) { } /** - * Creates an operator metadata builder + * Creates an immutable state metadata builder * * @param namespace the namespace for which we are storing metadata, e.g. file_settings * @param metadata the previous metadata */ - public Builder(String namespace, OperatorMetadata metadata) { + public Builder(String namespace, ImmutableStateMetadata metadata) { this(namespace); if (metadata != null) { this.version = metadata.version; @@ -239,13 +242,13 @@ public Builder(String namespace, OperatorMetadata metadata) { } /** - * Stores the version for the operator metadata. + * Stores the version for the immutable state metadata. * *

- * Each new cluster state update in operator - * mode requires a version bump. The version increase doesn't have to be monotonic. + * Each new immutable cluster state update mode requires a version bump. + * The version increase doesn't have to be monotonic. * - * @param version the new operator metadata version + * @param version the new immutable state metadata version * @return {@link Builder} */ public Builder version(Long version) { @@ -254,39 +257,39 @@ public Builder version(Long version) { } /** - * Adds {@link OperatorErrorMetadata} if we need to store error information about certain - * operator state processing. + * Adds {@link ImmutableStateErrorMetadata} if we need to store error information about certain + * immutable state processing. * - * @param errorMetadata {@link OperatorErrorMetadata} + * @param errorMetadata {@link ImmutableStateErrorMetadata} * @return {@link Builder} */ - public Builder errorMetadata(OperatorErrorMetadata errorMetadata) { + public Builder errorMetadata(ImmutableStateErrorMetadata errorMetadata) { this.errorMetadata = errorMetadata; return this; } /** - * Adds an {@link OperatorHandlerMetadata} for this {@link OperatorMetadata}. + * Adds an {@link ImmutableStateHandlerMetadata} for this {@link ImmutableStateMetadata}. * *

- * The handler metadata is stored in a map, keyed off the {@link OperatorHandlerMetadata} name. Previously - * stored {@link OperatorHandlerMetadata} for a given name is overwritten. + * The handler metadata is stored in a map, keyed off the {@link ImmutableStateHandlerMetadata} name. Previously + * stored {@link ImmutableStateHandlerMetadata} for a given name is overwritten. * - * @param handler {@link OperatorHandlerMetadata} + * @param handler {@link ImmutableStateHandlerMetadata} * @return {@link Builder} */ - public Builder putHandler(OperatorHandlerMetadata handler) { + public Builder putHandler(ImmutableStateHandlerMetadata handler) { this.handlers.put(handler.name(), handler); return this; } /** - * Builds an {@link OperatorMetadata} from this builder. + * Builds an {@link ImmutableStateMetadata} from this builder. * - * @return {@link OperatorMetadata} + * @return {@link ImmutableStateMetadata} */ - public OperatorMetadata build() { - return new OperatorMetadata(namespace, version, Collections.unmodifiableMap(handlers), errorMetadata); + public ImmutableStateMetadata build() { + return new ImmutableStateMetadata(namespace, version, Collections.unmodifiableMap(handlers), errorMetadata); } } } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java index e7cd7f80e3d71..e3b562c0e2b47 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java @@ -209,7 +209,7 @@ default boolean isRestorable() { private final ImmutableOpenMap> aliasedIndices; private final ImmutableOpenMap templates; private final ImmutableOpenMap customs; - private final Map operatorMetadata; + private final Map immutableStateMetadata; private final transient int totalNumberOfShards; // Transient ? not serializable anyway? private final int totalOpenIndexShards; @@ -250,7 +250,7 @@ private Metadata( SortedMap indicesLookup, Map mappingsByHash, Version oldestIndexVersion, - Map operatorMetadata + Map immutableStateMetadata ) { this.clusterUUID = clusterUUID; this.clusterUUIDCommitted = clusterUUIDCommitted; @@ -275,7 +275,7 @@ private Metadata( this.indicesLookup = indicesLookup; this.mappingsByHash = mappingsByHash; this.oldestIndexVersion = oldestIndexVersion; - this.operatorMetadata = operatorMetadata; + this.immutableStateMetadata = immutableStateMetadata; } public Metadata withIncrementedVersion() { @@ -303,7 +303,7 @@ public Metadata withIncrementedVersion() { indicesLookup, mappingsByHash, oldestIndexVersion, - operatorMetadata + immutableStateMetadata ); } @@ -364,7 +364,7 @@ public Metadata withLifecycleState(final Index index, final LifecycleExecutionSt indicesLookup, mappingsByHash, oldestIndexVersion, - operatorMetadata + immutableStateMetadata ); } @@ -393,7 +393,7 @@ public Metadata withCoordinationMetadata(CoordinationMetadata coordinationMetada indicesLookup, mappingsByHash, oldestIndexVersion, - operatorMetadata + immutableStateMetadata ); } @@ -977,12 +977,12 @@ public Map customs() { } /** - * Returns the full {@link OperatorMetadata} Map for all - * operator namespaces. - * @return a map of namespace to {@link OperatorMetadata} + * Returns the full {@link ImmutableStateMetadata} Map for all + * immutable state namespaces. + * @return a map of namespace to {@link ImmutableStateMetadata} */ - public Map operatorMetadata() { - return this.operatorMetadata; + public Map immutableStateMetadata() { + return this.immutableStateMetadata; } /** @@ -1126,7 +1126,7 @@ private static class MetadataDiff implements Diff { private final Diff> indices; private final Diff> templates; private final Diff> customs; - private final Diff> operatorMetadata; + private final Diff> immutableStateMetadata; MetadataDiff(Metadata before, Metadata after) { clusterUUID = after.clusterUUID; @@ -1139,15 +1139,19 @@ private static class MetadataDiff implements Diff { indices = DiffableUtils.diff(before.indices, after.indices, DiffableUtils.getStringKeySerializer()); templates = DiffableUtils.diff(before.templates, after.templates, DiffableUtils.getStringKeySerializer()); customs = DiffableUtils.diff(before.customs, after.customs, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); - operatorMetadata = DiffableUtils.diff(before.operatorMetadata, after.operatorMetadata, DiffableUtils.getStringKeySerializer()); + immutableStateMetadata = DiffableUtils.diff( + before.immutableStateMetadata, + after.immutableStateMetadata, + DiffableUtils.getStringKeySerializer() + ); } private static final DiffableUtils.DiffableValueReader INDEX_METADATA_DIFF_VALUE_READER = new DiffableUtils.DiffableValueReader<>(IndexMetadata::readFrom, IndexMetadata::readDiffFrom); private static final DiffableUtils.DiffableValueReader TEMPLATES_DIFF_VALUE_READER = new DiffableUtils.DiffableValueReader<>(IndexTemplateMetadata::readFrom, IndexTemplateMetadata::readDiffFrom); - private static final DiffableUtils.DiffableValueReader OPERATOR_DIFF_VALUE_READER = - new DiffableUtils.DiffableValueReader<>(OperatorMetadata::readFrom, OperatorMetadata::readDiffFrom); + private static final DiffableUtils.DiffableValueReader IMMUTABLE_DIFF_VALUE_READER = + new DiffableUtils.DiffableValueReader<>(ImmutableStateMetadata::readFrom, ImmutableStateMetadata::readDiffFrom); MetadataDiff(StreamInput in) throws IOException { clusterUUID = in.readString(); @@ -1165,9 +1169,13 @@ private static class MetadataDiff implements Diff { templates = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), TEMPLATES_DIFF_VALUE_READER); customs = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); if (in.getVersion().onOrAfter(Version.V_8_4_0)) { - operatorMetadata = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), OPERATOR_DIFF_VALUE_READER); + immutableStateMetadata = DiffableUtils.readJdkMapDiff( + in, + DiffableUtils.getStringKeySerializer(), + IMMUTABLE_DIFF_VALUE_READER + ); } else { - operatorMetadata = OperatorMetadata.EMPTY_DIFF; + immutableStateMetadata = ImmutableStateMetadata.EMPTY_DIFF; } } @@ -1186,7 +1194,7 @@ public void writeTo(StreamOutput out) throws IOException { templates.writeTo(out); customs.writeTo(out); if (out.getVersion().onOrAfter(Version.V_8_4_0)) { - operatorMetadata.writeTo(out); + immutableStateMetadata.writeTo(out); } } @@ -1205,7 +1213,7 @@ public Metadata apply(Metadata part) { builder.indices(indices.apply(part.indices)); builder.templates(templates.apply(part.templates)); builder.customs(customs.apply(part.customs)); - builder.put(Collections.unmodifiableMap(operatorMetadata.apply(part.operatorMetadata))); + builder.put(Collections.unmodifiableMap(immutableStateMetadata.apply(part.immutableStateMetadata))); return builder.build(); } } @@ -1248,9 +1256,9 @@ public static Metadata readFrom(StreamInput in) throws IOException { builder.putCustom(customIndexMetadata.getWriteableName(), customIndexMetadata); } if (in.getVersion().onOrAfter(Version.V_8_4_0)) { - int operatorSize = in.readVInt(); - for (int i = 0; i < operatorSize; i++) { - builder.put(OperatorMetadata.readFrom(in)); + int immutableStateSize = in.readVInt(); + for (int i = 0; i < immutableStateSize; i++) { + builder.put(ImmutableStateMetadata.readFrom(in)); } } return builder.build(); @@ -1280,7 +1288,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeCollection(templates.values()); VersionedNamedWriteable.writeVersionedWritables(out, customs); if (out.getVersion().onOrAfter(Version.V_8_4_0)) { - out.writeCollection(operatorMetadata.values()); + out.writeCollection(immutableStateMetadata.values()); } } @@ -1316,7 +1324,7 @@ public static class Builder { private SortedMap previousIndicesLookup; - private final Map operatorMetadata; + private final Map immutableStateMetadata; // If this is set to false we can skip checking #mappingsByHash for unused entries in #build(). Used as an optimization to save // the rather expensive call to #purgeUnusedEntries when building from another instance and we know that no mappings can have @@ -1345,7 +1353,7 @@ public Builder() { this.previousIndicesLookup = metadata.indicesLookup; this.mappingsByHash = new HashMap<>(metadata.mappingsByHash); this.checkForUnusedMappings = false; - this.operatorMetadata = new HashMap<>(metadata.operatorMetadata); + this.immutableStateMetadata = new HashMap<>(metadata.immutableStateMetadata); } private Builder(Map mappingsByHash) { @@ -1354,7 +1362,7 @@ private Builder(Map mappingsByHash) { aliasedIndices = ImmutableOpenMap.builder(); templates = ImmutableOpenMap.builder(); customs = ImmutableOpenMap.builder(); - operatorMetadata = new HashMap<>(); + immutableStateMetadata = new HashMap<>(); indexGraveyard(IndexGraveyard.builder().build()); // create new empty index graveyard to initialize previousIndicesLookup = null; this.mappingsByHash = new HashMap<>(mappingsByHash); @@ -1708,22 +1716,22 @@ public Builder customs(Map customs) { } /** - * Adds a map of namespace to {@link OperatorMetadata} into the metadata builder - * @param operatorMetadata a map of namespace to {@link OperatorMetadata} + * Adds a map of namespace to {@link ImmutableStateMetadata} into the metadata builder + * @param immutableStateMetadata a map of namespace to {@link ImmutableStateMetadata} * @return {@link Builder} */ - public Builder put(Map operatorMetadata) { - this.operatorMetadata.putAll(operatorMetadata); + public Builder put(Map immutableStateMetadata) { + this.immutableStateMetadata.putAll(immutableStateMetadata); return this; } /** - * Adds a {@link OperatorMetadata} for a given namespace to the metadata builder - * @param metadata an {@link OperatorMetadata} + * Adds a {@link ImmutableStateMetadata} for a given namespace to the metadata builder + * @param metadata an {@link ImmutableStateMetadata} * @return {@link Builder} */ - public Builder put(OperatorMetadata metadata) { - operatorMetadata.put(metadata.namespace(), metadata); + public Builder put(ImmutableStateMetadata metadata) { + immutableStateMetadata.put(metadata.namespace(), metadata); return this; } @@ -1929,7 +1937,7 @@ public Metadata build() { indicesLookup, Collections.unmodifiableMap(mappingsByHash), Version.fromId(oldestIndexVersionId), - Collections.unmodifiableMap(operatorMetadata) + Collections.unmodifiableMap(immutableStateMetadata) ); } @@ -2228,9 +2236,9 @@ public static void toXContent(Metadata metadata, XContentBuilder builder, ToXCon } } - builder.startObject("operator"); - for (OperatorMetadata operatorMetadata : metadata.operatorMetadata().values()) { - operatorMetadata.toXContent(builder, params); + builder.startObject("immutable_state"); + for (ImmutableStateMetadata immutableStateMetadata : metadata.immutableStateMetadata().values()) { + immutableStateMetadata.toXContent(builder, params); } builder.endObject(); @@ -2277,9 +2285,9 @@ public static Metadata fromXContent(XContentParser parser) throws IOException { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { builder.put(IndexTemplateMetadata.Builder.fromXContent(parser, parser.currentName())); } - } else if ("operator".equals(currentFieldName)) { + } else if ("immutable_state".equals(currentFieldName)) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - builder.put(OperatorMetadata.fromXContent(parser)); + builder.put(ImmutableStateMetadata.fromXContent(parser)); } } else { try { diff --git a/server/src/main/java/org/elasticsearch/operator/OperatorHandler.java b/server/src/main/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandler.java similarity index 66% rename from server/src/main/java/org/elasticsearch/operator/OperatorHandler.java rename to server/src/main/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandler.java index 216b99858e5c8..f8428a9423c51 100644 --- a/server/src/main/java/org/elasticsearch/operator/OperatorHandler.java +++ b/server/src/main/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandler.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.operator; +package org.elasticsearch.immutablestate; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.MasterNodeRequest; @@ -15,29 +15,29 @@ import java.util.Collections; /** - * OperatorHandler base interface used for implementing operator state actions. + * Base interface used for implementing 'operator mode' cluster state updates. * *

- * Updating cluster state in operator mode, for file based settings and modules/plugins, requires - * that we have a separate update handler interface that is different than REST handlers. This interface declares - * the basic contract for implementing cluster state update handlers in operator mode. + * Updating cluster state in immutable mode, for file based settings and modules/plugins, requires + * that we have a separate update handler interface that is different than the REST handlers. This interface class declares + * the basic contract for implementing cluster state update handlers that result in an immutable cluster state. *

*/ -public interface OperatorHandler { +public interface ImmutableClusterStateHandler { String CONTENT = "content"; /** * Unique identifier for the handler. * *

- * The operator handler name is a unique identifier that is matched to a section in a - * cluster state update content. The operator cluster state updates are done as a single + * The handler name is a unique identifier that is matched to a section in a + * cluster state update content. The immutable cluster state updates are done as a single * cluster state update and the cluster state is typically supplied as a combined content, * unlike the REST handlers. This name must match a desired content key name in the combined * cluster state update, e.g. "ilm" or "cluster_settings" (for persistent cluster settings update). *

* - * @return a String with the operator key name, e.g "ilm". + * @return a String with the handler name, e.g "ilm". */ String name(); @@ -45,9 +45,9 @@ public interface OperatorHandler { * The transformation method implemented by the handler. * *

- * The transform method of the operator handler should apply the necessary changes to + * The transform method of the handler should apply the necessary changes to * the cluster state as it normally would in a REST handler. One difference is that the - * transform method in an operator handler must perform all CRUD operations of the cluster + * transform method in an immutable state handler must perform all CRUD operations of the cluster * state in one go. For that reason, we supply a wrapper class to the cluster state called * {@link TransformState}, which contains the current cluster state as well as any previous keys * set by this handler on prior invocation. @@ -65,14 +65,14 @@ public interface OperatorHandler { * *

* Sometimes certain parts of the cluster state cannot be created/updated without previously - * setting other cluster state components, e.g. composable templates. Since the cluster state handlers - * are processed in random order by the OperatorClusterStateController, this method gives an opportunity - * to any operator handler to declare other operator handlers it depends on. Given dependencies exist, - * the OperatorClusterStateController will order those handlers such that the handlers that are dependent + * setting other cluster state components, e.g. composable templates. Since the immutable cluster state handlers + * are processed in random order by the ImmutableClusterStateController, this method gives an opportunity + * to any immutable handler to declare other immutable state handlers it depends on. Given dependencies exist, + * the ImmutableClusterStateController will order those handlers such that the handlers that are dependent * on are processed first. *

* - * @return a collection of operator handler names + * @return a collection of immutable state handler names */ default Collection dependencies() { return Collections.emptyList(); @@ -82,12 +82,12 @@ default Collection dependencies() { * Generic validation helper method that throws consistent exception for all handlers. * *

- * All implementations of OperatorHandler should call the request validate method, by calling this default - * implementation. To aid in any special validation logic that may need to be implemented by the operator handler + * All implementations of {@link ImmutableClusterStateHandler} should call the request validate method, by calling this default + * implementation. To aid in any special validation logic that may need to be implemented by the immutable cluster state handler * we provide this convenience method. *

* - * @param request the master node request that we base this operator handler on + * @param request the master node request that we base this immutable state handler on */ default void validate(MasterNodeRequest request) { ActionRequestValidationException exception = request.validate(); diff --git a/server/src/main/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandlerProvider.java b/server/src/main/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandlerProvider.java new file mode 100644 index 0000000000000..555c278182949 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandlerProvider.java @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.immutablestate; + +import java.util.Collection; + +/** + * SPI service interface for supplying {@link ImmutableClusterStateHandler} implementations to Elasticsearch + * from plugins/modules. + */ +public interface ImmutableClusterStateHandlerProvider { + /** + * Returns a list of {@link ImmutableClusterStateHandler} implementations that a module/plugin supplies. + * @see ImmutableClusterStateHandler + * + * @return a list of ${@link ImmutableClusterStateHandler}s + */ + Collection> handlers(); +} diff --git a/server/src/main/java/org/elasticsearch/operator/TransformState.java b/server/src/main/java/org/elasticsearch/immutablestate/TransformState.java similarity index 69% rename from server/src/main/java/org/elasticsearch/operator/TransformState.java rename to server/src/main/java/org/elasticsearch/immutablestate/TransformState.java index a68ce7e1f2290..4472bb1d18dc7 100644 --- a/server/src/main/java/org/elasticsearch/operator/TransformState.java +++ b/server/src/main/java/org/elasticsearch/immutablestate/TransformState.java @@ -6,15 +6,15 @@ * Side Public License, v 1. */ -package org.elasticsearch.operator; +package org.elasticsearch.immutablestate; import org.elasticsearch.cluster.ClusterState; import java.util.Set; /** - * A {@link ClusterState} wrapper used by the OperatorClusterStateController to pass the - * current state as well as previous keys set by an {@link OperatorHandler} to each transform + * A {@link ClusterState} wrapper used by the ImmutableClusterStateController to pass the + * current state as well as previous keys set by an {@link ImmutableClusterStateHandler} to each transform * step of the cluster state update. * */ diff --git a/server/src/main/java/org/elasticsearch/operator/OperatorHandlerProvider.java b/server/src/main/java/org/elasticsearch/operator/OperatorHandlerProvider.java deleted file mode 100644 index 0f168ea6e4d61..0000000000000 --- a/server/src/main/java/org/elasticsearch/operator/OperatorHandlerProvider.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.operator; - -import java.util.Collection; - -/** - * SPI service interface for supplying OperatorHandler implementations to Elasticsearch - * from plugins/modules. - */ -public interface OperatorHandlerProvider { - /** - * Returns a list of OperatorHandler implementations that a module/plugin supplies. - * @see OperatorHandler - * - * @return a list of ${@link OperatorHandler}s - */ - Collection> handlers(); -} diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/ImmutableStateMetadataTests.java similarity index 65% rename from server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java rename to server/src/test/java/org/elasticsearch/cluster/metadata/ImmutableStateMetadataTests.java index 9e02e2acb78ea..b8b252c3f2965 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/OperatorMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/ImmutableStateMetadataTests.java @@ -24,15 +24,15 @@ import static org.hamcrest.Matchers.not; /** - * Tests for the {@link OperatorMetadata}, {@link OperatorErrorMetadata}, {@link OperatorHandlerMetadata} classes + * Tests for the {@link ImmutableStateMetadata}, {@link ImmutableStateErrorMetadata}, {@link ImmutableStateHandlerMetadata} classes */ -public class OperatorMetadataTests extends ESTestCase { +public class ImmutableStateMetadataTests extends ESTestCase { private void equalsTest(boolean addHandlers, boolean addErrors) { - final OperatorMetadata meta = createRandom(addHandlers, addErrors); - assertThat(meta, equalTo(OperatorMetadata.builder(meta.namespace(), meta).build())); - final OperatorMetadata.Builder newMeta = OperatorMetadata.builder(meta.namespace(), meta); - newMeta.putHandler(new OperatorHandlerMetadata("1", Collections.emptySet())); + final ImmutableStateMetadata meta = createRandom(addHandlers, addErrors); + assertThat(meta, equalTo(ImmutableStateMetadata.builder(meta.namespace(), meta).build())); + final ImmutableStateMetadata.Builder newMeta = ImmutableStateMetadata.builder(meta.namespace(), meta); + newMeta.putHandler(new ImmutableStateHandlerMetadata("1", Collections.emptySet())); assertThat(newMeta.build(), not(meta)); } @@ -44,10 +44,10 @@ public void testEquals() { } private void serializationTest(boolean addHandlers, boolean addErrors) throws IOException { - final OperatorMetadata meta = createRandom(addHandlers, addErrors); + final ImmutableStateMetadata meta = createRandom(addHandlers, addErrors); final BytesStreamOutput out = new BytesStreamOutput(); meta.writeTo(out); - assertThat(OperatorMetadata.readFrom(out.bytes().streamInput()), equalTo(meta)); + assertThat(ImmutableStateMetadata.readFrom(out.bytes().streamInput()), equalTo(meta)); } public void testSerialization() throws IOException { @@ -58,14 +58,14 @@ public void testSerialization() throws IOException { } private void xContentTest(boolean addHandlers, boolean addErrors) throws IOException { - final OperatorMetadata meta = createRandom(addHandlers, addErrors); + final ImmutableStateMetadata meta = createRandom(addHandlers, addErrors); final XContentBuilder builder = JsonXContent.contentBuilder(); builder.startObject(); meta.toXContent(builder, ToXContent.EMPTY_PARAMS); builder.endObject(); XContentParser parser = createParser(JsonXContent.jsonXContent, BytesReference.bytes(builder)); parser.nextToken(); // the beginning of the object - assertThat(OperatorMetadata.fromXContent(parser), equalTo(meta)); + assertThat(ImmutableStateMetadata.fromXContent(parser), equalTo(meta)); } public void testXContent() throws IOException { @@ -75,24 +75,24 @@ public void testXContent() throws IOException { xContentTest(false, false); } - private static OperatorMetadata createRandom(boolean addHandlers, boolean addErrors) { - List handlers = randomList( + private static ImmutableStateMetadata createRandom(boolean addHandlers, boolean addErrors) { + List handlers = randomList( 0, 10, - () -> new OperatorHandlerMetadata(randomAlphaOfLength(5), randomSet(1, 5, () -> randomAlphaOfLength(6))) + () -> new ImmutableStateHandlerMetadata(randomAlphaOfLength(5), randomSet(1, 5, () -> randomAlphaOfLength(6))) ); - List errors = randomList( + List errors = randomList( 0, 10, - () -> new OperatorErrorMetadata( + () -> new ImmutableStateErrorMetadata( 1L, - randomFrom(OperatorErrorMetadata.ErrorKind.values()), + randomFrom(ImmutableStateErrorMetadata.ErrorKind.values()), randomList(1, 5, () -> randomAlphaOfLength(10)) ) ); - OperatorMetadata.Builder builder = OperatorMetadata.builder(randomAlphaOfLength(7)); + ImmutableStateMetadata.Builder builder = ImmutableStateMetadata.builder(randomAlphaOfLength(7)); if (addHandlers) { for (var handlerMeta : handlers) { builder.putHandler(handlerMeta); diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java index 0c29e13389c6c..6e651a70f717c 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetadataTests.java @@ -46,22 +46,22 @@ public void testSimpleJsonFromAndTo() throws IOException { IndexMetadata idx1 = createFirstBackingIndex("data-stream1").build(); IndexMetadata idx2 = createFirstBackingIndex("data-stream2").build(); - OperatorHandlerMetadata hmOne = new OperatorHandlerMetadata("one", Set.of("a", "b")); - OperatorHandlerMetadata hmTwo = new OperatorHandlerMetadata("two", Set.of("c", "d")); + ImmutableStateHandlerMetadata hmOne = new ImmutableStateHandlerMetadata("one", Set.of("a", "b")); + ImmutableStateHandlerMetadata hmTwo = new ImmutableStateHandlerMetadata("two", Set.of("c", "d")); - OperatorErrorMetadata emOne = new OperatorErrorMetadata( + ImmutableStateErrorMetadata emOne = new ImmutableStateErrorMetadata( 1L, - OperatorErrorMetadata.ErrorKind.VALIDATION, + ImmutableStateErrorMetadata.ErrorKind.VALIDATION, List.of("Test error 1", "Test error 2") ); - OperatorMetadata operatorMetadata = OperatorMetadata.builder("namespace_one") + ImmutableStateMetadata immutableStateMetadata = ImmutableStateMetadata.builder("namespace_one") .errorMetadata(emOne) .putHandler(hmOne) .putHandler(hmTwo) .build(); - OperatorMetadata operatorMetadata1 = OperatorMetadata.builder("namespace_two").putHandler(hmTwo).build(); + ImmutableStateMetadata immutableStateMetadata1 = ImmutableStateMetadata.builder("namespace_two").putHandler(hmTwo).build(); Metadata metadata = Metadata.builder() .put( @@ -126,8 +126,8 @@ public void testSimpleJsonFromAndTo() throws IOException { .put(idx2, false) .put(DataStreamTestHelper.newInstance("data-stream1", List.of(idx1.getIndex()))) .put(DataStreamTestHelper.newInstance("data-stream2", List.of(idx2.getIndex()))) - .put(operatorMetadata) - .put(operatorMetadata1) + .put(immutableStateMetadata) + .put(immutableStateMetadata1) .build(); XContentBuilder builder = JsonXContent.contentBuilder(); @@ -202,9 +202,9 @@ public void testSimpleJsonFromAndTo() throws IOException { assertThat(parsedMetadata.dataStreams().get("data-stream2").getTimeStampField().getName(), is("@timestamp")); assertThat(parsedMetadata.dataStreams().get("data-stream2").getIndices(), contains(idx2.getIndex())); - // operator metadata - assertEquals(operatorMetadata, parsedMetadata.operatorMetadata().get(operatorMetadata.namespace())); - assertEquals(operatorMetadata1, parsedMetadata.operatorMetadata().get(operatorMetadata1.namespace())); + // immutable 'operator' metadata + assertEquals(immutableStateMetadata, parsedMetadata.immutableStateMetadata().get(immutableStateMetadata.namespace())); + assertEquals(immutableStateMetadata1, parsedMetadata.immutableStateMetadata().get(immutableStateMetadata1.namespace())); } private static final String MAPPING_SOURCE1 = """ @@ -272,7 +272,7 @@ public void testToXContentGateway_FlatSettingTrue_ReduceMappingFalse() throws IO "index-graveyard" : { "tombstones" : [ ] }, - "operator" : { } + "immutable_state" : { } } }""".formatted(Version.CURRENT.id, Version.CURRENT.id), Strings.toString(builder)); } @@ -368,7 +368,7 @@ public void testToXContentAPI_SameTypeName() throws IOException { "index-graveyard" : { "tombstones" : [ ] }, - "operator" : { } + "immutable_state" : { } } }""".formatted(Version.CURRENT.id), Strings.toString(builder)); } @@ -433,7 +433,7 @@ public void testToXContentGateway_FlatSettingFalse_ReduceMappingTrue() throws IO "index-graveyard" : { "tombstones" : [ ] }, - "operator" : { } + "immutable_state" : { } } }""".formatted(Version.CURRENT.id, Version.CURRENT.id), Strings.toString(builder)); } @@ -536,7 +536,7 @@ public void testToXContentAPI_FlatSettingTrue_ReduceMappingFalse() throws IOExce "index-graveyard" : { "tombstones" : [ ] }, - "operator" : { } + "immutable_state" : { } } }""".formatted(Version.CURRENT.id, Version.CURRENT.id), Strings.toString(builder)); } @@ -645,12 +645,12 @@ public void testToXContentAPI_FlatSettingFalse_ReduceMappingTrue() throws IOExce "index-graveyard" : { "tombstones" : [ ] }, - "operator" : { } + "immutable_state" : { } } }""".formatted(Version.CURRENT.id, Version.CURRENT.id), Strings.toString(builder)); } - public void testToXContentAPIOperatorMetadata() throws IOException { + public void testToXContentAPIImmutableMetadata() throws IOException { Map mapParams = new HashMap<>() { { put(Metadata.CONTEXT_MODE_PARAM, CONTEXT_MODE_API); @@ -661,25 +661,29 @@ public void testToXContentAPIOperatorMetadata() throws IOException { Metadata metadata = buildMetadata(); - OperatorHandlerMetadata hmOne = new OperatorHandlerMetadata("one", Set.of("a", "b")); - OperatorHandlerMetadata hmTwo = new OperatorHandlerMetadata("two", Set.of("c", "d")); - OperatorHandlerMetadata hmThree = new OperatorHandlerMetadata("three", Set.of("e", "f")); + ImmutableStateHandlerMetadata hmOne = new ImmutableStateHandlerMetadata("one", Set.of("a", "b")); + ImmutableStateHandlerMetadata hmTwo = new ImmutableStateHandlerMetadata("two", Set.of("c", "d")); + ImmutableStateHandlerMetadata hmThree = new ImmutableStateHandlerMetadata("three", Set.of("e", "f")); - OperatorErrorMetadata emOne = new OperatorErrorMetadata( + ImmutableStateErrorMetadata emOne = new ImmutableStateErrorMetadata( 1L, - OperatorErrorMetadata.ErrorKind.VALIDATION, + ImmutableStateErrorMetadata.ErrorKind.VALIDATION, List.of("Test error 1", "Test error 2") ); - OperatorErrorMetadata emTwo = new OperatorErrorMetadata( + ImmutableStateErrorMetadata emTwo = new ImmutableStateErrorMetadata( 2L, - OperatorErrorMetadata.ErrorKind.TRANSIENT, + ImmutableStateErrorMetadata.ErrorKind.TRANSIENT, List.of("Test error 3", "Test error 4") ); - OperatorMetadata omOne = OperatorMetadata.builder("namespace_one").errorMetadata(emOne).putHandler(hmOne).putHandler(hmTwo).build(); + ImmutableStateMetadata omOne = ImmutableStateMetadata.builder("namespace_one") + .errorMetadata(emOne) + .putHandler(hmOne) + .putHandler(hmTwo) + .build(); - OperatorMetadata omTwo = OperatorMetadata.builder("namespace_two").errorMetadata(emTwo).putHandler(hmThree).build(); + ImmutableStateMetadata omTwo = ImmutableStateMetadata.builder("namespace_two").errorMetadata(emTwo).putHandler(hmThree).build(); metadata = Metadata.builder(metadata).put(omOne).put(omTwo).build(); @@ -776,7 +780,7 @@ public void testToXContentAPIOperatorMetadata() throws IOException { "index-graveyard" : { "tombstones" : [ ] }, - "operator" : { + "immutable_state" : { "namespace_one" : { "version" : 0, "handlers" : { diff --git a/server/src/test/java/org/elasticsearch/operator/OperatorHandlerTests.java b/server/src/test/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandlerTests.java similarity index 92% rename from server/src/test/java/org/elasticsearch/operator/OperatorHandlerTests.java rename to server/src/test/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandlerTests.java index da10b8b74bcf9..86eddbaadad17 100644 --- a/server/src/test/java/org/elasticsearch/operator/OperatorHandlerTests.java +++ b/server/src/test/java/org/elasticsearch/immutablestate/ImmutableClusterStateHandlerTests.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.operator; +package org.elasticsearch.immutablestate; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.MasterNodeRequest; @@ -23,9 +23,9 @@ import static org.hamcrest.Matchers.containsInAnyOrder; -public class OperatorHandlerTests extends ESTestCase { +public class ImmutableClusterStateHandlerTests extends ESTestCase { public void testValidation() { - OperatorHandler handler = new OperatorHandler<>() { + ImmutableClusterStateHandler handler = new ImmutableClusterStateHandler<>() { @Override public String name() { return "handler"; @@ -61,7 +61,7 @@ public void testAsMapAndFromMap() throws IOException { } }"""; - OperatorHandler persistentHandler = new OperatorHandler<>() { + ImmutableClusterStateHandler persistentHandler = new ImmutableClusterStateHandler<>() { @Override public String name() { return "persistent"; From 4c07b5c8fa0e2ed41f5bd0aa56ecee5a7dc3adbe Mon Sep 17 00:00:00 2001 From: Nikola Grcevski Date: Wed, 29 Jun 2022 14:33:03 -0400 Subject: [PATCH 7/7] Adjust tests for renamed field --- .../cluster/reroute/ClusterRerouteResponseTests.java | 4 ++-- .../java/org/elasticsearch/cluster/ClusterStateTests.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java index b9ee982a1f41e..87fb163fae03c 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java @@ -143,7 +143,7 @@ public void testToXContent() throws IOException { "index-graveyard": { "tombstones": [] }, - "operator":{} + "immutable_state":{} }, "routing_table": { "indices": {} @@ -248,7 +248,7 @@ public void testToXContent() throws IOException { "index-graveyard" : { "tombstones" : [ ] }, - "operator":{} + "immutable_state":{} } } }"""), XContentHelper.stripWhitespace(Strings.toString(builder))); diff --git a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java index 05d417bef0077..cee3bbd8bd506 100644 --- a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java @@ -287,7 +287,7 @@ public void testToXContent() throws IOException { "index-graveyard": { "tombstones": [] }, - "operator" : { } + "immutable_state" : { } }, "routing_table": { "indices": { @@ -491,7 +491,7 @@ public void testToXContent_FlatSettingTrue_ReduceMappingFalse() throws IOExcepti "index-graveyard" : { "tombstones" : [ ] }, - "operator" : { } + "immutable_state" : { } }, "routing_table" : { "indices" : { @@ -702,7 +702,7 @@ public void testToXContent_FlatSettingFalse_ReduceMappingTrue() throws IOExcepti "index-graveyard" : { "tombstones" : [ ] }, - "operator" : { } + "immutable_state" : { } }, "routing_table" : { "indices" : { @@ -844,7 +844,7 @@ public void testToXContentSameTypeName() throws IOException { "index-graveyard" : { "tombstones" : [ ] }, - "operator" : { } + "immutable_state" : { } }, "routing_table" : { "indices" : { }