Browse Source

Add prefer_v2_templates flag and index setting (#55411)

This commit adds a new querystring parameter on the following APIs:
- Index
- Update
- Bulk
- Create Index
- Rollover

These APIs now support a `?prefer_v2_templates=true|false` flag. This flag changes the preference
creation to use either V2 index templates or V1 templates. This flag defaults to `false` and will be
changed to `true` for 8.0+ in subsequent work.

Additionally, setting this flag internally sets the `index.prefer_v2_templates` index-level setting.
This setting is used so that actions that automatically create a new index (things like rollover
initiated by ILM) will inherit the preference from the original index. This setting is dynamic so
that a transition from v1 to v2 templates can occur for long-running indices grouped by an alias
performing periodic rollover.

This also adds support for sending this parameter to the High Level Rest Client.

Relates to #53101
Lee Hinman 5 years ago
parent
commit
0202e1ae96
37 changed files with 517 additions and 37 deletions
  1. 3 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesRequestConverters.java
  2. 10 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java
  3. 12 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/indices/CreateIndexRequest.java
  4. 3 1
      docs/reference/api-conventions.asciidoc
  5. 4 0
      rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json
  6. 4 0
      rest-api-spec/src/main/resources/rest-api-spec/api/create.json
  7. 4 0
      rest-api-spec/src/main/resources/rest-api-spec/api/index.json
  8. 4 0
      rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json
  9. 4 0
      rest-api-spec/src/main/resources/rest-api-spec/api/indices.rollover.json
  10. 4 0
      rest-api-spec/src/main/resources/rest-api-spec/api/update.json
  11. 37 4
      rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_index_template/15_composition.yml
  12. 13 0
      server/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java
  13. 19 0
      server/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java
  14. 1 0
      server/src/main/java/org/elasticsearch/action/admin/indices/create/TransportCreateIndexAction.java
  15. 2 1
      server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverService.java
  16. 8 0
      server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java
  17. 18 0
      server/src/main/java/org/elasticsearch/action/bulk/BulkRequest.java
  18. 9 5
      server/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java
  19. 4 0
      server/src/main/java/org/elasticsearch/action/bulk/TransportSingleItemBulkWriteAction.java
  20. 17 0
      server/src/main/java/org/elasticsearch/action/index/IndexRequest.java
  21. 5 2
      server/src/main/java/org/elasticsearch/action/update/TransportUpdateAction.java
  22. 17 0
      server/src/main/java/org/elasticsearch/action/update/UpdateRequest.java
  23. 1 1
      server/src/main/java/org/elasticsearch/client/IndicesAdminClient.java
  24. 1 1
      server/src/main/java/org/elasticsearch/client/support/AbstractClient.java
  25. 4 1
      server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java
  26. 24 8
      server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java
  27. 1 0
      server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java
  28. 8 0
      server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestCreateIndexAction.java
  29. 1 0
      server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestRolloverIndexAction.java
  30. 2 0
      server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java
  31. 2 0
      server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java
  32. 2 0
      server/src/main/java/org/elasticsearch/rest/action/document/RestUpdateAction.java
  33. 2 1
      server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java
  34. 3 1
      server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java
  35. 12 11
      server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTests.java
  36. 251 0
      server/src/test/java/org/elasticsearch/indices/template/TemplatePreferenceIT.java
  37. 1 0
      x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowAction.java

+ 3 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesRequestConverters.java

@@ -113,6 +113,9 @@ final class IndicesRequestConverters {
         parameters.withTimeout(createIndexRequest.timeout());
         parameters.withMasterTimeout(createIndexRequest.masterNodeTimeout());
         parameters.withWaitForActiveShards(createIndexRequest.waitForActiveShards());
+        if (createIndexRequest.preferV2Templates() != null) {
+            parameters.putParam(IndexMetadata.PREFER_V2_TEMPLATES_FLAG, Boolean.toString(createIndexRequest.preferV2Templates()));
+        }
         request.addParameters(parameters.asMap());
         request.setEntity(RequestConverters.createEntity(createIndexRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE));
         return request;

+ 10 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

@@ -56,6 +56,7 @@ import org.elasticsearch.client.indices.AnalyzeRequest;
 import org.elasticsearch.client.security.RefreshPolicy;
 import org.elasticsearch.client.tasks.TaskId;
 import org.elasticsearch.cluster.health.ClusterHealthStatus;
+import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.Priority;
 import org.elasticsearch.common.Strings;
@@ -131,6 +132,9 @@ final class RequestConverters {
         parameters.withRefreshPolicy(bulkRequest.getRefreshPolicy());
         parameters.withPipeline(bulkRequest.pipeline());
         parameters.withRouting(bulkRequest.routing());
+        if (bulkRequest.preferV2Templates() != null) {
+            parameters.putParam(IndexMetadata.PREFER_V2_TEMPLATES_FLAG, Boolean.toString(bulkRequest.preferV2Templates()));
+        }
         // Bulk API only supports newline delimited JSON or Smile. Before executing
         // the bulk, we need to check that all requests have the same content-type
         // and this content-type is supported by the Bulk API.
@@ -331,6 +335,9 @@ final class RequestConverters {
         parameters.withPipeline(indexRequest.getPipeline());
         parameters.withRefreshPolicy(indexRequest.getRefreshPolicy());
         parameters.withWaitForActiveShards(indexRequest.waitForActiveShards());
+        if (indexRequest.preferV2Templates() != null) {
+            parameters.putParam(IndexMetadata.PREFER_V2_TEMPLATES_FLAG, Boolean.toString(indexRequest.preferV2Templates()));
+        }
 
         BytesRef source = indexRequest.source().toBytesRef();
         ContentType contentType = createContentType(indexRequest.getContentType());
@@ -357,6 +364,9 @@ final class RequestConverters {
         parameters.withRetryOnConflict(updateRequest.retryOnConflict());
         parameters.withVersion(updateRequest.version());
         parameters.withVersionType(updateRequest.versionType());
+        if (updateRequest.preferV2Templates() != null) {
+            parameters.putParam(IndexMetadata.PREFER_V2_TEMPLATES_FLAG, Boolean.toString(updateRequest.preferV2Templates()));
+        }
 
         // The Java API allows update requests with different content types
         // set for the partial document and the upsert document. This client

+ 12 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/indices/CreateIndexRequest.java

@@ -25,6 +25,7 @@ import org.elasticsearch.action.admin.indices.alias.Alias;
 import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.client.TimedRequest;
 import org.elasticsearch.client.Validatable;
+import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.bytes.BytesArray;
@@ -62,6 +63,7 @@ public class CreateIndexRequest extends TimedRequest implements Validatable, ToX
 
     private BytesReference mappings;
     private XContentType mappingsXContentType;
+    private Boolean preferV2Templates;
 
     private final Set<Alias> aliases = new HashSet<>();
 
@@ -265,6 +267,16 @@ public class CreateIndexRequest extends TimedRequest implements Validatable, ToX
         return this;
     }
 
+    public CreateIndexRequest preferV2Templates(Boolean preferV2Templates) {
+        this.preferV2Templates = preferV2Templates;
+        return this;
+    }
+
+    @Nullable
+    public Boolean preferV2Templates() {
+        return this.preferV2Templates;
+    }
+
     /**
      * Sets the settings and mappings as a single source.
      *

+ 3 - 1
docs/reference/api-conventions.asciidoc

@@ -385,6 +385,7 @@ Returns:
     "settings": {
       "index.number_of_replicas": "1",
       "index.number_of_shards": "1",
+      "index.prefer_v2_templates": "false",
       "index.creation_date": "1474389951325",
       "index.uuid": "n6gzFZTgS664GUfx0Xrpjw",
       "index.version.created": ...,
@@ -421,7 +422,8 @@ Returns:
         "version": {
           "created": ...
         },
-        "provided_name" : "twitter"
+        "provided_name" : "twitter",
+        "prefer_v2_templates": "false"
       }
     }
   }

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json

@@ -87,6 +87,10 @@
       "pipeline":{
         "type":"string",
         "description":"The pipeline id to preprocess incoming documents with"
+      },
+      "prefer_v2_templates": {
+        "type": "boolean",
+        "description": "favor V2 templates instead of V1 templates during automatic index creation"
       }
     },
     "body":{

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/create.json

@@ -90,6 +90,10 @@
       "pipeline":{
         "type":"string",
         "description":"The pipeline id to preprocess incoming documents with"
+      },
+      "prefer_v2_templates": {
+        "type": "boolean",
+        "description": "favor V2 templates instead of V1 templates during automatic index creation"
       }
     },
     "body":{

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/index.json

@@ -92,6 +92,10 @@
       "pipeline":{
         "type":"string",
         "description":"The pipeline id to preprocess incoming documents with"
+      },
+      "prefer_v2_templates": {
+        "type": "boolean",
+        "description": "favor V2 templates instead of V1 templates during automatic index creation"
       }
     },
     "body":{

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json

@@ -33,6 +33,10 @@
       "master_timeout":{
         "type":"time",
         "description":"Specify timeout for connection to master"
+      },
+      "prefer_v2_templates": {
+        "type": "boolean",
+        "description": "favor V2 templates instead of V1 templates during index creation"
       }
     },
     "body":{

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/indices.rollover.json

@@ -53,6 +53,10 @@
       "wait_for_active_shards":{
         "type":"string",
         "description":"Set the number of active shards to wait for on the newly created rollover index before the operation returns."
+      },
+      "prefer_v2_templates": {
+        "type": "boolean",
+        "description": "favor V2 templates instead of V1 templates during automatic index creation"
       }
     },
     "body":{

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/update.json

@@ -99,6 +99,10 @@
       "if_primary_term":{
         "type":"number",
         "description":"only perform the update operation if the last operation that has changed the document has the specified primary term"
+      },
+      "prefer_v2_templates": {
+        "type": "boolean",
+        "description": "favor V2 templates instead of V1 templates during automatic index creation"
       }
     },
     "body":{

+ 37 - 4
rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_index_template/15_composition.yml

@@ -1,7 +1,7 @@
 ---
 "Component and index template composition":
   - skip:
-      version: " - 7.7.99"
+      version: " - 7.9.99"
       reason: "index template v2 API unavailable before 7.8"
       features: allowed_warnings
 
@@ -61,6 +61,7 @@
   - do:
       indices.create:
         index: bar-baz
+        prefer_v2_templates: true
         body:
           settings:
             index.priority: 17
@@ -88,7 +89,7 @@
 ---
 "Index template priority":
   - skip:
-      version: " - 7.7.99"
+      version: " - 7.9.99"
       reason: "index template v2 API unavailable before 7.8"
       features: allowed_warnings
 
@@ -120,6 +121,7 @@
 
   - do:
       indices.create:
+        prefer_v2_templates: true
         index: bar-baz
 
   - do:
@@ -131,7 +133,7 @@
 ---
 "Component template only composition":
   - skip:
-      version: " - 7.7.99"
+      version: " - 7.9.99"
       reason: "index template v2 API unavailable before 7.8"
       features: allowed_warnings
 
@@ -164,6 +166,7 @@
 
   - do:
       indices.create:
+        prefer_v2_templates: true
         index: bazfoo
 
   - do:
@@ -176,7 +179,7 @@
 ---
 "Index template without component templates":
   - skip:
-      version: " - 7.7.99"
+      version: " - 7.9.99"
       reason: "index template v2 API unavailable before 7.8"
       features: allowed_warnings
 
@@ -193,6 +196,7 @@
 
   - do:
       indices.create:
+        prefer_v2_templates: true
         index: eggplant
 
   - do:
@@ -200,3 +204,32 @@
         index: eggplant
 
   - match: {eggplant.settings.index.number_of_shards: "3"}
+
+---
+"Version 1 templates are preferred if the flag is set":
+  - skip:
+      version: " - 7.9.99"
+      reason: "not backported yet"
+      features: allowed_warnings
+
+  - do:
+      allowed_warnings:
+        - "index template [my-template] has index patterns [eggplant] matching patterns from existing older templates [global] with patterns (global => [*]); this template [my-template] will take precedence during new index creation"
+      indices.put_index_template:
+        name: my-template
+        body:
+          index_patterns: ["eggplant"]
+          template:
+            settings:
+              number_of_replicas: 2
+
+  - do:
+      indices.create:
+        prefer_v2_templates: false
+        index: eggplant
+
+  - do:
+      indices.get:
+        index: eggplant
+
+  - match: {eggplant.settings.index.number_of_replicas: "1"}

+ 13 - 0
server/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java

@@ -25,6 +25,7 @@ import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.cluster.ack.ClusterStateUpdateRequest;
 import org.elasticsearch.cluster.block.ClusterBlock;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
+import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.Index;
 
@@ -42,6 +43,7 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ
     private Index recoverFrom;
     private ResizeType resizeType;
     private boolean copySettings;
+    private Boolean preferV2Templates;
 
     private Settings settings = Settings.Builder.EMPTY_SETTINGS;
 
@@ -94,6 +96,11 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ
         return this;
     }
 
+    public CreateIndexClusterStateUpdateRequest preferV2Templates(@Nullable Boolean preferV2Templates) {
+        this.preferV2Templates = preferV2Templates;
+        return this;
+    }
+
     public String cause() {
         return cause;
     }
@@ -145,6 +152,11 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ
         return copySettings;
     }
 
+    @Nullable
+    public Boolean preferV2Templates() {
+        return preferV2Templates;
+    }
+
     @Override
     public String toString() {
         return "CreateIndexClusterStateUpdateRequest{" +
@@ -158,6 +170,7 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ
             ", aliases=" + aliases +
             ", blocks=" + blocks +
             ", waitForActiveShards=" + waitForActiveShards +
+            ", preferV2Templates=" + preferV2Templates +
             '}';
     }
 }

+ 19 - 0
server/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java

@@ -29,6 +29,7 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
 import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.action.support.master.AcknowledgedRequest;
+import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.bytes.BytesArray;
@@ -80,6 +81,8 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest>
 
     private String mappings = "{}";
 
+    private Boolean preferV2Templates;
+
     private final Set<Alias> aliases = new HashSet<>();
 
     private ActiveShardCount waitForActiveShards = ActiveShardCount.DEFAULT;
@@ -107,6 +110,9 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest>
             aliases.add(new Alias(in));
         }
         waitForActiveShards = ActiveShardCount.readFrom(in);
+        if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
+            preferV2Templates = in.readOptionalBoolean();
+        }
     }
 
     public CreateIndexRequest() {
@@ -158,6 +164,16 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest>
         return this;
     }
 
+    public CreateIndexRequest preferV2Templates(@Nullable Boolean preferV2Templates) {
+        this.preferV2Templates = preferV2Templates;
+        return this;
+    }
+
+    @Nullable
+    public Boolean preferV2Templates() {
+        return this.preferV2Templates;
+    }
+
     /**
      * The settings to create the index with.
      */
@@ -468,6 +484,9 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest>
             alias.writeTo(out);
         }
         waitForActiveShards.writeTo(out);
+        if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
+            out.writeOptionalBoolean(preferV2Templates);
+        }
     }
 
 }

+ 1 - 0
server/src/main/java/org/elasticsearch/action/admin/indices/create/TransportCreateIndexAction.java

@@ -82,6 +82,7 @@ public class TransportCreateIndexAction extends TransportMasterNodeAction<Create
                 .ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout())
                 .settings(request.settings()).mappings(request.mappings())
                 .aliases(request.aliases())
+                .preferV2Templates(request.preferV2Templates())
                 .waitForActiveShards(request.waitForActiveShards());
 
         createIndexService.createIndex(updateRequest, ActionListener.map(listener, response ->

+ 2 - 1
server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverService.java

@@ -138,7 +138,8 @@ public class MetadataRolloverService {
             .settings(createIndexRequest.settings())
             .aliases(createIndexRequest.aliases())
             .waitForActiveShards(ActiveShardCount.NONE) // not waiting for shards here, will wait on the alias switch operation
-            .mappings(createIndexRequest.mappings());
+            .mappings(createIndexRequest.mappings())
+            .preferV2Templates(createIndexRequest.preferV2Templates());
     }
 
     /**

+ 8 - 0
server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java

@@ -40,6 +40,7 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.ByteSizeValue;
 import org.elasticsearch.index.shard.DocsStats;
 import org.elasticsearch.tasks.Task;
@@ -127,6 +128,13 @@ public class TransportRolloverAction extends TransportMasterNodeAction<RolloverR
                             + rolloverIndexName + "]", new ClusterStateUpdateTask() {
                             @Override
                             public ClusterState execute(ClusterState currentState) throws Exception {
+                                // If they haven't explicitly specified whether to use V2 or V1 templates, inherit their preference
+                                // from the existing index (the source index) settings.
+                                if (rolloverRequest.getCreateIndexRequest().preferV2Templates() == null) {
+                                    Settings originalIndexSettings = currentState.metadata().index(sourceIndexName).getSettings();
+                                    rolloverRequest.getCreateIndexRequest()
+                                        .preferV2Templates(IndexMetadata.PREFER_V2_TEMPLATES_SETTING.get(originalIndexSettings));
+                                }
                                 MetadataRolloverService.RolloverResult rolloverResult = rolloverService.rolloverClusterState(currentState,
                                     rolloverRequest.getAlias(), rolloverRequest.getNewIndexName(), rolloverRequest.getCreateIndexRequest(),
                                     metConditions, false);

+ 18 - 0
server/src/main/java/org/elasticsearch/action/bulk/BulkRequest.java

@@ -19,6 +19,7 @@
 
 package org.elasticsearch.action.bulk;
 
+import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionRequest;
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.CompositeIndicesRequest;
@@ -73,6 +74,7 @@ public class BulkRequest extends ActionRequest implements CompositeIndicesReques
     private String globalPipeline;
     private String globalRouting;
     private String globalIndex;
+    private Boolean preferV2Templates;
 
     private long sizeInBytes = 0;
 
@@ -87,6 +89,9 @@ public class BulkRequest extends ActionRequest implements CompositeIndicesReques
         }
         refreshPolicy = RefreshPolicy.readFrom(in);
         timeout = in.readTimeValue();
+        if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
+            this.preferV2Templates = in.readOptionalBoolean();
+        }
     }
 
     public BulkRequest(@Nullable String globalIndex) {
@@ -196,6 +201,16 @@ public class BulkRequest extends ActionRequest implements CompositeIndicesReques
         return this.requests;
     }
 
+    public BulkRequest preferV2Templates(@Nullable Boolean preferV2Templates) {
+        this.preferV2Templates = preferV2Templates;
+        return this;
+    }
+
+    @Nullable
+    public Boolean preferV2Templates() {
+        return this.preferV2Templates;
+    }
+
     /**
      * The number of actions in the bulk request.
      */
@@ -356,6 +371,9 @@ public class BulkRequest extends ActionRequest implements CompositeIndicesReques
         }
         refreshPolicy.writeTo(out);
         out.writeTimeValue(timeout);
+        if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
+            out.writeOptionalBoolean(preferV2Templates);
+        }
     }
 
     @Override

+ 9 - 5
server/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java

@@ -164,7 +164,9 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
             IndexRequest indexRequest = getIndexWriteRequest(actionRequest);
             if (indexRequest != null) {
                 // Each index request needs to be evaluated, because this method also modifies the IndexRequest
-                boolean indexRequestHasPipeline = resolvePipelines(actionRequest, indexRequest, metadata);
+                boolean preferV2Templates = bulkRequest.preferV2Templates() == null ?
+                    IndexMetadata.PREFER_V2_TEMPLATES_SETTING.getDefault(Settings.EMPTY) : bulkRequest.preferV2Templates();
+                boolean indexRequestHasPipeline = resolvePipelines(actionRequest, indexRequest, preferV2Templates, metadata);
                 hasIndexRequestsWithPipelines |= indexRequestHasPipeline;
             }
 
@@ -235,7 +237,7 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
             } else {
                 final AtomicInteger counter = new AtomicInteger(autoCreateIndices.size());
                 for (String index : autoCreateIndices) {
-                    createIndex(index, bulkRequest.timeout(), new ActionListener<>() {
+                    createIndex(index, bulkRequest.preferV2Templates(), bulkRequest.timeout(), new ActionListener<>() {
                         @Override
                         public void onResponse(CreateIndexResponse result) {
                             if (counter.decrementAndGet() == 0) {
@@ -270,7 +272,8 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
         }
     }
 
-    static boolean resolvePipelines(final DocWriteRequest<?> originalRequest, final IndexRequest indexRequest, final Metadata metadata) {
+    static boolean resolvePipelines(final DocWriteRequest<?> originalRequest, final IndexRequest indexRequest,
+                                    final boolean preferV2Templates, final Metadata metadata) {
         if (indexRequest.isPipelineResolved() == false) {
             final String requestPipeline = indexRequest.getPipeline();
             indexRequest.setPipeline(IngestService.NOOP_PIPELINE_NAME);
@@ -310,7 +313,7 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
                 // templates to look for pipelines in either a matching V2 template (which takes
                 // precedence), or if a V2 template does not match, any V1 templates
                 String v2Template = MetadataIndexTemplateService.findV2Template(metadata, indexRequest.index(), false);
-                if (v2Template != null) {
+                if (v2Template != null && preferV2Templates) {
                     Settings settings = MetadataIndexTemplateService.resolveSettings(metadata, v2Template);
                     if (defaultPipeline == null && IndexSettings.DEFAULT_PIPELINE.exists(settings)) {
                         defaultPipeline = IndexSettings.DEFAULT_PIPELINE.get(settings);
@@ -378,11 +381,12 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
         return autoCreateIndex.shouldAutoCreate(index, state);
     }
 
-    void createIndex(String index, TimeValue timeout, ActionListener<CreateIndexResponse> listener) {
+    void createIndex(String index, Boolean preferV2Templates, TimeValue timeout, ActionListener<CreateIndexResponse> listener) {
         CreateIndexRequest createIndexRequest = new CreateIndexRequest();
         createIndexRequest.index(index);
         createIndexRequest.cause("auto(bulk api)");
         createIndexRequest.masterNodeTimeout(timeout);
+        createIndexRequest.preferV2Templates(preferV2Templates);
         client.admin().indices().create(createIndexRequest, listener);
     }
 

+ 4 - 0
server/src/main/java/org/elasticsearch/action/bulk/TransportSingleItemBulkWriteAction.java

@@ -22,6 +22,7 @@ package org.elasticsearch.action.bulk;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.DocWriteRequest;
 import org.elasticsearch.action.DocWriteResponse;
+import org.elasticsearch.action.index.IndexRequest;
 import org.elasticsearch.action.support.ActionFilters;
 import org.elasticsearch.action.support.HandledTransportAction;
 import org.elasticsearch.action.support.WriteRequest;
@@ -72,6 +73,9 @@ public abstract class TransportSingleItemBulkWriteAction<
         bulkRequest.setRefreshPolicy(request.getRefreshPolicy());
         bulkRequest.timeout(request.timeout());
         bulkRequest.waitForActiveShards(request.waitForActiveShards());
+        if (request instanceof IndexRequest) {
+            bulkRequest.preferV2Templates(((IndexRequest) request).preferV2Templates());
+        }
         request.setRefreshPolicy(WriteRequest.RefreshPolicy.NONE);
         return bulkRequest;
     }

+ 17 - 0
server/src/main/java/org/elasticsearch/action/index/IndexRequest.java

@@ -114,6 +114,7 @@ public class IndexRequest extends ReplicatedWriteRequest<IndexRequest> implement
     private boolean isRetry = false;
     private long ifSeqNo = UNASSIGNED_SEQ_NO;
     private long ifPrimaryTerm = UNASSIGNED_PRIMARY_TERM;
+    private Boolean preferV2Templates;
 
     public IndexRequest(StreamInput in) throws IOException {
         super(in);
@@ -143,6 +144,9 @@ public class IndexRequest extends ReplicatedWriteRequest<IndexRequest> implement
         }
         ifSeqNo = in.readZLong();
         ifPrimaryTerm = in.readVLong();
+        if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
+            this.preferV2Templates = in.readOptionalBoolean();
+        }
     }
 
     public IndexRequest() {
@@ -561,6 +565,16 @@ public class IndexRequest extends ReplicatedWriteRequest<IndexRequest> implement
         return ifSeqNo;
     }
 
+    public IndexRequest preferV2Templates(@Nullable Boolean preferV2Templates) {
+        this.preferV2Templates = preferV2Templates;
+        return this;
+    }
+
+    @Nullable
+    public Boolean preferV2Templates() {
+        return this.preferV2Templates;
+    }
+
     /**
      * If set, only perform this indexing request if the document was last modification was assigned this primary term.
      *
@@ -642,6 +656,9 @@ public class IndexRequest extends ReplicatedWriteRequest<IndexRequest> implement
         }
         out.writeZLong(ifSeqNo);
         out.writeVLong(ifPrimaryTerm);
+        if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
+            out.writeOptionalBoolean(preferV2Templates);
+        }
     }
 
     @Override

+ 5 - 2
server/src/main/java/org/elasticsearch/action/update/TransportUpdateAction.java

@@ -117,8 +117,11 @@ public class TransportUpdateAction extends TransportInstanceSingleOperationActio
     protected void doExecute(Task task, final UpdateRequest request, final ActionListener<UpdateResponse> listener) {
         // if we don't have a master, we don't have metadata, that's fine, let it find a master using create index API
         if (autoCreateIndex.shouldAutoCreate(request.index(), clusterService.state())) {
-            client.admin().indices().create(new CreateIndexRequest().index(request.index()).cause("auto(update api)")
-                    .masterNodeTimeout(request.timeout()), new ActionListener<CreateIndexResponse>() {
+            client.admin().indices().create(new CreateIndexRequest()
+                .index(request.index())
+                .cause("auto(update api)")
+                .preferV2Templates(request.preferV2Templates())
+                .masterNodeTimeout(request.timeout()), new ActionListener<CreateIndexResponse>() {
                 @Override
                 public void onResponse(CreateIndexResponse result) {
                     innerExecute(task, request, listener);

+ 17 - 0
server/src/main/java/org/elasticsearch/action/update/UpdateRequest.java

@@ -120,6 +120,7 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
     private boolean scriptedUpsert = false;
     private boolean docAsUpsert = false;
     private boolean detectNoop = true;
+    private Boolean preferV2Templates;
 
     @Nullable
     private IndexRequest doc;
@@ -152,6 +153,9 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
         ifPrimaryTerm = in.readVLong();
         detectNoop = in.readBoolean();
         scriptedUpsert = in.readBoolean();
+        if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
+            preferV2Templates = in.readOptionalBoolean();
+        }
     }
 
     public UpdateRequest(String index, String id) {
@@ -800,6 +804,16 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
         return this;
     }
 
+    @Nullable
+    public Boolean preferV2Templates() {
+        return this.preferV2Templates;
+    }
+
+    public UpdateRequest preferV2Templates(@Nullable Boolean preferV2Templates) {
+        this.preferV2Templates = preferV2Templates;
+        return this;
+    }
+
     @Override
     public void writeTo(StreamOutput out) throws IOException {
         super.writeTo(out);
@@ -841,6 +855,9 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
         out.writeVLong(ifPrimaryTerm);
         out.writeBoolean(detectNoop);
         out.writeBoolean(scriptedUpsert);
+        if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
+            out.writeOptionalBoolean(preferV2Templates);
+        }
     }
 
     @Override

+ 1 - 1
server/src/main/java/org/elasticsearch/client/IndicesAdminClient.java

@@ -709,7 +709,7 @@ public interface IndicesAdminClient extends ElasticsearchClient {
     /**
      * Swaps the index pointed to by an alias given all provided conditions are satisfied
      */
-    ActionFuture<RolloverResponse> rolloversIndex(RolloverRequest request);
+    ActionFuture<RolloverResponse> rolloverIndex(RolloverRequest request);
 
     /**
      * Swaps the index pointed to by an alias given all provided conditions are satisfied

+ 1 - 1
server/src/main/java/org/elasticsearch/client/support/AbstractClient.java

@@ -1642,7 +1642,7 @@ public abstract class AbstractClient implements Client {
         }
 
         @Override
-        public ActionFuture<RolloverResponse> rolloversIndex(RolloverRequest request) {
+        public ActionFuture<RolloverResponse> rolloverIndex(RolloverRequest request) {
             return execute(RolloverAction.INSTANCE, request);
         }
 

+ 4 - 1
server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java

@@ -247,6 +247,10 @@ public class IndexMetadata implements Diffable<IndexMetadata>, ToXContentFragmen
     public static final Setting.AffixSetting<String> INDEX_ROUTING_INITIAL_RECOVERY_GROUP_SETTING =
         Setting.prefixKeySetting("index.routing.allocation.initial_recovery.", key -> Setting.simpleString(key));
         // this is only setable internally not a registered setting!!
+    public static final String PREFER_V2_TEMPLATES_FLAG = "prefer_v2_templates";
+    public static final String SETTING_PREFER_V2_TEMPLATES = "index." + PREFER_V2_TEMPLATES_FLAG;
+    public static final Setting<Boolean> PREFER_V2_TEMPLATES_SETTING = Setting.boolSetting(SETTING_PREFER_V2_TEMPLATES, false,
+        Property.Dynamic, Property.IndexScope);
 
     /**
      * The number of active shard copies to check for before proceeding with a write operation.
@@ -1077,7 +1081,6 @@ public class IndexMetadata implements Diffable<IndexMetadata>, ToXContentFragmen
             Arrays.fill(primaryTerms, SequenceNumbers.UNASSIGNED_PRIMARY_TERM);
         }
 
-
         public IndexMetadata build() {
             ImmutableOpenMap.Builder<String, AliasMetadata> tmpAliases = aliases;
             Settings tmpSettings = settings;

+ 24 - 8
server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java

@@ -329,14 +329,19 @@ public class MetadataCreateIndexService {
             // Check to see if a v2 template matched
             final String v2Template = MetadataIndexTemplateService.findV2Template(currentState.metadata(),
                 request.index(), isHiddenFromRequest == null ? false : isHiddenFromRequest);
+            final boolean preferV2Templates = resolvePreferV2Templates(request);
 
-            if (v2Template != null) {
+            if (v2Template != null && preferV2Templates) {
                 // If a v2 template was found, it takes precedence over all v1 templates, so create
                 // the index using that template and the request's specified settings
                 return applyCreateIndexRequestWithV2Template(currentState, request, silent, v2Template, metadataTransformer);
             } else {
-                // A v2 template wasn't found, check the v1 templates, in the event no templates are
-                // found creation still works using the request's specified index settings
+                if (v2Template != null) {
+                    logger.debug("ignoring matching index template [{}] as [prefer_v2_templates] is set to false", v2Template);
+                }
+                // A v2 template wasn't found (or is not preferred), check the v1 templates, in the
+                // event no templates are found creation still works using the request's specified
+                // index settings
                 final List<IndexTemplateMetadata> v1Templates = MetadataIndexTemplateService.findV1Templates(currentState.metadata(),
                     request.index(), isHiddenFromRequest);
 
@@ -345,6 +350,11 @@ public class MetadataCreateIndexService {
         }
     }
 
+    private static boolean resolvePreferV2Templates(CreateIndexClusterStateUpdateRequest request) {
+        return request.preferV2Templates() == null ?
+            IndexMetadata.PREFER_V2_TEMPLATES_SETTING.get(request.settings()) : request.preferV2Templates();
+    }
+
     public ClusterState applyCreateIndexRequest(ClusterState currentState, CreateIndexClusterStateUpdateRequest request,
                                                 boolean silent) throws Exception {
         return applyCreateIndexRequest(currentState, request, silent, null);
@@ -413,7 +423,8 @@ public class MetadataCreateIndexService {
     private IndexMetadata buildAndValidateTemporaryIndexMetadata(final ClusterState currentState,
                                                                  final Settings aggregatedIndexSettings,
                                                                  final CreateIndexClusterStateUpdateRequest request,
-                                                                 final int routingNumShards) {
+                                                                 final int routingNumShards,
+                                                                 final boolean preferV2Templates) {
 
         final boolean isHiddenAfterTemplates = IndexMetadata.INDEX_HIDDEN_SETTING.get(aggregatedIndexSettings);
         validateDotIndex(request.index(), currentState, isHiddenAfterTemplates);
@@ -421,6 +432,7 @@ public class MetadataCreateIndexService {
         // remove the setting it's temporary and is only relevant once we create the index
         final Settings.Builder settingsBuilder = Settings.builder().put(aggregatedIndexSettings);
         settingsBuilder.remove(IndexMetadata.INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.getKey());
+        settingsBuilder.put(IndexMetadata.PREFER_V2_TEMPLATES_SETTING.getKey(), preferV2Templates);
         final Settings indexSettings = settingsBuilder.build();
 
         final IndexMetadata.Builder tmpImdBuilder = IndexMetadata.builder(request.index());
@@ -441,7 +453,8 @@ public class MetadataCreateIndexService {
                                                                 final List<IndexTemplateMetadata> templates,
                                                                 final BiConsumer<Metadata.Builder, IndexMetadata> metadataTransformer)
                                                                                         throws Exception {
-        logger.info("applying create index request using v1 templates {}", templates);
+        logger.info("applying create index request using v1 templates {}",
+            templates.stream().map(IndexTemplateMetadata::name).collect(Collectors.toList()));
 
         final Map<String, Object> mappings = Collections.unmodifiableMap(parseMappings(request.mappings(),
             templates.stream().map(IndexTemplateMetadata::getMappings).collect(toList()), xContentRegistry));
@@ -450,7 +463,8 @@ public class MetadataCreateIndexService {
             aggregateIndexSettings(currentState, request, MetadataIndexTemplateService.resolveSettings(templates), mappings,
                 null, settings, indexScopedSettings);
         int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, null);
-        IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
+        IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards,
+            resolvePreferV2Templates(request));
 
         return applyCreateIndexWithTemporaryService(currentState, request, silent, null, tmpImd, mappings,
             indexService -> resolveAndValidateAliases(request.index(), request.aliases(),
@@ -477,7 +491,8 @@ public class MetadataCreateIndexService {
                 MetadataIndexTemplateService.resolveSettings(currentState.metadata(), templateName),
                 mappings, null, settings, indexScopedSettings);
         int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, null);
-        IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
+        IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards,
+            resolvePreferV2Templates(request));
 
         return applyCreateIndexWithTemporaryService(currentState, request, silent, null, tmpImd, mappings,
             indexService -> resolveAndValidateAliases(request.index(), request.aliases(),
@@ -501,7 +516,8 @@ public class MetadataCreateIndexService {
         final Settings aggregatedIndexSettings =
             aggregateIndexSettings(currentState, request, Settings.EMPTY, mappings, sourceMetadata, settings, indexScopedSettings);
         final int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, sourceMetadata);
-        IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
+        IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards,
+            IndexMetadata.PREFER_V2_TEMPLATES_SETTING.get(sourceMetadata.getSettings()));
 
         return applyCreateIndexWithTemporaryService(currentState, request, silent, sourceMetadata, tmpImd, mappings,
             indexService -> resolveAndValidateAliases(request.index(), request.aliases(), Collections.emptyList(),

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

@@ -80,6 +80,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings {
             IndexMetadata.INDEX_DATA_PATH_SETTING,
             IndexMetadata.INDEX_HIDDEN_SETTING,
             IndexMetadata.INDEX_FORMAT_SETTING,
+            IndexMetadata.PREFER_V2_TEMPLATES_SETTING,
             SearchSlowLog.INDEX_SEARCH_SLOWLOG_THRESHOLD_FETCH_DEBUG_SETTING,
             SearchSlowLog.INDEX_SEARCH_SLOWLOG_THRESHOLD_FETCH_WARN_SETTING,
             SearchSlowLog.INDEX_SEARCH_SLOWLOG_THRESHOLD_FETCH_INFO_SETTING,

+ 8 - 0
server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestCreateIndexAction.java

@@ -22,6 +22,8 @@ package org.elasticsearch.rest.action.admin.indices;
 import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
 import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.client.node.NodeClient;
+import org.elasticsearch.cluster.metadata.IndexMetadata;
+import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
 import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.index.mapper.MapperService;
@@ -49,10 +51,16 @@ public class RestCreateIndexAction extends BaseRestHandler {
         return "create_index_action";
     }
 
+    @Nullable
+    public static Boolean preferV2Templates(final RestRequest request) {
+        return request.paramAsBoolean(IndexMetadata.PREFER_V2_TEMPLATES_FLAG, null);
+    }
+
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
 
         CreateIndexRequest createIndexRequest = new CreateIndexRequest(request.param("index"));
+        createIndexRequest.preferV2Templates(preferV2Templates(request));
 
         if (request.hasContent()) {
             Map<String, Object> sourceAsMap = XContentHelper.convertToMap(request.requiredContent(), false,

+ 1 - 0
server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestRolloverIndexAction.java

@@ -54,6 +54,7 @@ public class RestRolloverIndexAction extends BaseRestHandler {
         rolloverIndexRequest.masterNodeTimeout(request.paramAsTime("master_timeout", rolloverIndexRequest.masterNodeTimeout()));
         rolloverIndexRequest.getCreateIndexRequest().waitForActiveShards(
                 ActiveShardCount.parseString(request.param("wait_for_active_shards")));
+        rolloverIndexRequest.getCreateIndexRequest().preferV2Templates(RestCreateIndexAction.preferV2Templates(request));
         return channel -> client.admin().indices().rolloverIndex(rolloverIndexRequest, new RestToXContentListener<>(channel));
     }
 }

+ 2 - 0
server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java

@@ -28,6 +28,7 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.rest.BaseRestHandler;
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.rest.action.RestStatusToXContentListener;
+import org.elasticsearch.rest.action.admin.indices.RestCreateIndexAction;
 import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
 
 import java.io.IOException;
@@ -82,6 +83,7 @@ public class RestBulkAction extends BaseRestHandler {
         bulkRequest.setRefreshPolicy(request.param("refresh"));
         bulkRequest.add(request.requiredContent(), defaultIndex, defaultRouting,
             defaultFetchSourceContext, defaultPipeline, allowExplicitIndex, request.getXContentType());
+        bulkRequest.preferV2Templates(RestCreateIndexAction.preferV2Templates(request));
 
         return channel -> client.bulk(bulkRequest, new RestStatusToXContentListener<>(channel));
     }

+ 2 - 0
server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java

@@ -29,6 +29,7 @@ import org.elasticsearch.rest.BaseRestHandler;
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.rest.action.RestActions;
 import org.elasticsearch.rest.action.RestStatusToXContentListener;
+import org.elasticsearch.rest.action.admin.indices.RestCreateIndexAction;
 
 import java.io.IOException;
 import java.util.List;
@@ -122,6 +123,7 @@ public class RestIndexAction extends BaseRestHandler {
         indexRequest.versionType(VersionType.fromString(request.param("version_type"), indexRequest.versionType()));
         indexRequest.setIfSeqNo(request.paramAsLong("if_seq_no", indexRequest.ifSeqNo()));
         indexRequest.setIfPrimaryTerm(request.paramAsLong("if_primary_term", indexRequest.ifPrimaryTerm()));
+        indexRequest.preferV2Templates(RestCreateIndexAction.preferV2Templates(request));
         String sOpType = request.param("op_type");
         String waitForActiveShards = request.param("wait_for_active_shards");
         if (waitForActiveShards != null) {

+ 2 - 0
server/src/main/java/org/elasticsearch/rest/action/document/RestUpdateAction.java

@@ -29,6 +29,7 @@ import org.elasticsearch.rest.BaseRestHandler;
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.rest.action.RestActions;
 import org.elasticsearch.rest.action.RestStatusToXContentListener;
+import org.elasticsearch.rest.action.admin.indices.RestCreateIndexAction;
 import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
 
 import java.io.IOException;
@@ -54,6 +55,7 @@ public class RestUpdateAction extends BaseRestHandler {
         updateRequest.routing(request.param("routing"));
         updateRequest.timeout(request.paramAsTime("timeout", updateRequest.timeout()));
         updateRequest.setRefreshPolicy(request.param("refresh"));
+        updateRequest.preferV2Templates(RestCreateIndexAction.preferV2Templates(request));
         String waitForActiveShards = request.param("wait_for_active_shards");
         if (waitForActiveShards != null) {
             updateRequest.waitForActiveShards(ActiveShardCount.parseString(waitForActiveShards));

+ 2 - 1
server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java

@@ -137,7 +137,8 @@ public class TransportBulkActionIndicesThatCannotBeCreatedTests extends ESTestCa
             }
 
             @Override
-            void createIndex(String index, TimeValue timeout, ActionListener<CreateIndexResponse> listener) {
+            void createIndex(String index, Boolean preferV2Templates,
+                             TimeValue timeout, ActionListener<CreateIndexResponse> listener) {
                 // If we try to create an index just immediately assume it worked
                 listener.onResponse(new CreateIndexResponse(true, true, index) {});
             }

+ 3 - 1
server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java

@@ -157,7 +157,8 @@ public class TransportBulkActionIngestTests extends ESTestCase {
         }
 
         @Override
-        void createIndex(String index, TimeValue timeout, ActionListener<CreateIndexResponse> listener) {
+        void createIndex(String index, Boolean preferV2Templates,
+                         TimeValue timeout, ActionListener<CreateIndexResponse> listener) {
             indexCreated = true;
             listener.onResponse(null);
         }
@@ -590,6 +591,7 @@ public class TransportBulkActionIngestTests extends ESTestCase {
         when(state.getMetadata()).thenReturn(metadata);
 
         IndexRequest indexRequest = new IndexRequest("missing_index").id("id");
+        indexRequest.preferV2Templates(true);
         indexRequest.source(Collections.emptyMap());
         AtomicBoolean responseCalled = new AtomicBoolean(false);
         AtomicBoolean failureCalled = new AtomicBoolean(false);

+ 12 - 11
server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTests.java

@@ -81,7 +81,8 @@ public class TransportBulkActionTests extends ESTestCase {
         }
 
         @Override
-        void createIndex(String index, TimeValue timeout, ActionListener<CreateIndexResponse> listener) {
+        void createIndex(String index, Boolean preferV2Templates,
+                         TimeValue timeout, ActionListener<CreateIndexResponse> listener) {
             indexCreated = true;
             listener.onResponse(null);
         }
@@ -175,14 +176,14 @@ public class TransportBulkActionTests extends ESTestCase {
 
         // index name matches with IDM:
         IndexRequest indexRequest = new IndexRequest("idx");
-        boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+        boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
         assertThat(result, is(true));
         assertThat(indexRequest.isPipelineResolved(), is(true));
         assertThat(indexRequest.getPipeline(), equalTo("default-pipeline"));
 
         // alias name matches with IDM:
         indexRequest = new IndexRequest("alias");
-        result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+        result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
         assertThat(result, is(true));
         assertThat(indexRequest.isPipelineResolved(), is(true));
         assertThat(indexRequest.getPipeline(), equalTo("default-pipeline"));
@@ -193,7 +194,7 @@ public class TransportBulkActionTests extends ESTestCase {
             .settings(settings(Version.CURRENT).put(IndexSettings.DEFAULT_PIPELINE.getKey(), "default-pipeline"));
         metadata = Metadata.builder().put(templateBuilder).build();
         indexRequest = new IndexRequest("idx");
-        result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+        result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
         assertThat(result, is(true));
         assertThat(indexRequest.isPipelineResolved(), is(true));
         assertThat(indexRequest.getPipeline(), equalTo("default-pipeline"));
@@ -209,7 +210,7 @@ public class TransportBulkActionTests extends ESTestCase {
 
         // index name matches with IDM:
         IndexRequest indexRequest = new IndexRequest("idx");
-        boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+        boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
         assertThat(result, is(true));
         assertThat(indexRequest.isPipelineResolved(), is(true));
         assertThat(indexRequest.getPipeline(), equalTo("_none"));
@@ -217,7 +218,7 @@ public class TransportBulkActionTests extends ESTestCase {
 
         // alias name matches with IDM:
         indexRequest = new IndexRequest("alias");
-        result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+        result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
         assertThat(result, is(true));
         assertThat(indexRequest.isPipelineResolved(), is(true));
         assertThat(indexRequest.getPipeline(), equalTo("_none"));
@@ -229,7 +230,7 @@ public class TransportBulkActionTests extends ESTestCase {
             .settings(settings(Version.CURRENT).put(IndexSettings.FINAL_PIPELINE.getKey(), "final-pipeline"));
         metadata = Metadata.builder().put(templateBuilder).build();
         indexRequest = new IndexRequest("idx");
-        result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+        result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
         assertThat(result, is(true));
         assertThat(indexRequest.isPipelineResolved(), is(true));
         assertThat(indexRequest.getPipeline(), equalTo("_none"));
@@ -241,7 +242,7 @@ public class TransportBulkActionTests extends ESTestCase {
         {
             Metadata metadata = Metadata.builder().build();
             IndexRequest indexRequest = new IndexRequest("idx");
-            boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+            boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
             assertThat(result, is(false));
             assertThat(indexRequest.isPipelineResolved(), is(true));
             assertThat(indexRequest.getPipeline(), equalTo(IngestService.NOOP_PIPELINE_NAME));
@@ -251,7 +252,7 @@ public class TransportBulkActionTests extends ESTestCase {
         {
             Metadata metadata = Metadata.builder().build();
             IndexRequest indexRequest = new IndexRequest("idx").setPipeline("request-pipeline");
-            boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+            boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
             assertThat(result, is(true));
             assertThat(indexRequest.isPipelineResolved(), is(true));
             assertThat(indexRequest.getPipeline(), equalTo("request-pipeline"));
@@ -265,7 +266,7 @@ public class TransportBulkActionTests extends ESTestCase {
                 .numberOfReplicas(0);
             Metadata metadata = Metadata.builder().put(builder).build();
             IndexRequest indexRequest = new IndexRequest("idx").setPipeline("request-pipeline");
-            boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+            boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
             assertThat(result, is(true));
             assertThat(indexRequest.isPipelineResolved(), is(true));
             assertThat(indexRequest.getPipeline(), equalTo("request-pipeline"));
@@ -279,7 +280,7 @@ public class TransportBulkActionTests extends ESTestCase {
                 .numberOfReplicas(0);
             Metadata metadata = Metadata.builder().put(builder).build();
             IndexRequest indexRequest = new IndexRequest("idx").setPipeline("request-pipeline");
-            boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, metadata);
+            boolean result = TransportBulkAction.resolvePipelines(indexRequest, indexRequest, false, metadata);
             assertThat(result, is(true));
             assertThat(indexRequest.isPipelineResolved(), is(true));
             assertThat(indexRequest.getPipeline(), equalTo("request-pipeline"));

+ 251 - 0
server/src/test/java/org/elasticsearch/indices/template/TemplatePreferenceIT.java

@@ -0,0 +1,251 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.indices.template;
+
+import org.elasticsearch.action.admin.indices.alias.Alias;
+import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
+import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
+import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
+import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateV2Action;
+import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateV2Action;
+import org.elasticsearch.action.bulk.BulkRequest;
+import org.elasticsearch.action.index.IndexRequest;
+import org.elasticsearch.action.update.UpdateRequest;
+import org.elasticsearch.cluster.metadata.IndexTemplateV2;
+import org.elasticsearch.cluster.metadata.Template;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.test.ESSingleNodeTestCase;
+import org.junit.After;
+import org.junit.Before;
+
+import java.util.Collections;
+
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
+import static org.hamcrest.Matchers.equalTo;
+
+public class TemplatePreferenceIT extends ESSingleNodeTestCase {
+
+    private static final String INDEX = "index";
+
+    @Before
+    public void setup() throws Exception {
+        assertAcked(client().admin().indices().preparePutTemplate("v1")
+            .setSettings(Settings.builder()
+                .put("index.priority", 15)
+                .build())
+            .setPatterns(Collections.singletonList(INDEX + "*")).get());
+
+        Template v2Settings = new Template(Settings.builder()
+            .put("index.priority", 23)
+            .build(), null, null);
+        IndexTemplateV2 v2template = new IndexTemplateV2(Collections.singletonList(INDEX + "*"), v2Settings, null, null, null, null);
+        PutIndexTemplateV2Action.Request request = new PutIndexTemplateV2Action.Request("v2");
+        request.indexTemplate(v2template);
+        assertAcked(client().execute(PutIndexTemplateV2Action.INSTANCE, request).get());
+    }
+
+    @After
+    public void cleanup() throws Exception {
+        assertAcked(client().admin().indices().prepareDeleteTemplate("v1").get());
+        assertAcked(client().execute(DeleteIndexTemplateV2Action.INSTANCE, new DeleteIndexTemplateV2Action.Request("v2")).get());
+    }
+
+    public void testCreateIndexPreference() throws Exception {
+        client().admin().indices().prepareCreate(INDEX).get();
+        assertUsedV1();
+
+        client().admin().indices().create(new CreateIndexRequest(INDEX).preferV2Templates(false)).get();
+        assertUsedV1();
+
+        client().admin().indices().create(new CreateIndexRequest(INDEX).preferV2Templates(true)).get();
+        assertUsedV2();
+    }
+
+    public void testIndexingRequestPreference() throws Exception {
+        client().index(new IndexRequest(INDEX).source("foo", "bar")).get();
+        assertUsedV1();
+
+        client().index(new IndexRequest(INDEX).source("foo", "bar").preferV2Templates(false)).get();
+        assertUsedV1();
+
+        client().index(new IndexRequest(INDEX).source("foo", "bar").preferV2Templates(true)).get();
+        assertUsedV2();
+
+        client().update(new UpdateRequest(INDEX, "1").doc("foo", "bar").docAsUpsert(true)).get();
+        assertUsedV1();
+
+        client().update(new UpdateRequest(INDEX, "1").doc("foo", "bar").docAsUpsert(true).preferV2Templates(false)).get();
+        assertUsedV1();
+
+        client().update(new UpdateRequest(INDEX, "1").doc("foo", "bar").docAsUpsert(true).preferV2Templates(true)).get();
+        assertUsedV2();
+
+        client().bulk(new BulkRequest(INDEX).add(new IndexRequest(INDEX).source("foo", "bar"))).get();
+        assertUsedV1();
+
+        client().bulk(new BulkRequest(INDEX).add(new IndexRequest(INDEX).source("foo", "bar")).preferV2Templates(false)).get();
+        assertUsedV1();
+
+        client().bulk(new BulkRequest(INDEX).add(new IndexRequest(INDEX).source("foo", "bar")).preferV2Templates(true)).get();
+        assertUsedV2();
+    }
+
+    public void testRolloverMaintainsSetting() throws Exception {
+        {
+            client().admin().indices().prepareCreate(INDEX + "-1")
+                .addAlias(new Alias("alias").writeIndex(true))
+                .get();
+
+            client().admin().indices().prepareRolloverIndex("alias").get();
+            GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX + "-000002").get();
+            assertThat("expected index to use V1 template and have priority of 15",
+                resp.getSetting(INDEX + "-000002", "index.priority"), equalTo("15"));
+            client().admin().indices().prepareDelete(INDEX + "*").get();
+        }
+
+        {
+            client().admin().indices().prepareCreate(INDEX + "-1")
+                .addAlias(new Alias("alias").writeIndex(true))
+                .get();
+
+            RolloverRequest request = new RolloverRequest("alias", INDEX + "-000002");
+            request.getCreateIndexRequest().preferV2Templates(false);
+            client().admin().indices().rolloverIndex(request).get();
+            GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX + "-000002").get();
+            assertThat("expected index to use V1 template and have priority of 15",
+                resp.getSetting(INDEX + "-000002", "index.priority"), equalTo("15"));
+            client().admin().indices().prepareDelete(INDEX + "*").get();
+        }
+
+        {
+            client().admin().indices().prepareCreate(INDEX + "-1")
+                .addAlias(new Alias("alias").writeIndex(true))
+                .get();
+
+            RolloverRequest request = new RolloverRequest("alias", INDEX + "-000002");
+            request.getCreateIndexRequest().preferV2Templates(true);
+            client().admin().indices().rolloverIndex(request).get();
+            GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX + "-000002").get();
+            assertThat("expected index to use V2 template and have priority of 23",
+                resp.getSetting(INDEX + "-000002", "index.priority"), equalTo("23"));
+            client().admin().indices().prepareDelete(INDEX + "*").get();
+        }
+
+        {
+            client().admin().indices().create(new CreateIndexRequest(INDEX + "-1")
+                .alias(new Alias("alias").writeIndex(true))
+                .preferV2Templates(false))
+                .get();
+
+            client().admin().indices().prepareRolloverIndex("alias").get();
+            GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX + "-000002").get();
+            assertThat("expected index to use V1 template and have priority of 15",
+                resp.getSetting(INDEX + "-000002", "index.priority"), equalTo("15"));
+            client().admin().indices().prepareDelete(INDEX + "*").get();
+        }
+
+        {
+            client().admin().indices().create(new CreateIndexRequest(INDEX + "-1")
+                .alias(new Alias("alias").writeIndex(true))
+                .preferV2Templates(false))
+                .get();
+
+            RolloverRequest request = new RolloverRequest("alias", INDEX + "-000002");
+            request.getCreateIndexRequest().preferV2Templates(false);
+            client().admin().indices().rolloverIndex(request).get();
+            GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX + "-000002").get();
+            assertThat("expected index to use V1 template and have priority of 15",
+                resp.getSetting(INDEX + "-000002", "index.priority"), equalTo("15"));
+            client().admin().indices().prepareDelete(INDEX + "*").get();
+        }
+
+        {
+            client().admin().indices().create(new CreateIndexRequest(INDEX + "-1")
+                .alias(new Alias("alias").writeIndex(true))
+                .preferV2Templates(false))
+                .get();
+
+            RolloverRequest request = new RolloverRequest("alias", INDEX + "-000002");
+            request.getCreateIndexRequest().preferV2Templates(true);
+            client().admin().indices().rolloverIndex(request).get();
+            GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX + "-000002").get();
+            assertThat("expected index to use V2 template and have priority of 23",
+                resp.getSetting(INDEX + "-000002", "index.priority"), equalTo("23"));
+            client().admin().indices().prepareDelete(INDEX + "*").get();
+        }
+
+        {
+            client().admin().indices().create(new CreateIndexRequest(INDEX + "-1")
+                .alias(new Alias("alias").writeIndex(true))
+                .preferV2Templates(true))
+                .get();
+
+            client().admin().indices().prepareRolloverIndex("alias").get();
+            GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX + "-000002").get();
+            assertThat("expected index to use V2 template and have priority of 23",
+                resp.getSetting(INDEX + "-000002", "index.priority"), equalTo("23"));
+            client().admin().indices().prepareDelete(INDEX + "*").get();
+        }
+
+        {
+            client().admin().indices().create(new CreateIndexRequest(INDEX + "-1")
+                .alias(new Alias("alias").writeIndex(true))
+                .preferV2Templates(true))
+                .get();
+
+            RolloverRequest request = new RolloverRequest("alias", INDEX + "-000002");
+            request.getCreateIndexRequest().preferV2Templates(false);
+            client().admin().indices().rolloverIndex(request).get();
+            GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX + "-000002").get();
+            assertThat("expected index to use V1 template and have priority of 15",
+                resp.getSetting(INDEX + "-000002", "index.priority"), equalTo("15"));
+            client().admin().indices().prepareDelete(INDEX + "*").get();
+        }
+
+        {
+            client().admin().indices().create(new CreateIndexRequest(INDEX + "-1")
+                .alias(new Alias("alias").writeIndex(true))
+                .preferV2Templates(true))
+                .get();
+
+            RolloverRequest request = new RolloverRequest("alias", INDEX + "-000002");
+            request.getCreateIndexRequest().preferV2Templates(true);
+            client().admin().indices().rolloverIndex(request).get();
+            GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX + "-000002").get();
+            assertThat("expected index to use V2 template and have priority of 23",
+                resp.getSetting(INDEX + "-000002", "index.priority"), equalTo("23"));
+            client().admin().indices().prepareDelete(INDEX + "*").get();
+        }
+    }
+
+    private void assertUsedV1() {
+        GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX).get();
+        assertThat("expected index to use V1 template and have priority of 15",
+            resp.getSetting(INDEX, "index.priority"), equalTo("15"));
+        client().admin().indices().prepareDelete(INDEX).get();
+    }
+
+    private void assertUsedV2() {
+        GetSettingsResponse resp = client().admin().indices().prepareGetSettings().setIndices(INDEX).get();
+        assertThat("expected index to use V2 template and have priority of 23",
+            resp.getSetting(INDEX, "index.priority"), equalTo("23"));
+        client().admin().indices().prepareDelete(INDEX).get();
+    }
+}

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

@@ -365,6 +365,7 @@ public class TransportResumeFollowAction extends TransportMasterNodeAction<Resum
             IndexMetadata.INDEX_PRIORITY_SETTING,
             IndexMetadata.SETTING_WAIT_FOR_ACTIVE_SHARDS,
             IndexMetadata.INDEX_HIDDEN_SETTING,
+            IndexMetadata.PREFER_V2_TEMPLATES_SETTING,
             EnableAllocationDecider.INDEX_ROUTING_REBALANCE_ENABLE_SETTING,
             EnableAllocationDecider.INDEX_ROUTING_ALLOCATION_ENABLE_SETTING,
             ShardsLimitAllocationDecider.INDEX_TOTAL_SHARDS_PER_NODE_SETTING,