[[docker]] = Docker Project include::_attributes.adoc[] In this section, we publish a `springcloud/spring-cloud-contract` Docker image that contains a project that generates tests and runs them in `EXPLICIT` mode against a running application. TIP: The `EXPLICIT` mode means that the tests generated from contracts send real requests and not mocked ones. We also publish a `spring-cloud/spring-cloud-contract-stub-runner` Docker image that starts the standalone version of Stub Runner. [[docker-intro]] == A Short Introduction to Maven, JARs, and Binary Storage Since non-JVM projects can use the Docker image, it is good to explain the basic terms behind Spring Cloud Contract packaging defaults. Parts of the following definitions were taken from the https://maven.apache.org/glossary.html[Maven Glossary]: - `Project`: Maven thinks in terms of projects. Projects are all you build. Those projects follow a well defined "`Project Object Model`". Projects can depend on other projects -- in that case, the latter are called "`dependencies`". A project may consistent of several subprojects. However, these subprojects are still treated equally as projects. - `Artifact`: An artifact is something that is either produced or used by a project. Examples of artifacts produced by Maven for a project include JAR files and source and binary distributions. Each artifact is uniquely identified by a group ID and an artifact ID that is unique within a group. - `JAR`: JAR stands for Java ARchive. Its format is based on the ZIP file format. Spring Cloud Contract packages the contracts and generated stubs in a JAR file. - `GroupId`: A group ID is a universally unique identifier for a project. While this is often just the project name (for example, `commons-collections`), it is helpful to use a fully-qualified package name to distinguish it from other projects with a similar name (for example, `org.apache.maven`). Typically, when published to the Artifact Manager, the `GroupId` gets slash separated and forms part of the URL. For example, for a group ID of `com.example` and an artifact ID of `application`, the result would be `/com/example/application/`. - `Classifier`: The Maven dependency notation looks as follows: `groupId:artifactId:version:classifier`. The classifier is an additional suffix passed to the dependency -- for example, `stubs` or `sources`. The same dependency (for example, `com.example:application`) can produce multiple artifacts that differ from each other with the classifier. - `Artifact manager`: When you generate binaries, sources, or packages, you would like them to be available for others to download, reference, or reuse. In the case of the JVM world, those artifacts are generally JARs. For Ruby, those artifacts are gems. For Docker, those artifacts are Docker images. You can store those artifacts in a manager. Examples of such managers include https://jfrog.com/artifactory/[Artifactory] and https://www.sonatype.org/nexus/[Nexus]. [[docker-how-it-works]] == Generating Tests on the Producer Side The image searches for contracts under the `/contracts` folder. The output from running the tests is available in the `/spring-cloud-contract/build` folder (useful for debugging purposes). You can mount your contracts and pass the environment variables. The image then: - Generates the contract tests - Runs the tests against the provided URL - Generates the https://github.com/tomakehurst/wiremock[WireMock] stubs - Publishes the stubs to a Artifact Manager (optional -- turned on by default) [[docker-env-vars]] === Environment Variables The Docker image requires some environment variables to point to your running application, to the Artifact manager instance, and so on. The following list describes the environment variables: // TODO: reenable //include::{project-root}/docker/spring-cloud-contract-docker/target/adoc/props.adoc[indent=0] The following environment variables are used when tests are run: // TODO: reenable //include::{project-root}/docker/spring-cloud-contract-docker/target/adoc/appProps.adoc[indent=0] ### Customizing the gradle build You can provide a customized `gradle.build` to be run in the container by mounting your customized build file as a volume when running the container: ==== [source,bash] ---- $ docker run -v :/spring-cloud-contract/build.gradle springcloud/spring-cloud-contract: ---- ==== [[docker-example-of-usage]] === Example of Usage via HTTP In this section, we explore a simple MVC application. To get started, clone the following git repository and cd to the resulting directory, by running the following commands: ==== [source,bash] ---- $ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs $ cd bookstore ---- ==== The contracts are available in the `/contracts` folder. Since we want to run tests, we can run the following command: ==== [source,bash] ---- $ npm test ---- ==== However, for learning purposes, we split it into pieces, as follows: ==== [source,bash] ---- # Stop docker infra (nodejs, artifactory) $ ./stop_infra.sh # Start docker infra (nodejs, artifactory) $ ./setup_infra.sh # Kill & Run app $ pkill -f "node app" $ nohup node app & # Prepare environment variables $ SC_CONTRACT_DOCKER_VERSION="..." $ APP_IP="192.168.0.100" $ APP_PORT="3000" $ ARTIFACTORY_PORT="8081" $ APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}" $ ARTIFACTORY_URL="http://${APP_IP}:${ARTIFACTORY_PORT}/artifactory/libs-release-local" $ CURRENT_DIR="$( pwd )" $ CURRENT_FOLDER_NAME=${PWD##*/} $ PROJECT_VERSION="0.0.1.RELEASE" # Run contract tests $ docker run --rm -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" -e "PUBLISH_ARTIFACTS=true" -e "PROJECT_NAME=${CURRENT_FOLDER_NAME}" -e "REPO_WITH_BINARIES_URL=${ARTIFACTORY_URL}" -e "PROJECT_VERSION=${PROJECT_VERSION}" -v "${CURRENT_DIR}/contracts/:/contracts:ro" -v "${CURRENT_DIR}/node_modules/spring-cloud-contract/output:/spring-cloud-contract-output/" springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}" # Kill app $ pkill -f "node app" ---- ==== Through bash scripts, the following happens: - The infrastructure (MongoDb and Artifactory) is set up. In a real-life scenario, you would run the NodeJS application with a mocked database. In this example, we want to show how we can benefit from Spring Cloud Contract in very little time. - Due to those constraints, the contracts also represent the stateful situation. ** The first request is a `POST` that causes data to get inserted into the database. ** The second request is a `GET` that returns a list of data with 1 previously inserted element. - The NodeJS application is started (on port `3000`). - The contract tests are generated through Docker, and tests are run against the running application. ** The contracts are taken from `/contracts` folder. ** The output of the test is available under `node_modules/spring-cloud-contract/output`. - The stubs are uploaded to Artifactory. You can find them in http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/. The stubs are at http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar. [[docker-example-of-usage-messaging]] === Example of Usage via Messaging If you want to use Spring Cloud Contract with messaging via the Docker images (e.g. in case of polyglot applications) then you'll have to have the following prerequisites met: * Middleware (e.g. RabbitMQ or Kafka) must be running before generating tests * Your contract needs to call a method `triggerMessage(...)` with a `String` parameter that is equal to the contract's `label`. * Your application needs to have a HTTP endpoint via which we can trigger a message ** That endpoint should not be available on production (could be enabled via an environment variable) [[docker-example-of-usage-messaging-contract]] ==== Example of a Messaging Contract The contract needs to call a `triggerMessage(...)` method. That method is already provided in the base class for all tests in the docker image and will send out a request to the HTTP endpoint on the producer side. Below you can find examples of such contracts. ==== [source,groovy,indent=0,subs="verbatim,attributes",role="primary"] .Groovy ---- import org.springframework.cloud.contract.spec.Contract Contract.make { description 'Send a pong message in response to a ping message' label 'ping_pong' input { // You have to provide the `triggerMessage` method with the `label` // as a String parameter of the method triggeredBy('triggerMessage("ping_pong")') } outputMessage { sentTo('output') body([ message: 'pong' ]) } metadata( [amqp: [ outputMessage: [ connectToBroker: [ declareQueueWithName: "queue" ], messageProperties: [ receivedRoutingKey: '#' ] ] ] ]) } ---- [source,yml,indent=0,subs="verbatim,attributes",role="secondary"] .YAML ---- description: 'Send a pong message in response to a ping message' label: 'ping_pong' input: # You have to provide the `triggerMessage` method with the `label` # as a String parameter of the method triggeredBy: 'triggerMessage("ping_pong")' outputMessage: sentTo: 'output' body: message: 'pong' metadata: amqp: outputMessage: connectToBroker: declareQueueWithName: "queue" messageProperties: receivedRoutingKey: '#' ---- ==== [[docker-example-of-usage-messaging-endpoint]] ==== HTTP Endpoint to Trigger a Message Why is there need to develop such an endpoint? Spring Cloud Contract would have to generate code in various languages (as it does in Java) to make it possible to trigger production code that sends a message to a broker. If such code is not generated then we need to be able to trigger the message anyways, and the way to do it is to provide an HTTP endpoint that the user will prepare in the language of their choosing. The endpoint must have the following configuration: - URL: `/springcloudcontract/{label}` where `label` can be any text - Method: `POST` - Basing on the `label` will generate a message that will be sent to a given destination according to the contract definition Below you have an example of such an endpoint. If you're interested in providing an example in your language don't hesitate to file an issue in the https://github.com/spring-cloud/spring-cloud-contract/issues/new?assignees=&labels=&template=feature_request.md&title=New+Polyglot+Sample+of+a+HTTP+controller[Spring Cloud Contract repository at Github]. ==== [source,python,indent=0,subs="verbatim,attributes"] .Python ---- #!/usr/bin/env python from flask import Flask from flask import jsonify import pika import os app = Flask(__name__) # Production code that sends a message to RabbitMQ def send_message(cmd): connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.basic_publish( exchange='output', routing_key='#', body=cmd, properties=pika.BasicProperties( delivery_mode=2, # make message persistent )) connection.close() return " [x] Sent via Rabbit: %s" % cmd # This should be ran in tests (shouldn't be publicly available) if 'CONTRACT_TEST' in os.environ: @app.route('/springcloudcontract/