Fix kubernetes client latest dep tests (#10433)
This commit is contained in:
parent
1f5fe2fdc3
commit
b3d2aaf855
|
@ -14,9 +14,29 @@ muzzle {
|
||||||
dependencies {
|
dependencies {
|
||||||
library("io.kubernetes:client-java-api:7.0.0")
|
library("io.kubernetes:client-java-api:7.0.0")
|
||||||
|
|
||||||
implementation(project(":instrumentation:okhttp:okhttp-3.0:javaagent"))
|
|
||||||
|
|
||||||
testInstrumentation(project(":instrumentation:okhttp:okhttp-3.0:javaagent"))
|
testInstrumentation(project(":instrumentation:okhttp:okhttp-3.0:javaagent"))
|
||||||
|
|
||||||
|
latestDepTestLibrary("io.kubernetes:client-java-api:19.+")
|
||||||
|
}
|
||||||
|
|
||||||
|
testing {
|
||||||
|
suites {
|
||||||
|
val version20Test by registering(JvmTestSuite::class) {
|
||||||
|
dependencies {
|
||||||
|
if (findProperty("testLatestDeps") as Boolean) {
|
||||||
|
implementation("io.kubernetes:client-java-api:+")
|
||||||
|
} else {
|
||||||
|
implementation("io.kubernetes:client-java-api:20.0.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
check {
|
||||||
|
dependsOn(testing.suites)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Test>().configureEach {
|
tasks.withType<Test>().configureEach {
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class ApiClientInstrumentation implements TypeInstrumentation {
|
||||||
@Override
|
@Override
|
||||||
public void transform(TypeTransformer transformer) {
|
public void transform(TypeTransformer transformer) {
|
||||||
transformer.applyAdviceToMethod(
|
transformer.applyAdviceToMethod(
|
||||||
isPublic().and(named("buildRequest")).and(takesArguments(10)),
|
isPublic().and(named("buildRequest")).and(takesArguments(10).or(takesArguments(11))),
|
||||||
this.getClass().getName() + "$BuildRequestAdvice");
|
this.getClass().getName() + "$BuildRequestAdvice");
|
||||||
transformer.applyAdviceToMethod(
|
transformer.applyAdviceToMethod(
|
||||||
isPublic()
|
isPublic()
|
||||||
|
|
|
@ -89,7 +89,7 @@ class KubernetesRequestDigest {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (isNonResourceRequest) {
|
if (isNonResourceRequest) {
|
||||||
return verb.value() + ' ' + urlPath;
|
return (verb != null ? verb.value() + ' ' : "") + urlPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
String groupVersion;
|
String groupVersion;
|
||||||
|
|
|
@ -12,7 +12,7 @@ class KubernetesResource {
|
||||||
|
|
||||||
public static final Pattern CORE_RESOURCE_URL_PATH_PATTERN =
|
public static final Pattern CORE_RESOURCE_URL_PATH_PATTERN =
|
||||||
Pattern.compile(
|
Pattern.compile(
|
||||||
"^/api/v1(/namespaces/(?<namespace>[\\w-]+))?/(?<resource>[\\w-]+)(/(?<name>[\\w-]+))?(/(?<subresource>[\\w-]+))?");
|
"^/api/v1(/namespaces/(?<namespace>[\\w-]+))?/(?<resource>[\\w-]+)(/(?<name>[\\w-]+))?(/(?<subresource>[\\w-]+))?(/.*)?");
|
||||||
|
|
||||||
public static final Pattern REGULAR_RESOURCE_URL_PATH_PATTERN =
|
public static final Pattern REGULAR_RESOURCE_URL_PATH_PATTERN =
|
||||||
Pattern.compile(
|
Pattern.compile(
|
||||||
|
|
|
@ -0,0 +1,276 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.kubernetesclient;
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanName;
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import io.kubernetes.client.openapi.ApiCallback;
|
||||||
|
import io.kubernetes.client.openapi.ApiClient;
|
||||||
|
import io.kubernetes.client.openapi.ApiException;
|
||||||
|
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||||
|
import io.opentelemetry.api.common.AttributeKey;
|
||||||
|
import io.opentelemetry.api.trace.SpanKind;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.sdk.trace.data.StatusData;
|
||||||
|
import io.opentelemetry.semconv.SemanticAttributes;
|
||||||
|
import io.opentelemetry.testing.internal.armeria.common.HttpResponse;
|
||||||
|
import io.opentelemetry.testing.internal.armeria.common.HttpStatus;
|
||||||
|
import io.opentelemetry.testing.internal.armeria.common.MediaType;
|
||||||
|
import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class KubernetesClientVer20Test {
|
||||||
|
|
||||||
|
private static final String TEST_USER_AGENT = "test-user-agent";
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||||
|
|
||||||
|
private final MockWebServerExtension mockWebServer = new MockWebServerExtension();
|
||||||
|
|
||||||
|
private CoreV1Api coreV1Api;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach() {
|
||||||
|
mockWebServer.start();
|
||||||
|
ApiClient apiClient = new ApiClient();
|
||||||
|
apiClient.setUserAgent(TEST_USER_AGENT);
|
||||||
|
apiClient.setBasePath(mockWebServer.httpUri().toString());
|
||||||
|
coreV1Api = new CoreV1Api(apiClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void afterEach() {
|
||||||
|
mockWebServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void synchronousCall() throws ApiException {
|
||||||
|
mockWebServer.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "42"));
|
||||||
|
String response =
|
||||||
|
testing.runWithSpan(
|
||||||
|
"parent",
|
||||||
|
() ->
|
||||||
|
coreV1Api
|
||||||
|
.connectGetNamespacedPodProxyWithPath("name", "namespace", "path")
|
||||||
|
.execute());
|
||||||
|
|
||||||
|
assertThat(response).isEqualTo("42");
|
||||||
|
assertThat(mockWebServer.takeRequest().request().headers().get("traceparent")).isNotBlank();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||||
|
span ->
|
||||||
|
span.hasName("get pods/proxy")
|
||||||
|
.hasKind(SpanKind.CLIENT)
|
||||||
|
.hasParent(trace.getSpan(0))
|
||||||
|
.hasAttributesSatisfyingExactly(
|
||||||
|
equalTo(
|
||||||
|
SemanticAttributes.URL_FULL,
|
||||||
|
mockWebServer.httpUri()
|
||||||
|
+ "/api/v1/namespaces/namespace/pods/name/proxy/path"),
|
||||||
|
equalTo(SemanticAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, 200),
|
||||||
|
equalTo(SemanticAttributes.SERVER_ADDRESS, "127.0.0.1"),
|
||||||
|
equalTo(SemanticAttributes.SERVER_PORT, mockWebServer.httpPort()),
|
||||||
|
equalTo(
|
||||||
|
AttributeKey.stringKey("kubernetes-client.namespace"), "namespace"),
|
||||||
|
equalTo(AttributeKey.stringKey("kubernetes-client.name"), "name"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void handleErrorsInSyncCall() {
|
||||||
|
mockWebServer.enqueue(
|
||||||
|
HttpResponse.of(HttpStatus.valueOf(451), MediaType.PLAIN_TEXT_UTF_8, "42"));
|
||||||
|
ApiException exception = null;
|
||||||
|
try {
|
||||||
|
testing.runWithSpan(
|
||||||
|
"parent",
|
||||||
|
() -> {
|
||||||
|
coreV1Api.connectGetNamespacedPodProxyWithPath("name", "namespace", "path").execute();
|
||||||
|
});
|
||||||
|
} catch (ApiException e) {
|
||||||
|
exception = e;
|
||||||
|
}
|
||||||
|
ApiException apiException = exception;
|
||||||
|
assertThat(apiException).isNotNull();
|
||||||
|
assertThat(mockWebServer.takeRequest().request().headers().get("traceparent")).isNotBlank();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span ->
|
||||||
|
span.hasName("parent")
|
||||||
|
.hasKind(SpanKind.INTERNAL)
|
||||||
|
.hasNoParent()
|
||||||
|
.hasStatus(StatusData.error())
|
||||||
|
.hasException(apiException),
|
||||||
|
span ->
|
||||||
|
span.hasName("get pods/proxy")
|
||||||
|
.hasKind(SpanKind.CLIENT)
|
||||||
|
.hasParent(trace.getSpan(0))
|
||||||
|
.hasStatus(StatusData.error())
|
||||||
|
.hasException(apiException)
|
||||||
|
.hasAttributesSatisfyingExactly(
|
||||||
|
equalTo(
|
||||||
|
SemanticAttributes.URL_FULL,
|
||||||
|
mockWebServer.httpUri()
|
||||||
|
+ "/api/v1/namespaces/namespace/pods/name/proxy/path"),
|
||||||
|
equalTo(SemanticAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, 451),
|
||||||
|
equalTo(SemanticAttributes.SERVER_ADDRESS, "127.0.0.1"),
|
||||||
|
equalTo(SemanticAttributes.SERVER_PORT, mockWebServer.httpPort()),
|
||||||
|
equalTo(SemanticAttributes.ERROR_TYPE, "451"),
|
||||||
|
equalTo(
|
||||||
|
AttributeKey.stringKey("kubernetes-client.namespace"), "namespace"),
|
||||||
|
equalTo(AttributeKey.stringKey("kubernetes-client.name"), "name"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asynchronousCall() throws ApiException, InterruptedException {
|
||||||
|
mockWebServer.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "42"));
|
||||||
|
|
||||||
|
AtomicReference<String> responseBodyReference = new AtomicReference<>();
|
||||||
|
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
testing.runWithSpan(
|
||||||
|
"parent",
|
||||||
|
() -> {
|
||||||
|
coreV1Api
|
||||||
|
.connectGetNamespacedPodProxyWithPath("name", "namespace", "path")
|
||||||
|
.executeAsync(
|
||||||
|
new ApiCallbackTemplate() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(
|
||||||
|
String result, int statusCode, Map<String, List<String>> responseHeaders) {
|
||||||
|
responseBodyReference.set(result);
|
||||||
|
countDownLatch.countDown();
|
||||||
|
testing.runWithSpan("callback", () -> {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
countDownLatch.await();
|
||||||
|
|
||||||
|
assertThat(responseBodyReference.get()).isEqualTo("42");
|
||||||
|
assertThat(mockWebServer.takeRequest().request().headers().get("traceparent")).isNotBlank();
|
||||||
|
|
||||||
|
testing.waitAndAssertSortedTraces(
|
||||||
|
orderByRootSpanName("parent", "get pods/proxy", "callback"),
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||||
|
span ->
|
||||||
|
span.hasName("get pods/proxy")
|
||||||
|
.hasKind(SpanKind.CLIENT)
|
||||||
|
.hasParent(trace.getSpan(0))
|
||||||
|
.hasAttributesSatisfyingExactly(
|
||||||
|
equalTo(
|
||||||
|
SemanticAttributes.URL_FULL,
|
||||||
|
mockWebServer.httpUri()
|
||||||
|
+ "/api/v1/namespaces/namespace/pods/name/proxy/path"),
|
||||||
|
equalTo(SemanticAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, 200),
|
||||||
|
equalTo(SemanticAttributes.SERVER_ADDRESS, "127.0.0.1"),
|
||||||
|
equalTo(SemanticAttributes.SERVER_PORT, mockWebServer.httpPort()),
|
||||||
|
equalTo(
|
||||||
|
AttributeKey.stringKey("kubernetes-client.namespace"), "namespace"),
|
||||||
|
equalTo(AttributeKey.stringKey("kubernetes-client.name"), "name")),
|
||||||
|
span ->
|
||||||
|
span.hasName("callback")
|
||||||
|
.hasKind(SpanKind.INTERNAL)
|
||||||
|
.hasParent(trace.getSpan(0))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void handleErrorsInAsynchronousCall() throws ApiException, InterruptedException {
|
||||||
|
|
||||||
|
mockWebServer.enqueue(
|
||||||
|
HttpResponse.of(HttpStatus.valueOf(451), MediaType.PLAIN_TEXT_UTF_8, "42"));
|
||||||
|
|
||||||
|
AtomicReference<ApiException> exceptionReference = new AtomicReference<>();
|
||||||
|
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
testing.runWithSpan(
|
||||||
|
"parent",
|
||||||
|
() -> {
|
||||||
|
coreV1Api
|
||||||
|
.connectGetNamespacedPodProxyWithPath("name", "namespace", "path")
|
||||||
|
.executeAsync(
|
||||||
|
new ApiCallbackTemplate() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(
|
||||||
|
ApiException e, int statusCode, Map<String, List<String>> responseHeaders) {
|
||||||
|
exceptionReference.set(e);
|
||||||
|
countDownLatch.countDown();
|
||||||
|
testing.runWithSpan("callback", () -> {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
countDownLatch.await();
|
||||||
|
|
||||||
|
assertThat(exceptionReference.get()).isNotNull();
|
||||||
|
assertThat(mockWebServer.takeRequest().request().headers().get("traceparent")).isNotBlank();
|
||||||
|
|
||||||
|
testing.waitAndAssertSortedTraces(
|
||||||
|
orderByRootSpanName("parent", "get pods/proxy", "callback"),
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||||
|
span ->
|
||||||
|
span.hasName("get pods/proxy")
|
||||||
|
.hasKind(SpanKind.CLIENT)
|
||||||
|
.hasParent(trace.getSpan(0))
|
||||||
|
.hasStatus(StatusData.error())
|
||||||
|
.hasException(exceptionReference.get())
|
||||||
|
.hasAttributesSatisfyingExactly(
|
||||||
|
equalTo(
|
||||||
|
SemanticAttributes.URL_FULL,
|
||||||
|
mockWebServer.httpUri()
|
||||||
|
+ "/api/v1/namespaces/namespace/pods/name/proxy/path"),
|
||||||
|
equalTo(SemanticAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, 451),
|
||||||
|
equalTo(SemanticAttributes.SERVER_ADDRESS, "127.0.0.1"),
|
||||||
|
equalTo(SemanticAttributes.SERVER_PORT, mockWebServer.httpPort()),
|
||||||
|
equalTo(SemanticAttributes.ERROR_TYPE, "451"),
|
||||||
|
equalTo(
|
||||||
|
AttributeKey.stringKey("kubernetes-client.namespace"), "namespace"),
|
||||||
|
equalTo(AttributeKey.stringKey("kubernetes-client.name"), "name")),
|
||||||
|
span ->
|
||||||
|
span.hasName("callback")
|
||||||
|
.hasKind(SpanKind.INTERNAL)
|
||||||
|
.hasParent(trace.getSpan(0))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ApiCallbackTemplate implements ApiCallback<String> {
|
||||||
|
@Override
|
||||||
|
public void onFailure(
|
||||||
|
ApiException e, int statusCode, Map<String, List<String>> responseHeaders) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(
|
||||||
|
String result, int statusCode, Map<String, List<String>> responseHeaders) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUploadProgress(long bytesWritten, long contentLength, boolean done) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadProgress(long bytesRead, long contentLength, boolean done) {}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue