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 f90b524b1d1b54d4c1871b95a9253daa1f623f03 Author: Marat Gubaidullin <[email protected]> AuthorDate: Sun Jul 23 12:55:41 2023 -0400 Log in UI with Docker #817 --- .../apache/camel/karavan/api/LogWatchResource.java | 33 +++++++++++++++++++--- .../camel/karavan/docker/DockerEventListener.java | 2 +- .../apache/camel/karavan/docker/DockerService.java | 6 ++-- .../apache/camel/karavan/docker/LogCallback.java | 11 ++++---- .../apache/camel/karavan/service/EventService.java | 1 - .../camel/karavan/service/ProjectService.java | 9 +++++- .../camel/karavan/service/ScheduledService.java | 17 +++++++++-- .../src/main/resources/application.properties | 7 +++-- .../src/main/webui/src/api/ProjectStore.ts | 17 ++++++++--- .../src/main/webui/src/project/ProjectToolbar.tsx | 9 +----- .../camel/karavan/headless/HeadlessService.java | 2 +- .../src/main/resources/application.properties | 2 +- 12 files changed, 80 insertions(+), 36 deletions(-) diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java index 3a235592..40cbc79a 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java @@ -17,10 +17,15 @@ package org.apache.camel.karavan.api; import io.fabric8.kubernetes.client.dsl.LogWatch; +import io.smallrye.context.api.ManagedExecutorConfig; +import io.smallrye.context.api.NamedInstance; +import org.apache.camel.karavan.docker.DockerService; +import org.apache.camel.karavan.docker.LogCallback; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.kubernetes.KubernetesService; import org.apache.camel.karavan.shared.ConfigService; import org.eclipse.microprofile.context.ManagedExecutor; +import org.eclipse.microprofile.context.ThreadContext; import org.jboss.logging.Logger; import javax.inject.Inject; @@ -30,11 +35,13 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.SecurityContext; import javax.ws.rs.sse.Sse; import javax.ws.rs.sse.SseEventSink; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; @Path("/api/logwatch") @@ -47,9 +54,11 @@ public class LogWatchResource { KubernetesService kubernetesService; @Inject - InfinispanService infinispanService; + DockerService dockerService; @Inject + @ManagedExecutorConfig() + @NamedInstance("logExecutor") ManagedExecutor managedExecutor; @GET @@ -57,19 +66,35 @@ public class LogWatchResource { @Path("/{type}/{name}") public void eventSourcing(@PathParam("type") String type, @PathParam("name") String name, + @Context SecurityContext securityContext, @Context SseEventSink eventSink, - @Context Sse sse - ) { + @Context Sse sse) { + managedExecutor.execute(() -> { LOGGER.info("LogWatch for " + name + " starting..."); if (ConfigService.inKubernetes()) { getKubernetesLogs(type, name, eventSink, sse); } else { -// infinispanService.sendDevModeCommand(DevModeCommand.createForContainer(DevModeCommandName.LOG, name)); + getDockerLogs(type, name, eventSink, sse); } }); } + private void getDockerLogs(String type, String name, SseEventSink eventSink, Sse sse) { + LOGGER.info("LogCallback for " + name + " starting"); + try (SseEventSink sink = eventSink) { + LogCallback logCallback = new LogCallback(line -> { + sink.send(sse.newEvent(line)); + }); + dockerService.logContainer(name, logCallback); + logCallback.close(); + sink.close(); + LOGGER.info("LogCallback for " + name + " closed"); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } + } + private void getKubernetesLogs(String type, String name, SseEventSink eventSink, Sse sse) { try (SseEventSink sink = eventSink) { LogWatch logWatch = type.equals("container") diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java index 0ee8000a..8d4c9cc3 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java @@ -159,7 +159,7 @@ public class DockerEventListener implements ResultCallback<Event> { @Override public void close() throws IOException { - LOGGER.error("DockerEventListener close"); + LOGGER.info("DockerEventListener close"); } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java index 3b380749..9f1f4198 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java @@ -120,6 +120,7 @@ public class DockerService { public void deleteKaravanHeadlessContainer() { try { + stopContainer(KARAVAN_CONTAINER_NAME); deleteContainer(KARAVAN_CONTAINER_NAME); } catch (Exception e) { LOGGER.error(e.getMessage()); @@ -252,11 +253,10 @@ public class DockerService { startContainer(name); } - public LogCallback logContainer(String containerName) { + public void logContainer(String containerName, LogCallback callback) { try { Container container = getContainerByName(containerName); if (container != null) { - LogCallback callback = new LogCallback(eventBus); getDockerClient().logContainerCmd(container.getId()) .withStdOut(true) .withStdErr(true) @@ -265,12 +265,10 @@ public class DockerService { .withTailAll() .exec(callback); callback.awaitCompletion(); - return callback; } } catch (Exception e) { LOGGER.error(e.getMessage()); } - return null; } public void stopContainer(String name) { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/LogCallback.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/LogCallback.java index a05efb31..bd0de584 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/LogCallback.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/LogCallback.java @@ -2,19 +2,20 @@ package org.apache.camel.karavan.docker; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.model.Frame; -import io.vertx.core.eventbus.EventBus; + +import java.util.function.Consumer; public class LogCallback extends ResultCallback.Adapter<Frame> { - private final EventBus eventBus; + private final Consumer<String> action; - public LogCallback(EventBus eventBus) { - this.eventBus = eventBus; + public LogCallback(Consumer<String> action) { + this.action = action; } @Override public void onNext(Frame frame) { - System.out.println(new String(frame.getPayload())); + action.accept(new String(frame.getPayload())); } } \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java index fe48ad1c..4d6f0b7a 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java @@ -49,7 +49,6 @@ public class EventService { infinispanService.start(false); infinispanService.clearAllStatuses(); if (!ConfigService.inKubernetes()) { - dockerService.deleteKaravanHeadlessContainer(); dockerService.startKaravanHeadlessContainer(); dockerService.collectContainersStatuses(); } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java index 87c7dbe4..815eab38 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java @@ -36,6 +36,7 @@ import javax.enterprise.inject.Default; import javax.inject.Inject; import java.time.Instant; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.camel.karavan.shared.EventType.IMPORT_PROJECTS; @@ -47,6 +48,9 @@ public class ProjectService implements HealthCheck{ private static final Logger LOGGER = Logger.getLogger(ProjectService.class.getName()); + @ConfigProperty(name = "karavan.git.pull.interval") + String gitPullInterval; + @Inject InfinispanService infinispanService; @@ -91,6 +95,7 @@ public class ProjectService implements HealthCheck{ void importCommits() { LOGGER.info("Import commits..."); gitService.getAllCommits().forEach(commitInfo -> { + System.out.println(commitInfo.getCommitId() + " " + Instant.ofEpochSecond(commitInfo.getTime()).toString()); infinispanService.saveCommit(commitInfo.getCommitId(), commitInfo.getTime()); infinispanService.saveLastCommit(commitInfo.getCommitId()); }); @@ -103,7 +108,9 @@ public class ProjectService implements HealthCheck{ } addTemplatesProject(); addServicesProject(); - importCommits(); + if (!Objects.equals("disabled", gitPullInterval.toLowerCase()) && !Objects.equals("off", gitPullInterval.toLowerCase())) { + importCommits(); + } } private void importAllProjects() { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java index d7f32e95..7886a86d 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java @@ -18,6 +18,7 @@ package org.apache.camel.karavan.service; import io.quarkus.scheduler.Scheduled; import org.apache.camel.karavan.docker.DockerService; +import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.shared.ConfigService; import org.jboss.logging.Logger; @@ -38,12 +39,22 @@ public class ScheduledService { @Inject CamelService camelService; - @Scheduled(every = "{karavan.container.statistics.container}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + @Inject + InfinispanService infinispanService; + + @Scheduled(every = "{karavan.container.statistics.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) void collectContainersStats() { dockerService.collectContainersStats(); } - @Scheduled(every = "{karavan.camel.status.pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + @Scheduled(every = "{karavan.container.infinispan.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + void checkInfinispanHealth() { + if (!infinispanService.isReady()) { + dockerService.checkInfinispanHealth(); + } + } + + @Scheduled(every = "{karavan.camel.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) void collectCamelStatuses() { LOGGER.info("Collect info statuses"); if (ConfigService.inKubernetes()) { @@ -52,7 +63,7 @@ public class ScheduledService { } } - @Scheduled(every = "{karavan.git.pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + @Scheduled(every = "{karavan.git.pull.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) void pullCommitsFromGit() { projectService.pullCommits(); } diff --git a/karavan-web/karavan-app/src/main/resources/application.properties b/karavan-web/karavan-app/src/main/resources/application.properties index 76045045..2cee473c 100644 --- a/karavan-web/karavan-app/src/main/resources/application.properties +++ b/karavan-web/karavan-app/src/main/resources/application.properties @@ -3,8 +3,9 @@ karavan.environment=dev karavan.environments=dev,test,prod karavan.default-runtime=quarkus karavan.runtimes=quarkus,spring-boot -karavan.camel.status.pull-interval=off -karavan.container.statistics.container=3s +karavan.camel.status.interval=off +karavan.container.statistics.interval=3s +karavan.container.infinispan.interval=5s karavan.devmode.image=ghcr.io/apache/camel-karavan-runner:3.21.1-snapshot karavan.headless.image=entropy1/karavan-headless:3.21.1-SNAPSHOT @@ -13,7 +14,7 @@ karavan.git.repository=${GIT_REPOSITORY} karavan.git.username=${GIT_USERNAME} karavan.git.password=${GIT_TOKEN} karavan.git.branch=main -karavan.git.pull-interval=disabled +karavan.git.pull.interval=disabled # Infinispan container config in Docker infinispan.image=quay.io/infinispan/server:14.0.6.Final diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts index 9a891b3e..b4930ead 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts +++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts @@ -191,10 +191,16 @@ export const useLogStore = create<LogState>((set) => ({ set({data: data}) }, addData: (data: string) => { - set((state: LogState) => ({data: state.data.concat('\n').concat(data)})) + set((state: LogState) => { + const delimiter = state.data.endsWith('\n') ? '' : '\n'; + return ({data: state.data.concat(delimiter, data)}) + }) }, addDataAsync: async (data: string) => { - set((state: LogState) => ({data: state.data.concat('\n').concat(data)})) + set((state: LogState) => { + const delimiter = state.data.endsWith('\n') ? '' : '\n'; + return ({data: state.data.concat(delimiter, data)}) + }) }, currentLine: 0, setCurrentLine: (currentLine: number) => { @@ -217,8 +223,11 @@ console.log("Start log subscriber"); const sub = ProjectEventBus.onLog()?.subscribe((result: ["add" | "set", string]) => { if (result[0] === 'add') { unstable_batchedUpdates(() => { - useLogStore.setState((state: LogState) => - ({data: state.data ? state.data.concat('\n').concat(result[1]) : result[1], currentLine: state.currentLine+1})) + useLogStore.setState((state: LogState) => { + const delimiter = state.data.endsWith('\n') ? '' : '\n'; + const newData = state.data ? state.data.concat(delimiter, result[1]) : result[1] + return ({data: newData, currentLine: state.currentLine+1}); + }) }) } else if (result[0] === 'set') { diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx index 4e6c5704..2f639228 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx @@ -4,13 +4,11 @@ import { Checkbox, Flex, FlexItem, - Label, ToggleGroup, ToggleGroupItem, Toolbar, ToolbarContent, - Tooltip, - TooltipPosition + Tooltip } from '@patternfly/react-core'; import '../designer/karavan.css'; import DownloadImageIcon from "@patternfly/react-icons/dist/esm/icons/image-icon"; @@ -79,11 +77,6 @@ export const ProjectToolbar = (props: Props) => { return <Toolbar id="toolbar-group-types"> <ToolbarContent> <Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}> - {isJava() && <FlexItem> - <Tooltip content="File size" position={TooltipPosition.bottom}> - <Label>{file?.code?.length}</Label> - </Tooltip> - </FlexItem>} {isRunnable() && <DevModeToolbar reloadOnly={true}/>} {isYaml() && <FlexItem> <ToggleGroup> diff --git a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java index 01b00da7..e6f8624f 100644 --- a/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java +++ b/karavan-web/karavan-headless/src/main/java/org/apache/camel/karavan/headless/HeadlessService.java @@ -42,7 +42,7 @@ public class HeadlessService { infinispanService.clearAllStatuses(); } - @Scheduled(every = "{karavan.camel.status.pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + @Scheduled(every = "{karavan.camel.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) void collectCamelStatuses() { LOGGER.info("Collect info statuses"); // collect Camel statuses diff --git a/karavan-web/karavan-headless/src/main/resources/application.properties b/karavan-web/karavan-headless/src/main/resources/application.properties index dedfb854..ec2c2f7c 100644 --- a/karavan-web/karavan-headless/src/main/resources/application.properties +++ b/karavan-web/karavan-headless/src/main/resources/application.properties @@ -1,5 +1,5 @@ karavan.environment=dev -karavan.camel.status.pull-interval=3s +karavan.camel.status.interval=3s infinispan.hosts=infinispan:11222 infinispan.username=admin
