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 3d937a8b731869199b47259a369840d1656cc6de Author: Marat Gubaidullin <[email protected]> AuthorDate: Fri Sep 23 17:52:33 2022 -0400 Pipeline and Deployment status cleanup --- .../apache/camel/karavan/api/StatusResource.java | 82 ++++++----- .../camel/karavan/model/DeploymentStatus.java | 25 +++- .../apache/camel/karavan/model/PipelineStatus.java | 74 ++++++++++ .../camel/karavan/model/ProjectEnvStatus.java | 14 +- .../apache/camel/karavan/model/ProjectStatus.java | 60 -------- .../camel/karavan/model/ProjectStoreSchema.java | 4 +- .../camel/karavan/service/InfinispanService.java | 34 +++-- .../camel/karavan/service/KaravanService.java | 5 + .../camel/karavan/service/KubernetesService.java | 117 ++++++++++----- .../camel/karavan/service/StatusService.java | 158 +++++++++------------ .../camel/karavan/watcher/DeploymentWatcher.java | 59 ++++++++ .../camel/karavan/watcher/PipelineRunWatcher.java | 74 ++++++++++ .../apache/camel/karavan/watcher/PodWatcher.java | 40 ++++++ .../src/main/resources/application.properties | 14 +- karavan-app/src/main/webapp/src/api/KaravanApi.tsx | 17 ++- .../main/webapp/src/projects/ProjectDashboard.tsx | 100 ++++++------- .../src/main/webapp/src/projects/ProjectInfo.tsx | 52 ++++--- .../src/main/webapp/src/projects/ProjectModels.ts | 34 +++-- .../src/main/webapp/src/projects/ProjectPage.tsx | 3 +- .../src/main/webapp/src/projects/ProjectsPage.tsx | 4 +- 20 files changed, 621 insertions(+), 349 deletions(-) diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java index b33cac1..9492cc2 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java @@ -17,11 +17,10 @@ package org.apache.camel.karavan.api; import io.vertx.core.eventbus.EventBus; +import org.apache.camel.karavan.model.DeploymentStatus; import org.apache.camel.karavan.model.KaravanConfiguration; -import org.apache.camel.karavan.model.ProjectEnvStatus; -import org.apache.camel.karavan.model.ProjectStatus; +import org.apache.camel.karavan.model.PipelineStatus; import org.apache.camel.karavan.service.InfinispanService; -import org.apache.camel.karavan.service.StatusService; import org.jboss.logging.Logger; import javax.inject.Inject; @@ -31,9 +30,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; +import javax.ws.rs.core.Response; @Path("/api/status") public class StatusResource { @@ -51,44 +48,51 @@ public class StatusResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Path("/project/{projectId}") - public ProjectStatus getStatus(@HeaderParam("username") String username, @PathParam("projectId") String projectId) { - bus.publish(StatusService.CMD_COLLECT_STATUSES, projectId); - ProjectStatus status = infinispanService.getProjectStatus(projectId); - if (status != null){ - return status; + @Path("/pipeline/{projectId}") + public Response getPipelineStatus(@HeaderParam("username") String username, @PathParam("projectId") String projectId) { + PipelineStatus status = infinispanService.getPipelineStatus(projectId); + if (status != null) { + return Response.ok(status).build(); } else { - return new ProjectStatus( projectId, - configuration.environments() - .stream().map(e -> new ProjectEnvStatus(e.name())) - .collect(Collectors.toList()), - Long.valueOf(0)); + return Response.noContent().build(); } } @GET @Produces(MediaType.APPLICATION_JSON) - @Path("/projects") - public Map<String, Map> getSimpleStatus(@HeaderParam("username") String username) throws Exception { - Map<String, Map> result = new HashMap<>(); - infinispanService.getProjects().forEach(project -> { - ProjectStatus ps = getStatus(username, project.getProjectId()); - Map<String, String> statuses = new HashMap<>(); - ps.getStatuses().forEach(pes -> { - if (pes.getLastPipelineRunResult() == null || pes.getDeploymentStatus() == null || pes.getContextStatus() == null){ - statuses.put(pes.getEnvironment(), "N/A"); - } else { - boolean pipelineOK = pes.getLastPipelineRunResult().equals("Succeeded"); - System.out.println(pes.getLastPipelineRunResult()); - boolean deploymentOK = pes.getDeploymentStatus().getReadyReplicas() == pes.getDeploymentStatus().getReplicas() && pes.getDeploymentStatus().getUnavailableReplicas() == 0; - boolean camelOK = pes.getContextStatus().equals(ProjectEnvStatus.Status.UP) && pes.getConsumerStatus().equals(ProjectEnvStatus.Status.UP) && pes.getRoutesStatus().equals(ProjectEnvStatus.Status.UP); - String status = (pipelineOK && deploymentOK && camelOK) ? "UP" : "DOWN"; - statuses.put(pes.getEnvironment(), status); - } - }); - result.put(project.getProjectId(), statuses); - }); - - return result; + @Path("/deployment/{projectId}") + public Response getDeploymentStatus(@HeaderParam("username") String username, @PathParam("projectId") String projectId) { + DeploymentStatus status = infinispanService.getDeploymentStatus(projectId); + if (status != null) { + return Response.ok(status).build(); + } else { + return Response.noContent().build(); + } } + +// @GET +// @Produces(MediaType.APPLICATION_JSON) +// @Path("/projects") +// public Map<String, Map> getSimpleStatus(@HeaderParam("username") String username) throws Exception { +// Map<String, Map> result = new HashMap<>(); +// infinispanService.getProjects().forEach(project -> { +// ProjectStatus ps = getStatus(username, project.getProjectId()); +// Map<String, String> statuses = new HashMap<>(); +// ps.getStatuses().forEach(pes -> { +// if (pes.getLastPipelineRunResult() == null || pes.getDeploymentStatus() == null || pes.getContextStatus() == null){ +// statuses.put(pes.getEnvironment(), "N/A"); +// } else { +// boolean pipelineOK = pes.getLastPipelineRunResult().equals("Succeeded"); +// System.out.println(pes.getLastPipelineRunResult()); +// boolean deploymentOK = pes.getDeploymentStatus().getReadyReplicas() == pes.getDeploymentStatus().getReplicas() && pes.getDeploymentStatus().getUnavailableReplicas() == 0; +// boolean camelOK = pes.getContextStatus().equals(ProjectEnvStatus.Status.UP) && pes.getConsumerStatus().equals(ProjectEnvStatus.Status.UP) && pes.getRoutesStatus().equals(ProjectEnvStatus.Status.UP); +// String status = (pipelineOK && deploymentOK && camelOK) ? "UP" : "DOWN"; +// statuses.put(pes.getEnvironment(), status); +// } +// }); +// result.put(project.getProjectId(), statuses); +// }); +// +// return result; +// } } \ No newline at end of file diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/DeploymentStatus.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/DeploymentStatus.java index 445e5a9..732bdb1 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/model/DeploymentStatus.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/DeploymentStatus.java @@ -7,19 +7,23 @@ import java.util.ArrayList; import java.util.List; public class DeploymentStatus { + public static final String CACHE = "deployment_statuses"; @ProtoField(number = 1) - String image; + String projectId; @ProtoField(number = 2) - Integer replicas; + String image; @ProtoField(number = 3) - Integer readyReplicas; + Integer replicas; @ProtoField(number = 4) + Integer readyReplicas; + @ProtoField(number = 5) Integer unavailableReplicas; - @ProtoField(number = 5, collectionImplementation = ArrayList.class) + @ProtoField(number = 6, collectionImplementation = ArrayList.class) List<PodStatus> podStatuses; - public DeploymentStatus() { + public DeploymentStatus(String projectId) { + this.projectId = projectId; this.image = ""; this.replicas = 0; this.readyReplicas = 0; @@ -28,7 +32,8 @@ public class DeploymentStatus { } @ProtoFactory - public DeploymentStatus(String image, Integer replicas, Integer readyReplicas, Integer unavailableReplicas, List<PodStatus> podStatuses) { + public DeploymentStatus(String projectId, String image, Integer replicas, Integer readyReplicas, Integer unavailableReplicas, List<PodStatus> podStatuses) { + this.projectId = projectId; this.image = image; this.replicas = replicas; this.readyReplicas = readyReplicas; @@ -36,6 +41,14 @@ public class DeploymentStatus { this.podStatuses = podStatuses; } + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + public String getImage() { return image; } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/PipelineStatus.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/PipelineStatus.java new file mode 100644 index 0000000..8f6f807 --- /dev/null +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/PipelineStatus.java @@ -0,0 +1,74 @@ +package org.apache.camel.karavan.model; + +import org.infinispan.protostream.annotations.ProtoFactory; +import org.infinispan.protostream.annotations.ProtoField; + +public class PipelineStatus { + public static final String CACHE = "pipeline_statuses"; + @ProtoField(number = 1) + String projectId; + @ProtoField(number = 2) + String pipelineName; + @ProtoField(number = 3) + String result; + @ProtoField(number = 5) + String startTime; + @ProtoField(number = 6) + String completionTime; + + @ProtoFactory + public PipelineStatus(String projectId, String pipelineName, String result, String startTime, String completionTime) { + this.projectId = projectId; + this.pipelineName = pipelineName; + this.result = result; + this.startTime = startTime; + this.completionTime = completionTime; + } + + public PipelineStatus(String projectId) { + this.projectId = projectId; + } + + public PipelineStatus() { + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getPipelineName() { + return pipelineName; + } + + public void setPipelineName(String pipelineName) { + this.pipelineName = pipelineName; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getCompletionTime() { + return completionTime; + } + + public void setCompletionTime(String completionTime) { + this.completionTime = completionTime; + } +} diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectEnvStatus.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectEnvStatus.java index ff83922..99192a6 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectEnvStatus.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectEnvStatus.java @@ -27,6 +27,8 @@ public class ProjectEnvStatus { Long lastPipelineRunTime; @ProtoField(number = 11) DeploymentStatus deploymentStatus; + @ProtoField(number = 12) + String lastPipelineRunStartTime; public enum Status { @ProtoEnumValue(number = 0, name = "DOWN") @@ -38,7 +40,8 @@ public class ProjectEnvStatus { } @ProtoFactory - public ProjectEnvStatus(String environment, Status status, Status contextStatus, Status consumerStatus, Status routesStatus, Status registryStatus, String contextVersion, String lastPipelineRun, String lastPipelineRunResult, Long lastPipelineRunTime, DeploymentStatus deploymentStatus) { + public ProjectEnvStatus(String environment, Status status, Status contextStatus, Status consumerStatus, Status routesStatus, Status registryStatus, String contextVersion, + String lastPipelineRun, String lastPipelineRunResult, Long lastPipelineRunTime, DeploymentStatus deploymentStatus, String lastPipelineRunStartTime) { this.environment = environment; this.status = status; this.contextStatus = contextStatus; @@ -50,6 +53,7 @@ public class ProjectEnvStatus { this.lastPipelineRunResult = lastPipelineRunResult; this.lastPipelineRunTime = lastPipelineRunTime; this.deploymentStatus = deploymentStatus; + this.lastPipelineRunStartTime = lastPipelineRunStartTime; } public ProjectEnvStatus(String environment) { @@ -143,4 +147,12 @@ public class ProjectEnvStatus { public void setLastPipelineRunTime(Long lastPipelineRunTime) { this.lastPipelineRunTime = lastPipelineRunTime; } + + public String getLastPipelineRunStartTime() { + return lastPipelineRunStartTime; + } + + public void setLastPipelineRunStartTime(String lastPipelineRunStartTime) { + this.lastPipelineRunStartTime = lastPipelineRunStartTime; + } } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStatus.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStatus.java deleted file mode 100644 index e7145ea..0000000 --- a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStatus.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.apache.camel.karavan.model; - -import org.infinispan.protostream.annotations.ProtoEnumValue; -import org.infinispan.protostream.annotations.ProtoFactory; -import org.infinispan.protostream.annotations.ProtoField; - -import java.util.ArrayList; -import java.util.List; - -public class ProjectStatus { - public static final String CACHE = "project_statuses"; - @ProtoField(number = 1) - String projectId; - @ProtoField(number = 2, collectionImplementation = ArrayList.class) - List<ProjectEnvStatus> statuses; - @ProtoField(number = 3) - Long lastUpdate; - - public enum Status { - @ProtoEnumValue(number = 0, name = "DOWN") - DOWN, - @ProtoEnumValue(number = 1, name = "UP") - UP - } - - @ProtoFactory - public ProjectStatus(String projectId, List<ProjectEnvStatus> statuses, Long lastUpdate) { - this.projectId = projectId; - this.statuses = statuses; - this.lastUpdate = lastUpdate; - } - - public ProjectStatus() { - } - - public String getProjectId() { - return projectId; - } - - public void setProjectId(String projectId) { - this.projectId = projectId; - } - - public List<ProjectEnvStatus> getStatuses() { - return statuses; - } - - public void setStatuses(List<ProjectEnvStatus> statuses) { - this.statuses = statuses; - } - - public Long getLastUpdate() { - return lastUpdate; - } - - public void setLastUpdate(Long lastUpdate) { - this.lastUpdate = lastUpdate; - } - -} diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStoreSchema.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStoreSchema.java index a049831..4721887 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStoreSchema.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/ProjectStoreSchema.java @@ -3,11 +3,9 @@ package org.apache.camel.karavan.model; import org.infinispan.protostream.GeneratedSchema; import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; -import java.util.HashMap; - @AutoProtoSchemaBuilder( includeClasses = { - GroupedKey.class, Project.class, ProjectFile.class, ProjectStatus.class, ProjectEnvStatus.class, DeploymentStatus.class, + GroupedKey.class, Project.class, ProjectFile.class, PipelineStatus.class, ProjectEnvStatus.class, DeploymentStatus.class, PodStatus.class }, schemaPackageName = "karavan") diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java index e99a406..189bed0 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java @@ -16,14 +16,14 @@ */ package org.apache.camel.karavan.service; -import io.quarkus.runtime.StartupEvent; import io.quarkus.runtime.configuration.ProfileManager; import io.vertx.core.eventbus.EventBus; +import org.apache.camel.karavan.model.DeploymentStatus; import org.apache.camel.karavan.model.GroupedKey; import org.apache.camel.karavan.model.Kamelet; +import org.apache.camel.karavan.model.PipelineStatus; import org.apache.camel.karavan.model.Project; import org.apache.camel.karavan.model.ProjectFile; -import org.apache.camel.karavan.model.ProjectStatus; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.infinispan.Cache; import org.infinispan.client.hotrod.RemoteCache; @@ -35,14 +35,12 @@ import org.infinispan.commons.configuration.XMLStringConfiguration; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.configuration.cache.SingleFileStoreConfigurationBuilder; -import org.infinispan.configuration.cache.StorageType; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.query.dsl.QueryFactory; import org.jboss.logging.Logger; import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.event.Observes; import javax.inject.Inject; import java.util.List; import java.util.Map; @@ -55,7 +53,9 @@ public class InfinispanService { BasicCache<GroupedKey, ProjectFile> files; - BasicCache<GroupedKey, ProjectStatus> statuses; + BasicCache<GroupedKey, PipelineStatus> pipelineStatuses; + + BasicCache<GroupedKey, DeploymentStatus> deploymentStatus; BasicCache<String, String> kamelets; @@ -78,7 +78,7 @@ public class InfinispanService { private static final Logger LOGGER = Logger.getLogger(KaravanService.class.getName()); - void onStart(@Observes StartupEvent ev) { + void start() { if (cacheManager == null) { LOGGER.info("InfinispanService is starting in local mode"); GlobalConfigurationBuilder global = GlobalConfigurationBuilder.defaultClusteredBuilder(); @@ -94,13 +94,15 @@ public class InfinispanService { .fetchPersistentState(true); projects = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(Project.CACHE, builder.build()); files = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(ProjectFile.CACHE, builder.build()); - statuses = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(ProjectStatus.CACHE, builder.build()); + pipelineStatuses = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(PipelineStatus.CACHE, builder.build()); + deploymentStatus = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(DeploymentStatus.CACHE, builder.build()); kamelets = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(Kamelet.CACHE, builder.build()); } else { LOGGER.info("InfinispanService is starting in remote mode"); projects = cacheManager.administration().getOrCreateCache(Project.CACHE, new XMLStringConfiguration(String.format(CACHE_CONFIG, Project.CACHE))); files = cacheManager.administration().getOrCreateCache(ProjectFile.CACHE, new XMLStringConfiguration(String.format(CACHE_CONFIG, ProjectFile.CACHE))); - statuses = cacheManager.administration().getOrCreateCache(ProjectFile.CACHE, new XMLStringConfiguration(String.format(CACHE_CONFIG, ProjectStatus.CACHE))); + pipelineStatuses = cacheManager.administration().getOrCreateCache(ProjectFile.CACHE, new XMLStringConfiguration(String.format(CACHE_CONFIG, PipelineStatus.CACHE))); + deploymentStatus = cacheManager.administration().getOrCreateCache(ProjectFile.CACHE, new XMLStringConfiguration(String.format(CACHE_CONFIG, DeploymentStatus.CACHE))); kamelets = cacheManager.administration().getOrCreateCache(Kamelet.CACHE, new XMLStringConfiguration(String.format(CACHE_CONFIG, Kamelet.CACHE))); } if (getProjects().isEmpty()) { @@ -164,12 +166,20 @@ public class InfinispanService { return projects.get(GroupedKey.create(project, project)); } - public ProjectStatus getProjectStatus(String projectId) { - return statuses.get(GroupedKey.create(projectId, projectId)); + public PipelineStatus getPipelineStatus(String projectId) { + return pipelineStatuses.get(GroupedKey.create(projectId, projectId)); + } + + public void savePipelineStatus(PipelineStatus status) { + pipelineStatuses.put(GroupedKey.create(status.getProjectId(), status.getProjectId()), status); + } + + public DeploymentStatus getDeploymentStatus(String projectId) { + return deploymentStatus.get(GroupedKey.create(projectId, projectId)); } - public void saveProjectStatus(ProjectStatus status) { - statuses.put(GroupedKey.create(status.getProjectId(), status.getProjectId()), status); + public void saveDeploymentStatus(DeploymentStatus status) { + deploymentStatus.put(GroupedKey.create(status.getProjectId(), status.getProjectId()), status); } public List<String> getKameletNames() { diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java index 8d43128..6a64e39 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java @@ -44,6 +44,9 @@ public class KaravanService { @Inject InfinispanService infinispanService; + @Inject + KubernetesService kubernetesService; + @Inject GitService gitService; @@ -54,6 +57,8 @@ public class KaravanService { KaravanConfiguration configuration; void onStart(@Observes StartupEvent ev) { + infinispanService.start(); + kubernetesService.start(); } @ConsumeEvent(value = IMPORT_PROJECTS, blocking = true) diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java index 92a583d..ed8e785 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java @@ -23,6 +23,7 @@ import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.Watch; import io.fabric8.kubernetes.client.dsl.LogWatch; import io.fabric8.openshift.api.model.ImageStream; import io.fabric8.openshift.client.OpenShiftClient; @@ -35,24 +36,33 @@ import io.fabric8.tekton.pipeline.v1beta1.PipelineRunBuilder; import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpec; import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpecBuilder; import io.fabric8.tekton.pipeline.v1beta1.WorkspaceBindingBuilder; +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.StartupEvent; import io.vertx.mutiny.core.eventbus.EventBus; import org.apache.camel.karavan.model.DeploymentStatus; +import org.apache.camel.karavan.model.KaravanConfiguration; import org.apache.camel.karavan.model.PipelineRunLog; import org.apache.camel.karavan.model.PodStatus; import org.apache.camel.karavan.model.Project; +import org.apache.camel.karavan.watcher.DeploymentWatcher; +import org.apache.camel.karavan.watcher.PipelineRunWatcher; +import org.apache.camel.karavan.watcher.PodWatcher; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; import javax.enterprise.inject.Produces; import javax.inject.Inject; import java.io.IOException; import java.io.InputStream; +import java.nio.channels.Pipe; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; @ApplicationScoped @@ -64,6 +74,12 @@ public class KubernetesService { @ConfigProperty(name = "kubernetes.namespace", defaultValue = "localhost") String currentNamespace; + @Inject + KaravanConfiguration config; + + @Inject + InfinispanService infinispanService; + @Produces public KubernetesClient kubernetesClient() { return new DefaultKubernetesClient(); @@ -81,6 +97,29 @@ public class KubernetesService { private static final Logger LOGGER = Logger.getLogger(KubernetesService.class.getName()); + private List<Watch> watches = new ArrayList<>(); + + void start() { + LOGGER.info("Start KubernetesService"); + Optional<KaravanConfiguration.Environment> env = config.environments().stream() + .filter(environment -> environment.name().equals("dev")).findFirst(); + if (env.isPresent()) { + watches.add(kubernetesClient().apps().deployments().inNamespace(env.get().namespace()) + .watch(new DeploymentWatcher(infinispanService, this))); + + watches.add(kubernetesClient().pods().inNamespace(env.get().namespace()).withLabel("app.openshift.io/runtime", "camel") + .watch(new PodWatcher(infinispanService, this))); + + watches.add(tektonClient().v1beta1().pipelineRuns().inNamespace(env.get().namespace()) + .watch(new PipelineRunWatcher(infinispanService))); + } + } + + void onStop(@Observes ShutdownEvent ev) { + LOGGER.info("Stop KubernetesService"); + watches.forEach(watch -> watch.close()); + } + public String createPipelineRun(Project project, String pipelineName, String namespace) throws Exception { LOGGER.info("Pipeline is creating for " + project.getProjectId()); @@ -189,41 +228,55 @@ public class KubernetesService { } } - public DeploymentStatus getDeploymentStatus(String name, String namespace) { + public DeploymentStatus getDeploymentStatus(String name, Deployment deployment) { try { - Deployment deployment = kubernetesClient().apps().deployments().inNamespace(namespace).withName(name).get(); - if (deployment != null) { - String dsImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage(); - String imageName = dsImage.startsWith("image-registry.openshift-image-registry.svc") - ? dsImage.replace("image-registry.openshift-image-registry.svc:5000/", "") - : dsImage; - - List<Pod> pods = kubernetesClient().pods().inNamespace(namespace) - .withLabel("app.kubernetes.io/name", name) - .withLabel("app.openshift.io/runtime", "camel") - .list().getItems(); - - List<PodStatus> podStatuses = pods.stream().map(pod -> new PodStatus( - pod.getMetadata().getName(), - pod.getStatus().getContainerStatuses().get(0).getStarted(), - pod.getStatus().getContainerStatuses().get(0).getReady(), - getPodReason(pod), - pod.getMetadata().getLabels().get("app.kubernetes.io/name") - )).collect(Collectors.toList()); - - return new DeploymentStatus( - imageName, - deployment.getSpec().getReplicas(), - deployment.getStatus().getReadyReplicas(), - deployment.getStatus().getUnavailableReplicas(), - podStatuses - ); - } else { - return new DeploymentStatus("", 0, 0, 0, List.of()); - } + String dsImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage(); + String imageName = dsImage.startsWith("image-registry.openshift-image-registry.svc") + ? dsImage.replace("image-registry.openshift-image-registry.svc:5000/", "") + : dsImage; + + List<PodStatus> podStatuses = getDeploymentPodsStatuses(name, deployment.getMetadata().getNamespace()); + + return new DeploymentStatus( + name, + imageName, + deployment.getSpec().getReplicas(), + deployment.getStatus().getReadyReplicas(), + deployment.getStatus().getUnavailableReplicas(), + podStatuses + ); + } catch (Exception ex) { + LOGGER.error(ex.getMessage()); + return new DeploymentStatus(name); + } + } + + public List<PodStatus> getDeploymentPodsStatuses(String name, String namespace) { + try { + List<Pod> pods = kubernetesClient().pods().inNamespace(namespace) + .withLabel("app.kubernetes.io/name", name) + .withLabel("app.openshift.io/runtime", "camel") + .list().getItems(); + + return pods.stream().map(pod -> new PodStatus( + pod.getMetadata().getName(), + pod.getStatus().getContainerStatuses().get(0).getStarted(), + pod.getStatus().getContainerStatuses().get(0).getReady(), + getPodReason(pod), + pod.getMetadata().getLabels().get("app.kubernetes.io/name") + )).collect(Collectors.toList()); + } catch (Exception ex) { + LOGGER.error(ex.getMessage()); + return List.of(); + } + } + + public Deployment getDeployment(String name, String namespace) { + try { + return kubernetesClient().apps().deployments().inNamespace(namespace).withName(name).get(); } catch (Exception ex) { LOGGER.error(ex.getMessage()); - return new DeploymentStatus(); + return null; } } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java index a947236..fa5329a 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java @@ -17,30 +17,19 @@ package org.apache.camel.karavan.service; import io.fabric8.tekton.pipeline.v1beta1.PipelineRun; -import io.quarkus.runtime.configuration.ProfileManager; -import io.quarkus.scheduler.Scheduled; import io.quarkus.vertx.ConsumeEvent; import io.smallrye.mutiny.tuples.Tuple4; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.Vertx; -import io.vertx.mutiny.core.buffer.Buffer; -import io.vertx.mutiny.ext.web.client.HttpResponse; import io.vertx.mutiny.ext.web.client.WebClient; -import org.apache.camel.karavan.model.DeploymentStatus; import org.apache.camel.karavan.model.KaravanConfiguration; import org.apache.camel.karavan.model.ProjectEnvStatus; -import org.apache.camel.karavan.model.ProjectStatus; import org.jboss.logging.Logger; - import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import java.time.Instant; -import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; @ApplicationScoped public class StatusService { @@ -71,66 +60,47 @@ public class StatusService { return webClient; } - @Scheduled(every="10s") - void checkDeployedProjects() { - LOGGER.info("Check deployed projects"); - infinispanService.getProjects().forEach(project -> { - Optional<KaravanConfiguration.Environment> env = configuration.environments().stream().filter(environment -> environment.name().equals("dev")).findFirst(); - if (env.isPresent()) { - boolean hasDeployment = kubernetesService.hasDeployment(project.getProjectId(), env.get().namespace()); - if (!project.getDeployed() && hasDeployment) { - project.setDeployed(true); - infinispanService.saveProject(project); - } else if (project.getDeployed() && !hasDeployment) { - project.setDeployed(false); - infinispanService.saveProject(project); - infinispanService.saveProjectStatus(new ProjectStatus(project.getProjectId(), List.of(), System.currentTimeMillis())); - } - } - }); - } - @ConsumeEvent(value = CMD_COLLECT_STATUSES, blocking = true, ordered = true) public void collectStatuses(String projectId) throws Exception { if ((System.currentTimeMillis() - lastCollect) > configuration.statusThreshold()) { - collectStatusesForProject(projectId); +// collectStatusesForProject(projectId); lastCollect = System.currentTimeMillis(); } } - private void collectStatusesForProject(String projectId) { - ProjectStatus old = infinispanService.getProjectStatus(projectId); - ProjectStatus status = new ProjectStatus(); - status.setProjectId(projectId); - status.setLastUpdate(System.currentTimeMillis()); - List<ProjectEnvStatus> statuses = new ArrayList<>(1); - configuration.environments().stream().filter(e -> e.active()).forEach(e -> { - String url = ProfileManager.getActiveProfile().equals("dev") - ? String.format("http://%s-%s.%s/q/health", projectId, e.namespace(), e.cluster()) - : String.format("http://%s.%s.%s/q/health", projectId, e.namespace(), e.cluster()); - ProjectEnvStatus pes = getProjectEnvStatus(url, e.name()); - DeploymentStatus ds = kubernetesService.getDeploymentStatus(projectId, e.namespace()); - Tuple4<Boolean, String, String, Long> pipeline = getProjectPipelineStatus(projectId, e.pipeline(), e.namespace()); - - pes.setDeploymentStatus(ds); - - if (pipeline.getItem1()){ - pes.setLastPipelineRun(pipeline.getItem2()); - pes.setLastPipelineRunResult(pipeline.getItem3()); - pes.setLastPipelineRunTime(pipeline.getItem4()); - } else if (old != null){ - Optional<ProjectEnvStatus> opes = old.getStatuses().stream().filter(x -> x.getEnvironment().equals(e.name())).findFirst(); - if (opes.isPresent()) { - pes.setLastPipelineRun(opes.get().getLastPipelineRun()); - pes.setLastPipelineRunResult(opes.get().getLastPipelineRunResult()); - } - } - statuses.add(pes); - }); - status.setStatuses(statuses); - infinispanService.saveProjectStatus(status); - } +// private void collectStatusesForProject(String projectId) { +// ProjectStatus old = infinispanService.getProjectStatus(projectId); +// ProjectStatus status = new ProjectStatus(); +// status.setProjectId(projectId); +// status.setLastUpdate(System.currentTimeMillis()); +// List<ProjectEnvStatus> statuses = new ArrayList<>(1); +// configuration.environments().stream().filter(e -> e.active()).forEach(e -> { +// String url = ProfileManager.getActiveProfile().equals("dev") +// ? String.format("http://%s-%s.%s/q/health", projectId, e.namespace(), e.cluster()) +// : String.format("http://%s.%s.%s/q/health", projectId, e.namespace(), e.cluster()); +// ProjectEnvStatus pes = getProjectEnvStatus(url, e.name()); +// DeploymentStatus ds = kubernetesService.getDeploymentStatus(projectId, e.namespace()); +// Tuple4<Boolean, String, String, Long> pipeline = getProjectPipelineStatus(projectId, e.pipeline(), e.namespace()); +// +// pes.setDeploymentStatus(ds); +// +// if (pipeline.getItem1()){ +// pes.setLastPipelineRun(pipeline.getItem2()); +// pes.setLastPipelineRunResult(pipeline.getItem3()); +// pes.setLastPipelineRunTime(pipeline.getItem4()); +// } else if (old != null){ +// Optional<ProjectEnvStatus> opes = old.getStatuses().stream().filter(x -> x.getEnvironment().equals(e.name())).findFirst(); +// if (opes.isPresent()) { +// pes.setLastPipelineRun(opes.get().getLastPipelineRun()); +// pes.setLastPipelineRunResult(opes.get().getLastPipelineRunResult()); +// } +// } +// statuses.add(pes); +// }); +// status.setStatuses(statuses); +// infinispanService.saveProjectStatus(status); +// } private Tuple4<Boolean, String, String, Long> getProjectPipelineStatus(String projectId, String pipelineName, String namespace) { try { @@ -152,37 +122,37 @@ public class StatusService { } } - private ProjectEnvStatus getProjectEnvStatus(String url, String env) { - // TODO: make it reactive - try { - HttpResponse<Buffer> result = getWebClient().getAbs(url).timeout(1000).send().subscribeAsCompletionStage().toCompletableFuture().get(); - if (result.statusCode() == 200) { - JsonObject res = result.bodyAsJsonObject(); - List<JsonObject> checks = res.getJsonArray("checks").stream().map(o -> (JsonObject)o).collect(Collectors.toList()); - - JsonObject context = checks.stream().filter(o -> Objects.equals(o.getString("name"), "context")).findFirst().get(); - return new ProjectEnvStatus( - env, - res != null && res.containsKey("status") && res.getString("status").equals("UP") ? ProjectEnvStatus.Status.UP : ProjectEnvStatus.Status.DOWN, - getStatus(checks, "context"), - getStatus(checks, "camel-consumers"), - getStatus(checks, "camel-routes"), - getStatus(checks, "camel-registry"), - context.getJsonObject("data").getString("context.version"), - "", - "", - 0L, - new DeploymentStatus() - ); - } else { - return new ProjectEnvStatus(env); - } - } catch (Exception ex) { - LOGGER.error(ex.getMessage()); -// ex.printStackTrace(); - return new ProjectEnvStatus(env); - } - } +// private ProjectEnvStatus getProjectEnvStatus(String url, String env) { +// // TODO: make it reactive +// try { +// HttpResponse<Buffer> result = getWebClient().getAbs(url).timeout(1000).send().subscribeAsCompletionStage().toCompletableFuture().get(); +// if (result.statusCode() == 200) { +// JsonObject res = result.bodyAsJsonObject(); +// List<JsonObject> checks = res.getJsonArray("checks").stream().map(o -> (JsonObject)o).collect(Collectors.toList()); +// +// JsonObject context = checks.stream().filter(o -> Objects.equals(o.getString("name"), "context")).findFirst().get(); +// return new ProjectEnvStatus( +// env, +// res != null && res.containsKey("status") && res.getString("status").equals("UP") ? ProjectEnvStatus.Status.UP : ProjectEnvStatus.Status.DOWN, +// getStatus(checks, "context"), +// getStatus(checks, "camel-consumers"), +// getStatus(checks, "camel-routes"), +// getStatus(checks, "camel-registry"), +// context.getJsonObject("data").getString("context.version"), +// "", +// "", +// 0L, +// new DeploymentStatus() +// ); +// } else { +// return new ProjectEnvStatus(env); +// } +// } catch (Exception ex) { +// LOGGER.error(ex.getMessage()); +//// ex.printStackTrace(); +// return new ProjectEnvStatus(env); +// } +// } private ProjectEnvStatus.Status getStatus(List<JsonObject> checks, String name){ try { diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/watcher/DeploymentWatcher.java b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/DeploymentWatcher.java new file mode 100644 index 0000000..8f635f3 --- /dev/null +++ b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/DeploymentWatcher.java @@ -0,0 +1,59 @@ +package org.apache.camel.karavan.watcher; + +import io.fabric8.kubernetes.client.Watcher; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.client.WatcherException; +import org.apache.camel.karavan.model.DeploymentStatus; +import org.apache.camel.karavan.model.PodStatus; +import org.apache.camel.karavan.model.Project; +import org.apache.camel.karavan.service.InfinispanService; +import org.apache.camel.karavan.service.KubernetesService; +import org.jboss.logging.Logger; + +import java.util.List; + +public class DeploymentWatcher implements Watcher<Deployment> { + + private static final Logger LOGGER = Logger.getLogger(DeploymentWatcher.class.getName()); + private InfinispanService infinispanService; + private KubernetesService kubernetesService; + + public DeploymentWatcher(InfinispanService infinispanService, KubernetesService kubernetesService) { + this.infinispanService = infinispanService; + this.kubernetesService = kubernetesService; + } + + @Override + public void eventReceived(Watcher.Action action, Deployment deployment) { + LOGGER.info(action.name() + " " + deployment.getMetadata().getName()); + Project project = infinispanService.getProject(deployment.getMetadata().getName()); + if (project != null) { + switch (action.name()) { + case "ADDED": + project.setDeployed(true); + infinispanService.saveProject(project); + DeploymentStatus s = kubernetesService.getDeploymentStatus(project.getProjectId(), deployment); + infinispanService.saveDeploymentStatus(s); + break; + case "MODIFIED": + if (!project.getDeployed()) { + project.setDeployed(true); + infinispanService.saveProject(project); + } + DeploymentStatus ds = kubernetesService.getDeploymentStatus(project.getProjectId(), deployment); + infinispanService.saveDeploymentStatus(ds); + break; + case "DELETED": + project.setDeployed(false); + infinispanService.saveProject(project); + infinispanService.saveDeploymentStatus(new DeploymentStatus(project.getProjectId())); + break; + } + } + } + + @Override + public void onClose(WatcherException cause) { + + } +} \ No newline at end of file diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PipelineRunWatcher.java b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PipelineRunWatcher.java new file mode 100644 index 0000000..df966a3 --- /dev/null +++ b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PipelineRunWatcher.java @@ -0,0 +1,74 @@ +package org.apache.camel.karavan.watcher; + +import io.fabric8.kubernetes.client.Watcher; +import io.fabric8.kubernetes.client.WatcherException; +import io.fabric8.tekton.pipeline.v1beta1.PipelineRun; +import org.apache.camel.karavan.model.PipelineStatus; +import org.apache.camel.karavan.model.Project; +import org.apache.camel.karavan.service.InfinispanService; +import org.jboss.logging.Logger; + +import java.time.Instant; +import java.util.List; + +public class PipelineRunWatcher implements Watcher<PipelineRun> { + + private static final Logger LOGGER = Logger.getLogger(PipelineRunWatcher.class.getName()); + private InfinispanService infinispanService; + + public PipelineRunWatcher(InfinispanService infinispanService) { + this.infinispanService = infinispanService; + } + + @Override + public void eventReceived(Action action, PipelineRun pipelineRun) { + LOGGER.info(action.name() + " " + pipelineRun.getMetadata().getName()); + String projectId = pipelineRun.getMetadata().getLabels().get("karavan-project-id"); + if (projectId != null) { + Project project = infinispanService.getProject(projectId); + if (project != null && List.of("MODIFIED", "ADDED").contains(action.name())) { + PipelineStatus pipelineStatus = infinispanService.getPipelineStatus(projectId); + if (pipelineStatus == null) pipelineStatus = new PipelineStatus(project.getProjectId()); + + if (pipelineRun.getStatus() != null) { + LOGGER.info(action.name()+ " " + pipelineRun.getMetadata().getName() + " " + pipelineRun.getStatus().getConditions().get(0).getReason()); + Instant runStartTime = Instant.parse(pipelineRun.getStatus().getStartTime()); + Instant savedStartTime = pipelineStatus.getStartTime() != null + ? Instant.parse(pipelineStatus.getStartTime()) + : Instant.MIN; + + if (runStartTime.isAfter(savedStartTime) || pipelineRun.getMetadata().getName().equals(pipelineStatus.getPipelineName())) { + pipelineStatus.setPipelineName(pipelineRun.getMetadata().getName()); + pipelineStatus.setResult(pipelineRun.getStatus().getConditions().get(0).getReason()); + pipelineStatus.setStartTime(pipelineRun.getStatus().getStartTime()); + pipelineStatus.setCompletionTime(pipelineRun.getStatus().getCompletionTime()); + } + } else { + pipelineStatus.setPipelineName(pipelineRun.getMetadata().getName()); + pipelineStatus.setResult(null); + pipelineStatus.setStartTime(null); + pipelineStatus.setCompletionTime(null); + } + infinispanService.savePipelineStatus(pipelineStatus); + } + } + } + + private Long getPipelineRunDuration(PipelineRun pipelineRun) { + try { + Instant create = Instant.parse(pipelineRun.getMetadata().getCreationTimestamp()); + Instant completion = pipelineRun.getStatus().getCompletionTime() != null + ? Instant.parse(pipelineRun.getStatus().getCompletionTime()) + : Instant.now(); + return completion.getEpochSecond() - create.getEpochSecond(); + } catch (Exception ex) { + LOGGER.error(ex.getMessage()); + return 0L; + } + } + + @Override + public void onClose(WatcherException cause) { + + } +} \ No newline at end of file diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PodWatcher.java b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PodWatcher.java new file mode 100644 index 0000000..23fb200 --- /dev/null +++ b/karavan-app/src/main/java/org/apache/camel/karavan/watcher/PodWatcher.java @@ -0,0 +1,40 @@ +package org.apache.camel.karavan.watcher; + +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.client.Watcher; +import io.fabric8.kubernetes.client.WatcherException; +import org.apache.camel.karavan.model.DeploymentStatus; +import org.apache.camel.karavan.model.Project; +import org.apache.camel.karavan.service.InfinispanService; +import org.apache.camel.karavan.service.KubernetesService; +import org.jboss.logging.Logger; + +public class PodWatcher implements Watcher<Pod> { + + private static final Logger LOGGER = Logger.getLogger(PodWatcher.class.getName()); + private InfinispanService infinispanService; + private KubernetesService kubernetesService; + + public PodWatcher(InfinispanService infinispanService, KubernetesService kubernetesService) { + this.infinispanService = infinispanService; + this.kubernetesService = kubernetesService; + } + + @Override + public void eventReceived(Action action, Pod pod) { + LOGGER.info(action.name() + " " + pod.getMetadata().getName()); + String name = pod.getMetadata().getLabels().get("app.kubernetes.io/name"); + Project project = infinispanService.getProject(name); + Deployment deployment = kubernetesService.getDeployment(name, pod.getMetadata().getNamespace()); + if (project != null && deployment != null) { + DeploymentStatus s = kubernetesService.getDeploymentStatus(project.getProjectId(), deployment); + infinispanService.saveDeploymentStatus(s); + } + } + + @Override + public void onClose(WatcherException cause) { + + } +} \ No newline at end of file diff --git a/karavan-app/src/main/resources/application.properties b/karavan-app/src/main/resources/application.properties index 63e986e..bc96a84 100644 --- a/karavan-app/src/main/resources/application.properties +++ b/karavan-app/src/main/resources/application.properties @@ -1,8 +1,5 @@ karavan.version=3.18.4 -karavan.master-username=admin -karavan.master-password=karavan - # Git repository Configuration karavan.projects-git-repository=${GIT_REPOSITORY} karavan.projects-git-username=${GIT_USERNAME} @@ -40,6 +37,10 @@ karavan.config.environments[2].pipeline=karavan-quarkus karavan.config.environments[2].cluster=svc.cluster.local karavan.config.environments[2].active=false +%dev.karavan.config.environments[0].cluster=apps.home.myocp.net +%dev.karavan.config.environments[1].cluster=apps.home.myocp.net +%dev.karavan.config.environments[2].cluster=apps.home.myocp.net + # Infinispan Server address #quarkus.infinispan-client.server-list=localhost:12345 quarkus.infinispan-client.devservices.enabled=false @@ -53,6 +54,13 @@ quarkus.infinispan-client.auth-password=password # Use BASIC as a Docker for Mac workaround quarkus.infinispan-client.client-intelligence=BASIC +# Public Dev +%dev.karavan.auth=public +%dev.quarkus.oidc.enabled=false +%dev.quarkus.http.auth.basic=false +%dev.quarkus.security.users.embedded.enabled=false +%dev.quarkus.http.auth.permission.authenticated.enabled=false +%dev.quarkus.http.auth.permission.public.enabled=false # Public %public.karavan.auth=public diff --git a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx index a7031e9..4441d52 100644 --- a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx +++ b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx @@ -1,5 +1,5 @@ import axios, {AxiosResponse} from "axios"; -import {Project, ProjectFile, ProjectStatus} from "../projects/ProjectModels"; +import {DeploymentStatus, PipelineStatus, Project, ProjectFile} from "../projects/ProjectModels"; import {Buffer} from 'buffer'; import {SsoApi} from "./SsoApi"; @@ -149,8 +149,19 @@ export class KaravanApi { }); } - static async getProjectStatus(projectId: string, after: (status: ProjectStatus) => void) { - instance.get('/api/status/project/' + projectId) + static async getProjectPipelineStatus(projectId: string, after: (status: PipelineStatus) => void) { + instance.get('/api/status/pipeline/' + projectId) + .then(res => { + if (res.status === 200) { + after(res.data); + } + }).catch(err => { + console.log(err); + }); + } + + static async getProjectDeploymentStatus(projectId: string, after: (status: DeploymentStatus) => void) { + instance.get('/api/status/deployment/' + projectId) .then(res => { if (res.status === 200) { after(res.data); diff --git a/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx b/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx index c40d1d6..b3dffe3 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx @@ -7,7 +7,7 @@ import { } from '@patternfly/react-core'; import '../designer/karavan.css'; import {KaravanApi} from "../api/KaravanApi"; -import {Project, ProjectFileTypes, ProjectStatus} from "./ProjectModels"; +import {DeploymentStatus, Project, ProjectFileTypes} from "./ProjectModels"; import {ChartDonutThreshold} from "@patternfly/react-charts"; interface Props { @@ -18,7 +18,7 @@ interface Props { interface State { project?: Project, - status?: ProjectStatus, + status?: DeploymentStatus, key?: string, } @@ -50,13 +50,13 @@ export class ProjectDashboard extends React.Component<Props, State> { onRefreshStatus = () => { if (this.props.project) { - KaravanApi.getProjectStatus(this.props.project.projectId, (status: ProjectStatus) => { - this.setState({ - key: Math.random().toString(), - status: status - }); - // console.log(status); - }); + // KaravanApi.getProjectStatus(this.props.project.projectId, (status: ProjectStatus) => { + // this.setState({ + // key: Math.random().toString(), + // status: status + // }); + // // console.log(status); + // }); } } @@ -70,47 +70,47 @@ export class ProjectDashboard extends React.Component<Props, State> { } } - isUp(env: string): boolean { - if (this.state.status) { - return this.state.status.statuses.find(s => s.environment === env)?.status === 'UP'; - } else { - return false; - } - } + // isUp(env: string): boolean { + // if (this.state.status) { + // return this.state.status.statuses.find(s => s.environment === env)?.status === 'UP'; + // } else { + // return false; + // } + // } getEnvironmentData(env: string) { - const pes = this.state.status?.statuses.find(s => s.environment == env); - if (pes){ - const replicas = pes.deploymentStatus.replicas; - const data = Array.from({length: replicas}, (v, k) => { - return { x: k, y: 100/replicas } - }); - const unavailableReplicas = pes.deploymentStatus.unavailableReplicas; - const dataU = Array.from({length: unavailableReplicas}, (v, k) => { - return { x: k, y: 100/unavailableReplicas } - }); - const readyReplicas = pes.deploymentStatus.readyReplicas; - const colorScale = Array.from({length: replicas}, (v, k) => { - if (k < readyReplicas) return "rgb(56, 129, 47)" - else return "#8bc1f7" - }) - return ( - <div style={{ height: '185px', width: '185px' }}> - <ChartDonutThreshold - constrainToVisibleArea - data={data} - colorScale={colorScale} - height={185} - width={185} - > - <ChartDonutThreshold - data={dataU} - title="Pods" - /> - </ChartDonutThreshold> - </div> - ); - } + // const pes = this.state.status?.statuses.find(s => s.environment == env); + // if (pes){ + // const replicas = pes.deploymentStatus.replicas; + // const data = Array.from({length: replicas}, (v, k) => { + // return { x: k, y: 100/replicas } + // }); + // const unavailableReplicas = pes.deploymentStatus.unavailableReplicas; + // const dataU = Array.from({length: unavailableReplicas}, (v, k) => { + // return { x: k, y: 100/unavailableReplicas } + // }); + // const readyReplicas = pes.deploymentStatus.readyReplicas; + // const colorScale = Array.from({length: replicas}, (v, k) => { + // if (k < readyReplicas) return "rgb(56, 129, 47)" + // else return "#8bc1f7" + // }) + // return ( + // <div style={{ height: '185px', width: '185px' }}> + // <ChartDonutThreshold + // constrainToVisibleArea + // data={data} + // colorScale={colorScale} + // height={185} + // width={185} + // > + // <ChartDonutThreshold + // data={dataU} + // title="Pods" + // /> + // </ChartDonutThreshold> + // </div> + // ); + // } } render() { @@ -124,10 +124,10 @@ export class ProjectDashboard extends React.Component<Props, State> { <CardBody> <Flex direction={{default: "row"}} alignItems={{default:"alignItemsCenter"}}> <FlexItem> - <Badge className={this.isUp(e) ? "badge-env-up" : ""} isRead>{e}</Badge> + {/*<Badge className={this.isUp(e) ? "badge-env-up" : ""} isRead>{e}</Badge>*/} </FlexItem> <FlexItem> - {this.getEnvironmentData(e)} + {/*{this.getEnvironmentData(e)}*/} </FlexItem> </Flex> </CardBody> diff --git a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx index 74427e4..64e089b 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx @@ -11,7 +11,7 @@ import { } from '@patternfly/react-core'; import '../designer/karavan.css'; import {KaravanApi} from "../api/KaravanApi"; -import {DeploymentStatus, Project, ProjectStatus} from "./ProjectModels"; +import {DeploymentStatus, Project, PipelineStatus, CamelStatus} from "./ProjectModels"; import BuildIcon from "@patternfly/react-icons/dist/esm/icons/build-icon"; import RolloutIcon from "@patternfly/react-icons/dist/esm/icons/process-automation-icon"; import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon"; @@ -29,7 +29,9 @@ interface Props { interface State { project?: Project, - status?: ProjectStatus, + pipelineStatus?: PipelineStatus, + deploymentStatus?: DeploymentStatus, + camelStatus?: CamelStatus, isPushing: boolean, isBuilding: boolean, isRolling: boolean, @@ -77,12 +79,14 @@ export class ProjectInfo extends React.Component<Props, State> { } onRefreshStatus = () => { + // console.log("onRefreshStatus", this.props.project) if (this.props.project) { - KaravanApi.getProjectStatus(this.props.project.projectId, (status: ProjectStatus) => { - this.setState({ - key: Math.random().toString(), - status: status - }); + KaravanApi.getProjectPipelineStatus(this.props.project.projectId, (status: PipelineStatus) => { + this.setState({key: Math.random().toString(), pipelineStatus: status}); + // console.log(status); + }); + KaravanApi.getProjectDeploymentStatus(this.props.project.projectId, (status: DeploymentStatus) => { + this.setState({key: Math.random().toString(), deploymentStatus: status}); // console.log(status); }); } @@ -141,20 +145,17 @@ export class ProjectInfo extends React.Component<Props, State> { } buildButton = (env: string) => { - const isDeploying = this.state.isBuilding; - const isPushing = this.state.isPushing; - const status = this.state.status?.statuses.find(s => s.environment === env) - const pipelineResult = status?.lastPipelineRunResult; - const isRunning = pipelineResult === 'Running'; + const {isBuilding, isPushing, pipelineStatus} = this.state; + const isRunning = pipelineStatus?.result === 'Running'; return (<Tooltip content="Build and deploy" position={"left"}> - <Button isLoading={isDeploying ? true : undefined} - isDisabled={isDeploying || isRunning || isPushing} + <Button isLoading={isBuilding ? true : undefined} + isDisabled={isBuilding || isRunning || isPushing} isSmall variant="secondary" className="project-button" - icon={!isDeploying ? <BuildIcon/> : <div></div>} + icon={!isBuilding ? <BuildIcon/> : <div></div>} onClick={e => this.build()}> - {isDeploying ? "..." : "Deploy"} + {isBuilding ? "..." : "Deploy"} </Button> </Tooltip>) } @@ -203,8 +204,7 @@ export class ProjectInfo extends React.Component<Props, State> { } getEnvPanel(env: string) { - const {status, project} = this.state; - const deploymentStatus = status?.statuses.find(s => s.environment === env)?.deploymentStatus; + const {deploymentStatus} = this.state; return ( <DescriptionList isHorizontal> <DescriptionListGroup> @@ -305,7 +305,7 @@ export class ProjectInfo extends React.Component<Props, State> { } getHealthPanel(env: string) { - const status = this.state.status?.statuses.find(s => s.environment === env) + const status = this.state.camelStatus; const registryStatus = status?.registryStatus; const routesStatus = status?.routesStatus; const consumersStatus = status?.consumerStatus; @@ -325,10 +325,16 @@ export class ProjectInfo extends React.Component<Props, State> { } getPipelineState(env: string) { - const status = this.state.status?.statuses.find(s => s.environment === env) - const pipeline = status?.lastPipelineRun; - const pipelineResult = status?.lastPipelineRunResult; - const lastPipelineRunTime = status?.lastPipelineRunTime; + // console.log(this.state.pipelineStatus) + const status = this.state.pipelineStatus; + const pipeline = status?.pipelineName; + const pipelineResult = status?.result; + let lastPipelineRunTime = 0; + if (status?.startTime){ + const start:Date = new Date(status.startTime); + const finish:Date = status.completionTime != undefined && status.completionTime != null ? new Date(status.completionTime) : new Date(); + lastPipelineRunTime =Math.round( (finish.getTime() - start.getTime()) / 1000 ); + } const showTime = lastPipelineRunTime && lastPipelineRunTime > 0; const isRunning = pipelineResult === 'Running'; const isFailed = pipelineResult === 'Failed'; diff --git a/karavan-app/src/main/webapp/src/projects/ProjectModels.ts b/karavan-app/src/main/webapp/src/projects/ProjectModels.ts index 2d98870..11bf478 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectModels.ts +++ b/karavan-app/src/main/webapp/src/projects/ProjectModels.ts @@ -8,7 +8,7 @@ export class Project { public constructor(projectId: string, name: string, description: string, lastCommit: string); public constructor(init?: Partial<Project>); public constructor(...args: any[]) { - if (args.length === 1){ + if (args.length === 1) { Object.assign(this, args[0]); return; } else { @@ -21,21 +21,8 @@ export class Project { } } -export class ProjectEnvStatus { - environment: string = ''; - status: string = ''; - contextStatus: string = ''; - consumerStatus: string = ''; - routesStatus: string = ''; - registryStatus: string = ''; - contextVersion: string = ''; - lastPipelineRun: string = ''; - lastPipelineRunResult: string = ''; - lastPipelineRunTime: number = 0; - deploymentStatus: DeploymentStatus = new DeploymentStatus(); -} - export class DeploymentStatus { + projectId: string = ''; image: string = ''; replicas: number = 0; readyReplicas: number = 0; @@ -51,10 +38,21 @@ export class PodStatus { deployment: string = ''; } -export class ProjectStatus { +export class CamelStatus { + projectId: string = ''; + registryStatus: string = ''; + routesStatus: string = ''; + consumerStatus: string = ''; + contextStatus: string = ''; + contextVersion: string = ''; +} + +export class PipelineStatus { projectId: string = ''; - lastUpdate: number = 0; - statuses: ProjectEnvStatus[] = []; + pipelineName: string = ''; + result: string = ''; + startTime: string = ''; + completionTime: string = ''; } export class ProjectFile { diff --git a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx index cc913a8..abe01a4 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx @@ -26,7 +26,7 @@ import { import '../designer/karavan.css'; import {MainToolbar} from "../MainToolbar"; import {KaravanApi} from "../api/KaravanApi"; -import {Project, ProjectFile, ProjectFileTypes, ProjectStatus} from "./ProjectModels"; +import {Project, ProjectFile, ProjectFileTypes} from "./ProjectModels"; import {CamelUi} from "../designer/utils/CamelUi"; import UploadIcon from "@patternfly/react-icons/dist/esm/icons/upload-icon"; import {TableComposable, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table"; @@ -55,7 +55,6 @@ interface Props { interface State { project?: Project, - status?: ProjectStatus, file?: ProjectFile, files: ProjectFile[], isUploadModalOpen: boolean, diff --git a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx index 798fdd3..d7c65c8 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx @@ -194,7 +194,6 @@ export class ProjectsPage extends React.Component<Props, State> { } deleteModalForm() { - const {isCopy, projectToCopy, projectId, name, isCreateModalOpen} = this.state; return ( <Modal title="Confirmation" @@ -258,8 +257,7 @@ export class ProjectsPage extends React.Component<Props, State> { </Td> <Td noPadding style={{width:"180px"}}> <Flex direction={{default: "row"}}> - {environments.filter(e => e !== undefined) - .map(e => <FlexItem key={e}><Badge isRead={!project.deployed}>{e}</Badge></FlexItem>)} + <FlexItem key={"dev"}><Badge isRead={!project.deployed}>{"dev"}</Badge></FlexItem> </Flex> </Td> <Td isActionCell>
