/** * Copyright 2015-2017 The OpenZipkin Authors * * Licensed 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 zipkin.storage.elasticsearch.http; import com.squareup.moshi.JsonReader; import okhttp3.Request; import zipkin.storage.elasticsearch.http.internal.client.HttpCall; import static zipkin.moshi.JsonReaders.enterPath; /** Ensures the index template exists and saves off the version */ final class VersionSpecificTemplate { final String indexTemplate; VersionSpecificTemplate(ElasticsearchHttpStorage es) { this.indexTemplate = INDEX_TEMPLATE .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__}", es.strictTraceId() ? "{ KEYWORD }" : "{ \"type\": \"string\", \"analyzer\": \"traceId_analyzer\" }"); } /** Templatized due to version differences. Only fields used in search are declared */ static final String INDEX_TEMPLATE = "{\n" + " \"template\": \"${__INDEX__}-*\",\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" + " \"analysis\": {\n" + " \"analyzer\": {\n" + " \"traceId_analyzer\": {\n" + " \"type\": \"custom\",\n" + " \"tokenizer\": \"keyword\",\n" + " \"filter\": \"traceId_filter\"\n" + " }\n" + " },\n" + " \"filter\": {\n" + " \"traceId_filter\": {\n" + " \"type\": \"pattern_capture\",\n" + " \"patterns\": [\"([0-9a-f]{1,16})$\"],\n" + " \"preserve_original\": true\n" + " }\n" + " }\n" + " }\n" + " },\n" + " \"mappings\": {\n" + " \"_default_\": {\n" + " \"_all\": {\n" + " \"enabled\": false\n" + " }\n" + " },\n" + " \"" + ElasticsearchHttpSpanStore.SPAN + "\": {\n" + " \"properties\": {\n" + " \"traceId\": ${__TRACE_ID_MAPPING__},\n" + " \"name\": { KEYWORD },\n" + " \"timestamp_millis\": {\n" + " \"type\": \"date\",\n" + " \"format\": \"epoch_millis\"\n" + " },\n" + " \"duration\": { \"type\": \"long\" },\n" + " \"annotations\": {\n" + " \"type\": \"nested\",\n" + " \"dynamic\": false,\n" + " \"properties\": {\n" + " \"value\": { KEYWORD },\n" + " \"endpoint\": {\n" + " \"type\": \"object\",\n" + " \"dynamic\": false,\n" + " \"properties\": { \"serviceName\": { KEYWORD } }\n" + " }\n" + " }\n" + " },\n" + " \"binaryAnnotations\": {\n" + " \"type\": \"nested\",\n" + " \"dynamic\": false,\n" + " \"properties\": {\n" + " \"key\": { KEYWORD },\n" + " \"value\": { KEYWORD },\n" + " \"endpoint\": {\n" + " \"type\": \"object\",\n" + " \"dynamic\": false,\n" + " \"properties\": { \"serviceName\": { KEYWORD } }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " },\n" + " \"" + ElasticsearchHttpSpanStore.DEPENDENCY_LINK + "\": { \"enabled\": false },\n" + " \"" + ElasticsearchHttpSpanStore.SERVICE_SPAN + "\": {\n" + " \"properties\": {\n" + " \"serviceName\": { KEYWORD },\n" + " \"spanName\": { KEYWORD }\n" + " }\n" + " }\n" + " }\n" + "}"; /** Returns a version-specific index template */ String get(HttpCall.Factory callFactory) { String version = getVersion(callFactory); return versionSpecificTemplate(version); } static String getVersion(HttpCall.Factory callFactory) { Request getNode = new Request.Builder().url(callFactory.baseUrl).tag("get-node").build(); return callFactory.execute(getNode, b -> { JsonReader version = enterPath(JsonReader.of(b), "version", "number"); if (version == null) throw new IllegalStateException(".version.number not in response"); return version.nextString(); }); } private String versionSpecificTemplate(String version) { if (version.startsWith("2")) { return indexTemplate .replace("KEYWORD", "\"type\": \"string\", \"ignore_above\": 256, \"norms\": {\"enabled\": false }, \"index\": \"not_analyzed\""); } else if (version.startsWith("5")) { return indexTemplate .replace("KEYWORD", "\"type\": \"keyword\", \"ignore_above\": 256, \"norms\": false") .replace("\"analyzer\": \"traceId_analyzer\" }", "\"fielddata\": \"true\", \"analyzer\": \"traceId_analyzer\" }"); } else { throw new IllegalStateException("Elasticsearch 2.x and 5.x are supported, was: " + version); } } }