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
The following commit(s) were added to refs/heads/main by this push:
new 9620c0dc Fix #1010
9620c0dc is described below
commit 9620c0dcad12f56d59fcc8c030e1ff2b962ad2c8
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Mon Dec 4 17:17:46 2023 -0500
Fix #1010
---
.../camel/karavan/api/ContainerResource.java | 124 ++++++++++++---------
.../apache/camel/karavan/api/UsersResource.java | 1 -
.../org/apache/camel/karavan/code/CodeService.java | 55 ++++++++-
.../camel/karavan/code/model/DockerCompose.java | 19 ++++
...ockerCompose.java => DockerComposeNetwork.java} | 30 +++--
.../karavan/code/model/DockerComposeService.java | 20 ++++
.../camel/karavan/docker/DockerEventListener.java | 14 ++-
.../camel/karavan/docker/DockerForGitea.java | 2 +-
.../camel/karavan/docker/DockerForInfinispan.java | 2 +-
.../camel/karavan/docker/DockerForKaravan.java | 12 +-
.../camel/karavan/docker/DockerForRegistry.java | 2 +-
.../apache/camel/karavan/docker/DockerService.java | 57 +++++++---
.../apache/camel/karavan/docker/PullCallback.java | 47 ++++++++
.../camel/karavan/service/ProjectService.java | 28 +----
.../src/main/webui/src/api/KaravanApi.tsx | 13 ++-
.../src/main/webui/src/api/ProjectService.ts | 4 +-
.../src/main/webui/src/api/ProjectStore.ts | 24 ++++
.../src/containers/ContainerPage.css} | 39 ++-----
.../webui/src/containers/ContainerTableRow.tsx | 13 ++-
.../main/webui/src/containers/ContainersPage.tsx | 44 ++------
.../src/main/webui/src/project/ProjectPage.tsx | 16 ++-
.../main/webui/src/project/builder/BuildPanel.tsx | 2 +-
.../src/project/container/ContainerButtons.tsx | 57 ++++++----
.../webui/src/project/container/ContainerPanel.tsx | 12 +-
.../src/project/container/ProjectContainerTab.tsx | 6 +-
.../src/main/webui/src/projects/ProjectsPage.tsx | 4 +-
.../main/webui/src/projects/ProjectsTableRow.tsx | 35 ++----
.../main/webui/src/services/ServicesTableRow.tsx | 6 +-
28 files changed, 419 insertions(+), 269 deletions(-)
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java
index 23e0d8e8..8effafe0 100644
---
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java
@@ -79,67 +79,81 @@ public class ContainerResource {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
- @Path("/{env}/{type}/{name}")
- public Response manageContainer(@PathParam("env") String env,
@PathParam("type") String type, @PathParam("name") String name, JsonObject
command) throws Exception {
- if (infinispanService.isReady()) {
- if (ConfigService.inKubernetes()) {
- if (command.getString("command").equalsIgnoreCase("delete")) {
- kubernetesService.deletePod(name);
- return Response.ok().build();
- }
- } else {
- // set container statuses
- setContainerStatusTransit(name, type);
- // exec docker commands
- if (command.containsKey("command")) {
- if (command.getString("command").equalsIgnoreCase("run")) {
- if (Objects.equals(type,
ContainerStatus.ContainerType.devservice.name())) {
- String code = projectService.getDevServiceCode();
- DockerComposeService dockerComposeService =
DockerComposeConverter.fromCode(code, name);
- if (dockerComposeService != null) {
- Map<String,String> labels = new HashMap<>();
- labels.put(LABEL_TYPE,
ContainerStatus.ContainerType.devservice.name());
- labels.put(LABEL_CAMEL_RUNTIME,
CamelRuntime.CAMEL_MAIN.getValue());
- labels.put(LABEL_PROJECT_ID, name);
-
dockerService.createContainerFromCompose(dockerComposeService, labels);
-
dockerService.runContainer(dockerComposeService.getContainer_name());
- }
- } else if (Objects.equals(type,
ContainerStatus.ContainerType.project.name())) {
- DockerComposeService dockerComposeService =
projectService.getProjectDockerComposeService(name);
- if (dockerComposeService != null) {
- Map<String,String> labels = new HashMap<>();
- labels.put(LABEL_TYPE,
ContainerStatus.ContainerType.project.name());
- labels.put(LABEL_CAMEL_RUNTIME,
CamelRuntime.CAMEL_MAIN.getValue());
- labels.put(LABEL_PROJECT_ID, name);
-
dockerService.createContainerFromCompose(dockerComposeService, labels);
-
dockerService.runContainer(dockerComposeService.getContainer_name());
- }
- } else if (Objects.equals(type,
ContainerStatus.ContainerType.devmode.name())) {
-// TODO: merge with DevMode service
-// dockerForKaravan.createDevmodeContainer(name, "");
-// dockerService.runContainer(name);
- }
- return Response.ok().build();
- } else if
(command.getString("command").equalsIgnoreCase("stop")) {
- dockerService.stopContainer(name);
- return Response.ok().build();
- } else if
(command.getString("command").equalsIgnoreCase("pause")) {
- dockerService.pauseContainer(name);
+ @Path("/{projectId}/{type}/{name}")
+ public Response manageContainer(@PathParam("projectId") String projectId,
@PathParam("type") String type, @PathParam("name") String name, JsonObject
command) {
+ try {
+ if (infinispanService.isReady()) {
+ if (ConfigService.inKubernetes()) {
+ if
(command.getString("command").equalsIgnoreCase("delete")) {
+ kubernetesService.deletePod(name);
return Response.ok().build();
- } else if
(command.getString("command").equalsIgnoreCase("delete")) {
- dockerService.deleteContainer(name);
+ }
+ } else {
+ // set container statuses
+ setContainerStatusTransit(projectId, name, type);
+ // exec docker commands
+ if (command.containsKey("command")) {
+ if
(command.getString("command").equalsIgnoreCase("deploy")) {
+ deployContainer(projectId, type, command);
+ } else if
(command.getString("command").equalsIgnoreCase("run")) {
+ dockerService.runContainer(name);
+ } else if
(command.getString("command").equalsIgnoreCase("stop")) {
+ dockerService.stopContainer(name);
+ } else if
(command.getString("command").equalsIgnoreCase("pause")) {
+ dockerService.pauseContainer(name);
+ } else if
(command.getString("command").equalsIgnoreCase("delete")) {
+ dockerService.deleteContainer(name);
+ }
return Response.ok().build();
}
}
}
+ return Response.notModified().build();
+ } catch (Exception e) {
+ return Response.serverError().entity(e.getMessage()).build();
}
- return Response.notModified().build();
}
- private void setContainerStatusTransit(String name, String type){
- ContainerStatus status = infinispanService.getContainerStatus(name,
environment, name);
+ private void deployContainer(String projectId, String type, JsonObject
command) throws InterruptedException {
+ if (Objects.equals(type,
ContainerStatus.ContainerType.devservice.name())) {
+ String code = projectService.getDevServiceCode();
+ DockerComposeService dockerComposeService =
DockerComposeConverter.fromCode(code, projectId);
+ if (dockerComposeService != null) {
+ Map<String, String> labels = new HashMap<>();
+ labels.put(LABEL_TYPE,
ContainerStatus.ContainerType.devservice.name());
+ labels.put(LABEL_CAMEL_RUNTIME,
CamelRuntime.CAMEL_MAIN.getValue());
+ labels.put(LABEL_PROJECT_ID, projectId);
+ dockerService.createContainerFromCompose(dockerComposeService,
labels, needPull(command));
+
dockerService.runContainer(dockerComposeService.getContainer_name());
+ }
+ } else if (Objects.equals(type,
ContainerStatus.ContainerType.project.name())) {
+ DockerComposeService dockerComposeService =
projectService.getProjectDockerComposeService(projectId);
+ if (dockerComposeService != null) {
+ Map<String, String> labels = new HashMap<>();
+ labels.put(LABEL_TYPE,
ContainerStatus.ContainerType.project.name());
+ labels.put(LABEL_CAMEL_RUNTIME,
CamelRuntime.CAMEL_MAIN.getValue());
+ labels.put(LABEL_PROJECT_ID, projectId);
+ dockerService.createContainerFromCompose(dockerComposeService,
labels, needPull(command));
+
dockerService.runContainer(dockerComposeService.getContainer_name());
+ }
+ } else if (Objects.equals(type,
ContainerStatus.ContainerType.devmode.name())) {
+// TODO: merge with DevMode service
+// dockerForKaravan.createDevmodeContainer(name, "");
+// dockerService.runContainer(name);
+ }
+ }
+
+ private boolean needPull(JsonObject command) {
+ if (command != null && command.containsKey("pullImage")) {
+ return command.getBoolean("pullImage");
+ }
+ return false;
+ }
+
+ private void setContainerStatusTransit(String projectId, String name,
String type) {
+ ContainerStatus status =
infinispanService.getContainerStatus(projectId, environment, name);
if (status == null) {
- status = ContainerStatus.createByType(name, environment,
ContainerStatus.ContainerType.valueOf(type));
+ status = ContainerStatus.createByType(projectId, environment,
ContainerStatus.ContainerType.valueOf(type));
}
status.setInTransit(true);
eventBus.send(CONTAINER_STATUS, JsonObject.mapFrom(status));
@@ -167,11 +181,11 @@ public class ContainerResource {
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
- @Path("/{env}/{type}/{name}")
- public Response deleteContainer(@PathParam("env") String env,
@PathParam("type") String type, @PathParam("name") String name) {
+ @Path("/{projectId}/{type}/{name}")
+ public Response deleteContainer(@PathParam("projectId") String projectId,
@PathParam("type") String type, @PathParam("name") String name) {
if (infinispanService.isReady()) {
// set container statuses
- setContainerStatusTransit(name, type);
+ setContainerStatusTransit(projectId, name, type);
try {
if (ConfigService.inKubernetes()) {
kubernetesService.deletePod(name);
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/UsersResource.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/UsersResource.java
index 83bd9469..fbf6e61e 100644
---
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/UsersResource.java
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/UsersResource.java
@@ -17,7 +17,6 @@
package org.apache.camel.karavan.api;
-import io.quarkus.oidc.UserInfo;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/CodeService.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/CodeService.java
index 18e44f57..cab3a77e 100644
---
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/CodeService.java
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/CodeService.java
@@ -61,6 +61,7 @@ public class CodeService {
public static final String PROJECT_JKUBE_EXTENSION = ".jkube.yaml";
public static final String PROJECT_DEPLOYMENT_JKUBE_FILENAME =
"deployment" + PROJECT_JKUBE_EXTENSION;
private static final String SNIPPETS_PATH = "/snippets/";
+ private static final String DATA_FOLDER = System.getProperty("user.dir") +
File.separator + "data";
private static final int INTERNAL_PORT = 8080;
@Inject
@@ -257,7 +258,7 @@ public class CodeService {
.data("projectId", project.getProjectId())
.data("projectPort", port)
.data("projectImage", project.getProjectId());
- String code = instance.render();
+ String code = instance.render();
return new ProjectFile(PROJECT_COMPOSE_FILENAME, code,
project.getProjectId(), Instant.now().toEpochMilli());
}
@@ -293,11 +294,63 @@ public class CodeService {
return port.orElse(null);
}
+ public Integer getProjectPort(String projectId) {
+ ProjectFile composeFile = infinispanService.getProjectFile(projectId,
PROJECT_COMPOSE_FILENAME);
+ return getProjectPort(composeFile);
+ }
+
public DockerComposeService getInternalDockerComposeService (String name) {
String composeText = getResourceFile("/services/internal.yaml");
return DockerComposeConverter.fromCode(composeText, name);
}
+ public DockerComposeService getDockerComposeService(String projectId) {
+ ProjectFile compose = infinispanService.getProjectFile(projectId,
PROJECT_COMPOSE_FILENAME);
+ if (compose != null) {
+ return DockerComposeConverter.fromCode(compose.getCode(),
projectId);
+ }
+ return null;
+ }
+
+ public void updateDockerComposeImage(String projectId, String imageName) {
+ ProjectFile compose = infinispanService.getProjectFile(projectId,
PROJECT_COMPOSE_FILENAME);
+ if (compose != null) {
+ DockerComposeService service =
DockerComposeConverter.fromCode(compose.getCode(), projectId);
+ service.setImage(imageName);
+ String code = DockerComposeConverter.toCode(service);
+ compose.setCode(code);
+ infinispanService.saveProjectFile(compose);
+ }
+ }
+
+ public List<String> getComposeEnvironmentVariables(String projectId) {
+ DockerComposeService compose = getDockerComposeService(projectId);
+ return getComposeEnvironmentVariables(compose);
+ }
+
+ public List<String> getComposeEnvironmentVariables(DockerComposeService
compose) {
+ List<String> vars = new ArrayList<>();
+ if (compose.getEnv_file() != null && !compose.getEnv_file().isEmpty())
{
+ compose.getEnv_file().forEach(name -> {
+ String file = getDataFile(name);
+ vars.addAll(getEnvironmentVariablesFromString(file));
+ });
+ }
+ return vars;
+ }
+
+ private List<String> getEnvironmentVariablesFromString(String file) {
+ List<String> vars = new ArrayList<>();
+ if (file != null) {
+ vars = file.lines().collect(Collectors.toList());
+ }
+ return vars;
+ }
+
+ public String getDataFile(String name) {
+ String fileName = DATA_FOLDER + File.separator + name;
+ return vertx.fileSystem().readFileBlocking(fileName).toString();
+ }
}
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
index 856df6c5..d75ba9ec 100644
---
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
@@ -17,11 +17,14 @@
package org.apache.camel.karavan.code.model;
+
import java.util.HashMap;
import java.util.Map;
public class DockerCompose {
+ private String version;
private Map<String, DockerComposeService> services;
+ private Map<String, DockerComposeNetwork> networks;
public DockerCompose() {
}
@@ -43,4 +46,20 @@ public class DockerCompose {
public void setServices(Map<String, DockerComposeService> services) {
this.services = services;
}
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public Map<String, DockerComposeNetwork> getNetworks() {
+ return networks;
+ }
+
+ public void setNetworks(Map<String, DockerComposeNetwork> networks) {
+ this.networks = networks;
+ }
}
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/DockerComposeNetwork.java
similarity index 56%
copy from
karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerCompose.java
copy to
karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeNetwork.java
index 856df6c5..fb682af3 100644
---
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/DockerComposeNetwork.java
@@ -17,30 +17,28 @@
package org.apache.camel.karavan.code.model;
-import java.util.HashMap;
-import java.util.Map;
-public class DockerCompose {
- private Map<String, DockerComposeService> services;
+public class DockerComposeNetwork {
- public DockerCompose() {
+ private String name;
+ private Boolean external;
+
+ public DockerComposeNetwork() {
}
- public DockerCompose(Map<String, DockerComposeService> services) {
- this.services = services;
+ public String getName() {
+ return name;
}
- public static DockerCompose create(DockerComposeService service) {
- Map<String, DockerComposeService> map = new HashMap<>();
- map.put(service.getContainer_name(), service);
- return new DockerCompose(map);
+ public void setName(String name) {
+ this.name = name;
}
- public Map<String, DockerComposeService> getServices() {
- return services;
+ public Boolean getExternal() {
+ return external;
}
- public void setServices(Map<String, DockerComposeService> services) {
- this.services = services;
+ public void setExternal(Boolean external) {
+ this.external = external;
}
-}
+}
\ No newline at end of file
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeService.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeService.java
index 0370faae..a9df8b33 100644
---
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeService.java
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerComposeService.java
@@ -31,6 +31,8 @@ public class DockerComposeService {
private List<String> ports;
private List<String> expose;
private List<String> depends_on;
+ private List<String> env_file;
+ private List<String> networks;
private Map<String,String> environment;
private DockerComposeHealthCheck healthcheck;
@@ -124,6 +126,22 @@ public class DockerComposeService {
this.healthcheck = healthcheck;
}
+ public List<String> getEnv_file() {
+ return env_file;
+ }
+
+ public void setEnv_file(List<String> env_file) {
+ this.env_file = env_file;
+ }
+
+ public List<String> getNetworks() {
+ return networks;
+ }
+
+ public void setNetworks(List<String> networks) {
+ this.networks = networks;
+ }
+
@Override
public String toString() {
return "DockerComposeService {" +
@@ -131,6 +149,8 @@ public class DockerComposeService {
", image='" + image + '\'' +
", restart='" + restart + '\'' +
", ports=" + ports +
+ ", env_file=" + env_file +
+ ", networks=" + networks +
", expose=" + expose +
", depends_on='" + depends_on + '\'' +
", environment=" + environment +
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
index d3ed22a3..31e589e3 100644
---
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerEventListener.java
@@ -25,6 +25,7 @@ import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.apache.camel.karavan.infinispan.InfinispanService;
import org.apache.camel.karavan.infinispan.model.ContainerStatus;
+import org.apache.camel.karavan.registry.RegistryService;
import org.jboss.logging.Logger;
import java.io.Closeable;
@@ -40,7 +41,7 @@ public class DockerEventListener implements
ResultCallback<Event> {
DockerService dockerService;
@Inject
- DockerForKaravan dockerForKaravan;
+ RegistryService registryService;
@Inject
InfinispanService infinispanService;
@@ -72,11 +73,16 @@ public class DockerEventListener implements
ResultCallback<Event> {
&& Objects.equals(container.getLabels().get(LABEL_TYPE),
ContainerStatus.ContainerType.build.name())) {
String tag = container.getLabels().get(LABEL_TAG);
String projectId = container.getLabels().get(LABEL_PROJECT_ID);
- dockerForKaravan.syncImage(projectId, tag);
+ syncImage(projectId, tag);
}
}
}
+ private void syncImage(String projectId, String tag) throws
InterruptedException {
+ String image = registryService.getRegistryWithGroupForSync() + "/" +
projectId + ":" + tag;
+ dockerService.pullImage(image, true);
+ }
+
@Override
public void onError(Throwable throwable) {
LOGGER.error(throwable.getMessage());
@@ -91,6 +97,4 @@ public class DockerEventListener implements
ResultCallback<Event> {
public void close() throws IOException {
LOGGER.info("DockerEventListener close");
}
-
-
-}
+}
\ No newline at end of file
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 1e640240..7fec9e0d 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
@@ -46,7 +46,7 @@ public class DockerForGitea {
try {
LOGGER.info("Gitea container is starting...");
var compose =
codeService.getInternalDockerComposeService(GITEA_CONTAINER_NAME);
- Container c = dockerService.createContainerFromCompose(compose,
ContainerStatus.ContainerType.internal);
+ Container c = dockerService.createContainerFromCompose(compose,
ContainerStatus.ContainerType.internal, false);
dockerService.runContainer(GITEA_CONTAINER_NAME);
LOGGER.info("Gitea container is started");
} catch (Exception e) {
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 29874102..5c594513 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
@@ -47,7 +47,7 @@ public class DockerForInfinispan {
var compose =
codeService.getInternalDockerComposeService(INFINISPAN_CONTAINER_NAME);
compose.addEnvironment("USER", infinispanUsername);
compose.addEnvironment("PASS", infinispanPassword);
- dockerService.createContainerFromCompose(compose,
ContainerStatus.ContainerType.internal);
+ dockerService.createContainerFromCompose(compose,
ContainerStatus.ContainerType.internal, false);
dockerService.runContainer(INFINISPAN_CONTAINER_NAME);
LOGGER.info("Infinispan is started");
} catch (Exception e) {
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 54c31973..9c757097 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
@@ -23,7 +23,6 @@ import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.apache.camel.karavan.infinispan.model.ContainerStatus;
import org.apache.camel.karavan.infinispan.model.Project;
-import org.apache.camel.karavan.registry.RegistryService;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;
@@ -47,9 +46,6 @@ public class DockerForKaravan {
@Inject
DockerService dockerService;
- @Inject
- RegistryService registryService;
-
public void runProjectInDevMode(String projectId, String jBangOptions,
Map<Integer, Integer> ports,
Map<String, String> files) throws
Exception {
Map<String, String> volumes = getMavenVolumes();
@@ -74,7 +70,7 @@ public class DockerForKaravan {
LABEL_PROJECT_ID, projectId,
LABEL_CAMEL_RUNTIME, CamelRuntime.CAMEL_MAIN.getValue()
),
- volumes, null, RestartPolicy.noRestart());
+ volumes, null, RestartPolicy.noRestart(), false);
}
@@ -97,15 +93,11 @@ public class DockerForKaravan {
LABEL_PROJECT_ID, project.getProjectId(),
LABEL_TAG, tag
),
- volumes, null,RestartPolicy.noRestart(),
"/karavan/builder/build.sh");
+ volumes, null,RestartPolicy.noRestart(), false,
"/karavan/builder/build.sh");
}
private Map<String,String> getMavenVolumes(){
return mavenCache.map(s -> Map.of(s,
"/karavan/.m2")).orElseGet(Map::of);
}
- public void syncImage(String projectId, String tag) throws
InterruptedException {
- String image = registryService.getRegistryWithGroupForSync() + "/" +
projectId + ":" + tag;
- 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 0bd9c6d0..4638fe82 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
@@ -39,7 +39,7 @@ public class DockerForRegistry {
try {
LOGGER.info("Registry is starting...");
var compose =
codeService.getInternalDockerComposeService(REGISTRY_CONTAINER_NAME);
- dockerService.createContainerFromCompose(compose,
ContainerStatus.ContainerType.internal);
+ dockerService.createContainerFromCompose(compose,
ContainerStatus.ContainerType.internal, false);
dockerService.runContainer(REGISTRY_CONTAINER_NAME);
LOGGER.info("Registry is started");
} catch (Exception e) {
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 00389096..b87e945d 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
@@ -62,6 +62,15 @@ public class DockerService extends DockerServiceUtils {
@ConfigProperty(name = "karavan.docker.network")
String networkName;
+ @ConfigProperty(name = "karavan.image-registry")
+ String registry;
+ @ConfigProperty(name = "karavan.image-group")
+ String group;
+ @ConfigProperty(name = "karavan.image-registry-username")
+ Optional<String> username;
+ @ConfigProperty(name = "karavan.image-registry-password")
+ Optional<String> password;
+
@Inject
DockerEventListener dockerEventListener;
@@ -154,25 +163,31 @@ public class DockerService extends DockerServiceUtils {
return stats;
}
- public Container createContainerFromCompose(DockerComposeService compose,
ContainerStatus.ContainerType type, String... command) throws
InterruptedException {
- return createContainerFromCompose(compose, type, Map.of(), command);
+ public Container createContainerFromCompose(DockerComposeService compose,
ContainerStatus.ContainerType type, Boolean pull, String... command) throws
InterruptedException {
+ return createContainerFromCompose(compose, type, Map.of(), pull,
command);
}
- public Container createContainerFromCompose(DockerComposeService compose,
ContainerStatus.ContainerType type, Map<String, String> volumes, String...
command) throws InterruptedException {
+ public Container createContainerFromCompose(DockerComposeService compose,
ContainerStatus.ContainerType type, Map<String, String> volumes, Boolean
pullAlways, String... command) throws InterruptedException {
Map<String,String> labels = new HashMap<>();
labels.put(LABEL_TYPE, type.name());
- return createContainerFromCompose(compose, labels, volumes, command);
+ return createContainerFromCompose(compose, labels, volumes,
pullAlways, command);
}
- public Container createContainerFromCompose(DockerComposeService compose,
Map<String, String> labels, String... command) throws InterruptedException {
- return createContainerFromCompose(compose, labels, Map.of(), command);
+ public Container createContainerFromCompose(DockerComposeService compose,
Map<String, String> labels, Boolean pullAlways, String... command) throws
InterruptedException {
+ return createContainerFromCompose(compose, labels, Map.of(),
pullAlways, command);
}
- public Container createContainerFromCompose(DockerComposeService compose,
Map<String, String> labels, Map<String, String> volumes, String... command)
throws InterruptedException {
+ public Container createContainerFromCompose(DockerComposeService compose,
Map<String, String> labels, Map<String, String> volumes, Boolean pullAlways,
String... command) throws InterruptedException {
List<Container> containers =
findContainer(compose.getContainer_name());
if (containers.isEmpty()) {
HealthCheck healthCheck = getHealthCheck(compose.getHealthcheck());
- List<String> env = compose.getEnvironment() != null ?
compose.getEnvironmentList() : List.of();
+
+ List<String> env = new ArrayList<>();
+ if (compose.getEnv_file() != null) {
+
env.addAll(codeService.getComposeEnvironmentVariables(compose));
+ } else {
+ env.addAll(compose.getEnvironmentList());
+ }
LOGGER.infof("Compose Service started for %s in network:%s",
compose.getContainer_name(), networkName);
@@ -184,7 +199,7 @@ public class DockerService extends DockerServiceUtils {
}
return createContainer(compose.getContainer_name(),
compose.getImage(),
- env, compose.getPortsMap(), healthCheck, labels, volumes,
networkName, restartPolicy, command);
+ env, compose.getPortsMap(), healthCheck, labels, volumes,
networkName, restartPolicy, pullAlways, command);
} else {
LOGGER.info("Compose Service already exists: " +
containers.get(0).getId());
@@ -200,10 +215,11 @@ public class DockerService extends DockerServiceUtils {
public Container createContainer(String name, String image, List<String>
env, Map<Integer, Integer> ports,
HealthCheck healthCheck, Map<String,
String> labels,
Map<String, String> volumes, String
network, RestartPolicy restartPolicy,
+ boolean pullAlways,
String... command) throws
InterruptedException {
List<Container> containers = findContainer(name);
if (containers.isEmpty()) {
- pullImage(image);
+ pullImage(image, pullAlways);
CreateContainerCmd createContainerCmd =
getDockerClient().createContainerCmd(image)
.withName(name).withLabels(labels).withEnv(env).withHostName(name).withHealthcheck(healthCheck);
@@ -290,7 +306,7 @@ public class DockerService extends DockerServiceUtils {
dockerClient.execStartCmd(id).exec(callBack).awaitCompletion();
}
- public void copyFiles(String containerId, String containerPath,
Map<String, String> files, boolean dirChildrenOnly) throws IOException {
+ protected void copyFiles(String containerId, String containerPath,
Map<String, String> files, boolean dirChildrenOnly) throws IOException {
String temp = codeService.saveProjectFilesInTemp(files);
dockerClient.copyArchiveToContainerCmd(containerId).withRemotePath(containerPath)
.withDirChildrenOnly(dirChildrenOnly).withHostResource(temp).exec();
@@ -355,7 +371,7 @@ public class DockerService extends DockerServiceUtils {
List<Container> containers = findContainer(name);
if (containers.size() == 1) {
Container container = containers.get(0);
- if (container.getState().equals("running")) {
+ if (container.getState().equals("running") ||
container.getState().equals("paused")) {
getDockerClient().stopContainerCmd(container.getId()).exec();
}
}
@@ -369,20 +385,31 @@ public class DockerService extends DockerServiceUtils {
}
}
- public void pullImage(String image) throws InterruptedException {
+ public void pullImage(String image, boolean pullAlways) throws
InterruptedException {
List<Image> images =
getDockerClient().listImagesCmd().withShowAll(true).exec();
List<String> tags = images.stream()
.map(i ->
Arrays.stream(i.getRepoTags()).collect(Collectors.toList()))
.flatMap(Collection::stream)
.toList();
- if (!images.stream().anyMatch(i -> tags.contains(image))) {
- ResultCallback.Adapter<PullResponseItem> pull =
getDockerClient().pullImageCmd(image).start().awaitCompletion();
+ if (pullAlways || !images.stream().anyMatch(i ->
tags.contains(image))) {
+ var callback = new PullCallback(LOGGER::info);
+ getDockerClient().pullImageCmd(image).exec(callback);
+ callback.awaitCompletion();
}
}
private DockerClientConfig getDockerClientConfig() {
+ LOGGER.info("Docker Client Configuring....");
+ LOGGER.info("Docker Client Registry " + registry);
+ LOGGER.info("Docker Client Username " + (username.isPresent() ? "is
not empty " : "is empty"));
+ LOGGER.info("Docker Client Password " + (password.isPresent() ? "is
not empty " : "is empty"));
DefaultDockerClientConfig.Builder builder =
DefaultDockerClientConfig.createDefaultConfigBuilder();
+ if (!Objects.equals(registry, "registry:5000") && username.isPresent()
&& password.isPresent()) {
+ builder.withRegistryUrl(registry);
+ builder.withRegistryUsername(username.get());
+ builder.withRegistryPassword(password.get());
+ }
return builder.build();
}
diff --git
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/PullCallback.java
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/PullCallback.java
new file mode 100644
index 00000000..3f87e3b4
--- /dev/null
+++
b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/docker/PullCallback.java
@@ -0,0 +1,47 @@
+/*
+ * 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.docker;
+
+import com.github.dockerjava.api.async.ResultCallback;
+import com.github.dockerjava.api.model.PullResponseItem;
+
+import java.util.function.Consumer;
+
+public class PullCallback extends ResultCallback.Adapter<PullResponseItem> {
+
+ private final Consumer<String> action;
+
+ public PullCallback(Consumer<String> action) {
+ this.action = action;
+ }
+
+ @Override
+ public void onNext(PullResponseItem item) {
+ StringBuilder line = new StringBuilder();
+ if (item.getId() != null) {
+ line.append("Layer ").append(item.getId()).append(", ");
+ }
+ line.append(item.getStatus()).append(" ");
+ if (item.getProgressDetail() != null &&
item.getProgressDetail().getCurrent() != null &&
item.getProgressDetail().getTotal() != null) {
+ long progress = (long)
((item.getProgressDetail().getCurrent().doubleValue() /
item.getProgressDetail().getTotal().doubleValue()) * 100);
+ line.append(" ").append(progress).append("%");
+ }
+ action.accept(line.toString());
+ }
+
+}
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 37e71092..fca9c3c1 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
@@ -60,8 +60,6 @@ public class ProjectService implements HealthCheck {
@ConfigProperty(name = "karavan.environment")
String environment;
-
-
@Inject
InfinispanService infinispanService;
@@ -110,8 +108,7 @@ public class ProjectService implements HealthCheck {
} else {
Map<String, String> files =
codeService.getProjectFilesForDevMode(project.getProjectId(), true);
- ProjectFile compose =
infinispanService.getProjectFile(project.getProjectId(),
PROJECT_COMPOSE_FILENAME);
- DockerComposeService dcs =
DockerComposeConverter.fromCode(compose.getCode(), project.getProjectId());
+ DockerComposeService dcs =
codeService.getDockerComposeService(project.getProjectId());
dockerForKaravan.runProjectInDevMode(project.getProjectId(),
jBangOptions, dcs.getPortsMap(), files);
}
return containerName;
@@ -150,7 +147,6 @@ public class ProjectService implements HealthCheck {
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))
@@ -245,8 +241,7 @@ public class ProjectService implements HealthCheck {
}
public Integer getProjectPort(String projectId) {
- ProjectFile composeFile = infinispanService.getProjectFile(projectId,
PROJECT_COMPOSE_FILENAME);
- return codeService.getProjectPort(composeFile);
+ return codeService.getProjectPort(projectId);
}
// @Retry(maxRetries = 100, delay = 2000)
@@ -399,24 +394,13 @@ public class ProjectService implements HealthCheck {
}
public void setProjectImage(String projectId, String imageName, boolean
commit, String message) throws Exception {
- 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);
- file.setCode(code);
- infinispanService.saveProjectFile(file);
- if (commit) {
- commitAndPushProject(projectId, message);
- }
+ codeService.updateDockerComposeImage(projectId, imageName);
+ if (commit) {
+ commitAndPushProject(projectId, message);
}
}
public DockerComposeService getProjectDockerComposeService(String
projectId) {
- ProjectFile file = infinispanService.getProjectFile(projectId,
PROJECT_COMPOSE_FILENAME);
- if (file != null) {
- return DockerComposeConverter.fromCode(file.getCode(), projectId);
- }
- return null;
+ return codeService.getDockerComposeService(projectId);
}
}
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 4452b2ca..8f584d93 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
@@ -463,12 +463,13 @@ export class KaravanApi {
});
}
- static async manageContainer(environment: string,
+ static async manageContainer(projectId: string,
type: 'devmode' | 'devservice' | 'project' |
'internal' | 'build' | 'unknown',
name: string,
- command: 'run' | 'pause' | 'stop' | 'delete',
- after: (res: AxiosResponse<any>) => void) {
- instance.post('/api/container/' + environment + '/' + type + "/" +
name, {command: command})
+ command: 'deploy' | 'run' | 'pause' | 'stop'
| 'delete',
+ pullImage: boolean,
+ after: (res: AxiosResponse<any> | any) =>
void) {
+ instance.post('/api/container/' + projectId + '/' + type + "/" + name,
{command: command, pullImage: pullImage})
.then(res => {
after(res);
}).catch(err => {
@@ -476,8 +477,8 @@ export class KaravanApi {
});
}
- static async deleteContainer(environment: string, type: 'devmode' |
'devservice' | 'project' | 'internal' | 'build' | 'unknown', name: string,
after: (res: AxiosResponse<any>) => void) {
- instance.delete('/api/container/' + environment + '/' + type + "/" +
name)
+ static async deleteContainer(projectId: string, type: 'devmode' |
'devservice' | 'project' | 'internal' | 'build' | 'unknown', name: string,
after: (res: AxiosResponse<any>) => void) {
+ instance.delete('/api/container/' + projectId + '/' + type + "/" +
name)
.then(res => {
after(res);
}).catch(err => {
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
index 26400e4d..3c1588bc 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
@@ -61,7 +61,7 @@ export class ProjectService {
public static stopDevModeContainer(project: Project) {
useDevModeStore.setState({status: 'wip'})
- KaravanApi.manageContainer('dev', 'devmode', project.projectId,
'stop', res => {
+ KaravanApi.manageContainer(project.projectId, 'devmode',
project.projectId, 'stop', false,res => {
useDevModeStore.setState({status: 'none'})
if (res.status === 200) {
useLogStore.setState({showLog: false, type: 'container'})
@@ -73,7 +73,7 @@ export class ProjectService {
public static pauseDevModeContainer(project: Project) {
useDevModeStore.setState({status: 'wip'})
- KaravanApi.manageContainer('dev', 'devmode', project.projectId,
'pause', res => {
+ KaravanApi.manageContainer(project.projectId, 'devmode',
project.projectId, 'pause', false,res => {
useDevModeStore.setState({status: 'none'})
if (res.status === 200) {
useLogStore.setState({showLog: false, type: 'container'})
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 e1c5d47c..23d90b90 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
@@ -36,6 +36,9 @@ interface AppConfigState {
setConfig: (config: AppConfig) => void;
readiness: any;
setReadiness: (readiness: any) => void;
+ selectedEnv: string[];
+ setSelectedEnv: (selectedEnv: string[]) => void;
+ selectEnvironment: (name: string, selected: boolean) => void;
}
export const useAppConfigStore = createWithEqualityFn<AppConfigState>((set) =>
({
@@ -57,6 +60,27 @@ export const useAppConfigStore =
createWithEqualityFn<AppConfigState>((set) => (
}
});
},
+ selectedEnv: [],
+ setSelectedEnv: (selectedEnv: string[]) => {
+ set((state: AppConfigState) => {
+ state.selectedEnv.length = 0;
+ state.selectedEnv.push(...selectedEnv);
+ return {selectedEnv: state.selectedEnv};
+ });
+ },
+ selectEnvironment(name: string, selected: boolean) {
+ console.log(name, selected)
+ set((state: AppConfigState) => {
+ if (selected && !state.selectedEnv.includes(name)) {
+ state.selectedEnv.push(name);
+ } else if (!selected && state.selectedEnv.includes(name)) {
+ const filtered = state.selectedEnv.filter(e => e !== name);
+ state.selectedEnv.length = 0;
+ state.selectedEnv.push(...filtered);
+ }
+ return {selectedEnv: state.selectedEnv};
+ });
+ }
}), shallow)
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/webui/src/containers/ContainerPage.css
similarity index 51%
copy from
karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerCompose.java
copy to karavan-web/karavan-app/src/main/webui/src/containers/ContainerPage.css
index 856df6c5..6f32d1ef 100644
---
a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/code/model/DockerCompose.java
+++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerPage.css
@@ -15,32 +15,15 @@
* limitations under the License.
*/
-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;
- }
+.container-page {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
}
+
+.container-page-section {
+ background-color: var(--pf-v5-global--BackgroundColor--light-300);
+ flex-grow: 1;
+ flex-shrink: 1;
+ overflow: auto;
+}
\ No newline at end of file
diff --git
a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
index b93c8543..0e43e5f8 100644
---
a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
+++
b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
@@ -29,6 +29,8 @@ import {ContainerStatus} from "../api/ProjectModels";
import PauseIcon from "@patternfly/react-icons/dist/esm/icons/pause-icon";
import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
import {KaravanApi} from "../api/KaravanApi";
+import {useAppConfigStore} from "../api/ProjectStore";
+import {shallow} from "zustand/shallow";
interface Props {
index: number
@@ -37,6 +39,7 @@ interface Props {
export function ContainerTableRow(props: Props) {
+ const [config] = useAppConfigStore((state) => [state.config], shallow);
const [isExpanded, setIsExpanded] = useState<boolean>(false);
const [showConfirmation, setShowConfirmation] = useState<boolean>(false);
const [command, setCommand] = useState<'run' | 'pause' | 'stop' |
'delete'>();
@@ -50,14 +53,15 @@ export function ContainerTableRow(props: Props) {
function getConfirmation() {
return (<Modal
- className="modal-delete"
+ className="modal-confirm"
title="Confirmation"
+ variant={"small"}
isOpen={showConfirmation}
onClose={() => setShowConfirmation(false)}
actions={[
<Button key="confirm" variant="primary" onClick={e => {
if (command) {
- KaravanApi.manageContainer(container.env,
container.type, container.containerName, command, res => {
+ KaravanApi.manageContainer(container.projectId,
container.type, container.containerName, command, false,res => {
});
setCommand(undefined);
setShowConfirmation(false);
@@ -90,6 +94,9 @@ export function ContainerTableRow(props: Props) {
: undefined}
modifier={"fitContent"}>
</Td>
+ <Td>
+ {container.env}
+ </Td>
<Td style={{verticalAlign: "middle"}} modifier={"fitContent"}>
<Badge className="badge">{container.type}</Badge>
</Td>
@@ -107,7 +114,7 @@ export function ContainerTableRow(props: Props) {
{inTransit && <Spinner size="lg" aria-label="spinner"/>}
</Td>
<Td>
- {container.type !== 'internal' &&
+ {container.type !== 'internal' && container.env ===
config.environment &&
<Flex direction={{default: "row"}} flexWrap={{default:
"nowrap"}}
spaceItems={{default: 'spaceItemsNone'}}>
<FlexItem>
diff --git
a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
index 5f8bae73..41d6b467 100644
--- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
@@ -28,7 +28,8 @@ import {
ToolbarItem, EmptyStateHeader
} from '@patternfly/react-core';
import '../designer/karavan.css';
-import {ContainerStatus, ProjectType} from "../api/ProjectModels";
+import './ContainerPage.css';
+import {ContainerStatus} from "../api/ProjectModels";
import {
TableVariant,
Tbody,
@@ -46,16 +47,13 @@ import {useAppConfigStore, useStatusesStore} from
"../api/ProjectStore";
import {shallow} from "zustand/shallow";
import {ContainerTableRow} from "./ContainerTableRow";
import {ProjectService} from "../api/ProjectService";
-import {KaravanApi} from "../api/KaravanApi";
-import {DockerCompose, ServicesYaml} from "../api/ServiceModels";
export function ContainersPage () {
- const [config] = useAppConfigStore((state) => [state.config], shallow)
+ const [config, selectedEnv, selectEnvironment] = useAppConfigStore((state)
=> [state.config,state.selectedEnv, state.selectEnvironment], shallow)
const [containers] = useStatusesStore((state) => [state.containers,
state.setContainers], shallow);
const [filter, setFilter] = useState<string>('');
const [loading] = useState<boolean>(true);
- const [selectedEnv, setSelectedEnv] =
useState<string[]>([config.environment]);
useEffect(() => {
const interval = setInterval(() => {
@@ -64,19 +62,6 @@ export function ContainersPage () {
return () => clearInterval(interval);
}, []);
- function selectEnvironment(name: string, selected: boolean) {
- if (selected && !selectedEnv.includes(name)) {
- setSelectedEnv((state: string[]) => {
- state.push(name);
- return state;
- })
- } else if (!selected && selectedEnv.includes(name)) {
- setSelectedEnv((state: string[]) => {
- return state.filter(e => e !== name)
- })
- }
- }
-
function tools() {
return (<Toolbar id="toolbar-group-types">
<ToolbarContent>
@@ -104,18 +89,6 @@ export function ContainersPage () {
</TextContent>);
}
- function getSelectedEnvironments(): string [] {
- return config.environments.filter(e => selectedEnv.includes(e));
- }
-
- function getContainerByEnvironments(name: string): [string,
ContainerStatus | undefined] [] {
- return selectedEnv.map(e => {
- const env: string = e as string;
- const container = containers.find(d => d.containerName === name &&
d.env === env);
- return [env, container];
- });
- }
-
function getEmptyState() {
return (
<Tbody>
@@ -135,17 +108,20 @@ export function ContainersPage () {
)
}
- const conts = containers.filter(d =>
d.containerName.toLowerCase().includes(filter));
+ const conts = containers
+ .filter(c => selectedEnv.includes(c.env))
+ .filter(d => d.containerName.toLowerCase().includes(filter));
return (
- <PageSection className="kamelet-section" padding={{default:
'noPadding'}}>
+ <PageSection className="container-page" padding={{default:
'noPadding'}}>
<PageSection className="tools-section" padding={{default:
'noPadding'}}>
<MainToolbar title={title()} tools={tools()}/>
</PageSection>
- <PageSection isFilled className="kamelets-page">
+ <PageSection isFilled className="container-page-section">
<Table aria-label="Projects" variant={TableVariant.compact}>
<Thead>
<Tr>
<Th modifier="fitContent" textCenter={true} />
+ <Th modifier="fitContent" textCenter={true}
key='env'>Env</Th>
<Th modifier="fitContent" textCenter={true}
key='type'>Type</Th>
<Th key='name'>Name</Th>
<Th modifier="fitContent" textCenter={true}
key='cpuInfo'>CPU</Th>
@@ -155,7 +131,7 @@ export function ContainersPage () {
</Tr>
</Thead>
{conts?.map((container: ContainerStatus, index: number) =>
(
- <ContainerTableRow key={container.containerName}
index={index} container={container}/>
+ <ContainerTableRow
key={`${container.containerName}-${container.env}`} index={index}
container={container}/>
))}
{conts?.length === 0 && getEmptyState()}
</Table>
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 8928d4f9..95e72b12 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
@@ -25,7 +25,7 @@ import '../designer/karavan.css';
import {ProjectToolbar} from "./ProjectToolbar";
import {ProjectLogPanel} from "./log/ProjectLogPanel";
import {Project} from "../api/ProjectModels";
-import {useAppConfigStore, useFileStore, useProjectsStore, useProjectStore}
from "../api/ProjectStore";
+import {useAppConfigStore, useFilesStore, useFileStore, useProjectsStore,
useProjectStore} from "../api/ProjectStore";
import {MainToolbar} from "../designer/MainToolbar";
import {ProjectTitle} from "./ProjectTitle";
import {ProjectPanel} from "./ProjectPanel";
@@ -40,6 +40,7 @@ export function ProjectPage() {
const [config] = useAppConfigStore((state) => [state.config], shallow)
const {file, operation} = useFileStore();
+ const [files] = useFilesStore((s) => [s.files], shallow);
const [projects] = useProjectsStore((state) => [state.projects], shallow)
const [project, setProject, tabIndex, setTabIndex, refreshTrace] =
useProjectStore((s) => [s.project, s.setProject, s.tabIndex,
s.setTabIndex, s.refreshTrace], shallow);
@@ -72,11 +73,16 @@ export function ProjectPage() {
return ['kamelets', 'templates',
'services'].includes(project.projectId);
}
+ function isEphemeral(): boolean {
+ return files.filter(f => f.name.endsWith(".camel.yaml") || f.name ===
'application.properties').length === 0;
+ }
+
function showTabs(): boolean {
return !isBuildIn() && !showFilePanel;
}
const buildIn = isBuildIn();
+ const ephemeral = isEphemeral();
const showFilePanel = file !== undefined && operation === 'select';
return (
<PageSection className="project-page" padding={{default: 'noPadding'}}>
@@ -88,10 +94,10 @@ export function ProjectPage() {
<FlexItem className="project-tabs">
{showTabs() && <Tabs activeKey={tabIndex}
onSelect={(event, tabIndex) => setTabIndex(tabIndex)}>
<Tab eventKey="files" title="Files"/>
- <Tab eventKey="topology" title="Topology"/>
- <Tab eventKey="dashboard" title="Dashboard"/>
- <Tab eventKey="trace" title="Trace"/>
- <Tab eventKey="build" title="Build"/>
+ {!ephemeral && <Tab eventKey="topology"
title="Topology"/>}
+ {!ephemeral && <Tab eventKey="dashboard"
title="Dashboard"/>}
+ {!ephemeral && <Tab eventKey="trace"
title="Trace"/>}
+ {!ephemeral && <Tab eventKey="build"
title="Build"/>}
<Tab eventKey="container" title="Container"/>
</Tabs>}
</FlexItem>
diff --git
a/karavan-web/karavan-app/src/main/webui/src/project/builder/BuildPanel.tsx
b/karavan-web/karavan-app/src/main/webui/src/project/builder/BuildPanel.tsx
index f1dedba6..0fa657f6 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/builder/BuildPanel.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/builder/BuildPanel.tsx
@@ -54,7 +54,7 @@ export function BuildPanel () {
function deleteEntity() {
const buildName = getBuildName();
if (buildName) {
- KaravanApi.manageContainer(config.environment, 'build', buildName,
'delete', res => {
+ KaravanApi.manageContainer(project.projectId, 'build', buildName,
'delete', false,res => {
EventBus.sendAlert("Container deleted", "Container " +
buildName + " deleted", 'info')
setShowLog(false, 'container', undefined)
});
diff --git
a/karavan-web/karavan-app/src/main/webui/src/project/container/ContainerButtons.tsx
b/karavan-web/karavan-app/src/main/webui/src/project/container/ContainerButtons.tsx
index 788e1fdd..dfacb55f 100644
---
a/karavan-web/karavan-app/src/main/webui/src/project/container/ContainerButtons.tsx
+++
b/karavan-web/karavan-app/src/main/webui/src/project/container/ContainerButtons.tsx
@@ -16,15 +16,16 @@
*/
import React, {useState} from 'react';
-import {Button, Flex, FlexItem, Modal, Spinner, Tooltip, TooltipPosition} from
'@patternfly/react-core';
+import {Button, Flex, FlexItem, Modal, Spinner, Switch, Tooltip,
TooltipPosition} from '@patternfly/react-core';
import '../../designer/karavan.css';
import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/trash-icon";
+import StopIcon from "@patternfly/react-icons/dist/esm/icons/stop-icon";
+import DeployIcon from "@patternfly/react-icons/dist/esm/icons/upload-icon";
import {useAppConfigStore, useDevModeStore, useLogStore, useProjectStore,
useStatusesStore} from "../../api/ProjectStore";
import {shallow} from "zustand/shallow";
import RunIcon from "@patternfly/react-icons/dist/esm/icons/play-icon";
import {KaravanApi} from "../../api/KaravanApi";
-import StopIcon from "@patternfly/react-icons/dist/js/icons/stop-icon";
-
+import {EventBus} from "../../designer/utils/EventBus";
interface Props {
env: string,
@@ -38,33 +39,25 @@ export function ContainerButtons (props: Props) {
const [containers] = useStatusesStore((state) => [state.containers],
shallow);
const [setShowLog] = useLogStore((s) => [s.setShowLog], shallow);
+ const [pullImage, setPullImage] = useState<boolean>(false);
const [showConfirmation, setShowConfirmation] = useState<boolean>(false);
- const [actionType, setActionType] = useState<'run' | 'stop' |
'delete'>('run');
+ const [actionType, setActionType] = useState<'deploy' | 'run' | 'stop' |
'delete'>('run');
const containerStatus = containers.filter(c => c.containerName ===
project.projectId).at(0);
- const commands = containerStatus?.commands || ['run'];
+ const commands = containerStatus?.commands || ['deploy'];
+ const exists = containerStatus != undefined;
const isRunning = containerStatus?.state === 'running';
const inTransit = containerStatus?.inTransit;
const isLoading = status === 'wip';
function act() {
- switch (actionType) {
- case "run":
- KaravanApi.manageContainer(props.env, 'project',
project.projectId, 'run', res => {
- setShowLog(false, 'container', undefined)
- });
- break;
- case "stop":
- KaravanApi.manageContainer(props.env, 'project',
project.projectId, 'stop', res => {
- setShowLog(false, 'container', undefined)
- });
- break;
- case "delete":
- KaravanApi.manageContainer(props.env, 'project',
project.projectId, 'delete', res => {
- setShowLog(false, 'container', undefined)
- });
- break;
- }
+ KaravanApi.manageContainer(project.projectId, 'project',
project.projectId, actionType, pullImage,res => {
+ const response = res?.response;
+ if (response?.status === 500) {
+ EventBus.sendAlert('Error', response.data !== undefined &&
response.data.length > 0 ? response.data : response.statusText, 'warning')
+ }
+ setShowLog(false, 'container', undefined)
+ });
}
function getConfirmationModal() {
@@ -86,6 +79,14 @@ export function ContainerButtons (props: Props) {
]}
onEscapePress={e => setShowConfirmation(false)}>
<div>{"Confirm " + actionType + " container?"}</div>
+ {actionType === 'deploy' && <div>
+ <Switch
+ label="Pull image"
+ isChecked={pullImage}
+ onChange={(_, checked) => setPullImage(checked)}
+ isReversed
+ />
+ </div>}
</Modal>)
}
@@ -93,6 +94,18 @@ export function ContainerButtons (props: Props) {
<FlexItem>
{(inTransit || isLoading) && <Spinner size="lg"
aria-label="spinner"/>}
</FlexItem>
+ {!isRunning && config.infrastructure !== 'kubernetes' &&
commands.includes('deploy') && <FlexItem>
+ <Tooltip content="Deploy container"
position={TooltipPosition.bottom}>
+ <Button size="sm"
+ isDisabled={(!(commands.length === 0) &&
!commands.includes('deploy')) || inTransit}
+ variant={"primary"}
+ icon={<DeployIcon/>}
+ onClick={() => {
+ setActionType('deploy');
+ setShowConfirmation(true);
+ }}>Deploy</Button>
+ </Tooltip>
+ </FlexItem>}
{!isRunning && config.infrastructure !== 'kubernetes' && <FlexItem>
<Tooltip content="Run container" position={TooltipPosition.bottom}>
<Button size="sm"
diff --git
a/karavan-web/karavan-app/src/main/webui/src/project/container/ContainerPanel.tsx
b/karavan-web/karavan-app/src/main/webui/src/project/container/ContainerPanel.tsx
index 2e619023..39221675 100644
---
a/karavan-web/karavan-app/src/main/webui/src/project/container/ContainerPanel.tsx
+++
b/karavan-web/karavan-app/src/main/webui/src/project/container/ContainerPanel.tsx
@@ -19,18 +19,10 @@ import React, {useState} from 'react';
import {
Badge,
Button,
- Card,
- CardBody,
- DescriptionList,
- DescriptionListDescription,
- DescriptionListGroup,
- DescriptionListTerm,
Flex,
FlexItem,
Label,
LabelGroup, Modal,
- Tooltip,
- TooltipPosition
} from '@patternfly/react-core';
import '../../designer/karavan.css';
import UpIcon from "@patternfly/react-icons/dist/esm/icons/running-icon";
@@ -95,7 +87,9 @@ export function ContainerPanel (props: Props) {
}
const env = props.env;
- const conts = containers.filter(d => d.projectId === project?.projectId &&
d.type === 'project');
+ const conts = containers
+ .filter(c => c.env == env)
+ .filter(d => d.projectId === project?.projectId && d.type ===
'project');
return (
<Flex justifyContent={{default: "justifyContentSpaceBetween"}}
alignItems={{default: "alignItemsFlexStart"}}>
diff --git
a/karavan-web/karavan-app/src/main/webui/src/project/container/ProjectContainerTab.tsx
b/karavan-web/karavan-app/src/main/webui/src/project/container/ProjectContainerTab.tsx
index a08b320e..a4f525da 100644
---
a/karavan-web/karavan-app/src/main/webui/src/project/container/ProjectContainerTab.tsx
+++
b/karavan-web/karavan-app/src/main/webui/src/project/container/ProjectContainerTab.tsx
@@ -34,7 +34,7 @@ export function ProjectContainerTab() {
<PageSection className="project-tab-panel project-build-panel"
padding={{default: "padding"}}>
<div>
{config.environments.map(env =>
- <Card className="project-status">
+ <Card key={env} className="project-status">
<CardBody>
<DescriptionList isHorizontal
horizontalTermWidthModifier={{default: '20ch'}}>
<DescriptionListGroup>
@@ -47,14 +47,14 @@ export function ProjectContainerTab() {
<DescriptionListGroup>
<DescriptionListTerm>Deployment</DescriptionListTerm>
<DescriptionListDescription>
- <DeploymentPanel key={env}
env={env}/>
+ <DeploymentPanel env={env}/>
</DescriptionListDescription>
</DescriptionListGroup>
}
<DescriptionListGroup>
<DescriptionListTerm>Containers</DescriptionListTerm>
<DescriptionListDescription>
- <ContainerPanel key={env} env={env}/>
+ <ContainerPanel env={env}/>
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
diff --git
a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
index 60cc0f65..05a68026 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
@@ -124,8 +124,8 @@ export function ProjectsPage () {
<Th key='projectId'>Project ID</Th>
<Th key='name'>Name</Th>
<Th key='description'>Description</Th>
- <Th key='commit'>Commit</Th>
- <Th key='deployment'>Environment</Th>
+ <Th key='commit' modifier={"fitContent"}>Commit</Th>
+ <Th key='deployment'
modifier={"fitContent"}>Environment</Th>
<Th key='action'></Th>
</Tr>
</Thead>
diff --git
a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
index 1746eb7f..e8e8972b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
@@ -16,22 +16,13 @@
*/
import React from 'react';
-import {
- Button,
- Badge,
- Tooltip,
- Flex, FlexItem, Label
-} from '@patternfly/react-core';
+import {Badge, Button, Flex, FlexItem, Label, Tooltip} from
'@patternfly/react-core';
import '../designer/karavan.css';
-import { Td, Tr} from "@patternfly/react-table";
+import {Td, Tr} from "@patternfly/react-table";
import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
import CopyIcon from "@patternfly/react-icons/dist/esm/icons/copy-icon";
import {Project} from '../api/ProjectModels';
-import {
- useAppConfigStore,
- useLogStore,
- useProjectStore, useStatusesStore,
-} from "../api/ProjectStore";
+import {useAppConfigStore, useLogStore, useProjectStore, useStatusesStore,}
from "../api/ProjectStore";
import {shallow} from "zustand/shallow";
import {useNavigate} from "react-router-dom";
@@ -56,14 +47,14 @@ export function ProjectsTableRow (props: Props) {
const env: string = e as string;
const status = config.infrastructure === 'kubernetes'
? deployments.find(d => d.projectId === name && d.env === env)
- : containers.find(d => d.containerName === name && d.env ===
env);
- return [env, status];
+ : containers.find(d => d.projectId === name && d.env === env);
+ return [env, status != null];
});
}
const project = props.project;
const isBuildIn = ['kamelets', 'templates'].includes(project.projectId);
- const commit = project.lastCommit ? project.lastCommit?.substr(0, 7) :
"...";
+ const commit = project.lastCommit ? project.lastCommit?.substr(0, 7) :
undefined;
return (
<Tr key={project.projectId}>
<Td>
@@ -79,22 +70,20 @@ export function ProjectsTableRow (props: Props) {
<Td>{project.name}</Td>
<Td>{project.description}</Td>
<Td isActionCell>
- <Tooltip content={project.lastCommit} position={"bottom"}>
+ {commit && <Tooltip content={project.lastCommit}
position={"bottom"}>
<Badge className="badge">{commit}</Badge>
- </Tooltip>
+ </Tooltip>}
</Td>
- <Td noPadding style={{width: "180px"}}>
+ <Td noPadding>
{!isBuildIn &&
- <Flex direction={{default: "row"}}>
+ <div style={{display: "flex", gap:"2px"}}>
{getStatusByEnvironments(project.projectId).map(value
=> {
const active = value[1];
const color = active ? "green" : "grey"
const style = active ? {fontWeight: "bold"} : {}
- return <FlexItem className="badge-flex-item"
key={value[0]}>
- <Label style={style} color={color}
>{value[0]}</Label>
- </FlexItem>
+ return <Label style={style} color={color}
>{value[0]}</Label>
})}
- </Flex>
+ </div>
}
</Td>
<Td className="project-action-buttons">
diff --git
a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
index cb04b01f..0d52d7e0 100644
--- a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
@@ -59,7 +59,7 @@ export function ServicesTableRow (props: Props) {
<Tooltip content={"Start container"}
position={"bottom"}>
<Button variant={"plain"} icon={<PlayIcon/>}
isDisabled={!commands.includes('run') || inTransit}
onClick={e => {
-
KaravanApi.manageContainer(config.environment, 'devservice',
service.container_name, 'run', res => {});
+
KaravanApi.manageContainer(service.container_name, 'devservice',
service.container_name, 'run', false,res => {});
}}></Button>
</Tooltip>
</FlexItem>
@@ -75,7 +75,7 @@ export function ServicesTableRow (props: Props) {
<Tooltip content={"Stop container"}
position={"bottom"}>
<Button variant={"plain"} icon={<StopIcon/>}
isDisabled={!commands.includes('stop') || inTransit}
onClick={e => {
-
KaravanApi.manageContainer(config.environment, 'devservice',
service.container_name, 'stop', res => {});
+
KaravanApi.manageContainer(service.container_name, 'devservice',
service.container_name, 'stop', false,res => {});
}}></Button>
</Tooltip>
</FlexItem>
@@ -83,7 +83,7 @@ export function ServicesTableRow (props: Props) {
<Tooltip content={"Delete container"}
position={"bottom"}>
<Button variant={"plain"} icon={<DeleteIcon/>}
isDisabled={!commands.includes('delete') || inTransit}
onClick={e => {
-
KaravanApi.deleteContainer(config.environment, 'devservice',
service.container_name, res => {});
+
KaravanApi.deleteContainer(service.container_name, 'devservice',
service.container_name, res => {});
}}></Button>
</Tooltip>
</FlexItem>