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 562cf3b940775570001a2f5a863e6e03c9103234 Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Fri Sep 8 12:55:59 2023 -0400 Refactor for #817 --- .../camel/karavan/api/ComponentResources.java | 2 +- .../camel/karavan/api/ConfigurationResource.java | 2 +- .../apache/camel/karavan/api/DevModeResource.java | 4 +- .../apache/camel/karavan/api/ImagesResource.java | 20 ++ .../camel/karavan/api/InfrastructureResource.java | 11 +- .../apache/camel/karavan/api/KameletResources.java | 4 +- .../apache/camel/karavan/api/LogWatchResource.java | 5 +- .../camel/karavan/api/ProjectFileResource.java | 2 +- .../apache/camel/karavan/api/ProjectResource.java | 25 +- .../karavan/{service => code}/CodeService.java | 49 +-- .../camel/karavan/code/DockerComposeConverter.java | 111 +++++++ .../camel/karavan/code/model/DockerCompose.java | 29 ++ .../model/DockerComposeHealthCheck.java} | 6 +- .../model/DockerComposeService.java | 8 +- .../camel/karavan/docker/DockerForGitea.java | 20 +- .../camel/karavan/docker/DockerForInfinispan.java | 4 +- .../camel/karavan/docker/DockerForKaravan.java | 4 +- .../camel/karavan/docker/DockerForRegistry.java | 2 +- .../apache/camel/karavan/docker/DockerService.java | 3 +- .../camel/karavan/docker/DockerServiceUtils.java | 4 +- .../camel/karavan/docker/GiteaCheckCallback.java | 53 --- .../camel/karavan/{service => git}/GitService.java | 9 +- .../karavan/{service => git}/GiteaService.java | 6 +- .../{infinispan => git}/model/GitConfig.java | 2 +- .../karavan/{infinispan => git}/model/GitRepo.java | 2 +- .../{infinispan => git}/model/GitRepoFile.java | 2 +- .../camel/karavan/infinispan/model/Project.java | 21 ++ .../karavan/kubernetes/KubernetesService.java | 6 +- .../camel/karavan/kubernetes/PodEventHandler.java | 14 +- .../apache/camel/karavan/service/CamelService.java | 10 +- .../karavan/{shared => service}/ConfigService.java | 3 +- ...entService.java => ContainerStatusService.java} | 7 +- .../camel/karavan/service/KaravanService.java | 2 +- .../camel/karavan/service/ProjectService.java | 65 +++- .../camel/karavan/service/RegistryService.java | 3 - .../camel/karavan/service/ScheduledService.java | 7 +- .../org/apache/camel/karavan/shared/Constants.java | 2 - .../org/apache/camel/karavan/shared/EventType.java | 22 -- .../src/main/webui/src/api/KaravanApi.tsx | 24 +- .../src/main/webui/src/api/ProjectModels.ts | 7 +- .../src/main/webui/src/api/ProjectStore.ts | 20 +- .../karavan-app/src/main/webui/src/index.css | 13 +- .../karavan-app/src/main/webui/src/main/Main.tsx | 44 ++- .../src/main/webui/src/main/MainDataPoller.tsx | 2 +- .../src/main/webui/src/project/BuildToolbar.tsx | 22 ++ .../src/main/webui/src/project/DevModeToolbar.tsx | 24 +- .../main/webui/src/project/ProjectDataPoller.tsx | 7 +- .../src/main/webui/src/project/ProjectPage.tsx | 3 +- .../src/main/webui/src/project/ProjectPanel.tsx | 8 +- .../src/main/webui/src/project/ProjectToolbar.tsx | 10 +- .../main/webui/src/project/build/BuildStatus.tsx | 355 --------------------- .../webui/src/project/build/ProjectBuildTab.tsx | 9 +- 52 files changed, 454 insertions(+), 645 deletions(-) diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ComponentResources.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ComponentResources.java index a1ba443d..a42d1354 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ComponentResources.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ComponentResources.java @@ -16,7 +16,7 @@ */ package org.apache.camel.karavan.api; -import org.apache.camel.karavan.service.CodeService; +import org.apache.camel.karavan.code.CodeService; import jakarta.inject.Inject; import jakarta.ws.rs.GET; diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ConfigurationResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ConfigurationResource.java index 4b43f169..7cdd7012 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ConfigurationResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ConfigurationResource.java @@ -22,7 +22,7 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import org.apache.camel.karavan.shared.ConfigService; +import org.apache.camel.karavan.service.ConfigService; @Path("/api/configuration") public class ConfigurationResource { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java index 05aff589..9a49d65b 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/DevModeResource.java @@ -30,10 +30,10 @@ import org.apache.camel.karavan.infinispan.model.Project; import org.apache.camel.karavan.kubernetes.KubernetesService; import org.apache.camel.karavan.service.CamelService; import org.apache.camel.karavan.service.ProjectService; -import org.apache.camel.karavan.shared.ConfigService; +import org.apache.camel.karavan.service.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; -import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; +import static org.apache.camel.karavan.service.ContainerStatusService.CONTAINER_STATUS; @Path("/api/devmode") public class DevModeResource { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java index 882ef5db..ae4ca231 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java @@ -16,10 +16,14 @@ */ package org.apache.camel.karavan.api; +import io.vertx.core.json.JsonObject; import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.apache.camel.karavan.docker.DockerService; +import org.apache.camel.karavan.infinispan.model.Project; +import org.apache.camel.karavan.service.ProjectService; import org.apache.camel.karavan.service.RegistryService; import java.util.List; @@ -33,6 +37,9 @@ public class ImagesResource { @Inject RegistryService registryService; + @Inject + ProjectService projectService; + @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{projectId}") @@ -41,4 +48,17 @@ public class ImagesResource { String pattern = registryService.getRegistryWithGroup() + "/" + projectId; return dockerService.getImages().stream().filter(s -> s.startsWith(pattern)).toList(); } + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{projectId}") + public Response build(JsonObject imageName, @PathParam("projectId") String projectId) throws Exception { + try { + projectService.setProjectImage(projectId, imageName.getString("imageName")); + return Response.ok().entity(imageName).build(); + } catch (Exception e) { + return Response.serverError().entity(e.getMessage()).build(); + } + } } \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java index 8f94786a..f6408f84 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java @@ -20,18 +20,19 @@ import io.smallrye.mutiny.Multi; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.eventbus.EventBus; import io.vertx.mutiny.core.eventbus.Message; +import org.apache.camel.karavan.code.DockerComposeConverter; import org.apache.camel.karavan.docker.DockerForKaravan; import org.apache.camel.karavan.docker.DockerService; -import org.apache.camel.karavan.docker.model.DockerComposeService; +import org.apache.camel.karavan.code.model.DockerComposeService; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.infinispan.model.DeploymentStatus; import org.apache.camel.karavan.infinispan.model.Project; import org.apache.camel.karavan.infinispan.model.ServiceStatus; import org.apache.camel.karavan.kubernetes.KubernetesService; -import org.apache.camel.karavan.service.CodeService; +import org.apache.camel.karavan.code.CodeService; import org.apache.camel.karavan.service.ProjectService; -import org.apache.camel.karavan.shared.ConfigService; +import org.apache.camel.karavan.service.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; @@ -44,7 +45,7 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; +import static org.apache.camel.karavan.service.ContainerStatusService.CONTAINER_STATUS; @Path("/api/infrastructure") public class InfrastructureResource { @@ -199,7 +200,7 @@ public class InfrastructureResource { if (command.getString("command").equalsIgnoreCase("run")) { if (Objects.equals(type, ContainerStatus.ContainerType.devservice.name())) { String code = projectService.getDevServiceCode(); - DockerComposeService dockerComposeService = codeService.convertToDockerComposeService(code, name); + DockerComposeService dockerComposeService = DockerComposeConverter.fromCode(code, name); if (dockerComposeService != null) { dockerForKaravan.createDevserviceContainer(dockerComposeService); dockerService.runContainer(dockerComposeService.getContainer_name()); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/KameletResources.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/KameletResources.java index 95c8594d..8a437935 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/KameletResources.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/KameletResources.java @@ -19,7 +19,7 @@ package org.apache.camel.karavan.api; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.Project; import org.apache.camel.karavan.infinispan.model.ProjectFile; -import org.apache.camel.karavan.service.CodeService; +import org.apache.camel.karavan.code.CodeService; import org.yaml.snakeyaml.Yaml; import jakarta.inject.Inject; @@ -47,7 +47,7 @@ public class KameletResources { StringBuilder kamelets = new StringBuilder(codeService.getResourceFile("/kamelets/kamelets.yaml")); if (infinispanService.isReady()) { List<ProjectFile> custom = infinispanService.getProjectFiles(Project.Type.kamelets.name()); - if (custom.size() > 0) { + if (!custom.isEmpty()) { kamelets.append("\n---\n"); kamelets.append(custom.stream() .map(ProjectFile::getCode) diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java index 6f702dd4..78cc5d6e 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java @@ -21,11 +21,9 @@ import io.smallrye.context.api.ManagedExecutorConfig; import io.smallrye.context.api.NamedInstance; import org.apache.camel.karavan.docker.DockerService; import org.apache.camel.karavan.docker.LogCallback; -import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.kubernetes.KubernetesService; -import org.apache.camel.karavan.shared.ConfigService; +import org.apache.camel.karavan.service.ConfigService; import org.eclipse.microprofile.context.ManagedExecutor; -import org.eclipse.microprofile.context.ThreadContext; import org.jboss.logging.Logger; import jakarta.inject.Inject; @@ -41,7 +39,6 @@ import jakarta.ws.rs.sse.SseEventSink; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.util.List; import java.util.concurrent.ConcurrentHashMap; @Path("/api/logwatch") diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java index bd1ade90..ae8e50f0 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java @@ -18,7 +18,7 @@ package org.apache.camel.karavan.api; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.ProjectFile; -import org.apache.camel.karavan.service.CodeService; +import org.apache.camel.karavan.code.CodeService; import jakarta.inject.Inject; import jakarta.ws.rs.Consumes; 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 b0bdf4cd..302d9746 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 @@ -19,30 +19,24 @@ 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 org.apache.camel.karavan.git.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.apache.camel.karavan.service.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; -import java.util.Comparator; import java.util.List; 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 { @@ -67,14 +61,7 @@ public class ProjectResource { @GET @Produces(MediaType.APPLICATION_JSON) public List<Project> getAll(@QueryParam("type") String type) { - if (infinispanService.isReady()) { - return infinispanService.getProjects().stream() - .filter(p -> type == null || Objects.equals(p.getType().name(), type)) - .sorted(Comparator.comparing(Project::getProjectId)) - .collect(Collectors.toList()); - } else { - return List.of(); - } + return projectService.getAllProjects(type); } @GET @@ -105,10 +92,10 @@ public class ProjectResource { @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Path("/build") - public Response build(Project project) throws Exception { + @Path("/build/{tag}") + public Response build(Project project, @PathParam("tag") String tag) throws Exception { try { - projectService.buildProject(project); + projectService.buildProject(project, tag); return Response.ok().entity(project).build(); } catch (Exception e) { return Response.serverError().entity(e.getMessage()).build(); 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/code/CodeService.java similarity index 85% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CodeService.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/CodeService.java index f1e02e2a..c86eefc6 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/code/CodeService.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.karavan.service; +package org.apache.camel.karavan.code; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -23,8 +23,6 @@ import io.apicurio.datamodels.openapi.models.OasDocument; import io.quarkus.qute.Engine; import io.quarkus.qute.Template; import io.quarkus.qute.TemplateInstance; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.CamelContext; @@ -32,14 +30,14 @@ import org.apache.camel.generator.openapi.RestDslGenerator; import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.karavan.api.KameletResources; import org.apache.camel.karavan.docker.DockerService; -import org.apache.camel.karavan.docker.model.DockerComposeService; +import org.apache.camel.karavan.code.model.DockerComposeService; import org.apache.camel.karavan.infinispan.InfinispanService; -import org.apache.camel.karavan.infinispan.model.GitRepo; -import org.apache.camel.karavan.infinispan.model.GitRepoFile; +import org.apache.camel.karavan.git.model.GitRepo; +import org.apache.camel.karavan.git.model.GitRepoFile; 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.shared.ConfigService; +import org.apache.camel.karavan.service.ConfigService; import org.jboss.logging.Logger; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; @@ -287,7 +285,7 @@ public class CodeService { } public Integer getProjectPort(ProjectFile composeFile) { - DockerComposeService dcs = convertToDockerComposeService(composeFile.getCode(), composeFile.getProjectId()); + DockerComposeService dcs = DockerComposeConverter.fromCode(composeFile.getCode(), composeFile.getProjectId()); Optional<Integer> port = dcs.getPortsMap().entrySet().stream() .filter(e -> Objects.equals(e.getValue(), INTERNAL_PORT)).map(Map.Entry::getKey).findFirst(); return port.orElse(null); @@ -296,41 +294,8 @@ public class CodeService { public DockerComposeService getInternalDockerComposeService (String name) { String composeText = getResourceFile("/services/internal.yaml"); - return convertToDockerComposeService(composeText, name); + return DockerComposeConverter.fromCode(composeText, name); } - public DockerComposeService convertToDockerComposeService(String code, String name) { - Yaml yaml = new Yaml(); - Map<String, Object> obj = yaml.load(code); - JsonObject json = JsonObject.mapFrom(obj); - JsonObject services = json.getJsonObject("services"); - if (services.containsKey(name)) { - JsonObject service = services.getJsonObject(name); - return convertToDockerComposeService(name, service); - } else { - Optional<JsonObject> j = services.fieldNames().stream() - .map(services::getJsonObject) - .filter(s -> s.getString("container_name").equalsIgnoreCase(name)).findFirst(); - if (j.isPresent()) { - return convertToDockerComposeService(name, j.get()); - } - } - return null; - } - public DockerComposeService convertToDockerComposeService(String name, JsonObject service) { - if (service.containsKey(ENVIRONMENT) && service.getValue(ENVIRONMENT) instanceof JsonArray) { - JsonObject env = new JsonObject(); - service.getJsonArray(ENVIRONMENT).forEach(o -> { - String[] kv = o.toString().split("="); - env.put(kv[0], kv[1]); - }); - service.put(ENVIRONMENT, env); - } - DockerComposeService ds = service.mapTo(DockerComposeService.class); - if (ds.getContainer_name() == null) { - ds.setContainer_name(name); - } - return ds; - } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/DockerComposeConverter.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/DockerComposeConverter.java new file mode 100644 index 00000000..8424ee53 --- /dev/null +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/DockerComposeConverter.java @@ -0,0 +1,111 @@ +package org.apache.camel.karavan.code; + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import org.apache.camel.karavan.code.model.DockerCompose; +import org.apache.camel.karavan.code.model.DockerComposeService; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.introspector.Property; +import org.yaml.snakeyaml.nodes.*; +import org.yaml.snakeyaml.representer.Representer; + +import java.util.Map; + +public class DockerComposeConverter { + + private static final String ENVIRONMENT = "environment"; + + public static DockerCompose fromCode(String code) { + Yaml yaml = new Yaml(); + Map<String, Object> obj = yaml.load(code); + JsonObject json = JsonObject.mapFrom(obj); + JsonObject services = json.getJsonObject("services"); + JsonObject composeServices = new JsonObject(); + + services.getMap().forEach((name, value) -> { + JsonObject serviceJson = services.getJsonObject(name); + DockerComposeService service = convertToDockerComposeService(name, serviceJson); + composeServices.put(name, service); + }); + json.put("services", composeServices); + return json.mapTo(DockerCompose.class); + } + + public static DockerComposeService fromCode(String code, String serviceName) { + DockerCompose compose = fromCode(code); + return compose.getServices().get(serviceName); + } + + public static String toCode(DockerCompose compose) { + Yaml yaml = new Yaml(new ComposeRepresenter()); + return yaml.dumpAs(compose, Tag.MAP, DumperOptions.FlowStyle.BLOCK); + } + + public static String toCode(DockerComposeService service) { + DockerCompose dc = DockerCompose.create(service); + return toCode(dc); + } + + private static DockerComposeService convertToDockerComposeService(String name, JsonObject service) { + if (service.containsKey(ENVIRONMENT) && service.getValue(ENVIRONMENT) instanceof JsonArray) { + JsonObject env = new JsonObject(); + service.getJsonArray(ENVIRONMENT).forEach(o -> { + String[] kv = o.toString().split("="); + env.put(kv[0], kv[1]); + }); + service.put(ENVIRONMENT, env); + } + DockerComposeService ds = service.mapTo(DockerComposeService.class); + if (ds.getContainer_name() == null) { + ds.setContainer_name(name); + } + return ds; + } + + private static class ComposeRepresenter extends Representer { + + public ComposeRepresenter() { + super(create()); + } + + private static DumperOptions create() { + DumperOptions options = new DumperOptions(); + options.setExplicitStart(true); + options.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN); + options.setLineBreak(DumperOptions.LineBreak.UNIX); + return options; + } + + @Override + protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) { + if (propertyValue == null) { + return null; + } else { + NodeTuple tuple = super.representJavaBeanProperty(javaBean, property, propertyValue, customTag); + Node valueNode = tuple.getValueNode(); + if (Tag.NULL.equals(valueNode.getTag())) { + return null;// skip 'null' values + } + if (propertyValue instanceof String && (((String) propertyValue).isEmpty() || ((String) propertyValue).isBlank()) ) { + return null;// skip '' values + } + if (valueNode instanceof CollectionNode) { + if (Tag.SEQ.equals(valueNode.getTag())) { + SequenceNode seq = (SequenceNode) valueNode; + if (seq.getValue().isEmpty()) { + return null;// skip empty lists + } + } + if (Tag.MAP.equals(valueNode.getTag())) { + MappingNode seq = (MappingNode) valueNode; + if (seq.getValue().isEmpty()) { + return null;// skip empty maps + } + } + } + return tuple; + } + } + } +} \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerCompose.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerCompose.java new file mode 100644 index 00000000..36e505a8 --- /dev/null +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerCompose.java @@ -0,0 +1,29 @@ +package org.apache.camel.karavan.code.model; + +import java.util.HashMap; +import java.util.Map; + +public class DockerCompose { + private Map<String, DockerComposeService> services; + + public DockerCompose() { + } + + public DockerCompose(Map<String, DockerComposeService> services) { + this.services = services; + } + + public static DockerCompose create(DockerComposeService service) { + Map<String, DockerComposeService> map = new HashMap<>(); + map.put(service.getContainer_name(), service); + return new DockerCompose(map); + } + + public Map<String, DockerComposeService> getServices() { + return services; + } + + public void setServices(Map<String, DockerComposeService> services) { + this.services = services; + } +} diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/HealthCheckConfig.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeHealthCheck.java similarity index 91% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/HealthCheckConfig.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeHealthCheck.java index 4a631414..7cff2a90 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/HealthCheckConfig.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeHealthCheck.java @@ -1,8 +1,8 @@ -package org.apache.camel.karavan.docker.model; +package org.apache.camel.karavan.code.model; import java.util.List; -public class HealthCheckConfig { +public class DockerComposeHealthCheck { private String interval; private Integer retries; @@ -10,7 +10,7 @@ public class HealthCheckConfig { private String start_period; private List<String> test; - public HealthCheckConfig() { + public DockerComposeHealthCheck() { } public String getInterval() { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/DockerComposeService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeService.java similarity index 93% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/DockerComposeService.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeService.java index 629153e4..b3fa284a 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/model/DockerComposeService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeService.java @@ -1,4 +1,4 @@ -package org.apache.camel.karavan.docker.model; +package org.apache.camel.karavan.code.model; import java.util.*; import java.util.stream.Collectors; @@ -12,7 +12,7 @@ public class DockerComposeService { private List<String> expose; private List<String> depends_on; private Map<String,String> environment; - private HealthCheckConfig healthcheck; + private DockerComposeHealthCheck healthcheck; public DockerComposeService() { } @@ -96,11 +96,11 @@ public class DockerComposeService { this.environment = environment; } - public HealthCheckConfig getHealthcheck() { + public DockerComposeHealthCheck getHealthcheck() { return healthcheck; } - public void setHealthcheck(HealthCheckConfig healthcheck) { + public void setHealthcheck(DockerComposeHealthCheck healthcheck) { this.healthcheck = healthcheck; } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForGitea.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForGitea.java index c6386808..d1dd236f 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForGitea.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForGitea.java @@ -21,10 +21,10 @@ import com.github.dockerjava.api.model.Container; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.karavan.infinispan.model.ContainerStatus; -import org.apache.camel.karavan.infinispan.model.GitConfig; -import org.apache.camel.karavan.service.CodeService; -import org.apache.camel.karavan.service.GitService; -import org.apache.camel.karavan.service.GiteaService; +import org.apache.camel.karavan.git.model.GitConfig; +import org.apache.camel.karavan.code.CodeService; +import org.apache.camel.karavan.git.GitService; +import org.apache.camel.karavan.git.GiteaService; import org.jboss.logging.Logger; @ApplicationScoped @@ -77,16 +77,4 @@ public class DockerForGitea { LOGGER.error(e.getMessage()); } } - - protected void checkGiteaInstance() { - try { - Container gitea = dockerService.getContainerByName(GITEA_CONTAINER_NAME); - ExecCreateCmdResponse user = dockerService.execCreate(gitea.getId(), - "curl", "-Is", "localhost:3000/user/login"); - - dockerService.execStart(user.getId(), new GiteaCheckCallback(o -> createGiteaUser(), o -> checkGiteaInstance())); - } catch (Exception e) { - LOGGER.error(e.getMessage()); - } - } } \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForInfinispan.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForInfinispan.java index de1a33d7..7af32ff3 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForInfinispan.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForInfinispan.java @@ -19,7 +19,7 @@ package org.apache.camel.karavan.docker; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.karavan.infinispan.model.ContainerStatus; -import org.apache.camel.karavan.service.CodeService; +import org.apache.camel.karavan.code.CodeService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; @@ -51,7 +51,7 @@ public class DockerForInfinispan { dockerService.runContainer(INFINISPAN_CONTAINER_NAME); LOGGER.info("Infinispan is started"); } catch (Exception e) { - LOGGER.error(e.getCause().getMessage()); + LOGGER.error(e.getMessage()); } } } 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 13b4bf76..cfa74f84 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 @@ -20,10 +20,9 @@ import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.HealthCheck; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import org.apache.camel.karavan.docker.model.DockerComposeService; +import org.apache.camel.karavan.code.model.DockerComposeService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.service.RegistryService; -import org.apache.camel.karavan.shared.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; @@ -93,7 +92,6 @@ public class DockerForKaravan { public void syncImage(String projectId, String tag) throws InterruptedException { String image = registryService.getRegistryWithGroup() + "/" + projectId + ":" + tag; - System.out.println(image); dockerService.pullImage(image); } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForRegistry.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForRegistry.java index b2d1fa4e..f3fee4c7 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForRegistry.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForRegistry.java @@ -19,7 +19,7 @@ package org.apache.camel.karavan.docker; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.karavan.infinispan.model.ContainerStatus; -import org.apache.camel.karavan.service.CodeService; +import org.apache.camel.karavan.code.CodeService; import org.jboss.logging.Logger; @ApplicationScoped 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 6a06a0c9..0f680ad8 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 @@ -30,7 +30,7 @@ import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import org.apache.camel.karavan.docker.model.DockerComposeService; +import org.apache.camel.karavan.code.model.DockerComposeService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; @@ -41,7 +41,6 @@ import org.jboss.logging.Logger; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; -import java.time.Instant; import java.util.*; import java.util.stream.Collectors; 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 ebbd8403..8224d264 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 @@ -19,7 +19,7 @@ package org.apache.camel.karavan.docker; import com.github.dockerjava.api.model.*; import io.smallrye.mutiny.tuples.Tuple2; import org.apache.camel.karavan.api.KameletResources; -import org.apache.camel.karavan.docker.model.HealthCheckConfig; +import org.apache.camel.karavan.code.model.DockerComposeHealthCheck; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import java.io.BufferedReader; @@ -62,7 +62,7 @@ public class DockerServiceUtils { } } - protected HealthCheck getHealthCheck(HealthCheckConfig config) { + protected HealthCheck getHealthCheck(DockerComposeHealthCheck config) { if (config != null) { HealthCheck healthCheck = new HealthCheck().withTest(config.getTest()); if (config.getInterval() != null) { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/GiteaCheckCallback.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/GiteaCheckCallback.java deleted file mode 100644 index 0db0978e..00000000 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/GiteaCheckCallback.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.apache.camel.karavan.docker; - -import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.model.Frame; - -import java.io.Closeable; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -public class GiteaCheckCallback extends ResultCallback.Adapter<Frame> { - - private static final AtomicBoolean giteaStarted = new AtomicBoolean(false); - private static final AtomicInteger giteaCheck = new AtomicInteger(0); - - - private final Consumer createGiteaUser; - private final Consumer checkGiteaInstance; - - public GiteaCheckCallback(Consumer createGiteaUser, Consumer checkGiteaInstance) { - this.createGiteaUser = createGiteaUser; - this.checkGiteaInstance = checkGiteaInstance; - } - - @Override - public void onStart(Closeable stream) { - } - - @Override - public void onNext(Frame object) { - if (!giteaStarted.get()) { - String line = new String(object.getPayload()); - if (line.startsWith("HTTP/1.1 200")) { - giteaStarted.set(true); - createGiteaUser.accept(null); - } - } - } - - @Override - public void onError(Throwable throwable) { - - } - - @Override - public void onComplete() { - giteaCheck.incrementAndGet(); - if (!giteaStarted.get() && giteaCheck.get() < 1000) { - checkGiteaInstance.accept(null); - } - } - -} \ No newline at end of file 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/git/GitService.java similarity index 98% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/GitService.java index 7fb99882..534781e6 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/git/GitService.java @@ -14,14 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.karavan.service; +package org.apache.camel.karavan.git; import io.fabric8.kubernetes.api.model.Secret; import io.smallrye.mutiny.tuples.Tuple2; import io.vertx.core.Vertx; +import org.apache.camel.karavan.git.model.GitConfig; +import org.apache.camel.karavan.git.model.GitRepo; +import org.apache.camel.karavan.git.model.GitRepoFile; import org.apache.camel.karavan.infinispan.model.*; import org.apache.camel.karavan.kubernetes.KubernetesService; -import org.apache.camel.karavan.shared.ConfigService; +import org.apache.camel.karavan.service.ConfigService; import org.eclipse.jgit.api.CheckoutCommand; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.FetchCommand; @@ -56,8 +59,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; @ApplicationScoped public class GitService { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/GiteaService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/GiteaService.java similarity index 97% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/GiteaService.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/GiteaService.java index 469986e3..7ad208e7 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/GiteaService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/GiteaService.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.karavan.service; +package org.apache.camel.karavan.git; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.Vertx; @@ -23,8 +23,8 @@ import io.vertx.mutiny.ext.web.client.HttpResponse; import io.vertx.mutiny.ext.web.client.WebClient; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import org.apache.camel.karavan.infinispan.model.GitConfig; -import org.apache.camel.karavan.shared.ConfigService; +import org.apache.camel.karavan.git.model.GitConfig; +import org.apache.camel.karavan.service.ConfigService; import org.eclipse.microprofile.faulttolerance.Retry; import org.jboss.logging.Logger; diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/GitConfig.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/model/GitConfig.java similarity index 94% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/GitConfig.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/model/GitConfig.java index 580ee799..854f3f28 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/GitConfig.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/model/GitConfig.java @@ -1,4 +1,4 @@ -package org.apache.camel.karavan.infinispan.model; +package org.apache.camel.karavan.git.model; public class GitConfig { private String uri; diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/GitRepo.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/model/GitRepo.java similarity index 95% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/GitRepo.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/model/GitRepo.java index 4736814f..773baab7 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/GitRepo.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/model/GitRepo.java @@ -1,4 +1,4 @@ -package org.apache.camel.karavan.infinispan.model; +package org.apache.camel.karavan.git.model; import java.util.List; diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/GitRepoFile.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/model/GitRepoFile.java similarity index 93% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/GitRepoFile.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/model/GitRepoFile.java index a1186efe..ead83e90 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/GitRepoFile.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/model/GitRepoFile.java @@ -1,4 +1,4 @@ -package org.apache.camel.karavan.infinispan.model; +package org.apache.camel.karavan.git.model; public class GitRepoFile { private String name; diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/Project.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/Project.java index 9c31e296..22a7bac8 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/Project.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/model/Project.java @@ -32,8 +32,21 @@ public class Project { Long lastCommitTimestamp; @ProtoField(number = 7) Type type; + @ProtoField(number = 8) + String image; @ProtoFactory + public Project(String projectId, String name, String description, String runtime, String lastCommit, Long lastCommitTimestamp, Type type, String image) { + this.projectId = projectId; + this.name = name; + this.description = description; + this.runtime = runtime; + this.lastCommit = lastCommit; + this.lastCommitTimestamp = lastCommitTimestamp; + this.type = type; + this.image = image; + } + public Project(String projectId, String name, String description, String runtime, String lastCommit, Long lastCommitTimestamp, Type type) { this.projectId = projectId; this.name = name; @@ -122,4 +135,12 @@ public class Project { public void setType(Type type) { this.type = type; } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java index abdf4f48..4c84c03d 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java @@ -32,8 +32,8 @@ import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.infinispan.model.Project; import org.apache.camel.karavan.infinispan.model.ProjectFile; -import org.apache.camel.karavan.service.CodeService; -import org.apache.camel.karavan.shared.ConfigService; +import org.apache.camel.karavan.code.CodeService; +import org.apache.camel.karavan.service.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.health.HealthCheck; import org.eclipse.microprofile.health.HealthCheckResponse; @@ -47,7 +47,7 @@ import jakarta.inject.Inject; import java.util.*; import java.util.stream.Collectors; -import static org.apache.camel.karavan.service.CodeService.APPLICATION_PROPERTIES_FILENAME; +import static org.apache.camel.karavan.code.CodeService.APPLICATION_PROPERTIES_FILENAME; import static org.apache.camel.karavan.shared.Constants.*; @Default diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java index bdd9a7cd..a783df7c 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/PodEventHandler.java @@ -12,13 +12,11 @@ import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.jboss.logging.Logger; import java.util.List; -import java.util.Map; import java.util.Objects; -import static org.apache.camel.karavan.service.CodeService.DEFAULT_CONTAINER_RESOURCES; +import static org.apache.camel.karavan.code.CodeService.DEFAULT_CONTAINER_RESOURCES; import static org.apache.camel.karavan.shared.Constants.LABEL_PROJECT_ID; -import static org.apache.camel.karavan.shared.Constants.RELOAD_TRY_COUNT; -import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; +import static org.apache.camel.karavan.service.ContainerStatusService.CONTAINER_STATUS; public class PodEventHandler implements ResourceEventHandler<Pod> { @@ -55,10 +53,10 @@ public class PodEventHandler implements ResourceEventHandler<Pod> { if (ps != null) { eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(ps)); if (Objects.equals(ps.getState(), ContainerStatus.State.running.name())) { - Map<String, Object> message = Map.of( - LABEL_PROJECT_ID, ps.getProjectId(), - RELOAD_TRY_COUNT, 1 - ); +// Map<String, Object> message = Map.of( +// LABEL_PROJECT_ID, ps.getProjectId(), +// RELOAD_TRY_COUNT, 1 +// ); // eventBus.publish(DEVMODE_CONTAINER_READY, JsonObject.mapFrom(message)); } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java index ef96461b..7f054871 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/CamelService.java @@ -27,7 +27,6 @@ import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.CamelStatus; import org.apache.camel.karavan.infinispan.model.ContainerStatus; import org.apache.camel.karavan.kubernetes.KubernetesService; -import org.apache.camel.karavan.shared.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.faulttolerance.CircuitBreaker; import org.jboss.logging.Logger; @@ -35,20 +34,14 @@ import org.jboss.logging.Logger; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.util.Arrays; -import java.util.Map; import java.util.Objects; import java.util.concurrent.ExecutionException; -import static org.apache.camel.karavan.shared.Constants.LABEL_PROJECT_ID; -import static org.apache.camel.karavan.shared.Constants.RELOAD_TRY_COUNT; -import static org.apache.camel.karavan.shared.EventType.*; - @ApplicationScoped public class CamelService { private static final Logger LOGGER = Logger.getLogger(CamelService.class.getName()); public static final String CMD_COLLECT_CAMEL_STATUS = "collect-camel-status"; - public static final String CMD_DELETE_CAMEL_STATUS = "delete-camel-status"; @Inject InfinispanService infinispanService; @@ -96,7 +89,7 @@ public class CamelService { reloadRequest(projectId); ContainerStatus containerStatus = infinispanService.getDevModeContainerStatus(projectId, environment); containerStatus.setCodeLoaded(true); - eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); + eventBus.send(ContainerStatusService.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); } catch (Exception ex) { LOGGER.error(ex.getMessage()); } @@ -143,7 +136,6 @@ public class CamelService { cs.getType() == ContainerStatus.ContainerType.project || cs.getType() == ContainerStatus.ContainerType.devmode ).forEach(pod -> { - System.out.println(pod.getProjectId()); CamelStatusRequest csr = new CamelStatusRequest(pod.getProjectId(), pod.getContainerName()); eventBus.publish(CMD_COLLECT_CAMEL_STATUS, JsonObject.mapFrom(csr)); }); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/ConfigService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ConfigService.java similarity index 96% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/ConfigService.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ConfigService.java index 392da8a6..a1e24955 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/ConfigService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ConfigService.java @@ -14,13 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.karavan.shared; +package org.apache.camel.karavan.service; import io.quarkus.runtime.StartupEvent; import io.quarkus.runtime.configuration.ConfigUtils; import io.quarkus.runtime.configuration.ProfileManager; import io.vertx.core.Vertx; import jakarta.inject.Inject; +import org.apache.camel.karavan.shared.Configuration; import org.eclipse.microprofile.config.inject.ConfigProperty; import jakarta.enterprise.context.ApplicationScoped; 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/ContainerStatusService.java similarity index 91% rename from karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/EventService.java rename to karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ContainerStatusService.java index eb7a1302..5fc126eb 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/ContainerStatusService.java @@ -11,12 +11,11 @@ import org.jboss.logging.Logger; import java.time.Instant; import java.util.Objects; -import static org.apache.camel.karavan.shared.EventType.CONTAINER_STATUS; - @ApplicationScoped -public class EventService { +public class ContainerStatusService { - private static final Logger LOGGER = Logger.getLogger(EventService.class.getName()); + public static final String CONTAINER_STATUS = "CONTAINER_STATUS"; + private static final Logger LOGGER = Logger.getLogger(ContainerStatusService.class.getName()); @Inject InfinispanService infinispanService; diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java index 1d2270d4..77f536d0 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java @@ -27,9 +27,9 @@ import org.apache.camel.karavan.docker.DockerForGitea; import org.apache.camel.karavan.docker.DockerForInfinispan; import org.apache.camel.karavan.docker.DockerForRegistry; import org.apache.camel.karavan.docker.DockerService; +import org.apache.camel.karavan.git.GiteaService; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.kubernetes.KubernetesService; -import org.apache.camel.karavan.shared.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.health.HealthCheck; import org.eclipse.microprofile.health.HealthCheckResponse; 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 0005395a..8d0ced7d 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 @@ -18,13 +18,16 @@ package org.apache.camel.karavan.service; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.eventbus.EventBus; +import org.apache.camel.karavan.code.CodeService; +import org.apache.camel.karavan.code.DockerComposeConverter; import org.apache.camel.karavan.docker.DockerForKaravan; -import org.apache.camel.karavan.docker.model.DockerComposeService; +import org.apache.camel.karavan.code.model.DockerComposeService; +import org.apache.camel.karavan.git.GitService; +import org.apache.camel.karavan.git.model.GitConfig; +import org.apache.camel.karavan.git.model.GitRepo; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.*; import org.apache.camel.karavan.kubernetes.KubernetesService; -import org.apache.camel.karavan.shared.ConfigService; -import org.apache.camel.karavan.shared.EventType; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.faulttolerance.Retry; @@ -42,8 +45,8 @@ import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import static org.apache.camel.karavan.service.CodeService.DEV_SERVICES_FILENAME; -import static org.apache.camel.karavan.service.CodeService.PROJECT_COMPOSE_FILENAME; +import static org.apache.camel.karavan.code.CodeService.DEV_SERVICES_FILENAME; +import static org.apache.camel.karavan.code.CodeService.PROJECT_COMPOSE_FILENAME; @Default @Readiness @@ -99,7 +102,7 @@ public class ProjectService implements HealthCheck { if (!Objects.equals(status.getState(), ContainerStatus.State.running.name())) { status.setInTransit(true); - eventBus.send(EventType.CONTAINER_STATUS, JsonObject.mapFrom(status)); + eventBus.send(ContainerStatusService.CONTAINER_STATUS, JsonObject.mapFrom(status)); if (ConfigService.inKubernetes()) { kubernetesService.runDevModeContainer(project, jBangOptions); @@ -108,7 +111,7 @@ public class ProjectService implements HealthCheck { .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()); + DockerComposeService dcs = DockerComposeConverter.fromCode(compose.getCode(), project.getProjectId()); dockerForKaravan.runProjectInDevMode(project.getProjectId(), jBangOptions, dcs.getPortsMap(), files); } return containerName; @@ -117,7 +120,7 @@ public class ProjectService implements HealthCheck { } } - public String buildProject(Project project) throws Exception { + public String buildProject(Project project, String tag) throws Exception { if (ConfigService.inKubernetes()) { return kubernetesService.createPipelineRun(project); } else { @@ -128,7 +131,10 @@ public class ProjectService implements HealthCheck { String templateName = project.getRuntime() + "-builder-script-docker.sh"; String script = codeService.getTemplateText(templateName); - String tag = Instant.now().toString().substring(0, 19).replace(":", "-"); + tag = tag != null && !tag.isEmpty() && !tag.isBlank() + ? tag + : Instant.now().toString().substring(0, 19).replace(":", "-"); + List<String> env = getEnvForBuild(project, tag); Map<String, String> volumes = mavenCache .map(s -> Map.of(s, "/root/.m2")) @@ -154,6 +160,35 @@ public class ProjectService implements HealthCheck { return env; } + public List<Project> getAllProjects(String type) { + if (infinispanService.isReady()) { + List<ProjectFile> files = infinispanService.getProjectFilesByName(PROJECT_COMPOSE_FILENAME); + return infinispanService.getProjects().stream() + .filter(p -> type == null || Objects.equals(p.getType().name(), type)) + .sorted(Comparator.comparing(Project::getProjectId)) + .map(project -> { + if (Objects.equals(project.getType(), Project.Type.normal)) { + project.setImage(getImage(files, project.getProjectId())); + } + return project; + }) + .collect(Collectors.toList()); + } else { + return List.of(); + } + } + + private String getImage(List<ProjectFile> files, String projectId) { + Optional<ProjectFile> file = files.stream().filter(f -> Objects.equals(f.getProjectId(), projectId)).findFirst(); + if (file.isPresent()) { + DockerComposeService service = DockerComposeConverter.fromCode(file.get().getCode(), projectId); + String image = service.getImage(); + return Objects.equals(image, projectId) ? null : image; + } else { + return null; + } + } + public Project save(Project project) throws Exception { boolean isNew = infinispanService.getProject(project.getProjectId()) == null; infinispanService.saveProject(project); @@ -343,4 +378,16 @@ public class ProjectService implements HealthCheck { Optional<ProjectFile> file = files.stream().filter(f -> f.getName().equals(DEV_SERVICES_FILENAME)).findFirst(); return file.orElse(new ProjectFile()).getCode(); } + + public void setProjectImage(String projectId, String imageName) { + ProjectFile file = infinispanService.getProjectFile(projectId, PROJECT_COMPOSE_FILENAME); + if (file != null) { + DockerComposeService service = DockerComposeConverter.fromCode(file.getCode(), projectId); + service.setImage(imageName); + String code = DockerComposeConverter.toCode(service); + System.out.println(code); + file.setCode(code); + infinispanService.saveProjectFile(file); + } + } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/RegistryService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/RegistryService.java index 33968ed2..287ce35a 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/RegistryService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/RegistryService.java @@ -17,9 +17,6 @@ package org.apache.camel.karavan.service; import jakarta.enterprise.context.ApplicationScoped; -import org.apache.camel.karavan.infinispan.model.GitConfig; -import org.apache.camel.karavan.infinispan.model.Project; -import org.apache.camel.karavan.shared.ConfigService; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; 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 c851a93c..1a90dc6c 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 @@ -23,15 +23,12 @@ import org.apache.camel.karavan.docker.DockerForInfinispan; import org.apache.camel.karavan.docker.DockerService; import org.apache.camel.karavan.infinispan.InfinispanService; import org.apache.camel.karavan.infinispan.model.ContainerStatus; -import org.apache.camel.karavan.shared.ConfigService; -import org.apache.camel.karavan.shared.EventType; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.util.List; -import java.util.stream.Collectors; @ApplicationScoped public class ScheduledService { @@ -64,7 +61,7 @@ public class ScheduledService { if (infinispanService.isReady()) { List<ContainerStatus> statusesInDocker = dockerService.collectContainersStatistics(); statusesInDocker.forEach(containerStatus -> { - eventBus.send(EventType.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); + eventBus.send(ContainerStatusService.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); }); } } @@ -74,7 +71,7 @@ public class ScheduledService { if (infinispanService.isReady()) { List<ContainerStatus> statusesInDocker = dockerService.collectContainersStatuses(); statusesInDocker.forEach(containerStatus -> { - eventBus.send(EventType.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); + eventBus.send(ContainerStatusService.CONTAINER_STATUS, JsonObject.mapFrom(containerStatus)); }); cleanContainersStatuses(statusesInDocker); } 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 1e38924c..1b339060 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 @@ -24,7 +24,5 @@ public class Constants { public static final String LABEL_PROJECT_ID = "org.apache.camel.karavan/projectId"; public static final String LABEL_TAG = "org.apache.camel.karavan/tag"; - public static final String RELOAD_TRY_COUNT = "reloadTryCount"; - public static final String BUILDER_SUFFIX = "-builder"; } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java deleted file mode 100644 index add236cd..00000000 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/EventType.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.camel.karavan.shared; - -public class EventType { - - public static final String CONTAINER_STATUS = "CONTAINER_STATUS"; -} 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 e9704a20..40601212 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,8 +257,8 @@ export class KaravanApi { }); } - static async buildProject(project: Project, environment: string, after: (res: AxiosResponse<any>) => void) { - instance.post('/api/project/build', project) + static async buildProject(project: Project, tag: string, after: (res: AxiosResponse<any>) => void) { + instance.post('/api/project/build/' + tag, project) .then(res => { after(res); }).catch(err => { @@ -371,6 +371,15 @@ export class KaravanApi { }); } + static async setProjectImage(projectId: string, imageName: string, after: (res: AxiosResponse<any>) => void) { + instance.post('/api/image/' + projectId, {imageName: imageName}) + .then(res => { + after(res); + }).catch(err => { + after(err); + }); + } + static async pipelineRun(project: Project, environment: string, after: (res: AxiosResponse<any>) => void) { instance.post('/api/infrastructure/pipeline/' + environment, project) .then(res => { @@ -515,6 +524,17 @@ export class KaravanApi { }); } + static async getImages(projectId: string, after: (string: []) => void) { + instance.get('/api/image/' + projectId) + .then(res => { + if (res.status === 200) { + after(res.data); + } + }).catch(err => { + console.log(err); + }); + } + static async getSecrets(after: (any: []) => void) { instance.get('/api/infrastructure/secrets') .then(res => { 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 29f62995..05d76e69 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 @@ -25,9 +25,10 @@ export class Project { runtime: string = ''; lastCommit: string = ''; lastCommitTimestamp: number = 0; - type: string = ProjectType.normal + type: string = ProjectType.normal; + image: string = ''; - public constructor(projectId: string, name: string, description: string, runtime: string, lastCommit: string, type: string); + public constructor(projectId: string, name: string, description: string, runtime: string, lastCommit: string, type: string, image: string); public constructor(init?: Partial<Project>); public constructor(...args: any[]) { if (args.length === 1) { @@ -41,6 +42,7 @@ export class Project { this.lastCommit = args[4]; this.lastCommitTimestamp = args[5]; this.type = args[6]; + this.image = args[7]; return; } } @@ -141,6 +143,7 @@ export const ProjectFileTypes: ProjectFileType[] = [ new ProjectFileType("JSON", "JSON", "json"), new ProjectFileType("YAML", "YAML", "yaml"), new ProjectFileType("LOG", "Log", "log"), + new ProjectFileType("SH", "Script", "sh"), ]; 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 d42886ee..42939630 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 @@ -81,9 +81,13 @@ export const useProjectsStore = createWithEqualityFn<ProjectsState>((set) => ({ interface ProjectState { isPushing: boolean, isRunning: boolean, + images: string [], + setImages: (images: string []) => void; project: Project; setProject: (project: Project, operation: "create" | "select" | "delete"| "none" | "copy") => void; operation: "create" | "select" | "delete" | "none" | "copy"; + tabIndex: string | number; + setTabIndex: (tabIndex: string | number) => void; setOperation: (o: "create" | "select" | "delete"| "none" | "copy") => void; memory: any, setMemory: (memory: any) => void; @@ -99,7 +103,9 @@ interface ProjectState { export const useProjectStore = createWithEqualityFn<ProjectState>((set) => ({ project: new Project(), - operation: "none", + images: [], + operation: 'none', + tabIndex: 'files', isPushing: false, isRunning: false, setProject: (project: Project, operation: "create" | "select" | "delete"| "none" | "copy") => { @@ -118,6 +124,18 @@ export const useProjectStore = createWithEqualityFn<ProjectState>((set) => ({ operation: o })); }, + setTabIndex: (tabIndex: string | number) => { + set((state: ProjectState) => ({ + tabIndex: tabIndex + })); + }, + setImages: (images: string[]) => { + set((state: ProjectState) => { + state.images.length = 0; + state.images.push(...images); + return {images: state.images}; + }); + }, memory: {}, setMemory: (memory: boolean) => { set({memory: memory}) diff --git a/karavan-web/karavan-app/src/main/webui/src/index.css b/karavan-web/karavan-app/src/main/webui/src/index.css index 6cf02634..45542add 100644 --- a/karavan-web/karavan-app/src/main/webui/src/index.css +++ b/karavan-web/karavan-app/src/main/webui/src/index.css @@ -153,6 +153,7 @@ .karavan .project-page .pf-v5-c-description-list__group { min-height: 30px; + align-items: center; } .karavan .project-page .pf-v5-c-tabs__link, @@ -191,10 +192,6 @@ padding-bottom: 0; } -.karavan .project-page .project-operations { - margin-bottom: 100px; -} - .karavan .project-log { position: absolute; bottom: 0; @@ -409,6 +406,14 @@ padding: 0 5px 0 5px; } +.karavan .project-page .project-build-panel { + padding-bottom: 10px; +} + +.karavan .project-page .project-images-panel { + padding-top: 0; +} + .projects-page tr:hover .project-action-buttons { opacity: 1; } diff --git a/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx b/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx index 6e4d70d6..7ae54de6 100644 --- a/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx @@ -2,13 +2,14 @@ import {Navigate, Route, Routes} from 'react-router-dom'; import React, {useEffect, useRef} from "react"; import {KaravanApi} from "../api/KaravanApi"; import { - Bullseye, capitalize, + Bullseye, Flex, FlexItem, Page, ProgressStep, ProgressStepper, Spinner, + Text, TextContent, TextVariants, Tooltip, TooltipPosition } from "@patternfly/react-core"; @@ -29,6 +30,7 @@ import {MainDataPoller} from "./MainDataPoller"; import {TemplatesPage} from "../templates/TemplatesPage"; import {EventBus} from "../designer/utils/EventBus"; import {Notification} from "../designer/utils/Notification"; +import CheckIcon from "@patternfly/react-icons/dist/esm/icons/check-icon"; export function Main() { @@ -76,21 +78,35 @@ export function Main() { } function getStepper() { - const steps: any[] = Array.isArray(readiness.checks) ? readiness.checks : []; + const steps: any[] = Array.isArray(readiness?.checks) ? readiness.checks : []; return ( <Bullseye className=""> - <ProgressStepper aria-label="Readiness progress" isCenterAligned isVertical > - {steps.map(step => ( - <ProgressStep - variant={step.status === 'UP' ? "success" : "info"} - id={step.name} - titleId={step.name} - aria-label={step.name} - > - {step.name} - </ProgressStep> - ))} - </ProgressStepper> + <Flex direction={{default:"column"}} justifyContent={{default: "justifyContentCenter"}}> + <FlexItem style={{textAlign: "center"}}> + {Icon()} + <TextContent> + <Text component={TextVariants.h2}> + Waiting for services + </Text> + </TextContent> + </FlexItem> + <FlexItem> + <ProgressStepper aria-label="Readiness progress" isCenterAligned isVertical > + {steps.map(step => ( + <ProgressStep + variant={step.status === 'UP' ? "success" : "info"} + isCurrent={step.status !== 'UP'} + icon={step.status !== 'UP' ? <Spinner isInline aria-label="Loading..."/> : undefined} + id={step.name} + titleId={step.name} + aria-label={step.name} + > + {step.name} + </ProgressStep> + ))} + </ProgressStepper> + </FlexItem> + </Flex> </Bullseye> ) } diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx index 31885ad0..c371444c 100644 --- a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx @@ -30,7 +30,7 @@ export function MainDataPoller () { return () => { clearInterval(interval); }; - }, [project]); + }, [project, readiness]); function getData() { KaravanApi.getReadiness((r: any) => { diff --git a/karavan-web/karavan-app/src/main/webui/src/project/BuildToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/BuildToolbar.tsx new file mode 100644 index 00000000..1fa7a8f0 --- /dev/null +++ b/karavan-web/karavan-app/src/main/webui/src/project/BuildToolbar.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import {Button, Flex, FlexItem} from '@patternfly/react-core'; +import '../designer/karavan.css'; +import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/trash-icon"; +import {useAppConfigStore} from "../api/ProjectStore"; +import {shallow} from "zustand/shallow"; + +interface Props { + reloadOnly?: boolean +} + +export function BuildToolbar (props: Props) { + + const [config] = useAppConfigStore((state) => [state.config], shallow) + + return (<Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}> + <FlexItem> + <Button style={{visibility:"hidden"}} size="sm" variant={"control"} icon={<DeleteIcon/>} onClick={() => {}}> + </Button> + </FlexItem> + </Flex>); +} diff --git a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx index 78877fba..bcb9bbf1 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx @@ -76,18 +76,18 @@ export function DevModeToolbar (props: Props) { </Button> </Tooltip> </FlexItem>} - {config.infrastructure !== 'kubernetes' && - <FlexItem> - <Tooltip content="Stop container" position={TooltipPosition.bottom}> - <Button size="sm" - isDisabled={!commands.includes('stop') || inTransit} - variant={"control"} - icon={<StopIcon/>} - onClick={() => ProjectService.stopDevModeContainer(project)}> - </Button> - </Tooltip> - </FlexItem> - } + {/*{config.infrastructure !== 'kubernetes' &&*/} + {/* <FlexItem>*/} + {/* <Tooltip content="Stop container" position={TooltipPosition.bottom}>*/} + {/* <Button size="sm"*/} + {/* isDisabled={!commands.includes('stop') || inTransit}*/} + {/* variant={"control"}*/} + {/* icon={<StopIcon/>}*/} + {/* onClick={() => ProjectService.stopDevModeContainer(project)}>*/} + {/* </Button>*/} + {/* </Tooltip>*/} + {/* </FlexItem>*/} + {/*}*/} <FlexItem> <Tooltip content="Delete container" position={TooltipPosition.bottom}> <Button size="sm" diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx index 062b53e6..a70205f8 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx @@ -8,8 +8,8 @@ import {shallow} from "zustand/shallow"; export function ProjectDataPoller () { const [config] = useAppConfigStore((state) => [state.config], shallow) - const [project, setMemory, setJvm, setContext, refreshTrace, setTrace] = useProjectStore((s) => - [s.project, s.setMemory, s.setJvm, s.setContext, s.refreshTrace, s.setTrace], shallow); + const [project, setMemory, setJvm, setContext, refreshTrace, setTrace, setImages] = useProjectStore((s) => + [s.project, s.setMemory, s.setJvm, s.setContext, s.refreshTrace, s.setTrace, s.setImages], shallow); useEffect(() => { const interval = setInterval(() => onRefreshStatus(), 1000); @@ -41,6 +41,9 @@ export function ProjectDataPoller () { setContext({}); } }) + KaravanApi.getImages(project.projectId, (res: any) => { + setImages(res) + }); if (refreshTrace) { KaravanApi.getDevModeStatus(projectId, "trace", res => { if (res.status === 200) { diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx index 2101f0e4..c932c5f3 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx @@ -22,8 +22,9 @@ export function ProjectPage () { const [mode, setMode] = useState<"design" | "code">("design"); const [key, setKey] = useState<string>(''); const [projects] = useProjectsStore((state) => [state.projects], shallow) - const [project, setProject] = useProjectStore((state) => [state.project, state.setProject], shallow ) + const [project, setProject] = useProjectStore((s) => [s.project, s.setProject], shallow ) let { projectId } = useParams(); + const [tab, setTab] = useState<string | number>('files'); useEffect(() => { const p = projects.filter(project => project.projectId === projectId).at(0); diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx index 94fb1d28..27246306 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from 'react'; +import React, {useEffect} from 'react'; import { Flex, FlexItem, Tabs, Tab @@ -11,11 +11,11 @@ import {TraceTab} from "./trace/TraceTab"; import {ProjectBuildTab} from "./build/ProjectBuildTab"; import {ProjectService} from "../api/ProjectService"; import {shallow} from "zustand/shallow"; +import {ImagesPanel} from "./build/ImagesPanel"; export function ProjectPanel () { - const [tab, setTab] = useState<string | number>('files'); - const [project] = useProjectStore((state) => [state.project], shallow ) + const [project,tab, setTab] = useProjectStore((s) => [s.project, s.tabIndex, s.setTabIndex], shallow ); useEffect(() => { onRefresh(); @@ -32,7 +32,6 @@ export function ProjectPanel () { } const buildIn = isBuildIn(); - const isCustomKamelets = project.projectId === 'kamelets'; return ( <Flex direction={{default: "column"}} spaceItems={{default: "spaceItemsNone"}}> <FlexItem className="project-tabs"> @@ -51,6 +50,7 @@ export function ProjectPanel () { {tab === 'dashboard' && project && <DashboardTab/>} {tab === 'trace' && project && <TraceTab/>} {tab === 'build' && <ProjectBuildTab/>} + {tab === 'build' && <ImagesPanel/>} </> } </FlexItem> diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx index 1cd17496..c1741c72 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx @@ -21,11 +21,12 @@ import {ProjectService} from "../api/ProjectService"; import {shallow} from "zustand/shallow"; import {ProjectModelApi} from "karavan-core/lib/api/ProjectModelApi"; import {ProjectModel, ProjectProperty} from "karavan-core/lib/model/ProjectModel"; +import {BuildToolbar} from "./BuildToolbar"; export function ProjectToolbar () { - const [project, isPushing] = useProjectStore((state) => [state.project, state.isPushing], shallow ) + const [project, isPushing, tabIndex] = useProjectStore((s) => [s.project, s.isPushing, s.tabIndex], shallow ) const [file, editAdvancedProperties, setEditAdvancedProperties, setAddProperty, mode, setMode] = useFileStore((state) => [state.file, state.editAdvancedProperties, state.setEditAdvancedProperties, state.setAddProperty, state.mode, state.setMode], shallow ) @@ -108,6 +109,7 @@ export function ProjectToolbar () { return (<Toolbar id="toolbar-group-types"> <ToolbarContent> {isRunnable() && <DevModeToolbar/>} + {isBuild() && <BuildToolbar/>} </ToolbarContent> </Toolbar>) } @@ -125,7 +127,11 @@ export function ProjectToolbar () { } function isRunnable(): boolean { - return !isKameletsProject() && !isTemplatesProject() && !isServicesProject(); + return !isKameletsProject() && !isTemplatesProject() && !isServicesProject() && tabIndex !== 'build'; + } + + function isBuild(): boolean { + return !isKameletsProject() && !isTemplatesProject() && !isServicesProject() && tabIndex === 'build'; } function allowAddFiles(): boolean { 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 deleted file mode 100644 index e3a29ffc..00000000 --- a/karavan-web/karavan-app/src/main/webui/src/project/build/BuildStatus.tsx +++ /dev/null @@ -1,355 +0,0 @@ -import React, {useState} from 'react'; -import { - Button, - DescriptionList, - DescriptionListTerm, - DescriptionListGroup, - DescriptionListDescription, Spinner, Tooltip, Flex, FlexItem, LabelGroup, Label, Modal, Badge, CardBody, Card -} from '@patternfly/react-core'; -import '../../designer/karavan.css'; -import {KaravanApi} from "../../api/KaravanApi"; -import BuildIcon from "@patternfly/react-icons/dist/esm/icons/build-icon"; -import RolloutIcon from "@patternfly/react-icons/dist/esm/icons/process-automation-icon"; -import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon"; -import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon"; -import ClockIcon from "@patternfly/react-icons/dist/esm/icons/clock-icon"; -import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon"; -import {ContainerStatus} from "../../api/ProjectModels"; -import {useLogStore, useProjectStore, useStatusesStore} from "../../api/ProjectStore"; -import {shallow} from "zustand/shallow"; - -interface Props { - env: string, -} - -export function BuildStatus (props: Props) { - - const [project] = useProjectStore((s) => [s.project], shallow); - const [setShowLog] = useLogStore((s) => [s.setShowLog], shallow); - const [containers, deployments, camels, pipelineStatuses] = - useStatusesStore((s) => [s.containers, s.deployments, s.camels, s.pipelineStatuses], shallow); - const [isPushing, setIsPushing] = useState<boolean>(false); - const [isBuilding, setIsBuilding] = useState<boolean>(false); - const [isRolling, setIsRolling] = useState<boolean>(false); - const [showDeleteConfirmation, setShowDeleteConfirmation] = useState<boolean>(false); - const [deleteEntityType, setDeleteEntityType] = useState<'pod' | 'deployment' | 'build'>('pod'); - const [deleteEntityName, setDeleteEntityName] = useState<string>(); - const [deleteEntityEnv, setDeleteEntityEnv] = useState<string>(); - - function deleteEntity(type: 'pod' | 'deployment' | 'build', name: string, environment: string) { - switch (type) { - case "deployment": - KaravanApi.deleteDeployment(environment, name, (res: any) => { - // if (Array.isArray(res) && Array.from(res).length > 0) - // onRefresh(); - }); - break; - case "pod": - KaravanApi.deleteContainer(environment, 'project', name, (res: any) => { - // if (Array.isArray(res) && Array.from(res).length > 0) - // onRefresh(); - }); - break; - case "build": - KaravanApi.stopBuild(environment, name, (res: any) => { - // if (Array.isArray(res) && Array.from(res).length > 0) - // onRefresh(); - }); - break; - } - } - - function build() { - setIsBuilding(true); - setShowLog(false,'none') - KaravanApi.buildProject(project, env, res => { - if (res.status === 200 || res.status === 201) { - setIsBuilding(false); - } else { - // Todo notification - } - }); - } - - function rollout() { - setIsRolling(true); - KaravanApi.rolloutDeployment(project.projectId, env, res => { - if (res.status === 200 || res.status === 201) { - setIsRolling(false); - } else { - // Todo notification - } - }); - } - - function buildButton(env: string) { - const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0); - const isRunning = status?.result === 'Running'; - return (<Tooltip content="Start build pipeline" position={"left"}> - <Button isLoading={isBuilding ? true : undefined} - isDisabled={isBuilding || isRunning || isPushing} - size="sm" - variant="secondary" - className="project-button" - icon={!isBuilding ? <BuildIcon/> : <div></div>} - onClick={e => build()}> - {isBuilding ? "..." : "Build"} - </Button> - </Tooltip>) - } - - function rolloutButton() { - return (<Tooltip content="Rollout deployment" position={"left"}> - <Button isLoading={isRolling ? true : undefined} size="sm" variant="secondary" - className="project-button" - icon={!isRolling ? <RolloutIcon/> : <div></div>} - onClick={e => rollout()}> - {isRolling ? "..." : "Rollout"} - </Button> - </Tooltip>) - } - - function deleteDeploymentButton(env: string) { - return (<Tooltip content="Delete deployment" position={"left"}> - <Button size="sm" variant="secondary" - className="project-button" - icon={<DeleteIcon/>} - onClick={e => { - setShowDeleteConfirmation(true); - setDeleteEntityType("deployment"); - setDeleteEntityEnv(env); - setDeleteEntityName(project?.projectId); - }}> - {"Delete"} - </Button> - </Tooltip>) - } - - function getReplicasPanel(env: string) { - const deploymentStatus = deployments.find(d => d.name === project?.projectId); - const ok = (deploymentStatus && deploymentStatus?.readyReplicas > 0 - && (deploymentStatus.unavailableReplicas === 0 || deploymentStatus.unavailableReplicas === undefined || deploymentStatus.unavailableReplicas === null) - && deploymentStatus?.replicas === deploymentStatus?.readyReplicas) - return ( - <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}> - <FlexItem> - {deploymentStatus && <LabelGroup numLabels={3}> - <Tooltip content={"Ready Replicas / Replicas"} position={"left"}> - <Label icon={ok ? <UpIcon/> : <DownIcon/>} - color={ok ? "green" : "grey"}>{"Replicas: " + deploymentStatus.readyReplicas + " / " + deploymentStatus.replicas}</Label> - </Tooltip> - {deploymentStatus.unavailableReplicas > 0 && - <Tooltip content={"Unavailable replicas"} position={"right"}> - <Label icon={<DownIcon/>} color={"red"}>{deploymentStatus.unavailableReplicas}</Label> - </Tooltip> - } - </LabelGroup>} - {deploymentStatus === undefined && <Label icon={<DownIcon/>} color={"grey"}>No deployments</Label>} - </FlexItem> - <FlexItem>{env === "dev" && deleteDeploymentButton(env)}</FlexItem> - </Flex> - ) - } - - function getPodsPanel(env: string) { - const podStatuses = containers.filter(d => d.projectId === project?.projectId && d.type === 'project'); - return ( - <Flex justifyContent={{default: "justifyContentSpaceBetween"}} - alignItems={{default: "alignItemsFlexStart"}}> - <FlexItem> - {podStatuses.length === 0 && <Label icon={<DownIcon/>} color={"grey"}>No pods</Label>} - <LabelGroup numLabels={2} isVertical> - {podStatuses.map((pod: ContainerStatus) => { - const ready = pod.state === 'running'; - return ( - <Tooltip key={pod.containerName} content={pod.state}> - <Label icon={ready ? <UpIcon/> : <DownIcon/>} color={ready ? "green" : "red"}> - <Button variant="link" className="labeled-button" - onClick={e => { - setShowLog(true,'container', pod.containerName); - }}> - {pod.containerName} - </Button> - <Tooltip content={"Delete Pod"}> - <Button icon={<DeleteIcon/>} - className="labeled-button" - variant="link" - onClick={e => { - setShowDeleteConfirmation(true); - setDeleteEntityType("pod"); - setDeleteEntityEnv(env); - setDeleteEntityName(pod.containerName); - }}></Button> - </Tooltip> - </Label> - </Tooltip> - ) - } - )} - </LabelGroup> - </FlexItem> - <FlexItem>{env === "dev" && rolloutButton()}</FlexItem> - </Flex> - ) - } - - function getPipelineState(env: string) { - const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0); - 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'; - const isSucceeded = pipelineResult === 'Succeeded'; - const color = isSucceeded ? "green" : (isFailed ? "red" : (isRunning ? "blue" : "grey")) - const icon = isSucceeded ? <UpIcon className="not-spinner"/> : <DownIcon className="not-spinner"/> - return ( - <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}> - <FlexItem> - <Tooltip content={pipelineResult} position={"right"}> - <LabelGroup numLabels={2}> - <Label icon={isRunning ? <Spinner diameter="16px" className="spinner"/> : icon} - color={color}> - {pipeline - ? <Button className='labeled-button' variant="link" onClick={e => - useLogStore.setState({showLog: true, type: 'build', podName: pipeline}) - }> - {pipeline} - </Button> - : "No builder"} - {isRunning && <Tooltip content={"Stop build"}> - <Button - icon={<DeleteIcon/>} - className="labeled-button" - variant="link" onClick={e => { - setShowDeleteConfirmation(true); - setDeleteEntityType("build"); - setDeleteEntityEnv(env); - setDeleteEntityName(pipeline); - }}></Button> - </Tooltip>} - </Label> - {pipeline !== undefined && showTime === true && lastPipelineRunTime !== undefined && - <Label icon={<ClockIcon className="not-spinner"/>} - color={color}>{lastPipelineRunTime + "s"}</Label>} - </LabelGroup> - </Tooltip> - </FlexItem> - <FlexItem>{env === "dev" && buildButton(env)}</FlexItem> - </Flex> - ) - } - - 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" - title="Confirmation" - isOpen={showDeleteConfirmation} - onClose={() => setShowDeleteConfirmation(false)} - actions={[ - <Button key="confirm" variant="primary" onClick={e => { - if (deleteEntityEnv && deleteEntityName && deleteEntity) { - deleteEntity(deleteEntityType, deleteEntityName, deleteEntityEnv); - setShowDeleteConfirmation(false); - } - }}>Delete - </Button>, - <Button key="cancel" variant="link" - onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button> - ]} - onEscapePress={e => setShowDeleteConfirmation(false)}> - <div>{"Delete " + deleteEntityType + " " + deleteEntityName + "?"}</div> - </Modal>) - } - - const env = props.env; - return ( - <Card className="project-status"> - <CardBody> - <DescriptionList isHorizontal> - <DescriptionListGroup> - <DescriptionListTerm>Environment</DescriptionListTerm> - <DescriptionListDescription> - <Badge className="badge">{env}</Badge> - </DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Build container</DescriptionListTerm> - <DescriptionListDescription> - {getBuildState(env)} - </DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Deployment</DescriptionListTerm> - <DescriptionListDescription> - {getReplicasPanel(env)} - </DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Pods</DescriptionListTerm> - <DescriptionListDescription> - {getPodsPanel(env)} - </DescriptionListDescription> - </DescriptionListGroup> - </DescriptionList> - </CardBody> - {showDeleteConfirmation && getDeleteConfirmation()} - </Card> - ) -} diff --git a/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx index 89d4bbbf..6c916da4 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/build/ProjectBuildTab.tsx @@ -1,21 +1,20 @@ import React from 'react'; import '../../designer/karavan.css'; -import {BuildStatus} from "./BuildStatus"; +import {BuildPanel} from "./BuildPanel"; import {PageSection} from "@patternfly/react-core"; import {useAppConfigStore, useProjectStore} from "../../api/ProjectStore"; - export function ProjectBuildTab () { const {config} = useAppConfigStore(); const {project} = useProjectStore(); return ( - <PageSection className="project-tab-panel" padding={{default: "padding"}}> - <div className="project-operations"> + <PageSection className="project-tab-panel project-build-panel" padding={{default: "padding"}}> + <div> {/*{["dev", "test", "prod"].map(env =>*/} {config.environments.map(env => - <BuildStatus env={env}/> + <BuildPanel env={env}/> )} </div> </PageSection>