自分が担当している Azure Functions kafka Extensionだが、最近 Pull Request がおおいので、C#以外の言語のE2Eテストを書きたくなってきた。大抵の人は、C#のところだけみて、いろいろ変えるわけだけど、自分からすると、ちゃんと他言語でも動くんかいな?というのを毎回テストするのはめんどくさいので、仕組みを作ることにした。
現在のパッケージをビルドして、他言語のパッケージに読み込ませる時の課題
このプロジェクトは、もともとの Extension が C# で書かれている。そこで、作成された NuGet パッケージを使って、ほかの言語が動作するという感じなので次のことが必要だ。
- Linux 上で、現在のソースから パッケージを作る。
- Java, Python 等のパッケージで使っている csproj ファイルにそのパッケージを読み込ませる
- サーバーを起動して、テストを行う
一見なんでもなさそうだがいくつかの考慮事項がある
- ビルドするバージョンをどうするか?現在のプロジェクトのバージョンで素直にパッケージすると、現在のバージョンでパッケージされる。そうなると、NuGetソースから現在のバージョンがPublishされている場合、そちらを参照される可能性があるので、FalsePositveが発生しやすそう
- Java, Python のパッケージ側から、読み込ませるバージョンを何にするか?バージョンが上がるたびに手動で変更するのはだるいので変更しなくていいようにしたい。
- パッケージをどのように読み込ませるか?csproj ではファイル指定でパッケージの読み込みはできなさそう
結局どうしたかというと、100.100.100
などという、まぁ、そこまでアップデートしないだろう。ぐらいのバージョンを設定しておいて、ソースコードもそうしておくという単純な解決策にしておいた。もっと良いのがありそうだけど。
C# のパッケージのビルド
dotnet が既にインストールされていると想定すると、私はWSL2上で次のようなコマンドを書いて実行した。
重要なポイントは dotnet pack -o temp --include-symbols src/Microsoft.Azure.WebJobs.Extensions.Kafka/Microsoft.Azure.WebJobs.Extensions.Kafka.csproj /p:Version=100.100.100
パッケージを 100.100.100
というバージョンに指定して、pack
を実行し、temp
ディレクトリに出力している。その後、各言語用のディレクトリにコピーしている。それぞれの言語のディレクトリは Docker
にパックされるように構成している。
# !/bin/bash
CURRENT_DIR=`pwd`
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd $DIR/..
WORKING_DIR=temp
if [ -d "$WORKING_DIR" ]; then rm -rf $WORKING_DIR; fi
dotnet pack -o temp --include-symbols src/Microsoft.Azure.WebJobs.Extensions.Kafka/Microsoft.Azure.WebJobs.Extensions.Kafka.csproj /p:Version=100.100.100
PACKAGE_DIR=test/Microsoft.Azure.WebJobs.Extensions.Kafka.LangEndToEndTests/server/java8/packages
if [ -d "$PACKAGE_DIR" ]; then rm -rf $PACKAGE_DIR; fi
mkdir $PACKAGE_DIR
cp temp/* $PACKAGE_DIR
cd $CURRENT_DIR
NuGet Source の指定
NuGetのファイルを読み込ませるためには、どうしたらいいだろう?と考えて、Local Source
を設定して、読ませるという方法にした。現在は dotnet nuget add source
というコマンドがあったので使ってみた。古い情報だと、mono をインストールして、nuget.exe を使うとかあったが、今は不要っぽい。
RUN dotnet nuget add source /workspace/packages/
ちなみに、このコマンドを発行すると、デフォルトの NuGet.Config
が次のように更新される。最初は、バージョン固定の方法と、nuget source の読み込みの優先順位を変えて、必ずLocalが先に読ませるようにするとも考えたのだが、それだとやっぱりFalsePositiveが気になるので、バージョンの固定にした。
# pwd
/root/.nuget/NuGet
# cat NuGet.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="Package source 1" value="/workspace/packages/" />
</packageSources>
Java, Python 側の csproj ファイル
という仕様にしたので、Java, Python 側の csproj ファイルは、バージョンだけ、100.100.100.100
になったものになっている。これで自動的に現在のソースコードをビルドしたものが組み込まれるようになっている。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<WarningsAsErrors></WarningsAsErrors>
<DefaultItemExcludes>**</DefaultItemExcludes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Kafka" Version="100.100.100" />
<PackageReference Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.1.7" />
</ItemGroup>
<ItemGroup>
<None Update="confluent_cloud_cacert.pem">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Dockerfile
最終的な Dockerfile は次の通りになっている。簡単でござった。
FROM mcr.microsoft.com/azure-functions/java:3.0-java8-core-tools
# Copy Local File
RUN mkdir /workspace
COPY . /workspace
WORKDIR /workspace
# dotnet
ENV LD_LIBRARY_PATH=/workspace/target/azure-functions/kafka-function-20190419163130420/bin/runtimes/linux-x64/native
RUN dotnet nuget add source /workspace/packages/
RUN mvn clean package
ENTRYPOINT [ "/usr/bin/mvn", "azure-functions:run" ]
まとめ
C#の達人だったらもっといい方法思いつきそう。もしあったら是非教えてください。
この後、docker-compose で、Kafka サーバーと一緒に起動して動作するところまでいけたので、後は、E2E のフレームワークをC#で書いて、Azure DevOps に組み込んだら終了でござる。
補足 11/9/2020
しばやんさんからいいアドバイスをもらったので、パッケージリファレンスで実施することにしました。もうLocalNuget作らなくていいので、こちらの方がよさそうです。Dockerにパックするので、PackageReferenceは使えないと思い込んでいましたが、単純で、docker build のコンテキストをプロジェクトルートにしてしまえばよいです。ただ、コマンドのいい学びになったのでエントリは残しておきます。
extension.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<WarningsAsErrors></WarningsAsErrors>
<DefaultItemExcludes>**</DefaultItemExcludes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.1.7" />
</ItemGroup>
<ItemGroup>
<None Update="confluent_cloud_cacert.pem">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Azure.WebJobs.Extensions.Kafka\Microsoft.Azure.WebJobs.Extensions.Kafka.csproj" />
</ItemGroup>
</Project>
Dockerfile
FROM mcr.microsoft.com/azure-functions/java:3.0-java8-core-tools
# Copy Local File
RUN mkdir /workspace
COPY . /workspace
WORKDIR /workspace/test/Microsoft.Azure.WebJobs.Extensions.Kafka.LangEndToEndTests/server/java8
# dotnet
ENV LD_LIBRARY_PATH=/workspace/target/azure-functions/kafka-function-20190419163130420/bin/runtimes/linux-x64/native
RUN mvn clean package
ENTRYPOINT [ "/usr/bin/mvn", "azure-functions:run" ]
docker build -t testj8 -f test/Microsoft.Azure.WebJobs.Extensions.Kafka.LangEndToEndTests/server/java8/Dockerfile .