Backports the following commits to 7.x: - Don't allow invalid template combinations (#56397)
This commit is contained in:
parent
0fd756d511
commit
a73d7d9e2b
|
@ -31,6 +31,32 @@ the settings from the create index request take precedence over settings specifi
|
||||||
|
|
||||||
[source,console]
|
[source,console]
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
PUT _component_template/component_template1
|
||||||
|
{
|
||||||
|
"template": {
|
||||||
|
"mappings": {
|
||||||
|
"properties": {
|
||||||
|
"@timestamp": {
|
||||||
|
"type": "date"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PUT _component_template/other_component_template
|
||||||
|
{
|
||||||
|
"template": {
|
||||||
|
"mappings": {
|
||||||
|
"properties": {
|
||||||
|
"ip_address": {
|
||||||
|
"type": "ip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PUT _index_template/template_1
|
PUT _index_template/template_1
|
||||||
{
|
{
|
||||||
"index_patterns": ["te*", "bar*"],
|
"index_patterns": ["te*", "bar*"],
|
||||||
|
@ -233,7 +259,7 @@ When multiple component templates are specified in the `composed_of` field for a
|
||||||
they are merged in the order specified, meaning that later component templates override earlier
|
they are merged in the order specified, meaning that later component templates override earlier
|
||||||
component templates.
|
component templates.
|
||||||
|
|
||||||
For two component templates:
|
For two component templates, the order they are specified changes the number of shards for an index:
|
||||||
|
|
||||||
[source,console]
|
[source,console]
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
@ -245,10 +271,7 @@ PUT /_component_template/template_with_2_shards
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
--------------------------------------------------
|
|
||||||
|
|
||||||
[source,console]
|
|
||||||
--------------------------------------------------
|
|
||||||
PUT /_component_template/template_with_3_shards
|
PUT /_component_template/template_with_3_shards
|
||||||
{
|
{
|
||||||
"template": {
|
"template": {
|
||||||
|
@ -257,12 +280,7 @@ PUT /_component_template/template_with_3_shards
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
--------------------------------------------------
|
|
||||||
|
|
||||||
The order they are specified changes the number of shards for an index:
|
|
||||||
|
|
||||||
[source,console]
|
|
||||||
--------------------------------------------------
|
|
||||||
PUT /_index_template/template_1
|
PUT /_index_template/template_1
|
||||||
{
|
{
|
||||||
"index_patterns": ["t*"],
|
"index_patterns": ["t*"],
|
||||||
|
|
|
@ -290,6 +290,15 @@
|
||||||
reason: "format changed in 7.8 to accomodate V2 index templates"
|
reason: "format changed in 7.8 to accomodate V2 index templates"
|
||||||
features: allowed_warnings
|
features: allowed_warnings
|
||||||
|
|
||||||
|
- do:
|
||||||
|
cluster.put_component_template:
|
||||||
|
name: foo
|
||||||
|
body:
|
||||||
|
template:
|
||||||
|
settings:
|
||||||
|
number_of_shards: 1
|
||||||
|
number_of_replicas: 0
|
||||||
|
|
||||||
- do:
|
- do:
|
||||||
indices.put_template:
|
indices.put_template:
|
||||||
name: test
|
name: test
|
||||||
|
@ -310,7 +319,7 @@
|
||||||
index_patterns: [v2-test]
|
index_patterns: [v2-test]
|
||||||
priority: 4
|
priority: 4
|
||||||
version: 3
|
version: 3
|
||||||
composed_of: [foo, bar]
|
composed_of: [foo]
|
||||||
|
|
||||||
- do:
|
- do:
|
||||||
cat.templates: {}
|
cat.templates: {}
|
||||||
|
@ -332,5 +341,5 @@
|
||||||
\[v2-test\] \s+
|
\[v2-test\] \s+
|
||||||
4 \s+
|
4 \s+
|
||||||
3 \s+
|
3 \s+
|
||||||
\[foo,\ bar\]
|
\[foo\]
|
||||||
/
|
/
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.elasticsearch.common.regex.Regex;
|
||||||
import org.elasticsearch.common.settings.IndexScopedSettings;
|
import org.elasticsearch.common.settings.IndexScopedSettings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
|
@ -246,6 +247,7 @@ public class MetadataIndexTemplateService {
|
||||||
*/
|
*/
|
||||||
public void removeComponentTemplate(final String name, final TimeValue masterTimeout,
|
public void removeComponentTemplate(final String name, final TimeValue masterTimeout,
|
||||||
final ActionListener<AcknowledgedResponse> listener) {
|
final ActionListener<AcknowledgedResponse> listener) {
|
||||||
|
validateNotInUse(clusterService.state().metadata(), name);
|
||||||
clusterService.submitStateUpdateTask("remove-component-template [" + name + "]",
|
clusterService.submitStateUpdateTask("remove-component-template [" + name + "]",
|
||||||
new ClusterStateUpdateTask(Priority.URGENT) {
|
new ClusterStateUpdateTask(Priority.URGENT) {
|
||||||
|
|
||||||
|
@ -291,6 +293,33 @@ public class MetadataIndexTemplateService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates that the given component template is not used by any index
|
||||||
|
* templates, throwing an error if it is still in use
|
||||||
|
*/
|
||||||
|
static void validateNotInUse(Metadata metadata, String templateNameOrWildcard) {
|
||||||
|
final Set<String> matchingComponentTemplates = metadata.componentTemplates().keySet().stream()
|
||||||
|
.filter(name -> Regex.simpleMatch(templateNameOrWildcard, name))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
final Set<String> componentsBeingUsed = new HashSet<>();
|
||||||
|
final List<String> templatesStillUsing = metadata.templatesV2().entrySet().stream()
|
||||||
|
.filter(e -> {
|
||||||
|
Set<String> intersecting = Sets.intersection(new HashSet<>(e.getValue().composedOf()), matchingComponentTemplates);
|
||||||
|
if (intersecting.size() > 0) {
|
||||||
|
componentsBeingUsed.addAll(intersecting);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.map(Map.Entry::getKey)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (templatesStillUsing.size() > 0) {
|
||||||
|
throw new IllegalArgumentException("component templates " + componentsBeingUsed +
|
||||||
|
" cannot be removed as they are still in use by index templates " + templatesStillUsing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the given index template to the cluster state. If {@code create} is true, an
|
* Add the given index template to the cluster state. If {@code create} is true, an
|
||||||
* exception will be thrown if the component template already exists
|
* exception will be thrown if the component template already exists
|
||||||
|
@ -331,6 +360,16 @@ public class MetadataIndexTemplateService {
|
||||||
+ IndexMetadata.INDEX_HIDDEN_SETTING.getKey());
|
+ IndexMetadata.INDEX_HIDDEN_SETTING.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Map<String, ComponentTemplate> componentTemplates = metadata.componentTemplates();
|
||||||
|
final List<String> missingComponentTemplates = template.composedOf().stream()
|
||||||
|
.filter(componentTemplate -> componentTemplates.containsKey(componentTemplate) == false)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (missingComponentTemplates.size() > 0) {
|
||||||
|
throw new InvalidIndexTemplateException(name, "index template [" + name + "] specifies component templates " +
|
||||||
|
missingComponentTemplates + " that do not exist");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterState addIndexTemplateV2(final ClusterState currentState, final boolean create,
|
public ClusterState addIndexTemplateV2(final ClusterState currentState, final boolean create,
|
||||||
|
|
|
@ -55,6 +55,7 @@ import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
@ -785,6 +786,74 @@ public class MetadataIndexTemplateServiceTests extends ESSingleNodeTestCase {
|
||||||
assertThat(resolvedAliases, equalTo(Arrays.asList(a3, a1, a2)));
|
assertThat(resolvedAliases, equalTo(Arrays.asList(a3, a1, a2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAddInvalidTemplate() throws Exception {
|
||||||
|
IndexTemplateV2 template = new IndexTemplateV2(Collections.singletonList("a"), null,
|
||||||
|
Arrays.asList("good", "bad"), null, null, null);
|
||||||
|
ComponentTemplate ct = new ComponentTemplate(new Template(Settings.EMPTY, null, null), null, null);
|
||||||
|
|
||||||
|
final MetadataIndexTemplateService service = getMetadataIndexTemplateService();
|
||||||
|
CountDownLatch ctLatch = new CountDownLatch(1);
|
||||||
|
service.putComponentTemplate("api", randomBoolean(), "good", TimeValue.timeValueSeconds(5), ct,
|
||||||
|
ActionListener.wrap(r -> ctLatch.countDown(), e -> {
|
||||||
|
logger.error("unexpected error", e);
|
||||||
|
fail("unexpected error");
|
||||||
|
}));
|
||||||
|
ctLatch.await(5, TimeUnit.SECONDS);
|
||||||
|
InvalidIndexTemplateException e = expectThrows(InvalidIndexTemplateException.class,
|
||||||
|
() -> {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
AtomicReference<Exception> err = new AtomicReference<>();
|
||||||
|
service.putIndexTemplateV2("api", randomBoolean(), "template", TimeValue.timeValueSeconds(30), template,
|
||||||
|
ActionListener.wrap(r -> fail("should have failed!"), exception -> {
|
||||||
|
err.set(exception);
|
||||||
|
latch.countDown();
|
||||||
|
}));
|
||||||
|
latch.await(5, TimeUnit.SECONDS);
|
||||||
|
if (err.get() != null) {
|
||||||
|
throw err.get();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThat(e.name(), equalTo("template"));
|
||||||
|
assertThat(e.getMessage(), containsString("index template [template] specifies " +
|
||||||
|
"component templates [bad] that do not exist"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRemoveComponentTemplateInUse() throws Exception {
|
||||||
|
IndexTemplateV2 template = new IndexTemplateV2(Collections.singletonList("a"), null,
|
||||||
|
Collections.singletonList("ct"), null, null, null);
|
||||||
|
ComponentTemplate ct = new ComponentTemplate(new Template(null, new CompressedXContent("{}"), null), null, null);
|
||||||
|
|
||||||
|
final MetadataIndexTemplateService service = getMetadataIndexTemplateService();
|
||||||
|
CountDownLatch ctLatch = new CountDownLatch(1);
|
||||||
|
service.putComponentTemplate("api", false, "ct", TimeValue.timeValueSeconds(5), ct,
|
||||||
|
ActionListener.wrap(r -> ctLatch.countDown(), e -> fail("unexpected error")));
|
||||||
|
ctLatch.await(5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
service.putIndexTemplateV2("api", false, "template", TimeValue.timeValueSeconds(30), template,
|
||||||
|
ActionListener.wrap(r -> latch.countDown(), e -> fail("unexpected error")));
|
||||||
|
latch.await(5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||||
|
() -> {
|
||||||
|
AtomicReference<Exception> err = new AtomicReference<>();
|
||||||
|
CountDownLatch errLatch = new CountDownLatch(1);
|
||||||
|
service.removeComponentTemplate("c*", TimeValue.timeValueSeconds(30),
|
||||||
|
ActionListener.wrap(r -> fail("should have failed!"), exception -> {
|
||||||
|
err.set(exception);
|
||||||
|
errLatch.countDown();
|
||||||
|
}));
|
||||||
|
errLatch.await(5, TimeUnit.SECONDS);
|
||||||
|
if (err.get() != null) {
|
||||||
|
throw err.get();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThat(e.getMessage(),
|
||||||
|
containsString("component templates [ct] cannot be removed as they are still in use by index templates [template]"));
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Throwable> putTemplate(NamedXContentRegistry xContentRegistry, PutRequest request) {
|
private static List<Throwable> putTemplate(NamedXContentRegistry xContentRegistry, PutRequest request) {
|
||||||
MetadataCreateIndexService createIndexService = new MetadataCreateIndexService(
|
MetadataCreateIndexService createIndexService = new MetadataCreateIndexService(
|
||||||
Settings.EMPTY,
|
Settings.EMPTY,
|
||||||
|
|
Loading…
Reference in New Issue