Skip to content

Commit 12c2693

Browse files
Merge branch 'main' into date-nanos-implicit-casting-behind-snapshot
2 parents 3ac7e58 + 3a69d45 commit 12c2693

File tree

25 files changed

+563
-397
lines changed

25 files changed

+563
-397
lines changed

docs/changelog/128866.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 128866
2+
summary: Add `age_in_millis` to ILM Explain Response
3+
area: ILM+SLM
4+
type: enhancement
5+
issues:
6+
- 103659

docs/reference/query-languages/esql/README.md

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,95 @@ To help differentiate between the static and generated content, the generated co
105105
% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
106106
```
107107

108+
## Version differentiation in Docs V3
109+
110+
> [!IMPORTANT]
111+
> Starting with 9.0, we no longer publish separate documentation branches for every minor release (`9.0`, `9.1`, `9.2`, etc.).
112+
> This means there won't be a different page for `9.1`, `9.2`, and so on. Instead, all changes landing in subsequent minor releases **will appear on the same page**.
113+
114+
Because we now publish just one docs set off of the `main` branch, we use the [`applies_to` metadata](https://elastic.github.io/docs-builder/syntax/applies/) to differentiate features and their availability across different versions. This is a [cumulative approach](https://elastic.github.io/docs-builder/contribute/#cumulative-docs): instead of creating separate pages for each product and release, we update a **single page** with product- and version-specific details over time.
115+
116+
`applies_to` allows us to clearly communicate when features are introduced, when they transition from preview to GA, and which versions support specific functionality.
117+
118+
This metadata accepts a lifecycle and an optional version.
119+
120+
### Functions and operators
121+
122+
Use the `@FunctionAppliesTo` annotation within the `@FunctionInfo` annotation on function and operator classes to specify the lifecycle and version for functions and operators.
123+
124+
For example, to indicate that a function is in technical preview and applies to version 9.0.0, you would use:
125+
126+
```java
127+
@FunctionInfo(
128+
returnType = "boolean",
129+
appliesTo = {
130+
@FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.PREVIEW, version = "9.0.0")
131+
},
132+
...
133+
)
134+
```
135+
136+
When a feature evolves from preview in `9.0` to GA in `9.2`, add a new entry alongside the existing preview entry and remove the `preview = true` boolean:
137+
138+
```java
139+
@FunctionInfo(
140+
returnType = "boolean",
141+
preview = false, // the preview boolean can be removed (or flipped to false) when the function becomes GA
142+
appliesTo = {
143+
@FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.PREVIEW, version = "9.0.0"),
144+
@FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.GA, version = "9.2.0")
145+
},
146+
...
147+
)
148+
```
149+
150+
We updated [`DocsV3Support.java`](https://github.com/elastic/elasticsearch/blob/main/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/DocsV3Support.java) to generate the `applies_to` metadata correctly for functions and operators.
151+
152+
### Inline `applies_to` metadata
153+
154+
Use [inline annotations](https://elastic.github.io/docs-builder/syntax/applies/#inline-annotations) to specify `applies_to` metadata in descriptions, parameter lists, etc.
155+
156+
For example, the second item in this list is in technical preview as of version 9.2:
157+
158+
```markdown
159+
- Item 1
160+
- Item 2 {applies_to}`stack: preview 9.2.`
161+
```
162+
163+
### Key rules
164+
165+
1. **Use the `preview = true` boolean** for any tech preview feature - this is required for the Kibana inline docs
166+
- **Remove `preview = true`** only when the feature becomes GA on serverless and is _definitely_ going GA in the next minor release
167+
2. **Never delete `appliesTo` entries** - only add new ones as features evolve from preview to GA
168+
3. **Use specific versions** (`9.0.0`, `9.1.0`) when known, or just `PREVIEW` without a version if timing is uncertain
169+
4. **Add `applies_to` to examples** where necessary
170+
171+
> [!IMPORTANT]
172+
> We don't use `applies_to` in the legacy asciidoc system for 8.x and earlier versions.
173+
174+
### Supported lifecycles
175+
176+
- `PREVIEW` - Feature is in technical preview
177+
- `GA` - Feature is generally available
178+
- `DEPRECATED` - Feature is deprecated and will be removed in a future release
179+
- `UNAVAILABLE` - Feature is not available in the current version, but may be available in future releases
180+
181+
> [!NOTE]
182+
> Unreleased version information is automatically sanitized in the docs build output. For example, say you specify `preview 9.3.0`:
183+
> - Before `9.3.0` is released, the live documentation will display "Planned for a future release" instead of the specific version number.
184+
> - This will be updated automatically when the version is released.
185+
108186
## Tutorials
109187

110188
### Adding a new command
111189

112190
When adding a new command, for example adding the `CHANGE_POINT` command, do the following:
113191
1. Create a new file in the `_snippets/commands/layout` directory with the name of the command, for example `change_point.md`.
114-
2. Add the content for the command to the file. See other files in this directory for examples.
115-
3. Add the command to the list in `_snippets/lists/processing-commands.md`.
116-
4. Add an include directive to the `commands/processing-commands.md` file to include the new command.
117-
5. Add tested examples to the `_snippets/commands/examples` directory. See below for details.
192+
2. Ensure to specify what versions the command applies to. See [Version differentiation in Docs V3](#version-differentiation-in-docs-v3) for details. [Example PR](https://github.com/elastic/elasticsearch/pull/130314/files#diff-0ab90b6202c5d9eeea75dc95a7cb71dc4d720230342718bff887816771a5a803R3-R6).
193+
3. Add the content for the command to the file. See other files in this directory for examples.
194+
4. Add the command to the list in `_snippets/lists/processing-commands.md`.
195+
5. Add an include directive to the `commands/processing-commands.md` file to include the new command.
196+
6. Add tested examples to the `_snippets/commands/examples` directory. See below for details.
118197

119198
### Adding examples to commands
120199

modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/240_data_stream_settings.yml

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,6 @@ setup:
327327
index: "my-data-stream-1"
328328
wait_for_status: green
329329

330-
331-
332330
- do:
333331
indices.get_data_stream:
334332
name: my-data-stream-1
@@ -398,3 +396,96 @@ setup:
398396
- match: { .$idx0name.settings.index.number_of_shards: "1" }
399397
- match: { .$idx0name.settings.index.lifecycle.name: "my-policy" }
400398
- match: { .$idx0name.settings.index.lifecycle.prefer_ilm: "true" }
399+
400+
---
401+
"Test null out settings component templates only":
402+
- requires:
403+
cluster_features: [ "logs_stream" ]
404+
reason: requires setting 'logs_stream' to get or set data stream settings
405+
406+
- do:
407+
cluster.put_component_template:
408+
name: settings-template
409+
body:
410+
template:
411+
settings:
412+
lifecycle.name: my-policy
413+
414+
- do:
415+
allowed_warnings:
416+
- "index template [my-component-only-template] has index patterns [my-component-only-data-stream-*] matching patterns from existing older templates [global] with patterns (global => [*]); this template [my-component-only-template] will take precedence during new index creation"
417+
indices.put_index_template:
418+
name: my-component-only-template
419+
body:
420+
index_patterns: [ my-component-only-data-stream-* ]
421+
data_stream: { }
422+
composed_of:
423+
- settings-template
424+
425+
- do:
426+
indices.create_data_stream:
427+
name: my-component-only-data-stream-1
428+
429+
- do:
430+
cluster.health:
431+
index: "my-component-only-data-stream-1"
432+
wait_for_status: green
433+
434+
- do:
435+
indices.get_data_stream:
436+
name: my-component-only-data-stream-1
437+
- match: { data_streams.0.name: my-component-only-data-stream-1 }
438+
- match: { data_streams.0.settings: {} }
439+
- match: { data_streams.0.effective_settings: null }
440+
441+
- do:
442+
indices.put_data_stream_settings:
443+
name: my-component-only-data-stream-1
444+
body:
445+
index:
446+
lifecycle:
447+
name: my-new-policy
448+
prefer_ilm: true
449+
- match: { data_streams.0.name: my-component-only-data-stream-1 }
450+
- match: { data_streams.0.applied_to_data_stream: true }
451+
- length: { data_streams.0.index_settings_results.applied_to_data_stream_and_backing_indices: 2 }
452+
- match: { data_streams.0.settings.index.lifecycle.name: "my-new-policy" }
453+
- match: { data_streams.0.settings.index.lifecycle.prefer_ilm: "true" }
454+
- match: { data_streams.0.effective_settings.index.lifecycle.name: "my-new-policy" }
455+
- match: { data_streams.0.effective_settings.index.lifecycle.prefer_ilm: "true" }
456+
457+
- do:
458+
indices.put_data_stream_settings:
459+
name: my-component-only-data-stream-1
460+
body:
461+
index:
462+
lifecycle:
463+
name: null
464+
prefer_ilm: null
465+
- match: { data_streams.0.name: my-component-only-data-stream-1 }
466+
- match: { data_streams.0.applied_to_data_stream: true }
467+
- length: { data_streams.0.index_settings_results.applied_to_data_stream_and_backing_indices: 2 }
468+
- match: { data_streams.0.settings.index.lifecycle.name: null }
469+
- match: { data_streams.0.settings.index.lifecycle.prefer_ilm: null }
470+
- match: { data_streams.0.effective_settings.index.lifecycle.name: "my-policy" }
471+
- match: { data_streams.0.effective_settings.index.lifecycle.prefer_ilm: null }
472+
473+
- do:
474+
indices.get_data_stream_settings:
475+
name: my-component-only-data-stream-1
476+
- match: { data_streams.0.name: my-component-only-data-stream-1 }
477+
- match: { data_streams.0.settings.index.lifecycle.name: null }
478+
- match: { data_streams.0.settings.index.lifecycle.prefer_ilm: null }
479+
- match: { data_streams.0.effective_settings.index.lifecycle.name: "my-policy" }
480+
- match: { data_streams.0.effective_settings.index.lifecycle.prefer_ilm: null }
481+
482+
- do:
483+
indices.get_data_stream:
484+
name: my-component-only-data-stream-1
485+
- set: { data_streams.0.indices.0.index_name: idx0name }
486+
487+
- do:
488+
indices.get_settings:
489+
index: my-component-only-data-stream-1
490+
- match: { .$idx0name.settings.index.lifecycle.name: "my-policy" }
491+
- match: { .$idx0name.settings.index.lifecycle.prefer_ilm: null }

server/src/main/java/org/elasticsearch/cluster/metadata/MetadataDataStreamsService.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -480,10 +480,9 @@ private DataStream createDataStreamForUpdatedDataStreamSettings(
480480
ProjectMetadata projectMetadata = clusterState.metadata().getProject(projectId);
481481
Map<String, DataStream> dataStreamMap = projectMetadata.dataStreams();
482482
DataStream dataStream = dataStreamMap.get(dataStreamName);
483-
Settings existingSettings = dataStream.getSettings();
483+
Settings existingDataStreamSettings = dataStream.getSettings();
484484

485-
Template.Builder templateBuilder = Template.builder();
486-
Settings.Builder mergedSettingsBuilder = Settings.builder().put(existingSettings).put(settingsOverrides);
485+
Settings.Builder mergedSettingsBuilder = Settings.builder().put(existingDataStreamSettings).put(settingsOverrides);
487486
/*
488487
* A null value for a setting override means that we remove it from the data stream, and let the value from the template (if any)
489488
* be used.
@@ -493,18 +492,18 @@ private DataStream createDataStreamForUpdatedDataStreamSettings(
493492
mergedSettingsBuilder.remove(key);
494493
}
495494
});
496-
Settings mergedSettings = mergedSettingsBuilder.build();
495+
Settings mergedDataStreamSettings = mergedSettingsBuilder.build();
497496

498497
final ComposableIndexTemplate template = lookupTemplateForDataStream(dataStreamName, projectMetadata);
499-
ComposableIndexTemplate mergedTemplate = template.mergeSettings(mergedSettings);
498+
Settings templateSettings = MetadataIndexTemplateService.resolveSettings(template, projectMetadata.componentTemplates());
499+
Settings mergedEffectiveSettings = templateSettings.merge(mergedDataStreamSettings);
500500
MetadataIndexTemplateService.validateTemplate(
501-
mergedTemplate.template().settings(),
501+
mergedEffectiveSettings,
502502
dataStream.getEffectiveMappings(projectMetadata),
503503
indicesService
504504
);
505505

506-
templateBuilder.settings(mergedSettingsBuilder);
507-
return dataStream.copy().setSettings(mergedSettings).build();
506+
return dataStream.copy().setSettings(mergedDataStreamSettings).build();
508507
}
509508

510509
private DataStream createDataStreamForUpdatedDataStreamMappings(

server/src/test/java/org/elasticsearch/index/mapper/flattened/FlattenedFieldMapperTests.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,35 @@ public void testSyntheticSourceWithEmptyObject() throws IOException {
931931
{"field":{"key1":"foo"}}"""));
932932
}
933933

934+
public void testSyntheticSourceWithMatchesInNestedPath() throws IOException {
935+
DocumentMapper mapper = createSytheticSourceMapperService(
936+
mapping(b -> { b.startObject("field").field("type", "flattened").endObject(); })
937+
).documentMapper();
938+
939+
// This test covers a scenario that previously had a bug.
940+
// Since a.b.c and b.b.d have a matching middle key `b`, and b.b.d starts with a `b`,
941+
// startObject was not called for the first `b` in b.b.d.
942+
// For a full explanation see this comment: https://github.com/elastic/elasticsearch/pull/129600#issuecomment-3024476134
943+
var syntheticSource = syntheticSource(mapper, b -> {
944+
b.startObject("field");
945+
{
946+
b.startObject("a");
947+
{
948+
b.startObject("b").field("c", "1").endObject();
949+
}
950+
b.endObject();
951+
b.startObject("b");
952+
{
953+
b.startObject("b").field("d", "2").endObject();
954+
}
955+
b.endObject();
956+
}
957+
b.endObject();
958+
});
959+
assertThat(syntheticSource, equalTo("""
960+
{"field":{"a":{"b":{"c":"1"}},"b":{"b":{"d":"2"}}}}"""));
961+
}
962+
934963
@Override
935964
protected boolean supportsCopyTo() {
936965
return false;

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/IndexLifecycleExplainResponse.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
5151
private static final ParseField PREVIOUS_STEP_INFO_FIELD = new ParseField("previous_step_info");
5252
private static final ParseField PHASE_EXECUTION_INFO = new ParseField("phase_execution");
5353
private static final ParseField AGE_FIELD = new ParseField("age");
54+
private static final ParseField AGE_IN_MILLIS_FIELD = new ParseField("age_in_millis");
5455
private static final ParseField TIME_SINCE_INDEX_CREATION_FIELD = new ParseField("time_since_index_creation");
5556
private static final ParseField REPOSITORY_NAME = new ParseField("repository_name");
5657
private static final ParseField SHRINK_INDEX_NAME = new ParseField("shrink_index_name");
@@ -83,6 +84,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
8384
Objects.requireNonNullElse((Boolean) a[22], false)
8485
// a[13] == "age"
8586
// a[20] == "time_since_index_creation"
87+
// a[23] = "age_in_millis"
8688
)
8789
);
8890
static {
@@ -121,6 +123,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
121123
return BytesReference.bytes(builder);
122124
}, PREVIOUS_STEP_INFO_FIELD);
123125
PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), SKIP_NAME);
126+
PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), AGE_IN_MILLIS_FIELD);
124127
}
125128

126129
private final String index;
@@ -529,7 +532,10 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
529532
LIFECYCLE_DATE_FIELD.getPreferredName(),
530533
lifecycleDate
531534
);
532-
builder.field(AGE_FIELD.getPreferredName(), getAge(nowSupplier).toHumanReadableString(2));
535+
536+
final TimeValue ageNow = getAge(nowSupplier);
537+
builder.field(AGE_FIELD.getPreferredName(), ageNow.toHumanReadableString(2));
538+
builder.field(AGE_IN_MILLIS_FIELD.getPreferredName(), ageNow.getMillis());
533539
}
534540
if (phase != null) {
535541
builder.field(PHASE_FIELD.getPreferredName(), phase);

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtils.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,7 @@ public static void addAuthorizationInfo(final XContentBuilder builder, final Map
112112
private static void addSubjectInfo(XContentBuilder builder, Subject subject) throws IOException {
113113
switch (subject.getType()) {
114114
case USER -> builder.array(User.Fields.ROLES.getPreferredName(), subject.getUser().roles());
115-
case API_KEY -> {
116-
addApiKeyInfo(builder, subject);
117-
}
115+
case API_KEY -> addApiKeyInfo(builder, subject);
118116
case SERVICE_ACCOUNT -> builder.field("service_account", subject.getUser().principal());
119117
case CROSS_CLUSTER_ACCESS -> {
120118
builder.startObject("cross_cluster_access");
@@ -129,7 +127,16 @@ private static void addSubjectInfo(XContentBuilder builder, Subject subject) thr
129127
builder.endObject();
130128
}
131129
case CLOUD_API_KEY -> {
132-
// TODO Add cloud API key information here
130+
builder.startObject("cloud_api_key");
131+
Map<String, Object> metadata = subject.getUser().metadata();
132+
builder.field("id", subject.getUser().principal());
133+
Object name = metadata.get(AuthenticationField.API_KEY_NAME_KEY);
134+
if (name instanceof String) {
135+
builder.field("name", name);
136+
}
137+
builder.field("internal", metadata.get(AuthenticationField.API_KEY_INTERNAL_KEY));
138+
builder.array(User.Fields.ROLES.getPreferredName(), subject.getUser().roles());
139+
builder.endObject();
133140
}
134141
}
135142
}

0 commit comments

Comments
 (0)