From eeca9aaf12d40511ae47ef02824f198e4cfd746a Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 15 Apr 2024 15:25:34 +0300 Subject: [PATCH 1/2] add loading options --- .../org/testcontainers/k3s/K3sContainer.java | 81 +++++++++++++++++++ .../testcontainers/k3s/K3sLoadImagesTest.java | 58 +++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 modules/k3s/src/test/java/org/testcontainers/k3s/K3sLoadImagesTest.java diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index b014c5b4ed6..f7c14fcbdc0 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -5,17 +5,33 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.SaveImageCmd; +import com.github.dockerjava.api.command.SaveImagesCmd; import lombok.SneakyThrows; import org.apache.commons.io.IOUtils; import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.CopyOption; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.time.Duration; import java.util.HashMap; import java.util.Map; +import java.util.Set; + +import static org.awaitility.Awaitility.await; /** * Testcontainers implementation for K3S @@ -50,6 +66,54 @@ public K3sContainer(DockerImageName dockerImageName) { setWaitStrategy(new LogMessageWaitStrategy().withRegEx(".*Node controller sync successful.*")); } + /** + * import images into the running k3s instance. Images are assumed to be present + * on the host already. + * + * @param images images to be imported into the k3s. + * @throws IOException in case temporary directory/files needed for the import can not be created + * @throws InterruptedException thrown when the thread is interrupted + */ + public void loadImages(Set images) throws IOException, InterruptedException { + + if (images != null && !images.isEmpty()) { + + // create temporary directory on the host where all tar(s) will reside + File tarsTempFolder = Files.createTempDirectory("").toFile(); + File tarFile = File.createTempFile("images", ".tar", tarsTempFolder); + + for (String image : images) { + try (SaveImageCmd saveImageCmd = getDockerClient().saveImageCmd(image)) { + InputStream imageStream = saveImageCmd.exec(); + // tar file for a single image + Files.copy(imageStream, tarFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + importTar(tarFile, true); + } + } + + // at this point, directory is empty + Files.deleteIfExists(tarsTempFolder.toPath()); + } + } + + /** + * Import this tar file into the running k3s. + * + * @param paths absolute paths of the tar files on the host. + * @throws IOException in case temporary directory/files needed for the import can not be created + * @throws InterruptedException thrown when the thread is interrupted + */ + public void loadImageFromTar(Set paths) throws IOException, InterruptedException { + if (paths != null && !paths.isEmpty()) { + for (String path : paths) { + File tarFile = new File(path); + importTar(tarFile, false); + } + } + + + } + @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { String rawKubeConfig = copyFileFromContainer( @@ -103,4 +167,21 @@ private String kubeConfigWithServerUrl(String kubeConfigYaml, String serverUrl) return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(kubeConfigObjectNode); } + + private void importTar(File tarFile, boolean deleteAfterCopy) throws IOException, InterruptedException { + try (CopyArchiveToContainerCmd copyCmd = getDockerClient().copyArchiveToContainerCmd(getContainerId())) { + String remotePathName = "/tmp/" + tarFile.getName(); + copyCmd.withHostResource(tarFile.getAbsolutePath()).withRemotePath("/tmp").exec(); + Container.ExecResult result = execInContainer("ctr", "i", "import", remotePathName); + boolean noErrors = result.getStderr() == null || result.getStderr().isEmpty(); + if (!noErrors) { + throw new RuntimeException(result.getStderr()); + } + } + finally { + if (deleteAfterCopy) { + Files.deleteIfExists(tarFile.toPath()); + } + } + } } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/K3sLoadImagesTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/K3sLoadImagesTest.java new file mode 100644 index 00000000000..aed17a952b5 --- /dev/null +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/K3sLoadImagesTest.java @@ -0,0 +1,58 @@ +package org.testcontainers.k3s; + +import com.github.dockerjava.api.command.PullImageCmd; +import com.github.dockerjava.api.command.SaveImageCmd; +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.Container; +import org.testcontainers.utility.DockerImageName; + +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +public class K3sLoadImagesTest { + + @ClassRule + public static K3sContainer k3s = new K3sContainer(DockerImageName.parse("rancher/k3s:v1.21.3-k3s1")); + + @Test + public void testLoadImages() throws Exception { + try (PullImageCmd pullImageCmd = k3s.getDockerClient().pullImageCmd("busybox:1.35")) { + pullImageCmd.start().awaitCompletion(); + + k3s.loadImages(Collections.singleton("busybox:1.35")); + Container.ExecResult result = k3s.execInContainer("crictl", "images"); + assertThat(result.getStdout()).contains("busybox"); + } + } + + @Test + public void testLoadImageFromTar() throws Exception { + + try (PullImageCmd pullImageCmd = k3s.getDockerClient().pullImageCmd("busybox:1.35")) { + pullImageCmd.start().awaitCompletion(); + + File tarsTempFolder = Files.createTempDirectory("").toFile(); + File tarFile = File.createTempFile("images", ".tar", tarsTempFolder); + + try (SaveImageCmd saveImageCmd = k3s.getDockerClient().saveImageCmd("busybox:1.35")) { + InputStream imageStream = saveImageCmd.exec(); + Files.copy(imageStream, tarFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + + k3s.loadImageFromTar(Collections.singleton(tarFile.getAbsolutePath())); + + Container.ExecResult result = k3s.execInContainer("crictl", "images"); + assertThat(result.getStdout()).contains("busybox"); + } + finally { + Files.delete(tarFile.toPath()); + Files.delete(tarsTempFolder.toPath()); + } + } + } +} From 571a53246bf9a8a294e9eb303fc07b40822c17be Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 15 Apr 2024 15:43:39 +0300 Subject: [PATCH 2/2] with spotless --- .../java/org/testcontainers/k3s/K3sContainer.java | 14 +------------- .../org/testcontainers/k3s/K3sLoadImagesTest.java | 4 +--- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index f7c14fcbdc0..4aea6617140 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -8,7 +8,6 @@ import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.command.SaveImageCmd; -import com.github.dockerjava.api.command.SaveImagesCmd; import lombok.SneakyThrows; import org.apache.commons.io.IOUtils; import org.testcontainers.containers.BindMode; @@ -21,18 +20,12 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.nio.file.CopyOption; import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.Set; -import static org.awaitility.Awaitility.await; - /** * Testcontainers implementation for K3S *

@@ -75,9 +68,7 @@ public K3sContainer(DockerImageName dockerImageName) { * @throws InterruptedException thrown when the thread is interrupted */ public void loadImages(Set images) throws IOException, InterruptedException { - if (images != null && !images.isEmpty()) { - // create temporary directory on the host where all tar(s) will reside File tarsTempFolder = Files.createTempDirectory("").toFile(); File tarFile = File.createTempFile("images", ".tar", tarsTempFolder); @@ -110,8 +101,6 @@ public void loadImageFromTar(Set paths) throws IOException, InterruptedE importTar(tarFile, false); } } - - } @Override @@ -177,8 +166,7 @@ private void importTar(File tarFile, boolean deleteAfterCopy) throws IOException if (!noErrors) { throw new RuntimeException(result.getStderr()); } - } - finally { + } finally { if (deleteAfterCopy) { Files.deleteIfExists(tarFile.toPath()); } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/K3sLoadImagesTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/K3sLoadImagesTest.java index aed17a952b5..ea68bd739c0 100644 --- a/modules/k3s/src/test/java/org/testcontainers/k3s/K3sLoadImagesTest.java +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/K3sLoadImagesTest.java @@ -33,7 +33,6 @@ public void testLoadImages() throws Exception { @Test public void testLoadImageFromTar() throws Exception { - try (PullImageCmd pullImageCmd = k3s.getDockerClient().pullImageCmd("busybox:1.35")) { pullImageCmd.start().awaitCompletion(); @@ -48,8 +47,7 @@ public void testLoadImageFromTar() throws Exception { Container.ExecResult result = k3s.execInContainer("crictl", "images"); assertThat(result.getStdout()).contains("busybox"); - } - finally { + } finally { Files.delete(tarFile.toPath()); Files.delete(tarsTempFolder.toPath()); }