* Add ComponentTemplate to MetaData (#53290) * Add ComponentTemplate to MetaData This adds a `ComponentTemplate` datastructure that will be used as part of #53101 (Index Templates v2) to the `MetaData` class. Currently there are no APIs for interacting with this class, so it will always be an empty map (other than in tests). This infrastructure will be built upon to add APIs in a subsequent commit. A `ComponentTemplate` is made up of a `Template`, a version, and a MetaData.Custom class. The `Template` contains similar information to an `IndexTemplateMetaData` object— settings, mappings, and alias configuration. * Update minimal supported version constant Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
9dcd64c110
commit
2789fe4179
|
@ -22,6 +22,7 @@ package org.elasticsearch.cluster;
|
|||
import org.elasticsearch.cluster.action.index.MappingUpdatedAction;
|
||||
import org.elasticsearch.cluster.action.index.NodeMappingRefreshAction;
|
||||
import org.elasticsearch.cluster.action.shard.ShardStateAction;
|
||||
import org.elasticsearch.cluster.metadata.ComponentTemplateMetadata;
|
||||
import org.elasticsearch.cluster.metadata.IndexGraveyard;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
|
@ -130,6 +131,8 @@ public class ClusterModule extends AbstractModule {
|
|||
registerMetaDataCustom(entries, IndexGraveyard.TYPE, IndexGraveyard::new, IndexGraveyard::readDiffFrom);
|
||||
registerMetaDataCustom(entries, PersistentTasksCustomMetaData.TYPE, PersistentTasksCustomMetaData::new,
|
||||
PersistentTasksCustomMetaData::readDiffFrom);
|
||||
registerMetaDataCustom(entries, ComponentTemplateMetadata.TYPE, ComponentTemplateMetadata::new,
|
||||
ComponentTemplateMetadata::readDiffFrom);
|
||||
// Task Status (not Diffable)
|
||||
entries.add(new Entry(Task.Status.class, PersistentTasksNodeService.Status.NAME, PersistentTasksNodeService.Status::new));
|
||||
return entries;
|
||||
|
@ -177,6 +180,8 @@ public class ClusterModule extends AbstractModule {
|
|||
IndexGraveyard::fromXContent));
|
||||
entries.add(new NamedXContentRegistry.Entry(MetaData.Custom.class, new ParseField(PersistentTasksCustomMetaData.TYPE),
|
||||
PersistentTasksCustomMetaData::fromXContent));
|
||||
entries.add(new NamedXContentRegistry.Entry(MetaData.Custom.class, new ParseField(ComponentTemplateMetadata.TYPE),
|
||||
ComponentTemplateMetadata::fromXContent));
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.cluster.AbstractDiffable;
|
||||
import org.elasticsearch.cluster.Diff;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A component template is a re-usable template as well as metadata about the template. Each
|
||||
* component template is expected to be valid on its own. For example, if a component template
|
||||
* contains a field "foo", it's expected to contain all the necessary settings/mappings/etc for the
|
||||
* "foo" field. These component templates make up the individual pieces composing an index template.
|
||||
*/
|
||||
public class ComponentTemplate extends AbstractDiffable<ComponentTemplate> implements ToXContentObject {
|
||||
private static final ParseField TEMPLATE = new ParseField("template");
|
||||
private static final ParseField VERSION = new ParseField("version");
|
||||
private static final ParseField METADATA = new ParseField("_meta");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final ConstructingObjectParser<ComponentTemplate, Void> PARSER =
|
||||
new ConstructingObjectParser<>("component_template", false,
|
||||
a -> new ComponentTemplate((Template) a[0], (Long) a[1], (Map<String, Object>) a[2]));
|
||||
|
||||
static {
|
||||
PARSER.declareObject(ConstructingObjectParser.constructorArg(), Template.PARSER, TEMPLATE);
|
||||
PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), VERSION);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.map(), METADATA);
|
||||
}
|
||||
|
||||
private final Template template;
|
||||
@Nullable
|
||||
private final Long version;
|
||||
@Nullable
|
||||
private final Map<String, Object> metadata;
|
||||
|
||||
static Diff<ComponentTemplate> readComponentTemplateDiffFrom(StreamInput in) throws IOException {
|
||||
return AbstractDiffable.readDiffFrom(ComponentTemplate::new, in);
|
||||
}
|
||||
|
||||
public static ComponentTemplate parse(XContentParser parser) {
|
||||
return PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
public ComponentTemplate(Template template, @Nullable Long version, @Nullable Map<String, Object> metadata) {
|
||||
this.template = template;
|
||||
this.version = version;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public ComponentTemplate(StreamInput in) throws IOException {
|
||||
this.template = new Template(in);
|
||||
this.version = in.readOptionalVLong();
|
||||
if (in.readBoolean()) {
|
||||
this.metadata = in.readMap();
|
||||
} else {
|
||||
this.metadata = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Template template() {
|
||||
return template;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, Object> metadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
this.template.writeTo(out);
|
||||
out.writeOptionalVLong(this.version);
|
||||
if (this.metadata == null) {
|
||||
out.writeBoolean(false);
|
||||
} else {
|
||||
out.writeBoolean(true);
|
||||
out.writeMap(this.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(template, version, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
ComponentTemplate other = (ComponentTemplate) obj;
|
||||
return Objects.equals(template, other.template) &&
|
||||
Objects.equals(version, other.version) &&
|
||||
Objects.equals(metadata, other.metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Strings.toString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(TEMPLATE.getPreferredName(), this.template);
|
||||
if (this.version != null) {
|
||||
builder.field(VERSION.getPreferredName(), this.version);
|
||||
}
|
||||
if (this.metadata != null) {
|
||||
builder.field(METADATA.getPreferredName(), this.metadata);
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
static class Template extends AbstractDiffable<Template> implements ToXContentObject {
|
||||
private static final ParseField SETTINGS = new ParseField("settings");
|
||||
private static final ParseField MAPPINGS = new ParseField("mappings");
|
||||
private static final ParseField ALIASES = new ParseField("aliases");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final ConstructingObjectParser<Template, Void> PARSER = new ConstructingObjectParser<>("template", false,
|
||||
a -> new Template((Settings) a[0], (CompressedXContent) a[1], (Map<String, AliasMetaData>) a[2]));
|
||||
|
||||
static {
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> Settings.fromXContent(p), SETTINGS);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) ->
|
||||
new CompressedXContent(Strings.toString(XContentFactory.jsonBuilder().map(p.mapOrdered()))), MAPPINGS);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> {
|
||||
Map<String, AliasMetaData> aliasMap = new HashMap<>();
|
||||
while ((p.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
AliasMetaData alias = AliasMetaData.Builder.fromXContent(p);
|
||||
aliasMap.put(alias.alias(), alias);
|
||||
}
|
||||
return aliasMap;
|
||||
}, ALIASES);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private final Settings settings;
|
||||
@Nullable
|
||||
private final CompressedXContent mappings;
|
||||
@Nullable
|
||||
private final Map<String, AliasMetaData> aliases;
|
||||
|
||||
Template(@Nullable Settings settings, @Nullable CompressedXContent mappings, @Nullable Map<String, AliasMetaData> aliases) {
|
||||
this.settings = settings;
|
||||
this.mappings = mappings;
|
||||
this.aliases = aliases;
|
||||
}
|
||||
|
||||
Template(StreamInput in) throws IOException {
|
||||
if (in.readBoolean()) {
|
||||
this.settings = Settings.readSettingsFromStream(in);
|
||||
} else {
|
||||
this.settings = null;
|
||||
}
|
||||
if (in.readBoolean()) {
|
||||
this.mappings = CompressedXContent.readCompressedString(in);
|
||||
} else {
|
||||
this.mappings = null;
|
||||
}
|
||||
if (in.readBoolean()) {
|
||||
this.aliases = in.readMap(StreamInput::readString, AliasMetaData::new);
|
||||
} else {
|
||||
this.aliases = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Settings settings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public CompressedXContent mappings() {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public Map<String, AliasMetaData> aliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
if (this.settings == null) {
|
||||
out.writeBoolean(false);
|
||||
} else {
|
||||
out.writeBoolean(true);
|
||||
Settings.writeSettingsToStream(this.settings, out);
|
||||
}
|
||||
if (this.mappings == null) {
|
||||
out.writeBoolean(false);
|
||||
} else {
|
||||
out.writeBoolean(true);
|
||||
this.mappings.writeTo(out);
|
||||
}
|
||||
if (this.aliases == null) {
|
||||
out.writeBoolean(false);
|
||||
} else {
|
||||
out.writeBoolean(true);
|
||||
out.writeMap(this.aliases, StreamOutput::writeString, (stream, aliasMetaData) -> aliasMetaData.writeTo(stream));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(settings, mappings, aliases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
Template other = (Template) obj;
|
||||
return Objects.equals(settings, other.settings) &&
|
||||
Objects.equals(mappings, other.mappings) &&
|
||||
Objects.equals(aliases, other.aliases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Strings.toString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
if (this.settings != null) {
|
||||
builder.startObject(SETTINGS.getPreferredName());
|
||||
this.settings.toXContent(builder, params);
|
||||
builder.endObject();
|
||||
}
|
||||
if (this.mappings != null) {
|
||||
Map<String, Object> uncompressedMapping =
|
||||
XContentHelper.convertToMap(new BytesArray(this.mappings.uncompressed()), true, XContentType.JSON).v2();
|
||||
if (uncompressedMapping.size() > 0) {
|
||||
builder.field(MAPPINGS.getPreferredName());
|
||||
builder.map(uncompressedMapping);
|
||||
}
|
||||
}
|
||||
if (this.aliases != null) {
|
||||
builder.startObject(ALIASES.getPreferredName());
|
||||
for (AliasMetaData alias : this.aliases.values()) {
|
||||
AliasMetaData.Builder.toXContent(alias, builder, params);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.Diff;
|
||||
import org.elasticsearch.cluster.DiffableUtils;
|
||||
import org.elasticsearch.cluster.NamedDiff;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@link ComponentTemplateMetadata} is a custom {@link MetaData} implementation for storing a map
|
||||
* of component templates and their names.
|
||||
*/
|
||||
public class ComponentTemplateMetadata implements MetaData.Custom {
|
||||
public static final String TYPE = "component_template";
|
||||
private static final ParseField COMPONENT_TEMPLATE = new ParseField("component_template");
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final ConstructingObjectParser<ComponentTemplateMetadata, Void> PARSER = new ConstructingObjectParser<>(TYPE, false,
|
||||
a -> new ComponentTemplateMetadata((Map<String, ComponentTemplate>) a[0]));
|
||||
|
||||
static {
|
||||
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> {
|
||||
Map<String, ComponentTemplate> templates = new HashMap<>();
|
||||
while (p.nextToken() != XContentParser.Token.END_OBJECT) {
|
||||
String name = p.currentName();
|
||||
templates.put(name, ComponentTemplate.parse(p));
|
||||
}
|
||||
return templates;
|
||||
}, COMPONENT_TEMPLATE);
|
||||
}
|
||||
private final Map<String, ComponentTemplate> componentTemplates;
|
||||
|
||||
public ComponentTemplateMetadata(Map<String, ComponentTemplate> componentTemplates) {
|
||||
this.componentTemplates = componentTemplates;
|
||||
}
|
||||
|
||||
public ComponentTemplateMetadata(StreamInput in) throws IOException {
|
||||
this.componentTemplates = in.readMap(StreamInput::readString, ComponentTemplate::new);
|
||||
}
|
||||
|
||||
public Map<String, ComponentTemplate> componentTemplates() {
|
||||
return this.componentTemplates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diff<MetaData.Custom> diff(MetaData.Custom before) {
|
||||
return new ComponentTemplateMetadataDiff((ComponentTemplateMetadata) before, this);
|
||||
}
|
||||
|
||||
public static NamedDiff<MetaData.Custom> readDiffFrom(StreamInput in) throws IOException {
|
||||
return new ComponentTemplateMetadataDiff(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<MetaData.XContentContext> context() {
|
||||
return MetaData.ALL_CONTEXTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version getMinimalSupportedVersion() {
|
||||
return Version.V_7_7_0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeMap(this.componentTemplates, StreamOutput::writeString, (stream, val) -> val.writeTo(stream));
|
||||
}
|
||||
|
||||
public static ComponentTemplateMetadata fromXContent(XContentParser parser) throws IOException {
|
||||
return PARSER.parse(parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(COMPONENT_TEMPLATE.getPreferredName());
|
||||
for (Map.Entry<String, ComponentTemplate> template : componentTemplates.entrySet()) {
|
||||
builder.field(template.getKey(), template.getValue());
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.componentTemplates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
ComponentTemplateMetadata other = (ComponentTemplateMetadata) obj;
|
||||
return Objects.equals(this.componentTemplates, other.componentTemplates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Strings.toString(this);
|
||||
}
|
||||
|
||||
static class ComponentTemplateMetadataDiff implements NamedDiff<MetaData.Custom> {
|
||||
|
||||
final Diff<Map<String, ComponentTemplate>> componentTemplateDiff;
|
||||
|
||||
ComponentTemplateMetadataDiff(ComponentTemplateMetadata before, ComponentTemplateMetadata after) {
|
||||
this.componentTemplateDiff = DiffableUtils.diff(before.componentTemplates, after.componentTemplates,
|
||||
DiffableUtils.getStringKeySerializer());
|
||||
}
|
||||
|
||||
ComponentTemplateMetadataDiff(StreamInput in) throws IOException {
|
||||
this.componentTemplateDiff = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(),
|
||||
ComponentTemplate::new, ComponentTemplate::readComponentTemplateDiffFrom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaData.Custom apply(MetaData.Custom part) {
|
||||
return new ComponentTemplateMetadata(componentTemplateDiff.apply(((ComponentTemplateMetadata) part).componentTemplates));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
componentTemplateDiff.writeTo(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -75,6 +75,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
@ -720,6 +721,12 @@ public class MetaData implements Iterable<IndexMetaData>, Diffable<MetaData>, To
|
|||
return this.templates;
|
||||
}
|
||||
|
||||
public Map<String, ComponentTemplate> componentTemplates() {
|
||||
return Optional.ofNullable((ComponentTemplateMetadata) this.custom(ComponentTemplateMetadata.TYPE))
|
||||
.map(ComponentTemplateMetadata::componentTemplates)
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
public ImmutableOpenMap<String, Custom> customs() {
|
||||
return this.customs;
|
||||
}
|
||||
|
@ -1128,6 +1135,34 @@ public class MetaData implements Iterable<IndexMetaData>, Diffable<MetaData>, To
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder put(String name, ComponentTemplate componentTemplate) {
|
||||
Objects.requireNonNull(componentTemplate, "it is invalid to add a null component template: " + name);
|
||||
// ಠ_ಠ at ImmutableOpenMap
|
||||
Map<String, ComponentTemplate> existingTemplates =
|
||||
Optional.ofNullable((ComponentTemplateMetadata) this.customs.get(ComponentTemplateMetadata.TYPE))
|
||||
.map(ctm -> new HashMap<>(ctm.componentTemplates()))
|
||||
.orElse(new HashMap<>());
|
||||
existingTemplates.put(name, componentTemplate);
|
||||
this.customs.put(ComponentTemplateMetadata.TYPE, new ComponentTemplateMetadata(existingTemplates));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder removeComponentTemplate(String name) {
|
||||
// ಠ_ಠ at ImmutableOpenMap
|
||||
Map<String, ComponentTemplate> existingTemplates =
|
||||
Optional.ofNullable((ComponentTemplateMetadata) this.customs.get(ComponentTemplateMetadata.TYPE))
|
||||
.map(ctm -> new HashMap<>(ctm.componentTemplates()))
|
||||
.orElse(new HashMap<>());
|
||||
existingTemplates.remove(name);
|
||||
this.customs.put(ComponentTemplateMetadata.TYPE, new ComponentTemplateMetadata(existingTemplates));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder componentTemplates(Map<String, ComponentTemplate> componentTemplates) {
|
||||
this.customs.put(ComponentTemplateMetadata.TYPE, new ComponentTemplateMetadata(componentTemplates));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Custom getCustom(String type) {
|
||||
return customs.get(type);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.test.AbstractNamedWriteableTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ComponentTemplateMetadataTests extends AbstractNamedWriteableTestCase<ComponentTemplateMetadata> {
|
||||
|
||||
@Override
|
||||
protected ComponentTemplateMetadata createTestInstance() {
|
||||
int count = randomIntBetween(0, 3);
|
||||
Map<String, ComponentTemplate> templateMap = new HashMap<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
templateMap.put(randomAlphaOfLength(4), ComponentTemplateTests.randomInstance());
|
||||
}
|
||||
return new ComponentTemplateMetadata(templateMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ComponentTemplateMetadata mutateInstance(ComponentTemplateMetadata instance) throws IOException {
|
||||
if (instance.componentTemplates().size() == 0) {
|
||||
// Not really much to mutate, so just generate a new one
|
||||
return randomValueOtherThan(instance, this::createTestInstance);
|
||||
}
|
||||
Map<String, ComponentTemplate> templates = new HashMap<>(instance.componentTemplates());
|
||||
Map.Entry<String, ComponentTemplate> newTemplate = instance.componentTemplates().entrySet().iterator().next();
|
||||
if (randomBoolean()) {
|
||||
// Change the key
|
||||
templates.put(randomAlphaOfLength(4), newTemplate.getValue());
|
||||
} else {
|
||||
// Change the value
|
||||
templates.put(newTemplate.getKey(), ComponentTemplateTests.mutateTemplate(newTemplate.getValue()));
|
||||
}
|
||||
return new ComponentTemplateMetadata(templates);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedWriteableRegistry getNamedWriteableRegistry() {
|
||||
return new NamedWriteableRegistry(Collections.singletonList(new NamedWriteableRegistry.Entry(ComponentTemplateMetadata.class,
|
||||
ComponentTemplateMetadata.TYPE, ComponentTemplateMetadata::new)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<ComponentTemplateMetadata> categoryClass() {
|
||||
return ComponentTemplateMetadata.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.cluster.Diff;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractDiffableSerializationTestCase;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class ComponentTemplateTests extends AbstractDiffableSerializationTestCase<ComponentTemplate> {
|
||||
@Override
|
||||
protected ComponentTemplate makeTestChanges(ComponentTemplate testInstance) {
|
||||
try {
|
||||
return mutateInstance(testInstance);
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
fail("mutating should not throw an exception, but got: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<Diff<ComponentTemplate>> diffReader() {
|
||||
return ComponentTemplate::readComponentTemplateDiffFrom;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ComponentTemplate doParseInstance(XContentParser parser) throws IOException {
|
||||
return ComponentTemplate.parse(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<ComponentTemplate> instanceReader() {
|
||||
return ComponentTemplate::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ComponentTemplate createTestInstance() {
|
||||
return randomInstance();
|
||||
}
|
||||
|
||||
public static ComponentTemplate randomInstance() {
|
||||
Settings settings = null;
|
||||
CompressedXContent mappings = null;
|
||||
Map<String, AliasMetaData> aliases = null;
|
||||
if (randomBoolean()) {
|
||||
settings = randomSettings();
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
mappings = randomMappings();
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
aliases = randomAliases();
|
||||
}
|
||||
ComponentTemplate.Template template = new ComponentTemplate.Template(settings, mappings, aliases);
|
||||
|
||||
Map<String, Object> meta = null;
|
||||
if (randomBoolean()) {
|
||||
meta = randomMeta();
|
||||
}
|
||||
return new ComponentTemplate(template, randomBoolean() ? null : randomNonNegativeLong(), meta);
|
||||
}
|
||||
|
||||
private static Map<String, AliasMetaData> randomAliases() {
|
||||
String aliasName = randomAlphaOfLength(5);
|
||||
AliasMetaData aliasMeta = AliasMetaData.builder(aliasName)
|
||||
.filter(Collections.singletonMap(randomAlphaOfLength(2), randomAlphaOfLength(2)))
|
||||
.routing(randomBoolean() ? null : randomAlphaOfLength(3))
|
||||
.isHidden(randomBoolean() ? null : randomBoolean())
|
||||
.writeIndex(randomBoolean() ? null : randomBoolean())
|
||||
.build();
|
||||
return Collections.singletonMap(aliasName, aliasMeta);
|
||||
}
|
||||
|
||||
private static CompressedXContent randomMappings() {
|
||||
try {
|
||||
return new CompressedXContent("{\"" + randomAlphaOfLength(3) + "\":\"" + randomAlphaOfLength(7) + "\"}");
|
||||
} catch (IOException e) {
|
||||
fail("got an IO exception creating fake mappings: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Settings randomSettings() {
|
||||
return Settings.builder()
|
||||
.put(randomAlphaOfLength(4), randomAlphaOfLength(10))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Map<String, Object> randomMeta() {
|
||||
if (randomBoolean()) {
|
||||
return Collections.singletonMap(randomAlphaOfLength(4), randomAlphaOfLength(4));
|
||||
} else {
|
||||
return Collections.singletonMap(randomAlphaOfLength(5),
|
||||
Collections.singletonMap(randomAlphaOfLength(4), randomAlphaOfLength(4)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ComponentTemplate mutateInstance(ComponentTemplate orig) throws IOException {
|
||||
return mutateTemplate(orig);
|
||||
}
|
||||
|
||||
public static ComponentTemplate mutateTemplate(ComponentTemplate orig) {
|
||||
switch (randomIntBetween(0, 2)) {
|
||||
case 0:
|
||||
switch (randomIntBetween(0, 2)) {
|
||||
case 0:
|
||||
ComponentTemplate.Template ot = orig.template();
|
||||
return new ComponentTemplate(
|
||||
new ComponentTemplate.Template(randomValueOtherThan(ot.settings(), ComponentTemplateTests::randomSettings),
|
||||
ot.mappings(), ot.aliases()),
|
||||
orig.version(), orig.metadata());
|
||||
case 1:
|
||||
ComponentTemplate.Template ot2 = orig.template();
|
||||
return new ComponentTemplate(
|
||||
new ComponentTemplate.Template(ot2.settings(),
|
||||
randomValueOtherThan(ot2.mappings(), ComponentTemplateTests::randomMappings), ot2.aliases()),
|
||||
orig.version(), orig.metadata());
|
||||
case 2:
|
||||
ComponentTemplate.Template ot3 = orig.template();
|
||||
return new ComponentTemplate(
|
||||
new ComponentTemplate.Template(ot3.settings(), ot3.mappings(),
|
||||
randomValueOtherThan(ot3.aliases(), ComponentTemplateTests::randomAliases)),
|
||||
orig.version(), orig.metadata());
|
||||
default:
|
||||
throw new IllegalStateException("illegal randomization branch");
|
||||
}
|
||||
case 1:
|
||||
return new ComponentTemplate(orig.template(), randomValueOtherThan(orig.version(), ESTestCase::randomNonNegativeLong),
|
||||
orig.metadata());
|
||||
case 2:
|
||||
return new ComponentTemplate(orig.template(), orig.version(),
|
||||
randomValueOtherThan(orig.metadata(), ComponentTemplateTests::randomMeta));
|
||||
default:
|
||||
throw new IllegalStateException("illegal randomization branch");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ import org.elasticsearch.plugins.MapperPlugin;
|
|||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -750,7 +751,7 @@ public class MetaDataTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private IndexMetaData.Builder buildIndexMetaData(String name, String alias, Boolean writeIndex) {
|
||||
private static IndexMetaData.Builder buildIndexMetaData(String name, String alias, Boolean writeIndex) {
|
||||
return IndexMetaData.builder(name)
|
||||
.settings(settings(Version.CURRENT)).creationDate(randomNonNegativeLong())
|
||||
.putAlias(AliasMetaData.builder(alias).writeIndex(writeIndex))
|
||||
|
@ -940,4 +941,37 @@ public class MetaDataTests extends ESTestCase {
|
|||
final ImmutableOpenMap<String, MetaData.Custom> map = mapBuilder.build();
|
||||
assertThat(expectThrows(NullPointerException.class, () -> builder.customs(map)).getMessage(), containsString(key));
|
||||
}
|
||||
|
||||
public void testSerialization() throws IOException {
|
||||
final MetaData orig = randomMetaData();
|
||||
final BytesStreamOutput out = new BytesStreamOutput();
|
||||
orig.writeTo(out);
|
||||
NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry(ClusterModule.getNamedWriteables());
|
||||
final MetaData fromStreamMeta = MetaData.readFrom(new NamedWriteableAwareStreamInput(out.bytes().streamInput(),
|
||||
namedWriteableRegistry));
|
||||
assertTrue(MetaData.isGlobalStateEquals(orig, fromStreamMeta));
|
||||
}
|
||||
|
||||
public static MetaData randomMetaData() {
|
||||
return MetaData.builder()
|
||||
.put(buildIndexMetaData("index", "alias", randomBoolean() ? null : randomBoolean()).build(), randomBoolean())
|
||||
.put(IndexTemplateMetaData.builder("template" + randomAlphaOfLength(3))
|
||||
.patterns(Arrays.asList("bar-*", "foo-*"))
|
||||
.settings(Settings.builder()
|
||||
.put("random_index_setting_" + randomAlphaOfLength(3), randomAlphaOfLength(5))
|
||||
.build())
|
||||
.build())
|
||||
.persistentSettings(Settings.builder()
|
||||
.put("setting" + randomAlphaOfLength(3), randomAlphaOfLength(4))
|
||||
.build())
|
||||
.transientSettings(Settings.builder()
|
||||
.put("other_setting" + randomAlphaOfLength(3), randomAlphaOfLength(4))
|
||||
.build())
|
||||
.clusterUUID("uuid" + randomAlphaOfLength(3))
|
||||
.clusterUUIDCommitted(randomBoolean())
|
||||
.indexGraveyard(IndexGraveyardTests.createRandom())
|
||||
.version(randomNonNegativeLong())
|
||||
.put("component_template_" + randomAlphaOfLength(3), ComponentTemplateTests.randomInstance())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
@ -122,6 +123,11 @@ public class ToAndFromJsonMetaDataTests extends ESTestCase {
|
|||
.putAlias(newAliasMetaDataBuilder("alias-bar1"))
|
||||
.putAlias(newAliasMetaDataBuilder("alias-bar2").filter("{\"term\":{\"user\":\"kimchy\"}}"))
|
||||
.putAlias(newAliasMetaDataBuilder("alias-bar3").routing("routing-bar")))
|
||||
.put("component_template", new ComponentTemplate(
|
||||
new ComponentTemplate.Template(Settings.builder().put("setting", "value").build(),
|
||||
new CompressedXContent("{\"baz\":\"eggplant\"}"),
|
||||
Collections.singletonMap("alias", AliasMetaData.builder("alias").build())),
|
||||
5L, Collections.singletonMap("my_meta", Collections.singletonMap("foo", "bar"))))
|
||||
.put(IndexMetaData.builder("test12")
|
||||
.settings(settings(Version.CURRENT)
|
||||
.put("setting1", "value1")
|
||||
|
@ -307,6 +313,16 @@ public class ToAndFromJsonMetaDataTests extends ESTestCase {
|
|||
assertThat(parsedMetaData.templates().get("foo").aliases().get("alias-bar3").alias(), equalTo("alias-bar3"));
|
||||
assertThat(parsedMetaData.templates().get("foo").aliases().get("alias-bar3").indexRouting(), equalTo("routing-bar"));
|
||||
assertThat(parsedMetaData.templates().get("foo").aliases().get("alias-bar3").searchRouting(), equalTo("routing-bar"));
|
||||
|
||||
// component template
|
||||
assertNotNull(parsedMetaData.componentTemplates().get("component_template"));
|
||||
assertThat(parsedMetaData.componentTemplates().get("component_template").version(), is(5L));
|
||||
assertThat(parsedMetaData.componentTemplates().get("component_template").metadata(),
|
||||
equalTo(Collections.singletonMap("my_meta", Collections.singletonMap("foo", "bar"))));
|
||||
assertThat(parsedMetaData.componentTemplates().get("component_template").template(),
|
||||
equalTo(new ComponentTemplate.Template(Settings.builder().put("setting", "value").build(),
|
||||
new CompressedXContent("{\"baz\":\"eggplant\"}"),
|
||||
Collections.singletonMap("alias", AliasMetaData.builder("alias").build()))));
|
||||
}
|
||||
|
||||
private static final String MAPPING_SOURCE1 = "{\"mapping1\":{\"text1\":{\"type\":\"string\"}}}";
|
||||
|
|
Loading…
Reference in New Issue