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 b9985be44545422ab24fd62852f045fc9234e096 Author: Marat Gubaidullin <[email protected]> AuthorDate: Fri Sep 8 17:52:42 2023 -0400 Run prod container for #817 --- karavan-designer/src/designer/karavan.css | 3 +- ...ructureResource.java => ContainerResource.java} | 190 +++------------------ .../apache/camel/karavan/api/ImagesResource.java | 15 ++ .../camel/karavan/api/InfrastructureResource.java | 154 +---------------- .../camel/karavan/docker/DockerForKaravan.java | 18 +- .../apache/camel/karavan/docker/DockerService.java | 46 +++-- .../karavan/infinispan/model/ContainerStatus.java | 33 ++++ .../apache/camel/karavan/service/CamelService.java | 27 +-- .../karavan/service/ContainerStatusService.java | 56 ++++++ .../camel/karavan/service/ProjectService.java | 8 + .../camel/karavan/service/ScheduledService.java | 100 ----------- .../camel-main-docker-application.properties | 3 + .../src/main/webui/src/api/KaravanApi.tsx | 27 ++- .../src/main/webui/src/api/ProjectModels.ts | 2 +- .../src/main/webui/src/project/BuildToolbar.tsx | 84 ++++++++- .../webui/src/project/build/ProjectBuildTab.tsx | 2 +- .../src/project/trace/RunnerInfoTraceModal.tsx | 1 - 17 files changed, 288 insertions(+), 481 deletions(-) diff --git a/karavan-designer/src/designer/karavan.css b/karavan-designer/src/designer/karavan.css index 11324971..bf4d9d49 100644 --- a/karavan-designer/src/designer/karavan.css +++ b/karavan-designer/src/designer/karavan.css @@ -38,7 +38,8 @@ height: 36px; } -.karavan .pf-v5-c-switch__input:focus ~ .pf-v5-c-switch__toggle { +.karavan .pf-v5-c-switch__input:focus ~ .pf-v5-c-switch__toggle, +.pf-v5-c-modal-box .pf-v5-c-switch__input:focus ~ .pf-v5-c-switch__toggle { outline: transparent; outline-offset: 0; } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java similarity index 52% copy from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java copy to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java index f6408f84..8a237d20 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java @@ -20,26 +20,23 @@ import io.smallrye.mutiny.Multi; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.eventbus.EventBus; import io.vertx.mutiny.core.eventbus.Message; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.apache.camel.karavan.code.CodeService; import org.apache.camel.karavan.code.DockerComposeConverter; +import org.apache.camel.karavan.code.model.DockerComposeService; import org.apache.camel.karavan.docker.DockerForKaravan; import org.apache.camel.karavan.docker.DockerService; -import org.apache.camel.karavan.code.model.DockerComposeService; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; -import org.apache.camel.karavan.infinispan.model.DeploymentStatus; -import org.apache.camel.karavan.infinispan.model.Project; -import org.apache.camel.karavan.infinispan.model.ServiceStatus; import org.apache.camel.karavan.kubernetes.KubernetesService; -import org.apache.camel.karavan.code.CodeService; -import org.apache.camel.karavan.service.ProjectService; import org.apache.camel.karavan.service.ConfigService; +import org.apache.camel.karavan.service.ProjectService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; -import jakarta.inject.Inject; -import jakarta.ws.rs.*; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -47,8 +44,8 @@ import java.util.stream.Collectors; import static org.apache.camel.karavan.service.ContainerStatusService.CONTAINER_STATUS; -@Path("/api/infrastructure") -public class InfrastructureResource { +@Path("/api/container") +public class ContainerResource { @Inject EventBus eventBus; @@ -74,109 +71,10 @@ public class InfrastructureResource { @ConfigProperty(name = "karavan.environment") String environment; - private static final Logger LOGGER = Logger.getLogger(InfrastructureResource.class.getName()); - - @POST - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @Path("/pipeline/{env}") - public String createPipeline(@PathParam("env") String env, Project project) throws Exception { - Project p = infinispanService.getProject(project.getProjectId()); - return kubernetesService.createPipelineRun(project); - } + private static final Logger LOGGER = Logger.getLogger(ContainerResource.class.getName()); @GET @Produces(MediaType.APPLICATION_JSON) - @Path("/pipeline/{env}/{name}") - public Response getPipeline(@PathParam("env") String env, - @PathParam("name") String name) throws Exception { - return Response.ok(kubernetesService.getPipelineRun(name, kubernetesService.getNamespace())).build(); - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/pipeline/log/{env}/{name}") - public Response getPipelineLog(@PathParam("env") String env, - @PathParam("name") String name) throws Exception { - return Response.ok(kubernetesService.getPipelineRunLog(name, kubernetesService.getNamespace())).build(); - } - - @DELETE - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @Path("/pipelinerun/{env}/{name}") - public Response stopPipelineRun(@PathParam("env") String env, @PathParam("name") String name) throws Exception { - kubernetesService.stopPipelineRun(name, kubernetesService.getNamespace()); - return Response.ok().build(); - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/container/log/{env}/{name}") - public Response getContainerLog(@PathParam("env") String env, - @PathParam("name") String name) throws Exception { - return Response.ok(kubernetesService.getContainerLog(name, kubernetesService.getNamespace())).build(); - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/deployment") - public List<DeploymentStatus> getAllDeploymentStatuses() throws Exception { - if (infinispanService.isReady()) { - return infinispanService.getDeploymentStatuses().stream() - .sorted(Comparator.comparing(DeploymentStatus::getProjectId)) - .collect(Collectors.toList()); - } else { - return List.of(); - } - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/deployment/{env}") - public List<DeploymentStatus> getDeploymentStatusesByEnv(@PathParam("env") String env) throws Exception { - if (infinispanService.isReady()) { - return infinispanService.getDeploymentStatuses(env).stream() - .sorted(Comparator.comparing(DeploymentStatus::getProjectId)) - .collect(Collectors.toList()); - } else { - return List.of(); - } - } - - @POST - @Produces(MediaType.APPLICATION_JSON) - @Path("/deployment/rollout/{env}/{name}") - public Response rollout(@PathParam("env") String env, @PathParam("name") String name) throws Exception { - kubernetesService.rolloutDeployment(name, kubernetesService.getNamespace()); - return Response.ok().build(); - } - - @DELETE - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @Path("/deployment/{env}/{name}") - public Response deleteDeployment(@PathParam("env") String env, @PathParam("name") String name) throws Exception { - kubernetesService.deleteDeployment(name, kubernetesService.getNamespace()); - return Response.ok().build(); - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/service") - public List<ServiceStatus> getAllServiceStatuses() throws Exception { - if (infinispanService.isReady()) { - return infinispanService.getServiceStatuses().stream() - .sorted(Comparator.comparing(ServiceStatus::getProjectId)) - .collect(Collectors.toList()); - } else { - return List.of(); - } - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/container") public List<ContainerStatus> getAllContainerStatuses() throws Exception { if (infinispanService.isReady()) { return infinispanService.getContainerStatuses().stream() @@ -190,7 +88,7 @@ public class InfrastructureResource { @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Path("/container/{env}/{type}/{name}") + @Path("/{env}/{type}/{name}") public Response manageContainer(@PathParam("env") String env, @PathParam("type") String type, @PathParam("name") String name, JsonObject command) throws Exception { if (infinispanService.isReady()) { // set container statuses @@ -202,7 +100,13 @@ public class InfrastructureResource { String code = projectService.getDevServiceCode(); DockerComposeService dockerComposeService = DockerComposeConverter.fromCode(code, name); if (dockerComposeService != null) { - dockerForKaravan.createDevserviceContainer(dockerComposeService); + dockerService.createContainerFromCompose(dockerComposeService, ContainerStatus.ContainerType.devmode); + dockerService.runContainer(dockerComposeService.getContainer_name()); + } + } else if (Objects.equals(type, ContainerStatus.ContainerType.project.name())) { + DockerComposeService dockerComposeService = projectService.getProjectDockerComposeService(name); + if (dockerComposeService != null) { + dockerService.createContainerFromCompose(dockerComposeService, ContainerStatus.ContainerType.project); dockerService.runContainer(dockerComposeService.getContainer_name()); } } else if (Objects.equals(type, ContainerStatus.ContainerType.devmode.name())) { @@ -217,6 +121,9 @@ public class InfrastructureResource { } else if (command.getString("command").equalsIgnoreCase("pause")) { dockerService.pauseContainer(name); return Response.ok().build(); + } else if (command.getString("command").equalsIgnoreCase("delete")) { + dockerService.deleteContainer(name); + return Response.ok().build(); } } } @@ -234,7 +141,7 @@ public class InfrastructureResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Path("/container/{env}") + @Path("/{env}") public List<ContainerStatus> getContainerStatusesByEnv(@PathParam("env") String env) throws Exception { return infinispanService.getContainerStatuses(env).stream() .sorted(Comparator.comparing(ContainerStatus::getProjectId)) @@ -243,7 +150,7 @@ public class InfrastructureResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Path("/container/{projectId}/{env}") + @Path("/{projectId}/{env}") public List<ContainerStatus> getContainerStatusesByProjectAndEnv(@PathParam("projectId") String projectId, @PathParam("env") String env) throws Exception { return infinispanService.getContainerStatuses(projectId, env).stream() .filter(podStatus -> Objects.equals(podStatus.getType(), ContainerStatus.ContainerType.project)) @@ -254,7 +161,7 @@ public class InfrastructureResource { @DELETE @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Path("/container/{env}/{type}/{name}") + @Path("/{env}/{type}/{name}") public Response deleteContainer(@PathParam("env") String env, @PathParam("type") String type, @PathParam("name") String name) { if (infinispanService.isReady()) { // set container statuses @@ -274,56 +181,9 @@ public class InfrastructureResource { return Response.notModified().build(); } - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/imagetag/{env}/{projectId}") - public Response getProjectImageTags(@PathParam("env") String env, @PathParam("projectId") String projectId) throws Exception { - return Response.ok(kubernetesService.getProjectImageTags(projectId, kubernetesService.getNamespace())).build(); - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/configmaps") - public Response getConfigMaps() throws Exception { - if (ConfigService.inKubernetes()) { - return Response.ok(kubernetesService.getConfigMaps(kubernetesService.getNamespace())).build(); - } else { - return Response.ok(List.of()).build(); - } - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/secrets") - public Response getSecrets() throws Exception { - if (ConfigService.inKubernetes()) { - return Response.ok(kubernetesService.getSecrets(kubernetesService.getNamespace())).build(); - } else { - return Response.ok(List.of()).build(); - } - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/services") - public Response getServices() throws Exception { - if (infinispanService.isReady()) { - if (ConfigService.inKubernetes()) { - return Response.ok(kubernetesService.getServices(kubernetesService.getNamespace())).build(); - } else { - List<String> list = infinispanService.getContainerStatuses(environment).stream() - .map(ci -> ci.getPorts().stream().map(i -> ci.getContainerName() + ":" + i).collect(Collectors.toList())) - .flatMap(List::stream).collect(Collectors.toList()); - return Response.ok(list).build(); - } - } else { - return Response.ok(List.of()).build(); - } - } - // TODO: implement log watch @GET - @Path("/container/log/watch/{env}/{name}") + @Path("/log/watch/{env}/{name}") @Produces(MediaType.SERVER_SENT_EVENTS) public Multi<String> getContainerLogWatch(@PathParam("env") String env, @PathParam("name") String name) { LOGGER.info("Start sourcing"); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java index 9d4e0371..fe1a9f7a 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java @@ -23,9 +23,12 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.apache.camel.karavan.docker.DockerService; import org.apache.camel.karavan.infinispan.model.Project; +import org.apache.camel.karavan.service.ConfigService; import org.apache.camel.karavan.service.ProjectService; import org.apache.camel.karavan.service.RegistryService; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Comparator; import java.util.List; @@ -65,4 +68,16 @@ public class ImagesResource { return Response.serverError().entity(e.getMessage()).build(); } } + + @DELETE + @Produces(MediaType.APPLICATION_JSON) + @Path("/{imageName}") + public Response deleteImage(@HeaderParam("username") String username, @PathParam("imageName") String imageName) { + if (ConfigService.inKubernetes()) { + return Response.ok().build(); + } else { + dockerService.deleteImage(imageName); + return Response.ok().build(); + } + } } \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java index f6408f84..7c2a6b6a 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java @@ -16,61 +16,32 @@ */ package org.apache.camel.karavan.api; -import io.smallrye.mutiny.Multi; -import io.vertx.core.json.JsonObject; -import io.vertx.mutiny.core.eventbus.EventBus; -import io.vertx.mutiny.core.eventbus.Message; -import org.apache.camel.karavan.code.DockerComposeConverter; -import org.apache.camel.karavan.docker.DockerForKaravan; -import org.apache.camel.karavan.docker.DockerService; -import org.apache.camel.karavan.code.model.DockerComposeService; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.apache.camel.karavan.infinispan.InfinispanService; -import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.infinispan.model.DeploymentStatus; import org.apache.camel.karavan.infinispan.model.Project; import org.apache.camel.karavan.infinispan.model.ServiceStatus; import org.apache.camel.karavan.kubernetes.KubernetesService; -import org.apache.camel.karavan.code.CodeService; -import org.apache.camel.karavan.service.ProjectService; import org.apache.camel.karavan.service.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; -import jakarta.inject.Inject; -import jakarta.ws.rs.*; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; import java.util.Comparator; import java.util.List; -import java.util.Objects; import java.util.stream.Collectors; -import static org.apache.camel.karavan.service.ContainerStatusService.CONTAINER_STATUS; - @Path("/api/infrastructure") public class InfrastructureResource { - @Inject - EventBus eventBus; - @Inject InfinispanService infinispanService; @Inject KubernetesService kubernetesService; - @Inject - DockerForKaravan dockerForKaravan; - - @Inject - DockerService dockerService; - - @Inject - ProjectService projectService; - - @Inject - CodeService codeService; - @ConfigProperty(name = "karavan.environment") String environment; @@ -110,14 +81,6 @@ public class InfrastructureResource { return Response.ok().build(); } - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/container/log/{env}/{name}") - public Response getContainerLog(@PathParam("env") String env, - @PathParam("name") String name) throws Exception { - return Response.ok(kubernetesService.getContainerLog(name, kubernetesService.getNamespace())).build(); - } - @GET @Produces(MediaType.APPLICATION_JSON) @Path("/deployment") @@ -174,106 +137,6 @@ public class InfrastructureResource { } } - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/container") - public List<ContainerStatus> getAllContainerStatuses() throws Exception { - if (infinispanService.isReady()) { - return infinispanService.getContainerStatuses().stream() - .sorted(Comparator.comparing(ContainerStatus::getProjectId)) - .collect(Collectors.toList()); - } else { - return List.of(); - } - } - - @POST - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @Path("/container/{env}/{type}/{name}") - public Response manageContainer(@PathParam("env") String env, @PathParam("type") String type, @PathParam("name") String name, JsonObject command) throws Exception { - if (infinispanService.isReady()) { - // set container statuses - setContainerStatusTransit(name, type); - // exec docker commands - if (command.containsKey("command")) { - if (command.getString("command").equalsIgnoreCase("run")) { - if (Objects.equals(type, ContainerStatus.ContainerType.devservice.name())) { - String code = projectService.getDevServiceCode(); - DockerComposeService dockerComposeService = DockerComposeConverter.fromCode(code, name); - if (dockerComposeService != null) { - dockerForKaravan.createDevserviceContainer(dockerComposeService); - dockerService.runContainer(dockerComposeService.getContainer_name()); - } - } else if (Objects.equals(type, ContainerStatus.ContainerType.devmode.name())) { -// TODO: merge with DevMode service -// dockerForKaravan.createDevmodeContainer(name, ""); -// dockerService.runContainer(name); - } - return Response.ok().build(); - } else if (command.getString("command").equalsIgnoreCase("stop")) { - dockerService.stopContainer(name); - return Response.ok().build(); - } else if (command.getString("command").equalsIgnoreCase("pause")) { - dockerService.pauseContainer(name); - return Response.ok().build(); - } - } - } - return Response.notModified().build(); - } - - private void setContainerStatusTransit(String name, String type){ - ContainerStatus status = infinispanService.getContainerStatus(name, environment, name); - if (status == null) { - status = ContainerStatus.createByType(name, environment, ContainerStatus.ContainerType.valueOf(type)); - } - status.setInTransit(true); - eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(status)); - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/container/{env}") - public List<ContainerStatus> getContainerStatusesByEnv(@PathParam("env") String env) throws Exception { - return infinispanService.getContainerStatuses(env).stream() - .sorted(Comparator.comparing(ContainerStatus::getProjectId)) - .collect(Collectors.toList()); - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/container/{projectId}/{env}") - public List<ContainerStatus> getContainerStatusesByProjectAndEnv(@PathParam("projectId") String projectId, @PathParam("env") String env) throws Exception { - return infinispanService.getContainerStatuses(projectId, env).stream() - .filter(podStatus -> Objects.equals(podStatus.getType(), ContainerStatus.ContainerType.project)) - .sorted(Comparator.comparing(ContainerStatus::getContainerName)) - .collect(Collectors.toList()); - } - - @DELETE - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @Path("/container/{env}/{type}/{name}") - public Response deleteContainer(@PathParam("env") String env, @PathParam("type") String type, @PathParam("name") String name) { - if (infinispanService.isReady()) { - // set container statuses - setContainerStatusTransit(name, type); - try { - if (ConfigService.inKubernetes()) { - kubernetesService.deletePod(name, kubernetesService.getNamespace()); - } else { - dockerService.deleteContainer(name); - } - return Response.accepted().build(); - } catch (Exception e) { - LOGGER.error(e.getMessage()); - return Response.notModified().build(); - } - } - return Response.notModified().build(); - } - @GET @Produces(MediaType.APPLICATION_JSON) @Path("/imagetag/{env}/{projectId}") @@ -320,13 +183,4 @@ public class InfrastructureResource { return Response.ok(List.of()).build(); } } - - // TODO: implement log watch - @GET - @Path("/container/log/watch/{env}/{name}") - @Produces(MediaType.SERVER_SENT_EVENTS) - public Multi<String> getContainerLogWatch(@PathParam("env") String env, @PathParam("name") String name) { - LOGGER.info("Start sourcing"); - return eventBus.<String>consumer(name + "-" + kubernetesService.getNamespace()).toMulti().map(Message::body); - } } \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java index cfa74f84..41861af7 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java @@ -69,27 +69,23 @@ public class DockerForKaravan { } public void runBuildProject(String projectId, String script, Map<String, String> files, List<String> env, Map<String, String> volumes, String tag) throws Exception { - dockerService.deleteContainer(projectId + BUILDER_SUFFIX); - Container c = createBuildContainer(projectId, env, volumes, tag); + String containerName = projectId + BUILDER_SUFFIX; + dockerService.deleteContainer(containerName); + Container c = createBuildContainer(containerName, projectId, env, volumes, tag); dockerService.copyFiles(c.getId(), "/code", files); dockerService.copyExecFile(c.getId(), "/karavan", "build.sh", script); - dockerService.runContainer(projectId); + dockerService.runContainer(c); } - protected Container createBuildContainer(String projectId, List<String> env, Map<String, String> volumes, String tag) throws InterruptedException { - LOGGER.infof("Starting Build Container for %s ", projectId); + protected Container createBuildContainer(String containerName, String projectId, List<String> env, Map<String, String> volumes, String tag) throws InterruptedException { + LOGGER.infof("Starting Build Container ", containerName); - return dockerService.createContainer(projectId + BUILDER_SUFFIX, devmodeImage, + return dockerService.createContainer(containerName, devmodeImage, env, Map.of(), new HealthCheck(), Map.of(LABEL_TYPE, ContainerStatus.ContainerType.build.name(), LABEL_PROJECT_ID, projectId, LABEL_TAG, tag), volumes, null,"/karavan/build.sh"); } - public void createDevserviceContainer(DockerComposeService dockerComposeService) throws InterruptedException { - LOGGER.infof("DevService starting for ", dockerComposeService.getContainer_name()); - dockerService.createContainerFromCompose(dockerComposeService, ContainerStatus.ContainerType.devservice); - } - public void syncImage(String projectId, String tag) throws InterruptedException { String image = registryService.getRegistryWithGroup() + "/" + projectId + ":" + tag; dockerService.pullImage(image); 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 0f680ad8..9d5ac75f 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 @@ -103,6 +103,7 @@ public class DockerService extends DockerServiceUtils { public void stopListeners() throws IOException { dockerEventListener.close(); } + public void createNetwork() { if (!getDockerClient().listNetworksCmd().exec().stream() .filter(n -> n.getName().equals(NETWORK_NAME)) @@ -124,7 +125,7 @@ public class DockerService extends DockerServiceUtils { } public Container getContainerByName(String name) { - List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec(); + List<Container> containers = findContainer(name); return containers.size() > 0 ? containers.get(0) : null; } @@ -142,7 +143,7 @@ public class DockerService extends DockerServiceUtils { } public Container createContainerFromCompose(DockerComposeService compose, ContainerStatus.ContainerType type) throws InterruptedException { - List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(compose.getContainer_name())).exec(); + List<Container> containers = findContainer(compose.getContainer_name()); if (containers.isEmpty()) { LOGGER.infof("Compose Service starting for %s", compose.getContainer_name()); @@ -160,10 +161,15 @@ public class DockerService extends DockerServiceUtils { } } + public List<Container> findContainer(String containerName) { + return getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(containerName)).exec() + .stream().filter(c -> Objects.equals(c.getNames()[0].replaceFirst("/", ""), containerName)).toList(); + } + public Container createContainer(String name, String image, List<String> env, Map<Integer, Integer> ports, HealthCheck healthCheck, Map<String, String> labels, Map<String, String> volumes, String network, String... command) throws InterruptedException { - List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec(); + List<Container> containers = findContainer(name); if (containers.size() == 0) { pullImage(image); @@ -186,7 +192,7 @@ public class DockerService extends DockerServiceUtils { } createContainerCmd.withHostConfig(new HostConfig() .withPortBindings(portBindings) - .withMounts(mounts) + .withMounts(mounts) .withNetworkMode(network != null ? network : NETWORK_NAME)); CreateContainerResponse response = createContainerCmd.exec(); @@ -200,14 +206,17 @@ public class DockerService extends DockerServiceUtils { } public void runContainer(String name) { - List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec(); + List<Container> containers = findContainer(name); if (containers.size() == 1) { - Container container = containers.get(0); - if (container.getState().equals("paused")) { - getDockerClient().unpauseContainerCmd(container.getId()).exec(); - } else if (!container.getState().equals("running")) { - getDockerClient().startContainerCmd(container.getId()).exec(); - } + runContainer(containers.get(0)); + } + } + + public void runContainer(Container container) { + if (container.getState().equals("paused")) { + getDockerClient().unpauseContainerCmd(container.getId()).exec(); + } else if (!container.getState().equals("running")) { + getDockerClient().startContainerCmd(container.getId()).exec(); } } @@ -247,10 +256,10 @@ public class DockerService extends DockerServiceUtils { } public void copyFiles(String containerId, String containerPath, Map<String, String> files) { - String temp = vertx.fileSystem().createTempDirectoryBlocking(containerId); - files.forEach((fileName, code) -> addFile(temp, fileName, code)); - dockerClient.copyArchiveToContainerCmd(containerId).withRemotePath(containerPath) - .withDirChildrenOnly(true).withHostResource(temp).exec(); + String temp = vertx.fileSystem().createTempDirectoryBlocking(containerId); + files.forEach((fileName, code) -> addFile(temp, fileName, code)); + dockerClient.copyArchiveToContainerCmd(containerId).withRemotePath(containerPath) + .withDirChildrenOnly(true).withHostResource(temp).exec(); } public void copyExecFile(String containerId, String containerPath, String filename, String script) { @@ -259,7 +268,7 @@ public class DockerService extends DockerServiceUtils { vertx.fileSystem().writeFileBlocking(path, Buffer.buffer(script)); try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(byteArrayOutputStream)) { + TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(byteArrayOutputStream)) { tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); tarArchive.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); @@ -328,7 +337,7 @@ public class DockerService extends DockerServiceUtils { } public void deleteContainer(String name) { - List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec(); + List<Container> containers = findContainer(name); if (containers.size() == 1) { Container container = containers.get(0); getDockerClient().removeContainerCmd(container.getId()).withForce(true).exec(); @@ -382,4 +391,7 @@ public class DockerService extends DockerServiceUtils { return getDockerClient().listImagesCmd().withShowAll(true).exec().stream() .map(image -> image.getRepoTags()[0]).toList(); } + + public void deleteImage(String imageName) { + } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java index ab7c04b8..e734abc4 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/ContainerStatus.java @@ -4,6 +4,7 @@ import org.infinispan.protostream.annotations.ProtoEnumValue; import org.infinispan.protostream.annotations.ProtoFactory; import org.infinispan.protostream.annotations.ProtoField; +import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -65,8 +66,29 @@ public class ContainerStatus { Boolean codeLoaded; @ProtoField(number = 15) Boolean inTransit = false; + @ProtoField(number = 16) + String initDate; @ProtoFactory + public ContainerStatus(String projectId, String containerName, String containerId, String image, List<Integer> ports, String env, ContainerType type, String memoryInfo, String cpuInfo, String created, String finished, List<Command> commands, String state, Boolean codeLoaded, Boolean inTransit, String initDate) { + this.projectId = projectId; + this.containerName = containerName; + this.containerId = containerId; + this.image = image; + this.ports = ports; + this.env = env; + this.type = type; + this.memoryInfo = memoryInfo; + this.cpuInfo = cpuInfo; + this.created = created; + this.finished = finished; + this.commands = commands; + this.state = state; + this.codeLoaded = codeLoaded; + this.inTransit = inTransit; + this.initDate = initDate; + } + public ContainerStatus(String projectId, String containerName, String containerId, String image, List<Integer> ports, String env, ContainerType type, String memoryInfo, String cpuInfo, String created, String finished, List<Command> commands, String state, Boolean codeLoaded, Boolean inTransit) { this.projectId = projectId; this.containerName = containerName; @@ -83,6 +105,7 @@ public class ContainerStatus { this.state = state; this.codeLoaded = codeLoaded; this.inTransit = inTransit; + this.initDate = Instant.now().toString(); } public ContainerStatus(String containerName, List<Command> commands, String projectId, String env, ContainerType type, String memoryInfo, String cpuInfo, String created) { @@ -94,6 +117,7 @@ public class ContainerStatus { this.memoryInfo = memoryInfo; this.cpuInfo = cpuInfo; this.created = created; + this.initDate = Instant.now().toString(); } public ContainerStatus(String containerName, List<Command> commands, String projectId, String env, ContainerType type, String created) { @@ -103,6 +127,7 @@ public class ContainerStatus { this.env = env; this.created = created; this.type = type; + this.initDate = Instant.now().toString(); } public static ContainerStatus createDevMode(String projectId, String env) { @@ -242,6 +267,14 @@ public class ContainerStatus { this.finished = finished; } + public String getInitDate() { + return initDate; + } + + public void setInitDate(String initDate) { + this.initDate = initDate; + } + @Override public String toString() { return "ContainerStatus{" + diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java index 7f054871..8bc62635 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java @@ -16,6 +16,7 @@ */ package org.apache.camel.karavan.service; +import io.quarkus.scheduler.Scheduled; import io.quarkus.vertx.ConsumeEvent; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.Vertx; @@ -70,6 +71,19 @@ public class CamelService { return webClient; } + @Scheduled(every = "{karavan.camel.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + public void collectCamelStatuses() { + if (infinispanService.isReady()) { + infinispanService.getContainerStatuses(environment).stream() + .filter(cs -> + cs.getType() == ContainerStatus.ContainerType.project + || cs.getType() == ContainerStatus.ContainerType.devmode + ).forEach(pod -> { + CamelStatusRequest csr = new CamelStatusRequest(pod.getProjectId(), pod.getContainerName()); + eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr)); + }); + } + } private boolean camelIsStarted(CamelStatus camelStatus) { try { @@ -129,19 +143,6 @@ public class CamelService { } } - public void collectCamelStatuses() { - if (infinispanService.isReady()) { - infinispanService.getContainerStatuses(environment).stream() - .filter(cs -> - cs.getType() == ContainerStatus.ContainerType.project - || cs.getType() == ContainerStatus.ContainerType.devmode - ).forEach(pod -> { - CamelStatusRequest csr = new CamelStatusRequest(pod.getProjectId(), pod.getContainerName()); - eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr)); - }); - } - } - @ConsumeEvent(value = CMD_COLLECT_CAMEL_STATUS, blocking = true, ordered = true) public void collectCamelStatuses(JsonObject data) { CamelStatusRequest dms = data.mapTo(CamelStatusRequest.class); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ContainerStatusService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ContainerStatusService.java index 5fc126eb..c3b310bc 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ContainerStatusService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ContainerStatusService.java @@ -1,14 +1,20 @@ package org.apache.camel.karavan.service; +import io.quarkus.scheduler.Scheduled; import io.quarkus.vertx.ConsumeEvent; +import io.vertx.core.eventbus.EventBus; import io.vertx.core.json.JsonObject; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import org.apache.camel.karavan.docker.DockerService; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.List; import java.util.Objects; @ApplicationScoped @@ -16,10 +22,60 @@ public class ContainerStatusService { public static final String CONTAINER_STATUS = "CONTAINER_STATUS"; private static final Logger LOGGER = Logger.getLogger(ContainerStatusService.class.getName()); + @ConfigProperty(name = "karavan.environment") + String environment; @Inject InfinispanService infinispanService; + @Inject + DockerService dockerService; + + @Inject + EventBus eventBus; + + @Scheduled(every = "{karavan.container.statistics.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + void collectContainersStatistics() { + if (infinispanService.isReady()) { + List<ContainerStatus> statusesInDocker = dockerService.collectContainersStatistics(); + statusesInDocker.forEach(containerStatus -> { + eventBus.send(ContainerStatusService.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); + }); + } + } + + @Scheduled(every = "{karavan.container.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + void collectContainersStatuses() { + if (infinispanService.isReady()) { + List<ContainerStatus> statusesInDocker = dockerService.collectContainersStatuses(); + statusesInDocker.forEach(containerStatus -> { + eventBus.send(ContainerStatusService.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); + }); + cleanContainersStatuses(statusesInDocker); + } + } + + void cleanContainersStatuses(List<ContainerStatus> statusesInDocker) { + if (infinispanService.isReady()) { + List<String> namesInDocker = statusesInDocker.stream().map(ContainerStatus::getContainerName).toList(); + List<ContainerStatus> statusesInInfinispan = infinispanService.getContainerStatuses(environment); + // clean deleted + statusesInInfinispan.stream() + .filter(cs -> !checkTransit(cs)) + .filter(cs -> !namesInDocker.contains(cs.getContainerName())) + .forEach(containerStatus -> { + infinispanService.deleteContainerStatus(containerStatus); + infinispanService.deleteCamelStatuses(containerStatus.getProjectId(), containerStatus.getEnv()); + }); + } + } + + private boolean checkTransit(ContainerStatus cs) { + if (cs.getContainerId() == null && cs.getInTransit()) { + return Instant.parse(cs.getInitDate()).until(Instant.now(), ChronoUnit.SECONDS) < 10; + } + return false; + } @ConsumeEvent(value = CONTAINER_STATUS, blocking = true, ordered = true) public void saveContainerStatus(JsonObject data) { 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 5ce1e34f..ebd49e08 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 @@ -387,4 +387,12 @@ public class ProjectService implements HealthCheck { } } } + + public DockerComposeService getProjectDockerComposeService(String projectId) { + ProjectFile file = infinispanService.getProjectFile(projectId, PROJECT_COMPOSE_FILENAME); + if (file != null) { + return DockerComposeConverter.fromCode(file.getCode(), projectId); + } + return null; + } } 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 deleted file mode 100644 index 1a90dc6c..00000000 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ScheduledService.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.camel.karavan.service; - -import io.quarkus.scheduler.Scheduled; -import io.vertx.core.eventbus.EventBus; -import io.vertx.core.json.JsonObject; -import org.apache.camel.karavan.docker.DockerForInfinispan; -import org.apache.camel.karavan.docker.DockerService; -import org.apache.camel.karavan.infinispan.InfinispanService; -import org.apache.camel.karavan.infinispan.model.ContainerStatus; -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.jboss.logging.Logger; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import java.util.List; - -@ApplicationScoped -public class ScheduledService { - - private static final Logger LOGGER = Logger.getLogger(ScheduledService.class.getName()); - - @ConfigProperty(name = "karavan.environment") - String environment; - - @Inject - DockerService dockerService; - - @Inject - DockerForInfinispan dockerForInfinispan; - - @Inject - ProjectService projectService; - - @Inject - CamelService camelService; - - @Inject - InfinispanService infinispanService; - - @Inject - EventBus eventBus; - - @Scheduled(every = "{karavan.container.statistics.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) - void collectContainersStatistics() { - if (infinispanService.isReady()) { - List<ContainerStatus> statusesInDocker = dockerService.collectContainersStatistics(); - statusesInDocker.forEach(containerStatus -> { - eventBus.send(ContainerStatusService.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); - }); - } - } - - @Scheduled(every = "{karavan.container.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) - void collectContainersStatuses() { - if (infinispanService.isReady()) { - List<ContainerStatus> statusesInDocker = dockerService.collectContainersStatuses(); - statusesInDocker.forEach(containerStatus -> { - eventBus.send(ContainerStatusService.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); - }); - cleanContainersStatuses(statusesInDocker); - } - } - - void cleanContainersStatuses(List<ContainerStatus> statusesInDocker) { - if (infinispanService.isReady()) { - List<String> namesInDocker = statusesInDocker.stream().map(ContainerStatus::getContainerName).toList(); - List<ContainerStatus> statusesInInfinispan = infinispanService.getContainerStatuses(environment); - // clean deleted - statusesInInfinispan.stream() - .filter(cs -> !(cs.getContainerId() == null && cs.getInTransit())) - .filter(cs -> !namesInDocker.contains(cs.getContainerName())) - .forEach(containerStatus -> { - infinispanService.deleteContainerStatus(containerStatus); - infinispanService.deleteCamelStatuses(containerStatus.getProjectId(), containerStatus.getEnv()); - }); - } - } - - - @Scheduled(every = "{karavan.camel.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) - void collectCamelStatuses() { - camelService.collectCamelStatuses(); - } -} diff --git a/karavan-web/karavan-app/src/main/resources/snippets/camel-main-docker-application.properties b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-docker-application.properties index 5048638c..2ce5adb2 100644 --- a/karavan-web/karavan-app/src/main/resources/snippets/camel-main-docker-application.properties +++ b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-docker-application.properties @@ -5,6 +5,9 @@ camel.jbang.gav=org.camel.karavan.demo:{projectId}:1 camel.jbang.runtime=camel-main camel.jbang.version=4.0.0-RC2 camel.jbang.dependencies=camel-console,camel-platform-http-main +camel.context.dev-console=true +camel.main.backlogTracing=true +camel.main.beanIntrospectionExtendedStatistics=true camel.health.enabled=true camel.health.exposure-level=full camel.server.enabled=true diff --git a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx index d6bcfcd2..fd66d5cd 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx @@ -412,7 +412,7 @@ export class KaravanApi { } static async getContainerLog(environment: string, name: string, after: (res: AxiosResponse<string>) => void) { - instance.get('/api/infrastructure/container/log/' + environment + "/" + name) + instance.get('/api/container/log/' + environment + "/" + name) .then(res => { if (res.status === 200) { after(res.data); @@ -434,7 +434,7 @@ export class KaravanApi { } static async getAllContainerStatuses(after: (statuses: ContainerStatus[]) => void) { - instance.get('/api/infrastructure/container') + instance.get('/api/container') .then(res => { if (res.status === 200) { after(res.data); @@ -484,19 +484,12 @@ export class KaravanApi { }); } - static async getProjectPodStatuses(project: string, env: string, after: (statuses: ContainerStatus[]) => void) { - instance.get('/api/infrastructure/container/' + project + "/" + env) - .then(res => { - if (res.status === 200) { - after(res.data); - } - }).catch(err => { - console.log(err); - }); - } - - static async manageContainer(environment: string, type: 'devmove' | 'devservice' | 'project' | 'internal' | 'unknown', name: string, command: 'run' | 'pause' | 'stop', after: (res: AxiosResponse<any>) => void) { - instance.post('/api/infrastructure/container/' + environment + '/' + type + "/" + name, {command: command}) + static async manageContainer(environment: string, + type: 'devmove' | 'devservice' | 'project' | 'internal' | 'build' | 'unknown', + name: string, + command: 'run' | 'pause' | 'stop' | 'delete', + after: (res: AxiosResponse<any>) => void) { + instance.post('/api/container/' + environment + '/' + type + "/" + name, {command: command}) .then(res => { after(res); }).catch(err => { @@ -504,8 +497,8 @@ export class KaravanApi { }); } - static async deleteContainer(environment: string, type: 'devmove' | 'devservice' | 'project' | 'internal' | 'unknown', name: string, after: (res: AxiosResponse<any>) => void) { - instance.delete('/api/infrastructure/container/' + environment + '/' + type + "/" + name) + static async deleteContainer(environment: string, type: 'devmove' | 'devservice' | 'project' | 'internal' | 'build' | 'unknown', name: string, after: (res: AxiosResponse<any>) => void) { + instance.delete('/api/container/' + environment + '/' + type + "/" + name) .then(res => { after(res); }).catch(err => { diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts index 460690b7..8dd5e485 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts +++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts @@ -73,7 +73,7 @@ export class ContainerStatus { deployment: string = ''; projectId: string = ''; env: string = ''; - type: 'devmove' | 'devservice' | 'project' | 'internal' | 'unknown' = 'unknown'; + type: 'devmove' | 'devservice' | 'project' | 'internal' | 'build' | 'unknown' = 'unknown'; memoryInfo: string = ''; cpuInfo: string = ''; created: string = ''; diff --git a/karavan-web/karavan-app/src/main/webui/src/project/BuildToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/BuildToolbar.tsx index 1fa7a8f0..e18284c5 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/BuildToolbar.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/BuildToolbar.tsx @@ -1,9 +1,18 @@ import React from 'react'; -import {Button, Flex, FlexItem} from '@patternfly/react-core'; +import {Button, Flex, FlexItem, Label, Spinner, Tooltip, TooltipPosition} from '@patternfly/react-core'; import '../designer/karavan.css'; import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/trash-icon"; -import {useAppConfigStore} from "../api/ProjectStore"; +import {useAppConfigStore, useDevModeStore, useLogStore, useProjectStore, useStatusesStore} from "../api/ProjectStore"; import {shallow} from "zustand/shallow"; +import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon"; +import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon"; +import RunIcon from "@patternfly/react-icons/dist/esm/icons/play-icon"; +import {ProjectService} from "../api/ProjectService"; +import ReloadIcon from "@patternfly/react-icons/dist/esm/icons/bolt-icon"; +import {KaravanApi} from "../api/KaravanApi"; +import {ProjectEventBus} from "../api/ProjectEventBus"; +import {EventBus} from "../designer/utils/EventBus"; +import StopIcon from "@patternfly/react-icons/dist/js/icons/stop-icon"; interface Props { reloadOnly?: boolean @@ -12,11 +21,78 @@ interface Props { export function BuildToolbar (props: Props) { const [config] = useAppConfigStore((state) => [state.config], shallow) + const [status] = useDevModeStore((state) => [state.status], shallow) + const [project] = useProjectStore((state) => [state.project], shallow) + const [containers] = useStatusesStore((state) => [state.containers], shallow); + const [setShowLog] = useLogStore((s) => [s.setShowLog], shallow); + + const containerStatus = containers.filter(c => c.containerName === project.projectId).at(0); + const commands = containerStatus?.commands || ['run']; + const isRunning = containerStatus?.state === 'running'; + const inTransit = containerStatus?.inTransit; + const isLoading = status === 'wip'; + const color = containerStatus?.state === 'running' ? "green" : "grey"; + const icon = isRunning ? <UpIcon/> : <DownIcon/>; return (<Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}> <FlexItem> - <Button style={{visibility:"hidden"}} size="sm" variant={"control"} icon={<DeleteIcon/>} onClick={() => {}}> + {(inTransit || isLoading) && <Spinner size="lg" aria-label="spinner"/>} + </FlexItem> + {containerStatus?.containerId && <FlexItem> + <Label icon={icon} color={color}> + <Tooltip content={"Show log"} position={TooltipPosition.bottom}> + <Button className='labeled-button' variant="link" isDisabled={!isRunning} + onClick={e => + setShowLog(true, 'container', containerStatus.containerName)}> + {containerStatus.containerName} + </Button> + </Tooltip> + </Label> + </FlexItem>} + {!isRunning && <FlexItem> + <Tooltip content="Run container" position={TooltipPosition.bottom}> + <Button size="sm" + isDisabled={(!(commands.length === 0) && !commands.includes('run')) || inTransit} + variant={"primary"} + icon={<RunIcon/>} + onClick={() => { + KaravanApi.manageContainer('dev', 'project', project.projectId, 'run', res => { + setShowLog(false, 'container', undefined) + }); + }}> + {"Run"} </Button> + </Tooltip> + </FlexItem>} + {config.infrastructure !== 'kubernetes' && + <FlexItem> + <Tooltip content="Stop container" position={TooltipPosition.bottom}> + <Button size="sm" + isDisabled={!commands.includes('stop') || inTransit} + variant={"control"} + icon={<StopIcon/>} + onClick={() => { + KaravanApi.manageContainer('dev', 'project', project.projectId, 'stop', res => { + setShowLog(false, 'container', undefined) + }); + }}> + </Button> + </Tooltip> + </FlexItem> + } + <FlexItem> + <Tooltip content="Delete container" position={TooltipPosition.bottom}> + <Button size="sm" + isDisabled={!commands.includes('delete') || inTransit} + variant={"control"} + icon={<DeleteIcon/>} + onClick={() => { + KaravanApi.manageContainer('dev', 'project', project.projectId, 'delete', res => { + setShowLog(false, 'container', undefined) + }); + }}> + </Button> + </Tooltip> </FlexItem> </Flex>); -} +} \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx index 6c916da4..e761bbcb 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx @@ -14,7 +14,7 @@ export function ProjectBuildTab () { <div> {/*{["dev", "test", "prod"].map(env =>*/} {config.environments.map(env => - <BuildPanel env={env}/> + <BuildPanel key={env} env={env}/> )} </div> </PageSection> diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx index 6a2b9986..af950b60 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx @@ -42,7 +42,6 @@ export function RunnerInfoTraceModal (props: Props) { return Array.from(new Set((props.nodes).map((item: any) => item?.routeId))); } - console.log(props.nodes) return ( <Modal title={"Exchange: " + props.exchangeId}
