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 d72e4dd2 Fix #1357 d72e4dd2 is described below commit d72e4dd2ba75fe4b88fbd5141f6a403604531daf Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Mon Aug 12 07:07:20 2024 -0400 Fix #1357 --- .../camel/karavan/api/ContainerResource.java | 10 ++-- .../karavan/docker/DockerComposeConverter.java | 25 ++++++++++ .../camel/karavan/docker/DockerForKaravan.java | 15 ++---- .../apache/camel/karavan/docker/DockerService.java | 35 ++++++++++--- .../camel/karavan/model/DockerComposeService.java | 17 ++----- .../camel/karavan/model/DockerComposeVolume.java | 58 ++++++++++++++++++++++ .../camel/karavan/service/ConfigService.java | 13 ++--- .../camel/karavan/service/ProjectService.java | 26 ++++++---- 8 files changed, 145 insertions(+), 54 deletions(-) diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java index d20c0e6e..adf945a4 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/ContainerResource.java @@ -140,11 +140,11 @@ public class ContainerResource { } } - private boolean needPull(JsonObject command) { - if (command != null && command.containsKey("pullImage")) { - return command.getBoolean("pullImage"); - } - return false; + private DockerService.PULL_IMAGE needPull(JsonObject command) { + try { + return DockerService.PULL_IMAGE.valueOf(command.getString("pullImage")); + } catch (Exception ignored) {} + return DockerService.PULL_IMAGE.never; } private void setContainerStatusTransit(String projectId, String name, String type) { diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerComposeConverter.java b/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerComposeConverter.java index 27323646..61534da3 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerComposeConverter.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerComposeConverter.java @@ -21,6 +21,7 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import org.apache.camel.karavan.model.DockerCompose; import org.apache.camel.karavan.model.DockerComposeService; +import org.apache.camel.karavan.model.DockerComposeVolume; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.introspector.Property; @@ -29,9 +30,13 @@ import org.yaml.snakeyaml.representer.Representer; import java.util.Map; +import static com.github.dockerjava.api.model.MountType.BIND; +import static com.github.dockerjava.api.model.MountType.VOLUME; + public class DockerComposeConverter { private static final String ENVIRONMENT = "environment"; + private static final String VOLUMES = "volumes"; public static DockerCompose fromCode(String code) { Yaml yaml = new Yaml(); @@ -75,6 +80,26 @@ public class DockerComposeConverter { }); service.put(ENVIRONMENT, env); } + if (service.containsKey(VOLUMES) && service.getValue(VOLUMES) instanceof JsonArray) { + JsonArray volumes = new JsonArray(); + JsonArray yamlVolumes = service.getJsonArray(VOLUMES); + yamlVolumes.forEach(o -> { + if (o instanceof JsonObject) { + volumes.add(o); + } else if (o instanceof String) { + var parts = ((String) o).split(":"); + if (parts.length == 2) { + var part0 = parts[0]; + var type = part0.startsWith("/") || part0.startsWith("~") || part0.startsWith("./") ? VOLUME : BIND; + volumes.add(JsonObject.mapFrom(new DockerComposeVolume(type.name().toLowerCase(), parts[0], parts[1]))); + } else if (parts.length == 1) { + volumes.add(JsonObject.mapFrom(new DockerComposeVolume(VOLUME.name().toLowerCase(), null, parts[0]))); + } + } + }); + service.put(VOLUMES, volumes); + } + DockerComposeService ds = service.mapTo(DockerComposeService.class); if (ds.getContainer_name() == null) { ds.setContainer_name(name); diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java b/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java index feb08009..8b59d527 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerForKaravan.java @@ -22,6 +22,7 @@ import com.github.dockerjava.api.model.RestartPolicy; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.karavan.model.DockerComposeService; +import org.apache.camel.karavan.model.DockerComposeVolume; import org.apache.camel.karavan.model.PodContainerStatus; import org.apache.camel.karavan.model.Project; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -30,7 +31,6 @@ import org.jboss.logging.Logger; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; import static org.apache.camel.karavan.KaravanConstants.*; import static org.apache.camel.karavan.service.CodeService.BUILD_SCRIPT_FILENAME; @@ -68,11 +68,6 @@ public class DockerForKaravan { var imageName = projectDevmodeImage != null ? projectDevmodeImage : devmodeImage; - if (dockerService.getImages().stream().noneMatch(i -> Objects.equals(i.getTag(), imageName))) { - LOGGER.info("Pulling DevMode image from DockerHub: " + imageName); - dockerService.pullImageFromDockerHub(imageName, true); - } - return dockerService.createContainer(projectId, (imageName), env, compose.getPortsMap(), healthCheck, @@ -80,14 +75,14 @@ public class DockerForKaravan { LABEL_PROJECT_ID, projectId, LABEL_CAMEL_RUNTIME, CamelRuntime.CAMEL_MAIN.getValue() ), - compose.getVolumesMap(), null, RestartPolicy.noRestart(), false, + compose.getVolumes(), null, RestartPolicy.noRestart(), DockerService.PULL_IMAGE.ifNotExists, compose.getCpus(), compose.getCpu_percent(), compose.getMem_limit(), compose.getMem_reservation()); } public void runBuildProject(Project project, String script, DockerComposeService compose, Map<String, String> sshFiles, String tag) throws Exception { String containerName = project.getProjectId() + BUILDER_SUFFIX; dockerService.deleteContainer(containerName); - Container c = createBuildContainer(containerName, project, compose.getEnvironmentList(), compose.getVolumesMap(), tag); + Container c = createBuildContainer(containerName, project, compose.getEnvironmentList(), compose.getVolumes(), tag); dockerService.copyExecFile(c.getId(), "/karavan/builder", BUILD_SCRIPT_FILENAME, script); sshFiles.forEach((name, text) -> { dockerService.copyExecFile(c.getId(), "/karavan/.ssh", name, text); @@ -95,7 +90,7 @@ public class DockerForKaravan { dockerService.runContainer(c); } - protected Container createBuildContainer(String containerName, Project project, List<String> env, Map<String, String> volumes, String tag) throws InterruptedException { + protected Container createBuildContainer(String containerName, Project project, List<String> env, List<DockerComposeVolume> volumes, String tag) throws InterruptedException { LOGGER.infof("Starting Build Container ", containerName); return dockerService.createContainer(containerName, devmodeImage, @@ -105,7 +100,7 @@ public class DockerForKaravan { LABEL_PROJECT_ID, project.getProjectId(), LABEL_TAG, tag ), - volumes, null,RestartPolicy.noRestart(), false, + volumes, null,RestartPolicy.noRestart(), DockerService.PULL_IMAGE.ifNotExists, null, null, null, null, "/karavan/builder/build.sh"); } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java b/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java index e5710187..ed89fd79 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/docker/DockerService.java @@ -34,6 +34,7 @@ import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; import org.apache.camel.karavan.model.ContainerImage; import org.apache.camel.karavan.model.DockerComposeService; +import org.apache.camel.karavan.model.DockerComposeVolume; import org.apache.camel.karavan.model.PodContainerStatus; import org.apache.camel.karavan.service.CodeService; import org.apache.camel.karavan.service.ConfigService; @@ -54,10 +55,15 @@ import java.util.*; import java.util.stream.Collectors; import static org.apache.camel.karavan.KaravanConstants.LABEL_PROJECT_ID; +import static org.apache.camel.karavan.KaravanConstants.LABEL_TYPE; @ApplicationScoped public class DockerService { + public enum PULL_IMAGE { + always, ifNotExists, never + } + private static final Logger LOGGER = Logger.getLogger(DockerService.class.getName()); @ConfigProperty(name = "karavan.docker.network") @@ -120,7 +126,7 @@ public class DockerService { public Container getContainer(String id) { try (ListContainersCmd cmd = getDockerClient().listContainersCmd().withShowAll(true).withIdFilter(List.of(id))) { - List<Container> containers = cmd.exec(); + List<Container> containers = cmd.exec(); return containers.isEmpty() ? null : containers.get(0); } } @@ -136,7 +142,7 @@ public class DockerService { } } - public Container createContainerFromCompose(DockerComposeService compose, Map<String, String> labels, Boolean pullAlways, String... command) throws InterruptedException { + public Container createContainerFromCompose(DockerComposeService compose, Map<String, String> labels, PULL_IMAGE pullImage, String... command) throws InterruptedException { List<Container> containers = findContainer(compose.getContainer_name()); if (containers.isEmpty()) { HealthCheck healthCheck = DockerUtils.getHealthCheck(compose.getHealthcheck()); @@ -153,7 +159,7 @@ public class DockerService { } return createContainer(compose.getContainer_name(), compose.getImage(), - env, compose.getPortsMap(), healthCheck, labels, compose.getVolumesMap(), networkName, restartPolicy, pullAlways, + env, compose.getPortsMap(), healthCheck, labels, compose.getVolumes(), networkName, restartPolicy, pullImage, compose.getCpus(), compose.getCpu_percent(), compose.getMem_limit(), compose.getMem_reservation(), command); } else { @@ -170,20 +176,33 @@ public class DockerService { 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 cpus, String cpu_percent, String mem_limit, String mem_reservation, + List<DockerComposeVolume> volumes, String network, RestartPolicy restartPolicy, + PULL_IMAGE pullImage, String cpus, String cpu_percent, String mem_limit, String mem_reservation, String... command) throws InterruptedException { List<Container> containers = findContainer(name); if (containers.isEmpty()) { - pullImage(image, pullAlways); + if (Objects.equals(labels.get(LABEL_TYPE), PodContainerStatus.ContainerType.devmode.name()) + || Objects.equals(labels.get(LABEL_TYPE), PodContainerStatus.ContainerType.build.name()) + || Objects.equals(labels.get(LABEL_TYPE), PodContainerStatus.ContainerType.devservice.name())) { + LOGGER.info("Pulling DevMode image from DockerHub: " + image); + pullImageFromDockerHub(image, Objects.equals(pullImage, PULL_IMAGE.always)); + } + if (Objects.equals(labels.get(LABEL_TYPE), PodContainerStatus.ContainerType.project.name())) { + LOGGER.info("Pulling Project image from Registry: " + image); + pullImage(image, Objects.equals(pullImage, PULL_IMAGE.always)); + } try (CreateContainerCmd createContainerCmd = getDockerClient().createContainerCmd(image).withName(name).withLabels(labels).withEnv(env).withHostName(name).withHealthcheck(healthCheck)) { Ports portBindings = DockerUtils.getPortBindings(ports); List<Mount> mounts = new ArrayList<>(); if (volumes != null && !volumes.isEmpty()) { - volumes.forEach((hostPath, containerPath) -> { - mounts.add(new Mount().withType(MountType.BIND).withSource(hostPath).withTarget(containerPath)); + volumes.forEach(volume -> { + var mount = new Mount().withType(MountType.valueOf(volume.getType())).withTarget(volume.getTarget()); + if (volume.getSource() != null) { + mount = mount.withSource(volume.getSource()); + } + mounts.add(mount); }); } if (command.length > 0) { diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/DockerComposeService.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/DockerComposeService.java index 75e3db3a..1e295399 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/model/DockerComposeService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/DockerComposeService.java @@ -33,7 +33,7 @@ public class DockerComposeService { private String mem_limit; private String mem_reservation; private List<String> ports; - private List<String> volumes; + private List<DockerComposeVolume> volumes; private List<String> expose; private List<String> depends_on; private List<String> networks; @@ -86,17 +86,6 @@ public class DockerComposeService { return p; } - public Map<String, String> getVolumesMap() { - Map<String, String> p = new HashMap<>(); - if (volumes != null && !volumes.isEmpty()) { - volumes.forEach(s -> { - String[] values = s.split(":"); - p.put(values[0], values[1]); - }); - } - return p; - } - public List<String> getExpose() { return expose; } @@ -181,11 +170,11 @@ public class DockerComposeService { this.mem_reservation = mem_reservation; } - public List<String> getVolumes() { + public List<DockerComposeVolume> getVolumes() { return volumes; } - public void setVolumes(List<String> volumes) { + public void setVolumes(List<DockerComposeVolume> volumes) { this.volumes = volumes; } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/DockerComposeVolume.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/DockerComposeVolume.java new file mode 100644 index 00000000..4f9920e3 --- /dev/null +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/DockerComposeVolume.java @@ -0,0 +1,58 @@ +/* + * 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.model; + +public class DockerComposeVolume { + + private String type; + private String source; + private String target; + + public DockerComposeVolume() { + } + + public DockerComposeVolume(String type, String source, String target) { + this.type = type; + this.source = source; + this.target = target; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } +} diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/ConfigService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/ConfigService.java index a4cb1e4d..87212c68 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/ConfigService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/ConfigService.java @@ -137,18 +137,19 @@ public class ConfigService { var parts = filename.split("\\."); var prefix = parts[0]; if (environment.equals(DEV_ENVIRONMENT) && !getEnvs().contains(prefix)) { // no prefix AND dev env - storeFile(f); - } else if (Objects.equals(prefix, environment)){ // with prefix == env - storeFile(f); + storeFile(f.getName(), f.getCode()); + } else if (Objects.equals(prefix, environment)) { // with prefix == env + filename = f.getName().substring(environment.length() + 1); + storeFile(filename, f.getCode()); } } - private void storeFile(ProjectFile f) throws Exception { + private void storeFile(String filename , String code) throws Exception { if (inKubernetes()) { - createConfigMapFromFile(f.getName(), f.getCode()); + createConfigMapFromFile(filename, code); } else { if (sharedFolder.isPresent()) { - Files.writeString(Paths.get(sharedFolder.get(), f.getName()), f.getCode()); + Files.writeString(Paths.get(sharedFolder.get(), filename), code); } else { throw new Exception("Shared folder not configured"); } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java index 2890eabd..e2954b68 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java @@ -69,17 +69,21 @@ public class ProjectService { EventBus eventBus; public Project commitAndPushProject(String projectId, String message) throws Exception { - LOGGER.info("Commit project: " + projectId); - Project p = karavanCache.getProject(projectId); - List<ProjectFile> files = karavanCache.getProjectFiles(projectId); - RevCommit commit = gitService.commitAndPushProject(p, files, message); - karavanCache.syncFilesCommited(projectId); - String commitId = commit.getId().getName(); - Long lastUpdate = commit.getCommitTime() * 1000L; - p.setLastCommit(commitId); - p.setLastCommitTimestamp(lastUpdate); - karavanCache.saveProject(p); - return p; + if (Objects.equals(environment, DEV_ENVIRONMENT)) { + LOGGER.info("Commit project: " + projectId); + Project p = karavanCache.getProject(projectId); + List<ProjectFile> files = karavanCache.getProjectFiles(projectId); + RevCommit commit = gitService.commitAndPushProject(p, files, message); + karavanCache.syncFilesCommited(projectId); + String commitId = commit.getId().getName(); + Long lastUpdate = commit.getCommitTime() * 1000L; + p.setLastCommit(commitId); + p.setLastCommitTimestamp(lastUpdate); + karavanCache.saveProject(p); + return p; + } else { + throw new RuntimeException("Unsupported environment: " + environment); + } } public String runProjectWithJBangOptions(Project project, String jBangOptions) throws Exception {