Building PatrIoT framework from sources

For development purposes it might be necessary to obtain development versions of PatrIoT framework which will enable test developer to use new unreleased versions of framework or to modify the source code to achieve specific behaviour of the framework.

In order to obtain the source code of the framework modules, specifically:

  • patriot-api java source code

  • docker-network-simulator java source code

  • patriot-data-generator java source code

  • patriot-router utilitary source codes in python and go for internal network routing.

Building java sources

In all of the java source codes the test developer needs to check the artifact version in the pom.xml file within such module, which for the development version should contain version similar to 3.0.X-SNAPSHOT.

For the situation when at-hoc change to the released verion is needed, the developer needs to change the project.version to custom string e.g. 3.0.0-DEVEL to all of the java modules see following snippet:

Example modification to project pom.xml
<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/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>io.patriot-framework</groupId>
   <artifactId>patriot-api</artifactId>
   <version>2.0.1</version>
<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/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>io.patriot-framework</groupId>
   <artifactId>patriot-api</artifactId>
   <version>3.0.1-SNAPSHOT</version>

Since all of the sources do depend on each other, the compilation of artifacts must be done in precise order to meet all the dependencies between submodules, such order is:

  • patriot-data-generator doesn’t have dependency on other modules, so it must be built first.

  • patriot-api depends on Data Generator module, so it must be built second.

  • docker-network-simulator implements patriot-api and hence it must be built as last.

With this order in mind the actual compilation can be achieved by running following shell script. Assume that all of the sources are available in the directory patriot-src and named as seen above:

Snippet of shell script to build all of the sources
for k in patriot-data-generator patriot-api docker-network-simulator; do
    cd $k
    mvn clean install
done

Once all of the sources are compiled and available in the local maven repository test developer needs to change the version of PatrIoT framework in the project containing the developed tests:

Example of updated dependency to development version
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>my-group</groupId>
   <artifactId>my-simple-test</artifactId>
   <version>1.0-SNAPSHOT</version>

   <properties>
      <junit.jupiter.version>5.4.0</junit.jupiter.version>
      <patriot.framework.version>3.0.1-SNAPSHOT</patriot.framework.version>
   </properties>

</project>

Building router docker image

The patriot-router component is principal part of network simulation and as such must be present to enable ability of PatrIoT framework to control the simulated environment. In order to build it, following command must be executed within patriot-router directory.

Building router image
$ cd patriot-router
$ docker build . --tag patriot-router

Once the image is built, it can be used by PatrIoT framework by setting property io.patriot_framework.router.

patriot.properties content
io.patriot_framework.router=patriot-router

Creating custom base image

To deploy any containerized application with full PatrIoT capabilities - e.g. advanced network control - some modifications to the base image are necessary. For Java applications there is base image patriotframework/simulator-base which can be built by running following command.

Building Java 1.8 base image
$ cd patriot-router
$ docker build -f base-image/Dockerfile . --tag java-baseimage

With existing base image the tested java application can be containerized by creation of new image which is built on top of the java-baseimage. Since all of the runtime dependencies are already contained within the image, the only one thing necessary is to put full jar file with built Java application into the image and set CMD property of container image.

Example Dockerfile for containerized Java application
FROM java-baseimage
COPY app.jar /app.jar
CMD java -jar /app.jar

Building image with different base image and customizations

The base image doesn’t need to be built with Java, but can be created from any other linux base image with any customizations needed, if following prerequisites can be met:

  • The base image is created for linux distributions and based on linux.

  • User can extend the image by installation of images.

  • The golang package is available in version 1.11 at least.

Once all of those requirements are met, the Dockerfile can be produced to extend the base image with netowrk control capabilities. The steps necessary are:

  1. Locate place where docker build context will be executed - typically directory where Dockerfile would be placed.

  2. Copy whole directory patriot-router/api directory to the build context directory

  3. Find packages for

    1. golang in version 1.11 or higher

    2. iptables

    3. iputils or package providing similar functions

    4. git

    5. Standard C library - development files

    6. Make

  4. Extend Dockerfile to install those packages.

  5. Find directory, which contains go installation and export it as GOROOT environment variable

Afther those steps following snippet must be added to the Dockerfile to install api for network control.

Neccessary content of Dockerfile to build network control api.
ENV GOPATH=/go
ENV PATH=/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/jdk/bin

RUN mkdir -p ${GOPATH}/src ${GOPATH}/bin

RUN go get -d -v github.com/olivere/elastic && \
go get -d -v github.com/sirupsen/logrus && \
go get -d -v gopkg.in/sohlich/elogrus.v3 &&\
go get -d -v github.com/abbot/go-http-auth && \
go get -d -v github.com/gorilla/handlers && \
go get -d -v github.com/gorilla/mux &&\
go get -d -v github.com/oxalide/go-iptables/iptables &&\
go get -d -v github.com/seatgeek/logrus-gelf-formatter

WORKDIR /go/src
ADD api/iprouteRESt /go/src/api/iprouteRESt
ADD api/iptables-api /go/src/api/iptables-api
ADD api/iproute2 /go/src/api/iproute2
WORKDIR /

WORKDIR /go/src/api/iptables-api/
RUN go build  -o /api/iptables-api main.go
WORKDIR /go/src/api/iprouteRESt/
RUN go build -o /api/iprouteRESt Controller.go

As last step it is necessary to ensure that iprouteRESt will be started with the container as separate proces within it. In case, that your application doesn’t need custom entrypoint as a decorator over executed command, then following shell script can be copied to the container image and configured as entrypoint.

Shell script to start daemon for network controll
#!/bin/bash
# Bash script for starting all necessary daemons for app gateway


function stop_daemons() {
    echo "Stopping daemons"
    kill -TERM "$REST_PID"
    kill -TERM "$API_PID"
}

if [ "$1" == '/api/iptables-api' ]; then
    echo "Starting daemons"
    trap 'stop_daemons' EXIT

    /api/iprouteRESt &
    export REST_PID=$!

    exec "$@" &
    export API_PID=$!

    wait $API_PID

else
    /api/iprouteRESt &
    export REST_PID=$!
    exec "$@"
fi

In case that your application needs a custom entrypoint, then it must be extended to start /api/iprouteRESt as a background process and it must be able to kill it later, when the main process is being terminated. If the child process woudln’t be terminated, the container won’t stop properly, because docker-daemon expects all processes to end.

For bash based entrypoint it would be enough to ensure that following command is executed upon start of container.

iprouteRESt execution in bash
/api/iprouteRESt &
export REST_PID=$!
trap "kill -TERM $REST_PID" EXIT