EvenMoreG
EvenMoreG

Reputation: 221

Small binary file is changed on building Docker image using spotify maven plugin

I'm using spotify's docker-maven-plugin for building a docker image. More exactly this one:

<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.13</version>

My development machine has a Windows 7, so I'm running docker machine with version docker-machine version 0.9.0, build 15fd4c7 Docker version is this

Client:
 Version:      1.13.1
 API version:  1.26
 Go version:   go1.7.5
 Git commit:   092cba3
 Built:        Wed Feb  8 08:47:51 2017
 OS/Arch:      windows/amd64

Server:
 Version:      17.03.0-ce
 API version:  1.26 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   3a232c8
 Built:        Tue Feb 28 07:52:04 2017
 OS/Arch:      linux/amd64
 Experimental: false

My application uses a certificate which I want to prepare in advance and include in the image.

If I build the Docker image directly using Docker cli it works, the keystore file with the certificate is transferred correctly.

If I build the Docker image using the spotify maven plugin, the keystore file will be corrupted. A comparison shows that it is much larger in size, a comparison of its content (hexdump) looks like it has been sprinkled (I don't know how to say it better) with extra bytes.

I've created a small example which shows the behavior: Project structure:

-src
|-main
| |-docker
|   |-binaries
|   | |-example.jks
|   |-Dockerfile
|-pom.xml

Create example.jks like this (using keytool from openjdk 8)

keytool -genkey -keyalg RSA -alias selfsigned -keystore example.jks -storepass password -keypass password -validity 18250 -keysize 2048 -dname "CN=Unknown, OU=Example, O=Example, L=Example, ST=Unknown, C=US" 

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">


    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>smallbinary</artifactId>
    <name>Small binary problem</name>
    <version>1.0</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <resources>
            <resource>
                <!--
                 | Enable resource filtering pre dockerfile build calls.
                 -->
                <directory>src/main/docker</directory>
                <targetPath>${project.build.directory}/docker-derived</targetPath>
                <filtering>true</filtering>
            </resource>
        </resources>
        <pluginManagement>
            <plugins>
                <plugin>
                   <groupId>com.spotify</groupId>
                   <artifactId>docker-maven-plugin</artifactId>
                   <version>0.4.13</version>
                   <executions>
                       <execution>
                           <id>build-image</id>
                           <phase>compile</phase>
                           <goals>
                               <goal>build</goal>
                           </goals>
                       </execution>
                   </executions>
                   <configuration>
                       <imageName>${project.artifactId}</imageName>
                       <dockerDirectory>${project.build.directory}/docker-derived</dockerDirectory>
                   </configuration>
               </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin><!--  Triggers the Docker build configured within the plugin management. -->
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Dockerfile

FROM openjdk:8-jre-alpine

COPY binaries/* /opt/service/

CMD ["keytool", "-list", "-keystore", "/opt/service/example.jks", "-storepass", "password"]

Output if built directly per docker build . and run by docker run [imagename]

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

selfsigned, Apr 3, 2017, PrivateKeyEntry,
Certificate fingerprint (SHA1): 07:56:26:66:16:82:DD:BF:6A:61:4B:94:E8:67:69:F8:77:36:5C:6D

And sadly the output when built with maven:

keytool error: java.io.IOException: Invalid keystore format

In other contexts, when copying larger binaries like jars, wars, ears or zip archives I haven't encountered any difficulties. But this one just doesn't seem to work.

My current workaround is to create the certificate directly during image build via a RUN command in the Dockerfile.

Is there anything I am missing?

P.S. I experience the same issues on my linux Ubuntu 16.04 LTS laptop.

Upvotes: 1

Views: 463

Answers (1)

EvenMoreG
EvenMoreG

Reputation: 221

The problem is caused by the resource filtering which is activated in my example (and in my production code as well). That I learned from My Technical Life which again links to a stackoverflow question jar file gets corrupted while building with maven

The offending code is this:

<resource>
  <!--
   | Enable resource filtering pre dockerfile build calls.
  -->
  <directory>src/main/docker</directory>
  <targetPath>${project.build.directory}/docker-derived</targetPath>
  <-- The next line breaks my binary-->
  <filtering>true</filtering>
 </resource>

Removing <filtering>true</filtering> or explicitly setting it to false fixes my example code and creates a working Docker image.

What to do if you need filtering, like I do because I refer to project versions in my production code Dockerfile and want Maven to replace some tokens?

The solution is to change the project structure a bit and separate filterable and non-filterable resources.

I changed the folder structure as follows:

-src
|-main
| |-docker
|   |-Dockerfile
|-resources
| |-binaries
|   |-example.jks
|-pom.xml

And changed the resource section of my example like this:

<resources>
  <resource>
    <!--
     | Enable resource filtering pre dockerfile build calls for non binaries.
    -->
    <directory>src/main/docker</directory>
    <targetPath>${project.build.directory}/docker-derived</targetPath>
    <filtering>true</filtering>
  </resource>
  <resource>
    <directory>src/main/resources/binaries</directory>
    <targetPath>${project.build.directory}/docker-derived/binaries</targetPath>
  </resource>
</resources>

And then it works like a charm. Sorry for doubting the spotify plugin!

Upvotes: 3

Related Questions