Dataspace Protocol of EDC

メモ

EDCは現在IDSが提唱する、Dataspace Protocolにしたがって、コネクタ間でやりとりする。

DSP Data Planeの実装を確認する

data-protocols/dspdsp)以下に、Dataspace Protocolに対応したモジュールが含まれている。

例えば、org.eclipse.edc.protocol.dsp.dispatcher.PostDspHttpRequestFactoryorg.eclipse.edc.protocol.dsp.dispatcher.GetDspHttpRequestFactoryなどのファクトリが定義されている。 これは、前述のPOST、GETオペレーションに対応するリクエストを生成するためのファクトリである。

以下は、カタログのリクエストを送るための実装である。

org/eclipse/edc/protocol/dsp/catalog/dispatcher/DspCatalogHttpDispatcherExtension.java:54

1
2
3
4
5
6
7
8
9
10
11
12
public void initialize(ServiceExtensionContext context) {
messageDispatcher.registerMessage(
CatalogRequestMessage.class,
new PostDspHttpRequestFactory<>(remoteMessageSerializer, m -> BASE_PATH + CATALOG_REQUEST),
new CatalogRequestHttpRawDelegate()
);
messageDispatcher.registerMessage(
DatasetRequestMessage.class,
new GetDspHttpRequestFactory<>(m -> BASE_PATH + DATASET_REQUEST + "/" + m.getDatasetId()),
new DatasetRequestHttpRawDelegate()
);
}

他にも、org.eclipse.edc.protocol.dsp.transferprocess.dispatcher.DspTransferProcessDispatcherExtensionなどが挙げられる。 これは以下のように、org.eclipse.edc.connector.transfer.spi.types.protocol.TransferRequestMessageが含まれており、ConsumerがProviderにデータ転送プロセスをリクエストする際のメッセージのディスパッチャが登録されていることがわかる。

org/eclipse/edc/protocol/dsp/transferprocess/dispatcher/DspTransferProcessDispatcherExtension.java:60

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void initialize(ServiceExtensionContext context) {
messageDispatcher.registerMessage(
TransferRequestMessage.class,
new PostDspHttpRequestFactory<>(remoteMessageSerializer, m -> BASE_PATH + TRANSFER_INITIAL_REQUEST),
new TransferRequestDelegate(remoteMessageSerializer)
);
messageDispatcher.registerMessage(
TransferCompletionMessage.class,
new PostDspHttpRequestFactory<>(remoteMessageSerializer, m -> BASE_PATH + m.getProcessId() + TRANSFER_COMPLETION),
new TransferCompletionDelegate(remoteMessageSerializer)
);
messageDispatcher.registerMessage(
TransferStartMessage.class,
new PostDspHttpRequestFactory<>(remoteMessageSerializer, m -> BASE_PATH + m.getProcessId() + TRANSFER_START),
new TransferStartDelegate(remoteMessageSerializer)
);
messageDispatcher.registerMessage(
TransferTerminationMessage.class,
new PostDspHttpRequestFactory<>(remoteMessageSerializer, m -> BASE_PATH + m.getProcessId() + TRANSFER_TERMINATION),
new TransferTerminationDelegate(remoteMessageSerializer)
);
}

◆参考情報はじめ

このファクトリは、ディスパッチャの org.eclipse.edc.protocol.dsp.dispatcher.DspHttpRemoteMessageDispatcherImpl#dispatch メソッドから、間接的に呼び出されて利用される。 このメソッドはorg.eclipse.edc.spi.message.RemoteMessageDispatcher#dispatchメソッドを実装したものである。ディスパッチャとして、リモートへ送信するメッセージ生成をディスパッチするための。メソッドである。 さらに、これは org.eclipse.edc.connector.core.base.RemoteMessageDispatcherRegistryImpl 内で使われている。ディスパッチャのレジストリ内で、ディスパッチ処理が起動、管理されるようだ。 なお、これはorg.eclipse.edc.spi.message.RemoteMessageDispatcherRegistry#dispatch を実装したものである。このメソッドは、色々なところから呼び出される。

例えば、TransferCoreExtensionクラスではサービス起動時に、転送プロセスを管理するorg.eclipse.edc.connector.transfer.process.TransferProcessManagerImplを起動する。

org/eclipse/edc/connector/transfer/TransferCoreExtension.java:205

1
2
3
4
@Override
public void start() {
processManager.start();
}

これにより、以下のようにステートマシンがビルド、起動され、各プロセッサが登録される。

org/eclipse/edc/connector/transfer/process/TransferProcessManagerImpl.java:143

1
2
3
4
5
6
7
8
9
10
11
12
stateMachineManager = StateMachineManager.Builder.newInstance("transfer-process", monitor, executorInstrumentation, waitStrategy)
.processor(processTransfersInState(INITIAL, this::processInitial))
.processor(processTransfersInState(PROVISIONING, this::processProvisioning))
.processor(processTransfersInState(PROVISIONED, this::processProvisioned))
.processor(processTransfersInState(REQUESTING, this::processRequesting))
.processor(processTransfersInState(STARTING, this::processStarting))
.processor(processTransfersInState(STARTED, this::processStarted))
.processor(processTransfersInState(COMPLETING, this::processCompleting))
.processor(processTransfersInState(TERMINATING, this::processTerminating))
.processor(processTransfersInState(DEPROVISIONING, this::processDeprovisioning))
.build();
stateMachineManager.start();

上記のプロセッサとして登録されているorg.eclipse.edc.connector.transfer.process.TransferProcessManagerImpl#processStartingの中では org.eclipse.edc.connector.transfer.process.TransferProcessManagerImpl#sendTransferStartMessage が呼び出されている。

org/eclipse/edc/connector/transfer/process/TransferProcessManagerImpl.java:376

1
2
3
4
5
6
return entityRetryProcessFactory.doSyncProcess(process, () -> dataFlowManager.initiate(process.getDataRequest(), contentAddress, policy))
.onSuccess((p, dataFlowResponse) -> sendTransferStartMessage(p, dataFlowResponse, policy))
.onFatalError((p, failure) -> transitionToTerminating(p, failure.getFailureDetail()))
.onFailure((t, failure) -> transitionToStarting(t))
.onRetryExhausted((p, failure) -> transitionToTerminating(p, failure.getFailureDetail()))
.execute(description);

org.eclipse.edc.connector.transfer.process.TransferProcessManagerImpl#sendTransferStartMessage メソッド内では、 org.eclipse.edc.connector.transfer.spi.types.protocol.TransferStartMessageのメッセージがビルドされ、 ディスパッチャにメッセージとして渡される。

org/eclipse/edc/connector/transfer/process/TransferProcessManagerImpl.java:386

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var message = TransferStartMessage.Builder.newInstance()
.processId(process.getCorrelationId())
.protocol(process.getProtocol())
.dataAddress(dataFlowResponse.getDataAddress())
.counterPartyAddress(process.getConnectorAddress())
.policy(policy)
.build();

var description = format("Send %s to %s", message.getClass().getSimpleName(), process.getConnectorAddress());

entityRetryProcessFactory.doAsyncStatusResultProcess(process, () -> dispatcherRegistry.dispatch(Object.class, message))
.entityRetrieve(id -> transferProcessStore.findById(id))
.onSuccess((t, content) -> transitionToStarted(t))
.onFailure((t, throwable) -> transitionToStarting(t))
.onFatalError((n, failure) -> transitionToTerminated(n, failure.getFailureDetail()))
.onRetryExhausted((t, throwable) -> transitionToTerminating(t, throwable.getMessage(), throwable))
.execute(description);

◆参考情報おわり

ということで、org.eclipse.edc.protocol.dsp.spi.dispatcher.DspHttpRemoteMessageDispatcherというディスパッチャは、Dataspace Protocolに基づくリモートメッセージを生成する際に用いられるディスパッチャである。

おまけ)古い(?)Data Planeの実装を確認する(HTTPの例)

Dataspace Protocol以前の実装か?

extensions/data-plane 以下にData Planeの実装が拡張として含まれている。

例えば、 extensions/data-plane/data-plane-http には、HTTPを用いてデータ共有するための拡張の実装が含まれている。 当該拡張のREADMEの通り、 (transfer APIの)DataFlowRequestHttpDataだった場合に、

  • HttpDataSourceFactory
  • HttpDataSinkFactory
  • HttpDataSource
  • HttpDataSink

の実装が用いられる。パラメータもREADMEに(data-plane-httpのデザイン指針)記載されている。 基本的には、バックエンドがHTTPなのでそれにアクセスするためのパラメータが定義されている。

当該ファクトリは、 org.eclipse.edc.connector.dataplane.http.DataPlaneHttpExtension#initialize 内で用いられている。

org/eclipse/edc/connector/dataplane/http/DataPlaneHttpExtension.java:75

1
2
3
4
5
6
7
var httpRequestFactory = new HttpRequestFactory();

var sourceFactory = new HttpDataSourceFactory(httpClient, paramsProvider, monitor, httpRequestFactory);
pipelineService.registerFactory(sourceFactory);

var sinkFactory = new HttpDataSinkFactory(httpClient, executorContainer.getExecutorService(), sinkPartitionSize, monitor, paramsProvider, httpRequestFactory);
pipelineService.registerFactory(sinkFactory);

ここでは、試しにData Source側を確認してみる。

org/eclipse/edc/connector/dataplane/http/pipeline/HttpDataSourceFactory.java:63

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public DataSource createSource(DataFlowRequest request) {
var dataAddress = HttpDataAddress.Builder.newInstance()
.copyFrom(request.getSourceDataAddress())
.build();
return HttpDataSource.Builder.newInstance()
.httpClient(httpClient)
.monitor(monitor)
.requestId(request.getId())
.name(dataAddress.getName())
.params(requestParamsProvider.provideSourceParams(request))
.requestFactory(requestFactory)
.build();
}

上記の通り、まずデータのアドレスを格納するインスタンスが生成され、 つづいて、HTTPのデータソースがビルドされる。

HTTPのData Sourceの実体は org.eclipse.edc.connector.dataplane.http.pipeline.HttpDataSource である。 このクラスはSPIの org.eclipse.edc.connector.dataplane.spi.pipeline.DataSourceインタフェースを実装したものである。

org.eclipse.edc.connector.dataplane.http.pipeline.HttpDataSource#openPartStream がオーバライドされて実装されている。 詳しくは、openPartStream参照。

参考

ドキュメント

ソースコード

共有

Generate OpenAPI Spec of EDC Connector

メモ

EDCのConnectorのOpenAPIスペックを出力するための手順がGenerating the OpenApi Spec (*.yaml)に記載されている。 これに従い、試しに出力してみることにする。

ただ、このSpecはいわゆる現在EDCが採用している、Dataspace Protocol仕様ではないものが含まれている可能性が高い。 pathが/v2となっているのは、Dataspace Protocol準拠か? → 実際に調べてみると、v2が必ずしも、Dataspace Protocol向けというわけではなさそうである。

ちなみに、参考までに、IDSA Dataspace ConnectorのOpenAPI Specは Dataspace ConnectorのOpenAPI Spec にある。 このコネクタは昨年からあまり更新されていないので注意。

準備

もしまだソースコードを取得していなければ取得しておく。

1
2
git pull git@github.com:eclipse-edc/Connector.git
cd Connector

生成

ビルド環境にはJDK17を利用したいので、今回はDockerで簡単に用意する。

そのまま実行する場合:

1
docker run --rm -v ${PWD}:/local --workdir /local openjdk:17-alpine  ./gradlew clean resolve

いったんシェル立ち上げる場合:

1
2
docker run -it --rm -v ${PWD}:/local --workdir /local openjdk:17-alpine sh
./gradlew clean resolve

BUILD SUCCESSFULとなったらOK。

ちなみに、このYAMLファイル生成は自前のビルドツールを用いているようだ。参考:SwaggerGeneratorExtension

Data Planeの中身を軽く確認

resources/openapi/yaml/control-api/data-plane-api.yaml にある、Data Planeを試しに見てみる。

概要

description部分を機械翻訳したのが以下である。

1
2
Data PlaneのパブリックAPIはデータプロキシであり、データコンシューマがData Planeインスタンスを通じて、プロバイダのデータソース(バックエンドのRest APIや内部データベースなど)から能動的にデータを問い合わせることを可能にします。
Data PlaneのパブリックAPIはプロキシであるため、すべての動詞(GET、POST、PUT、PATCH、DELETEなど)をサポートしており、データソースが必要になるまでデータを転送することができます。これは、実際のデータソースがRest APIそのものである場合に特に便利です。同じように、任意のクエリパラメータ、パスパラメータ、リクエストボディのセットも(HTTPサーバによって固定された範囲内で)サポートされ、実際のデータソースに伝えることができます。

企業が持つデータストアをデータソースとしてデータ連携する際、そのプロキシとして働く。

paths

APIのパスを確認する。

transfer

データ転送をリクエストする。 リクエストボディには、データ転送のリクエスト情報が含まれる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/transfer:
post:
description: Initiates a data transfer for the given request. The transfer will
be performed asynchronously.
operationId: initiateTransfer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DataFlowRequest'
responses:
"200":
description: Data transfer initiated
"400":
description: Failed to validate request
tags:
- Data Plane control API

transfer/{processId}

パラメータprocessIdで与えられたIDのデータ転送処理の状態を確認する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/transfer/{processId}:
get:
description: Get the current state of a data transfer.
operationId: getTransferState
parameters:
- in: path
name: processId
required: true
schema:
type: string
responses:
"200":
description: Missing access token
tags:
- Data Plane control API

/{any}

/{any}以下にはDELETE、GET、PATCH、POST、PUTのOperationが定義されている。

1
2
3
4
5
6
7
8
9
10
/{any}:
delete:
(snip)
get:
(snip)
patch:
(snip)
post:
(snip)
put:

単純にデータを取得するだけではない。

Transfer Data Plane

resources/openapi/yaml/control-api/transfer-data-plane.yaml に含まれるのは以下のSpecだった。 トークンを受け取り検証するAPIのようだ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
openapi: 3.0.1
paths:
/token:
get:
description: "Checks that the provided token has been signed by the present\
\ entity and asserts its validity. If token is valid, then the data address\
\ contained in its claims is decrypted and returned back to the caller."
operationId: validate
parameters:
- in: header
name: Authorization
schema:
type: string
responses:
"200":
description: Token is valid
"400":
description: Request was malformed
"403":
description: Token is invalid
tags:
- Consumer Pull Token Validation
components:
schemas:
DataAddress:
type: object
properties:
properties:
type: object
additionalProperties:
type: object

control-plane-api

resources/openapi/yaml/control-api/control-plane-api.yaml にコントロールプレーンのSpecが含まれている。

/transferprocess/{processId}/complete

転送プロセスの完了をリクエストする。 転送が非同期、処理なので、受付成功が返る。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/transferprocess/{processId}/complete:
post:
description: "Requests completion of the transfer process. Due to the asynchronous\
\ nature of transfers, a successful response only indicates that the request\
\ was successfully received"
operationId: complete
parameters:
- in: path
name: processId
required: true
schema:
type: string
responses:
"400":
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ApiErrorDetail'
description: "Request was malformed, e.g. id was null"
tags:
- Transfer Process Control Api

/transferprocess/{processId}/fail

転送プロセスを失敗で完了させるリクエストを送る。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
post:
description: "Requests completion of the transfer process. Due to the asynchronous\
\ nature of transfers, a successful response only indicates that the request\
\ was successfully received"
operationId: fail
parameters:
- in: path
name: processId
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/TransferProcessFailStateDto'
responses:
"400":
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ApiErrorDetail'
description: "Request was malformed, e.g. id was null"
tags:
- Transfer Process Control Api

マネージメントAPIの類

resources/openapi/yaml/management-api 以下には、マネージメント系のAPIのSpecが。含まれている。

例えば、

  • カタログ: おそらくDataspace Protocolに対応している。DCATカタログのやり取り。
    • /v2/catalog/dataset/request
    • /v2/catalog/request
  • データアセット: データアドレスの情報と合わせて、データアセットを登録する
    • /v2/assets
      • post: 登録
      • put: 更新
    • /v2/assets/request: クエリに従ってアセット群を取得する
    • /v2/assets/{assetId}/dataaddress: データアドレスの更新
    • /v2/assets/{id}
      • delete: 消す
      • get: アセット取得
    • /v2/assets/{id}/dataaddress: アドレス取得
    • /v3/assets ... v3とは?
      • v2とおおよそ同じ
    • /v3/assets/request
      • v2とおおよそ同じ

など。ただ、/v2としていながら、DSPではなかったりするものがある(例:/v2/contractnegotiations)など注意が必要。

Dataspace Protocol Architecture

IDS Dataspace Protocolのドキュメント にIDSプロトコル対応の概要が記載されている。

後方互換性

当該ドキュメントに記載の通り、後方互換性を保証するものではない。 新しいプロトコルに対応次第、古い実装は破棄される。

ゴール

  • (将来リリースされる?)IDS-TCK(IDS Test Compatibility Kit)の必須項目をパスすること
  • Dataspace Protocol仕様を満たす他のコネクタと相互運用可能であること
  • Dataspace Protocolよりも前のバージョンのIDSには対応しない。
  • Usage Policyは実装しない。他のプロジェクトで実装される。

アプローチ

Dataspace ProtocolはJSON-LD、DCAT、ODRLで実現されている。 このプロトコルの対応で、Contract NegotiationとTransfer Processステートが新たに実装されることになる。 ただし、新しいプロトコルの対応が完了するまで、テストが通るようにする。

  1. JSON-LD Processing Architecture に基きJSON-LD対応する。
  2. Dataspace Protocol Endpoints and Services Architecture に基きエンドポイントとサービスの拡張を実装する。
  3. Dataspace Protocol Contract Negotiation Architecture に基きContract Negotiationマネージャのステートマシンを更新する。
  4. The Dataspace Protocol Transfer Process Architecture に基きTransfer Processのステートマシンを更新する。
  5. この1から4項目が安定すると、古いモジュールとサービスが削除される。
  6. Management APIを更新する。

JSON-LD Processing Architecture

JSON-LD Processing Architecture にJSON-LDを処理するアーキテクチャに関するコンセプトとアプローチが記載されている。

冒頭に記載あるとおり、結果として、JDS InfoModel Java Libraryを用いるのをやめ、JSON-LDメッセージをやり取りすることになる。

既存の TypeManagerに機能付加する。 JSONP対応する。

文書上は、以下のようなコンセプトが例として載っていた。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var mapper = new ObjectMapper();

mapper.registerModule(new JSONPModule());

var module = new SimpleModule() {

@Override
public void setupModule(SetupContext context){
super.setupModule(context);
}

};

mapper.registerModule(module);

typeManager.registerContext("json-ld",mapper)

実際に、2023/9/24時点での実装においても、以下のようにTypeManagerに登録されたJSON_JDのマッパーを利用していることが見られます。

org/eclipse/edc/protocol/dsp/api/configuration/DspApiConfigurationExtension.java:128

1
2
3
var jsonLdMapper = typeManager.getMapper(JSON_LD);
webService.registerResource(config.getContextAlias(), new ObjectMapperProvider(jsonLdMapper));
webService.registerResource(config.getContextAlias(), new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper));

org/eclipse/edc/protocol/dsp/api/configuration/DspApiConfigurationExtension.java:135

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void registerTransformers() {
var mapper = typeManager.getMapper(JSON_LD);
mapper.registerSubtypes(AtomicConstraint.class, LiteralExpression.class);

var jsonBuilderFactory = Json.createBuilderFactory(Map.of());

// EDC model to JSON-LD transformers
transformerRegistry.register(new JsonObjectFromCatalogTransformer(jsonBuilderFactory, mapper));
transformerRegistry.register(new JsonObjectFromDatasetTransformer(jsonBuilderFactory, mapper));
transformerRegistry.register(new JsonObjectFromPolicyTransformer(jsonBuilderFactory));
transformerRegistry.register(new JsonObjectFromDistributionTransformer(jsonBuilderFactory));
transformerRegistry.register(new JsonObjectFromDataServiceTransformer(jsonBuilderFactory));
transformerRegistry.register(new JsonObjectFromAssetTransformer(jsonBuilderFactory, mapper));
transformerRegistry.register(new JsonObjectFromDataAddressTransformer(jsonBuilderFactory));
transformerRegistry.register(new JsonObjectFromQuerySpecTransformer(jsonBuilderFactory));
transformerRegistry.register(new JsonObjectFromCriterionTransformer(jsonBuilderFactory, mapper));

// JSON-LD to EDC model transformers
// DCAT transformers
transformerRegistry.register(new JsonObjectToCatalogTransformer());
transformerRegistry.register(new JsonObjectToDataServiceTransformer());
transformerRegistry.register(new JsonObjectToDatasetTransformer());
transformerRegistry.register(new JsonObjectToDistributionTransformer());

// ODRL Transformers
OdrlTransformersFactory.jsonObjectToOdrlTransformers().forEach(transformerRegistry::register);

transformerRegistry.register(new JsonValueToGenericTypeTransformer(mapper));
transformerRegistry.register(new JsonObjectToAssetTransformer());
transformerRegistry.register(new JsonObjectToQuerySpecTransformer());
transformerRegistry.register(new JsonObjectToCriterionTransformer());
transformerRegistry.register(new JsonObjectToDataAddressTransformer());
}

特に後者の実装は、各種情報をJSONのオブジェクトに変換するトランスフォーマー(や、その逆)を登録している。 JSON-LDにてメッセージをやりとりしている様子の一端をみられる。

また、ドキュメントの方ではコンセプトとして、以下のような変換の流れが例として挙げられていた。

1
2
3
4
5
6
7
8
9
10
11
12
13
// message is de-serialized as Map<String, Object> by Jersey 
var document = JsonDocument.of(mapper.convertValue(message, JsonObject.class));

try {

var compacted = JsonLd.compact(document,EMPTY_CONTEXT).get();
var convertedDocument = mapper.convertValue(compacted,Map.class);

// process converted document

} catch(JsonLdError e) {
throw new RuntimeException(e);
}

もし実際の実装をみるのであれば、 org.eclipse.edc.core.transform.transformer.from.JsonObjectFromCatalogTransformer#transform メソッドのようなものを確認すると良い。

なお、ドキュメントローダとしては、titanium-json-ldが使われているようだ。 参考→ org.eclipse.edc.jsonld.TitaniumJsonLd Dataspace Protocol Endpoints and Services Architecture にもその旨記載されている。

Dataspace Protocol Endpoints and Services Architecture

Dataspace Protocol Endpoints and Services Architecture にIDS Controller Endpoint実装のアプローチが記載されている。

また当該ドキュメントには、以下のように拡張との対応関係が示されている。

Description Repository Extension
Contract Negotiation Connector control-plane-ids
Transfer Process Connector control-plane-ids
Catalog requests Connector catalog-ids

また前述の通り、Dataspace ProtocolではJSON-LDにてメッセージがやりとりされる。 それらを「(デ)マーシャル」(シリアライズ、デシリアライズ)する必要がある。

デシリアライズは以下のように行われると例示されている。

1
2
var document = JsonDocument.of(jsonObject);
var expanded = JsonLd.expand(document).get();

シリアライズは以下のように行われると例示されている。

1
2
3
var document = JsonDocument.of(jsonObject);
var compacted = JsonLd.compact(document,EMPTY_CONTEXT).get();
var compacted = mapper.convertValue(compacted,Map.class);

マイグレーションのポイント

大きなポイントの例は、

  • アセット(DCATにおけるデータセット)にODRLポリシーであるofferを含むようになること
  • データセットはカタログに含まれる。

(もともとEDCが採用していたIDS Infomodelでは、offerにアセットが含まれる)

Contract Definition、Asset、Dataset、ODRL Offerの関係は以下のように表現されていた。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CD = Contract Definition
A = Asset
DS = Dataset
O = ODRL Offer

If the Contract Definitions are:

CD 1 --selects--> [A1, A2]
CD 2 --selects--> [A1, A3]

the resulting Catalog containing Datasets is:

DS 1 -> A1 [O:CD1, O:CD2]
DS 2 -> A2 [O:CD1]
DS 3 -> A3 [O:CD2]

上記は包含関係を表している。

また、ProviderにContract Negotiationyや転送タイプをリクエストするためのエンドポイントは、DCAT Distributionである。 Distributionは、コネクタエンドポイントのメタデータとDataAdress属性で示される転送タイプの組み合わせで示される。

なお、現状のEDCではまだ未実装の部分があり、フューチャーワークとされていた。

また、DCAT CatalogやDatasetは名前空間プロパティを使用して拡張可能である必要がある。CatalogDecoratorが必要。

型変換

もともとあったIdsTypeTransfomerを実装し直す必要がある。 これは先に上げていたJsonObjectFromCatalogTransformerのようなTransformerである。 本ドキュメントには、その実装コンセプト/アプローチが記載されている。

その他

Identificationの取り扱い方についても変更あり。

RemoteMessageDispatcherも変更あり。 以下のようなクラス設計になっている。

1
2
3
4
5
RemoteMessageDispatcher (org.eclipse.edc.spi.message)
GenericHttpRemoteDispatcher (org.eclipse.edc.connector.callback.dispatcher.http)
GenericHttpRemoteDispatcherImpl (org.eclipse.edc.connector.callback.dispatcher.http)
DspHttpRemoteMessageDispatcher (org.eclipse.edc.protocol.dsp.spi.dispatcher)
DspHttpRemoteMessageDispatcherImpl (org.eclipse.edc.protocol.dsp.dispatcher)

Dataspace Protocol対応は、org.eclipse.edc.protocol.dsp.dispatcher.DspHttpRemoteMessageDispatcherImplと考えておくとよい。

Dataspace Protocol Contract Negotiation Architecture

Dataspace Protocol Contract Negotiation Architecture にContract Negotiationの変更アプローチが記載されている。

ステートマシンの変化内容を一覧化した表が載っていた。 表の通り、Dataspace Protocolに対応したのちも、IDSにはもともと無いステートが一部残っている。 Contract Negotiationでは、その状態が重要であるから、より厳密に扱っている印象がある。

EDC Existing EDC New IDS Transition Function Notes
UNSAVED (remove) N/A This state is not needed
INITIAL INITIAL N/A
REQUESTING REQUESTING N/A
REQUESTED REQUESTED REQUESTED Provider (new & counter)
PROVIDER_OFFERING OFFERING N/A
PROVIDER_OFFERED OFFERED OFFERED Consumer
CONSUMER_OFFERING (REQUESTING)
CONSUMER_OFFERED (REQUESTED)
CONSUMER_APPROVING ACCEPTING N/A
CONSUMER_APPROVED ACCEPTED ACCEPTED Provider
DECLINING (TERMINATING)
DECLINED (TERMINATED)
CONFIRMING AGREEING N/A
CONFIRMED AGREED AGREED Consumer
VERIFYING N/A
VERIFIED VERIFIED Provider
FINALIZING N/A
FINALIZED FINALIZED Consumer
TERMINATING N/A
TERMINATED TERMINATED P & C
ERROR (TERMINATED)

参考

プロジェクト

ドキュメント

ソースコード

共有

OpenAPI Generator for Flask

メモ

簡単な動作確認

ひとまず分かりやすかった OpenAPI GeneratorでPython Web API構築 をそのまま試す。

1
2
3
4
5
6
7
$ mkdir -p  ~/Sources/OpenAPIGenFlaskSample/original
$ cd ~/Sources/OpenAPIGenFlaskSample/original
$ cat << EOF > openapi.yaml

(snip)

EOF

openapi.yamlの中身は OpenAPI GeneratorでPython Web API構築 に記載されている。

1
2
3
$ mkdir -p server client
$ docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/openapi.yaml -g python-flask -o /local/server
$ docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/openapi.yaml -g go -o /local/client

今回用があるのはサーバ側のPython実装(Flask)の方なので、そちらを確認する。 記事にもあるが以下のようなファイルが生成されているはず。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ tree
.
├── Dockerfile
├── README.md
├── git_push.sh
├── openapi_server
│   ├── __init__.py
│   ├── __main__.py
│   ├── controllers
│   │   ├── __init__.py
│   │   ├── security_controller.py
│   │   └── stock_price_controller.py
│   ├── encoder.py
│   ├── models
│   │   ├── __init__.py
│   │   ├── base_model.py
│   │   ├── error.py
│   │   ├── ok.py
│   │   └── stock_price.py
│   ├── openapi
│   │   └── openapi.yaml
│   ├── test
│   │   ├── __init__.py
│   │   └── test_stock_price_controller.py
│   ├── typing_utils.py
│   └── util.py
├── requirements.txt
├── setup.py
├── test-requirements.txt
└── tox.ini

まずは、 __main__.py を見てみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3

import connexion

from openapi_server import encoder


def main():
app = connexion.App(__name__, specification_dir='./openapi/')
app.app.json_encoder = encoder.JSONEncoder
app.add_api('openapi.yaml',
arguments={'title': 'Stock API'},
pythonic_params=True)

app.run(port=8080)


if __name__ == '__main__':
main()

上記の通り、 connexionを用いていることがわかる。 connexionはFlaskで動作する、APIとpython関数をマッピングするためのパッケージである。 connexionについては、 connexionを使ってPython APIサーバのAPI定義と実装を関連付ける のような記事を参考にされたし。

openapi_server/controllers/stock_price_controller.py を確認する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import connexion
from typing import Dict
from typing import Tuple
from typing import Union

from openapi_server.models.error import Error # noqa: E501
from openapi_server.models.stock_price import StockPrice # noqa: E501
from openapi_server import util


def stock_price(security_cd): # noqa: E501
"""株価取得

現在の株価を取得する # noqa: E501

:param security_cd: 証券コードを指定する
:type security_cd: str

:rtype: Union[StockPrice, Tuple[StockPrice, int], Tuple[StockPrice, int, Dict[str, str]]
"""
return 'do some magic!'

operationId にて指定した名称がコントローラのメソッド名に反映されている。

parameters にて指定したパラメータがコントローラの引数になっていることが確認できる。

components にて指定したスキーマに基づき、openapi_server/models 以下に反映されていることが分かる。 今回の例だと、戻り値用の StockPrice やOK、Errorが定義されている。 なお、これはOpenAPIにて生成されたものであり、それを編集して使うことはあまり想定されていないようだ。

openapi_server/util.py にはデシリアライザなどが含まれている。

さて、記事通り、Dockerで動かしてみる。

1
2
$ docker build -t openapi_server .
$ docker run -p 8080:8080 openapi_server

試しに、適当な引数を与えて動かすと、実装通り戻り値を得られる。

1
2
$ curl http://localhost:8080/v1/sc/4721/stockPrice
"do some magic!"

なおDockerfileはこんな感じである。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM python:3-alpine

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

COPY requirements.txt /usr/src/app/

RUN pip3 install --no-cache-dir -r requirements.txt

COPY . /usr/src/app

EXPOSE 8080

ENTRYPOINT ["python3"]

CMD ["-m", "openapi_server"]

Docker化しなくてもそのままでも動く。 ここでは一応venvを使って仮想環境を構築しておく。

1
2
3
4
$ python -m venv venv
$ . venv/bin/activate
$ pip install -r requirements.txt
$ python -m openapi_server

参考

記事

共有

IDS Dataspace Protocol

メモ

最近のEDCでは、IDSが提唱しているDataspace Protocolが使用されている。 International-Data-Spaces-Association/ids-specification を眺めてコンテンツをざっくり書き下す。

v0.8の位置づけ

以下のような記載あり。あくまでドラフト扱い。

Working Draft 1 February 2023

概要

スキーマとプロトコルの仕様を示すものである。

  • データのPublish
  • 利用合意形成
  • データアクセス

自律エンティティ(要はコネクタか)間でデータ共有するにはメタデータの提供が必要である。 以下、メタデータ管理として挙げられていた項目。

  • data assetをDCAT Catalogに入れる方法
  • Usage ControlをODRLポリシーとして表現する方法
  • 契約合意を構文的に電子記録する方法
  • データ転送プロトコルを用いてデータアセットにアクセスする方法

仕様として挙げられていたのは以下。

  • Dataspace ModelとDataspace Terminologyドキュメント
  • Catalog ProtocolとCatalog HTTPS Bindings(DCATカタログの公開、アクセス方法)
  • Contract Negotiation ProtocolとContract Negotiation HTTPS Bindingドキュメント
  • Transfer Process ProtocolとTransfer Process HTTPS BIndingsドキュメント

概ね、モデルを明らかにし、そのうえでカタログ、契約合意、データ転送の一連の流れに沿って仕様が示されている、と言える。

注意点として、データ転送プロセス自体は言及せずプロトコルのみ示されていることである。

Protocol Overviewに各種情報へのリンクが載っている。

インターオペラビリティの確保

Dataspace Protocolはインターオペラビリティを確保するために用いられる、とされている。 ただし、本プロトコルによりテクニカルな側面は担保されるが、セマンティックな側面はData Space参加者により担保されるべきとしている。

なお、異なるData Spaceを跨ぐインターオペラビリティは本ドキュメントの対象外である。

全体概要は、Protocol Overviewの図を参照されたし。

以下は本ドキュメントではスコープ外だが、Data Space Protocolにとって必要。

  • Identity Provider
    • Trust Frameworkを実現するための情報を提供する
    • 参加者(のエージェント、コネクタ)のバリデーション、請求内容のバリデーションが基本的な機能であるが、 その請求の構造・内容はData Spaceごと、もっといえばIdentity Provider毎に異なる。
  • モニタリング
  • Policy Engine

Terminology

Terminologyに記載されているが、量は多くない。

特にポリシー周りの用語には注意したい。

  • Assest: Participantによって共有されるデータやテクニカルサービス
  • Policy: Asset利用のためのルール、義務、制限
  • Offer: とあるAssetに結びつけられたPolicy
  • Agreement: とあるAssetに結びつけられた具体的なPolicy。Provier、Consumerの両Participantにより署名されている

なお、関係性という意味では、Information ModelやParticipantAgent周りのクラス設計の方がわかりやすい。

Information Model

Information Modelに記載されている。 Information Modelの関係図 に関係図が載っている。

以下、ポイントのみ記載。

Dataspace Authority

ひとつ or 複数のDataspaceを管理する。 Participantの登録も含む。Participantにビジネスサーティフィケーションの取得(提出?)を求める、など。

Participant、Participant Agent

ParticipantがDataspaceへの参加者であり、Participant Agentが実際のタスクを担う。 Participant Agentはcredentialから生成されたverifiable presentationを用いる。credentialは第三者のissuerから発行されたものを用いる。 また第三者のIdentity providerか提供されたID tokenも用いる。

Identity Provider

トラストアンカー。 ID tokenを払い出す。ID tokenはParticipant Agentのアイデンティティの検証を行う。

複数のIdentity ProviderがひとつのDataspaceに存在することも許容される。

ID tokenのセマンティクスは本仕様書の対象外。

Identity Providerは外部でもよいし、Participant自身(e.g. DID)でもよい。

Credential Issuer

Verifiable Credentialを発行する。

ParticipantAgent周りのクラス設計

ParticipantAgent周りのクラス設計の図に大まかな設計が書かれている。

これによると、CatalogServiceとConnectorは同じParticipantAgentの一種である。

DCAT CatalogにはAsest EntryとDCAT DataServiceが含まれる。 ちなみに、DCAT DataServiceはAssetの提供元となるConnectorへのReferenceである。

Asset EntryはODRL Offerを保持する。当該Assetに紐づけられたUsage Control Policyである。

ConnectorもParticipant Agentの一種である。 Contract NegotiationとData Transferを担う。 Contract Negotiation結果、ODRL Agreementが生成される。ODRL Agreementは、合意された当該Assetに関するUsage Control Policyと言える。

実態的なクラス

Dataspace AuthorityやParticipant Agentのように、実際のフローには登場しないエンティティもあるようだ。 Classesドキュメント に実際のフローに登場するクラスの説明が記載されている。

Catalog Protocol仕様

Catalog Protocolの仕様に以下のような要素の仕様が記載されている。

  • message type: メッセージの構造
  • message: message typeのインスタンス
  • catalog: データ提供者によりオファーされたDCAT Catalog
  • catalog service: 提供されたasset(の情報)を公表するParticipant Agent
  • Consumer: 提供されたassetを要望するParticipant Agent

Message Type

JSON-LDでシリアライズされたメッセージ。なお、将来的にはシリアライズ方式が追加される可能性がるようだ。

  • CatalogRequestMessage: ConsumerからCatalog Serviceに送られる要望メッセージ。filterプロパティあり。
  • Catalog: Providerから送られるAsset Entiry
  • CatalogEror: ConsumerもしくはProviderから送られるエラーメッセージ
  • DatasetRequestMessage: ConsumerからCatalog Serviceに送られる要望メッセージ。Catalog ServiceはDataset(DCAT Dataset)を答える。それにはデータセットのIDが含まれる。

DCATとIDS Informationモデルの対応関係

  • Asset Entity: DCAT Dataset
  • Distributions: DCAT Distributions
  • DataService: ConnectorのようなIDSサービスエンドポイント。なお、dataServiceTypeが定義されている。現状ではdspace:connectorのみか。

Technical Consideration

Technical Considerationsとしてはクエリやフィルタ、レプリケーションプロトコル、セキュリティ、ブローカについて言及されている。

Catalog HTTPS Binding

Contract Negotiation仕様

ProviderとConsumerの間のContract Negotiation(CN)。 CNは、 https://www.w3.org/International/articles/idn-and-iri/ にてユニークに識別できる。 CNは状態遷移を経る。それらはProviderとConsumerにトラックされる。

ステート変化

Contract Negotiationのステート変化の図 にステート変化の概要が描かれている。

ステート 説明
REQUESTED ConsumerからOfferに基づいてリクエストが送られた状態。ProviderがAckを返した後。
OFFERED ProviderがConsumerにOfferを送った状態。ConsumerがAckを返した後。
ACCEPTED Consumerが最新のContract Offerを承諾した状態。ProviderがAckを返した後。
AGREED Providerが最新のContract Offerに承諾し、Consumerに合意を送った状態。ConsumerがAckを返した後。
VERIFIED ConsumerがProviderに合意検証結果を返した状態。ProviderがAckを返した後。
FINALIZED Providerが自身の合意検証結果を含むファイナライズのメッセージをConsumerに送った状態。ConsumerがAckを返した後。データがConsumerに利用可能な状態になっている。
TERMINATED ProviderかConsumerがCNを終了させた状態。どちらからか終了メッセージが送られ、Ackが返る。

メッセージタイプ

Contract NegotiationのMessage Types に上記ステート変化における各種メッセージの説明が記載されている。 ほぼ名称通りの意味。

Contract Negotiation HTTPS Bindings

Transfer Process仕様

CNと同じく、Transfer Process (TP) もProviderとConsumerの両方に関係する。 またCNと同じくステート変化が定義されている。

Control and Data Plane

TPはConnectorにより管理される。Connectorは2個のロジカルコンポーネントで成り立つ。

  • Control Plane: 対抗のメッセージを受領し、TPステートを管理する
  • Data Plane: 実際の転送を担う

これらはロジカルなものであり、実装では単独プロセスとしてもよい。

アセット転送のタイプ

  • push / pull
  • finite / non-finite

Push Transferの流れPull Transferの流れ にそれぞれの流れのイメージが載っている。 基本的な流れは同じだが、実際のデータやり取りの部分だけ異なる。

finiteデータの場合、データの転送が終わったらTPが終わる。 一方、non-finiteデータの場合は明示的に終了させる必要がある。 non-finiteデータはストリームデータ、APIエンドポイントが例として挙げられている。

ステート変化

Trasnfer Processのステート変化 にステート変化の様子が描かれている。

ステートはREQUESTED、STARTED、COMPLETED、SUSPENDED、TERMINATED。

Message Type

Transfer ProcessのMessage Type にメッセージタイプが記載されている。 特筆すべきものはない。

参考

共有

How to create SDK (WIP)

メモ

注意

まだ書き始めの殴り書きなので、中身の完成度や信ぴょう性がかなり低い文章である。

世の中でSDKと呼ばれているものには何が含まれているのか

  • 開発者の強い味方!「SDK(ソフトウェア開発キット)」とは?
    • KDDIによるブログ記事
    • 「ソフトウェアを開発する際に必要なプログラムやAPI・文書・サンプルなどをまとめてパッケージ化したもの」
  • e-WordsのSDK 【Software Development Kit】 ソフトウェア開発キット
    • 「SDKとは、あるシステムに対応したソフトウェアを開発するために必要なプログラムや文書などをひとまとめにしたパッケージのこと。システムの開発元や販売元が希望する開発者に配布あるいは販売する。インターネットを通じて公開されているものもある。」
  • AWS SDKとは?
    • 「特定のプラットフォーム、オペレーティングシステム、またはプログラミング言語で実行されるコードを作成するには、デバッガー、コンパイラー、ライブラリなどのコンポーネントが必要です。SDK は、ソフトウェアの開発と実行に必要なすべてを 1 か所にまとめます。」

製品により異なるのが前提ではあるが、おおむね含まれるとされるのは以下。

  • 開発ツール
    • コンパイラ、デバッガ、プロファイラー、デプロイツール、IDE
  • 既成のプログラム
    • クラスファイル、(APIなど)ライブラリ、モジュール、ドライバ
  • 文書ファイル
    • API、通信プロトコル
  • プログラムファイル
    • サンプルプログラム

SDKの具体例

工夫点

開発ツールの提供形態

  1. 開発に必要な環境ごと丸ごと提供する方法、2. 特定のIDEを前提としたプラグインを提供する方法、などがある。
方法例 メリット デメリット
丸ごと提供 すぐに開発始められる 自由度が低い
プラグイン提供 自由度が高い 環境準備が手間大きめ

ライセンス

SDK自体のライセンスだけではなく、SDKに含まれるライセンスにも注意を払う必要がある。

各言語向けのバインディング

サービスを使うためのSDKの場合は、様々な開発言語向けのバインディングを提供することも考慮したい。

先人の知恵

SDK の開発と維持の難しさ にはAndroidやiOS向けのSDKを開発した際の経緯が書かれていた。

参考

SDKとは?

SDKの具体例

ばいんでぃば

先人の知恵

共有

check windows cpu resources

メモ

マシンが暴走気味になることがあり、原因追跡するためにLinuxなどでいうpsコマンド相当の内容をログ取りたいと思った。 ので、軽く調べたところ、以下のウェブサイトがヒットした。

Windows がなんか重いときにコマンドで調べる(WMIC PROCESS)

上記サイトが丁寧に解説してくれている。 その中から、今回は CPUの利用率でフィルタ あたりを参考にした。

コマンド例

1
WMIC PATH Win32_PerfFormattedData_PerfProc_Process WHERE "PercentUserTime > 1" GET Name,IDProcess,PercentUserTime,CommandLine /FORMAT:CSV

ユーザタイムが1%以上使われたものをリストしている。 もし行数制限したい場合は、 PowerShellでhead,tail相当の処理を行う あたりを参考にする。

コマンド例

1
(WMIC PATH Win32_PerfFormattedData_PerfProc_Process WHERE "PercentUserTime > 1" GET Name,IDProcess,PercentUserTime /FORMAT:CSV)[0..10]

上記では、コマンドの引数が分からない。 そこで、それを詳細に調べようとすると、 コマンドラインからプロセスを特定 を参考にするとよい。 おいおい、自動的に要素を結合できるようにしよう。

参考

共有

Open project of EDC Connector with Intellij

1. メモ

EDC Connector 公式GitHub のプロジェクトをIntellijで開くための手順メモ。 いろいろなやり方があるが一例として。

1.1. 準備

gitクローンしておく。 ここでは、gitプロトコルを用いているが環境に合わせて適宜変更してクローンする。

1
2
$ git clone git@github.com:eclipse-edc/Connector.git
$ cd Connector

なお、必要に応じて特定のタグをチェックアウトしてもよい。 ここでは、v0.2.0をチェックアウトした。

1
$ git checkout -b v0.2.0 refs/tags/v0.2.0

当該プロジェクトでは、ビルドツールにGradleを用いている。プロジェクトに gradlew も含まれている。 本環境では、以下のようにGradle8.0を利用した。

◇参考情報

1
2
3
4
5
6
7
$ cat gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

◇参考情報おわり

さて、ビルドに利用するOpenJDKをインストールしておく。 ここでは17系を用いた。(もともと手元の環境にあった8系を用いようとしたら互換性エラーが生じたため、17系を用いることとした)

1
$ sudo apt install openjdk-17-jdk

なお、 Gradle Compatibility に互換性のあるGradleとJDKのバージョンが記載されているので参考にされたし。

この状態でIntellijを使わずにビルドするには、以下のように実行する。 ここでは念のために、コマンドでビルドできることを確かめるため、あらかじめ以下を実行しておいた。

1
$ ./gradlew clean build

特に問題なければ、success表示が出て終わるはず。

1.2. Intellijで開く

ひとまず、プロジェクトトップでInteliljを開く。

1
$ <Intellijのホームディレクトリ>/bin/idea.sh . &

なお、IntellijにGradle拡張機能がインストールされていなければ、インストールしておく。「Files」→「Settings」→「Plugins」→「Gradleで検索」。

また使用するJDKを先にインストールしたJDK17を用いるようにする。「Files」→「Project Structure」→「Project」を開く。 「SDK:」のところで先ほどインストールしたJDKを設定する。 「Language Lavel:」も合わせて変更しておく。

開いたら、右側の「Gradle」ペインを開き、設定ボタンを押して「Gradle settings ...」を選択する。

「Build and run」章のところは、「Gradle」が選択されていることを確認する。

「Gradle」章のところは、「Use Gradle from:」でgradle wrapperの情報を用いるようになっていること、「Gradle JVM」でProject JDKを用いるようになっていることを確認する。

特に問題なければ、 BUILD SUCCESSFUL となるはずである。

1.3. トラブルシュート

1.3.1. Intellijのメモリ不足

ビルド中にヒープが不足することがある。 その場合は、 Intellijのメモリを増やす設定 を参考に、ヒープサイズを増やす。

「Help」→「Memory Setting」を開き、「2024」(MB)あたりにしておく。

1.3.2. Gradleのメモリ不足

ビルド中にヒープが不足することがある。 プロジェクト内にある、 gradle.properties 内に以下を追記する。ここでは最大4GBとした。

◇diff

1
2
3
4
5
6
7
8
9
10
11
diff --git a/gradle.properties b/gradle.properties
index 376da414a..d8da811a9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -7,3 +7,6 @@ edcGradlePluginsVersion=0.1.4-SNAPSHOT
metaModelVersion=0.1.4-SNAPSHOT
edcScmUrl=https://github.com/eclipse-edc/Connector.git
edcScmConnection=scm:git:git@github.com:eclipse-edc/Connector.git
+
+# Increase Gradle JVM Heap size
+org.gradle.jvmargs=-Xmx4096M

1.3.3. JDKバージョンの不一致

当初、環境にあったJDK8系を用いていたが、ビルド時に構文エラー(互換性のエラー)が生じたため、JDK17を用いるようにした。

1.3.4. Gradleで使うJDKの選択

環境によっては、独自の証明書などをJDKのcacertsに導入して用いているかもしれない。 その場合は、用いているGradle、JDKに注意が必要。

例えば、/etc/ssl/certs/java/cacertsに独自の証明書を追加し、それを各JDKで用いるようにすると良い。

1
2
3
4
5
6
7
cd /etc/ssl/certs/java
cp cacerts cacerts.org
keytool -import -alias CA -keystore cacerts -file ~/any.cer
# passwordはchnageit
cd ~/.jdks/jbr-xxxxxx/lib/security
mv cacerts cacerts.org
ln -s /etc/ssl/certs/java/cacerts cacerts

2. 参考

共有

GitHub Actions for Hexo with Pandoc

メモ

遅ればせながら(?)、Circle CIからGitHub ActionsにHexo使ったブログのビルド・公開を移行する、と思い立ち、 このブログ生成ではPandocなどを利用していることから、独自のコンテナ環境があるとよいかと思い、調査。 Pandocインストールを含むDockerfileを準備し利用することにした。

◆理由

  • Pandoc環境を含む、GitHub Actionがなさそうだった
  • デプロイまで含めたActionがほとんどだったが、ビルドとデプロイを分けたかった(デプロイ部分は他の取り組みと共通化したかった)

Docker コンテナのアクションを作成する を参考にアクションを作成し、登録する。 またHexo環境の作成については、 heowc/action-hexo が参考になった。

GitHub Actionで用いられるDockerfile等の作成

Docker コンテナのアクションを作成する を参考に以下のファイルを作成する。 なお、 dobachi/hexo-pandoc-action に該当するファイルを置いてある。

1
2
3
4
5
[dobachi@home HexoPandocDocker]$ ls -1
Dockerfile
README.md
action.yml
entrypoint.sh

Dockerfile

Dockerファイル内では依存するパッケージのインストール、Pandocのインストールを実施。 NPMの必要パッケージインストールは、entrypoint側で実施するため、ここでは不要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[dobachi@home HexoPandocDocker]$ cat Dockerfile
FROM ubuntu:20.04

# You may need to configure the time zone
ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# Install minimal packages to prepare Hexo
RUN apt-get update && \
apt-get install -y git-core npm wget

# Install Pandoc using deb package
RUN wget https://github.com/jgm/pandoc/releases/download/2.18/pandoc-2.18-1-amd64.deb
RUN apt-get install -y ./pandoc-2.18-1-amd64.deb

# Copy script for GitHub Action
COPY entrypoint.sh /entrypoint.sh

# Configure entrypointo for GitHub Action
ENTRYPOINT ["/entrypoint.sh"]

action.yml

最低限の設定のみ使用。

1
2
3
4
5
6
7
[dobachi@home HexoPandocDocker]$ cat action.yml
# action.yml
name: 'Build Hexo with Pandoc'
description: 'build Hexo blog using Pandoc generator'
runs:
using: 'docker'
image: 'Dockerfile'

entrypoint.yml

npmパッケージの依存パッケージをインストールし、HTMLを生成する。

1
2
3
4
5
6
7
8
9
[dobachi@home HexoPandocDocker]$ cat entrypoint.sh
#!/bin/sh -l

# Instlal Hexo and dependencies.
npm install -g hexo-cli
npm install

# Build
hexo g

GitHub Actionの設定

パーソナルトークンの生成

予め、GitHubの設定からパーソナルトークンを生成しておく。後ほど使用する。

GitHub Actionを動かしたいレポジトリのAction用トークンを設定する。

ここでは PERSONAL_TOKEN という名前で設定した。 内容は、先に作っておいたもの。

ワークフローの設定ファイル作成

GitHub Actionを使ってビルドするHexoブログのレポジトリにて、 .github/workflows/gh-pages.yml を生成する。

ここではプライベートのレポジトリで作成したHexo原稿をビルドし、 パブリックなGitHub Pages用レポジトリにpushする例を示す。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[dobachi@home memo-blog-text]$ cat .github/workflows/gh-pages.yml
name: GitHub Pages

on:
push:
branches:
- master # Set a branch name to trigger deployment
pull_request:

jobs:
deploy:
runs-on: ubuntu-20.04
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
steps:
- uses: actions/checkout@v2
with:
submodules: true # Fetch Hugo themes (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod

# Deploy hexo blog website.
- name: Generate
uses: dobachi/hexo-pandoc-action@v1.0.8

# Copy source to repository for convinience
- name: copy sources
run: |
sudo chown -R runner public
cp -r ./source ./public/source

- name: Deploy
uses: peaceiris/actions-gh-pages@v3
if: ${{ github.ref == 'refs/heads/master' }}
with:
external_repository: dobachi/memo-blog
personal_token: ${{ secrets.PERSONAL_TOKEN }}
publish_dir: ./public

参考

共有

Nest VM on Hyper-V

メモ

Get-VM を利用して、VMの名称を確認する。

1
2
3
4
5
PS C:\Windows\system32> Get-VM   

(snip)

ubu18 Running 0 9344 24.00:30:35.9900000 正常稼働中 9.0

入れ子になった仮想化による仮想マシンでの Hyper-V の実行 の通り、VM 名称を指定しながら設定変更する。

1
PS C:\Windows\system32> Set-VMProcessor -VMName ubu18 -ExposeVirtualizationExtensions $true

参考

共有

Getting_started_CKAN

メモ

CKAN公式サイト によると、CKANはデータマネジメントシステムである。 CKAN公式サイト の内容をもとに進める。

このメモの時点では、CKAN 2.9.5を対象とする。

なお、公式ドキュメントによると CKAN is a tool for making open data websites. とされており、 あくまで「オープンデータ向け」であることが伺える。

ドキュメントより

特徴

CKANの特徴 の通り、以下のような特徴を有している。

  • CKAN APIを提供
  • バックエンドのデータストア(アドホックなデータ置き場)
    • データソースかデータがpullされ、ストアされる
    • Data Explorer拡張を利用することでデータのプレビューが可能になる
    • 検索、更新などをデータ全てをダウンロード・アップロードせずにできる ★要確認
    • DataPusher 機能と一緒に使われることが多い
      • データソースから表形式のデータを取得し、DataStore APIを用いてCKANにデータ登録する仕組み
  • 拡張機能
  • 地理空間機能
  • メタデータ
  • データ管理のためのUI
  • キーワード検索
  • テーマ設定
  • 可視化
  • フェデレーション
  • ファイルストア

データのモデル

Users, organizations and authorization に記載の通り、基本的にはOrganizationに紐づけてDatasetが登録される。 Datasetは任意のフォーマットでよい。CKANにアップロードしてもよいし、単純にURLを登録してもよい。 デフォルトの挙動では、登録されたDatasetはPrivate扱いになり、所有するOrganizationに所属するユーザのみ見られる。

Adding a new dataset にある通り、Datasetを登録できる。 なお、Licenseに並んでいる項目を見ると、やはりオープンデータとの相性がよさそう、と感じる。

DataStoreとDataPusher

DataStore Extension に記載の通り、 DataStore拡張機能を利用すると、Data Explorer拡張機能と併用することで自動的にプレビュー可能になる。 またDataStore APIを利用し、データ本体を丸ごとダウンロードすることなく、検索、フィルタ、更新できる。

自動的にデータを取り込むData Pusher拡張機能と併用されることが多い。

DataPusher の通り、データを取り出し自動的にCKANに登録できる。 ただし、注意書きにある通り、制約があるので注意。

Data Dictionary の通り、DataStoreのスキーマ情報はデータ辞書で管理できる。

Downloading Resources の通り、表形式のデータをCSVやエクセルで読み込める形式でダウンロードできる。

DataStore APIを利用すると、DataStoreリソースを検索したり、インクリメンタルにデータを追加できる。 バリデーションの機能もあるようだ。

APIについては、 API reference に記載されている。

DataStoreの拡張

Extending DataStore に記載の通り、独自のDataStoreを利用可能。 正確には、実装可能。

DatastoreBackend がベースクラスである。

バックグラウンドジョブ

Background jobs の通り、メインのアプリケーションをブロックせずに処理を実行できる。

Dockerで動作確認

セットアップ

Installing CKAN with Docker Compose を参考に、Docker Composeを利用して簡易的に動作確認する。 事前に、DockerとDocker Composeを導入しておくこと。

また、 オープンソースのデータ管理システム「CKAN」を試してみた が分かりやすかったのでおすすめ。

ソースコードをダウンロード。 ここではステーブルバージョンを利用することにした。

1
2
3
4
$ mkdir -p ~/Sources
$ cd ~/Sources
$ git clone https://github.com/ckan/ckan.git
$ git checkout -b ckan-2.9.5 tags/ckan-2.9.5

設定ファイルのコピー 適宜パスワードを変更するなど。 環境によっては、サイトURLを変更するのを忘れずに。

1
$ cp contrib/docker/.env.template contrib/docker/.env

ビルドと起動

1
2
$ cd contrib/docker
$ docker-compose up -d --build

後の作業のため、ストレージを変数化(しなくても、作業はできる。ボリュームのパスを覚えておけばよい) (公式サイト手順の「Convenience: paths to named volumes」に記載あり)

1
2
3
4
5
6
$ export VOL_CKAN_HOME=$(docker volume inspect docker_ckan_home | jq -r -c '.[] | .Mountpoint')
$ echo $VOL_CKAN_HOME
$ export VOL_CKAN_CONFIG=$(docker volume inspect docker_ckan_config | jq -r -c '.[] | .Mountpoint')
$ echo $VOL_CKAN_CONFIG
$ export VOL_CKAN_STORAGE=$(docker volume inspect docker_ckan_storage | jq -r -c '.[] | .Mountpoint')
$ echo $VOL_CKAN_STORAGE

Datastoreとdatapusherを機能させるため、いくつか設定する。 まずはデータベースの設定。

1
$ docker exec ckan /usr/local/bin/ckan -c /etc/ckan/production.ini datastore set-permissions | docker exec -i db psql -U ckan

プラグインなどの設定

1
$ sudo vim $VOL_CKAN_CONFIG/production.ini

なお変更点は以下の通り。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
$ sudo diff -u $VOL_CKAN_CONFIG/production.ini{.org,}
--- /var/lib/docker/volumes/docker_ckan_config/_data/production.ini.org 2022-01-22 22:22:11.992878002 +0900
+++ /var/lib/docker/volumes/docker_ckan_config/_data/production.ini 2022-01-22 22:23:56.367637134 +0900
@@ -21,7 +21,7 @@
use = egg:ckan

## Development settings
-ckan.devserver.host = localhost
+ckan.devserver.host = ubu18
ckan.devserver.port = 5000

@@ -47,10 +47,10 @@ [2/7337] # who.timeout = 86400

## Database Settings
-sqlalchemy.url = postgresql://ckan_default:pass@localhost/ckan_default
+sqlalchemy.url = postgresql://ckan_default:pass@ubu18/ckan_default

-#ckan.datastore.write_url = postgresql://ckan_default:pass@localhost/datastore_default
-#ckan.datastore.read_url = postgresql://datastore_default:pass@localhost/datastore_default
+ckan.datastore.write_url = postgresql://ckan_default:pass@ubu18/datastore_default
+ckan.datastore.read_url = postgresql://datastore_default:pass@ubu18/datastore_default

# PostgreSQL' full-text search parameters
ckan.datastore.default_fts_lang = english
@@ -101,7 +101,7 @@
## Redis Settings

# URL to your Redis instance, including the database to be used.
-#ckan.redis.url = redis://localhost:6379/0
+ckan.redis.url = redis://ubu18:6379/0


## CORS Settings
@@ -119,7 +119,7 @@
# Add ``datapusher`` to enable DataPusher
# Add ``resource_proxy`` to enable resorce proxying and get around the
# same origin policy
-ckan.plugins = stats text_view image_view recline_view
+ckan.plugins = stats text_view image_view recline_view datastore datapusher

# Define which views should be created by default
# (plugins must be loaded in ckan.plugins)
@@ -181,7 +181,7 @@

# Make sure you have set up the DataStore

-#ckan.datapusher.formats = csv xls xlsx tsv application/csv application/vnd.ms-excel application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
+ckan.datapusher.formats = csv xls xlsx tsv application/csv application/vnd.ms-excel application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
#ckan.datapusher.url = http://127.0.0.1:8800/
#ckan.datapusher.assume_task_stale_after = 3600

@@ -206,7 +206,7 @@

#email_to = errors@example.com
#error_email_from = ckan-errors@example.com
-#smtp.server = localhost
+smtp.server = ubu18
#smtp.starttls = False
#smtp.user = username@example.com
#smtp.password = your_password

↑の変更点を説明する。

まず、今回はVM上のDockerで起動したので、SSHトンネル経由でのアクセスで困らないように開発環境ホスト名を localhost から変更した。

また、Datastore機能、Datapusher機能を利用するためにプラグインを追加。

Datapusherのフォーマットを有効化(コメントアウトを解除)

コンテナを再起動。

1
$ docker-compose restart

公式サイトに記載の通り、APIに試しにアクセスるとレスポンスがあるはず。

1
$ curl 'http://localhost:5000/api/3/action/datastore_search?resource_id=_table_metadata'

アドミンユーザ作成。

1
$ docker exec -it ckan /usr/local/bin/ckan -c /etc/ckan/production.ini sysadmin add johndoe

http://<サイトのurl>/ckan にアクセスすれば、ウェブUIが確認できる。 先に設定した、アドミンユーザでとりあえずログインする。

データストアとしてMinIO環境を整える

MinIOのDocker を参考に、Dockerでサーバを立ち上げる。

1
$ docker run -p 9000:9000 -p 9001:9001 minio/minio server /data --console-address ":9001"

http://<立ち上げたインスタンス>:9000 でMinIOのウェブコンソールにアクセスできる。 特に設定していなければ、ID:minioadmin、パスワード:minioadminである。

MinIのUI
MinIのUI
MinIのUI

別のコンソールで mcクライアントを立ち上げる。

1
$ docker run --name my-mc --hostname my-mc -it --entrypoint /bin/bash --rm minio/mc

エイリアスを設定し、バケットを作成する。 (URLは、MinIO起動時にコンソールに表示されるAPI用のURLを利用すること)

1
2
# mc alias set myminio http://172.17.0.2:9000 minioadmin minioadmin
# mc mb myminio/mybucket

ファイルを作成し、MinIOにアップロード(コピー)する。

1
2
# echo "hoge" > /tmp/hoge.txt
# mc cp /tmp/hoge.txt myminio/mybucket/hoge.txt

AWS CLIでアクセスする

AWS CLI v2 Docker image を利用してAWS CLIを起動し、MinIOにアクセスする。 なお、コンテナで起動するのでそのままではホスト側の ~/.aws にアクセスできない。そこで -v オプションを利用しマウントしてから configure するようにしている。 また、S3プロトコルでアクセスする際には、今回は手元のMinIO環境を利用するため、エンドポイントを指定していることに注意。

1
2
3
$ docker run --rm -it -v ~/.aws:/root/.aws amazon/aws-cli configure --profile myminio
$ docker run --rm -it -v ~/.aws:/root/.aws amazon/aws-cli --profile myminio s3 --endpoint-url http://172.17.0.2:9000 ls s3://mybucket/hoge.txt
2022-01-21 16:38:19 5 hoge.txt

無事にS3プロトコルでアクセスできたことが確かめられた。

CKANのUIで操作

組織の作成

組織作成
組織作成
組織作成

データセット作成

データセット作成
データセット作成
データセット作成

先ほどのS3プロトコルURL( s3://mybucket/hoge.txt )を登録する。

(補足)初期化

何らかの理由でDockerの環境をまっさらに戻したいことがあるかもしれない。 その場合は以下の通り。

1
2
3
4
5
6
7
$ docker-compose down
$ docker rmi -f docker_ckan docker_db
$ docker rmi $(docker images -f dangling=true -q)
$ docker volume rm docker_ckan_config docker_ckan_home docker_ckan_storage docker_pg_data docker_solr_data
$ docker-compose build
$ docker-compose up -d
$ docker-compose restart ckan # give the db service time to initialize the db cluster on first run

(補足)FIWAREのCKANは拡張済?

FIWAREのCKAN の説明を見ると、Dataset登録時に Searchable のような項目がある。公式2.9.5で試したときにはなかった。 他にも Additional Info という項目があり、そこには OAuth-Token という項目もあった。 このように、特にアクセス管理や認証認可の機能が拡張されているのか、と思った。

インストール(パッケージ)

Ubuntu18環境にCKANをインストールする。 基本的には、 Installing CKAN from package に従う。

参考

CKAN

MinIO

AWS CLI

共有