Handle v2 template with index.hidden setting#55015
Conversation
This commit handles the case where the user request doesn't explicitly set the index as hidden but a mathcing template has the `index.hidden` setting configured to true. In this case the global templates need to be excluded.
|
Pinging @elastic/es-core-features (:Core/Features/Indices APIs) |
| throw new IllegalStateException("A global index V2 template [" + indexTemplateV2ToNameEntry.getValue() + | ||
| "] defined the index hidden setting, which is not allowed"); |
There was a problem hiding this comment.
#55010 guards against this when creating a v2 template
| // then we need to exclude global templates | ||
| if (isHidden == null) { | ||
| final Optional<Map.Entry<IndexTemplateV2, String>> templateWithHiddenSetting = matchedTemplates.entrySet().stream() | ||
| .filter(entry -> IndexMetadata.INDEX_HIDDEN_SETTING.exists(resolveSettings(metadata, entry.getValue()))).findFirst(); |
There was a problem hiding this comment.
So this brings a question:
- Should we throw an error (the error below for validation) on index creation for a template that won't actually "win" for the creation?
If the answer is yes, then I think rather than returning only the first one, it would be good to return all templates (in the error message I mean) that exhibit this behavior.
If the answer is no, then we can move this check to be below the candidate sorting and only check the winner template.
What do you think?
There was a problem hiding this comment.
For me the answer is no and we should only be interested in winning template - index can became hidden only by the means of the winner, doesn't matter what is in other templates.
So I'd move the code after winner is selected.
I'd also change sorting to linear min selection to avoid creating new list and sorting candidates that won't win:
IndexTemplateV2 winner = matchedTemplates.keySet().stream().min(Comparator.comparing(IndexTemplateV2::priority,
Comparator.nullsLast(Comparator.reverseOrder()))).get();There was a problem hiding this comment.
Great points. Thanks for bringing this up.
I saw this validation as "filter the invalid templates before we find the winner template" (ie. the global templates should not even be considered when finding the winning template).
The other way around, as I understand it, applying this validation on the winner template would be a no-op (since we don't support inheritance anymore, so settings from the global templates cannot be applied) and would look something along the lines of:
- the winning template is a global template - no index.hiddne property is allowed on the global templates - so just return it
- the winning template is not a global one but it has an
index.hiddensetting set to true in the resolved settings, but the other templates have a lower priority so just return it - the winning template is not a global one and doesn't have
index.hiddenset, so just return it
I do agree that having a lower priority (an explicit template) influence and remove a higher priority template (the global one - as illustrated in this test https://github.com/elastic/elasticsearch/pull/55015/files#diff-4e391d69e5a1fe53728ba9530499a6e8R552-R554) from the list of candidates might be (is) counter-intuitive so I think we either go along with this approach or, given the user has the option to override a global template using the priority, we could just issue a warning that a gloabl template is used as the matching one but that it is shadowing explicit templates that try to configure the index as hidden (I do however think this would go unnoticed in the logs, so treating this as expected and dropping this complex use case would also be an option I think)
There was a problem hiding this comment.
Ok, we decided to validate only the winner in this particular case. Generally (together with #55010) we will be failing if the index.hidden setting is present in the resolved settings of a global template (regardless of the setting value).
With regards to this particular validation, if the winner is a global template we'll do a sanity check to make sure it doesn't include the index.hidden setting, in which case we'll fail, but otherwise we'll return it as the winner template.
probakowski
left a comment
There was a problem hiding this comment.
Thanks Andrei, I left few comments
| // then we need to exclude global templates | ||
| if (isHidden == null) { | ||
| final Optional<Map.Entry<IndexTemplateV2, String>> templateWithHiddenSetting = matchedTemplates.entrySet().stream() | ||
| .filter(entry -> IndexMetadata.INDEX_HIDDEN_SETTING.exists(resolveSettings(metadata, entry.getValue()))).findFirst(); |
There was a problem hiding this comment.
For me the answer is no and we should only be interested in winning template - index can became hidden only by the means of the winner, doesn't matter what is in other templates.
So I'd move the code after winner is selected.
I'd also change sorting to linear min selection to avoid creating new list and sorting candidates that won't win:
IndexTemplateV2 winner = matchedTemplates.keySet().stream().min(Comparator.comparing(IndexTemplateV2::priority,
Comparator.nullsLast(Comparator.reverseOrder()))).get();|
|
||
| // this is complex but if the index is not hidden in the create request but is hidden as the result of template application, | ||
| // then we need to exclude global templates | ||
| if (isHidden == null) { |
There was a problem hiding this comment.
This can be simplified to something like this:
if (isHidden == null) {
matchedTemplates.entrySet().stream()
.filter(entry -> IndexMetadata.INDEX_HIDDEN_SETTING.get(resolveSettings(metadata, entry.getValue())))
// relies on the fact that default for INDEX_HIDDEN_SETTING is false
.findAny()
.ifPresent(indexTemplateV2ToNameEntry -> {
matchedTemplates.keySet().removeIf(current -> current.indexPatterns().stream().anyMatch(Regex::isMatchAllPattern));
if (matchedTemplates.containsKey(indexTemplateV2ToNameEntry.getKey()) == false) {
throw new IllegalStateException("A global index V2 template [" + indexTemplateV2ToNameEntry.getValue() +
"] defined the index hidden setting, which is not allowed");
}
});
}There was a problem hiding this comment.
On the second iteration on this I think there's a problem (fixed by proposal above): we can have 2 templates one with INDEX_HIDDEN_SETTING=false (explicitly set) and one with INDEX_HIDDEN_SETTING=true, findFirst can pick up false one and skip the whole block despite the fact that there's still one template with true
|
@elasticmachine update branch |
|
@dakrone @probakowski Looking at the new code I believe we can drop the What do you think? |
|
@andreidan I think it's reasonable to drop the null check, we're doing everything we can to prevent these templates from being settable. The |
dakrone
left a comment
There was a problem hiding this comment.
LGTM (with the null check removal), I left one really minor nit
|
|
||
| /** | ||
| * Add the given component 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 |
server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java
Outdated
Show resolved
Hide resolved
Co-Authored-By: Lee Hinman <dakrone@users.noreply.github.com>
|
@elasticmachine update branch |
This validates that if the winner v2 template is a global one, it doesn't specify the index.hidden setting. (cherry picked from commit 19a97f7) Signed-off-by: Andrei Dan <andrei.dan@elastic.co>
isHidden was a `Boolean` in order to treat a special case identified with V1 templates where if the create index request didn't specify if the index should be hidden or not (ie. isHidden was `null`) but the index matched a template that specified the `index.hidden` setting we needed to remove the global templates from the templates we'll apply to the new index (note: this is important with V1 templates as inheritance is supported). With V2 templates we match only one template with an index so the equivalent check did not need to exist (we added a sanity check in elastic#55015 where we make sure we don't apply an invalid global template - one that specifes the `index.hidden` setting, but this is a check we make irrespective of the user specifying or not if the index should be hidden) This commit makes `isHidden` when matching V2 templates a boolean primitive, eliminating the need for the `null` state to exist. Note that some methods which use the matching V2 templates still work with a `Boolean` object `isHidden` attribute as they are also matching the V1 templates. These methods will pass in `false` instead of `null` when finding the V2 templates.
isHidden was a `Boolean` in order to treat a special case identified with V1 templates where if the create index request didn't specify if the index should be hidden or not (ie. isHidden was `null`) but the index matched a template that specified the `index.hidden` setting we needed to remove the global templates from the templates we'll apply to the new index (note: this is important with V1 templates as inheritance is supported). With V2 templates we match only one template with an index so the equivalent check did not need to exist (we added a sanity check in #55015 where we make sure we don't apply an invalid global template - one that specifes the `index.hidden` setting, but this is a check we make irrespective of the user specifying or not if the index should be hidden) This commit makes `isHidden` when matching V2 templates a boolean primitive, eliminating the need for the `null` state to exist. Note that some methods which use the matching V2 templates still work with a `Boolean` object `isHidden` attribute as they are also matching the V1 templates. These methods will pass in `false` instead of `null` when finding the V2 templates.
isHidden was a `Boolean` in order to treat a special case identified with V1 templates where if the create index request didn't specify if the index should be hidden or not (ie. isHidden was `null`) but the index matched a template that specified the `index.hidden` setting we needed to remove the global templates from the templates we'll apply to the new index (note: this is important with V1 templates as inheritance is supported). With V2 templates we match only one template with an index so the equivalent check did not need to exist (we added a sanity check in elastic#55015 where we make sure we don't apply an invalid global template - one that specifes the `index.hidden` setting, but this is a check we make irrespective of the user specifying or not if the index should be hidden) This commit makes `isHidden` when matching V2 templates a boolean primitive, eliminating the need for the `null` state to exist. Note that some methods which use the matching V2 templates still work with a `Boolean` object `isHidden` attribute as they are also matching the V1 templates. These methods will pass in `false` instead of `null` when finding the V2 templates. (cherry picked from commit c5b923a) Signed-off-by: Andrei Dan <andrei.dan@elastic.co>
isHidden was a `Boolean` in order to treat a special case identified with V1 templates where if the create index request didn't specify if the index should be hidden or not (ie. isHidden was `null`) but the index matched a template that specified the `index.hidden` setting we needed to remove the global templates from the templates we'll apply to the new index (note: this is important with V1 templates as inheritance is supported). With V2 templates we match only one template with an index so the equivalent check did not need to exist (we added a sanity check in #55015 where we make sure we don't apply an invalid global template - one that specifes the `index.hidden` setting, but this is a check we make irrespective of the user specifying or not if the index should be hidden) This commit makes `isHidden` when matching V2 templates a boolean primitive, eliminating the need for the `null` state to exist. Note that some methods which use the matching V2 templates still work with a `Boolean` object `isHidden` attribute as they are also matching the V1 templates. These methods will pass in `false` instead of `null` when finding the V2 templates. (cherry picked from commit c5b923a) Signed-off-by: Andrei Dan <andrei.dan@elastic.co>
This validates that if the winner v2 template is a global one, it doesn't specify the
index.hidden setting.