package com.jamierf.dropwizard.debpkg.packaging; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.io.Files; import com.google.common.io.Resources; import com.jamierf.dropwizard.debpkg.config.PgpConfiguration; import com.jamierf.dropwizard.debpkg.resource.Resource; import com.jamierf.dropwizard.debpkg.resource.StringResource; import com.jamierf.dropwizard.debpkg.template.MissingParameterException; import com.jamierf.dropwizard.debpkg.util.ArchiveUtils; import com.jamierf.dropwizard.debpkg.util.SystemConsole; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.tools.tar.TarEntry; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.*; import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRingCollection; import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.vafer.jdeb.Console; import org.vafer.jdeb.PackagingException; import org.vafer.jdeb.shaded.bc.openpgp.PGPException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; import java.security.NoSuchProviderException; import java.security.Security; import java.security.SignatureException; import java.util.Collection; import java.util.zip.GZIPInputStream; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class PackageBuilderTest { static { Security.addProvider(new BouncyCastleProvider()); } private static final Console LOG = new SystemConsole(); private static final String NAME = "test"; private static final String DESCRIPTION = "functional test"; private static final String USER = "timmy"; private static final String PGP_ALIAS = "FFC5302F"; private static final String PGP_PASSWORD = "test"; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); private File inputDir; private ResourceExtractor extractor; private PackageBuilder builder; @Before public void setUp() throws IOException { inputDir = temporaryFolder.newFolder("package"); assertTrue(new File(inputDir, "control").mkdir()); extractor = new ResourceExtractor(ImmutableMap.<String, Object>builder() .put("deb", ImmutableMap.of("name", NAME, "version", "1.0-SNAPSHOT", "maintainer", "Jamie Furness")) .put("project", ImmutableMap.of("description", DESCRIPTION, "artifactId", "com.jamierf.dropwizard")) .put("jvm", ImmutableMap.of("packageName", "openjdk-7-jdk", "packageVersion", "")) .put("unix", ImmutableMap.of("user", USER)) .put("path", ImmutableMap.of("logDirectory", "/tmp/logs", "configFile", "/tmp/config.yml", "jvmConfigFile", "/tmp/jvm.conf")) .build(), LOG); final File keyring = temporaryFolder.newFile(); Resources.asByteSource(PackageBuilderTest.class.getResource("private.asc")).copyTo(Files.asByteSink(keyring)); builder = new PackageBuilder(NAME, DESCRIPTION, URI.create("http://www.example.org"), LOG, Optional.of( new PgpConfiguration().setAlias(PGP_ALIAS).setKeyring(keyring).setPassphrase(PGP_PASSWORD) )); } private File createPackage(Collection<Resource> resources) throws IOException, PackagingException { extractor.extractResources(resources, inputDir); final File debFile = temporaryFolder.newFile("package.deb"); builder.createPackage(resources, inputDir, debFile); return debFile; } @Test public void testCreatesPackageWithoutPgp() throws IOException, PackagingException { builder = new PackageBuilder(NAME, DESCRIPTION, URI.create("http://www.example.org"), LOG, Optional.<PgpConfiguration>absent()); testCreatesPackage(); } @Test(expected = MissingParameterException.class) public void testFailsOnMissingTemplateVariables() throws IOException, PackagingException { createPackage(ImmutableList.<Resource>of( new StringResource("hello {{{missing.variable}}}", true, "/tmp/test.txt", USER, USER, TarEntry.DEFAULT_FILE_MODE) )); } @Test public void testCreatesPackage() throws IOException, PackagingException { final File debFile = createPackage(ImmutableList.<Resource>of( new StringResource("hello world", true, "/tmp/test.txt", USER, USER, TarEntry.DEFAULT_FILE_MODE) )); assertTrue(debFile.exists()); } @Test public void testPackageSignature() throws IOException, PackagingException, PGPException, SignatureException, org.bouncycastle.openpgp.PGPException, NoSuchProviderException { final File debFile = createPackage(ImmutableList.<Resource>of( new StringResource("hello world", true, "/tmp/test.txt", USER, USER, TarEntry.DEFAULT_FILE_MODE) )); final File packageDir = temporaryFolder.newFolder(); ArchiveUtils.extractAr(debFile, packageDir); final File pgpSignatureFile = new File(packageDir, "_gpgorigin"); assertTrue(pgpSignatureFile.exists()); try (final InputStream keyringIn = PGPUtil.getDecoderStream(PackageBuilderTest.class.getResourceAsStream("public.asc"))) { try (final InputStream signatureIn = PGPUtil.getDecoderStream(new FileInputStream(pgpSignatureFile))) { final PGPPublicKey publicKey = ((PGPPublicKeyRing) new BcPGPPublicKeyRingCollection(keyringIn).getKeyRings().next()).getPublicKey(); final PGPSignature signature = ((PGPSignatureList) new BcPGPObjectFactory(signatureIn).nextObject()).get(0); signature.init(new BcPGPContentVerifierBuilderProvider(), publicKey); signature.update(Files.asByteSource(new File(packageDir, "debian-binary")).read()); signature.update(Files.asByteSource(new File(packageDir, "control.tar.gz")).read()); signature.update(Files.asByteSource(new File(packageDir, "data.tar.gz")).read()); assertTrue(signature.verify()); } } } @Test public void testValidArchive() throws IOException, PackagingException { final File debFile = createPackage(ImmutableList.<Resource>of( new StringResource("hello world", true, "/tmp/test.txt", USER, USER, TarEntry.DEFAULT_FILE_MODE) )); final File packageDir = temporaryFolder.newFolder(); ArchiveUtils.extractAr(debFile, packageDir); } @Test public void testExpectedControlFiles() throws IOException, PackagingException { final File debFile = createPackage(ImmutableList.<Resource>of( new StringResource("hello world", true, "/tmp/test.txt", USER, USER, TarEntry.DEFAULT_FILE_MODE) )); final File packageDir = temporaryFolder.newFolder(); ArchiveUtils.extractAr(debFile, packageDir); final File controlDir = temporaryFolder.newFolder(); ArchiveUtils.extractTarGzip(new File(packageDir, "control.tar.gz"), controlDir); for (final String file : ImmutableSet.of( "control", "conffiles", "preinst", "postinst", "prerm", "postrm")) { assertTrue(new File(controlDir, file).exists()); } } @Test public void testExpectedDataFiles() throws IOException, PackagingException { final File debFile = createPackage(ImmutableList.<Resource>of( new StringResource("hello world", true, "/tmp/test.txt", USER, USER, TarEntry.DEFAULT_FILE_MODE) )); final File packageDir = temporaryFolder.newFolder(); ArchiveUtils.extractAr(debFile, packageDir); final File dataDir = temporaryFolder.newFolder(); ArchiveUtils.extractTarGzip(new File(packageDir, "data.tar.gz"), dataDir); final File testFile = new File(dataDir, "/tmp/test.txt"); assertTrue(testFile.exists()); } @Test public void testTemplatesCorrectlyExecuted() throws IOException, PackagingException { final File debFile = createPackage(ImmutableList.<Resource>of( new StringResource("hello {{{deb.name}}}", true, "/tmp/test.txt", USER, USER, TarEntry.DEFAULT_FILE_MODE) )); final File packageDir = temporaryFolder.newFolder(); ArchiveUtils.extractAr(debFile, packageDir); final File dataDir = temporaryFolder.newFolder(); ArchiveUtils.extractTarGzip(new File(packageDir, "data.tar.gz"), dataDir); final File testFile = new File(dataDir, "/tmp/test.txt"); assertTrue(testFile.exists()); assertEquals("hello test", Files.asCharSource(testFile, StandardCharsets.UTF_8).read().trim()); } @Test public void testTemplatesNotExecutedWhenNotFiltered() throws IOException, PackagingException { final File debFile = createPackage(ImmutableList.<Resource>of( new StringResource("hello {{{deb.name}}}", false, "/tmp/test.txt", USER, USER, TarEntry.DEFAULT_FILE_MODE) )); final File packageDir = temporaryFolder.newFolder(); ArchiveUtils.extractAr(debFile, packageDir); final File dataDir = temporaryFolder.newFolder(); ArchiveUtils.extractTarGzip(new File(packageDir, "data.tar.gz"), dataDir); final File testFile = new File(dataDir, "/tmp/test.txt"); assertTrue(testFile.exists()); assertEquals("hello {{{deb.name}}}", Files.asCharSource(testFile, StandardCharsets.UTF_8).read().trim()); } @Test public void testFileOwnershipAndPermissions() throws IOException, PackagingException { final File debFile = createPackage(ImmutableList.<Resource>of( new StringResource("hello world", true, "/test.txt", USER, USER, 0764) )); final File packageDir = temporaryFolder.newFolder(); ArchiveUtils.extractAr(debFile, packageDir); try (final TarArchiveInputStream in = new TarArchiveInputStream(new GZIPInputStream(new FileInputStream(new File(packageDir, "data.tar.gz"))))) { final TarArchiveEntry entry = in.getNextTarEntry(); assertEquals("./test.txt", entry.getName()); assertEquals(USER, entry.getUserName()); assertEquals(USER, entry.getGroupName()); assertEquals(0764, entry.getMode()); } } }