All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mework.cloud.spring-cloud-contract-docs.4.0.4.source-code.docker-project.adoc Maven / Gradle / Ivy

There is a newer version: 4.1.5
Show newest version
[[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:

include::{project-root}/docker/spring-cloud-contract-docker/target/adoc/props.adoc[indent=0]

The following environment variables are used when tests are run:

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/




© 2015 - 2024 Weber Informatics LLC | Privacy Policy