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 93e99f50bc7c5a4b9a18ff1e35a5af558b83e0eb Author: Marat Gubaidullin <[email protected]> AuthorDate: Wed Sep 6 10:14:29 2023 -0400 Image builder for #817 --- .../apache/camel/karavan/api/ProjectResource.java | 45 +++++++++ .../camel/karavan/docker/DockerEventListener.java | 5 +- .../camel/karavan/docker/DockerForKaravan.java | 103 +++------------------ .../apache/camel/karavan/docker/DockerService.java | 63 +++++++++---- .../camel/karavan/docker/DockerServiceUtils.java | 21 ++--- .../karavan/infinispan/InfinispanService.java | 26 ------ .../camel/karavan/infinispan/model/CommitInfo.java | 53 ----------- .../karavan/infinispan/model/ContainerStatus.java | 35 ++++--- .../apache/camel/karavan/service/CodeService.java | 19 +--- .../apache/camel/karavan/service/EventService.java | 16 +++- .../apache/camel/karavan/service/GitService.java | 38 -------- .../camel/karavan/service/ProjectService.java | 81 +++++++--------- .../camel/karavan/service/ScheduledService.java | 32 ++++--- .../org/apache/camel/karavan/shared/Constants.java | 2 +- .../src/main/resources/application.properties | 7 +- .../snippets/camel-main-builder-script-docker.sh | 1 - .../src/main/webui/src/api/KaravanApi.tsx | 15 ++- .../src/main/webui/src/api/ProjectModels.ts | 1 + .../src/main/webui/src/api/ProjectStore.ts | 6 +- .../webui/src/containers/ContainerTableRow.tsx | 2 +- .../main/webui/src/project/build/BuildStatus.tsx | 76 ++++++++++++--- 21 files changed, 294 insertions(+), 353 deletions(-) diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java index b62ad193..b0bdf4cd 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java @@ -16,16 +16,22 @@ */ package org.apache.camel.karavan.api; +import jakarta.ws.rs.core.Response; +import org.apache.camel.karavan.docker.DockerService; import org.apache.camel.karavan.infinispan.InfinispanService; +import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.infinispan.model.GroupedKey; import org.apache.camel.karavan.infinispan.model.Project; import org.apache.camel.karavan.infinispan.model.ProjectFile; +import org.apache.camel.karavan.kubernetes.KubernetesService; import org.apache.camel.karavan.service.CodeService; import org.apache.camel.karavan.service.GitService; import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import org.apache.camel.karavan.service.ProjectService; +import org.apache.camel.karavan.shared.ConfigService; +import org.eclipse.microprofile.config.inject.ConfigProperty; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; @@ -35,12 +41,23 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import static org.apache.camel.karavan.shared.Constants.BUILDER_SUFFIX; + @Path("/api/project") public class ProjectResource { + @ConfigProperty(name = "karavan.environment") + String environment; + @Inject InfinispanService infinispanService; + @Inject + KubernetesService kubernetesService; + + @Inject + DockerService dockerService; + @Inject GitService gitService; @@ -85,6 +102,34 @@ public class ProjectResource { infinispanService.deleteProject(projectId); } + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/build") + public Response build(Project project) throws Exception { + try { + projectService.buildProject(project); + return Response.ok().entity(project).build(); + } catch (Exception e) { + return Response.serverError().entity(e.getMessage()).build(); + } + } + + @DELETE + @Produces(MediaType.APPLICATION_JSON) + @Path("/build/{env}/{buildName}") + public Response deleteBuild(@HeaderParam("username") String username, + @PathParam("env") String env, @PathParam("buildName") String buildName) { + buildName = URLDecoder.decode(buildName, StandardCharsets.UTF_8); + if (ConfigService.inKubernetes()) { + kubernetesService.stopPipelineRun(buildName, kubernetesService.getNamespace()); + return Response.ok().build(); + } else { + dockerService.deleteContainer(buildName); + return Response.ok().build(); + } + } + @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) 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 8ed7b546..86195c79 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 @@ -85,9 +85,10 @@ public class DockerEventListener implements ResultCallback<Event> { List<ContainerStatus.Command> commands = getContainerCommand(container.getState()); ContainerStatus.ContainerType type = getContainerType(container.getLabels()); String created = Instant.ofEpochSecond(container.getCreated()).toString(); - ContainerStatus ci = infinispanService.getContainerStatus(name, environment, name); + String projectId = container.getLabels().getOrDefault(LABEL_PROJECT_ID, name); + ContainerStatus ci = infinispanService.getContainerStatus(projectId, environment, name); if (ci == null) { - ci = ContainerStatus.createWithId(name, environment, container.getId(), container.getImage(), ports, type, commands, container.getState(), created); + ci = ContainerStatus.createWithId(projectId, name, environment, container.getId(), container.getImage(), ports, type, commands, container.getState(), created); } else { ci.setContainerId(container.getId()); ci.setPorts(ports); 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 38a5a563..474265d2 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 @@ -16,6 +16,7 @@ */ package org.apache.camel.karavan.docker; +import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.HealthCheck; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -46,12 +47,12 @@ public class DockerForKaravan { DockerService dockerService; public void runProjectInDevMode(String projectId, String jBangOptions, Map<Integer, Integer> ports, Map<String, String> files) throws Exception { - createDevmodeContainer(projectId, jBangOptions, ports); + Container c = createDevmodeContainer(projectId, jBangOptions, ports); dockerService.runContainer(projectId); - dockerService.copyFiles(projectId, "/code", files); + dockerService.copyFiles(c.getId(), "/code", files); } - protected void createDevmodeContainer(String projectId, String jBangOptions, Map<Integer, Integer> ports) throws InterruptedException { + protected Container createDevmodeContainer(String projectId, String jBangOptions, Map<Integer, Integer> ports) throws InterruptedException { LOGGER.infof("DevMode starting for %s with JBANG_OPTIONS=%s", projectId, jBangOptions); HealthCheck healthCheck = new HealthCheck().withTest(List.of("CMD", "curl", "-f", "http://localhost:8080/q/dev/health")) @@ -61,101 +62,27 @@ public class DockerForKaravan { ? List.of(ENV_VAR_JBANG_OPTIONS + "=" + jBangOptions) : List.of(); - dockerService.createContainer(projectId, devmodeImage, + return dockerService.createContainer(projectId, devmodeImage, env, ports, healthCheck, Map.of(LABEL_TYPE, ContainerStatus.ContainerType.devmode.name(), LABEL_PROJECT_ID, projectId), Map.of()); - LOGGER.infof("DevMode started for %s", projectId); } - public void runBuildProject(String projectId, String script, Map<String, String> files) throws Exception { -// createBuildContainer(projectId, jBangOptions, ports); -// dockerService.runContainer(projectId); -// dockerService.copyFiles(projectId, "/code", files); - - - // String scriptName = "camel-main-builder-script-docker.sh"; -// String script = getResourceFile("/scripts/" + scriptName); -// try { -// CreateContainerResponse res = dockerClient.createContainerCmd("test") -// .withName("xxx") -// .withImage("ghcr.io/apache/camel-karavan-devmode:4.0.0-RC2") -// .withCmd("/karavan/"+scriptName) -// .withEnv("GIT_BRANCH=main", -// "GIT_REPOSITORY=http://gitea:3000/karavan/karavan.git", -// "GIT_USERNAME=karavan", -// "GIT_PASSWORD=karavan", -// "PROJECT_ID=zzzzz", -// "CAMEL_VERSION=4.0.0", -// "IMAGE_REGISTRY=registry:5000", -// "IMAGE_GROUP=karavan") -// .withHostConfig(new HostConfig().withNetworkMode("karavan")).exec(); -// -// } catch (Exception e) { -// System.out.println(e.getMessage()); -// } -// -// Container c = dockerClient.listContainersCmd().withShowAll(true).withNameFilter(Collections.singleton("xxx")).exec().get(0); -// -// String temp = Vertx.vertx().fileSystem().createTempDirectoryBlocking("xxx"); -// String path = temp + File.separator + scriptName; -// Vertx.vertx().fileSystem().writeFileBlocking(path, Buffer.buffer(script)); -// -// try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); -// TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(byteArrayOutputStream)) { -// tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); -// tarArchive.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); -// -// TarArchiveEntry tarEntry = new TarArchiveEntry(new File(path)); -// tarEntry.setName(scriptName); -// tarEntry.setMode(0700); // -// tarArchive.putArchiveEntry(tarEntry); -// IOUtils.write(Files.readAllBytes(Paths.get(path)), tarArchive); -// tarArchive.closeArchiveEntry(); -// tarArchive.finish(); -// -// dockerClient.copyArchiveToContainerCmd(c.getId()) -// .withTarInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())) -// .withRemotePath("/karavan").exec(); -// } catch (Exception e) { -// e.printStackTrace(); -// } - -// dockerClient.startContainerCmd(c.getId()).exec(); + public void runBuildProject(String projectId, String script, Map<String, String> files, List<String> env) throws Exception { + Container c = createBuildContainer(projectId, env); + dockerService.copyFiles(c.getId(), "/code", files); + dockerService.copyExecFile(c.getId(), "/karavan", "build.sh", script); + dockerService.runContainer(projectId); } - - protected void createBuildContainer(String projectId, Map<Integer, Integer> ports) throws InterruptedException { + protected Container createBuildContainer(String projectId, List<String> env) throws InterruptedException { LOGGER.infof("Starting Build Container for %s ", projectId); - List<String> env = List.of( - "GIT_REPOSITORY=http://gitea:3000/karavan/karavan.git", - "GIT_USERNAME=karavan", - "GIT_PASSWORD=karavan", - "PROJECT_ID=" + projectId, - "CAMEL_VERSION=4.0.0", - "IMAGE_REGISTRY=registry:5000", - "IMAGE_GROUP=karavan" - ); - -// .withCmd("/karavan/"+scriptName) -// .withEnv("GIT_BRANCH=main", -// "GIT_REPOSITORY=http://gitea:3000/karavan/karavan.git", -// "GIT_USERNAME=karavan", -// "GIT_PASSWORD=karavan", -// "PROJECT_ID=zzzzz", -// "CAMEL_VERSION=4.0.0", -// "IMAGE_REGISTRY=registry:5000", -// "IMAGE_GROUP=karavan") -// .withHostConfig(new HostConfig().withNetworkMode("karavan")).exec(); - - dockerService.createContainer(projectId + "-builder", devmodeImage, - env, ports, new HealthCheck(), - Map.of(LABEL_TYPE, ContainerStatus.ContainerType.devmode.name(), LABEL_PROJECT_ID, projectId), - Map.of()); - - LOGGER.infof("Build Container started for %s", projectId); + return dockerService.createContainer(projectId + BUILDER_SUFFIX, devmodeImage, + env, Map.of(), new HealthCheck(), + Map.of(LABEL_TYPE, ContainerStatus.ContainerType.build.name(), LABEL_PROJECT_ID, projectId), + Map.of(), "/karavan/build.sh"); } public void createDevserviceContainer(DockerComposeService dockerComposeService) throws InterruptedException { 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 04ef2043..fcceccd2 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 @@ -24,7 +24,6 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.core.InvocationBuilder; -import com.github.dockerjava.core.util.CompressArchiveUtil; import com.github.dockerjava.transport.DockerHttpClient; import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; import io.vertx.core.Vertx; @@ -33,16 +32,14 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.karavan.docker.model.DockerComposeService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; -import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; -import org.apache.commons.compress.utils.IOUtils; +import org.apache.commons.io.IOUtils; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; import java.io.*; -import java.nio.file.LinkOption; +import java.nio.file.Files; import java.nio.file.Paths; import java.time.Instant; import java.util.*; @@ -82,21 +79,21 @@ public class DockerService extends DockerServiceUtils { public List<ContainerStatus> collectContainersStatuses() { List<ContainerStatus> result = new ArrayList<>(); getDockerClient().listContainersCmd().withShowAll(true).exec().forEach(container -> { - ContainerStatus containerStatus = getContainerStatus(container); - Statistics stats = getContainerStats(container.getId()); - updateStatistics(containerStatus, container, stats); + ContainerStatus containerStatus = getContainerStatus(container, environment); result.add(containerStatus); }); return result; } - private ContainerStatus getContainerStatus(Container container) { - String name = container.getNames()[0].replace("/", ""); - List<Integer> ports = Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList()); - List<ContainerStatus.Command> commands = getContainerCommand(container.getState()); - ContainerStatus.ContainerType type = getContainerType(container.getLabels()); - String created = Instant.ofEpochSecond(container.getCreated()).toString(); - return ContainerStatus.createWithId(name, environment, container.getId(), container.getImage(), ports, type, commands, container.getState(), created); + public List<ContainerStatus> collectContainersStatistics() { + List<ContainerStatus> result = new ArrayList<>(); + getDockerClient().listContainersCmd().withShowAll(true).exec().forEach(container -> { + ContainerStatus containerStatus = getContainerStatus(container, environment); + Statistics stats = getContainerStats(container.getId()); + updateStatistics(containerStatus, container, stats); + result.add(containerStatus); + }); + return result; } public void startListeners() { @@ -165,7 +162,7 @@ public class DockerService extends DockerServiceUtils { public Container createContainer(String name, String image, List<String> env, Map<Integer, Integer> ports, HealthCheck healthCheck, Map<String, String> labels, - Map<String, String> volumes) throws InterruptedException { + Map<String, String> volumes, String... command) throws InterruptedException { List<Container> containers = getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(name)).exec(); if (containers.size() == 0) { pullImage(image); @@ -181,7 +178,10 @@ public class DockerService extends DockerServiceUtils { mounts.add(new Mount().withType(MountType.BIND).withSource(hostPath).withTarget(containerPath)); }); } -// createContainerCmd.withExposedPorts(exposedPorts); + if (command.length > 0) { + createContainerCmd.withCmd(command); + } + createContainerCmd.withHostConfig(new HostConfig() .withPortBindings(portBindings) .withMounts(mounts) @@ -244,13 +244,40 @@ public class DockerService extends DockerServiceUtils { dockerClient.execStartCmd(id).exec(callBack).awaitCompletion(); } - public void copyFiles(String containerId, String containerPath, Map<String, String> files) throws IOException { + 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(); } + public void copyExecFile(String containerId, String containerPath, String filename, String script) { + String temp = vertx.fileSystem().createTempDirectoryBlocking(containerId); + String path = temp + File.separator + filename; + vertx.fileSystem().writeFileBlocking(path, Buffer.buffer(script)); + + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(byteArrayOutputStream)) { + tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + tarArchive.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); + + TarArchiveEntry tarEntry = new TarArchiveEntry(new File(path)); + tarEntry.setName(filename); + tarEntry.setMode(0700); + tarArchive.putArchiveEntry(tarEntry); + IOUtils.write(Files.readAllBytes(Paths.get(path)), tarArchive); + tarArchive.closeArchiveEntry(); + tarArchive.finish(); + + dockerClient.copyArchiveToContainerCmd(containerId) + .withTarInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())) + .withRemotePath(containerPath).exec(); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + e.printStackTrace(); + } + } + private void addFile(String temp, String fileName, String code) { try { String path = temp + File.separator + fileName; diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java index 0b8b698f..91ae2cf9 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerServiceUtils.java @@ -18,18 +18,13 @@ package org.apache.camel.karavan.docker; import com.github.dockerjava.api.model.*; import io.smallrye.mutiny.tuples.Tuple2; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; import org.apache.camel.karavan.api.KameletResources; -import org.apache.camel.karavan.docker.model.DockerComposeService; import org.apache.camel.karavan.docker.model.HealthCheckConfig; import org.apache.camel.karavan.infinispan.model.ContainerStatus; -import org.apache.camel.karavan.service.CodeService; -import org.yaml.snakeyaml.Yaml; -import jakarta.inject.Inject; - -import java.io.*; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; import java.text.DecimalFormat; import java.time.Duration; import java.time.Instant; @@ -38,6 +33,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static org.apache.camel.karavan.shared.Constants.LABEL_PROJECT_ID; import static org.apache.camel.karavan.shared.Constants.LABEL_TYPE; public class DockerServiceUtils { @@ -47,17 +43,14 @@ public class DockerServiceUtils { protected static final DecimalFormat formatGiB = new DecimalFormat("0.00"); protected static final Map<String, Tuple2<Long, Long>> previousStats = new ConcurrentHashMap<>(); - @Inject - CodeService codeService; - - protected ContainerStatus getContainerStatus(Container container, String environment) { String name = container.getNames()[0].replace("/", ""); List<Integer> ports = Arrays.stream(container.getPorts()).map(ContainerPort::getPrivatePort).filter(Objects::nonNull).collect(Collectors.toList()); List<ContainerStatus.Command> commands = getContainerCommand(container.getState()); ContainerStatus.ContainerType type = getContainerType(container.getLabels()); String created = Instant.ofEpochSecond(container.getCreated()).toString(); - return ContainerStatus.createWithId(name, environment, container.getId(), container.getImage(), ports, type, commands, container.getState(), created); + String projectId = container.getLabels().getOrDefault(LABEL_PROJECT_ID, name); + return ContainerStatus.createWithId(projectId, name, environment, container.getId(), container.getImage(), ports, type, commands, container.getState(), created); } protected void updateStatistics(ContainerStatus containerStatus, Container container, Statistics stats) { @@ -141,6 +134,8 @@ public class DockerServiceUtils { return ContainerStatus.ContainerType.project; } else if (Objects.equals(type, ContainerStatus.ContainerType.internal.name())) { return ContainerStatus.ContainerType.internal; + } else if (Objects.equals(type, ContainerStatus.ContainerType.build.name())) { + return ContainerStatus.ContainerType.build; } return ContainerStatus.ContainerType.unknown; } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java index 5f48425d..cda9603d 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java @@ -17,9 +17,7 @@ package org.apache.camel.karavan.infinispan; import io.smallrye.mutiny.tuples.Tuple2; -import io.vertx.core.eventbus.EventBus; import jakarta.enterprise.inject.Default; -import jakarta.inject.Inject; import jakarta.inject.Singleton; import org.apache.camel.karavan.infinispan.model.*; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -60,9 +58,6 @@ public class InfinispanService implements HealthCheck { @ConfigProperty(name = "karavan.infinispan.password") String infinispanPassword; - @Inject - EventBus eventBus; - private RemoteCache<GroupedKey, Project> projects; private RemoteCache<GroupedKey, ProjectFile> files; private RemoteCache<GroupedKey, PipelineStatus> pipelineStatuses; @@ -71,7 +66,6 @@ public class InfinispanService implements HealthCheck { private RemoteCache<GroupedKey, Boolean> transits; private RemoteCache<GroupedKey, ServiceStatus> serviceStatuses; private RemoteCache<GroupedKey, CamelStatus> camelStatuses; - private RemoteCache<String, String> commits; private final AtomicBoolean ready = new AtomicBoolean(false); private RemoteCacheManager cacheManager; @@ -111,7 +105,6 @@ public class InfinispanService implements HealthCheck { deploymentStatuses = getOrCreateCache(DeploymentStatus.CACHE); serviceStatuses = getOrCreateCache(ServiceStatus.CACHE); camelStatuses = getOrCreateCache(CamelStatus.CACHE); - commits = getOrCreateCache("commits"); transits = getOrCreateCache("transits"); deploymentStatuses = getOrCreateCache(DeploymentStatus.CACHE); @@ -349,25 +342,6 @@ public class InfinispanService implements HealthCheck { }); } - public void saveCommit(String commitId, int time) { - commits.put(commitId, String.valueOf(time)); - } - - public void saveLastCommit(String commitId) { - commits.put("lastCommitId", commitId); - } - - public Tuple2<String, Integer> getLastCommit() { - String lastCommitId = commits.get("lastCommitId"); - String time = commits.get(lastCommitId); - return Tuple2.of(lastCommitId, Integer.parseInt(time)); - } - - public boolean hasCommit(String commitId) { - return commits.get(commitId) != null; - } - - public List<ContainerStatus> getLoadedDevModeStatuses() { QueryFactory queryFactory = Search.getQueryFactory(containerStatuses); return queryFactory.<ContainerStatus>create("FROM karavan.ContainerStatus WHERE type = :type AND codeLoaded = true") diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/CommitInfo.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/CommitInfo.java deleted file mode 100644 index b7ee2075..00000000 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/CommitInfo.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.apache.camel.karavan.infinispan.model; - -import java.util.List; - -public class CommitInfo { - private String commitId; - private Integer time; - private List<GitRepo> repos; - - public CommitInfo(String commitId, Integer time) { - this.commitId = commitId; - this.time = time; - } - - public CommitInfo(String commitId, Integer time, List<GitRepo> repos) { - this.commitId = commitId; - this.time = time; - this.repos = repos; - } - - public String getCommitId() { - return commitId; - } - - public void setCommitId(String commitId) { - this.commitId = commitId; - } - - public Integer getTime() { - return time; - } - - public void setTime(Integer time) { - this.time = time; - } - - public List<GitRepo> getRepos() { - return repos; - } - - public void setRepos(List<GitRepo> repos) { - this.repos = repos; - } - - @Override - public String toString() { - return "CommitInfo{" + - "commitId='" + commitId + '\'' + - ", time=" + time + - ", repos=" + repos + - '}'; - } -} 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 c9336b8e..ab7c04b8 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 @@ -23,7 +23,8 @@ public class ContainerStatus { @ProtoEnumValue(number = 1, name = "devmode") devmode, @ProtoEnumValue(number = 2, name = "devservice") devservice, @ProtoEnumValue(number = 4, name = "project") project, - @ProtoEnumValue(number = 5, name = "unknown") unknown, + @ProtoEnumValue(number = 5, name = "build") build, + @ProtoEnumValue(number = 6, name = "unknown") unknown, } public enum Command { @@ -55,16 +56,18 @@ public class ContainerStatus { @ProtoField(number = 10) String created; @ProtoField(number = 11) - List<Command> commands; + String finished; @ProtoField(number = 12) - String state; + List<Command> commands; @ProtoField(number = 13) - Boolean codeLoaded; + String state; @ProtoField(number = 14) + Boolean codeLoaded; + @ProtoField(number = 15) Boolean inTransit = false; @ProtoFactory - public ContainerStatus(String projectId, String containerName, String containerId, String image, List<Integer> ports, String env, ContainerType type, String memoryInfo, String cpuInfo, String created, List<Command> commands, String state, Boolean codeLoaded, Boolean inTransit) { + 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; this.containerId = containerId; @@ -75,6 +78,7 @@ public class ContainerStatus { this.memoryInfo = memoryInfo; this.cpuInfo = cpuInfo; this.created = created; + this.finished = finished; this.commands = commands; this.state = state; this.codeLoaded = codeLoaded; @@ -102,16 +106,16 @@ public class ContainerStatus { } public static ContainerStatus createDevMode(String projectId, String env) { - return new ContainerStatus(projectId, projectId, null, null, null, env, ContainerType.devmode, null, null, null, List.of(Command.run), null, false, false); + return new ContainerStatus(projectId, projectId, null, null, null, env, ContainerType.devmode, null, null, null, null, List.of(Command.run), null, false, false); } public static ContainerStatus createByType(String name, String env, ContainerType type) { - return new ContainerStatus(name, name, null, null, null, env, type, null, null, null, List.of(Command.run), null, false, false); + return new ContainerStatus(name, name, null, null, null, env, type, null, null, null, null, List.of(Command.run), null, false, false); } - public static ContainerStatus createWithId(String name, String env, String containerId, String image, List<Integer> ports, ContainerType type, List<Command> commands, String status, String created) { - return new ContainerStatus(name, name, containerId, image, ports, env, type, - null, null, created, commands, status, false, false); + public static ContainerStatus createWithId(String projectId, String containerName, String env, String containerId, String image, List<Integer> ports, ContainerType type, List<Command> commands, String status, String created) { + return new ContainerStatus(projectId, containerName, containerId, image, ports, env, type, + null, null, created, null, commands, status, false, false); } public ContainerStatus() { @@ -230,6 +234,14 @@ public class ContainerStatus { this.inTransit = inTransit; } + public String getFinished() { + return finished; + } + + public void setFinished(String finished) { + this.finished = finished; + } + @Override public String toString() { return "ContainerStatus{" + @@ -243,10 +255,11 @@ public class ContainerStatus { ", memoryInfo='" + memoryInfo + '\'' + ", cpuInfo='" + cpuInfo + '\'' + ", created='" + created + '\'' + + ", finished='" + finished + '\'' + ", commands=" + commands + ", state='" + state + '\'' + ", codeLoaded=" + codeLoaded + - ", logging=" + inTransit + + ", inTransit=" + inTransit + '}'; } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java index 594be91b..f1e02e2a 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java @@ -58,6 +58,7 @@ public class CodeService { private static final Logger LOGGER = Logger.getLogger(CodeService.class.getName()); public static final String APPLICATION_PROPERTIES_FILENAME = "application.properties"; + public static final String BUILDER_SCRIPT_FILE_SUFFIX = "builder-script-"; public static final String DEV_SERVICES_FILENAME = "devservices.yaml"; public static final String PROJECT_COMPOSE_FILENAME = "project-compose.yaml"; private static final String SNIPPETS_PATH = "/snippets/"; @@ -123,6 +124,7 @@ public class CodeService { List<String> files = new ArrayList<>(interfaces); files.addAll(targets.stream().map(target -> target + "-" + APPLICATION_PROPERTIES_FILENAME).toList()); + files.addAll(targets.stream().map(target -> BUILDER_SCRIPT_FILE_SUFFIX + target + ".sh").toList()); runtimes.forEach(runtime -> { files.forEach(file -> { @@ -137,23 +139,6 @@ public class CodeService { return result; } - public Map<String, String> getPipelinesTemplates() { - Map<String, String> result = new HashMap<>(); - - List<String> files = new ArrayList<>(targets); - files.addAll(targets.stream().map(target -> target + ".yaml").collect(Collectors.toList())); - - runtimes.forEach(runtime -> { - files.forEach(file -> { - String templateName = runtime + "-" + file; - String templatePath = "/pipelines/" + templateName; - String templateText = getResourceFile(templatePath); - result.put(templateName, templateText); - }); - }); - return result; - } - public Map<String, String> getServices() { Map<String, String> result = new HashMap<>(); String templateText = getResourceFile("/services/" + DEV_SERVICES_FILENAME); 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 e873d25f..c74dde56 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 @@ -11,6 +11,7 @@ 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.util.Objects; import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; @@ -79,10 +80,23 @@ public class EventService { if (infinispanService.isReady()) { ContainerStatus newStatus = data.mapTo(ContainerStatus.class); ContainerStatus oldStatus = infinispanService.getContainerStatus(newStatus.getProjectId(), newStatus.getEnv(), newStatus.getContainerName()); - if (oldStatus == null || Objects.equals(oldStatus.getInTransit(), Boolean.FALSE)) { + if (oldStatus == null) { + infinispanService.saveContainerStatus(newStatus); + } else if (Objects.equals(oldStatus.getInTransit(), Boolean.FALSE)) { + if ("exited".equalsIgnoreCase(newStatus.getState()) && oldStatus.getFinished() == null) { + newStatus.setFinished(Instant.now().toString()); + } + if (newStatus.getCpuInfo() == null) { + newStatus.setCpuInfo(oldStatus.getCpuInfo()); + newStatus.setMemoryInfo(oldStatus.getMemoryInfo()); + } infinispanService.saveContainerStatus(newStatus); } else if (Objects.equals(oldStatus.getInTransit(), Boolean.TRUE)) { if (!Objects.equals(oldStatus.getState(), newStatus.getState())) { + if (newStatus.getCpuInfo() == null) { + newStatus.setCpuInfo(oldStatus.getCpuInfo()); + newStatus.setMemoryInfo(oldStatus.getMemoryInfo()); + } infinispanService.saveContainerStatus(newStatus); } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java index 7cf3c243..399a52f7 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java @@ -83,44 +83,6 @@ public class GitService { return gitForImport; } - public List<CommitInfo> getAllCommits() { - List<CommitInfo> result = new ArrayList<>(); - try { - Git pollGit = getGitForImport(); - if (pollGit != null) { - StreamSupport.stream(pollGit.log().all().call().spliterator(), false) - .sorted(Comparator.comparingInt(RevCommit::getCommitTime)) - .forEach(commit -> result.add(new CommitInfo(commit.getName(), commit.getCommitTime()))); - } - } catch (Exception e) { - LOGGER.error(e.getMessage()); - } - return result; - } - - public List<CommitInfo> getCommitsAfterCommit(int commitTime) { - List<CommitInfo> result = new ArrayList<>(); - try { - Git pollGit = getGitForImport(); - if (pollGit != null) { - GitConfig gitConfig = getGitConfig(); - CredentialsProvider cred = new UsernamePasswordCredentialsProvider(gitConfig.getUsername(), gitConfig.getPassword()); - pull(pollGit, cred); - List<RevCommit> commits = StreamSupport.stream(pollGit.log().all().call().spliterator(), false) - .filter(commit -> commit.getCommitTime() > commitTime) - .sorted(Comparator.comparingInt(RevCommit::getCommitTime)).collect(Collectors.toList()); - for (RevCommit commit: commits) { - List<String> projects = new ArrayList<>(getChangedProjects(commit)); - List<GitRepo> repo = readProjectsFromRepository(pollGit, projects.toArray(new String[projects.size()])); - result.add(new CommitInfo(commit.getName(), commit.getCommitTime(), repo)); - } - } - } catch (Exception e) { - LOGGER.error(e.getMessage()); - } - return result; - } - public GitConfig getGitConfig() { String propertiesPrefix = "karavan."; String branch = ConfigProvider.getConfig().getValue(propertiesPrefix + "git-branch", String.class); 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 8740d724..84f8ee0d 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 @@ -19,8 +19,6 @@ package org.apache.camel.karavan.service; import io.smallrye.mutiny.tuples.Tuple2; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.eventbus.EventBus; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.core.Response; import org.apache.camel.karavan.docker.DockerForKaravan; import org.apache.camel.karavan.docker.model.DockerComposeService; import org.apache.camel.karavan.infinispan.InfinispanService; @@ -61,8 +59,20 @@ public class ProjectService implements HealthCheck { @ConfigProperty(name = "karavan.environment") String environment; - @ConfigProperty(name = "karavan.git-pull-interval") - String gitPullInterval; + @ConfigProperty(name = "karavan.git-install-gitea") + boolean installGitea; + + @ConfigProperty(name = "karavan.image-registry-install-registry") + boolean installRegistry; + + @ConfigProperty(name = "karavan.image-registry") + String registry; + @ConfigProperty(name = "karavan.image-group") + String group; + @ConfigProperty(name = "karavan.image-registry-username") + Optional<String> username; + @ConfigProperty(name = "karavan.image-registry-password") + Optional<String> password; @Inject InfinispanService infinispanService; @@ -83,7 +93,6 @@ public class ProjectService implements HealthCheck { EventBus eventBus; private AtomicBoolean ready = new AtomicBoolean(false); - private AtomicBoolean readyToPull = new AtomicBoolean(false); @Override public HealthCheckResponse call() { @@ -128,27 +137,33 @@ public class ProjectService implements HealthCheck { Map<String, String> files = infinispanService.getProjectFiles(project.getProjectId()).stream() .filter(f -> !Objects.equals(f.getName(), PROJECT_COMPOSE_FILENAME)) .collect(Collectors.toMap(ProjectFile::getName, ProjectFile::getCode)); - ProjectFile compose = infinispanService.getProjectFile(project.getProjectId(), PROJECT_COMPOSE_FILENAME); - DockerComposeService dcs = codeService.convertToDockerComposeService(compose.getCode(), project.getProjectId()); String templateName = project.getRuntime() + "-builder-script-docker.sh"; String script = codeService.getTemplateText(templateName); - GitConfig gitConfig = gitService.getGitConfig(); - List<String> env = List.of( - "GIT_REPOSITORY=" + gitConfig.getUri(), - "GIT_USERNAME=" + gitConfig.getUsername(), - "GIT_PASSWORD=" + gitConfig.getPassword(), - "PROJECT_ID=" + project.getProjectId(), - "IMAGE_REGISTRY=registry:5000", - "IMAGE_GROUP=karavan" - ); - - dockerForKaravan.runBuildProject(project.getProjectId(), script, files); + List<String> env = getEnvForBuild(project); + + dockerForKaravan.runBuildProject(project.getProjectId(), script, files, env); return project.getProjectId(); } } + private List<String> getEnvForBuild(Project project) { + GitConfig gitConfig = gitService.getGitConfig(); + List<String> env = List.of( + "GIT_REPOSITORY=" + (installGitea ? gitConfig.getUri().replace("localhost", "gitea") : gitConfig.getUri()), + "GIT_USERNAME=" + gitConfig.getUsername(), + "GIT_PASSWORD=" + gitConfig.getPassword(), + "GIT_BRANCH=" + gitConfig.getBranch(), + "PROJECT_ID=" + project.getProjectId(), + "IMAGE_REGISTRY=" + (installRegistry ? "registry:5000" : registry), + "IMAGE_REGISTRY_USERNAME=" + username, + "IMAGE_REGISTRY_PASSWORD=" + password, + "IMAGE_GROUP=" + group + ); + return env; + } + public Project save(Project project) throws Exception { boolean isNew = infinispanService.getProject(project.getProjectId()) == null; infinispanService.saveProject(project); @@ -168,33 +183,6 @@ public class ProjectService implements HealthCheck { return codeService.getProjectPort(composeFile); } - public void pullCommits() { - if (readyToPull.get()) { - LOGGER.info("Pull commits..."); - Tuple2<String, Integer> lastCommit = infinispanService.getLastCommit(); - gitService.getCommitsAfterCommit(lastCommit.getItem2()).forEach(commitInfo -> { - if (!infinispanService.hasCommit(commitInfo.getCommitId())) { - commitInfo.getRepos().forEach(repo -> { - Project project = importProjectFromRepo(repo); - kubernetesService.createPipelineRun(project); - }); - infinispanService.saveCommit(commitInfo.getCommitId(), commitInfo.getTime()); - } - infinispanService.saveLastCommit(commitInfo.getCommitId()); - }); - } - } - - 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()); - }); - readyToPull.set(true); - } - @Retry(maxRetries = 100, delay = 2000) public void tryStart() throws Exception { if (infinispanService.isReady() && gitService.checkGit()) { @@ -204,9 +192,6 @@ public class ProjectService implements HealthCheck { addKameletsProject(); addTemplatesProject(); addServicesProject(); - if (!Objects.equals("disabled", gitPullInterval.toLowerCase()) && !Objects.equals("off", gitPullInterval.toLowerCase())) { - importCommits(); - } ready.set(true); } else { LOGGER.info("Projects are not ready"); 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 b026d21d..c851a93c 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 @@ -59,11 +59,30 @@ public class ScheduledService { @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(EventType.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(); - List<String> namesInDocker = statusesInDocker.stream().map(ContainerStatus::getContainerName).collect(Collectors.toList()); + statusesInDocker.forEach(containerStatus -> { + eventBus.send(EventType.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() @@ -73,23 +92,12 @@ public class ScheduledService { infinispanService.deleteContainerStatus(containerStatus); infinispanService.deleteCamelStatuses(containerStatus.getProjectId(), containerStatus.getEnv()); }); - // send statuses to save - statusesInDocker.forEach(containerStatus -> { - eventBus.send(EventType.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); - }); } } @Scheduled(every = "{karavan.camel.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) void collectCamelStatuses() { - LOGGER.info("Collect info statuses"); camelService.collectCamelStatuses(); } - - @Scheduled(every = "{karavan.git-pull-interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) - void pullCommitsFromGit() { - projectService.pullCommits(); - } - } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/Constants.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/Constants.java index 0d6871bd..3b77038f 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/Constants.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/Constants.java @@ -25,5 +25,5 @@ public class Constants { public static final String RELOAD_TRY_COUNT = "reloadTryCount"; - public static final String HEALTHY_STATUS = "healthy"; + public static final String BUILDER_SUFFIX = "-builder"; } diff --git a/karavan-web/karavan-app/src/main/resources/application.properties b/karavan-web/karavan-app/src/main/resources/application.properties index 2aeb804a..d8b07ccf 100644 --- a/karavan-web/karavan-app/src/main/resources/application.properties +++ b/karavan-web/karavan-app/src/main/resources/application.properties @@ -3,9 +3,9 @@ karavan.environment=dev karavan.environments=dev karavan.default-runtime=camel-main karavan.runtimes=camel-main,quarkus,spring-boot -karavan.camel.status.interval=3s -karavan.container.status.interval=3s -karavan.container.infinispan.interval=5s +karavan.camel.status.interval=2s +karavan.container.status.interval=2s +karavan.container.statistics.interval=10s karavan.devmode.image=ghcr.io/apache/camel-karavan-devmode:4.0.0-RC2 # Git repository Configuration @@ -14,7 +14,6 @@ karavan.git-username=karavan karavan.git-password=karavan karavan.git-branch=main karavan.git-install-gitea=true -karavan.git-pull-interval=disabled # Image registry configuration karavan.image-registry=localhost:5555 diff --git a/karavan-web/karavan-app/src/main/resources/snippets/camel-main-builder-script-docker.sh b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-builder-script-docker.sh index e28483e1..62b72584 100644 --- a/karavan-web/karavan-app/src/main/resources/snippets/camel-main-builder-script-docker.sh +++ b/karavan-web/karavan-app/src/main/resources/snippets/camel-main-builder-script-docker.sh @@ -7,7 +7,6 @@ then replacer=https://${GIT_USERNAME}:${GIT_PASSWORD}@ prefix=https:// url="${GIT_REPOSITORY/$prefix/$replacer}" - echo url git clone --depth 1 --branch ${GIT_BRANCH} $url ${CHECKOUT_DIR} elif [[ ${GIT_REPOSITORY} == http* ]] ; then 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 a8ab3c75..259f5420 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 @@ -257,6 +257,15 @@ export class KaravanApi { }); } + static async buildProject(project: Project, environment: string, after: (res: AxiosResponse<any>) => void) { + instance.post('/api/project/build', project) + .then(res => { + after(res); + }).catch(err => { + after(err); + }); + } + static async getFiles(projectId: string, after: (files: ProjectFile[]) => void) { instance.get('/api/file/' + projectId) .then(res => { @@ -382,8 +391,8 @@ export class KaravanApi { }); } - static async stopPipelineRun(environment: string, pipelineRunName: string, after: (res: AxiosResponse<any>) => void) { - instance.delete('/api/infrastructure/pipelinerun/' + environment + "/" + pipelineRunName) + static async stopBuild(environment: string, buildName: string, after: (res: AxiosResponse<any>) => void) { + instance.delete('/api/project/build/' + environment + "/" + buildName) .then(res => { if (res.status === 200) { after(res.data); @@ -603,7 +612,7 @@ export class KaravanApi { }); } - static async fetchData(type: 'container' | 'pipeline' | 'none', podName: string, controller: AbortController) { + static async fetchData(type: 'container' | 'build' | 'none', podName: string, controller: AbortController) { const fetchData = async () => { await fetchEventSource("/api/logwatch/" + type + "/" + podName, { method: "GET", 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 ce19158a..6888c0b0 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 @@ -78,6 +78,7 @@ export class ContainerStatus { memoryInfo: string = ''; cpuInfo: string = ''; created: string = ''; + finished: string = ''; image: string = ''; ports: [] = []; commands: string [] = []; 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 8290cbde..b8c064a5 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 @@ -273,8 +273,8 @@ interface LogState { setCurrentLine: (currentLine: number) => void; showLog: boolean, setShowLog: (showLog: boolean) => void; - type: 'container' | 'pipeline' | 'none', - setType: (type: 'container' | 'pipeline' | 'none') => void, + type: 'container' | 'build' | 'none', + setType: (type: 'container' | 'build' | 'none') => void, } export const useLogStore = createWithEqualityFn<LogState>((set) => ({ @@ -304,7 +304,7 @@ export const useLogStore = createWithEqualityFn<LogState>((set) => ({ set(() => ({showLog: showLog})); }, type: "none", - setType: (type: 'container' | 'pipeline' | 'none') => { + setType: (type: 'container' | 'build' | 'none') => { set((state: LogState) => ({type: type})); }, }), shallow) diff --git a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx index 97fe15cc..4cc43eb9 100644 --- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx @@ -104,7 +104,7 @@ export function ContainerTableRow (props: Props) { <Td colSpan={2}> <ExpandableRowContent> <Flex direction={{default: "column"}} cellPadding={"0px"}> - {container.containerId} + {container.containerId.substring(0, 10)+"..."} </Flex> </ExpandableRowContent> </Td> diff --git a/karavan-web/karavan-app/src/main/webui/src/project/build/BuildStatus.tsx b/karavan-web/karavan-app/src/main/webui/src/project/build/BuildStatus.tsx index c926c257..b757ac75 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/build/BuildStatus.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/build/BuildStatus.tsx @@ -31,11 +31,11 @@ export function BuildStatus (props: Props) { const [isBuilding, setIsBuilding] = useState<boolean>(false); const [isRolling, setIsRolling] = useState<boolean>(false); const [showDeleteConfirmation, setShowDeleteConfirmation] = useState<boolean>(false); - const [deleteEntityType, setDeleteEntityType] = useState<'pod' | 'deployment' | 'pipelinerun'>('pod'); + const [deleteEntityType, setDeleteEntityType] = useState<'pod' | 'deployment' | 'build'>('pod'); const [deleteEntityName, setDeleteEntityName] = useState<string>(); const [deleteEntityEnv, setDeleteEntityEnv] = useState<string>(); - function deleteEntity(type: 'pod' | 'deployment' | 'pipelinerun', name: string, environment: string) { + function deleteEntity(type: 'pod' | 'deployment' | 'build', name: string, environment: string) { switch (type) { case "deployment": KaravanApi.deleteDeployment(environment, name, (res: any) => { @@ -49,8 +49,8 @@ export function BuildStatus (props: Props) { // onRefresh(); }); break; - case "pipelinerun": - KaravanApi.stopPipelineRun(environment, name, (res: any) => { + case "build": + KaravanApi.stopBuild(environment, name, (res: any) => { // if (Array.isArray(res) && Array.from(res).length > 0) // onRefresh(); }); @@ -60,7 +60,7 @@ export function BuildStatus (props: Props) { function build() { setIsBuilding(true); - KaravanApi.pipelineRun(project, env, res => { + KaravanApi.buildProject(project, env, res => { if (res.status === 200 || res.status === 201) { setIsBuilding(false); } else { @@ -151,7 +151,7 @@ export function BuildStatus (props: Props) { } function getPodsPanel(env: string) { - const podStatuses = containers.filter(d => d.projectId === project?.projectId); + const podStatuses = containers.filter(d => d.projectId === project?.projectId && d.type === 'project'); return ( <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsFlexStart"}}> @@ -221,18 +221,18 @@ export function BuildStatus (props: Props) { color={color}> {pipeline ? <Button className='labeled-button' variant="link" onClick={e => - useLogStore.setState({showLog: true, type: 'pipeline', podName: pipeline}) + useLogStore.setState({showLog: true, type: 'build', podName: pipeline}) }> {pipeline} </Button> - : "No pipeline"} - {isRunning && <Tooltip content={"Stop PipelineRun"}> + : "No builder"} + {isRunning && <Tooltip content={"Stop build"}> <Button icon={<DeleteIcon/>} className="labeled-button" variant="link" onClick={e => { setShowDeleteConfirmation(true); - setDeleteEntityType("pipelinerun"); + setDeleteEntityType("build"); setDeleteEntityEnv(env); setDeleteEntityName(pipeline); }}></Button> @@ -249,6 +249,56 @@ export function BuildStatus (props: Props) { ) } + function getBuildState(env: string) { + const status = containers.filter(c => c.projectId === project.projectId).at(0); + const buildName = status?.containerName; + const state = status?.state; + let buildTime = 0; + if (status?.created) { + const start: Date = new Date(status.created); + const finish: Date = status.finished !== undefined && status.finished !== null ? new Date(status.finished) : new Date(); + buildTime = Math.round((finish.getTime() - start.getTime()) / 1000); + } + const showTime = buildTime && buildTime > 0; + const isRunning = state === 'running'; + const isExited = state === 'exited'; + const color = isExited ? "grey" : (isRunning ? "blue" : "grey"); + const icon = isExited ? <UpIcon className="not-spinner"/> : <DownIcon className="not-spinner"/> + return ( + <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}> + <FlexItem> + <LabelGroup numLabels={2}> + <Label icon={isRunning ? <Spinner diameter="16px" className="spinner"/> : icon} + color={color}> + {buildName + ? <Button className='labeled-button' variant="link" onClick={e => + useLogStore.setState({showLog: true, type: 'build', podName: buildName}) + }> + {buildName} + </Button> + : "No builder"} + {status !== undefined && <Tooltip content={"Delete build"}> + <Button + icon={<DeleteIcon/>} + className="labeled-button" + variant="link" onClick={e => { + setShowDeleteConfirmation(true); + setDeleteEntityType("build"); + setDeleteEntityEnv(env); + setDeleteEntityName(buildName); + }}></Button> + </Tooltip>} + </Label> + {buildName !== undefined && showTime === true && buildTime !== undefined && + <Label icon={<ClockIcon className="not-spinner"/>} + color={color}>{buildTime + "s"}</Label>} + </LabelGroup> + </FlexItem> + <FlexItem>{env === "dev" && buildButton(env)}</FlexItem> + </Flex> + ) + } + function getDeleteConfirmation() { return (<Modal className="modal-delete" @@ -267,7 +317,7 @@ export function BuildStatus (props: Props) { onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button> ]} onEscapePress={e => setShowDeleteConfirmation(false)}> - <div>{"Delete " + deleteEntity + " " + deleteEntityName + "?"}</div> + <div>{"Delete " + deleteEntityType + " " + deleteEntityName + "?"}</div> </Modal>) } @@ -283,9 +333,9 @@ export function BuildStatus (props: Props) { </DescriptionListDescription> </DescriptionListGroup> <DescriptionListGroup> - <DescriptionListTerm>Pipeline</DescriptionListTerm> + <DescriptionListTerm>Build container</DescriptionListTerm> <DescriptionListDescription> - {getPipelineState(env)} + {getBuildState(env)} </DescriptionListDescription> </DescriptionListGroup> <DescriptionListGroup>
