Healthcheck (#715)

* Add a pid-path parameter to write a pid file that can be used as a healthcheck

Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com>

* Add healthcheck to the Dockerfile

Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com>

* fix assertj use

Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com>

* Fix unit tests

Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com>
This commit is contained in:
Antoine Toulme
2020-04-19 16:01:49 -07:00
committed by GitHub
parent 74940d507e
commit 1d493597f7
7 changed files with 55 additions and 2 deletions

View File

@@ -29,7 +29,11 @@ import org.hyperledger.besu.nat.NatService;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
@@ -53,6 +57,7 @@ public class Runner implements AutoCloseable {
private final NetworkRunner networkRunner;
private final NatService natService;
private final Optional<Path> pidPath;
private final Optional<JsonRpcHttpService> jsonRpc;
private final Optional<GraphQLHttpService> graphQLHttp;
private final Optional<WebSocketService> websocketRpc;
@@ -75,12 +80,14 @@ public class Runner implements AutoCloseable {
final Optional<MetricsService> metrics,
final BesuController<?> besuController,
final Path dataDir,
final Optional<Path> pidPath,
final Optional<TransactionLogBloomCacher> transactionLogBloomCacher,
final Blockchain blockchain) {
this.vertx = vertx;
this.networkRunner = networkRunner;
this.natService = natService;
this.graphQLHttp = graphQLHttp;
this.pidPath = pidPath;
this.jsonRpc = jsonRpc;
this.websocketRpc = websocketRpc;
this.metrics = metrics;
@@ -114,6 +121,7 @@ public class Runner implements AutoCloseable {
writeBesuPortsToFile();
writeBesuNetworksToFile();
autoTransactionLogBloomCachingService.ifPresent(AutoTransactionLogBloomCachingService::start);
writePidFile();
} catch (final Exception ex) {
LOG.error("Startup failed", ex);
throw new IllegalStateException(ex);
@@ -256,6 +264,28 @@ public class Runner implements AutoCloseable {
"This file contains the IP Addresses (global and local) used by the running instance of Besu");
}
private void writePidFile() {
pidPath.ifPresent(
path -> {
String pid = "";
try {
pid = Long.toString(ProcessHandle.current().pid());
} catch (Throwable t) {
}
try {
Files.write(
path,
pid.getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE);
path.toFile().deleteOnExit();
} catch (IOException e) {
LOG.error("Error writing PID file", e);
}
});
}
public Optional<Integer> getJsonRpcPort() {
return jsonRpc.map(service -> service.socketAddress().getPort());
}

View File

@@ -143,6 +143,7 @@ public class RunnerBuilder {
private GraphQLConfiguration graphQLConfiguration;
private WebSocketConfiguration webSocketConfiguration;
private Path dataDir;
private Optional<Path> pidPath = Optional.empty();
private MetricsConfiguration metricsConfiguration;
private ObservableMetricsSystem metricsSystem;
private Optional<PermissioningConfiguration> permissioningConfiguration = Optional.empty();
@@ -241,6 +242,11 @@ public class RunnerBuilder {
return this;
}
public RunnerBuilder pidPath(final Path pidPath) {
this.pidPath = Optional.ofNullable(pidPath);
return this;
}
public RunnerBuilder dataDir(final Path dataDir) {
this.dataDir = dataDir;
return this;
@@ -546,6 +552,7 @@ public class RunnerBuilder {
metricsService,
besuController,
dataDir,
pidPath,
autoLogBloomCaching ? blockchainQueries.getTransactionLogBloomCacher() : Optional.empty(),
context.getBlockchain());
}

View File

@@ -848,6 +848,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private final Integer pruningBlockConfirmations =
PrunerConfiguration.DEFAULT_PRUNING_BLOCK_CONFIRMATIONS;
@CommandLine.Option(
names = {"--pid-path"},
paramLabel = MANDATORY_PATH_FORMAT_HELP,
description = "Path to PID file (optional)")
private final Path pidPath = null;
private EthNetworkConfig ethNetworkConfig;
private JsonRpcConfiguration jsonRpcConfiguration;
private GraphQLConfiguration graphQLConfiguration;
@@ -1068,7 +1074,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
webSocketConfiguration,
metricsConfiguration,
permissioningConfiguration,
staticNodes);
staticNodes,
pidPath);
}
private BesuCommand startPlugins() {
@@ -1727,7 +1734,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final WebSocketConfiguration webSocketConfiguration,
final MetricsConfiguration metricsConfiguration,
final Optional<PermissioningConfiguration> permissioningConfiguration,
final Collection<EnodeURL> staticNodes) {
final Collection<EnodeURL> staticNodes,
final Path pidPath) {
checkNotNull(runnerBuilder);
@@ -1753,6 +1761,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.graphQLConfiguration(graphQLConfiguration)
.jsonRpcConfiguration(jsonRpcConfiguration)
.webSocketConfiguration(webSocketConfiguration)
.pidPath(pidPath)
.dataDir(dataDir())
.bannedNodeIds(bannedNodeIds)
.metricsSystem(metricsSystem)

View File

@@ -193,6 +193,7 @@ public final class RunnerTest {
final GraphQLConfiguration aheadGraphQLConfiguration = graphQLConfiguration();
final WebSocketConfiguration aheadWebSocketConfiguration = wsRpcConfiguration();
final MetricsConfiguration aheadMetricsConfiguration = metricsConfiguration();
final Path pidPath = temp.getRoot().toPath().resolve("pid");
final RunnerBuilder runnerBuilder =
new RunnerBuilder()
.vertx(vertx)
@@ -213,11 +214,13 @@ public final class RunnerTest {
.webSocketConfiguration(aheadWebSocketConfiguration)
.metricsConfiguration(aheadMetricsConfiguration)
.dataDir(dbAhead)
.pidPath(pidPath)
.besuPluginContext(new BesuPluginContextImpl())
.build();
try {
runnerAhead.start();
assertThat(pidPath.toFile().exists()).isTrue();
final SynchronizerConfiguration syncConfigBehind =
SynchronizerConfiguration.builder()

View File

@@ -223,6 +223,7 @@ public abstract class CommandTestAbstract {
when(mockRunnerBuilder.identityString(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.besuPluginContext(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.autoLogBloomCaching(anyBoolean())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.pidPath(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.build()).thenReturn(mockRunner);
lenient()

View File

@@ -12,6 +12,7 @@
data-path="~/besudata"
logging="INFO"
node-private-key-file="./path/to/privateKey"
pid-path="~/.pid"
# P2P network
identity="PegaSysEng"

View File

@@ -15,9 +15,11 @@ EXPOSE 8545 8546 8547 30303
ENV BESU_RPC_HTTP_HOST 0.0.0.0
ENV BESU_RPC_WS_HOST 0.0.0.0
ENV BESU_GRAPHQL_HTTP_HOST 0.0.0.0
ENV BESU_PID_PATH "/tmp/pid"
ENV PATH="/opt/besu/bin:${PATH}"
ENTRYPOINT ["besu"]
HEALTHCHECK --start-period=5s --interval=5s --timeout=1s --retries=10 CMD bash -c "[ -f /tmp/pid ]"
# Build-time metadata as defined at http://label-schema.org
ARG BUILD_DATE