Writing a Dev Service

前提条件

  • すでに エクステンション構造 が準備されている。

  • 外部サービスのコンテナー化されたバージョンがある (すべての Dev Service がコンテナーに依存しているわけではありませんが、ほとんどは依存しています)。

Dev Service の作成

エクステンションが外部サービスに接続するための API を提供する場合は、Dev Service 実装を提供することを推奨します。

Dev Service を作成するには、 DevServicesResultBuildItem を返す新しいビルドステップをエクステンションプロセッサークラスに追加します。 ここでは、 hello-world イメージが使用されていますが、サービスに適切なイメージをセットアップする必要があります。

    @BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
    public DevServicesResultBuildItem createContainer() {
        DockerImageName dockerImageName = DockerImageName.parse("hello-world");
        GenericContainer container = new GenericContainer<>(dockerImageName)
                                         .withExposedPorts(SERVICE_PORT, OTHER_SERVICE_PORT)
                                         .waitingFor(Wait.forLogMessage(".*" + "Started" + ".*", 1))
                                         .withReuse(true);

        container.start();

        String newUrl = "http://" + container.getHost() + ":" + container.getMappedPort(SERVICE_PORT);
        Map<String, String> configOverrides = Map.of("some-service.base-url", newUrl);

        return new DevServicesResultBuildItem.RunningDevService(FEATURE, container.getContainerId(),
            container::close, configOverrides)
            .toBuildItem();
    }

このコードを使用すると、エクステンションをテストアプリケーションに追加して quarkus dev を実行すると、コンテナーの起動を確認できるはずです。 ただし、ポートが公開されていないため、アプリケーションは接続できません。ポートを公開するには、コンテナー構造に withExposedPorts を追加します。 以下に例を示します。

GenericContainer container = new GenericContainer<>(dockerImageName)
                                 .withExposedPorts(SERVICE_PORT, OTHER_SERVICE_PORT);

Testcontainers は、これらのポートをホスト上のランダムなポートにマッピングします。これによりポートの競合は回避されますが、アプリケーションはコンテナー内のサービスにどのように接続するのか、という新たな問題が発生します。

アプリケーションが接続できるようにするには、エクステンションはマッピングされたポートを使用してサービスのデフォルト設定をオーバーライドする必要があります。 これはコンテナーを起動した後に実行する必要があります。 以下に例を示します。

    container.start();
    Map<String, String> configOverrides = Map.of("some-service.base-url",
         "http://" + container.getHost() + ":" + container.getMappedPort(SERVICE_PORT));

他の設定のオーバーライドも同じマップに含めることができます。

コンテナーの起動を待機する

コンテナーが起動するまで待つには、コンテナー構築に .waitingFor 呼び出しを追加する必要があります。以下に例を示します。

            .waitingFor(Wait.forLogMessage(".*" + "Started" + ".*", 1))

別のオプションとして、ポートが開くのを待つことが挙げられます。待機ストラテジーの詳細は、Testcontainers のドキュメント を参照してください。

Dev Service の設定

Dev Service の起動プロセスを設定するには、ビルドステップのコンストラクターで ConfigPhase.BUILD_TIME 設定クラスを受け入れます。 以下に例を示します。

    @BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
    public DevServicesResultBuildItem createContainer(MyConfig config) {

たとえば、この設定を使用して固定ポートを設定したり、イメージ名を設定したりすることができます。

    if (config.port.isPresent()) {
        container.setPortBindings(List.of(config.port.get() + ":" + SERVICE_PORT));
    }

再利用の制御

開発モードでは、ライブリロードにより、Quarkus が頻繁に再起動する場合があります。デフォルトでは、これによりテストコンテナーも再起動されます。 Quarkus の再起動は通常非常に高速ですが、コンテナーの再起動にはさらに長い時間がかかる場合があります。 コードを変更するたびにコンテナーが再起動するのを防ぐには、コンテナーを再利用可能としてマークします。

         .withReuse(true)

一部の Dev Service は、プロセッサー自体のコンテナーの状態を追跡する高度な再利用ロジックを実装しています。 サービスに複雑な要件がある場合や、インスタンス間で共有する必要がある場合は、これが必要になることがあります。

関連コンテンツ