Skip to content

Commit

Permalink
Add ignore_missing_component_templates config option (elastic#92436)
Browse files Browse the repository at this point in the history
This change introduces the configuration option `ignore_missing_component_templates` as discussed in elastic#92426 The implementation [option 6](elastic#92426 (comment)) was picked with a slight adjustment meaning no patterns are allowed.

## Implementation

During the creation of an index template, the list of component templates is checked if all component templates exist. This check is extended to skip any component templates which are listed under `ignore_missing_component_templates`. An index template that skips the check for the component template `logs-foo@custom` looks as following:


```
PUT _index_template/logs-foo
{
  "index_patterns": ["logs-foo-*"],
  "data_stream": { },
  "composed_of": ["logs-foo@package", "logs-foo@custom"],
  "ignore_missing_component_templates": ["logs-foo@custom"],
  "priority": 500
}
```

The component template `logs-foo@package` has to exist before creation. It can be created with:

```
PUT _component_template/logs-foo@custom
{
  "template": {
    "mappings": {
      "properties": {
        "host.ip": {
          "type": "ip"
        }
      }
    }
  }
}
```

## Testing

For manual testing, different scenarios can be tested. To simplify testing, the commands from `.http` file are added. Before each test run, a clean cluster is expected.

### New behaviour, missing component template

With the new config option, it must be possible to create an index template with a missing component templates without getting an error:

```
### Add logs-foo@package component template

PUT http://localhost:9200/
    _component_template/logs-foo@package
Authorization: Basic elastic password
Content-Type: application/json

{
  "template": {
    "mappings": {
      "properties": {
        "host.name": {
          "type": "keyword"
        }
      }
    }
  }
}

### Add logs-foo index template

PUT http://localhost:9200/
    _index_template/logs-foo
Authorization: Basic elastic password
Content-Type: application/json

{
  "index_patterns": ["logs-foo-*"],
  "data_stream": { },
  "composed_of": ["logs-foo@package", "logs-foo@custom"],
  "ignore_missing_component_templates": ["logs-foo@custom"],
  "priority": 500
}

### Create data stream

PUT http://localhost:9200/
    _data_stream/logs-foo-bar
Authorization: Basic elastic password
Content-Type: application/json

### Check if mappings exist

GET http://localhost:9200/
    logs-foo-bar
Authorization: Basic elastic password
Content-Type: application/json
```

It is checked if all templates could be created and data stream mappings are correct.

### Old behaviour, with all component templates

In the following, a component template is made optional but it already exists. It is checked, that it will show up in the mappings:

```
### Add logs-foo@package component template

PUT http://localhost:9200/
    _component_template/logs-foo@package
Authorization: Basic elastic password
Content-Type: application/json

{
  "template": {
    "mappings": {
      "properties": {
        "host.name": {
          "type": "keyword"
        }
      }
    }
  }
}

### Add logs-foo@custom component template

PUT http://localhost:9200/
    _component_template/logs-foo@custom
Authorization: Basic elastic password
Content-Type: application/json

{
  "template": {
    "mappings": {
      "properties": {
        "host.ip": {
          "type": "ip"
        }
      }
    }
  }
}

### Add logs-foo index template

PUT http://localhost:9200/
    _index_template/logs-foo
Authorization: Basic elastic password
Content-Type: application/json

{
  "index_patterns": ["logs-foo-*"],
  "data_stream": { },
  "composed_of": ["logs-foo@package", "logs-foo@custom"],
  "ignore_missing_component_templates": ["logs-foo@custom"],
  "priority": 500
}

### Create data stream

PUT http://localhost:9200/
    _data_stream/logs-foo-bar
Authorization: Basic elastic password
Content-Type: application/json

### Check if mappings exist

GET http://localhost:9200/
    logs-foo-bar
Authorization: Basic elastic password
Content-Type: application/json
```

### Check old behaviour

Ensure, that the old behaviour still exists when a component template is used that is not part of `ignore_missing_component_templates`: 

```
### Add logs-foo index template

PUT http://localhost:9200/
    _index_template/logs-foo
Authorization: Basic elastic password
Content-Type: application/json

{
  "index_patterns": ["logs-foo-*"],
  "data_stream": { },
  "composed_of": ["logs-foo@package", "logs-foo@custom"],
  "ignore_missing_component_templates": ["logs-foo@custom"],
  "priority": 500
}
```

Co-authored-by: Lee Hinman <dakrone@users.noreply.github.com>
  • Loading branch information
2 people authored and mark-vieira committed Jan 31, 2023
1 parent 845178b commit 85a3187
Show file tree
Hide file tree
Showing 9 changed files with 423 additions and 19 deletions.
6 changes: 6 additions & 0 deletions docs/changelog/92436.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 92436
summary: Add `ignore_missing_component_templates` config option
area: Indices APIs
type: enhancement
issues:
- 92426
95 changes: 95 additions & 0 deletions docs/reference/indices/ignore-missing-component-templates.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
[[ignore_missing_component_templates]]
== Config ignore_missing_component_templates

The configuration option `ignore_missing_component_templates` can be used when an index template references a component template that might not exist. Every time a data stream is created based on the index template, the existence of the component template will be checked. If it exists, it will used to form the index's composite settings. If it does not exist, it is ignored.

=== Usage example

In the following, one component template and an index template are created. The index template references two component templates, but only the `@package` one exists.


Create the component template `logs-foo_component1`. This has to be created before the index template as it is not optional:

[source,console]
----
PUT _component_template/logs-foo_component1
{
"template": {
"mappings": {
"properties": {
"host.name": {
"type": "keyword"
}
}
}
}
}
----

Next, the index template will be created and it references two component templates:

[source,JSON]
----
"composed_of": ["logs-foo_component1", "logs-foo_component2"]
----

Before, only the `logs-foo_component1` compontent template was created, meaning the `logs-foo_component2` is missing. Because of this the following entry was added to the config:

[source,JSON]
----
"ignore_missing_component_templates": ["logs-foo_component2"],
----

During creation of the template, it will not validate that `logs-foo_component2` exists:


[source,console]
----
PUT _index_template/logs-foo
{
"index_patterns": ["logs-foo-*"],
"data_stream": { },
"composed_of": ["logs-foo_component1", "logs-foo_component2"],
"ignore_missing_component_templates": ["logs-foo_component2"],
"priority": 500
}
----
// TEST[continued]

The index template `logs-foo` was successfully created. A data stream can be created based on this template:

[source,console]
----
PUT _data_stream/logs-foo-bar
----
// TEST[continued]

Looking at the mappings of the data stream, it will contain the `host.name` field.

At a later stage, the missing component template might be added:

[source,console]
----
PUT _component_template/logs-foo_component2
{
"template": {
"mappings": {
"properties": {
"host.ip": {
"type": "ip"
}
}
}
}
}
----
// TEST[continued]

This will not have an immediate effect on the data stream. The mapping `host.ip` will only show up in the data stream mappings when the data stream is rolled over automatically next time or a manual rollover is triggered:

[source,console]
----
POST logs-foo-bar/_rollover
----
// TEST[continued]
// TEST[teardown:data_stream_cleanup]
2 changes: 2 additions & 0 deletions docs/reference/indices/index-templates.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,5 @@ DELETE _component_template/component_template1
////

include::simulate-multi-component-templates.asciidoc[]

include::ignore-missing-component-templates.asciidoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,66 @@

- is_false: purple-index.mappings.properties.nested.include_in_root
- is_true: purple-index.mappings.properties.nested.include_in_parent

---
"Index template ignore_missing_component_template valid":
- skip:
version: " - 8.6.99"
reason: "index template v2 ignore_missing_component_template config not available before 8.7"
features: allowed_warnings

- do:
cluster.put_component_template:
name: red
body:
template:
mappings:
properties:
foo:
type: keyword

- do:
allowed_warnings:
- "index template [blue] has index patterns [purple-index] matching patterns from existing older templates [global] with patterns (global => [*]); this template [blue] will take precedence during new index creation"
indices.put_index_template:
name: blue
body:
index_patterns: ["purple-index"]
composed_of: ["red", "blue"]
ignore_missing_component_templates: ["blue"]

- do:
indices.create:
index: purple-index

- do:
indices.get:
index: purple-index

- match: {purple-index.mappings.properties.foo: {type: keyword}}

---
"Index template ignore_missing_component_template invalid":
- skip:
version: " - 8.6.99"
reason: "index template v2 ignore_missing_component_template config not available before 8.7"
features: allowed_warnings

- do:
cluster.put_component_template:
name: red
body:
template:
mappings:
properties:
foo:
type: keyword

- do:
catch: /index_template \[blue\] invalid, cause \[index template \[blue\] specifies a missing component templates \[blue\] that does not exist/
indices.put_index_template:
name: blue
body:
index_patterns: ["purple-index"]
composed_of: ["red", "blue"]
ignore_missing_component_templates: ["foo"]
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.elasticsearch.cluster.metadata;

import org.elasticsearch.TransportVersion;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.Diff;
import org.elasticsearch.cluster.SimpleDiffable;
import org.elasticsearch.common.Strings;
Expand Down Expand Up @@ -46,6 +47,7 @@ public class ComposableIndexTemplate implements SimpleDiffable<ComposableIndexTe
private static final ParseField METADATA = new ParseField("_meta");
private static final ParseField DATA_STREAM = new ParseField("data_stream");
private static final ParseField ALLOW_AUTO_CREATE = new ParseField("allow_auto_create");
private static final ParseField IGNORE_MISSING_COMPONENT_TEMPLATES = new ParseField("ignore_missing_component_templates");

@SuppressWarnings("unchecked")
public static final ConstructingObjectParser<ComposableIndexTemplate, Void> PARSER = new ConstructingObjectParser<>(
Expand All @@ -59,7 +61,8 @@ public class ComposableIndexTemplate implements SimpleDiffable<ComposableIndexTe
(Long) a[4],
(Map<String, Object>) a[5],
(DataStreamTemplate) a[6],
(Boolean) a[7]
(Boolean) a[7],
(List<String>) a[8]
)
);

Expand All @@ -72,6 +75,7 @@ public class ComposableIndexTemplate implements SimpleDiffable<ComposableIndexTe
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.map(), METADATA);
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), DataStreamTemplate.PARSER, DATA_STREAM);
PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), ALLOW_AUTO_CREATE);
PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), IGNORE_MISSING_COMPONENT_TEMPLATES);
}

private final List<String> indexPatterns;
Expand All @@ -89,6 +93,8 @@ public class ComposableIndexTemplate implements SimpleDiffable<ComposableIndexTe
private final DataStreamTemplate dataStreamTemplate;
@Nullable
private final Boolean allowAutoCreate;
@Nullable
private final List<String> ignoreMissingComponentTemplates;

static Diff<ComposableIndexTemplate> readITV2DiffFrom(StreamInput in) throws IOException {
return SimpleDiffable.readDiffFrom(ComposableIndexTemplate::new, in);
Expand All @@ -106,7 +112,7 @@ public ComposableIndexTemplate(
@Nullable Long version,
@Nullable Map<String, Object> metadata
) {
this(indexPatterns, template, componentTemplates, priority, version, metadata, null, null);
this(indexPatterns, template, componentTemplates, priority, version, metadata, null, null, null);
}

public ComposableIndexTemplate(
Expand All @@ -118,7 +124,7 @@ public ComposableIndexTemplate(
@Nullable Map<String, Object> metadata,
@Nullable DataStreamTemplate dataStreamTemplate
) {
this(indexPatterns, template, componentTemplates, priority, version, metadata, dataStreamTemplate, null);
this(indexPatterns, template, componentTemplates, priority, version, metadata, dataStreamTemplate, null, null);
}

public ComposableIndexTemplate(
Expand All @@ -130,6 +136,20 @@ public ComposableIndexTemplate(
@Nullable Map<String, Object> metadata,
@Nullable DataStreamTemplate dataStreamTemplate,
@Nullable Boolean allowAutoCreate
) {
this(indexPatterns, template, componentTemplates, priority, version, metadata, dataStreamTemplate, null, null);
}

public ComposableIndexTemplate(
List<String> indexPatterns,
@Nullable Template template,
@Nullable List<String> componentTemplates,
@Nullable Long priority,
@Nullable Long version,
@Nullable Map<String, Object> metadata,
@Nullable DataStreamTemplate dataStreamTemplate,
@Nullable Boolean allowAutoCreate,
@Nullable List<String> ignoreMissingComponentTemplates
) {
this.indexPatterns = indexPatterns;
this.template = template;
Expand All @@ -139,6 +159,7 @@ public ComposableIndexTemplate(
this.metadata = metadata;
this.dataStreamTemplate = dataStreamTemplate;
this.allowAutoCreate = allowAutoCreate;
this.ignoreMissingComponentTemplates = ignoreMissingComponentTemplates;
}

public ComposableIndexTemplate(StreamInput in) throws IOException {
Expand All @@ -154,6 +175,11 @@ public ComposableIndexTemplate(StreamInput in) throws IOException {
this.metadata = in.readMap();
this.dataStreamTemplate = in.readOptionalWriteable(DataStreamTemplate::new);
this.allowAutoCreate = in.readOptionalBoolean();
if (in.getVersion().onOrAfter(Version.V_8_7_0)) {
this.ignoreMissingComponentTemplates = in.readOptionalStringList();
} else {
this.ignoreMissingComponentTemplates = null;
}
}

public List<String> indexPatterns() {
Expand Down Expand Up @@ -204,6 +230,11 @@ public Boolean getAllowAutoCreate() {
return this.allowAutoCreate;
}

@Nullable
public List<String> getIgnoreMissingComponentTemplates() {
return ignoreMissingComponentTemplates;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeStringCollection(this.indexPatterns);
Expand All @@ -219,6 +250,9 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeGenericMap(this.metadata);
out.writeOptionalWriteable(dataStreamTemplate);
out.writeOptionalBoolean(allowAutoCreate);
if (out.getVersion().onOrAfter(Version.V_8_7_0)) {
out.writeOptionalStringCollection(ignoreMissingComponentTemplates);
}
}

@Override
Expand Down Expand Up @@ -246,6 +280,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
if (this.allowAutoCreate != null) {
builder.field(ALLOW_AUTO_CREATE.getPreferredName(), allowAutoCreate);
}
if (this.ignoreMissingComponentTemplates != null) {
builder.stringListField(IGNORE_MISSING_COMPONENT_TEMPLATES.getPreferredName(), ignoreMissingComponentTemplates);
}
builder.endObject();
return builder;
}
Expand All @@ -260,7 +297,8 @@ public int hashCode() {
this.version,
this.metadata,
this.dataStreamTemplate,
this.allowAutoCreate
this.allowAutoCreate,
this.ignoreMissingComponentTemplates
);
}

Expand All @@ -280,7 +318,8 @@ && componentTemplatesEquals(this.componentTemplates, other.componentTemplates)
&& Objects.equals(this.version, other.version)
&& Objects.equals(this.metadata, other.metadata)
&& Objects.equals(this.dataStreamTemplate, other.dataStreamTemplate)
&& Objects.equals(this.allowAutoCreate, other.allowAutoCreate);
&& Objects.equals(this.allowAutoCreate, other.allowAutoCreate)
&& Objects.equals(this.ignoreMissingComponentTemplates, other.ignoreMissingComponentTemplates);
}

static boolean componentTemplatesEquals(List<String> c1, List<String> c2) {
Expand Down Expand Up @@ -421,6 +460,7 @@ public static class Builder {
private Map<String, Object> metadata;
private DataStreamTemplate dataStreamTemplate;
private Boolean allowAutoCreate;
private List<String> ignoreMissingComponentTemplates;

public Builder() {}

Expand Down Expand Up @@ -464,6 +504,11 @@ public Builder allowAutoCreate(Boolean allowAutoCreate) {
return this;
}

public Builder ignoreMissingComponentTemplates(List<String> ignoreMissingComponentTemplates) {
this.ignoreMissingComponentTemplates = ignoreMissingComponentTemplates;
return this;
}

public ComposableIndexTemplate build() {
return new ComposableIndexTemplate(
this.indexPatterns,
Expand All @@ -473,7 +518,8 @@ public ComposableIndexTemplate build() {
this.version,
this.metadata,
this.dataStreamTemplate,
this.allowAutoCreate
this.allowAutoCreate,
this.ignoreMissingComponentTemplates
);
}
}
Expand Down
Loading

0 comments on commit 85a3187

Please sign in to comment.