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>
pull/751/head
Antoine Toulme 5 years ago committed by GitHub
parent 74940d507e
commit 1d493597f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      besu/src/main/java/org/hyperledger/besu/Runner.java
  2. 7
      besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
  3. 13
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  4. 3
      besu/src/test/java/org/hyperledger/besu/RunnerTest.java
  5. 1
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  6. 1
      besu/src/test/resources/everything_config.toml
  7. 2
      docker/Dockerfile

@ -29,7 +29,11 @@ import org.hyperledger.besu.nat.NatService;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; 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.Path;
import java.nio.file.StandardOpenOption;
import java.util.Optional; import java.util.Optional;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -53,6 +57,7 @@ public class Runner implements AutoCloseable {
private final NetworkRunner networkRunner; private final NetworkRunner networkRunner;
private final NatService natService; private final NatService natService;
private final Optional<Path> pidPath;
private final Optional<JsonRpcHttpService> jsonRpc; private final Optional<JsonRpcHttpService> jsonRpc;
private final Optional<GraphQLHttpService> graphQLHttp; private final Optional<GraphQLHttpService> graphQLHttp;
private final Optional<WebSocketService> websocketRpc; private final Optional<WebSocketService> websocketRpc;
@ -75,12 +80,14 @@ public class Runner implements AutoCloseable {
final Optional<MetricsService> metrics, final Optional<MetricsService> metrics,
final BesuController<?> besuController, final BesuController<?> besuController,
final Path dataDir, final Path dataDir,
final Optional<Path> pidPath,
final Optional<TransactionLogBloomCacher> transactionLogBloomCacher, final Optional<TransactionLogBloomCacher> transactionLogBloomCacher,
final Blockchain blockchain) { final Blockchain blockchain) {
this.vertx = vertx; this.vertx = vertx;
this.networkRunner = networkRunner; this.networkRunner = networkRunner;
this.natService = natService; this.natService = natService;
this.graphQLHttp = graphQLHttp; this.graphQLHttp = graphQLHttp;
this.pidPath = pidPath;
this.jsonRpc = jsonRpc; this.jsonRpc = jsonRpc;
this.websocketRpc = websocketRpc; this.websocketRpc = websocketRpc;
this.metrics = metrics; this.metrics = metrics;
@ -114,6 +121,7 @@ public class Runner implements AutoCloseable {
writeBesuPortsToFile(); writeBesuPortsToFile();
writeBesuNetworksToFile(); writeBesuNetworksToFile();
autoTransactionLogBloomCachingService.ifPresent(AutoTransactionLogBloomCachingService::start); autoTransactionLogBloomCachingService.ifPresent(AutoTransactionLogBloomCachingService::start);
writePidFile();
} catch (final Exception ex) { } catch (final Exception ex) {
LOG.error("Startup failed", ex); LOG.error("Startup failed", ex);
throw new IllegalStateException(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"); "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() { public Optional<Integer> getJsonRpcPort() {
return jsonRpc.map(service -> service.socketAddress().getPort()); return jsonRpc.map(service -> service.socketAddress().getPort());
} }

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

@ -848,6 +848,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private final Integer pruningBlockConfirmations = private final Integer pruningBlockConfirmations =
PrunerConfiguration.DEFAULT_PRUNING_BLOCK_CONFIRMATIONS; 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 EthNetworkConfig ethNetworkConfig;
private JsonRpcConfiguration jsonRpcConfiguration; private JsonRpcConfiguration jsonRpcConfiguration;
private GraphQLConfiguration graphQLConfiguration; private GraphQLConfiguration graphQLConfiguration;
@ -1068,7 +1074,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
webSocketConfiguration, webSocketConfiguration,
metricsConfiguration, metricsConfiguration,
permissioningConfiguration, permissioningConfiguration,
staticNodes); staticNodes,
pidPath);
} }
private BesuCommand startPlugins() { private BesuCommand startPlugins() {
@ -1727,7 +1734,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final WebSocketConfiguration webSocketConfiguration, final WebSocketConfiguration webSocketConfiguration,
final MetricsConfiguration metricsConfiguration, final MetricsConfiguration metricsConfiguration,
final Optional<PermissioningConfiguration> permissioningConfiguration, final Optional<PermissioningConfiguration> permissioningConfiguration,
final Collection<EnodeURL> staticNodes) { final Collection<EnodeURL> staticNodes,
final Path pidPath) {
checkNotNull(runnerBuilder); checkNotNull(runnerBuilder);
@ -1753,6 +1761,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.graphQLConfiguration(graphQLConfiguration) .graphQLConfiguration(graphQLConfiguration)
.jsonRpcConfiguration(jsonRpcConfiguration) .jsonRpcConfiguration(jsonRpcConfiguration)
.webSocketConfiguration(webSocketConfiguration) .webSocketConfiguration(webSocketConfiguration)
.pidPath(pidPath)
.dataDir(dataDir()) .dataDir(dataDir())
.bannedNodeIds(bannedNodeIds) .bannedNodeIds(bannedNodeIds)
.metricsSystem(metricsSystem) .metricsSystem(metricsSystem)

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

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

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

@ -15,9 +15,11 @@ EXPOSE 8545 8546 8547 30303
ENV BESU_RPC_HTTP_HOST 0.0.0.0 ENV BESU_RPC_HTTP_HOST 0.0.0.0
ENV BESU_RPC_WS_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_GRAPHQL_HTTP_HOST 0.0.0.0
ENV BESU_PID_PATH "/tmp/pid"
ENV PATH="/opt/besu/bin:${PATH}" ENV PATH="/opt/besu/bin:${PATH}"
ENTRYPOINT ["besu"] 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 # Build-time metadata as defined at http://label-schema.org
ARG BUILD_DATE ARG BUILD_DATE

Loading…
Cancel
Save