Getting a basic build is great as a starting point. The pipeline we have now can be improved by adding some extra steps for verifying the image in regards to quality and security.
We enhance the pipeline with extra steps that
This is the enhanced pipeline:
Unit tests are the pillars of modern software quality. They should be easy to setup and easy to run requiring only the source code and nothing else.
Our example application already has JUnit tests that are normally executed with the command mvn test
via Maven.
To replicate the same thing in Codefresh we choose a Docker image that contains Maven and then use it in a freestyle step, that allows you to run any docker container in the context of a pipeline.
Add this snippet in your pipeline after the main_clone
step.
run_unit_tests:
title: Unit test
stage: package
image: 'maven:3.5.2-jdk-8-alpine'
commands:
- mvn -Dmaven.repo.local=/codefresh/volume/m2_repository test
In a similar way you could run tests with gradle, gulp, node, jasmine, protractor and any other programming framework that you use. Simply choose a docker image that contains the tool and its dependencies and pass it to a freestyle step. Most times all popular tools already have a public container image available in Dockerhub.
In the case of Maven we also want to cache all dependencies so we place the Maven repository on the Codefresh volume which is a shared volume between all steps all the pipeline. It is also saved for any subsequent runs of the same pipeline.
Apart from quality checks, another best practice is the automated security scans of your application to detect security issues and vulnerabilities.
There are any solutions for security scanning and several of them support scanning both the source code of the application as well as the resulting container images.
For the purposes of this workshop we will use the free Trivy scanner that is completely open source and self-contained (it doesn’t need a subscription or online account to a security service).
There is already a public Dockerhub image for Trivy. This means that we can use it right away in a Codefresh freestyle step right in the pipeline.
Add this snippet in your pipeline after the build_my_image
step.
scan_image:
title: Container security scan
stage: verify
image: 'aquasec/trivy'
commands:
- trivy image docker.io/<your docker username>/my-app-image:${{CF_SHORT_REVISION}}
Remember to replace <your docker username>
with your own Dockerhub username.
We run Trivy in the simplest way possible. In this mode it will only scan the image and then print on the log out any issues it finds in a text report.
In a real application you would probably have a security dashboard to track all vulnerabilities over time and you would have multiple security steps (that also focus on the source code as well and not just the container image).
In this example application, the pipeline will always continue running regardless of any security issues. With Codefresh you also have the capability to stop the pipeline if a certain threshold is reached (i.e. critical security issues).
Simple unit tests (that only need the source code) are trivial to run in the middle of the pipeline. Several times however you also have integration/component/end2end tests that need the running application to be deployed first before the tests can “hit” it.
Codefresh offers service containers that make integration tests very easy to run.
With service containers you can launch a container image inline with the pipeline with a well known DNS name that can be used by your pipeline steps. The syntax is similar to Docker compose.
Add this snippet in your pipeline after the scan_image
step.
run_integration_tests:
title: Integration tests
stage: verify
image: maven:3.5.2-jdk-8-alpine
commands:
- mvn -Dmaven.repo.local=/codefresh/volume/m2_repository verify -Dserver.host=http://my-spring-app
services:
composition:
my-spring-app:
image: '${{build_my_image}}'
ports:
- 8080
readiness:
timeoutSeconds: 30
periodSeconds: 15
image: byrnedo/alpine-curl
commands:
- "curl http://my-spring-app:8080/"
Here the pipeline takes the application image and launches it under the name my-spring-app
also exposing port 8080.
Then the pipeline is running the mvn verify
goal for running integration tests while also passing as a parameter to the tests the url http://my-spring-app
that the tests will use.
To make sure that the application is actually up and ready to serve requests before the tests run we define in the readiness
property a quick check with curl.
For more details see the Checking readiness of a service section in the Codefresh documentation.
To make sure that your pipeline has enough resources to execute integration tests, click the Settings tab on the pipeline editor screen and then the Runtime section on the left menu. Assign your pipeline to a Medium instance. If you don’t see these options then your Codefresh plan is already using Medium instances and there is nothing you need to do.
Codefresh has several more features for service containers such as preloading test data to databases, binding containers to localhost, reusing containers for all pipeline steps and so on.
Here is how your whole pipeline should look now:
codefresh.yml
version: '1.0'
stages:
- checkout
- package
- verify
steps:
main_clone:
title: Cloning main repository...
type: git-clone
repo: '${{CF_REPO_OWNER}}/${{CF_REPO_NAME}}'
revision: '${{CF_REVISION}}'
stage: checkout
run_unit_tests:
title: Compile/Unit test
stage: package
image: 'maven:3.5.2-jdk-8-alpine'
working_directory: ./simple-java-app
commands:
- mvn -Dmaven.repo.local=/codefresh/volume/m2_repository test
build_my_image:
title: Building Docker Image
type: build
stage: package
image_name: <your docker username>/my-app-image
working_directory: ./simple-java-app
tags:
- "${{CF_SHORT_REVISION}}"
- latest
dockerfile: Dockerfile
registry: dockerhub
scan_image:
title: Container security scan
stage: verify
image: 'aquasec/trivy'
commands:
- trivy image docker.io/<your docker username>/my-app-image:${{CF_SHORT_REVISION}}
run_integration_tests:
title: Integration tests
stage: verify
image: maven:3.5.2-jdk-8-alpine
working_directory: ./simple-java-app
commands:
- mvn -Dmaven.repo.local=/codefresh/volume/m2_repository verify -Dserver.host=http://my-spring-app
services:
composition:
my-spring-app:
image: '${{build_my_image}}'
ports:
- 8080
readiness:
timeoutSeconds: 30
periodSeconds: 15
image: byrnedo/alpine-curl
commands:
- "curl http://my-spring-app:8080/"
Remember to change the value of <your docker username>
to your own Dockerhub username.
Click the Run button at the bottom of the screen to launch a new build.
Once the pipeline is running you can click on any individual step and see the log messages of that step:
You are now practising Continuous integration. Every time somebody commits a code change, an automated process creates a deployment artifact that is tested and secure.