diff --git a/LICENSE b/LICENSE
index 3a9303dddb6..0c1111bc79a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -214,8 +214,3 @@ This product contains a modified part of Okio, distributed by Square:
* License: Apache License v2.0
* Homepage: https://github.com/square/okio
-
-This product contains a modified part of Armeria, distributed by LINE Corporation:
-
- * License: Apache License v2.0
- * Homepage: https://github.com/line/armeria
diff --git a/pom.xml b/pom.xml
index 4692aa4abbf..6e906ece1d1 100755
--- a/pom.xml
+++ b/pom.xml
@@ -57,9 +57,9 @@
com.linecorp.armeria
- 1.26.4
+ 1.27.1
- 4.1.100.Final
+ 4.1.106.Final
2.16.1
@@ -564,8 +564,6 @@
build-bin/configure_test
build-bin/deploy
build-bin/test
-
- **/HttpFile.java
true
diff --git a/zipkin-server/README.md b/zipkin-server/README.md
index c311c21dd61..981bf94893a 100644
--- a/zipkin-server/README.md
+++ b/zipkin-server/README.md
@@ -512,13 +512,13 @@ Zipkin can register itself in [Eureka](https://github.com/Netflix/eureka), allow
to discover its listen address and health state. This is enabled when `EUREKA_SERVICE_URL` is set to
a valid v2 endpoint of the [Eureka REST API](https://github.com/Netflix/eureka/wiki/Eureka-REST-operations).
-| Variable | Instance field | Description |
-|----------------------------|----------------|------------------------------------------------------------------------------------------------|
-| `DISCOVERY_EUREKA_ENABLED` | N/A | `false` disables Eureka registration. Defaults to `true`. |
-| `EUREKA_SERVICE_URL` | N/A | v2 endpoint of Eureka, e.g. `https://eureka-prod/eureka/v2`. No default |
-| `EUREKA_APP_NAME` | .app | The application this instance registers to. Defaults to `zipkin` |
-| `EUREKA_HOSTNAME` | .hostName | The hostname used with `${QUERY_PORT}` to build the instance `vipAddress`. Defaults to detect. |
-| `EUREKA_INSTANCE_ID` | .instanceId | Defaults to `${EUREKA_HOSTNAME}:${EUREKA_APP_NAME}:${QUERY_PORT}`. |
+| Variable | Instance field | Description |
+|----------------------------|----------------|-------------------------------------------------------------------------|
+| `DISCOVERY_EUREKA_ENABLED` | N/A | `false` disables Eureka registration. Defaults to `true`. |
+| `EUREKA_SERVICE_URL` | N/A | v2 endpoint of Eureka, e.g. `https://eureka-prod/eureka/v2`. No default |
+| `EUREKA_APP_NAME` | .app | The application this instance registers to. Defaults to `zipkin` |
+| `EUREKA_HOSTNAME` | .hostName | The instance `hostName` and `vipAddress`. Defaults to detect. |
+| `EUREKA_INSTANCE_ID` | .instanceId | Defaults to `${EUREKA_HOSTNAME}:${EUREKA_APP_NAME}:${QUERY_PORT}`. |
Example usage:
diff --git a/zipkin-server/src/main/java/com/linecorp/armeria/server/file/HttpFile.java b/zipkin-server/src/main/java/com/linecorp/armeria/server/file/HttpFile.java
deleted file mode 100644
index ec232e670ff..00000000000
--- a/zipkin-server/src/main/java/com/linecorp/armeria/server/file/HttpFile.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright 2019 LINE Corporation
- *
- * LINE Corporation 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:
- *
- * https://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 com.linecorp.armeria.server.file;
-
-import static java.util.Objects.requireNonNull;
-
-import java.io.File;
-import java.net.JarURLConnection;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Path;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.Executor;
-
-import com.linecorp.armeria.common.HttpData;
-import com.linecorp.armeria.common.HttpResponse;
-import com.linecorp.armeria.common.ResponseHeaders;
-import com.linecorp.armeria.common.annotation.Nullable;
-import com.linecorp.armeria.common.annotation.UnstableApi;
-import com.linecorp.armeria.server.HttpService;
-import com.linecorp.armeria.server.file.HttpFileBuilder.ClassPathHttpFileBuilder;
-import com.linecorp.armeria.server.file.HttpFileBuilder.FileSystemHttpFileBuilder;
-import com.linecorp.armeria.server.file.HttpFileBuilder.HttpDataFileBuilder;
-import com.linecorp.armeria.server.file.HttpFileBuilder.NonExistentHttpFileBuilder;
-import com.linecorp.armeria.unsafe.PooledObjects;
-
-import io.netty.buffer.ByteBufAllocator;
-
-/**
- * A file-like HTTP resource which yields an {@link HttpResponse}.
- *
{@code
- * HttpFile faviconFile = HttpFile.of(new File("/var/www/favicon.ico"));
- * ServerBuilder builder = Server.builder();
- * builder.service("/favicon.ico", faviconFile.asService());
- * Server server = builder.build();
- * }
- *
- * @see HttpFileBuilder
- */
-public interface HttpFile {
-
- /**
- * Creates a new {@link HttpFile} which streams the specified {@link File}.
- */
- static HttpFile of(File file) {
- return builder(file).build();
- }
-
- /**
- * Creates a new {@link HttpFile} which streams the file at the specified {@link Path}.
- */
- static HttpFile of(Path path) {
- return builder(path).build();
- }
-
- /**
- * Creates a new {@link HttpFile} which streams the resource at the specified {@code path}, loaded by
- * the specified {@link ClassLoader}.
- *
- * @param classLoader the {@link ClassLoader} which will load the resource at the {@code path}
- * @param path the path to the resource
- */
- static HttpFile of(ClassLoader classLoader, String path) {
- return builder(classLoader, path).build();
- }
-
- /**
- * Creates a new {@link HttpFile} which streams the specified {@link HttpData}. This method is
- * a shortcut for {@code HttpFile.of(data, System.currentTimeMillis()}.
- */
- static HttpFile of(HttpData data) {
- return builder(data).build();
- }
-
- /**
- * Creates a new {@link HttpFile} which streams the specified {@link HttpData} with the specified
- * {@code lastModifiedMillis}.
- *
- * @param data the data that provides the content of an HTTP response
- * @param lastModifiedMillis when the {@code data} has been last modified, represented as the number of
- * millisecond since the epoch
- */
- static HttpFile of(HttpData data, long lastModifiedMillis) {
- return builder(data, lastModifiedMillis).build();
- }
-
- /**
- * Creates a new {@link HttpFile} which caches the content and attributes of the specified {@link HttpFile}.
- * The cache is automatically invalidated when the {@link HttpFile} is updated.
- *
- * @param file the {@link HttpFile} to cache
- * @param maxCachingLength the maximum allowed length of the {@link HttpFile} to cache. if the length of
- * the {@link HttpFile} exceeds this value, no caching will be performed.
- */
- static HttpFile ofCached(HttpFile file, int maxCachingLength) {
- requireNonNull(file, "file");
- if (maxCachingLength<0){
- return null;
- }
- if (maxCachingLength == 0) {
- return file;
- } else {
- return new CachingHttpFile(file, maxCachingLength);
- }
- }
-
- /**
- * Returns an {@link HttpFile} which represents a non-existent file.
- */
- static HttpFile nonExistent() {
- return NonExistentHttpFile.INSTANCE;
- }
-
- /**
- * Returns an {@link HttpFile} redirected to the specified {@code location}.
- */
- static HttpFile ofRedirect(String location) {
- requireNonNull(location, "location");
- return new NonExistentHttpFile(location);
- }
-
- /**
- * Returns an {@link HttpFile} that becomes readable when the specified {@link CompletionStage} is complete.
- * All {@link HttpFile} operations will wait until the specified {@link CompletionStage} is completed.
- */
- static HttpFile from(CompletionStage extends HttpFile> stage) {
- return new DeferredHttpFile(requireNonNull(stage, "stage"));
- }
-
- /**
- * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the file at the specified
- * {@link File}.
- */
- static HttpFileBuilder builder(File file) {
- return builder(requireNonNull(file, "file").toPath());
- }
-
- /**
- * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the file at the specified
- * {@link Path}.
- */
- static HttpFileBuilder builder(Path path) {
- return new FileSystemHttpFileBuilder(requireNonNull(path, "path"));
- }
-
- /**
- * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the specified
- * {@link HttpData}. The last modified date of the file is set to 'now'.
- */
- static HttpFileBuilder builder(HttpData data) {
- return builder(data, System.currentTimeMillis());
- }
-
- /**
- * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the specified
- * {@link HttpData} and {@code lastModifiedMillis}.
- *
- * @param data the content of the file
- * @param lastModifiedMillis the last modified time represented as the number of milliseconds
- * since the epoch
- */
- static HttpFileBuilder builder(HttpData data, long lastModifiedMillis) {
- requireNonNull(data, "data");
- return new HttpDataFileBuilder(data, lastModifiedMillis)
- .autoDetectedContentType(false); // Can't auto-detect because there's no path or URI.
- }
-
- /**
- * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the specified
- * {@link URL}. {@code file:}, {@code jrt:} and {@linkplain JarURLConnection jar:file:} protocol.
- */
- static HttpFileBuilder builder(URL url) {
- requireNonNull(url, "url");
- if (url.getPath().endsWith("/")) {
- // Non-existent resource.
- return new NonExistentHttpFileBuilder();
- }
-
- // Convert to a real file if possible.
- if ("file".equals(url.getProtocol())) {
- File f;
- try {
- f = new File(url.toURI());
- } catch (URISyntaxException ignored) {
- f = new File(url.getPath());
- }
-
- return builder(f.toPath());
- } else if ("jar".equals(url.getProtocol()) && (url.getPath().startsWith("file:")||url.getPath().startsWith("nested:")) ||
- "jrt".equals(url.getProtocol()) ||
- "bundle".equals(url.getProtocol())) {
- return new ClassPathHttpFileBuilder(url);
- }
- throw new IllegalArgumentException("Unsupported URL: " + url + " (must start with " +
- "'file:', 'jar:file', 'jrt:' or 'bundle:')");
- }
-
- /**
- * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the classpath resource
- * at the specified {@code path} using the specified {@link ClassLoader}.
- */
- static HttpFileBuilder builder(ClassLoader classLoader, String path) {
- requireNonNull(classLoader, "classLoader");
- requireNonNull(path, "path");
-
- // Strip the leading slash.
- if (path.startsWith("/")) {
- path = path.substring(1);
- }
-
- // Retrieve the resource URL.
- @Nullable
- final URL url = classLoader.getResource(path);
- if (url == null) {
- // Non-existent resource.
- return new NonExistentHttpFileBuilder();
- }
- return builder(url);
- }
-
- /**
- * Retrieves the attributes of this file.
- *
- * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file
- *
- * @return the {@link CompletableFuture} that will be completed with the attributes of this file.
- * It will be completed with {@code null} if the file does not exist.
- */
- CompletableFuture<@Nullable HttpFileAttributes> readAttributes(Executor fileReadExecutor);
-
- /**
- * Reads the attributes of this file as {@link ResponseHeaders}, which could be useful for building
- * a response for a {@code HEAD} request.
- *
- * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file
- *
- * @return the {@link CompletableFuture} that will be completed with the headers.
- * It will be completed with {@code null} if the file does not exist.
- */
- CompletableFuture<@Nullable ResponseHeaders> readHeaders(Executor fileReadExecutor);
-
- /**
- * Starts to stream this file into the returned {@link HttpResponse}.
- *
- * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file
- * @param alloc the {@link ByteBufAllocator} which will allocate the buffers that hold the content of
- * the file
- * @return the {@link CompletableFuture} that will be completed with the response.
- * It will be completed with {@code null} if the file does not exist.
- */
- CompletableFuture<@Nullable HttpResponse> read(Executor fileReadExecutor, ByteBufAllocator alloc);
-
- /**
- * Converts this file into an {@link AggregatedHttpFile}.
- *
- * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file
- *
- * @return a {@link CompletableFuture} which will complete when the aggregation process is finished, or
- * a {@link CompletableFuture} successfully completed with {@code this}, if this file is already
- * an {@link AggregatedHttpFile}.
- */
- CompletableFuture aggregate(Executor fileReadExecutor);
-
- /**
- * (Advanced users only) Converts this file into an {@link AggregatedHttpFile}.
- * {@link AggregatedHttpFile#content()} will return a pooled {@link HttpData}, and the caller must
- * ensure to release it. If you don't know what this means, use {@link #aggregate(Executor)}.
- *
- * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file
- * @param alloc the {@link ByteBufAllocator} which will allocate the content buffer
- *
- * @return a {@link CompletableFuture} which will complete when the aggregation process is finished, or
- * a {@link CompletableFuture} successfully completed with {@code this}, if this file is already
- * an {@link AggregatedHttpFile}.
- *
- * @see PooledObjects
- */
- @UnstableApi
- CompletableFuture aggregateWithPooledObjects(Executor fileReadExecutor,
- ByteBufAllocator alloc);
-
- /**
- * Returns an {@link HttpService} which serves the file for {@code HEAD} and {@code GET} requests.
- */
- HttpService asService();
-}
diff --git a/zipkin-server/src/main/java/zipkin2/server/internal/eureka/ZipkinEurekaDiscoveryConfiguration.java b/zipkin-server/src/main/java/zipkin2/server/internal/eureka/ZipkinEurekaDiscoveryConfiguration.java
index 3582b6a641a..d3b44aef49a 100644
--- a/zipkin-server/src/main/java/zipkin2/server/internal/eureka/ZipkinEurekaDiscoveryConfiguration.java
+++ b/zipkin-server/src/main/java/zipkin2/server/internal/eureka/ZipkinEurekaDiscoveryConfiguration.java
@@ -13,6 +13,7 @@
*/
package zipkin2.server.internal.eureka;
+import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.eureka.EurekaUpdatingListener;
import com.linecorp.armeria.spring.ArmeriaServerConfigurator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
diff --git a/zipkin-server/src/main/java/zipkin2/server/internal/eureka/ZipkinEurekaDiscoveryProperties.java b/zipkin-server/src/main/java/zipkin2/server/internal/eureka/ZipkinEurekaDiscoveryProperties.java
index 22344b66587..2d562e0e5b1 100644
--- a/zipkin-server/src/main/java/zipkin2/server/internal/eureka/ZipkinEurekaDiscoveryProperties.java
+++ b/zipkin-server/src/main/java/zipkin2/server/internal/eureka/ZipkinEurekaDiscoveryProperties.java
@@ -14,12 +14,14 @@
package zipkin2.server.internal.eureka;
import com.linecorp.armeria.common.auth.BasicToken;
+import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.eureka.EurekaUpdatingListener;
import com.linecorp.armeria.server.eureka.EurekaUpdatingListenerBuilder;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import org.springframework.boot.context.properties.ConfigurationProperties;
+import zipkin2.storage.cassandra.internal.HostAndPort;
/**
* Settings for Eureka registration.
@@ -105,11 +107,17 @@ public void setHostname(String hostname) {
}
EurekaUpdatingListenerBuilder toBuilder() {
- EurekaUpdatingListenerBuilder result = EurekaUpdatingListener.builder(serviceUrl);
+ EurekaUpdatingListenerBuilder result = EurekaUpdatingListener.builder(serviceUrl)
+ .healthCheckUrlPath("/health");
if (auth != null) result.auth(auth);
if (appName != null) result.appName(appName);
if (instanceId != null) result.instanceId(instanceId);
- if (hostname != null) result.hostname(hostname);
+ if (hostname != null) {
+ result.hostname(hostname);
+ // Armeria defaults the vipAddress incorrectly to include the port. This is redundant with
+ // the server port, so override it until we have a PR to fix that.
+ result.vipAddress(hostname);
+ }
return result;
}
diff --git a/zipkin-server/src/main/resources/zipkin-server-shared.yml b/zipkin-server/src/main/resources/zipkin-server-shared.yml
index aa6e65e3e55..0ac443e1446 100644
--- a/zipkin-server/src/main/resources/zipkin-server-shared.yml
+++ b/zipkin-server/src/main/resources/zipkin-server-shared.yml
@@ -199,11 +199,6 @@ server:
min-response-size: 2048
armeria:
- # TODO: This is only to help with Eureka. After line/armeria#5369, add the
- # same in EurekaUpdatingListenerBuilder#toBuilder and remove from here. This
- # will remove the following log warning:
- # WARN RejectedRouteHandler - Virtual host '*' has a duplicate route: /health
- healthCheckPath: /health
ports:
- port: ${server.port}
protocols:
diff --git a/zipkin-server/src/test/java/zipkin2/server/internal/eureka/BaseITZipkinEureka.java b/zipkin-server/src/test/java/zipkin2/server/internal/eureka/BaseITZipkinEureka.java
index 5434787a56d..cc1a7c2882e 100644
--- a/zipkin-server/src/test/java/zipkin2/server/internal/eureka/BaseITZipkinEureka.java
+++ b/zipkin-server/src/test/java/zipkin2/server/internal/eureka/BaseITZipkinEureka.java
@@ -23,7 +23,6 @@
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
@@ -60,7 +59,8 @@
"zipkin.storage.type=", // cheat and test empty storage type
"zipkin.collector.http.enabled=false",
"zipkin.query.enabled=false",
- "zipkin.ui.enabled=false"
+ "zipkin.ui.enabled=false",
+ "zipkin.discovery.eureka.hostname=localhost"
})
@Tag("docker")
@Testcontainers(disabledWithoutDocker = true)
@@ -96,17 +96,16 @@ abstract class BaseITZipkinEureka {
assertThat(readString(json, "$.application.instance[0].status"))
.isEqualTo("UP");
- String zipkinHostname = zipkin.defaultHostname();
int zipkinPort = zipkin.activePort().localAddress().getPort();
// Note: Netflix/Eureka says use hostname, which can conflict on laptops.
// Armeria adopts the spring-cloud-netflix convention shown here.
assertThat(readString(json, "$.application.instance[0].instanceId"))
- .isEqualTo(zipkinHostname + ":zipkin:" + zipkinPort);
+ .isEqualTo("localhost:zipkin:" + zipkinPort);
- // Make sure the vip address is relevant
+ // Make sure the vip address does not include the port!
assertThat(readString(json, "$.application.instance[0].vipAddress"))
- .isEqualTo(zipkinHostname + ":" + zipkinPort);
+ .isEqualTo("localhost");
}
@Test @Order(2) void deregistersOnClose() {
diff --git a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/BulkCallBuilder.java b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/BulkCallBuilder.java
index 148c2019eba..b6e37996fa7 100644
--- a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/BulkCallBuilder.java
+++ b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/BulkCallBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2023 The OpenZipkin Authors
+ * Copyright 2015-2024 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
@@ -45,6 +45,7 @@
import static zipkin2.Call.propagateIfFatal;
import static zipkin2.elasticsearch.ElasticsearchVersion.V7_0;
import static zipkin2.elasticsearch.internal.JsonSerializers.OBJECT_MAPPER;
+import static zipkin2.elasticsearch.internal.client.HttpCall.maybeRootCauseReason;
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
// exposed to re-use for testing writes of dependency links
@@ -60,7 +61,7 @@ public final class BulkCallBuilder {
// only throw when we know it is an error
if (!root.at("/errors").booleanValue() && !root.at("/error").isObject()) return null;
- String message = root.findPath("reason").textValue();
+ String message = maybeRootCauseReason(root);
if (message == null) message = contentString.get();
Number status = root.findPath("status").numberValue();
if (status != null && status.intValue() == 429) {
diff --git a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/client/HttpCall.java b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/client/HttpCall.java
index 6f9dd13d128..629a2763c11 100644
--- a/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/client/HttpCall.java
+++ b/zipkin-storage/elasticsearch/src/main/java/zipkin2/elasticsearch/internal/client/HttpCall.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2023 The OpenZipkin Authors
+ * Copyright 2015-2024 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
@@ -233,7 +233,7 @@ V parseResponse(AggregatedHttpResponse response, BodyConverter bodyConverter)
String message = null;
try {
JsonNode root = OBJECT_MAPPER.readTree(parser);
- message = root.findPath("reason").textValue();
+ message = maybeRootCauseReason(root);
if (message == null) message = root.at("/Message").textValue();
} catch (RuntimeException | IOException possiblyParseException) {
// EmptyCatch ignored
@@ -252,4 +252,15 @@ V parseResponse(AggregatedHttpResponse response, BodyConverter bodyConverter)
return bodyConverter.convert(parser, content::toStringUtf8);
}
}
+
+ public static String maybeRootCauseReason(JsonNode root) {
+ // Prefer the root cause to an arbitrary reason.
+ String message;
+ if (!root.findPath("root_cause").isMissingNode()) {
+ message = root.findPath("root_cause").findPath("reason").textValue();
+ } else {
+ message = root.findPath("reason").textValue();
+ }
+ return message;
+ }
}
diff --git a/zipkin-storage/elasticsearch/src/test/java/zipkin2/elasticsearch/internal/BulkCallBuilderTest.java b/zipkin-storage/elasticsearch/src/test/java/zipkin2/elasticsearch/internal/BulkCallBuilderTest.java
index 6996cec416b..32c4a400ac0 100644
--- a/zipkin-storage/elasticsearch/src/test/java/zipkin2/elasticsearch/internal/BulkCallBuilderTest.java
+++ b/zipkin-storage/elasticsearch/src/test/java/zipkin2/elasticsearch/internal/BulkCallBuilderTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2023 The OpenZipkin Authors
+ * Copyright 2015-2024 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
@@ -32,13 +32,58 @@ class BulkCallBuilderTest {
"rejected execution of org.elasticsearch.transport.TransportService$7@7ec1ea93 on EsThreadPoolExecutor[bulk, queue capacity = 200, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@621571ba[Running, pool size = 4, active threads = 4, queued tasks = 200, completed tasks = 3838534]]");
}
- @Test void throwsRuntimeExceptionAsReasonWhenPresent() {
- String response =
- "{\"error\":{\"root_cause\":[{\"type\":\"illegal_argument_exception\",\"reason\":\"Fielddata is disabled on text fields by default. Set fielddata=true on [spanName] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.\"}],\"type\":\"search_phase_execution_exception\",\"reason\":\"all shards failed\",\"phase\":\"query\",\"grouped\":true,\"failed_shards\":[{\"shard\":0,\"index\":\"zipkin-2017-05-14\",\"node\":\"IqceAwZnSvyv0V0xALkEnQ\",\"reason\":{\"type\":\"illegal_argument_exception\",\"reason\":\"Fielddata is disabled on text fields by default. Set fielddata=true on [spanName] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.\"}}]},\"status\":400}";
+ @Test void throwsRuntimeExceptionAsRootCauseReasonWhenPresent() {
+ String response = """
+ {
+ "error": {
+ "root_cause": [
+ {
+ "type": "illegal_argument_exception",
+ "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [spanName] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
+ }
+ ],
+ "type": "search_phase_execution_exception",
+ "reason": "all shards failed",
+ "phase": "query",
+ "grouped": true,
+ "failed_shards": [
+ {
+ "shard": 0,
+ "index": "zipkin-2017-05-14",
+ "node": "IqceAwZnSvyv0V0xALkEnQ",
+ "reason": {
+ "type": "illegal_argument_exception",
+ "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [spanName] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
+ }
+ }
+ ]
+ },
+ "status": 400
+ }
+ """;
assertThatThrownBy(
() -> CHECK_FOR_ERRORS.convert(JSON_FACTORY.createParser(response), () -> response))
.isInstanceOf(RuntimeException.class)
.hasMessage("Fielddata is disabled on text fields by default. Set fielddata=true on [spanName] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.");
}
+
+ /** Tests lack of a root cause won't crash */
+ @Test void throwsRuntimeExceptionAsReasonWhenPresent() {
+ String response = """
+ {
+ "error": {
+ "type": "search_phase_execution_exception",
+ "reason": "all shards failed",
+ "phase": "query"
+ },
+ "status": 400
+ }
+ """;
+
+ assertThatThrownBy(
+ () -> CHECK_FOR_ERRORS.convert(JSON_FACTORY.createParser(response), () -> response))
+ .isInstanceOf(RuntimeException.class)
+ .hasMessage("all shards failed");
+ }
}