From 2b235ea22ec86466ddc28fa5e615df4f42ab520c Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Thu, 3 Dec 2020 07:13:44 +0300 Subject: [PATCH] Tests for copy goal when using temporary container. Signed-off-by: Marat Abrarov --- pom.xml | 1 + .../io/fabric8/maven/docker/CopyMojo.java | 5 +- .../io/fabric8/maven/docker/BaseMojoTest.java | 3 + .../io/fabric8/maven/docker/CopyMojoTest.java | 286 +++++++++++++++++- 4 files changed, 277 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 8e4d66cd5..78c59a157 100644 --- a/pom.xml +++ b/pom.xml @@ -399,6 +399,7 @@ -javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar + ${project.build.testOutputDirectory} diff --git a/src/main/java/io/fabric8/maven/docker/CopyMojo.java b/src/main/java/io/fabric8/maven/docker/CopyMojo.java index 6e50dc9bd..a3ed2225e 100644 --- a/src/main/java/io/fabric8/maven/docker/CopyMojo.java +++ b/src/main/java/io/fabric8/maven/docker/CopyMojo.java @@ -188,11 +188,12 @@ private void copy(DockerAccess dockerAccess, ArchiveService archiveService, Stri for (CopyConfiguration.Entry copyEntry : copyEntries) { String containerPath = copyEntry.getContainerPath(); if (containerPath == null) { - log.error("containerPath of copy goal entry for %s image is not specified", containerId, imageName); + log.error("containerPath of copy goal entry for %s image is not specified", imageName); throw new IllegalArgumentException("containerPath should be specified"); } File hostDirectory = getHostDirectory(copyEntry.getHostDirectory()); - log.info("Copying %s from %s container into %s host directory", containerPath, containerId, hostDirectory); + log.info("Copying %s from %s container into %s host directory", containerPath, containerId, + hostDirectory.getAbsolutePath()); Files.createDirectories(hostDirectory.toPath()); try (FileRemover fileRemover = new FileRemover(log)) { File archiveFile = Files.createTempFile(TEMP_ARCHIVE_FILE_PREFIX, TEMP_ARCHIVE_FILE_SUFFIX).toFile(); diff --git a/src/test/java/io/fabric8/maven/docker/BaseMojoTest.java b/src/test/java/io/fabric8/maven/docker/BaseMojoTest.java index e11872719..a03320b4e 100644 --- a/src/test/java/io/fabric8/maven/docker/BaseMojoTest.java +++ b/src/test/java/io/fabric8/maven/docker/BaseMojoTest.java @@ -67,6 +67,7 @@ public class BaseMojoTest { protected String projectGroupId; protected String projectArtifactId; protected String projectVersion; + protected String projectBaseDirectory; protected String projectBuildDirectory; protected GavLabel projectGavLabel; @@ -158,6 +159,7 @@ protected void givenMavenProject(AbstractDockerMojo mojo) { projectGroupId = "mock.group"; projectArtifactId = "mock-artifact"; projectVersion = "1.0.0-MOCK"; + projectBaseDirectory = "mock-base"; projectBuildDirectory = "mock-target"; projectGavLabel = new GavLabel(projectGroupId, projectArtifactId, projectVersion); @@ -167,6 +169,7 @@ protected void givenMavenProject(AbstractDockerMojo mojo) { mavenProject.getGroupId(); result = projectGroupId; minTimes = 0; mavenProject.getArtifactId(); result = projectArtifactId; minTimes = 0; mavenProject.getVersion(); result = projectVersion; minTimes = 0; + mavenProject.getBasedir(); result = projectBaseDirectory; minTimes = 0; mavenBuild.getDirectory(); result = projectBuildDirectory; minTimes = 0; }}; diff --git a/src/test/java/io/fabric8/maven/docker/CopyMojoTest.java b/src/test/java/io/fabric8/maven/docker/CopyMojoTest.java index 8e7d42833..decb4d4e7 100644 --- a/src/test/java/io/fabric8/maven/docker/CopyMojoTest.java +++ b/src/test/java/io/fabric8/maven/docker/CopyMojoTest.java @@ -7,26 +7,32 @@ import java.util.List; import java.util.Properties; -import org.apache.maven.plugin.MojoExecutionException; -import org.junit.Test; - import mockit.Deencapsulation; import mockit.Expectations; import mockit.Tested; import mockit.Verifications; +import org.apache.maven.plugin.MojoExecutionException; +import org.junit.Test; import io.fabric8.maven.docker.access.DockerAccessException; import io.fabric8.maven.docker.access.PortMapping; +import io.fabric8.maven.docker.config.CopyConfiguration.Entry; import io.fabric8.maven.docker.config.ImageConfiguration; import io.fabric8.maven.docker.model.Container; import io.fabric8.maven.docker.service.RunService.ContainerDescriptor; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.util.GavLabel; import static io.fabric8.maven.docker.AbstractDockerMojo.CONTEXT_KEY_START_CALLED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; public class CopyMojoTest extends BaseMojoTest { - @Tested(fullyInitialized = false) + @Tested private CopyMojo copyMojo; @Test @@ -79,7 +85,7 @@ public void copyWithStartGoalInvokedButNoContainersTracked() throws IOException, @Test public void copyWithStartGoalInvokedButNoCopyConfiguration() throws IOException, MojoExecutionException { givenProjectWithStartGoalInvoked(); - givenContainerTracked(singleContainerDescriptor(singleImageWithBuild())); + givenTrackedContainer(singleContainerDescriptor(singleImageWithBuild(), "some-container")); whenMojoExecutes(); @@ -90,7 +96,7 @@ public void copyWithStartGoalInvokedButNoCopyConfiguration() throws IOException, @Test public void copyWithStartGoalInvokedButNoCopyEntries() throws IOException, MojoExecutionException { givenProjectWithStartGoalInvoked(); - givenContainerTracked(singleContainerDescriptor(singleImageWithCopy(Collections.emptyList()))); + givenTrackedContainer(singleContainerDescriptor(singleImageWithCopy(Collections.emptyList()), "example")); whenMojoExecutes(); @@ -128,21 +134,182 @@ public void copyButNoCopyEntries() throws IOException, MojoExecutionException { thenCopyArchiveFromContainerIsNotCalled(); } + @Test + public void copyWithCreateContainersAndAbsoluteHostDirectory() throws IOException, MojoExecutionException { + final String containerPath = "/container/path/to/some/directory"; + final File hostDirectory = new File(projectBuildDirectory, "absolute-host-directory"); + final ImageConfiguration image = singleImageWithCopy( + singleCopyEntry(containerPath, hostDirectory.getAbsolutePath())); + final String containerNamePattern = "%i"; + final String temporaryContainerId = "some-test-container"; + + givenProjectWithResolvedImage(image); + givenCreateContainersIsTrue(); + givenContainerNamePattern(containerNamePattern); + givenCreatedContainerId(temporaryContainerId); - private void givenProjectWithStartGoalInvoked() { - givenMavenProject(copyMojo); - givenPluginContext(copyMojo, CONTEXT_KEY_START_CALLED, true); + whenMojoExecutes(); + + thenContainerIsCreated(image, containerNamePattern); + thenContainerPathIsCopied(temporaryContainerId, containerPath, hostDirectory); + thenContainerIsRemoved(temporaryContainerId); + } + + @Test + public void copyWithCreateContainersAndRelativeHostDirectory() throws IOException, MojoExecutionException { + final String containerPath = "/some/path/to/test/file.txt"; + final String hostDirectory = "project-base-dir-relative-host-directory"; + final ImageConfiguration image = singleImageWithCopy(singleCopyEntry(containerPath, hostDirectory)); + final String containerNamePattern = "temporary-container-name-pattern"; + final String temporaryContainerId = "one-more-test-container"; + + givenProjectWithResolvedImage(image); + givenCreateContainersIsTrue(); + givenContainerNamePattern(containerNamePattern); + givenCreatedContainerId(temporaryContainerId); + + whenMojoExecutes(); + + thenContainerIsCreated(image, containerNamePattern); + thenContainerPathIsCopied(temporaryContainerId, containerPath, new File(projectBaseDirectory, hostDirectory)); + thenContainerIsRemoved(temporaryContainerId); + } + + @Test + public void copyWithCreateContainersAndUndefinedHostDirectory() throws IOException, MojoExecutionException { + final String containerPath = "/absolute/path/to/some/container/filesystem/resource"; + final ImageConfiguration image = singleImageWithCopy(singleCopyEntry(containerPath, null)); + final String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN; + final String temporaryContainerId = "another-test-container"; + + givenProjectWithResolvedImage(image); + givenCreateContainersIsTrue(); + givenContainerNamePattern(containerNamePattern); + givenCreatedContainerId(temporaryContainerId); + + whenMojoExecutes(); + + thenContainerIsCreated(image, containerNamePattern); + thenContainerPathIsCopied(temporaryContainerId, containerPath, new File(projectBaseDirectory)); + thenContainerIsRemoved(temporaryContainerId); + } + + @Test + public void copyWithCreateContainersButExceptionWhenCopying() throws IOException { + final String containerPath = "/any/container/resource"; + final ImageConfiguration image = singleImageWithCopy(singleCopyEntry(containerPath, null)); + final String containerNamePattern = "constant-container-name"; + final String temporaryContainerId = "some-container-id"; + final Exception copyException = new RuntimeException("Test exception when copying from container"); + + givenProjectWithResolvedImage(image); + givenCreateContainersIsTrue(); + givenContainerNamePattern(containerNamePattern); + givenCreatedContainerId(temporaryContainerId); + givenExceptionWhenCopyingArchiveFromContainer(temporaryContainerId, copyException); + + try { + whenMojoExecutes(); + fail(); + } catch (MojoExecutionException e) { + assertEquals(copyException, e.getCause()); + } catch (Exception e) { + assertEquals(copyException, e); + } + + thenContainerIsCreated(image, containerNamePattern); + thenContainerIsRemoved(temporaryContainerId); + } + + @Test + public void copyWithCreateContainersButExceptionWhenExtractingArchive() throws IOException, MojoExecutionException { + final String containerPath = "/another/container/resource"; + final ImageConfiguration image = singleImageWithCopy(singleCopyEntry(containerPath, null)); + final String containerNamePattern = "container-name-pattern"; + final String temporaryContainerId = "created-container-id"; + final Exception extractException = new RuntimeException("Test exception when extracting copied archive"); + + givenProjectWithResolvedImage(image); + givenCreateContainersIsTrue(); + givenContainerNamePattern(containerNamePattern); + givenCreatedContainerId(temporaryContainerId); + givenExceptionWhenExtractingArchive(extractException); + + try { + whenMojoExecutes(); + fail(); + } catch (MojoExecutionException e) { + assertEquals(extractException, e.getCause()); + } catch (Exception e) { + assertEquals(extractException, e); + } + + thenContainerIsCreated(image, containerNamePattern); + thenCopiedArchiveIsRemoved(temporaryContainerId, containerPath); + thenContainerIsRemoved(temporaryContainerId); + } + + @Test + public void copyWithCreateContainersButUndefinedContainerPath() throws IOException { + final ImageConfiguration image = singleImageWithCopy(singleCopyEntry(null, null)); + final String containerNamePattern = "%i"; + final String temporaryContainerId = "container-id"; + + givenProjectWithResolvedImage(image); + givenCreateContainersIsTrue(); + givenContainerNamePattern(containerNamePattern); + givenCreatedContainerId(temporaryContainerId); + + try { + whenMojoExecutes(); + fail(); + } catch (Exception ignored) { + // Nothing to check + } + + thenContainerIsCreated(image, containerNamePattern); + thenCopyArchiveFromContainerIsNotCalled(); + thenContainerIsRemoved(temporaryContainerId); + } + + @Test + public void copyWithStartGoalInvoked() throws IOException, MojoExecutionException { + final String containerPath = "/container/test/path"; + final ImageConfiguration image = singleImageWithCopy(singleCopyEntry(containerPath, null)); + final String trackedContainerId = "tracked-container-id"; + + givenProjectWithStartGoalInvoked(image); + givenTrackedContainer(singleContainerDescriptor(image, trackedContainerId)); + + whenMojoExecutes(); + + thenNoContainerIsCreated(); + thenContainerPathIsCopied(trackedContainerId, containerPath, new File(projectBaseDirectory)); } private void givenMavenProject() { givenMavenProject(copyMojo); } + private void givenProjectWithStartGoalInvoked() { + givenMavenProject(); + givenPluginContext(copyMojo, CONTEXT_KEY_START_CALLED, true); + } + private void givenProjectWithResolvedImage(ImageConfiguration image) { - givenMavenProject(copyMojo); + givenMavenProject(); + givenResolvedImages(copyMojo, Collections.singletonList(image)); + } + + private void givenProjectWithStartGoalInvoked(ImageConfiguration image) { + givenProjectWithStartGoalInvoked(); givenResolvedImages(copyMojo, Collections.singletonList(image)); } + private List singleCopyEntry(String containerPath, String hostDirectory) { + return Collections.singletonList(new Entry(containerPath, hostDirectory)); + } + private void givenCreateContainersIsTrue() { Deencapsulation.setField(copyMojo, "createContainers", true); } @@ -155,7 +322,7 @@ private void givenNoContainersTracked() { }}; } - private void givenContainerTracked(ContainerDescriptor container) { + private void givenTrackedContainer(ContainerDescriptor container) { new Expectations() {{ runService.getContainers((GavLabel) any); result = Collections.singletonList(container); @@ -163,8 +330,36 @@ private void givenContainerTracked(ContainerDescriptor container) { }}; } - private ContainerDescriptor singleContainerDescriptor(ImageConfiguration imageConfiguration) { - return new ContainerDescriptor("example", imageConfiguration); + private void givenContainerNamePattern(String containerNamePattern) { + Deencapsulation.setField(copyMojo, "containerNamePattern", containerNamePattern); + } + + private void givenCreatedContainerId(String containerId) throws DockerAccessException { + new Expectations() {{ + runService.createContainer((ImageConfiguration) any, (PortMapping) any, projectGavLabel, (Properties) any, + (File) any, anyString, (Date) any); + result = containerId; + }}; + } + + private void givenExceptionWhenCopyingArchiveFromContainer(String containerId, Exception exception) + throws DockerAccessException { + new Expectations() {{ + dockerAccess.copyArchiveFromContainer(withEqual(containerId), anyString, (File) any); + result = exception; + }}; + } + + private void givenExceptionWhenExtractingArchive(Exception exception) throws MojoExecutionException { + new Expectations() {{ + archiveService.extractDockerCopyArchive((File) any, (File) any); + result = exception; + }}; + } + + private ContainerDescriptor singleContainerDescriptor(ImageConfiguration imageConfiguration, + final String containerId) { + return new ContainerDescriptor(containerId, imageConfiguration); } private void whenMojoExecutes() throws IOException, MojoExecutionException { @@ -185,9 +380,9 @@ private void thenListContainersIsNotCalled() throws DockerAccessException { }}; } + @SuppressWarnings("unchecked") private void thenGetLatestContainerIsNotCalled() { new Verifications() {{ - //noinspection unchecked queryService.getLatestContainer((List) any); times = 0; }}; @@ -209,8 +404,7 @@ private void thenNoLatestContainerLookupByImageOccurs() throws DockerAccessExcep private void thenNoContainerIsCreated() throws DockerAccessException { new Verifications() {{ - //noinspection ConstantConditions - runService.createContainer((ImageConfiguration) any, (PortMapping) any, (GavLabel) any, (Properties) any, + runService.createContainer((ImageConfiguration) any, (PortMapping) any, projectGavLabel, (Properties) any, (File) any, anyString, (Date) any); times = 0; }}; @@ -222,4 +416,64 @@ private void thenCopyArchiveFromContainerIsNotCalled() throws DockerAccessExcept times = 0; }}; } + + private void thenContainerIsCreated(ImageConfiguration image, String namePattern) throws DockerAccessException { + new Verifications() {{ + final ImageConfiguration containerImage; + final String defaultContainerNamePattern; + runService.createContainer(containerImage = withCapture(), (PortMapping) any, projectGavLabel, + (Properties) any, (File) any, defaultContainerNamePattern = withCapture(), (Date) any); + times = 1; + assertEquals(image.getName(), containerImage.getName()); + assertEquals(namePattern, defaultContainerNamePattern); + }}; + } + + private void thenContainerPathIsCopied(String containerId, String containerPath, File targetDirectory) + throws DockerAccessException, MojoExecutionException { + new Verifications() {{ + final File copied; + dockerAccess + .copyArchiveFromContainer(withEqual(containerId), withEqual(containerPath), copied = withCapture()); + times = 1; + assertNotNull(copied); + + final File archive; + final File destination; + archiveService.extractDockerCopyArchive(archive = withCapture(), destination = withCapture()); + times = 1; + assertAbsolutePathEquals(copied, archive); + assertAbsolutePathEquals(targetDirectory, destination); + assertFalse(copied.exists()); + }}; + } + + private void thenContainerIsRemoved(String containerId) throws DockerAccessException { + new Verifications() {{ + final String removedContainerId; + runService.removeContainer(removedContainerId = withCapture(), anyBoolean); + times = 1; + assertEquals(containerId, removedContainerId); + }}; + } + + private void thenCopiedArchiveIsRemoved(String containerId, String containerPath) throws DockerAccessException { + new Verifications() {{ + final File copied; + dockerAccess + .copyArchiveFromContainer(withEqual(containerId), withEqual(containerPath), copied = withCapture()); + times = 1; + assertNotNull(copied); + assertFalse(copied.exists()); + }}; + } + + private void assertAbsolutePathEquals(final File expected, final File actual) { + if (expected == null) { + assertNull(actual); + } else { + assertNotNull(actual); + assertEquals(expected.getAbsolutePath(), actual.getAbsolutePath()); + } + } }