This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit ac306f7fb85e344078f4d4e5c448a4f044305732 Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Thu Jul 6 16:29:26 2023 -0400 Infinispan and Karavan up and running #817 --- karavan-bashi/pom.xml | 8 ++++ .../apache/camel/karavan/bashi/HealthChecker.java | 31 ++++++++++++++ .../apache/camel/karavan/bashi/KaravanBashi.java | 49 ++++++++++++++++++---- .../camel/karavan/bashi/KaravanConstants.java | 4 ++ .../camel/karavan/bashi/KaravanContainers.java | 33 +++++++++++++++ .../karavan/bashi/docker/DockerEventListener.java | 27 +++++++++++- .../camel/karavan/bashi/docker/DockerService.java | 47 ++++++++++++++++----- .../src/main/resources/application.properties | 6 +-- 8 files changed, 182 insertions(+), 23 deletions(-) diff --git a/karavan-bashi/pom.xml b/karavan-bashi/pom.xml index 3f4f0b6b..a5ae14d4 100644 --- a/karavan-bashi/pom.xml +++ b/karavan-bashi/pom.xml @@ -33,6 +33,14 @@ <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-scheduler</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-vertx</artifactId> + </dependency> <dependency> <groupId>com.github.docker-java</groupId> <artifactId>docker-java-core</artifactId> diff --git a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/HealthChecker.java b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/HealthChecker.java new file mode 100644 index 00000000..681fbf19 --- /dev/null +++ b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/HealthChecker.java @@ -0,0 +1,31 @@ +package org.apache.camel.karavan.bashi; + +import com.github.dockerjava.api.model.Container; +import io.quarkus.scheduler.Scheduled; +import org.jboss.logging.Logger; + +import javax.enterprise.context.ApplicationScoped; +import java.util.concurrent.ConcurrentHashMap; + +@ApplicationScoped +public class HealthChecker { + + private static final Logger LOGGER = Logger.getLogger(HealthChecker.class.getName()); + + private static final ConcurrentHashMap<String, Container> containers = new ConcurrentHashMap<>(); + +// @Scheduled(every = "{karavan.health-checker-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) +// void collectHealthStatuses() { +// containers.forEach((s, s2) -> { +// LOGGER.infof("HealthCheck for %s", s); +// }); +// } + +// public void addContainer(Container container){ +// containers.put(container.getId(), container); +// } +// +// public void removeContainer(String id){ +// containers.remove(id); +// } +} diff --git a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanBashi.java b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanBashi.java index 1f21389b..3058c2a0 100644 --- a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanBashi.java +++ b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanBashi.java @@ -1,7 +1,9 @@ package org.apache.camel.karavan.bashi; +import com.github.dockerjava.api.model.HealthCheck; import io.quarkus.runtime.ShutdownEvent; import io.quarkus.runtime.StartupEvent; +import io.quarkus.vertx.ConsumeEvent; import org.apache.camel.karavan.bashi.docker.DockerService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; @@ -11,11 +13,24 @@ import javax.enterprise.event.Observes; import javax.inject.Inject; import java.util.List; -import static org.apache.camel.karavan.bashi.KaravanConstants.INFINISPAN_CONTAINER_NAME; +import static org.apache.camel.karavan.bashi.KaravanConstants.*; @ApplicationScoped public class KaravanBashi { + @ConfigProperty(name = "karavan.image") + String karavanImage; + @ConfigProperty(name = "karavan.port") + String karavanPort; + @ConfigProperty(name = "karavan.git-repository") + String gitRepository; + @ConfigProperty(name = "karavan.git-username") + String gitUsername; + @ConfigProperty(name = "karavan.git-password") + String gitPassword; + @ConfigProperty(name = "karavan.git-branch") + String gitBranch; + @ConfigProperty(name = "infinispan.image") String infinispanImage; @ConfigProperty(name = "infinispan.port") @@ -32,6 +47,7 @@ public class KaravanBashi { void onStart(@Observes StartupEvent ev) throws InterruptedException { LOGGER.info("Karavan Bashi is starting..."); + dockerService.checkContainersStatus(); dockerService.createNetwork(); dockerService.startListeners(); startInfinispan(); @@ -39,20 +55,35 @@ public class KaravanBashi { void startInfinispan() throws InterruptedException { LOGGER.info("Infinispan is starting..."); + + HealthCheck healthCheck = new HealthCheck().withTest(List.of("CMD", "curl", "-f", "http://localhost:11222/rest/v2/cache-managers/default/health/status")) + .withInterval(10000000000L).withTimeout(10000000000L).withStartPeriod(10000000000L).withRetries(30); + dockerService.createContainer(INFINISPAN_CONTAINER_NAME, infinispanImage, - List.of("USER=" + infinispanUsername, "PASS=" + infinispanPassword), infinispanPort, false + List.of("USER=" + infinispanUsername, "PASS=" + infinispanPassword), + infinispanPort, true, healthCheck ); dockerService.startContainer(INFINISPAN_CONTAINER_NAME); LOGGER.info("Infinispan is started"); } - void startKaravan() throws InterruptedException { - LOGGER.info("Karavan is starting..."); - dockerService.createContainer(INFINISPAN_CONTAINER_NAME, infinispanImage, - List.of("USER=" + infinispanUsername, "PASS=" + infinispanPassword), infinispanPort, false - ); - dockerService.startContainer(INFINISPAN_CONTAINER_NAME); - LOGGER.info("Karavan is started"); + @ConsumeEvent(value = ADDRESS_INFINISPAN_HEALTH, blocking = true, ordered = false) + void startKaravan(String infinispanHealth) throws InterruptedException { + if (infinispanHealth.equals("healthy")) { + LOGGER.info("Karavan is starting..."); + dockerService.createContainer(KARAVAN_CONTAINER_NAME, karavanImage, + List.of( + "QUARKUS_INFINISPAN_CLIENT_HOSTS=infinispan:11222", + "KARAVAN_GIT_REPOSITORY=" + gitRepository, + "KARAVAN_GIT_USERNAME=" + gitUsername, + "KARAVAN_GIT_PASSWORD=" + gitPassword, + "KARAVAN_GIT_BRANCH=" + gitBranch + ), + karavanPort, true, new HealthCheck() + ); + dockerService.startContainer(KARAVAN_CONTAINER_NAME); + LOGGER.info("Karavan is started"); + } } void onStop(@Observes ShutdownEvent ev) { diff --git a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanConstants.java b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanConstants.java index eab76977..8e53e011 100644 --- a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanConstants.java +++ b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanConstants.java @@ -4,4 +4,8 @@ public class KaravanConstants { public static final String NETWORK_NAME = "karavan"; public static final String INFINISPAN_CONTAINER_NAME = "infinispan"; + + public static final String KARAVAN_CONTAINER_NAME = "karavan"; + + public static final String ADDRESS_INFINISPAN_HEALTH = "ADDRESS_INFINISPAN_HEALTH"; } diff --git a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanContainers.java b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanContainers.java new file mode 100644 index 00000000..7fe14977 --- /dev/null +++ b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/KaravanContainers.java @@ -0,0 +1,33 @@ +package org.apache.camel.karavan.bashi; + +import com.github.dockerjava.api.model.Container; +import io.vertx.core.eventbus.EventBus; +import org.jboss.logging.Logger; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import java.util.concurrent.ConcurrentHashMap; + +import static org.apache.camel.karavan.bashi.KaravanConstants.ADDRESS_INFINISPAN_HEALTH; + +@ApplicationScoped +public class KaravanContainers { + + private static final Logger LOGGER = Logger.getLogger(KaravanContainers.class.getName()); + + private static final ConcurrentHashMap<String, String> containers = new ConcurrentHashMap<>(); + + @Inject + EventBus eventBus; + + public void addContainer(Container container, String health){ + containers.put(container.getId(), health); + if (container.getNames()[0].equals("/infinispan")) { + eventBus.publish(ADDRESS_INFINISPAN_HEALTH, health); + } + } + + public void removeContainer(String id){ + containers.remove(id); + } +} diff --git a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java index d0551ba7..9ea88796 100644 --- a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java +++ b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerEventListener.java @@ -1,16 +1,29 @@ package org.apache.camel.karavan.bashi.docker; import com.github.dockerjava.api.async.ResultCallback; +import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.api.model.EventType; +import org.apache.camel.karavan.bashi.HealthChecker; +import org.apache.camel.karavan.bashi.KaravanContainers; import org.jboss.logging.Logger; import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; import java.io.Closeable; import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; @ApplicationScoped public class DockerEventListener implements ResultCallback<Event> { + @Inject + KaravanContainers karavanContainers; + + @Inject + DockerService dockerService; + private static final Logger LOGGER = Logger.getLogger(DockerEventListener.class.getName()); @Override @@ -20,7 +33,19 @@ public class DockerEventListener implements ResultCallback<Event> { @Override public void onNext(Event event) { - LOGGER.info(event.getType() + " : " + event.getStatus()); +// LOGGER.info(event.getType() + " : " + event.getStatus()); + if (Objects.equals(event.getType(), EventType.CONTAINER)){ + Container c = dockerService.getContainer(event.getId()); + if (Arrays.asList("stop", "die", "kill", "pause", "destroy").contains(event.getStatus())) { + karavanContainers.removeContainer(c.getId()); + } else if (Arrays.asList("start", "unpause").contains(event.getStatus())) { + karavanContainers.addContainer(c, "unknown"); + } else if (event.getStatus().startsWith("health_status:")) { + String health = event.getStatus().replace("health_status: ", ""); + LOGGER.info(event.getType() + " : " + event.getId() + " : " + health); + karavanContainers.addContainer(c, health); + } + } } @Override diff --git a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerService.java b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerService.java index e65af77f..89d54d74 100644 --- a/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerService.java +++ b/karavan-bashi/src/main/java/org/apache/camel/karavan/bashi/docker/DockerService.java @@ -4,12 +4,15 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.CreateNetworkResponse; +import com.github.dockerjava.api.command.HealthState; import com.github.dockerjava.api.model.*; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; +import org.apache.camel.karavan.bashi.HealthChecker; +import org.apache.camel.karavan.bashi.KaravanContainers; import org.jboss.logging.Logger; import javax.enterprise.context.ApplicationScoped; @@ -18,6 +21,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static org.apache.camel.karavan.bashi.KaravanConstants.NETWORK_NAME; @@ -29,6 +33,9 @@ public class DockerService { @Inject DockerEventListener dockerEventListener; + @Inject + KaravanContainers karavanContainers; + public void startListeners() { getDockerClient().eventsCmd().exec(dockerEventListener); } @@ -36,28 +43,45 @@ public class DockerService { public void createNetwork() { if (!getDockerClient().listNetworksCmd().exec().stream().filter(n -> n.getName().equals(NETWORK_NAME)) .findFirst().isPresent()) { - CreateNetworkResponse res = getDockerClient().createNetworkCmd().withName(NETWORK_NAME).exec(); + CreateNetworkResponse res = getDockerClient().createNetworkCmd().withName(NETWORK_NAME).withAttachable(true).exec(); LOGGER.info("Network created: {}" + res); } else { LOGGER.info("Network already exists with name: " + NETWORK_NAME); } } - public void createContainer(String name, String image, List<String> env, String ports, boolean hostNet) throws InterruptedException { + public void checkContainersStatus() { + getDockerClient().listContainersCmd().withShowAll(true).exec().stream() + .filter(c -> c.getState().equals("running")) + .forEach(c -> { + HealthState hs = getDockerClient().inspectContainerCmd(c.getId()).exec().getState().getHealth(); + karavanContainers.addContainer(c, hs != null ? hs.getStatus() : "unknown"); + }); + } + + public Container getContainer(String id) { + List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withIdFilter(List.of(id)).exec(); + return containers.get(0); + } + + public void createContainer(String name, String image, List<String> env, String ports, boolean exposedPort, HealthCheck healthCheck) throws InterruptedException { List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec(); - System.out.println(containers); if (containers.size() == 0) { pullImage(image); + List<ExposedPort> exposedPorts = getPortsFromString(ports).values().stream().map(i -> ExposedPort.tcp(i)).collect(Collectors.toList()); + CreateContainerResponse container = getDockerClient().createContainerCmd(image) .withName(name) .withEnv(env) + .withExposedPorts(exposedPorts) .withHostName(name) - .withHostConfig(getHostConfig(ports, hostNet)) + .withHostConfig(getHostConfig(ports)) + .withHealthcheck(healthCheck) .exec(); - LOGGER.info("Container created: " + container.toString()); + LOGGER.info("Container created: " + container.getId()); } else { - LOGGER.info("Container already exists: " + containers.get(0).toString()); + LOGGER.info("Container already exists: " + containers.get(0).getId()); } } @@ -87,17 +111,20 @@ public class DockerService { } public void pullImage(String image) throws InterruptedException { - ResultCallback.Adapter<PullResponseItem> pull = getDockerClient().pullImageCmd(image).start().awaitCompletion(); + List<Image> images = getDockerClient().listImagesCmd().withShowAll(true).exec(); + if (!images.stream().filter(i -> Arrays.asList(i.getRepoTags()).contains(image)).findFirst().isPresent()) { + ResultCallback.Adapter<PullResponseItem> pull = getDockerClient().pullImageCmd(image).start().awaitCompletion(); + } } - private HostConfig getHostConfig(String ports, boolean hostNet) { + private HostConfig getHostConfig(String ports) { Ports portBindings = new Ports(); getPortsFromString(ports).forEach((hostPort, containerPort) -> { - portBindings.bind(ExposedPort.tcp(containerPort), Ports.Binding.bindPort(hostPort)); + portBindings.bind(ExposedPort.tcp(containerPort), Ports.Binding.bindIp("0.0.0.0").bindPort(hostPort)); }); return new HostConfig() .withPortBindings(portBindings) - .withNetworkMode(hostNet ? "host" : NETWORK_NAME); + .withNetworkMode(NETWORK_NAME); } private Map<Integer,Integer> getPortsFromString(String ports){ diff --git a/karavan-bashi/src/main/resources/application.properties b/karavan-bashi/src/main/resources/application.properties index d9eb55bb..8649065e 100644 --- a/karavan-bashi/src/main/resources/application.properties +++ b/karavan-bashi/src/main/resources/application.properties @@ -1,10 +1,10 @@ -infinispan.image=quay.io/infinispan/server:14.0 +infinispan.image=quay.io/infinispan/server:14.0.6.Final infinispan.port=11222:11222 infinispan.username=admin infinispan.password=karavan -karavan.image=ghcr.io/apache/camel-karavan - +karavan.image=marat/karavan:3.21.1-SNAPSHOT +karavan.port=8080:8080 karavan.environment=dev karavan.default-runtime=quarkus karavan.runtimes=quarkus,spring-boot