You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zipkin.apache.org by ad...@apache.org on 2019/05/07 08:19:08 UTC

[incubator-zipkin] branch master updated: Conditionally wraps in Elasticsearch index template based on version (#2562)

This is an automated email from the ASF dual-hosted git repository.

adriancole pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-zipkin.git


The following commit(s) were added to refs/heads/master by this push:
     new 38a0e73  Conditionally wraps in Elasticsearch index template based on version (#2562)
38a0e73 is described below

commit 38a0e73734885ee5b2c25f4e8d05b1a6700ae471
Author: Adrian Cole <ad...@users.noreply.github.com>
AuthorDate: Tue May 7 16:19:02 2019 +0800

    Conditionally wraps in Elasticsearch index template based on version (#2562)
    
    Elasticsearch 7.x no longer wraps mappings by top-level type. They have
    a compatibility mode, but it complicates secondary templates as noticed
    by @chefky
    
    > For my custom index issue in elasticsearch 7.0 -- I figured it out, but I think it does have an implication with the code. You are still creating the index template with the mapping type, which ElasticSearch has deprecated in 7.0. When I do a 'GET /_template/zipkin-span_template', the mapping type 'span' is removed, and so my updated version did not include the mapping type. I can only get it to work by including the mapping type 'span' in my updated index template, but I have to ap [...]
    
    This addresses the issue depending on the version of Elasticsearch in
    use.
    
    Fixes #2559
---
 .../elasticsearch/ElasticsearchSpanConsumer.java   |   5 +-
 .../elasticsearch/ElasticsearchStorage.java        |   4 +-
 .../elasticsearch/VersionSpecificTemplates.java    | 225 ++++++++++-----------
 .../elasticsearch/internal/HttpBulkIndexer.java    |   6 +-
 .../VersionSpecificTemplatesTest.java              |  83 +++++++-
 5 files changed, 193 insertions(+), 130 deletions(-)

diff --git a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchSpanConsumer.java b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchSpanConsumer.java
index e793394..1bbf27a 100644
--- a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchSpanConsumer.java
+++ b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchSpanConsumer.java
@@ -87,9 +87,8 @@ class ElasticsearchSpanConsumer implements SpanConsumer { // not final for testi
       } else {
         // guessTimestamp is made for determining the span's authoritative timestamp. When choosing
         // the index bucket, any annotation is better than using current time.
-        for (int i = 0, length = span.annotations().size(); i < length; i++) {
-          indexTimestamp = span.annotations().get(i).timestamp() / 1000;
-          break;
+        if (!span.annotations().isEmpty()) {
+          indexTimestamp = span.annotations().get(0).timestamp() / 1000;
         }
         if (indexTimestamp == 0L) indexTimestamp = System.currentTimeMillis();
       }
diff --git a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchStorage.java b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchStorage.java
index 1d12681..974f9ce 100644
--- a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchStorage.java
+++ b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/ElasticsearchStorage.java
@@ -342,7 +342,7 @@ public abstract class ElasticsearchStorage extends zipkin2.storage.StorageCompon
   @Memoized // since we don't want overlapping calls to apply the index templates
   IndexTemplates ensureIndexTemplates() {
     try {
-      IndexTemplates templates = new VersionSpecificTemplates(this).get(http());
+      IndexTemplates templates = new VersionSpecificTemplates(this).get();
       HttpCall.Factory http = http();
       ensureIndexTemplate(http, buildUrl(http, templates, SPAN), templates.span());
       ensureIndexTemplate(http, buildUrl(http, templates, DEPENDENCY), templates.dependency());
@@ -355,8 +355,6 @@ public abstract class ElasticsearchStorage extends zipkin2.storage.StorageCompon
 
   HttpUrl buildUrl(HttpCall.Factory http, IndexTemplates templates, String type) {
     HttpUrl.Builder builder = http.baseUrl.newBuilder("_template");
-    // ES 7.x defaults include_type_name to false https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html#_literal_include_type_name_literal_now_defaults_to_literal_false_literal
-    if (templates.version() >= 7) builder.addQueryParameter("include_type_name", "true");
     String indexPrefix = indexNameFormatter().index() + templates.indexTypeDelimiter();
     return builder.addPathSegment(indexPrefix + type + "_template").build();
   }
diff --git a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/VersionSpecificTemplates.java b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/VersionSpecificTemplates.java
index 244aa87..ee522bb 100644
--- a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/VersionSpecificTemplates.java
+++ b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/VersionSpecificTemplates.java
@@ -22,6 +22,9 @@ import okhttp3.Request;
 import okio.BufferedSource;
 import zipkin2.elasticsearch.internal.client.HttpCall;
 
+import static zipkin2.elasticsearch.ElasticsearchAutocompleteTags.AUTOCOMPLETE;
+import static zipkin2.elasticsearch.ElasticsearchSpanStore.DEPENDENCY;
+import static zipkin2.elasticsearch.ElasticsearchSpanStore.SPAN;
 import static zipkin2.elasticsearch.internal.JsonReaders.enterPath;
 
 /** Returns a version-specific span and dependency index template */
@@ -32,45 +35,50 @@ final class VersionSpecificTemplates {
    */
   static final String KEYWORD = "{ \"type\": \"keyword\", \"norms\": false }";
 
-  final boolean searchEnabled, strictTraceId;
-  final String spanIndexTemplate;
-  final String dependencyIndexTemplate;
-  final String autocompleteIndexTemplate;
+  final ElasticsearchStorage es;
 
   VersionSpecificTemplates(ElasticsearchStorage es) {
-    this.searchEnabled = es.searchEnabled();
-    this.strictTraceId = es.strictTraceId();
-    this.spanIndexTemplate = spanIndexTemplate()
-      .replace("${INDEX}", es.indexNameFormatter().index())
-      .replace("${NUMBER_OF_SHARDS}", String.valueOf(es.indexShards()))
-      .replace("${NUMBER_OF_REPLICAS}", String.valueOf(es.indexReplicas()))
-      .replace("${TRACE_ID_MAPPING}", strictTraceId ? KEYWORD
-        // Supporting mixed trace ID length is expensive due to needing a special analyzer and
-        // "fielddata" which consumes a lot of heap. Sites should only turn off strict trace ID when
-        // in a transition, and keep trace ID length transitions as short time as possible.
-        : "{ \"type\": \"text\", \"fielddata\": \"true\", \"analyzer\": \"traceId_analyzer\" }");
-    this.dependencyIndexTemplate = DEPENDENCY_INDEX_TEMPLATE
-      .replace("${INDEX}", es.indexNameFormatter().index())
-      .replace("${NUMBER_OF_SHARDS}", String.valueOf(es.indexShards()))
-      .replace("${NUMBER_OF_REPLICAS}", String.valueOf(es.indexReplicas()));
-    this.autocompleteIndexTemplate = AUTOCOMPLETE_INDEX_TEMPLATE
-      .replace("${INDEX}", es.indexNameFormatter().index())
-      .replace("${NUMBER_OF_SHARDS}", String.valueOf(es.indexShards()))
-      .replace("${NUMBER_OF_REPLICAS}", String.valueOf(es.indexReplicas()));
+    this.es = es;
   }
 
-  /** Templatized due to version differences. Only fields used in search are declared */
-  String spanIndexTemplate() {
+  String indexPattern(String type, float version) {
+    return new StringBuilder()
+      .append('"')
+      .append(version < 6.0f ? "template" : "index_patterns")
+      .append("\": \"")
+      .append(es.indexNameFormatter().index())
+      .append(indexTypeDelimiter(version))
+      .append(type)
+      .append("-*")
+      .append("\"").toString();
+  }
+
+  String indexProperties(float version) {
+    // 6.x _all disabled https://www.elastic.co/guide/en/elasticsearch/reference/6.7/breaking-changes-6.0.html#_the_literal__all_literal_meta_field_is_now_disabled_by_default
+    // 7.x _default disallowed https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html#_the_literal__default__literal_mapping_is_no_longer_allowed
     String result = ""
-      + "{\n"
-      + "  \"index_patterns\": \"${INDEX}${INDEX_TYPE_DELIMITER}span-*\",\n"
-      + "  \"settings\": {\n"
-      + "    \"index.number_of_shards\": ${NUMBER_OF_SHARDS},\n"
-      + "    \"index.number_of_replicas\": ${NUMBER_OF_REPLICAS},\n"
-      + "    \"index.requests.cache.enable\": true,\n"
-      + "    \"index.mapper.dynamic\": false";
+      + "    \"index.number_of_shards\": " + es.indexShards() + ",\n"
+      + "    \"index.number_of_replicas\": " + es.indexReplicas() + ",\n"
+      + "    \"index.requests.cache.enable\": true";
+    // There is no explicit documentation of index.mapper.dynamic being removed in v7, but it was.
+    if (version >= 7.0f) return result + "\n";
+    return result + (",\n    \"index.mapper.dynamic\": false\n");
+  }
 
-    if (!strictTraceId) {
+  /** Templatized due to version differences. Only fields used in search are declared */
+  String spanIndexTemplate(float version) {
+    String result = "{\n"
+      + "  " + indexPattern(SPAN, version) + ",\n"
+      + "  \"settings\": {\n"
+      + indexProperties(version);
+
+    String traceIdMapping = KEYWORD;
+    if (!es.strictTraceId()) {
+      // Supporting mixed trace ID length is expensive due to needing a special analyzer and
+      // "fielddata" which consumes a lot of heap. Sites should only turn off strict trace ID when
+      // in a transition, and keep trace ID length transitions as short time as possible.
+      traceIdMapping =
+        "{ \"type\": \"text\", \"fielddata\": \"true\", \"analyzer\": \"traceId_analyzer\" }";
       result += (",\n"
         + "    \"analysis\": {\n"
         + "      \"analyzer\": {\n"
@@ -92,97 +100,93 @@ final class VersionSpecificTemplates {
 
     result += "  },\n";
 
-    if (searchEnabled) {
+    if (es.searchEnabled()) {
       return result
         + ("  \"mappings\": {\n"
-        + "    \"span\": {\n"
-        + "      \"_source\": {\"excludes\": [\"_q\"] },\n"
-        + "      \"dynamic_templates\": [\n"
-        + "        {\n"
-        + "          \"strings\": {\n"
-        + "            \"mapping\": {\n"
-        + "              \"type\": \"keyword\",\"norms\": false,\n"
-        + "              \"ignore_above\": 256\n"
-        + "            },\n"
-        + "            \"match_mapping_type\": \"string\",\n"
-        + "            \"match\": \"*\"\n"
-        + "          }\n"
+        + maybeWrap(SPAN, version, ""
+        + "    \"_source\": {\"excludes\": [\"_q\"] },\n"
+        + "    \"dynamic_templates\": [\n"
+        + "      {\n"
+        + "        \"strings\": {\n"
+        + "          \"mapping\": {\n"
+        + "            \"type\": \"keyword\",\"norms\": false, \"ignore_above\": 256\n"
+        + "          },\n"
+        + "          \"match_mapping_type\": \"string\",\n"
+        + "          \"match\": \"*\"\n"
         + "        }\n"
-        + "      ],\n"
-        + "      \"properties\": {\n"
-        + "        \"traceId\": ${TRACE_ID_MAPPING},\n"
-        + "        \"name\": " + KEYWORD + ",\n"
-        + "        \"localEndpoint\": {\n"
-        + "          \"type\": \"object\",\n"
-        + "          \"dynamic\": false,\n"
-        + "          \"properties\": { \"serviceName\": " + KEYWORD + " }\n"
-        + "        },\n"
-        + "        \"remoteEndpoint\": {\n"
-        + "          \"type\": \"object\",\n"
-        + "          \"dynamic\": false,\n"
-        + "          \"properties\": { \"serviceName\": " + KEYWORD + " }\n"
-        + "        },\n"
-        + "        \"timestamp_millis\": {\n"
-        + "          \"type\":   \"date\",\n"
-        + "          \"format\": \"epoch_millis\"\n"
-        + "        },\n"
-        + "        \"duration\": { \"type\": \"long\" },\n"
-        + "        \"annotations\": { \"enabled\": false },\n"
-        + "        \"tags\": { \"enabled\": false },\n"
-        + "        \"_q\": " + KEYWORD + "\n"
         + "      }\n"
-        + "    }\n"
+        + "    ],\n"
+        + "    \"properties\": {\n"
+        + "      \"traceId\": " + traceIdMapping + ",\n"
+        + "      \"name\": " + KEYWORD + ",\n"
+        + "      \"localEndpoint\": {\n"
+        + "        \"type\": \"object\",\n"
+        + "        \"dynamic\": false,\n"
+        + "        \"properties\": { \"serviceName\": " + KEYWORD + " }\n"
+        + "      },\n"
+        + "      \"remoteEndpoint\": {\n"
+        + "        \"type\": \"object\",\n"
+        + "        \"dynamic\": false,\n"
+        + "        \"properties\": { \"serviceName\": " + KEYWORD + " }\n"
+        + "      },\n"
+        + "      \"timestamp_millis\": {\n"
+        + "        \"type\":   \"date\",\n"
+        + "        \"format\": \"epoch_millis\"\n"
+        + "      },\n"
+        + "      \"duration\": { \"type\": \"long\" },\n"
+        + "      \"annotations\": { \"enabled\": false },\n"
+        + "      \"tags\": { \"enabled\": false },\n"
+        + "      \"_q\": " + KEYWORD + "\n"
+        + "    }\n")
         + "  }\n"
         + "}");
     }
     return result
       + ("  \"mappings\": {\n"
-      + "    \"span\": {\n"
-      + "      \"properties\": {\n"
-      + "        \"traceId\": ${TRACE_ID_MAPPING},\n"
-      + "        \"annotations\": { \"enabled\": false },\n"
-      + "        \"tags\": { \"enabled\": false }\n"
-      + "      }\n"
-      + "    }\n"
+      + maybeWrap(SPAN, version, ""
+      + "    \"properties\": {\n"
+      + "      \"traceId\": " + traceIdMapping + ",\n"
+      + "      \"annotations\": { \"enabled\": false },\n"
+      + "      \"tags\": { \"enabled\": false }\n"
+      + "    }\n")
       + "  }\n"
       + "}");
   }
 
   /** Templatized due to version differences. Only fields used in search are declared */
-  static final String DEPENDENCY_INDEX_TEMPLATE =
-    "{\n"
-      + "  \"index_patterns\": \"${INDEX}${INDEX_TYPE_DELIMITER}dependency-*\",\n"
+  String dependencyTemplate(float version) {
+    return "{\n"
+      + "  " + indexPattern(DEPENDENCY, version) + ",\n"
       + "  \"settings\": {\n"
-      + "    \"index.number_of_shards\": ${NUMBER_OF_SHARDS},\n"
-      + "    \"index.number_of_replicas\": ${NUMBER_OF_REPLICAS},\n"
-      + "    \"index.requests.cache.enable\": true,\n"
-      + "    \"index.mapper.dynamic\": false\n"
+      + indexProperties(version)
       + "  },\n"
-      + "  \"mappings\": {\"dependency\": { \"enabled\": false }}\n"
+      + "  \"mappings\": {\n"
+      + maybeWrap(DEPENDENCY, version, "    \"enabled\": false\n")
+      + "  }\n"
       + "}";
+  }
 
   // The key filed of a autocompleteKeys is intentionally names as tagKey since it clashes with the
   // BodyConverters KEY
-  static final String AUTOCOMPLETE_INDEX_TEMPLATE =
-    "{\n"
-      + "  \"index_patterns\": \"${INDEX}${INDEX_TYPE_DELIMITER}autocomplete-*\",\n"
+  String autocompleteTemplate(float version) {
+    return "{\n"
+      + "  " + indexPattern(AUTOCOMPLETE, version) + ",\n"
       + "  \"settings\": {\n"
-      + "    \"index.number_of_shards\": ${NUMBER_OF_SHARDS},\n"
-      + "    \"index.number_of_replicas\": ${NUMBER_OF_REPLICAS},\n"
-      + "    \"index.requests.cache.enable\": true,\n"
-      + "    \"index.mapper.dynamic\": false\n"
+      + indexProperties(version)
       + "  },\n"
       + "  \"mappings\": {\n"
-      + "   \"autocomplete\": {\n"
-      + "      \"enabled\": true,\n"
-      + "      \"properties\": {\n"
-      + "        \"tagKey\": " + KEYWORD + ",\n"
-      + "        \"tagValue\": " + KEYWORD + "\n"
-      + "  }}}\n"
+      + maybeWrap(AUTOCOMPLETE, version, ""
+      + "    \"enabled\": true,\n"
+      + "    \"properties\": {\n"
+      + "      \"tagKey\": " + KEYWORD + ",\n"
+      + "      \"tagValue\": " + KEYWORD + "\n"
+      + "    }\n")
+      + "  }\n"
       + "}";
+  }
 
-  IndexTemplates get(HttpCall.Factory callFactory) throws IOException {
-    float version = getVersion(callFactory);
+  IndexTemplates get() throws IOException {
+    float version = getVersion(es.http());
     if (version < 5.0f || version >= 8.0f) {
       throw new IllegalArgumentException(
         "Elasticsearch versions 5-7.x are supported, was: " + version);
@@ -190,9 +194,9 @@ final class VersionSpecificTemplates {
     return IndexTemplates.newBuilder()
       .version(version)
       .indexTypeDelimiter(indexTypeDelimiter(version))
-      .span(versionSpecificIndexTemplate(spanIndexTemplate, version))
-      .dependency(versionSpecificIndexTemplate(dependencyIndexTemplate, version))
-      .autocomplete(versionSpecificIndexTemplate(autocompleteIndexTemplate, version))
+      .span(spanIndexTemplate(version))
+      .dependency(dependencyTemplate(version))
+      .autocomplete(autocompleteTemplate(version))
       .build();
   }
 
@@ -208,6 +212,12 @@ final class VersionSpecificTemplates {
     return version < 7.0f ? ':' : '-';
   }
 
+  static String maybeWrap(String type, float version, String json) {
+    // ES 7.x defaults include_type_name to false https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html#_literal_include_type_name_literal_now_defaults_to_literal_false_literal
+    if (version >= 7.0f) return json;
+    return "    \"" + type + "\": {\n  " + json.replace("\n", "\n  ") + "  }\n";
+  }
+
   static float getVersion(HttpCall.Factory callFactory) throws IOException {
     Request getNode = new Request.Builder().url(callFactory.baseUrl).tag("get-node").build();
     return callFactory.newCall(getNode, ReadVersionNumber.INSTANCE).execute();
@@ -223,16 +233,5 @@ final class VersionSpecificTemplates {
       return Float.valueOf(versionString.substring(0, 3));
     }
   }
-
-  static String versionSpecificIndexTemplate(String template, float version) {
-    String indexTypeDelimiter = Character.toString(indexTypeDelimiter(version));
-    template = template.replace("${INDEX_TYPE_DELIMITER}", indexTypeDelimiter);
-    if (version < 6.0f) return template.replace("index_patterns", "template");
-    // 6.x _all disabled https://www.elastic.co/guide/en/elasticsearch/reference/6.7/breaking-changes-6.0.html#_the_literal__all_literal_meta_field_is_now_disabled_by_default
-    // 7.x _default disallowed https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html#_the_literal__default__literal_mapping_is_no_longer_allowed
-    if (version < 7.0f) return template;
-    // There is no explicit documentation of this setting being removed in 7.0f, but it was.
-    return template.replace(",\n    \"index.mapper.dynamic\": false", "");
-  }
 }
 
diff --git a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/HttpBulkIndexer.java b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/HttpBulkIndexer.java
index c52cda1..2eeffea 100644
--- a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/HttpBulkIndexer.java
+++ b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/HttpBulkIndexer.java
@@ -36,6 +36,7 @@ public final class HttpBulkIndexer {
   static final MediaType APPLICATION_JSON = MediaType.parse("application/json");
 
   final String tag;
+  final boolean shouldAddType;
   final HttpCall.Factory http;
   final String pipeline;
   final boolean waitForRefresh;
@@ -45,6 +46,7 @@ public final class HttpBulkIndexer {
 
   public HttpBulkIndexer(String tag, ElasticsearchStorage es) {
     this.tag = tag;
+    shouldAddType = es.version() < 7.0f;
     http = es.http();
     pipeline = es.pipeline();
     waitForRefresh = es.flushOnWrites();
@@ -74,8 +76,8 @@ public final class HttpBulkIndexer {
 
   void writeIndexMetadata(String index, String typeName, @Nullable String id) {
     body.writeUtf8("{\"index\":{\"_index\":\"").writeUtf8(index).writeByte('"');
-    // the _type parameter is needed for Elasticsearch <6.x
-    body.writeUtf8(",\"_type\":\"").writeUtf8(typeName).writeByte('"');
+    // the _type parameter is needed for Elasticsearch < 6.x
+    if (shouldAddType) body.writeUtf8(",\"_type\":\"").writeUtf8(typeName).writeByte('"');
     if (id != null) {
       body.writeUtf8(",\"_id\":\"").writeUtf8(jsonEscape(id).toString()).writeByte('"');
     }
diff --git a/zipkin-storage/elasticsearch/src/test/java/zipkin2/elasticsearch/VersionSpecificTemplatesTest.java b/zipkin-storage/elasticsearch/src/test/java/zipkin2/elasticsearch/VersionSpecificTemplatesTest.java
index 0636403..164db5b 100644
--- a/zipkin-storage/elasticsearch/src/test/java/zipkin2/elasticsearch/VersionSpecificTemplatesTest.java
+++ b/zipkin-storage/elasticsearch/src/test/java/zipkin2/elasticsearch/VersionSpecificTemplatesTest.java
@@ -107,13 +107,13 @@ public class VersionSpecificTemplatesTest {
 
     thrown.expectMessage("Elasticsearch versions 5-7.x are supported, was: 2.4");
 
-    new VersionSpecificTemplates(storage).get(storage.http());
+    new VersionSpecificTemplates(storage).get();
   }
 
   @Test public void version5() throws Exception {
     es.enqueue(VERSION_RESPONSE_5);
 
-    IndexTemplates template = new VersionSpecificTemplates(storage).get(storage.http());
+    IndexTemplates template = new VersionSpecificTemplates(storage).get();
 
     assertThat(template.version()).isEqualTo(5.0f);
     assertThat(template.autocomplete())
@@ -129,7 +129,7 @@ public class VersionSpecificTemplatesTest {
   @Test public void version6() throws Exception {
     es.enqueue(VERSION_RESPONSE_6);
 
-    IndexTemplates template = new VersionSpecificTemplates(storage).get(storage.http());
+    IndexTemplates template = new VersionSpecificTemplates(storage).get();
 
     assertThat(template.version()).isEqualTo(6.7f);
     assertThat(template.autocomplete())
@@ -139,28 +139,72 @@ public class VersionSpecificTemplatesTest {
       .contains("\"index.mapper.dynamic\": false");
   }
 
+  @Test public void version6_wrapsPropertiesWithType() throws Exception {
+    es.enqueue(VERSION_RESPONSE_6);
+
+    IndexTemplates template = new VersionSpecificTemplates(storage).get();
+
+    assertThat(template.dependency()).contains(""
+      + "  \"mappings\": {\n"
+      + "    \"dependency\": {\n"
+      + "      \"enabled\": false\n"
+      + "    }\n"
+      + "  }");
+
+    assertThat(template.autocomplete()).contains(""
+      + "  \"mappings\": {\n"
+      + "    \"autocomplete\": {\n"
+      + "      \"enabled\": true,\n"
+      + "      \"properties\": {\n"
+      + "        \"tagKey\": { \"type\": \"keyword\", \"norms\": false },\n"
+      + "        \"tagValue\": { \"type\": \"keyword\", \"norms\": false }\n"
+      + "      }\n"
+      + "    }\n"
+      + "  }");
+  }
+
   @Test public void version7() throws Exception {
     es.enqueue(VERSION_RESPONSE_7);
 
-    IndexTemplates template = new VersionSpecificTemplates(storage).get(storage.http());
+    IndexTemplates template = new VersionSpecificTemplates(storage).get();
 
     assertThat(template.version()).isEqualTo(7.0f);
     assertThat(template.autocomplete())
       .withFailMessage("Starting at v7.x, we delimit index and type with hyphen")
       .contains("\"index_patterns\": \"zipkin-autocomplete-*\"");
     assertThat(template.autocomplete())
-      //.withFailMessage("7.x does not support the key index.mapper.dynamic")
+      .withFailMessage("7.x does not support the key index.mapper.dynamic")
       .doesNotContain("\"index.mapper.dynamic\": false");
   }
 
-  @Test public void searchEnabled_minimalSpanIndexing() throws Exception {
+  @Test public void version7_doesntWrapPropertiesWithType() throws Exception {
+    es.enqueue(VERSION_RESPONSE_7);
+
+    IndexTemplates template = new VersionSpecificTemplates(storage).get();
+
+    assertThat(template.dependency()).contains(""
+      + "  \"mappings\": {\n"
+      + "    \"enabled\": false\n"
+      + "  }");
+
+    assertThat(template.autocomplete()).contains(""
+      + "  \"mappings\": {\n"
+      + "    \"enabled\": true,\n"
+      + "    \"properties\": {\n"
+      + "      \"tagKey\": { \"type\": \"keyword\", \"norms\": false },\n"
+      + "      \"tagValue\": { \"type\": \"keyword\", \"norms\": false }\n"
+      + "    }\n"
+      + "  }");
+  }
+
+  @Test public void searchEnabled_minimalSpanIndexing_6x() throws Exception {
     storage = ElasticsearchStorage.newBuilder().hosts(storage.hostsSupplier().get())
       .searchEnabled(false)
       .build();
 
     es.enqueue(VERSION_RESPONSE_6);
 
-    IndexTemplates template = new VersionSpecificTemplates(storage).get(storage.http());
+    IndexTemplates template = new VersionSpecificTemplates(storage).get();
 
     assertThat(template.span())
       .contains(""
@@ -175,10 +219,31 @@ public class VersionSpecificTemplatesTest {
         + "  }");
   }
 
+  @Test public void searchEnabled_minimalSpanIndexing_7x() throws Exception {
+    storage = ElasticsearchStorage.newBuilder().hosts(storage.hostsSupplier().get())
+      .searchEnabled(false)
+      .build();
+
+    es.enqueue(VERSION_RESPONSE_7);
+
+    IndexTemplates template = new VersionSpecificTemplates(storage).get();
+
+    // doesn't wrap in a type name
+    assertThat(template.span())
+      .contains(""
+        + "  \"mappings\": {\n"
+        + "    \"properties\": {\n"
+        + "      \"traceId\": { \"type\": \"keyword\", \"norms\": false },\n"
+        + "      \"annotations\": { \"enabled\": false },\n"
+        + "      \"tags\": { \"enabled\": false }\n"
+        + "    }\n"
+        + "  }");
+  }
+
   @Test public void strictTraceId_doesNotIncludeAnalysisSection() throws Exception {
     es.enqueue(VERSION_RESPONSE_6);
 
-    IndexTemplates template = new VersionSpecificTemplates(storage).get(storage.http());
+    IndexTemplates template = new VersionSpecificTemplates(storage).get();
 
     assertThat(template.span()).doesNotContain("analysis");
   }
@@ -190,7 +255,7 @@ public class VersionSpecificTemplatesTest {
 
     es.enqueue(VERSION_RESPONSE_6);
 
-    IndexTemplates template = new VersionSpecificTemplates(storage).get(storage.http());
+    IndexTemplates template = new VersionSpecificTemplates(storage).get();
 
     assertThat(template.span()).contains("analysis");
   }