CircleCI step isolation
To use MockServer inside a CircleCI build, using the docker executor with MockServer as secondary container works well.
In this article I explored an other way: start MockServer as java process.
From a CircleCI perspective you need to run in a docker container or on a machine that that provides a JVM.
With the docker executor, you can run using the docker image circleci/openjdk:8-jdk
for your primary container.
The machine executor using the image ubuntu-1604:201903-01
is also working well (but is slower).
Mock-Server as Java process
Because MockServer is written in java, it is possible to run it using java -jar
.
One of the published artifact is a convenient jar containing all the dependencies, so that you have no issue with the java classpath.
It is available using following coordinates:
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>5.9.0</version>
<classifier>jar-with-dependencies</classifier>
<packaging>jar</packaging>
</dependency>
This all-in-one jar can be fetched directly from the corresponding url (for version 5.9.0
):
https://repo1.maven.org/maven2/org/mock-server/mockserver-netty/5.9.0/mockserver-netty-5.9.0-jar-with-dependencies.jar
An other idea is to use maven to perform the download, this way the artifact is also cached locally (the maven local repository can be reused between builds):
mvn org.apache.maven.plugins:maven-dependency-plugin:3.1.1:copy \
-Dartifact=org.mock-server:mockserver-netty:5.9.0:jar:jar-with-dependencies \
-DoutputDirectory=build
The previous command will put the mockserver-netty-5.9.0-jar-with-dependencies.jar
file inside a build/
folder.
We can start it using the java command line:
java -jar build/mockserver-netty-5.9.0-jar-with-dependencies.jar -serverPort 1080
But if we are using maven, we can also use the Maven Plugin form the command line (without any maven build):
mvn -Dmockserver.serverPort=1080 -Dmockserver.logLevel=INFO org.mock-server:mockserver-maven-plugin:5.9.0:runForked
The CircleCI step isolation
My first version was more or less looking like this (I removed the steps to cache and restore the local maven repository):
version: 2
jobs:
build:
docker:
- image: circleci/openjdk:8-jdk
steps:
- run:
name: Start MockServer
command: mvn -Dmockserver.serverPort=1080 -Dmockserver.logLevel=INFO org.mock-server:mockserver-maven-plugin:5.9.0:runForked
- run:
name: Curl test
command: |
curl --request PUT "http://localhost:1080/mockserver/reset"
- run:
name: Stop MockServer
command: mvn -Dmockserver.serverPort=1080 org.mock-server:mockserver-maven-plugin:5.9.0:stopForked
when: always
But I got the error:
#!/bin/bash -eo pipefail
curl --request PUT "http://localhost:1080/mockserver/reset"
curl: (7) Failed to connect to localhost port 1080: Connection refused
Exited with code exit status 7
CircleCI received exit code 7
And it seems that I am not the only one:
- Can reach localhost using curl either inside or outside docker container
- Cannot curl to localhost: connection refused
- …
It was difficult for me to understand why this wasn’t working.
Even if I did not find a precise statement in the steps reference guide, my conclusion is that because each steps is using a separated shell, the operations started during one step might no longer be running when the next steps are executed.
It works as expected when I start MockServer and perform the curl
command inside the same step:
version: 2
jobs:
build:
docker:
- image: circleci/openjdk:8-jdk
steps:
- run:
name: Single step
command: |
mvn -Dmockserver.serverPort=1080 -Dmockserver.logLevel=INFO org.mock-server:mockserver-maven-plugin:5.9.0:runForked
curl --request PUT "http://localhost:1080/mockserver/reset"
mvn -Dmockserver.serverPort=1080 org.mock-server:mockserver-maven-plugin:5.9.0:stopForked
CircleCI background steps
CircleCI has also the possibility to run background commands. In this case you need to start a blocking command inside this step. For MockServer you have two options:
java -jar
combined with a previous step to download thejar
(as discussed at the beginning of this article)- the
mockserver-maven-plugin
but with therun
command instead ofrunForked
in order to run MockServer synchronously and block.
Note that CircleCI will start the step marked with background
and without waiting for anything else it will continue with the next steps immediately.
This makes sense because background steps are typically starting a synchronous process that is never ending.
In case of MockServer the startup takes few seconds. This means that if you perform directly a curl
command, you will get the “Connection refused” error.
As described in my article where MockServer is started as secondary container, you need to introduce a “Wait for MockServer” step using sleep
or “curl
with retries”.
version: 2
jobs:
build:
docker:
- image: circleci/openjdk:8-jdk
steps:
- run:
name: Start MockServer
command: mvn -Dmockserver.serverPort=1080 -Dmockserver.logLevel=INFO org.mock-server:mockserver-maven-plugin:5.9.0:run
background: true
- run:
name: Wait for MockServer
command: |
curl --retry 30 --retry-delay 2 --retry-connrefused --request PUT "http://localhost:1080/mockserver/reset"
- run:
name: Curl test
# TODO: do something real instead of this placeholder command
command: |
curl --request PUT "http://localhost:1080/mockserver/reset"
I hope this article provides some valuable insights about how the steps can be combined when working with a local server and curl
inside a CircleCI.