Contents
EDCはIDSのリファレンスをそのまま踏襲するものではない。
- https://github.com/eclipse-edc/docs/blob/cc53fe25ea9c89797d2d04ebb3f857c6edcf1152/docs/README.md#statement-edc-vs-dsc
- https://github.com/eclipse-edc/Connector/discussions/1037
- https://github.com/eclipse-edc/Connector/blob/9adb0e4a09f4b0518a903e61890f94229ebda69e/docs/developer/decision-records/2022-06-02-ids-serializer/README.md
JettyとJerseyを使ってREST APIをserve
idscp2は利用/対応しない
IDSコネクタと連携するためのREST APIの口がある。
ServeceLoaderと独自アノテーションを使ったフレームワークを独自実装
org.eclipse.edc.spi.system.ServiceExtensionの実装を、 いろいろ組み合わせてコネクタとして機能するまとまりを形成する。
Extensionは、他のExtension(がregisterするService)に依存する形で作られがち。 モジュール間の依存関係は、build.gradle.ktsに書くが、 どのServiceがどのモジュールのどのExtensionにあるかは自明ではない。 Extension間のdependency hellみたいになりそうな。
ServiceLoaderでロードするための META-INF/services/org.eclipse.edc.spi.system.ServiceExtension が用意されている。コード上のモジュール間のつながりは見えにくい。 build.gradle.ktsを見ると、どこで使われているは分かる。:
$ find . -name build.gradle.kts | xargs grep iam-mock ./extensions/control-plane/api/management-api/catalog-api/build.gradle.kts: testImplementation(project(":extensions:common:iam:iam-mock")) ./system-tests/e2e-transfer-test/control-plane/build.gradle.kts: implementation(project(":extensions:common:iam:iam-mock")) ./system-tests/runtimes/azure-storage-transfer-consumer/build.gradle.kts: implementation(project(":extensions:common:iam:iam-mock")) ...
ソースツリーは最近リファクタリングされた。しかし、上記の課題が解決するわけではない。
extensionの依存関係が循環していると、ロード時にエラーになる。see ExtensionLoader#loadServiceExtensions.
gradleのdependenciesタスクを使うと、依存関係をツリー表示できる。:
$ ./gradlew -p system-tests/e2e-transfer-test/control-plane-postgresql -q dependencies --configuration compileClasspath | grep project +--- project :system-tests:e2e-transfer-test:control-plane +--- project :extensions:control-plane:store:sql:control-plane-sql +--- project :extensions:common:sql:pool:apache-commons-pool-sql | +--- project :spi:common:transaction-datasource-spi | | \--- project :spi:common:core-spi | | \--- project :core:common:policy-evaluator | \--- project :extensions:common:sql:common-sql | \--- project :spi:common:core-spi (*) +--- project :extensions:common:transaction:transaction-local | +--- project :spi:common:core-spi (*) | \--- project :spi:common:transaction-spi
BaseRuntimeクラスが、ServiceExtension実装をロードしていくようなmainクラスを提供している。
- https://github.com/eclipse-edc/Connector/blob/73a6d9b49d164c927031de71c384f239e05f33d4/core/common/boot/src/main/java/org/eclipse/edc/boot/system/runtime/BaseRuntime.java
- https://github.com/eclipse-edc/Connector/blob/73a6d9b49d164c927031de71c384f239e05f33d4/launchers/ids-connector/README.md
- https://github.com/eclipse-edc/Connector/blob/73a6d9b49d164c927031de71c384f239e05f33d4/launchers/ids-connector/build.gradle.kts#L39-L41
#1832 で多少整理された感がある。
どのモジュールがどのSPIを実装してるのかは、モジュールの依存関係から見るのが早いのかな..?:
$ find . -name build.gradle.kts | xargs grep 'api(project(":spi:' ./core/data-plane-selector/data-plane-selector-core/build.gradle.kts: api(project(":spi:data-plane-selector:data-plane-selector-spi")) ./core/data-plane/data-plane-framework/build.gradle.kts: api(project(":spi:common:core-spi")) ./core/data-plane/data-plane-framework/build.gradle.kts: api(project(":spi:data-plane:data-plane-spi")) ./core/data-plane/data-plane-framework/build.gradle.kts: api(project(":spi:control-plane:control-plane-api-client-spi")) ./core/data-plane/data-plane-util/build.gradle.kts: api(project(":spi:data-plane:data-plane-spi")) ...
web.http.{context}.path and web.http.{context}.port のような設定プロパティの組で、ポートとpathの組を指定する。
- https://github.com/eclipse-edc/Connector/blob/9adb0e4a09f4b0518a903e61890f94229ebda69e/extensions/common/http/jetty-core/src/main/java/org/eclipse/edc/web/jetty/JettyConfiguration.java
- 各ポート番号ごとにServerConnectorが作られる。 ここで指定されたcontext名を ServerConnectorの名前 と、 ContextHandlerのバーチャルホスト名に埋め込めるコネクタ名 の両方にセットすることで、connectorとhandlerの紐付けをしている。 JettyServer#registerServlet で、context名をキーとして、servletがhandlerに結び付けられる。
上記のcontext aliasとしてはcontrol、management、protocol、publicがある。
- https://github.com/eclipse-edc/Connector/blob/9adb0e4a09f4b0518a903e61890f94229ebda69e/docs/developer/decision-records/2022-11-09-api-refactoring/renaming.md
- controlはコネクタが内部的に使うもの。
という理解だったが、コネクタ間のやりとりでDataspace Protocol以外のものだと、少ないように見える。
transferのsample実行時に/controlというcontext pathにregisterされたcontrollerは以下。
- TransferProcessControlApiController
- ConsumerPullTransferTokenValidationApiController
- DataPlaneControlApiController
- managementはコネクタのクライアントが呼び出すもの。
- protocolはDataspace Protocol用のもので、Dataspace Protocolへの移行前はidsだった。
- publicはdata planeがデータを送るときに使うもの。
どのAPIがどのport/contextに対応してるいるのか、コードを静的に眺めて判断するのが以外と難しい。 JerseyRestService#start にブレークポイントを仕掛けて、contextとcontrolerとの対応付けをデバッガで見るとわかりやすいか。 Samples/transferのコネクタ でやると、以下のような感じ。:
$ java \ -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=18888 \ -Dedc.keystore=transfer/transfer-00-prerequisites/resources/certs/cert.pfx \ -Dedc.keystore.password=123456 \ -Dedc.vault=transfer/transfer-00-prerequisites/resources/configuration/provider-vault.properties \ -Dedc.fs.config=transfer/transfer-00-prerequisites/resources/configuration/provider-configuration.properties \ -jar \ transfer/transfer-00-prerequisites/connector/build/libs/connector.jar
web.http.path and web.http.port は、defaultコンテキストに対応づけられる。 controlとmanagementは固有の指定( web.http.control.path や web.http.management.path )がない場合、defaultを使う。 ( useDefaultContext(true) されている。)
Swaggerのアノテーションを利用して、.yamlなどを生成している。
- https://github.com/eclipse-edc/Connector/blob/9adb0e4a09f4b0518a903e61890f94229ebda69e/docs/developer/decision-records/2022-03-15-swagger-annotations/README.md
- resolveタスクを実行すると、.yamlファイルが生成される。
- connector同士がやりとりするためのIDSのAPIは、Swaggerによるドキュメント生成の対象外になっている。 eclipse-edc/Connector#1563
OpenAPIで生成したドキュメントはSwagger Hubでホストされることになり、 ソースツリー内のdocs/swaggeruiは削除された。 generateSwaggerUiタスクによるローカルにドキュメント閲覧もできなくなった。
- https://github.com/eclipse-edc/Connector/discussions/2329
- eclipse-edc/Connector#2328
- eclipse-edc/Connector#2209
- バージョンが0.0.1-SNAPSHOTのまま、中身だけ変わっていくのだろうか??
- と思ったが、0.1.0リリース後は0.1.1-SNAPSHOTに変わった
Swagger UIのドキュメント上、management-apiとcontrol-apiの2つのくくりに分かれている。 v0.1.0で見た時の分類は以下。 context aliasとの対応で見ると、managementはmanagement-apiで、 残りのcontrol、protocol、publicはcontrol-apiなのかしら。:
$ find . -name build.gradle.kts | xargs grep management-api | grep apiGroup ./extensions/data-plane-selector/data-plane-selector-api/build.gradle.kts: apiGroup.set("management-api") ./extensions/control-plane/provision/provision-http/build.gradle.kts: apiGroup.set("management-api") ./extensions/control-plane/api/management-api/policy-definition-api/build.gradle.kts: apiGroup.set("management-api") ./extensions/control-plane/api/management-api/contract-definition-api/build.gradle.kts: apiGroup.set("management-api") ./extensions/control-plane/api/management-api/contract-negotiation-api/build.gradle.kts: apiGroup.set("management-api") ./extensions/control-plane/api/management-api/transfer-process-api/build.gradle.kts: apiGroup.set("management-api") ./extensions/control-plane/api/management-api/catalog-api/build.gradle.kts: apiGroup.set("management-api") ./extensions/control-plane/api/management-api/asset-api/build.gradle.kts: apiGroup.set("management-api") ./extensions/control-plane/api/management-api/contract-agreement-api/build.gradle.kts: apiGroup.set("management-api") ./extensions/common/api/api-observability/build.gradle.kts: apiGroup.set("management-api") ./extensions/common/api/management-api-configuration/build.gradle.kts: apiGroup.set("management-api") $ find . -name build.gradle.kts | xargs grep control-api | grep apiGroup ./extensions/data-plane/data-plane-api/build.gradle.kts: apiGroup.set("control-api") ./extensions/control-plane/transfer/transfer-data-plane/build.gradle.kts: apiGroup.set("control-api") ./extensions/control-plane/api/control-plane-api/build.gradle.kts: apiGroup.set("control-api")
- edc.web.https.keystore.path が設定されている場合は、httpsが利用される。 そうでなければhttp 。 この設定は、コンテキストごとに別れていない。
-PverboseTest を指定すると、出力されるログが増える。:
$ ./gradlew test -PverboseTest
特定のテストだけを実行したい場合は以下の要領。
$ ./gradlew extensions:api:data-management:transferprocess:test --tests '*TransferProcessEventDispatchTest'
特定のディレクトリ下のサブモジュールのテストすべてを実行したい場合は、 -p でディレクトリを指定する。:
$ ./gradlew test -p extensions/api/data-management/transferprocess --tests '*TransferProcessEventDispatchTest'
@EndToEntTest アノテーションがついたテストを実行するためには、以下の要領。:
$ ./gradlew test -DincludeTags="EndToEndTest"
特定のテストメソッドだけ実行する例:
$ ./gradlew clean test -p system-tests/e2e-transfer-test/runner -PverboseTest -DincludeTags="EndToEndTest" --tests "*EndToEndTransferInMemoryTest.httpPull_dataTransfer" 2>&1 | tee /tmp/test.log
@PostgresqlDbIntegrationTest アノテーションが付いたテストを実行する場合、下記の要領。:
$ ./gradlew clean test -p system-tests/e2e-transfer-test/runner -DincludeTags=PostgresqlIntegrationTest $ ./gradlew clean test -p system-tests/e2e-transfer-test/runner -DincludeTags=PostgresqlIntegrationTest --tests '*TransferPullEndToEndTest*pullFromHttp' -PverboseTest
JUnitのテストケース内でServiceExtension実装をテストするための枠組みが、 core/common/junit下に定義されている。
- EdcExtensionは、各テストメソッドの前後でbootしてshutdownするようなBaseRuntimeの拡張。 テストクラスに @ExtendWith(EdcExtension.class) して利用する。
- EdcExtensionはParameterResolverを実装しているので、 テストメソッドの引数としてregister済みのサービス(mock)を指定できる。
- EdcExtension#registerServiceMock はテスト用のserviceをregisterする。 ServiceExtensionContext#registerService で既にregister済みのserviceでもオーバーライドできる。
- EdcExtension#registerSystemExtension はテスト用にextensionをregisterする。 @Inject なフィールドに @Provider なメソッドで生成したインスタンスをセットする処理は、 ExtensionLoader#bootServiceExtensions で実行される。 そのため、 @BetoreEach なメソッドの中など、bootされるタイミングより前で、 呼び出しておかなければならない。
コネクタによるデータ転送の一連の流れを実行するテストコードが定義されている。
AbstractEndToEndTransferがベースクラスで、データの永続化先によって3種類の派生がある。 各派生には @EndToEndTest のようなアノテーションがついていて、それに応じて -DincludeTags=EndToEndTest のような指定をしないと、テストが実行されない。
EndToEndTransferInMemoryTestはデータをメモリ上に持ち、永続化しないパターンで、それ単体で実行できる。:
$ ./gradlew clean test -p system-tests/e2e-transfer-test/runner -DincludeTags=EndToEndTest --tests '*EndToEndTransferInMemoryTest' -PverboseTest
EndToEndTransferPostgresqlTestはPostgreSQLにデータを永続化する。 これも、コンテナを利用してPostgreSQLのサーバを建てることで、簡単に実行できる。:
$ docker run --rm --name edc-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres $ ./gradlew clean test -p system-tests/e2e-transfer-test/runner -DincludeTags=PostgresqlIntegrationTest --tests '*EndToEndTransferPostgresqlTest' -PverboseTest
テスト実行後に、データベース内のデータを見てみるのも、理解を深めるのに役立つかもしれない。 concsumerとproducerというデータベースができている。:
$ psql -U postgres -W -h localhost -l psql: warning: extra command-line argument "postgres" ignored Password: List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+------------+------------+----------------------- consumer | postgres | UTF8 | en_US.utf8 | en_US.utf8 | postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | provider | postgres | UTF8 | en_US.utf8 | en_US.utf8 | template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres (5 rows) $ psql -U postgres -W -h localhost -c 'SELECT * FROM edc_policydefinitions LIMIT 1;' provider policy_id | created_at | permissions | prohibitions | duties | extensible_properties | inherits_from | assigner | assignee | target | policy_type --------------------------------------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+--------+-----------------------+---------------+----------+----------+--------+----------------------- f5ed763c-7ec1-427d-a47d-3099236b61bd | 1682079999930 | [{"edctype":"dataspaceconnector:permission","uid":null,"target":null,"action":{"type":"USE","includedIn":null,"constraint":null},"assignee":null,"assigner":null,"constraints":[],"duties":[]}] | [] | [] | {} | | | | | {"@policytype":"set"} (1 row)
PostgreSQLの設定を変えたい場合、 dockerhubのドキュメント にあるように、 postgresql.confを置き換えればよい。 コンテナはpostgresプロセスがPID 1で起動してくるので、 pg_ctl reload はできるが、 pg_ctl restart はできない。:
$ docker run -i --rm postgres cat /usr/share/postgresql/postgresql.conf.sample > my-postgres.conf $ vi my-postgres.conf $ docker run --rm --name edc-postgres -v "$PWD/my-postgres.conf":/etc/postgresql/postgresql.conf -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres -c 'config_file=/etc/postgresql/postgresql.conf'
log_statement = 'all' , log_destination = 'stderr' , log_directory = '/var/log/postgresql' という設定で、クエリログを出力するのも、内部動作を確認する上でよい。
- ドキュメント自動生成用のモジュールやアノテーションの定義は、 #2001で、 GradlePlugins 配下に移動された。
- 設定プロパティは、ConfigurationExtensionがロードしたもの、環境変数からのもの、システムプロパティからのものがマージされる。競合があれば後のものほど強い。
- FsConfigurationExtension は、edc.fs.configでpathを指定されたファイルから、設定内容を読み込む。
- データの永続化のための仕組み/抽象化は独自実装で、外部ライブラリの依存性が増えることを避ける方針に見える。
- 永続化が必要なサブモジュールは、それぞれ
org.eclipse.edc.spi.persistence.*.*Store
のような名前の、インターフェースを定義する。 この定義はサブモジュールごとに行っていて、意外と共通化されていない。 - デフォルト実装として、データを永続化しない
InMemory*Store
があり、ユニットテストやサンプルの実行に利用される。 こちらも、あまり共通化する余地なし。 - RDBMSを利用してデータを永続化する実装として
Sql*Store
がある。 これらの実装は、 common/sql/sql-core下のorg.eclipse.edc.sql.store.AbstractSqlStore
を、 ベースロジックとしてモジュール横断的に利用されているようだ。Sql*Store
では、*Statements
のような名前のクラスを使い、 SQLステートメントを組み上げてJDBCドライバで実行する。*Statements
は*DialectStatements
のようなクラスをベースにしている。 このDialectを切り替えることで、複数RDBMSに対応できるようにする方針。 デフォルトで用意されているのはPostgresDialectStatements
でPostgreSQLが前提。
- 永続化が必要なサブモジュールは、それぞれ
- management APIについては、AuthenticationService#isAuthenticatedを呼ぶようなfilterで認証している。
- https://github.com/eclipse-edc/Connector/blob/v0.3.1/spi/common/auth-spi/src/main/java/org/eclipse/edc/api/auth/spi/AuthenticationRequestFilter.java
- Connector配下にあるAuthenticationServiceの実装は2種類だけ。
- 未指定デフォルトで利用される AllPassAuthenticationService は文字通り素通し。
- BasicAuthenticationService
は、
edc.api.auth.key
で指定されたキー文字列が、 X-Api-Keyヘッダーにセットされているかをセットする、素朴なもの。
- コネクタの認証には、org.eclipse.edc.spi.iam.IdentityServiceが利用される。
- Connector配下にある実装はDIDとOAuth2の2択。
- https://github.com/eclipse-edc/Connector/blob/72d8b8ef58de41db7111c9928f777ce60781f51c/extensions/common/iam/decentralized-identity/identity-did-service/src/main/java/org/eclipse/edc/iam/did/service/DecentralizedIdentityService.java
- https://github.com/eclipse-edc/Connector/blob/72d8b8ef58de41db7111c9928f777ce60781f51c/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImpl.java
- ログの出力はMonitorというインターフェースで抽象化されている。 明示的にMonitor実装がregisterされていない場合、 ConsoleMonitorという単純な実装が使われる。 ロギングライブラリは使用せずに、コンソールにDEBUGレベルを含むすべてのログを出力する。
- MonitorExtension実装をロードすることで、monitorの切りかえ/追加ができる。
- LoggerMonitorExtensionは、java.util.loggingでログ出力するLoggerMonitorを提供するもの。
- BaseRuntimeは MonitorProvider
というSLF4JServiceProvider実装をロードし、SLF4J APIで出力されたログを、Monitor側に送る仕組みを用意している。
結果として、ほかのSLF4J bindingを使うことができない。
- removed: eclipse-edc/Connector#3463
コネクタ内のHTTPリクエストは、okhttp3で実行されている。 logging-interceptorを仕込むと、リクエストの内容をログ出力できる。:
$ git diff diff --git a/core/common/connector-core/src/main/java/org/eclipse/edc/connector/core/base/OkHttpClientFactory.java b/core/common/connector-core/src/main/java/org/eclipse/edc/connector/core/base/OkHttpClientFactory.java index 10dc4d5d2..1c7bc3eab 100644 --- a/core/common/connector-core/src/main/java/org/eclipse/edc/connector/core/base/OkHttpClientFactory.java +++ b/core/common/connector-core/src/main/java/org/eclipse/edc/connector/core/base/OkHttpClientFactory.java @@ -77,6 +77,9 @@ public class OkHttpClientFactory { context.getMonitor().info("HTTPS enforcement it not enabled, please enable it in a production environment"); } + var logging = new okhttp3.logging.HttpLoggingInterceptor(); + logging.setLevel(okhttp3.logging.HttpLoggingInterceptor.Level.BODY); + builder.addInterceptor(logging); return builder.build(); } diff --git a/gradle.properties b/gradle.properties index 9bd583ee1..e86600c1b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ group=org.eclipse.edc -version=0.3.1-SNAPSHOT +version=0.3.1 # for now, we're using the same version for the autodoc plugin, the processor and the runtime-metamodel lib, but that could # change in the future -annotationProcessorVersion=0.3.1-SNAPSHOT -edcGradlePluginsVersion=0.3.1-SNAPSHOT -metaModelVersion=0.3.1-SNAPSHOT +annotationProcessorVersion=0.3.1 +edcGradlePluginsVersion=0.3.1 +metaModelVersion=0.3.1 edcScmUrl=https://github.com/eclipse-edc/Connector.git edcScmConnection=scm:git:[email protected]:eclipse-edc/Connector.git diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 97672f052..12b80c690 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -79,6 +79,7 @@ mockserver-client = { module = "org.mock-server:mockserver-client-java", version mockserver-netty = { module = "org.mock-server:mockserver-netty", version.ref = "httpMockServer" } nimbus-jwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "nimbus" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } +okhttp-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" } opentelemetry-api = { module = "io.opentelemetry:opentelemetry-api", version.ref = "opentelemetry" } opentelemetry-instrumentation-annotations = { module = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations", version.ref = "opentelemetry" } opentelemetry-proto = { module = "io.opentelemetry.proto:opentelemetry-proto", version.ref = "opentelemetry-proto" } diff --git a/spi/common/http-spi/build.gradle.kts b/spi/common/http-spi/build.gradle.kts index 9aaf288b5..d9fa0bfa7 100644 --- a/spi/common/http-spi/build.gradle.kts +++ b/spi/common/http-spi/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { api(project(":spi:common:core-spi")) api(libs.okhttp) + api(libs.okhttp.logging.interceptor) api(libs.failsafe.okhttp) }
okhttp3のロギングはjava.util.loggingを使っているので、
-Djava.util.logging.config.file=/tmp/logging.properties
のようにシステムプロパティ経由で設定ファイルを指定できる。:
$ cat >/tmp/logging.properties <<EOF handlers = java.util.logging.ConsoleHandler .level = INFO java.util.logging.ConsoleHandler.level = ALL java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.SimpleFormatter.format = %1\$tF %1\$tT %4\$s : %5\$s %n EOF
指定したファイルのpathが誤っているなどすると、単にログが出なくなるため、原因を見つけにくい。
StatefulEntityは、StateMachineManagerのsingle threadedなExecutorによって状態遷移される。
StateMachineManagerが使われるのは3か所。
- ContractServiceExtensionで初期化される ProviderContractNegotiationManagerと、ConsumerContractNegotiationManager。
- CoreTransferExtensionで初期化されるTransferProcessManager。
基本的にはWaitStrategyはExponentialWaitStrategy(1000)が使われる。 なんらかのprosessが実行された場合はスリープなし、 そうでなければ1000ミリ秒後に次回、という感じ。
テスト用にWaitStrategyを差し込み可能になっている。 see NegotiationWaitStrategy and TransferWaitStrategy
StateMachineManager#loop 一回につき、 登録されたprocessor のすべてが実行される。 バックエンドのstoreがRDBの場合、以下のようなクエリが、 各stateを処理する各processorによって繰り返し実行される。:
SELECT * FROM edc_transfer_process WHERE state = $1 AND pending = $2 AND (lease_id IS NULL OR lease_id IN (SELECT lease_id FROM edc_lease WHERE ($3 > (leased_at + lease_duration)))) LIMIT $4 OFFSET $5
- https://github.com/eclipse-edc/Connector/blob/v0.3.1/docs/developer/architecture/usage-control/policies.md
- PolicyEngine が registerしておいた関数群 を実行することで、 Policy を満たすかどうか評価する。
- Catalog, ContractNegotiation, TransferProcess等のサービス内で、
PolicyEngine#evaluate
が評価される。 - 関数によって評価される Rule には、 Duty 、 Permission 、 Prohibition の3種類がある。
- 上記のPolicyやRuleの概念は、 ODRL で定義されたもの。
- Policyは policydefinitions のAPIで登録する。
- MVDの関連部分が参考になるかもしれない。
- CatalogはContractOfferの集まり。だったが、Dataspace Protocol対応で、DatasetやDataServiceという概念が登場した。
CatalogServiceにはEDCのとIDSのと、2種類ある。 e2e-transfer-test等の既存のテストやサンプルで使われているのは、後者のIDSのもののみに見える。 Catalogのデータモデルは共通。
- https://github.com/eclipse-edc/Connector/blob/0ac9755d7a058117fb8372181af7389760818e7e/spi/control-plane/control-plane-spi/src/main/java/org/eclipse/edc/connector/spi/catalog/CatalogService.java
- https://github.com/eclipse-edc/Connector/blob/0ac9755d7a058117fb8372181af7389760818e7e/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/catalog/CatalogServiceImpl.java
- https://github.com/eclipse-edc/Connector/blob/0ac9755d7a058117fb8372181af7389760818e7e/data-protocols/ids/ids-spi/src/main/java/org/eclipse/edc/protocol/ids/spi/service/CatalogService.java
- https://github.com/eclipse-edc/Connector/blob/0ac9755d7a058117fb8372181af7389760818e7e/data-protocols/ids/ids-core/src/main/java/org/eclipse/edc/protocol/ids/service/CatalogServiceImpl.java
/v2/transferprocesses は、consumer connectorが、データ転送のためのリクエストを受けるAPI。
- sourceは、ContractAgreementに含まれるassetIdで指定される。
- destinationは、dataDestinationで具体的にtypeとその他propertyで指定される。 例えばAzure Blobだと、typeはAzureStorageで、 accountでストレージアカウント名、containerはcontainer名を指す。
データ転送の処理それ自体は、transfer-data-plane側にコードがある。 https://github.com/eclipse-edc/Connector/blob/65479dc186ad0517565c77047672d1783a2188d7/extensions/control-plane/transfer/transfer-data-plane/README.md
リクエストが呼ばれると、TransferProcessインスタンスが作成され、 状態(state)を含む情報がTransferProcessStoreに保存される。 StateMachineManagerのスレッドがprocess*を順次呼び出すことで、 TransferProcessの状態は遷移していく。
processInitialで、destinationのtypeに応じて必要なら、 登録されたConsumerResourceManifestGeneratorにが、ResourceDefinitionを作成する。 現状destinationがAzure Blob/Amazon S3/GCSのオブジェクトの場合に、この処理が入る。
processProvisioningで、上記のResourceDefinitionに応じて、 ProvisionManagerが登録されたProvisioner実装を利用して、resourceを作成する。 destinationがAzure Blog/Amazon S3/GCSのオブジェクトの場合に、 container/bucketを(無ければ)作成し、provider connecterに書き込みを許可するための、 tokenを作成する。
processRequestingで、provider connectorにDataRequestを送る。 リクエストはRemoteMessageDispatcherを利用して送信される。
DataRequestメッセージを受信したprovider connector側では、 ArtifactRequestHandlerがリクエストを処理する。 ここでも、consumer側と同じようにTransferProcessManagerImplが使われ、 TransferProcessが作られる。 consumer側のTransferProcessとは独立だが、同じDataRequestのidに紐づくので、 consumerとproducerでTransferProcessStoreは独立になっていないとダメ。
- (provider側の)processProvisioningの段階で、initiateDataTransferが呼ばれ、
DataFlowManagerを介して、data-planeの処理が呼ばれる。
- DataFlowManagerは、DataFlowControllerを切り替える。 destinationがHttpProxyだとConsumerPullTransferDataFlowControllerが、 それ以外だとProviderPushTransferDataFlwoControllerが使われる。
- DataPlaneSelectorで、接続先を選択する。 DataPlaneSelectorも、個別に建ててREST APIでアクセスする方式を取れる。
- 接続先を示すDataplaneInstanceは、 data-plane-selector-apiの提供するREST API(/instances)で、事前に追加(定義)する。
- DataPlaneClientで、DataFlowRequestをdata-planeに送る。 DataPlaneManagerが同居しているどうかで、クラスが違う。 EmbeddedDataPlaneTransferClientとRemotDataPlaneTransferClientがある。
- (provider側の)processProvisioningの段階で、initiateDataTransferが呼ばれ、
DataFlowManagerを介して、data-planeの処理が呼ばれる。
processInprogressで、StatusChecker実装が、transferが終わったか確認する。 例えばAzure Blobだと、container内に、名前のsuffixが".complete"なblobがあるかを見る。
provider pushの場合、 provider側でsink.transfer(source)という形で、データコピーが実行される。 sinkはconsumer側に属するリソースなので、書き込み権限をどうやって与えるかがポイントになる。 例えば、sinkがAzure Blobなら、consumer側のコネクタが、自身のstorage accountで、 コンテナと、書き込みのにを許すSASトークンを作成し、それをvault経由でprovider側が読めるようにする。
- eclipse-edc/Connector#463
- DataPlaneFrameWorkExtensionが本体。 サンプル類はdata-plane-coreにdependencyを付けてロードしている。
- PipelineServiceImpl#transferがデータコピー処理の本体。 sink.transfer(source) する。 PipelineServiceImpl implements PipelineService extends TransferService みたいな階層で、 インターフェースが切られているが、その理由は?
- (data-plane-apiモジュールの)DataPlaneApiExtensionが、REST APIを提供する。 controlとpublicという2種類のcontextを使い分ける。 そのため、web.http.control.*とweb.http.public.*の2種類の設定(port mapping)が必要。 DataFlowRequestを受け取る/transferはcontrolの範疇。
- DataPlanePublicApiControllerは、transferされたデータをByteArrayOutputStreamで受け取って、 クライアントにtoStringして渡すので、大きなデータを受け渡せるわけではない。
- consumerがHTTPレスポンスのbodyとしてデータを受け取るパターンは、e2e-transfer-testの方に例が追加された。
- providerは、 asset typeをcanHandleなSourceから、 dataDestination typeをcanHandleなSinkに、 transferする。
- assetのtypeを増やす場合、DataSourceFactoryとDataSinkFactoryの実装をつくり、 PipelineService#registerFactory する。
- 元々あったprovider pushは、provider connector側でデータ送信の処理(
sink.transfer(source)
)が呼ばれるのでわかりやすい。 それに対して、後から追加されたデータ転送方式であるところのconsumer pullはちょっとわかりにくい。- 現状consumer pullになるのは、destinationのtypeがHttpProxyの場合のみ。
- consumer pullの場合は、TransferStartMessageのペイロードとして、データの在処を示すEndpointDataReference(EDR)をconsumer connectorに渡す。 consumer connectorは、受け取ったEDRをbackendにPOSTする。
- consumer clientは(backendから取り出した)EDRに入っているendpointのURLに対して、authCodeに入っているトークンをAuthorizationヘッダに入れて、GETする。
このendpointのURLはコネクタのdata-plane APIを指すもの。
コネクタは、authCodeに含まれている真のデータの在処を示すURLからデータを取得し、clientに渡す。つまり、プロキシサーバとして振る舞う。
authCodeに含まれる情報で認証を行うために、クライアントは直接データの在処にアクセスしない。
- という仕組み上、sourceのtypeはHttpDataでなければ成立しないような。
- ドキュメント上はprovider connectorがデータを中継する想定になっているように見える が、 Samplesのtransfer-02-consumer-pull では、consumer connectorがHTTP proxyとして振る舞っているように見える。
- おそらくconsumer pullの場合に、いつdata transfergが終わったか制御する必要があることから、 そのための仕組みを導入するために、大きめのリファクタリング込みで、 data plane signaling という機構が追加された。
- eclipse-edc/Connector#2429
- https://github.com/eclipse-edc/Connector/blob/73a6d9b49d164c927031de71c384f239e05f33d4/docs/developer/architecture/ids-dataspace-protocol/README.md
#1645 で、 chunked transferが問題となる場合の対策として、 HttpAddressのpropertyで、nonChunkedTransferをtrueにすることで、 chunked transferをオフにできるようになった。:
"dataDestination": { "type": "HttpData", "baseUrl": "http://localhost:4000/api/consumer/store", "nonChunkedTransfer": "true" }
IDE(IntelliJ)で開くと、Gradleデーモンがメモリ不足で落ちる場合がある。 落ちない場合もある。 ヒープサイズはgradle.propertiesに以下を追加することで増やせるが..。:
org.gradle.jvmargs=-Xmx4g
- 元々はEclipse Dataspace Connectorだったが、Eclipse Dataspace Componentsにrebrandされた。
バージョンはずっと0.0.1-SNAPSHOTだったが、ソースコードを分割して、 それぞれのリポジトリで非互換な修正が入るとビルドが通らなくなるので、 ある瞬間を示すための0.0.1-20230301-SNAPSHOTのようなバージョン番号をつけて参照する形になった。
snapshotはNexusから取得できる。
Maven Centralにpublishされるrelease artifactのバージョンは、0.0.1-milestone-8のような形式になった。
依存ライブラリのバージョン定義は、GradlePluginsリポジトリで定義された、 edc-versionsというアーティファクトにまとめられた。
- https://github.com/eclipse-edc/Connector/blob/cc5b34833574be9b5f20d7c128f4e1c6a840e129/docs/developer/version-catalogs.md
- https://github.com/eclipse-edc/GradlePlugins/blob/96f9cc05047c111a547f6ac78168cb6ce9a84fd4/version-catalog/build.gradle.kts
- https://github.com/eclipse-edc/GradlePlugins/blob/96f9cc05047c111a547f6ac78168cb6ce9a84fd4/gradle/libs.versions.toml
その後、あまりうまくないことが分かり、各コンポーネントがバージョンカタログを持つやり方に変わった。
- https://github.com/eclipse-edc/Connector/blob/e7a092bf81fc43b42c349d98e3e6ad3939f181a6/docs/developer/decision-records/2023-03-31-version-catalog-per-component/README.md
- GradlePlugins側にも、共通のパーツだけ少し残されてはいる。
version catalog自体はGradleが提供する機能 。
- libs.versions.tomlという TOML形式 のファイルによるバージョン定義を読んで解釈するのは、 GradleのVersionCatalobBuilder 。
- groovy-core のようにハイフン区切りで定義されたaliasには、 libs.groovy.core のようにドット区切りのアクセサでアクセスする 流儀 らしい。
- Maven等にpublishして、外部から参照できるようにするためには、 version-catalogプラグイン を利用する。
ローカルでEDCのコードを修正して、それをSamplesから使って試したりするには、ちょっと手順が必要。
まずGradlePlugins側のバージョン定義を修正したものをローカルリポジトリにインストールする。:
$ ./gradlew publishToMavenLocal -Pskip.signing
dependencyResolutionManagementのrepositoriesの設定 の設定で、mavenLocalをmavenCentralの上に持ってこないと、 ローカルのリポジトリ(~/.m2/repository)を見てくれないのかな? と思ったが、 そうしなくても、ローカルからartifactsが取得されるように見える。
Gradleのキャッシュ(~/.gradle/caches)はキャッシュであって、リポジトリではない。 キャッシュに対してartifactsをpublishするようなことはできない。 mavenLocalから取得したartifactsはキャッシュされない。 そのため、mavenLocalを使うとビルドが遅くなるので、使うべき場面以外では使わないほうがよいというトーンになっている。
Gradleのドキュメント的には、ローカルなartifactsを使ってビルドする場合、 composite build を使うことを推奨している。 だがしかし、Samplesを --include-build を使ってビルドしようとしたところ、 モジュール名の重複みたいな感じのエラーになった。:
$ ./gradlew --include-build /path/to/Connector clean build ... > Could not resolve all task dependencies for configuration ':advanced:advanced-01-open-telemetry:open-telemetry-consumer:compileClasspath'. > Could not resolve org.eclipse.edc:management-api:0.3.1. Required by: project :advanced:advanced-01-open-telemetry:open-telemetry-consumer > Module version 'org.eclipse.edc:management-api:0.3.1' is not unique in composite: can be provided by [project :Connector:extensions:control-plane:api:management-api, project :Connector:system-tests:management-api].
複数のリポジトリのドキュメントをまとめて一つに見せる仕組み。 もともとはConnectorにあったドキュメントを移動し、拡張したもの。
eclipse-edc.github.io でサイトがserveされている。
docsify というツールが使われていて、
docsify serve
コマンドでローカルで確認可能。git submodule
で、 各種リポジトリを取得し、そのdocsをまとめてserveする。:
$ git clone https://github.com/eclipse-edc/docs $ cd docs $ git submodule update --init --remote $ docsify serve docs
submoduleは、GitHubのworkflowで、 週一回更新 されていく。
docsも自身のsubmodule になっている。 docs/documentation/配下のmarkdownを編集すると、 docsifyでserveされてるサイトに即時反映されるので便利だが、 その修正をpull requestにするためには、 submoduleではない本体側に変更を反映する必要がある。
samplesの内容は、個別のソースツリーに移動された。
transferのサンプルが雰囲気をつかむのによい。
- https://github.com/eclipse-edc/Samples/tree/66b108bd9e30efe430c62aaa1aebe445ba81c2fe/transfer
- consumer, providerはどちら同じモジュールを利用し、設定で使い分ける。
- client(curlコマンド)はconsumerにREST APIでmanagemnent APIのリクエストを送る。 consumerは受付情報的な内容をすぐにレスポンスとして返す。
- consumerはclientリクエストを受けて、providerにDataspace Protocolのリクエストを送る。
手でcurlコマンドを叩く代わりに、一連の処理をtestタスクで実行することもできる。:
$ ./gradlew clean test -p system-tests -DincludeTags=EndToEndTest --tests Transfer03providerPushTest -PverboseTest
- 最近は、testcontainersを使って、backendなどをDockerで起動するようになった様子。
- EDCのモジュール構造を記述するためのアノテーションを定義している。
- フィールドのinjectionなどの、
DependencyGraph内の処理に関する順序
としては、
BaseExtension > CoreExtension > その他
。 - ExtensionPointは、 GradlePlugins で定義された、モジュール定義のドキュメントを自動生成する仕組みの中で利用される。 単に、実装すべきInterfaceであることを示すという感じ。
- いくつかのプラグインを定義している。
- モジュール定義のドキュメントを自動生成。
- 各サブモジュールをビルドしたときにできる
build/edc.json
がそれ。 - 対象となるのはextensionかspi 。
spiモジュールについては、
@Spi
なインターフェースがただ一つあるという前提のようだ。
- 各サブモジュールをビルドしたときにできる
- EDCのGradleモジュールの convention を定義。
- サブモジュール名の重複がないかどうかを確認。
- OpenAPIの定義をマージ。
- テストのサマリを出力。
- モジュール定義のドキュメントを自動生成。
- org.eclipse.edc.edc-build (のベースとなるedc-build-base)により、 上記のプラグイン一式が組み込まれる 。
- Connectorのbuild.gradle.kts を見ると使い方がなんとなく分かる。
- https://github.com/eclipse-edc/MinimumViableDataspace
- EDCを使ったDSのデモ
- AssetはAzureのBlob。ローカル環境ではAzuriteを利用。
- assetを定義する仕込みために、コネクタのdata management APIを呼び出す部分は、 Postmanで作った.jsonをNewmanで実行する形で実装。
- policyとregistrationに関連して、extensionを2個独自に実装して利用。
- https://github.com/eclipse-edc/MinimumViableDataspace/blob/8141afce75613f62ed236cb325a862b8af40b903/extensions/policies/src/main/java/org/eclipse/edc/mvd/SeedPoliciesExtension.java
- https://github.com/eclipse-edc/MinimumViableDataspace/blob/8141afce75613f62ed236cb325a862b8af40b903/extensions/refresh-catalog/src/main/java/org/eclipse/edc/mvd/RegistrationServiceNodeDirectoryExtension.java
- DID/VCでParticipantを認証する仕組みとして
IdentityHub と RegistrationService を利用。
- https://github.com/eclipse-edc/MinimumViableDataspace/tree/8141afce75613f62ed236cb325a862b8af40b903/docs/developer/decision-records/2022-06-20-mvd-onboarding
- https://github.com/eclipse-edc/MinimumViableDataspace/tree/8141afce75613f62ed236cb325a862b8af40b903/docs/developer/decision-records/2022-06-16-distributed-authorization
- https://github.com/eclipse-edc/MinimumViableDataspace/tree/8141afce75613f62ed236cb325a862b8af40b903/docs/developer/decision-records/2022-06-15-registration-service
- FederatedCatalogを利用。
Docker Composeを利用して、ローカルノードで動作確認できる。
-DuseFsVault="true" をつけてビルドしないと、(azuriteではなく)Azure前提のVaultが使われて、エラーになる。
MVD_UI_PATHをexportして、DataDashboardのUIを動かす場合も、上記の仕込みは必要。
MVDはAxure BlobのみをAssetのデータ置き場としてサポートしているため、azuriteのコンテナがいる。
WebDidResolverがDIDを取得するために、nginxのコンテナがいる。
上記をまとめてると、以下の要領でコマンドを実行することになる。:
$ cd /home/iwasakims/srcs $ git clone https://github.com/eclipse-edc/MinimumViableDataspace $ git clone https://github.com/eclipse-edc/DataDashBoard $ cd MinimumViableDataspace
$ ./gradlew -DuseFsVault="true" :launchers:connector:shadowJar $ ./gradlew -DuseFsVault="true" :launchers:registrationservice:shadowJar $ export MVD_UI_PATH=/home/iwasakims/srcs/eclipse-edc/DataDashboard $ docker compose --profile ui -f system-tests/docker-compose.yml up --build
cli-toolsというコンテナの、 entrypoint.sh のなかで、participantのenrollmentを実行。
- identity-hub-cliでverifiable credentialを登録
- registry-service-cliでparticipantを登録
did-serverというnginxのコンテナが、 DID document の置き場。
MVDはAxure BlobのみをAssetのデータ置き場としてサポートしているため、 試すには現物のAzureを使うか、Azuriteのコンテナをローカル実行する必要がある。
テスト用にAzure Blogのcontainerやblobを作る上では、 Azure CLIのazコマンドを使うのが楽。 Ubuntu環境であれば、 LinuxにAzure CLIをインストールする 手順にあるように、aptでインストールできる。:
$ sudo apt-get update $ sudo apt-get install ca-certificates curl apt-transport-https lsb-release gnupg $ curl -sLS https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/keyrings/microsoft.gpg > /dev/null $ sudo chmod go+r /etc/apt/keyrings/microsoft.gpg $ AZ_DIST=$(lsb_release -cs) $ echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/microsoft.gpg] https://packages.microsoft.com/repos/azure-cli/ $AZ_DIST main" | sudo tee /etc/apt/sources.list.d/azure-cli.list $ sudo apt-get update $ sudo apt-get install azure-cli $ az version
azuriteを使う場合、ストレージアカウント名とキーは、 docker-compose.ymlのAZURITE_ACCOUNTS で設定されている。 それに合わせて、接続文字列を設定して使う。:
$ az config set storage.connection_string="DefaultEndpointsProtocol=http;AccountName=company1assets;AccountKey=key1;BlobEndpoint=http://localhost:10000/company1assets" $ az storage container list $ az storage container create --name src-container $ az storage blob upload --container-name src-container --file ./deployment/azure/terraform/modules/participant/sample-data/text-document.txt
Azure CLIでblobを操作する方法は、 Azure Blob Storage documentation 等に説明がある。
- https://github.com/eclipse-edc/IdentityHub
- おもに以下の機能を提供
- verifiable credentialの保存
- Dataspace AuthorityのRegistration Serviceが、participantのenrollmentの過程で、 署名したverifiable credential(JWS?)を書き込む。
- キーバリュー的にVCを保存するが、キーは単にUUID#randomUUIDで決まるようだ。
- verifiable presentationの取得
- 検索条件などはなく、登録されているすべてのVCが返ってくるようだが?
- verifiable credentialの保存
- DIDに関する概念ででてくる Decentralized Web Node なるものに相当するらしい。 だがしかし、そのコードの多くは identity_dwn ブランチに残し、 PR#160 で削除され、 2023-09-06のdecision records に書かれているように、EDCの仕組みを作る方向に向かうようだ。 いまは Tractus-X配下でされている枠組み が、標準化のために、 Eclipse Dataspace Working Group の方に移されるとも。
- Gaia-Xとの関係性てきな話題については、以下も参照。
- participantが各自IdentityHubを立てておき、DID documentの中にそのURLを入れる。
- Connectorと同一プロセスにすることもできる。 RegistrationServiceのintegration test用コンテナ がその例。
- https://github.com/eclipse-edc/RegistrationService
- MVDのための簡易サービス。
- DIDで識別されるParticipantを登録する。
/registry/participant[s] で、単純な追加と取得ができるAPIだけ定義されている。
- https://github.com/eclipse-edc/RegistrationService/blob/04df5c8f361d71520b48385872db63df68291537/extensions/registration-service-api/src/main/java/org/eclipse/edc/registration/api/RegistrationServiceApiController.java
- Participant追加は、 Authorization: Bearer DID-JWT のようなヘッダー付きのリクエストをPOSTすることで行う。
- Participantの情報は一旦storeに格納し、非同期的にstoreの情報を見ているParticipantManagerがDataspaceRegistrationPolicyに応じて参加を許可するか判断する。
- https://github.com/eclipse-edc/RegistrationService/blob/v0.3.1/core/registration-service/src/main/java/org/eclipse/edc/registration/RegistrationServiceExtension.java#L93-L96
- https://github.com/eclipse-edc/RegistrationService/blob/v0.3.1/core/registration-service-onboarding-policy-verifier/src/main/java/org/eclipse/edc/registration/verifier/OnboardingPolicyVerifierImpl.java#L53-L90
- DidDocumentをDidResolverを使って取得する。 WebDidResolverはWebサーバからGETする。アクセス先のURLは、DIDのURNを加工して作る。
- participantの参加登録は、
IdentityHubとやりとりして
実行する。
- まずparticipantのVCを取得する。
- その後、 membership VC というJWSを格納する。
/federatedcatalogというpathに対応したAPIをserveする。 指定された条件を満たすContractOfferを返す。
(test用ではない)extensionとしては4つ。:
40 FederatedCatalog/core/federated-catalog-core/src/main/java/org/eclipse/edc/catalog/cache/FederatedCatalogCacheExtension.java public class FederatedCatalogCacheExtension implements ServiceExtension { 37 FederatedCatalog/core/federated-catalog-core/src/main/java/org/eclipse/edc/catalog/cache/FederatedCatalogDefaultServicesExtension.java public class FederatedCatalogDefaultServicesExtension implements ServiceExtension { 28 FederatedCatalog/extensions/api/federated-catalog-api/src/main/java/org/eclipse/edc/catalog/api/query/FederatedCatalogCacheQueryApiExtension.java public class FederatedCatalogCacheQueryApiExtension implements ServiceExtension { 35 FederatedCatalog/extensions/store/fcc-node-directory-cosmos/src/main/java/org/eclipse/edc/catalog/node/directory/azure/CosmosFederatedCacheNodeDirectoryExtension.java public class CosmosFederatedCacheNodeDirectoryExtension implements ServiceExtension {
- https://github.com/eclipse-edc/DataDashboard
- デモ用のWeb UI。TypeScriptで実装されている。
- Catalogの画面は、/federatedcatalogから取得したContractOfferをすべて並べて表示している感じ。
- `#3818<https://github.com/eclipse-edc/Connector/issues/3818>`_ :
JWTトークンの検証をDSPから切り離すrefatoring
の一環で、
*TransferService
からPolicyEngine
を使えるようにした。 結果として、IdentityAndTrustServiceのコンストラクタのシグネチャに変化あり。 - SamplesではEDCのバージョンを 0.4.1から0.5.1に上げている ため、その差分から0.5.0と0.5.1の違いを理解しにくいが、あまり影響ない様子。