aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHåkon Hallingstad <hakon@yahooinc.com>2022-10-31 21:35:02 +0100
committerHåkon Hallingstad <hakon@yahooinc.com>2022-10-31 21:35:02 +0100
commit613464e11b5c9b478873014ccb1d629bd1ae91fe (patch)
treeb283d6a952b58653368898afce53dd06b1ede6ad
parent42d67fa4e4fd247296b1e99bcfe44e1fdcb50834 (diff)
New cores client in node-admin
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerClients.java4
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerException.java10
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/RealConfigServerClients.java9
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/StandardConfigServerResponse.java22
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoreDumpMetadata.java60
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/Cores.java16
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoresImpl.java37
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/bindings/ReportCoreDumpRequest.java27
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java32
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/NodeMessageResponse.java18
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/package-info.java5
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoresTest.java100
12 files changed, 297 insertions, 43 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerClients.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerClients.java
index 4a9f761a72f..1bf4120c071 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerClients.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerClients.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.node.admin.configserver;
import com.yahoo.vespa.flags.FlagRepository;
+import com.yahoo.vespa.hosted.node.admin.configserver.cores.Cores;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.configserver.orchestrator.Orchestrator;
import com.yahoo.vespa.hosted.node.admin.configserver.state.State;
@@ -24,5 +25,8 @@ public interface ConfigServerClients {
/** Get handle to the /flags/v1 REST API */
FlagRepository flagRepository();
+ /** Get handle to the /cores/v1 REST API */
+ Cores cores();
+
void stop();
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerException.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerException.java
new file mode 100644
index 00000000000..369a1c93dc4
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/ConfigServerException.java
@@ -0,0 +1,10 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.configserver;
+
+/**
+ * @author hakonhall
+ */
+public class ConfigServerException extends RuntimeException {
+ public ConfigServerException(String message) { super(message); }
+ public ConfigServerException(String message, Throwable cause) { super(message, cause); }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/RealConfigServerClients.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/RealConfigServerClients.java
index 6c650700432..4311a31816a 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/RealConfigServerClients.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/RealConfigServerClients.java
@@ -2,6 +2,8 @@
package com.yahoo.vespa.hosted.node.admin.configserver;
import com.yahoo.vespa.flags.FlagRepository;
+import com.yahoo.vespa.hosted.node.admin.configserver.cores.Cores;
+import com.yahoo.vespa.hosted.node.admin.configserver.cores.CoresImpl;
import com.yahoo.vespa.hosted.node.admin.configserver.flags.RealFlagRepository;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.NodeRepository;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.RealNodeRepository;
@@ -22,6 +24,7 @@ public class RealConfigServerClients implements ConfigServerClients {
private final Orchestrator orchestrator;
private final State state;
private final RealFlagRepository flagRepository;
+ private final Cores cores;
/**
* @param configServerApi the backend API to use - will be closed at {@link #stop()}.
@@ -32,6 +35,7 @@ public class RealConfigServerClients implements ConfigServerClients {
orchestrator = new OrchestratorImpl(configServerApi);
state = new StateImpl(configServerApi);
flagRepository = new RealFlagRepository(configServerApi);
+ cores = new CoresImpl(configServerApi);
}
@Override
@@ -55,6 +59,11 @@ public class RealConfigServerClients implements ConfigServerClients {
}
@Override
+ public Cores cores() {
+ return cores;
+ }
+
+ @Override
public void stop() {
configServerApi.close();
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/StandardConfigServerResponse.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/StandardConfigServerResponse.java
new file mode 100644
index 00000000000..0aa4af3fe22
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/StandardConfigServerResponse.java
@@ -0,0 +1,22 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.configserver;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Strings;
+
+/**
+ * @author hakonhall
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class StandardConfigServerResponse {
+ @JsonProperty("message") public String message;
+ @JsonProperty("error-code") public String errorCode;
+
+ public void throwOnError(String detail) {
+ if (!Strings.isNullOrEmpty(errorCode))
+ throw new ConfigServerException(detail + ": " + message + " " + errorCode);
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoreDumpMetadata.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoreDumpMetadata.java
new file mode 100644
index 00000000000..8ea39e37489
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoreDumpMetadata.java
@@ -0,0 +1,60 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.configserver.cores;
+
+import com.yahoo.component.Version;
+import com.yahoo.config.provision.DockerImage;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author hakonhall
+ */
+public class CoreDumpMetadata {
+ private String binPath;
+ private List<String> backtrace;
+ private List<String> backtraceAllThreads;
+ private Path coreDumpPath;
+ private String kernelVersion;
+ private String cpuMicrocodeVersion;
+ private DockerImage dockerImage;
+ private String vespaVersion;
+
+ public CoreDumpMetadata() {}
+
+ public Optional<String> binPath() { return Optional.ofNullable(binPath); };
+ public Optional<List<String>> backtrace() { return Optional.ofNullable(backtrace); };
+ public Optional<List<String>> backtraceAllThreads() { return Optional.ofNullable(backtraceAllThreads); };
+ public Optional<Path> coredumpPath() { return Optional.ofNullable(coreDumpPath); };
+ public Optional<String> kernelVersion() { return Optional.ofNullable(kernelVersion); };
+ public Optional<String> cpuMicrocodeVersion() { return Optional.ofNullable(cpuMicrocodeVersion); };
+ public Optional<DockerImage> dockerImage() { return Optional.ofNullable(dockerImage); };
+ public Optional<String> vespaVersion() { return Optional.ofNullable(vespaVersion); };
+
+ public CoreDumpMetadata setBinPath(String binPath) { this.binPath = binPath; return this; };
+ public CoreDumpMetadata setBacktrace(List<String> backtrace) { this.backtrace = backtrace; return this; };
+ public CoreDumpMetadata setBacktraceAllThreads(List<String> backtraceAllThreads) { this.backtraceAllThreads = backtraceAllThreads; return this; };
+ public CoreDumpMetadata setCoreDumpPath(Path coreDumpPath) { this.coreDumpPath = coreDumpPath; return this; };
+ public CoreDumpMetadata setKernelVersion(String kernelVersion) { this.kernelVersion = kernelVersion; return this; };
+ public CoreDumpMetadata setCpuMicrocodeVersion(String cpuMicrocodeVersion) { this.cpuMicrocodeVersion = cpuMicrocodeVersion; return this; };
+ public CoreDumpMetadata setDockerImage(DockerImage dockerImage) { this.dockerImage = dockerImage; return this; };
+ public CoreDumpMetadata setVespaVersion(String vespaVersion) { this.vespaVersion = vespaVersion; return this; };
+
+ @Override
+ public String toString() {
+ return "CoreDumpMetadata{" +
+ "binPath=" + binPath +
+ ", backtrace=" + backtrace +
+ ", backtraceAllThreads=" + backtraceAllThreads +
+ ", coreDumpPath=" + coreDumpPath +
+ ", kernelVersion='" + kernelVersion + '\'' +
+ ", cpuMicrocodeVersion='" + cpuMicrocodeVersion + '\'' +
+ ", dockerImage=" + dockerImage +
+ ", vespaVersion=" + vespaVersion +
+ '}';
+ }
+
+ @Override public boolean equals(Object o) { throw new UnsupportedOperationException(); }
+ @Override public int hashCode() { throw new UnsupportedOperationException(); }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/Cores.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/Cores.java
new file mode 100644
index 00000000000..6f72e4e9551
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/Cores.java
@@ -0,0 +1,16 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.configserver.cores;
+
+import com.yahoo.config.provision.HostName;
+
+/**
+ * @author hakonhall
+ */
+public interface Cores {
+ /**
+ * @param hostname Hostname of the node that produced the core.
+ * @param id The ID (aka UUID aka docid) of the core.
+ * @param metadata Core dump metadata.
+ */
+ void report(HostName hostname, String id, CoreDumpMetadata metadata);
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoresImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoresImpl.java
new file mode 100644
index 00000000000..f6393d3726c
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoresImpl.java
@@ -0,0 +1,37 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.configserver.cores;
+
+import com.yahoo.config.provision.HostName;
+import com.yahoo.vespa.hosted.node.admin.configserver.ConfigServerApi;
+import com.yahoo.vespa.hosted.node.admin.configserver.StandardConfigServerResponse;
+import com.yahoo.vespa.hosted.node.admin.configserver.cores.bindings.ReportCoreDumpRequest;
+
+import java.util.List;
+
+/**
+ * @author hakonhall
+ */
+public class CoresImpl implements Cores {
+ private final ConfigServerApi configServerApi;
+
+ public CoresImpl(ConfigServerApi configServerApi) {
+ this.configServerApi = configServerApi;
+ }
+
+ @Override
+ public void report(HostName hostname, String id, CoreDumpMetadata metadata) {
+ var request = new ReportCoreDumpRequest();
+ metadata.binPath().ifPresent(binPath -> request.bin_path = binPath);
+ metadata.backtrace().ifPresent(backtrace -> request.backtrace = List.copyOf(backtrace));
+ metadata.backtraceAllThreads().ifPresent(backtraceAllThreads -> request.backtrace_all_threads = List.copyOf(backtraceAllThreads));
+ metadata.coredumpPath().ifPresent(coredumpPath -> request.coredump_path = coredumpPath.toString());
+ metadata.kernelVersion().ifPresent(kernelVersion -> request.kernel_version = kernelVersion);
+ metadata.cpuMicrocodeVersion().ifPresent(cpuMicrocodeVersion -> request.cpu_microcode_version = cpuMicrocodeVersion);
+ metadata.dockerImage().ifPresent(dockerImage -> request.docker_image = dockerImage.asString());
+ metadata.vespaVersion().ifPresent(vespaVersion -> request.vespa_version = vespaVersion);
+
+ String uriPath = "/cores/v1/report/" + hostname.value() + "/" + id;
+ configServerApi.post(uriPath, request, StandardConfigServerResponse.class)
+ .throwOnError("Failed to report core dump at " + metadata.coredumpPath());
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/bindings/ReportCoreDumpRequest.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/bindings/ReportCoreDumpRequest.java
new file mode 100644
index 00000000000..f31c1e71bae
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/bindings/ReportCoreDumpRequest.java
@@ -0,0 +1,27 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.configserver.cores.bindings;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import java.util.List;
+
+/**
+ * Jackson class of JSON request, with names of fields verified in unit test.
+ *
+ * @author hakonhall
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ReportCoreDumpRequest {
+ public List<String> backtrace;
+ public List<String> backtrace_all_threads;
+ public String bin_path;
+ public String coredump_path;
+ public String cpu_microcode_version;
+ public String docker_image;
+ public String kernel_version;
+ public String vespa_version;
+
+ public ReportCoreDumpRequest() {}
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java
index b70ce491c73..a4a047b524c 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.hosted.node.admin.configserver.noderepository;
import com.fasterxml.jackson.databind.JsonNode;
-import com.google.common.base.Strings;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.DockerImage;
@@ -11,9 +10,9 @@ import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.host.FlavorOverrides;
import com.yahoo.vespa.hosted.node.admin.configserver.ConfigServerApi;
import com.yahoo.vespa.hosted.node.admin.configserver.HttpException;
+import com.yahoo.vespa.hosted.node.admin.configserver.StandardConfigServerResponse;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings.GetAclResponse;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings.GetNodesResponse;
-import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings.NodeMessageResponse;
import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings.NodeRepositoryNode;
import java.net.URI;
@@ -48,9 +47,8 @@ public class RealNodeRepository implements NodeRepository {
.map(RealNodeRepository::nodeRepositoryNodeFromAddNode)
.collect(Collectors.toList());
- NodeMessageResponse response = configServerApi.post("/nodes/v2/node", nodesToPost, NodeMessageResponse.class);
- if (Strings.isNullOrEmpty(response.errorCode)) return;
- throw new NodeRepositoryException("Failed to add nodes: " + response.message + " " + response.errorCode);
+ configServerApi.post("/nodes/v2/node", nodesToPost, StandardConfigServerResponse.class)
+ .throwOnError("Failed to add nodes");
}
@Override
@@ -119,26 +117,20 @@ public class RealNodeRepository implements NodeRepository {
@Override
public void updateNodeAttributes(String hostName, NodeAttributes nodeAttributes) {
- NodeMessageResponse response = configServerApi.patch(
- "/nodes/v2/node/" + hostName,
- nodeRepositoryNodeFromNodeAttributes(nodeAttributes),
- NodeMessageResponse.class);
-
- if (Strings.isNullOrEmpty(response.errorCode)) return;
- throw new NodeRepositoryException("Failed to update node attributes: " + response.message + " " + response.errorCode);
+ configServerApi.patch("/nodes/v2/node/" + hostName,
+ nodeRepositoryNodeFromNodeAttributes(nodeAttributes),
+ StandardConfigServerResponse.class)
+ .throwOnError("Failed to update node attributes");
}
@Override
public void setNodeState(String hostName, NodeState nodeState) {
String state = nodeState.name();
- NodeMessageResponse response = configServerApi.put(
- "/nodes/v2/state/" + state + "/" + hostName,
- Optional.empty(), /* body */
- NodeMessageResponse.class);
+ StandardConfigServerResponse response = configServerApi.put("/nodes/v2/state/" + state + "/" + hostName,
+ Optional.empty(), /* body */
+ StandardConfigServerResponse.class);
logger.info(response.message);
-
- if (Strings.isNullOrEmpty(response.errorCode)) return;
- throw new NodeRepositoryException("Failed to set node state: " + response.message + " " + response.errorCode);
+ response.throwOnError("Failed to set node state");
}
private static NodeSpec createNodeSpec(NodeRepositoryNode node) {
@@ -153,7 +145,7 @@ public class RealNodeRepository implements NodeRepository {
NodeReports reports = NodeReports.fromMap(Optional.ofNullable(node.reports).orElseGet(Map::of));
List<Event> events = node.history.stream()
.map(event -> new Event(event.agent, event.event, Optional.ofNullable(event.at).map(Instant::ofEpochMilli).orElse(Instant.EPOCH)))
- .collect(Collectors.toUnmodifiableList());
+ .toList();
List<TrustStoreItem> trustStore = Optional.ofNullable(node.trustStore).orElse(List.of()).stream()
.map(item -> new TrustStoreItem(item.fingerprint, Instant.ofEpochMilli(item.expiry)))
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/NodeMessageResponse.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/NodeMessageResponse.java
deleted file mode 100644
index f4c6181f219..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/NodeMessageResponse.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * Response from PUT /nodes/v2/state/ call to node-repository.
- *
- * @author dybis
- */
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class NodeMessageResponse {
- @JsonProperty("message")
- public String message;
- @JsonProperty("error-code")
- public String errorCode;
-}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/package-info.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/package-info.java
deleted file mode 100644
index 6e529fe8d77..00000000000
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/package-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-@ExportPackage
-package com.yahoo.vespa.hosted.node.admin;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoresTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoresTest.java
new file mode 100644
index 00000000000..b9ecbdc0837
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoresTest.java
@@ -0,0 +1,100 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.configserver.cores;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yahoo.config.provision.DockerImage;
+import com.yahoo.config.provision.HostName;
+import com.yahoo.test.json.JsonTestHelper;
+import com.yahoo.vespa.hosted.node.admin.configserver.ConfigServerApi;
+import com.yahoo.vespa.hosted.node.admin.configserver.ConfigServerException;
+import com.yahoo.vespa.hosted.node.admin.configserver.StandardConfigServerResponse;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.nio.file.Path;
+import java.util.List;
+
+import static com.yahoo.yolean.Exceptions.uncheck;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author hakonhall
+ */
+class CoresTest {
+ private final ObjectMapper mapper = new ObjectMapper();
+ private final ConfigServerApi configServerApi = mock(ConfigServerApi.class);
+ private final Cores cores = new CoresImpl(configServerApi);
+ private final HostName hostname = HostName.of("foo.com");
+ private final String id = "5c987afb-347a-49ee-a0c5-bef56bbddeb0";
+ private final CoreDumpMetadata metadata = new CoreDumpMetadata()
+ .setKernelVersion("4.18.0-372.26.1.el8_6.x86_64")
+ .setCpuMicrocodeVersion("0x1000065")
+ .setCoreDumpPath(Path.of("/data/vespa/processed-coredumps/h7641a/5c987afb-347a-49ee-a0c5-bef56bbddeb0/dump_java.core.813"))
+ .setDockerImage(DockerImage.fromString("us-central1-docker.pkg.dev/vespa-external-cd/vespa-cloud/vespa/cloud-tenant-rhel8:8.68.8"))
+ .setBinPath("/usr/bin/java")
+ .setVespaVersion("8.68.8")
+ .setBacktraceAllThreads(List.of("Attaching to core /opt/vespa/var/crash/processing/5c987afb-347a-49ee-a0c5-bef56bbddeb0/dump_java.core.813 from executable /usr/bin/java, please wait...",
+ "Debugger attached successfully.",
+ " - com.yahoo.jdisc.core.TimeoutManagerImpl$ManagerTask.run() @bci=3, line=123 (Interpreted frame)",
+ " - java.lang.Thread.run() @bci=11, line=833 (Interpreted frame)"))
+ .setBacktrace(List.of("Example", "of", "backtrace"));
+
+ @Test
+ void reportOK() {
+ var oKResponse = new StandardConfigServerResponse();
+ oKResponse.message = "OK";
+ when(configServerApi.post(any(), any(), any())).thenReturn(oKResponse);
+
+ cores.report(hostname, id, metadata);
+
+ var pathCaptor = ArgumentCaptor.forClass(String.class);
+ var bodyJsonPojoCaptor = ArgumentCaptor.forClass(Object.class);
+ verify(configServerApi, times(1)).post(pathCaptor.capture(), bodyJsonPojoCaptor.capture(), any());
+
+ assertEquals("/cores/v1/report/" + hostname + "/" + id, pathCaptor.getValue());
+
+ assertEquals("""
+ {
+ "backtrace": [
+ "Example",
+ "of",
+ "backtrace"
+ ],
+ "backtrace_all_threads": [
+ "Attaching to core /opt/vespa/var/crash/processing/5c987afb-347a-49ee-a0c5-bef56bbddeb0/dump_java.core.813 from executable /usr/bin/java, please wait...",
+ "Debugger attached successfully.",
+ " - com.yahoo.jdisc.core.TimeoutManagerImpl$ManagerTask.run() @bci=3, line=123 (Interpreted frame)",
+ " - java.lang.Thread.run() @bci=11, line=833 (Interpreted frame)"
+ ],
+ "bin_path": "/usr/bin/java",
+ "coredump_path": "/data/vespa/processed-coredumps/h7641a/5c987afb-347a-49ee-a0c5-bef56bbddeb0/dump_java.core.813",
+ "cpu_microcode_version": "0x1000065",
+ "docker_image": "us-central1-docker.pkg.dev/vespa-external-cd/vespa-cloud/vespa/cloud-tenant-rhel8:8.68.8",
+ "kernel_version": "4.18.0-372.26.1.el8_6.x86_64",
+ "vespa_version": "8.68.8"
+ }""",
+ JsonTestHelper.normalize(uncheck(() -> mapper.writeValueAsString(bodyJsonPojoCaptor.getValue()))));
+ }
+
+ @Test
+ void reportFails() {
+ var response = new StandardConfigServerResponse();
+ response.errorCode = "503";
+ response.message = "error detail";
+ when(configServerApi.post(any(), any(), any())).thenReturn(response);
+
+ assertThrows(ConfigServerException.class,
+ () -> cores.report(hostname, "abcde-1234", metadata),
+ "Failed to report core dump at Optional[/data/vespa/processed-coredumps/h7641a/5c987afb-347a-49ee-a0c5-bef56bbddeb0/dump_java.core.813]: error detail 503");
+
+ var pathCaptor = ArgumentCaptor.forClass(String.class);
+ var bodyJsonPojoCaptor = ArgumentCaptor.forClass(Object.class);
+ verify(configServerApi).post(pathCaptor.capture(), bodyJsonPojoCaptor.capture(), any());
+ }
+} \ No newline at end of file