diff --git a/Dockerfile b/Dockerfile index 1ddc78d9a19badff6f0c0081dc2794f87b04b77f..130856b515b6ba1b833cb64ecf15ed5c4ad230ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,16 +19,20 @@ ARG BUILD_DATE ARG VCS_REF ARG VERSION +RUN apk add --update --no-cache curl + COPY docker/config/application.tmpl /config/application.tmpl COPY docker/README.md docker/run.sh / COPY --from=java-build-env /project/target/portal-backend.jar /usr/share/jars/ -# 8080: Web service API, health checks on http://host:8087/health +ENTRYPOINT ["/run.sh"] + +# 8080: Web service API, health checks on http://host:8080/health # 4089: Akka cluster EXPOSE 4089 8080 -ENTRYPOINT ["/run.sh"] +HEALTHCHECK --start-period=60s CMD curl -v --silent http://localhost:8080/health 2>&1 | grep UP LABEL org.label-schema.build-date=$BUILD_DATE \ org.label-schema.name="hbpmip/portal-backend" \ diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 911af6200bda7587d08d1543ad269d969fba3c81..65f3335d9cd7de1be24bece07abbb4c0db7b8270 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -78,8 +78,8 @@ endpoints: enabled: true health: enabled: true - endoint: /health - sentitive: false + endpoint: /health + sensitive: false services: exareme: diff --git a/src/main/java/eu/hbp/mip/akka/AkkaClusterHealthCheck.java b/src/main/java/eu/hbp/mip/akka/AkkaClusterHealthCheck.java new file mode 100644 index 0000000000000000000000000000000000000000..af32ff950f6fcd3cf86b12a3d7a051defc1b490c --- /dev/null +++ b/src/main/java/eu/hbp/mip/akka/AkkaClusterHealthCheck.java @@ -0,0 +1,30 @@ +package eu.hbp.mip.akka; + +import akka.actor.ActorRef; +import akka.cluster.Cluster; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + + +@Component +public class AkkaClusterHealthCheck implements HealthIndicator { + + @Autowired + private ActorRef wokenMediator; + + @Autowired + private Cluster cluster; + + @Override + public Health health() { + if (cluster.state().getLeader() == null) { + return Health.down().withDetail("Error", "No leader in the cluster").build(); + } else if (!cluster.state().allRoles().contains("woken")) { + return Health.down().withDetail("Error", "Woken server cannot be seen in the cluster").build(); + } + return Health.up().build(); + } + +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/akka/WokenClientController.java b/src/main/java/eu/hbp/mip/akka/WokenClientController.java index 5931c13eb1aa64afdb0b573fb9844b06ad84ead5..6c12dc40366a86af4cadb2f03875f6dfb7afb34c 100644 --- a/src/main/java/eu/hbp/mip/akka/WokenClientController.java +++ b/src/main/java/eu/hbp/mip/akka/WokenClientController.java @@ -2,7 +2,6 @@ package eu.hbp.mip.akka; import akka.actor.ActorRef; import akka.actor.ActorSystem; -import akka.cluster.pubsub.DistributedPubSub; import akka.cluster.pubsub.DistributedPubSubMediator; import akka.pattern.Patterns; import akka.util.Timeout; @@ -19,7 +18,6 @@ import scala.concurrent.ExecutionContext; import scala.concurrent.Future; import scala.concurrent.duration.Duration; -import javax.annotation.PostConstruct; import java.util.function.Function; /** @@ -38,15 +36,9 @@ public abstract class WokenClientController { @Value("#{'${akka.woken.path:/user/entrypoint}'}") private String wokenPath; + @Autowired private ActorRef wokenMediator; - @SuppressWarnings("unused") - @PostConstruct - public void initClusterClient() { - LOGGER.info("Start Woken client " + wokenReceptionistPath); - wokenMediator = DistributedPubSub.get(actorSystem).mediator(); - } - @SuppressWarnings("unchecked") protected <A, B> B askWoken(A message, int waitInSeconds) throws Exception { LOGGER.info("Akka is trying to reach remote " + wokenPath); diff --git a/src/main/java/eu/hbp/mip/configuration/AkkaConfiguration.java b/src/main/java/eu/hbp/mip/configuration/AkkaConfiguration.java index cfcfefe7dbb2bc637e4090a42c037ee1e3563b63..6351d4711387bc4dbc2f4e0920e3c91c027ddbf9 100644 --- a/src/main/java/eu/hbp/mip/configuration/AkkaConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/AkkaConfiguration.java @@ -1,8 +1,14 @@ package eu.hbp.mip.configuration; +import akka.actor.ActorRef; import akka.actor.ActorSystem; +import akka.actor.ExtendedActorSystem; +import akka.cluster.Cluster; +import akka.cluster.pubsub.DistributedPubSub; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; @@ -20,6 +26,8 @@ import static eu.hbp.mip.akka.SpringExtension.SPRING_EXTENSION_PROVIDER; @ComponentScan class AkkaConfiguration { + protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); + @Autowired private ApplicationContext applicationContext; @@ -33,17 +41,27 @@ class AkkaConfiguration { private String wokenPath; @Bean - public ActorSystem actorSystem() { + public ExtendedActorSystem actorSystem() { Config config = ConfigFactory.load("application.conf"); - ActorSystem system = ActorSystem.create("woken", config); + ExtendedActorSystem system = (ExtendedActorSystem) ActorSystem.create("woken", config); SPRING_EXTENSION_PROVIDER.get(system).initialize(applicationContext); return system; } + @Bean + public Cluster cluster() { + return new Cluster(actorSystem()); + } + @Bean public String wokenReceptionistPath() { return "akka.tcp://woken@" + wokenHost + ":" + wokenPort + "/system/receptionist"; } + @Bean + public ActorRef wokenMediator() { + LOGGER.info("Start Woken client " + wokenReceptionistPath()); + return DistributedPubSub.get(actorSystem()).mediator(); + } }