Преглед на файлове

Merge remote-tracking branch 'upstream-main/main' into merge-main-10-01-25T17

Simon Cooper преди 9 месеца
родител
ревизия
b0cd47de08
променени са 100 файла, в които са добавени 1261 реда и са изтрити 860 реда
  1. 6 0
      docs/changelog/118968.yaml
  2. 5 0
      docs/changelog/119730.yaml
  3. 5 0
      docs/changelog/119780.yaml
  4. 11 0
      docs/changelog/119863.yaml
  5. 63 14
      docs/reference/connector/docs/connectors-sharepoint-online.asciidoc
  6. 1 1
      docs/reference/esql/esql-limitations.asciidoc
  7. 1 1
      docs/reference/esql/functions/kibana/definition/kql.json
  8. 2 0
      docs/reference/esql/functions/search-functions.asciidoc
  9. 15 0
      docs/reference/rest-api/security/get-service-accounts.asciidoc
  10. 7 2
      docs/reference/scripting/using.asciidoc
  11. 103 103
      docs/reference/transform/painless-examples.asciidoc
  12. 5 2
      modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/SearchCancellationIT.java
  13. 3 9
      muted-tests.yml
  14. 17 1
      qa/full-cluster-restart/src/javaRestTest/java/org/elasticsearch/upgrades/QueryBuilderBWCIT.java
  15. 6 0
      rest-api-spec/build.gradle
  16. 122 0
      rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cat.component_templates/10_basic.yml
  17. 51 217
      rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/logsdb/10_settings.yml
  18. 1 0
      server/src/main/java/module-info.java
  19. 1 0
      server/src/main/java/org/elasticsearch/TransportVersions.java
  20. 2 0
      server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java
  21. 30 0
      server/src/main/java/org/elasticsearch/index/IndexFeatures.java
  22. 9 9
      server/src/main/java/org/elasticsearch/index/IndexMode.java
  23. 27 0
      server/src/main/java/org/elasticsearch/index/IndexSettings.java
  24. 38 20
      server/src/main/java/org/elasticsearch/index/IndexSortConfig.java
  25. 2 1
      server/src/main/java/org/elasticsearch/index/IndexVersions.java
  26. 1 1
      server/src/main/java/org/elasticsearch/index/mapper/Mapping.java
  27. 60 17
      server/src/main/java/org/elasticsearch/index/shard/IndexShard.java
  28. 1 5
      server/src/main/java/org/elasticsearch/rest/action/search/SearchCapabilities.java
  29. 1 0
      server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification
  30. 80 1
      server/src/test/java/org/elasticsearch/index/LogsIndexModeTests.java
  31. 15 0
      server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java
  32. 2 0
      test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java
  33. 12 39
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/FreezeAction.java
  34. 0 35
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/FreezeStep.java
  35. 12 0
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicy.java
  36. 26 17
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/Phase.java
  37. 31 2
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java
  38. 15 1
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/KibanaOwnedReservedRoleDescriptors.java
  39. 8 8
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/FreezeActionTests.java
  40. 0 43
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/FreezeStepTests.java
  41. 1 0
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java
  42. 48 0
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationSerializationTests.java
  43. 22 0
      x-pack/plugin/core/template-resources/src/main/resources/agentless@mappings.json
  44. 15 0
      x-pack/plugin/core/template-resources/src/main/resources/agentless@settings.json
  45. 15 0
      x-pack/plugin/core/template-resources/src/main/resources/agentless@template.json
  46. 3 5
      x-pack/plugin/ent-search/qa/rest/roles.yml
  47. 130 91
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorIndexService.java
  48. 2 16
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/ConnectorActionRequest.java
  49. 1 10
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/DeleteConnectorAction.java
  50. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/GetConnectorAction.java
  51. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/ListConnectorAction.java
  52. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/PostConnectorAction.java
  53. 2 3
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/PutConnectorAction.java
  54. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorActiveFilteringAction.java
  55. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorApiKeyIdAction.java
  56. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorConfigurationAction.java
  57. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorErrorAction.java
  58. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorFeaturesAction.java
  59. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorFilteringAction.java
  60. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorFilteringValidationAction.java
  61. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorIndexNameAction.java
  62. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorLastSeenAction.java
  63. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorLastSyncStatsAction.java
  64. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorNameAction.java
  65. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorNativeAction.java
  66. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorPipelineAction.java
  67. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorSchedulingAction.java
  68. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorServiceTypeAction.java
  69. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorStatusAction.java
  70. 16 13
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexService.java
  71. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/CancelConnectorSyncJobAction.java
  72. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/CheckInConnectorSyncJobAction.java
  73. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/ClaimConnectorSyncJobAction.java
  74. 1 16
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/ConnectorSyncJobActionRequest.java
  75. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/DeleteConnectorSyncJobAction.java
  76. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/GetConnectorSyncJobAction.java
  77. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/ListConnectorSyncJobsAction.java
  78. 1 10
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/PostConnectorSyncJobAction.java
  79. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/UpdateConnectorSyncJobErrorAction.java
  80. 1 1
      x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/UpdateConnectorSyncJobIngestionStatsAction.java
  81. 14 1
      x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/predicate/Range.java
  82. 0 8
      x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/plugin/KqlFunctionIT.java
  83. 1 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
  84. 4 2
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java
  85. 1 3
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/FullTextWritables.java
  86. 4 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java
  87. 0 17
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java
  88. 0 7
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/KqlTests.java
  89. 0 15
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java
  90. 53 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/ConstantFoldingTests.java
  91. 18 19
      x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesDataStreamsIT.java
  92. 16 6
      x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java
  93. 45 15
      x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/SearchableSnapshotActionIT.java
  94. 1 0
      x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleAction.java
  95. 1 0
      x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportPutLifecycleAction.java
  96. 15 5
      x-pack/plugin/inference/qa/inference-service-tests/src/javaRestTest/java/org/elasticsearch/xpack/inference/InferenceCrudIT.java
  97. 1 6
      x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/KqlPlugin.java
  98. 1 1
      x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/query/KqlQueryBuilder.java
  99. 0 8
      x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/query/KqlQueryBuilderTests.java
  100. 5 6
      x-pack/plugin/logsdb/src/main/java/org/elasticsearch/xpack/logsdb/LogsDBPlugin.java

+ 6 - 0
docs/changelog/118968.yaml

@@ -0,0 +1,6 @@
+pr: 118968
+summary: Configure index sorting through index settings for logsdb
+area: Logs
+type: enhancement
+issues:
+ - 118686

+ 5 - 0
docs/changelog/119730.yaml

@@ -0,0 +1,5 @@
+pr: 119730
+summary: Enable KQL function as a tech preview
+area: ES|QL
+type: enhancement
+issues: []

+ 5 - 0
docs/changelog/119780.yaml

@@ -0,0 +1,5 @@
+pr: 119780
+summary: Add index and reindex request settings to speed up reindex
+area: Data streams
+type: enhancement
+issues: []

+ 11 - 0
docs/changelog/119863.yaml

@@ -0,0 +1,11 @@
+pr: 119863
+summary: Restrict Connector APIs to manage/monitor_connector privileges
+area: Extract&Transform
+type: breaking
+issues: []
+breaking:
+  title: Restrict Connector APIs to manage/monitor_connector privileges
+  area: REST API
+  details: Connector APIs now enforce the manage_connector and monitor_connector privileges (introduced in 8.15), replacing the previous reliance on index-level permissions for .elastic-connectors and .elastic-connectors-sync-jobs in API calls.
+  impact: Connector APIs now require manage_connector and monitor_connector privileges
+  notable: false

+ 63 - 14
docs/reference/connector/docs/connectors-sharepoint-online.asciidoc

@@ -75,12 +75,10 @@ Follow these steps:
 * Leave the *Redirect URIs* blank for now.
 * *Register* the application.
 * Find and keep the **Application (client) ID** and **Directory (tenant) ID** handy.
-* Locate the **Secret** by navigating to **Client credentials: Certificates & Secrets**.
-* Select **New client secret**
-* Pick a name for your client secret.
-Select an expiration date. (At this expiration date, you will need to generate a new secret and update your connector configuration.)
-** Save the client secret **Secret ID** before leaving this screen.
-** Save the client secret **Value** before leaving this screen.
+* Create a certificate and private key. This can, for example, be done by running `openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout azure_app.key -out azure_app.crt` command. Store both in a safe and secure place
+* Locate the **Certificates** by navigating to **Client credentials: Certificates & Secrets**.
+* Select **Upload certificate**
+* Upload the certificate created in one of previous steps: `azure_app.crt`
 * Set up the permissions the OAuth App will request from the Azure Portal service account.
 ** Navigate to **API Permissions** and click **Add Permission**.
 ** Add **application permissions** until the list looks like the following:
@@ -114,6 +112,24 @@ When entities are not available via the Graph API the connector falls back to us
 [discrete#es-connectors-sharepoint-online-oauth-app-permissions]
 ====== SharePoint permissions
 
+Microsoft is https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/retirement-announcement-for-azure-acs[retiring Azure Access Control Service (ACS)]. This affects permission configuration:
+
+* *Tenants created after November 1st, 2024*: Certificate authentication is required
+* *Tenants created before November 1st, 2024*: Secret-based authentication must be migrated to certificate authentication by April 2nd, 2026
+
+[discrete#es-connectors-sharepoint-online-oauth-app-certificate-auth]
+===== Certificate Authentication
+
+This authentication method does not require additional setup other than creating and uploading certificates to the OAuth App.
+
+[discrete#es-connectors-sharepoint-online-oauth-app-secret-auth]
+===== Secret Authentication
+
+[IMPORTANT]
+====
+This method is only applicable to tenants created before November 1st, 2024. This method will be fully retired as of April 2nd, 2026.
+====
+
 Refer to the following documentation for setting https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azureacs[SharePoint permissions^].
 
 * To set `DisableCustomAppAuthentication` to false, connect to SharePoint using PowerShell and run `set-spotenant -DisableCustomAppAuthentication $false`
@@ -219,8 +235,17 @@ The tenant name for the Azure account hosting the Sharepoint Online instance.
 Client ID::
 The client id to authenticate with SharePoint Online.
 
+Authentication Method::
+Authentication method to use to connector to Sharepoint Online and Rest APIs. `secret` is deprecated and `certificate` is recommended.
+
 Secret value::
-The secret value to authenticate with SharePoint Online.
+The secret value to authenticate with SharePoint Online, if Authentication Method: `secret` is chosen.
+
+Content of certificate file::
+Content of certificate file if Authentication Method: `certificate` is chosen.
+
+Content of private key file::
+Content of private key file if Authentication Method: `certificate` is chosen.
 
 Comma-separated list of sites::
 List of site collection names or paths to fetch from SharePoint.
@@ -588,12 +613,10 @@ Follow these steps:
 * Leave the *Redirect URIs* blank for now.
 * *Register* the application.
 * Find and keep the **Application (client) ID** and **Directory (tenant) ID** handy.
-* Locate the **Secret** by navigating to **Client credentials: Certificates & Secrets**.
-* Select **New client secret**
-* Pick a name for your client secret.
-Select an expiration date. (At this expiration date, you will need to generate a new secret and update your connector configuration.)
-** Save the client secret **Secret ID** before leaving this screen.
-** Save the client secret **Value** before leaving this screen.
+* Create a certificate and private key. This can, for example, be done by running `openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout azure_app.key -out azure_app.crt` command. Store both in a safe and secure place
+* Locate the **Certificates** by navigating to **Client credentials: Certificates & Secrets**.
+* Select **Upload certificate**
+* Upload the certificate created in one of previous steps: `azure_app.crt`
 * Set up the permissions the OAuth App will request from the Azure Portal service account.
 ** Navigate to **API Permissions** and click **Add Permission**.
 ** Add **application permissions** until the list looks like the following:
@@ -627,6 +650,23 @@ When entities are not available via the Graph API the connector falls back to us
 [discrete#es-connectors-sharepoint-online-client-oauth-app-permissions]
 ====== SharePoint permissions
 
+Microsoft is https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/retirement-announcement-for-azure-acs[retiring Azure Access Control Service (ACS)]. This affects permission configuration:
+* *Tenants created after November 1st, 2024*: Certificate authentication is required
+* *Tenants created before November 1st, 2024*: Secret-based authentication must be migrated to certificate authentication by April 2nd, 2026
+
+[discrete#es-connectors-sharepoint-online-client-oauth-app-certificate-auth]
+===== Certificate Authentication
+
+This authentication method does not require additional setup other than creating and uploading certificates to the OAuth App.
+
+[discrete#es-connectors-sharepoint-online-client-oauth-app-secret-auth]
+===== Secret Authentication
+
+[IMPORTANT]
+====
+This method is only applicable to tenants created before November 1st, 2024. This method will be fully retired as of April 2nd, 2026.
+====
+
 Refer to the following documentation for setting https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azureacs[SharePoint permissions^].
 
 * To set `DisableCustomAppAuthentication` to false, connect to SharePoint using PowerShell and run `set-spotenant -DisableCustomAppAuthentication $false`
@@ -742,8 +782,17 @@ The tenant name for the Azure account hosting the Sharepoint Online instance.
 `client_id`::
 The client id to authenticate with SharePoint Online.
 
+`auth_method`::
+Authentication method to use to connector to Sharepoint Online and Rest APIs. `secret` is deprecated and `certificate` is recommended.
+
 `secret_value`::
-The secret value to authenticate with SharePoint Online.
+The secret value to authenticate with SharePoint Online, if auth_method: `secret` is chosen.
+
+`certificate`::
+Content of certificate file if auth_method: `certificate` is chosen.
+
+`private_key`::
+Content of private key file if auth_method: `certificate` is chosen.
 
 `site_collections`::
 List of site collection names or paths to fetch from SharePoint.

+ 1 - 1
docs/reference/esql/esql-limitations.asciidoc

@@ -150,7 +150,7 @@ FROM books
 
 Note that, because of <<esql-limitations-text-fields,the way {esql} treats `text` values>>,
 any queries on `text` fields that do not explicitly use the full-text functions,
-<<esql-match>> or <<esql-qstr>>, will behave as if the fields are actually `keyword` fields:
+<<esql-match>>, <<esql-qstr>> or <<esql-kql>>, will behave as if the fields are actually `keyword` fields:
 they are case-sensitive and need to match the full string.
 
 [discrete]

+ 1 - 1
docs/reference/esql/functions/kibana/definition/kql.json

@@ -33,5 +33,5 @@
     "FROM books \n| WHERE KQL(\"author: Faulkner\")\n| KEEP book_no, author \n| SORT book_no \n| LIMIT 5;"
   ],
   "preview" : true,
-  "snapshot_only" : true
+  "snapshot_only" : false
 }

+ 2 - 0
docs/reference/esql/functions/search-functions.asciidoc

@@ -18,9 +18,11 @@ See <<esql-limitations-full-text-search,full text search limitations>> for infor
 {esql} supports these full-text search functions:
 
 // tag::search_list[]
+* experimental:[] <<esql-kql>>
 * experimental:[] <<esql-match>>
 * experimental:[] <<esql-qstr>>
 // end::search_list[]
 
+include::layout/kql.asciidoc[]
 include::layout/match.asciidoc[]
 include::layout/qstr.asciidoc[]

+ 15 - 0
docs/reference/rest-api/security/get-service-accounts.asciidoc

@@ -276,6 +276,21 @@ GET /_security/service/elastic/fleet-server
             "view_index_metadata"
           ],
           "allow_restricted_indices": false
+        },
+        {
+          "names": [
+            "agentless-*",
+          ],
+          "privileges": [
+            "read",
+            "write",
+            "monitor",
+            "create_index",
+            "auto_configure",
+            "maintenance",
+            "view_index_metadata"
+          ],
+          "allow_restricted_indices": false
         }
       ],
       "applications": [

+ 7 - 2
docs/reference/scripting/using.asciidoc

@@ -201,8 +201,13 @@ when you're creating <<runtime-mapping-fields,runtime fields>>.
 [[script-stored-scripts]]
 === Store and retrieve scripts
 You can store and retrieve scripts from the cluster state using the
-<<stored-script-apis,stored script APIs>>. Stored scripts reduce compilation
-time and make searches faster.
+<<stored-script-apis,stored script APIs>>. Stored scripts allow you to reference 
+shared scripts for operations like scoring, aggregating, filtering, and 
+reindexing. Instead of embedding scripts inline in each query, you can reference 
+these shared operations.
+
+Stored scripts can also reduce request payload size. Depending on script size 
+and request frequency, this can help lower latency and data transfer costs.
 
 NOTE: Unlike regular scripts, stored scripts require that you specify a script
 language using the `lang` parameter.

+ 103 - 103
docs/reference/transform/painless-examples.asciidoc

@@ -8,8 +8,8 @@
 
 IMPORTANT: The examples that use the `scripted_metric` aggregation are not supported on {es} Serverless.
 
-These examples demonstrate how to use Painless in {transforms}. You can learn 
-more about the Painless scripting language in the 
+These examples demonstrate how to use Painless in {transforms}. You can learn
+more about the Painless scripting language in the
 {painless}/painless-guide.html[Painless guide].
 
 * <<painless-top-hits>>
@@ -20,24 +20,24 @@ more about the Painless scripting language in the
 * <<painless-compare>>
 * <<painless-web-session>>
 
-[NOTE] 
+[NOTE]
 --
-* While the context of the following examples is the {transform} use case, 
-the Painless scripts in the snippets below can be used in other {es} search 
+* While the context of the following examples is the {transform} use case,
+the Painless scripts in the snippets below can be used in other {es} search
 aggregations, too.
-* All the following examples use scripts, {transforms} cannot deduce mappings of 
-output fields when the fields are created by a script. {transforms-cap} don't 
-create any mappings in the destination index for these fields, which means they 
-get dynamically mapped. Create the destination index prior to starting the 
+* All the following examples use scripts, {transforms} cannot deduce mappings of
+output fields when the fields are created by a script. {transforms-cap} don't
+create any mappings in the destination index for these fields, which means they
+get dynamically mapped. Create the destination index prior to starting the
 {transform} in case you want explicit mappings.
 --
 
 [[painless-top-hits]]
 == Getting top hits by using scripted metric aggregation
 
-This snippet shows how to find the latest document, in other words the document 
-with the latest timestamp. From a technical perspective, it helps to achieve 
-the function of a <<search-aggregations-metrics-top-hits-aggregation>> by using 
+This snippet shows how to find the latest document, in other words the document
+with the latest timestamp. From a technical perspective, it helps to achieve
+the function of a <<search-aggregations-metrics-top-hits-aggregation>> by using
 scripted metric aggregation in a {transform}, which provides a metric output.
 
 IMPORTANT: This example uses a `scripted_metric` aggregation which is not supported on {es} Serverless.
@@ -45,12 +45,12 @@ IMPORTANT: This example uses a `scripted_metric` aggregation which is not suppor
 [source,js]
 --------------------------------------------------
 "aggregations": {
-  "latest_doc": { 
+  "latest_doc": {
     "scripted_metric": {
       "init_script": "state.timestamp_latest = 0L; state.last_doc = ''", <1>
       "map_script": """ <2>
-        def current_date = doc['@timestamp'].getValue().toInstant().toEpochMilli(); 
-        if (current_date > state.timestamp_latest) 
+        def current_date = doc['@timestamp'].getValue().toInstant().toEpochMilli();
+        if (current_date > state.timestamp_latest)
         {state.timestamp_latest = current_date;
         state.last_doc = new HashMap(params['_source']);}
       """,
@@ -59,7 +59,7 @@ IMPORTANT: This example uses a `scripted_metric` aggregation which is not suppor
         def last_doc = '';
         def timestamp_latest = 0L;
         for (s in states) {if (s.timestamp_latest > (timestamp_latest))
-        {timestamp_latest = s.timestamp_latest; last_doc = s.last_doc;}} 
+        {timestamp_latest = s.timestamp_latest; last_doc = s.last_doc;}}
         return last_doc
       """
     }
@@ -68,23 +68,23 @@ IMPORTANT: This example uses a `scripted_metric` aggregation which is not suppor
 --------------------------------------------------
 // NOTCONSOLE
 
-<1> The `init_script` creates a long type `timestamp_latest` and a string type 
+<1> The `init_script` creates a long type `timestamp_latest` and a string type
 `last_doc` in the `state` object.
-<2> The `map_script` defines `current_date` based on the timestamp of the 
-document, then compares `current_date` with `state.timestamp_latest`, finally 
-returns `state.last_doc` from the shard. By using `new HashMap(...)` you copy 
-the source document, this is important whenever you want to pass the full source 
+<2> The `map_script` defines `current_date` based on the timestamp of the
+document, then compares `current_date` with `state.timestamp_latest`, finally
+returns `state.last_doc` from the shard. By using `new HashMap(...)` you copy
+the source document, this is important whenever you want to pass the full source
 object from one phase to the next.
 <3> The `combine_script` returns `state` from each shard.
-<4> The `reduce_script` iterates through the value of `s.timestamp_latest` 
-returned by each shard and returns the document with the latest timestamp 
-(`last_doc`). In the response, the top hit (in other words, the `latest_doc`) is 
+<4> The `reduce_script` iterates through the value of `s.timestamp_latest`
+returned by each shard and returns the document with the latest timestamp
+(`last_doc`). In the response, the top hit (in other words, the `latest_doc`) is
 nested below the `latest_doc` field.
 
-Check the <<scripted-metric-aggregation-scope,scope of scripts>> for detailed 
+Check the <<scripted-metric-aggregation-scope,scope of scripts>> for detailed
 explanation on the respective scripts.
 
-You can retrieve the last value in a similar way: 
+You can retrieve the last value in a similar way:
 
 [source,js]
 --------------------------------------------------
@@ -93,17 +93,17 @@ You can retrieve the last value in a similar way:
     "scripted_metric": {
       "init_script": "state.timestamp_latest = 0L; state.last_value = ''",
       "map_script": """
-        def current_date = doc['@timestamp'].getValue().toInstant().toEpochMilli(); 
-        if (current_date > state.timestamp_latest) 
+        def current_date = doc['@timestamp'].getValue().toInstant().toEpochMilli();
+        if (current_date > state.timestamp_latest)
         {state.timestamp_latest = current_date;
         state.last_value = params['_source']['value'];}
       """,
       "combine_script": "return state",
       "reduce_script": """
         def last_value = '';
-        def timestamp_latest = 0L; 
-        for (s in states) {if (s.timestamp_latest > (timestamp_latest)) 
-        {timestamp_latest = s.timestamp_latest; last_value = s.last_value;}} 
+        def timestamp_latest = 0L;
+        for (s in states) {if (s.timestamp_latest > (timestamp_latest))
+        {timestamp_latest = s.timestamp_latest; last_value = s.last_value;}}
         return last_value
       """
     }
@@ -117,10 +117,10 @@ You can retrieve the last value in a similar way:
 [[top-hits-stored-scripts]]
 === Getting top hits by using stored scripts
 
-You can also use the power of 
-{ref}/create-stored-script-api.html[stored scripts] to get the latest value. 
-Stored scripts reduce compilation time,  make searches faster, and are 
-updatable. 
+You can also use the power of
+{ref}/create-stored-script-api.html[stored scripts] to get the latest value.
+Stored scripts are updatable, enable collaboration, and avoid duplication across 
+queries.
 
 1. Create the stored scripts:
 +
@@ -202,7 +202,7 @@ POST _scripts/last-value-reduce
          }
 --------------------------------------------------
 // NOTCONSOLE
-<1> The parameter `field_with_last_value` can be set any field that you want the 
+<1> The parameter `field_with_last_value` can be set any field that you want the
 latest value for.
 --
 
@@ -210,8 +210,8 @@ latest value for.
 [[painless-time-features]]
 == Getting time features by using aggregations
 
-This snippet shows how to extract time based features by using Painless in a 
-{transform}. The snippet uses an index where `@timestamp` is defined as a `date` 
+This snippet shows how to extract time based features by using Painless in a
+{transform}. The snippet uses an index where `@timestamp` is defined as a `date`
 type field.
 
 [source,js]
@@ -225,11 +225,11 @@ type field.
           return date.getHour(); <4>
         """
       }
-    }  
+    }
   },
   "avg_month_of_year": { <5>
     "avg":{
-      "script": { <6> 
+      "script": { <6>
         "source": """
           ZonedDateTime date =  doc['@timestamp'].value; <7>
           return date.getMonthValue(); <8>
@@ -255,9 +255,9 @@ type field.
 [[painless-group-by]]
 == Using Painless in `group_by`
 
-It is possible to base the `group_by` property of a {transform} on the output of 
-a script. The following example uses the {kib} sample web logs dataset. The goal 
-here is to make the {transform} output easier to understand through normalizing 
+It is possible to base the `group_by` property of a {transform} on the output of
+a script. The following example uses the {kib} sample web logs dataset. The goal
+here is to make the {transform} output easier to understand through normalizing
 the value of the fields that the data is grouped by.
 
 [source,console]
@@ -274,12 +274,12 @@ POST _transform/_preview
       "agent": {
         "terms": {
           "script": { <2>
-            "source": """String agent = doc['agent.keyword'].value; 
-            if (agent.contains("MSIE")) { 
+            "source": """String agent = doc['agent.keyword'].value;
+            if (agent.contains("MSIE")) {
               return "internet explorer";
-            } else if (agent.contains("AppleWebKit")) { 
-              return "safari"; 
-            } else if (agent.contains('Firefox')) { 
+            } else if (agent.contains("AppleWebKit")) {
+              return "safari";
+            } else if (agent.contains('Firefox')) {
               return "firefox";
             } else { return agent }""",
             "lang": "painless"
@@ -314,18 +314,18 @@ POST _transform/_preview
   "dest": { <4>
     "index": "pivot_logs"
   }
-} 
+}
 --------------------------------------------------
 // TEST[skip:setup kibana sample data]
 
 <1> Specifies the source index or indices.
-<2> The script defines an `agent` string based on the `agent` field of the 
-documents, then iterates through the values. If an `agent` field contains 
-"MSIE", than the script returns "Internet Explorer". If it contains 
-`AppleWebKit`, it returns "safari". It returns "firefox" if the field value 
-contains "Firefox". Finally, in every other case, the value of the field is 
+<2> The script defines an `agent` string based on the `agent` field of the
+documents, then iterates through the values. If an `agent` field contains
+"MSIE", than the script returns "Internet Explorer". If it contains
+`AppleWebKit`, it returns "safari". It returns "firefox" if the field value
+contains "Firefox". Finally, in every other case, the value of the field is
 returned.
-<3> The aggregations object contains filters that narrow down the results to 
+<3> The aggregations object contains filters that narrow down the results to
 documents that contains `200`, `404`, or `503` values in the `response` field.
 <4> Specifies the destination index of the {transform}.
 
@@ -374,14 +374,14 @@ The API returns the following result:
 --------------------------------------------------
 // NOTCONSOLE
 
-You can see that the `agent` values are simplified so it is easier to interpret 
-them. The table below shows how normalization modifies the output of the 
+You can see that the `agent` values are simplified so it is easier to interpret
+them. The table below shows how normalization modifies the output of the
 {transform} in our example compared to the non-normalized values.
 
 [width="50%"]
 
 |===
-| Non-normalized `agent` value                                                 | Normalized `agent` value 
+| Non-normalized `agent` value                                                 | Normalized `agent` value
 
 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)" | "internet explorer"
 | "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24" | "safari"
@@ -393,9 +393,9 @@ them. The table below shows how normalization modifies the output of the
 [[painless-bucket-script]]
 == Getting duration by using bucket script
 
-This example shows you how to get the duration of a session by client IP from a 
-data log by using 
-<<search-aggregations-pipeline-bucket-script-aggregation,bucket script>>. 
+This example shows you how to get the duration of a session by client IP from a
+data log by using
+<<search-aggregations-pipeline-bucket-script-aggregation,bucket script>>.
 The example uses the {kib} sample web logs dataset.
 
 [source,console]
@@ -440,22 +440,22 @@ PUT _transform/data_log
 // TEST[skip:setup kibana sample data]
 
 <1> To define the length of the sessions, we use a bucket script.
-<2> The bucket path is a map of script variables and their associated path to 
-the buckets you want to use for the variable. In this particular case, `min` and 
+<2> The bucket path is a map of script variables and their associated path to
+the buckets you want to use for the variable. In this particular case, `min` and
 `max` are variables mapped to `time_frame.gte.value` and `time_frame.lte.value`.
-<3> Finally, the script substracts the start date of the session from the end 
+<3> Finally, the script substracts the start date of the session from the end
 date which results in the duration of the session.
 
 [[painless-count-http]]
 == Counting HTTP responses by using scripted metric aggregation
 
-You can count the different HTTP response types in a web log data set by using 
-scripted metric aggregation as part of the {transform}. You can achieve a 
-similar function with filter aggregations, check the 
-{ref}/transform-examples.html#example-clientips[Finding suspicious client IPs] 
+You can count the different HTTP response types in a web log data set by using
+scripted metric aggregation as part of the {transform}. You can achieve a
+similar function with filter aggregations, check the
+{ref}/transform-examples.html#example-clientips[Finding suspicious client IPs]
 example for details.
 
-The example below assumes that the HTTP response codes are stored as keywords in 
+The example below assumes that the HTTP response codes are stored as keywords in
 the `response` field of the documents.
 
 IMPORTANT: This example uses a `scripted_metric` aggregation which is not supported on {es} Serverless.
@@ -488,32 +488,32 @@ IMPORTANT: This example uses a `scripted_metric` aggregation which is not suppor
         """
       }
     },
-  ...  
+  ...
 }
 --------------------------------------------------
 // NOTCONSOLE
 
 <1> The `aggregations` object of the {transform} that contains all aggregations.
 <2> Object of the `scripted_metric` aggregation.
-<3> This `scripted_metric` performs a distributed operation on the web log data 
+<3> This `scripted_metric` performs a distributed operation on the web log data
 to count specific types of HTTP responses (error, success, and other).
-<4> The `init_script` creates a `responses` array in the `state` object with 
+<4> The `init_script` creates a `responses` array in the `state` object with
 three properties (`error`, `success`, `other`) with long data type.
-<5> The `map_script` defines `code` based on the `response.keyword` value of the 
-document, then it counts the errors, successes, and other responses based on the 
+<5> The `map_script` defines `code` based on the `response.keyword` value of the
+document, then it counts the errors, successes, and other responses based on the
 first digit of the responses.
 <6> The `combine_script` returns `state.responses` from each shard.
-<7> The `reduce_script` creates a `counts` array with the `error`, `success`, 
-and `other` properties, then iterates through the value of `responses` returned 
-by each shard and assigns the different response types to the appropriate 
-properties of the `counts` object; error responses to the error counts, success 
-responses to the success counts, and other responses to the other counts. 
+<7> The `reduce_script` creates a `counts` array with the `error`, `success`,
+and `other` properties, then iterates through the value of `responses` returned
+by each shard and assigns the different response types to the appropriate
+properties of the `counts` object; error responses to the error counts, success
+responses to the success counts, and other responses to the other counts.
 Finally, returns the `counts` array with the response counts.
 
 [[painless-compare]]
 == Comparing indices by using scripted metric aggregations
 
-This example shows how to compare the content of two indices by a {transform} 
+This example shows how to compare the content of two indices by a {transform}
 that uses a scripted metric aggregation.
 
 IMPORTANT: This example uses a `scripted_metric` aggregation which is not supported on {es} Serverless.
@@ -570,19 +570,19 @@ POST _transform/_preview
 <2> The `dest` index contains the results of the comparison.
 <3> The `group_by` field needs to be a unique identifier for each document.
 <4> Object of the `scripted_metric` aggregation.
-<5> The `map_script` defines `doc` in the state object. By using 
-`new HashMap(...)` you copy the source document, this is important whenever you 
+<5> The `map_script` defines `doc` in the state object. By using
+`new HashMap(...)` you copy the source document, this is important whenever you
 want to pass the full source object from one phase to the next.
 <6> The `combine_script` returns `state` from each shard.
-<7> The `reduce_script` checks if the size of the indices are equal. If they are 
-not equal, than it reports back a `count_mismatch`. Then it iterates through all 
-the values of the two indices and compare them. If the values are equal, then it 
+<7> The `reduce_script` checks if the size of the indices are equal. If they are
+not equal, than it reports back a `count_mismatch`. Then it iterates through all
+the values of the two indices and compare them. If the values are equal, then it
 returns a `match`, otherwise returns a `mismatch`.
 
 [[painless-web-session]]
 == Getting web session details by using scripted metric aggregation
 
-This example shows how to derive multiple features from a single transaction. 
+This example shows how to derive multiple features from a single transaction.
 Let's take a look on the example source document from the data:
 
 .Source document
@@ -628,8 +628,8 @@ Let's take a look on the example source document from the data:
 =====
 
 
-By using the `sessionid` as a group-by field, you are able to enumerate events 
-through the session and get more details of the session by using scripted metric 
+By using the `sessionid` as a group-by field, you are able to enumerate events
+through the session and get more details of the session by using scripted metric
 aggregation.
 
 IMPORTANT: This example uses a `scripted_metric` aggregation which is not supported on {es} Serverless.
@@ -650,7 +650,7 @@ POST _transform/_preview
       }
     },
     "aggregations": { <2>
-      "distinct_paths": { 
+      "distinct_paths": {
         "cardinality": {
           "field": "apache.access.path"
         }
@@ -665,21 +665,21 @@ POST _transform/_preview
           "init_script": "state.docs = []", <3>
           "map_script": """ <4>
             Map span = [
-              '@timestamp':doc['@timestamp'].value, 
+              '@timestamp':doc['@timestamp'].value,
               'url':doc['apache.access.url'].value,
               'referrer':doc['apache.access.referrer'].value
-            ]; 
+            ];
             state.docs.add(span)
           """,
           "combine_script": "return state.docs;", <5>
           "reduce_script": """ <6>
-            def all_docs = []; 
-            for (s in states) { 
-              for (span in s) { 
-                all_docs.add(span); 
+            def all_docs = [];
+            for (s in states) {
+              for (span in s) {
+                all_docs.add(span);
               }
             }
-            all_docs.sort((HashMap o1, HashMap o2)->o1['@timestamp'].toEpochMilli().compareTo(o2['@timestamp'].toEpochMilli())); 
+            all_docs.sort((HashMap o1, HashMap o2)->o1['@timestamp'].toEpochMilli().compareTo(o2['@timestamp'].toEpochMilli()));
             def size = all_docs.size();
             def min_time = all_docs[0]['@timestamp'];
             def max_time = all_docs[size-1]['@timestamp'];
@@ -705,17 +705,17 @@ POST _transform/_preview
 // NOTCONSOLE
 
 <1> The data is grouped by `sessionid`.
-<2> The aggregations counts the number of paths and enumerate the viewed pages 
+<2> The aggregations counts the number of paths and enumerate the viewed pages
 during the session.
 <3> The `init_script` creates an array type `doc` in the `state` object.
-<4> The `map_script` defines a `span` array with a timestamp, a URL, and a 
-referrer value which are based on the corresponding values of the document, then 
+<4> The `map_script` defines a `span` array with a timestamp, a URL, and a
+referrer value which are based on the corresponding values of the document, then
 adds the value of the `span` array to the `doc` object.
 <5> The `combine_script` returns `state.docs` from each shard.
-<6> The `reduce_script` defines various objects like `min_time`, `max_time`, and 
-`duration` based on the document fields, then declares a `ret` object, and 
-copies the source document by using `new HashMap ()`. Next, the script defines 
-`first_time`, `last_time`, `duration` and other fields inside the `ret` object 
+<6> The `reduce_script` defines various objects like `min_time`, `max_time`, and
+`duration` based on the document fields, then declares a `ret` object, and
+copies the source document by using `new HashMap ()`. Next, the script defines
+`first_time`, `last_time`, `duration` and other fields inside the `ret` object
 based on the corresponding object defined earlier, finally returns `ret`.
 
 The API call results in a similar response:

+ 5 - 2
modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/SearchCancellationIT.java

@@ -19,6 +19,7 @@ import org.elasticsearch.action.search.TransportSearchAction;
 import org.elasticsearch.action.support.WriteRequest;
 import org.elasticsearch.aggregations.AggregationsPlugin;
 import org.elasticsearch.aggregations.bucket.timeseries.TimeSeriesAggregationBuilder;
+import org.elasticsearch.client.internal.Client;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.index.IndexMode;
 import org.elasticsearch.index.IndexSettings;
@@ -97,9 +98,11 @@ public class SearchCancellationIT extends AbstractSearchCancellationTestCase {
 
         logger.info("Executing search");
         // we have to explicitly set error_trace=true for the later exception check for `TimeSeriesIndexSearcher`
-        client().threadPool().getThreadContext().putHeader("error_trace", "true");
+        Client client = client();
+        client.threadPool().getThreadContext().putHeader("error_trace", "true");
         TimeSeriesAggregationBuilder timeSeriesAggregationBuilder = new TimeSeriesAggregationBuilder("test_agg");
-        ActionFuture<SearchResponse> searchResponse = prepareSearch("test").setQuery(matchAllQuery())
+        ActionFuture<SearchResponse> searchResponse = client.prepareSearch("test")
+            .setQuery(matchAllQuery())
             .addAggregation(
                 timeSeriesAggregationBuilder.subAggregation(
                     new ScriptedMetricAggregationBuilder("sub_agg").initScript(

+ 3 - 9
muted-tests.yml

@@ -155,12 +155,6 @@ tests:
 - class: org.elasticsearch.xpack.ml.integration.RegressionIT
   method: testTwoJobsWithSameRandomizeSeedUseSameTrainingSet
   issue: https://github.com/elastic/elasticsearch/issues/117805
-- class: org.elasticsearch.upgrades.QueryBuilderBWCIT
-  method: testQueryBuilderBWC {cluster=UPGRADED}
-  issue: https://github.com/elastic/elasticsearch/issues/116990
-- class: org.elasticsearch.xpack.restart.QueryBuilderBWCIT
-  method: testQueryBuilderBWC {p0=UPGRADED}
-  issue: https://github.com/elastic/elasticsearch/issues/116989
 - class: org.elasticsearch.xpack.remotecluster.CrossClusterEsqlRCS2UnavailableRemotesIT
   method: testEsqlRcs2UnavailableRemoteScenarios
   issue: https://github.com/elastic/elasticsearch/issues/117419
@@ -202,9 +196,6 @@ tests:
 - class: org.elasticsearch.cluster.service.MasterServiceTests
   method: testThreadContext
   issue: https://github.com/elastic/elasticsearch/issues/118914
-- class: org.elasticsearch.aggregations.bucket.SearchCancellationIT
-  method: testCancellationDuringTimeSeriesAggregation
-  issue: https://github.com/elastic/elasticsearch/issues/118992
 - class: org.elasticsearch.xpack.security.authc.AuthenticationServiceTests
   method: testInvalidToken
   issue: https://github.com/elastic/elasticsearch/issues/119019
@@ -254,6 +245,9 @@ tests:
 - class: org.elasticsearch.xpack.security.authz.IndicesAndAliasesResolverTests
   method: testBackingIndicesAreNotVisibleWhenNotIncludedByRequestWithoutWildcard
   issue: https://github.com/elastic/elasticsearch/issues/119909
+- class: org.elasticsearch.xpack.inference.InferenceCrudIT
+  method: testGetServicesWithCompletionTaskType
+  issue: https://github.com/elastic/elasticsearch/issues/119959
 
 # Examples:
 #

+ 17 - 1
qa/full-cluster-restart/src/javaRestTest/java/org/elasticsearch/upgrades/QueryBuilderBWCIT.java

@@ -12,6 +12,8 @@ package org.elasticsearch.upgrades;
 import com.carrotsearch.randomizedtesting.annotations.Name;
 
 import org.elasticsearch.TransportVersion;
+import org.elasticsearch.TransportVersions;
+import org.elasticsearch.Version;
 import org.elasticsearch.client.Request;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.common.Strings;
@@ -21,6 +23,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.Fuzziness;
+import org.elasticsearch.core.UpdateForV10;
 import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.ConstantScoreQueryBuilder;
 import org.elasticsearch.index.query.DisMaxQueryBuilder;
@@ -51,6 +54,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import static org.elasticsearch.cluster.ClusterState.VERSION_INTRODUCING_TRANSPORT_VERSIONS;
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 
 /**
@@ -245,7 +249,19 @@ public class QueryBuilderBWCIT extends ParameterizedFullClusterRestartTestCase {
                     InputStream in = new ByteArrayInputStream(qbSource, 0, qbSource.length);
                     StreamInput input = new NamedWriteableAwareStreamInput(new InputStreamStreamInput(in), registry)
                 ) {
-                    input.setTransportVersion(TransportVersion.readVersion(input));
+                    @UpdateForV10(owner = UpdateForV10.Owner.SEARCH_FOUNDATIONS)    // won't need to read <8.8 data anymore
+                    boolean originalClusterHasTransportVersion = parseLegacyVersion(getOldClusterVersion()).map(
+                        v -> v.onOrAfter(VERSION_INTRODUCING_TRANSPORT_VERSIONS)
+                    ).orElse(true);
+                    TransportVersion transportVersion;
+                    if (originalClusterHasTransportVersion == false) {
+                        transportVersion = TransportVersion.fromId(
+                            parseLegacyVersion(getOldClusterVersion()).map(Version::id).orElse(TransportVersions.MINIMUM_COMPATIBLE.id())
+                        );
+                    } else {
+                        transportVersion = TransportVersion.readVersion(input);
+                    }
+                    input.setTransportVersion(transportVersion);
                     QueryBuilder queryBuilder = input.readNamedWriteable(QueryBuilder.class);
                     assert in.read() == -1;
                     assertEquals(expectedQueryBuilder, queryBuilder);

+ 6 - 0
rest-api-spec/build.gradle

@@ -60,6 +60,12 @@ tasks.named("yamlRestCompatTestTransform").configure ({ task ->
   task.skipTest("cat.aliases/10_basic/Deprecated local parameter", "CAT APIs not covered by compatibility policy")
   task.skipTest("cat.shards/10_basic/Help", "sync_id is removed in 9.0")
   task.skipTest("search/500_date_range/from, to, include_lower, include_upper deprecated", "deprecated parameters are removed in 9.0")
+  task.skipTest("logsdb/10_settings/logsdb with default ignore dynamic beyond limit and default sorting", "skip until pr/118968 gets backported")
+  task.skipTest("logsdb/10_settings/logsdb with default ignore dynamic beyond limit and too low limit", "skip until pr/118968 gets backported")
+  task.skipTest("logsdb/10_settings/logsdb with default ignore dynamic beyond limit and subobjects false", "skip until pr/118968 gets backported")
+  task.skipTest("logsdb/10_settings/override sort missing settings", "skip until pr/118968 gets backported")
+  task.skipTest("logsdb/10_settings/override sort order settings", "skip until pr/118968 gets backported")
+  task.skipTest("logsdb/10_settings/override sort mode settings", "skip until pr/118968 gets backported")
   task.skipTest("search.vectors/41_knn_search_bbq_hnsw/Test knn search", "Scoring has changed in latest versions")
   task.skipTest("search.vectors/42_knn_search_bbq_flat/Test knn search", "Scoring has changed in latest versions")
   task.skipTest("search.vectors/180_update_dense_vector_type/Test create and update dense vector mapping with bulk indexing", "waiting for #118774 backport")

+ 122 - 0
rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cat.component_templates/10_basic.yml

@@ -0,0 +1,122 @@
+---
+setup:
+
+  - do:
+      cluster.put_component_template:
+        name: captain_america
+        body:
+          template:
+            settings:
+              sort:
+               field: field
+            mappings:
+              properties:
+                field:
+                  type: keyword
+            aliases:
+              steve_rogers: {}
+
+  - do:
+      cluster.put_component_template:
+        name: ms_marvel
+        body:
+          template:
+            settings:
+              default_pipeline: pipeline_a
+              final_pipeline: pipeline_b
+          version: 2
+          _meta:
+            data: {}
+
+  - do:
+      cluster.put_component_template:
+        name: captain_marvel
+        body:
+          version: 3
+          template:
+            mappings:
+              properties:
+                field1:
+                  type: keyword
+                field2:
+                  type: long
+            aliases:
+              carol_danvers: {}
+              monica_rambeau: {}
+
+
+---
+"Retrieve all":
+
+  - do:
+      cat.component_templates: {}
+
+  - match:
+      $body: >
+             /
+               (^|\n)captain_america \s*
+               1 \s*
+               1 \s*
+               1 \s*
+               0 \s*
+               \[\]\s*
+               (\n|$)
+             /
+
+  - match:
+      $body: >
+             /
+               (^|\n)captain_marvel  \s+
+               3   \s+
+               2   \s+
+               2   \s+
+               0   \s+
+               0   \s+
+               \[\]\s*
+               (\n|$)
+             /
+
+  - match:
+      $body: >
+             /
+               (^|\n)ms_marvel  \s+
+               2   \s+
+               0   \s+
+               0   \s+
+               2   \s+
+               1   \s+
+               \[\]\s*
+               (\n|$)
+             /
+
+---
+"Retrieve by name (verbose/headers)":
+
+  - do:
+      cat.component_templates:
+         name: ms_marvel
+         v: true
+
+  - match:
+      $body: >
+             /
+               ^name \s+version \s*alias_count \s*mapping_count \s*settings_count \s*metadata_count \s*included_in\n
+                ms_marvel \s* 2 \s*          0 \s*            0 \s*             2 \s*             1 \s*\[\]\s*$
+             /
+
+
+---
+"Retrieve by wildcard (sorted)":
+
+  - do:
+      cat.component_templates:
+         name: captain_*
+         s: name
+
+  - match:
+      $body: >
+             /
+               ^captain_america \s*  \s*1 \s*1 \s*1 \s*0 \s*\[\]\s*\n
+                captain_marvel  \s*3 \s*2 \s*2 \s*0 \s*0 \s*\[\]\s*$
+             /
+

+ 51 - 217
rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/logsdb/10_settings.yml

@@ -10,14 +10,6 @@ setup:
 
 ---
 create logs index:
-  - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
   - do:
       indices.create:
         index: test
@@ -78,14 +70,6 @@ create logs index:
 
 ---
 using default timestamp field mapping:
-  - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
   - do:
       indices.create:
         index: test-timestamp-missing
@@ -110,14 +94,6 @@ using default timestamp field mapping:
 
 ---
 missing hostname field:
-  - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
   - do:
       indices.create:
         index: test-hostname-missing
@@ -149,14 +125,6 @@ missing hostname field:
 
 ---
 missing sort field:
-  - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
   - do:
       catch: bad_request
       indices.create:
@@ -190,14 +158,6 @@ missing sort field:
 
 ---
 non-default sort settings:
-  - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
   - do:
       indices.create:
         index: test-sort
@@ -244,14 +204,10 @@ non-default sort settings:
 ---
 override sort order settings:
   - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
+      cluster_features: [ "index.logsdb_no_host_name_field" ]
+      reason: "Change in default sort config for logsdb"
   - do:
+      catch: bad_request
       indices.create:
         index: test-sort-order
         body:
@@ -278,28 +234,16 @@ override sort order settings:
               message:
                 type: text
 
-  - do:
-      indices.get_settings:
-        index: test-sort-order
-
-  - is_true: test-sort-order
-  - match: { test-sort-order.settings.index.mode: "logsdb" }
-  - match: { test-sort-order.settings.index.sort.field.0: null }
-  - match: { test-sort-order.settings.index.sort.field.1: null }
-  - match: { test-sort-order.settings.index.sort.order.0: "asc" }
-  - match: { test-sort-order.settings.index.sort.order.1: "asc" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.reason: "index.sort.fields:[] index.sort.order:[asc, asc], size mismatch" }
 
 ---
 override sort missing settings:
   - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
+      cluster_features: [ "index.logsdb_no_host_name_field" ]
+      reason: "Change in default sort config for logsdb"
   - do:
+      catch: bad_request
       indices.create:
         index: test-sort-missing
         body:
@@ -326,28 +270,16 @@ override sort missing settings:
               message:
                 type: text
 
-  - do:
-      indices.get_settings:
-        index: test-sort-missing
-
-  - is_true: test-sort-missing
-  - match: { test-sort-missing.settings.index.mode: "logsdb" }
-  - match: { test-sort-missing.settings.index.sort.field.0: null }
-  - match: { test-sort-missing.settings.index.sort.field.1: null }
-  - match: { test-sort-missing.settings.index.sort.missing.0: "_last" }
-  - match: { test-sort-missing.settings.index.sort.missing.1: "_first" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.reason: "index.sort.fields:[] index.sort.missing:[_last, _first], size mismatch" }
 
 ---
 override sort mode settings:
   - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
+      cluster_features: [ "index.logsdb_no_host_name_field" ]
+      reason: "Change in default sort config for logsdb"
   - do:
+      catch: bad_request
       indices.create:
         index: test-sort-mode
         body:
@@ -374,16 +306,8 @@ override sort mode settings:
               message:
                 type: text
 
-  - do:
-      indices.get_settings:
-        index: test-sort-mode
-
-  - is_true: test-sort-mode
-  - match: { test-sort-mode.settings.index.mode: "logsdb" }
-  - match: { test-sort-mode.settings.index.sort.field.0: null }
-  - match: { test-sort-mode.settings.index.sort.field.1: null }
-  - match: { test-sort-mode.settings.index.sort.mode.0: "max" }
-  - match: { test-sort-mode.settings.index.sort.mode.1: "max" }
+  - match: { error.type: "illegal_argument_exception" }
+  - match: { error.reason: "index.sort.fields:[] index.sort.mode:[MAX, MAX], size mismatch" }
 
 ---
 override sort field using nested field type in sorting:
@@ -436,12 +360,7 @@ override sort field using nested field type in sorting:
 override sort field using nested field type:
   - requires:
       cluster_features: ["mapper.index_sorting_on_nested"]
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
+      reason: "Support for index sorting on indexes with nested objects required"
 
   - do:
       indices.create:
@@ -475,14 +394,6 @@ override sort field using nested field type:
 
 ---
 routing path not allowed in logs mode:
-  - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
   - do:
       catch: bad_request
       indices.create:
@@ -557,14 +468,6 @@ routing path allowed in logs mode with routing on sort fields:
 
 ---
 start time not allowed in logs mode:
-  - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
   - do:
       catch: bad_request
       indices.create:
@@ -598,14 +501,6 @@ start time not allowed in logs mode:
 
 ---
 end time not allowed in logs mode:
-  - requires:
-      test_runner_features: [ capabilities ]
-      capabilities:
-        - method: PUT
-          path: /{index}
-          capabilities: [ logsdb_index_mode ]
-      reason: "Support for 'logsdb' index mode capability required"
-
   - do:
       catch: bad_request
       indices.create:
@@ -685,10 +580,10 @@ ignore dynamic beyond limit logsdb override value:
   - match: { test-ignore-dynamic-override.settings.index.mapping.total_fields.ignore_dynamic_beyond_limit: "false" }
 
 ---
-logsdb with default ignore dynamic beyond limit and default sorting:
+default ignore dynamic beyond limit and default sorting:
   - requires:
-      cluster_features: ["mapper.logsdb_default_ignore_dynamic_beyond_limit"]
-      reason: requires default value for ignore_dynamic_beyond_limit
+      cluster_features: [ "index.logsdb_no_host_name_field" ]
+      reason: "No host.name field injection"
 
   - do:
       indices.create:
@@ -698,19 +593,8 @@ logsdb with default ignore dynamic beyond limit and default sorting:
             index:
               mode: logsdb
               mapping:
-                # NOTE: When the index mode is set to `logsdb`, the `host.name` field is automatically injected if
-                # sort settings are not overridden.
-                # With `subobjects` set to `true` (default), this creates a `host` object field and a nested `name`
-                # keyword field (`host.name`).
-                #
-                # As a result, there are always at least 4 statically mapped fields (`@timestamp`, `host`, `host.name`
-                # and `name`). We cannot use a field limit lower than 4 because these fields are always present.
-                #
-                # Indeed, if `index.mapping.total_fields.ignore_dynamic_beyond_limit` is `true`, any dynamically
-                # mapped fields beyond the limit `index.mapping.total_fields.limit` are ignored, but the statically
-                # mapped fields are always counted.
                 total_fields:
-                  limit: 4
+                  limit: 2
           mappings:
             properties:
               "@timestamp":
@@ -730,9 +614,9 @@ logsdb with default ignore dynamic beyond limit and default sorting:
         refresh: true
         body:
           - '{ "index": { } }'
-          - '{ "@timestamp": "2024-08-13T12:30:00Z", "name": "foo", "host.name": "92f4a67c", "value": 10, "message": "the quick brown fox", "region": "us-west", "pid": 153462 }'
+          - '{ "@timestamp": "2024-08-13T12:30:00Z", "name": "foo", "value": 10, "message": "the quick brown fox", "region": "us-west", "pid": 153462 }'
           - '{ "index": { } }'
-          - '{ "@timestamp": "2024-08-13T12:01:00Z", "name": "bar", "host.name": "24eea278", "value": 20, "message": "jumps over the lazy dog", "region": "us-central", "pid": 674972 }'
+          - '{ "@timestamp": "2024-08-13T12:01:00Z", "name": "bar", "value": 20, "message": "jumps over the lazy dog", "region": "us-central", "pid": 674972 }'
   - match: { errors: false }
 
   - do:
@@ -754,132 +638,82 @@ logsdb with default ignore dynamic beyond limit and default sorting:
   - match: { hits.hits.1._ignored: [ "message", "pid", "region", "value" ] }
 
 ---
-logsdb with default ignore dynamic beyond limit and non-default sorting:
+default ignore dynamic beyond limit and default sorting with hostname:
   - requires:
-      cluster_features: ["mapper.logsdb_default_ignore_dynamic_beyond_limit"]
-      reason: requires default value for ignore_dynamic_beyond_limit
+      cluster_features: [ "index.logsdb_no_host_name_field" ]
+      reason: "No host.name field injection"
 
   - do:
       indices.create:
-        index: test-logsdb-non-default-sort
+        index: test-logsdb-default-sort
         body:
           settings:
             index:
-              sort.field: [ "name" ]
-              sort.order: [ "desc" ]
               mode: logsdb
               mapping:
-                # NOTE: Here sort settings are overridden and we do not have any additional statically mapped field other
-                # than `name` and `timestamp`. As a result, there are only 2 statically mapped fields.
                 total_fields:
-                  limit: 2
+                  limit: 3
           mappings:
             properties:
               "@timestamp":
                 type: date
-              name:
+              host.name:
                 type: keyword
 
   - do:
       indices.get_settings:
-        index: test-logsdb-non-default-sort
+        index: test-logsdb-default-sort
 
-  - match: { test-logsdb-non-default-sort.settings.index.mode: "logsdb" }
+  - match: { test-logsdb-default-sort.settings.index.mode: "logsdb" }
 
   - do:
       bulk:
-        index: test-logsdb-non-default-sort
+        index: test-logsdb-default-sort
         refresh: true
         body:
           - '{ "index": { } }'
-          - '{ "@timestamp": "2024-08-13T12:30:00Z", "name": "foo", "host.name": "92f4a67c", "value": 10, "message": "the quick brown fox", "region": "us-west", "pid": 153462 }'
+          - '{ "@timestamp": "2024-08-13T12:30:00Z", "host.name": "foo", "value": 10, "message": "the quick brown fox", "region": "us-west", "pid": 153462 }'
           - '{ "index": { } }'
-          - '{ "@timestamp": "2024-08-13T12:01:00Z", "name": "bar", "host.name": "24eea278", "value": 20, "message": "jumps over the lazy dog", "region": "us-central", "pid": 674972 }'
+          - '{ "@timestamp": "2024-08-13T12:01:00Z", "host.name": "bar", "value": 20, "message": "jumps over the lazy dog", "region": "us-central", "pid": 674972 }'
   - match: { errors: false }
 
   - do:
       search:
-        index: test-logsdb-non-default-sort
+        index: test-logsdb-default-sort
         body:
           query:
             match_all: {}
           sort: "@timestamp"
 
   - match: { hits.total.value: 2 }
-  - match: { hits.hits.0._source.name: "bar" }
+  - match: { hits.hits.0._source.host.name: "bar" }
   - match: { hits.hits.0._source.value: 20 }
   - match: { hits.hits.0._source.message: "jumps over the lazy dog" }
-  - match: { hits.hits.0._ignored: [ "host", "message", "pid", "region", "value" ] }
-  - match: { hits.hits.1._source.name: "foo" }
+  - match: { hits.hits.0._ignored: [ "message", "pid", "region", "value" ] }
+  - match: { hits.hits.1._source.host.name: "foo" }
   - match: { hits.hits.1._source.value: 10 }
   - match: { hits.hits.1._source.message: "the quick brown fox" }
-  - match: { hits.hits.1._ignored: [ "host", "message", "pid", "region", "value" ] }
-
----
-logsdb with default ignore dynamic beyond limit and too low limit:
-  - requires:
-      cluster_features: ["mapper.logsdb_default_ignore_dynamic_beyond_limit"]
-      reason: requires default value for ignore_dynamic_beyond_limit
-
-  - do:
-      catch: bad_request
-      indices.create:
-        index: test-logsdb-low-limit
-        body:
-          settings:
-            index:
-              mode: logsdb
-              mapping:
-                # NOTE: When the index mode is set to `logsdb`, the `host.name` field is automatically injected if
-                # sort settings are not overridden.
-                # With `subobjects` set to `true` (default), this creates a `host` object field and a nested `name`
-                # keyword field (`host.name`).
-                #
-                # As a result, there are always at least 4 statically mapped fields (`@timestamp`, `host`, `host.name`
-                # and `name`). We cannot use a field limit lower than 4 because these fields are always present.
-                #
-                # Indeed, if `index.mapping.total_fields.ignore_dynamic_beyond_limit` is `true`, any dynamically
-                # mapped fields beyond the limit `index.mapping.total_fields.limit` are ignored, but the statically
-                # mapped fields are always counted.
-                total_fields:
-                  limit: 3
-          mappings:
-            properties:
-              "@timestamp":
-                type: date
-              name:
-                type: keyword
-  - match: { error.type: "illegal_argument_exception" }
-  - match: { error.reason: "Limit of total fields [3] has been exceeded" }
+  - match: { hits.hits.1._ignored: [ "message", "pid", "region", "value" ] }
 
 ---
-logsdb with default ignore dynamic beyond limit and subobjects false:
+default ignore dynamic beyond limit and non-default sorting:
   - requires:
-      cluster_features: ["mapper.logsdb_default_ignore_dynamic_beyond_limit"]
-      reason: requires default value for ignore_dynamic_beyond_limit
+      cluster_features: [ "index.logsdb_no_host_name_field" ]
+      reason: "No host.name field injection"
 
   - do:
       indices.create:
-        index: test-logsdb-subobjects-false
+        index: test-logsdb-non-default-sort
         body:
           settings:
             index:
+              sort.field: [ "name" ]
+              sort.order: [ "desc" ]
               mode: logsdb
               mapping:
-                # NOTE: When the index mode is set to `logsdb`, the `host.name` field is automatically injected if
-                # sort settings are not overridden.
-                # With `subobjects` set to `false` anyway, a single `host.name` keyword field is automatically mapped.
-                #
-                # As a result, there are just 3 statically mapped fields (`@timestamp`, `host.name` and `name`).
-                # We cannot use a field limit lower than 3 because these fields are always present.
-                #
-                # Indeed, if `index.mapping.total_fields.ignore_dynamic_beyond_limit` is `true`, any dynamically
-                # mapped fields beyond the limit `index.mapping.total_fields.limit` are ignored, but the statically
-                # mapped fields are always counted.
                 total_fields:
-                  limit: 3
+                  limit: 2
           mappings:
-            subobjects: false
             properties:
               "@timestamp":
                 type: date
@@ -888,13 +722,13 @@ logsdb with default ignore dynamic beyond limit and subobjects false:
 
   - do:
       indices.get_settings:
-        index: test-logsdb-subobjects-false
+        index: test-logsdb-non-default-sort
 
-  - match: { test-logsdb-subobjects-false.settings.index.mode: "logsdb" }
+  - match: { test-logsdb-non-default-sort.settings.index.mode: "logsdb" }
 
   - do:
       bulk:
-        index: test-logsdb-subobjects-false
+        index: test-logsdb-non-default-sort
         refresh: true
         body:
           - '{ "index": { } }'
@@ -905,7 +739,7 @@ logsdb with default ignore dynamic beyond limit and subobjects false:
 
   - do:
       search:
-        index: test-logsdb-subobjects-false
+        index: test-logsdb-non-default-sort
         body:
           query:
             match_all: {}
@@ -915,8 +749,8 @@ logsdb with default ignore dynamic beyond limit and subobjects false:
   - match: { hits.hits.0._source.name: "bar" }
   - match: { hits.hits.0._source.value: 20 }
   - match: { hits.hits.0._source.message: "jumps over the lazy dog" }
-  - match: { hits.hits.0._ignored: [ "message", "pid", "region", "value" ] }
+  - match: { hits.hits.0._ignored: [ "host", "message", "pid", "region", "value" ] }
   - match: { hits.hits.1._source.name: "foo" }
   - match: { hits.hits.1._source.value: 10 }
   - match: { hits.hits.1._source.message: "the quick brown fox" }
-  - match: { hits.hits.1._ignored: [ "message", "pid", "region", "value" ] }
+  - match: { hits.hits.1._ignored: [ "host", "message", "pid", "region", "value" ] }

+ 1 - 0
server/src/main/java/module-info.java

@@ -437,6 +437,7 @@ module org.elasticsearch.server {
             org.elasticsearch.action.admin.cluster.allocation.AllocationStatsFeatures,
             org.elasticsearch.rest.action.admin.cluster.ClusterRerouteFeatures,
             org.elasticsearch.index.mapper.MapperFeatures,
+            org.elasticsearch.index.IndexFeatures,
             org.elasticsearch.ingest.IngestGeoIpFeatures,
             org.elasticsearch.search.SearchFeatures,
             org.elasticsearch.script.ScriptFeatures,

+ 1 - 0
server/src/main/java/org/elasticsearch/TransportVersions.java

@@ -156,6 +156,7 @@ public class TransportVersions {
     public static final TransportVersion TRACK_INDEX_FAILED_DUE_TO_VERSION_CONFLICT_METRIC = def(8_820_00_0);
     public static final TransportVersion REPLACE_FAILURE_STORE_OPTIONS_WITH_SELECTOR_SYNTAX = def(8_821_00_0);
     public static final TransportVersion ELASTIC_INFERENCE_SERVICE_UNIFIED_CHAT_COMPLETIONS_INTEGRATION = def(8_822_00_0);
+    public static final TransportVersion KQL_QUERY_TECH_PREVIEW = def(8_823_00_0);
 
     /*
      * WARNING: DO NOT MERGE INTO MAIN!

+ 2 - 0
server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java

@@ -184,6 +184,8 @@ public final class IndexScopedSettings extends AbstractScopedSettings {
         IndexSettings.LIFECYCLE_PARSE_ORIGINATION_DATE_SETTING,
         IndexSettings.TIME_SERIES_ES87TSDB_CODEC_ENABLED_SETTING,
         IndexSettings.LOGSDB_ROUTE_ON_SORT_FIELDS,
+        IndexSettings.LOGSDB_SORT_ON_HOST_NAME,
+        IndexSettings.LOGSDB_ADD_HOST_NAME_FIELD,
         IndexSettings.PREFER_ILM_SETTING,
         DataStreamFailureStoreDefinition.FAILURE_STORE_DEFINITION_VERSION_SETTING,
         FieldMapper.SYNTHETIC_SOURCE_KEEP_INDEX_SETTING,

+ 30 - 0
server/src/main/java/org/elasticsearch/index/IndexFeatures.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.index;
+
+import org.elasticsearch.features.FeatureSpecification;
+import org.elasticsearch.features.NodeFeature;
+
+import java.util.Set;
+
+public class IndexFeatures implements FeatureSpecification {
+
+    @Override
+    public Set<NodeFeature> getFeatures() {
+        return Set.of();
+    }
+
+    public static final NodeFeature LOGSDB_NO_HOST_NAME_FIELD = new NodeFeature("index.logsdb_no_host_name_field");
+
+    @Override
+    public Set<NodeFeature> getTestFeatures() {
+        return Set.of(LOGSDB_NO_HOST_NAME_FIELD);
+    }
+}

+ 9 - 9
server/src/main/java/org/elasticsearch/index/IndexMode.java

@@ -178,7 +178,7 @@ public enum IndexMode {
 
         @Override
         public CompressedXContent getDefaultMapping(final IndexSettings indexSettings) {
-            return DEFAULT_TIME_SERIES_TIMESTAMP_MAPPING;
+            return DEFAULT_MAPPING_TIMESTAMP;
         }
 
         @Override
@@ -260,9 +260,9 @@ public enum IndexMode {
 
         @Override
         public CompressedXContent getDefaultMapping(final IndexSettings indexSettings) {
-            return indexSettings != null && indexSettings.getIndexSortConfig().hasPrimarySortOnField(HOST_NAME)
-                ? DEFAULT_LOGS_TIMESTAMP_MAPPING_WITH_HOSTNAME
-                : DEFAULT_TIME_SERIES_TIMESTAMP_MAPPING;
+            return indexSettings != null && indexSettings.logsdbAddHostNameField()
+                ? DEFAULT_MAPPING_TIMESTAMP_HOSTNAME
+                : DEFAULT_MAPPING_TIMESTAMP;
         }
 
         @Override
@@ -392,7 +392,7 @@ public enum IndexMode {
         }
     };
 
-    private static final String HOST_NAME = "host.name";
+    static final String HOST_NAME = "host.name";
 
     private static void validateRoutingPathSettings(Map<Setting<?>, Object> settings) {
         settingRequiresTimeSeries(settings, IndexMetadata.INDEX_ROUTING_PATH);
@@ -432,14 +432,14 @@ public enum IndexMode {
         });
     }
 
-    private static final CompressedXContent DEFAULT_TIME_SERIES_TIMESTAMP_MAPPING;
+    private static final CompressedXContent DEFAULT_MAPPING_TIMESTAMP;
 
-    private static final CompressedXContent DEFAULT_LOGS_TIMESTAMP_MAPPING_WITH_HOSTNAME;
+    private static final CompressedXContent DEFAULT_MAPPING_TIMESTAMP_HOSTNAME;
 
     static {
         try {
-            DEFAULT_TIME_SERIES_TIMESTAMP_MAPPING = createDefaultMapping(false);
-            DEFAULT_LOGS_TIMESTAMP_MAPPING_WITH_HOSTNAME = createDefaultMapping(true);
+            DEFAULT_MAPPING_TIMESTAMP = createDefaultMapping(false);
+            DEFAULT_MAPPING_TIMESTAMP_HOSTNAME = createDefaultMapping(true);
         } catch (IOException e) {
             throw new AssertionError(e);
         }

+ 27 - 0
server/src/main/java/org/elasticsearch/index/IndexSettings.java

@@ -711,6 +711,22 @@ public final class IndexSettings {
         Property.Final
     );
 
+    public static final Setting<Boolean> LOGSDB_ADD_HOST_NAME_FIELD = Setting.boolSetting(
+        "index.logsdb.add_host_name_field",
+        false,
+        Property.IndexScope,
+        Property.PrivateIndex,
+        Property.Final
+    );
+
+    public static final Setting<Boolean> LOGSDB_SORT_ON_HOST_NAME = Setting.boolSetting(
+        "index.logsdb.sort_on_host_name",
+        false,
+        Property.IndexScope,
+        Property.PrivateIndex,
+        Property.Final
+    );
+
     /**
      * The {@link IndexMode "mode"} of the index.
      */
@@ -833,6 +849,8 @@ public final class IndexSettings {
     private volatile long softDeleteRetentionOperations;
     private final boolean es87TSDBCodecEnabled;
     private final boolean logsdbRouteOnSortFields;
+    private final boolean logsdbSortOnHostName;
+    private final boolean logsdbAddHostNameField;
 
     private volatile long retentionLeaseMillis;
 
@@ -950,6 +968,13 @@ public final class IndexSettings {
         return logsdbRouteOnSortFields;
     }
 
+    /**
+     * Returns <code>true</code> if the index is in logsdb mode and needs a [host.name] keyword field. The default is <code>false</code>
+     */
+    public boolean logsdbAddHostNameField() {
+        return logsdbAddHostNameField;
+    }
+
     /**
      * Creates a new {@link IndexSettings} instance. The given node settings will be merged with the settings in the metadata
      * while index level settings will overwrite node settings.
@@ -1043,6 +1068,8 @@ public final class IndexSettings {
         sourceKeepMode = scopedSettings.get(Mapper.SYNTHETIC_SOURCE_KEEP_INDEX_SETTING);
         es87TSDBCodecEnabled = scopedSettings.get(TIME_SERIES_ES87TSDB_CODEC_ENABLED_SETTING);
         logsdbRouteOnSortFields = scopedSettings.get(LOGSDB_ROUTE_ON_SORT_FIELDS);
+        logsdbSortOnHostName = scopedSettings.get(LOGSDB_SORT_ON_HOST_NAME);
+        logsdbAddHostNameField = scopedSettings.get(LOGSDB_ADD_HOST_NAME_FIELD);
         skipIgnoredSourceWrite = scopedSettings.get(IgnoredSourceFieldMapper.SKIP_IGNORED_SOURCE_WRITE_SETTING);
         skipIgnoredSourceRead = scopedSettings.get(IgnoredSourceFieldMapper.SKIP_IGNORED_SOURCE_READ_SETTING);
         indexMappingSourceMode = scopedSettings.get(INDEX_MAPPER_SOURCE_MODE_SETTING);

+ 38 - 20
server/src/main/java/org/elasticsearch/index/IndexSortConfig.java

@@ -13,7 +13,6 @@ import org.apache.lucene.search.Sort;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.SortedNumericSortField;
 import org.apache.lucene.search.SortedSetSortField;
-import org.elasticsearch.cluster.metadata.DataStream;
 import org.elasticsearch.common.logging.DeprecationCategory;
 import org.elasticsearch.common.logging.DeprecationLogger;
 import org.elasticsearch.common.settings.Setting;
@@ -103,12 +102,23 @@ public final class IndexSortConfig {
         Setting.Property.ServerlessPublic
     );
 
-    public static final FieldSortSpec[] TIME_SERIES_SORT;
-
+    public static final FieldSortSpec[] TIME_SERIES_SORT, TIMESTAMP_SORT, HOSTNAME_TIMESTAMP_SORT, HOSTNAME_TIMESTAMP_BWC_SORT;
     static {
         FieldSortSpec timeStampSpec = new FieldSortSpec(DataStreamTimestampFieldMapper.DEFAULT_PATH);
         timeStampSpec.order = SortOrder.DESC;
         TIME_SERIES_SORT = new FieldSortSpec[] { new FieldSortSpec(TimeSeriesIdFieldMapper.NAME), timeStampSpec };
+        TIMESTAMP_SORT = new FieldSortSpec[] { timeStampSpec };
+
+        FieldSortSpec hostnameSpec = new FieldSortSpec(IndexMode.HOST_NAME);
+        hostnameSpec.order = SortOrder.ASC;
+        hostnameSpec.missingValue = "_last";
+        hostnameSpec.mode = MultiValueMode.MIN;
+        HOSTNAME_TIMESTAMP_SORT = new FieldSortSpec[] { hostnameSpec, timeStampSpec };
+
+        // Older indexes use ascending ordering for host name and timestamp.
+        HOSTNAME_TIMESTAMP_BWC_SORT = new FieldSortSpec[] {
+            new FieldSortSpec(IndexMode.HOST_NAME),
+            new FieldSortSpec(DataStreamTimestampFieldMapper.DEFAULT_PATH) };
     }
 
     private static String validateMissingValue(String missing) {
@@ -148,26 +158,40 @@ public final class IndexSortConfig {
         this.indexName = indexSettings.getIndex().getName();
         this.indexMode = indexSettings.getMode();
 
-        if (this.indexMode == IndexMode.TIME_SERIES) {
-            this.sortSpecs = TIME_SERIES_SORT;
+        if (indexMode == IndexMode.TIME_SERIES) {
+            sortSpecs = TIME_SERIES_SORT;
             return;
         }
 
         List<String> fields = INDEX_SORT_FIELD_SETTING.get(settings);
-        if (this.indexMode == IndexMode.LOGSDB && fields.isEmpty()) {
-            fields = List.of("host.name", DataStream.TIMESTAMP_FIELD_NAME);
+        if (indexMode == IndexMode.LOGSDB && INDEX_SORT_FIELD_SETTING.exists(settings) == false) {
+            if (INDEX_SORT_ORDER_SETTING.exists(settings)) {
+                var order = INDEX_SORT_ORDER_SETTING.get(settings);
+                throw new IllegalArgumentException("index.sort.fields:" + fields + " index.sort.order:" + order + ", size mismatch");
+            }
+            if (INDEX_SORT_MODE_SETTING.exists(settings)) {
+                var mode = INDEX_SORT_MODE_SETTING.get(settings);
+                throw new IllegalArgumentException("index.sort.fields:" + fields + " index.sort.mode:" + mode + ", size mismatch");
+            }
+            if (INDEX_SORT_MISSING_SETTING.exists(settings)) {
+                var missing = INDEX_SORT_MISSING_SETTING.get(settings);
+                throw new IllegalArgumentException("index.sort.fields:" + fields + " index.sort.missing:" + missing + ", size mismatch");
+            }
+            var version = indexSettings.getIndexVersionCreated();
+            if (version.onOrAfter(IndexVersions.LOGSB_OPTIONAL_SORTING_ON_HOST_NAME)
+                || version.between(IndexVersions.LOGSB_OPTIONAL_SORTING_ON_HOST_NAME_BACKPORT, IndexVersions.UPGRADE_TO_LUCENE_10_0_0)) {
+                sortSpecs = (IndexSettings.LOGSDB_SORT_ON_HOST_NAME.get(settings)) ? HOSTNAME_TIMESTAMP_SORT : TIMESTAMP_SORT;
+            } else {
+                sortSpecs = HOSTNAME_TIMESTAMP_BWC_SORT;
+            }
+            return;
         }
-        this.sortSpecs = fields.stream().map(FieldSortSpec::new).toArray(FieldSortSpec[]::new);
+        sortSpecs = fields.stream().map(FieldSortSpec::new).toArray(FieldSortSpec[]::new);
 
         if (INDEX_SORT_ORDER_SETTING.exists(settings)) {
             List<SortOrder> orders = INDEX_SORT_ORDER_SETTING.get(settings);
-            if (this.indexMode == IndexMode.LOGSDB && orders.isEmpty()) {
-                orders = List.of(SortOrder.DESC, SortOrder.DESC);
-            }
             if (orders.size() != sortSpecs.length) {
-                throw new IllegalArgumentException(
-                    "index.sort.field:" + fields + " index.sort.order:" + orders.toString() + ", size mismatch"
-                );
+                throw new IllegalArgumentException("index.sort.field:" + fields + " index.sort.order:" + orders + ", size mismatch");
             }
             for (int i = 0; i < sortSpecs.length; i++) {
                 sortSpecs[i].order = orders.get(i);
@@ -176,9 +200,6 @@ public final class IndexSortConfig {
 
         if (INDEX_SORT_MODE_SETTING.exists(settings)) {
             List<MultiValueMode> modes = INDEX_SORT_MODE_SETTING.get(settings);
-            if (this.indexMode == IndexMode.LOGSDB && modes.isEmpty()) {
-                modes = List.of(MultiValueMode.MIN, MultiValueMode.MIN);
-            }
             if (modes.size() != sortSpecs.length) {
                 throw new IllegalArgumentException("index.sort.field:" + fields + " index.sort.mode:" + modes + ", size mismatch");
             }
@@ -189,9 +210,6 @@ public final class IndexSortConfig {
 
         if (INDEX_SORT_MISSING_SETTING.exists(settings)) {
             List<String> missingValues = INDEX_SORT_MISSING_SETTING.get(settings);
-            if (this.indexMode == IndexMode.LOGSDB && missingValues.isEmpty()) {
-                missingValues = List.of("_first", "_first");
-            }
             if (missingValues.size() != sortSpecs.length) {
                 throw new IllegalArgumentException(
                     "index.sort.field:" + fields + " index.sort.missing:" + missingValues + ", size mismatch"

+ 2 - 1
server/src/main/java/org/elasticsearch/index/IndexVersions.java

@@ -134,13 +134,14 @@ public class IndexVersions {
     public static final IndexVersion USE_SYNTHETIC_SOURCE_FOR_RECOVERY_BACKPORT = def(8_522_00_0, Version.LUCENE_9_12_0);
     public static final IndexVersion UPGRADE_TO_LUCENE_9_12_1 = def(8_523_00_0, parseUnchecked("9.12.1"));
     public static final IndexVersion INFERENCE_METADATA_FIELDS_BACKPORT = def(8_524_00_0, parseUnchecked("9.12.1"));
+    public static final IndexVersion LOGSB_OPTIONAL_SORTING_ON_HOST_NAME_BACKPORT = def(8_525_00_0, parseUnchecked("9.12.1"));
     public static final IndexVersion UPGRADE_TO_LUCENE_10_0_0 = def(9_000_00_0, Version.LUCENE_10_0_0);
     public static final IndexVersion LOGSDB_DEFAULT_IGNORE_DYNAMIC_BEYOND_LIMIT = def(9_001_00_0, Version.LUCENE_10_0_0);
     public static final IndexVersion TIME_BASED_K_ORDERED_DOC_ID = def(9_002_00_0, Version.LUCENE_10_0_0);
     public static final IndexVersion DEPRECATE_SOURCE_MODE_MAPPER = def(9_003_00_0, Version.LUCENE_10_0_0);
     public static final IndexVersion USE_SYNTHETIC_SOURCE_FOR_RECOVERY = def(9_004_00_0, Version.LUCENE_10_0_0);
     public static final IndexVersion INFERENCE_METADATA_FIELDS = def(9_005_00_0, Version.LUCENE_10_0_0);
-
+    public static final IndexVersion LOGSB_OPTIONAL_SORTING_ON_HOST_NAME = def(9_006_00_0, Version.LUCENE_10_0_0);
     /*
      * STOP! READ THIS FIRST! No, really,
      *        ____ _____ ___  ____  _        ____  _____    _    ____    _____ _   _ ___ ____    _____ ___ ____  ____ _____ _

+ 1 - 1
server/src/main/java/org/elasticsearch/index/mapper/Mapping.java

@@ -81,7 +81,7 @@ public final class Mapping implements ToXContentFragment {
     /**
      * Returns the root object for the current mapping
      */
-    RootObjectMapper getRoot() {
+    public RootObjectMapper getRoot() {
         return root;
     }
 

+ 60 - 17
server/src/main/java/org/elasticsearch/index/shard/IndexShard.java

@@ -189,6 +189,7 @@ import static org.elasticsearch.cluster.metadata.DataStream.TIMESERIES_LEAF_READ
 import static org.elasticsearch.core.Strings.format;
 import static org.elasticsearch.index.seqno.RetentionLeaseActions.RETAIN_ALL;
 import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
+import static org.elasticsearch.index.shard.IndexShard.PrimaryPermitCheck.CHECK_PRIMARY_MODE;
 
 public class IndexShard extends AbstractIndexShardComponent implements IndicesClusterStateService.Shard {
 
@@ -3568,24 +3569,48 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl
         );
     }
 
+    /**
+     * Check to run before running the primary permit operation
+     */
+    public enum PrimaryPermitCheck {
+        CHECK_PRIMARY_MODE,
+        /**
+         * IMPORTANT: Currently intented to be used only for acquiring primary permits during the recovery of hollow shards.
+         * Don't disable primary mode checks unless you're really sure.
+         */
+        NONE
+    }
+
     /**
      * Acquire a primary operation permit whenever the shard is ready for indexing. If a permit is directly available, the provided
      * ActionListener will be called on the calling thread. During relocation hand-off, permit acquisition can be delayed. The provided
      * ActionListener will then be called using the provided executor.
-     *
      */
     public void acquirePrimaryOperationPermit(ActionListener<Releasable> onPermitAcquired, Executor executorOnDelay) {
-        acquirePrimaryOperationPermit(onPermitAcquired, executorOnDelay, false);
+        acquirePrimaryOperationPermit(onPermitAcquired, executorOnDelay, false, CHECK_PRIMARY_MODE);
     }
 
     public void acquirePrimaryOperationPermit(
         ActionListener<Releasable> onPermitAcquired,
         Executor executorOnDelay,
         boolean forceExecution
+    ) {
+        acquirePrimaryOperationPermit(onPermitAcquired, executorOnDelay, forceExecution, CHECK_PRIMARY_MODE);
+    }
+
+    public void acquirePrimaryOperationPermit(
+        ActionListener<Releasable> onPermitAcquired,
+        Executor executorOnDelay,
+        boolean forceExecution,
+        PrimaryPermitCheck primaryPermitCheck
     ) {
         verifyNotClosed();
         assert shardRouting.primary() : "acquirePrimaryOperationPermit should only be called on primary shard: " + shardRouting;
-        indexShardOperationPermits.acquire(wrapPrimaryOperationPermitListener(onPermitAcquired), executorOnDelay, forceExecution);
+        indexShardOperationPermits.acquire(
+            wrapPrimaryOperationPermitListener(primaryPermitCheck, onPermitAcquired),
+            executorOnDelay,
+            forceExecution
+        );
     }
 
     public boolean isPrimaryMode() {
@@ -3593,33 +3618,51 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl
         return replicationTracker.isPrimaryMode();
     }
 
+    public void acquireAllPrimaryOperationsPermits(final ActionListener<Releasable> onPermitAcquired, final TimeValue timeout) {
+        acquireAllPrimaryOperationsPermits(onPermitAcquired, timeout, CHECK_PRIMARY_MODE);
+    }
+
     /**
      * Acquire all primary operation permits. Once all permits are acquired, the provided ActionListener is called.
      * It is the responsibility of the caller to close the {@link Releasable}.
      */
-    public void acquireAllPrimaryOperationsPermits(final ActionListener<Releasable> onPermitAcquired, final TimeValue timeout) {
+    public void acquireAllPrimaryOperationsPermits(
+        final ActionListener<Releasable> onPermitAcquired,
+        final TimeValue timeout,
+        final PrimaryPermitCheck primaryPermitCheck
+    ) {
         verifyNotClosed();
         assert shardRouting.primary() : "acquireAllPrimaryOperationsPermits should only be called on primary shard: " + shardRouting;
 
-        asyncBlockOperations(wrapPrimaryOperationPermitListener(onPermitAcquired), timeout.duration(), timeout.timeUnit());
+        asyncBlockOperations(
+            wrapPrimaryOperationPermitListener(primaryPermitCheck, onPermitAcquired),
+            timeout.duration(),
+            timeout.timeUnit()
+        );
     }
 
     /**
-     * Wraps the action to run on a primary after acquiring permit. This wrapping is used to check if the shard is in primary mode before
-     * executing the action.
+     * Wraps the action to run on a primary after acquiring permit.
      *
+     * @param primaryPermitCheck check to run before the primary mode operation
      * @param listener the listener to wrap
      * @return the wrapped listener
      */
-    private ActionListener<Releasable> wrapPrimaryOperationPermitListener(final ActionListener<Releasable> listener) {
-        return listener.delegateFailure((l, r) -> {
-            if (isPrimaryMode()) {
-                l.onResponse(r);
-            } else {
-                r.close();
-                l.onFailure(new ShardNotInPrimaryModeException(shardId, state));
-            }
-        });
+    private ActionListener<Releasable> wrapPrimaryOperationPermitListener(
+        final PrimaryPermitCheck primaryPermitCheck,
+        final ActionListener<Releasable> listener
+    ) {
+        return switch (primaryPermitCheck) {
+            case CHECK_PRIMARY_MODE -> listener.delegateFailure((l, r) -> {
+                if (isPrimaryMode()) {
+                    l.onResponse(r);
+                } else {
+                    r.close();
+                    l.onFailure(new ShardNotInPrimaryModeException(shardId, state));
+                }
+            });
+            case NONE -> listener;
+        };
     }
 
     private void asyncBlockOperations(ActionListener<Releasable> onPermitAcquired, long timeout, TimeUnit timeUnit) {
@@ -3657,7 +3700,7 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl
                 runnable.run();
             }
         }, onFailure);
-        acquirePrimaryOperationPermit(onPermitAcquired, executorOnDelay);
+        acquirePrimaryOperationPermit(onPermitAcquired, executorOnDelay, false, CHECK_PRIMARY_MODE);
     }
 
     private <E extends Exception> void bumpPrimaryTerm(

+ 1 - 5
server/src/main/java/org/elasticsearch/rest/action/search/SearchCapabilities.java

@@ -9,8 +9,6 @@
 
 package org.elasticsearch.rest.action.search;
 
-import org.elasticsearch.Build;
-
 import java.util.HashSet;
 import java.util.Set;
 
@@ -60,9 +58,7 @@ public final class SearchCapabilities {
         capabilities.add(KNN_QUANTIZED_VECTOR_RESCORE);
         capabilities.add(MOVING_FN_RIGHT_MATH);
         capabilities.add(K_DEFAULT_TO_SIZE);
-        if (Build.current().isSnapshot()) {
-            capabilities.add(KQL_QUERY_SUPPORTED);
-        }
+        capabilities.add(KQL_QUERY_SUPPORTED);
         capabilities.add(HIGHLIGHT_MAX_ANALYZED_OFFSET_DEFAULT);
         CAPABILITIES = Set.copyOf(capabilities);
     }

+ 1 - 0
server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification

@@ -16,6 +16,7 @@ org.elasticsearch.rest.RestFeatures
 org.elasticsearch.repositories.RepositoriesFeatures
 org.elasticsearch.action.admin.cluster.allocation.AllocationStatsFeatures
 org.elasticsearch.rest.action.admin.cluster.ClusterRerouteFeatures
+org.elasticsearch.index.IndexFeatures
 org.elasticsearch.index.mapper.MapperFeatures
 org.elasticsearch.ingest.IngestGeoIpFeatures
 org.elasticsearch.search.SearchFeatures

+ 80 - 1
server/src/test/java/org/elasticsearch/index/LogsIndexModeTests.java

@@ -12,6 +12,7 @@ package org.elasticsearch.index;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.test.index.IndexVersionUtils;
 
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
@@ -25,11 +26,89 @@ public class LogsIndexModeTests extends ESTestCase {
     public void testDefaultHostNameSortField() {
         final IndexMetadata metadata = IndexSettingsTests.newIndexMeta("test", buildSettings());
         assertThat(metadata.getIndexMode(), equalTo(IndexMode.LOGSDB));
-        final IndexSettings settings = new IndexSettings(metadata, Settings.EMPTY);
+        boolean sortOnHostName = randomBoolean();
+        final IndexSettings settings = new IndexSettings(
+            metadata,
+            Settings.builder().put(IndexSettings.LOGSDB_SORT_ON_HOST_NAME.getKey(), sortOnHostName).build()
+        );
+        assertThat(settings.getIndexSortConfig().hasPrimarySortOnField("host.name"), equalTo(sortOnHostName));
+    }
+
+    public void testDefaultHostNameSortFieldAndMapping() {
+        final IndexMetadata metadata = IndexSettingsTests.newIndexMeta("test", buildSettings());
+        assertThat(metadata.getIndexMode(), equalTo(IndexMode.LOGSDB));
+        final IndexSettings settings = new IndexSettings(
+            metadata,
+            Settings.builder()
+                .put(IndexSettings.LOGSDB_SORT_ON_HOST_NAME.getKey(), true)
+                .put(IndexSettings.LOGSDB_ADD_HOST_NAME_FIELD.getKey(), true)
+                .build()
+        );
         assertThat(settings.getIndexSortConfig().hasPrimarySortOnField("host.name"), equalTo(true));
         assertThat(IndexMode.LOGSDB.getDefaultMapping(settings).string(), containsString("host.name"));
     }
 
+    public void testDefaultHostNameSortFieldBwc() {
+        final IndexMetadata metadata = IndexMetadata.builder("test")
+            .settings(
+                indexSettings(IndexVersionUtils.getPreviousVersion(IndexVersions.LOGSB_OPTIONAL_SORTING_ON_HOST_NAME), 1, 1).put(
+                    buildSettings()
+                )
+            )
+            .build();
+        assertThat(metadata.getIndexMode(), equalTo(IndexMode.LOGSDB));
+        final IndexSettings settings = new IndexSettings(metadata, Settings.EMPTY);
+        assertThat(settings.getIndexSortConfig().hasPrimarySortOnField("host.name"), equalTo(true));
+    }
+
+    public void testDefaultHostNameSortWithOrder() {
+        final IndexMetadata metadata = IndexSettingsTests.newIndexMeta("test", buildSettings());
+        assertThat(metadata.getIndexMode(), equalTo(IndexMode.LOGSDB));
+        var exception = expectThrows(
+            IllegalArgumentException.class,
+            () -> new IndexSettings(
+                metadata,
+                Settings.builder()
+                    .put(IndexSettings.LOGSDB_SORT_ON_HOST_NAME.getKey(), randomBoolean())
+                    .put(IndexSortConfig.INDEX_SORT_ORDER_SETTING.getKey(), "desc")
+                    .build()
+            )
+        );
+        assertEquals("index.sort.fields:[] index.sort.order:[desc], size mismatch", exception.getMessage());
+    }
+
+    public void testDefaultHostNameSortWithMode() {
+        final IndexMetadata metadata = IndexSettingsTests.newIndexMeta("test", buildSettings());
+        assertThat(metadata.getIndexMode(), equalTo(IndexMode.LOGSDB));
+        var exception = expectThrows(
+            IllegalArgumentException.class,
+            () -> new IndexSettings(
+                metadata,
+                Settings.builder()
+                    .put(IndexSettings.LOGSDB_SORT_ON_HOST_NAME.getKey(), randomBoolean())
+                    .put(IndexSortConfig.INDEX_SORT_MODE_SETTING.getKey(), "MAX")
+                    .build()
+            )
+        );
+        assertEquals("index.sort.fields:[] index.sort.mode:[MAX], size mismatch", exception.getMessage());
+    }
+
+    public void testDefaultHostNameSortWithMissing() {
+        final IndexMetadata metadata = IndexSettingsTests.newIndexMeta("test", buildSettings());
+        assertThat(metadata.getIndexMode(), equalTo(IndexMode.LOGSDB));
+        var exception = expectThrows(
+            IllegalArgumentException.class,
+            () -> new IndexSettings(
+                metadata,
+                Settings.builder()
+                    .put(IndexSettings.LOGSDB_SORT_ON_HOST_NAME.getKey(), randomBoolean())
+                    .put(IndexSortConfig.INDEX_SORT_MISSING_SETTING.getKey(), "_first")
+                    .build()
+            )
+        );
+        assertEquals("index.sort.fields:[] index.sort.missing:[_first], size mismatch", exception.getMessage());
+    }
+
     public void testCustomSortField() {
         final Settings sortSettings = Settings.builder()
             .put(buildSettings())

+ 15 - 0
server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java

@@ -790,6 +790,21 @@ public class IndexShardTests extends IndexShardTestCase {
                 }
             }, TimeValue.timeValueSeconds(30));
             latch.await();
+
+            // It's possible to acquire permits if we skip the primary mode check
+            var permitAcquiredLatch = new CountDownLatch(1);
+            indexShard.acquirePrimaryOperationPermit(ActionListener.wrap(r -> {
+                r.close();
+                permitAcquiredLatch.countDown();
+            }, Assert::assertNotNull), EsExecutors.DIRECT_EXECUTOR_SERVICE, false, IndexShard.PrimaryPermitCheck.NONE);
+            safeAwait(permitAcquiredLatch);
+
+            var allPermitsAcquiredLatch = new CountDownLatch(1);
+            indexShard.acquireAllPrimaryOperationsPermits(ActionListener.wrap(r -> {
+                r.close();
+                allPermitsAcquiredLatch.countDown();
+            }, Assert::assertNotNull), TimeValue.timeValueSeconds(30), IndexShard.PrimaryPermitCheck.NONE);
+            safeAwait(allPermitsAcquiredLatch);
         }
 
         if (Assertions.ENABLED && indexShard.routingEntry().isRelocationTarget() == false) {

+ 2 - 0
test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java

@@ -781,6 +781,7 @@ public abstract class ESRestTestCase extends ESTestCase {
             "profiling-60-days",
             "profiling-60-days@lifecycle",
             "synthetics",
+            "agentless",
             "synthetics@lifecycle",
             "traces@lifecycle",
             "7-days-default",
@@ -2215,6 +2216,7 @@ public abstract class ESRestTestCase extends ESTestCase {
             case "metrics-tsdb-settings":
             case "metrics-mappings":
             case "synthetics":
+            case "agentless":
             case "synthetics-settings":
             case "synthetics-mappings":
             case ".snapshot-blob-cache":

+ 12 - 39
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/FreezeAction.java

@@ -6,12 +6,10 @@
  */
 package org.elasticsearch.xpack.core.ilm;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 import org.elasticsearch.client.internal.Client;
-import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.core.UpdateForV10;
 import org.elasticsearch.xcontent.ObjectParser;
 import org.elasticsearch.xcontent.XContentBuilder;
 import org.elasticsearch.xcontent.XContentParser;
@@ -21,10 +19,14 @@ import java.io.IOException;
 import java.util.List;
 
 /**
- * A {@link LifecycleAction} which freezes the index.
+ * A noop {@link LifecycleAction} that replaces the removed freeze action. We keep it for backwards compatibility purposes in case we
+ * encounter a policy or an index that refers to this action and its steps in the lifecycle state.
+ * At 10.x we would like to sanitize the freeze action from input and expunge from the lifecycle execution
+ * state of indices.
  */
+@Deprecated
+@UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT)
 public class FreezeAction implements LifecycleAction {
-    private static final Logger logger = LogManager.getLogger(FreezeAction.class);
 
     public static final String NAME = "freeze";
     public static final String CONDITIONAL_SKIP_FREEZE_STEP = BranchingStep.NAME + "-freeze-check-prerequisites";
@@ -34,6 +36,7 @@ public class FreezeAction implements LifecycleAction {
     private static final ObjectParser<FreezeAction, Void> PARSER = new ObjectParser<>(NAME, () -> INSTANCE);
 
     public static FreezeAction parse(XContentParser parser) {
+
         return PARSER.apply(parser, null);
     }
 
@@ -63,40 +66,10 @@ public class FreezeAction implements LifecycleAction {
     public List<Step> toSteps(Client client, String phase, StepKey nextStepKey) {
         StepKey preFreezeMergeBranchingKey = new StepKey(phase, NAME, CONDITIONAL_SKIP_FREEZE_STEP);
         StepKey checkNotWriteIndex = new StepKey(phase, NAME, CheckNotDataStreamWriteIndexStep.NAME);
-        StepKey freezeStepKey = new StepKey(phase, NAME, FreezeStep.NAME);
-
-        BranchingStep conditionalSkipFreezeStep = new BranchingStep(
-            preFreezeMergeBranchingKey,
-            checkNotWriteIndex,
-            nextStepKey,
-            (index, clusterState) -> {
-                IndexMetadata indexMetadata = clusterState.getMetadata().getProject().index(index);
-                assert indexMetadata != null : "index " + index.getName() + " must exist in the cluster state";
-                String policyName = indexMetadata.getLifecyclePolicyName();
-                if (indexMetadata.getSettings().get(LifecycleSettings.SNAPSHOT_INDEX_NAME) != null) {
-                    logger.warn(
-                        "[{}] action is configured for index [{}] in policy [{}] which is mounted as searchable snapshot. "
-                            + "Skipping this action",
-                        FreezeAction.NAME,
-                        index.getName(),
-                        policyName
-                    );
-                    return true;
-                }
-                if (indexMetadata.getSettings().getAsBoolean("index.frozen", false)) {
-                    logger.debug(
-                        "skipping [{}] action for index [{}] in policy [{}] as the index is already frozen",
-                        FreezeAction.NAME,
-                        index.getName(),
-                        policyName
-                    );
-                    return true;
-                }
-                return false;
-            }
-        );
-        CheckNotDataStreamWriteIndexStep checkNoWriteIndexStep = new CheckNotDataStreamWriteIndexStep(checkNotWriteIndex, freezeStepKey);
-        FreezeStep freezeStep = new FreezeStep(freezeStepKey, nextStepKey, client);
+        StepKey freezeStepKey = new StepKey(phase, NAME, NAME);
+        NoopStep conditionalSkipFreezeStep = new NoopStep(preFreezeMergeBranchingKey, nextStepKey);
+        NoopStep checkNoWriteIndexStep = new NoopStep(checkNotWriteIndex, nextStepKey);
+        NoopStep freezeStep = new NoopStep(freezeStepKey, nextStepKey);
         return List.of(conditionalSkipFreezeStep, checkNoWriteIndexStep, freezeStep);
     }
 

+ 0 - 35
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/FreezeStep.java

@@ -1,35 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-package org.elasticsearch.xpack.core.ilm;
-
-import org.elasticsearch.action.ActionListener;
-import org.elasticsearch.client.internal.Client;
-import org.elasticsearch.cluster.ClusterState;
-import org.elasticsearch.cluster.metadata.IndexMetadata;
-
-/**
- * Freezes an index.
- */
-@Deprecated // To be removed in 9.0
-public class FreezeStep extends AsyncRetryDuringSnapshotActionStep {
-    public static final String NAME = "freeze";
-
-    public FreezeStep(StepKey key, StepKey nextStepKey, Client client) {
-        super(key, nextStepKey, client);
-    }
-
-    @Override
-    public void performDuringNoSnapshot(IndexMetadata indexMetadata, ClusterState currentState, ActionListener<Void> listener) {
-        // Deprecated in 7.x, the freeze action is a noop in 8.x, so immediately return here
-        listener.onResponse(null);
-    }
-
-    @Override
-    public boolean isRetryable() {
-        return true;
-    }
-}

+ 12 - 0
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicy.java

@@ -13,6 +13,7 @@ import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.core.Nullable;
+import org.elasticsearch.core.UpdateForV10;
 import org.elasticsearch.license.XPackLicenseState;
 import org.elasticsearch.xcontent.ConstructingObjectParser;
 import org.elasticsearch.xcontent.ParseField;
@@ -33,6 +34,8 @@ import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.COLD_PHASE;
+
 /**
  * Represents the lifecycle of an index from creation to deletion. A
  * {@link LifecyclePolicy} is made up of a set of {@link Phase}s which it will
@@ -365,4 +368,13 @@ public class LifecyclePolicy implements SimpleDiffable<LifecyclePolicy>, ToXCont
     public String toString() {
         return Strings.toString(this, true, true);
     }
+
+    @UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT)
+    public boolean maybeAddDeprecationWarningForFreezeAction(String policyName) {
+        Phase coldPhase = phases.get(COLD_PHASE);
+        if (coldPhase != null) {
+            return coldPhase.maybeAddDeprecationWarningForFreezeAction(policyName);
+        }
+        return false;
+    }
 }

+ 26 - 17
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/Phase.java

@@ -6,15 +6,15 @@
  */
 package org.elasticsearch.xpack.core.ilm;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.io.stream.Writeable;
+import org.elasticsearch.common.logging.DeprecationCategory;
+import org.elasticsearch.common.logging.DeprecationLogger;
 import org.elasticsearch.common.util.Maps;
 import org.elasticsearch.core.TimeValue;
-import org.elasticsearch.core.UpdateForV9;
+import org.elasticsearch.core.UpdateForV10;
 import org.elasticsearch.xcontent.ConstructingObjectParser;
 import org.elasticsearch.xcontent.ContextParser;
 import org.elasticsearch.xcontent.ObjectParser.ValueType;
@@ -34,7 +34,7 @@ import java.util.TreeMap;
  * particular point in the lifecycle of an index.
  */
 public class Phase implements ToXContentObject, Writeable {
-    private static final Logger logger = LogManager.getLogger(Phase.class);
+    private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(Phase.class);
 
     public static final ParseField MIN_AGE = new ParseField("min_age");
     public static final ParseField ACTIONS_FIELD = new ParseField("actions");
@@ -51,19 +51,12 @@ public class Phase implements ToXContentObject, Writeable {
         return new Phase(name, (TimeValue) a[0], map);
     });
     static {
-        PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(), (ContextParser<String, Object>) (p, c) -> {
-            // In earlier versions it was possible to create a Phase with a negative `min_age` which would then cause errors
-            // when the phase is read from the cluster state during startup (even before negative timevalues were strictly
-            // disallowed) so this is a hack to treat negative `min_age`s as 0 to prevent those errors.
-            // They will be saved as `0` so this hack can be removed once we no longer have to read cluster states from 7.x.
-            @UpdateForV9(owner = UpdateForV9.Owner.DATA_MANAGEMENT) // remove this hack now that we don't have to read 7.x cluster states
-            final String timeValueString = p.text();
-            if (timeValueString.startsWith("-")) {
-                logger.warn("phase has negative min_age value of [{}] - this will be treated as a min_age of 0", timeValueString);
-                return TimeValue.ZERO;
-            }
-            return TimeValue.parseTimeValue(timeValueString, MIN_AGE.getPreferredName());
-        }, MIN_AGE, ValueType.VALUE);
+        PARSER.declareField(
+            ConstructingObjectParser.optionalConstructorArg(),
+            (ContextParser<String, Object>) (p, c) -> TimeValue.parseTimeValue(p.text(), MIN_AGE.getPreferredName()),
+            MIN_AGE,
+            ValueType.VALUE
+        );
         PARSER.declareNamedObjects(
             ConstructingObjectParser.constructorArg(),
             (p, c, n) -> p.namedObject(LifecycleAction.class, n, null),
@@ -183,4 +176,20 @@ public class Phase implements ToXContentObject, Writeable {
         return Strings.toString(this, true, true);
     }
 
+    @UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT)
+    public boolean maybeAddDeprecationWarningForFreezeAction(String policyName) {
+        if (getActions().containsKey(FreezeAction.NAME)) {
+            deprecationLogger.warn(
+                DeprecationCategory.OTHER,
+                "ilm_freeze_action_deprecation",
+                "The freeze action in ILM is deprecated and will be removed in a future version;"
+                    + " this action is already a noop so it can be safely removed. Please remove the freeze action from the '"
+                    + policyName
+                    + "' policy."
+            );
+            return true;
+        }
+        return false;
+    }
+
 }

+ 31 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java

@@ -55,6 +55,7 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static org.elasticsearch.common.Strings.EMPTY_ARRAY;
 import static org.elasticsearch.transport.RemoteClusterPortSettings.TRANSPORT_VERSION_ADVANCED_REMOTE_CLUSTER_SECURITY;
 import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg;
 import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg;
@@ -170,21 +171,49 @@ public final class Authentication implements ToXContentObject {
             type = AuthenticationType.REALM;
             metadata = Map.of();
         }
+
         if (innerUser != null) {
-            authenticatingSubject = new Subject(innerUser, authenticatedBy, version, metadata);
+            authenticatingSubject = new Subject(
+                copyUserWithRolesRemovedForLegacyApiKeys(version, innerUser),
+                authenticatedBy,
+                version,
+                metadata
+            );
             // The lookup user for run-as currently doesn't have authentication metadata associated with them because
             // lookupUser only returns the User object. The lookup user for authorization delegation does have
             // authentication metadata, but the realm does not expose this difference between authenticatingUser and
             // delegateUser so effectively this is handled together with the authenticatingSubject not effectiveSubject.
+            // Note: we do not call copyUserWithRolesRemovedForLegacyApiKeys here because an API key is never the target of run-as
             effectiveSubject = new Subject(outerUser, lookedUpBy, version, Map.of());
         } else {
-            authenticatingSubject = effectiveSubject = new Subject(outerUser, authenticatedBy, version, metadata);
+            authenticatingSubject = effectiveSubject = new Subject(
+                copyUserWithRolesRemovedForLegacyApiKeys(version, outerUser),
+                authenticatedBy,
+                version,
+                metadata
+            );
         }
+
         if (Assertions.ENABLED) {
             checkConsistency();
         }
     }
 
+    private User copyUserWithRolesRemovedForLegacyApiKeys(TransportVersion version, User user) {
+        // API keys prior to 7.8 had synthetic role names. Strip these out to maintain the invariant that API keys don't have role names
+        if (type == AuthenticationType.API_KEY && version.onOrBefore(TransportVersions.V_7_8_0) && user.roles().length > 0) {
+            logger.debug(
+                "Stripping [{}] roles from API key user [{}] for legacy version [{}]",
+                user.roles().length,
+                user.principal(),
+                version
+            );
+            return new User(user.principal(), EMPTY_ARRAY, user.fullName(), user.email(), user.metadata(), user.enabled());
+        } else {
+            return user;
+        }
+    }
+
     /**
      * Get the {@link Subject} that performs the actual authentication. This normally means it provides a credentials.
      */

+ 15 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/KibanaOwnedReservedRoleDescriptors.java

@@ -9,6 +9,7 @@ package org.elasticsearch.xpack.core.security.authz.store;
 
 import org.elasticsearch.action.admin.indices.alias.TransportIndicesAliasesAction;
 import org.elasticsearch.action.admin.indices.delete.TransportDeleteIndexAction;
+import org.elasticsearch.action.admin.indices.mapping.put.TransportAutoPutMappingAction;
 import org.elasticsearch.action.admin.indices.mapping.put.TransportPutMappingAction;
 import org.elasticsearch.action.admin.indices.rollover.RolloverAction;
 import org.elasticsearch.action.admin.indices.settings.put.TransportUpdateSettingsAction;
@@ -428,7 +429,6 @@ class KibanaOwnedReservedRoleDescriptors {
                 RoleDescriptor.IndicesPrivileges.builder()
                     .indices(
                         "logs-cloud_security_posture.findings_latest-default*",
-                        "logs-cloud_security_posture.scores-default*",
                         "logs-cloud_security_posture.vulnerabilities_latest-default*"
                     )
                     .privileges(
@@ -440,6 +440,20 @@ class KibanaOwnedReservedRoleDescriptors {
                         TransportUpdateSettingsAction.TYPE.name()
                     )
                     .build(),
+                // For destination indices of the Cloud Security Posture packages that ships a
+                // transform (specific for scores indexes, as of 9.0.0 score indices will need to have auto_put priviliges)
+                RoleDescriptor.IndicesPrivileges.builder()
+                    .indices("logs-cloud_security_posture.scores-default*")
+                    .privileges(
+                        "create_index",
+                        "read",
+                        "index",
+                        "delete",
+                        TransportIndicesAliasesAction.NAME,
+                        TransportUpdateSettingsAction.TYPE.name(),
+                        TransportAutoPutMappingAction.TYPE.name()
+                    )
+                    .build(),
                 // For source indices of the Cloud Detection & Response (CDR) packages that ships a
                 // transform
                 RoleDescriptor.IndicesPrivileges.builder()

+ 8 - 8
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/FreezeActionTests.java

@@ -29,7 +29,8 @@ public class FreezeActionTests extends AbstractActionTestCase<FreezeAction> {
 
     @Override
     protected FreezeAction mutateInstance(FreezeAction instance) {
-        return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
+        // This class is a singleton
+        return null;
     }
 
     @Override
@@ -50,16 +51,15 @@ public class FreezeActionTests extends AbstractActionTestCase<FreezeAction> {
         assertEquals(3, steps.size());
         StepKey expectedFirstStepKey = new StepKey(phase, FreezeAction.NAME, FreezeAction.CONDITIONAL_SKIP_FREEZE_STEP);
         StepKey expectedSecondStepKey = new StepKey(phase, FreezeAction.NAME, CheckNotDataStreamWriteIndexStep.NAME);
-        StepKey expectedThirdStepKey = new StepKey(phase, FreezeAction.NAME, FreezeStep.NAME);
-
-        BranchingStep firstStep = (BranchingStep) steps.get(0);
-        CheckNotDataStreamWriteIndexStep secondStep = (CheckNotDataStreamWriteIndexStep) steps.get(1);
-        FreezeStep thirdStep = (FreezeStep) steps.get(2);
+        StepKey expectedThirdStepKey = new StepKey(phase, FreezeAction.NAME, FreezeAction.NAME);
 
+        NoopStep firstStep = (NoopStep) steps.get(0);
         assertThat(firstStep.getKey(), equalTo(expectedFirstStepKey));
-
+        assertThat(firstStep.getNextStepKey(), equalTo(nextStepKey));
+        NoopStep secondStep = (NoopStep) steps.get(1);
         assertEquals(expectedSecondStepKey, secondStep.getKey());
-        assertEquals(expectedThirdStepKey, secondStep.getNextStepKey());
+        assertEquals(nextStepKey, secondStep.getNextStepKey());
+        NoopStep thirdStep = (NoopStep) steps.get(2);
         assertEquals(expectedThirdStepKey, thirdStep.getKey());
         assertEquals(nextStepKey, thirdStep.getNextStepKey());
     }

+ 0 - 43
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/FreezeStepTests.java

@@ -1,43 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-package org.elasticsearch.xpack.core.ilm;
-
-import org.elasticsearch.xpack.core.ilm.Step.StepKey;
-
-public class FreezeStepTests extends AbstractStepTestCase<FreezeStep> {
-
-    @Override
-    public FreezeStep createRandomInstance() {
-        StepKey stepKey = randomStepKey();
-        StepKey nextStepKey = randomStepKey();
-
-        return new FreezeStep(stepKey, nextStepKey, client);
-    }
-
-    @Override
-    public FreezeStep mutateInstance(FreezeStep instance) {
-        StepKey key = instance.getKey();
-        StepKey nextKey = instance.getNextStepKey();
-
-        switch (between(0, 1)) {
-            case 0 -> key = new StepKey(key.phase(), key.action(), key.name() + randomAlphaOfLength(5));
-            case 1 -> nextKey = new StepKey(nextKey.phase(), nextKey.action(), nextKey.name() + randomAlphaOfLength(5));
-            default -> throw new AssertionError("Illegal randomisation branch");
-        }
-
-        return new FreezeStep(key, nextKey, instance.getClient());
-    }
-
-    @Override
-    public FreezeStep copyInstance(FreezeStep instance) {
-        return new FreezeStep(instance.getKey(), instance.getNextStepKey(), instance.getClient());
-    }
-
-    public void testIndexSurvives() {
-        assertTrue(createRandomInstance().indexSurvives());
-    }
-}

+ 1 - 0
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java

@@ -35,6 +35,7 @@ import static org.mockito.Mockito.mock;
 
 public class LifecyclePolicyTests extends AbstractXContentSerializingTestCase<LifecyclePolicy> {
 
+    // Excluding the deprecated freeze action and test it separately
     private String lifecycleName;
 
     @Override

+ 48 - 0
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationSerializationTests.java

@@ -13,16 +13,21 @@ import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.TransportVersionUtils;
 import org.elasticsearch.transport.RemoteClusterPortSettings;
+import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
 import org.elasticsearch.xpack.core.security.user.ElasticUser;
 import org.elasticsearch.xpack.core.security.user.InternalUsers;
 import org.elasticsearch.xpack.core.security.user.KibanaSystemUser;
 import org.elasticsearch.xpack.core.security.user.KibanaUser;
 import org.elasticsearch.xpack.core.security.user.User;
 
+import java.io.IOException;
 import java.util.Arrays;
+import java.util.Map;
 
 import static org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationSerializationHelper;
+import static org.hamcrest.Matchers.arrayContaining;
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.emptyArray;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
@@ -171,4 +176,47 @@ public class AuthenticationSerializationTests extends ESTestCase {
 
         assertEquals(kibanaSystemUser, readFrom);
     }
+
+    public void testRolesRemovedFromUserForLegacyApiKeys() throws IOException {
+        TransportVersion transportVersion = TransportVersionUtils.randomVersionBetween(
+            random(),
+            TransportVersions.V_7_0_0,
+            TransportVersions.V_7_8_0
+        );
+        Subject authenticatingSubject = new Subject(
+            new User("foo", "role"),
+            new Authentication.RealmRef(AuthenticationField.API_KEY_REALM_NAME, AuthenticationField.API_KEY_REALM_TYPE, "node"),
+            transportVersion,
+            Map.of(AuthenticationField.API_KEY_ID_KEY, "abc")
+        );
+        Subject effectiveSubject = new Subject(
+            new User("bar", "role"),
+            new Authentication.RealmRef("native", "native", "node"),
+            transportVersion,
+            Map.of()
+        );
+
+        {
+            Authentication actual = AuthenticationContextSerializer.decode(
+                Authentication.doEncode(authenticatingSubject, authenticatingSubject, Authentication.AuthenticationType.API_KEY)
+            );
+            assertThat(actual.getAuthenticatingSubject().getUser().roles(), is(emptyArray()));
+        }
+
+        {
+            Authentication actual = AuthenticationContextSerializer.decode(
+                Authentication.doEncode(effectiveSubject, authenticatingSubject, Authentication.AuthenticationType.API_KEY)
+            );
+            assertThat(actual.getAuthenticatingSubject().getUser().roles(), is(emptyArray()));
+            assertThat(actual.getEffectiveSubject().getUser().roles(), is(arrayContaining("role")));
+        }
+
+        {
+            // do not strip roles for authentication methods other than API key
+            Authentication actual = AuthenticationContextSerializer.decode(
+                Authentication.doEncode(effectiveSubject, effectiveSubject, Authentication.AuthenticationType.REALM)
+            );
+            assertThat(actual.getAuthenticatingSubject().getUser().roles(), is(arrayContaining("role")));
+        }
+    }
 }

+ 22 - 0
x-pack/plugin/core/template-resources/src/main/resources/agentless@mappings.json

@@ -0,0 +1,22 @@
+{
+  "template": {
+    "mappings": {
+      "properties": {
+        "v": {
+          "type": "object",
+          "enabled": false
+        },
+        "updated_at": {
+          "type": "date",
+          "format": "strict_date_optional_time||epoch_millis"
+        }
+      }
+    }
+  },
+  "_meta": {
+    "description": "default mappings for the agentless index template installed by x-pack",
+    "managed": true
+  },
+  "version": ${xpack.stack.template.version},
+  "deprecated": ${xpack.stack.template.deprecated}
+}

+ 15 - 0
x-pack/plugin/core/template-resources/src/main/resources/agentless@settings.json

@@ -0,0 +1,15 @@
+{
+  "template": {
+    "settings": {
+      "index": {
+        "hidden": true
+      }
+    }
+  },
+  "_meta": {
+    "description": "default settings for the agentless index template installed by x-pack",
+    "managed": true
+  },
+  "version": ${xpack.stack.template.version},
+  "deprecated": ${xpack.stack.template.deprecated}
+}

+ 15 - 0
x-pack/plugin/core/template-resources/src/main/resources/agentless@template.json

@@ -0,0 +1,15 @@
+{
+  "index_patterns": ["agentless-*-*"],
+  "priority": 100,
+  "composed_of": [
+    "agentless@mappings",
+    "agentless@settings"
+  ],
+  "allow_auto_create": true,
+  "_meta": {
+    "description": "default agentless template installed by x-pack",
+    "managed": true
+  },
+  "version": ${xpack.stack.template.version},
+  "deprecated": ${xpack.stack.template.deprecated}
+}

+ 3 - 5
x-pack/plugin/ent-search/qa/rest/roles.yml

@@ -4,13 +4,12 @@ admin:
     - manage_behavioral_analytics
     - manage
     - monitor
+    - manage_connector
   indices:
     - names: [
         # indices and search applications
         "test-*",
         "another-test-search-application",
-        ".elastic-connectors-v1",
-        ".elastic-connectors-sync-jobs-v1"
     ]
       privileges: [ "manage", "write", "read" ]
 
@@ -20,6 +19,7 @@ user:
     - manage_api_key
     - read_connector_secrets
     - write_connector_secrets
+    - monitor_connector
   indices:
     - names: [
       "test-index1",
@@ -27,9 +27,7 @@ user:
       "test-search-application-1",
       "test-search-application-with-aggs",
       "test-search-application-with-list",
-      "test-search-application-with-list-invalid",
-      ".elastic-connectors-v1",
-      ".elastic-connectors-sync-jobs-v1"
+      "test-search-application-with-list-invalid"
     ]
       privileges: [ "read" ]
 

+ 130 - 91
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorIndexService.java

@@ -21,6 +21,7 @@ import org.elasticsearch.action.support.WriteRequest;
 import org.elasticsearch.action.update.UpdateRequest;
 import org.elasticsearch.action.update.UpdateResponse;
 import org.elasticsearch.client.internal.Client;
+import org.elasticsearch.client.internal.OriginSettingClient;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.index.IndexNotFoundException;
 import org.elasticsearch.index.query.BoolQueryBuilder;
@@ -74,13 +75,15 @@ import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.elasticsearch.xpack.application.connector.ConnectorFiltering.fromXContentBytesConnectorFiltering;
 import static org.elasticsearch.xpack.application.connector.ConnectorFiltering.sortFilteringRulesByOrder;
 import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.MANAGED_CONNECTOR_INDEX_PREFIX;
+import static org.elasticsearch.xpack.core.ClientHelper.CONNECTORS_ORIGIN;
 
 /**
  * A service that manages persistent {@link Connector} configurations.
  */
 public class ConnectorIndexService {
 
-    private final Client client;
+    // The client to interact with the system index (internal user).
+    private final Client clientWithOrigin;
 
     public static final String CONNECTOR_INDEX_NAME = ConnectorTemplateRegistry.CONNECTOR_INDEX_NAME_PATTERN;
 
@@ -88,7 +91,7 @@ public class ConnectorIndexService {
      * @param client A client for executing actions on the connector index
      */
     public ConnectorIndexService(Client client) {
-        this.client = client;
+        this.clientWithOrigin = new OriginSettingClient(client, CONNECTORS_ORIGIN);
     }
 
     /**
@@ -134,7 +137,7 @@ public class ConnectorIndexService {
                         indexRequest = indexRequest.id(connectorId);
                     }
 
-                    client.index(
+                    clientWithOrigin.index(
                         indexRequest,
                         listener.delegateFailureAndWrap(
                             (ll, indexResponse) -> ll.onResponse(
@@ -201,7 +204,7 @@ public class ConnectorIndexService {
         try {
             final GetRequest getRequest = new GetRequest(CONNECTOR_INDEX_NAME).id(connectorId).realtime(true);
 
-            client.get(getRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, getResponse) -> {
+            clientWithOrigin.get(getRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, getResponse) -> {
                 if (getResponse.isExists() == false) {
                     l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
                     return;
@@ -248,13 +251,16 @@ public class ConnectorIndexService {
                             .id(connectorId)
                             .source(Map.of(Connector.IS_DELETED_FIELD.getPreferredName(), true))
                     );
-                client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, l, (ll, updateResponse) -> {
+                clientWithOrigin.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, l, (ll, updateResponse) -> {
                     if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
                         ll.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
                         return;
                     }
                     if (shouldDeleteSyncJobs) {
-                        new ConnectorSyncJobIndexService(client).deleteAllSyncJobsByConnectorId(connectorId, ll.map(r -> updateResponse));
+                        new ConnectorSyncJobIndexService(clientWithOrigin).deleteAllSyncJobsByConnectorId(
+                            connectorId,
+                            ll.map(r -> updateResponse)
+                        );
                     } else {
                         ll.onResponse(updateResponse);
                     }
@@ -294,7 +300,7 @@ public class ConnectorIndexService {
                 .fetchSource(true)
                 .sort(Connector.INDEX_NAME_FIELD.getPreferredName(), SortOrder.ASC);
             final SearchRequest req = new SearchRequest(CONNECTOR_INDEX_NAME).source(source);
-            client.search(req, new ActionListener<>() {
+            clientWithOrigin.search(req, new ActionListener<>() {
                 @Override
                 public void onResponse(SearchResponse searchResponse) {
                     try {
@@ -476,7 +482,7 @@ public class ConnectorIndexService {
                     return;
                 }
 
-                client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, l, (ll, updateResponse) -> {
+                clientWithOrigin.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, l, (ll, updateResponse) -> {
                     if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
                         ll.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
                         return;
@@ -513,13 +519,16 @@ public class ConnectorIndexService {
                         }
                     })
             );
-            client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
-                if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                    l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                    return;
-                }
-                l.onResponse(updateResponse);
-            }));
+            clientWithOrigin.update(
+                updateRequest,
+                new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
+                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                        l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                        return;
+                    }
+                    l.onResponse(updateResponse);
+                })
+            );
         } catch (Exception e) {
             listener.onFailure(e);
         }
@@ -541,13 +550,16 @@ public class ConnectorIndexService {
                     .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
                     .source(request.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS))
             );
-            client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
-                if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                    l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                    return;
-                }
-                l.onResponse(updateResponse);
-            }));
+            clientWithOrigin.update(
+                updateRequest,
+                new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
+                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                        l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                        return;
+                    }
+                    l.onResponse(updateResponse);
+                })
+            );
         } catch (Exception e) {
             listener.onFailure(e);
         }
@@ -568,13 +580,16 @@ public class ConnectorIndexService {
                     .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
                     .source(Map.of(Connector.FILTERING_FIELD.getPreferredName(), filtering))
             );
-            client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
-                if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                    l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                    return;
-                }
-                l.onResponse(updateResponse);
-            }));
+            clientWithOrigin.update(
+                updateRequest,
+                new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
+                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                        l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                        return;
+                    }
+                    l.onResponse(updateResponse);
+                })
+            );
         } catch (Exception e) {
             listener.onFailure(e);
         }
@@ -595,13 +610,16 @@ public class ConnectorIndexService {
                     .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
                     .source(Map.of(Connector.FEATURES_FIELD.getPreferredName(), features))
             );
-            client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
-                if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                    l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                    return;
-                }
-                l.onResponse(updateResponse);
-            }));
+            clientWithOrigin.update(
+                updateRequest,
+                new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
+                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                        l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                        return;
+                    }
+                    l.onResponse(updateResponse);
+                })
+            );
         } catch (Exception e) {
             listener.onFailure(e);
         }
@@ -657,13 +675,16 @@ public class ConnectorIndexService {
                         .source(Map.of(Connector.FILTERING_FIELD.getPreferredName(), List.of(connectorFilteringWithUpdatedDraft)))
                 );
 
-                client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (ll, updateResponse) -> {
-                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                        ll.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                        return;
-                    }
-                    ll.onResponse(updateResponse);
-                }));
+                clientWithOrigin.update(
+                    updateRequest,
+                    new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (ll, updateResponse) -> {
+                        if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                            ll.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                            return;
+                        }
+                        ll.onResponse(updateResponse);
+                    })
+                );
             }));
 
         } catch (Exception e) {
@@ -705,7 +726,7 @@ public class ConnectorIndexService {
                         .source(Map.of(Connector.FILTERING_FIELD.getPreferredName(), List.of(activatedConnectorFiltering)))
                 );
 
-                client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, l, (ll, updateResponse) -> {
+                clientWithOrigin.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, l, (ll, updateResponse) -> {
                     if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
                         ll.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
                         return;
@@ -762,7 +783,7 @@ public class ConnectorIndexService {
                         .source(Map.of(Connector.FILTERING_FIELD.getPreferredName(), List.of(activatedConnectorFiltering)))
                 );
 
-                client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, l, (ll, updateResponse) -> {
+                clientWithOrigin.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, l, (ll, updateResponse) -> {
                     if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
                         ll.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
                         return;
@@ -790,13 +811,16 @@ public class ConnectorIndexService {
                     .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
                     .source(Map.of(Connector.LAST_SEEN_FIELD.getPreferredName(), Instant.now()))
             );
-            client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
-                if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                    l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                    return;
-                }
-                l.onResponse(updateResponse);
-            }));
+            clientWithOrigin.update(
+                updateRequest,
+                new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
+                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                        l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                        return;
+                    }
+                    l.onResponse(updateResponse);
+                })
+            );
         } catch (Exception e) {
             listener.onFailure(e);
         }
@@ -817,13 +841,16 @@ public class ConnectorIndexService {
                     .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
                     .source(request.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS))
             );
-            client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
-                if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                    l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                    return;
-                }
-                l.onResponse(updateResponse);
-            }));
+            clientWithOrigin.update(
+                updateRequest,
+                new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
+                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                        l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                        return;
+                    }
+                    l.onResponse(updateResponse);
+                })
+            );
         } catch (Exception e) {
             listener.onFailure(e);
         }
@@ -887,13 +914,16 @@ public class ConnectorIndexService {
                                 )
                             )
                     );
-                client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (ll, updateResponse) -> {
-                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                        ll.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                        return;
-                    }
-                    ll.onResponse(updateResponse);
-                }));
+                clientWithOrigin.update(
+                    updateRequest,
+                    new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (ll, updateResponse) -> {
+                        if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                            ll.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                            return;
+                        }
+                        ll.onResponse(updateResponse);
+                    })
+                );
             }));
         } catch (Exception e) {
             listener.onFailure(e);
@@ -916,13 +946,16 @@ public class ConnectorIndexService {
                     .source(Map.of(Connector.PIPELINE_FIELD.getPreferredName(), request.getPipeline()))
                     .source(request.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS))
             );
-            client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
-                if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                    l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                    return;
-                }
-                l.onResponse(updateResponse);
-            }));
+            clientWithOrigin.update(
+                updateRequest,
+                new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
+                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                        l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                        return;
+                    }
+                    l.onResponse(updateResponse);
+                })
+            );
         } catch (Exception e) {
             listener.onFailure(e);
         }
@@ -981,7 +1014,7 @@ public class ConnectorIndexService {
                                 }
                             })
                     );
-                    client.update(
+                    clientWithOrigin.update(
                         updateRequest,
                         new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (lll, updateResponse) -> {
                             if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
@@ -1014,13 +1047,16 @@ public class ConnectorIndexService {
                     .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
                     .source(Map.of(Connector.SCHEDULING_FIELD.getPreferredName(), request.getScheduling()))
             );
-            client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
-                if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                    l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                    return;
-                }
-                l.onResponse(updateResponse);
-            }));
+            clientWithOrigin.update(
+                updateRequest,
+                new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
+                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                        l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                        return;
+                    }
+                    l.onResponse(updateResponse);
+                })
+            );
         } catch (Exception e) {
             listener.onFailure(e);
         }
@@ -1056,7 +1092,7 @@ public class ConnectorIndexService {
                         )
 
                 );
-                client.update(
+                clientWithOrigin.update(
                     updateRequest,
                     new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (updateListener, updateResponse) -> {
                         if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
@@ -1099,7 +1135,7 @@ public class ConnectorIndexService {
                         .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
                         .source(Map.of(Connector.STATUS_FIELD.getPreferredName(), request.getStatus()))
                 );
-                client.update(
+                clientWithOrigin.update(
                     updateRequest,
                     new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (updateListener, updateResponse) -> {
                         if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
@@ -1127,13 +1163,16 @@ public class ConnectorIndexService {
                     .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
                     .source(request.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS))
             );
-            client.update(updateRequest, new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
-                if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
-                    l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
-                    return;
-                }
-                l.onResponse(updateResponse);
-            }));
+            clientWithOrigin.update(
+                updateRequest,
+                new DelegatingIndexNotFoundActionListener<>(connectorId, listener, (l, updateResponse) -> {
+                    if (updateResponse.getResult() == UpdateResponse.Result.NOT_FOUND) {
+                        l.onFailure(new ResourceNotFoundException(connectorNotFoundErrorMsg(connectorId)));
+                        return;
+                    }
+                    l.onResponse(updateResponse);
+                })
+            );
         } catch (Exception e) {
             listener.onFailure(e);
         }
@@ -1211,7 +1250,7 @@ public class ConnectorIndexService {
             final SearchSourceBuilder searchSource = new SearchSourceBuilder().query(boolFilterQueryBuilder);
 
             final SearchRequest searchRequest = new SearchRequest(CONNECTOR_INDEX_NAME).source(searchSource);
-            client.search(searchRequest, new ActionListener<>() {
+            clientWithOrigin.search(searchRequest, new ActionListener<>() {
                 @Override
                 public void onResponse(SearchResponse searchResponse) {
                     boolean indexNameIsInUse = searchResponse.getHits().getTotalHits().value() > 0L;

+ 2 - 16
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/ConnectorActionRequest.java

@@ -9,12 +9,9 @@ package org.elasticsearch.xpack.application.connector.action;
 
 import org.elasticsearch.action.ActionRequest;
 import org.elasticsearch.action.ActionRequestValidationException;
-import org.elasticsearch.action.IndicesRequest;
-import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.cluster.metadata.MetadataCreateIndexService;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.indices.InvalidIndexNameException;
-import org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry;
 
 import java.io.IOException;
 
@@ -22,10 +19,9 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
 import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.MANAGED_CONNECTOR_INDEX_PREFIX;
 
 /**
- * Abstract base class for action requests targeting the connectors index. Implements {@link org.elasticsearch.action.IndicesRequest}
- * to ensure index-level privilege support. This class defines the connectors index as the target for all derived action requests.
+ * Abstract base class for action requests targeting the connectors index.
  */
-public abstract class ConnectorActionRequest extends ActionRequest implements IndicesRequest {
+public abstract class ConnectorActionRequest extends ActionRequest {
 
     public ConnectorActionRequest() {
         super();
@@ -78,14 +74,4 @@ public abstract class ConnectorActionRequest extends ActionRequest implements In
         }
         return validationException;
     }
-
-    @Override
-    public String[] indices() {
-        return new String[] { ConnectorTemplateRegistry.CONNECTOR_INDEX_NAME_PATTERN };
-    }
-
-    @Override
-    public IndicesOptions indicesOptions() {
-        return IndicesOptions.lenientExpandHidden();
-    }
 }

+ 1 - 10
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/DeleteConnectorAction.java

@@ -18,7 +18,6 @@ import org.elasticsearch.xcontent.ParseField;
 import org.elasticsearch.xcontent.ToXContentObject;
 import org.elasticsearch.xcontent.XContentBuilder;
 import org.elasticsearch.xcontent.XContentParser;
-import org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry;
 
 import java.io.IOException;
 import java.util.Objects;
@@ -28,7 +27,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class DeleteConnectorAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/delete";
+    public static final String NAME = "cluster:admin/xpack/connector/delete";
     public static final ActionType<AcknowledgedResponse> INSTANCE = new ActionType<>(NAME);
 
     private DeleteConnectorAction() {/* no instances */}
@@ -71,14 +70,6 @@ public class DeleteConnectorAction {
             return deleteSyncJobs;
         }
 
-        @Override
-        public String[] indices() {
-            // When deleting a connector, corresponding sync jobs can also be deleted
-            return new String[] {
-                ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN,
-                ConnectorTemplateRegistry.CONNECTOR_INDEX_NAME_PATTERN };
-        }
-
         @Override
         public void writeTo(StreamOutput out) throws IOException {
             super.writeTo(out);

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/GetConnectorAction.java

@@ -30,7 +30,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class GetConnectorAction {
 
-    public static final String NAME = "indices:data/read/xpack/connector/get";
+    public static final String NAME = "cluster:admin/xpack/connector/get";
     public static final ActionType<GetConnectorAction.Response> INSTANCE = new ActionType<>(NAME);
 
     private GetConnectorAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/ListConnectorAction.java

@@ -35,7 +35,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class ListConnectorAction {
 
-    public static final String NAME = "indices:data/read/xpack/connector/list";
+    public static final String NAME = "cluster:admin/xpack/connector/list";
     public static final ActionType<ListConnectorAction.Response> INSTANCE = new ActionType<>(NAME);
 
     private ListConnectorAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/PostConnectorAction.java

@@ -25,7 +25,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class PostConnectorAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/post";
+    public static final String NAME = "cluster:admin/xpack/connector/post";
     public static final ActionType<ConnectorCreateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private PostConnectorAction() {/* no instances */}

+ 2 - 3
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/PutConnectorAction.java

@@ -9,7 +9,6 @@ package org.elasticsearch.xpack.application.connector.action;
 
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.ActionType;
-import org.elasticsearch.action.IndicesRequest;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
@@ -27,12 +26,12 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class PutConnectorAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/put";
+    public static final String NAME = "cluster:admin/xpack/connector/put";
     public static final ActionType<ConnectorCreateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private PutConnectorAction() {/* no instances */}
 
-    public static class Request extends ConnectorActionRequest implements IndicesRequest, ToXContentObject {
+    public static class Request extends ConnectorActionRequest implements ToXContentObject {
 
         @Nullable
         private final String connectorId;

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorActiveFilteringAction.java

@@ -22,7 +22,7 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
 
 public class UpdateConnectorActiveFilteringAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_filtering/activate";
+    public static final String NAME = "cluster:admin/xpack/connector/update_filtering/activate";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorActiveFilteringAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorApiKeyIdAction.java

@@ -27,7 +27,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class UpdateConnectorApiKeyIdAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_api_key_id";
+    public static final String NAME = "cluster:admin/xpack/connector/update_api_key_id";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorApiKeyIdAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorConfigurationAction.java

@@ -32,7 +32,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class UpdateConnectorConfigurationAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_configuration";
+    public static final String NAME = "cluster:admin/xpack/connector/update_configuration";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorConfigurationAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorErrorAction.java

@@ -27,7 +27,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class UpdateConnectorErrorAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_error";
+    public static final String NAME = "cluster:admin/xpack/connector/update_error";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorErrorAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorFeaturesAction.java

@@ -27,7 +27,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class UpdateConnectorFeaturesAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_features";
+    public static final String NAME = "cluster:admin/xpack/connector/update_features";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorFeaturesAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorFilteringAction.java

@@ -32,7 +32,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class UpdateConnectorFilteringAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_filtering";
+    public static final String NAME = "cluster:admin/xpack/connector/update_filtering";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorFilteringAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorFilteringValidationAction.java

@@ -27,7 +27,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class UpdateConnectorFilteringValidationAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_filtering/draft_validation";
+    public static final String NAME = "cluster:admin/xpack/connector/update_filtering/draft_validation";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorFilteringValidationAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorIndexNameAction.java

@@ -27,7 +27,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class UpdateConnectorIndexNameAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_index_name";
+    public static final String NAME = "cluster:admin/xpack/connector/update_index_name";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorIndexNameAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorLastSeenAction.java

@@ -23,7 +23,7 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
 
 public class UpdateConnectorLastSeenAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_last_seen";
+    public static final String NAME = "cluster:admin/xpack/connector/update_last_seen";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorLastSeenAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorLastSyncStatsAction.java

@@ -32,7 +32,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class UpdateConnectorLastSyncStatsAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_last_sync_stats";
+    public static final String NAME = "cluster:admin/xpack/connector/update_last_sync_stats";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorLastSyncStatsAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorNameAction.java

@@ -27,7 +27,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class UpdateConnectorNameAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_name";
+    public static final String NAME = "cluster:admin/xpack/connector/update_name";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorNameAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorNativeAction.java

@@ -26,7 +26,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class UpdateConnectorNativeAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_native";
+    public static final String NAME = "cluster:admin/xpack/connector/update_native";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorNativeAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorPipelineAction.java

@@ -27,7 +27,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class UpdateConnectorPipelineAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_pipeline";
+    public static final String NAME = "cluster:admin/xpack/connector/update_pipeline";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorPipelineAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorSchedulingAction.java

@@ -32,7 +32,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class UpdateConnectorSchedulingAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_scheduling";
+    public static final String NAME = "cluster:admin/xpack/connector/update_scheduling";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorSchedulingAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorServiceTypeAction.java

@@ -26,7 +26,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class UpdateConnectorServiceTypeAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_service_type";
+    public static final String NAME = "cluster:admin/xpack/connector/update_service_type";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorServiceTypeAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/action/UpdateConnectorStatusAction.java

@@ -28,7 +28,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class UpdateConnectorStatusAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/update_status";
+    public static final String NAME = "cluster:admin/xpack/connector/update_status";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     public UpdateConnectorStatusAction() {/* no instances */}

+ 16 - 13
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexService.java

@@ -28,6 +28,7 @@ import org.elasticsearch.action.support.WriteRequest;
 import org.elasticsearch.action.update.UpdateRequest;
 import org.elasticsearch.action.update.UpdateResponse;
 import org.elasticsearch.client.internal.Client;
+import org.elasticsearch.client.internal.OriginSettingClient;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.index.IndexNotFoundException;
 import org.elasticsearch.index.engine.DocumentMissingException;
@@ -68,6 +69,7 @@ import java.util.stream.Stream;
 
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.elasticsearch.xpack.application.connector.ConnectorIndexService.CONNECTOR_INDEX_NAME;
+import static org.elasticsearch.xpack.core.ClientHelper.CONNECTORS_ORIGIN;
 
 /**
  * A service that manages persistent {@link ConnectorSyncJob} configurations.
@@ -76,7 +78,8 @@ public class ConnectorSyncJobIndexService {
 
     private static final Long ZERO = 0L;
 
-    private final Client client;
+    // The client to interact with the system index (internal user).
+    private final Client clientWithOrigin;
 
     public static final String CONNECTOR_SYNC_JOB_INDEX_NAME = ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN;
 
@@ -84,7 +87,7 @@ public class ConnectorSyncJobIndexService {
      * @param client A client for executing actions on the connectors sync jobs index.
      */
     public ConnectorSyncJobIndexService(Client client) {
-        this.client = client;
+        this.clientWithOrigin = new OriginSettingClient(client, CONNECTORS_ORIGIN);
     }
 
     /**
@@ -149,7 +152,7 @@ public class ConnectorSyncJobIndexService {
 
                     indexRequest.source(syncJob.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS));
 
-                    client.index(
+                    clientWithOrigin.index(
                         indexRequest,
                         l.delegateFailureAndWrap(
                             (ll, indexResponse) -> ll.onResponse(new PostConnectorSyncJobAction.Response(indexResponse.getId()))
@@ -175,7 +178,7 @@ public class ConnectorSyncJobIndexService {
             .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
 
         try {
-            client.delete(
+            clientWithOrigin.delete(
                 deleteRequest,
                 new DelegatingIndexNotFoundOrDocumentMissingActionListener<>(connectorSyncJobId, listener, (l, deleteResponse) -> {
                     if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
@@ -205,7 +208,7 @@ public class ConnectorSyncJobIndexService {
         ).doc(Map.of(ConnectorSyncJob.LAST_SEEN_FIELD.getPreferredName(), newLastSeen));
 
         try {
-            client.update(
+            clientWithOrigin.update(
                 updateRequest,
                 new DelegatingIndexNotFoundOrDocumentMissingActionListener<>(connectorSyncJobId, listener, (l, updateResponse) -> {
                     if (updateResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
@@ -230,7 +233,7 @@ public class ConnectorSyncJobIndexService {
         final GetRequest getRequest = new GetRequest(CONNECTOR_SYNC_JOB_INDEX_NAME).id(connectorSyncJobId).realtime(true);
 
         try {
-            client.get(
+            clientWithOrigin.get(
                 getRequest,
                 new DelegatingIndexNotFoundOrDocumentMissingActionListener<>(connectorSyncJobId, listener, (l, getResponse) -> {
                     if (getResponse.isExists() == false) {
@@ -306,7 +309,7 @@ public class ConnectorSyncJobIndexService {
                     WriteRequest.RefreshPolicy.IMMEDIATE
                 ).doc(syncJobFieldsToUpdate);
 
-                client.update(
+                clientWithOrigin.update(
                     updateRequest,
                     new DelegatingIndexNotFoundOrDocumentMissingActionListener<>(
                         connectorSyncJobId,
@@ -355,7 +358,7 @@ public class ConnectorSyncJobIndexService {
 
             final SearchRequest searchRequest = new SearchRequest(CONNECTOR_SYNC_JOB_INDEX_NAME).source(searchSource);
 
-            client.search(searchRequest, new ActionListener<>() {
+            clientWithOrigin.search(searchRequest, new ActionListener<>() {
                 @Override
                 public void onResponse(SearchResponse searchResponse) {
                     try {
@@ -474,7 +477,7 @@ public class ConnectorSyncJobIndexService {
         ).doc(fieldsToUpdate);
 
         try {
-            client.update(
+            clientWithOrigin.update(
                 updateRequest,
                 new DelegatingIndexNotFoundOrDocumentMissingActionListener<>(syncJobId, listener, (l, updateResponse) -> {
                     if (updateResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
@@ -501,7 +504,7 @@ public class ConnectorSyncJobIndexService {
 
             final GetRequest request = new GetRequest(CONNECTOR_INDEX_NAME, connectorId);
 
-            client.get(request, new ActionListener<>() {
+            clientWithOrigin.get(request, new ActionListener<>() {
                 @Override
                 public void onResponse(GetResponse response) {
                     final boolean connectorDoesNotExist = response.isExists() == false;
@@ -594,7 +597,7 @@ public class ConnectorSyncJobIndexService {
                         )
                     );
 
-                client.update(
+                clientWithOrigin.update(
                     updateRequest,
                     new DelegatingIndexNotFoundOrDocumentMissingActionListener<>(
                         connectorSyncJobId,
@@ -629,7 +632,7 @@ public class ConnectorSyncJobIndexService {
             )
         ).setRefresh(true).setIndicesOptions(IndicesOptions.fromOptions(true, true, false, false));
 
-        client.execute(DeleteByQueryAction.INSTANCE, deleteByQueryRequest, listener.delegateFailureAndWrap((l, r) -> {
+        clientWithOrigin.execute(DeleteByQueryAction.INSTANCE, deleteByQueryRequest, listener.delegateFailureAndWrap((l, r) -> {
             final List<BulkItemResponse.Failure> bulkDeleteFailures = r.getBulkFailures();
             if (bulkDeleteFailures.isEmpty() == false) {
                 l.onFailure(
@@ -681,7 +684,7 @@ public class ConnectorSyncJobIndexService {
                     WriteRequest.RefreshPolicy.IMMEDIATE
                 ).doc(document);
 
-                client.update(
+                clientWithOrigin.update(
                     updateRequest,
                     new DelegatingIndexNotFoundOrDocumentMissingActionListener<>(
                         connectorSyncJobId,

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/CancelConnectorSyncJobAction.java

@@ -28,7 +28,7 @@ import static org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyn
 
 public class CancelConnectorSyncJobAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/sync_job/cancel";
+    public static final String NAME = "cluster:admin/xpack/connector/sync_job/cancel";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<ConnectorUpdateActionResponse>(NAME);
 
     private CancelConnectorSyncJobAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/CheckInConnectorSyncJobAction.java

@@ -28,7 +28,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class CheckInConnectorSyncJobAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/sync_job/check_in";
+    public static final String NAME = "cluster:admin/xpack/connector/sync_job/check_in";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private CheckInConnectorSyncJobAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/ClaimConnectorSyncJobAction.java

@@ -31,7 +31,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class ClaimConnectorSyncJobAction {
     public static final ParseField CONNECTOR_SYNC_JOB_ID_FIELD = new ParseField("connector_sync_job_id");
-    public static final String NAME = "indices:data/write/xpack/connector/sync_job/claim";
+    public static final String NAME = "cluster:admin/xpack/connector/sync_job/claim";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private ClaimConnectorSyncJobAction() {/* no instances */}

+ 1 - 16
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/ConnectorSyncJobActionRequest.java

@@ -8,19 +8,14 @@
 package org.elasticsearch.xpack.application.connector.syncjob.action;
 
 import org.elasticsearch.action.ActionRequest;
-import org.elasticsearch.action.IndicesRequest;
-import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.common.io.stream.StreamInput;
-import org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry;
 
 import java.io.IOException;
 
 /**
  * Abstract base class for action requests targeting the connector sync job index.
- * Implements {@link org.elasticsearch.action.IndicesRequest} to ensure index-level privilege support.
- * This class defines the connectors sync job index as the target for all derived action requests.
  */
-public abstract class ConnectorSyncJobActionRequest extends ActionRequest implements IndicesRequest {
+public abstract class ConnectorSyncJobActionRequest extends ActionRequest {
 
     public ConnectorSyncJobActionRequest() {
         super();
@@ -29,14 +24,4 @@ public abstract class ConnectorSyncJobActionRequest extends ActionRequest implem
     public ConnectorSyncJobActionRequest(StreamInput in) throws IOException {
         super(in);
     }
-
-    @Override
-    public String[] indices() {
-        return new String[] { ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN };
-    }
-
-    @Override
-    public IndicesOptions indicesOptions() {
-        return IndicesOptions.lenientExpandHidden();
-    }
 }

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/DeleteConnectorSyncJobAction.java

@@ -28,7 +28,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class DeleteConnectorSyncJobAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/sync_job/delete";
+    public static final String NAME = "cluster:admin/xpack/connector/sync_job/delete";
     public static final ActionType<AcknowledgedResponse> INSTANCE = new ActionType<>(NAME);
 
     private DeleteConnectorSyncJobAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/GetConnectorSyncJobAction.java

@@ -29,7 +29,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class GetConnectorSyncJobAction {
 
-    public static final String NAME = "indices:data/read/xpack/connector/sync_job/get";
+    public static final String NAME = "cluster:admin/xpack/connector/sync_job/get";
     public static final ActionType<GetConnectorSyncJobAction.Response> INSTANCE = new ActionType<>(NAME);
 
     private GetConnectorSyncJobAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/ListConnectorSyncJobsAction.java

@@ -33,7 +33,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class ListConnectorSyncJobsAction {
 
-    public static final String NAME = "indices:data/read/xpack/connector/sync_job/list";
+    public static final String NAME = "cluster:admin/xpack/connector/sync_job/list";
     public static final ActionType<ListConnectorSyncJobsAction.Response> INSTANCE = new ActionType<>(NAME);
 
     private ListConnectorSyncJobsAction() {/* no instances */}

+ 1 - 10
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/PostConnectorSyncJobAction.java

@@ -18,7 +18,6 @@ import org.elasticsearch.xcontent.ToXContentObject;
 import org.elasticsearch.xcontent.XContentBuilder;
 import org.elasticsearch.xcontent.XContentParser;
 import org.elasticsearch.xpack.application.connector.Connector;
-import org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry;
 import org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyncJob;
 import org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyncJobTriggerMethod;
 import org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyncJobType;
@@ -32,7 +31,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstr
 
 public class PostConnectorSyncJobAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/sync_job/post";
+    public static final String NAME = "cluster:admin/xpack/connector/sync_job/post";
     public static final ActionType<PostConnectorSyncJobAction.Response> INSTANCE = new ActionType<>(NAME);
 
     private PostConnectorSyncJobAction() {/* no instances */}
@@ -140,14 +139,6 @@ public class PostConnectorSyncJobAction {
         public int hashCode() {
             return Objects.hash(id, jobType, triggerMethod);
         }
-
-        @Override
-        public String[] indices() {
-            // Creating a new sync job requires reading from connector index
-            return new String[] {
-                ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN,
-                ConnectorTemplateRegistry.CONNECTOR_INDEX_NAME_PATTERN };
-        }
     }
 
     public static class Response extends ActionResponse implements ToXContentObject {

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/UpdateConnectorSyncJobErrorAction.java

@@ -28,7 +28,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
 
 public class UpdateConnectorSyncJobErrorAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/sync_job/update_error";
+    public static final String NAME = "cluster:admin/xpack/connector/sync_job/update_error";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorSyncJobErrorAction() {/* no instances */}

+ 1 - 1
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/action/UpdateConnectorSyncJobIngestionStatsAction.java

@@ -35,7 +35,7 @@ import static org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyn
 
 public class UpdateConnectorSyncJobIngestionStatsAction {
 
-    public static final String NAME = "indices:data/write/xpack/connector/sync_job/update_stats";
+    public static final String NAME = "cluster:admin/xpack/connector/sync_job/update_stats";
     public static final ActionType<ConnectorUpdateActionResponse> INSTANCE = new ActionType<>(NAME);
 
     private UpdateConnectorSyncJobIngestionStatsAction() {/* no instances */}

+ 14 - 1
x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/predicate/Range.java

@@ -8,6 +8,7 @@ package org.elasticsearch.xpack.esql.core.expression.predicate;
 
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.expression.Literal;
 import org.elasticsearch.xpack.esql.core.expression.function.scalar.ScalarFunction;
 import org.elasticsearch.xpack.esql.core.expression.predicate.operator.comparison.BinaryComparison;
 import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
@@ -85,10 +86,22 @@ public class Range extends ScalarFunction {
         return zoneId;
     }
 
+    /**
+     * In case that the range is empty due to foldable, invalid bounds, but the bounds themselves are not yet folded, the optimizer will
+     * need two passes to fold this.
+     * That's because we shouldn't perform folding when trying to determine foldability.
+     */
     @Override
     public boolean foldable() {
         if (lower.foldable() && upper.foldable()) {
-            return areBoundariesInvalid() || value.foldable();
+            if (value().foldable()) {
+                return true;
+            }
+
+            // We cannot fold the bounds here; but if they're already literals, we can check if the range is always empty.
+            if (lower() instanceof Literal && upper() instanceof Literal) {
+                return areBoundariesInvalid();
+            }
         }
 
         return false;

+ 0 - 8
x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/plugin/KqlFunctionIT.java

@@ -15,10 +15,8 @@ import org.elasticsearch.index.query.QueryShardException;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.xpack.esql.VerificationException;
 import org.elasticsearch.xpack.esql.action.AbstractEsqlIntegTestCase;
-import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
 import org.elasticsearch.xpack.kql.KqlPlugin;
 import org.junit.Before;
-import org.junit.BeforeClass;
 
 import java.util.Collection;
 import java.util.List;
@@ -27,12 +25,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcke
 import static org.hamcrest.CoreMatchers.containsString;
 
 public class KqlFunctionIT extends AbstractEsqlIntegTestCase {
-
-    @BeforeClass
-    protected static void ensureKqlFunctionEnabled() {
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-    }
-
     @Before
     public void setupIndex() {
         createAndPopulateIndex();

+ 1 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

@@ -452,7 +452,7 @@ public class EsqlCapabilities {
         /**
          * KQL function
          */
-        KQL_FUNCTION(Build.current().isSnapshot()),
+        KQL_FUNCTION,
 
         /**
          * Hash function

+ 4 - 2
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java

@@ -424,7 +424,10 @@ public class EsqlFunctionRegistry {
                 def(MvSum.class, MvSum::new, "mv_sum"),
                 def(Split.class, Split::new, "split") },
             // fulltext functions
-            new FunctionDefinition[] { def(Match.class, bi(Match::new), "match"), def(QueryString.class, uni(QueryString::new), "qstr") } };
+            new FunctionDefinition[] {
+                def(Kql.class, uni(Kql::new), "kql"),
+                def(Match.class, bi(Match::new), "match"),
+                def(QueryString.class, uni(QueryString::new), "qstr") } };
 
     }
 
@@ -434,7 +437,6 @@ public class EsqlFunctionRegistry {
                 // The delay() function is for debug/snapshot environments only and should never be enabled in a non-snapshot build.
                 // This is an experimental function and can be removed without notice.
                 def(Delay.class, Delay::new, "delay"),
-                def(Kql.class, uni(Kql::new), "kql"),
                 def(Rate.class, Rate::withUnresolvedTimestamp, "rate"),
                 def(Term.class, bi(Term::new), "term") } };
     }

+ 1 - 3
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/FullTextWritables.java

@@ -25,10 +25,8 @@ public class FullTextWritables {
         entries.add(MultiMatchQueryPredicate.ENTRY);
         entries.add(QueryString.ENTRY);
         entries.add(Match.ENTRY);
+        entries.add(Kql.ENTRY);
 
-        if (EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled()) {
-            entries.add(Kql.ENTRY);
-        }
         if (EsqlCapabilities.Cap.TERM_FUNCTION.isEnabled()) {
             entries.add(Term.ENTRY);
         }

+ 4 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java

@@ -270,6 +270,10 @@ public class CsvTests extends ESTestCase {
                 "can't use TERM function in csv tests",
                 testCase.requiredCapabilities.contains(EsqlCapabilities.Cap.TERM_FUNCTION.capabilityName())
             );
+            assumeFalse(
+                "CSV tests cannot correctly handle the field caps change",
+                testCase.requiredCapabilities.contains(EsqlCapabilities.Cap.SEMANTIC_TEXT_FIELD_CAPS.capabilityName())
+            );
             if (Build.current().isSnapshot()) {
                 assertThat(
                     "Capability is not included in the enabled list capabilities on a snapshot build. Spelling mistake?",

+ 0 - 17
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java

@@ -1288,9 +1288,6 @@ public class VerifierTests extends ESTestCase {
     }
 
     public void testKqlFunctionsNotAllowedAfterCommands() throws Exception {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         // Source commands
         assertEquals("1:13: [KQL] function cannot be used after SHOW", error("show info | where kql(\"8.16.0\")"));
         assertEquals("1:17: [KQL] function cannot be used after ROW", error("row a= \"Anna\" | where kql(\"Anna\")"));
@@ -1348,9 +1345,6 @@ public class VerifierTests extends ESTestCase {
     }
 
     public void testKqlFunctionOnlyAllowedInWhere() throws Exception {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         assertEquals("1:9: [KQL] function is only supported in WHERE commands", error("row a = kql(\"Anna\")"));
         checkFullTextFunctionsOnlyAllowedInWhere("KQL", "kql(\"Anna\")", "function");
     }
@@ -1402,9 +1396,6 @@ public class VerifierTests extends ESTestCase {
     }
 
     public void testKqlFunctionArgNotNullOrConstant() throws Exception {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         assertEquals(
             "1:19: argument of [kql(first_name)] must be a constant, received [first_name]",
             error("from test | where kql(first_name)")
@@ -1418,9 +1409,6 @@ public class VerifierTests extends ESTestCase {
     }
 
     public void testKqlFunctionWithDisjunctions() {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         checkWithDisjunctions("KQL", "kql(\"first_name: Anna\")", "function");
     }
 
@@ -1463,8 +1451,6 @@ public class VerifierTests extends ESTestCase {
         checkWithFullTextFunctionsDisjunctions("MATCH", "match(last_name, \"Smith\")", "function");
         checkWithFullTextFunctionsDisjunctions(":", "last_name : \"Smith\"", "operator");
         checkWithFullTextFunctionsDisjunctions("QSTR", "qstr(\"last_name: Smith\")", "function");
-
-        assumeTrue("KQL function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
         checkWithFullTextFunctionsDisjunctions("KQL", "kql(\"last_name: Smith\")", "function");
     }
 
@@ -1493,9 +1479,6 @@ public class VerifierTests extends ESTestCase {
     }
 
     public void testKqlFunctionWithNonBooleanFunctions() {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         checkFullTextFunctionsWithNonBooleanFunctions("KQL", "kql(\"first_name: Anna\")", "function");
     }
 

+ 0 - 7
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/KqlTests.java

@@ -10,21 +10,14 @@ package org.elasticsearch.xpack.esql.expression.function.fulltext;
 import com.carrotsearch.randomizedtesting.annotations.Name;
 import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
 
-import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
 import org.elasticsearch.xpack.esql.core.expression.Expression;
 import org.elasticsearch.xpack.esql.core.tree.Source;
 import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
-import org.junit.BeforeClass;
 
 import java.util.List;
 import java.util.function.Supplier;
 
 public class KqlTests extends NoneFieldFullTextFunctionTestCase {
-    @BeforeClass
-    protected static void ensureKqlFunctionEnabled() {
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-    }
-
     public KqlTests(@Name("TestCase") Supplier<TestCaseSupplier.TestCase> testCaseSupplier) {
         super(testCaseSupplier);
     }

+ 0 - 15
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java

@@ -796,9 +796,6 @@ public class LocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
      *       \_EsQueryExec[test], indexMode[standard], query[{"kql":{"query":"last_name: Smith"}}]
      */
     public void testKqlFunction() {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         var plan = plannerOptimizer.plan("""
             from test
             | where kql("last_name: Smith")
@@ -827,9 +824,6 @@ public class LocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
      *        "boost":1.0}}][_doc{f}#1423], limit[1000], sort[] estimatedRowSize[324]
      */
     public void testKqlFunctionConjunctionWhereOperands() {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         String queryText = """
             from test
             | where kql("last_name: Smith") and emp_no > 10010
@@ -864,9 +858,6 @@ public class LocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
      *       "source":"cidr_match(ip, \"127.0.0.1/32\")@2:38"}}],"boost":1.0}}][_doc{f}#21], limit[1000], sort[] estimatedRowSize[354]
      */
     public void testKqlFunctionWithFunctionsPushedToLucene() {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         String queryText = """
             from test
             | where kql("last_name: Smith") and cidr_match(ip, "127.0.0.1/32")
@@ -902,9 +893,6 @@ public class LocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
      *       "boost":1.0}}][_doc{f}#1167], limit[1000], sort[] estimatedRowSize[324]
      */
     public void testKqlFunctionMultipleWhereClauses() {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         String queryText = """
             from test
             | where kql("last_name: Smith")
@@ -939,9 +927,6 @@ public class LocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
      *       {"kql":{"query":"emp_no > 10010"}}],"boost":1.0}}]
      */
     public void testKqlFunctionMultipleKqlClauses() {
-        // Skip test if the kql function is not enabled.
-        assumeTrue("kql function capability not available", EsqlCapabilities.Cap.KQL_FUNCTION.isEnabled());
-
         String queryText = """
             from test
             | where kql("last_name: Smith") and kql("emp_no > 10010")

+ 53 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/ConstantFoldingTests.java

@@ -14,6 +14,7 @@ import org.elasticsearch.xpack.esql.core.expression.Expressions;
 import org.elasticsearch.xpack.esql.core.expression.Literal;
 import org.elasticsearch.xpack.esql.core.expression.Nullability;
 import org.elasticsearch.xpack.esql.core.expression.predicate.BinaryOperator;
+import org.elasticsearch.xpack.esql.core.expression.predicate.Range;
 import org.elasticsearch.xpack.esql.core.expression.predicate.logical.And;
 import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Not;
 import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Or;
@@ -27,11 +28,16 @@ import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Div
 import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Mod;
 import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Mul;
 import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Sub;
+import org.elasticsearch.xpack.esql.plan.logical.Filter;
+import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
 
 import static org.elasticsearch.xpack.esql.EsqlTestUtils.FIVE;
 import static org.elasticsearch.xpack.esql.EsqlTestUtils.THREE;
 import static org.elasticsearch.xpack.esql.EsqlTestUtils.TWO;
+import static org.elasticsearch.xpack.esql.EsqlTestUtils.as;
+import static org.elasticsearch.xpack.esql.EsqlTestUtils.emptySource;
 import static org.elasticsearch.xpack.esql.EsqlTestUtils.equalsOf;
+import static org.elasticsearch.xpack.esql.EsqlTestUtils.fieldAttribute;
 import static org.elasticsearch.xpack.esql.EsqlTestUtils.greaterThanOf;
 import static org.elasticsearch.xpack.esql.EsqlTestUtils.greaterThanOrEqualOf;
 import static org.elasticsearch.xpack.esql.EsqlTestUtils.lessThanOf;
@@ -111,6 +117,53 @@ public class ConstantFoldingTests extends ESTestCase {
         assertEquals(1, foldOperator(new Mod(EMPTY, new Literal(EMPTY, 7, DataType.INTEGER), THREE)));
     }
 
+    public void testFoldRange() {
+        // 1 + 9 < value AND value < 20-1
+        // with value = 12 and randomly replacing the `<` by `<=`
+        Expression lowerBound = new Add(EMPTY, new Literal(EMPTY, 1, DataType.INTEGER), new Literal(EMPTY, 9, DataType.INTEGER));
+        Expression upperBound = new Sub(EMPTY, new Literal(EMPTY, 20, DataType.INTEGER), new Literal(EMPTY, 1, DataType.INTEGER));
+        Expression value = new Literal(EMPTY, 12, DataType.INTEGER);
+        Range range = new Range(EMPTY, value, lowerBound, randomBoolean(), upperBound, randomBoolean(), randomZone());
+
+        Expression folded = new ConstantFolding().rule(range);
+        assertTrue((Boolean) as(folded, Literal.class).value());
+    }
+
+    public void testFoldRangeWithInvalidBoundaries() {
+        // 1 + 9 < value AND value <= 11 - 1
+        // This is always false. We also randomly test versions with `<=`.
+        Expression lowerBound;
+        boolean includeLowerBound = randomBoolean();
+        if (includeLowerBound) {
+            // 1 + 10 <= value
+            lowerBound = new Add(EMPTY, new Literal(EMPTY, 1, DataType.INTEGER), new Literal(EMPTY, 10, DataType.INTEGER));
+        } else {
+            // 1 + 9 < value
+            lowerBound = new Add(EMPTY, new Literal(EMPTY, 1, DataType.INTEGER), new Literal(EMPTY, 9, DataType.INTEGER));
+        }
+
+        boolean includeUpperBound = randomBoolean();
+        // value < 11 - 1
+        // or
+        // value <= 11 - 1
+        Expression upperBound = new Sub(EMPTY, new Literal(EMPTY, 11, DataType.INTEGER), new Literal(EMPTY, 1, DataType.INTEGER));
+
+        Expression value = fieldAttribute();
+
+        Range range = new Range(EMPTY, value, lowerBound, includeLowerBound, upperBound, includeUpperBound, randomZone());
+
+        // We need to test this as part of a logical plan, to correctly simulate how we traverse down the expression tree.
+        // Just applying this to the range directly won't perform a transformDown.
+        LogicalPlan filter = new Filter(EMPTY, emptySource(), range);
+
+        Filter foldedOnce = as(new ConstantFolding().apply(filter), Filter.class);
+        // We need to run the rule twice, because during the first run only the boundaries can be folded - the range doesn't know it's
+        // foldable, yet.
+        Filter foldedTwice = as(new ConstantFolding().apply(foldedOnce), Filter.class);
+
+        assertFalse((Boolean) as(foldedTwice.condition(), Literal.class).value());
+    }
+
     private static Object foldOperator(BinaryOperator<?, ?, ?, ?> b) {
         return ((Literal) new ConstantFolding().rule(b)).value();
     }

+ 18 - 19
x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesDataStreamsIT.java

@@ -9,6 +9,7 @@ package org.elasticsearch.xpack.ilm;
 
 import org.elasticsearch.client.Request;
 import org.elasticsearch.client.Response;
+import org.elasticsearch.client.WarningFailureException;
 import org.elasticsearch.cluster.metadata.DataStream;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.metadata.Template;
@@ -216,29 +217,27 @@ public class TimeSeriesDataStreamsIT extends ESRestTestCase {
     }
 
     public void testFreezeAction() throws Exception {
-        createNewSingletonPolicy(client(), policyName, "cold", FreezeAction.INSTANCE);
+        try {
+            createNewSingletonPolicy(client(), policyName, "cold", FreezeAction.INSTANCE);
+        } catch (WarningFailureException e) {
+            assertThat(e.getMessage(), containsString("The freeze action in ILM is deprecated and will be removed in a future version"));
+        }
         createComposableTemplate(client(), template, dataStream + "*", getTemplate(policyName));
         indexDocument(client(), dataStream, true);
 
+        // The freeze action is a noop action with only noop steps and should pass through to complete the phase asap.
         String backingIndexName = DataStream.getDefaultBackingIndexName(dataStream, 1);
-        assertBusy(
-            () -> assertThat(
-                "index must wait in the " + CheckNotDataStreamWriteIndexStep.NAME + " until it is not the write index anymore",
-                explainIndex(client(), backingIndexName).get("step"),
-                is(CheckNotDataStreamWriteIndexStep.NAME)
-            ),
-            30,
-            TimeUnit.SECONDS
-        );
-
-        // Manual rollover the original index such that it's not the write index in the data stream anymore
-        rolloverMaxOneDocCondition(client(), dataStream);
-
-        assertBusy(
-            () -> assertThat(explainIndex(client(), backingIndexName).get("step"), is(PhaseCompleteStep.NAME)),
-            30,
-            TimeUnit.SECONDS
-        );
+        assertBusy(() -> {
+            try {
+                assertThat(explainIndex(client(), backingIndexName).get("step"), is(PhaseCompleteStep.NAME));
+                fail("expected a deprecation warning");
+            } catch (WarningFailureException e) {
+                assertThat(
+                    e.getMessage(),
+                    containsString("The freeze action in ILM is deprecated and will be removed in a future version")
+                );
+            }
+        }, 30, TimeUnit.SECONDS);
 
         Map<String, Object> settings = getOnlyIndexSettings(client(), backingIndexName);
         assertNull(settings.get("index.frozen"));

+ 16 - 6
x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java

@@ -14,6 +14,7 @@ import org.apache.logging.log4j.Logger;
 import org.elasticsearch.client.Request;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.client.ResponseException;
+import org.elasticsearch.client.WarningFailureException;
 import org.elasticsearch.cluster.metadata.DataStream;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.Strings;
@@ -190,7 +191,11 @@ public class TimeSeriesLifecycleActionsIT extends ESRestTestCase {
     }
 
     public void testFreezeNoop() throws Exception {
-        createNewSingletonPolicy(client(), policy, "cold", FreezeAction.INSTANCE);
+        try {
+            createNewSingletonPolicy(client(), policy, "cold", FreezeAction.INSTANCE);
+        } catch (WarningFailureException e) {
+            assertThat(e.getMessage(), containsString("The freeze action in ILM is deprecated and will be removed in a future version"));
+        }
 
         createIndexWithSettings(
             client(),
@@ -202,11 +207,16 @@ public class TimeSeriesLifecycleActionsIT extends ESRestTestCase {
                 .put("index.lifecycle.name", policy)
         );
 
-        assertBusy(
-            () -> assertThat(getStepKeyForIndex(client(), index), equalTo(PhaseCompleteStep.finalStep("cold").getKey())),
-            30,
-            TimeUnit.SECONDS
-        );
+        assertBusy(() -> {
+            try {
+                assertThat(getStepKeyForIndex(client(), index), equalTo(PhaseCompleteStep.finalStep("cold").getKey()));
+            } catch (WarningFailureException e) {
+                assertThat(
+                    e.getMessage(),
+                    containsString("The freeze action in ILM is deprecated and will be removed in a future version")
+                );
+            }
+        }, 30, TimeUnit.SECONDS);
         assertFalse(getOnlyIndexSettings(client(), index).containsKey("index.frozen"));
     }
 

+ 45 - 15
x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/SearchableSnapshotActionIT.java

@@ -12,6 +12,7 @@ import org.apache.http.entity.StringEntity;
 import org.elasticsearch.client.Request;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.client.ResponseException;
+import org.elasticsearch.client.WarningFailureException;
 import org.elasticsearch.cluster.metadata.DataStream;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.metadata.Template;
@@ -361,9 +362,10 @@ public class SearchableSnapshotActionIT extends ESRestTestCase {
             null
         );
 
+        String template = randomAlphaOfLengthBetween(5, 10).toLowerCase(Locale.ROOT);
         createComposableTemplate(
             client(),
-            randomAlphaOfLengthBetween(5, 10).toLowerCase(Locale.ROOT),
+            template,
             dataStream,
             new Template(
                 Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 5).put(LifecycleSettings.LIFECYCLE_NAME, policy).build(),
@@ -407,19 +409,24 @@ public class SearchableSnapshotActionIT extends ESRestTestCase {
         // snapshot)
         assertOK(client().performRequest(new Request("DELETE", "/_data_stream/" + dataStream)));
 
-        createPolicy(
-            client(),
-            policy,
-            new Phase("hot", TimeValue.ZERO, Map.of()),
-            new Phase(
-                "warm",
-                TimeValue.ZERO,
-                Map.of(ShrinkAction.NAME, new ShrinkAction(1, null, false), ForceMergeAction.NAME, new ForceMergeAction(1, null))
-            ),
-            new Phase("cold", TimeValue.ZERO, Map.of(FreezeAction.NAME, FreezeAction.INSTANCE)),
-            null,
-            null
-        );
+        try {
+            createPolicy(
+                client(),
+                policy,
+                new Phase("hot", TimeValue.ZERO, Map.of()),
+                new Phase(
+                    "warm",
+                    TimeValue.ZERO,
+                    Map.of(ShrinkAction.NAME, new ShrinkAction(1, null, false), ForceMergeAction.NAME, new ForceMergeAction(1, null))
+                ),
+                new Phase("cold", TimeValue.ZERO, Map.of(FreezeAction.NAME, FreezeAction.INSTANCE)),
+                null,
+                null
+            );
+            fail("Expected a deprecation warning.");
+        } catch (WarningFailureException e) {
+            assertThat(e.getMessage(), containsString("The freeze action in ILM is deprecated and will be removed in a future version"));
+        }
 
         // restore the datastream
         Request restoreSnapshot = new Request("POST", "/_snapshot/" + snapshotRepo + "/" + dsSnapshotName + "/_restore");
@@ -432,7 +439,16 @@ public class SearchableSnapshotActionIT extends ESRestTestCase {
 
         // the restored index is now managed by the now updated ILM policy and needs to go through the warm and cold phase
         assertBusy(() -> {
-            Step.StepKey stepKeyForIndex = getStepKeyForIndex(client(), searchableSnapMountedIndexName);
+            Step.StepKey stepKeyForIndex;
+            try {
+                stepKeyForIndex = getStepKeyForIndex(client(), searchableSnapMountedIndexName);
+            } catch (WarningFailureException e) {
+                assertThat(
+                    e.getMessage(),
+                    containsString("The freeze action in ILM is deprecated and will be removed in a future version")
+                );
+                stepKeyForIndex = getKeyForIndex(e.getResponse(), searchableSnapMountedIndexName);
+            }
             assertThat(stepKeyForIndex.phase(), is("cold"));
             assertThat(stepKeyForIndex.name(), is(PhaseCompleteStep.NAME));
         }, 30, TimeUnit.SECONDS);
@@ -984,4 +1000,18 @@ public class SearchableSnapshotActionIT extends ESRestTestCase {
         Request rerouteRequest = new Request("POST", "/_cluster/reroute");
         client().performRequest(rerouteRequest);
     }
+
+    private Step.StepKey getKeyForIndex(Response response, String indexName) throws IOException {
+        Map<String, Object> responseMap;
+        try (InputStream is = response.getEntity().getContent()) {
+            responseMap = XContentHelper.convertToMap(XContentType.JSON.xContent(), is, true);
+        }
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> indexResponse = ((Map<String, Map<String, Object>>) responseMap.get("indices")).get(indexName);
+        String phase = (String) indexResponse.get("phase");
+        String action = (String) indexResponse.get("action");
+        String step = (String) indexResponse.get("step");
+        return new Step.StepKey(phase, action, step);
+    }
 }

+ 1 - 0
x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleAction.java

@@ -156,6 +156,7 @@ public class TransportExplainLifecycleAction extends TransportClusterInfoAction<
                 // Try to add default rollover conditions to the response.
                 var phase = phaseExecutionInfo.getPhase();
                 if (phase != null) {
+                    phase.maybeAddDeprecationWarningForFreezeAction(policyName);
                     var rolloverAction = (RolloverAction) phase.getActions().get(RolloverAction.NAME);
                     if (rolloverAction != null) {
                         var conditions = applyDefaultConditions(rolloverAction.getConditions(), rolloverOnlyIfHasDocuments);

+ 1 - 0
x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportPutLifecycleAction.java

@@ -115,6 +115,7 @@ public class TransportPutLifecycleAction extends TransportMasterNodeAction<PutLi
         Map<String, String> filteredHeaders = ClientHelper.getPersistableSafeSecurityHeaders(threadPool.getThreadContext(), state);
 
         LifecyclePolicy.validatePolicyName(request.getPolicy().getName());
+        request.getPolicy().maybeAddDeprecationWarningForFreezeAction(request.getPolicy().getName());
 
         ProjectMetadata projectMetadata = projectResolver.getProjectMetadata(state);
         IndexLifecycleMetadata lifecycleMetadata = projectMetadata.custom(IndexLifecycleMetadata.TYPE, IndexLifecycleMetadata.EMPTY);

+ 15 - 5
x-pack/plugin/inference/qa/inference-service-tests/src/javaRestTest/java/org/elasticsearch/xpack/inference/InferenceCrudIT.java

@@ -242,7 +242,12 @@ public class InferenceCrudIT extends InferenceBaseRestTest {
     @SuppressWarnings("unchecked")
     public void testGetServicesWithCompletionTaskType() throws IOException {
         List<Object> services = getServices(TaskType.COMPLETION);
-        assertThat(services.size(), equalTo(10));
+        if ((ElasticInferenceServiceFeature.DEPRECATED_ELASTIC_INFERENCE_SERVICE_FEATURE_FLAG.isEnabled()
+            || ElasticInferenceServiceFeature.ELASTIC_INFERENCE_SERVICE_FEATURE_FLAG.isEnabled())) {
+            assertThat(services.size(), equalTo(10));
+        } else {
+            assertThat(services.size(), equalTo(9));
+        }
 
         String[] providers = new String[services.size()];
         for (int i = 0; i < services.size(); i++) {
@@ -250,8 +255,7 @@ public class InferenceCrudIT extends InferenceBaseRestTest {
             providers[i] = (String) serviceConfig.get("service");
         }
 
-        assertArrayEquals(
-            providers,
+        var providerList = new ArrayList<>(
             List.of(
                 "alibabacloud-ai-search",
                 "amazonbedrock",
@@ -259,12 +263,18 @@ public class InferenceCrudIT extends InferenceBaseRestTest {
                 "azureaistudio",
                 "azureopenai",
                 "cohere",
-                "elastic",
                 "googleaistudio",
                 "openai",
                 "streaming_completion_test_service"
-            ).toArray()
+            )
         );
+
+        if ((ElasticInferenceServiceFeature.DEPRECATED_ELASTIC_INFERENCE_SERVICE_FEATURE_FLAG.isEnabled()
+            || ElasticInferenceServiceFeature.ELASTIC_INFERENCE_SERVICE_FEATURE_FLAG.isEnabled())) {
+            providerList.add(6, "elastic");
+        }
+
+        assertArrayEquals(providers, providerList.toArray());
     }
 
     @SuppressWarnings("unchecked")

+ 1 - 6
x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/KqlPlugin.java

@@ -7,7 +7,6 @@
 
 package org.elasticsearch.xpack.kql;
 
-import org.elasticsearch.Build;
 import org.elasticsearch.plugins.ExtensiblePlugin;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.plugins.SearchPlugin;
@@ -18,10 +17,6 @@ import java.util.List;
 public class KqlPlugin extends Plugin implements SearchPlugin, ExtensiblePlugin {
     @Override
     public List<QuerySpec<?>> getQueries() {
-        if (Build.current().isSnapshot()) {
-            return List.of(new SearchPlugin.QuerySpec<>(KqlQueryBuilder.NAME, KqlQueryBuilder::new, KqlQueryBuilder::fromXContent));
-        }
-
-        return List.of();
+        return List.of(new SearchPlugin.QuerySpec<>(KqlQueryBuilder.NAME, KqlQueryBuilder::new, KqlQueryBuilder::fromXContent));
     }
 }

+ 1 - 1
x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/query/KqlQueryBuilder.java

@@ -97,7 +97,7 @@ public class KqlQueryBuilder extends AbstractQueryBuilder<KqlQueryBuilder> {
 
     @Override
     public TransportVersion getMinimalSupportedVersion() {
-        return TransportVersions.KQL_QUERY_ADDED;
+        return TransportVersions.KQL_QUERY_TECH_PREVIEW;
     }
 
     public String queryString() {

+ 0 - 8
x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/query/KqlQueryBuilderTests.java

@@ -8,7 +8,6 @@
 package org.elasticsearch.xpack.kql.query;
 
 import org.apache.lucene.search.Query;
-import org.elasticsearch.Build;
 import org.elasticsearch.core.Strings;
 import org.elasticsearch.index.query.MultiMatchQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
@@ -22,7 +21,6 @@ import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.test.AbstractQueryTestCase;
 import org.elasticsearch.xpack.kql.KqlPlugin;
 import org.hamcrest.Matchers;
-import org.junit.BeforeClass;
 
 import java.io.IOException;
 import java.util.Collection;
@@ -36,12 +34,6 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
 
 public class KqlQueryBuilderTests extends AbstractQueryTestCase<KqlQueryBuilder> {
-    @BeforeClass
-    protected static void ensureSnapshotBuild() {
-        assumeTrue("requires snapshot builds", Build.current().isSnapshot());
-    }
-
-    @Override
     protected Collection<Class<? extends Plugin>> getPlugins() {
         return List.of(KqlPlugin.class);
     }

+ 5 - 6
x-pack/plugin/logsdb/src/main/java/org/elasticsearch/xpack/logsdb/LogsDBPlugin.java

@@ -67,12 +67,11 @@ public class LogsDBPlugin extends Plugin implements ActionPlugin {
 
     @Override
     public Collection<IndexSettingProvider> getAdditionalIndexSettingProviders(IndexSettingProvider.Parameters parameters) {
-        if (DiscoveryNode.isStateless(settings) == false) {
-            logsdbIndexModeSettingsProvider.init(
-                parameters.mapperServiceFactory(),
-                () -> parameters.clusterService().state().nodes().getMinSupportedIndexVersion()
-            );
-        }
+        logsdbIndexModeSettingsProvider.init(
+            parameters.mapperServiceFactory(),
+            () -> parameters.clusterService().state().nodes().getMinSupportedIndexVersion(),
+            DiscoveryNode.isStateless(settings) == false
+        );
         return List.of(logsdbIndexModeSettingsProvider);
     }
 

Някои файлове не бяха показани, защото твърде много файлове са промени