此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 spring-cloud-contract 4.2.0! |
Docker 项目
在本节中,我们将发布一个springcloud/spring-cloud-contract
Docker 镜像
,其中包含一个生成测试并在EXPLICIT
模式
针对正在运行的应用程序。
这EXPLICIT mode 表示从 Contract 生成的测试发送
真实的请求,而不是嘲笑的请求。 |
我们还发布了一个spring-cloud/spring-cloud-contract-stub-runner
Docker 镜像
,这将启动 Stub Runner 的独立版本。
Maven、JAR 和二进制存储简介
由于非 JVM 项目可以使用 Docker 镜像,因此最好 解释 Spring Cloud Contract 打包默认值背后的基本术语。
以下定义的一部分摘自 Maven 词汇表:
-
Project
:Maven 从项目的角度思考。项目 都是你构建的全部。这些项目遵循定义明确的 “项目对象模型”。项目可以依赖于其他项目 — 在这种情况下,后者称为 “依赖项”。一个项目可以 多个子项目的一致性。但是,这些子项目仍然是 平等地视为项目。 -
Artifact
:工件是生产或使用的东西 按项目。Maven 为项目生成的工件示例 包括 JAR 文件以及源代码和二进制分发。每个工件 由组 ID 和工件 ID 唯一标识,该 ID 为 在组中唯一。 -
JAR
:JAR 代表 Java ARchive。其格式基于 ZIP 文件格式。Spring Cloud Contract 将 Contract 打包并生成 stub 中的 stub 文件。 -
GroupId
:组 ID 是项目的通用唯一标识符。 虽然这通常只是项目名称(例如commons-collections
), 使用完全限定的包名称来区分它很有帮助 从具有相似名称的其他项目(例如org.apache.maven
). 通常,当发布到 Artifact Manager 时,GroupId
得到 斜杠分隔,并构成 URL 的一部分。例如,对于组 ID 为com.example
工件 ID 为application
,结果将是/com/example/application/
. -
Classifier
:Maven 依赖关系表示法如下所示:groupId:artifactId:version:classifier
.分类器是一个附加后缀 传递给依赖项 — 例如stubs
或sources
.相同的依赖项 (例如,com.example:application
) 可以生成多个工件,这些工件 分类器彼此不同。 -
Artifact manager
:生成二进制文件、源或包时,您将 喜欢它们以供其他人下载、引用或重复使用。在这种情况下 在 JVM 世界中,这些工件通常是 JAR。对于 Ruby 来说,这些工件就是宝石。 对于 Docker,这些工件是 Docker 镜像。您可以存储这些工件 在经理中。此类管理人的示例包括 Artifactory 和 Nexus。
在生产者端生成测试
该图像在/contracts
文件夹。
运行测试的输出在/spring-cloud-contract/build
文件夹(用于调试
目的)。
您可以挂载 Contract 并传递环境变量。 然后,图像:
-
生成 Contract 测试
-
针对提供的 URL 运行测试
-
生成 WireMock 存根
-
将存根发布到 Artifact Manager(可选 — 默认打开)
环境变量
Docker 镜像需要一些环境变量来指向 您正在运行的应用程序,到 Artifact Manager 实例,依此类推。 以下列表描述了环境变量:
名字 |
描述 |
违约 |
ADDITIONAL_FLAGS |
(仅限 Docker 映像)要传递给 Gradle 构建的其他标志 |
|
调试 |
(仅限 Docker 映像)适用于 Docker 映像 - 打开 Gradle 构建的调试模式 |
假 |
EXTERNAL_CONTRACTS_ARTIFACT_ID |
包含合同的项目的工件 ID |
|
EXTERNAL_CONTRACTS_CLASSIFIER |
具有 Contract 的项目的分类器 |
|
EXTERNAL_CONTRACTS_GROUP_ID |
包含合同的项目的组 ID |
com.示例 |
EXTERNAL_CONTRACTS_PATH |
给定项目的合同路径,在具有 Contract 的项目内。默认为斜杠分隔 |
|
EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_PASSWORD |
(可选)Password (如果 |
|
EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL |
Artifact Manager 的 URL。它默认为 |
|
EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_USERNAME |
(可选)用户名 (如果 |
|
EXTERNAL_CONTRACTS_VERSION |
包含合同的项目版本。Defautls 相当于选择最新的 |
+ |
EXTERNAL_CONTRACTS_WORK_OFFLINE |
如果设置为 |
假 |
FAIL_ON_NO_CONTRACTS |
如果没有 Contract 存在,构建是否应该失败? |
假 |
MESSAGING_TYPE |
消息类型。可以是 [rabbit] 或 [kafka]。 |
|
PRODUCER_STUBS_CLASSIFIER |
用于生成的生产者存根的存档分类器 |
存根 |
PROJECT_GROUP |
项目的群组 ID |
com.示例 |
PROJECT_NAME |
项目的工件 ID |
例 |
PROJECT_VERSION |
您的项目版本 |
0.0.1-快照 |
PUBLISH_ARTIFACTS |
如果设置为 |
真 |
PUBLISH_ARTIFACTS_OFFLINE |
如果设置为 |
假 |
PUBLISH_STUBS_TO_SCM |
如果设置为 |
假 |
REPO_ALLOW_INSECURE_PROTOCOL |
(可选)如果 <true>则允许通过不安全的 HTTP 将工件发布到 Artifact Manager |
假 |
REPO_WITH_BINARIES_PASSWORD |
(可选)保护 Artifact Manager 时的密码 |
密码 |
REPO_WITH_BINARIES_URL |
Artifact Manager 的 URL(在本地运行时,默认为 Artifactory 的默认 URL) |
|
REPO_WITH_BINARIES_USERNAME |
(可选)Artifact Manager 受到保护时的用户名 |
管理 |
STANDALONE_PROTOCOL |
对于独立版本,应添加哪些附加协议 |
运行测试时使用以下环境变量:
名字 |
描述 |
违约 |
APPLICATION_BASE_URL |
运行应用程序的 URL。 |
|
APPLICATION_PASSWORD |
用于访问应用程序的可选密码。 |
|
APPLICATION_USERNAME |
用于访问应用程序的可选用户名。 |
|
MESSAGING_TRIGGER_CONNECT_TIMEOUT |
连接到应用程序以触发消息的超时。 |
5000 |
MESSAGING_TRIGGER_READ_TIMEOUT |
Timeout 从应用程序读取响应以触发消息。 |
5000 |
MESSAGING_TYPE |
消息类型。可以是 [rabbit] 或 [kafka]。 |
|
MESSAGING_TYPE |
定义处理基于消息的 Contract 时的消息类型。 |
|
SPRING_KAFKA_BOOTSTRAP_SERVERS |
对于 Kafka - 代理地址。 |
|
SPRING_RABBITMQ_ADDRESSES |
对于 RabbitMQ - 代理地址。 |
自定义 gradle 构建
您可以提供自定义的gradle.build
要在运行容器时将自定义构建文件挂载为卷,即可在容器中运行:
$ docker run -v <absolute-path-of-your-custom-file>:/spring-cloud-contract/build.gradle springcloud/spring-cloud-contract:<version>
通过 HTTP 的使用示例
在本节中,我们将探讨一个简单的 MVC 应用程序。要开始使用,请克隆以下内容 git 存储库并 cd 到结果目录,方法是运行以下命令:
$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore
这些合同位于/contracts
文件夹。
由于我们想要运行测试,因此可以运行以下命令:
$ npm test
但是,出于学习目的,我们将其拆分为多个部分,如下所示:
# 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"
通过 bash 脚本,将发生以下情况:
-
基础结构(MongoDb 和 Artifactory)已设置完毕。 在实际场景中,您将运行 NodeJS 应用程序 替换为模拟数据库。在这个例子中,我们想展示我们如何 在很短的时间内从 Spring Cloud Contract 中受益。
-
由于这些约束,这些 Contract 还表示 stateful 情况。
-
第一个请求是
POST
这会导致数据插入到数据库中。 -
第二个请求是
GET
这将返回一个数据列表,其中包含 1 个先前插入的元素。
-
-
NodeJS 应用程序已启动(在端口
3000
). -
合约测试是通过 Docker 生成的,并且测试 针对正在运行的应用程序运行。
-
合约取自
/contracts
文件夹。 -
测试的输出位于
node_modules/spring-cloud-contract/output
.
-
-
存根将上载到 Artifactory。您可以在 localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/ 中找到它们。 存根位于 localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar。
通过消息收发使用示例
如果您想通过 Docker 镜像(例如 如果是多语言应用程序),则必须满足以下先决条件:
-
中间件(例如 RabbitMQ 或 Kafka)必须在生成测试之前运行
-
你的合约需要调用一个方法
triggerMessage(…)
替换为String
参数等于合约的label
. -
您的应用程序需要有一个 HTTP 终端节点,我们可以通过该终端节点触发消息
-
该端点在生产中不应可用(可以通过环境变量启用)
-
消息传送协定示例
合约需要调用triggerMessage(…)
方法。该方法已在 docker 映像中所有测试的基类中提供,并将向生产者端的 HTTP 端点发送请求。您可以在下面找到此类合同的示例。
- 槽的
-
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: '#' ] ] ] ]) }
- 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: '#'
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 Spring Cloud Contract repository at Github.
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/<label>', methods=['POST'])
def springcloudcontract(label):
if label == "ping_pong":
return send_message('{"message":"pong"}')
else:
raise ValueError('No such label expected.')
Running Message Tests on the Producer Side
Now, let’s generate tests from contracts to test the producer side.
We will run bash code to start the Docker image
with attached contracts, however we will also add variables for the messaging
code to work. In this case let’s assume that the contracts are being stored in
a Git repository.
#!/bin/bash
set -x
CURRENT_DIR="$( pwd )"
export SC_CONTRACT_DOCKER_VERSION="${SC_CONTRACT_DOCKER_VERSION:-4.0.1-SNAPSHOT}"
export APP_IP="$( ./whats_my_ip.sh )"
export APP_PORT="${APP_PORT:-8000}"
export APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
export PROJECT_GROUP="${PROJECT_GROUP:-group}"
export PROJECT_NAME="${PROJECT_NAME:-application}"
export PROJECT_VERSION="${PROJECT_VERSION:-0.0.1-SNAPSHOT}"
export PRODUCER_STUBS_CLASSIFIER="${PRODUCER_STUBS_CLASSIFIER:-stubs}"
export FAIL_ON_NO_CONTRACTS="${FAIL_ON_NO_CONTRACTS:-false}"
# In our Python app we want to enable the HTTP endpoint
export CONTRACT_TEST="true"
# In the Verifier docker container we want to add support for RabbitMQ
export MESSAGING_TYPE="rabbit"
# Let's start the infrastructure (e.g. via Docker Compose)
yes | docker-compose kill || echo "Nothing running"
docker-compose up -d
echo "SC Contract Version [${SC_CONTRACT_DOCKER_VERSION}]"
echo "Application URL [${APPLICATION_BASE_URL}]"
echo "Project Version [${PROJECT_VERSION}]"
# Let's run python app
gunicorn -w 4 --bind 0.0.0.0 main:app &
APP_PID=$!
# Generate and run tests
docker run --rm \
--name verifier \
# For the image to find the RabbitMQ running in another container
-e "SPRING_RABBITMQ_ADDRESSES=${APP_IP}:5672" \
# We need to tell the container what messaging middleware we will use
-e "MESSAGING_TYPE=${MESSAGING_TYPE}" \
-e "PUBLISH_STUBS_TO_SCM=false" \
-e "PUBLISH_ARTIFACTS=false" \
-e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" \
-e "PROJECT_NAME=${PROJECT_NAME}" \
-e "PROJECT_GROUP=${PROJECT_GROUP}" \
-e "PROJECT_VERSION=${PROJECT_VERSION}" \
-e "EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL=git://https://github.com/marcingrzejszczak/cdct_python_contracts.git" \
-e "EXTERNAL_CONTRACTS_ARTIFACT_ID=${PROJECT_NAME}" \
-e "EXTERNAL_CONTRACTS_GROUP_ID=${PROJECT_GROUP}" \
-e "EXTERNAL_CONTRACTS_VERSION=${PROJECT_VERSION}" \
-v "${CURRENT_DIR}/build/spring-cloud-contract/output:/spring-cloud-contract-output/" \
springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"
kill $APP_PID
yes | docker-compose kill
What will happen is:
-
Tests will be generated from contracts taken from Git
-
In the contract we’ve provided an entry in metadata called declareQueueWithName
that will lead to creation of a queue in RabbitMQ with the given name before the request to trigger the message is sent
-
Via the triggerMessage("ping_pong")
method call a POST request to the Python application to the /springcloudcontract/ping_pong
endpoint will be made
-
The Python application will generate and send a '{"message":"pong"}'
JSON via RabbitMQ to an exchange called output
-
The generated test will poll for a message sent to the output
exchange
-
Once the message was received will assert its contents
After the tests have passed we know that the message was properly sent from the Python app to RabbitMQ.
Running Stubs on the Consumer Side
This section describes how to use Docker on the consumer side to fetch and run stubs.
We publish a spring-cloud/spring-cloud-contract-stub-runner
Docker image
that starts the standalone version of Stub Runner.
Security
Since the Spring Cloud Contract Stub Runner Docker Image uses the standalone version of Stub Runner the same security considerations need to be taken. You can read more about those in this section of the documentation.
Environment Variables
You can run the docker image and pass any of the common properties for JUnit and Spring
as environment variables. The convention is that all the
letters should be upper case.
The dot (.
) should be replaced with underscore () characters. For example,
the _
stubrunner.repositoryRoot
property should be represented
as a STUBRUNNER_REPOSITORY_ROOT
environment variable.
In addition to those variables you can set the following ones:
-
MESSAGING_TYPE
- what type of messaging system are you using (currently supported are rabbit
, kafka
)
-
ADDITIONAL_OPTS
- any additional properties that you would like to pass to the application
Example of Usage
We want to use the stubs created in this [docker-server-side] step.
Assume that we want to run the stubs on port 9876
. You can see the NodeJS code
by cloning the repository and changing to the directory indicated in the following commands:
$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore
Now we can run the Stub Runner Boot application with the stubs, by running the following
commands:
# Provide the Spring Cloud Contract Docker version
$ SC_CONTRACT_DOCKER_VERSION="..."
# The IP at which the app is running and Docker container can reach it
$ APP_IP="192.168.0.100"
# Spring Cloud Contract Stub Runner properties
$ STUBRUNNER_PORT="8083"
# Stub coordinates 'groupId:artifactId:version:classifier:port'
$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
# Run the docker with Stub Runner Boot
$ docker run --rm \
-e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" \
-e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" \
-e "STUBRUNNER_STUBS_MODE=REMOTE" \
-p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" \
-p "9876:9876" \
springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"
When the preceding commands run,
-
A standalone Stub Runner application gets started.
-
It downloads the stub with coordinates com.example:bookstore:0.0.1.RELEASE:stubs
on port 9876
.
-
It gets downloads from Artifactory running at 192.168.0.100:8081/artifactory/libs-release-local
.
-
After a while, Stub Runner is running on port 8083
.
-
The stubs are running at port 9876
.
On the server side, we built a stateful stub. We can use curl to assert
that the stubs are setup properly. To do so, run the following commands:
# let's run the first request (no response is returned)
$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
# Now time for the second request
$ curl -X GET http://localhost:9876/api/books
# You will receive contents of the JSON
If you want use the stubs that you have built locally, on your host,
you should set the -e STUBRUNNER_STUBS_MODE=LOCAL
environment variable and mount
the volume of your local m2 (-v "${HOME}/.m2/:/home/scc/.m2:rw"
).
Example of Usage with Messaging
In order to make messaging work it’s enough to pass the MESSAGING_TYPE
environment variable with kafka
or rabbit
values. This will lead to setting up
the Stub Runner Boot Docker image with dependencies required to connect to the broker.
In order to set the connection properties you can check out Spring Cloud Stream properties page to set proper environment variables.
-
Spring Boot Integration properties
-
You can search for spring.rabbitmq.xxx
or spring.kafka.xxx
properties
-
-
The most common property you would set is the location of the running middlewara.
If a property to set it is called spring.rabbitmq.addresses
or spring.kafka.bootstrap-servers
then you should name the environment variable SPRING_RABBITMQ_ADDRESSES
and SPRING_KAFKA_BOOTSTRAP_SERVERS
respectively.
Running Contract Tests against Existing Middleware
There is legitimate reason to run your contract tests against existing middleware. Some
testing frameworks might give you false positive results - the test within your build
passes whereas on production the communication fails.
In Spring Cloud Contract docker images we give an option to connect to existing middleware.
As presented in previous subsections we do support Kafka and RabbitMQ out of the box. However,
via Apache Camel Components we can support
other middleware too. Let’s take a look at the following examples of usage.
Spring Cloud Contract Docker and running Middleware
In order to connect to arbitrary middleware, we’ll leverage the standalone
metadata entry
in the contract section.
description: 'Send a pong message in response to a ping message'
label: 'standalone_ping_pong' (1)
input:
triggeredBy: 'triggerMessage("ping_pong")' (2)
outputMessage:
sentTo: 'rabbitmq:output' (3)
body: (4)
message: 'pong'
metadata:
standalone: (5)
setup: (6)
options: rabbitmq:output?queue=output&routingKey=(7)
outputMessage: (8)
additionalOptions: routingKey=#&queue=output (9)
1
Label by which we’ll be able to trigger the message via Stub Runner
2
As in the previous messaging examples we’ll need to trigger the HTTP endpoint in the running application to make it send a message according to the provided protocol
3
protocol:destination
as requested by Apache Camel
4
Output message body
5
Standalone metadata entry
6
Setup part will contain information about how to prepare for running contract tests before the actual call to HTTP endpoint of the running application is made
7
Apache Camel URI to be called in the setup phase. In this case we will try to poll for a message at the output
exchange and due to to having the queue=output
and routingKey=
a queue with name output
will be set and bound to the output
exchange with routing key
8
Additional options (more technical ones) to be appended to the protocol:destination
from point (3) - together will be combined in the following format rabbitmq:output?routingKey=#&queue=output
.
For the contract tests to pass we will need as usual in case of messaging in polyglot environment
a running application and running middleware. This time we will have different environment variables set for the Spring Cloud Contract Docker image.
#!/bin/bash
set -x
# Setup
# Run the middleware
docker-compose up -d rabbitmq (1)
# Run the python application
gunicorn -w 4 --bind 0.0.0.0 main:app & (2)
APP_PID=$!
docker run --rm \
--name verifier \
-e "STANDALONE_PROTOCOL=rabbitmq" \ (3)
-e "CAMEL_COMPONENT_RABBITMQ_ADDRESSES=172.18.0.1:5672" \ (4)
-e "PUBLISH_STUBS_TO_SCM=false" \
-e "PUBLISH_ARTIFACTS=false" \
-e "APPLICATION_BASE_URL=172.18.0.1" \
-e "PROJECT_NAME=application" \
-e "PROJECT_GROUP=group" \
-e "EXTERNAL_CONTRACTS_ARTIFACT_ID=application" \
-e "EXTERNAL_CONTRACTS_GROUP_ID=group" \
-e "EXTERNAL_CONTRACTS_VERSION=0.0.1-SNAPSHOT" \
-v "${CURRENT_DIR}/build/spring-cloud-contract/output:/spring-cloud-contract-output/" \
springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"
# Teardown
kill $APP_PID
yes | docker-compose kill
1
We need to have the middleware running first
2
The application needs to be up and running
3
Via the STANDALONE_PROTOCOL
environment variable we will fetch a Apache Camel Component. The artifact that we will fetch is org.apache.camel.springboot:camel-${STANDALONE_PROTOCOL}-starter
. In other words STANDALONE_PROTOCOL
is matching Camel’s component.
4
We’re setting addresses (we could be setting credentials) via Camel’s Spring Boot Starter mechanisms. Example for Apache Camel’s RabbitMQ Spring Boot Auto-Configuration
Stub Runner Docker and running Middleware
In order to trigger a stub message against running middleware, we can run Stub Runner Docker image in the following manner.
Example of usage
$ docker run \
-e "CAMEL_COMPONENT_RABBITMQ_ADDRESSES=172.18.0.1:5672" \ (1)
-e "STUBRUNNER_IDS=group:application:0.0.1-SNAPSHOT" \ (2)
-e "STUBRUNNER_REPOSITORY_ROOT=git://https://github.com/marcingrzejszczak/cdct_python_contracts.git" \ (3)
-e ADDITIONAL_OPTS="--thin.properties.dependencies.rabbitmq=org.apache.camel.springboot:camel-rabbitmq-starter:3.4.0" \ (4)
-e "STUBRUNNER_STUBS_MODE=REMOTE" \ (5)
-v "${HOME}/.m2/:/home/scc/.m2:rw" \ (6)
-p 8750:8750 \ (7)
springcloud/spring-cloud-contract-stub-runner:3.0.4-SNAPSHOT (8)
1
We’re injecting the address of RabbitMQ via Apache Camel’s Spring Boot Auto-Configuration
2
We’re telling Stub Runner which stubs to download
3
We’re providing an external location for our stubs (Git repository)
4
Via the ADDITIONAL_OPTS=--thin.properties.dependencies.XXX=GROUP:ARTIFACT:VERSION
property we’re telling Stub Runner which additional dependency to fetch at runtime. In this case we want to fetch camel-rabbitmq-starter
so XXX
is a random string and we want to fetch org.apache.camel.springboot:camel-rabbitmq-starter
artifact in version 3.4.0
.
5
Since we’re using Git, the remote option of fetching stubs needs to be set
6
So that we speed up launching of Stub Runner, we’re attaching our local Maven repository .m2
as a volume. If you don’t have it populated you can consider setting the write permissions via :rw
instead read only :ro
.
7
We expose the port 8750
at which Stub Runner is running.
8
Coordinates of the Stub Runner Docker image.
After a while you’ll notice the following text in your console, which means that Stub Runner is ready to accept requests.
o.a.c.impl.engine.AbstractCamelContext : Apache Camel 3.4.3 (camel-1) started in 0.007 seconds
o.s.c.c.s.server.StubRunnerBoot : Started StubRunnerBoot in 14.483 seconds (JVM running for 18.666)
o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
To get the list of triggers you can send an HTTP GET request to localhost:8750/triggers
endpoint. To trigger a stub message, you can send a HTTP POST request to localhost:8750/triggers/standalone_ping_pong
. In the console you’ll see:
o.s.c.c.v.m.camel.CamelStubMessages : Will send a message to URI [rabbitmq:output?routingKey=#&queue=output]
If you check the RabbitMQ management console, you’ll see that there’s 1 message available in the output
queue.