Docker プロジェクト

このセクションでは、テストを生成し、実行中のアプリケーションに対して EXPLICIT モードでテストを実行するプロジェクトを含む springcloud/spring-cloud-contract Docker イメージを公開します。

EXPLICIT モードは、契約から生成されたテストがモックリクエストではなく実際のリクエストを送信することを意味します。

また、Stub Runner のスタンドアロンバージョンを起動する spring-cloud/spring-cloud-contract-stub-runner Docker イメージも公開します。

Maven、JAR、バイナリストレージの簡単な導入

非 JVM プロジェクトでは Docker イメージを使用できるため、Spring Cloud Contract パッケージ化のデフォルトの背後にある基本用語を説明しておくとよいでしょう。

次の定義の一部は Maven 用語集 [Apache] (英語) から取得されています。

  • Project: Maven はプロジェクトという観点から考えます。プロジェクトはすべて構築するものです。これらのプロジェクトは、明確に定義された「プロジェクトオブジェクトモデル」に従います。プロジェクトは他のプロジェクトに依存することができます。この場合、後者は「依存関係」と呼ばれます。プロジェクトは複数のサブプロジェクトから構成される場合があります。ただし、これらのサブプロジェクトは引き続きプロジェクトとして同等に扱われます。

  • Artifact: アーティファクトは、プロジェクトによって生成または使用されるものです。プロジェクト用に Maven によって生成されるアーティファクトの例には、JAR ファイル、ソースおよびバイナリの配布が含まれます。各成果物は、グループ ID とグループ内で一意の成果物 ID によって一意に識別されます。

  • JAR: JAR は Java アーカイブの略です。その形式は ZIP ファイル形式に基づいています。Spring Cloud Contract は、契約と生成されたスタブを JAR ファイルにパッケージ化します。

  • 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 の場合、それらのアーティファクトは gems です。Docker の場合、それらのアーティファクトは Docker イメージです。これらのアーティファクトはマネージャーに保存できます。このようなマネージャーの例には、アーティファクトリ (英語) およびネクサス (英語) が含まれます。

プロデューサー側でのテストの生成

このイメージは、/contracts フォルダーで契約を検索します。テストの実行からの出力は、/spring-cloud-contract/build フォルダーで入手できます (デバッグの目的で役立ちます)。

契約をマウントして環境変数を渡すことができます。イメージは次のようになります。

  • 契約テストを生成します

  • 指定された URL に対してテストを実行します

  • WireMock [GitHub] (英語) スタブを生成します

  • スタブをアーティファクトマネージャーに公開します (オプション — デフォルトでオンになっている)

環境変数

Docker イメージには、実行中のアプリケーションやアーティファクトマネージャーインスタンスなどを指すいくつかの環境変数が必要です。次のリストでは、環境変数について説明します。

表 1: Docker 環境変数

名前

説明

デフォルト

ADDITIONAL_FLAGS

(Docker イメージのみ) Gradle ビルドに渡される追加のフラグ

DEBUG

(Docker イメージのみ) Docker イメージに適用 - Gradle ビルドのデバッグモードをオンにします

false

EXTERNAL_CONTRACTS_ARTIFACT_ID

契約のあるプロジェクトのアーティファクト ID

EXTERNAL_CONTRACTS_CLASSIFIER

契約のあるプロジェクトの分類子

EXTERNAL_CONTRACTS_GROUP_ID

契約のあるプロジェクトのグループ ID

com.example

EXTERNAL_CONTRACTS_PATH

契約のあるプロジェクト内の、指定されたプロジェクトの契約へのパス。デフォルトでは、スラッシュで区切られた EXTERNAL_CONTRACTS_GROUP_ID と / および EXTERNAL_CONTRACTS_ARTIFACT_ID. For example, for group id `cat-server-side.dog およびアーティファクト ID fish が連結され、契約 パスは cat/dog/fish になります。

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_PASSWORD

(オプション) EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL が認証を必要とする場合のパスワード。デフォルトは `REPO_WITH_BINARIES_PASSWORD, If that is not set, it defaults to ` パスワードです

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL

Artifact Manager の URL。デフォルトは REPO_WITH_BINARIES_URL 環境変数の値で、それが設定されていない場合はデフォルトで localhost:8081/artifactory/libs-release-local になります。

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_USERNAME

(オプション) EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL が認証を必要とする場合のユーザー名。デフォルトは REPO_WITH_BINARIES_USERNAME です。これが設定されていない場合、デフォルトは "admin" になります。

EXTERNAL_CONTRACTS_VERSION

契約のあるプロジェクトのバージョン。デフォルトでは最新のものを選択するのと同等になります

+

EXTERNAL_CONTRACTS_WORK_OFFLINE

true に設定すると、コンテナーの .m2 から契約を含むアーティファクトを取得します。ローカルの .m2 を、コンテナーの /root/.m2 パスで利用可能なボリュームとしてマウントします。

false

FAIL_ON_NO_CONTRACTS

契約が存在しない場合、ビルドは失敗するでしょうか ?

false

MESSAGING_TYPE

メッセージングの型。[ ウサギ ] または [ カフカ ] のいずれかになります。

PRODUCER_STUBS_CLASSIFIER

生成されたプロデューサースタブに使用されるアーカイブ分類子

スタブ

PROJECT_GROUP

プロジェクトのグループ ID

com.example

PROJECT_NAME

プロジェクトのアーティファクト ID

PROJECT_VERSION

プロジェクトのバージョン

0.0.1-SNAPSHOT

PUBLISH_ARTIFACTS

true に設定すると、アーティファクトをバイナリストレージに公開します

true

PUBLISH_ARTIFACTS_OFFLINE

true に設定すると、アーティファクトがローカル m2 に公開されます。

false

PUBLISH_STUBS_TO_SCM

true に設定すると、スタブを scm に公開するタスクが実行されます。

false

REPO_ALLOW_INSECURE_PROTOCOL

(オプション) <true> の場合、安全でない HTTP 経由で Artifact Manager にアーティファクトを公開することが許可されます。

false

REPO_WITH_BINARIES_PASSWORD

(オプション) Artifact Manager が保護されている場合のパスワード

パスワード

REPO_WITH_BINARIES_URL

Artifact Manager の URL (ローカルで実行する場合、デフォルトはアーティファクトリ (英語) のデフォルト URL になります。)

localhost:8081/artifactory/libs-release-local

REPO_WITH_BINARIES_USERNAME

(オプション) Artifact Manager が保護されている場合のユーザー名

admin

STANDALONE_PROTOCOL

スタンドアロンバージョンの場合、どのプロトコルを追加する必要がありますか

テストの実行時に次の環境変数が使用されます。

表 2: Docker 環境変数 - 実行時に読み取ります

名前

説明

デフォルト

APPLICATION_BASE_URL

アプリケーションが実行されている URL。

APPLICATION_PASSWORD

アプリケーションにアクセスするためのオプションのパスワード。

APPLICATION_USERNAME

アプリケーションにアクセスするためのオプションのユーザー名。

MESSAGING_TRIGGER_CONNECT_TIMEOUT

メッセージをトリガーするためにアプリケーションに接続するためのタイムアウト。

5000

MESSAGING_TRIGGER_READ_TIMEOUT

メッセージをトリガーするためにアプリケーションからのレスポンスを読み取るタイムアウト。

5000

MESSAGING_TYPE

メッセージベースの契約を処理する場合のメッセージングの型を定義します。

MESSAGING_TYPE

メッセージングの型。[ ウサギ ] または [ カフカ ] のいずれかになります。

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 の恩恵を受ける方法を示したいと思います。

  • これらの制約により、契約はステートフルな状況も表します。

    • 最初のリクエストは POST で、データをデータベースに挿入します。

    • 2 番目のリクエストは、以前に挿入された 1 つの要素を含むデータのリストを返す GET です。

  • NodeJS アプリケーションが開始されます (ポート 3000 上)。

  • 契約 テストは Docker を通じて生成され、実行中のアプリケーションに対してテストが実行されます。

    • 契約は /contracts フォルダーから取得されます。

    • テストの出力は node_modules/spring-cloud-contract/output で入手できます。

  • スタブは Artifactory にアップロードされます。localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1. リリース / で見つけることができます。スタブは localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/ ブックストア -0.0.1.RELEASE- スタブ .jar にあります。

メッセージングでの利用例

Docker イメージを介したメッセージングで Spring Cloud Contract を使用する場合 (多言語アプリケーションの場合など)、次の前提条件を満たす必要があります。

  • テストを生成する前にミドルウェア (RabbitMQ や Kafka など) が実行されている必要があります

  • 契約は、契約の label と等しい String パラメーターを指定してメソッド triggerMessage(…​) を呼び出す必要があります。

  • アプリケーションには、メッセージをトリガーできる HTTP エンドポイントが必要です

    • そのエンドポイントは本番環境では使用できないはずです (環境変数を介して有効にすることができます)

メッセージング契約の例

契約では triggerMessage(…​) メソッドを呼び出す必要があります。このメソッドは、docker イメージ内のすべてのテストの基本クラスですでに提供されており、プロデューサー側の HTTP エンドポイントにリクエストを送信します。以下にそのような契約の例を示します。

  • Groovy

  • YAML

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: '#'
                ]
           ]
         ]
        ])
}
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 エンドポイント

なぜそのようなエンドポイントを開発する必要があるのでしょうか ? Spring Cloud Contract は、ブローカーにメッセージを送信する本番コードをトリガーできるように、さまざまな言語でコードを生成する必要があります (Java の場合と同様)。そのようなコードが生成されない場合は、メッセージをトリガーできる必要があります。その方法は、ユーザーが選択した言語で準備する HTTP エンドポイントを提供することです。

エンドポイントは次の構成である必要があります。

  • URL: /springcloudcontract/{label} (label は任意のテキスト)

  • メソッド: POST

  • label に基づいて、契約定義に従って指定された宛先に送信されるメッセージを生成します

以下にそのようなエンドポイントの例を示します。あなたの言語で例を提供することに興味がある場合は、遠慮せずに Github の Spring Cloud Contract リポジトリ (英語) に課題を提出してください。

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.')

プロデューサー側でのメッセージテストの実行

次に、契約からテストを生成して、プロデューサー側をテストしましょう。bash コードを実行して、契約が添付された Docker イメージを開始しますが、メッセージングコードが機能するための変数も追加します。この場合、契約が Git リポジトリに保存されていると仮定します。

#!/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

何が起こるかというと、次のとおりです。

  • テストは Git から取得した契約から生成されます

  • 契約では、メッセージをトリガーするリクエストが送信される前に、指定された名前で RabbitMQ にキューを作成する declareQueueWithName というメタデータのエントリを提供しました。

  • triggerMessage("ping_pong") メソッド呼び出しを介して、Python アプリケーションから /springcloudcontract/ping_pong エンドポイントへの POST リクエストが行われます。

  • Python アプリケーションは '{"message":"pong"}' JSON を生成し、RabbitMQ 経由で output という交換局に送信します。

  • 生成されたテストは、output 交換に送信されるメッセージをポーリングします。

  • メッセージが受信されると、その内容がアサートされます

テストに合格すると、メッセージが Python アプリから RabbitMQ に適切に送信されたことがわかります。

コンシューマー側でのスタブの実行

このセクションでは、コンシューマー側で Docker を使用してスタブをフェッチして実行する方法について説明します。

Stub Runner のスタンドアロンバージョンを起動する spring-cloud/spring-cloud-contract-stub-runner Docker イメージを公開します。

セキュリティ

Spring Cloud Contract Stub Runner Docker Image は Stub Runner のスタンドアロンバージョンを使用するため、同じセキュリティを考慮する必要があります。これらについて詳しくは、ドキュメントのこのセクションを参照してください。

環境変数

docker イメージを実行し、JUnit と Spring の共通プロパティのいずれかを環境変数として渡すことができます。慣例として、すべての文字は大文字にする必要があります。ドット (.) はアンダースコア (_) 文字に置き換える必要があります。例: stubrunner.repositoryRoot プロパティは STUBRUNNER_REPOSITORY_ROOT 環境変数として表す必要があります。

これらの変数に加えて、次の変数を設定できます。

  • MESSAGING_TYPE - どの型のメッセージングシステムを使用していますか (現在サポートされているのは rabbitkafka です)

  • ADDITIONAL_OPTS - アプリケーションに渡したい追加のプロパティ

使用例

この [ ドッカーサーバー側 ] ステップで作成されたスタブを使用したいと考えています。ポート 9876 でスタブを実行するとします。リポジトリを複製し、次のコマンドで示されているディレクトリに変更すると、NodeJS コードを確認できます。

$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore

これで、次のコマンドを実行して、スタブを使用して Stub Runner Boot アプリケーションを実行できるようになります。

# 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}"

前述のコマンドを実行する

  • スタンドアロンの Stub Runner アプリケーションが開始されます。

  • 座標 com.example:bookstore:0.0.1.RELEASE:stubs のスタブをポート 9876 にダウンロードします。

  • 192.168.0.100:8081/artifactory/libs-release-local (英語) で実行されている Artifactory からダウンロードを取得します。

  • しばらくすると、Stub Runner がポート 8083 で実行されます。

  • スタブはポート 9876 で実行されています。

サーバー側では、ステートフルスタブを構築しました。curl を使用すると、スタブが正しく設定されていることを確認できます。これを行うには、次のコマンドを実行します。

# 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
ホスト上でローカルに構築したスタブを使用したい場合は、-e STUBRUNNER_STUBS_MODE=LOCAL 環境変数を設定し、ローカル m2 (-v "${HOME}/.m2/:/home/scc/.m2:ro") のボリュームをマウントする必要があります。

メッセージングでの使用例

メッセージングを機能させるには、kafka または rabbit 値を含む MESSAGING_TYPE 環境変数を渡すだけで十分です。これにより、ブローカーへの接続に必要な依存関係を含むスタブランナー Boot Docker イメージがセットアップされます。

接続プロパティを設定するには、Spring Cloud Stream プロパティページをチェックして、適切な環境変数を設定します。

設定する最も一般的なプロパティは、実行中のミドルワラの場所です。設定するプロパティの名前が spring.rabbitmq.addresses または spring.kafka.bootstrap-servers の場合は、環境変数にそれぞれ SPRING_RABBITMQ_ADDRESSES および SPRING_KAFKA_BOOTSTRAP_SERVERS という名前を付ける必要があります。

既存のミドルウェアに対する契約テストの実行

既存のミドルウェアに対して契約テストを実行する正当な理由があります。一部のテストフレームワークでは、誤検知の結果が得られる場合があります。つまり、ビルド内のテストは合格しますが、運用環境では通信が失敗します。

Spring Cloud Contract docker イメージでは、既存のミドルウェアに接続するオプションが提供されています。前のサブセクションで説明したように、Kafka と RabbitMQ はすぐにサポートされます。ただし、Apache Camel コンポーネント (英語) を介して他のミドルウェアもサポートできます。以下の使用例を見てみましょう。

Spring Cloud Contract Docker および実行中のミドルウェア

任意のミドルウェアに接続するために、契約 セクションの standalone メタデータエントリを利用します。

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 スタブランナー経由でメッセージをトリガーできるラベル
2 前のメッセージングの例と同様に、実行中のアプリケーションで HTTP エンドポイントをトリガーして、指定されたプロトコルに従ってメッセージを送信させる必要があります。
3 Apache Camel からのリクエストに応じた protocol:destination 
4 メッセージ本文を出力する
5 スタンドアロンのメタデータエントリ
6 セットアップ部分には、実行中のアプリケーションの HTTP エンドポイントへの実際の呼び出しが行われる前に、契約 テストの実行を準備する方法に関する情報が含まれます。
7Apache Camel セットアップフェーズで呼び出される URI。この場合、output 交換機でメッセージのポーリングを試みます。queue=output と routingKey= があるため、output という名前のキューが設定され、ルーティングキー  を持つ output 交換機にバインドされます。
8 ポイント (3) から protocol:destination に追加される追加オプション (より技術的なもの) は、次の形式 rabbitmq:output?routingKey=#&queue=output で結合されます。

契約 テストに合格するには、ポリグロット環境でメッセージングを行う場合と同様に、実行中のアプリケーションと実行中のミドルウェアが必要です。今回は、Spring Cloud Contract Docker イメージに異なる環境変数を設定します。

#!/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 最初にミドルウェアを実行する必要があります
2 アプリケーションが稼働している必要があります
3STANDALONE_PROTOCOL 環境変数を介して Apache Camel コンポーネント (英語) を取得します。取得するアーティファクトは org.apache.camel.springboot:camel-${STANDALONE_PROTOCOL}-starter です。言い換えれば、STANDALONE_PROTOCOL は Camel のコンポーネントと一致します。
4Camel の Spring Boot Starter メカニズムを介してアドレスを設定しています (資格情報を設定している可能性もあります)。Apache Camel の RabbitMQ Spring Boot 自動構成 (英語) の例

Stub Runner Docker と実行中のミドルウェア

実行中のミドルウェアに対してスタブメッセージをトリガーするには、次の方法で Stub Runner Docker イメージを実行します。

使用例

$ 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:ro" \ (6)
    -p 8750:8750 \ (7)
    springcloud/spring-cloud-contract-stub-runner:3.0.4-SNAPSHOT (8)
1Apache Camel の Spring Boot 自動構成 (英語) 経由で RabbitMQ のアドレスを注入しています
2 どのスタブをダウンロードするかを Stub Runner に指示します
3 スタブの外部の場所を提供しています (Git リポジトリ)
4ADDITIONAL_OPTS=--thin.properties.dependencies.XXX=GROUP:ARTIFACT:VERSION プロパティを介して、実行時に取得する追加の依存関係を Stub Runner に伝えます。この場合、camel-rabbitmq-starter を取得する必要があるため、XXX はランダムな文字列になり、バージョン 3.4.0 の org.apache.camel.springboot:camel-rabbitmq-starter アーティファクトを取得する必要があります。
5Git を使用しているため、スタブを取得する リモートオプションを設定する必要があります
6Stub Runner の起動を高速化するために、ローカル Maven リポジトリ .m2 をボリュームとして接続します。設定されていない場合は、読み取り専用 :ro の代わりに :rw 経由で書き込み権限を設定することを検討できます。
7Stub Runner が実行されているポート 8750 を公開します。
8 スタブランナー Docker イメージのコーディネート。

しばらくすると、コンソールに次のテキストが表示されます。これは、Stub Runner がリクエストを受け入れる準備ができていることを意味します。

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

トリガーのリストを取得するには、HTTP GET リクエストを localhost:8750/triggers エンドポイントに送信します。スタブメッセージをトリガーするには、HTTP POST リクエストを localhost:8750/triggers/standalone_ping_pong に送信します。コンソールには次の内容が表示されます。

o.s.c.c.v.m.camel.CamelStubMessages      : Will send a message to URI [rabbitmq:output?routingKey=#&queue=output]

RabbitMQ 管理コンソールを確認すると、output キューに利用可能なメッセージが 1 つあることがわかります。