Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/run-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ jobs:
uses: helm/kind-action@v1.14.0
with:
cluster_name: dcp-demo
# maps host ports 80/443 into the node container; together with the Traefik
# hostPort settings in values.yaml this exposes the gateway on localhost
# without a port-forward
config: kind-config.yaml

- name: "Load runtime images into KinD"
run: |
Expand All @@ -84,9 +88,6 @@ jobs:
# install Gateway API CRDs
kubectl apply --server-side --force-conflicts -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.1/standard-install.yaml

# forward port 80 -> 8080
kubectl -n traefik port-forward svc/traefik 8080:80 &

sleep 5 # to be safe

- name: "Deploy MVD common resources"
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,14 @@ documentation for more information.

```shell
# Create the cluster
kind create cluster -n mvd
kind create cluster -n mvd --config kind-config.yaml
```

> [`kind-config.yaml`](kind-config.yaml) maps host ports 80/443 into the node container. Together with the `hostPort`
> settings in the Traefik values ([`values.yaml`](values.yaml)) this makes the gateway reachable on `http://localhost`
> without any port-forwarding. Port mappings are fixed at cluster creation — if you created the cluster without this
> config, recreate it. Host ports 80/443 must be free when the cluster is created.

### 4.2 Deploy the MVD components

The following commands deploy the MVD components to the cluster.
Expand Down Expand Up @@ -290,11 +295,11 @@ kubectl wait -A \
--selector=type=edc-job \
--for=condition=complete job --all \
--timeout=90s

# forward port 80 (might require sudo)
kubectl port-forward svc/traefik 80:80 -n traefik
```

No port-forwarding is needed: Traefik binds ports 80/443 directly on the KinD node, and the cluster's port mappings
(see [`kind-config.yaml`](kind-config.yaml)) expose them on `http://localhost`.

Once all jobs have finished, type `kubectl get pods -A` and verify the output:

```shell
Expand Down
5 changes: 3 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ format.version = "1.1"

[versions]
awaitility = "4.3.0"
dataplane-sdk = "0.0.10-SNAPSHOT"
dataplane-sdk = "1.0.0"
edc = "0.19.0-SNAPSHOT"
edc-build = "1.5.2"
jackson = "2.22.0"
Expand Down Expand Up @@ -56,7 +56,8 @@ jakarta-json-api = { module = "jakarta.json:jakarta.json-api", version.ref = "ja
tink = { module = "com.google.crypto.tink:tink", version = "1.21.0" }
jackson-datatype-jakarta-jsonp = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jakarta-jsonp", version.ref = "jackson" }
parsson = { module = "org.eclipse.parsson:parsson", version.ref = "parsson" }
dataplane-sdk = { module = "org.eclipse.dataplane-core:dataplane-sdk", version.ref = "dataplane-sdk" }
dataplane-sdk = { module = "org.eclipse.dataplane-core:dataplane-sdk-core", version.ref = "dataplane-sdk" }
dataplane-sdk-jakarta-ee = { module = "org.eclipse.dataplane-core:dataplane-sdk-jakarta-ee", version.ref = "dataplane-sdk" }
nimbus-jwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "nimbus" }

# BOM modules
Expand Down
30 changes: 30 additions & 0 deletions kind-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#
# Copyright (c) 2026 Metaform Systems, Inc.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License, Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
#
# Contributors:
# Metaform Systems, Inc. - initial API and implementation
#

# KinD cluster config that maps host ports 80/443 into the (single) node container.
# Together with the hostPort settings in the Traefik values (values.yaml), this makes
# the gateway reachable on http://localhost without any kubectl port-forward.
#
# Note: port mappings are fixed at cluster creation - to change them, the cluster
# must be recreated.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
3 changes: 2 additions & 1 deletion launchers/controlplane/src/main/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ FROM eclipse-temurin:23.0.2_7-jre-alpine
ARG JVM_ARGS=""
ARG JAR

RUN apk --no-cache add curl
# gcompat provides the glibc dynamic loader required by Bouncy Castle LTS' native probe on musl-based Alpine
RUN apk --no-cache add curl gcompat

WORKDIR /app

Expand Down
1 change: 1 addition & 0 deletions launchers/dataplane/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies {
implementation(libs.edc.core.runtime)
implementation(libs.edc.ext.http)
implementation(libs.dataplane.sdk)
implementation(libs.dataplane.sdk.jakarta.ee)
implementation(libs.edc.spi.core)
implementation(libs.edc.lib.core)

Expand Down
3 changes: 2 additions & 1 deletion launchers/dataplane/src/main/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ FROM eclipse-temurin:23.0.2_7-jre-alpine
ARG JVM_ARGS=""
ARG JAR

RUN apk --no-cache add curl
# gcompat provides the glibc dynamic loader required by Bouncy Castle LTS' native probe on musl-based Alpine
RUN apk --no-cache add curl gcompat

WORKDIR /app

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.eclipse.dataplane.domain.Result;
import org.eclipse.dataplane.domain.dataflow.DataFlow;
import org.eclipse.dataplane.domain.registration.Oauth2ClientCredentialsAuthorization;
import org.eclipse.dataplane.port.DataPlaneRegistrationApiController;
import org.eclipse.dataplane.port.DataPlaneSignalingApiController;
import org.eclipse.edc.http.spi.EdcHttpClient;
import org.eclipse.edc.keys.KeyParserRegistryImpl;
import org.eclipse.edc.keys.LocalPublicKeyServiceImpl;
Expand Down Expand Up @@ -109,7 +111,7 @@ public void initialize(ServiceExtensionContext context) {
.registerAuthorization(new Oauth2ClientCredentialsAuthorization())
.registerAuthorization(new NoneAuthorization())
.endpoint(signalingApiConfig.dataFlowEndpoint(hostname.get()))
.transferType("HttpData-PULL")
.profile("HttpData-PULL")
.onPrepare(this::prepare)
.onStart(this::startDataFlow)
.onStarted(this::requestData)
Expand Down Expand Up @@ -139,8 +141,8 @@ public void initialize(ServiceExtensionContext context) {
var proxyPortMapping = new PortMapping(APICONTEXT_PROXY, proxyApiConfig.port(), proxyApiConfig.path());
portMappingRegistry.register(proxyPortMapping);

webService.registerResource(ApiContext.SIGNALING, dataplane.controller());
webService.registerResource(ApiContext.SIGNALING, dataplane.registrationController());
webService.registerResource(ApiContext.SIGNALING, new DataPlaneSignalingApiController(dataplane));
webService.registerResource(ApiContext.SIGNALING, new DataPlaneRegistrationApiController(dataplane));
webService.registerResource(APICONTEXT_PUBLIC, new DataPlanePublicApiController(httpClient, monitor, tokenValidationService, publicKeyResolver));
webService.registerResource(APICONTEXT_PROXY, new ConsumerProxyController(dataFetcher));
}
Expand All @@ -153,13 +155,16 @@ private Result<DataFlow> requestData(DataFlow dataFlow) {
}

private Result<DataFlow> prepare(DataFlow dataFlow) {
return dataFlow.getTransferType().equals("HttpData-PULL")
return "HttpData-PULL".equals(dataFlow.getProfile())
? Result.success(dataFlow)
: Result.failure(new UnsupportedOperationException("unsupported transfer type: " + dataFlow.getTransferType()));
: Result.failure(new UnsupportedOperationException("unsupported profile: " + dataFlow.getProfile()));
}

private @NotNull Result<DataFlow> startDataFlow(DataFlow dataFlow) {
switch (dataFlow.getTransferType()) {
if (dataFlow.getProfile() == null) {
return Result.failure(new UnsupportedOperationException("no profile specified on data flow " + dataFlow.getId()));
}
switch (dataFlow.getProfile()) {
case "NonFinite-PULL", "Finite-PULL", "HttpData-PULL" -> {

var token = tokenGenerationService.generate(privateKeyId,
Expand All @@ -171,7 +176,7 @@ private Result<DataFlow> prepare(DataFlow dataFlow) {
);

if (token.succeeded()) {
var dataAddress = new DataAddress(dataFlow.getTransferType(), "http",
var dataAddress = new DataAddress("http",
publicApiConfig.dataSourceEndpoint(hostname.get(), dataFlow.getId()),
List.of(new DataAddress.EndpointProperty("authorization", "access_token", token.getContent().getToken())));
dataFlow.setDataAddress(dataAddress);
Expand All @@ -183,7 +188,7 @@ private Result<DataFlow> prepare(DataFlow dataFlow) {

}
default -> {
return Result.failure(new RuntimeException("TransferType %s not supported".formatted(dataFlow.getTransferType())));
return Result.failure(new RuntimeException("Profile %s not supported".formatted(dataFlow.getProfile())));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ public ConsumerDataHandler(EdcHttpClient httpClient, Monitor monitor) {
}

public Result<DataFlow> storeDataflow(DataFlow dataFlow) {
if (dataFlow.getTransferType().equals("HttpData-PULL")) {
if ("HttpData-PULL".equals(dataFlow.getProfile())) {
ongoingTransfers.put(dataFlow.getId(), dataFlow.getDataAddress());
return Result.success(dataFlow);
} else {
monitor.warning("TransferType %s not supported".formatted(dataFlow.getTransferType()));
return Result.failure(new UnsupportedOperationException("TransferType %s not supported".formatted(dataFlow.getTransferType())));
monitor.warning("Profile %s not supported".formatted(dataFlow.getProfile()));
return Result.failure(new UnsupportedOperationException("Profile %s not supported".formatted(dataFlow.getProfile())));
}
}

Expand Down
3 changes: 2 additions & 1 deletion launchers/identity-hub/src/main/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ FROM eclipse-temurin:23.0.2_7-jre-alpine
ARG JVM_ARGS=""
ARG JAR

RUN apk --no-cache add curl
# gcompat provides the glibc dynamic loader required by Bouncy Castle LTS' native probe on musl-based Alpine
RUN apk --no-cache add curl gcompat

WORKDIR /app

Expand Down
3 changes: 2 additions & 1 deletion launchers/issuerservice/src/main/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ FROM eclipse-temurin:23_37-jre-alpine
ARG JVM_ARGS=""
ARG JAR

RUN apk --no-cache add curl
# gcompat provides the glibc dynamic loader required by Bouncy Castle LTS' native probe on musl-based Alpine
RUN apk --no-cache add curl gcompat

WORKDIR /app

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@
@EndToEndTest
public class CredentialIssuanceEndToEndTest {

private static final String CONSUMER_IDENTITYHUB_IDENTITY_URL = "http://ih.consumer.localhost:8080/cs";
private static final String CONSUMER_IDENTITYHUB_IDENTITY_URL = "http://ih.consumer.localhost/cs";
private static final String PARTICIPANT_CONTEXT_ID = "consumer-participant";
private static final String ISSUER_DID = "did:web:issuerservice.issuer.svc.cluster.local%3A10016:issuer";
private static final String HOLDER_PID = UUID.randomUUID().toString();
private static final String KEYCLOAK_URL = "http://keycloak.localhost:8080";
private static final String KEYCLOAK_URL = "http://keycloak.localhost";

private static RequestSpecification baseRequest() {
return given()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@
@EndToEndTest
public class TransferEndToEndTest {
// Management API base URL of the consumer connector, goes through Ingress controller
private static final String CONSUMER_MANAGEMENT_URL = "http://cp.consumer.localhost:8080";
private static final String CONSUMER_MANAGEMENT_URL = "http://cp.consumer.localhost";
// DSP service URL of the provider, not reachable outside the cluster
private static final String PROVIDER_DSP_URL = "http://controlplane.provider.svc.cluster.local:8082/api/dsp/2025-1";
// DID of the provider company
private static final String PROVIDER_ID = "did:web:identityhub.provider.svc.cluster.local%3A7083:provider";
private static final String CONSUMER_PROXY_URL = "http://dp.consumer.localhost:8080/api/proxy";
private static final String PROVIDER_MANAGEMENT_URL = "http://cp.provider.localhost:8080";
private static final String CONSUMER_PROXY_URL = "http://dp.consumer.localhost/api/proxy";
private static final String PROVIDER_MANAGEMENT_URL = "http://cp.provider.localhost";

private final TypeTransformerRegistry transformerRegistry = new TypeTransformerRegistryImpl();
private final JsonLd jsonLd = new TitaniumJsonLd(new ConsoleMonitor());
Expand Down
6 changes: 6 additions & 0 deletions values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ accessLog:
# capabilities:
# drop: []

# hostPort binds Traefik to ports 80/443 of the KinD node container. Combined with the
# extraPortMappings in kind-config.yaml, the gateway is reachable on http://localhost
# without a kubectl port-forward. Requires a single-node cluster (the pod must land on
# the node whose ports are mapped).
ports:
web:
port: 80
hostPort: 80
websecure:
port: 443
exposedPort: 443
hostPort: 443

# we enable gateway-api features
providers:
Expand Down
Loading